Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
34 changes: 34 additions & 0 deletions src/components/SelectionList/RightElementRequiredStatus.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<View style={styles.flexRow}>
{!!required && <Text style={[styles.alignSelfCenter, styles.textSupporting, styles.pl2, styles.label]}>{translate('workspace.common.required')}</Text>}
<View style={[styles.p1, styles.pl2]}>
<Icon
src={Expensicons.ArrowRight}
fill={theme.icon}
/>
</View>
</View>
);
}

RightElementRequiredStatus.displayName = 'RightElementRequiredStatus';

export default RightElementRequiredStatus;
1 change: 1 addition & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1843,6 +1843,7 @@ export default {
disabled: 'Disabled',
delete: 'Delete workspace',
settings: 'Settings',
required: 'Required',
reimburse: 'Reimbursements',
categories: 'Categories',
tags: 'Tags',
Expand Down
1 change: 1 addition & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP
[SCREENS.WORKSPACE.DISTANCE_RATE_EDIT]: () => 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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,14 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial<Record<FullScreenName, string[]>> = {
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,
Expand Down
6 changes: 6 additions & 0 deletions src/libs/Navigation/linkingConfig/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,12 @@ const config: LinkingOptions<RootStackParamList>['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,
},
Expand Down
4 changes: 4 additions & 0 deletions src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)),
Expand Down
83 changes: 50 additions & 33 deletions src/pages/workspace/tags/WorkspaceTagsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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);
Expand All @@ -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<PolicyForList[]>(
() =>
policyTagLists
.map((policyTagList) =>
lodashSortBy(Object.values(policyTagList.tags || []), 'name', localeCompare).map((value) => {
const tag = value as OnyxCommon.OnyxValueWithOfflineFeedback<OnyxTypes.PolicyTag>;
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: <RightElementEnabledStatus enabled={tag.enabled} />,
};
}),
)
.flat(),
[policyTagLists, selectedTags],
);
const tagList = useMemo<PolicyForList[]>(() => {
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: <RightElementRequiredStatus required={policyTagList.required} />,
}));
}
return policyTagLists
.map((policyTagList) =>
lodashSortBy(Object.values(policyTagList.tags || []), 'name', localeCompare).map((value) => {
const tag = value as OnyxCommon.OnyxValueWithOfflineFeedback<OnyxTypes.PolicyTag>;
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: <RightElementEnabledStatus enabled={tag.enabled} />,
};
}),
)
.flat();
}, [isOnlyOneTagsLists, policyTagsLists, selectedTags]);

const tagListKeyedByName = tagList.reduce<Record<string, PolicyForList>>((acc, tag) => {
acc[tag.value] = tag;
Expand Down Expand Up @@ -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));
};

Expand Down Expand Up @@ -233,14 +248,16 @@ function WorkspaceTagsPage({route, policy}: WorkspaceTagsPageProps) {

return (
<View style={[styles.w100, styles.flexRow, isSmallScreenWidth && styles.mb3]}>
<Button
medium
success
onPress={navigateToCreateTagPage}
icon={Expensicons.Plus}
text={translate('workspace.tags.addTag')}
style={[styles.mr3, isSmallScreenWidth && styles.w50]}
/>
{isOnlyOneTagsLists && (
<Button
medium
success
onPress={navigateToCreateTagPage}
icon={Expensicons.Plus}
text={translate('workspace.tags.addTag')}
style={[styles.mr3, isSmallScreenWidth && styles.w50]}
/>
)}
{policyTags && (
<Button
medium
Expand Down
Loading