Fix dashboard creation + role permission page design (#18565)

1. **Creating a new dashboard crashes with "Tab not found"** and widgets
can't be added after the crash is prevented.

**Root cause:** `initializePageLayout` wrapped both the persisted and
draft state updates behind an `isDeeplyEqual` guard. After navigation,
`resetPageLayoutEditMode` resets the draft atom to its default but
leaves the persisted atom untouched. On re-initialization,
`isDeeplyEqual` returns true (persisted unchanged), so the draft is
never repopulated. But edit mode is still activated.

**Fix**: Move the draft store.set outside the isDeeplyEqual guard so
it's always set on initialization. Also add a defensive check in
`PageLayoutRendererContent` to prevent the crash when activeTabId
doesn't match available tabs.


https://github.com/user-attachments/assets/bcd69866-63eb-4e5e-a1bb-655e71ba6dc5

2. **Permission role page design broken**
Before
<img width="573" height="1130" alt="role-page-broken"
src="https://github.com/user-attachments/assets/09f60fd2-ef08-4133-bb28-034b15579481"
/>

After
<img width="573" height="266" alt="Capture d’écran 2026-03-11 à 14 25
55"
src="https://github.com/user-attachments/assets/c34f9993-51e1-4108-a7e6-f434f558edfd"
/>
This commit is contained in:
Thomas Trompette
2026-03-11 18:57:54 +01:00
committed by GitHub
parent a024a04e01
commit 2af3121c51
8 changed files with 45 additions and 39 deletions
@@ -1,11 +1,13 @@
import { useBasePageLayout } from '@/page-layout/hooks/useBasePageLayout';
import { usePageLayoutWithRelationWidgets } from '@/page-layout/hooks/usePageLayoutWithRelationWidgets';
import { useSetIsPageLayoutInEditMode } from '@/page-layout/hooks/useSetIsPageLayoutInEditMode';
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
import { pageLayoutIsInitializedComponentState } from '@/page-layout/states/pageLayoutIsInitializedComponentState';
import { pageLayoutPersistedComponentState } from '@/page-layout/states/pageLayoutPersistedComponentState';
import { type PageLayout } from '@/page-layout/types/PageLayout';
import { convertPageLayoutToTabLayouts } from '@/page-layout/utils/convertPageLayoutToTabLayouts';
import { isPageLayoutEmpty } from '@/page-layout/utils/isPageLayoutEmpty';
import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState';
import { useAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentState';
import { useStore } from 'jotai';
@@ -15,12 +17,10 @@ import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
type PageLayoutInitializationQueryEffectProps = {
pageLayoutId: string;
onInitialized?: (pageLayout: PageLayout) => void;
};
export const PageLayoutInitializationQueryEffect = ({
pageLayoutId,
onInitialized,
}: PageLayoutInitializationQueryEffectProps) => {
const [pageLayoutIsInitialized, setPageLayoutIsInitialized] =
useAtomComponentState(pageLayoutIsInitializedComponentState);
@@ -29,6 +29,9 @@ export const PageLayoutInitializationQueryEffect = ({
const pageLayout = usePageLayoutWithRelationWidgets(basePageLayout);
const { setIsPageLayoutInEditMode } =
useSetIsPageLayoutInEditMode(pageLayoutId);
const pageLayoutPersistedComponentCallbackState =
useAtomComponentStateCallbackState(pageLayoutPersistedComponentState);
@@ -48,22 +51,26 @@ export const PageLayoutInitializationQueryEffect = ({
if (!isDeeplyEqual(layout, currentPersisted)) {
store.set(pageLayoutPersistedComponentCallbackState, layout);
store.set(pageLayoutDraftComponentCallbackState, {
id: layout.id,
name: layout.name,
type: layout.type,
objectMetadataId: layout.objectMetadataId,
tabs: layout.tabs,
});
const tabLayouts = convertPageLayoutToTabLayouts(layout);
store.set(pageLayoutCurrentLayoutsComponentCallbackState, tabLayouts);
}
store.set(pageLayoutDraftComponentCallbackState, {
id: layout.id,
name: layout.name,
type: layout.type,
objectMetadataId: layout.objectMetadataId,
tabs: layout.tabs,
});
const tabLayouts = convertPageLayoutToTabLayouts(layout);
store.set(pageLayoutCurrentLayoutsComponentCallbackState, tabLayouts);
setIsPageLayoutInEditMode(isPageLayoutEmpty(layout));
},
[
pageLayoutCurrentLayoutsComponentCallbackState,
pageLayoutDraftComponentCallbackState,
pageLayoutPersistedComponentCallbackState,
setIsPageLayoutInEditMode,
store,
],
);
@@ -71,14 +78,12 @@ export const PageLayoutInitializationQueryEffect = ({
useEffect(() => {
if (!pageLayoutIsInitialized && isDefined(pageLayout)) {
initializePageLayout(pageLayout);
onInitialized?.(pageLayout);
setPageLayoutIsInitialized(true);
}
}, [
initializePageLayout,
pageLayoutIsInitialized,
pageLayout,
onInitialized,
setPageLayoutIsInitialized,
]);