Skip to content

Commit e9799d9

Browse files
authored
Merge pull request #42972 from Expensify/hayata-add-requires-tags-button
Add a switch for requiring tags on the multi level tags RHP
2 parents a451de4 + 9b97a25 commit e9799d9

File tree

8 files changed

+113
-10
lines changed

8 files changed

+113
-10
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
type SetPolicyTagsRequired = {
2+
policyID: string;
3+
tagListIndex: number;
4+
requireTagList: boolean;
5+
};
6+
7+
export default SetPolicyTagsRequired;

src/libs/API/parameters/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ export type {default as SetWorkspaceApprovalModeParams} from './SetWorkspaceAppr
172172
export type {default as SetWorkspacePayerParams} from './SetWorkspacePayerParams';
173173
export type {default as SetWorkspaceReimbursementParams} from './SetWorkspaceReimbursementParams';
174174
export type {default as SetPolicyRequiresTag} from './SetPolicyRequiresTag';
175+
export type {default as SetPolicyTagsRequired} from './SetPolicyTagsRequired';
175176
export type {default as RenamePolicyTaglistParams} from './RenamePolicyTaglistParams';
176177
export type {default as SwitchToOldDotParams} from './SwitchToOldDotParams';
177178
export type {default as TrackExpenseParams} from './TrackExpenseParams';

src/libs/API/types.ts

+2
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ const WRITE_COMMANDS = {
133133
RENAME_POLICY_TAG: 'RenamePolicyTag',
134134
SET_WORKSPACE_REQUIRES_CATEGORY: 'SetWorkspaceRequiresCategory',
135135
DELETE_WORKSPACE_CATEGORIES: 'DeleteWorkspaceCategories',
136+
SET_POLICY_TAGS_REQUIRED: 'SetPolicyTagsRequired',
136137
SET_POLICY_REQUIRES_TAG: 'SetPolicyRequiresTag',
137138
RENAME_POLICY_TAG_LIST: 'RenamePolicyTaglist',
138139
DELETE_POLICY_TAGS: 'DeletePolicyTags',
@@ -345,6 +346,7 @@ type WriteCommandParameters = {
345346
[WRITE_COMMANDS.SET_WORKSPACE_REQUIRES_CATEGORY]: Parameters.SetWorkspaceRequiresCategoryParams;
346347
[WRITE_COMMANDS.DELETE_WORKSPACE_CATEGORIES]: Parameters.DeleteWorkspaceCategoriesParams;
347348
[WRITE_COMMANDS.SET_POLICY_REQUIRES_TAG]: Parameters.SetPolicyRequiresTag;
349+
[WRITE_COMMANDS.SET_POLICY_TAGS_REQUIRED]: Parameters.SetPolicyTagsRequired;
348350
[WRITE_COMMANDS.RENAME_POLICY_TAG_LIST]: Parameters.RenamePolicyTaglistParams;
349351
[WRITE_COMMANDS.CREATE_POLICY_TAG]: Parameters.CreatePolicyTagsParams;
350352
[WRITE_COMMANDS.RENAME_POLICY_TAG]: Parameters.RenamePolicyTagsParams;

src/libs/actions/Policy/Tag.ts

+80-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type {NullishDeep, OnyxCollection, OnyxEntry} from 'react-native-onyx';
22
import Onyx from 'react-native-onyx';
33
import * as API from '@libs/API';
4-
import type {EnablePolicyTagsParams, OpenPolicyTagsPageParams, RenamePolicyTaglistParams, RenamePolicyTagsParams, SetPolicyTagsEnabled} from '@libs/API/parameters';
4+
import type {EnablePolicyTagsParams, OpenPolicyTagsPageParams, RenamePolicyTaglistParams, RenamePolicyTagsParams, SetPolicyTagsEnabled, SetPolicyTagsRequired} from '@libs/API/parameters';
55
import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types';
66
import * as ErrorUtils from '@libs/ErrorUtils';
77
import getIsNarrowLayout from '@libs/getIsNarrowLayout';
@@ -161,6 +161,7 @@ function createPolicyTag(policyID: string, tagName: string) {
161161
tags: {
162162
[newTagName]: {
163163
errors: ErrorUtils.getMicroSecondOnyxError('workspace.tags.genericFailureMessage'),
164+
pendingAction: null,
164165
},
165166
},
166167
},
@@ -329,8 +330,8 @@ function deletePolicyTags(policyID: string, tagsToDelete: string[]) {
329330
API.write(WRITE_COMMANDS.DELETE_POLICY_TAGS, parameters, onyxData);
330331
}
331332

332-
function clearPolicyTagErrors(policyID: string, tagName: string) {
333-
const tagListName = Object.keys(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})[0];
333+
function clearPolicyTagErrors(policyID: string, tagName: string, tagListIndex: number) {
334+
const tagListName = Object.keys(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})[tagListIndex];
334335
const tag = allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`]?.[tagListName].tags?.[tagName];
335336
if (!tag) {
336337
return;
@@ -359,10 +360,25 @@ function clearPolicyTagErrors(policyID: string, tagName: string) {
359360
});
360361
}
361362

363+
function clearPolicyTagListError(policyID: string, tagListIndex: number, errorField: string) {
364+
const policyTag = PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.[tagListIndex] ?? {};
365+
366+
if (!policyTag.name) {
367+
return;
368+
}
369+
370+
Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, {
371+
[policyTag.name]: {
372+
errorFields: {
373+
[errorField]: null,
374+
},
375+
},
376+
});
377+
}
378+
362379
function renamePolicyTag(policyID: string, policyTag: {oldName: string; newName: string}, tagListIndex: number) {
363380
const tagList = PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.[tagListIndex] ?? {};
364381
const tag = tagList.tags?.[policyTag.oldName];
365-
366382
const oldTagName = policyTag.oldName;
367383
const newTagName = PolicyUtils.escapeTagName(policyTag.newName);
368384
const onyxData: OnyxData = {
@@ -611,15 +627,75 @@ function setPolicyRequiresTag(policyID: string, requiresTag: boolean) {
611627
API.write(WRITE_COMMANDS.SET_POLICY_REQUIRES_TAG, parameters, onyxData);
612628
}
613629

630+
function setPolicyTagsRequired(policyID: string, requiresTag: boolean, tagListIndex: number) {
631+
const policyTag = PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.[tagListIndex] ?? {};
632+
633+
if (!policyTag.name) {
634+
return;
635+
}
636+
637+
const onyxData: OnyxData = {
638+
optimisticData: [
639+
{
640+
onyxMethod: Onyx.METHOD.MERGE,
641+
key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`,
642+
value: {
643+
[policyTag.name]: {
644+
required: requiresTag,
645+
pendingFields: {required: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE},
646+
errorFields: {required: null},
647+
},
648+
},
649+
},
650+
],
651+
successData: [
652+
{
653+
onyxMethod: Onyx.METHOD.MERGE,
654+
key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`,
655+
value: {
656+
[policyTag.name]: {
657+
pendingFields: {required: null},
658+
},
659+
},
660+
},
661+
],
662+
failureData: [
663+
{
664+
onyxMethod: Onyx.METHOD.MERGE,
665+
key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`,
666+
value: {
667+
[policyTag.name]: {
668+
required: policyTag.required,
669+
pendingFields: {required: null},
670+
errorFields: {
671+
required: ErrorUtils.getMicroSecondOnyxError('workspace.tags.genericFailureMessage'),
672+
},
673+
},
674+
},
675+
},
676+
],
677+
};
678+
679+
const parameters: SetPolicyTagsRequired = {
680+
policyID,
681+
tagListIndex,
682+
requireTagList: requiresTag,
683+
};
684+
685+
API.write(WRITE_COMMANDS.SET_POLICY_TAGS_REQUIRED, parameters, onyxData);
686+
}
687+
614688
export {
615689
openPolicyTagsPage,
616690
buildOptimisticPolicyRecentlyUsedTags,
617691
setPolicyRequiresTag,
692+
setPolicyTagsRequired,
618693
renamePolicyTaglist,
619694
enablePolicyTags,
620695
createPolicyTag,
621696
renamePolicyTag,
622697
clearPolicyTagErrors,
698+
clearPolicyTagListError,
623699
deletePolicyTags,
624700
setWorkspaceTagEnabled,
625701
};

src/pages/workspace/tags/TagSettingsPage.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ function TagSettingsPage({route, policyTags, navigation}: TagSettingsPageProps)
106106
errors={ErrorUtils.getLatestErrorMessageField(currentPolicyTag)}
107107
pendingAction={currentPolicyTag.pendingFields?.enabled}
108108
errorRowStyles={styles.mh5}
109-
onClose={() => Tag.clearPolicyTagErrors(route.params.policyID, route.params.tagName)}
109+
onClose={() => Tag.clearPolicyTagErrors(route.params.policyID, route.params.tagName, route.params.orderWeight)}
110110
>
111111
<View style={[styles.mt2, styles.mh5]}>
112112
<View style={[styles.flexRow, styles.mb5, styles.mr2, styles.alignItemsCenter, styles.justifyContentBetween]}>

src/pages/workspace/tags/WorkspaceTagsPage.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ function WorkspaceTagsPage({route}: WorkspaceTagsPageProps) {
350350
customListHeader={getCustomListHeader()}
351351
shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()}
352352
listHeaderWrapperStyle={[styles.ph9, styles.pv3, styles.pb5]}
353-
onDismissError={(item) => Tag.clearPolicyTagErrors(policyID, item.value)}
353+
onDismissError={(item) => !isMultiLevelTags && Tag.clearPolicyTagErrors(policyID, item.value, 0)}
354354
listHeaderContent={isSmallScreenWidth ? getHeaderText() : null}
355355
showScrollIndicator={false}
356356
/>

src/pages/workspace/tags/WorkspaceViewTagsPage.tsx

+18-4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import * as PolicyUtils from '@libs/PolicyUtils';
2727
import type {SettingsNavigatorParamList} from '@navigation/types';
2828
import NotFoundPage from '@pages/ErrorPage/NotFoundPage';
2929
import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
30+
import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOptionRow';
3031
import * as Tag from '@userActions/Policy/Tag';
3132
import CONST from '@src/CONST';
3233
import ONYXKEYS from '@src/ONYXKEYS';
@@ -65,10 +66,9 @@ function WorkspaceViewTagsPage({route}: WorkspaceViewTagsProps) {
6566
setSelectedTags({});
6667
}, [isFocused]);
6768

68-
const policyTagList = useMemo(() => PolicyUtils.getTagLists(policyTags).find((policyTag) => policyTag.name === currentTagListName), [currentTagListName, policyTags]);
6969
const tagList = useMemo<TagListItem[]>(
7070
() =>
71-
Object.values(policyTagList?.tags ?? {})
71+
Object.values(currentPolicyTag?.tags ?? {})
7272
.sort((tagA, tagB) => localeCompare(tagA.name, tagB.name))
7373
.map((tag) => ({
7474
value: tag.name,
@@ -81,7 +81,7 @@ function WorkspaceViewTagsPage({route}: WorkspaceViewTagsProps) {
8181
isDisabled: tag.pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE,
8282
rightElement: <ListItemRightCaretWithLabel labelText={tag.enabled ? translate('workspace.common.enabled') : translate('workspace.common.disabled')} />,
8383
})),
84-
[policyTagList, selectedTags, translate],
84+
[currentPolicyTag, selectedTags, translate],
8585
);
8686

8787
const tagListKeyedByName = useMemo(
@@ -234,6 +234,18 @@ function WorkspaceViewTagsPage({route}: WorkspaceViewTagsProps) {
234234
cancelText={translate('common.cancel')}
235235
danger
236236
/>
237+
<View style={[styles.pv4, styles.ph5]}>
238+
<ToggleSettingOptionRow
239+
title={translate('common.required')}
240+
switchAccessibilityLabel={translate('common.required')}
241+
isActive={!!currentPolicyTag?.required}
242+
onToggle={(on) => Tag.setPolicyTagsRequired(policyID, on, route.params.orderWeight)}
243+
pendingAction={currentPolicyTag.pendingFields?.required}
244+
errors={currentPolicyTag?.errorFields?.required ?? undefined}
245+
onCloseError={() => Tag.clearPolicyTagListError(policyID, route.params.orderWeight, 'required')}
246+
disabled={!currentPolicyTag?.required && !Object.values(currentPolicyTag?.tags ?? {}).some((tag) => tag.enabled)}
247+
/>
248+
</View>
237249
<OfflineWithFeedback
238250
errors={currentPolicyTag.errors}
239251
pendingAction={currentPolicyTag.pendingAction}
@@ -264,7 +276,9 @@ function WorkspaceViewTagsPage({route}: WorkspaceViewTagsProps) {
264276
customListHeader={getCustomListHeader()}
265277
shouldPreventDefaultFocusOnSelectRow={!DeviceCapabilities.canUseTouchScreen()}
266278
listHeaderWrapperStyle={[styles.ph9, styles.pv3, styles.pb5]}
267-
onDismissError={(item) => Tag.clearPolicyTagErrors(policyID, item.value)}
279+
onDismissError={(item) => {
280+
Tag.clearPolicyTagErrors(policyID, item.value, route.params.orderWeight);
281+
}}
268282
/>
269283
)}
270284
</ScreenWrapper>

src/types/onyx/PolicyTag.ts

+3
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ type PolicyTagList<T extends string = string> = Record<
5555

5656
/** A list of errors keyed by microtime */
5757
errors?: OnyxCommon.Errors;
58+
59+
/** Error objects keyed by field name containing errors keyed by microtime */
60+
errorFields?: OnyxCommon.ErrorFields;
5861
}>
5962
>;
6063

0 commit comments

Comments
 (0)