Keep fallback record page layouts read-only in edition mode (#20023)

When a record has no associated `pageLayoutId`, the FE falls back to a
hardcoded default page layout (`DEFAULT_*_RECORD_PAGE_LAYOUT`). These
mocks use non-UUID ids for the layout, tabs, and widgets
(e.g. `default-person-page-layout`).

Today nothing distinguishes these fallbacks from real layouts at edit
time. Clicking "Edit layout" flips the global customization mode,
`PageLayoutRecordPageCustomizationSessionRegistrationEffect` registers
the mock id, and on Save `useSaveLayoutCustomization` fires
`UpdatePageLayoutWithTabsAndWidgets` with those mock ids — the BE
rejects them and we end up in a partial-save state (nav / command menu
commit while the page layout fails).

This PR keeps fallback record page layouts in read mode even when
global layout-edition mode is on, and defends the save loop so mock
ids can never be sent to the BE.

## Non editable "base" record page layout (=mock)
<img width="1508" height="616" alt="Screenshot 2026-04-24 at 10 35 02"
src="https://github.com/user-attachments/assets/34b093d1-f4bb-4076-ab8c-ce97ecb945a4"
/>

## Editable record page layout
<img width="1512" height="597" alt="Screenshot 2026-04-24 at 10 35 20"
src="https://github.com/user-attachments/assets/5ff6f3ff-79a5-4e6f-aa1c-01c7e09273b3"
/>
This commit is contained in:
Weiko
2026-04-24 10:54:37 +02:00
committed by GitHub
parent e8f58fd23a
commit 9a9daf77ca
6 changed files with 46 additions and 15 deletions
@@ -16,6 +16,7 @@ import { type DraftPageLayout } from '@/page-layout/types/DraftPageLayout';
import { type PageLayout } from '@/page-layout/types/PageLayout';
import { convertPageLayoutDraftToUpdateInput } from '@/page-layout/utils/convertPageLayoutDraftToUpdateInput';
import { convertPageLayoutToTabLayouts } from '@/page-layout/utils/convertPageLayoutToTabLayouts';
import { isDefaultPageLayoutId } from '@/page-layout/utils/isDefaultPageLayoutId';
import { reInjectDynamicRelationWidgetsFromDraft } from '@/page-layout/utils/reInjectDynamicRelationWidgetsFromDraft';
import { transformPageLayout } from '@/page-layout/utils/transformPageLayout';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
@@ -76,6 +77,10 @@ export const useSaveLayoutCustomization = () => {
let hasAnyFailure = false;
for (const pageLayoutId of activePageLayoutIds) {
if (isDefaultPageLayoutId(pageLayoutId)) {
continue;
}
const draft = store.get(
pageLayoutDraftComponentState.atomFamily({
instanceId: pageLayoutId,