Add record page layout tabs editing (#18702)

- Fix duplication of tabs in dashboards that didn't open the duplicated
tab in the side panel: replaced the logic that used Math.round with an
algorithm that switches the positions of the elements. We will keep
relying on integers, but it will work well in all cases.
- Make dnd work with record page layouts, where there is a pinned tab
- Make tab movements work with pinned tab, too
- Allow user to set a tab as the pinned tab
- Make backend changes to be able to save tabs with `layoutMode`



https://github.com/user-attachments/assets/ce1130fa-71df-49ba-ba2f-6f971e15dd49

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Baptiste Devessier
2026-03-18 15:48:23 +01:00
committed by GitHub
parent f2fa958f20
commit fe4512f80c
77 changed files with 1153 additions and 662 deletions
@@ -1,10 +1,13 @@
import { Command } from '@/command-menu-item/display/components/Command';
import { useSelectedRecordIdOrThrow } from '@/command-menu-item/record/single-record/hooks/useSelectedRecordIdOrThrow';
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { useResetDraftPageLayoutToPersistedPageLayout } from '@/page-layout/hooks/useResetDraftPageLayoutToPersistedPageLayout';
import { useSetIsPageLayoutInEditMode } from '@/page-layout/hooks/useSetIsPageLayoutInEditMode';
import { getTabListInstanceIdFromPageLayoutAndRecord } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutAndRecord';
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
import { useAtomFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilyStateValue';
import { isDefined } from 'twenty-shared/utils';
import { PageLayoutType } from '~/generated-metadata/graphql';
export const CancelDashboardSingleRecordCommand = () => {
const recordId = useSelectedRecordIdOrThrow();
@@ -13,13 +16,28 @@ export const CancelDashboardSingleRecordCommand = () => {
const pageLayoutId = recordStore?.pageLayoutId;
if (!isDefined(pageLayoutId)) {
throw new Error(
'CancelDashboardSingleRecordCommand requires a valid pageLayoutId from the record store.',
);
}
const tabListInstanceId = getTabListInstanceIdFromPageLayoutAndRecord({
pageLayoutId,
layoutType: PageLayoutType.DASHBOARD,
targetRecordIdentifier: { id: recordId, targetObjectNameSingular: '' },
});
const { closeSidePanelMenu } = useSidePanelMenu();
const { setIsPageLayoutInEditMode } =
useSetIsPageLayoutInEditMode(pageLayoutId);
const { resetDraftPageLayoutToPersistedPageLayout } =
useResetDraftPageLayoutToPersistedPageLayout(pageLayoutId);
useResetDraftPageLayoutToPersistedPageLayout({
pageLayoutId,
tabListInstanceId,
});
const handleClick = () => {
closeSidePanelMenu();