From 9ed72070c20bc5ed118e7903a7a935cc50075272 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Thu, 8 Feb 2024 16:00:50 +0100 Subject: [PATCH 01/32] feat: init empty workspace categories page --- assets/images/folder.svg | 1 + .../simple-illustration__folder-open.svg | 7 ++ src/ROUTES.ts | 4 ++ src/SCREENS.ts | 5 +- src/components/Icon/Expensicons.ts | 2 + src/components/Icon/Illustrations.ts | 2 + src/languages/en.ts | 1 + src/languages/es.ts | 1 + .../BaseCentralPaneNavigator.tsx | 1 + .../TAB_TO_CENTRAL_PANE_MAPPING.ts | 1 + src/libs/Navigation/linkingConfig/config.ts | 3 + src/libs/Navigation/types.ts | 3 + src/pages/workspace/WorkspaceInitialPage.tsx | 6 ++ .../categories/WorkspaceCategoriesPage.js | 31 ++++++++ .../categories/withWorkspaceAccess.tsx | 70 +++++++++++++++++++ 15 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 assets/images/folder.svg create mode 100644 assets/images/simple-illustrations/simple-illustration__folder-open.svg create mode 100644 src/pages/workspace/categories/WorkspaceCategoriesPage.js create mode 100644 src/pages/workspace/categories/withWorkspaceAccess.tsx diff --git a/assets/images/folder.svg b/assets/images/folder.svg new file mode 100644 index 000000000000..17cef959132f --- /dev/null +++ b/assets/images/folder.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/images/simple-illustrations/simple-illustration__folder-open.svg b/assets/images/simple-illustrations/simple-illustration__folder-open.svg new file mode 100644 index 000000000000..c104313a9b6c --- /dev/null +++ b/assets/images/simple-illustrations/simple-illustration__folder-open.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 03682a19bff4..256d5bb0cdfa 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -500,6 +500,10 @@ const ROUTES = { route: 'workspace/:policyID/members', getRoute: (policyID: string) => `workspace/${policyID}/members` as const, }, + WORKSPACE_CATEGORIES: { + route: 'workspace/:policyID/categories', + getRoute: (policyID: string) => `workspace/${policyID}/categories` as const, + }, // Referral program promotion REFERRAL_DETAILS_MODAL: { route: 'referral/:contentType', diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 949adcba4d44..e326d33b8468 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -207,8 +207,9 @@ const SCREENS = { MEMBERS: 'Workspace_Members', INVITE: 'Workspace_Invite', INVITE_MESSAGE: 'Workspace_Invite_Message', - CURRENCY: 'Workspace_Profile_Currency', - DESCRIPTION: 'Workspace_Profile_Description', + CATEGORIES: 'Workspace_Categories', + CURRENCY: 'Workspace_Overview_Currency', + DESCRIPTION: 'Workspace_Description', NAME: 'Workspace_Profile_Name', }, diff --git a/src/components/Icon/Expensicons.ts b/src/components/Icon/Expensicons.ts index 06619e2e215f..176b807a7cc6 100644 --- a/src/components/Icon/Expensicons.ts +++ b/src/components/Icon/Expensicons.ts @@ -67,6 +67,7 @@ import Flag from '@assets/images/flag.svg'; import FlagLevelOne from '@assets/images/flag_level_01.svg'; import FlagLevelTwo from '@assets/images/flag_level_02.svg'; import FlagLevelThree from '@assets/images/flag_level_03.svg'; +import Folder from '@assets/images/folder.svg'; import Fullscreen from '@assets/images/fullscreen.svg'; import Gallery from '@assets/images/gallery.svg'; import Gear from '@assets/images/gear.svg'; @@ -215,6 +216,7 @@ export { FlagLevelTwo, FlagLevelThree, Fullscreen, + Folder, Gallery, Gear, Globe, diff --git a/src/components/Icon/Illustrations.ts b/src/components/Icon/Illustrations.ts index 911f09104349..1e1bd4aefc33 100644 --- a/src/components/Icon/Illustrations.ts +++ b/src/components/Icon/Illustrations.ts @@ -37,6 +37,7 @@ import ConciergeBubble from '@assets/images/simple-illustrations/simple-illustra import ConciergeNew from '@assets/images/simple-illustrations/simple-illustration__concierge.svg'; import CreditCardsNew from '@assets/images/simple-illustrations/simple-illustration__credit-cards.svg'; import EmailAddress from '@assets/images/simple-illustrations/simple-illustration__email-address.svg'; +import FolderOpen from '@assets/images/simple-illustrations/simple-illustration__folder-open.svg'; import Gears from '@assets/images/simple-illustrations/simple-illustration__gears.svg'; import HandCard from '@assets/images/simple-illustrations/simple-illustration__handcard.svg'; import HandEarth from '@assets/images/simple-illustrations/simple-illustration__handearth.svg'; @@ -77,6 +78,7 @@ export { ConciergeExclamation, CreditCardsBlue, EmailAddress, + FolderOpen, HandCard, HotDogStand, InvoiceOrange, diff --git a/src/languages/en.ts b/src/languages/en.ts index f33ddde1fb53..3f17f230fda7 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1674,6 +1674,7 @@ export default { delete: 'Delete workspace', settings: 'Settings', reimburse: 'Reimbursements', + categories: 'Categories', bills: 'Bills', invoices: 'Invoices', travel: 'Travel', diff --git a/src/languages/es.ts b/src/languages/es.ts index 6bc225fbea28..156e353b1be5 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1698,6 +1698,7 @@ export default { delete: 'Eliminar espacio de trabajo', settings: 'Configuración', reimburse: 'Reembolsos', + categories: 'Categorías', bills: 'Pagar facturas', invoices: 'Enviar facturas', travel: 'Viajes', diff --git a/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx index 087e963b3892..c25154d2d635 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/CentralPaneNavigator/BaseCentralPaneNavigator.tsx @@ -22,6 +22,7 @@ const workspaceSettingsScreens = { [SCREENS.WORKSPACE.INVOICES]: () => require('../../../../../pages/workspace/invoices/WorkspaceInvoicesPage').default as React.ComponentType, [SCREENS.WORKSPACE.TRAVEL]: () => require('../../../../../pages/workspace/travel/WorkspaceTravelPage').default as React.ComponentType, [SCREENS.WORKSPACE.MEMBERS]: () => require('../../../../../pages/workspace/WorkspaceMembersPage').default as React.ComponentType, + [SCREENS.WORKSPACE.CATEGORIES]: () => require('../../../../../pages/workspace/categories/WorkspaceCategoriesPage').default as React.ComponentType, } satisfies Screens; function BaseCentralPaneNavigator() { diff --git a/src/libs/Navigation/linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING.ts b/src/libs/Navigation/linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING.ts index 446fb479ea09..47b646f4d150 100755 --- a/src/libs/Navigation/linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/TAB_TO_CENTRAL_PANE_MAPPING.ts @@ -12,6 +12,7 @@ const TAB_TO_CENTRAL_PANE_MAPPING: Record = { SCREENS.WORKSPACE.INVOICES, SCREENS.WORKSPACE.TRAVEL, SCREENS.WORKSPACE.MEMBERS, + SCREENS.WORKSPACE.CATEGORIES, ], }; diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index a1579832ca54..26024743adc2 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -61,6 +61,9 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.MEMBERS]: { path: ROUTES.WORKSPACE_MEMBERS.route, }, + [SCREENS.WORKSPACE.CATEGORIES]: { + path: ROUTES.WORKSPACE_CATEGORIES.route, + }, }, }, [SCREENS.NOT_FOUND]: '*', diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index ba59bcf48c05..20edf62e00d6 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -74,6 +74,9 @@ type CentralPaneNavigatorParamList = { [SCREENS.WORKSPACE.MEMBERS]: { policyID: string; }; + [SCREENS.WORKSPACE.CATEGORIES]: { + policyID: string; + }; }; type WorkspaceSwitcherNavigatorParamList = { diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index 07049a91bee9..51ae9cc9f295 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -137,6 +137,12 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, policyMembers, r brickRoadIndicator: hasMembersError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, routeName: SCREENS.WORKSPACE.MEMBERS, }, + { + translationKey: 'workspace.common.categories', + icon: Expensicons.Folder, + action: singleExecution(waitForNavigate(() => Navigation.navigate(ROUTES.WORKSPACE_CATEGORIES.getRoute(policy.id)))), + routeName: SCREENS.WORKSPACE.CATEGORIES, + }, { translationKey: 'workspace.common.bankAccount', icon: Expensicons.Bank, diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.js b/src/pages/workspace/categories/WorkspaceCategoriesPage.js new file mode 100644 index 000000000000..389d5aa3c101 --- /dev/null +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.js @@ -0,0 +1,31 @@ +import React from 'react'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import * as Illustrations from '@components/Icon/Illustrations'; +import ScreenWrapper from '@components/ScreenWrapper'; +import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; +import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions'; +import useWindowDimensions from '@hooks/useWindowDimensions'; +import compose from '@libs/compose'; + +const propTypes = { + ...windowDimensionsPropTypes, + ...withLocalizePropTypes, +}; + +function WorkspaceCategoriesPage(props) { + const {isSmallScreenWidth} = useWindowDimensions(); + + return ( + + + + ); +} + +WorkspaceCategoriesPage.propTypes = propTypes; + +export default compose(withLocalize, withWindowDimensions)(WorkspaceCategoriesPage); diff --git a/src/pages/workspace/categories/withWorkspaceAccess.tsx b/src/pages/workspace/categories/withWorkspaceAccess.tsx new file mode 100644 index 000000000000..01509afdae3c --- /dev/null +++ b/src/pages/workspace/categories/withWorkspaceAccess.tsx @@ -0,0 +1,70 @@ +import type {RouteProp} from '@react-navigation/native'; +import {useEffect} from 'react'; +import type {ForwardedRef} from 'react'; +import type {OnyxEntry} from 'react-native-onyx'; +import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; +import getComponentDisplayName from '@libs/getComponentDisplayName'; +import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; +import * as Policy from '@userActions/Policy'; +import type * as OnyxTypes from '@src/types/onyx'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; + +type WithWorkspaceAccessOnyxProps = { + report: OnyxEntry; + isLoadingWorkspaceData: OnyxEntry; +}; + +type WithWorkspaceAccessProps = WithWorkspaceAccessOnyxProps & { + route: RouteProp<{params: {policyID: string}}>; +}; + +export default function (): ( + WrappedComponent: React.ComponentType>, +) => React.ComponentType, keyof WithWorkspaceAccessOnyxProps>> { + return function (WrappedComponent: React.ComponentType>) { + function WithWorkspaceAccess(props: TProps, ref: ForwardedRef) { + const isPolicyIDInRoute = !!props.route.params.policyID?.length; + + useEffect(() => { + if (!isPolicyIDInRoute || !isEmptyObject(props.report)) { + return; + } + + Policy.openWorkspace(props.route.params.policyID, []); + }, [isPolicyIDInRoute, props.report, props.route.params.policyID]); + + const shouldShowFullScreenLoadingIndicator = props.isLoadingWorkspaceData !== false && (!isEmptyObject(props.report) || !props.report?.policyID); + // eslint-disable-next-line rulesdir/no-negated-variables + const shouldShowNotFoundPage = !isEmptyObject(props.report) || !props.report?.policyID || !Policy.canAccessWorkspace(props.report); + + if (shouldShowFullScreenLoadingIndicator) { + return ; + } + + if (shouldShowNotFoundPage) { + return ; + } + + return ( + + ); + } + + WithWorkspaceAccess.displayName = `withWorkspaceAccess(${getComponentDisplayName(WrappedComponent)})`; + + return withOnyx, WithWorkspaceAccessOnyxProps>({ + report: { + key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.policyID}`, + }, + isLoadingWorkspaceData: { + key: ONYXKEYS.IS_LOADING_REPORT_DATA, + }, + })(React.forwardRef(WithWorkspaceAccess)); + }; +} + +export type {WithWorkspaceAccessProps}; From 436e2ff1a3a8cb16f83d4ff33ed1cbb21786192b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Mon, 12 Feb 2024 13:07:31 +0100 Subject: [PATCH 02/32] refactor: add right element to ratio list item, refactor workspace page --- .../SelectionList/RadioListItem.tsx | 27 +++--- src/components/SelectionList/types.ts | 3 + src/languages/en.ts | 2 + src/languages/es.ts | 2 + .../categories/WorkspaceCategoriesPage.js | 84 ++++++++++++++++++- ...ess.tsx => withPolicyAccessOrNotFound.tsx} | 33 +++++--- 6 files changed, 125 insertions(+), 26 deletions(-) rename src/pages/workspace/categories/{withWorkspaceAccess.tsx => withPolicyAccessOrNotFound.tsx} (65%) diff --git a/src/components/SelectionList/RadioListItem.tsx b/src/components/SelectionList/RadioListItem.tsx index ca0ae859c3ad..71660383052a 100644 --- a/src/components/SelectionList/RadioListItem.tsx +++ b/src/components/SelectionList/RadioListItem.tsx @@ -8,21 +8,24 @@ function RadioListItem({item, showTooltip, textStyles, alternateTextStyles}: Rad const styles = useThemeStyles(); return ( - - - - {!!item.alternateText && ( + <> + - )} - + + {!!item.alternateText && ( + + )} + + {!!item.rightElement && item.rightElement} + ); } diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index be871971ee92..9f532c9b93a5 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -104,6 +104,9 @@ type RadioItem = { /** Represents the index of the option within the section it came from */ index?: number; + + /** Element to show on the right side of the item */ + rightElement?: ReactNode; }; type RadioListItemProps = CommonListItemProps & { diff --git a/src/languages/en.ts b/src/languages/en.ts index 3f17f230fda7..c0eec6bbdc6f 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1671,6 +1671,8 @@ export default { card: 'Cards', workspace: 'Workspace', edit: 'Edit workspace', + enabled: 'Enabled', + disabled: 'Disabled', delete: 'Delete workspace', settings: 'Settings', reimburse: 'Reimbursements', diff --git a/src/languages/es.ts b/src/languages/es.ts index 156e353b1be5..d7decda09c97 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1695,6 +1695,8 @@ export default { card: 'Tarjetas', workspace: 'Espacio de trabajo', edit: 'Editar espacio de trabajo', + enabled: 'Activada', + disabled: 'Desactivada', delete: 'Eliminar espacio de trabajo', settings: 'Configuración', reimburse: 'Reembolsos', diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.js b/src/pages/workspace/categories/WorkspaceCategoriesPage.js index 389d5aa3c101..975c29949002 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.js +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.js @@ -1,31 +1,107 @@ -import React from 'react'; +import React, {useMemo, useState} from 'react'; +import {View} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; +import _ from 'underscore'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import Icon from '@components/Icon'; +import * as Expensicons from '@components/Icon/Expensicons'; import * as Illustrations from '@components/Icon/Illustrations'; import ScreenWrapper from '@components/ScreenWrapper'; +import SelectionList from '@components/SelectionList'; +import Text from '@components/Text'; import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions'; +import useLocalize from '@hooks/useLocalize'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import compose from '@libs/compose'; +import ONYXKEYS from '@src/ONYXKEYS'; +import withPolicyAccessOrNotFound from './withPolicyAccessOrNotFound'; const propTypes = { ...windowDimensionsPropTypes, ...withLocalizePropTypes, }; -function WorkspaceCategoriesPage(props) { +function WorkspaceCategoriesPage({policyCategories}) { const {isSmallScreenWidth} = useWindowDimensions(); + const styles = useThemeStyles(); + const theme = useTheme(); + const {translate} = useLocalize(); + const [selectedCategories, setSelectedCategories] = useState([]); + + const data = useMemo( + () => + _.map(_.values(policyCategories), (value) => ({ + value: value.name, + text: value.name, + keyForList: value.name, + isSelected: selectedCategories.includes(value.name), + rightElement: ( + + {value.enabled ? translate('workspace.common.enabled') : translate('workspace.common.disabled')} + + + + + ), + })), + [policyCategories, selectedCategories, styles.alignSelfCenter, styles.disabledText, styles.flexRow, styles.p1, theme.icon, translate], + ); + + const toggleCategory = (category) => { + setSelectedCategories((prev) => { + if (prev.includes(category.value)) { + return _.filter(prev, (item) => item !== category.value); + } + return [...prev, category.value]; + }); + }; + + const toggleAllCategories = () => { + const isAllSelected = _.every(data, (category) => category.isSelected); + if (isAllSelected) { + setSelectedCategories([]); + } else { + setSelectedCategories(_.map(data, (item) => item.value)); + } + }; return ( + {key}} + showScrollIndicator + /> ); } WorkspaceCategoriesPage.propTypes = propTypes; -export default compose(withLocalize, withWindowDimensions)(WorkspaceCategoriesPage); +export default compose( + withPolicyAccessOrNotFound(), + withWindowDimensions, + withOnyx({ + policyCategories: { + key: ({ + route: { + params: {policyID}, + }, + }) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + }, + }), +)(WorkspaceCategoriesPage); diff --git a/src/pages/workspace/categories/withWorkspaceAccess.tsx b/src/pages/workspace/categories/withPolicyAccessOrNotFound.tsx similarity index 65% rename from src/pages/workspace/categories/withWorkspaceAccess.tsx rename to src/pages/workspace/categories/withPolicyAccessOrNotFound.tsx index 01509afdae3c..642dce2ebd20 100644 --- a/src/pages/workspace/categories/withWorkspaceAccess.tsx +++ b/src/pages/workspace/categories/withPolicyAccessOrNotFound.tsx @@ -1,21 +1,32 @@ +/* eslint-disable rulesdir/no-negated-variables */ import type {RouteProp} from '@react-navigation/native'; -import {useEffect} from 'react'; -import type {ForwardedRef} from 'react'; +import React, {useEffect} from 'react'; +import type {ForwardedRef, RefAttributes} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import getComponentDisplayName from '@libs/getComponentDisplayName'; +import * as PolicyUtils from '@libs/PolicyUtils'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; import * as Policy from '@userActions/Policy'; +import ONYXKEYS from '@src/ONYXKEYS'; import type * as OnyxTypes from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; type WithWorkspaceAccessOnyxProps = { - report: OnyxEntry; + /** The report currently being looked at */ + policy: OnyxEntry; + + /** Indicated whether the report data is loading */ isLoadingWorkspaceData: OnyxEntry; }; type WithWorkspaceAccessProps = WithWorkspaceAccessOnyxProps & { + /** The report currently being looked at */ route: RouteProp<{params: {policyID: string}}>; + + /** The report currently being looked at */ + policy: OnyxTypes.Policy; }; export default function (): ( @@ -26,16 +37,18 @@ export default function (): ( const isPolicyIDInRoute = !!props.route.params.policyID?.length; useEffect(() => { - if (!isPolicyIDInRoute || !isEmptyObject(props.report)) { + if (!isPolicyIDInRoute || !isEmptyObject(props.policy)) { + // If the workspace is not required or is already loaded, we don't need to call the API return; } Policy.openWorkspace(props.route.params.policyID, []); - }, [isPolicyIDInRoute, props.report, props.route.params.policyID]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isPolicyIDInRoute, props.route.params.policyID]); + + const shouldShowFullScreenLoadingIndicator = props.isLoadingWorkspaceData !== false && (!Object.entries(props.policy ?? {}).length || !props.policy?.id); - const shouldShowFullScreenLoadingIndicator = props.isLoadingWorkspaceData !== false && (!isEmptyObject(props.report) || !props.report?.policyID); - // eslint-disable-next-line rulesdir/no-negated-variables - const shouldShowNotFoundPage = !isEmptyObject(props.report) || !props.report?.policyID || !Policy.canAccessWorkspace(props.report); + const shouldShowNotFoundPage = isEmptyObject(props.policy) || !props.policy?.id || !PolicyUtils.isPolicyAdmin(props.policy); if (shouldShowFullScreenLoadingIndicator) { return ; @@ -57,8 +70,8 @@ export default function (): ( WithWorkspaceAccess.displayName = `withWorkspaceAccess(${getComponentDisplayName(WrappedComponent)})`; return withOnyx, WithWorkspaceAccessOnyxProps>({ - report: { - key: ({route}) => `${ONYXKEYS.COLLECTION.REPORT}${route.params.policyID}`, + policy: { + key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY}${route.params.policyID ?? ''}`, }, isLoadingWorkspaceData: { key: ONYXKEYS.IS_LOADING_REPORT_DATA, From 89aa49c0bdf266bfc9e0aa952aa8e746a5e8b341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Mon, 12 Feb 2024 13:07:47 +0100 Subject: [PATCH 03/32] refactor: remove prop types --- src/pages/workspace/categories/WorkspaceCategoriesPage.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.js b/src/pages/workspace/categories/WorkspaceCategoriesPage.js index 975c29949002..eeaa80fc865f 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.js +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.js @@ -9,7 +9,6 @@ import * as Illustrations from '@components/Icon/Illustrations'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import Text from '@components/Text'; -import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions'; import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; @@ -21,7 +20,6 @@ import withPolicyAccessOrNotFound from './withPolicyAccessOrNotFound'; const propTypes = { ...windowDimensionsPropTypes, - ...withLocalizePropTypes, }; function WorkspaceCategoriesPage({policyCategories}) { From c329279709895089186df49d90d985138676a467 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Mon, 12 Feb 2024 13:15:38 +0100 Subject: [PATCH 04/32] fix: remove rightHandSideComponent --- src/pages/workspace/categories/WorkspaceCategoriesPage.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.js b/src/pages/workspace/categories/WorkspaceCategoriesPage.js index eeaa80fc865f..6d66c8cdc352 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.js +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.js @@ -81,7 +81,6 @@ function WorkspaceCategoriesPage({policyCategories}) { sections={[{data, indexOffset: 0, isDisabled: false}]} onSelectRow={toggleCategory} onSelectAll={toggleAllCategories} - rightHandSideComponent={({key}) => {key}} showScrollIndicator /> From 1aa6c21a47e7818d12fd8005c614bbc2254d14f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Mon, 12 Feb 2024 13:25:17 +0100 Subject: [PATCH 05/32] fix: rebase --- src/SCREENS.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SCREENS.ts b/src/SCREENS.ts index e326d33b8468..349585ed5780 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -208,9 +208,9 @@ const SCREENS = { INVITE: 'Workspace_Invite', INVITE_MESSAGE: 'Workspace_Invite_Message', CATEGORIES: 'Workspace_Categories', - CURRENCY: 'Workspace_Overview_Currency', - DESCRIPTION: 'Workspace_Description', + CURRENCY: 'Workspace_Profile_Currency', NAME: 'Workspace_Profile_Name', + DESCRIPTION: 'Workspace_Description', }, EDIT_REQUEST: { From 8e4358809f5795e6a18f99cbd0d9c11505a25f62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Mon, 12 Feb 2024 13:26:31 +0100 Subject: [PATCH 06/32] refactor: remove withWindowDimensions --- src/pages/workspace/categories/WorkspaceCategoriesPage.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.js b/src/pages/workspace/categories/WorkspaceCategoriesPage.js index 6d66c8cdc352..5af437b1dede 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.js +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.js @@ -9,7 +9,6 @@ import * as Illustrations from '@components/Icon/Illustrations'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import Text from '@components/Text'; -import withWindowDimensions, {windowDimensionsPropTypes} from '@components/withWindowDimensions'; import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -18,10 +17,6 @@ import compose from '@libs/compose'; import ONYXKEYS from '@src/ONYXKEYS'; import withPolicyAccessOrNotFound from './withPolicyAccessOrNotFound'; -const propTypes = { - ...windowDimensionsPropTypes, -}; - function WorkspaceCategoriesPage({policyCategories}) { const {isSmallScreenWidth} = useWindowDimensions(); const styles = useThemeStyles(); @@ -87,11 +82,8 @@ function WorkspaceCategoriesPage({policyCategories}) { ); } -WorkspaceCategoriesPage.propTypes = propTypes; - export default compose( withPolicyAccessOrNotFound(), - withWindowDimensions, withOnyx({ policyCategories: { key: ({ From aab6876e290e2d872e42e01bb9213bdbde66c8dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Mon, 12 Feb 2024 13:34:36 +0100 Subject: [PATCH 07/32] feat: add prop types --- .../categories/WorkspaceCategoriesPage.js | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.js b/src/pages/workspace/categories/WorkspaceCategoriesPage.js index 5af437b1dede..222304acf543 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.js +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.js @@ -1,7 +1,9 @@ +import PropTypes from 'prop-types'; import React, {useMemo, useState} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import _ from 'underscore'; +import categoryPropTypes from '@components/categoryPropTypes'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; @@ -17,6 +19,25 @@ import compose from '@libs/compose'; import ONYXKEYS from '@src/ONYXKEYS'; import withPolicyAccessOrNotFound from './withPolicyAccessOrNotFound'; +const propTypes = { + /* Onyx Props */ + /** Collection of categories attached to a policy */ + policyCategories: PropTypes.objectOf(categoryPropTypes), + + /** URL Route params */ + route: PropTypes.shape({ + /** Params from the URL path */ + params: PropTypes.shape({ + /** policyID passed via route: /workspace/:policyID/categories */ + policyID: PropTypes.string, + }), + }).isRequired, +}; + +const defaultProps = { + policyCategories: {}, +}; + function WorkspaceCategoriesPage({policyCategories}) { const {isSmallScreenWidth} = useWindowDimensions(); const styles = useThemeStyles(); @@ -82,6 +103,10 @@ function WorkspaceCategoriesPage({policyCategories}) { ); } +WorkspaceCategoriesPage.propTypes = propTypes; +WorkspaceCategoriesPage.defaultProps = defaultProps; +WorkspaceCategoriesPage.displayName = 'WorkspaceCategoriesPage'; + export default compose( withPolicyAccessOrNotFound(), withOnyx({ From 105cdc7bca1a46380bd25881c0fb2c448b38facf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Mon, 12 Feb 2024 13:37:31 +0100 Subject: [PATCH 08/32] refactor: use policyID instead policy.id --- src/pages/workspace/WorkspaceInitialPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index 51ae9cc9f295..075aaa5046cc 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -140,7 +140,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, policyMembers, r { translationKey: 'workspace.common.categories', icon: Expensicons.Folder, - action: singleExecution(waitForNavigate(() => Navigation.navigate(ROUTES.WORKSPACE_CATEGORIES.getRoute(policy.id)))), + action: singleExecution(waitForNavigate(() => Navigation.navigate(ROUTES.WORKSPACE_CATEGORIES.getRoute(policyID)))), routeName: SCREENS.WORKSPACE.CATEGORIES, }, { From 7c596f44e6b89cbe56851eda31404d4b53d31a92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Mon, 12 Feb 2024 14:01:23 +0100 Subject: [PATCH 09/32] refactor: migrate to typescript --- ...iesPage.js => WorkspaceCategoriesPage.tsx} | 66 +++++++++---------- 1 file changed, 32 insertions(+), 34 deletions(-) rename src/pages/workspace/categories/{WorkspaceCategoriesPage.js => WorkspaceCategoriesPage.tsx} (66%) diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.js b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx similarity index 66% rename from src/pages/workspace/categories/WorkspaceCategoriesPage.js rename to src/pages/workspace/categories/WorkspaceCategoriesPage.tsx index 222304acf543..ab175ba860a5 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.js +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx @@ -1,9 +1,8 @@ -import PropTypes from 'prop-types'; +import type {StackScreenProps} from '@react-navigation/stack'; import React, {useMemo, useState} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; -import _ from 'underscore'; -import categoryPropTypes from '@components/categoryPropTypes'; +import type {OnyxEntry} from 'react-native-onyx'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; @@ -16,38 +15,38 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import compose from '@libs/compose'; +import type {CentralPaneNavigatorParamList} from '@navigation/types'; import ONYXKEYS from '@src/ONYXKEYS'; +import type SCREENS from '@src/SCREENS'; +import type * as OnyxTypes from '@src/types/onyx'; import withPolicyAccessOrNotFound from './withPolicyAccessOrNotFound'; +import type {WithWorkspaceAccessProps} from './withPolicyAccessOrNotFound'; -const propTypes = { - /* Onyx Props */ - /** Collection of categories attached to a policy */ - policyCategories: PropTypes.objectOf(categoryPropTypes), - - /** URL Route params */ - route: PropTypes.shape({ - /** Params from the URL path */ - params: PropTypes.shape({ - /** policyID passed via route: /workspace/:policyID/categories */ - policyID: PropTypes.string, - }), - }).isRequired, +type PolicyForList = { + value: string; + text: string; + keyForList: string; + isSelected: boolean; + rightElement: React.ReactNode; }; -const defaultProps = { - policyCategories: {}, +type WorkspaceCategoriesOnyxProps = { + /** Collection of categories attached to a policy */ + policyCategories: OnyxEntry; }; -function WorkspaceCategoriesPage({policyCategories}) { +type WorkspaceCategoriesPageProps = WorkspaceCategoriesOnyxProps & WithWorkspaceAccessProps & StackScreenProps; + +function WorkspaceCategoriesPage({policyCategories}: WorkspaceCategoriesPageProps) { const {isSmallScreenWidth} = useWindowDimensions(); const styles = useThemeStyles(); const theme = useTheme(); const {translate} = useLocalize(); - const [selectedCategories, setSelectedCategories] = useState([]); + const [selectedCategories, setSelectedCategories] = useState([]); const data = useMemo( () => - _.map(_.values(policyCategories), (value) => ({ + Object.values(policyCategories ?? {}).map((value) => ({ value: value.name, text: value.name, keyForList: value.name, @@ -67,26 +66,31 @@ function WorkspaceCategoriesPage({policyCategories}) { [policyCategories, selectedCategories, styles.alignSelfCenter, styles.disabledText, styles.flexRow, styles.p1, theme.icon, translate], ); - const toggleCategory = (category) => { + const toggleCategory = (category: PolicyForList) => { setSelectedCategories((prev) => { if (prev.includes(category.value)) { - return _.filter(prev, (item) => item !== category.value); + return prev.filter((item) => item !== category.value); } return [...prev, category.value]; }); }; const toggleAllCategories = () => { - const isAllSelected = _.every(data, (category) => category.isSelected); + const isAllSelected = data.every((category) => category.isSelected); if (isAllSelected) { setSelectedCategories([]); } else { - setSelectedCategories(_.map(data, (item) => item.value)); + setSelectedCategories(data.map((item) => item.value)); } }; return ( - + ({ policyCategories: { - key: ({ - route: { - params: {policyID}, - }, - }) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${route.params.policyID}`, }, }), )(WorkspaceCategoriesPage); From d3b0e4413e0a0f31bd3f671417ab03bacd2d1bb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Tue, 13 Feb 2024 10:25:16 +0100 Subject: [PATCH 10/32] fix: ts params issue --- .../workspace/categories/WorkspaceCategoriesPage.tsx | 8 +++----- .../workspace/categories/withPolicyAccessOrNotFound.tsx | 3 +-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx index ab175ba860a5..832c07c90431 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx @@ -14,7 +14,6 @@ import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; -import compose from '@libs/compose'; import type {CentralPaneNavigatorParamList} from '@navigation/types'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; @@ -109,11 +108,10 @@ function WorkspaceCategoriesPage({policyCategories}: WorkspaceCategoriesPageProp WorkspaceCategoriesPage.displayName = 'WorkspaceCategoriesPage'; -export default compose( - withPolicyAccessOrNotFound(), +export default withPolicyAccessOrNotFound()( withOnyx({ policyCategories: { key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${route.params.policyID}`, }, - }), -)(WorkspaceCategoriesPage); + })(WorkspaceCategoriesPage), +); diff --git a/src/pages/workspace/categories/withPolicyAccessOrNotFound.tsx b/src/pages/workspace/categories/withPolicyAccessOrNotFound.tsx index 642dce2ebd20..12ffdce22937 100644 --- a/src/pages/workspace/categories/withPolicyAccessOrNotFound.tsx +++ b/src/pages/workspace/categories/withPolicyAccessOrNotFound.tsx @@ -1,5 +1,4 @@ /* eslint-disable rulesdir/no-negated-variables */ -import type {RouteProp} from '@react-navigation/native'; import React, {useEffect} from 'react'; import type {ForwardedRef, RefAttributes} from 'react'; import type {OnyxEntry} from 'react-native-onyx'; @@ -23,7 +22,7 @@ type WithWorkspaceAccessOnyxProps = { type WithWorkspaceAccessProps = WithWorkspaceAccessOnyxProps & { /** The report currently being looked at */ - route: RouteProp<{params: {policyID: string}}>; + route: {params: {policyID: string}}; /** The report currently being looked at */ policy: OnyxTypes.Policy; From 3b4edc4fe0fa1f81e21433d4cdf6298eb956350e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Tue, 13 Feb 2024 11:37:34 +0100 Subject: [PATCH 11/32] feat: handle go back to workspace if policy not found --- .../categories/withPolicyAccessOrNotFound.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/pages/workspace/categories/withPolicyAccessOrNotFound.tsx b/src/pages/workspace/categories/withPolicyAccessOrNotFound.tsx index 12ffdce22937..149ed5a3764e 100644 --- a/src/pages/workspace/categories/withPolicyAccessOrNotFound.tsx +++ b/src/pages/workspace/categories/withPolicyAccessOrNotFound.tsx @@ -5,10 +5,12 @@ import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import getComponentDisplayName from '@libs/getComponentDisplayName'; +import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; import * as Policy from '@userActions/Policy'; import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; @@ -17,7 +19,7 @@ type WithWorkspaceAccessOnyxProps = { policy: OnyxEntry; /** Indicated whether the report data is loading */ - isLoadingWorkspaceData: OnyxEntry; + isLoadingReportData: OnyxEntry; }; type WithWorkspaceAccessProps = WithWorkspaceAccessOnyxProps & { @@ -45,7 +47,7 @@ export default function (): ( // eslint-disable-next-line react-hooks/exhaustive-deps }, [isPolicyIDInRoute, props.route.params.policyID]); - const shouldShowFullScreenLoadingIndicator = props.isLoadingWorkspaceData !== false && (!Object.entries(props.policy ?? {}).length || !props.policy?.id); + const shouldShowFullScreenLoadingIndicator = props.isLoadingReportData !== false && (!Object.entries(props.policy ?? {}).length || !props.policy?.id); const shouldShowNotFoundPage = isEmptyObject(props.policy) || !props.policy?.id || !PolicyUtils.isPolicyAdmin(props.policy); @@ -54,7 +56,7 @@ export default function (): ( } if (shouldShowNotFoundPage) { - return ; + return Navigation.goBack(ROUTES.SETTINGS_WORKSPACES)} />; } return ( @@ -72,7 +74,7 @@ export default function (): ( policy: { key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY}${route.params.policyID ?? ''}`, }, - isLoadingWorkspaceData: { + isLoadingReportData: { key: ONYXKEYS.IS_LOADING_REPORT_DATA, }, })(React.forwardRef(WithWorkspaceAccess)); From 88671737cdc2c6bd7d7f2f4654c766615ebfd5ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Wed, 14 Feb 2024 09:37:39 +0100 Subject: [PATCH 12/32] refactor: cr --- .../workspace/categories/WorkspaceCategoriesPage.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx index 832c07c90431..fab3323fc44a 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx @@ -43,7 +43,7 @@ function WorkspaceCategoriesPage({policyCategories}: WorkspaceCategoriesPageProp const {translate} = useLocalize(); const [selectedCategories, setSelectedCategories] = useState([]); - const data = useMemo( + const categoryList = useMemo( () => Object.values(policyCategories ?? {}).map((value) => ({ value: value.name, @@ -75,11 +75,11 @@ function WorkspaceCategoriesPage({policyCategories}: WorkspaceCategoriesPageProp }; const toggleAllCategories = () => { - const isAllSelected = data.every((category) => category.isSelected); + const isAllSelected = categoryList.every((category) => category.isSelected); if (isAllSelected) { setSelectedCategories([]); } else { - setSelectedCategories(data.map((item) => item.value)); + setSelectedCategories(categoryList.map((item) => item.value)); } }; @@ -97,7 +97,7 @@ function WorkspaceCategoriesPage({policyCategories}: WorkspaceCategoriesPageProp /> Date: Wed, 14 Feb 2024 10:14:17 +0100 Subject: [PATCH 13/32] feat: add loading when categories empty --- src/pages/workspace/categories/WorkspaceCategoriesPage.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx index fab3323fc44a..d3d0c8e061ab 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx @@ -3,6 +3,7 @@ import React, {useMemo, useState} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; +import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; @@ -83,6 +84,10 @@ function WorkspaceCategoriesPage({policyCategories}: WorkspaceCategoriesPageProp } }; + if (Object.keys(policyCategories ?? {}).length === 0) { + return ; + } + return ( Date: Thu, 15 Feb 2024 16:02:55 +0100 Subject: [PATCH 14/32] refactor: refactor policyAccess to paidPolicyAccess HOC --- src/pages/workspace/WorkspaceInitialPage.tsx | 15 +++++++++------ .../categories/WorkspaceCategoriesPage.tsx | 15 +++++---------- ....tsx => withPaidPolicyAccessOrNotFound.tsx} | 18 +++++++++--------- 3 files changed, 23 insertions(+), 25 deletions(-) rename src/pages/workspace/categories/{withPolicyAccessOrNotFound.tsx => withPaidPolicyAccessOrNotFound.tsx} (82%) diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index 075aaa5046cc..70d0a1d9f899 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -137,12 +137,6 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, policyMembers, r brickRoadIndicator: hasMembersError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, routeName: SCREENS.WORKSPACE.MEMBERS, }, - { - translationKey: 'workspace.common.categories', - icon: Expensicons.Folder, - action: singleExecution(waitForNavigate(() => Navigation.navigate(ROUTES.WORKSPACE_CATEGORIES.getRoute(policyID)))), - routeName: SCREENS.WORKSPACE.CATEGORIES, - }, { translationKey: 'workspace.common.bankAccount', icon: Expensicons.Bank, @@ -154,6 +148,15 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, policyMembers, r }, ]; + if (policy?.isPolicyExpenseChatEnabled) { + protectedMenuItems.push({ + translationKey: 'workspace.common.categories', + icon: Expensicons.Folder, + action: singleExecution(waitForNavigate(() => Navigation.navigate(ROUTES.WORKSPACE_CATEGORIES.getRoute(policyID)))), + routeName: SCREENS.WORKSPACE.CATEGORIES, + }); + } + const menuItems: WorkspaceMenuItem[] = [ { translationKey: 'workspace.common.profile', diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx index d3d0c8e061ab..7d6275ce9a58 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx @@ -3,7 +3,6 @@ import React, {useMemo, useState} from 'react'; import {View} from 'react-native'; import {withOnyx} from 'react-native-onyx'; import type {OnyxEntry} from 'react-native-onyx'; -import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import Icon from '@components/Icon'; import * as Expensicons from '@components/Icon/Expensicons'; @@ -19,8 +18,8 @@ import type {CentralPaneNavigatorParamList} from '@navigation/types'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; -import withPolicyAccessOrNotFound from './withPolicyAccessOrNotFound'; -import type {WithWorkspaceAccessProps} from './withPolicyAccessOrNotFound'; +import withPaidPolicyAccessOrNotFound from './withPaidPolicyAccessOrNotFound'; +import type {WithPaidPolicyAccessProps} from './withPaidPolicyAccessOrNotFound'; type PolicyForList = { value: string; @@ -35,9 +34,9 @@ type WorkspaceCategoriesOnyxProps = { policyCategories: OnyxEntry; }; -type WorkspaceCategoriesPageProps = WorkspaceCategoriesOnyxProps & WithWorkspaceAccessProps & StackScreenProps; +type WorkspaceCategoriesPageProps = WorkspaceCategoriesOnyxProps & WithPaidPolicyAccessProps & StackScreenProps; -function WorkspaceCategoriesPage({policyCategories}: WorkspaceCategoriesPageProps) { +function WorkspaceCategoriesPage({policyCategories, policy}: WorkspaceCategoriesPageProps) { const {isSmallScreenWidth} = useWindowDimensions(); const styles = useThemeStyles(); const theme = useTheme(); @@ -84,10 +83,6 @@ function WorkspaceCategoriesPage({policyCategories}: WorkspaceCategoriesPageProp } }; - if (Object.keys(policyCategories ?? {}).length === 0) { - return ; - } - return ( ({ policyCategories: { key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${route.params.policyID}`, diff --git a/src/pages/workspace/categories/withPolicyAccessOrNotFound.tsx b/src/pages/workspace/categories/withPaidPolicyAccessOrNotFound.tsx similarity index 82% rename from src/pages/workspace/categories/withPolicyAccessOrNotFound.tsx rename to src/pages/workspace/categories/withPaidPolicyAccessOrNotFound.tsx index 149ed5a3764e..0ff27e8ecf2e 100644 --- a/src/pages/workspace/categories/withPolicyAccessOrNotFound.tsx +++ b/src/pages/workspace/categories/withPaidPolicyAccessOrNotFound.tsx @@ -14,7 +14,7 @@ import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -type WithWorkspaceAccessOnyxProps = { +type WithPaidPolicyAccessOnyxProps = { /** The report currently being looked at */ policy: OnyxEntry; @@ -22,7 +22,7 @@ type WithWorkspaceAccessOnyxProps = { isLoadingReportData: OnyxEntry; }; -type WithWorkspaceAccessProps = WithWorkspaceAccessOnyxProps & { +type WithPaidPolicyAccessProps = WithPaidPolicyAccessOnyxProps & { /** The report currently being looked at */ route: {params: {policyID: string}}; @@ -30,10 +30,10 @@ type WithWorkspaceAccessProps = WithWorkspaceAccessOnyxProps & { policy: OnyxTypes.Policy; }; -export default function (): ( +export default function (): ( WrappedComponent: React.ComponentType>, -) => React.ComponentType, keyof WithWorkspaceAccessOnyxProps>> { - return function (WrappedComponent: React.ComponentType>) { +) => React.ComponentType, keyof WithPaidPolicyAccessOnyxProps>> { + return function (WrappedComponent: React.ComponentType>) { function WithWorkspaceAccess(props: TProps, ref: ForwardedRef) { const isPolicyIDInRoute = !!props.route.params.policyID?.length; @@ -49,7 +49,7 @@ export default function (): ( const shouldShowFullScreenLoadingIndicator = props.isLoadingReportData !== false && (!Object.entries(props.policy ?? {}).length || !props.policy?.id); - const shouldShowNotFoundPage = isEmptyObject(props.policy) || !props.policy?.id || !PolicyUtils.isPolicyAdmin(props.policy); + const shouldShowNotFoundPage = isEmptyObject(props.policy) || !props.policy?.id || !PolicyUtils.isPolicyAdmin(props.policy) || !props.policy.isPolicyExpenseChatEnabled; if (shouldShowFullScreenLoadingIndicator) { return ; @@ -68,9 +68,9 @@ export default function (): ( ); } - WithWorkspaceAccess.displayName = `withWorkspaceAccess(${getComponentDisplayName(WrappedComponent)})`; + WithWorkspaceAccess.displayName = `withPaidPolicyAccess(${getComponentDisplayName(WrappedComponent)})`; - return withOnyx, WithWorkspaceAccessOnyxProps>({ + return withOnyx, WithPaidPolicyAccessOnyxProps>({ policy: { key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY}${route.params.policyID ?? ''}`, }, @@ -81,4 +81,4 @@ export default function (): ( }; } -export type {WithWorkspaceAccessProps}; +export type {WithPaidPolicyAccessProps}; From 1f5ea898a99b2686e539816092e5cc10424f0dc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Thu, 15 Feb 2024 16:19:20 +0100 Subject: [PATCH 15/32] fix: chevron padding --- src/pages/workspace/categories/WorkspaceCategoriesPage.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx index 7d6275ce9a58..60a69d813e0e 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx @@ -36,7 +36,7 @@ type WorkspaceCategoriesOnyxProps = { type WorkspaceCategoriesPageProps = WorkspaceCategoriesOnyxProps & WithPaidPolicyAccessProps & StackScreenProps; -function WorkspaceCategoriesPage({policyCategories, policy}: WorkspaceCategoriesPageProps) { +function WorkspaceCategoriesPage({policyCategories}: WorkspaceCategoriesPageProps) { const {isSmallScreenWidth} = useWindowDimensions(); const styles = useThemeStyles(); const theme = useTheme(); @@ -53,7 +53,7 @@ function WorkspaceCategoriesPage({policyCategories, policy}: WorkspaceCategories rightElement: ( {value.enabled ? translate('workspace.common.enabled') : translate('workspace.common.disabled')} - + ), })), - [policyCategories, selectedCategories, styles.alignSelfCenter, styles.disabledText, styles.flexRow, styles.p1, theme.icon, translate], + [policyCategories, selectedCategories, styles.alignSelfCenter, styles.disabledText, styles.flexRow, styles.p1, styles.pl2, theme.icon, translate], ); const toggleCategory = (category: PolicyForList) => { From c52ca2bfc3b69c228d5ade7b4e2e3e659552845f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Thu, 15 Feb 2024 16:36:41 +0100 Subject: [PATCH 16/32] fix: paid policy condition --- .../workspace/categories/withPaidPolicyAccessOrNotFound.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/categories/withPaidPolicyAccessOrNotFound.tsx b/src/pages/workspace/categories/withPaidPolicyAccessOrNotFound.tsx index 0ff27e8ecf2e..c641044f075a 100644 --- a/src/pages/workspace/categories/withPaidPolicyAccessOrNotFound.tsx +++ b/src/pages/workspace/categories/withPaidPolicyAccessOrNotFound.tsx @@ -49,7 +49,7 @@ export default function (): ( const shouldShowFullScreenLoadingIndicator = props.isLoadingReportData !== false && (!Object.entries(props.policy ?? {}).length || !props.policy?.id); - const shouldShowNotFoundPage = isEmptyObject(props.policy) || !props.policy?.id || !PolicyUtils.isPolicyAdmin(props.policy) || !props.policy.isPolicyExpenseChatEnabled; + const shouldShowNotFoundPage = isEmptyObject(props.policy) || !props.policy?.id || (!PolicyUtils.isPolicyAdmin(props.policy) && !props.policy.isPolicyExpenseChatEnabled); if (shouldShowFullScreenLoadingIndicator) { return ; From bd5f8e971f9e56ad3b759b021ea3ab153df5e1f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Thu, 15 Feb 2024 16:42:14 +0100 Subject: [PATCH 17/32] refactor: rename paid policy hoc name & props, move to workspace dir --- .../categories/WorkspaceCategoriesPage.tsx | 6 +++--- .../withPaidPolicyAccessOrNotFound.tsx | 20 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) rename src/pages/workspace/{categories => }/withPaidPolicyAccessOrNotFound.tsx (80%) diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx index 60a69d813e0e..b6c207b5bc26 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx @@ -15,11 +15,11 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import type {CentralPaneNavigatorParamList} from '@navigation/types'; +import withPaidPolicyAccessOrNotFound from '@pages/workspace/withPaidPolicyAccessOrNotFound'; +import type {WithPaidPolicyAccessOrNotFoundProps} from '@pages/workspace/withPaidPolicyAccessOrNotFound'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; -import withPaidPolicyAccessOrNotFound from './withPaidPolicyAccessOrNotFound'; -import type {WithPaidPolicyAccessProps} from './withPaidPolicyAccessOrNotFound'; type PolicyForList = { value: string; @@ -34,7 +34,7 @@ type WorkspaceCategoriesOnyxProps = { policyCategories: OnyxEntry; }; -type WorkspaceCategoriesPageProps = WorkspaceCategoriesOnyxProps & WithPaidPolicyAccessProps & StackScreenProps; +type WorkspaceCategoriesPageProps = WorkspaceCategoriesOnyxProps & WithPaidPolicyAccessOrNotFoundProps & StackScreenProps; function WorkspaceCategoriesPage({policyCategories}: WorkspaceCategoriesPageProps) { const {isSmallScreenWidth} = useWindowDimensions(); diff --git a/src/pages/workspace/categories/withPaidPolicyAccessOrNotFound.tsx b/src/pages/workspace/withPaidPolicyAccessOrNotFound.tsx similarity index 80% rename from src/pages/workspace/categories/withPaidPolicyAccessOrNotFound.tsx rename to src/pages/workspace/withPaidPolicyAccessOrNotFound.tsx index c641044f075a..fd6199f6bf7b 100644 --- a/src/pages/workspace/categories/withPaidPolicyAccessOrNotFound.tsx +++ b/src/pages/workspace/withPaidPolicyAccessOrNotFound.tsx @@ -14,7 +14,7 @@ import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -type WithPaidPolicyAccessOnyxProps = { +type WithPaidPolicyAccessOrNotFoundOnyxProps = { /** The report currently being looked at */ policy: OnyxEntry; @@ -22,7 +22,7 @@ type WithPaidPolicyAccessOnyxProps = { isLoadingReportData: OnyxEntry; }; -type WithPaidPolicyAccessProps = WithPaidPolicyAccessOnyxProps & { +type WithPaidPolicyAccessOrNotFoundProps = WithPaidPolicyAccessOrNotFoundOnyxProps & { /** The report currently being looked at */ route: {params: {policyID: string}}; @@ -30,11 +30,11 @@ type WithPaidPolicyAccessProps = WithPaidPolicyAccessOnyxProps & { policy: OnyxTypes.Policy; }; -export default function (): ( +export default function (): ( WrappedComponent: React.ComponentType>, -) => React.ComponentType, keyof WithPaidPolicyAccessOnyxProps>> { - return function (WrappedComponent: React.ComponentType>) { - function WithWorkspaceAccess(props: TProps, ref: ForwardedRef) { +) => React.ComponentType, keyof WithPaidPolicyAccessOrNotFoundOnyxProps>> { + return function (WrappedComponent: React.ComponentType>) { + function WithPaidPolicyAccessOrNotFound(props: TProps, ref: ForwardedRef) { const isPolicyIDInRoute = !!props.route.params.policyID?.length; useEffect(() => { @@ -68,17 +68,17 @@ export default function (): ( ); } - WithWorkspaceAccess.displayName = `withPaidPolicyAccess(${getComponentDisplayName(WrappedComponent)})`; + WithPaidPolicyAccessOrNotFound.displayName = `withPaidPolicyAccessOrNotFound(${getComponentDisplayName(WrappedComponent)})`; - return withOnyx, WithPaidPolicyAccessOnyxProps>({ + return withOnyx, WithPaidPolicyAccessOrNotFoundOnyxProps>({ policy: { key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY}${route.params.policyID ?? ''}`, }, isLoadingReportData: { key: ONYXKEYS.IS_LOADING_REPORT_DATA, }, - })(React.forwardRef(WithWorkspaceAccess)); + })(React.forwardRef(WithPaidPolicyAccessOrNotFound)); }; } -export type {WithPaidPolicyAccessProps}; +export type {WithPaidPolicyAccessOrNotFoundProps}; From 0114c1d917eea1e9e6def503a32d97d8048a0f69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Thu, 15 Feb 2024 17:02:13 +0100 Subject: [PATCH 18/32] fix: paid policy condition & add team policy hoc --- src/pages/workspace/WorkspaceInitialPage.tsx | 2 +- .../categories/WorkspaceCategoriesPage.tsx | 15 ++-- .../withPaidPolicyAccessOrNotFound.tsx | 2 +- .../withTeamPolicyAccessOrNotFound.tsx | 85 +++++++++++++++++++ 4 files changed, 96 insertions(+), 8 deletions(-) create mode 100644 src/pages/workspace/withTeamPolicyAccessOrNotFound.tsx diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index 70d0a1d9f899..8bc74aba4426 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -148,7 +148,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, policyMembers, r }, ]; - if (policy?.isPolicyExpenseChatEnabled) { + if (policy?.isPolicyExpenseChatEnabled && policy.type === CONST.POLICY.TYPE.TEAM) { protectedMenuItems.push({ translationKey: 'workspace.common.categories', icon: Expensicons.Folder, diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx index b6c207b5bc26..0788748f8f60 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx @@ -17,6 +17,7 @@ import useWindowDimensions from '@hooks/useWindowDimensions'; import type {CentralPaneNavigatorParamList} from '@navigation/types'; import withPaidPolicyAccessOrNotFound from '@pages/workspace/withPaidPolicyAccessOrNotFound'; import type {WithPaidPolicyAccessOrNotFoundProps} from '@pages/workspace/withPaidPolicyAccessOrNotFound'; +import withTeamPolicyAccessOrNotFound from '@pages/workspace/withTeamPolicyAccessOrNotFound'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; @@ -108,10 +109,12 @@ function WorkspaceCategoriesPage({policyCategories}: WorkspaceCategoriesPageProp WorkspaceCategoriesPage.displayName = 'WorkspaceCategoriesPage'; -export default withPaidPolicyAccessOrNotFound()( - withOnyx({ - policyCategories: { - key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${route.params.policyID}`, - }, - })(WorkspaceCategoriesPage), +export default withTeamPolicyAccessOrNotFound()( + withPaidPolicyAccessOrNotFound()( + withOnyx({ + policyCategories: { + key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${route.params.policyID}`, + }, + })(WorkspaceCategoriesPage), + ), ); diff --git a/src/pages/workspace/withPaidPolicyAccessOrNotFound.tsx b/src/pages/workspace/withPaidPolicyAccessOrNotFound.tsx index fd6199f6bf7b..0de973994976 100644 --- a/src/pages/workspace/withPaidPolicyAccessOrNotFound.tsx +++ b/src/pages/workspace/withPaidPolicyAccessOrNotFound.tsx @@ -49,7 +49,7 @@ export default function (): ; diff --git a/src/pages/workspace/withTeamPolicyAccessOrNotFound.tsx b/src/pages/workspace/withTeamPolicyAccessOrNotFound.tsx new file mode 100644 index 000000000000..63e6ad658b68 --- /dev/null +++ b/src/pages/workspace/withTeamPolicyAccessOrNotFound.tsx @@ -0,0 +1,85 @@ +/* eslint-disable rulesdir/no-negated-variables */ +import React, {useEffect} from 'react'; +import type {ForwardedRef, RefAttributes} from 'react'; +import type {OnyxEntry} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; +import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; +import getComponentDisplayName from '@libs/getComponentDisplayName'; +import Navigation from '@libs/Navigation/Navigation'; +import * as PolicyUtils from '@libs/PolicyUtils'; +import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; +import * as Policy from '@userActions/Policy'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import type * as OnyxTypes from '@src/types/onyx'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; + +type WithTeamPolicyAccessOrNotFoundOnyxProps = { + /** The report currently being looked at */ + policy: OnyxEntry; + + /** Indicated whether the report data is loading */ + isLoadingReportData: OnyxEntry; +}; + +type WithTeamPolicyAccessOrNotFoundProps = WithTeamPolicyAccessOrNotFoundOnyxProps & { + /** The report currently being looked at */ + route: {params: {policyID: string}}; + + /** The report currently being looked at */ + policy: OnyxTypes.Policy; +}; + +export default function (): ( + WrappedComponent: React.ComponentType>, +) => React.ComponentType, keyof WithTeamPolicyAccessOrNotFoundOnyxProps>> { + return function (WrappedComponent: React.ComponentType>) { + function WithTeamPolicyAccessOrNotFound(props: TProps, ref: ForwardedRef) { + const isPolicyIDInRoute = !!props.route.params.policyID?.length; + + useEffect(() => { + if (!isPolicyIDInRoute || !isEmptyObject(props.policy)) { + // If the workspace is not required or is already loaded, we don't need to call the API + return; + } + + Policy.openWorkspace(props.route.params.policyID, []); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isPolicyIDInRoute, props.route.params.policyID]); + + const shouldShowFullScreenLoadingIndicator = props.isLoadingReportData !== false && (!Object.entries(props.policy ?? {}).length || !props.policy?.id); + + const shouldShowNotFoundPage = isEmptyObject(props.policy) || !props.policy?.id || props.policy.type !== CONST.POLICY.TYPE.TEAM || !PolicyUtils.isPolicyAdmin(props.policy); + + if (shouldShowFullScreenLoadingIndicator) { + return ; + } + + if (shouldShowNotFoundPage) { + return Navigation.goBack(ROUTES.SETTINGS_WORKSPACES)} />; + } + + return ( + + ); + } + + WithTeamPolicyAccessOrNotFound.displayName = `withTeamPolicyAccessOrNotFound(${getComponentDisplayName(WrappedComponent)})`; + + return withOnyx, WithTeamPolicyAccessOrNotFoundOnyxProps>({ + policy: { + key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY}${route.params.policyID ?? ''}`, + }, + isLoadingReportData: { + key: ONYXKEYS.IS_LOADING_REPORT_DATA, + }, + })(React.forwardRef(WithTeamPolicyAccessOrNotFound)); + }; +} + +export type {WithTeamPolicyAccessOrNotFoundProps}; From 7dba039d3fc23f6667ac78a55e80ba24909cf44a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Thu, 15 Feb 2024 17:09:37 +0100 Subject: [PATCH 19/32] refactor: admin policy access hoc name wip --- .../categories/WorkspaceCategoriesPage.tsx | 8 ++++--- ...sx => withAdminPolicyAccessOrNotFound.tsx} | 22 +++++++++---------- 2 files changed, 16 insertions(+), 14 deletions(-) rename src/pages/workspace/{withPaidPolicyAccessOrNotFound.tsx => withAdminPolicyAccessOrNotFound.tsx} (76%) diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx index 0788748f8f60..60997bbc9d2a 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx @@ -15,8 +15,8 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import type {CentralPaneNavigatorParamList} from '@navigation/types'; -import withPaidPolicyAccessOrNotFound from '@pages/workspace/withPaidPolicyAccessOrNotFound'; -import type {WithPaidPolicyAccessOrNotFoundProps} from '@pages/workspace/withPaidPolicyAccessOrNotFound'; +import withPaidPolicyAccessOrNotFound from '@pages/workspace/withAdminPolicyAccessOrNotFound'; +import type {WithAdminPolicyAccessOrNotFoundProps} from '@pages/workspace/withAdminPolicyAccessOrNotFound'; import withTeamPolicyAccessOrNotFound from '@pages/workspace/withTeamPolicyAccessOrNotFound'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; @@ -35,7 +35,9 @@ type WorkspaceCategoriesOnyxProps = { policyCategories: OnyxEntry; }; -type WorkspaceCategoriesPageProps = WorkspaceCategoriesOnyxProps & WithPaidPolicyAccessOrNotFoundProps & StackScreenProps; +type WorkspaceCategoriesPageProps = WorkspaceCategoriesOnyxProps & + WithAdminPolicyAccessOrNotFoundProps & + StackScreenProps; function WorkspaceCategoriesPage({policyCategories}: WorkspaceCategoriesPageProps) { const {isSmallScreenWidth} = useWindowDimensions(); diff --git a/src/pages/workspace/withPaidPolicyAccessOrNotFound.tsx b/src/pages/workspace/withAdminPolicyAccessOrNotFound.tsx similarity index 76% rename from src/pages/workspace/withPaidPolicyAccessOrNotFound.tsx rename to src/pages/workspace/withAdminPolicyAccessOrNotFound.tsx index 0de973994976..0388aea098df 100644 --- a/src/pages/workspace/withPaidPolicyAccessOrNotFound.tsx +++ b/src/pages/workspace/withAdminPolicyAccessOrNotFound.tsx @@ -14,7 +14,7 @@ import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -type WithPaidPolicyAccessOrNotFoundOnyxProps = { +type WithAdminAccessOrNotFoundOnyxProps = { /** The report currently being looked at */ policy: OnyxEntry; @@ -22,7 +22,7 @@ type WithPaidPolicyAccessOrNotFoundOnyxProps = { isLoadingReportData: OnyxEntry; }; -type WithPaidPolicyAccessOrNotFoundProps = WithPaidPolicyAccessOrNotFoundOnyxProps & { +type WithAdminPolicyAccessOrNotFoundProps = WithAdminAccessOrNotFoundOnyxProps & { /** The report currently being looked at */ route: {params: {policyID: string}}; @@ -30,11 +30,11 @@ type WithPaidPolicyAccessOrNotFoundProps = WithPaidPolicyAccessOrNotFoundOnyxPro policy: OnyxTypes.Policy; }; -export default function (): ( +export default function (): ( WrappedComponent: React.ComponentType>, -) => React.ComponentType, keyof WithPaidPolicyAccessOrNotFoundOnyxProps>> { - return function (WrappedComponent: React.ComponentType>) { - function WithPaidPolicyAccessOrNotFound(props: TProps, ref: ForwardedRef) { +) => React.ComponentType, keyof WithAdminAccessOrNotFoundOnyxProps>> { + return function (WrappedComponent: React.ComponentType>) { + function WithAdminPolicyAccessOrNotFound(props: TProps, ref: ForwardedRef) { const isPolicyIDInRoute = !!props.route.params.policyID?.length; useEffect(() => { @@ -49,7 +49,7 @@ export default function (): ; @@ -68,17 +68,17 @@ export default function (): , WithPaidPolicyAccessOrNotFoundOnyxProps>({ + return withOnyx, WithAdminAccessOrNotFoundOnyxProps>({ policy: { key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY}${route.params.policyID ?? ''}`, }, isLoadingReportData: { key: ONYXKEYS.IS_LOADING_REPORT_DATA, }, - })(React.forwardRef(WithPaidPolicyAccessOrNotFound)); + })(React.forwardRef(WithAdminPolicyAccessOrNotFound)); }; } -export type {WithPaidPolicyAccessOrNotFoundProps}; +export type {WithAdminPolicyAccessOrNotFoundProps}; From c96ea1d565833630a17d8d4a0f8b65e4391d3445 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Thu, 15 Feb 2024 17:39:57 +0100 Subject: [PATCH 20/32] fix: change team to paid hoc policy --- .../categories/WorkspaceCategoriesPage.tsx | 2 +- ...tsx => withPaidPolicyAccessOrNotFound.tsx} | 23 +++++++++---------- 2 files changed, 12 insertions(+), 13 deletions(-) rename src/pages/workspace/{withTeamPolicyAccessOrNotFound.tsx => withPaidPolicyAccessOrNotFound.tsx} (80%) diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx index 60997bbc9d2a..896385688f7c 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx @@ -17,7 +17,7 @@ import useWindowDimensions from '@hooks/useWindowDimensions'; import type {CentralPaneNavigatorParamList} from '@navigation/types'; import withPaidPolicyAccessOrNotFound from '@pages/workspace/withAdminPolicyAccessOrNotFound'; import type {WithAdminPolicyAccessOrNotFoundProps} from '@pages/workspace/withAdminPolicyAccessOrNotFound'; -import withTeamPolicyAccessOrNotFound from '@pages/workspace/withTeamPolicyAccessOrNotFound'; +import withTeamPolicyAccessOrNotFound from '@pages/workspace/withPaidPolicyAccessOrNotFound'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; diff --git a/src/pages/workspace/withTeamPolicyAccessOrNotFound.tsx b/src/pages/workspace/withPaidPolicyAccessOrNotFound.tsx similarity index 80% rename from src/pages/workspace/withTeamPolicyAccessOrNotFound.tsx rename to src/pages/workspace/withPaidPolicyAccessOrNotFound.tsx index 63e6ad658b68..f65e422ecbdf 100644 --- a/src/pages/workspace/withTeamPolicyAccessOrNotFound.tsx +++ b/src/pages/workspace/withPaidPolicyAccessOrNotFound.tsx @@ -9,13 +9,12 @@ import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; import * as Policy from '@userActions/Policy'; -import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type * as OnyxTypes from '@src/types/onyx'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; -type WithTeamPolicyAccessOrNotFoundOnyxProps = { +type WithPaidPolicyAccessOrNotFoundOnyxProps = { /** The report currently being looked at */ policy: OnyxEntry; @@ -23,7 +22,7 @@ type WithTeamPolicyAccessOrNotFoundOnyxProps = { isLoadingReportData: OnyxEntry; }; -type WithTeamPolicyAccessOrNotFoundProps = WithTeamPolicyAccessOrNotFoundOnyxProps & { +type WithPaidPolicyAccessOrNotFoundProps = WithPaidPolicyAccessOrNotFoundOnyxProps & { /** The report currently being looked at */ route: {params: {policyID: string}}; @@ -31,11 +30,11 @@ type WithTeamPolicyAccessOrNotFoundProps = WithTeamPolicyAccessOrNotFoundOnyxPro policy: OnyxTypes.Policy; }; -export default function (): ( +export default function (): ( WrappedComponent: React.ComponentType>, -) => React.ComponentType, keyof WithTeamPolicyAccessOrNotFoundOnyxProps>> { - return function (WrappedComponent: React.ComponentType>) { - function WithTeamPolicyAccessOrNotFound(props: TProps, ref: ForwardedRef) { +) => React.ComponentType, keyof WithPaidPolicyAccessOrNotFoundOnyxProps>> { + return function (WrappedComponent: React.ComponentType>) { + function WithPaidPolicyAccessOrNotFound(props: TProps, ref: ForwardedRef) { const isPolicyIDInRoute = !!props.route.params.policyID?.length; useEffect(() => { @@ -50,7 +49,7 @@ export default function (): ; @@ -69,17 +68,17 @@ export default function (): , WithTeamPolicyAccessOrNotFoundOnyxProps>({ + return withOnyx, WithPaidPolicyAccessOrNotFoundOnyxProps>({ policy: { key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY}${route.params.policyID ?? ''}`, }, isLoadingReportData: { key: ONYXKEYS.IS_LOADING_REPORT_DATA, }, - })(React.forwardRef(WithTeamPolicyAccessOrNotFound)); + })(React.forwardRef(WithPaidPolicyAccessOrNotFound)); }; } -export type {WithTeamPolicyAccessOrNotFoundProps}; +export type {WithPaidPolicyAccessOrNotFoundProps}; From 0c03ff2ae5fa7cef9e5dc399104b8e7141ea65d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Thu, 15 Feb 2024 17:42:29 +0100 Subject: [PATCH 21/32] fix: use isPaidGroupPolicy to conditionaly display categories menu item --- src/pages/workspace/WorkspaceInitialPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index 8bc74aba4426..bbd8b5668d0f 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -148,7 +148,7 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, policyMembers, r }, ]; - if (policy?.isPolicyExpenseChatEnabled && policy.type === CONST.POLICY.TYPE.TEAM) { + if (PolicyUtils.isPaidGroupPolicy(policy)) { protectedMenuItems.push({ translationKey: 'workspace.common.categories', icon: Expensicons.Folder, From b547fb80320bbefa75122f26f75471406c88559d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Fri, 16 Feb 2024 10:34:00 +0100 Subject: [PATCH 22/32] fix: typo --- .../workspace/categories/WorkspaceCategoriesPage.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx index 896385688f7c..c618dc03a453 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx @@ -15,9 +15,9 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import type {CentralPaneNavigatorParamList} from '@navigation/types'; -import withPaidPolicyAccessOrNotFound from '@pages/workspace/withAdminPolicyAccessOrNotFound'; +import withAdminPolicyAccessOrNotFound from '@pages/workspace/withAdminPolicyAccessOrNotFound'; import type {WithAdminPolicyAccessOrNotFoundProps} from '@pages/workspace/withAdminPolicyAccessOrNotFound'; -import withTeamPolicyAccessOrNotFound from '@pages/workspace/withPaidPolicyAccessOrNotFound'; +import withPaidPolicyAccessOrNotFound from '@pages/workspace/withPaidPolicyAccessOrNotFound'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; @@ -111,8 +111,8 @@ function WorkspaceCategoriesPage({policyCategories}: WorkspaceCategoriesPageProp WorkspaceCategoriesPage.displayName = 'WorkspaceCategoriesPage'; -export default withTeamPolicyAccessOrNotFound()( - withPaidPolicyAccessOrNotFound()( +export default withPaidPolicyAccessOrNotFound()( + withAdminPolicyAccessOrNotFound()( withOnyx({ policyCategories: { key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${route.params.policyID}`, From 4e32046bcf24b8f3bb3a84c4fb7c74fa44febd5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Tue, 20 Feb 2024 11:12:19 +0100 Subject: [PATCH 23/32] fix: old conflict --- src/SCREENS.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SCREENS.ts b/src/SCREENS.ts index ed7fb45a7e5f..9ead3b50b3ab 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -209,8 +209,8 @@ const SCREENS = { INVITE_MESSAGE: 'Workspace_Invite_Message', CATEGORIES: 'Workspace_Categories', CURRENCY: 'Workspace_Profile_Currency', + DESCRIPTION: 'Workspace_Profile_Description', NAME: 'Workspace_Profile_Name', - DESCRIPTION: 'Workspace_Description', }, EDIT_REQUEST: { From d564a41a582b67d41fca7058dba3c6491bf8a555 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Tue, 20 Feb 2024 11:36:42 +0100 Subject: [PATCH 24/32] refactor: use selected categories as an object instead of array --- .../categories/WorkspaceCategoriesPage.tsx | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx index c618dc03a453..a12ad0cd52d6 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx @@ -44,7 +44,7 @@ function WorkspaceCategoriesPage({policyCategories}: WorkspaceCategoriesPageProp const styles = useThemeStyles(); const theme = useTheme(); const {translate} = useLocalize(); - const [selectedCategories, setSelectedCategories] = useState([]); + const [selectedCategories, setSelectedCategories] = useState>({}); const categoryList = useMemo( () => @@ -52,7 +52,7 @@ function WorkspaceCategoriesPage({policyCategories}: WorkspaceCategoriesPageProp value: value.name, text: value.name, keyForList: value.name, - isSelected: selectedCategories.includes(value.name), + isSelected: !!selectedCategories[value.name], rightElement: ( {value.enabled ? translate('workspace.common.enabled') : translate('workspace.common.disabled')} @@ -69,21 +69,15 @@ function WorkspaceCategoriesPage({policyCategories}: WorkspaceCategoriesPageProp ); const toggleCategory = (category: PolicyForList) => { - setSelectedCategories((prev) => { - if (prev.includes(category.value)) { - return prev.filter((item) => item !== category.value); - } - return [...prev, category.value]; - }); + setSelectedCategories((prev) => ({ + ...prev, + [category.value]: !prev[category.value], + })); }; const toggleAllCategories = () => { - const isAllSelected = categoryList.every((category) => category.isSelected); - if (isAllSelected) { - setSelectedCategories([]); - } else { - setSelectedCategories(categoryList.map((item) => item.value)); - } + const isAllSelected = categoryList.every((category) => !!selectedCategories[category.value]); + setSelectedCategories(isAllSelected ? {} : Object.fromEntries(categoryList.map((item) => [item.value, true]))); }; return ( From 874f051ed27ca8d89051c2c46f158a29dee3fb8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Tue, 20 Feb 2024 12:57:31 +0100 Subject: [PATCH 25/32] feat: add workspace empty state card & header message --- .../emptystate__expenses.svg | 58 +++++++++++++++++++ src/components/Icon/Illustrations.ts | 2 + src/components/WorkspaceEmptyStateSection.tsx | 50 ++++++++++++++++ src/languages/en.ts | 7 +++ src/languages/es.ts | 7 +++ .../categories/WorkspaceCategoriesPage.tsx | 26 ++++++--- src/styles/index.ts | 13 +++++ src/styles/utils/spacing.ts | 4 ++ 8 files changed, 160 insertions(+), 7 deletions(-) create mode 100644 assets/images/product-illustrations/emptystate__expenses.svg create mode 100644 src/components/WorkspaceEmptyStateSection.tsx diff --git a/assets/images/product-illustrations/emptystate__expenses.svg b/assets/images/product-illustrations/emptystate__expenses.svg new file mode 100644 index 000000000000..c01a89109cbf --- /dev/null +++ b/assets/images/product-illustrations/emptystate__expenses.svg @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/Icon/Illustrations.ts b/src/components/Icon/Illustrations.ts index a82a6ca92cc4..299b694df3f2 100644 --- a/src/components/Icon/Illustrations.ts +++ b/src/components/Icon/Illustrations.ts @@ -5,6 +5,7 @@ import BankUserGreen from '@assets/images/product-illustrations/bank-user--green import ConciergeBlue from '@assets/images/product-illustrations/concierge--blue.svg'; import ConciergeExclamation from '@assets/images/product-illustrations/concierge--exclamation.svg'; import CreditCardsBlue from '@assets/images/product-illustrations/credit-cards--blue.svg'; +import EmptyStateExpenses from '@assets/images/product-illustrations/emptystate__expenses.svg'; import GpsTrackOrange from '@assets/images/product-illustrations/gps-track--orange.svg'; import Hands from '@assets/images/product-illustrations/home-illustration-hands.svg'; import InvoiceOrange from '@assets/images/product-illustrations/invoice--orange.svg'; @@ -77,6 +78,7 @@ export { ConciergeExclamation, CreditCardsBlue, EmailAddress, + EmptyStateExpenses, FolderOpen, HandCard, HotDogStand, diff --git a/src/components/WorkspaceEmptyStateSection.tsx b/src/components/WorkspaceEmptyStateSection.tsx new file mode 100644 index 000000000000..330f8e1ebbf5 --- /dev/null +++ b/src/components/WorkspaceEmptyStateSection.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import {View} from 'react-native'; +import useThemeStyles from '@hooks/useThemeStyles'; +import useWindowDimensions from '@hooks/useWindowDimensions'; +import type IconAsset from '@src/types/utils/IconAsset'; +import Icon from './Icon'; +import Text from './Text'; + +type WorkspaceEmptyStateSectionProps = { + /** The text to display in the title of the section */ + title: string; + + /** The text to display in the subtitle of the section */ + subtitle?: string; + + /** The icon to display along with the title */ + icon: IconAsset; +}; + +function WorkspaceEmptyStateSection({icon, subtitle, title}: WorkspaceEmptyStateSectionProps) { + const styles = useThemeStyles(); + const {isSmallScreenWidth} = useWindowDimensions(); + + return ( + <> + + + + + + {title} + + + {!!subtitle && ( + + {subtitle} + + )} + + + + ); +} +WorkspaceEmptyStateSection.displayName = 'WorkspaceEmptyStateSection'; + +export default WorkspaceEmptyStateSection; diff --git a/src/languages/en.ts b/src/languages/en.ts index 21cedb43a8e7..8a00f5bd87f1 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1712,6 +1712,13 @@ export default { control: 'Control', collect: 'Collect', }, + categories: { + subtitle: 'Get a better overview of where money is being spent. Use our default categories or add your own.', + emptyCategories: { + title: "You haven't created any categories", + subtitle: 'Add a category to organize your spend.', + }, + }, emptyWorkspace: { title: 'Create a workspace', subtitle: 'Workspaces are where you’ll chat with your team, reimburse expenses, issue cards, send invoices, pay bills, and more - all in one place.', diff --git a/src/languages/es.ts b/src/languages/es.ts index 4e1dfe6be737..1c35487138d2 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1736,6 +1736,13 @@ export default { control: 'Control', collect: 'Recolectar', }, + categories: { + subtitle: 'Obtenga una mejor visión general de dónde se gasta el dinero. Utilice nuestras categorías predeterminadas o agregue las suyas propias.', + emptyCategories: { + title: 'No has creado ninguna categoría', + subtitle: 'Agregue una categoría para organizar su gasto.', + }, + }, emptyWorkspace: { title: 'Crea un espacio de trabajo', subtitle: 'En los espacios de trabajo podrás chatear con tu equipo, reembolsar gastos, emitir tarjetas, enviar y pagar facturas, y mucho más - todo en un mismo lugar.', diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx index a12ad0cd52d6..220bb44d475a 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx @@ -10,6 +10,7 @@ import * as Illustrations from '@components/Icon/Illustrations'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import Text from '@components/Text'; +import WorkspaceEmptyStateSection from '@components/WorkspaceEmptyStateSection'; import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -92,13 +93,24 @@ function WorkspaceCategoriesPage({policyCategories}: WorkspaceCategoriesPageProp title={translate('workspace.common.categories')} shouldShowBackButton={isSmallScreenWidth} /> - + + {translate('workspace.categories.subtitle')} + + {categoryList.length ? ( + + ) : ( + + )} ); } diff --git a/src/styles/index.ts b/src/styles/index.ts index 856f8ece917c..9b244b3ef05f 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -3433,6 +3433,19 @@ const styles = (theme: ThemeColors) => alignItems: 'center', }, + emptyCardSectionTitle: { + fontSize: variables.fontSizeXLarge, + lineHeight: variables.lineHeightXXLarge, + textAlign: 'center', + }, + + emptyCardSectionSubtitle: { + fontSize: variables.fontSizeNormal, + lineHeight: variables.lineHeightXLarge, + color: theme.textSupporting, + textAlign: 'center', + }, + transferBalance: { width: 'auto', borderRadius: 0, diff --git a/src/styles/utils/spacing.ts b/src/styles/utils/spacing.ts index 354c3ae3cea5..47ccf6967b89 100644 --- a/src/styles/utils/spacing.ts +++ b/src/styles/utils/spacing.ts @@ -377,6 +377,10 @@ export default { paddingVertical: 40, }, + pv12: { + paddingVertical: 48, + }, + ph0: { paddingHorizontal: 0, }, From db79355df50dc6cee2ef51d42d61fccdad08056e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Wed, 21 Feb 2024 16:13:55 +0100 Subject: [PATCH 26/32] add categories menu item to protectedCollectPolicyMenuItems --- src/pages/workspace/WorkspaceInitialPage.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/pages/workspace/WorkspaceInitialPage.tsx b/src/pages/workspace/WorkspaceInitialPage.tsx index 70d871849ee6..3f0cf65187ee 100644 --- a/src/pages/workspace/WorkspaceInitialPage.tsx +++ b/src/pages/workspace/WorkspaceInitialPage.tsx @@ -158,6 +158,12 @@ function WorkspaceInitialPage({policyDraft, policy: policyProp, policyMembers, r brickRoadIndicator: hasMembersError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined, routeName: SCREENS.WORKSPACE.MEMBERS, }, + { + translationKey: 'workspace.common.categories', + icon: Expensicons.Folder, + action: singleExecution(waitForNavigate(() => Navigation.navigate(ROUTES.WORKSPACE_CATEGORIES.getRoute(policyID)))), + routeName: SCREENS.WORKSPACE.CATEGORIES, + }, ]; const menuItems: WorkspaceMenuItem[] = [ From 074a51b730cd6d4562129c775e2226c76d03555a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Wed, 21 Feb 2024 16:29:56 +0100 Subject: [PATCH 27/32] fix: ts error --- src/pages/workspace/categories/WorkspaceCategoriesPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx index 220bb44d475a..ffe28ebaf39e 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx @@ -124,5 +124,5 @@ export default withPaidPolicyAccessOrNotFound()( key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${route.params.policyID}`, }, })(WorkspaceCategoriesPage), - ), + ) as React.ComponentType>, ); From 75068f75f6db37bedb80e6a3ead56c0969b1ab79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Wed, 21 Feb 2024 17:35:29 +0100 Subject: [PATCH 28/32] refactor: HOC to wrappers --- .../AdminPolicyAccessOrNotFoundWrapper.tsx | 66 ++++++++++++++ .../PaidPolicyAccessOrNotFoundWrapper.tsx | 66 ++++++++++++++ .../categories/WorkspaceCategoriesPage.tsx | 89 +++++++++---------- .../withAdminPolicyAccessOrNotFound.tsx | 84 ----------------- .../withPaidPolicyAccessOrNotFound.tsx | 84 ----------------- 5 files changed, 175 insertions(+), 214 deletions(-) create mode 100644 src/pages/workspace/AdminPolicyAccessOrNotFoundWrapper.tsx create mode 100644 src/pages/workspace/PaidPolicyAccessOrNotFoundWrapper.tsx delete mode 100644 src/pages/workspace/withAdminPolicyAccessOrNotFound.tsx delete mode 100644 src/pages/workspace/withPaidPolicyAccessOrNotFound.tsx diff --git a/src/pages/workspace/AdminPolicyAccessOrNotFoundWrapper.tsx b/src/pages/workspace/AdminPolicyAccessOrNotFoundWrapper.tsx new file mode 100644 index 000000000000..fe5597827ae4 --- /dev/null +++ b/src/pages/workspace/AdminPolicyAccessOrNotFoundWrapper.tsx @@ -0,0 +1,66 @@ +/* eslint-disable rulesdir/no-negated-variables */ +import React, {useEffect} from 'react'; +import type {OnyxEntry} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; +import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; +import Navigation from '@libs/Navigation/Navigation'; +import * as PolicyUtils from '@libs/PolicyUtils'; +import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; +import * as Policy from '@userActions/Policy'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import type * as OnyxTypes from '@src/types/onyx'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; + +type AdminAccessOrNotFoundOnyxProps = { + /** The report currently being looked at */ + policy: OnyxEntry; + + /** Indicated whether the report data is loading */ + isLoadingReportData: OnyxEntry; +}; + +type AdminPolicyAccessOrNotFoundComponentProps = AdminAccessOrNotFoundOnyxProps & { + /** The children to render */ + children: ((props: AdminAccessOrNotFoundOnyxProps) => React.ReactNode) | React.ReactNode; + + /** The report currently being looked at */ + policyID: string; +}; + +function AdminPolicyAccessOrNotFoundComponent(props: AdminPolicyAccessOrNotFoundComponentProps) { + const isPolicyIDInRoute = !!props.policyID?.length; + + useEffect(() => { + if (!isPolicyIDInRoute || !isEmptyObject(props.policy)) { + // If the workspace is not required or is already loaded, we don't need to call the API + return; + } + + Policy.openWorkspace(props.policyID, []); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isPolicyIDInRoute, props.policyID]); + + const shouldShowFullScreenLoadingIndicator = props.isLoadingReportData !== false && (!Object.entries(props.policy ?? {}).length || !props.policy?.id); + + const shouldShowNotFoundPage = isEmptyObject(props.policy) || !props.policy?.id || !PolicyUtils.isPolicyAdmin(props.policy); + + if (shouldShowFullScreenLoadingIndicator) { + return ; + } + + if (shouldShowNotFoundPage) { + return Navigation.goBack(ROUTES.WORKSPACE_PROFILE.getRoute(props.policyID))} />; + } + + return <>{typeof props.children === 'function' ? props.children(props) : props.children}; +} + +export default withOnyx({ + policy: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID ?? ''}`, + }, + isLoadingReportData: { + key: ONYXKEYS.IS_LOADING_REPORT_DATA, + }, +})(AdminPolicyAccessOrNotFoundComponent); diff --git a/src/pages/workspace/PaidPolicyAccessOrNotFoundWrapper.tsx b/src/pages/workspace/PaidPolicyAccessOrNotFoundWrapper.tsx new file mode 100644 index 000000000000..eafd42c6e996 --- /dev/null +++ b/src/pages/workspace/PaidPolicyAccessOrNotFoundWrapper.tsx @@ -0,0 +1,66 @@ +/* eslint-disable rulesdir/no-negated-variables */ +import React, {useEffect} from 'react'; +import type {OnyxEntry} from 'react-native-onyx'; +import {withOnyx} from 'react-native-onyx'; +import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; +import Navigation from '@libs/Navigation/Navigation'; +import * as PolicyUtils from '@libs/PolicyUtils'; +import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; +import * as Policy from '@userActions/Policy'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import type * as OnyxTypes from '@src/types/onyx'; +import {isEmptyObject} from '@src/types/utils/EmptyObject'; + +type PaidPolicyAccessOrNotFoundOnyxProps = { + /** The report currently being looked at */ + policy: OnyxEntry; + + /** Indicated whether the report data is loading */ + isLoadingReportData: OnyxEntry; +}; + +type PaidPolicyAccessOrNotFoundComponentProps = PaidPolicyAccessOrNotFoundOnyxProps & { + /** The children to render */ + children: ((props: PaidPolicyAccessOrNotFoundOnyxProps) => React.ReactNode) | React.ReactNode; + + /** The report currently being looked at */ + policyID: string; +}; + +function PaidPolicyAccessOrNotFoundComponent(props: PaidPolicyAccessOrNotFoundComponentProps) { + const isPolicyIDInRoute = !!props.policyID?.length; + + useEffect(() => { + if (!isPolicyIDInRoute || !isEmptyObject(props.policy)) { + // If the workspace is not required or is already loaded, we don't need to call the API + return; + } + + Policy.openWorkspace(props.policyID, []); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isPolicyIDInRoute, props.policyID]); + + const shouldShowFullScreenLoadingIndicator = props.isLoadingReportData !== false && (!Object.entries(props.policy ?? {}).length || !props.policy?.id); + + const shouldShowNotFoundPage = isEmptyObject(props.policy) || !props.policy?.id || !PolicyUtils.isPaidGroupPolicy(props.policy) || !props.policy.isPolicyExpenseChatEnabled; + + if (shouldShowFullScreenLoadingIndicator) { + return ; + } + + if (shouldShowNotFoundPage) { + return Navigation.goBack(ROUTES.WORKSPACE_PROFILE.getRoute(props.policyID))} />; + } + + return <>{typeof props.children === 'function' ? props.children(props) : props.children}; +} + +export default withOnyx({ + policy: { + key: ({policyID}) => `${ONYXKEYS.COLLECTION.POLICY}${policyID ?? ''}`, + }, + isLoadingReportData: { + key: ONYXKEYS.IS_LOADING_REPORT_DATA, + }, +})(PaidPolicyAccessOrNotFoundComponent); diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx index ffe28ebaf39e..77263c90c970 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx @@ -16,9 +16,8 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; import type {CentralPaneNavigatorParamList} from '@navigation/types'; -import withAdminPolicyAccessOrNotFound from '@pages/workspace/withAdminPolicyAccessOrNotFound'; -import type {WithAdminPolicyAccessOrNotFoundProps} from '@pages/workspace/withAdminPolicyAccessOrNotFound'; -import withPaidPolicyAccessOrNotFound from '@pages/workspace/withPaidPolicyAccessOrNotFound'; +import AdminPolicyAccessOrNotFoundWrapper from '@pages/workspace/AdminPolicyAccessOrNotFoundWrapper'; +import PaidPolicyAccessOrNotFoundWrapper from '@pages/workspace/PaidPolicyAccessOrNotFoundWrapper'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; @@ -36,11 +35,9 @@ type WorkspaceCategoriesOnyxProps = { policyCategories: OnyxEntry; }; -type WorkspaceCategoriesPageProps = WorkspaceCategoriesOnyxProps & - WithAdminPolicyAccessOrNotFoundProps & - StackScreenProps; +type WorkspaceCategoriesPageProps = WorkspaceCategoriesOnyxProps & StackScreenProps; -function WorkspaceCategoriesPage({policyCategories}: WorkspaceCategoriesPageProps) { +function WorkspaceCategoriesPage({policyCategories, route}: WorkspaceCategoriesPageProps) { const {isSmallScreenWidth} = useWindowDimensions(); const styles = useThemeStyles(); const theme = useTheme(); @@ -82,47 +79,47 @@ function WorkspaceCategoriesPage({policyCategories}: WorkspaceCategoriesPageProp }; return ( - - - - {translate('workspace.categories.subtitle')} - - {categoryList.length ? ( - - ) : ( - - )} - + + + + + + {translate('workspace.categories.subtitle')} + + {categoryList.length ? ( + + ) : ( + + )} + + + ); } WorkspaceCategoriesPage.displayName = 'WorkspaceCategoriesPage'; -export default withPaidPolicyAccessOrNotFound()( - withAdminPolicyAccessOrNotFound()( - withOnyx({ - policyCategories: { - key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${route.params.policyID}`, - }, - })(WorkspaceCategoriesPage), - ) as React.ComponentType>, -); +export default withOnyx({ + policyCategories: { + key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${route.params.policyID}`, + }, +})(WorkspaceCategoriesPage); diff --git a/src/pages/workspace/withAdminPolicyAccessOrNotFound.tsx b/src/pages/workspace/withAdminPolicyAccessOrNotFound.tsx deleted file mode 100644 index 0388aea098df..000000000000 --- a/src/pages/workspace/withAdminPolicyAccessOrNotFound.tsx +++ /dev/null @@ -1,84 +0,0 @@ -/* eslint-disable rulesdir/no-negated-variables */ -import React, {useEffect} from 'react'; -import type {ForwardedRef, RefAttributes} from 'react'; -import type {OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; -import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; -import getComponentDisplayName from '@libs/getComponentDisplayName'; -import Navigation from '@libs/Navigation/Navigation'; -import * as PolicyUtils from '@libs/PolicyUtils'; -import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; -import * as Policy from '@userActions/Policy'; -import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; -import type * as OnyxTypes from '@src/types/onyx'; -import {isEmptyObject} from '@src/types/utils/EmptyObject'; - -type WithAdminAccessOrNotFoundOnyxProps = { - /** The report currently being looked at */ - policy: OnyxEntry; - - /** Indicated whether the report data is loading */ - isLoadingReportData: OnyxEntry; -}; - -type WithAdminPolicyAccessOrNotFoundProps = WithAdminAccessOrNotFoundOnyxProps & { - /** The report currently being looked at */ - route: {params: {policyID: string}}; - - /** The report currently being looked at */ - policy: OnyxTypes.Policy; -}; - -export default function (): ( - WrappedComponent: React.ComponentType>, -) => React.ComponentType, keyof WithAdminAccessOrNotFoundOnyxProps>> { - return function (WrappedComponent: React.ComponentType>) { - function WithAdminPolicyAccessOrNotFound(props: TProps, ref: ForwardedRef) { - const isPolicyIDInRoute = !!props.route.params.policyID?.length; - - useEffect(() => { - if (!isPolicyIDInRoute || !isEmptyObject(props.policy)) { - // If the workspace is not required or is already loaded, we don't need to call the API - return; - } - - Policy.openWorkspace(props.route.params.policyID, []); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isPolicyIDInRoute, props.route.params.policyID]); - - const shouldShowFullScreenLoadingIndicator = props.isLoadingReportData !== false && (!Object.entries(props.policy ?? {}).length || !props.policy?.id); - - const shouldShowNotFoundPage = isEmptyObject(props.policy) || !props.policy?.id || !PolicyUtils.isPolicyAdmin(props.policy); - - if (shouldShowFullScreenLoadingIndicator) { - return ; - } - - if (shouldShowNotFoundPage) { - return Navigation.goBack(ROUTES.SETTINGS_WORKSPACES)} />; - } - - return ( - - ); - } - - WithAdminPolicyAccessOrNotFound.displayName = `withAdminPolicyAccessOrNotFound(${getComponentDisplayName(WrappedComponent)})`; - - return withOnyx, WithAdminAccessOrNotFoundOnyxProps>({ - policy: { - key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY}${route.params.policyID ?? ''}`, - }, - isLoadingReportData: { - key: ONYXKEYS.IS_LOADING_REPORT_DATA, - }, - })(React.forwardRef(WithAdminPolicyAccessOrNotFound)); - }; -} - -export type {WithAdminPolicyAccessOrNotFoundProps}; diff --git a/src/pages/workspace/withPaidPolicyAccessOrNotFound.tsx b/src/pages/workspace/withPaidPolicyAccessOrNotFound.tsx deleted file mode 100644 index f65e422ecbdf..000000000000 --- a/src/pages/workspace/withPaidPolicyAccessOrNotFound.tsx +++ /dev/null @@ -1,84 +0,0 @@ -/* eslint-disable rulesdir/no-negated-variables */ -import React, {useEffect} from 'react'; -import type {ForwardedRef, RefAttributes} from 'react'; -import type {OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; -import FullscreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; -import getComponentDisplayName from '@libs/getComponentDisplayName'; -import Navigation from '@libs/Navigation/Navigation'; -import * as PolicyUtils from '@libs/PolicyUtils'; -import NotFoundPage from '@pages/ErrorPage/NotFoundPage'; -import * as Policy from '@userActions/Policy'; -import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; -import type * as OnyxTypes from '@src/types/onyx'; -import {isEmptyObject} from '@src/types/utils/EmptyObject'; - -type WithPaidPolicyAccessOrNotFoundOnyxProps = { - /** The report currently being looked at */ - policy: OnyxEntry; - - /** Indicated whether the report data is loading */ - isLoadingReportData: OnyxEntry; -}; - -type WithPaidPolicyAccessOrNotFoundProps = WithPaidPolicyAccessOrNotFoundOnyxProps & { - /** The report currently being looked at */ - route: {params: {policyID: string}}; - - /** The report currently being looked at */ - policy: OnyxTypes.Policy; -}; - -export default function (): ( - WrappedComponent: React.ComponentType>, -) => React.ComponentType, keyof WithPaidPolicyAccessOrNotFoundOnyxProps>> { - return function (WrappedComponent: React.ComponentType>) { - function WithPaidPolicyAccessOrNotFound(props: TProps, ref: ForwardedRef) { - const isPolicyIDInRoute = !!props.route.params.policyID?.length; - - useEffect(() => { - if (!isPolicyIDInRoute || !isEmptyObject(props.policy)) { - // If the workspace is not required or is already loaded, we don't need to call the API - return; - } - - Policy.openWorkspace(props.route.params.policyID, []); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isPolicyIDInRoute, props.route.params.policyID]); - - const shouldShowFullScreenLoadingIndicator = props.isLoadingReportData !== false && (!Object.entries(props.policy ?? {}).length || !props.policy?.id); - - const shouldShowNotFoundPage = isEmptyObject(props.policy) || !props.policy?.id || !PolicyUtils.isPaidGroupPolicy(props.policy) || !props.policy.isPolicyExpenseChatEnabled; - - if (shouldShowFullScreenLoadingIndicator) { - return ; - } - - if (shouldShowNotFoundPage) { - return Navigation.goBack(ROUTES.SETTINGS_WORKSPACES)} />; - } - - return ( - - ); - } - - WithPaidPolicyAccessOrNotFound.displayName = `withPaidPolicyAccessOrNotFound(${getComponentDisplayName(WrappedComponent)})`; - - return withOnyx, WithPaidPolicyAccessOrNotFoundOnyxProps>({ - policy: { - key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY}${route.params.policyID ?? ''}`, - }, - isLoadingReportData: { - key: ONYXKEYS.IS_LOADING_REPORT_DATA, - }, - })(React.forwardRef(WithPaidPolicyAccessOrNotFound)); - }; -} - -export type {WithPaidPolicyAccessOrNotFoundProps}; From 3a984acaf98ae20a9edc7d1b4d533f28abb5b5b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Thu, 22 Feb 2024 10:00:02 +0100 Subject: [PATCH 29/32] fix: improve es translations --- src/languages/es.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/languages/es.ts b/src/languages/es.ts index 146317090159..084705956d17 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1740,10 +1740,10 @@ export default { collect: 'Recolectar', }, categories: { - subtitle: 'Obtenga una mejor visión general de dónde se gasta el dinero. Utilice nuestras categorías predeterminadas o agregue las suyas propias.', + subtitle: 'Obtén una visión general de dónde te gastas el dinero. Utiliza las categorías predeterminadas o añade las tuyas propias.', emptyCategories: { title: 'No has creado ninguna categoría', - subtitle: 'Agregue una categoría para organizar su gasto.', + subtitle: 'Añade una categoría para organizar tu gasto.', }, }, emptyWorkspace: { From 5c19e7fe432e3a78c5793e77fa07eef0c0c02f8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Fri, 23 Feb 2024 10:29:27 +0100 Subject: [PATCH 30/32] feat: utilize tablist --- src/languages/en.ts | 1 + src/languages/es.ts | 1 + .../categories/WorkspaceCategoriesPage.tsx | 15 +++++++++++++-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 48da8a648064..ffb764b40e6a 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -121,6 +121,7 @@ export default { no: 'No', ok: 'OK', buttonConfirm: 'Got it', + name: 'Name', attachment: 'Attachment', to: 'To', optional: 'Optional', diff --git a/src/languages/es.ts b/src/languages/es.ts index e6991ce74727..b03cbdd3772b 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -111,6 +111,7 @@ export default { no: 'No', ok: 'OK', buttonConfirm: 'Ok, entendido', + name: 'Nombre', attachment: 'Archivo adjunto', to: 'A', optional: 'Opcional', diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx index 15e49794c952..60188a66bad4 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx @@ -9,10 +9,11 @@ import * as Expensicons from '@components/Icon/Expensicons'; import * as Illustrations from '@components/Icon/Illustrations'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; -import RadioListItem from '@components/SelectionList/RadioListItem'; +import TableListItem from '@components/SelectionList/TableListItem'; import Text from '@components/Text'; import WorkspaceEmptyStateSection from '@components/WorkspaceEmptyStateSection'; import useLocalize from '@hooks/useLocalize'; +import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; @@ -42,6 +43,7 @@ function WorkspaceCategoriesPage({policyCategories, route}: WorkspaceCategoriesP const {isSmallScreenWidth} = useWindowDimensions(); const styles = useThemeStyles(); const theme = useTheme(); + const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); const [selectedCategories, setSelectedCategories] = useState>({}); @@ -79,6 +81,13 @@ function WorkspaceCategoriesPage({policyCategories, route}: WorkspaceCategoriesP setSelectedCategories(isAllSelected ? {} : Object.fromEntries(categoryList.map((item) => [item.value, true]))); }; + const getCustomListHeader = () => ( + + {translate('common.name')} + {translate('statusPage.status')} + + ); + return ( @@ -103,7 +112,9 @@ function WorkspaceCategoriesPage({policyCategories, route}: WorkspaceCategoriesP onSelectRow={toggleCategory} onSelectAll={toggleAllCategories} showScrollIndicator - ListItem={RadioListItem} + ListItem={TableListItem} + customListHeader={getCustomListHeader()} + listHeaderWrapperStyle={[styles.ph9, styles.pv3, styles.pb5]} /> ) : ( Date: Fri, 23 Feb 2024 10:37:53 +0100 Subject: [PATCH 31/32] fix: add cursor pointer style to checkbox --- src/components/SelectionList/BaseListItem.tsx | 2 +- src/styles/utils/index.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/SelectionList/BaseListItem.tsx b/src/components/SelectionList/BaseListItem.tsx index eb9450f6ad98..2f853dc55839 100644 --- a/src/components/SelectionList/BaseListItem.tsx +++ b/src/components/SelectionList/BaseListItem.tsx @@ -67,7 +67,7 @@ function BaseListItem({ {canSelectMultiple && ( diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index 833907549133..3ac9a7f8e718 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -17,6 +17,7 @@ import type {ThemeStyles} from '..'; import shouldPreventScrollOnAutoCompleteSuggestion from './autoCompleteSuggestion'; import getCardStyles from './cardStyles'; import containerComposeStyles from './containerComposeStyles'; +import cursor from './cursor'; import FontUtils from './FontUtils'; import createModalStyleUtils from './generators/ModalStyleUtils'; import createReportActionContextMenuStyleUtils from './generators/ReportActionContextMenuStyleUtils'; @@ -928,6 +929,7 @@ function getCheckboxPressableStyle(borderRadius = 6): ViewStyle { alignItems: 'center', // eslint-disable-next-line object-shorthand borderRadius: borderRadius, + ...cursor.cursorPointer, }; } From a25a43becb9a0126696c7f621ed996450734c095 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arkadiusz=20Chrab=C4=85szczewski?= Date: Fri, 23 Feb 2024 11:23:05 +0100 Subject: [PATCH 32/32] refactor: remove unused style utils --- src/pages/workspace/categories/WorkspaceCategoriesPage.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx index 60188a66bad4..b8a65c28806b 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx @@ -13,7 +13,6 @@ import TableListItem from '@components/SelectionList/TableListItem'; import Text from '@components/Text'; import WorkspaceEmptyStateSection from '@components/WorkspaceEmptyStateSection'; import useLocalize from '@hooks/useLocalize'; -import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import useWindowDimensions from '@hooks/useWindowDimensions'; @@ -43,7 +42,6 @@ function WorkspaceCategoriesPage({policyCategories, route}: WorkspaceCategoriesP const {isSmallScreenWidth} = useWindowDimensions(); const styles = useThemeStyles(); const theme = useTheme(); - const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); const [selectedCategories, setSelectedCategories] = useState>({});