Skip to content

Commit

Permalink
Merge pull request #38335 from waterim/feat-37313-tagSettings
Browse files Browse the repository at this point in the history
Create TagSettingsPage
  • Loading branch information
luacmartins authored Mar 15, 2024
2 parents 1f232f2 + 397e102 commit 40acece
Show file tree
Hide file tree
Showing 14 changed files with 226 additions and 4 deletions.
4 changes: 4 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,10 @@ const ROUTES = {
route: 'settings/workspaces/:policyID/tags/edit',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/tags/edit` as const,
},
WORKSPACE_TAG_SETTINGS: {
route: 'settings/workspaces/:policyID/tag/:tagName',
getRoute: (policyID: string, tagName: string) => `settings/workspaces/${policyID}/tag/${encodeURIComponent(tagName)}` 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 @@ -221,6 +221,7 @@ const SCREENS = {
TAGS_EDIT: 'Tags_Edit',
TAXES: 'Workspace_Taxes',
TAG_CREATE: 'Tag_Create',
TAG_SETTINGS: 'Tag_Settings',
CURRENCY: 'Workspace_Profile_Currency',
WORKFLOWS: 'Workspace_Workflows',
WORKFLOWS_PAYER: 'Workspace_Workflows_Payer',
Expand Down
1 change: 1 addition & 0 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1837,6 +1837,7 @@ export default {
},
},
tags: {
tagName: 'Tag name',
requiresTag: 'Members must tag all spend',
customTagName: 'Custom tag name',
enableTag: 'Enable tag',
Expand Down
1 change: 1 addition & 0 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1861,6 +1861,7 @@ export default {
},
},
tags: {
tagName: 'Nombre de etiqueta',
requiresTag: 'Los miembros deben etiquetar todos los gastos',
customTagName: 'Nombre de etiqueta personalizada',
enableTag: 'Habilitar etiqueta',
Expand Down
10 changes: 10 additions & 0 deletions src/libs/API/parameters/SetPolicyTagsEnabled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
type SetPolicyTagsEnabled = {
policyID: string;
/**
* Stringified JSON object with type of following structure:
* Array<{name: string; enabled: boolean}>
*/
tags: string;
};

export default SetPolicyTagsEnabled;
3 changes: 2 additions & 1 deletion src/libs/API/parameters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ export type {default as EnablePolicyCategoriesParams} from './EnablePolicyCatego
export type {default as EnablePolicyConnectionsParams} from './EnablePolicyConnectionsParams';
export type {default as EnablePolicyDistanceRatesParams} from './EnablePolicyDistanceRatesParams';
export type {default as EnablePolicyTagsParams} from './EnablePolicyTagsParams';
export type {default as EnablePolicyTaxesParams} from './EnablePolicyTaxesParams';
export type {default as SetPolicyTagsEnabled} from './SetPolicyTagsEnabled';
export type {default as EnablePolicyWorkflowsParams} from './EnablePolicyWorkflowsParams';
export type {default as EnablePolicyReportFieldsParams} from './EnablePolicyReportFieldsParams';
export type {default as AcceptJoinRequestParams} from './AcceptJoinRequest';
Expand All @@ -177,6 +177,7 @@ export type {default as JoinPolicyInviteLinkParams} from './JoinPolicyInviteLink
export type {default as OpenPolicyWorkflowsPageParams} from './OpenPolicyWorkflowsPageParams';
export type {default as OpenPolicyDistanceRatesPageParams} from './OpenPolicyDistanceRatesPageParams';
export type {default as OpenPolicyTaxesPageParams} from './OpenPolicyTaxesPageParams';
export type {default as EnablePolicyTaxesParams} from './EnablePolicyTaxesParams';
export type {default as OpenPolicyMoreFeaturesPageParams} from './OpenPolicyMoreFeaturesPageParams';
export type {default as CreatePolicyDistanceRateParams} from './CreatePolicyDistanceRateParams';
export type {default as CreatePolicyTagsParams} from './CreatePolicyTagsParams';
2 changes: 2 additions & 0 deletions src/libs/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ const WRITE_COMMANDS = {
CREATE_WORKSPACE: 'CreateWorkspace',
CREATE_WORKSPACE_FROM_IOU_PAYMENT: 'CreateWorkspaceFromIOUPayment',
SET_WORKSPACE_CATEGORIES_ENABLED: 'SetWorkspaceCategoriesEnabled',
SET_POLICY_TAGS_ENABLED: 'SetPolicyTagsEnabled',
CREATE_WORKSPACE_CATEGORIES: 'CreateWorkspaceCategories',
RENAME_WORKSPACE_CATEGORY: 'RenameWorkspaceCategory',
CREATE_POLICY_TAG: 'CreatePolicyTag',
Expand Down Expand Up @@ -292,6 +293,7 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.SET_POLICY_REQUIRES_TAG]: Parameters.SetPolicyRequiresTag;
[WRITE_COMMANDS.RENAME_POLICY_TAG_LIST]: Parameters.RenamePolicyTaglist;
[WRITE_COMMANDS.CREATE_POLICY_TAG]: Parameters.CreatePolicyTagsParams;
[WRITE_COMMANDS.SET_POLICY_TAGS_ENABLED]: Parameters.SetPolicyTagsEnabled;
[WRITE_COMMANDS.CREATE_TASK]: Parameters.CreateTaskParams;
[WRITE_COMMANDS.CANCEL_TASK]: Parameters.CancelTaskParams;
[WRITE_COMMANDS.EDIT_TASK_ASSIGNEE]: Parameters.EditTaskAssigneeParams;
Expand Down
1 change: 1 addition & 0 deletions src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP
[SCREENS.WORKSPACE.CATEGORY_EDIT]: () => require('../../../pages/workspace/categories/EditCategoryPage').default as React.ComponentType,
[SCREENS.WORKSPACE.CREATE_DISTANCE_RATE]: () => require('@pages/workspace/distanceRates/CreateDistanceRatePage').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.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.REIMBURSEMENT_ACCOUNT]: () => require('../../../pages/ReimbursementAccount/ReimbursementAccountPage').default as React.ComponentType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial<Record<FullScreenName, string[]>> = {
SCREENS.WORKSPACE.WORKFLOWS_AUTO_REPORTING_MONTHLY_OFFSET,
SCREENS.WORKSPACE.WORKFLOWS_PAYER,
],
[SCREENS.WORKSPACE.TAGS]: [SCREENS.WORKSPACE.TAGS_SETTINGS, SCREENS.WORKSPACE.TAGS_EDIT, SCREENS.WORKSPACE.TAG_CREATE],
[SCREENS.WORKSPACE.TAGS]: [SCREENS.WORKSPACE.TAGS_SETTINGS, SCREENS.WORKSPACE.TAGS_EDIT, SCREENS.WORKSPACE.TAG_CREATE, SCREENS.WORKSPACE.TAG_SETTINGS],
[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 @@ -301,6 +301,12 @@ const config: LinkingOptions<RootStackParamList>['config'] = {
[SCREENS.WORKSPACE.TAG_CREATE]: {
path: ROUTES.WORKSPACE_TAG_CREATE.route,
},
[SCREENS.WORKSPACE.TAG_SETTINGS]: {
path: ROUTES.WORKSPACE_TAG_SETTINGS.route,
parse: {
tagName: (tagName: string) => decodeURIComponent(tagName),
},
},
[SCREENS.REIMBURSEMENT_ACCOUNT]: {
path: ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.route,
exact: true,
Expand Down
6 changes: 5 additions & 1 deletion src/libs/Navigation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,10 @@ type SettingsNavigatorParamList = {
[SCREENS.WORKSPACE.TAGS_SETTINGS]: {
policyID: string;
};
[SCREENS.WORKSPACE.TAG_SETTINGS]: {
policyID: string;
tagName: string;
};
[SCREENS.WORKSPACE.TAGS_EDIT]: {
policyID: string;
tagName: string;
Expand Down Expand Up @@ -545,7 +549,7 @@ type WorkspacesCentralPaneNavigatorParamList = {
};
[SCREENS.WORKSPACE.TAGS]: {
policyID: string;
categoryName: string;
tagName: string;
};
[SCREENS.WORKSPACE.TAXES]: {
policyID: string;
Expand Down
89 changes: 89 additions & 0 deletions src/libs/actions/Policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import Navigation from '@libs/Navigation/Navigation';
import * as NumberUtils from '@libs/NumberUtils';
import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
import * as PhoneNumber from '@libs/PhoneNumber';
import * as PolicyUtils from '@libs/PolicyUtils';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import * as ReportUtils from '@libs/ReportUtils';
import * as TransactionUtils from '@libs/TransactionUtils';
Expand All @@ -70,6 +71,7 @@ import type {
PolicyCategory,
PolicyMember,
PolicyTagList,
PolicyTags,
RecentlyUsedCategories,
RecentlyUsedTags,
ReimbursementAccount,
Expand Down Expand Up @@ -2901,6 +2903,92 @@ function createPolicyTag(policyID: string, tagName: string) {
API.write(WRITE_COMMANDS.CREATE_POLICY_TAG, parameters, onyxData);
}

function setWorkspaceTagEnabled(policyID: string, tagsToUpdate: Record<string, {name: string; enabled: boolean}>) {
const policyTag = PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.[0] ?? {};

const onyxData: OnyxData = {
optimisticData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`,
value: {
[policyTag.name]: {
tags: {
...Object.keys(tagsToUpdate).reduce<PolicyTags>((acc, key) => {
acc[key] = {
...policyTag.tags[key],
...tagsToUpdate[key],
errors: null,
pendingFields: {
enabled: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
},
};

return acc;
}, {}),
},
},
},
},
],
successData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`,
value: {
[policyTag.name]: {
tags: {
...Object.keys(tagsToUpdate).reduce<PolicyTags>((acc, key) => {
acc[key] = {
...policyTag.tags[key],
...tagsToUpdate[key],
errors: null,
pendingFields: {
enabled: null,
},
};

return acc;
}, {}),
},
},
},
},
],
failureData: [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`,
value: {
[policyTag.name]: {
tags: {
...Object.keys(tagsToUpdate).reduce<PolicyTags>((acc, key) => {
acc[key] = {
...policyTag.tags[key],
...tagsToUpdate[key],
errors: ErrorUtils.getMicroSecondOnyxError('workspace.tags.genericFailureMessage'),
pendingFields: {
enabled: null,
},
};

return acc;
}, {}),
},
},
},
},
],
};

const parameters = {
policyID,
tags: JSON.stringify(Object.keys(tagsToUpdate).map((key) => tagsToUpdate[key])),
};

API.write(WRITE_COMMANDS.SET_POLICY_TAGS_ENABLED, parameters, onyxData);
}

function clearPolicyTagErrors(policyID: string, tagName: string) {
const tagListName = Object.keys(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})[0];
const tag = allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`]?.[tagListName].tags?.[tagName];
Expand Down Expand Up @@ -3777,4 +3865,5 @@ export {
clearPolicyTagErrors,
clearWorkspaceReimbursementErrors,
deleteWorkspaceCategories,
setWorkspaceTagEnabled,
};
92 changes: 92 additions & 0 deletions src/pages/workspace/tags/TagSettingsPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import type {StackScreenProps} from '@react-navigation/stack';
import React, {useMemo} from 'react';
import {View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
import type {OnyxEntry} from 'react-native-onyx';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import OfflineWithFeedback from '@components/OfflineWithFeedback';
import ScreenWrapper from '@components/ScreenWrapper';
import Switch from '@components/Switch';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import {setWorkspaceTagEnabled} from '@libs/actions/Policy';
import * as ErrorUtils from '@libs/ErrorUtils';
import * as PolicyUtils from '@libs/PolicyUtils';
import type {SettingsNavigatorParamList} from '@navigation/types';
import NotFoundPage from '@pages/ErrorPage/NotFoundPage';
import AdminPolicyAccessOrNotFoundWrapper from '@pages/workspace/AdminPolicyAccessOrNotFoundWrapper';
import PaidPolicyAccessOrNotFoundWrapper from '@pages/workspace/PaidPolicyAccessOrNotFoundWrapper';
import * as Policy from '@userActions/Policy';
import ONYXKEYS from '@src/ONYXKEYS';
import type SCREENS from '@src/SCREENS';
import type {PolicyTagList} from '@src/types/onyx';

type TagSettingsPageOnyxProps = {
/** All policy tags */
policyTags: OnyxEntry<PolicyTagList>;
};

type TagSettingsPageProps = TagSettingsPageOnyxProps & StackScreenProps<SettingsNavigatorParamList, typeof SCREENS.WORKSPACE.TAG_SETTINGS>;

function TagSettingsPage({route, policyTags}: TagSettingsPageProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const policyTag = useMemo(() => PolicyUtils.getTagList(policyTags, 0), [policyTags]);

const currentPolicyTag = policyTag.tags[decodeURIComponent(route.params.tagName)];

if (!currentPolicyTag) {
return <NotFoundPage />;
}

const updateWorkspaceTagEnabled = (value: boolean) => {
setWorkspaceTagEnabled(route.params.policyID, {[currentPolicyTag.name]: {name: currentPolicyTag.name, enabled: value}});
};

return (
<AdminPolicyAccessOrNotFoundWrapper policyID={route.params.policyID}>
<PaidPolicyAccessOrNotFoundWrapper policyID={route.params.policyID}>
<ScreenWrapper
includeSafeAreaPaddingBottom={false}
style={[styles.defaultModalContainer]}
testID={TagSettingsPage.displayName}
>
<HeaderWithBackButton title={route.params.tagName} />
<View style={styles.flexGrow1}>
<OfflineWithFeedback
errors={ErrorUtils.getLatestErrorMessageField(currentPolicyTag)}
pendingAction={currentPolicyTag.pendingFields?.enabled}
errorRowStyles={styles.mh5}
onClose={() => Policy.clearPolicyTagErrors(route.params.policyID, route.params.tagName)}
>
<View style={[styles.mt2, styles.mh5]}>
<View style={[styles.flexRow, styles.mb5, styles.mr2, styles.alignItemsCenter, styles.justifyContentBetween]}>
<Text>{translate('workspace.tags.enableTag')}</Text>
<Switch
isOn={currentPolicyTag.enabled}
accessibilityLabel={translate('workspace.tags.enableTag')}
onToggle={updateWorkspaceTagEnabled}
/>
</View>
</View>
</OfflineWithFeedback>
<MenuItemWithTopDescription
title={currentPolicyTag.name}
description={translate(`workspace.tags.tagName`)}
/>
</View>
</ScreenWrapper>
</PaidPolicyAccessOrNotFoundWrapper>
</AdminPolicyAccessOrNotFoundWrapper>
);
}

TagSettingsPage.displayName = 'TagSettingsPage';

export default withOnyx<TagSettingsPageProps, TagSettingsPageOnyxProps>({
policyTags: {
key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${route.params.policyID}`,
},
})(TagSettingsPage);
12 changes: 11 additions & 1 deletion src/pages/workspace/tags/WorkspaceTagsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import * as Illustrations from '@components/Icon/Illustrations';
import ScreenWrapper from '@components/ScreenWrapper';
import SelectionList from '@components/SelectionList';
import TableListItem from '@components/SelectionList/TableListItem';
import type {ListItem} from '@components/SelectionList/types';
import Text from '@components/Text';
import WorkspaceEmptyStateSection from '@components/WorkspaceEmptyStateSection';
import useLocalize from '@hooks/useLocalize';
Expand Down Expand Up @@ -38,6 +39,11 @@ type PolicyForList = {
rightElement: React.ReactNode;
};

type PolicyOption = ListItem & {
/** Tag name is used as a key for the selectedTags state */
keyForList: string;
};

type WorkspaceTagsOnyxProps = {
/** Collection of tags attached to a policy */
policyTags: OnyxEntry<OnyxTypes.PolicyTagList>;
Expand Down Expand Up @@ -121,6 +127,10 @@ function WorkspaceTagsPage({policyTags, route}: WorkspaceTagsPageProps) {
Navigation.navigate(ROUTES.WORKSPACE_TAG_CREATE.getRoute(route.params.policyID));
};

const navigateToTagSettings = (tag: PolicyOption) => {
Navigation.navigate(ROUTES.WORKSPACE_TAG_SETTINGS.getRoute(route.params.policyID, tag.keyForList));
};

const isLoading = !isOffline && policyTags === undefined;

const headerButtons = (
Expand Down Expand Up @@ -184,7 +194,7 @@ function WorkspaceTagsPage({policyTags, route}: WorkspaceTagsPageProps) {
canSelectMultiple
sections={[{data: tagList, indexOffset: 0, isDisabled: false}]}
onCheckboxPress={toggleTag}
onSelectRow={() => {}}
onSelectRow={navigateToTagSettings}
onSelectAll={toggleAllTags}
showScrollIndicator
ListItem={TableListItem}
Expand Down

0 comments on commit 40acece

Please sign in to comment.