Files
twenty/packages/twenty-front/src/modules/layout-customization/hooks/useEnterLayoutCustomizationMode.ts
T
Félix Malfait 37c37ddae6 fix(settings-layout): proper loading state + gated customize navigation
Address #20914 review feedback:

- The five detail pages (FrontComponent, Command, SidebarItem, View,
  PageLayout) were inferring loading state from `array.length === 0`,
  which can't distinguish "still loading" from "loaded but empty" and
  leaves the page stuck on the skeleton when the workspace legitimately
  has no items of that kind. Switch to the explicit metadata-store
  status via `metadataStoreStatusFamilySelector` keyed by the
  entity name, treating `'empty'` as loading.
- `useEnterLayoutCustomizationMode` now returns a boolean so the
  Settings/Layout "Customize" button only navigates to `/` when the
  hook actually entered the mode. Previously a rejected entry (e.g.
  unsaved dashboard edits) still triggered the redirect, dropping the
  user out of the page they were warned about.
2026-05-26 20:01:33 +02:00

91 lines
3.6 KiB
TypeScript

import { t } from '@lingui/core/macro';
import { useStore } from 'jotai';
import { useCallback } from 'react';
import { SidePanelPages } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { IconPencil } from 'twenty-ui/display';
import { commandMenuItemsDraftState } from '@/command-menu-item/edit/states/commandMenuItemsDraftState';
import { commandMenuItemsSelector } from '@/command-menu-item/states/commandMenuItemsSelector';
import { activeCustomizationPageLayoutIdsState } from '@/layout-customization/states/activeCustomizationPageLayoutIdsState';
import { isLayoutCustomizationModeEnabledState } from '@/layout-customization/states/isLayoutCustomizationModeEnabledState';
import { navigationMenuItemsDraftState } from '@/navigation-menu-item/common/states/navigationMenuItemsDraftState';
import { navigationMenuItemsSelector } from '@/navigation-menu-item/common/states/navigationMenuItemsSelector';
import { filterWorkspaceNavigationMenuItems } from '@/navigation-menu-item/common/utils/filterWorkspaceNavigationMenuItems';
import { currentPageLayoutIdState } from '@/page-layout/states/currentPageLayoutIdState';
import { isDashboardInEditModeComponentState } from '@/page-layout/states/isDashboardInEditModeComponentState';
import { useNavigateSidePanel } from '@/side-panel/hooks/useNavigateSidePanel';
import { isSidePanelOpenedState } from '@/side-panel/states/isSidePanelOpenedState';
import { sidePanelPageState } from '@/side-panel/states/sidePanelPageState';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
export const useEnterLayoutCustomizationMode = () => {
const store = useStore();
const { navigateSidePanel } = useNavigateSidePanel();
const { enqueueWarningSnackBar } = useSnackBar();
const enterLayoutCustomizationMode = useCallback((): boolean => {
const isLayoutCustomizationModeAlreadyEnabled = store.get(
isLayoutCustomizationModeEnabledState.atom,
);
if (isLayoutCustomizationModeAlreadyEnabled) {
return true;
}
const dashboardPageLayoutIdInEditMode = store.get(
currentPageLayoutIdState.atom,
);
if (isDefined(dashboardPageLayoutIdInEditMode)) {
const isDashboardInEditMode = store.get(
isDashboardInEditModeComponentState.atomFamily({
instanceId: dashboardPageLayoutIdInEditMode,
}),
);
if (isDashboardInEditMode) {
enqueueWarningSnackBar({
message: t`Save or cancel dashboard changes before editing the layout.`,
});
return false;
}
}
const prefetchNavigationMenuItems = store.get(
navigationMenuItemsSelector.atom,
);
const workspaceNavigationMenuItems = filterWorkspaceNavigationMenuItems(
prefetchNavigationMenuItems,
);
store.set(navigationMenuItemsDraftState.atom, workspaceNavigationMenuItems);
const persistedCommandMenuItems = store.get(commandMenuItemsSelector.atom);
store.set(commandMenuItemsDraftState.atom, persistedCommandMenuItems);
store.set(activeCustomizationPageLayoutIdsState.atom, []);
store.set(isLayoutCustomizationModeEnabledState.atom, true);
const isSidePanelOpened = store.get(isSidePanelOpenedState.atom);
const currentSidePanelPage = store.get(sidePanelPageState.atom);
if (
isSidePanelOpened &&
currentSidePanelPage === SidePanelPages.CommandMenuDisplay
) {
navigateSidePanel({
page: SidePanelPages.CommandMenuEdit,
pageTitle: t`Edit actions`,
pageIcon: IconPencil,
resetNavigationStack: true,
});
}
return true;
}, [enqueueWarningSnackBar, navigateSidePanel, store]);
return { enterLayoutCustomizationMode };
};