Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NEW] Sections layout and featured apps for marketplace #26514

Merged
merged 52 commits into from
Sep 9, 2022
Merged
Show file tree
Hide file tree
Changes from 49 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
b75b755
feat: :sparkles: Surface featured apps endpoint to RC
rique223 Jul 29, 2022
acc5fbf
Merge remote-tracking branch 'origin/develop' into feat/surface-featu…
rique223 Aug 1, 2022
12be875
Review
rique223 Aug 1, 2022
f820a8d
fix: :bug: Fix priority for featured apps endpoint and typo
rique223 Aug 1, 2022
535b683
Remove unecessary console.log
rique223 Aug 1, 2022
c28547d
WIP: Generalize AppsList
rique223 Aug 5, 2022
84a1c18
chore: :recycle: Refactor AppList to be generic for App arrays
rique223 Aug 8, 2022
cd9973b
feat: :sparkles: Consume featured endpoint and show new sections
rique223 Aug 10, 2022
4d73b60
Merge remote-tracking branch 'origin/develop' into feat/featured-apps…
rique223 Aug 10, 2022
d88f397
Merge branch 'develop' into feat/featured-apps-marketplace
rique223 Aug 10, 2022
7baec2c
feat: :sparkles: Implement search and loading behavior for new featur…
rique223 Aug 11, 2022
d3db6b2
Merge branch 'feat/featured-apps-marketplace' of github.com:RocketCha…
rique223 Aug 11, 2022
4af41f1
Merge remote-tracking branch 'origin/develop' into feat/featured-apps…
rique223 Aug 11, 2022
7c2a337
Merge branch 'develop' into feat/featured-apps-marketplace
rique223 Aug 11, 2022
7cec2db
feat: :sparkles: Hide featured app sections on filter
rique223 Aug 11, 2022
3057461
Remove unnecessary console.log
rique223 Aug 11, 2022
0cf70da
style: :lipstick: Restyle marketplace apps page tabs
rique223 Aug 12, 2022
5a13da3
refactor: :recycle: Change featured endpoint and solve reviews
rique223 Aug 12, 2022
3f6433a
Merge remote-tracking branch 'origin/develop' into feat/featured-apps…
rique223 Aug 12, 2022
0c8d9fb
Merge branch 'develop' into feat/featured-apps-marketplace
rique223 Aug 15, 2022
15eec3c
Merge branch 'develop' into feat/featured-apps-marketplace
rique223 Aug 16, 2022
95f85c6
Reviews
rique223 Aug 17, 2022
c12815c
Merge branch 'feat/featured-apps-marketplace' of github.com:RocketCha…
rique223 Aug 17, 2022
bb8bed7
Merge remote-tracking branch 'origin/develop' into feat/featured-apps…
rique223 Aug 17, 2022
c27cdbb
Reviews
rique223 Aug 17, 2022
af709fe
Merge branch 'develop' into feat/featured-apps-marketplace
rique223 Aug 19, 2022
1a09de1
Merge branch 'develop' into feat/featured-apps-marketplace
rique223 Aug 22, 2022
e4dc090
fix: :bug: Pagination bug and reviews
rique223 Aug 22, 2022
276126c
Merge branch 'develop' into feat/featured-apps-marketplace
rique223 Aug 23, 2022
8122a24
refactor: :recycle: AppsList loading skeletons behavior
rique223 Aug 23, 2022
02aefb9
Merge branch 'develop' into feat/featured-apps-marketplace
rique223 Aug 23, 2022
45fb166
Merge branch 'develop' into feat/featured-apps-marketplace
casalsgh Aug 23, 2022
d324f7d
Merge branch 'develop' into feat/featured-apps-marketplace
rique223 Aug 24, 2022
2fabdbb
Update apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json
rique223 Aug 24, 2022
bc733b1
Merge branch 'develop' into feat/featured-apps-marketplace
rique223 Aug 24, 2022
174ea2e
Merge branch 'develop' into feat/featured-apps-marketplace
rique223 Aug 25, 2022
d507cdc
refactor: :recycle: Component function definitions
rique223 Aug 25, 2022
82cbe70
Merge branch 'develop' into feat/featured-apps-marketplace
rique223 Aug 26, 2022
fcf2845
Merge branch 'develop' into feat/featured-apps-marketplace
casalsgh Aug 29, 2022
2a31d57
refactor: :recycle: Solve reviews
rique223 Aug 30, 2022
c508aa4
Merge branch 'feat/featured-apps-marketplace' of github.com:RocketCha…
rique223 Aug 30, 2022
bf8286f
Merge remote-tracking branch 'origin/develop' into feat/featured-apps…
rique223 Aug 30, 2022
8329ea0
Merge branch 'develop' into feat/featured-apps-marketplace
rique223 Sep 1, 2022
3cbdafb
Review
rique223 Sep 5, 2022
194eba3
Merge branch 'develop' into feat/featured-apps-marketplace
rique223 Sep 5, 2022
4c074b0
Merge branch 'develop' into feat/featured-apps-marketplace
geekgonecrazy Sep 6, 2022
365b864
review
ggazzo Sep 7, 2022
4b602cb
fix: :bug: Fix pagination behavior, data badge component and dupped b…
rique223 Sep 8, 2022
dfbd0d4
Merge branch 'develop' into feat/featured-apps-marketplace
rique223 Sep 8, 2022
2819393
Merge branch 'develop' into feat/featured-apps-marketplace
rique223 Sep 9, 2022
e34d3f5
Merge branch 'develop' into feat/featured-apps-marketplace
rique223 Sep 9, 2022
3e97f33
Merge branch 'develop' into feat/featured-apps-marketplace
kodiakhq[bot] Sep 9, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/meteor/app/apps/server/communication/rest.js
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ export class AppsRestApi {
);

this.api.addRoute(
'featured',
'featured-apps',
{ authRequired: true },
{
async get() {
Expand All @@ -362,7 +362,7 @@ export class AppsRestApi {

let result;
try {
result = HTTP.get(`${baseUrl}/v1/apps/featured`, {
result = HTTP.get(`${baseUrl}/v1/featured-apps`, {
headers,
});
} catch (e) {
Expand Down
15 changes: 13 additions & 2 deletions apps/meteor/client/components/Page/Page.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
import { Box } from '@rocket.chat/fuselage';
import Colors from '@rocket.chat/fuselage-tokens/colors';
import React, { useState, ReactElement, ComponentProps } from 'react';

import PageContext from './PageContext';

const Page = (props: ComponentProps<typeof Box>): ReactElement => {
type PageProps = Omit<ComponentProps<typeof Box>, 'backgroundColor'> & {
background?: 'light' | 'tint';
};

const surfaceMap = {
light: Colors.white,
tint: Colors.n100,
neutral: Colors.n400,
}; // TODO: Remove this export after the migration is complete

const Page = ({ background = 'light', ...props }: PageProps): ReactElement => {
const [border, setBorder] = useState(false);
return (
<PageContext.Provider value={[border, setBorder]}>
<Box
backgroundColor='surface'
is='section'
display='flex'
flexDirection='column'
Expand All @@ -17,6 +27,7 @@ const Page = (props: ComponentProps<typeof Box>): ReactElement => {
height='full'
overflow='hidden'
{...props}
backgroundColor={`var(--rcx-color-surface-${background}, ${surfaceMap[background]})`}
/>
</PageContext.Provider>
);
Expand Down
10 changes: 5 additions & 5 deletions apps/meteor/client/views/admin/apps/AppDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Box, Callout, Chip, Margins } from '@rocket.chat/fuselage';
import { Box, ButtonGroup, Callout, Chip, Margins } from '@rocket.chat/fuselage';
import { ExternalLink } from '@rocket.chat/ui-client';
import { TranslationKey, useTranslation } from '@rocket.chat/ui-contexts';
import React, { FC } from 'react';
Expand Down Expand Up @@ -59,13 +59,13 @@ const AppDetails: FC<AppDetailsProps> = ({ app }) => {
<Box fontScale='h4' mbe='x8'>
{t('Categories')}
</Box>
<Box display='flex' flexDirection='row'>
<ButtonGroup medium flexWrap='wrap'>
{categories?.map((current) => (
<Chip key={current} textTransform='uppercase' mie='x8'>
<Box color='hint'>{current}</Box>
<Chip key={current} textTransform='uppercase'>
{current}
</Chip>
))}
</Box>
</ButtonGroup>
</Box>

<Box is='section'>
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/client/views/admin/apps/AppDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ const AppDetailsPage: FC<{ id: string }> = function AppDetailsPage({ id }) {
<>
<AppDetailsHeader app={appData} />

<Tabs mis='-x24' mb='x36'>
<Tabs>
<Tabs.Item onClick={(): void => handleTabClick('details')} selected={!tab || tab === 'details'}>
{t('Details')}
</Tabs.Item>
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/client/views/admin/apps/AppStatus.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ const AppStatus = ({ app, showStatus = true, isAppDetailsPage, isSubscribed, ins
flexDirection='row'
alignItems='center'
justifyContent='center'
borderRadius='x2'
borderRadius='x4'
invisible={!showStatus && !loading}
>
<Button
Expand Down
216 changes: 20 additions & 196 deletions apps/meteor/client/views/admin/apps/AppsList.tsx
Original file line number Diff line number Diff line change
@@ -1,202 +1,26 @@
import {
Box,
States,
StatesAction,
StatesActions,
StatesIcon,
StatesSubtitle,
StatesSuggestion,
StatesSuggestionList,
StatesSuggestionListItem,
StatesSuggestionText,
StatesTitle,
Icon,
} from '@rocket.chat/fuselage';
import { useDebouncedState } from '@rocket.chat/fuselage-hooks';
import { useRoute, useTranslation } from '@rocket.chat/ui-contexts';
import React, { FC, useMemo, useState } from 'react';
import { App } from '@rocket.chat/core-typings';
import { Box } from '@rocket.chat/fuselage';
import React, { ReactElement } from 'react';

import { usePagination } from '../../../components/GenericTable/hooks/usePagination';
import { AsyncStatePhase } from '../../../lib/asyncState';
import { useAppsReload, useAppsResult } from './AppsContext';
import AppsFilters from './AppsFilters';
import AppsListMain from './AppsListMain';
import { RadioDropDownGroup } from './definitions/RadioDropDownDefinitions';
import { useCategories } from './hooks/useCategories';
import { useFilteredApps } from './hooks/useFilteredApps';
import { useRadioToggle } from './hooks/useRadioToggle';
import AppRow from './AppRow';

const AppsList: FC<{
type AppsListProps = {
apps: App[];
title: string;
isMarketplace: boolean;
}> = ({ isMarketplace }) => {
const t = useTranslation();
const { marketplaceApps, installedApps } = useAppsResult();
const [text, setText] = useDebouncedState('', 500);
const reload = useAppsReload();
const { current, itemsPerPage, setItemsPerPage: onSetItemsPerPage, setCurrent: onSetCurrent, ...paginationProps } = usePagination();

const marketplaceRoute = useRoute('admin-marketplace');

const [freePaidFilterStructure, setFreePaidFilterStructure] = useState({
label: t('Filter_By_Price'),
items: [
{ id: 'all', label: t('All_Prices'), checked: true },
{ id: 'free', label: t('Free_Apps'), checked: false },
{ id: 'paid', label: t('Paid_Apps'), checked: false },
],
});
const freePaidFilterOnSelected = useRadioToggle(setFreePaidFilterStructure);

const [statusFilterStructure, setStatusFilterStructure] = useState({
label: t('Filter_By_Status'),
items: [
{ id: 'all', label: t('All_status'), checked: true },
{ id: 'enabled', label: t('Enabled'), checked: false },
{ id: 'disabled', label: t('Disabled'), checked: false },
],
});
const statusFilterOnSelected = useRadioToggle(setStatusFilterStructure);

const [sortFilterStructure, setSortFilterStructure] = useState<RadioDropDownGroup>({
label: t('Sort_By'),
items: [
{ id: 'az', label: 'A-Z', checked: true },
{ id: 'za', label: 'Z-A', checked: false },
{ id: 'mru', label: t('Most_recent_updated'), checked: false },
{ id: 'lru', label: t('Least_recent_updated'), checked: false },
],
});
const sortFilterOnSelected = useRadioToggle(setSortFilterStructure);

const [categories, selectedCategories, categoryTagList, onSelected] = useCategories();
const appsResult = useFilteredApps({
appsData: isMarketplace ? marketplaceApps : installedApps,
text,
current,
itemsPerPage,
categories: useMemo(() => selectedCategories.map(({ label }) => label), [selectedCategories]),
purchaseType: useMemo(() => freePaidFilterStructure.items.find(({ checked }) => checked)?.id, [freePaidFilterStructure]),
sortingMethod: useMemo(() => sortFilterStructure.items.find(({ checked }) => checked)?.id, [sortFilterStructure]),
status: useMemo(() => statusFilterStructure.items.find(({ checked }) => checked)?.id, [statusFilterStructure]),
});

const isAppListReadyOrLoading =
appsResult.phase === AsyncStatePhase.LOADING || (appsResult.phase === AsyncStatePhase.RESOLVED && Boolean(appsResult.value.count));

const noInstalledAppsFound = appsResult.phase === AsyncStatePhase.RESOLVED && !isMarketplace && appsResult.value.total === 0;

const noMarketplaceOrInstalledAppMatches = appsResult.phase === AsyncStatePhase.RESOLVED && isMarketplace && appsResult.value.count === 0;

const noInstalledAppMatches =
appsResult.phase === AsyncStatePhase.RESOLVED && !isMarketplace && appsResult.value.total !== 0 && appsResult.value.count === 0;

return (
<>
<AppsFilters
setText={setText}
freePaidFilterStructure={freePaidFilterStructure}
freePaidFilterOnSelected={freePaidFilterOnSelected}
categories={categories}
selectedCategories={selectedCategories}
onSelected={onSelected}
sortFilterStructure={sortFilterStructure}
sortFilterOnSelected={sortFilterOnSelected}
categoryTagList={categoryTagList}
statusFilterStructure={statusFilterStructure}
statusFilterOnSelected={statusFilterOnSelected}
/>

{isAppListReadyOrLoading && (
<AppsListMain
appsResult={appsResult}
current={current}
itemsPerPage={itemsPerPage}
onSetItemsPerPage={onSetItemsPerPage}
onSetCurrent={onSetCurrent}
paginationProps={paginationProps}
isMarketplace={isMarketplace}
/>
)}

{noMarketplaceOrInstalledAppMatches && (
<Box mbs='x20'>
<States>
<StatesIcon name='magnifier' />
<StatesTitle>{t('No_app_matches')}</StatesTitle>
{appsResult?.value?.shouldShowSearchText ? (
<StatesSubtitle>
{t('No_marketplace_matches_for')}: <strong>"{text}"</strong>
</StatesSubtitle>
) : (
''
)}
<StatesSuggestion>
<StatesSuggestionText>{t('You_can_try_to')}:</StatesSuggestionText>
<StatesSuggestionList>
<StatesSuggestionListItem>{t('Search_by_category')}</StatesSuggestionListItem>
<StatesSuggestionListItem>{t('Search_for_a_more_general_term')}</StatesSuggestionListItem>
<StatesSuggestionListItem>{t('Search_for_a_more_specific_term')}</StatesSuggestionListItem>
<StatesSuggestionListItem>{t('Check_if_the_spelling_is_correct')}</StatesSuggestionListItem>
</StatesSuggestionList>
</StatesSuggestion>
</States>
</Box>
)}

{noInstalledAppMatches && (
<Box mbs='x20'>
<States>
<StatesIcon name='magnifier' />
<StatesTitle>{t('No_installed_app_matches')}</StatesTitle>
{appsResult?.value?.shouldShowSearchText ? (
<StatesSubtitle>
<span>
{t('No_app_matches_for')} <strong>"{text}"</strong>
</span>
</StatesSubtitle>
) : (
''
)}
<StatesSuggestion>
<StatesSuggestionText>{t('Try_searching_in_the_marketplace_instead')}</StatesSuggestionText>
</StatesSuggestion>
<StatesActions>
<StatesAction onClick={(): void => marketplaceRoute.push({ context: '' })}>{t('Search_on_marketplace')}</StatesAction>
</StatesActions>
</States>
</Box>
)}

{noInstalledAppsFound && (
<Box mbs='x20'>
<States>
<StatesIcon name='magnifier' />
<StatesTitle>{t('No_apps_installed')}</StatesTitle>
<StatesSubtitle>{t('Explore_the_marketplace_to_find_awesome_apps')}</StatesSubtitle>
<StatesActions>
<StatesAction onClick={(): void => marketplaceRoute.push({ context: '' })}>{t('Explore_marketplace')}</StatesAction>
</StatesActions>
</States>
</Box>
)}

{appsResult.phase === AsyncStatePhase.REJECTED && (
<Box mbs='x20'>
<States>
<StatesIcon variation='danger' name='circle-exclamation' />
<StatesTitle>{t('Connection_error')}</StatesTitle>
<StatesSubtitle>{t('Marketplace_error')}</StatesSubtitle>
<StatesActions>
<StatesAction onClick={reload}>
<Icon mie='x4' size='x20' name='reload' />
{t('Reload_page')}
</StatesAction>
</StatesActions>
</States>
</Box>
)}
</>
);
};

const AppsList = ({ apps, title, isMarketplace }: AppsListProps): ReactElement => (
<>
<Box is='h3' fontScale='h3' color='default' mbe='x20'>
{title}
</Box>
<Box mbe='x36'>
{apps.map((app) => (
<AppRow key={app.id} isMarketplace={isMarketplace} {...app} />
))}
</Box>
</>
);

export default AppsList;
68 changes: 0 additions & 68 deletions apps/meteor/client/views/admin/apps/AppsListMain.tsx

This file was deleted.

Loading