From 152902d1beef916ffd92a2d31e966cc03e2c0e55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Malfait?= Date: Sat, 18 Jan 2025 13:58:12 +0100 Subject: [PATCH] New useNavigateApp (#9729) Todo : - replace all instances of useNavigate( - remove getSettingsPagePath - add eslint rule to enfore usage of useNavigateApp instead of useNavigate --- .../src/generated-metadata/graphql.ts | 1 - .../twenty-front/src/generated/graphql.tsx | 3 +- .../hooks/__tests__/useNavigateApp.test.tsx | 72 ++++++++++++++++++ .../__tests__/useNavigateSettings.test.tsx | 74 +++++++++++++++++++ .../src/hooks/useCleanRecoilState.ts | 8 +- .../twenty-front/src/hooks/useNavigateApp.ts | 20 +++++ .../src/hooks/useNavigateSettings.ts | 20 +++++ ...ActiveVersionWorkflowSingleRecordAction.ts | 12 +-- .../useSeeRunsWorkflowSingleRecordAction.ts | 29 ++++---- ...seSeeVersionsWorkflowSingleRecordAction.ts | 33 ++++----- ...eeRunsWorkflowVersionSingleRecordAction.ts | 37 +++++----- ...rsionsWorkflowVersionSingleRecordAction.ts | 33 ++++----- ...DraftWorkflowVersionSingleRecordAction.tsx | 17 ++--- .../src/modules/app/components/AppRouter.tsx | 4 - .../modules/app/components/SettingsRoutes.tsx | 53 ++++--------- .../modules/app/hooks/useCreateAppRouter.tsx | 2 - .../modules/auth/components/VerifyEffect.tsx | 5 +- .../auth/components/VerifyEmailEffect.tsx | 7 +- .../hooks/useWorkspaceFromInviteHash.ts | 5 +- .../billing/hooks/useHandleCheckoutSession.ts | 4 +- .../constants/CommandMenuNavigateCommands.ts | 23 ++++-- .../isLocationMatchingFavorite.test.ts | 10 +-- .../modules/favorites/utils/sortFavorites.ts | 8 +- ...rmationBannerBillingSubscriptionPaused.tsx | 4 +- .../InformationBannerFailPaymentInfo.tsx | 4 +- .../__tests__/useDefaultHomePagePath.test.ts | 2 +- .../hooks/useDefaultHomePagePath.ts | 11 ++- .../hooks/useLastVisitedObjectMetadataItem.ts | 6 +- ...igationDrawerItemForObjectMetadataItem.tsx | 28 +++++-- ...jectOptionsDropdownHiddenFieldsContent.tsx | 4 +- ...tionsDropdownHiddenRecordGroupsContent.tsx | 13 ++-- ...ptionsDropdownRecordGroupFieldsContent.tsx | 4 +- .../hooks/useRecordGroupActions.ts | 13 ++-- .../hooks/useHandleIndexIdentifierClick.ts | 17 +++-- .../hooks/useRecordShowPagePagination.ts | 69 +++++++++++------ .../RecordDetailRelationSection.tsx | 13 +++- .../record-show/utils/buildShowPageURL.ts | 9 --- .../RecordTableEmptyStateRemote.tsx | 7 +- .../RecordTableHeaderPlusButtonContent.tsx | 6 +- .../record-table/utils/buildIndexTableURL.ts | 6 -- ...tingsAccountsConnectedAccountsListCard.tsx | 13 ++-- .../SettingsAccountsRowDropdownMenu.tsx | 9 ++- .../SettingsAccountsSettingsSection.tsx | 8 +- .../SettingsNavigationDrawerItem.tsx | 4 +- .../SettingsNavigationDrawerItems.tsx | 14 +--- ...ngsDataModelNewFieldBreadcrumbDropDown.tsx | 19 ++--- .../SettingsObjectNewFieldSelector.tsx | 12 ++- .../SettingsDataModelOverviewObject.tsx | 6 +- .../SettingsObjectFieldItemTableRow.tsx | 39 ++++++++-- .../components/tabs/ObjectSettings.tsx | 21 ++++-- ...grationDatabaseConnectionShowContainer.tsx | 12 +-- ...IntegrationDatabaseConnectionsListCard.tsx | 18 ++++- ...tegrationEditDatabaseConnectionContent.tsx | 19 +++-- .../hooks/useDatabaseConnection.ts | 9 ++- ...SettingsSSOIdentitiesProvidersListCard.tsx | 12 +-- ...sSSOIdentitiesProvidersListCardWrapper.tsx | 15 ++-- .../SettingsServerlessFunctionsTable.tsx | 4 +- .../SettingsServerlessFunctionsTableEmpty.tsx | 4 +- ...ettingsServerlessFunctionCodeEditorTab.tsx | 7 +- ...ettingsServerlessFunctionMonitoringTab.tsx | 7 +- .../SettingsServerlessFunctionSettingsTab.tsx | 9 +-- .../SettingsServerlessFunctionTestTab.tsx | 11 ++- .../__tests__/getSettingsPagePath.test.ts | 15 ---- .../settings/utils/getSettingsPagePath.ts | 37 ---------- .../src/modules/types/SettingsPath.ts | 13 ++-- .../__stories__/NavigationDrawer.stories.tsx | 16 ++-- .../hooks/useGetAvailableFieldsForKanban.ts | 18 +++-- ...OverrideWorkflowDraftConfirmationModal.tsx | 17 +++-- .../RecordShowPageWorkflowVersionHeader.tsx | 16 ++-- .../twenty-front/src/pages/auth/Authorize.tsx | 7 +- .../src/pages/auth/PasswordReset.tsx | 9 ++- .../src/pages/onboarding/PaymentSuccess.tsx | 11 ++- .../src/pages/settings/Releases.tsx | 4 +- .../src/pages/settings/SettingsBilling.tsx | 4 +- .../src/pages/settings/SettingsProfile.tsx | 4 +- .../src/pages/settings/SettingsWorkspace.tsx | 6 +- .../settings/SettingsWorkspaceMembers.tsx | 4 +- .../__stories__/SettingsBilling.stories.tsx | 4 +- .../settings/accounts/SettingsAccounts.tsx | 4 +- .../accounts/SettingsAccountsCalendars.tsx | 6 +- .../accounts/SettingsAccountsEmails.tsx | 6 +- .../settings/accounts/SettingsNewAccount.tsx | 6 +- .../settings/admin-panel/SettingsAdmin.tsx | 6 +- .../crm-migration/SettingsCRMMigration.tsx | 45 ----------- .../settings/data-model/SettingsNewObject.tsx | 16 ++-- .../data-model/SettingsObjectDetailPage.tsx | 15 ++-- .../data-model/SettingsObjectFieldEdit.tsx | 40 +++++++--- .../SettingsObjectNewFieldConfigure.tsx | 30 ++++++-- .../SettingsObjectNewFieldSelect.tsx | 11 ++- .../data-model/SettingsObjectOverview.tsx | 4 +- .../settings/data-model/SettingsObjects.tsx | 13 ++-- .../developers/SettingsDevelopers.tsx | 8 +- .../SettingsDevelopersApiKeysNew.stories.tsx | 4 +- .../SettingsDevelopersApiKeyDetail.tsx | 22 ++++-- .../api-keys/SettingsDevelopersApiKeysNew.tsx | 16 ++-- .../SettingsDevelopersWebhookDetail.tsx | 22 +++--- .../SettingsDevelopersWebhooksNew.tsx | 17 +++-- .../SettingsIntegrationDatabase.tsx | 15 ++-- ...tingsIntegrationEditDatabaseConnection.tsx | 6 +- ...ttingsIntegrationNewDatabaseConnection.tsx | 32 +++++--- ...tingsIntegrationShowDatabaseConnection.tsx | 6 +- .../integrations/SettingsIntegrations.tsx | 4 +- .../SettingsIntegrationDatabase.stories.tsx | 4 +- ...egrationShowDatabaseConnection.stories.tsx | 4 +- .../SettingsIntegrations.stories.tsx | 4 +- .../components/SettingsExperience.tsx | 4 +- .../settings/security/SettingsSecurity.tsx | 4 +- .../SettingsSecuritySSOIdentifyProvider.tsx | 14 ++-- .../SettingsServerlessFunctionDetail.tsx | 6 +- .../SettingsServerlessFunctions.tsx | 6 +- .../SettingsServerlessFunctionsNew.tsx | 22 +++--- .../settings/workspace/SettingsDomain.tsx | 12 +-- .../src/utils/navigation/getAppPath.ts | 30 ++++++++ .../src/utils/navigation/getSettingsPath.ts | 36 +++++++++ .../enums/feature-flag-key.enum.ts | 1 - 115 files changed, 975 insertions(+), 679 deletions(-) create mode 100644 packages/twenty-front/src/hooks/__tests__/useNavigateApp.test.tsx create mode 100644 packages/twenty-front/src/hooks/__tests__/useNavigateSettings.test.tsx create mode 100644 packages/twenty-front/src/hooks/useNavigateApp.ts create mode 100644 packages/twenty-front/src/hooks/useNavigateSettings.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-show/utils/buildShowPageURL.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-table/utils/buildIndexTableURL.ts delete mode 100644 packages/twenty-front/src/modules/settings/utils/__tests__/getSettingsPagePath.test.ts delete mode 100644 packages/twenty-front/src/modules/settings/utils/getSettingsPagePath.ts delete mode 100644 packages/twenty-front/src/pages/settings/crm-migration/SettingsCRMMigration.tsx create mode 100644 packages/twenty-front/src/utils/navigation/getAppPath.ts create mode 100644 packages/twenty-front/src/utils/navigation/getSettingsPath.ts diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index ca749c941..cf98a31ce 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -388,7 +388,6 @@ export enum FeatureFlagKey { IsAnalyticsV2Enabled = 'IsAnalyticsV2Enabled', IsCommandMenuV2Enabled = 'IsCommandMenuV2Enabled', IsCopilotEnabled = 'IsCopilotEnabled', - IsCrmMigrationEnabled = 'IsCrmMigrationEnabled', IsEventObjectEnabled = 'IsEventObjectEnabled', IsFreeAccessEnabled = 'IsFreeAccessEnabled', IsFunctionSettingsEnabled = 'IsFunctionSettingsEnabled', diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index 00c8e56d6..4006fa0eb 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -1,5 +1,5 @@ -import { gql } from '@apollo/client'; import * as Apollo from '@apollo/client'; +import { gql } from '@apollo/client'; export type Maybe = T | null; export type InputMaybe = Maybe; export type Exact = { [K in keyof T]: T[K] }; @@ -321,7 +321,6 @@ export enum FeatureFlagKey { IsAnalyticsV2Enabled = 'IsAnalyticsV2Enabled', IsCommandMenuV2Enabled = 'IsCommandMenuV2Enabled', IsCopilotEnabled = 'IsCopilotEnabled', - IsCrmMigrationEnabled = 'IsCrmMigrationEnabled', IsEventObjectEnabled = 'IsEventObjectEnabled', IsFreeAccessEnabled = 'IsFreeAccessEnabled', IsFunctionSettingsEnabled = 'IsFunctionSettingsEnabled', diff --git a/packages/twenty-front/src/hooks/__tests__/useNavigateApp.test.tsx b/packages/twenty-front/src/hooks/__tests__/useNavigateApp.test.tsx new file mode 100644 index 000000000..3ef38ac74 --- /dev/null +++ b/packages/twenty-front/src/hooks/__tests__/useNavigateApp.test.tsx @@ -0,0 +1,72 @@ +import { renderHook } from '@testing-library/react'; +import { MemoryRouter, useNavigate } from 'react-router-dom'; + +import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { AppPath } from '@/types/AppPath'; +import { useNavigateApp } from '~/hooks/useNavigateApp'; + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useNavigate: jest.fn(), +})); + +const Wrapper = ({ children }: { children: React.ReactNode }) => ( + {children} +); + +describe('useNavigateApp', () => { + const mockNavigate = jest.fn(); + + beforeEach(() => { + jest.clearAllMocks(); + (useNavigate as jest.Mock).mockReturnValue(mockNavigate); + }); + + it('should navigate to the correct path without params', () => { + const { result } = renderHook(() => useNavigateApp(), { + wrapper: Wrapper, + }); + + result.current(AppPath.Index); + + expect(mockNavigate).toHaveBeenCalledWith('/', undefined); + }); + + it('should navigate to the correct path with params', () => { + const { result } = renderHook(() => useNavigateApp(), { + wrapper: Wrapper, + }); + + result.current(AppPath.RecordShowPage, { + objectNameSingular: CoreObjectNameSingular.Company, + objectRecordId: '123', + }); + + expect(mockNavigate).toHaveBeenCalledWith('/object/company/123', undefined); + }); + + it('should navigate with query params', () => { + const { result } = renderHook(() => useNavigateApp(), { + wrapper: Wrapper, + }); + + const queryParams = { viewId: '123', filter: 'test' }; + result.current(AppPath.Index, undefined, queryParams); + + expect(mockNavigate).toHaveBeenCalledWith( + '/?viewId=123&filter=test', + undefined, + ); + }); + + it('should navigate with options', () => { + const { result } = renderHook(() => useNavigateApp(), { + wrapper: Wrapper, + }); + + const options = { replace: true, state: { test: true } }; + result.current(AppPath.Index, undefined, undefined, options); + + expect(mockNavigate).toHaveBeenCalledWith('/', options); + }); +}); diff --git a/packages/twenty-front/src/hooks/__tests__/useNavigateSettings.test.tsx b/packages/twenty-front/src/hooks/__tests__/useNavigateSettings.test.tsx new file mode 100644 index 000000000..24b41e1fb --- /dev/null +++ b/packages/twenty-front/src/hooks/__tests__/useNavigateSettings.test.tsx @@ -0,0 +1,74 @@ +import { renderHook } from '@testing-library/react'; +import { MemoryRouter, useNavigate } from 'react-router-dom'; + +import { SettingsPath } from '@/types/SettingsPath'; +import { useNavigateSettings } from '~/hooks/useNavigateSettings'; + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useNavigate: jest.fn(), +})); + +const Wrapper = ({ children }: { children: React.ReactNode }) => ( + {children} +); + +describe('useNavigateSettings', () => { + const mockNavigate = jest.fn(); + + beforeEach(() => { + jest.clearAllMocks(); + (useNavigate as jest.Mock).mockReturnValue(mockNavigate); + }); + + it('should navigate to the correct settings path without params', () => { + const { result } = renderHook(() => useNavigateSettings(), { + wrapper: Wrapper, + }); + + result.current(SettingsPath.Accounts); + + expect(mockNavigate).toHaveBeenCalledWith('/settings/accounts', undefined); + }); + + it('should navigate to the correct settings path with params', () => { + const { result } = renderHook(() => useNavigateSettings(), { + wrapper: Wrapper, + }); + + result.current(SettingsPath.ObjectFieldEdit, { + objectNamePlural: 'companies', + fieldName: 'name', + }); + + expect(mockNavigate).toHaveBeenCalledWith( + '/settings/objects/companies/name', + undefined, + ); + }); + + it('should navigate with query params', () => { + const { result } = renderHook(() => useNavigateSettings(), { + wrapper: Wrapper, + }); + + const queryParams = { viewId: '123', filter: 'test' }; + result.current(SettingsPath.Accounts, undefined, queryParams); + + expect(mockNavigate).toHaveBeenCalledWith( + '/settings/accounts?viewId=123&filter=test', + undefined, + ); + }); + + it('should navigate with options', () => { + const { result } = renderHook(() => useNavigateSettings(), { + wrapper: Wrapper, + }); + + const options = { replace: true, state: { test: true } }; + result.current(SettingsPath.Accounts, undefined, undefined, options); + + expect(mockNavigate).toHaveBeenCalledWith('/settings/accounts', options); + }); +}); diff --git a/packages/twenty-front/src/hooks/useCleanRecoilState.ts b/packages/twenty-front/src/hooks/useCleanRecoilState.ts index 9da98c006..79e931032 100644 --- a/packages/twenty-front/src/hooks/useCleanRecoilState.ts +++ b/packages/twenty-front/src/hooks/useCleanRecoilState.ts @@ -1,20 +1,16 @@ import { apiKeyTokenState } from '@/settings/developers/states/generatedApiKeyTokenState'; -import { AppPath } from '@/types/AppPath'; import { SettingsPath } from '@/types/SettingsPath'; import { useRecoilValue, useResetRecoilState } from 'recoil'; +import { isDefined } from 'twenty-ui'; import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation'; -import { isDefined } from '~/utils/isDefined'; - export const useCleanRecoilState = () => { const isMatchingLocation = useIsMatchingLocation(); const resetApiKeyToken = useResetRecoilState(apiKeyTokenState); const apiKeyToken = useRecoilValue(apiKeyTokenState); const cleanRecoilState = () => { if ( - !isMatchingLocation( - `${AppPath.Settings}/${AppPath.Developers}/${SettingsPath.DevelopersApiKeyDetail}`, - ) && + !isMatchingLocation(SettingsPath.DevelopersApiKeyDetail) && isDefined(apiKeyToken) ) { resetApiKeyToken(); diff --git a/packages/twenty-front/src/hooks/useNavigateApp.ts b/packages/twenty-front/src/hooks/useNavigateApp.ts new file mode 100644 index 000000000..99c24d5b3 --- /dev/null +++ b/packages/twenty-front/src/hooks/useNavigateApp.ts @@ -0,0 +1,20 @@ +import { AppPath } from '@/types/AppPath'; +import { useNavigate } from 'react-router-dom'; +import { getAppPath } from '~/utils/navigation/getAppPath'; + +export const useNavigateApp = () => { + const navigate = useNavigate(); + + return ( + to: T, + params?: Parameters>[1], + queryParams?: Record, + options?: { + replace?: boolean; + state?: any; + }, + ) => { + const path = getAppPath(to, params, queryParams); + return navigate(path, options); + }; +}; diff --git a/packages/twenty-front/src/hooks/useNavigateSettings.ts b/packages/twenty-front/src/hooks/useNavigateSettings.ts new file mode 100644 index 000000000..1303e9d2d --- /dev/null +++ b/packages/twenty-front/src/hooks/useNavigateSettings.ts @@ -0,0 +1,20 @@ +import { SettingsPath } from '@/types/SettingsPath'; +import { useNavigate } from 'react-router-dom'; +import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; + +export const useNavigateSettings = () => { + const navigate = useNavigate(); + + return ( + to: T, + params?: Parameters>[1], + queryParams?: Record, + options?: { + replace?: boolean; + state?: any; + }, + ) => { + const path = getSettingsPath(to, params, queryParams); + return navigate(path, options); + }; +}; diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeActiveVersionWorkflowSingleRecordAction.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeActiveVersionWorkflowSingleRecordAction.ts index 527df28b4..e5d653c88 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeActiveVersionWorkflowSingleRecordAction.ts +++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeActiveVersionWorkflowSingleRecordAction.ts @@ -1,10 +1,11 @@ import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow'; import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { AppPath } from '@/types/AppPath'; import { useActiveWorkflowVersion } from '@/workflow/hooks/useActiveWorkflowVersion'; import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; -import { useNavigate } from 'react-router-dom'; import { isDefined } from 'twenty-ui'; +import { useNavigateApp } from '~/hooks/useNavigateApp'; export const useSeeActiveVersionWorkflowSingleRecordAction: ActionHookWithoutObjectMetadataItem = () => { @@ -16,7 +17,7 @@ export const useSeeActiveVersionWorkflowSingleRecordAction: ActionHookWithoutObj const workflowActiveVersion = useActiveWorkflowVersion(recordId); - const navigate = useNavigate(); + const navigateApp = useNavigateApp(); const shouldBeRegistered = isDefined(workflowActiveVersion) && isDraft; @@ -25,9 +26,10 @@ export const useSeeActiveVersionWorkflowSingleRecordAction: ActionHookWithoutObj return; } - navigate( - `/object/${CoreObjectNameSingular.WorkflowVersion}/${workflowActiveVersion.id}`, - ); + navigateApp(AppPath.RecordShowPage, { + objectNameSingular: CoreObjectNameSingular.WorkflowVersion, + objectRecordId: workflowActiveVersion.id, + }); }; return { diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeRunsWorkflowSingleRecordAction.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeRunsWorkflowSingleRecordAction.ts index 0b245f7f3..b5933d9a9 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeRunsWorkflowSingleRecordAction.ts +++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeRunsWorkflowSingleRecordAction.ts @@ -1,11 +1,11 @@ import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow'; import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook'; import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural'; +import { AppPath } from '@/types/AppPath'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; -import qs from 'qs'; -import { useNavigate } from 'react-router-dom'; import { isDefined } from 'twenty-ui'; +import { useNavigateApp } from '~/hooks/useNavigateApp'; export const useSeeRunsWorkflowSingleRecordAction: ActionHookWithoutObjectMetadataItem = () => { @@ -13,7 +13,7 @@ export const useSeeRunsWorkflowSingleRecordAction: ActionHookWithoutObjectMetada const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId); - const navigate = useNavigate(); + const navigateApp = useNavigateApp(); const shouldBeRegistered = isDefined(workflowWithCurrentVersion); @@ -22,20 +22,21 @@ export const useSeeRunsWorkflowSingleRecordAction: ActionHookWithoutObjectMetada return; } - const filterQueryParams = { - filter: { - workflow: { - [ViewFilterOperand.Is]: { - selectedRecordIds: [workflowWithCurrentVersion.id], + navigateApp( + AppPath.RecordIndexPage, + { + objectNamePlural: CoreObjectNamePlural.WorkflowRun, + }, + { + filter: { + workflow: { + [ViewFilterOperand.Is]: { + selectedRecordIds: [workflowWithCurrentVersion.id], + }, }, }, }, - }; - const filterLinkHref = `/objects/${CoreObjectNamePlural.WorkflowRun}?${qs.stringify( - filterQueryParams, - )}`; - - navigate(filterLinkHref); + ); }; return { diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeVersionsWorkflowSingleRecordAction.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeVersionsWorkflowSingleRecordAction.ts index 6abcc3993..bb2f70f8b 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeVersionsWorkflowSingleRecordAction.ts +++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-actions/hooks/useSeeVersionsWorkflowSingleRecordAction.ts @@ -1,11 +1,11 @@ import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow'; import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook'; import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural'; +import { AppPath } from '@/types/AppPath'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; -import qs from 'qs'; -import { useNavigate } from 'react-router-dom'; import { isDefined } from 'twenty-ui'; +import { useNavigateApp } from '~/hooks/useNavigateApp'; export const useSeeVersionsWorkflowSingleRecordAction: ActionHookWithoutObjectMetadataItem = () => { @@ -13,29 +13,28 @@ export const useSeeVersionsWorkflowSingleRecordAction: ActionHookWithoutObjectMe const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(recordId); - const navigate = useNavigate(); + const navigateApp = useNavigateApp(); const shouldBeRegistered = isDefined(workflowWithCurrentVersion); const onClick = () => { - if (!shouldBeRegistered) { - return; - } + if (!shouldBeRegistered) return; - const filterQueryParams = { - filter: { - workflow: { - [ViewFilterOperand.Is]: { - selectedRecordIds: [workflowWithCurrentVersion.id], + navigateApp( + AppPath.RecordIndexPage, + { + objectNamePlural: CoreObjectNamePlural.WorkflowVersion, + }, + { + filter: { + workflow: { + [ViewFilterOperand.Is]: { + selectedRecordIds: [workflowWithCurrentVersion.id], + }, }, }, }, - }; - const filterLinkHref = `/objects/${CoreObjectNamePlural.WorkflowVersion}?${qs.stringify( - filterQueryParams, - )}`; - - navigate(filterLinkHref); + ); }; return { diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeRunsWorkflowVersionSingleRecordAction.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeRunsWorkflowVersionSingleRecordAction.ts index ab5e2795d..dc3c0a33b 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeRunsWorkflowVersionSingleRecordAction.ts +++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeRunsWorkflowVersionSingleRecordAction.ts @@ -2,12 +2,12 @@ import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook'; import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; +import { AppPath } from '@/types/AppPath'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; -import qs from 'qs'; -import { useNavigate } from 'react-router-dom'; import { useRecoilValue } from 'recoil'; import { isDefined } from 'twenty-ui'; +import { useNavigateApp } from '~/hooks/useNavigateApp'; export const useSeeRunsWorkflowVersionSingleRecordAction: ActionHookWithoutObjectMetadataItem = () => { @@ -19,32 +19,33 @@ export const useSeeRunsWorkflowVersionSingleRecordAction: ActionHookWithoutObjec workflowVersion?.workflow.id, ); - const navigate = useNavigate(); + const navigateApp = useNavigateApp(); const shouldBeRegistered = isDefined(workflowWithCurrentVersion); const onClick = () => { if (!shouldBeRegistered) return; - const filterQueryParams = { - filter: { - workflow: { - [ViewFilterOperand.Is]: { - selectedRecordIds: [workflowWithCurrentVersion.id], + navigateApp( + AppPath.RecordIndexPage, + { + objectNamePlural: CoreObjectNamePlural.WorkflowRun, + }, + { + filter: { + workflow: { + [ViewFilterOperand.Is]: { + selectedRecordIds: [workflowWithCurrentVersion.id], + }, }, - }, - workflowVersion: { - [ViewFilterOperand.Is]: { - selectedRecordIds: [recordId], + workflowVersion: { + [ViewFilterOperand.Is]: { + selectedRecordIds: [recordId], + }, }, }, }, - }; - const filterLinkHref = `/objects/${CoreObjectNamePlural.WorkflowRun}?${qs.stringify( - filterQueryParams, - )}`; - - navigate(filterLinkHref); + ); }; return { diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeVersionsWorkflowVersionSingleRecordAction.ts b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeVersionsWorkflowVersionSingleRecordAction.ts index 61bb53994..01c6340bd 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeVersionsWorkflowVersionSingleRecordAction.ts +++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useSeeVersionsWorkflowVersionSingleRecordAction.ts @@ -2,12 +2,12 @@ import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook'; import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; +import { AppPath } from '@/types/AppPath'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; -import qs from 'qs'; -import { useNavigate } from 'react-router-dom'; import { useRecoilValue } from 'recoil'; import { isDefined } from 'twenty-ui'; +import { useNavigateApp } from '~/hooks/useNavigateApp'; export const useSeeVersionsWorkflowVersionSingleRecordAction: ActionHookWithoutObjectMetadataItem = () => { @@ -19,29 +19,28 @@ export const useSeeVersionsWorkflowVersionSingleRecordAction: ActionHookWithoutO workflowVersion?.workflowId, ); - const navigate = useNavigate(); + const navigateApp = useNavigateApp(); const shouldBeRegistered = isDefined(workflowWithCurrentVersion); const onClick = () => { - if (!shouldBeRegistered) { - return; - } + if (!shouldBeRegistered) return; - const filterQueryParams = { - filter: { - workflow: { - [ViewFilterOperand.Is]: { - selectedRecordIds: [workflowWithCurrentVersion.id], + navigateApp( + AppPath.RecordIndexPage, + { + objectNamePlural: CoreObjectNamePlural.WorkflowVersion, + }, + { + filter: { + workflow: { + [ViewFilterOperand.Is]: { + selectedRecordIds: [workflowWithCurrentVersion.id], + }, }, }, }, - }; - const filterLinkHref = `/objects/${CoreObjectNamePlural.WorkflowVersion}?${qs.stringify( - filterQueryParams, - )}`; - - navigate(filterLinkHref); + ); }; return { diff --git a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useUseAsDraftWorkflowVersionSingleRecordAction.tsx b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useUseAsDraftWorkflowVersionSingleRecordAction.tsx index ead701182..3f94091f2 100644 --- a/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useUseAsDraftWorkflowVersionSingleRecordAction.tsx +++ b/packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/workflow-version-actions/hooks/useUseAsDraftWorkflowVersionSingleRecordAction.tsx @@ -1,15 +1,15 @@ import { useSelectedRecordIdOrThrow } from '@/action-menu/actions/record-actions/single-record/hooks/useSelectedRecordIdOrThrow'; import { ActionHookWithoutObjectMetadataItem } from '@/action-menu/actions/types/ActionHook'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { buildShowPageURL } from '@/object-record/record-show/utils/buildShowPageURL'; +import { AppPath } from '@/types/AppPath'; import { OverrideWorkflowDraftConfirmationModal } from '@/workflow/components/OverrideWorkflowDraftConfirmationModal'; import { useCreateDraftFromWorkflowVersion } from '@/workflow/hooks/useCreateDraftFromWorkflowVersion'; import { useWorkflowVersion } from '@/workflow/hooks/useWorkflowVersion'; import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; import { openOverrideWorkflowDraftConfirmationModalState } from '@/workflow/states/openOverrideWorkflowDraftConfirmationModalState'; -import { useNavigate } from 'react-router-dom'; import { useSetRecoilState } from 'recoil'; import { isDefined } from 'twenty-ui'; +import { useNavigateApp } from '~/hooks/useNavigateApp'; export const useUseAsDraftWorkflowVersionSingleRecordAction: ActionHookWithoutObjectMetadataItem = () => { @@ -28,7 +28,7 @@ export const useUseAsDraftWorkflowVersionSingleRecordAction: ActionHookWithoutOb openOverrideWorkflowDraftConfirmationModalState, ); - const navigate = useNavigate(); + const navigate = useNavigateApp(); const hasAlreadyDraftVersion = workflow?.versions.some((version) => version.status === 'DRAFT') || false; @@ -48,13 +48,10 @@ export const useUseAsDraftWorkflowVersionSingleRecordAction: ActionHookWithoutOb workflowId: workflowVersion.workflow.id, workflowVersionIdToCopy: workflowVersion.id, }); - - navigate( - buildShowPageURL( - CoreObjectNameSingular.Workflow, - workflowVersion.workflow.id, - ), - ); + navigate(AppPath.RecordShowPage, { + objectNameSingular: CoreObjectNameSingular.Workflow, + objectRecordId: workflowVersion.workflow.id, + }); } }; diff --git a/packages/twenty-front/src/modules/app/components/AppRouter.tsx b/packages/twenty-front/src/modules/app/components/AppRouter.tsx index 7ded521bb..391142831 100644 --- a/packages/twenty-front/src/modules/app/components/AppRouter.tsx +++ b/packages/twenty-front/src/modules/app/components/AppRouter.tsx @@ -11,9 +11,6 @@ export const AppRouter = () => { const isFreeAccessEnabled = useIsFeatureEnabled( FeatureFlagKey.IsFreeAccessEnabled, ); - const isCRMMigrationEnabled = useIsFeatureEnabled( - FeatureFlagKey.IsCrmMigrationEnabled, - ); const isServerlessFunctionSettingsEnabled = useIsFeatureEnabled( FeatureFlagKey.IsFunctionSettingsEnabled, ); @@ -29,7 +26,6 @@ export const AppRouter = () => { @@ -226,14 +225,6 @@ const SettingsObjectFieldEdit = lazy(() => ), ); -const SettingsCRMMigration = lazy(() => - import('~/pages/settings/crm-migration/SettingsCRMMigration').then( - (module) => ({ - default: module.SettingsCRMMigration, - }), - ), -); - const SettingsSecurity = lazy(() => import('~/pages/settings/security/SettingsSecurity').then((module) => ({ default: module.SettingsSecurity, @@ -264,14 +255,12 @@ const SettingsAdminContent = lazy(() => type SettingsRoutesProps = { isBillingEnabled?: boolean; - isCRMMigrationEnabled?: boolean; isServerlessFunctionSettingsEnabled?: boolean; isAdminPageEnabled?: boolean; }; export const SettingsRoutes = ({ isBillingEnabled, - isCRMMigrationEnabled, isServerlessFunctionSettingsEnabled, isAdminPageEnabled, }: SettingsRoutesProps) => ( @@ -310,34 +299,22 @@ export const SettingsRoutes = ({ /> } /> } /> - {isCRMMigrationEnabled && ( - } - /> - )} + - } - /> - } - /> - } - /> - } - /> - - } + path={SettingsPath.DevelopersNewApiKey} + element={} + /> + } + /> + } + /> + } /> {isServerlessFunctionSettingsEnabled && ( <> diff --git a/packages/twenty-front/src/modules/app/hooks/useCreateAppRouter.tsx b/packages/twenty-front/src/modules/app/hooks/useCreateAppRouter.tsx index bae199e35..e98ce7f7d 100644 --- a/packages/twenty-front/src/modules/app/hooks/useCreateAppRouter.tsx +++ b/packages/twenty-front/src/modules/app/hooks/useCreateAppRouter.tsx @@ -28,7 +28,6 @@ import { SyncEmails } from '~/pages/onboarding/SyncEmails'; export const useCreateAppRouter = ( isBillingEnabled?: boolean, - isCRMMigrationEnabled?: boolean, isServerlessFunctionSettingsEnabled?: boolean, isAdminPageEnabled?: boolean, ) => @@ -63,7 +62,6 @@ export const useCreateAppRouter = ( element={ { const [searchParams] = useSearchParams(); @@ -18,7 +19,7 @@ export const VerifyEffect = () => { const { enqueueSnackBar } = useSnackBar(); const isLogged = useIsLogged(); - const navigate = useNavigate(); + const navigate = useNavigateApp(); const { verify } = useAuth(); diff --git a/packages/twenty-front/src/modules/auth/components/VerifyEmailEffect.tsx b/packages/twenty-front/src/modules/auth/components/VerifyEmailEffect.tsx index 96c6c2268..81c0ed330 100644 --- a/packages/twenty-front/src/modules/auth/components/VerifyEmailEffect.tsx +++ b/packages/twenty-front/src/modules/auth/components/VerifyEmailEffect.tsx @@ -5,7 +5,8 @@ import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { useReadCaptchaToken } from '@/captcha/hooks/useReadCaptchaToken'; import { useEffect, useState } from 'react'; -import { useNavigate, useSearchParams } from 'react-router-dom'; +import { useSearchParams } from 'react-router-dom'; +import { useNavigateApp } from '~/hooks/useNavigateApp'; import { EmailVerificationSent } from '../sign-in-up/components/EmailVerificationSent'; export const VerifyEmailEffect = () => { @@ -18,7 +19,7 @@ export const VerifyEmailEffect = () => { const email = searchParams.get('email'); const emailVerificationToken = searchParams.get('emailVerificationToken'); - const navigate = useNavigate(); + const navigate = useNavigateApp(); const { readCaptchaToken } = useReadCaptchaToken(); useEffect(() => { @@ -44,7 +45,7 @@ export const VerifyEmailEffect = () => { variant: SnackBarVariant.Success, }); - navigate(`${AppPath.Verify}?loginToken=${loginToken.token}`); + navigate(AppPath.Verify, undefined, { loginToken: loginToken.token }); } catch (error) { enqueueSnackBar('Email verification failed.', { dedupeKey: 'email-verification-dedupe-key', diff --git a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useWorkspaceFromInviteHash.ts b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useWorkspaceFromInviteHash.ts index 0e238c3ab..e2c7e4b0b 100644 --- a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useWorkspaceFromInviteHash.ts +++ b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useWorkspaceFromInviteHash.ts @@ -1,5 +1,5 @@ import { useState } from 'react'; -import { useNavigate, useParams } from 'react-router-dom'; +import { useParams } from 'react-router-dom'; import { useRecoilValue, useSetRecoilState } from 'recoil'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; @@ -10,11 +10,12 @@ import { isDefaultLayoutAuthModalVisibleState } from '@/ui/layout/states/isDefau import { AppPath } from '@/types/AppPath'; import { useGetWorkspaceFromInviteHashQuery } from '~/generated/graphql'; +import { useNavigateApp } from '~/hooks/useNavigateApp'; import { isDefined } from '~/utils/isDefined'; export const useWorkspaceFromInviteHash = () => { const { enqueueSnackBar } = useSnackBar(); - const navigate = useNavigate(); + const navigate = useNavigateApp(); const workspaceInviteHash = useParams().workspaceInviteHash; const currentWorkspace = useRecoilValue(currentWorkspaceState); const [initiallyLoggedIn] = useState(isDefined(currentWorkspace)); diff --git a/packages/twenty-front/src/modules/billing/hooks/useHandleCheckoutSession.ts b/packages/twenty-front/src/modules/billing/hooks/useHandleCheckoutSession.ts index 788cea521..16056098b 100644 --- a/packages/twenty-front/src/modules/billing/hooks/useHandleCheckoutSession.ts +++ b/packages/twenty-front/src/modules/billing/hooks/useHandleCheckoutSession.ts @@ -1,4 +1,3 @@ -import { AppPath } from '@/types/AppPath'; import { SettingsPath } from '@/types/SettingsPath'; import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; @@ -8,6 +7,7 @@ import { SubscriptionInterval, } from '~/generated-metadata/graphql'; import { useCheckoutSessionMutation } from '~/generated/graphql'; +import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; export const useHandleCheckoutSession = ({ recurringInterval, @@ -29,7 +29,7 @@ export const useHandleCheckoutSession = ({ const { data } = await checkoutSession({ variables: { recurringInterval, - successUrlPath: `${AppPath.Settings}/${SettingsPath.Billing}`, + successUrlPath: getSettingsPath(SettingsPath.Billing), plan, requirePaymentMethod, }, diff --git a/packages/twenty-front/src/modules/command-menu/constants/CommandMenuNavigateCommands.ts b/packages/twenty-front/src/modules/command-menu/constants/CommandMenuNavigateCommands.ts index 6089cdcb7..93ca1f9df 100644 --- a/packages/twenty-front/src/modules/command-menu/constants/CommandMenuNavigateCommands.ts +++ b/packages/twenty-front/src/modules/command-menu/constants/CommandMenuNavigateCommands.ts @@ -6,12 +6,19 @@ import { IconUser, } from 'twenty-ui'; +import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural'; +import { AppPath } from '@/types/AppPath'; +import { SettingsPath } from '@/types/SettingsPath'; +import { getAppPath } from '~/utils/navigation/getAppPath'; +import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; import { Command, CommandType } from '../types/Command'; export const COMMAND_MENU_NAVIGATE_COMMANDS: { [key: string]: Command } = { people: { id: 'go-to-people', - to: '/objects/people', + to: getAppPath(AppPath.RecordIndexPage, { + objectNamePlural: CoreObjectNamePlural.Person, + }), label: 'Go to People', type: CommandType.Navigate, firstHotKey: 'G', @@ -21,7 +28,9 @@ export const COMMAND_MENU_NAVIGATE_COMMANDS: { [key: string]: Command } = { }, companies: { id: 'go-to-companies', - to: '/objects/companies', + to: getAppPath(AppPath.RecordIndexPage, { + objectNamePlural: CoreObjectNamePlural.Company, + }), label: 'Go to Companies', type: CommandType.Navigate, firstHotKey: 'G', @@ -31,7 +40,9 @@ export const COMMAND_MENU_NAVIGATE_COMMANDS: { [key: string]: Command } = { }, opportunities: { id: 'go-to-activities', - to: '/objects/opportunities', + to: getAppPath(AppPath.RecordIndexPage, { + objectNamePlural: CoreObjectNamePlural.Opportunity, + }), label: 'Go to Opportunities', type: CommandType.Navigate, firstHotKey: 'G', @@ -41,7 +52,7 @@ export const COMMAND_MENU_NAVIGATE_COMMANDS: { [key: string]: Command } = { }, settings: { id: 'go-to-settings', - to: '/settings/profile', + to: getSettingsPath(SettingsPath.ProfilePage), label: 'Go to Settings', type: CommandType.Navigate, firstHotKey: 'G', @@ -51,7 +62,9 @@ export const COMMAND_MENU_NAVIGATE_COMMANDS: { [key: string]: Command } = { }, tasks: { id: 'go-to-tasks', - to: '/objects/tasks', + to: getAppPath(AppPath.RecordIndexPage, { + objectNamePlural: CoreObjectNamePlural.Task, + }), label: 'Go to Tasks', type: CommandType.Navigate, firstHotKey: 'G', diff --git a/packages/twenty-front/src/modules/favorites/utils/__tests__/isLocationMatchingFavorite.test.ts b/packages/twenty-front/src/modules/favorites/utils/__tests__/isLocationMatchingFavorite.test.ts index 83ce5a6d7..a5e958dec 100644 --- a/packages/twenty-front/src/modules/favorites/utils/__tests__/isLocationMatchingFavorite.test.ts +++ b/packages/twenty-front/src/modules/favorites/utils/__tests__/isLocationMatchingFavorite.test.ts @@ -3,7 +3,7 @@ import { isLocationMatchingFavorite } from '../isLocationMatchingFavorite'; describe('isLocationMatchingFavorite', () => { it('should return true if favorite link matches current path', () => { const currentPath = '/app/objects/people'; - const currentViewPath = '/app/objects/people?view=123'; + const currentViewPath = '/app/objects/people?viewId=123'; const favorite = { objectNameSingular: 'object', link: '/app/objects/people', @@ -16,7 +16,7 @@ describe('isLocationMatchingFavorite', () => { it('should return true if favorite link matches current view path', () => { const currentPath = '/app/object/company/12'; - const currentViewPath = '/app/object/company/12?view=123'; + const currentViewPath = '/app/object/company/12?viewId=123'; const favorite = { objectNameSingular: 'company', link: '/app/object/company/12', @@ -29,7 +29,7 @@ describe('isLocationMatchingFavorite', () => { it('should return false if favorite link does not match current path', () => { const currentPath = '/app/objects/people'; - const currentViewPath = '/app/objects/people?view=123'; + const currentViewPath = '/app/objects/people?viewId=123'; const favorite = { objectNameSingular: 'object', link: '/app/objects/company', @@ -42,10 +42,10 @@ describe('isLocationMatchingFavorite', () => { it('should return false if favorite link does not match current view path', () => { const currentPath = '/app/objects/companies'; - const currentViewPath = '/app/objects/companies?view=123'; + const currentViewPath = '/app/objects/companies?viewId=123'; const favorite = { objectNameSingular: 'view', - link: '/app/objects/companies/view=246', + link: '/app/objects/companies?viewId=246', }; expect( diff --git a/packages/twenty-front/src/modules/favorites/utils/sortFavorites.ts b/packages/twenty-front/src/modules/favorites/utils/sortFavorites.ts index c4814a0d7..59e79004d 100644 --- a/packages/twenty-front/src/modules/favorites/utils/sortFavorites.ts +++ b/packages/twenty-front/src/modules/favorites/utils/sortFavorites.ts @@ -3,8 +3,10 @@ import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { ObjectRecord } from '@/object-record/types/ObjectRecord'; import { ObjectRecordIdentifier } from '@/object-record/types/ObjectRecordIdentifier'; +import { AppPath } from '@/types/AppPath'; import { View } from '@/views/types/View'; import { isDefined } from 'twenty-ui'; +import { getAppPath } from '~/utils/navigation/getAppPath'; import { getObjectMetadataLabelPluralFromViewId } from './getObjectMetadataLabelPluralFromViewId'; export type ProcessedFavorite = Favorite & { @@ -40,7 +42,11 @@ export const sortFavorites = ( avatarType: 'icon', avatarUrl: '', labelIdentifier: view?.name, - link: `/objects/${labelPlural.toLocaleLowerCase()}${favorite.viewId ? `?view=${favorite.viewId}` : ''}`, + link: getAppPath( + AppPath.RecordIndexPage, + { objectNamePlural: labelPlural.toLowerCase() }, + favorite.viewId ? { viewId: favorite.viewId } : undefined, + ), workspaceMemberId: favorite.workspaceMemberId, favoriteFolderId: favorite.favoriteFolderId, objectNameSingular: 'view', diff --git a/packages/twenty-front/src/modules/information-banner/components/billing/InformationBannerBillingSubscriptionPaused.tsx b/packages/twenty-front/src/modules/information-banner/components/billing/InformationBannerBillingSubscriptionPaused.tsx index 397f00f7e..e554aa114 100644 --- a/packages/twenty-front/src/modules/information-banner/components/billing/InformationBannerBillingSubscriptionPaused.tsx +++ b/packages/twenty-front/src/modules/information-banner/components/billing/InformationBannerBillingSubscriptionPaused.tsx @@ -1,13 +1,13 @@ import { InformationBanner } from '@/information-banner/components/InformationBanner'; -import { AppPath } from '@/types/AppPath'; import { SettingsPath } from '@/types/SettingsPath'; import { isDefined } from 'twenty-ui'; import { useBillingPortalSessionQuery } from '~/generated/graphql'; +import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; export const InformationBannerBillingSubscriptionPaused = () => { const { data, loading } = useBillingPortalSessionQuery({ variables: { - returnUrlPath: `${AppPath.Settings}/${SettingsPath.Billing}`, + returnUrlPath: getSettingsPath(SettingsPath.Billing), }, }); diff --git a/packages/twenty-front/src/modules/information-banner/components/billing/InformationBannerFailPaymentInfo.tsx b/packages/twenty-front/src/modules/information-banner/components/billing/InformationBannerFailPaymentInfo.tsx index d17b2e5d2..0337d1dc0 100644 --- a/packages/twenty-front/src/modules/information-banner/components/billing/InformationBannerFailPaymentInfo.tsx +++ b/packages/twenty-front/src/modules/information-banner/components/billing/InformationBannerFailPaymentInfo.tsx @@ -1,13 +1,13 @@ import { InformationBanner } from '@/information-banner/components/InformationBanner'; -import { AppPath } from '@/types/AppPath'; import { SettingsPath } from '@/types/SettingsPath'; import { isDefined } from 'twenty-ui'; import { useBillingPortalSessionQuery } from '~/generated/graphql'; +import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; export const InformationBannerFailPaymentInfo = () => { const { data, loading } = useBillingPortalSessionQuery({ variables: { - returnUrlPath: `${AppPath.Settings}/${SettingsPath.Billing}`, + returnUrlPath: getSettingsPath(SettingsPath.Billing), }, }); diff --git a/packages/twenty-front/src/modules/navigation/hooks/__tests__/useDefaultHomePagePath.test.ts b/packages/twenty-front/src/modules/navigation/hooks/__tests__/useDefaultHomePagePath.test.ts index a7fb3b4e5..31a7172df 100644 --- a/packages/twenty-front/src/modules/navigation/hooks/__tests__/useDefaultHomePagePath.test.ts +++ b/packages/twenty-front/src/modules/navigation/hooks/__tests__/useDefaultHomePagePath.test.ts @@ -70,7 +70,7 @@ describe('useDefaultHomePagePath', () => { setupMockPrefetchedData('viewId'); const { result } = renderHooks(true); expect(result.current.defaultHomePagePath).toEqual( - '/objects/companies?view=viewId', + '/objects/companies?viewId=viewId', ); }); }); diff --git a/packages/twenty-front/src/modules/navigation/hooks/useDefaultHomePagePath.ts b/packages/twenty-front/src/modules/navigation/hooks/useDefaultHomePagePath.ts index 8f3afb3e1..cb3168ab8 100644 --- a/packages/twenty-front/src/modules/navigation/hooks/useDefaultHomePagePath.ts +++ b/packages/twenty-front/src/modules/navigation/hooks/useDefaultHomePagePath.ts @@ -9,6 +9,7 @@ import { View } from '@/views/types/View'; import { useCallback, useMemo } from 'react'; import { useRecoilValue } from 'recoil'; import { isDefined } from '~/utils/isDefined'; +import { getAppPath } from '~/utils/navigation/getAppPath'; export const useDefaultHomePagePath = () => { const currentUser = useRecoilValue(currentUserState); @@ -79,11 +80,13 @@ export const useDefaultHomePagePath = () => { } const namePlural = defaultObjectPathInfo.objectMetadataItem?.namePlural; - const viewParam = defaultObjectPathInfo.view - ? `?view=${defaultObjectPathInfo.view.id}` - : ''; + const viewId = defaultObjectPathInfo.view?.id; - return `/objects/${namePlural}${viewParam}`; + return getAppPath( + AppPath.RecordIndexPage, + { objectNamePlural: namePlural }, + viewId ? { viewId } : undefined, + ); }, [currentUser, defaultObjectPathInfo]); return { defaultHomePagePath }; diff --git a/packages/twenty-front/src/modules/navigation/hooks/useLastVisitedObjectMetadataItem.ts b/packages/twenty-front/src/modules/navigation/hooks/useLastVisitedObjectMetadataItem.ts index f03d0be3c..fca96e2cb 100644 --- a/packages/twenty-front/src/modules/navigation/hooks/useLastVisitedObjectMetadataItem.ts +++ b/packages/twenty-front/src/modules/navigation/hooks/useLastVisitedObjectMetadataItem.ts @@ -1,11 +1,13 @@ import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; import { lastVisitedObjectMetadataItemIdStateSelector } from '@/navigation/states/selectors/lastVisitedObjectMetadataItemIdStateSelector'; import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; +import { AppPath } from '@/types/AppPath'; import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState'; import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState'; import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; import { isDefined } from 'twenty-ui'; import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; +import { getAppPath } from '~/utils/navigation/getAppPath'; export const useLastVisitedObjectMetadataItem = () => { const currentWorkspace = useRecoilValue(currentWorkspaceState); @@ -44,7 +46,9 @@ export const useLastVisitedObjectMetadataItem = () => { if (isDeactivateDefault) { setLastVisitedObjectMetadataItemId(newFallbackObjectMetadataItem.id); setNavigationMemorizedUrl( - `/objects/${newFallbackObjectMetadataItem.namePlural}`, + getAppPath(AppPath.RecordIndexPage, { + objectNamePlural: newFallbackObjectMetadataItem.namePlural, + }), ); } }; diff --git a/packages/twenty-front/src/modules/object-metadata/components/NavigationDrawerItemForObjectMetadataItem.tsx b/packages/twenty-front/src/modules/object-metadata/components/NavigationDrawerItemForObjectMetadataItem.tsx index 8ebd71f36..1ce5a6598 100644 --- a/packages/twenty-front/src/modules/object-metadata/components/NavigationDrawerItemForObjectMetadataItem.tsx +++ b/packages/twenty-front/src/modules/object-metadata/components/NavigationDrawerItemForObjectMetadataItem.tsx @@ -2,6 +2,7 @@ import { useLastVisitedView } from '@/navigation/hooks/useLastVisitedView'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData'; import { PrefetchKey } from '@/prefetch/types/PrefetchKey'; +import { AppPath } from '@/types/AppPath'; import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem'; import { NavigationDrawerItemsCollapsableContainer } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItemsCollapsableContainer'; import { NavigationDrawerSubItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSubItem'; @@ -10,6 +11,7 @@ import { View } from '@/views/types/View'; import { getObjectMetadataItemViews } from '@/views/utils/getObjectMetadataItemViews'; import { useLocation } from 'react-router-dom'; import { AnimatedExpandableContainer, useIcons } from 'twenty-ui'; +import { getAppPath } from '~/utils/navigation/getAppPath'; export type NavigationDrawerItemForObjectMetadataItemProps = { objectMetadataItem: ObjectMetadataItem; @@ -35,13 +37,23 @@ export const NavigationDrawerItemForObjectMetadataItem = ({ const viewId = lastVisitedViewId ?? objectMetadataViews[0]?.id; - const navigationPath = `/objects/${objectMetadataItem.namePlural}${ - viewId ? `?view=${viewId}` : '' - }`; + const navigationPath = getAppPath( + AppPath.RecordIndexPage, + { objectNamePlural: objectMetadataItem.namePlural }, + viewId ? { viewId } : undefined, + ); const isActive = - currentPath === `/objects/${objectMetadataItem.namePlural}` || - currentPath.includes(`object/${objectMetadataItem.nameSingular}/`); + currentPath === + getAppPath(AppPath.RecordIndexPage, { + objectNamePlural: objectMetadataItem.namePlural, + }) || + currentPath.includes( + getAppPath(AppPath.RecordShowPage, { + objectNameSingular: objectMetadataItem.nameSingular, + objectRecordId: '', + }).slice(0, -1), + ); const shouldSubItemsBeDisplayed = isActive && objectMetadataViews.length > 1; @@ -76,7 +88,11 @@ export const NavigationDrawerItemForObjectMetadataItem = ({ {sortedObjectMetadataViews.map((view, index) => ( { const { @@ -34,7 +34,7 @@ export const ObjectOptionsDropdownHiddenFieldsContent = () => { objectNameSingular: objectMetadataItem.nameSingular, }); - const settingsUrl = getSettingsPagePath(SettingsPath.ObjectDetail, { + const settingsUrl = getSettingsPath(SettingsPath.ObjectDetail, { objectNamePlural, }); diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownHiddenRecordGroupsContent.tsx b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownHiddenRecordGroupsContent.tsx index d535f92e7..ba55065be 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownHiddenRecordGroupsContent.tsx +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownHiddenRecordGroupsContent.tsx @@ -13,7 +13,6 @@ import { RecordGroupsVisibilityDropdownSection } from '@/object-record/record-gr import { useRecordGroupVisibility } from '@/object-record/record-group/hooks/useRecordGroupVisibility'; import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState'; import { hiddenRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/hiddenRecordGroupIdsComponentSelector'; -import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; import { SettingsPath } from '@/types/SettingsPath'; import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; @@ -22,6 +21,7 @@ import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMe import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useLocation } from 'react-router-dom'; import { useSetRecoilState } from 'recoil'; +import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => { const { @@ -51,13 +51,10 @@ export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => { viewType, }); - const viewGroupSettingsUrl = getSettingsPagePath( - SettingsPath.ObjectFieldEdit, - { - objectNamePlural, - fieldName: recordGroupFieldMetadata?.name ?? '', - }, - ); + const viewGroupSettingsUrl = getSettingsPath(SettingsPath.ObjectFieldEdit, { + objectNamePlural, + fieldName: recordGroupFieldMetadata?.name ?? '', + }); const location = useLocation(); const setNavigationMemorizedUrl = useSetRecoilState( diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupFieldsContent.tsx b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupFieldsContent.tsx index 8cb7beb4c..8a57f3a87 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupFieldsContent.tsx +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupFieldsContent.tsx @@ -17,7 +17,6 @@ import { useSearchRecordGroupField } from '@/object-record/object-options-dropdo import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState'; import { hiddenRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/hiddenRecordGroupIdsComponentSelector'; import { useHandleRecordGroupField } from '@/object-record/record-index/hooks/useHandleRecordGroupField'; -import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; import { SettingsPath } from '@/types/SettingsPath'; import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; @@ -29,6 +28,7 @@ import { useLocation } from 'react-router-dom'; import { useSetRecoilState } from 'recoil'; import { FieldMetadataType } from '~/generated-metadata/graphql'; import { isDefined } from '~/utils/isDefined'; +import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; export const ObjectOptionsDropdownRecordGroupFieldsContent = () => { const { getIcon } = useIcons(); @@ -68,7 +68,7 @@ export const ObjectOptionsDropdownRecordGroupFieldsContent = () => { viewBarComponentId: recordIndexId, }); - const newSelectFieldSettingsUrl = getSettingsPagePath( + const newSelectFieldSettingsUrl = getSettingsPath( SettingsPath.ObjectNewFieldConfigure, { objectNamePlural, diff --git a/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroupActions.ts b/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroupActions.ts index 57fed7770..ce8e4659d 100644 --- a/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroupActions.ts +++ b/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroupActions.ts @@ -4,13 +4,15 @@ import { useRecordGroupVisibility } from '@/object-record/record-group/hooks/use import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState'; import { RecordGroupAction } from '@/object-record/record-group/types/RecordGroupActions'; import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext'; +import { SettingsPath } from '@/types/SettingsPath'; import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { ViewType } from '@/views/types/ViewType'; import { useCallback, useContext, useMemo } from 'react'; -import { useLocation, useNavigate } from 'react-router-dom'; +import { useLocation } from 'react-router-dom'; import { useSetRecoilState } from 'recoil'; import { IconEyeOff, IconSettings, isDefined } from 'twenty-ui'; +import { useNavigateSettings } from '~/hooks/useNavigateSettings'; type UseRecordGroupActionsParams = { viewType: ViewType; @@ -19,7 +21,7 @@ type UseRecordGroupActionsParams = { export const useRecordGroupActions = ({ viewType, }: UseRecordGroupActionsParams) => { - const navigate = useNavigate(); + const navigate = useNavigateSettings(); const location = useLocation(); const { objectNameSingular, recordIndexId } = useRecordIndexContextOrThrow(); @@ -53,9 +55,10 @@ export const useRecordGroupActions = ({ throw new Error('recordGroupFieldMetadata is not a non-empty string'); } - const settingsPath = `/settings/objects/${objectMetadataItem.namePlural}/${recordGroupFieldMetadata.name}`; - - navigate(settingsPath); + navigate(SettingsPath.ObjectFieldEdit, { + objectNamePlural: objectMetadataItem.namePlural, + fieldName: recordGroupFieldMetadata.name, + }); }, [ setNavigationMemorizedUrl, location.pathname, diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleIndexIdentifierClick.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleIndexIdentifierClick.ts index cc9f154ba..0cd17e877 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleIndexIdentifierClick.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleIndexIdentifierClick.ts @@ -1,7 +1,8 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; -import { buildShowPageURL } from '@/object-record/record-show/utils/buildShowPageURL'; +import { AppPath } from '@/types/AppPath'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { currentViewIdComponentState } from '@/views/states/currentViewIdComponentState'; +import { getAppPath } from '~/utils/navigation/getAppPath'; export const useHandleIndexIdentifierClick = ({ objectMetadataItem, @@ -16,12 +17,16 @@ export const useHandleIndexIdentifierClick = ({ ); const indexIdentifierUrl = (recordId: string) => { - const showPageURL = buildShowPageURL( - objectMetadataItem.nameSingular, - recordId, - currentViewId, + return getAppPath( + AppPath.RecordShowPage, + { + objectNameSingular: objectMetadataItem.nameSingular, + objectRecordId: recordId, + }, + { + viewId: currentViewId, + }, ); - return showPageURL; }; return { indexIdentifierUrl }; diff --git a/packages/twenty-front/src/modules/object-record/record-show/hooks/useRecordShowPagePagination.ts b/packages/twenty-front/src/modules/object-record/record-show/hooks/useRecordShowPagePagination.ts index 23e1bf847..1c5395f86 100644 --- a/packages/twenty-front/src/modules/object-record/record-show/hooks/useRecordShowPagePagination.ts +++ b/packages/twenty-front/src/modules/object-record/record-show/hooks/useRecordShowPagePagination.ts @@ -1,17 +1,17 @@ import { isNonEmptyString } from '@sniptt/guards'; import { useState } from 'react'; -import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; +import { useParams, useSearchParams } from 'react-router-dom'; import { useSetRecoilState } from 'recoil'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; import { lastShowPageRecordIdState } from '@/object-record/record-field/states/lastShowPageRecordId'; import { useRecordIdsFromFindManyCacheRootQuery } from '@/object-record/record-show/hooks/useRecordIdsFromFindManyCacheRootQuery'; -import { buildShowPageURL } from '@/object-record/record-show/utils/buildShowPageURL'; -import { buildIndexTablePageURL } from '@/object-record/record-table/utils/buildIndexTableURL'; +import { AppPath } from '@/types/AppPath'; import { useQueryVariablesFromActiveFieldsOfViewOrDefaultView } from '@/views/hooks/useQueryVariablesFromActiveFieldsOfViewOrDefaultView'; import { capitalize } from 'twenty-shared'; import { isDefined } from 'twenty-ui'; +import { useNavigateApp } from '~/hooks/useNavigateApp'; export const useRecordShowPagePagination = ( propsObjectNameSingular: string, @@ -22,9 +22,9 @@ export const useRecordShowPagePagination = ( objectRecordId: paramObjectRecordId, } = useParams(); - const navigate = useNavigate(); + const navigate = useNavigateApp(); const [searchParams] = useSearchParams(); - const viewIdQueryParam = searchParams.get('view'); + const viewIdQueryParam = searchParams.get('viewId'); const setLastShowPageRecordId = useSetRecoilState(lastShowPageRecordIdState); @@ -130,22 +130,32 @@ export const useRecordShowPagePagination = ( !isFirstRecord || (isFirstRecord && cacheIsAvailableForNavigation); const navigateToPreviousRecord = () => { - if (isFirstRecord) { + if (isFirstRecord || !recordBefore) { if (cacheIsAvailableForNavigation) { const lastRecordIdFromCache = recordIdsInCache[recordIdsInCache.length - 1]; navigate( - buildShowPageURL( + AppPath.RecordShowPage, + { objectNameSingular, - lastRecordIdFromCache, - viewIdQueryParam, - ), + objectRecordId: lastRecordIdFromCache, + }, + { + viewId: viewIdQueryParam, + }, ); } } else { navigate( - buildShowPageURL(objectNameSingular, recordBefore.id, viewIdQueryParam), + AppPath.RecordShowPage, + { + objectNameSingular, + objectRecordId: recordBefore.id, + }, + { + viewId: viewIdQueryParam, + }, ); } }; @@ -154,34 +164,47 @@ export const useRecordShowPagePagination = ( !isLastRecord || (isLastRecord && cacheIsAvailableForNavigation); const navigateToNextRecord = () => { - if (isLastRecord) { + if (isLastRecord || !recordAfter) { if (cacheIsAvailableForNavigation) { const firstRecordIdFromCache = recordIdsInCache[0]; navigate( - buildShowPageURL( + AppPath.RecordShowPage, + { objectNameSingular, - firstRecordIdFromCache, - viewIdQueryParam, - ), + objectRecordId: firstRecordIdFromCache, + }, + { + viewId: viewIdQueryParam, + }, ); } } else { navigate( - buildShowPageURL(objectNameSingular, recordAfter.id, viewIdQueryParam), + AppPath.RecordShowPage, + { + objectNameSingular, + objectRecordId: recordAfter.id, + }, + { + viewId: viewIdQueryParam, + }, ); } }; const navigateToIndexView = () => { - const indexTableURL = buildIndexTablePageURL( - objectMetadataItem.namePlural, - viewIdQueryParam, - ); - setLastShowPageRecordId(objectRecordId); - navigate(indexTableURL); + navigate( + AppPath.RecordIndexPage, + { + objectNamePlural: objectMetadataItem.namePlural, + }, + { + viewId: viewIdQueryParam, + }, + ); }; const rankInView = recordIdsInCache.findIndex((id) => id === objectRecordId); diff --git a/packages/twenty-front/src/modules/object-record/record-show/record-detail-section/components/RecordDetailRelationSection.tsx b/packages/twenty-front/src/modules/object-record/record-show/record-detail-section/components/RecordDetailRelationSection.tsx index 5da256c54..876034e28 100644 --- a/packages/twenty-front/src/modules/object-record/record-show/record-detail-section/components/RecordDetailRelationSection.tsx +++ b/packages/twenty-front/src/modules/object-record/record-show/record-detail-section/components/RecordDetailRelationSection.tsx @@ -1,5 +1,4 @@ import styled from '@emotion/styled'; -import qs from 'qs'; import { useCallback, useContext } from 'react'; import { useRecoilValue } from 'recoil'; import { IconForbid, IconPencil, IconPlus, LightIconButton } from 'twenty-ui'; @@ -26,6 +25,7 @@ import { RecordForSelect } from '@/object-record/relation-picker/types/RecordFor import { ObjectRecord } from '@/object-record/types/ObjectRecord'; import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData'; import { PrefetchKey } from '@/prefetch/types/PrefetchKey'; +import { AppPath } from '@/types/AppPath'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope'; @@ -33,6 +33,7 @@ import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; import { View } from '@/views/types/View'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; import { RelationDefinitionType } from '~/generated-metadata/graphql'; +import { getAppPath } from '~/utils/navigation/getAppPath'; type RecordDetailRelationSectionProps = { loading: boolean; }; @@ -139,9 +140,13 @@ export const RecordDetailRelationSection = ({ view: indexView?.id, }; - const filterLinkHref = `/objects/${ - relationObjectMetadataItem.namePlural - }?${qs.stringify(filterQueryParams)}`; + const filterLinkHref = getAppPath( + AppPath.RecordIndexPage, + { + objectNamePlural: relationObjectMetadataItem.namePlural, + }, + filterQueryParams, + ); const showContent = () => { return ( diff --git a/packages/twenty-front/src/modules/object-record/record-show/utils/buildShowPageURL.ts b/packages/twenty-front/src/modules/object-record/record-show/utils/buildShowPageURL.ts deleted file mode 100644 index 93d7878c1..000000000 --- a/packages/twenty-front/src/modules/object-record/record-show/utils/buildShowPageURL.ts +++ /dev/null @@ -1,9 +0,0 @@ -export const buildShowPageURL = ( - objectNameSingular: string, - recordId: string, - viewId?: string | null | undefined, -) => { - return `/object/${objectNameSingular}/${recordId}${ - viewId ? `?view=${viewId}` : '' - }`; -}; diff --git a/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateRemote.tsx b/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateRemote.tsx index 5bc98b1ad..f8177e25e 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateRemote.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateRemote.tsx @@ -2,13 +2,14 @@ import { IconSettings } from 'twenty-ui'; import { RecordTableEmptyStateDisplay } from '@/object-record/record-table/empty-state/components/RecordTableEmptyStateDisplay'; -import { useNavigate } from 'react-router-dom'; +import { SettingsPath } from '@/types/SettingsPath'; +import { useNavigateSettings } from '~/hooks/useNavigateSettings'; export const RecordTableEmptyStateRemote = () => { - const navigate = useNavigate(); + const navigate = useNavigateSettings(); const handleButtonClick = () => { - navigate('/settings/integrations'); + navigate(SettingsPath.Integrations); }; return ( diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderPlusButtonContent.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderPlusButtonContent.tsx index 9b364add0..b9a1c6641 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderPlusButtonContent.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/components/RecordTableHeaderPlusButtonContent.tsx @@ -8,11 +8,13 @@ import { useRecordTableContextOrThrow } from '@/object-record/record-table/conte import { useTableColumns } from '@/object-record/record-table/hooks/useTableColumns'; import { hiddenTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/hiddenTableColumnsComponentSelector'; import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition'; +import { SettingsPath } from '@/types/SettingsPath'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; export const RecordTableHeaderPlusButtonContent = () => { const { objectMetadataItem } = useRecordTableContextOrThrow(); @@ -55,7 +57,9 @@ export const RecordTableHeaderPlusButtonContent = () => { { setNavigationMemorizedUrl(location.pathname + location.search); }} diff --git a/packages/twenty-front/src/modules/object-record/record-table/utils/buildIndexTableURL.ts b/packages/twenty-front/src/modules/object-record/record-table/utils/buildIndexTableURL.ts deleted file mode 100644 index 6610eb835..000000000 --- a/packages/twenty-front/src/modules/object-record/record-table/utils/buildIndexTableURL.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const buildIndexTablePageURL = ( - objectNamePlural: string, - viewId?: string | null | undefined, -) => { - return `/objects/${objectNamePlural}${viewId ? `?view=${viewId}` : ''}`; -}; diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsConnectedAccountsListCard.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsConnectedAccountsListCard.tsx index c1f9090ad..405cf68b9 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsConnectedAccountsListCard.tsx +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsConnectedAccountsListCard.tsx @@ -1,16 +1,15 @@ -import { useNavigate } from 'react-router-dom'; import { IconComponent, IconGoogle, IconMicrosoft } from 'twenty-ui'; import { ConnectedAccount } from '@/accounts/types/ConnectedAccount'; import { SettingsAccountsListEmptyStateCard } from '@/settings/accounts/components/SettingsAccountsListEmptyStateCard'; -import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; import { SettingsPath } from '@/types/SettingsPath'; -import { SettingsAccountsConnectedAccountsRowRightContainer } from '@/settings/accounts/components/SettingsAccountsConnectedAccountsRowRightContainer'; -import { SettingsListCard } from '../../components/SettingsListCard'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; +import { SettingsAccountsConnectedAccountsRowRightContainer } from '@/settings/accounts/components/SettingsAccountsConnectedAccountsRowRightContainer'; import { useRecoilValue } from 'recoil'; +import { useNavigateSettings } from '~/hooks/useNavigateSettings'; import { isDefined } from '~/utils/isDefined'; +import { SettingsListCard } from '../../components/SettingsListCard'; const ProviderIcons: { [k: string]: IconComponent } = { google: IconGoogle, @@ -24,7 +23,7 @@ export const SettingsAccountsConnectedAccountsListCard = ({ accounts: ConnectedAccount[]; loading?: boolean; }) => { - const navigate = useNavigate(); + const navigate = useNavigateSettings(); const currentWorkspace = useRecoilValue(currentWorkspaceState); if (!accounts.length) { @@ -47,9 +46,7 @@ export const SettingsAccountsConnectedAccountsListCard = ({ )} hasFooter={atLeastOneProviderAvailable} footerButtonLabel="Add account" - onFooterButtonClick={() => - navigate(getSettingsPagePath(SettingsPath.NewAccount)) - } + onFooterButtonClick={() => navigate(SettingsPath.NewAccount)} /> ); }; diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsRowDropdownMenu.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsRowDropdownMenu.tsx index 55bdd9393..3192bd281 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsRowDropdownMenu.tsx +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsRowDropdownMenu.tsx @@ -1,4 +1,3 @@ -import { useNavigate } from 'react-router-dom'; import { IconCalendarEvent, IconDotsVertical, @@ -13,9 +12,11 @@ import { ConnectedAccount } from '@/accounts/types/ConnectedAccount'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { useDestroyOneRecord } from '@/object-record/hooks/useDestroyOneRecord'; import { useTriggerApisOAuth } from '@/settings/accounts/hooks/useTriggerApiOAuth'; +import { SettingsPath } from '@/types/SettingsPath'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; +import { useNavigateSettings } from '~/hooks/useNavigateSettings'; type SettingsAccountsRowDropdownMenuProps = { account: ConnectedAccount; @@ -26,7 +27,7 @@ export const SettingsAccountsRowDropdownMenu = ({ }: SettingsAccountsRowDropdownMenuProps) => { const dropdownId = `settings-account-row-${account.id}`; - const navigate = useNavigate(); + const navigate = useNavigateSettings(); const { closeDropdown } = useDropdown(dropdownId); const { destroyOneRecord } = useDestroyOneRecord({ @@ -49,7 +50,7 @@ export const SettingsAccountsRowDropdownMenu = ({ LeftIcon={IconMail} text="Emails settings" onClick={() => { - navigate(`/settings/accounts/emails`); + navigate(SettingsPath.AccountsEmails); closeDropdown(); }} /> @@ -57,7 +58,7 @@ export const SettingsAccountsRowDropdownMenu = ({ LeftIcon={IconCalendarEvent} text="Calendar settings" onClick={() => { - navigate(`/settings/accounts/calendars`); + navigate(SettingsPath.AccountsCalendars); closeDropdown(); }} /> diff --git a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsSettingsSection.tsx b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsSettingsSection.tsx index d709d97fc..9610b5061 100644 --- a/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsSettingsSection.tsx +++ b/packages/twenty-front/src/modules/settings/accounts/components/SettingsAccountsSettingsSection.tsx @@ -9,9 +9,9 @@ import { } from 'twenty-ui'; import { SettingsCard } from '@/settings/components/SettingsCard'; -import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; import { SettingsPath } from '@/types/SettingsPath'; import { useTheme } from '@emotion/react'; +import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; const StyledCardsContainer = styled.div` display: flex; @@ -32,7 +32,7 @@ export const SettingsAccountsSettingsSection = () => { description="Configure your emails and calendar settings." /> - + { description="Set email visibility, manage your blocklist and more." /> - + { - const href = getSettingsPagePath(path); + const href = getSettingsPath(path); const pathName = useResolvedPath(href).pathname; const isActive = !!useMatch({ diff --git a/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItems.tsx b/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItems.tsx index 07354f6d8..88536bfae 100644 --- a/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItems.tsx +++ b/packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItems.tsx @@ -24,7 +24,6 @@ import { currentUserState } from '@/auth/states/currentUserState'; import { billingState } from '@/client-config/states/billingState'; import { AdvancedSettingsWrapper } from '@/settings/components/AdvancedSettingsWrapper'; import { SettingsNavigationDrawerItem } from '@/settings/components/SettingsNavigationDrawerItem'; -import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; import { SettingsPath } from '@/types/SettingsPath'; import { NavigationDrawerItem, @@ -37,6 +36,7 @@ import { getNavigationSubItemLeftAdornment } from '@/ui/navigation/navigation-dr import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { matchPath, resolvePath, useLocation } from 'react-router-dom'; import { FeatureFlagKey } from '~/generated/graphql'; +import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; type SettingsNavigationItem = { label: string; @@ -56,9 +56,6 @@ export const SettingsNavigationDrawerItems = () => { const isFreeAccessEnabled = useIsFeatureEnabled( FeatureFlagKey.IsFreeAccessEnabled, ); - const isCRMMigrationEnabled = useIsFeatureEnabled( - FeatureFlagKey.IsCrmMigrationEnabled, - ); const isBillingPageEnabled = billing?.isBillingEnabled && !isFreeAccessEnabled; @@ -83,7 +80,7 @@ export const SettingsNavigationDrawerItems = () => { ]; const selectedIndex = accountSubSettings.findIndex((accountSubSetting) => { - const href = getSettingsPagePath(accountSubSetting.path); + const href = getSettingsPath(accountSubSetting.path); const pathName = resolvePath(href).pathname; return matchPath( @@ -161,13 +158,6 @@ export const SettingsNavigationDrawerItems = () => { path={SettingsPath.Integrations} Icon={IconApps} /> - {isCRMMigrationEnabled && ( - - )} { const dropdownId = `settings-object-new-field-breadcrumb-dropdown`; const { closeDropdown } = useDropdown(dropdownId); - const navigate = useNavigate(); + const navigate = useNavigateSettings(); const location = useLocation(); const { objectNamePlural = '' } = useParams(); const [searchParams] = useSearchParams(); @@ -78,11 +75,15 @@ export const SettingsDataModelNewFieldBreadcrumbDropDown = () => { const handleClick = (step: 'select' | 'configure') => { if (step === 'configure' && isDefined(fieldType)) { navigate( - `/settings/objects/${objectNamePlural}/new-field/configure?fieldType=${fieldType}`, + SettingsPath.ObjectNewFieldConfigure, + { objectNamePlural }, + { fieldType }, ); } else { navigate( - `/settings/objects/${objectNamePlural}/new-field/select${fieldType ? `?fieldType=${fieldType}` : ''}`, + SettingsPath.ObjectNewFieldSelect, + { objectNamePlural }, + fieldType ? { fieldType } : undefined, ); } closeDropdown(); diff --git a/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsObjectNewFieldSelector.tsx b/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsObjectNewFieldSelector.tsx index dd6387e0c..74d588f21 100644 --- a/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsObjectNewFieldSelector.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/fields/forms/components/SettingsObjectNewFieldSelector.tsx @@ -8,6 +8,7 @@ import { useBooleanSettingsFormInitialValues } from '@/settings/data-model/field import { useCurrencySettingsFormInitialValues } from '@/settings/data-model/fields/forms/currency/hooks/useCurrencySettingsFormInitialValues'; import { useSelectSettingsFormInitialValues } from '@/settings/data-model/fields/forms/select/hooks/useSelectSettingsFormInitialValues'; import { SettingsFieldType } from '@/settings/data-model/types/SettingsFieldType'; +import { SettingsPath } from '@/types/SettingsPath'; import { TextInput } from '@/ui/input/components/TextInput'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; @@ -17,6 +18,7 @@ import { Controller, useFormContext } from 'react-hook-form'; import { H2Title, IconSearch, UndecoratedLink } from 'twenty-ui'; import { FieldMetadataType } from '~/generated-metadata/graphql'; import { SettingsDataModelFieldTypeFormValues } from '~/pages/settings/data-model/SettingsObjectNewField/SettingsObjectNewFieldSelect'; +import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; type SettingsObjectNewFieldSelectorProps = { className?: string; @@ -128,7 +130,15 @@ export const SettingsObjectNewFieldSelector = ({ .map(([key, config]) => ( { setValue('type', key as SettingsFieldType); diff --git a/packages/twenty-front/src/modules/settings/data-model/graph-overview/components/SettingsDataModelOverviewObject.tsx b/packages/twenty-front/src/modules/settings/data-model/graph-overview/components/SettingsDataModelOverviewObject.tsx index 5b45a9c7f..bc056a14d 100644 --- a/packages/twenty-front/src/modules/settings/data-model/graph-overview/components/SettingsDataModelOverviewObject.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/graph-overview/components/SettingsDataModelOverviewObject.tsx @@ -13,8 +13,10 @@ import { capitalize } from 'twenty-shared'; import { FieldMetadataType } from '~/generated/graphql'; import { ObjectFieldRowWithoutRelation } from '@/settings/data-model/graph-overview/components/SettingsDataModelOverviewFieldWithoutRelation'; +import { SettingsPath } from '@/types/SettingsPath'; import '@xyflow/react/dist/style.css'; import { useState } from 'react'; +import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; type SettingsDataModelOverviewObjectNode = Node; type SettingsDataModelOverviewObjectProps = @@ -122,7 +124,9 @@ export const SettingsDataModelOverviewObject = ({ {}} onMouseLeave={() => {}}> {Icon && } {capitalize(objectMetadataItem.namePlural)} diff --git a/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectFieldItemTableRow.tsx b/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectFieldItemTableRow.tsx index f0efe47a8..6b626da0e 100644 --- a/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectFieldItemTableRow.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/object-details/components/SettingsObjectFieldItemTableRow.tsx @@ -12,6 +12,7 @@ import { SettingsObjectFieldActiveActionDropdown } from '@/settings/data-model/o import { SettingsObjectFieldInactiveActionDropdown } from '@/settings/data-model/object-details/components/SettingsObjectFieldDisabledActionDropdown'; import { settingsObjectFieldsFamilyState } from '@/settings/data-model/object-details/states/settingsObjectFieldsFamilyState'; import { isFieldTypeSupportedInSettings } from '@/settings/data-model/utils/isFieldTypeSupportedInSettings'; +import { SettingsPath } from '@/types/SettingsPath'; import { TableCell } from '@/ui/layout/table/components/TableCell'; import { TableRow } from '@/ui/layout/table/components/TableRow'; import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState'; @@ -19,7 +20,6 @@ import { View } from '@/views/types/View'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { useMemo } from 'react'; -import { useNavigate } from 'react-router-dom'; import { useRecoilState } from 'recoil'; import { IconMinus, @@ -30,7 +30,9 @@ import { useIcons, } from 'twenty-ui'; import { RelationDefinitionType } from '~/generated-metadata/graphql'; +import { useNavigateSettings } from '~/hooks/useNavigateSettings'; import { SettingsObjectDetailTableItem } from '~/pages/settings/data-model/types/SettingsObjectDetailTableItem'; +import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; import { RELATION_TYPES } from '../../constants/RelationTypes'; import { SettingsObjectFieldDataType } from './SettingsObjectFieldDataType'; @@ -72,7 +74,7 @@ export const SettingsObjectFieldItemTableRow = ({ const variant = objectMetadataItem.isCustom ? 'identifier' : 'field-type'; - const navigate = useNavigate(); + const navigate = useNavigateSettings(); const [navigationMemorizedUrl, setNavigationMemorizedUrl] = useRecoilState( navigationMemorizedUrlState, @@ -108,7 +110,10 @@ export const SettingsObjectFieldItemTableRow = ({ !isLabelIdentifier && LABEL_IDENTIFIER_FIELD_METADATA_TYPES.includes(fieldMetadataItem.type); - const linkToNavigate = `./${fieldMetadataItem.name}`; + const linkToNavigate = getSettingsPath(SettingsPath.ObjectFieldEdit, { + objectNamePlural: objectMetadataItem.namePlural, + fieldName: fieldMetadataItem.name, + }); const { activateMetadataField, @@ -212,7 +217,15 @@ export const SettingsObjectFieldItemTableRow = ({ return ( navigate(linkToNavigate) : undefined} + onClick={ + mode === 'view' + ? () => + navigate(SettingsPath.ObjectFieldEdit, { + objectNamePlural: objectMetadataItem.namePlural, + fieldName: fieldMetadataItem.name, + }) + : undefined + } > @@ -244,7 +257,9 @@ export const SettingsObjectFieldItemTableRow = ({ } to={ isRelatedObjectLinkable - ? `/settings/objects/${relationObjectMetadataItem.namePlural}` + ? getSettingsPath(SettingsPath.Objects, { + objectNamePlural: relationObjectMetadataItem.namePlural, + }) : undefined } value={fieldType} @@ -261,7 +276,12 @@ export const SettingsObjectFieldItemTableRow = ({ navigate(linkToNavigate)} + onEdit={() => + navigate(SettingsPath.ObjectFieldEdit, { + objectNamePlural: objectMetadataItem.namePlural, + fieldName: fieldMetadataItem.name, + }) + } onSetAsLabelIdentifier={ canBeSetAsLabelIdentifier ? () => handleSetLabelIdentifierField(fieldMetadataItem) @@ -286,7 +306,12 @@ export const SettingsObjectFieldItemTableRow = ({ navigate(linkToNavigate)} + onEdit={() => + navigate(SettingsPath.ObjectFieldEdit, { + objectNamePlural: objectMetadataItem.namePlural, + fieldName: fieldMetadataItem.name, + }) + } onActivate={() => activateMetadataField(fieldMetadataItem.id, objectMetadataItem.id) } diff --git a/packages/twenty-front/src/modules/settings/data-model/object-details/components/tabs/ObjectSettings.tsx b/packages/twenty-front/src/modules/settings/data-model/object-details/components/tabs/ObjectSettings.tsx index 9b656acdf..6fcf55ad3 100644 --- a/packages/twenty-front/src/modules/settings/data-model/object-details/components/tabs/ObjectSettings.tsx +++ b/packages/twenty-front/src/modules/settings/data-model/object-details/components/tabs/ObjectSettings.tsx @@ -2,7 +2,6 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { zodResolver } from '@hookform/resolvers/zod'; import { FormProvider, useForm } from 'react-hook-form'; -import { useNavigate } from 'react-router-dom'; import { Button, H2Title, IconArchive, Section } from 'twenty-ui'; import { z, ZodError } from 'zod'; @@ -18,7 +17,7 @@ import { import { settingsDataModelObjectIdentifiersFormSchema } from '@/settings/data-model/objects/forms/components/SettingsDataModelObjectIdentifiersForm'; import { SettingsDataModelObjectSettingsFormCard } from '@/settings/data-model/objects/forms/components/SettingsDataModelObjectSettingsFormCard'; import { settingsUpdateObjectInputSchema } from '@/settings/data-model/validation-schemas/settingsUpdateObjectInputSchema'; -import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; +import { AppPath } from '@/types/AppPath'; import { SettingsPath } from '@/types/SettingsPath'; import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; @@ -27,8 +26,10 @@ import styled from '@emotion/styled'; import isEmpty from 'lodash.isempty'; import pick from 'lodash.pick'; import { useSetRecoilState } from 'recoil'; +import { useNavigateSettings } from '~/hooks/useNavigateSettings'; import { updatedObjectNamePluralState } from '~/pages/settings/data-model/states/updatedObjectNamePluralState'; import { computeMetadataNameFromLabel } from '~/pages/settings/data-model/utils/compute-metadata-name-from-label.utils'; +import { getAppPath } from '~/utils/navigation/getAppPath'; const objectEditFormSchema = z .object({}) @@ -54,7 +55,7 @@ const StyledFormSection = styled(Section)` `; export const ObjectSettings = ({ objectMetadataItem }: ObjectSettingsProps) => { - const navigate = useNavigate(); + const navigate = useNavigateSettings(); const { enqueueSnackBar } = useSnackBar(); const setUpdatedObjectNamePlural = useSetRecoilState( updatedObjectNamePluralState, @@ -65,8 +66,6 @@ export const ObjectSettings = ({ objectMetadataItem }: ObjectSettingsProps) => { useLastVisitedObjectMetadataItem(); const { getLastVisitedViewIdFromObjectMetadataItemId } = useLastVisitedView(); - const settingsObjectsPagePath = getSettingsPagePath(SettingsPath.Objects); - const formConfig = useForm({ mode: 'onTouched', resolver: zodResolver(objectEditFormSchema), @@ -147,11 +146,17 @@ export const ObjectSettings = ({ objectMetadataItem }: ObjectSettingsProps) => { objectMetadataItem.id, ); setNavigationMemorizedUrl( - `/objects/${objectNamePluralForRedirection}?view=${lastVisitedView}`, + getAppPath( + AppPath.RecordIndexPage, + { objectNamePlural: objectNamePluralForRedirection }, + { viewId: lastVisitedView }, + ), ); } - navigate(`${settingsObjectsPagePath}/${objectNamePluralForRedirection}`); + navigate(SettingsPath.ObjectDetail, { + objectNamePlural: objectNamePluralForRedirection, + }); } catch (error) { if (error instanceof ZodError) { enqueueSnackBar(error.issues[0].message, { @@ -170,7 +175,7 @@ export const ObjectSettings = ({ objectMetadataItem }: ObjectSettingsProps) => { idToUpdate: objectMetadataItem.id, updatePayload: { isActive: false }, }); - navigate(settingsObjectsPagePath); + navigate(SettingsPath.Objects); }; return ( diff --git a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionShowContainer.tsx b/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionShowContainer.tsx index 6cffad757..97d6f745a 100644 --- a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionShowContainer.tsx +++ b/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionShowContainer.tsx @@ -2,15 +2,15 @@ import { useDeleteOneDatabaseConnection } from '@/databases/hooks/useDeleteOneDa import { SettingsIntegrationDatabaseConnectionSummaryCard } from '@/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionSummaryCard'; import { SettingsIntegrationDatabaseTablesListCard } from '@/settings/integrations/database-connection/components/SettingsIntegrationDatabaseTablesListCard'; import { useDatabaseConnection } from '@/settings/integrations/database-connection/hooks/useDatabaseConnection'; -import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; import { SettingsPath } from '@/types/SettingsPath'; import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb'; import { Section } from '@react-email/components'; -import { useNavigate } from 'react-router-dom'; import { H2Title } from 'twenty-ui'; +import { useNavigateSettings } from '~/hooks/useNavigateSettings'; +import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; export const SettingsIntegrationDatabaseConnectionShowContainer = () => { - const navigate = useNavigate(); + const navigate = useNavigateSettings(); const { connection, integration, databaseKey, tables } = useDatabaseConnection({ fetchPolicy: 'network-only' }); @@ -23,10 +23,12 @@ export const SettingsIntegrationDatabaseConnectionShowContainer = () => { const deleteConnection = async () => { await deleteOneDatabaseConnection({ id: connection.id }); - navigate(`${settingsIntegrationsPagePath}/${databaseKey}`); + navigate(SettingsPath.IntegrationDatabase, { + databaseKey, + }); }; - const settingsIntegrationsPagePath = getSettingsPagePath( + const settingsIntegrationsPagePath = getSettingsPath( SettingsPath.Integrations, ); diff --git a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionsListCard.tsx b/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionsListCard.tsx index 135861567..5f482b727 100644 --- a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionsListCard.tsx +++ b/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionsListCard.tsx @@ -1,11 +1,12 @@ import styled from '@emotion/styled'; -import { useNavigate } from 'react-router-dom'; import { IconChevronRight, LightIconButton } from 'twenty-ui'; import { SettingsListCard } from '@/settings/components/SettingsListCard'; import { SettingsIntegrationDatabaseConnectionSyncStatus } from '@/settings/integrations/database-connection/components/SettingsIntegrationDatabaseConnectionSyncStatus'; import { SettingsIntegration } from '@/settings/integrations/types/SettingsIntegration'; +import { SettingsPath } from '@/types/SettingsPath'; import { RemoteServer } from '~/generated-metadata/graphql'; +import { useNavigateSettings } from '~/hooks/useNavigateSettings'; type SettingsIntegrationDatabaseConnectionsListCardProps = { integration: SettingsIntegration; @@ -34,7 +35,7 @@ export const SettingsIntegrationDatabaseConnectionsListCard = ({ integration, connections, }: SettingsIntegrationDatabaseConnectionsListCardProps) => { - const navigate = useNavigate(); + const navigate = useNavigateSettings(); return ( )} - onRowClick={(connection) => navigate(`./${connection.id}`)} + onRowClick={(connection) => + navigate(SettingsPath.IntegrationDatabaseConnection, { + databaseKey: integration.from.key, + connectionId: connection.id, + }) + } getItemLabel={(connection) => connection.label} hasFooter footerButtonLabel="Add connection" - onFooterButtonClick={() => navigate('./new')} + onFooterButtonClick={() => + navigate(SettingsPath.IntegrationNewDatabaseConnection, { + databaseKey: integration.from.key, + }) + } /> ); }; diff --git a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationEditDatabaseConnectionContent.tsx b/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationEditDatabaseConnectionContent.tsx index 3540cad1e..d6669a809 100644 --- a/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationEditDatabaseConnectionContent.tsx +++ b/packages/twenty-front/src/modules/settings/integrations/database-connection/components/SettingsIntegrationEditDatabaseConnectionContent.tsx @@ -8,7 +8,6 @@ import { getFormDefaultValuesFromConnection, } from '@/settings/integrations/database-connection/utils/editDatabaseConnection'; import { SettingsIntegration } from '@/settings/integrations/types/SettingsIntegration'; -import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; import { SettingsPath } from '@/types/SettingsPath'; import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; @@ -17,7 +16,6 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { Section } from '@react-email/components'; import pick from 'lodash.pick'; import { FormProvider, useForm } from 'react-hook-form'; -import { useNavigate } from 'react-router-dom'; import { H2Title, Info } from 'twenty-ui'; import { z } from 'zod'; import { @@ -25,6 +23,8 @@ import { RemoteTable, RemoteTableStatus, } from '~/generated-metadata/graphql'; +import { useNavigateSettings } from '~/hooks/useNavigateSettings'; +import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; export const SettingsIntegrationEditDatabaseConnectionContent = ({ connection, @@ -38,7 +38,7 @@ export const SettingsIntegrationEditDatabaseConnectionContent = ({ tables: RemoteTable[]; }) => { const { enqueueSnackBar } = useSnackBar(); - const navigate = useNavigate(); + const navigate = useNavigateSettings(); const editConnectionSchema = getEditionSchemaForForm(databaseKey); type SettingsIntegrationEditConnectionFormValues = z.infer< @@ -56,7 +56,7 @@ export const SettingsIntegrationEditDatabaseConnectionContent = ({ const { updateOneDatabaseConnection } = useUpdateOneDatabaseConnection(); - const settingsIntegrationsPagePath = getSettingsPagePath( + const settingsIntegrationsPagePath = getSettingsPath( SettingsPath.Integrations, ); @@ -82,9 +82,10 @@ export const SettingsIntegrationEditDatabaseConnectionContent = ({ id: connection?.id ?? '', }); - navigate( - `${settingsIntegrationsPagePath}/${databaseKey}/${connection?.id}`, - ); + navigate(SettingsPath.IntegrationDatabaseConnection, { + databaseKey, + connectionId: connection?.id, + }); } catch (error) { enqueueSnackBar((error as Error).message, { variant: SnackBarVariant.Error, @@ -116,7 +117,9 @@ export const SettingsIntegrationEditDatabaseConnectionContent = ({ - navigate(`${settingsIntegrationsPagePath}/${databaseKey}`) + navigate(SettingsPath.IntegrationDatabase, { + databaseKey, + }) } onSave={handleSave} /> diff --git a/packages/twenty-front/src/modules/settings/integrations/database-connection/hooks/useDatabaseConnection.ts b/packages/twenty-front/src/modules/settings/integrations/database-connection/hooks/useDatabaseConnection.ts index ec3c73df0..79ca63302 100644 --- a/packages/twenty-front/src/modules/settings/integrations/database-connection/hooks/useDatabaseConnection.ts +++ b/packages/twenty-front/src/modules/settings/integrations/database-connection/hooks/useDatabaseConnection.ts @@ -1,12 +1,13 @@ import { WatchQueryFetchPolicy } from '@apollo/client'; import { useEffect } from 'react'; -import { useNavigate, useParams } from 'react-router-dom'; +import { useParams } from 'react-router-dom'; import { useGetDatabaseConnection } from '@/databases/hooks/useGetDatabaseConnection'; import { useGetDatabaseConnectionTables } from '@/databases/hooks/useGetDatabaseConnectionTables'; import { useIsSettingsIntegrationEnabled } from '@/settings/integrations/hooks/useIsSettingsIntegrationEnabled'; import { useSettingsIntegrationCategories } from '@/settings/integrations/hooks/useSettingsIntegrationCategories'; import { AppPath } from '@/types/AppPath'; +import { useNavigateApp } from '~/hooks/useNavigateApp'; export const useDatabaseConnection = ({ fetchPolicy, @@ -14,7 +15,7 @@ export const useDatabaseConnection = ({ fetchPolicy?: WatchQueryFetchPolicy; }) => { const { databaseKey = '', connectionId = '' } = useParams(); - const navigate = useNavigate(); + const navigateApp = useNavigateApp(); const [integrationCategoryAll] = useSettingsIntegrationCategories(); const integration = integrationCategoryAll.integrations.find( @@ -34,12 +35,12 @@ export const useDatabaseConnection = ({ useEffect(() => { if (!isIntegrationAvailable || (!loading && !connection)) { - navigate(AppPath.NotFound); + navigateApp(AppPath.NotFound); } }, [ integration, databaseKey, - navigate, + navigateApp, isIntegrationAvailable, connection, loading, diff --git a/packages/twenty-front/src/modules/settings/security/components/SettingsSSOIdentitiesProvidersListCard.tsx b/packages/twenty-front/src/modules/settings/security/components/SettingsSSOIdentitiesProvidersListCard.tsx index 13d748e85..aceeeefd8 100644 --- a/packages/twenty-front/src/modules/settings/security/components/SettingsSSOIdentitiesProvidersListCard.tsx +++ b/packages/twenty-front/src/modules/settings/security/components/SettingsSSOIdentitiesProvidersListCard.tsx @@ -2,20 +2,20 @@ import { Link } from 'react-router-dom'; -import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; import { SettingsPath } from '@/types/SettingsPath'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; import { SettingsCard } from '@/settings/components/SettingsCard'; import { SettingsSSOIdentitiesProvidersListCardWrapper } from '@/settings/security/components/SettingsSSOIdentitiesProvidersListCardWrapper'; +import { SSOIdentitiesProvidersState } from '@/settings/security/states/SSOIdentitiesProvidersState'; +import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; +import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import isPropValid from '@emotion/is-prop-valid'; import styled from '@emotion/styled'; -import { useRecoilValue, useRecoilState } from 'recoil'; +import { useRecoilState, useRecoilValue } from 'recoil'; import { IconKey } from 'twenty-ui'; import { useListSsoIdentityProvidersByWorkspaceIdQuery } from '~/generated/graphql'; -import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; -import { SSOIdentitiesProvidersState } from '@/settings/security/states/SSOIdentitiesProvidersState'; -import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; +import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; const StyledLink = styled(Link, { shouldForwardProp: (prop) => isPropValid(prop) && prop !== 'isDisabled', @@ -49,7 +49,7 @@ export const SettingsSSOIdentitiesProvidersListCard = () => { return loading || !SSOIdentitiesProviders.length ? ( { - const navigate = useNavigate(); + const navigate = useNavigateSettings(); const SSOIdentitiesProviders = useRecoilValue(SSOIdentitiesProvidersState); @@ -28,9 +27,7 @@ export const SettingsSSOIdentitiesProvidersListCardWrapper = () => { )} hasFooter footerButtonLabel="Add SSO Identity Provider" - onFooterButtonClick={() => - navigate(getSettingsPagePath(SettingsPath.NewSSOIdentityProvider)) - } + onFooterButtonClick={() => navigate(SettingsPath.NewSSOIdentityProvider)} /> ); }; diff --git a/packages/twenty-front/src/modules/settings/serverless-functions/components/SettingsServerlessFunctionsTable.tsx b/packages/twenty-front/src/modules/settings/serverless-functions/components/SettingsServerlessFunctionsTable.tsx index 286a90faa..c732b5edd 100644 --- a/packages/twenty-front/src/modules/settings/serverless-functions/components/SettingsServerlessFunctionsTable.tsx +++ b/packages/twenty-front/src/modules/settings/serverless-functions/components/SettingsServerlessFunctionsTable.tsx @@ -2,7 +2,6 @@ import { SettingsPageContainer } from '@/settings/components/SettingsPageContain import { SettingsServerlessFunctionsFieldItemTableRow } from '@/settings/serverless-functions/components/SettingsServerlessFunctionsFieldItemTableRow'; import { SettingsServerlessFunctionsTableEmpty } from '@/settings/serverless-functions/components/SettingsServerlessFunctionsTableEmpty'; import { useGetManyServerlessFunctions } from '@/settings/serverless-functions/hooks/useGetManyServerlessFunctions'; -import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; import { SettingsPath } from '@/types/SettingsPath'; import { Table } from '@/ui/layout/table/components/Table'; import { TableBody } from '@/ui/layout/table/components/TableBody'; @@ -10,6 +9,7 @@ import { TableHeader } from '@/ui/layout/table/components/TableHeader'; import { TableRow } from '@/ui/layout/table/components/TableRow'; import styled from '@emotion/styled'; import { ServerlessFunction } from '~/generated-metadata/graphql'; +import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; const StyledTableRow = styled(TableRow)` grid-template-columns: 312px 132px 68px; @@ -38,7 +38,7 @@ export const SettingsServerlessFunctionsTable = () => { diff --git a/packages/twenty-front/src/modules/settings/serverless-functions/components/SettingsServerlessFunctionsTableEmpty.tsx b/packages/twenty-front/src/modules/settings/serverless-functions/components/SettingsServerlessFunctionsTableEmpty.tsx index a67d4d358..990cf04dd 100644 --- a/packages/twenty-front/src/modules/settings/serverless-functions/components/SettingsServerlessFunctionsTableEmpty.tsx +++ b/packages/twenty-front/src/modules/settings/serverless-functions/components/SettingsServerlessFunctionsTableEmpty.tsx @@ -1,4 +1,3 @@ -import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; import { SettingsPath } from '@/types/SettingsPath'; import styled from '@emotion/styled'; import { @@ -11,6 +10,7 @@ import { EMPTY_PLACEHOLDER_TRANSITION_PROPS, IconPlus, } from 'twenty-ui'; +import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; const StyledEmptyFunctionsContainer = styled.div` height: 60vh; @@ -35,7 +35,7 @@ export const SettingsServerlessFunctionsTableEmpty = () => {