Fix empty record page on system objects for non-English workspace members (#20235)

## Context
Since #19890 (Translate standard page layouts), the server's
`PageLayoutTab.title` resolver translates standard tab titles at query
time. A workspace member in French viewing a `messageChannel` (or any
system object with a standard page layout) receives `tab.title =
"Accueil"` / `"Chronologie"` instead of `"Home"` / `"Timeline"`.

The `SYSTEM_OBJECT_TABS` guard in `PageLayoutTabsRenderer` was comparing
against an English-only literal allow-list, so every tab was dropped,
`sortedTabs` became empty, and `<PageLayoutMainContent />` never mounted
— the record page rendered blank (no fields, timeline, email thread,
etc.).

## Fix
Only run the allow-list filter when the resolved layout is the synthetic
`DEFAULT_RECORD_PAGE_LAYOUT` (the client-side fallback for the few
system objects with no server-side standard page layout config, e.g.
`workspaceMember`, `attachment`, `message`). That layout ships hardcoded
English tabs, so the English allow-list still works in every locale.

System objects that do have a server-side standard page layout
(`messageChannel`, `connectedAccount`, `workflowRun`, …) are no longer
filtered at all, the server only ever persists Home/Timeline/Flow tabs
for them, so no filter is needed.

## Before
<img width="1262" height="722" alt="Screenshot 2026-05-04 at 15 15 54"
src="https://github.com/user-attachments/assets/348c9e9d-0ae1-4046-8ead-470ed8263cb5"
/>


## After
<img width="1281" height="658" alt="Screenshot 2026-05-04 at 15 15 36"
src="https://github.com/user-attachments/assets/7a9953b1-7320-4f1a-8c02-1688d3eda3ae"
/>


## Note
Next step should be to backfill those system objects with real record
page layouts so we can remove this filter logic
This commit is contained in:
Weiko
2026-05-04 15:36:36 +02:00
committed by GitHub
parent 3c7c62c79f
commit 0bb345b75f
@@ -3,6 +3,7 @@ import { type FlatObjectMetadataItem } from '@/metadata-store/types/FlatObjectMe
import { PageLayoutLeftPanel } from '@/page-layout/components/PageLayoutLeftPanel';
import { PageLayoutTabList } from '@/page-layout/components/PageLayoutTabList';
import { PageLayoutTabListEffect } from '@/page-layout/components/PageLayoutTabListEffect';
import { DEFAULT_RECORD_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultRecordPageLayoutId';
import { PAGE_LAYOUT_LEFT_PANEL_CONTAINER_WIDTH } from '@/page-layout/constants/PageLayoutLeftPanelContainerWidth';
import { useCurrentPageLayoutOrThrow } from '@/page-layout/hooks/useCurrentPageLayoutOrThrow';
import { useIsPageLayoutInEditMode } from '@/page-layout/hooks/useIsPageLayoutInEditMode';
@@ -25,7 +26,6 @@ import { styled } from '@linaria/react';
import { isDefined } from 'twenty-shared/utils';
import { useIsMobile } from 'twenty-ui/utilities';
import { FeatureFlagKey } from '~/generated-metadata/graphql';
const StyledContainer = styled.div<{ hasPinnedTab: boolean }>`
display: grid;
grid-template-columns: ${({ hasPinnedTab }) =>
@@ -104,11 +104,15 @@ export const PageLayoutTabsRenderer = () => {
const SYSTEM_OBJECT_TABS = ['Home', 'Timeline', 'Overview', 'Flow'];
const tabsForCurrentObject = isSystemObject
? tabsWithVisibleWidgets.filter((tab) =>
SYSTEM_OBJECT_TABS.includes(tab.title),
)
: tabsWithVisibleWidgets;
const isUsingDefaultRecordPageLayout =
currentPageLayout.id === DEFAULT_RECORD_PAGE_LAYOUT_ID;
const tabsForCurrentObject =
isSystemObject && isUsingDefaultRecordPageLayout
? tabsWithVisibleWidgets.filter((tab) =>
SYSTEM_OBJECT_TABS.includes(tab.title),
)
: tabsWithVisibleWidgets;
const { tabsToRenderInTabList, pinnedLeftTab } = getTabsByDisplayMode({
tabs: tabsForCurrentObject,