From de33d178092665eb1b2323acbc5ee800dffcfa92 Mon Sep 17 00:00:00 2001 From: Rajdeep Das Date: Mon, 5 Jan 2026 23:16:22 +0530 Subject: [PATCH] Fix navigation memory after custom object rename (#16918) This PR fixes an issue where exiting Settings after renaming a custom object could redirect to a stale object URL and result in a `404`. When a custom object name was updated, the memorized navigation URL was not kept in sync, causing redirects to use the old object route. The navigation state is now updated only when the memorized URL belongs to the renamed object, ensuring redirects always point to the correct route while preserving unrelated navigation context. Fixes https://github.com/twentyhq/twenty/issues/11291 --------- Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com> --- ...SettingsUpdateDataModelObjectAboutForm.tsx | 22 ++++++++++++++++ ...izedUrlAfterObjectNamePluralChange.util.ts | 25 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 packages/twenty-front/src/modules/settings/data-model/object-details/utils/computeUpdatedNavigationMemorizedUrlAfterObjectNamePluralChange.util.ts diff --git a/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsUpdateDataModelObjectAboutForm.tsx b/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsUpdateDataModelObjectAboutForm.tsx index 485f8f4682..b8e1b07cca 100644 --- a/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsUpdateDataModelObjectAboutForm.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsUpdateDataModelObjectAboutForm.tsx @@ -1,15 +1,18 @@ import { useUpdateOneObjectMetadataItem } from '@/object-metadata/hooks/useUpdateOneObjectMetadataItem'; import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { isObjectMetadataReadOnly } from '@/object-record/read-only/utils/isObjectMetadataReadOnly'; +import { computeUpdatedNavigationMemorizedUrlAfterObjectNamePluralChange } from '@/settings/data-model/object-details/utils/computeUpdatedNavigationMemorizedUrlAfterObjectNamePluralChange.util'; import { SettingsDataModelObjectAboutForm } from '@/settings/data-model/objects/forms/components/SettingsDataModelObjectAboutForm'; import { type SettingsDataModelObjectAboutFormValues, settingsDataModelObjectAboutFormSchema, } from '@/settings/data-model/validation-schemas/settingsDataModelObjectAboutFormSchema'; +import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState'; import { zodResolver } from '@hookform/resolvers/zod'; import { FormProvider, useForm } from 'react-hook-form'; import { useSetRecoilState } from 'recoil'; import { SettingsPath } from 'twenty-shared/types'; +import { isDefined } from 'twenty-shared/utils'; import { useNavigateSettings } from '~/hooks/useNavigateSettings'; import { updatedObjectNamePluralState } from '~/pages/settings/data-model/states/updatedObjectNamePluralState'; @@ -27,6 +30,10 @@ export const SettingsUpdateDataModelObjectAboutForm = ({ const setUpdatedObjectNamePlural = useSetRecoilState( updatedObjectNamePluralState, ); + const setNavigationMemorizedUrl = useSetRecoilState( + navigationMemorizedUrlState, + ); + const { updateOneObjectMetadataItem } = useUpdateOneObjectMetadataItem(); const { description, @@ -95,6 +102,21 @@ export const SettingsUpdateDataModelObjectAboutForm = ({ navigate(SettingsPath.ObjectDetail, { objectNamePlural: objectNamePluralForRedirection, }); + + const updatedObjectNamePlural = + updatedObject?.data?.updateOneObject.namePlural; + + if (!isDefined(updatedObjectNamePlural)) { + return; + } + + setNavigationMemorizedUrl((previousNavigationMemorizedUrl) => + computeUpdatedNavigationMemorizedUrlAfterObjectNamePluralChange( + previousNavigationMemorizedUrl, + objectMetadataItem.namePlural, + updatedObjectNamePlural, + ), + ); }; const updateObjectMetadata = async ( diff --git a/packages/twenty-front/src/modules/settings/data-model/object-details/utils/computeUpdatedNavigationMemorizedUrlAfterObjectNamePluralChange.util.ts b/packages/twenty-front/src/modules/settings/data-model/object-details/utils/computeUpdatedNavigationMemorizedUrlAfterObjectNamePluralChange.util.ts new file mode 100644 index 0000000000..350dbd9e43 --- /dev/null +++ b/packages/twenty-front/src/modules/settings/data-model/object-details/utils/computeUpdatedNavigationMemorizedUrlAfterObjectNamePluralChange.util.ts @@ -0,0 +1,25 @@ +export const computeUpdatedNavigationMemorizedUrlAfterObjectNamePluralChange = ( + navigationMemorizedUrl: string, + previousObjectNamePlural: string, + updatedObjectNamePlural: string, +): string => { + const objectRoutePatternWithPreviousNamePlural = `^/objects/${previousObjectNamePlural}`; + const objectRouteRegexMatchingPreviousNamePlural = new RegExp( + `${objectRoutePatternWithPreviousNamePlural}(/|\\?|$)`, + ); + + const isNavigationMemorizedUrlMatchingPreviousObjectRoute = + objectRouteRegexMatchingPreviousNamePlural.test(navigationMemorizedUrl); + + if (!isNavigationMemorizedUrlMatchingPreviousObjectRoute) { + return navigationMemorizedUrl; + } + + const navigationMemorizedUrlWithUpdatedObjectNamePlural = + navigationMemorizedUrl.replace( + new RegExp(objectRoutePatternWithPreviousNamePlural), + `/objects/${updatedObjectNamePlural}`, + ); + + return navigationMemorizedUrlWithUpdatedObjectNamePlural; +};