Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 20, 2026

Note

This code is temporary. It will be dropped once Record Page Layouts can be fully configured.

Before

CleanShot.2026-01-20.at.11.15.58.mp4

After

CleanShot 2026-01-20 at 15 17 15@2x

The usePageLayoutWithRelationWidgets hook was appending relation widgets to the end of the widget list. This resulted in incorrect ordering where relation widgets appeared after Notes and other widgets instead of immediately following the FIELDS widget.

Changes

  • Widget injection logic: Modified injectRelationWidgetsIntoLayout to find the first FIELDS widget and insert relation widgets immediately after it, rather than appending to end
  • Notes positioning: Extract and reposition NOTES widget to appear after all relation widgets, maintaining correct semantic order: FIELDS → Relations → Notes → Other widgets
  • Fallback behavior: When no FIELDS widget exists, append relation widgets to end as before
  • Test coverage: Added 6 test cases covering injection order, Notes positioning, and edge cases (missing widgets, empty relations, non-record pages)

Example

Before:

[FIELDS, NOTES, GRAPH] → [FIELDS, NOTES, GRAPH, Relation1, Relation2]

After:

[FIELDS, NOTES, GRAPH] → [FIELDS, Relation1, Relation2, NOTES, GRAPH]

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • googlechromelabs.github.io
    • Triggering command: /usr/local/bin/node /usr/local/bin/node install.mjs (dns block)
  • https://storage.googleapis.com/chrome-for-testing-public/127.0.6533.88/linux64/chrome-headless-shell-linux64.zip
    • Triggering command: /usr/local/bin/node /usr/local/bin/node install.mjs (http block)
  • https://storage.googleapis.com/chrome-for-testing-public/127.0.6533.88/linux64/chrome-linux64.zip
    • Triggering command: /usr/local/bin/node /usr/local/bin/node install.mjs (http block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

Start implementation

[Chronological Review: The conversation began with the user requesting an update to the `usePageLayoutWithRelationWidgets` function to inject relation widgets after the first FIELDS widget and to position a Note widget after all relation widgets. The user then asked for a plan without comments, followed by a request to start the implementation of the plan.]

[Intent Mapping:

  1. "Update #sym:usePageLayoutWithRelationWidgets. We should inject the relation widgets directly after the first FIELDS widget."
  2. "We want the Note widget to be after all the relation widgets we inject."
  3. "Prepare a plan. Don't write any comment."
  4. "Start implementation."]

[Technical Inventory:

  • Function: usePageLayoutWithRelationWidgets
  • Context: This function is part of a page layout management system, likely dealing with dynamic widget placement based on certain conditions.]

[Code Archaeology:

  • File: /Users/devessier/Web/twenty/packages/twenty-front/src/modules/page-layout/hooks/usePageLayoutWithRelationWidgets.ts
  • Function Purpose: To manage the layout of widgets on a page, specifically handling relation widgets and their placement relative to other widgets.]

[Progress Assessment:

  • Completed: User has defined the requirements for widget placement.
  • Pending: Implementation of the specified changes to the usePageLayoutWithRelationWidgets function.]

[Context Validation: All necessary context for continuing the implementation of the widget placement logic is captured, including the specific order of widget injection.]

[Recent Commands Analysis:

  • Last Agent Commands: The user requested to "Start implementation."
  • Tool Results Summary: No specific tool results were generated as the conversation focused on planning and implementation requests.
  • Pre-Summary State: The agent was actively preparing to implement the changes to the usePageLayoutWithRelationWidgets function when the token budget was exceeded.
  • Operation Context: The commands executed were directly related to the user's goal of modifying the widget layout in the application.]
1. Conversation Overview: - Primary Objectives: - "Update #sym:usePageLayoutWithRelationWidgets. We should inject the relation widgets directly after the first FIELDS widget." - "We want the Note widget to be after all the relation widgets we inject." - "Prepare a plan. Don't write any comment." - "Start implementation." - Session Context: The user is focused on modifying the widget layout in a page layout management system, specifically the order of relation and Note widgets. - User Intent Evolution: The user transitioned from defining requirements to requesting a plan and then moving directly to implementation.
  1. Technical Foundation:
  • Function: usePageLayoutWithRelationWidgets
  • Purpose: To manage the layout of widgets on a page, particularly the placement of relation widgets.
  1. Codebase Status:
  • File Name: usePageLayoutWithRelationWidgets.ts
  • Purpose: To handle the layout of widgets dynamically based on specified rules.
  • Current State: Awaiting implementation of the user-defined widget placement logic.
  • Key Code Segments: The function signature is defined, but the implementation details are yet to be added.
  1. Problem Resolution:
  • Issues Encountered: No specific technical problems were reported; the focus was on planning and implementation.
  • Solutions Implemented: None yet, as the implementation phase has just begun.
  • Debugging Context: No ongoing troubleshooting efforts were mentioned.
  • Lessons Learned: The importance of clear widget placement requirements was emphasized.
  1. Progress Tracking:
  • Completed Tasks: User has articulated the requirements for widget placement.
  • Partially Complete Work: Implementation of the specified changes is pending.
  • Validated Outcomes: No features have been confirmed working yet as implementation has not started.
  1. Active Work State:
  • Current Focus: The user is preparing to implement the changes to the usePageLayoutWithRelationWidgets function.
  • Recent Context: The user has defined the order of widget placement and is ready to start coding.
  • Working Code: The function is currently defined but lacks the implementation logic.
  • Immediate Context: The user is focused on implementing the logic for injecting relation widgets and positioning the Note widget.
  1. Recent Operations:
  • Last Agent Commands: "Start implementation."
  • Tool Results Summary: No specific results were generated; the focus was on user requests.
  • Pre-Summary State: The agent was preparing to implement the changes to the usePageLayoutWithRelationWidgets function.
  • Operation Context: The commands executed were directly related to the user's goal of modifying the widget layout.
  1. Continuation Plan:
  • Pending Task 1: Implement the logic to inject relation widgets after the first FIELDS widget.
  • Pending Task 2: Ensure the Note widget is positioned after all relation widgets...

Created from VS Code.


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

…et repositioning

Co-authored-by: Devessier <29370468+Devessier@users.noreply.github.com>
Copilot AI changed the title [WIP] Update usePageLayoutWithRelationWidgets function for widget placement Fix widget injection order: place relation widgets after FIELDS, Notes after relations Jan 20, 2026
Copilot AI requested a review from Devessier January 20, 2026 11:49
@github-actions
Copy link
Contributor

github-actions bot commented Jan 20, 2026

🚀 Preview Environment Ready!

Your preview environment is available at: http://bore.pub:27895

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 20, 2026 14:21
Copilot AI review requested due to automatic review settings January 20, 2026 14:21
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Jan 20, 2026

Greptile Summary

Fixes widget injection order in record page layouts by placing relation widgets directly after the FIELDS widget and repositioning the NOTES widget to appear after all relation widgets. The implementation includes comprehensive test coverage with 6 test cases.

Key Changes:

  • Modified injectRelationWidgetsIntoLayout to find the first FIELDS widget and insert relation widgets immediately after it
  • Extracts NOTES widget from its original position and repositions it after relation widgets
  • Maintains correct order: FIELDS → Relations → NOTES → Other widgets
  • Added fallback behavior when no FIELDS widget exists (appends to end)

Critical Issue Found:

  • Logic bug when NOTES widget appears before FIELDS widget: the code will incorrectly handle noteWidgetIndex < firstFieldsWidgetIndex, causing widgets to be lost or duplicated
  • Missing test coverage for this edge case

Confidence Score: 3/5

  • This PR has a logic bug that needs fixing before merge
  • The widget injection logic contains a critical bug when NOTES widget appears before FIELDS widget. While this may be an uncommon scenario in practice, the code should handle it correctly. The test suite is comprehensive but misses this edge case. The rest of the implementation is clean and well-structured.
  • Pay close attention to packages/twenty-front/src/modules/page-layout/hooks/usePageLayoutWithRelationWidgets.ts lines 89-101

Important Files Changed

Filename Overview
packages/twenty-front/src/modules/page-layout/hooks/usePageLayoutWithRelationWidgets.ts Implements widget injection logic to place relation widgets after FIELDS widget and reposition NOTES widget after relations. Logic is correct and well-tested.
packages/twenty-front/src/modules/page-layout/hooks/tests/usePageLayoutWithRelationWidgets.test.tsx Comprehensive test suite covering all edge cases including widget ordering, missing FIELDS widget, missing NOTES widget, empty relations, and non-record pages.

Sequence Diagram

sequenceDiagram
    participant Hook as usePageLayoutWithRelationWidgets
    participant Context as LayoutRenderingContext
    participant Metadata as useFieldListFieldMetadataItems
    participant Inject as injectRelationWidgetsIntoLayout

    Hook->>Context: Get targetRecordIdentifier & layoutType
    Hook->>Metadata: Get boxedRelationFieldMetadataItems
    
    alt basePageLayout is undefined
        Hook-->>Hook: Return undefined
    else layoutType is not RECORD_PAGE
        Hook-->>Hook: Return unchanged basePageLayout
    else Normal flow
        Hook->>Inject: Pass layout + relation fields
        
        alt No relation fields
            Inject-->>Hook: Return unchanged layout
        else Has relation fields
            Inject->>Inject: Find first FIELDS widget
            
            alt No FIELDS widget found
                Inject->>Inject: Append relation widgets to end
            else FIELDS widget found
                Inject->>Inject: Find NOTES widget index
                Inject->>Inject: Slice widgets: before FIELDS
                Inject->>Inject: Slice widgets: after FIELDS (exclude NOTES)
                Inject->>Inject: Extract NOTES widget if exists
                Inject->>Inject: Build: [before] + [relations] + [NOTES] + [after]
            end
            
            Inject-->>Hook: Return modified layout
        end
        
        Hook-->>Hook: Return final layout
    end
Loading

Copy link
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.

2 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +89 to +98
const widgetsAfterRelation =
noteWidgetIndex === -1
? tab.widgets.slice(firstFieldsWidgetIndex + 1)
: [
...tab.widgets.slice(
firstFieldsWidgetIndex + 1,
noteWidgetIndex,
),
...tab.widgets.slice(noteWidgetIndex + 1),
];
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: Logic bug when noteWidgetIndex < firstFieldsWidgetIndex. If the NOTES widget appears before FIELDS widget, widgetsAfterRelation will incorrectly slice and produce wrong output.

Example: widgets = [NOTES, FIELDS, OTHER]

  • noteWidgetIndex = 0, firstFieldsWidgetIndex = 1
  • Line 93-96: slice(2, 0) returns [], then slice(1) returns [OTHER]
  • Result: widgetsAfterRelation = [OTHER] (NOTES is lost)
Suggested change
const widgetsAfterRelation =
noteWidgetIndex === -1
? tab.widgets.slice(firstFieldsWidgetIndex + 1)
: [
...tab.widgets.slice(
firstFieldsWidgetIndex + 1,
noteWidgetIndex,
),
...tab.widgets.slice(noteWidgetIndex + 1),
];
const widgetsAfterRelation =
noteWidgetIndex === -1 || noteWidgetIndex < firstFieldsWidgetIndex
? tab.widgets.slice(firstFieldsWidgetIndex + 1)
: [
...tab.widgets.slice(
firstFieldsWidgetIndex + 1,
noteWidgetIndex,
),
...tab.widgets.slice(noteWidgetIndex + 1),
];
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/twenty-front/src/modules/page-layout/hooks/usePageLayoutWithRelationWidgets.ts
Line: 89:98

Comment:
**logic:** Logic bug when `noteWidgetIndex < firstFieldsWidgetIndex`. If the NOTES widget appears before FIELDS widget, `widgetsAfterRelation` will incorrectly slice and produce wrong output.

Example: widgets = `[NOTES, FIELDS, OTHER]`
- `noteWidgetIndex = 0`, `firstFieldsWidgetIndex = 1`
- Line 93-96: `slice(2, 0)` returns `[]`, then `slice(1)` returns `[OTHER]`
- Result: `widgetsAfterRelation = [OTHER]` (NOTES is lost)

```suggestion
        const widgetsAfterRelation =
          noteWidgetIndex === -1 || noteWidgetIndex < firstFieldsWidgetIndex
            ? tab.widgets.slice(firstFieldsWidgetIndex + 1)
            : [
                ...tab.widgets.slice(
                  firstFieldsWidgetIndex + 1,
                  noteWidgetIndex,
                ),
                ...tab.widgets.slice(noteWidgetIndex + 1),
              ];
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +100 to +101
const noteWidget =
noteWidgetIndex !== -1 ? [tab.widgets[noteWidgetIndex]] : [];
Copy link
Contributor

Choose a reason for hiding this comment

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

logic: This will also extract NOTES widget incorrectly when noteWidgetIndex < firstFieldsWidgetIndex. The NOTES widget should only be repositioned if it appears after the FIELDS widget.

Suggested change
const noteWidget =
noteWidgetIndex !== -1 ? [tab.widgets[noteWidgetIndex]] : [];
const noteWidget =
noteWidgetIndex !== -1 && noteWidgetIndex > firstFieldsWidgetIndex
? [tab.widgets[noteWidgetIndex]]
: [];
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/twenty-front/src/modules/page-layout/hooks/usePageLayoutWithRelationWidgets.ts
Line: 100:101

Comment:
**logic:** This will also extract NOTES widget incorrectly when `noteWidgetIndex < firstFieldsWidgetIndex`. The NOTES widget should only be repositioned if it appears after the FIELDS widget.

```suggestion
        const noteWidget =
          noteWidgetIndex !== -1 && noteWidgetIndex > firstFieldsWidgetIndex
            ? [tab.widgets[noteWidgetIndex]]
            : [];
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +289 to +316
it('should handle layout without Note widget', () => {
const layoutWithoutNotes: PageLayout = {
...mockBasePageLayout,
tabs: [
{
...mockBasePageLayout.tabs[0],
widgets: [
mockBasePageLayout.tabs[0].widgets[0], // FIELDS widget
mockBasePageLayout.tabs[0].widgets[2], // OTHER widget
],
},
],
};

const { result } = renderHook(() =>
usePageLayoutWithRelationWidgets(layoutWithoutNotes),
);

const firstTab = result.current?.tabs[0];
const widgets = firstTab?.widgets || [];

expect(widgets.length).toBe(4); // 1 FIELDS + 2 relation + 1 OTHER

expect(widgets[0].type).toBe(WidgetType.FIELDS);
expect(widgets[1].type).toBe(WidgetType.FIELD);
expect(widgets[2].type).toBe(WidgetType.FIELD);
expect(widgets[3].type).toBe(WidgetType.GRAPH);
});
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Add test case for edge case where NOTES widget appears before FIELDS widget (e.g., [NOTES, FIELDS, OTHER]). Current tests don't cover this scenario, which would expose the logic bug at lines 89-101.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/twenty-front/src/modules/page-layout/hooks/__tests__/usePageLayoutWithRelationWidgets.test.tsx
Line: 289:316

Comment:
**style:** Add test case for edge case where NOTES widget appears before FIELDS widget (e.g., `[NOTES, FIELDS, OTHER]`). Current tests don't cover this scenario, which would expose the logic bug at lines 89-101.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
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

This PR fixes the widget injection order in page layouts to ensure relation widgets appear immediately after the FIELDS widget, and NOTES widgets are repositioned after all relation widgets. This addresses an issue where relation widgets were previously appended to the end of the widget list.

Changes:

  • Modified injectRelationWidgetsIntoLayout to insert relation widgets after the first FIELDS widget instead of appending to the end
  • Added logic to extract and reposition NOTES widgets after relation widgets
  • Added comprehensive test suite with 6 test cases covering various scenarios

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
packages/twenty-front/src/modules/page-layout/hooks/usePageLayoutWithRelationWidgets.ts Implements new widget injection logic to place relation widgets after FIELDS and NOTES after relations
packages/twenty-front/src/modules/page-layout/hooks/__tests__/usePageLayoutWithRelationWidgets.test.tsx Adds comprehensive test coverage for the new injection behavior and edge cases

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +81 to +83
const noteWidgetIndex = tab.widgets.findIndex(
(widget) => widget.type === WidgetType.NOTES,
);
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

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

The implementation uses findIndex to locate the NOTES widget, which only finds the first occurrence. If there are multiple NOTES widgets in the layout, only the first one will be repositioned after the relation widgets, while subsequent NOTES widgets will remain in their original positions. This could lead to inconsistent widget ordering. Consider whether this is the intended behavior or if all NOTES widgets should be repositioned, or if the presence of multiple NOTES widgets should be prevented.

Copilot uses AI. Check for mistakes.
Comment on lines +81 to +98
const noteWidgetIndex = tab.widgets.findIndex(
(widget) => widget.type === WidgetType.NOTES,
);

const widgetsBeforeRelation = tab.widgets.slice(
0,
firstFieldsWidgetIndex + 1,
);
const widgetsAfterRelation =
noteWidgetIndex === -1
? tab.widgets.slice(firstFieldsWidgetIndex + 1)
: [
...tab.widgets.slice(
firstFieldsWidgetIndex + 1,
noteWidgetIndex,
),
...tab.widgets.slice(noteWidgetIndex + 1),
];
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

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

The logic for extracting widgets after the FIELDS widget has an issue when a NOTES widget exists between FIELDS and the end of the list. When noteWidgetIndex !== -1, the code extracts widgets between FIELDS and NOTES (lines 93-96), then widgets after NOTES (line 97). However, if the NOTES widget is immediately after the FIELDS widget (e.g., at index firstFieldsWidgetIndex + 1), the slice on lines 93-96 will have firstFieldsWidgetIndex + 1 as start and noteWidgetIndex as end with the same value, resulting in an empty array. This is actually correct behavior, but the logic could be clearer.

More critically, the current implementation doesn't handle the case where NOTES appears BEFORE the FIELDS widget. In that scenario, noteWidgetIndex could be less than firstFieldsWidgetIndex, which would cause the slicing logic to behave incorrectly. The slice from firstFieldsWidgetIndex + 1 to noteWidgetIndex would produce an empty array (which is fine), but then the NOTES widget would still be repositioned after relations even though it was originally before FIELDS, changing the intended layout unexpectedly.

Copilot uses AI. Check for mistakes.
Comment on lines +289 to +317
it('should handle layout without Note widget', () => {
const layoutWithoutNotes: PageLayout = {
...mockBasePageLayout,
tabs: [
{
...mockBasePageLayout.tabs[0],
widgets: [
mockBasePageLayout.tabs[0].widgets[0], // FIELDS widget
mockBasePageLayout.tabs[0].widgets[2], // OTHER widget
],
},
],
};

const { result } = renderHook(() =>
usePageLayoutWithRelationWidgets(layoutWithoutNotes),
);

const firstTab = result.current?.tabs[0];
const widgets = firstTab?.widgets || [];

expect(widgets.length).toBe(4); // 1 FIELDS + 2 relation + 1 OTHER

expect(widgets[0].type).toBe(WidgetType.FIELDS);
expect(widgets[1].type).toBe(WidgetType.FIELD);
expect(widgets[2].type).toBe(WidgetType.FIELD);
expect(widgets[3].type).toBe(WidgetType.GRAPH);
});
});
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

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

Missing test coverage for the edge case where a NOTES widget appears before the FIELDS widget in the original layout. This scenario would expose a bug in the repositioning logic where the NOTES widget would be incorrectly moved after the relation widgets even though it was originally positioned before FIELDS. A test case should be added to verify this edge case is handled correctly.

Copilot uses AI. Check for mistakes.
Copy link
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.

1 issue found across 2 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="packages/twenty-front/src/modules/page-layout/hooks/usePageLayoutWithRelationWidgets.ts">

<violation number="1" location="packages/twenty-front/src/modules/page-layout/hooks/usePageLayoutWithRelationWidgets.ts:85">
P2: NOTE widgets that appear before the first FIELDS widget will be duplicated (and FIELDS may be duplicated) because the NOTE is not removed from widgetsBeforeRelation while also being reinserted. Filter NOTE out of both slices so it can be reinserted exactly once after relations regardless of its original position.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment on lines +85 to +101
const widgetsBeforeRelation = tab.widgets.slice(
0,
firstFieldsWidgetIndex + 1,
);
const widgetsAfterRelation =
noteWidgetIndex === -1
? tab.widgets.slice(firstFieldsWidgetIndex + 1)
: [
...tab.widgets.slice(
firstFieldsWidgetIndex + 1,
noteWidgetIndex,
),
...tab.widgets.slice(noteWidgetIndex + 1),
];

const noteWidget =
noteWidgetIndex !== -1 ? [tab.widgets[noteWidgetIndex]] : [];
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 20, 2026

Choose a reason for hiding this comment

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

P2: NOTE widgets that appear before the first FIELDS widget will be duplicated (and FIELDS may be duplicated) because the NOTE is not removed from widgetsBeforeRelation while also being reinserted. Filter NOTE out of both slices so it can be reinserted exactly once after relations regardless of its original position.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/twenty-front/src/modules/page-layout/hooks/usePageLayoutWithRelationWidgets.ts, line 85:

<comment>NOTE widgets that appear before the first FIELDS widget will be duplicated (and FIELDS may be duplicated) because the NOTE is not removed from widgetsBeforeRelation while also being reinserted. Filter NOTE out of both slices so it can be reinserted exactly once after relations regardless of its original position.</comment>

<file context>
@@ -67,9 +67,47 @@ const injectRelationWidgetsIntoLayout = (
+          (widget) => widget.type === WidgetType.NOTES,
+        );
+
+        const widgetsBeforeRelation = tab.widgets.slice(
+          0,
+          firstFieldsWidgetIndex + 1,
</file context>
Suggested change
const widgetsBeforeRelation = tab.widgets.slice(
0,
firstFieldsWidgetIndex + 1,
);
const widgetsAfterRelation =
noteWidgetIndex === -1
? tab.widgets.slice(firstFieldsWidgetIndex + 1)
: [
...tab.widgets.slice(
firstFieldsWidgetIndex + 1,
noteWidgetIndex,
),
...tab.widgets.slice(noteWidgetIndex + 1),
];
const noteWidget =
noteWidgetIndex !== -1 ? [tab.widgets[noteWidgetIndex]] : [];
const widgetsBeforeRelation = tab.widgets
.slice(0, firstFieldsWidgetIndex + 1)
.filter((_, index) => index !== noteWidgetIndex);
const widgetsAfterRelation = tab.widgets
.slice(firstFieldsWidgetIndex + 1)
.filter(
(_, index) =>
index + firstFieldsWidgetIndex + 1 !== noteWidgetIndex,
);
const noteWidget =
noteWidgetIndex !== -1 ? [tab.widgets[noteWidgetIndex]] : [];
Fix with Cubic

Comment on lines 70 to 104
const firstFieldsWidgetIndex = tab.widgets.findIndex(
(widget) => widget.type === WidgetType.FIELDS,
);

if (firstFieldsWidgetIndex === -1) {
return {
...tab,
widgets: [...tab.widgets, ...relationWidgets],
};
}

const noteWidgetIndex = tab.widgets.findIndex(
(widget) => widget.type === WidgetType.NOTES,
);

const widgetsBeforeRelation = tab.widgets.slice(
0,
firstFieldsWidgetIndex + 1,
);
const widgetsAfterRelation =
noteWidgetIndex === -1
? tab.widgets.slice(firstFieldsWidgetIndex + 1)
: [
...tab.widgets.slice(
firstFieldsWidgetIndex + 1,
noteWidgetIndex,
),
...tab.widgets.slice(noteWidgetIndex + 1),
];

const noteWidget =
noteWidgetIndex !== -1 ? [tab.widgets[noteWidgetIndex]] : [];

return {
...tab,
Copy link
Contributor

Choose a reason for hiding this comment

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

You need to move that in a unit tested utils

Comment on lines +81 to +83
const noteWidgetIndex = tab.widgets.findIndex(
(widget) => widget.type === WidgetType.NOTES,
);
Copy link
Contributor

Choose a reason for hiding this comment

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

you seems to assume there is maximum one WidgetType.NOTES is this the case?

Comment on lines +89 to +99
const widgetsAfterRelation =
noteWidgetIndex === -1
? tab.widgets.slice(firstFieldsWidgetIndex + 1)
: [
...tab.widgets.slice(
firstFieldsWidgetIndex + 1,
noteWidgetIndex,
),
...tab.widgets.slice(noteWidgetIndex + 1),
];

Copy link
Contributor

Choose a reason for hiding this comment

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

hard to understand to be honest. if there is no note widget then widget after relation in the next widget after fields? it looks not obvious i think we need at least some comment to explain business hypothesis here

@github-actions
Copy link
Contributor

github-actions bot commented Jan 20, 2026

Fails
🚫

node failed.

Log

Details
�[31mError: �[39m RequestError [HttpError]: Validation Failed: {"message":"The listed users cannot be searched either because the users do not exist or you do not have permission to view the users.","resource":"Search","field":"q","code":"invalid"} - https://docs.github.com/v3/search/
    at /home/runner/work/twenty/twenty/node_modules/�[4m@octokit/request�[24m/dist-node/index.js:125:21
�[90m    at process.processTicksAndRejections (node:internal/process/task_queues:103:5)�[39m {
  status: �[33m422�[39m,
  response: {
    url: �[32m'https://api.github.com/search/issues?q=is%3Apr%20author%3ACopilot%20is%3Aclosed%20repo%3Atwentyhq%2Ftwenty&per_page=2&page=1'�[39m,
    status: �[33m422�[39m,
    headers: {
      �[32m'access-control-allow-origin'�[39m: �[32m'*'�[39m,
      �[32m'access-control-expose-headers'�[39m: �[32m'ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset'�[39m,
      �[32m'cache-control'�[39m: �[32m'no-cache'�[39m,
      �[32m'content-length'�[39m: �[32m'300'�[39m,
      �[32m'content-security-policy'�[39m: �[32m"default-src 'none'"�[39m,
      �[32m'content-type'�[39m: �[32m'application/json; charset=utf-8'�[39m,
      date: �[32m'Wed, 21 Jan 2026 08:50:20 GMT'�[39m,
      �[32m'referrer-policy'�[39m: �[32m'origin-when-cross-origin, strict-origin-when-cross-origin'�[39m,
      server: �[32m'github.com'�[39m,
      �[32m'strict-transport-security'�[39m: �[32m'max-age=31536000; includeSubdomains; preload'�[39m,
      vary: �[32m'Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With'�[39m,
      �[32m'x-accepted-github-permissions'�[39m: �[32m'allows_permissionless_access=true'�[39m,
      �[32m'x-content-type-options'�[39m: �[32m'nosniff'�[39m,
      �[32m'x-frame-options'�[39m: �[32m'deny'�[39m,
      �[32m'x-github-api-version-selected'�[39m: �[32m'2022-11-28'�[39m,
      �[32m'x-github-media-type'�[39m: �[32m'github.v3; format=json'�[39m,
      �[32m'x-github-request-id'�[39m: �[32m'8411:B3230:2E347AB:C92B80C:6970934C'�[39m,
      �[32m'x-ratelimit-limit'�[39m: �[32m'30'�[39m,
      �[32m'x-ratelimit-remaining'�[39m: �[32m'30'�[39m,
      �[32m'x-ratelimit-reset'�[39m: �[32m'1768985480'�[39m,
      �[32m'x-ratelimit-resource'�[39m: �[32m'search'�[39m,
      �[32m'x-ratelimit-used'�[39m: �[32m'1'�[39m,
      �[32m'x-xss-protection'�[39m: �[32m'0'�[39m
    },
    data: {
      message: �[32m'Validation Failed'�[39m,
      errors: �[36m[Array]�[39m,
      documentation_url: �[32m'https://docs.github.com/v3/search/'�[39m,
      status: �[32m'422'�[39m
    }
  },
  request: {
    method: �[32m'GET'�[39m,
    url: �[32m'https://api.github.com/search/issues?q=is%3Apr%20author%3ACopilot%20is%3Aclosed%20repo%3Atwentyhq%2Ftwenty&per_page=2&page=1'�[39m,
    headers: {
      accept: �[32m'application/vnd.github.v3+json'�[39m,
      �[32m'user-agent'�[39m: �[32m'octokit-rest.js/20.1.2 octokit-core.js/5.2.2 Node.js/24'�[39m,
      authorization: �[32m'token [REDACTED]'�[39m
    },
    request: { hook: �[36m[Function: bound bound register]�[39m }
  }
}
danger-results://tmp/danger-results-c48618e4.json

Generated by 🚫 dangerJS against adbce8e

Copy link
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.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="packages/twenty-front/src/modules/page-layout/hooks/usePageLayoutWithRelationWidgets.ts">

<violation number="1" location="packages/twenty-front/src/modules/page-layout/hooks/usePageLayoutWithRelationWidgets.ts:82">
P3: Misleading comment: Notes are inserted after relation widgets, but the comment says they are displayed before relations. Update the comment to reflect the actual ordering to avoid future regressions.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

}

// TODO: This note widget repositioning logic is temporary and will be deleted soon.
// We need this to ensure the note editor is displayed before record relations,
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Jan 20, 2026

Choose a reason for hiding this comment

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

P3: Misleading comment: Notes are inserted after relation widgets, but the comment says they are displayed before relations. Update the comment to reflect the actual ordering to avoid future regressions.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/twenty-front/src/modules/page-layout/hooks/usePageLayoutWithRelationWidgets.ts, line 82:

<comment>Misleading comment: Notes are inserted after relation widgets, but the comment says they are displayed before relations. Update the comment to reflect the actual ordering to avoid future regressions.</comment>

<file context>
@@ -78,6 +78,9 @@ const injectRelationWidgetsIntoLayout = (
         }
 
+        // TODO: This note widget repositioning logic is temporary and will be deleted soon.
+        // We need this to ensure the note editor is displayed before record relations,
+        // matching the behavior of the old show page.
         const noteWidgetIndex = tab.widgets.findIndex(
</file context>
Suggested change
// We need this to ensure the note editor is displayed before record relations,
// We need this to ensure the note editor is displayed after record relations,
Fix with Cubic

@Devessier Devessier added this pull request to the merge queue Jan 20, 2026
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Jan 20, 2026
@Devessier Devessier added this pull request to the merge queue Jan 21, 2026
Merged via the queue into main with commit 9b7953e Jan 21, 2026
68 checks passed
@Devessier Devessier deleted the copilot/update-use-page-layout-function branch January 21, 2026 08:49
@twenty-eng-sync
Copy link

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

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

lucasbordeau pushed a commit to vasu1303/twenty that referenced this pull request Jan 21, 2026
…s after relations (twentyhq#17274)

> [!NOTE]
> This code is temporary. It will be dropped once Record Page Layouts
can be fully configured.

## Before



https://github.com/user-attachments/assets/3f6aed00-2fd5-47d4-bd74-002a68030627



## After

<img width="3456" height="2160" alt="CleanShot 2026-01-20 at 15 17
15@2x"
src="https://github.com/user-attachments/assets/2f9baf0a-e003-4cb6-a023-6e104b097df3"
/>

The `usePageLayoutWithRelationWidgets` hook was appending relation
widgets to the end of the widget list. This resulted in incorrect
ordering where relation widgets appeared after Notes and other widgets
instead of immediately following the FIELDS widget.

## Changes

- **Widget injection logic**: Modified `injectRelationWidgetsIntoLayout`
to find the first FIELDS widget and insert relation widgets immediately
after it, rather than appending to end
- **Notes positioning**: Extract and reposition NOTES widget to appear
after all relation widgets, maintaining correct semantic order: `FIELDS
→ Relations → Notes → Other widgets`
- **Fallback behavior**: When no FIELDS widget exists, append relation
widgets to end as before
- **Test coverage**: Added 6 test cases covering injection order, Notes
positioning, and edge cases (missing widgets, empty relations,
non-record pages)

## Example

Before:
```
[FIELDS, NOTES, GRAPH] → [FIELDS, NOTES, GRAPH, Relation1, Relation2]
```

After:
```
[FIELDS, NOTES, GRAPH] → [FIELDS, Relation1, Relation2, NOTES, GRAPH]
```

> [!WARNING]
>
> <details>
> <summary>Firewall rules blocked me from connecting to one or more
addresses (expand for details)</summary>
>
> #### I tried to connect to the following addresses, but was blocked by
firewall rules:
>
> - `googlechromelabs.github.io`
> - Triggering command: `/usr/local/bin/node /usr/local/bin/node
install.mjs` (dns block)
> -
`https://storage.googleapis.com/chrome-for-testing-public/127.0.6533.88/linux64/chrome-headless-shell-linux64.zip`
> - Triggering command: `/usr/local/bin/node /usr/local/bin/node
install.mjs` (http block)
> -
`https://storage.googleapis.com/chrome-for-testing-public/127.0.6533.88/linux64/chrome-linux64.zip`
> - Triggering command: `/usr/local/bin/node /usr/local/bin/node
install.mjs` (http block)
>
> If you need me to access, download, or install something from one of
these locations, you can either:
>
> - Configure [Actions setup
steps](https://gh.io/copilot/actions-setup-steps) to set up my
environment, which run before the firewall is enabled
> - Add the appropriate URLs or hosts to the custom allowlist in this
repository's [Copilot coding agent
settings](https://github.com/twentyhq/twenty/settings/copilot/coding_agent)
(admins only)
>
> </details>

<!-- START COPILOT ORIGINAL PROMPT -->



<details>

<summary>Original prompt</summary>

Start implementation

<analysis>
[Chronological Review: The conversation began with the user requesting
an update to the `usePageLayoutWithRelationWidgets` function to inject
relation widgets after the first FIELDS widget and to position a Note
widget after all relation widgets. The user then asked for a plan
without comments, followed by a request to start the implementation of
the plan.]

[Intent Mapping: 
1. "Update #sym:usePageLayoutWithRelationWidgets. We should inject the
relation widgets directly after the first FIELDS widget."
2. "We want the Note widget to be after all the relation widgets we
inject."
3. "Prepare a plan. Don't write any comment."
4. "Start implementation."]

[Technical Inventory: 
- Function: `usePageLayoutWithRelationWidgets`
- Context: This function is part of a page layout management system,
likely dealing with dynamic widget placement based on certain
conditions.]

[Code Archaeology: 
- File:
`/Users/devessier/Web/twenty/packages/twenty-front/src/modules/page-layout/hooks/usePageLayoutWithRelationWidgets.ts`
- Function Purpose: To manage the layout of widgets on a page,
specifically handling relation widgets and their placement relative to
other widgets.]

[Progress Assessment: 
- Completed: User has defined the requirements for widget placement.
- Pending: Implementation of the specified changes to the
`usePageLayoutWithRelationWidgets` function.]

[Context Validation: All necessary context for continuing the
implementation of the widget placement logic is captured, including the
specific order of widget injection.]

[Recent Commands Analysis: 
- Last Agent Commands: The user requested to "Start implementation."
- Tool Results Summary: No specific tool results were generated as the
conversation focused on planning and implementation requests.
- Pre-Summary State: The agent was actively preparing to implement the
changes to the `usePageLayoutWithRelationWidgets` function when the
token budget was exceeded.
- Operation Context: The commands executed were directly related to the
user's goal of modifying the widget layout in the application.]

</analysis>

<summary>
1. Conversation Overview:
- Primary Objectives: 
- "Update #sym:usePageLayoutWithRelationWidgets. We should inject the
relation widgets directly after the first FIELDS widget."
- "We want the Note widget to be after all the relation widgets we
inject."
  - "Prepare a plan. Don't write any comment."
  - "Start implementation."
- Session Context: The user is focused on modifying the widget layout in
a page layout management system, specifically the order of relation and
Note widgets.
- User Intent Evolution: The user transitioned from defining
requirements to requesting a plan and then moving directly to
implementation.

2. Technical Foundation:
- Function: `usePageLayoutWithRelationWidgets`
- Purpose: To manage the layout of widgets on a page, particularly the
placement of relation widgets.

3. Codebase Status:
- File Name: `usePageLayoutWithRelationWidgets.ts`
- Purpose: To handle the layout of widgets dynamically based on
specified rules.
- Current State: Awaiting implementation of the user-defined widget
placement logic.
- Key Code Segments: The function signature is defined, but the
implementation details are yet to be added.

4. Problem Resolution:
- Issues Encountered: No specific technical problems were reported; the
focus was on planning and implementation.
- Solutions Implemented: None yet, as the implementation phase has just
begun.
- Debugging Context: No ongoing troubleshooting efforts were mentioned.
- Lessons Learned: The importance of clear widget placement requirements
was emphasized.

5. Progress Tracking:
- Completed Tasks: User has articulated the requirements for widget
placement.
- Partially Complete Work: Implementation of the specified changes is
pending.
- Validated Outcomes: No features have been confirmed working yet as
implementation has not started.

6. Active Work State:
- Current Focus: The user is preparing to implement the changes to the
`usePageLayoutWithRelationWidgets` function.
- Recent Context: The user has defined the order of widget placement and
is ready to start coding.
- Working Code: The function is currently defined but lacks the
implementation logic.
- Immediate Context: The user is focused on implementing the logic for
injecting relation widgets and positioning the Note widget.

7. Recent Operations:
- Last Agent Commands: "Start implementation."
- Tool Results Summary: No specific results were generated; the focus
was on user requests.
- Pre-Summary State: The agent was preparing to implement the changes to
the `usePageLayoutWithRelationWidgets` function.
- Operation Context: The commands executed were directly related to the
user's goal of modifying the widget layout.

8. Continuation Plan:
- Pending Task 1: Implement the logic to inject relation widgets after
the first FIELDS widget.
- Pending Task 2: Ensure the Note widget is positioned after all
relation widgets...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

Created from [VS
Code](https://code.visualstudio.com/docs/copilot/copilot-coding-agent).

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in
our [2 minute survey](https://gh.io/copilot-coding-agent-survey).

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Devessier <29370468+Devessier@users.noreply.github.com>
Co-authored-by: Devessier <baptiste@devessier.fr>
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