Record page layout edition frontend#17519
Conversation
|
🚀 Preview Environment Ready! Your preview environment is available at: http://bore.pub:57336 This environment will automatically shut down when the PR is closed or after 5 hours. |
Greptile OverviewGreptile SummaryThis PR implements the frontend for record page layout editing functionality, properly gated behind the new Key Changes:
Code Quality:
Confidence Score: 5/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant User
participant EditAction as EditRecordPageLayout<br/>Action
participant SaveAction as SaveRecordPageLayout<br/>Action
participant CancelAction as CancelRecordPageLayout<br/>Action
participant Store as Record Store
participant LayoutHook as useRecordPageLayoutId<br/>Hook
participant LayoutUtil as getRecordPageLayoutId<br/>Util
participant EditMode as Page Layout<br/>Edit Mode State
participant SaveHook as useSavePageLayout
participant ResetHook as useResetDraft<br/>PageLayout
User->>EditAction: Click "Edit Layout" button
EditAction->>Store: Get record from recordStoreFamilyState
EditAction->>LayoutUtil: getRecordPageLayoutId(record)
LayoutUtil-->>EditAction: Return pageLayoutId
EditAction->>EditMode: setIsPageLayoutInEditMode(true)
EditAction->>User: Reset location hash
Note over User,EditMode: Edit mode activated - widgets now draggable
alt User saves changes
User->>SaveAction: Click "Save" button
SaveAction->>Store: Get record from recordStoreFamilyState
SaveAction->>LayoutUtil: getRecordPageLayoutId(record)
LayoutUtil-->>SaveAction: Return pageLayoutId
SaveAction->>SaveHook: savePageLayout()
SaveHook-->>SaveAction: Return {status: 'successful'}
SaveAction->>EditMode: setIsPageLayoutInEditMode(false)
SaveAction->>User: Close command menu
Note over User,EditMode: Changes persisted
else User cancels changes
User->>CancelAction: Click "Cancel" button
CancelAction->>Store: Get record from recordStoreFamilyState
CancelAction->>LayoutUtil: getRecordPageLayoutId(record)
LayoutUtil-->>CancelAction: Return pageLayoutId
CancelAction->>ResetHook: resetDraftPageLayoutToPersistedPageLayout()
CancelAction->>EditMode: setIsPageLayoutInEditMode(false)
CancelAction->>User: Close command menu
Note over User,EditMode: Draft changes discarded
end
|
...ons/single-record/record-page-layout-actions/types/RecordPageLayoutSingleRecordActionKeys.ts
Show resolved
Hide resolved
There was a problem hiding this comment.
Pull request overview
Adds frontend support for editing record page layouts behind a new feature flag (IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED), including new record-level actions and some layout/widget styling adjustments.
Changes:
- Introduces a new feature flag key (
IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED) across server + generated GraphQL enums. - Adds “Edit / Save / Cancel” record actions for record page layout editing, plus record-page-layout-id helpers.
- Updates widget card rendering/styling and vertical list layout behavior to support the editing experience.
Reviewed changes
Copilot reviewed 18 out of 19 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts | Adds server-side enum entry for the new record page layout editing feature flag. |
| packages/twenty-front/src/modules/page-layout/widgets/widget-card/components/WidgetCardHeader.tsx | Adds isReorderEnabled gating for showing the drag grip in edit mode. |
| packages/twenty-front/src/modules/page-layout/widgets/widget-card/components/WidgetCardContent.tsx | Adjusts content styling for editable side-column variant and record-page variant. |
| packages/twenty-front/src/modules/page-layout/widgets/widget-card/components/WidgetCard.tsx | Tweaks background styling differences between editable record-page vs side-column variants. |
| packages/twenty-front/src/modules/page-layout/widgets/components/WidgetRenderer.tsx | Wires isReorderEnabled and passes isEditable down to content; uses PageLayoutType. |
| packages/twenty-front/src/modules/page-layout/utils/getRecordPageLayoutId.ts | New utility to compute the effective record page layout id (record override vs default). |
| packages/twenty-front/src/modules/page-layout/hooks/useRecordPageLayoutIdFromRecordStore.ts | New recoil-backed hook for resolving pageLayoutId from the record store. |
| packages/twenty-front/src/modules/page-layout/hooks/useRecordPageLayoutId.ts | Refactors to reuse getRecordPageLayoutId instead of duplicating mapping logic. |
| packages/twenty-front/src/modules/page-layout/hooks/usePageLayoutShouldUseWhiteBackground.ts | Removes old helper hook; logic is inlined in components. |
| packages/twenty-front/src/modules/page-layout/components/PageLayoutVerticalListViewer.tsx | Inlines white-background logic and adds padding handling based on variant. |
| packages/twenty-front/src/modules/page-layout/components/PageLayoutVerticalListEditor.tsx | Adds isReorderEnabled to disable DnD and aligns variant/background/padding behavior. |
| packages/twenty-front/src/modules/page-layout/components/PageLayoutContent.tsx | Disables reordering for record-page layouts when rendering the vertical list editor. |
| packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/record-page-layout-actions/types/RecordPageLayoutSingleRecordActionKeys.ts | Adds action keys enum for record page layout edit/save/cancel actions. |
| packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/record-page-layout-actions/components/SaveRecordPageLayoutSingleRecordAction.tsx | Adds Save action component, gated by the new feature flag. |
| packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/record-page-layout-actions/components/EditRecordPageLayoutSingleRecordAction.tsx | Adds Edit action component, gated by the new feature flag. |
| packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/record-page-layout-actions/components/CancelRecordPageLayoutSingleRecordAction.tsx | Adds Cancel action component, gated by the new feature flag. |
| packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/DefaultRecordActionsConfig.tsx | Registers the new actions in the default record actions config. |
| packages/twenty-front/src/generated/graphql.ts | Updates generated GraphQL enums to include the new feature flag key. |
| packages/twenty-front/src/generated-metadata/graphql.ts | Updates generated metadata GraphQL enums to include the new feature flag key. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
packages/twenty-front/src/modules/page-layout/widgets/components/WidgetRenderer.tsx
Show resolved
Hide resolved
packages/twenty-front/src/modules/page-layout/components/PageLayoutContent.tsx
Show resolved
Hide resolved
packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts
Show resolved
Hide resolved
| export const useRecordPageLayoutIdFromRecordStoreOrThrow = ({ | ||
| id, | ||
| targetObjectNameSingular, |
There was a problem hiding this comment.
File name suggests a non-throwing hook, but the exported hook is useRecordPageLayoutIdFromRecordStoreOrThrow. For consistency with other *OrThrow hooks in this codebase (e.g., modules/page-layout/hooks/useCurrentPageLayoutOrThrow.ts), consider renaming the file to useRecordPageLayoutIdFromRecordStoreOrThrow.ts or exporting a non-throwing variant from this file.
| export const getRecordPageLayoutId = ({ | ||
| record, | ||
| targetObjectNameSingular, | ||
| }: { | ||
| record: ObjectRecord | null | undefined; | ||
| targetObjectNameSingular: string; | ||
| }): string | null => { | ||
| if (!isDefined(record)) { | ||
| return null; | ||
| } | ||
|
|
||
| if (isDefined(record.pageLayoutId)) { | ||
| return record.pageLayoutId; | ||
| } | ||
|
|
||
| const defaultLayoutId = | ||
| OBJECT_NAME_TO_DEFAULT_LAYOUT_ID[targetObjectNameSingular] ?? | ||
| DEFAULT_RECORD_PAGE_LAYOUT_ID; | ||
|
|
||
| return defaultLayoutId; | ||
| }; |
There was a problem hiding this comment.
This new utility introduces object→default-layout-id mapping logic and fallback behavior that’s relied on by multiple hooks/actions. There are existing unit tests under modules/page-layout/utils/__tests__; adding a small test suite for getRecordPageLayoutId (record-defined vs undefined, record.pageLayoutId override, known object mapping, fallback to DEFAULT_RECORD_PAGE_LAYOUT_ID) would help prevent regressions as more objects/layouts are added.
| type: ActionType.Standard, | ||
| scope: ActionScope.RecordSelection, | ||
| requiredPermissionFlag: PermissionFlagType.LAYOUTS, | ||
| shouldBeRegistered: ({ |
There was a problem hiding this comment.
Should we check the flag here too?
|
|
||
| const { closeCommandMenu } = useCommandMenu(); | ||
|
|
||
| const handleClick = async () => { |
There was a problem hiding this comment.
Note: Those 3 components seem very similar apart from their onClick behavior but I can understand if we want proper separation
There was a problem hiding this comment.
I think we prefer to strictly separate action components. However, I agree that they are quite similar right now.
|
Hey @Devessier! After you've done the QA of your Pull Request, you can mark it as done here. Thank you! |
1 similar comment
|
Hey @Devessier! After you've done the QA of your Pull Request, you can mark it as done here. Thank you! |
Hidden behind a new feature flag:
IS_RECORD_PAGE_LAYOUT_EDITING_ENABLEDCleanShot.2026-01-28.at.18.05.18.mp4