diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 6e69d2d4e53f..08623f0ad7ca 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -667,6 +667,10 @@ const ROUTES = { route: 'settings/workspaces/:policyID/tag/:tagName', getRoute: (policyID: string, tagName: string) => `settings/workspaces/${policyID}/tag/${encodeURIComponent(tagName)}` as const, }, + WORKSPACE_TAG_LIST_VIEW: { + route: 'settings/workspaces/:policyID/tag-list/:order', + getRoute: (policyID: string, order: string) => `settings/workspaces/${policyID}/tag-list/${encodeURIComponent(order)}` as const, + }, WORKSPACE_TAXES: { route: 'settings/workspaces/:policyID/taxes', getRoute: (policyID: string) => `settings/workspaces/${policyID}/taxes` as const, diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 725cab85f12b..1dc68cbe7a14 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -263,6 +263,7 @@ const SCREENS = { TAX_CREATE: 'Workspace_Tax_Create', TAG_CREATE: 'Tag_Create', TAG_SETTINGS: 'Tag_Settings', + TAG_LIST_VIEW: 'Tag_List_View', CURRENCY: 'Workspace_Profile_Currency', WORKFLOWS: 'Workspace_Workflows', WORKFLOWS_PAYER: 'Workspace_Workflows_Payer', diff --git a/src/components/SelectionList/RightElementRequiredStatus.tsx b/src/components/SelectionList/RightElementRequiredStatus.tsx new file mode 100644 index 000000000000..7b8ec89cf233 --- /dev/null +++ b/src/components/SelectionList/RightElementRequiredStatus.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import {View} from 'react-native'; +import Icon from '@components/Icon'; +import * as Expensicons from '@components/Icon/Expensicons'; +import Text from '@components/Text'; +import useLocalize from '@hooks/useLocalize'; +import useTheme from '@hooks/useTheme'; +import useThemeStyles from '@hooks/useThemeStyles'; + +type RightElementRequiredStatusProps = { + required?: boolean; +}; + +function RightElementRequiredStatus({required}: RightElementRequiredStatusProps) { + const styles = useThemeStyles(); + const theme = useTheme(); + const {translate} = useLocalize(); + + return ( + + {!!required && {translate('workspace.common.required')}} + + + + + ); +} + +RightElementRequiredStatus.displayName = 'RightElementRequiredStatus'; + +export default RightElementRequiredStatus; diff --git a/src/languages/en.ts b/src/languages/en.ts index 9d33d29c59bd..55e1b6b33a81 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1843,6 +1843,7 @@ export default { disabled: 'Disabled', delete: 'Delete workspace', settings: 'Settings', + required: 'Required', reimburse: 'Reimbursements', categories: 'Categories', tags: 'Tags', diff --git a/src/languages/es.ts b/src/languages/es.ts index ab170e06acc5..887c4534d2f6 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1865,6 +1865,7 @@ export default { edit: 'Editar espacio de trabajo', enabled: 'Activada', disabled: 'Desactivada', + required: 'Obligatorio', delete: 'Eliminar espacio de trabajo', settings: 'Configuración', reimburse: 'Reembolsos', diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index 96ae6fdfffcc..569e21708dba 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -242,6 +242,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../../pages/workspace/distanceRates/PolicyDistanceRateEditPage').default as React.ComponentType, [SCREENS.WORKSPACE.TAGS_SETTINGS]: () => require('../../../../pages/workspace/tags/WorkspaceTagsSettingsPage').default as React.ComponentType, [SCREENS.WORKSPACE.TAG_SETTINGS]: () => require('../../../../pages/workspace/tags/TagSettingsPage').default as React.ComponentType, + [SCREENS.WORKSPACE.TAG_LIST_VIEW]: () => require('../../../../pages/workspace/tags/WorkspaceViewTagsPage').default as React.ComponentType, [SCREENS.WORKSPACE.TAGS_EDIT]: () => require('../../../../pages/workspace/tags/WorkspaceEditTagsPage').default as React.ComponentType, [SCREENS.WORKSPACE.TAG_CREATE]: () => require('../../../../pages/workspace/tags/WorkspaceCreateTagPage').default as React.ComponentType, [SCREENS.WORKSPACE.TAG_EDIT]: () => require('../../../../pages/workspace/tags/EditTagPage').default as React.ComponentType, diff --git a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts index b2aa7ad35a05..69f98961749e 100755 --- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts @@ -51,7 +51,14 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = { SCREENS.WORKSPACE.TAX_NAME, SCREENS.WORKSPACE.TAX_VALUE, ], - [SCREENS.WORKSPACE.TAGS]: [SCREENS.WORKSPACE.TAGS_SETTINGS, SCREENS.WORKSPACE.TAGS_EDIT, SCREENS.WORKSPACE.TAG_CREATE, SCREENS.WORKSPACE.TAG_SETTINGS, SCREENS.WORKSPACE.TAG_EDIT], + [SCREENS.WORKSPACE.TAGS]: [ + SCREENS.WORKSPACE.TAGS_SETTINGS, + SCREENS.WORKSPACE.TAGS_EDIT, + SCREENS.WORKSPACE.TAG_CREATE, + SCREENS.WORKSPACE.TAG_SETTINGS, + SCREENS.WORKSPACE.TAG_EDIT, + SCREENS.WORKSPACE.TAG_LIST_VIEW, + ], [SCREENS.WORKSPACE.CATEGORIES]: [SCREENS.WORKSPACE.CATEGORY_CREATE, SCREENS.WORKSPACE.CATEGORY_SETTINGS, SCREENS.WORKSPACE.CATEGORIES_SETTINGS, SCREENS.WORKSPACE.CATEGORY_EDIT], [SCREENS.WORKSPACE.DISTANCE_RATES]: [ SCREENS.WORKSPACE.CREATE_DISTANCE_RATE, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index aa0e7fd20924..602755d41f1d 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -416,6 +416,12 @@ const config: LinkingOptions['config'] = { tagName: (tagName: string) => decodeURIComponent(tagName), }, }, + [SCREENS.WORKSPACE.TAG_LIST_VIEW]: { + path: ROUTES.WORKSPACE_TAG_LIST_VIEW.route, + parse: { + order: (order: string) => decodeURIComponent(order), + }, + }, [SCREENS.WORKSPACE.TAXES_SETTINGS]: { path: ROUTES.WORKSPACE_TAXES_SETTINGS.route, }, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index cf999631a803..7a6d5ea9104a 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -213,6 +213,10 @@ type SettingsNavigatorParamList = { policyID: string; tagName: string; }; + [SCREENS.WORKSPACE.TAG_LIST_VIEW]: { + policyID: string; + order: string; + }; [SCREENS.WORKSPACE.TAGS_EDIT]: { policyID: string; tagName: string; diff --git a/src/pages/workspace/accounting/qbo/import/QuickbooksImportPage.tsx b/src/pages/workspace/accounting/qbo/import/QuickbooksImportPage.tsx index 7d6ee38a1a29..460e8de4cc31 100644 --- a/src/pages/workspace/accounting/qbo/import/QuickbooksImportPage.tsx +++ b/src/pages/workspace/accounting/qbo/import/QuickbooksImportPage.tsx @@ -59,7 +59,7 @@ function QuickbooksImportPage({policy}: WithPolicyProps) { }, ]; - if (policy?.connections?.quickbooksOnline.data.country !== CONST.COUNTRY.US) { + if (policy?.connections?.quickbooksOnline?.data?.country !== CONST.COUNTRY.US) { sections.push({ description: translate('workspace.qbo.taxes'), action: () => Navigation.navigate(ROUTES.POLICY_ACCOUNTING_QUICKBOOKS_ONLINE_TAXES.getRoute(policyID)), diff --git a/src/pages/workspace/tags/WorkspaceTagsPage.tsx b/src/pages/workspace/tags/WorkspaceTagsPage.tsx index 4e4ab5ddb66e..e805f07b7230 100644 --- a/src/pages/workspace/tags/WorkspaceTagsPage.tsx +++ b/src/pages/workspace/tags/WorkspaceTagsPage.tsx @@ -13,6 +13,7 @@ import * as Illustrations from '@components/Icon/Illustrations'; import ScreenWrapper from '@components/ScreenWrapper'; import SelectionList from '@components/SelectionList'; import RightElementEnabledStatus from '@components/SelectionList/RightElementEnabledStatus'; +import RightElementRequiredStatus from '@components/SelectionList/RightElementRequiredStatus'; import TableListItem from '@components/SelectionList/TableListItem'; import type {ListItem} from '@components/SelectionList/types'; import Text from '@components/Text'; @@ -68,6 +69,8 @@ function WorkspaceTagsPage({route, policy}: WorkspaceTagsPageProps) { const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`); const {environmentURL} = useEnvironment(); const isConnectedToAccounting = Object.keys(policy?.connections ?? {}).length > 0; + const policyTagsLists = useMemo(() => PolicyUtils.getTagLists(policyTags), [policyTags]); + const isOnlyOneTagsLists = Object.keys(policyTagsLists).length === 1; const fetchTags = useCallback(() => { Policy.openPolicyTagsPage(policyID); @@ -88,31 +91,39 @@ function WorkspaceTagsPage({route, policy}: WorkspaceTagsPageProps) { setSelectedTags({}); }, [isFocused]); - // We currently don't support multi level tags, so let's only get the first level tags. - const policyTagLists = useMemo(() => PolicyUtils.getTagLists(policyTags).slice(0, 1), [policyTags]); - const tagList = useMemo( - () => - policyTagLists - .map((policyTagList) => - lodashSortBy(Object.values(policyTagList.tags || []), 'name', localeCompare).map((value) => { - const tag = value as OnyxCommon.OnyxValueWithOfflineFeedback; - const isDisabled = tag.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; - return { - value: tag.name, - text: PolicyUtils.getCleanedTagName(tag.name), - keyForList: tag.name, - isSelected: !!selectedTags[tag.name], - pendingAction: tag.pendingAction, - errors: tag.errors ?? undefined, - enabled: tag.enabled, - isDisabled, - rightElement: , - }; - }), - ) - .flat(), - [policyTagLists, selectedTags], - ); + const tagList = useMemo(() => { + const policyTagLists = isOnlyOneTagsLists ? policyTagsLists.slice(0, 1) : policyTagsLists; + if (!isOnlyOneTagsLists) { + return Object.values(policyTagLists).map((policyTagList) => ({ + value: policyTagList.name, + text: PolicyUtils.getCleanedTagName(policyTagList.name), + keyForList: policyTagList.orderWeight.toString(), + isSelected: !!selectedTags[policyTagList.name], + enabled: true, + required: policyTagList.required, + rightElement: , + })); + } + return policyTagLists + .map((policyTagList) => + lodashSortBy(Object.values(policyTagList.tags || []), 'name', localeCompare).map((value) => { + const tag = value as OnyxCommon.OnyxValueWithOfflineFeedback; + const isDisabled = tag.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE; + return { + value: tag.name, + text: PolicyUtils.getCleanedTagName(tag.name), + keyForList: tag.name, + isSelected: !!selectedTags[tag.name], + pendingAction: tag.pendingAction, + errors: tag.errors ?? undefined, + enabled: tag.enabled, + isDisabled, + rightElement: , + }; + }), + ) + .flat(); + }, [isOnlyOneTagsLists, policyTagsLists, selectedTags]); const tagListKeyedByName = tagList.reduce>((acc, tag) => { acc[tag.value] = tag; @@ -147,6 +158,10 @@ function WorkspaceTagsPage({route, policy}: WorkspaceTagsPageProps) { }; const navigateToTagSettings = (tag: PolicyOption) => { + if (!isOnlyOneTagsLists) { + Navigation.navigate(ROUTES.WORKSPACE_TAG_LIST_VIEW.getRoute(policyID, tag.keyForList)); + return; + } Navigation.navigate(ROUTES.WORKSPACE_TAG_SETTINGS.getRoute(policyID, tag.keyForList)); }; @@ -233,14 +248,16 @@ function WorkspaceTagsPage({route, policy}: WorkspaceTagsPageProps) { return ( -