From 9a9d55d09ff9b283ed9abb9947efcdd84164a62d Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Wed, 13 Mar 2024 17:21:44 +0100 Subject: [PATCH 01/10] initial edit page --- src/ONYXKEYS.ts | 6 +- src/ROUTES.ts | 4 + src/SCREENS.ts | 1 + src/languages/en.ts | 1 + src/languages/es.ts | 1 + .../API/parameters/RenamePolicyTagsParams.ts | 10 ++ src/libs/API/parameters/index.ts | 1 + src/libs/API/types.ts | 2 + .../AppNavigator/ModalStackNavigators.tsx | 1 + src/libs/Navigation/linkingConfig/config.ts | 6 + src/libs/Navigation/types.ts | 4 + src/libs/actions/Policy.ts | 71 +++++++++++ .../workspace/tags/WorkspaceCreateTagPage.tsx | 2 +- .../workspace/tags/WorkspaceEditTagPage.tsx | 114 ++++++++++++++++++ ...ceTagCreateForm.ts => WorkspaceTagForm.ts} | 4 +- src/types/form/index.ts | 2 +- 16 files changed, 223 insertions(+), 7 deletions(-) create mode 100644 src/libs/API/parameters/RenamePolicyTagsParams.ts create mode 100644 src/pages/workspace/tags/WorkspaceEditTagPage.tsx rename src/types/form/{WorkspaceTagCreateForm.ts => WorkspaceTagForm.ts} (78%) diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 8c48cbad561f..5393519f693d 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -332,8 +332,8 @@ const ONYXKEYS = { WORKSPACE_SETTINGS_FORM: 'workspaceSettingsForm', WORKSPACE_CATEGORY_CREATE_FORM: 'workspaceCategoryCreate', WORKSPACE_CATEGORY_CREATE_FORM_DRAFT: 'workspaceCategoryCreateDraft', - WORKSPACE_TAG_CREATE_FORM: 'workspaceTagCreate', - WORKSPACE_TAG_CREATE_FORM_DRAFT: 'workspaceTagCreateDraft', + WORKSPACE_TAG_FORM: 'workspaceTagForm', + WORKSPACE_TAG_FORM_DRAFT: 'workspaceTagFormDraft', WORKSPACE_SETTINGS_FORM_DRAFT: 'workspaceSettingsFormDraft', WORKSPACE_DESCRIPTION_FORM: 'workspaceDescriptionForm', WORKSPACE_DESCRIPTION_FORM_DRAFT: 'workspaceDescriptionFormDraft', @@ -418,7 +418,7 @@ type OnyxFormValuesMapping = { [ONYXKEYS.FORMS.ADD_DEBIT_CARD_FORM]: FormTypes.AddDebitCardForm; [ONYXKEYS.FORMS.WORKSPACE_SETTINGS_FORM]: FormTypes.WorkspaceSettingsForm; [ONYXKEYS.FORMS.WORKSPACE_CATEGORY_CREATE_FORM]: FormTypes.WorkspaceCategoryCreateForm; - [ONYXKEYS.FORMS.WORKSPACE_TAG_CREATE_FORM]: FormTypes.WorkspaceTagCreateForm; + [ONYXKEYS.FORMS.WORKSPACE_TAG_FORM]: FormTypes.WorkspaceTagForm; [ONYXKEYS.FORMS.WORKSPACE_RATE_AND_UNIT_FORM]: FormTypes.WorkspaceRateAndUnitForm; [ONYXKEYS.FORMS.CLOSE_ACCOUNT_FORM]: FormTypes.CloseAccountForm; [ONYXKEYS.FORMS.PROFILE_SETTINGS_FORM]: FormTypes.ProfileSettingsForm; diff --git a/src/ROUTES.ts b/src/ROUTES.ts index ad2d9c10700b..9f014a47b4c0 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -585,6 +585,10 @@ const ROUTES = { route: 'settings/workspaces/:policyID/tags/edit', getRoute: (policyID: string) => `settings/workspaces/${policyID}/tags/edit` as const, }, + WORKSPACE_TAG_EDIT: { + route: 'workspace/:policyID/tags/:tagName/edit', + getRoute: (policyID: string, tagName: string) => `workspace/${policyID}/tags/${encodeURI(tagName)}/edit` 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 6c742f08bfb7..db62371d4591 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -219,6 +219,7 @@ const SCREENS = { TAGS: 'Workspace_Tags', TAGS_SETTINGS: 'Tags_Settings', TAGS_EDIT: 'Tags_Edit', + TAG_EDIT: 'Tag_Edit', TAXES: 'Workspace_Taxes', TAG_CREATE: 'Tag_Create', CURRENCY: 'Workspace_Profile_Currency', diff --git a/src/languages/en.ts b/src/languages/en.ts index 6ec5983583fc..0054fd8326a4 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1840,6 +1840,7 @@ export default { customTagName: 'Custom tag name', enableTag: 'Enable tag', addTag: 'Add tag', + editTag: 'Edit tag', subtitle: 'Tags add more detailed ways to classify costs.', emptyTags: { title: "You haven't created any tags", diff --git a/src/languages/es.ts b/src/languages/es.ts index c2eb6374affa..b7de31f792ed 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1864,6 +1864,7 @@ export default { customTagName: 'Nombre de etiqueta personalizada', enableTag: 'Habilitar etiqueta', addTag: 'Añadir etiqueta', + editTag: 'Editar etiqueta', subtitle: 'Las etiquetas añaden formas más detalladas de clasificar los costos.', emptyTags: { title: 'No has creado ninguna etiqueta', diff --git a/src/libs/API/parameters/RenamePolicyTagsParams.ts b/src/libs/API/parameters/RenamePolicyTagsParams.ts new file mode 100644 index 000000000000..51686ade1b9e --- /dev/null +++ b/src/libs/API/parameters/RenamePolicyTagsParams.ts @@ -0,0 +1,10 @@ +type RenamePolicyTagsParams = { + policyID: string; + /** + * Stringified JSON object with type of following structure: + * {[oldName: string]: string;} where value is new tag name + */ + tags: string; +}; + +export default RenamePolicyTagsParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index 25c336753203..09f69fe1f535 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -177,3 +177,4 @@ export type {default as OpenPolicyDistanceRatesPageParams} from './OpenPolicyDis export type {default as OpenPolicyTaxesPageParams} from './OpenPolicyTaxesPageParams'; export type {default as OpenPolicyMoreFeaturesPageParams} from './OpenPolicyMoreFeaturesPageParams'; export type {default as CreatePolicyTagsParams} from './CreatePolicyTagsParams'; +export type {default as RenamePolicyTagsParams} from './RenamePolicyTagsParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index 07f1ca09d7c5..f8f128c6d4e8 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -119,6 +119,7 @@ const WRITE_COMMANDS = { SET_WORKSPACE_CATEGORIES_ENABLED: 'SetWorkspaceCategoriesEnabled', CREATE_WORKSPACE_CATEGORIES: 'CreateWorkspaceCategories', CREATE_POLICY_TAG: 'CreatePolicyTag', + RENAME_POLICY_TAG: 'RenamePolicyTag', SET_WORKSPACE_REQUIRES_CATEGORY: 'SetWorkspaceRequiresCategory', DELETE_WORKSPACE_CATEGORIES: 'DeleteWorkspaceCategories', SET_POLICY_REQUIRES_TAG: 'SetPolicyRequiresTag', @@ -287,6 +288,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.RENAME_POLICY_TAG]: Parameters.RenamePolicyTagsParams; [WRITE_COMMANDS.CREATE_TASK]: Parameters.CreateTaskParams; [WRITE_COMMANDS.CANCEL_TASK]: Parameters.CancelTaskParams; [WRITE_COMMANDS.EDIT_TASK_ASSIGNEE]: Parameters.EditTaskAssigneeParams; diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index a4d7593cf750..f8ea306d1cef 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -266,6 +266,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../pages/workspace/tags/WorkspaceTagsSettingsPage').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/WorkspaceEditTagPage').default as React.ComponentType, [SCREENS.REIMBURSEMENT_ACCOUNT]: () => require('../../../pages/ReimbursementAccount/ReimbursementAccountPage').default as React.ComponentType, [SCREENS.GET_ASSISTANCE]: () => require('../../../pages/GetAssistancePage').default as React.ComponentType, [SCREENS.SETTINGS.TWO_FACTOR_AUTH]: () => require('../../../pages/settings/Security/TwoFactorAuth/TwoFactorAuthPage').default as React.ComponentType, diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 04bc53e7b542..aacc759c4bfd 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -292,6 +292,12 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.TAG_CREATE]: { path: ROUTES.WORKSPACE_TAG_CREATE.route, }, + [SCREENS.WORKSPACE.TAG_EDIT]: { + path: ROUTES.WORKSPACE_TAG_EDIT.route, + parse: { + tagName: (tagName: string) => decodeURI(tagName), + }, + }, [SCREENS.REIMBURSEMENT_ACCOUNT]: { path: ROUTES.BANK_ACCOUNT_WITH_STEP_TO_OPEN.route, exact: true, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index da418625ff55..f58d45b76af8 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -185,6 +185,10 @@ type SettingsNavigatorParamList = { policyID: string; tagName: string; }; + [SCREENS.WORKSPACE.TAG_EDIT]: { + policyID: string; + tagName: string; + }; [SCREENS.WORKSPACE.MEMBER_DETAILS]: { policyID: string; accountID: string; diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 2adcfd29e00d..a4aba7e1ad5b 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -2826,6 +2826,76 @@ function createPolicyTag(policyID: string, tagName: string) { API.write(WRITE_COMMANDS.CREATE_POLICY_TAG, parameters, onyxData); } +function renamePolicyTag(policyID: string, policyTag: {oldName: string; newName: string}) { + const tagListName = Object.keys(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})[0]; + const oldTag = allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`]?.[policyTag.oldName] ?? {}; + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, + value: { + [tagListName]: { + tags: { + [policyTag.oldName]: null, + [policyTag.newName]: { + ...oldTag, + name: policyTag.newName, + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, + value: { + [tagListName]: { + tags: { + [policyTag.oldName]: null, + [policyTag.newName]: { + ...oldTag, + name: policyTag.newName, + errors: null, + pendingAction: null, + }, + }, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, + value: { + [tagListName]: { + tags: { + [policyTag.newName]: null, + [policyTag.oldName]: { + ...oldTag, + name: policyTag.oldName, + errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.genericFailureMessage'), + pendingAction: null, + }, + }, + }, + }, + }, + ], + }; + + const parameters = { + policyID, + tags: JSON.stringify({[policyTag.oldName]: policyTag.newName}), + }; + + API.write(WRITE_COMMANDS.RENAME_POLICY_TAG, parameters, onyxData); +} + function setWorkspaceRequiresCategory(policyID: string, requiresCategory: boolean) { const onyxData: OnyxData = { optimisticData: [ @@ -3589,6 +3659,7 @@ export { openPolicyDistanceRatesPage, openPolicyMoreFeaturesPage, createPolicyTag, + renamePolicyTag, clearWorkspaceReimbursementErrors, deleteWorkspaceCategories, }; diff --git a/src/pages/workspace/tags/WorkspaceCreateTagPage.tsx b/src/pages/workspace/tags/WorkspaceCreateTagPage.tsx index 2df7621c17d3..dc2c17427651 100644 --- a/src/pages/workspace/tags/WorkspaceCreateTagPage.tsx +++ b/src/pages/workspace/tags/WorkspaceCreateTagPage.tsx @@ -23,7 +23,7 @@ import * as Policy from '@userActions/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; -import INPUT_IDS from '@src/types/form/WorkspaceTagCreateForm'; +import INPUT_IDS from '@src/types/form/WorkspaceTagForm'; import type {PolicyTagList} from '@src/types/onyx'; type WorkspaceCreateTagPageOnyxProps = { diff --git a/src/pages/workspace/tags/WorkspaceEditTagPage.tsx b/src/pages/workspace/tags/WorkspaceEditTagPage.tsx new file mode 100644 index 000000000000..1ba3f88a3c68 --- /dev/null +++ b/src/pages/workspace/tags/WorkspaceEditTagPage.tsx @@ -0,0 +1,114 @@ +import type {StackScreenProps} from '@react-navigation/stack'; +import React, {useCallback} from 'react'; +import {Keyboard} from 'react-native'; +import {withOnyx} from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; +import FormProvider from '@components/Form/FormProvider'; +import InputWrapper from '@components/Form/InputWrapper'; +import type {FormInputErrors, FormOnyxValues} from '@components/Form/types'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import ScreenWrapper from '@components/ScreenWrapper'; +import TextInput from '@components/TextInput'; +import useAutoFocusInput from '@hooks/useAutoFocusInput'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import * as ErrorUtils from '@libs/ErrorUtils'; +import Navigation from '@libs/Navigation/Navigation'; +import * as PolicyUtils from '@libs/PolicyUtils'; +import * as ValidationUtils from '@libs/ValidationUtils'; +import type {SettingsNavigatorParamList} from '@navigation/types'; +import AdminPolicyAccessOrNotFoundWrapper from '@pages/workspace/AdminPolicyAccessOrNotFoundWrapper'; +import PaidPolicyAccessOrNotFoundWrapper from '@pages/workspace/PaidPolicyAccessOrNotFoundWrapper'; +import * as Policy from '@userActions/Policy'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; +import INPUT_IDS from '@src/types/form/WorkspaceTagForm'; +import type {PolicyTagList} from '@src/types/onyx'; + +type WorkspaceEditTagPageOnyxProps = { + /** All policy tags */ + policyTags: OnyxEntry; +}; + +type EditTagPageProps = WorkspaceEditTagPageOnyxProps & StackScreenProps; + +function EditTagPage({route, policyTags}: EditTagPageProps) { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + const {inputCallbackRef} = useAutoFocusInput(); + + const validate = useCallback( + (values: FormOnyxValues) => { + const errors: FormInputErrors = {}; + const tagName = values.tagName.trim(); + const {tags} = PolicyUtils.getTagList(policyTags, 0); + + if (!ValidationUtils.isRequiredFulfilled(tagName)) { + errors.tagName = 'workspace.tags.tagRequiredError'; + } else if (tags?.[tagName]) { + errors.tagName = 'workspace.tags.existingTagError'; + } else if ([...tagName].length > CONST.TAG_NAME_LIMIT) { + // Uses the spread syntax to count the number of Unicode code points instead of the number of UTF-16 code units. + ErrorUtils.addErrorMessage(errors, 'tagName', ['common.error.characterLimitExceedCounter', {length: [...tagName].length, limit: CONST.TAG_NAME_LIMIT}]); + } + + return errors; + }, + [policyTags], + ); + + const editTag = useCallback( + (values: FormOnyxValues) => { + Policy.renamePolicyTag(route.params.policyID, {oldName: route.params.tagName, newName: values.tagName.trim()}); + Keyboard.dismiss(); + Navigation.goBack(ROUTES.WORKSPACE_TAGS.getRoute(route.params.policyID)); + }, + [route.params.policyID, route.params.tagName], + ); + + return ( + + + + + + + + + + + ); +} + +EditTagPage.displayName = 'EditTagPage'; + +export default withOnyx({ + policyTags: { + key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${route?.params?.policyID}`, + }, +})(EditTagPage); diff --git a/src/types/form/WorkspaceTagCreateForm.ts b/src/types/form/WorkspaceTagForm.ts similarity index 78% rename from src/types/form/WorkspaceTagCreateForm.ts rename to src/types/form/WorkspaceTagForm.ts index 9a9670d84ae8..a6cc4c6c37cd 100644 --- a/src/types/form/WorkspaceTagCreateForm.ts +++ b/src/types/form/WorkspaceTagForm.ts @@ -7,12 +7,12 @@ const INPUT_IDS = { type InputID = ValueOf; -type WorkspaceTagCreateForm = Form< +type WorkspaceTagForm = Form< InputID, { [INPUT_IDS.TAG_NAME]: string; } >; -export type {WorkspaceTagCreateForm}; +export type {WorkspaceTagForm}; export default INPUT_IDS; diff --git a/src/types/form/index.ts b/src/types/form/index.ts index 5a574de3db54..01c682e54e14 100644 --- a/src/types/form/index.ts +++ b/src/types/form/index.ts @@ -39,5 +39,5 @@ export type {WorkspaceSettingsForm} from './WorkspaceSettingsForm'; export type {ReportPhysicalCardForm} from './ReportPhysicalCardForm'; export type {WorkspaceDescriptionForm} from './WorkspaceDescriptionForm'; export type {PolicyTagNameForm} from './PolicyTagNameForm'; -export type {WorkspaceTagCreateForm} from './WorkspaceTagCreateForm'; +export type {WorkspaceTagForm} from './WorkspaceTagForm'; export type {default as Form} from './Form'; From 863fbd3266e1c74c16364da8079a8f6b52e9ba78 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Fri, 15 Mar 2024 17:52:39 +0100 Subject: [PATCH 02/10] fixes --- src/ROUTES.ts | 4 ++-- src/libs/Navigation/linkingConfig/config.ts | 2 +- src/libs/actions/Policy.ts | 5 +---- .../{WorkspaceEditTagPage.tsx => EditTagPage.tsx} | 14 +++++++------- 4 files changed, 11 insertions(+), 14 deletions(-) rename src/pages/workspace/tags/{WorkspaceEditTagPage.tsx => EditTagPage.tsx} (92%) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 9f014a47b4c0..f41201dfea4b 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -586,8 +586,8 @@ const ROUTES = { getRoute: (policyID: string) => `settings/workspaces/${policyID}/tags/edit` as const, }, WORKSPACE_TAG_EDIT: { - route: 'workspace/:policyID/tags/:tagName/edit', - getRoute: (policyID: string, tagName: string) => `workspace/${policyID}/tags/${encodeURI(tagName)}/edit` as const, + route: 'workspace/:policyID/tag/:tagName/edit', + getRoute: (policyID: string, tagName: string) => `workspace/${policyID}/tag/${encodeURIComponent(tagName)}/edit` as const, }, WORKSPACE_TAXES: { route: 'settings/workspaces/:policyID/taxes', diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index aacc759c4bfd..38c7704ddf28 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -295,7 +295,7 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.TAG_EDIT]: { path: ROUTES.WORKSPACE_TAG_EDIT.route, parse: { - tagName: (tagName: string) => decodeURI(tagName), + tagName: (tagName: string) => decodeURIComponent(tagName), }, }, [SCREENS.REIMBURSEMENT_ACCOUNT]: { diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index a4aba7e1ad5b..7ddb4c7a8c12 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -2855,7 +2855,6 @@ function renamePolicyTag(policyID: string, policyTag: {oldName: string; newName: value: { [tagListName]: { tags: { - [policyTag.oldName]: null, [policyTag.newName]: { ...oldTag, name: policyTag.newName, @@ -2877,9 +2876,7 @@ function renamePolicyTag(policyID: string, policyTag: {oldName: string; newName: [policyTag.newName]: null, [policyTag.oldName]: { ...oldTag, - name: policyTag.oldName, - errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.genericFailureMessage'), - pendingAction: null, + errors: ErrorUtils.getMicroSecondOnyxError('workspace.tags.genericFailureMessage'), }, }, }, diff --git a/src/pages/workspace/tags/WorkspaceEditTagPage.tsx b/src/pages/workspace/tags/EditTagPage.tsx similarity index 92% rename from src/pages/workspace/tags/WorkspaceEditTagPage.tsx rename to src/pages/workspace/tags/EditTagPage.tsx index 1ba3f88a3c68..c32f089b53c1 100644 --- a/src/pages/workspace/tags/WorkspaceEditTagPage.tsx +++ b/src/pages/workspace/tags/EditTagPage.tsx @@ -27,12 +27,12 @@ import type SCREENS from '@src/SCREENS'; import INPUT_IDS from '@src/types/form/WorkspaceTagForm'; import type {PolicyTagList} from '@src/types/onyx'; -type WorkspaceEditTagPageOnyxProps = { +type EditTagPageOnyxProps = { /** All policy tags */ policyTags: OnyxEntry; }; -type EditTagPageProps = WorkspaceEditTagPageOnyxProps & StackScreenProps; +type EditTagPageProps = EditTagPageOnyxProps & StackScreenProps; function EditTagPage({route, policyTags}: EditTagPageProps) { const styles = useThemeStyles(); @@ -40,8 +40,8 @@ function EditTagPage({route, policyTags}: EditTagPageProps) { const {inputCallbackRef} = useAutoFocusInput(); const validate = useCallback( - (values: FormOnyxValues) => { - const errors: FormInputErrors = {}; + (values: FormOnyxValues) => { + const errors: FormInputErrors = {}; const tagName = values.tagName.trim(); const {tags} = PolicyUtils.getTagList(policyTags, 0); @@ -60,7 +60,7 @@ function EditTagPage({route, policyTags}: EditTagPageProps) { ); const editTag = useCallback( - (values: FormOnyxValues) => { + (values: FormOnyxValues) => { Policy.renamePolicyTag(route.params.policyID, {oldName: route.params.tagName, newName: values.tagName.trim()}); Keyboard.dismiss(); Navigation.goBack(ROUTES.WORKSPACE_TAGS.getRoute(route.params.policyID)); @@ -82,7 +82,7 @@ function EditTagPage({route, policyTags}: EditTagPageProps) { onBackButtonPress={Navigation.goBack} /> ({ +export default withOnyx({ policyTags: { key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY_TAGS}${route?.params?.policyID}`, }, From 3a46b9a585dd07e56c0688fdc9d72b1b72d98226 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Mon, 18 Mar 2024 15:13:46 +0100 Subject: [PATCH 03/10] Changes for tag edit to work --- src/libs/API/parameters/RenamePolicyTagsParams.ts | 7 ++----- src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx | 2 +- .../linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts | 2 +- src/libs/actions/Policy.ts | 3 ++- src/pages/workspace/tags/EditTagPage.tsx | 8 +++++--- src/pages/workspace/tags/TagSettingsPage.tsx | 8 ++++++++ src/pages/workspace/tags/WorkspaceCreateTagPage.tsx | 8 ++++---- 7 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/libs/API/parameters/RenamePolicyTagsParams.ts b/src/libs/API/parameters/RenamePolicyTagsParams.ts index 51686ade1b9e..bcf38384cf2c 100644 --- a/src/libs/API/parameters/RenamePolicyTagsParams.ts +++ b/src/libs/API/parameters/RenamePolicyTagsParams.ts @@ -1,10 +1,7 @@ type RenamePolicyTagsParams = { policyID: string; - /** - * Stringified JSON object with type of following structure: - * {[oldName: string]: string;} where value is new tag name - */ - tags: string; + oldName: string; + newName: string; }; export default RenamePolicyTagsParams; diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index 43021e112fe9..9523d041988d 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -268,7 +268,7 @@ const SettingsModalStackNavigator = createModalStackNavigator 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.WORKSPACE.TAG_EDIT]: () => require('../../../pages/workspace/tags/WorkspaceEditTagPage').default as React.ComponentType, + [SCREENS.WORKSPACE.TAG_EDIT]: () => require('../../../pages/workspace/tags/EditTagPage').default as React.ComponentType, [SCREENS.REIMBURSEMENT_ACCOUNT]: () => require('../../../pages/ReimbursementAccount/ReimbursementAccountPage').default as React.ComponentType, [SCREENS.GET_ASSISTANCE]: () => require('../../../pages/GetAssistancePage').default as React.ComponentType, [SCREENS.SETTINGS.TWO_FACTOR_AUTH]: () => require('../../../pages/settings/Security/TwoFactorAuth/TwoFactorAuthPage').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 8713cbbcf6ba..8d862439c736 100755 --- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts @@ -11,7 +11,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = { 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.TAG_SETTINGS], + [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.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/actions/Policy.ts b/src/libs/actions/Policy.ts index 2f83b54d73a2..1ddfac93676b 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -3079,7 +3079,8 @@ function renamePolicyTag(policyID: string, policyTag: {oldName: string; newName: const parameters = { policyID, - tags: JSON.stringify({[policyTag.oldName]: policyTag.newName}), + oldName: policyTag.oldName, + newName: policyTag.newName, }; API.write(WRITE_COMMANDS.RENAME_POLICY_TAG, parameters, onyxData); diff --git a/src/pages/workspace/tags/EditTagPage.tsx b/src/pages/workspace/tags/EditTagPage.tsx index c32f089b53c1..cfa60b5e8ff6 100644 --- a/src/pages/workspace/tags/EditTagPage.tsx +++ b/src/pages/workspace/tags/EditTagPage.tsx @@ -38,6 +38,7 @@ function EditTagPage({route, policyTags}: EditTagPageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const {inputCallbackRef} = useAutoFocusInput(); + const currentTagName = route.params.tagName; const validate = useCallback( (values: FormOnyxValues) => { @@ -61,11 +62,11 @@ function EditTagPage({route, policyTags}: EditTagPageProps) { const editTag = useCallback( (values: FormOnyxValues) => { - Policy.renamePolicyTag(route.params.policyID, {oldName: route.params.tagName, newName: values.tagName.trim()}); + Policy.renamePolicyTag(route.params.policyID, {oldName: currentTagName, newName: values.tagName.trim()}); Keyboard.dismiss(); - Navigation.goBack(ROUTES.WORKSPACE_TAGS.getRoute(route.params.policyID)); + Navigation.dismissModal(); }, - [route.params.policyID, route.params.tagName], + [route.params.policyID, currentTagName], ); return ( @@ -92,6 +93,7 @@ function EditTagPage({route, policyTags}: EditTagPageProps) { { + Navigation.navigate(ROUTES.WORKSPACE_TAG_EDIT.getRoute(route.params.policyID, currentPolicyTag.name)); + }; + return ( @@ -75,6 +81,8 @@ function TagSettingsPage({route, policyTags}: TagSettingsPageProps) { diff --git a/src/pages/workspace/tags/WorkspaceCreateTagPage.tsx b/src/pages/workspace/tags/WorkspaceCreateTagPage.tsx index dc2c17427651..04c0cf8038d0 100644 --- a/src/pages/workspace/tags/WorkspaceCreateTagPage.tsx +++ b/src/pages/workspace/tags/WorkspaceCreateTagPage.tsx @@ -39,8 +39,8 @@ function CreateTagPage({route, policyTags}: CreateTagPageProps) { const {inputCallbackRef} = useAutoFocusInput(); const validate = useCallback( - (values: FormOnyxValues) => { - const errors: FormInputErrors = {}; + (values: FormOnyxValues) => { + const errors: FormInputErrors = {}; const tagName = values.tagName.trim(); const {tags} = PolicyUtils.getTagList(policyTags, 0); @@ -59,7 +59,7 @@ function CreateTagPage({route, policyTags}: CreateTagPageProps) { ); const createTag = useCallback( - (values: FormOnyxValues) => { + (values: FormOnyxValues) => { Policy.createPolicyTag(route.params.policyID, values.tagName.trim()); Keyboard.dismiss(); Navigation.goBack(); @@ -81,7 +81,7 @@ function CreateTagPage({route, policyTags}: CreateTagPageProps) { onBackButtonPress={Navigation.goBack} /> Date: Mon, 18 Mar 2024 15:18:52 +0100 Subject: [PATCH 04/10] fix lint --- src/pages/workspace/tags/EditTagPage.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/workspace/tags/EditTagPage.tsx b/src/pages/workspace/tags/EditTagPage.tsx index cfa60b5e8ff6..4842fcb89fd8 100644 --- a/src/pages/workspace/tags/EditTagPage.tsx +++ b/src/pages/workspace/tags/EditTagPage.tsx @@ -22,7 +22,6 @@ import PaidPolicyAccessOrNotFoundWrapper from '@pages/workspace/PaidPolicyAccess import * as Policy from '@userActions/Policy'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; -import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import INPUT_IDS from '@src/types/form/WorkspaceTagForm'; import type {PolicyTagList} from '@src/types/onyx'; From 295259e50f133e0eaf2a9baac0e3e68e2eaa0a03 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Mon, 18 Mar 2024 17:31:17 +0100 Subject: [PATCH 05/10] fixes --- src/ROUTES.ts | 4 ++-- src/pages/workspace/tags/WorkspaceTagsPage.tsx | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 19aa75eec6d1..5eda71fb34d4 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -577,8 +577,8 @@ const ROUTES = { getRoute: (policyID: string) => `settings/workspaces/${policyID}/tags/edit` as const, }, WORKSPACE_TAG_EDIT: { - route: 'workspace/:policyID/tag/:tagName/edit', - getRoute: (policyID: string, tagName: string) => `workspace/${policyID}/tag/${encodeURIComponent(tagName)}/edit` as const, + route: 'settings/workspace/:policyID/tag/:tagName/edit', + getRoute: (policyID: string, tagName: string) => `settings/workspace/${policyID}/tag/${encodeURIComponent(tagName)}/edit` as const, }, WORKSPACE_TAG_SETTINGS: { route: 'settings/workspaces/:policyID/tag/:tagName', diff --git a/src/pages/workspace/tags/WorkspaceTagsPage.tsx b/src/pages/workspace/tags/WorkspaceTagsPage.tsx index 7cf42b17fe5e..02f8d57488d2 100644 --- a/src/pages/workspace/tags/WorkspaceTagsPage.tsx +++ b/src/pages/workspace/tags/WorkspaceTagsPage.tsx @@ -128,6 +128,7 @@ function WorkspaceTagsPage({policyTags, route}: WorkspaceTagsPageProps) { }; const navigateToTagSettings = (tag: PolicyOption) => { + setSelectedTags({}); Navigation.navigate(ROUTES.WORKSPACE_TAG_SETTINGS.getRoute(route.params.policyID, tag.keyForList)); }; From 8a850c657767259fdc9725f518907c7fb19b4618 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Mon, 18 Mar 2024 18:07:16 +0100 Subject: [PATCH 06/10] fixes --- src/libs/actions/Policy.ts | 2 -- src/pages/workspace/tags/EditTagPage.tsx | 3 --- 2 files changed, 5 deletions(-) diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index e1a543c88a72..af66aa8f1b89 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -3048,8 +3048,6 @@ function renamePolicyTag(policyID: string, policyTag: {oldName: string; newName: [tagListName]: { tags: { [policyTag.newName]: { - ...oldTag, - name: policyTag.newName, errors: null, pendingAction: null, }, diff --git a/src/pages/workspace/tags/EditTagPage.tsx b/src/pages/workspace/tags/EditTagPage.tsx index 4842fcb89fd8..bc89db4d3055 100644 --- a/src/pages/workspace/tags/EditTagPage.tsx +++ b/src/pages/workspace/tags/EditTagPage.tsx @@ -49,9 +49,6 @@ function EditTagPage({route, policyTags}: EditTagPageProps) { errors.tagName = 'workspace.tags.tagRequiredError'; } else if (tags?.[tagName]) { errors.tagName = 'workspace.tags.existingTagError'; - } else if ([...tagName].length > CONST.TAG_NAME_LIMIT) { - // Uses the spread syntax to count the number of Unicode code points instead of the number of UTF-16 code units. - ErrorUtils.addErrorMessage(errors, 'tagName', ['common.error.characterLimitExceedCounter', {length: [...tagName].length, limit: CONST.TAG_NAME_LIMIT}]); } return errors; From f042015061c098621784fbb538a8edb1c9de78ac Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Mon, 18 Mar 2024 19:15:35 +0100 Subject: [PATCH 07/10] fixes --- src/pages/workspace/tags/EditTagPage.tsx | 26 ++++++++---------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/src/pages/workspace/tags/EditTagPage.tsx b/src/pages/workspace/tags/EditTagPage.tsx index bc89db4d3055..4fc6e2d64559 100644 --- a/src/pages/workspace/tags/EditTagPage.tsx +++ b/src/pages/workspace/tags/EditTagPage.tsx @@ -12,7 +12,6 @@ import TextInput from '@components/TextInput'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as ErrorUtils from '@libs/ErrorUtils'; import Navigation from '@libs/Navigation/Navigation'; import * as PolicyUtils from '@libs/PolicyUtils'; import * as ValidationUtils from '@libs/ValidationUtils'; @@ -39,22 +38,15 @@ function EditTagPage({route, policyTags}: EditTagPageProps) { const {inputCallbackRef} = useAutoFocusInput(); const currentTagName = route.params.tagName; - const validate = useCallback( - (values: FormOnyxValues) => { - const errors: FormInputErrors = {}; - const tagName = values.tagName.trim(); - const {tags} = PolicyUtils.getTagList(policyTags, 0); - - if (!ValidationUtils.isRequiredFulfilled(tagName)) { - errors.tagName = 'workspace.tags.tagRequiredError'; - } else if (tags?.[tagName]) { - errors.tagName = 'workspace.tags.existingTagError'; - } + const validate = useCallback((values: FormOnyxValues) => { + const errors: FormInputErrors = {}; + const tagName = values.tagName.trim(); + if (!ValidationUtils.isRequiredFulfilled(tagName)) { + errors.tagName = 'workspace.tags.tagRequiredError'; + } - return errors; - }, - [policyTags], - ); + return errors; + }, []); const editTag = useCallback( (values: FormOnyxValues) => { @@ -75,7 +67,7 @@ function EditTagPage({route, policyTags}: EditTagPageProps) { shouldEnableMaxHeight > Date: Mon, 18 Mar 2024 19:54:53 +0100 Subject: [PATCH 08/10] reverted validation, ability to save oldTag --- src/pages/workspace/tags/EditTagPage.tsx | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/pages/workspace/tags/EditTagPage.tsx b/src/pages/workspace/tags/EditTagPage.tsx index 4fc6e2d64559..c22ba9154146 100644 --- a/src/pages/workspace/tags/EditTagPage.tsx +++ b/src/pages/workspace/tags/EditTagPage.tsx @@ -38,15 +38,21 @@ function EditTagPage({route, policyTags}: EditTagPageProps) { const {inputCallbackRef} = useAutoFocusInput(); const currentTagName = route.params.tagName; - const validate = useCallback((values: FormOnyxValues) => { - const errors: FormInputErrors = {}; - const tagName = values.tagName.trim(); - if (!ValidationUtils.isRequiredFulfilled(tagName)) { - errors.tagName = 'workspace.tags.tagRequiredError'; - } + const validate = useCallback( + (values: FormOnyxValues) => { + const errors: FormInputErrors = {}; + const tagName = values.tagName.trim(); + const {tags} = PolicyUtils.getTagList(policyTags, 0); + if (!ValidationUtils.isRequiredFulfilled(tagName)) { + errors.tagName = 'workspace.tags.tagRequiredError'; + } else if (tags?.[tagName] && currentTagName !== tagName) { + errors.tagName = 'workspace.tags.existingTagError'; + } - return errors; - }, []); + return errors; + }, + [currentTagName, policyTags], + ); const editTag = useCallback( (values: FormOnyxValues) => { From af2c1b83006718a94a779651731955f53061d715 Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Tue, 19 Mar 2024 12:51:35 +0100 Subject: [PATCH 09/10] fixed decode bug --- src/ROUTES.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 5eda71fb34d4..82be3886d2c2 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -578,11 +578,11 @@ const ROUTES = { }, WORKSPACE_TAG_EDIT: { route: 'settings/workspace/:policyID/tag/:tagName/edit', - getRoute: (policyID: string, tagName: string) => `settings/workspace/${policyID}/tag/${encodeURIComponent(tagName)}/edit` as const, + getRoute: (policyID: string, tagName: string) => `settings/workspace/${policyID}/tag/${decodeURIComponent(tagName)}/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, + getRoute: (policyID: string, tagName: string) => `settings/workspaces/${policyID}/tag/${decodeURIComponent(tagName)}` as const, }, WORKSPACE_TAXES: { route: 'settings/workspaces/:policyID/taxes', From 24682f5a183ece27aedb296753c52f0acf66b4ad Mon Sep 17 00:00:00 2001 From: Artem Makushov Date: Tue, 19 Mar 2024 16:23:00 +0100 Subject: [PATCH 10/10] fixing decoding and encoding --- src/ROUTES.ts | 4 ++-- src/libs/Navigation/linkingConfig/config.ts | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ROUTES.ts b/src/ROUTES.ts index 82be3886d2c2..5eda71fb34d4 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -578,11 +578,11 @@ const ROUTES = { }, WORKSPACE_TAG_EDIT: { route: 'settings/workspace/:policyID/tag/:tagName/edit', - getRoute: (policyID: string, tagName: string) => `settings/workspace/${policyID}/tag/${decodeURIComponent(tagName)}/edit` as const, + getRoute: (policyID: string, tagName: string) => `settings/workspace/${policyID}/tag/${encodeURIComponent(tagName)}/edit` as const, }, WORKSPACE_TAG_SETTINGS: { route: 'settings/workspaces/:policyID/tag/:tagName', - getRoute: (policyID: string, tagName: string) => `settings/workspaces/${policyID}/tag/${decodeURIComponent(tagName)}` as const, + getRoute: (policyID: string, tagName: string) => `settings/workspaces/${policyID}/tag/${encodeURIComponent(tagName)}` as const, }, WORKSPACE_TAXES: { route: 'settings/workspaces/:policyID/taxes', diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 2f515c5f090a..053975e83f46 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -303,6 +303,9 @@ const config: LinkingOptions['config'] = { }, [SCREENS.WORKSPACE.TAG_EDIT]: { path: ROUTES.WORKSPACE_TAG_EDIT.route, + parse: { + tagName: (tagName: string) => decodeURIComponent(tagName), + }, }, [SCREENS.WORKSPACE.TAG_SETTINGS]: { path: ROUTES.WORKSPACE_TAG_SETTINGS.route,