Fix breadcrumbs to display friendly object names instead of API names… (#13573)

Fixes #13250

This PR fixes the breadcrumb display issue where API names were shown
instead of friendly object names when navigating to related objects in
the detail view.

---------

Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
aaron
2025-08-03 12:18:32 -07:00
committed by GitHub
parent 253a9221db
commit b8a26dfe8c
27 changed files with 211 additions and 109 deletions
@@ -24,7 +24,6 @@ import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
import styled from '@emotion/styled';
import { useRecoilCallback } from 'recoil';
import { capitalize } from 'twenty-shared/utils';
import { NotFound } from '~/pages/not-found/NotFound';
const StyledIndexContainer = styled.div`
@@ -102,9 +101,7 @@ export const RecordIndexContainerGater = () => {
instanceId: getActionMenuIdFromRecordIndexId(recordIndexId),
}}
>
<PageTitle
title={`${capitalize(objectMetadataItem.namePlural)}`}
/>
<PageTitle title={objectMetadataItem.labelPlural} />
<RecordIndexPageHeader />
<PageBody>
<StyledIndexContainer
@@ -9,7 +9,7 @@ import { PageHeader } from '@/ui/layout/page/components/PageHeader';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import styled from '@emotion/styled';
import { t } from '@lingui/core/macro';
import { capitalize, isDefined } from 'twenty-shared/utils';
import { isDefined } from 'twenty-shared/utils';
import { useIcons } from 'twenty-ui/display';
const StyledTitleWithSelectedRecords = styled.div`
@@ -44,7 +44,7 @@ export const RecordIndexPageHeader = () => {
const { getIcon } = useIcons();
const Icon = getIcon(objectMetadataItem?.icon);
const label = objectMetadataItem?.labelPlural ?? capitalize(objectNamePlural);
const label = objectMetadataItem?.labelPlural ?? objectNamePlural;
const pageHeaderTitle =
contextStoreNumberOfSelectedRecords > 0 ? (
@@ -10,7 +10,6 @@ import { SelectableListItem } from '@/ui/layout/selectable-list/components/Selec
import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2';
import { capitalize } from 'twenty-shared/utils';
import { Avatar } from 'twenty-ui/display';
import { MenuItemMultiSelectAvatar } from 'twenty-ui/navigation';
import { SearchRecord } from '~/generated-metadata/graphql';
@@ -61,7 +60,7 @@ export const MultipleRecordPickerMenuItemContent = ({
const displayText =
searchRecord.label?.trim() ||
`Untitled ${capitalize(objectMetadataItem.nameSingular)}`;
`Untitled ${objectMetadataItem.labelSingular}`;
return (
<StyledSelectableItem
@@ -11,7 +11,6 @@ import { RecordTitleCellContainerType } from '@/object-record/record-title-cell/
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { FieldMetadataType } from 'twenty-shared/types';
import { capitalize } from 'twenty-shared/utils';
const StyledEditableTitleContainer = styled.div`
align-items: center;
@@ -43,12 +42,12 @@ const StyledPaginationInformation = styled.span`
export const ObjectRecordShowPageBreadcrumb = ({
objectNameSingular,
objectRecordId,
objectLabelPlural,
objectLabel,
labelIdentifierFieldMetadataItem,
}: {
objectNameSingular: string;
objectRecordId: string;
objectLabelPlural: string;
objectLabel: string;
labelIdentifierFieldMetadataItem?: FieldMetadataItem;
}) => {
const { loading } = useFindOneRecord({
@@ -95,7 +94,7 @@ export const ObjectRecordShowPageBreadcrumb = ({
}}
>
{HeaderIcon && <HeaderIcon size={theme.icon.size.md} />}
{capitalize(objectLabelPlural)}
{objectLabel}
<span>{' / '}</span>
</StyledEditableTitlePrefix>
<StyledTitle>
@@ -9,7 +9,7 @@ import { lastShowPageRecordIdState } from '@/object-record/record-field/states/l
import { useRecordIdsFromFindManyCacheRootQuery } from '@/object-record/record-show/hooks/useRecordIdsFromFindManyCacheRootQuery';
import { AppPath } from '@/types/AppPath';
import { useQueryVariablesFromParentView } from '@/views/hooks/useQueryVariablesFromParentView';
import { capitalize, isDefined } from 'twenty-shared/utils';
import { isDefined } from 'twenty-shared/utils';
import { useNavigateApp } from '~/hooks/useNavigateApp';
export const useRecordShowPagePagination = (
@@ -216,13 +216,13 @@ export const useRecordShowPagePagination = (
const rankFoundInView = rankInView > -1;
const objectLabel = capitalize(objectMetadataItem.labelPlural);
const objectLabelPlural = objectMetadataItem.labelPlural;
const totalCount = 1 + Math.max(totalCountBefore, totalCountAfter);
const viewNameWithCount = rankFoundInView
? `${rankInView + 1} of ${totalCount} in ${objectLabel}`
: `${objectLabel} (${totalCount})`;
? `${rankInView + 1} of ${totalCount} in ${objectLabelPlural}`
: `${objectLabelPlural} (${totalCount})`;
return {
viewName: viewNameWithCount,
@@ -17,18 +17,18 @@ export const RecordTableEmptyStateNoGroupNoRecordAtAll = () => {
createNewIndexRecord();
};
const objectLabel = useObjectLabel(objectMetadataItem);
const objectLabelSingular = useObjectLabel(objectMetadataItem);
const buttonTitle = `Add a ${objectLabel}`;
const buttonTitle = `Add a ${objectLabelSingular}`;
const title = getEmptyStateTitle(
objectMetadataItem.nameSingular,
objectLabel,
objectLabelSingular,
);
const subTitle = getEmptyStateSubTitle(
objectMetadataItem.nameSingular,
objectLabel,
objectLabelSingular,
);
return (
@@ -15,11 +15,11 @@ export const RecordTableEmptyStateNoRecordFoundForFilter = () => {
createNewIndexRecord();
};
const objectLabel = useObjectLabel(objectMetadataItem);
const objectLabelSingular = useObjectLabel(objectMetadataItem);
const buttonTitle = `Add a ${objectLabel}`;
const buttonTitle = `Add a ${objectLabelSingular}`;
const title = `No ${objectLabel} found`;
const title = `No ${objectLabelSingular} found`;
const subTitle = 'No records matching the filter criteria were found.';
@@ -7,9 +7,9 @@ import { IconPlus } from 'twenty-ui/display';
export const RecordTableEmptyStateReadOnly = () => {
const { objectMetadataItem } = useRecordTableContextOrThrow();
const objectLabel = useObjectLabel(objectMetadataItem);
const objectLabelSingular = useObjectLabel(objectMetadataItem);
const buttonTitle = `Add a ${objectLabel}`;
const buttonTitle = `Add a ${objectLabelSingular}`;
return (
<RecordTableEmptyStateDisplay
@@ -39,13 +39,13 @@ export const RecordTableEmptyStateSoftDelete = () => {
toggleSoftDeleteFilterState(false);
};
const objectLabel = useObjectLabel(objectMetadataItem);
const objectLabelSingular = useObjectLabel(objectMetadataItem);
return (
<RecordTableEmptyStateDisplay
buttonTitle={'Remove Deleted filter'}
subTitle={'No deleted records matching the filter criteria were found.'}
title={`No Deleted ${objectLabel} found`}
title={`No Deleted ${objectLabelSingular} found`}
ButtonIcon={IconFilterOff}
animatedPlaceholderType="noDeletedRecord"
onClick={handleButtonClick}