From befbcef824e57f607d9a6e80e817b3c4d4ced59d Mon Sep 17 00:00:00 2001 From: PandaMan Date: Fri, 13 Feb 2026 08:04:07 -0500 Subject: [PATCH] fix: prevent tab synchronization between different records (#17559) Fixes issue where tabs were synchronized when opening two records of the same type in show page and side panel. The root cause was that tab instance IDs were only based on `pageLayoutId`, causing all records using the same page layout to share the same tab state. This change includes the record ID in the tab instance ID, making tabs unique per record while maintaining backward compatibility for cases where no record ID is available. Fixes #17522 --------- Co-authored-by: Eruis --- .../components/PageLayoutRenderer.tsx | 13 ++++++++++-- .../components/PageLayoutRendererContent.tsx | 13 +++++++----- ...abListInstanceIdFromPageLayoutAndRecord.ts | 21 +++++++++++++++++++ 3 files changed, 40 insertions(+), 7 deletions(-) create mode 100644 packages/twenty-front/src/modules/page-layout/utils/getTabListInstanceIdFromPageLayoutAndRecord.ts diff --git a/packages/twenty-front/src/modules/page-layout/components/PageLayoutRenderer.tsx b/packages/twenty-front/src/modules/page-layout/components/PageLayoutRenderer.tsx index ed01f85795..966fec5ab1 100644 --- a/packages/twenty-front/src/modules/page-layout/components/PageLayoutRenderer.tsx +++ b/packages/twenty-front/src/modules/page-layout/components/PageLayoutRenderer.tsx @@ -4,8 +4,9 @@ import { PageLayoutRendererContent } from '@/page-layout/components/PageLayoutRe import { useSetIsPageLayoutInEditMode } from '@/page-layout/hooks/useSetIsPageLayoutInEditMode'; import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext'; import { type PageLayout } from '@/page-layout/types/PageLayout'; -import { getTabListInstanceIdFromPageLayoutId } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutId'; +import { getTabListInstanceIdFromPageLayoutAndRecord } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutAndRecord'; import { isPageLayoutEmpty } from '@/page-layout/utils/isPageLayoutEmpty'; +import { useLayoutRenderingContext } from '@/ui/layout/contexts/LayoutRenderingContext'; import { TabListComponentInstanceContext } from '@/ui/layout/tab-list/states/contexts/TabListComponentInstanceContext'; import 'react-grid-layout/css/styles.css'; import 'react-resizable/css/styles.css'; @@ -20,6 +21,8 @@ export const PageLayoutRenderer = ({ const { setIsPageLayoutInEditMode } = useSetIsPageLayoutInEditMode(pageLayoutId); + const { targetRecordIdentifier, layoutType } = useLayoutRenderingContext(); + const onInitialized = (pageLayout: PageLayout) => { if (isPageLayoutEmpty(pageLayout)) { setIsPageLayoutInEditMode(true); @@ -28,6 +31,12 @@ export const PageLayoutRenderer = ({ } }; + const tabListInstanceId = getTabListInstanceIdFromPageLayoutAndRecord({ + pageLayoutId, + layoutType, + targetRecordIdentifier, + }); + return ( { const { currentPageLayout } = useCurrentPageLayout(); - const { isInRightDrawer } = useLayoutRenderingContext(); + const { isInRightDrawer, layoutType, targetRecordIdentifier } = + useLayoutRenderingContext(); const isPageLayoutInEditMode = useRecoilComponentValue( isPageLayoutInEditModeComponentState, @@ -104,9 +105,11 @@ export const PageLayoutRendererContent = () => { isInRightDrawer, }); - const tabListInstanceId = getTabListInstanceIdFromPageLayoutId( - currentPageLayout.id, - ); + const tabListInstanceId = getTabListInstanceIdFromPageLayoutAndRecord({ + pageLayoutId: currentPageLayout.id, + layoutType, + targetRecordIdentifier, + }); const sortedTabs = sortTabsByPosition(tabsToRenderInTabList); diff --git a/packages/twenty-front/src/modules/page-layout/utils/getTabListInstanceIdFromPageLayoutAndRecord.ts b/packages/twenty-front/src/modules/page-layout/utils/getTabListInstanceIdFromPageLayoutAndRecord.ts new file mode 100644 index 0000000000..3729594997 --- /dev/null +++ b/packages/twenty-front/src/modules/page-layout/utils/getTabListInstanceIdFromPageLayoutAndRecord.ts @@ -0,0 +1,21 @@ +import { type TargetRecordIdentifier } from '@/ui/layout/contexts/TargetRecordIdentifier'; +import { type LayoutRenderingContextType } from '@/ui/layout/contexts/LayoutRenderingContext'; +import { getTabListInstanceIdFromPageLayoutId } from './getTabListInstanceIdFromPageLayoutId'; +import { isDefined } from 'twenty-shared/utils'; + +export const getTabListInstanceIdFromPageLayoutAndRecord = ({ + pageLayoutId, + layoutType, + targetRecordIdentifier, +}: { + pageLayoutId: string; + layoutType: LayoutRenderingContextType['layoutType']; + targetRecordIdentifier?: TargetRecordIdentifier; +}) => { + // Include record ID in tab instance ID to prevent tab synchronization between different records + // Only for RECORD_PAGE layouts, as DASHBOARD layouts are standalone + const recordId = + layoutType === 'RECORD_PAGE' ? targetRecordIdentifier?.id : undefined; + const baseInstanceId = getTabListInstanceIdFromPageLayoutId(pageLayoutId); + return isDefined(recordId) ? `${baseInstanceId}-${recordId}` : baseInstanceId; +};