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 (
-
+ {isOnlyOneTagsLists && (
+
+ )}
{policyTags && (