diff --git a/packages/twenty-front/src/modules/types/SettingsPath.ts b/packages/twenty-front/src/modules/types/SettingsPath.ts index 656f57847..d0f453757 100644 --- a/packages/twenty-front/src/modules/types/SettingsPath.ts +++ b/packages/twenty-front/src/modules/types/SettingsPath.ts @@ -1,6 +1,6 @@ export enum SettingsPath { ProfilePage = 'profile', - Appearance = 'profile/appearance', + Appearance = 'appearance', Accounts = 'accounts', NewAccount = 'accounts/new', AccountsCalendars = 'accounts/calendars', diff --git a/packages/twenty-front/src/modules/views/components/ViewBar.tsx b/packages/twenty-front/src/modules/views/components/ViewBar.tsx index ca9903c46..1fa164e72 100644 --- a/packages/twenty-front/src/modules/views/components/ViewBar.tsx +++ b/packages/twenty-front/src/modules/views/components/ViewBar.tsx @@ -5,14 +5,17 @@ import { ObjectFilterDropdownButton } from '@/object-record/object-filter-dropdo import { FiltersHotkeyScope } from '@/object-record/object-filter-dropdown/types/FiltersHotkeyScope'; import { ObjectSortDropdownButton } from '@/object-record/object-sort-dropdown/components/ObjectSortDropdownButton'; import { TopBar } from '@/ui/layout/top-bar/TopBar'; +import { PageTitle } from '@/ui/utilities/page-title/PageTitle'; import { QueryParamsFiltersEffect } from '@/views/components/QueryParamsFiltersEffect'; import { QueryParamsViewIdEffect } from '@/views/components/QueryParamsViewIdEffect'; import { ViewBarEffect } from '@/views/components/ViewBarEffect'; import { ViewBarFilterEffect } from '@/views/components/ViewBarFilterEffect'; import { ViewBarSortEffect } from '@/views/components/ViewBarSortEffect'; +import { useGetCurrentView } from '@/views/hooks/useGetCurrentView'; import { ViewScope } from '@/views/scopes/ViewScope'; import { GraphQLView } from '@/views/types/GraphQLView'; import { ViewPickerDropdown } from '@/views/view-picker/components/ViewPickerDropdown'; +import { capitalize } from '~/utils/string/capitalize'; import { ViewsHotkeyScope } from '../types/ViewsHotkeyScope'; @@ -34,6 +37,9 @@ export const ViewBar = ({ }: ViewBarProps) => { const { objectNamePlural } = useParams(); + const { currentViewWithCombinedFiltersAndSorts: currentView } = + useGetCurrentView(viewBarId); + const filterDropdownId = 'view-filter'; const sortDropdownId = 'view-sort'; @@ -41,6 +47,10 @@ export const ViewBar = ({ return; } + const pageTitle = currentView?.name + ? `${currentView?.name} - ${capitalize(objectNamePlural)}` + : capitalize(objectNamePlural); + return ( + { return ( <> + diff --git a/packages/twenty-front/src/pages/object-record/RecordIndexPage.tsx b/packages/twenty-front/src/pages/object-record/RecordIndexPage.tsx index dbd2aacba..51ee50fce 100644 --- a/packages/twenty-front/src/pages/object-record/RecordIndexPage.tsx +++ b/packages/twenty-front/src/pages/object-record/RecordIndexPage.tsx @@ -9,7 +9,9 @@ import { useSelectedTableCellEditMode } from '@/object-record/record-table/recor import { PageBody } from '@/ui/layout/page/PageBody'; import { PageContainer } from '@/ui/layout/page/PageContainer'; import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; +import { PageTitle } from '@/ui/utilities/page-title/PageTitle'; import { RecordIndexPageHeader } from '~/pages/object-record/RecordIndexPageHeader'; +import { capitalize } from '~/utils/string/capitalize'; const StyledIndexContainer = styled.div` display: flex; @@ -40,6 +42,7 @@ export const RecordIndexPage = () => { return ( + diff --git a/packages/twenty-front/src/pages/object-record/RecordShowPage.tsx b/packages/twenty-front/src/pages/object-record/RecordShowPage.tsx index 52c1d9f68..0a9dde69d 100644 --- a/packages/twenty-front/src/pages/object-record/RecordShowPage.tsx +++ b/packages/twenty-front/src/pages/object-record/RecordShowPage.tsx @@ -18,6 +18,7 @@ import { ShowPageMoreButton } from '@/ui/layout/show-page/components/ShowPageMor import { PageTitle } from '@/ui/utilities/page-title/PageTitle'; import { FieldMetadataType } from '~/generated-metadata/graphql'; import { isDefined } from '~/utils/isDefined'; +import { capitalize } from '~/utils/string/capitalize'; export const RecordShowPage = () => { const { objectNameSingular, objectRecordId } = useParams<{ @@ -80,13 +81,20 @@ export const RecordShowPage = () => { const labelIdentifierFieldValue = record?.[labelIdentifierFieldMetadataItem?.name ?? '']; + const pageName = labelIdentifierFieldMetadataItem?.type === FieldMetadataType.FullName ? [ labelIdentifierFieldValue?.firstName, labelIdentifierFieldValue?.lastName, ].join(' ') - : `${labelIdentifierFieldValue}`; + : isDefined(labelIdentifierFieldValue) + ? `${labelIdentifierFieldValue}` + : ''; + + const pageTitle = pageName.trim() + ? `${pageName} - ${capitalize(objectNameSingular)}` + : capitalize(objectNameSingular); // Temporarily since we don't have relations for remote objects yet if (objectMetadataItem.isRemote) { @@ -95,7 +103,7 @@ export const RecordShowPage = () => { return ( - + { it('should return the correct title for a given path', () => { @@ -13,14 +13,39 @@ describe('title-utils', () => { expect(getPageTitleFromPath('/objects/opportunities')).toBe( 'Opportunities', ); - expect(getPageTitleFromPath('/settings/profile')).toBe('Profile'); - expect(getPageTitleFromPath('/settings/profile/appearance')).toBe( - 'Appearance', + expect(getPageTitleFromPath('/settings/objects/opportunities')).toBe( + SettingsPageTitles.Objects, + ); + expect(getPageTitleFromPath('/settings/profile')).toBe( + SettingsPageTitles.Profile, + ); + expect(getPageTitleFromPath('/settings/appearance')).toBe( + SettingsPageTitles.Appearance, + ); + expect(getPageTitleFromPath('/settings/accounts')).toBe( + SettingsPageTitles.Accounts, + ); + expect(getPageTitleFromPath('/settings/accounts/new')).toBe( + SettingsPageTitles.Accounts, + ); + expect(getPageTitleFromPath('/settings/accounts/calendars')).toBe( + SettingsPageTitles.Accounts, + ); + expect( + getPageTitleFromPath('/settings/accounts/calendars/:accountUuid'), + ).toBe(SettingsPageTitles.Accounts); + expect(getPageTitleFromPath('/settings/accounts/emails')).toBe( + SettingsPageTitles.Accounts, + ); + expect(getPageTitleFromPath('/settings/accounts/emails/:accountUuid')).toBe( + SettingsPageTitles.Accounts, ); expect(getPageTitleFromPath('/settings/workspace-members')).toBe( - 'Workspace Members', + SettingsPageTitles.Members, + ); + expect(getPageTitleFromPath('/settings/workspace')).toBe( + SettingsPageTitles.General, ); - expect(getPageTitleFromPath('/settings/workspace')).toBe('Workspace'); expect(getPageTitleFromPath('/')).toBe('Twenty'); expect(getPageTitleFromPath('/random')).toBe('Twenty'); }); diff --git a/packages/twenty-front/src/utils/title-utils.ts b/packages/twenty-front/src/utils/title-utils.ts index 8723e9aa5..9d604c5ee 100644 --- a/packages/twenty-front/src/utils/title-utils.ts +++ b/packages/twenty-front/src/utils/title-utils.ts @@ -2,8 +2,41 @@ import { AppBasePath } from '@/types/AppBasePath'; import { AppPath } from '@/types/AppPath'; import { SettingsPath } from '@/types/SettingsPath'; +export enum SettingsPageTitles { + Accounts = 'Account - Settings', + Appearance = 'Appearance - Settings', + Profile = 'Profile - Settings', + Objects = 'Data model - Settings', + Members = 'Members - Settings', + Developers = 'Developers - Settings', + Integration = 'Integrations - Settings', + General = 'General - Settings', + Default = 'Settings', +} + +enum SettingsPathPrefixes { + Accounts = `${AppBasePath.Settings}/${SettingsPath.Accounts}`, + Appearance = `${AppBasePath.Settings}/${SettingsPath.Appearance}`, + Profile = `${AppBasePath.Settings}/${SettingsPath.ProfilePage}`, + Objects = `${AppBasePath.Settings}/${SettingsPath.Objects}`, + Members = `${AppBasePath.Settings}/${SettingsPath.WorkspaceMembersPage}`, + Developers = `${AppBasePath.Settings}/${SettingsPath.Developers}`, + Integration = `${AppBasePath.Settings}/${SettingsPath.Integrations}`, + General = `${AppBasePath.Settings}/${SettingsPath.Workspace}`, +} + +const getPathnameOrPrefix = (pathname: string) => { + for (const prefix of Object.values(SettingsPathPrefixes)) { + if (pathname.startsWith(prefix)) { + return prefix; + } + } + return pathname; +}; + export const getPageTitleFromPath = (pathname: string): string => { - switch (pathname) { + const pathnameOrPrefix = getPathnameOrPrefix(pathname); + switch (pathnameOrPrefix) { case AppPath.Verify: return 'Verify'; case AppPath.SignInUp: @@ -18,14 +51,22 @@ export const getPageTitleFromPath = (pathname: string): string => { return 'Tasks'; case AppPath.OpportunitiesPage: return 'Opportunities'; - case `${AppBasePath.Settings}/${SettingsPath.ProfilePage}`: - return 'Profile'; - case `${AppBasePath.Settings}/${SettingsPath.Appearance}`: - return 'Appearance'; - case `${AppBasePath.Settings}/${SettingsPath.WorkspaceMembersPage}`: - return 'Workspace Members'; - case `${AppBasePath.Settings}/${SettingsPath.Workspace}`: - return 'Workspace'; + case SettingsPathPrefixes.Appearance: + return SettingsPageTitles.Appearance; + case SettingsPathPrefixes.Accounts: + return SettingsPageTitles.Accounts; + case SettingsPathPrefixes.Profile: + return SettingsPageTitles.Profile; + case SettingsPathPrefixes.Members: + return SettingsPageTitles.Members; + case SettingsPathPrefixes.Objects: + return SettingsPageTitles.Objects; + case SettingsPathPrefixes.Developers: + return SettingsPageTitles.Developers; + case SettingsPathPrefixes.Integration: + return SettingsPageTitles.Integration; + case SettingsPathPrefixes.General: + return SettingsPageTitles.General; default: return 'Twenty'; }