mirror of
https://github.com/twentyhq/twenty.git
synced 2025-01-08 09:47:07 +03:00
feat: Improved Page and History names (#4908)
Improved page and history names. Closes #4684 --------- Co-authored-by: Marie Stoppa <marie.stoppa@essec.edu>
This commit is contained in:
parent
756de8a31b
commit
5477665e5d
@ -1,6 +1,6 @@
|
|||||||
export enum SettingsPath {
|
export enum SettingsPath {
|
||||||
ProfilePage = 'profile',
|
ProfilePage = 'profile',
|
||||||
Appearance = 'profile/appearance',
|
Appearance = 'appearance',
|
||||||
Accounts = 'accounts',
|
Accounts = 'accounts',
|
||||||
NewAccount = 'accounts/new',
|
NewAccount = 'accounts/new',
|
||||||
AccountsCalendars = 'accounts/calendars',
|
AccountsCalendars = 'accounts/calendars',
|
||||||
|
@ -5,14 +5,17 @@ import { ObjectFilterDropdownButton } from '@/object-record/object-filter-dropdo
|
|||||||
import { FiltersHotkeyScope } from '@/object-record/object-filter-dropdown/types/FiltersHotkeyScope';
|
import { FiltersHotkeyScope } from '@/object-record/object-filter-dropdown/types/FiltersHotkeyScope';
|
||||||
import { ObjectSortDropdownButton } from '@/object-record/object-sort-dropdown/components/ObjectSortDropdownButton';
|
import { ObjectSortDropdownButton } from '@/object-record/object-sort-dropdown/components/ObjectSortDropdownButton';
|
||||||
import { TopBar } from '@/ui/layout/top-bar/TopBar';
|
import { TopBar } from '@/ui/layout/top-bar/TopBar';
|
||||||
|
import { PageTitle } from '@/ui/utilities/page-title/PageTitle';
|
||||||
import { QueryParamsFiltersEffect } from '@/views/components/QueryParamsFiltersEffect';
|
import { QueryParamsFiltersEffect } from '@/views/components/QueryParamsFiltersEffect';
|
||||||
import { QueryParamsViewIdEffect } from '@/views/components/QueryParamsViewIdEffect';
|
import { QueryParamsViewIdEffect } from '@/views/components/QueryParamsViewIdEffect';
|
||||||
import { ViewBarEffect } from '@/views/components/ViewBarEffect';
|
import { ViewBarEffect } from '@/views/components/ViewBarEffect';
|
||||||
import { ViewBarFilterEffect } from '@/views/components/ViewBarFilterEffect';
|
import { ViewBarFilterEffect } from '@/views/components/ViewBarFilterEffect';
|
||||||
import { ViewBarSortEffect } from '@/views/components/ViewBarSortEffect';
|
import { ViewBarSortEffect } from '@/views/components/ViewBarSortEffect';
|
||||||
|
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
|
||||||
import { ViewScope } from '@/views/scopes/ViewScope';
|
import { ViewScope } from '@/views/scopes/ViewScope';
|
||||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||||
import { ViewPickerDropdown } from '@/views/view-picker/components/ViewPickerDropdown';
|
import { ViewPickerDropdown } from '@/views/view-picker/components/ViewPickerDropdown';
|
||||||
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
|
|
||||||
import { ViewsHotkeyScope } from '../types/ViewsHotkeyScope';
|
import { ViewsHotkeyScope } from '../types/ViewsHotkeyScope';
|
||||||
|
|
||||||
@ -34,6 +37,9 @@ export const ViewBar = ({
|
|||||||
}: ViewBarProps) => {
|
}: ViewBarProps) => {
|
||||||
const { objectNamePlural } = useParams();
|
const { objectNamePlural } = useParams();
|
||||||
|
|
||||||
|
const { currentViewWithCombinedFiltersAndSorts: currentView } =
|
||||||
|
useGetCurrentView(viewBarId);
|
||||||
|
|
||||||
const filterDropdownId = 'view-filter';
|
const filterDropdownId = 'view-filter';
|
||||||
const sortDropdownId = 'view-sort';
|
const sortDropdownId = 'view-sort';
|
||||||
|
|
||||||
@ -41,6 +47,10 @@ export const ViewBar = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pageTitle = currentView?.name
|
||||||
|
? `${currentView?.name} - ${capitalize(objectNamePlural)}`
|
||||||
|
: capitalize(objectNamePlural);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ViewScope
|
<ViewScope
|
||||||
viewScopeId={viewBarId}
|
viewScopeId={viewBarId}
|
||||||
@ -52,6 +62,7 @@ export const ViewBar = ({
|
|||||||
<QueryParamsFiltersEffect />
|
<QueryParamsFiltersEffect />
|
||||||
<QueryParamsViewIdEffect />
|
<QueryParamsViewIdEffect />
|
||||||
|
|
||||||
|
<PageTitle title={pageTitle} />
|
||||||
<TopBar
|
<TopBar
|
||||||
className={className}
|
className={className}
|
||||||
leftComponent={
|
leftComponent={
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
AnimatedPlaceholderErrorSubTitle,
|
AnimatedPlaceholderErrorSubTitle,
|
||||||
AnimatedPlaceholderErrorTitle,
|
AnimatedPlaceholderErrorTitle,
|
||||||
} from '@/ui/layout/animated-placeholder/components/ErrorPlaceholderStyled';
|
} from '@/ui/layout/animated-placeholder/components/ErrorPlaceholderStyled';
|
||||||
|
import { PageTitle } from '@/ui/utilities/page-title/PageTitle';
|
||||||
|
|
||||||
const StyledBackDrop = styled.div`
|
const StyledBackDrop = styled.div`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -36,6 +37,7 @@ export const NotFound = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<PageTitle title="Page Not Found | Twenty" />
|
||||||
<StyledBackDrop>
|
<StyledBackDrop>
|
||||||
<AnimatedPlaceholderErrorContainer>
|
<AnimatedPlaceholderErrorContainer>
|
||||||
<AnimatedPlaceholder type="error404" />
|
<AnimatedPlaceholder type="error404" />
|
||||||
|
@ -9,7 +9,9 @@ import { useSelectedTableCellEditMode } from '@/object-record/record-table/recor
|
|||||||
import { PageBody } from '@/ui/layout/page/PageBody';
|
import { PageBody } from '@/ui/layout/page/PageBody';
|
||||||
import { PageContainer } from '@/ui/layout/page/PageContainer';
|
import { PageContainer } from '@/ui/layout/page/PageContainer';
|
||||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||||
|
import { PageTitle } from '@/ui/utilities/page-title/PageTitle';
|
||||||
import { RecordIndexPageHeader } from '~/pages/object-record/RecordIndexPageHeader';
|
import { RecordIndexPageHeader } from '~/pages/object-record/RecordIndexPageHeader';
|
||||||
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
|
|
||||||
const StyledIndexContainer = styled.div`
|
const StyledIndexContainer = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -40,6 +42,7 @@ export const RecordIndexPage = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContainer>
|
<PageContainer>
|
||||||
|
<PageTitle title={`${capitalize(objectNamePlural)}`} />
|
||||||
<RecordIndexPageHeader createRecord={handleAddButtonClick} />
|
<RecordIndexPageHeader createRecord={handleAddButtonClick} />
|
||||||
<PageBody>
|
<PageBody>
|
||||||
<StyledIndexContainer>
|
<StyledIndexContainer>
|
||||||
|
@ -18,6 +18,7 @@ import { ShowPageMoreButton } from '@/ui/layout/show-page/components/ShowPageMor
|
|||||||
import { PageTitle } from '@/ui/utilities/page-title/PageTitle';
|
import { PageTitle } from '@/ui/utilities/page-title/PageTitle';
|
||||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
import { capitalize } from '~/utils/string/capitalize';
|
||||||
|
|
||||||
export const RecordShowPage = () => {
|
export const RecordShowPage = () => {
|
||||||
const { objectNameSingular, objectRecordId } = useParams<{
|
const { objectNameSingular, objectRecordId } = useParams<{
|
||||||
@ -80,13 +81,20 @@ export const RecordShowPage = () => {
|
|||||||
|
|
||||||
const labelIdentifierFieldValue =
|
const labelIdentifierFieldValue =
|
||||||
record?.[labelIdentifierFieldMetadataItem?.name ?? ''];
|
record?.[labelIdentifierFieldMetadataItem?.name ?? ''];
|
||||||
|
|
||||||
const pageName =
|
const pageName =
|
||||||
labelIdentifierFieldMetadataItem?.type === FieldMetadataType.FullName
|
labelIdentifierFieldMetadataItem?.type === FieldMetadataType.FullName
|
||||||
? [
|
? [
|
||||||
labelIdentifierFieldValue?.firstName,
|
labelIdentifierFieldValue?.firstName,
|
||||||
labelIdentifierFieldValue?.lastName,
|
labelIdentifierFieldValue?.lastName,
|
||||||
].join(' ')
|
].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
|
// Temporarily since we don't have relations for remote objects yet
|
||||||
if (objectMetadataItem.isRemote) {
|
if (objectMetadataItem.isRemote) {
|
||||||
@ -95,7 +103,7 @@ export const RecordShowPage = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContainer>
|
<PageContainer>
|
||||||
<PageTitle title={pageName} />
|
<PageTitle title={pageTitle} />
|
||||||
<PageHeader
|
<PageHeader
|
||||||
title={pageName ?? ''}
|
title={pageName ?? ''}
|
||||||
hasBackButton
|
hasBackButton
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { getPageTitleFromPath } from '../title-utils';
|
import { getPageTitleFromPath, SettingsPageTitles } from '../title-utils';
|
||||||
|
|
||||||
describe('title-utils', () => {
|
describe('title-utils', () => {
|
||||||
it('should return the correct title for a given path', () => {
|
it('should return the correct title for a given path', () => {
|
||||||
@ -13,14 +13,39 @@ describe('title-utils', () => {
|
|||||||
expect(getPageTitleFromPath('/objects/opportunities')).toBe(
|
expect(getPageTitleFromPath('/objects/opportunities')).toBe(
|
||||||
'Opportunities',
|
'Opportunities',
|
||||||
);
|
);
|
||||||
expect(getPageTitleFromPath('/settings/profile')).toBe('Profile');
|
expect(getPageTitleFromPath('/settings/objects/opportunities')).toBe(
|
||||||
expect(getPageTitleFromPath('/settings/profile/appearance')).toBe(
|
SettingsPageTitles.Objects,
|
||||||
'Appearance',
|
);
|
||||||
|
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(
|
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('/')).toBe('Twenty');
|
||||||
expect(getPageTitleFromPath('/random')).toBe('Twenty');
|
expect(getPageTitleFromPath('/random')).toBe('Twenty');
|
||||||
});
|
});
|
||||||
|
@ -2,8 +2,41 @@ import { AppBasePath } from '@/types/AppBasePath';
|
|||||||
import { AppPath } from '@/types/AppPath';
|
import { AppPath } from '@/types/AppPath';
|
||||||
import { SettingsPath } from '@/types/SettingsPath';
|
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 => {
|
export const getPageTitleFromPath = (pathname: string): string => {
|
||||||
switch (pathname) {
|
const pathnameOrPrefix = getPathnameOrPrefix(pathname);
|
||||||
|
switch (pathnameOrPrefix) {
|
||||||
case AppPath.Verify:
|
case AppPath.Verify:
|
||||||
return 'Verify';
|
return 'Verify';
|
||||||
case AppPath.SignInUp:
|
case AppPath.SignInUp:
|
||||||
@ -18,14 +51,22 @@ export const getPageTitleFromPath = (pathname: string): string => {
|
|||||||
return 'Tasks';
|
return 'Tasks';
|
||||||
case AppPath.OpportunitiesPage:
|
case AppPath.OpportunitiesPage:
|
||||||
return 'Opportunities';
|
return 'Opportunities';
|
||||||
case `${AppBasePath.Settings}/${SettingsPath.ProfilePage}`:
|
case SettingsPathPrefixes.Appearance:
|
||||||
return 'Profile';
|
return SettingsPageTitles.Appearance;
|
||||||
case `${AppBasePath.Settings}/${SettingsPath.Appearance}`:
|
case SettingsPathPrefixes.Accounts:
|
||||||
return 'Appearance';
|
return SettingsPageTitles.Accounts;
|
||||||
case `${AppBasePath.Settings}/${SettingsPath.WorkspaceMembersPage}`:
|
case SettingsPathPrefixes.Profile:
|
||||||
return 'Workspace Members';
|
return SettingsPageTitles.Profile;
|
||||||
case `${AppBasePath.Settings}/${SettingsPath.Workspace}`:
|
case SettingsPathPrefixes.Members:
|
||||||
return 'Workspace';
|
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:
|
default:
|
||||||
return 'Twenty';
|
return 'Twenty';
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user