04eb913551
## Summary
- Both \`IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED\` and
\`IS_RECORD_PAGE_LAYOUT_GLOBAL_EDITION_ENABLED\` are force-enabled on
every existing workspace by the 1.23.0 upgrade command
\`BackfillRecordPageLayoutsCommand\` and seeded enabled for new
workspaces via \`DEFAULT_FEATURE_FLAGS\` +
\`seed-feature-flags.util.ts\`. They are no longer load-bearing.
- Unwrap all \`if (flag) { … }\` conditionals to their enabled branch on
both server and front.
- Delete legacy fallback files that only the disabled branch reached:
\`PageLayoutRelationWidgetsSyncEffect\`,
\`usePageLayoutWithRelationWidgets\`,
\`reInjectDynamicRelationWidgetsFromDraft\`,
\`injectRelationWidgetsIntoLayout\`, \`isDynamicRelationWidget\` (and
their tests).
- Strip the two \`enableFeatureFlags\` calls from the 1.23 upgrade
command — the page-layout backfill data logic itself is kept intact
since old workspaces upgrading from < 1.23 still need it.
- No DB cleanup migration: stale \`featureFlag\` rows are left in place,
matching the precedent set by #20531 and #20460.
Net diff: 37 files, +106 / -1727.
## Test plan
- [x] \`npx nx typecheck twenty-shared twenty-server twenty-front\` —
all pass
- [x] \`npx nx lint:diff-with-main twenty-server twenty-front\` — all
pass
- [x] \`cd packages/twenty-front && npx jest page-layout\` — 1240 tests,
all pass
- [x] \`cd packages/twenty-server && npx jest
workspace-entity-manager.spec\` — pass
- [ ] Manual smoke: open a record page, verify tabs render and \"Edit
Layout\" command-menu action is available
- [ ] Manual smoke: Settings → Data model → object → Layout tab is
visible (and hidden for remote / Dashboard objects)
- [ ] Manual smoke: edit a tab title, save, reload — confirm persistence
98 lines
3.7 KiB
TypeScript
98 lines
3.7 KiB
TypeScript
import { useBasePageLayout } from '@/page-layout/hooks/useBasePageLayout';
|
|
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 { useAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentState';
|
|
import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState';
|
|
import { useStore } from 'jotai';
|
|
import { useCallback, useEffect } from 'react';
|
|
import { isDefined } from 'twenty-shared/utils';
|
|
import { PageLayoutType } from '~/generated-metadata/graphql';
|
|
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
|
|
|
type PageLayoutInitializationQueryEffectProps = {
|
|
pageLayoutId: string;
|
|
};
|
|
|
|
// oxlint-disable-next-line twenty/effect-components
|
|
export const PageLayoutInitializationQueryEffect = ({
|
|
pageLayoutId,
|
|
}: PageLayoutInitializationQueryEffectProps) => {
|
|
const pageLayout = useBasePageLayout(pageLayoutId);
|
|
|
|
const [pageLayoutIsInitialized, setPageLayoutIsInitialized] =
|
|
useAtomComponentState(pageLayoutIsInitializedComponentState);
|
|
|
|
const { setIsPageLayoutInEditMode } =
|
|
useSetIsPageLayoutInEditMode(pageLayoutId);
|
|
|
|
const pageLayoutPersistedComponentCallbackState =
|
|
useAtomComponentStateCallbackState(pageLayoutPersistedComponentState);
|
|
|
|
const pageLayoutDraftComponentCallbackState =
|
|
useAtomComponentStateCallbackState(pageLayoutDraftComponentState);
|
|
|
|
const pageLayoutCurrentLayoutsComponentCallbackState =
|
|
useAtomComponentStateCallbackState(pageLayoutCurrentLayoutsComponentState);
|
|
|
|
const store = useStore();
|
|
|
|
const initializePageLayout = useCallback(
|
|
(layout: PageLayout) => {
|
|
const currentPersisted = store.get(
|
|
pageLayoutPersistedComponentCallbackState,
|
|
);
|
|
|
|
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,
|
|
defaultTabToFocusOnMobileAndSidePanelId:
|
|
layout.defaultTabToFocusOnMobileAndSidePanelId,
|
|
});
|
|
|
|
const tabLayouts = convertPageLayoutToTabLayouts(layout);
|
|
store.set(pageLayoutCurrentLayoutsComponentCallbackState, tabLayouts);
|
|
|
|
const isDashboardLayout = layout.type === PageLayoutType.DASHBOARD;
|
|
|
|
if (isDashboardLayout) {
|
|
const shouldEnterDashboardEditMode = isPageLayoutEmpty(layout);
|
|
setIsPageLayoutInEditMode(shouldEnterDashboardEditMode);
|
|
}
|
|
},
|
|
[
|
|
pageLayoutCurrentLayoutsComponentCallbackState,
|
|
pageLayoutDraftComponentCallbackState,
|
|
pageLayoutPersistedComponentCallbackState,
|
|
setIsPageLayoutInEditMode,
|
|
store,
|
|
],
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (!pageLayoutIsInitialized && isDefined(pageLayout)) {
|
|
initializePageLayout(pageLayout);
|
|
setPageLayoutIsInitialized(true);
|
|
}
|
|
}, [
|
|
initializePageLayout,
|
|
pageLayoutIsInitialized,
|
|
pageLayout,
|
|
setPageLayoutIsInitialized,
|
|
]);
|
|
|
|
return null;
|
|
};
|