From ef9328e2e9e9154a39d22b6b1f0a9324d7fa9da6 Mon Sep 17 00:00:00 2001 From: Etienne <45695613+etiennejouan@users.noreply.github.com> Date: Thu, 20 Feb 2025 09:17:52 +0100 Subject: [PATCH] add not found redirection logic if object in url param not exists (#10339) closes #10150 --- ...sePageChangeEffectNavigateLocation.test.ts | 35 +++++++++++++++++-- .../usePageChangeEffectNavigateLocation.ts | 17 +++++++++ .../app/components/AppRouterProviders.tsx | 2 +- 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts b/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts index 89fa8ac42..b76e2d51a 100644 --- a/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts +++ b/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts @@ -3,6 +3,8 @@ import { useDefaultHomePagePath } from '@/navigation/hooks/useDefaultHomePagePat import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus'; import { AppPath } from '@/types/AppPath'; import { useIsWorkspaceActivationStatusSuspended } from '@/workspace/hooks/useIsWorkspaceActivationStatusSuspended'; +import { useParams } from 'react-router-dom'; +import { useRecoilValue } from 'recoil'; import { OnboardingStatus } from '~/generated/graphql'; @@ -47,8 +49,30 @@ jest.mocked(useDefaultHomePagePath).mockReturnValue({ defaultHomePagePath, }); +jest.mock('react-router-dom'); +const setupMockUseParams = (objectNamePlural?: string) => { + jest + .mocked(useParams) + .mockReturnValueOnce({ objectNamePlural: objectNamePlural ?? '' }); +}; + +jest.mock('recoil'); +const setupMockRecoil = (objectNamePlural?: string) => { + jest + .mocked(useRecoilValue) + .mockReturnValueOnce([{ namePlural: objectNamePlural ?? '' }]); +}; + // prettier-ignore -const testCases = [ +const testCases: { + loc: AppPath; + isLoggedIn: boolean; + isWorkspaceSuspended: boolean; + onboardingStatus: OnboardingStatus | undefined; + res: string | undefined; + objectNamePluralFromParams?: string; + objectNamePluralFromMetadata?: string; +}[] = [ { loc: AppPath.Verify, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PLAN_REQUIRED, res: AppPath.PlanRequired }, { loc: AppPath.Verify, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.COMPLETED, res: '/settings/billing' }, { loc: AppPath.Verify, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: undefined }, @@ -183,6 +207,8 @@ const testCases = [ { loc: AppPath.RecordIndexPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SYNC_EMAIL, res: AppPath.SyncEmails }, { loc: AppPath.RecordIndexPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.INVITE_TEAM, res: AppPath.InviteTeam }, { loc: AppPath.RecordIndexPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.COMPLETED, res: undefined }, + { loc: AppPath.RecordIndexPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.COMPLETED, res: undefined, objectNamePluralFromParams: 'existing-object', objectNamePluralFromMetadata: 'existing-object' }, + { loc: AppPath.RecordIndexPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.COMPLETED, res: AppPath.NotFound, objectNamePluralFromParams: 'non-existing-object', objectNamePluralFromMetadata: 'existing-object' }, { loc: AppPath.RecordShowPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PLAN_REQUIRED, res: AppPath.PlanRequired }, { loc: AppPath.RecordShowPage, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.COMPLETED, res: '/settings/billing' }, @@ -248,6 +274,9 @@ describe('usePageChangeEffectNavigateLocation', () => { testCase.isWorkspaceSuspended, ); setupMockIsLogged(testCase.isLoggedIn); + setupMockUseParams(testCase.objectNamePluralFromParams); + setupMockRecoil(testCase.objectNamePluralFromMetadata); + expect(usePageChangeEffectNavigateLocation()).toEqual(testCase.res); }); }); @@ -257,7 +286,9 @@ describe('usePageChangeEffectNavigateLocation', () => { expect(testCases.length).toEqual( (Object.keys(AppPath).length - UNTESTED_APP_PATHS.length) * (Object.keys(OnboardingStatus).length + - ['isWorkspaceSuspended:true', 'isWorkspaceSuspended:false'].length), + ['isWorkspaceSuspended:true', 'isWorkspaceSuspended:false'] + .length) + + ['nonExistingObjectInParam', 'existingObjectInParam:false'].length, ); }); }); diff --git a/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts b/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts index 76858a93e..0ebfd2f2c 100644 --- a/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts +++ b/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts @@ -1,9 +1,13 @@ import { useIsLogged } from '@/auth/hooks/useIsLogged'; import { useDefaultHomePagePath } from '@/navigation/hooks/useDefaultHomePagePath'; +import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus'; import { AppPath } from '@/types/AppPath'; import { SettingsPath } from '@/types/SettingsPath'; import { useIsWorkspaceActivationStatusSuspended } from '@/workspace/hooks/useIsWorkspaceActivationStatusSuspended'; +import { useParams } from 'react-router-dom'; +import { useRecoilValue } from 'recoil'; +import { isDefined } from 'twenty-shared'; import { OnboardingStatus } from '~/generated/graphql'; import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation'; @@ -33,6 +37,12 @@ export const usePageChangeEffectNavigateLocation = () => { isMatchingLocation(AppPath.PlanRequired) || isMatchingLocation(AppPath.PlanRequiredSuccess); + const objectNamePlural = useParams().objectNamePlural ?? ''; + const objectMetadataItems = useRecoilValue(objectMetadataItemsState); + const objectMetadataItem = objectMetadataItems.find( + (objectMetadataItem) => objectMetadataItem.namePlural === objectNamePlural, + ); + if (isMatchingOpenRoute) { return; } @@ -96,5 +106,12 @@ export const usePageChangeEffectNavigateLocation = () => { return defaultHomePagePath; } + if ( + isMatchingLocation(AppPath.RecordIndexPage) && + !isDefined(objectMetadataItem) + ) { + return AppPath.NotFound; + } + return; }; diff --git a/packages/twenty-front/src/modules/app/components/AppRouterProviders.tsx b/packages/twenty-front/src/modules/app/components/AppRouterProviders.tsx index f5c45a946..39c0deab2 100644 --- a/packages/twenty-front/src/modules/app/components/AppRouterProviders.tsx +++ b/packages/twenty-front/src/modules/app/components/AppRouterProviders.tsx @@ -61,8 +61,8 @@ export const AppRouterProviders = () => { + -