Compare commits

...
Author SHA1 Message Date
Sonarly Claude Code 97339f6733 fix: merge page layout tabs from multiple SDK apps on the same object
https://sonarly.com/issue/22151?type=bug

When multiple SDK apps define page layouts for the same system object, only the first layout's tabs are displayed because the frontend selector uses `.find()` instead of collecting and merging all matching layouts.

Fix: Changed `recordPageLayoutByObjectMetadataIdFamilySelector` from using `.find()` (which returns only the first matching layout) to using `.filter()` to collect all page layouts matching the given `objectMetadataId` with type `RECORD_PAGE`.

When multiple layouts exist (from different SDK apps), the selector now merges their tabs into a single `PageLayout` object using the first layout as the base. This preserves the return type `PageLayout | undefined` so no consumer changes are needed.

The merging strategy:
- 0 matches → returns `undefined` (same as before)
- 1 match → returns it directly (same as before, no overhead)
- 2+ matches → uses first layout as base, concatenates all tabs from all layouts

The downstream `PageLayoutTabsRenderer` already sorts tabs by position (`sortTabsByPosition`), so tabs from different apps will be correctly ordered as long as their positions don't conflict. Tab position conflicts would be a separate concern handled at the SDK sync level.
2026-04-06 14:08:51 +00:00
@@ -14,10 +14,32 @@ export const recordPageLayoutByObjectMetadataIdFamilySelector =
({ get }) => {
const pageLayouts = get(pageLayoutsWithRelationsSelector);
return pageLayouts.find(
const matchingLayouts = pageLayouts.filter(
(pageLayout) =>
pageLayout.type === PageLayoutType.RECORD_PAGE &&
pageLayout.objectMetadataId === objectMetadataId,
);
if (matchingLayouts.length === 0) {
return undefined;
}
if (matchingLayouts.length === 1) {
return matchingLayouts[0];
}
// Merge tabs from all matching layouts (e.g. multiple SDK apps
// defining page layouts for the same object) into the first layout.
const [baseLayout, ...otherLayouts] = matchingLayouts;
const mergedTabs = [
...baseLayout.tabs,
...otherLayouts.flatMap((layout) => layout.tabs),
];
return {
...baseLayout,
tabs: mergedTabs,
};
},
});