diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 8c48cbad561f..820937b1eb89 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -330,8 +330,8 @@ const ONYXKEYS = { ADD_DEBIT_CARD_FORM: 'addDebitCardForm', ADD_DEBIT_CARD_FORM_DRAFT: 'addDebitCardFormDraft', WORKSPACE_SETTINGS_FORM: 'workspaceSettingsForm', - WORKSPACE_CATEGORY_CREATE_FORM: 'workspaceCategoryCreate', - WORKSPACE_CATEGORY_CREATE_FORM_DRAFT: 'workspaceCategoryCreateDraft', + WORKSPACE_CATEGORY_FORM: 'workspaceCategoryForm', + WORKSPACE_CATEGORY_FORM_DRAFT: 'workspaceCategoryFormDraft', WORKSPACE_TAG_CREATE_FORM: 'workspaceTagCreate', WORKSPACE_TAG_CREATE_FORM_DRAFT: 'workspaceTagCreateDraft', WORKSPACE_SETTINGS_FORM_DRAFT: 'workspaceSettingsFormDraft', @@ -417,7 +417,7 @@ type AllOnyxKeys = DeepValueOf; 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_CATEGORY_FORM]: FormTypes.WorkspaceCategoryForm; [ONYXKEYS.FORMS.WORKSPACE_TAG_CREATE_FORM]: FormTypes.WorkspaceTagCreateForm; [ONYXKEYS.FORMS.WORKSPACE_RATE_AND_UNIT_FORM]: FormTypes.WorkspaceRateAndUnitForm; [ONYXKEYS.FORMS.CLOSE_ACCOUNT_FORM]: FormTypes.CloseAccountForm; diff --git a/src/ROUTES.ts b/src/ROUTES.ts index ad2d9c10700b..5739aad8baa6 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -569,6 +569,10 @@ const ROUTES = { route: 'settings/workspaces/:policyID/categories/new', getRoute: (policyID: string) => `settings/workspaces/${policyID}/categories/new` as const, }, + WORKSPACE_CATEGORY_EDIT: { + route: 'workspace/:policyID/categories/:categoryName/edit', + getRoute: (policyID: string, categoryName: string) => `workspace/${policyID}/categories/${encodeURI(categoryName)}/edit` as const, + }, WORKSPACE_TAGS: { route: 'settings/workspaces/:policyID/tags', getRoute: (policyID: string) => `settings/workspaces/${policyID}/tags` as const, diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 6c742f08bfb7..7ccb24aa19e5 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -231,6 +231,7 @@ const SCREENS = { SHARE: 'Workspace_Profile_Share', NAME: 'Workspace_Profile_Name', CATEGORY_CREATE: 'Category_Create', + CATEGORY_EDIT: 'Category_Edit', CATEGORY_SETTINGS: 'Category_Settings', CATEGORIES_SETTINGS: 'Categories_Settings', MORE_FEATURES: 'Workspace_More_Features', diff --git a/src/languages/en.ts b/src/languages/en.ts index 6ec5983583fc..eecd81c54123 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1789,6 +1789,7 @@ export default { }, genericFailureMessage: 'An error occurred while updating the category, please try again.', addCategory: 'Add category', + editCategory: 'Edit category', categoryRequiredError: 'Category name is required.', existingCategoryError: 'A category with this name already exists.', invalidCategoryName: 'Invalid category name.', diff --git a/src/languages/es.ts b/src/languages/es.ts index c2eb6374affa..cd36f9071de6 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1813,6 +1813,7 @@ export default { }, genericFailureMessage: 'Se ha producido un error al intentar eliminar la categoría. Por favor, inténtalo más tarde.', addCategory: 'Añadir categoría', + editCategory: 'Editar categoría', categoryRequiredError: 'Lo nombre de la categoría es obligatorio.', existingCategoryError: 'Ya existe una categoría con este nombre.', invalidCategoryName: 'Lo nombre de la categoría es invalido.', diff --git a/src/libs/API/parameters/RenameWorkspaceCategoriesParams.ts b/src/libs/API/parameters/RenameWorkspaceCategoriesParams.ts new file mode 100644 index 000000000000..4ed07858564f --- /dev/null +++ b/src/libs/API/parameters/RenameWorkspaceCategoriesParams.ts @@ -0,0 +1,10 @@ +type RenameWorkspaceCategoriesParams = { + policyID: string; + /** + * Stringified JSON object with type of following structure: + * {[oldName: string]: string;} where value is new category name + */ + categories: string; +}; + +export default RenameWorkspaceCategoriesParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index 25c336753203..302b980685e1 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -151,6 +151,7 @@ export type {default as UpdateWorkspaceDescriptionParams} from './UpdateWorkspac export type {default as UpdateWorkspaceMembersRoleParams} from './UpdateWorkspaceMembersRoleParams'; export type {default as SetWorkspaceCategoriesEnabledParams} from './SetWorkspaceCategoriesEnabledParams'; export type {default as CreateWorkspaceCategoriesParams} from './CreateWorkspaceCategoriesParams'; +export type {default as RenameWorkspaceCategoriesParams} from './RenameWorkspaceCategoriesParams'; export type {default as SetWorkspaceRequiresCategoryParams} from './SetWorkspaceRequiresCategoryParams'; export type {default as DeleteWorkspaceCategoriesParams} from './DeleteWorkspaceCategoriesParams'; export type {default as SetWorkspaceAutoReportingParams} from './SetWorkspaceAutoReportingParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index 07f1ca09d7c5..50b6206bc341 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -118,6 +118,7 @@ const WRITE_COMMANDS = { CREATE_WORKSPACE_FROM_IOU_PAYMENT: 'CreateWorkspaceFromIOUPayment', SET_WORKSPACE_CATEGORIES_ENABLED: 'SetWorkspaceCategoriesEnabled', CREATE_WORKSPACE_CATEGORIES: 'CreateWorkspaceCategories', + RENAME_WORKSPACE_CATEGORY: 'RenameWorkspaceCategory', CREATE_POLICY_TAG: 'CreatePolicyTag', SET_WORKSPACE_REQUIRES_CATEGORY: 'SetWorkspaceRequiresCategory', DELETE_WORKSPACE_CATEGORIES: 'DeleteWorkspaceCategories', @@ -282,6 +283,7 @@ type WriteCommandParameters = { [WRITE_COMMANDS.CREATE_WORKSPACE_FROM_IOU_PAYMENT]: Parameters.CreateWorkspaceFromIOUPaymentParams; [WRITE_COMMANDS.SET_WORKSPACE_CATEGORIES_ENABLED]: Parameters.SetWorkspaceCategoriesEnabledParams; [WRITE_COMMANDS.CREATE_WORKSPACE_CATEGORIES]: Parameters.CreateWorkspaceCategoriesParams; + [WRITE_COMMANDS.RENAME_WORKSPACE_CATEGORY]: Parameters.RenameWorkspaceCategoriesParams; [WRITE_COMMANDS.SET_WORKSPACE_REQUIRES_CATEGORY]: Parameters.SetWorkspaceRequiresCategoryParams; [WRITE_COMMANDS.DELETE_WORKSPACE_CATEGORIES]: Parameters.DeleteWorkspaceCategoriesParams; [WRITE_COMMANDS.SET_POLICY_REQUIRES_TAG]: Parameters.SetPolicyRequiresTag; diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx index a4d7593cf750..2e55593ddd01 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators.tsx @@ -263,6 +263,7 @@ const SettingsModalStackNavigator = createModalStackNavigator require('../../../pages/workspace/members/WorkspaceMemberDetailsPage').default as React.ComponentType, [SCREENS.WORKSPACE.MEMBER_DETAILS_ROLE_SELECTION]: () => require('../../../pages/workspace/members/WorkspaceMemberDetailsRoleSelectionPage').default as React.ComponentType, [SCREENS.WORKSPACE.CATEGORY_CREATE]: () => require('../../../pages/workspace/categories/CreateCategoryPage').default as React.ComponentType, + [SCREENS.WORKSPACE.CATEGORY_EDIT]: () => require('../../../pages/workspace/categories/EditCategoryPage').default as React.ComponentType, [SCREENS.WORKSPACE.TAGS_SETTINGS]: () => 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, 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 fd108f2c95f3..7561fb44933c 100755 --- a/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts +++ b/src/libs/Navigation/linkingConfig/FULL_SCREEN_TO_RHP_MAPPING.ts @@ -12,7 +12,7 @@ const FULL_SCREEN_TO_RHP_MAPPING: Partial> = { SCREENS.WORKSPACE.WORKFLOWS_PAYER, ], [SCREENS.WORKSPACE.TAGS]: [SCREENS.WORKSPACE.TAGS_SETTINGS, SCREENS.WORKSPACE.TAGS_EDIT, SCREENS.WORKSPACE.TAG_CREATE], - [SCREENS.WORKSPACE.CATEGORIES]: [SCREENS.WORKSPACE.CATEGORY_CREATE, SCREENS.WORKSPACE.CATEGORY_SETTINGS, SCREENS.WORKSPACE.CATEGORIES_SETTINGS], + [SCREENS.WORKSPACE.CATEGORIES]: [SCREENS.WORKSPACE.CATEGORY_CREATE, SCREENS.WORKSPACE.CATEGORY_SETTINGS, SCREENS.WORKSPACE.CATEGORIES_SETTINGS, SCREENS.WORKSPACE.CATEGORY_EDIT], }; export default FULL_SCREEN_TO_RHP_MAPPING; diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 04bc53e7b542..7a86defcda59 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -283,6 +283,12 @@ const config: LinkingOptions['config'] = { [SCREENS.WORKSPACE.CATEGORY_CREATE]: { path: ROUTES.WORKSPACE_CATEGORY_CREATE.route, }, + [SCREENS.WORKSPACE.CATEGORY_EDIT]: { + path: ROUTES.WORKSPACE_CATEGORY_EDIT.route, + parse: { + categoryName: (categoryName: string) => decodeURI(categoryName), + }, + }, [SCREENS.WORKSPACE.TAGS_SETTINGS]: { path: ROUTES.WORKSPACE_TAGS_SETTINGS.route, }, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index da418625ff55..939841279eed 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -168,6 +168,10 @@ type SettingsNavigatorParamList = { [SCREENS.WORKSPACE.CATEGORY_CREATE]: { policyID: string; }; + [SCREENS.WORKSPACE.CATEGORY_EDIT]: { + policyID: string; + categoryName: string; + }; [SCREENS.WORKSPACE.CATEGORY_SETTINGS]: { policyID: string; categoryName: string; diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 2adcfd29e00d..0d7a4f97a9ad 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -2762,6 +2762,67 @@ function createPolicyCategory(policyID: string, categoryName: string) { API.write(WRITE_COMMANDS.CREATE_WORKSPACE_CATEGORIES, parameters, onyxData); } +function renamePolicyCategory(policyID: string, policyCategory: {oldName: string; newName: string}) { + const policyCategoryToUpdate = allPolicyCategories?.[`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`]?.[policyCategory.oldName] ?? {}; + + const onyxData: OnyxData = { + optimisticData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: { + [policyCategory.oldName]: null, + [policyCategory.newName]: { + ...policyCategoryToUpdate, + name: policyCategory.newName, + unencodedName: decodeURIComponent(policyCategory.newName), + pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE, + }, + }, + }, + ], + successData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: { + [policyCategory.oldName]: null, + [policyCategory.newName]: { + ...policyCategoryToUpdate, + name: policyCategory.newName, + unencodedName: decodeURIComponent(policyCategory.newName), + errors: null, + pendingAction: null, + }, + }, + }, + ], + failureData: [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, + value: { + [policyCategory.newName]: null, + [policyCategory.oldName]: { + ...policyCategoryToUpdate, + name: policyCategory.oldName, + unencodedName: decodeURIComponent(policyCategory.oldName), + errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.genericFailureMessage'), + pendingAction: null, + }, + }, + }, + ], + }; + + const parameters = { + policyID, + categories: JSON.stringify({[policyCategory.oldName]: policyCategory.newName}), + }; + + API.write(WRITE_COMMANDS.RENAME_WORKSPACE_CATEGORY, parameters, onyxData); +} + function createPolicyTag(policyID: string, tagName: string) { const tagListName = Object.keys(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})[0]; @@ -3572,6 +3633,7 @@ export { acceptJoinRequest, declineJoinRequest, createPolicyCategory, + renamePolicyCategory, clearCategoryErrors, setWorkspacePayer, clearWorkspacePayerError, diff --git a/src/pages/workspace/categories/CategoryForm.tsx b/src/pages/workspace/categories/CategoryForm.tsx new file mode 100644 index 000000000000..aaf954f64468 --- /dev/null +++ b/src/pages/workspace/categories/CategoryForm.tsx @@ -0,0 +1,91 @@ +import React, {useCallback} from 'react'; +import {Keyboard} from 'react-native'; +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 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 ValidationUtils from '@libs/ValidationUtils'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import INPUT_IDS from '@src/types/form/WorkspaceCategoryForm'; +import type {PolicyCategories} from '@src/types/onyx'; + +type CategoryFormProps = { + /** All policy categories */ + policyCategories: OnyxEntry; + + /** The name of the category */ + categoryName?: string; + + /** Function to call when the form is submitted */ + onSubmit: (values: FormOnyxValues) => void; +}; + +function CategoryForm({onSubmit, policyCategories, categoryName}: CategoryFormProps) { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + const {inputCallbackRef} = useAutoFocusInput(); + + const validate = useCallback( + (values: FormOnyxValues) => { + const errors: FormInputErrors = {}; + const newCategoryName = values.categoryName.trim(); + + if (!ValidationUtils.isRequiredFulfilled(newCategoryName)) { + errors.categoryName = 'workspace.categories.categoryRequiredError'; + } else if (policyCategories?.[newCategoryName]) { + errors.categoryName = 'workspace.categories.existingCategoryError'; + } else if (newCategoryName === CONST.INVALID_CATEGORY_NAME) { + errors.categoryName = 'workspace.categories.invalidCategoryName'; + } else if ([...newCategoryName].length > CONST.CATEGORY_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, 'categoryName', ['common.error.characterLimitExceedCounter', {length: [...newCategoryName].length, limit: CONST.CATEGORY_NAME_LIMIT}]); + } + + return errors; + }, + [policyCategories], + ); + + const submit = useCallback( + (values: FormOnyxValues) => { + onSubmit(values); + Keyboard.dismiss(); + Navigation.dismissModal(); + }, + [onSubmit], + ); + + return ( + + + + ); +} + +CategoryForm.displayName = 'CategoryForm'; + +export default CategoryForm; diff --git a/src/pages/workspace/categories/CategorySettingsPage.tsx b/src/pages/workspace/categories/CategorySettingsPage.tsx index 16f128e5ea1f..8aad48fd94a7 100644 --- a/src/pages/workspace/categories/CategorySettingsPage.tsx +++ b/src/pages/workspace/categories/CategorySettingsPage.tsx @@ -13,12 +13,14 @@ import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import {setWorkspaceCategoryEnabled} from '@libs/actions/Policy'; import * as ErrorUtils from '@libs/ErrorUtils'; +import Navigation from '@libs/Navigation/Navigation'; 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 ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; import type * as OnyxTypes from '@src/types/onyx'; @@ -43,6 +45,10 @@ function CategorySettingsPage({route, policyCategories}: CategorySettingsPagePro setWorkspaceCategoryEnabled(route.params.policyID, {[policyCategory.name]: {name: policyCategory.name, enabled: value}}); }; + const navigateToEditCategory = () => { + Navigation.navigate(ROUTES.WORKSPACE_CATEGORY_EDIT.getRoute(route.params.policyID, policyCategory.name)); + }; + return ( @@ -73,6 +79,7 @@ function CategorySettingsPage({route, policyCategories}: CategorySettingsPagePro diff --git a/src/pages/workspace/categories/CreateCategoryPage.tsx b/src/pages/workspace/categories/CreateCategoryPage.tsx index 8de0e3a07980..80370d2197fa 100644 --- a/src/pages/workspace/categories/CreateCategoryPage.tsx +++ b/src/pages/workspace/categories/CreateCategoryPage.tsx @@ -1,29 +1,21 @@ 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 type {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 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 type SCREENS from '@src/SCREENS'; -import INPUT_IDS from '@src/types/form/WorkspaceCategoryCreateForm'; import type {PolicyCategories} from '@src/types/onyx'; +import CategoryForm from './CategoryForm'; type WorkspaceCreateCategoryPageOnyxProps = { /** All policy categories */ @@ -35,34 +27,10 @@ type CreateCategoryPageProps = WorkspaceCreateCategoryPageOnyxProps & StackScree function CreateCategoryPage({route, policyCategories}: CreateCategoryPageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); - const {inputCallbackRef} = useAutoFocusInput(); - - const validate = useCallback( - (values: FormOnyxValues) => { - const errors: FormInputErrors = {}; - const categoryName = values.categoryName.trim(); - - if (!ValidationUtils.isRequiredFulfilled(categoryName)) { - errors.categoryName = 'workspace.categories.categoryRequiredError'; - } else if (policyCategories?.[categoryName]) { - errors.categoryName = 'workspace.categories.existingCategoryError'; - } else if (categoryName === CONST.INVALID_CATEGORY_NAME) { - errors.categoryName = 'workspace.categories.invalidCategoryName'; - } else if ([...categoryName].length > CONST.CATEGORY_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, 'categoryName', ['common.error.characterLimitExceedCounter', {length: [...categoryName].length, limit: CONST.CATEGORY_NAME_LIMIT}]); - } - - return errors; - }, - [policyCategories], - ); const createCategory = useCallback( - (values: FormOnyxValues) => { + (values: FormOnyxValues) => { Policy.createPolicyCategory(route.params.policyID, values.categoryName.trim()); - Keyboard.dismiss(); - Navigation.goBack(); }, [route.params.policyID], ); @@ -80,24 +48,10 @@ function CreateCategoryPage({route, policyCategories}: CreateCategoryPageProps) title={translate('workspace.categories.addCategory')} onBackButtonPress={Navigation.goBack} /> - - - + policyCategories={policyCategories} + /> diff --git a/src/pages/workspace/categories/EditCategoryPage.tsx b/src/pages/workspace/categories/EditCategoryPage.tsx new file mode 100644 index 000000000000..35a8648f1a18 --- /dev/null +++ b/src/pages/workspace/categories/EditCategoryPage.tsx @@ -0,0 +1,68 @@ +import type {StackScreenProps} from '@react-navigation/stack'; +import React, {useCallback} from 'react'; +import {withOnyx} from 'react-native-onyx'; +import type {OnyxEntry} from 'react-native-onyx'; +import type {FormOnyxValues} from '@components/Form/types'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import ScreenWrapper from '@components/ScreenWrapper'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import Navigation from '@libs/Navigation/Navigation'; +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 ONYXKEYS from '@src/ONYXKEYS'; +import type SCREENS from '@src/SCREENS'; +import type {PolicyCategories} from '@src/types/onyx'; +import CategoryForm from './CategoryForm'; + +type WorkspaceEditCategoryPageOnyxProps = { + /** All policy categories */ + policyCategories: OnyxEntry; +}; + +type EditCategoryPageProps = WorkspaceEditCategoryPageOnyxProps & StackScreenProps; + +function EditCategoryPage({route, policyCategories}: EditCategoryPageProps) { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + + const editCategory = useCallback( + (values: FormOnyxValues) => { + Policy.renamePolicyCategory(route.params.policyID, {oldName: route.params.categoryName, newName: values.categoryName}); + }, + [route.params.categoryName, route.params.policyID], + ); + + return ( + + + + + + + + + ); +} + +EditCategoryPage.displayName = 'EditCategoryPage'; + +export default withOnyx({ + policyCategories: { + key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${route?.params?.policyID}`, + }, +})(EditCategoryPage); diff --git a/src/types/form/WorkspaceCategoryCreateForm.ts b/src/types/form/WorkspaceCategoryForm.ts similarity index 77% rename from src/types/form/WorkspaceCategoryCreateForm.ts rename to src/types/form/WorkspaceCategoryForm.ts index 051bf705fbf8..4f5f9282373c 100644 --- a/src/types/form/WorkspaceCategoryCreateForm.ts +++ b/src/types/form/WorkspaceCategoryForm.ts @@ -7,12 +7,12 @@ const INPUT_IDS = { type InputID = ValueOf; -type WorkspaceCategoryCreateForm = Form< +type WorkspaceCategoryForm = Form< InputID, { [INPUT_IDS.CATEGORY_NAME]: string; } >; -export type {WorkspaceCategoryCreateForm}; +export type {WorkspaceCategoryForm}; export default INPUT_IDS; diff --git a/src/types/form/index.ts b/src/types/form/index.ts index 5a574de3db54..c4c0460b4f44 100644 --- a/src/types/form/index.ts +++ b/src/types/form/index.ts @@ -34,7 +34,7 @@ export type {SettingsStatusSetForm} from './SettingsStatusSetForm'; export type {WaypointForm} from './WaypointForm'; export type {WorkspaceInviteMessageForm} from './WorkspaceInviteMessageForm'; export type {WorkspaceRateAndUnitForm} from './WorkspaceRateAndUnitForm'; -export type {WorkspaceCategoryCreateForm} from './WorkspaceCategoryCreateForm'; +export type {WorkspaceCategoryForm} from './WorkspaceCategoryForm'; export type {WorkspaceSettingsForm} from './WorkspaceSettingsForm'; export type {ReportPhysicalCardForm} from './ReportPhysicalCardForm'; export type {WorkspaceDescriptionForm} from './WorkspaceDescriptionForm'; diff --git a/src/types/onyx/PolicyCategory.ts b/src/types/onyx/PolicyCategory.ts index 5cef49afc9d4..0fd498632dbc 100644 --- a/src/types/onyx/PolicyCategory.ts +++ b/src/types/onyx/PolicyCategory.ts @@ -4,6 +4,9 @@ type PolicyCategory = OnyxCommon.OnyxValueWithOfflineFeedback<{ /** Name of a category */ name: string; + /** Unencoded name of a category */ + unencodedName: string; + /** Flag that determines if a category is active and able to be selected */ enabled: boolean;