Skip to content

Record page layout edition frontend#17519

Merged
Devessier merged 18 commits intomainfrom
record-page-layout-edition-frontend
Jan 29, 2026
Merged

Record page layout edition frontend#17519
Devessier merged 18 commits intomainfrom
record-page-layout-edition-frontend

Conversation

@Devessier
Copy link
Copy Markdown
Contributor

@Devessier Devessier commented Jan 28, 2026

Hidden behind a new feature flag: IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED

CleanShot.2026-01-28.at.18.05.18.mp4

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Jan 28, 2026

🚀 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.

@Devessier Devessier marked this pull request as ready for review January 28, 2026 17:09
Copilot AI review requested due to automatic review settings January 28, 2026 17:09
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 19 files

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Jan 28, 2026

Greptile Overview

Greptile Summary

This PR implements the frontend for record page layout editing functionality, properly gated behind the new IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED feature flag. The implementation adds three new actions (Edit, Save, Cancel) to the record action menu and refactors several components to support conditional editing states.

Key Changes:

  • Added new feature flag IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED to control the editing feature
  • Implemented three new record page layout actions with proper permission checks and feature flag gating
  • Extracted getRecordPageLayoutId utility function from useRecordPageLayoutId hook for better code reuse
  • Created useRecordPageLayoutIdFromRecordStore hook for actions that need to throw when layout ID is not found
  • Refactored widget components to conditionally disable reordering for record page layouts while keeping it enabled for other layout types
  • Removed deprecated usePageLayoutShouldUseWhiteBackground hook and computed the condition inline
  • Updated styling logic across WidgetCard components to support editable and non-editable states

Code Quality:

  • Follows existing patterns for action registration and feature flag gating
  • Proper error handling with throw-on-error hooks for required data
  • Good separation of concerns with extracted utility functions
  • Consistent with React guidelines (functional components, named exports, object destructuring)

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk - well-structured implementation behind a feature flag
  • The implementation follows established patterns, includes proper feature flag gating, and makes logical refactoring improvements. The changes are isolated to the page layout editing feature with no breaking changes to existing functionality.
  • No files require special attention

Important Files Changed

Filename Overview
packages/twenty-front/src/modules/action-menu/actions/record-actions/constants/DefaultRecordActionsConfig.tsx Added three new record page layout actions (Edit, Save, Cancel) with proper permissions and feature flag gating
packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/record-page-layout-actions/components/EditRecordPageLayoutSingleRecordAction.tsx New action component to enter page layout edit mode, properly gated by feature flag
packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/record-page-layout-actions/components/SaveRecordPageLayoutSingleRecordAction.tsx New action component to save page layout changes with async handling
packages/twenty-front/src/modules/action-menu/actions/record-actions/single-record/record-page-layout-actions/components/CancelRecordPageLayoutSingleRecordAction.tsx New action component to cancel page layout editing and reset draft changes
packages/twenty-front/src/modules/page-layout/hooks/useRecordPageLayoutIdFromRecordStore.ts New hook to retrieve page layout ID from record store, throwing if not found
packages/twenty-front/src/modules/page-layout/utils/getRecordPageLayoutId.ts Extracted utility function for computing page layout ID based on record and object type

Sequence Diagram

sequenceDiagram
    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
Loading

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

6 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +7 to +9
export const useRecordPageLayoutIdFromRecordStoreOrThrow = ({
id,
targetObjectNameSingular,
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +27 to +47
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;
};
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Member

@Weiko Weiko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

type: ActionType.Standard,
scope: ActionScope.RecordSelection,
requiredPermissionFlag: PermissionFlagType.LAYOUTS,
shouldBeRegistered: ({
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we check the flag here too?


const { closeCommandMenu } = useCommandMenu();

const handleClick = async () => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: Those 3 components seem very similar apart from their onClick behavior but I can understand if we want proper separation

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we prefer to strictly separate action components. However, I agree that they are quite similar right now.

@Devessier Devessier added this pull request to the merge queue Jan 29, 2026
Merged via the queue into main with commit b15d092 Jan 29, 2026
74 checks passed
@Devessier Devessier deleted the record-page-layout-edition-frontend branch January 29, 2026 11:24
@twenty-eng-sync
Copy link
Copy Markdown

Hey @Devessier! After you've done the QA of your Pull Request, you can mark it as done here. Thank you!

1 similar comment
@twenty-eng-sync
Copy link
Copy Markdown

Hey @Devessier! After you've done the QA of your Pull Request, you can mark it as done here. Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants