Skip to content

Scaffold all company cards as widgets#15149

Merged
FelixMalfait merged 11 commits intomainfrom
create-all-company-cards-as-widgets
Oct 18, 2025
Merged

Scaffold all company cards as widgets#15149
FelixMalfait merged 11 commits intomainfrom
create-all-company-cards-as-widgets

Conversation

@Devessier
Copy link
Copy Markdown
Contributor

@Devessier Devessier commented Oct 16, 2025

This PR doesn't support mobile and side-panel modes. The fields tab will be displayed in these cases.

Demo

CleanShot.2025-10-17.at.17.14.12.mp4

Comment on lines +29 to +45
case WidgetType.TIMELINE:
return <TimelineWidget widget={widget} />;

case WidgetType.TASKS:
return <TaskWidget widget={widget} />;

case WidgetType.NOTES:
return <NoteWidget widget={widget} />;

case WidgetType.FILES:
return <FileWidget widget={widget} />;

case WidgetType.EMAILS:
return <EmailWidget widget={widget} />;

case WidgetType.CALENDAR:
return <CalendarWidget widget={widget} />;
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.

At one point or another, we'll have to lazy-load the components

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Oct 16, 2025

🚀 Preview Environment Ready!

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

This environment will automatically shut down when the PR is closed or after 5 hours.

Comment on lines +65 to +70
const tabsToRenderInTabList = currentPageLayout.tabs.filter(
(tab) => tab.selfDisplayMode !== 'pinned-left',
);
const pinnedLeftTab = currentPageLayout.tabs.find(
(tab) => tab.selfDisplayMode === 'pinned-left',
);
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.

selfDisplayMode: 'pinned-left' on tabs

Comment on lines +74 to +84
{isDefined(pinnedLeftTab) && (
<ShowPageLeftContainer forceMobile={false}>
<SummaryCard
objectNameSingular={targetRecordIdentifier.targetObjectNameSingular}
objectRecordId={targetRecordIdentifier.id}
isInRightDrawer={isInRightDrawer}
/>

<PageLayoutGridLayout activeTabId={pinnedLeftTab.id} />
</ShowPageLeftContainer>
)}
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.

Hard-coded left panel; not fan of it.

title: 'Fields',
position: 100,
layoutMode: 'vertical-list',
selfDisplayMode: 'pinned-left',
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.

⚠️

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.

Why "selfDisplayMode" and not just displayMode? I didn't get the nuance

@Devessier Devessier marked this pull request as ready for review October 17, 2025 16:29
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.

Greptile Overview

Greptile Summary

This PR transforms company detail pages from a fixed card-based layout to a flexible widget-based system. The implementation creates dedicated widget components (TimelineWidget, TaskWidget, NoteWidget, FileWidget, EmailWidget, CalendarWidget) that wrap existing card components, expanding the widget type enum from 4 to 10 types. Each widget provides proper context handling through RightDrawerProvider and maintains existing functionality while enabling modular page layouts.

The architectural changes introduce a two-column layout similar to existing show pages, with a left sidebar containing a summary card and a pinned Fields widget, and a right container with tabbed content for other widgets. A database migration adds the new widget types to the PostgreSQL enum, and the default page layout configuration is updated to use specific widget types instead of generic VIEW widgets.

Important Files Changed

Changed Files
Filename Score Overview
packages/twenty-server/src/database/typeorm/core/migrations/common/1760628085765-addNewWidgetTypes.ts 4/5 Database migration adding 6 new widget types to PostgreSQL enum
packages/twenty-server/src/engine/core-modules/page-layout/enums/widget-type.enum.ts 5/5 Backend enum expansion adding TIMELINE, TASKS, NOTES, FILES, EMAILS, CALENDAR
packages/twenty-front/src/generated/graphql.ts 5/5 Generated GraphQL types reflecting new widget type enum values
packages/twenty-front/src/modules/page-layout/constants/DefaultPageLayout.ts 4/5 Default layout configuration updated with specific widget types and pinned-left mode
packages/twenty-front/src/modules/page-layout/components/PageLayoutRendererContent.tsx 4/5 Major refactor implementing two-column layout with left sidebar and tab filtering
packages/twenty-front/src/modules/page-layout/widgets/components/WidgetContentRenderer.tsx 5/5 Widget renderer updated to support 6 new widget types with proper case handling
packages/twenty-front/src/modules/page-layout/widgets/timeline/components/TimelineWidget.tsx 5/5 New timeline widget wrapper with proper context providers
packages/twenty-front/src/modules/page-layout/widgets/tasks/components/TaskWidget.tsx 5/5 New task widget wrapper with context and styling
packages/twenty-front/src/modules/page-layout/widgets/notes/components/NoteWidget.tsx 4/5 New notes widget wrapper following established patterns
packages/twenty-front/src/modules/page-layout/widgets/files/components/FileWidget.tsx 5/5 New files widget wrapper with proper context handling
packages/twenty-front/src/modules/page-layout/widgets/emails/components/EmailWidget.tsx 4/5 New email widget wrapper with right drawer provider
packages/twenty-front/src/modules/page-layout/widgets/calendar/components/CalendarWidget.tsx 4/5 New calendar widget wrapper with context and styling
packages/twenty-front/src/modules/page-layout/widgets/fields/components/FieldsWidget.tsx 4/5 Updated fields widget with width styling adjustment
packages/twenty-front/src/modules/page-layout/components/PageLayoutGridLayout.tsx 4/5 Component refactored to accept explicit tabId prop instead of using global state
packages/twenty-front/src/modules/page-layout/types/PageLayoutTab.ts 5/5 Type definition extended with optional selfDisplayMode property
packages/twenty-front/src/modules/ui/layout/page/components/ShowPageContainer.tsx 2/5 Children type changed to ReactNode but contains React hooks rules violation
packages/twenty-server/src/engine/core-modules/open-api/utils/components.utils.ts 2/5 OpenAPI schema updated inconsistently - new types missing from ForUpdate/ForResponse schemas
packages/twenty-server/test/integration/constants/widget-configuration-test-data.constants.ts 5/5 Test configuration updated to support new widget types returning null config
packages/twenty-front/src/modules/page-layout/components/__stories__/PageLayoutRenderer.stories.tsx 4/5 Storybook stories updated with proper LayoutRenderingProvider context

Confidence score: 4/5

  • This PR implements a well-structured widget architecture that maintains existing functionality while adding modularity
  • Score reduced due to React hooks violations in ShowPageContainer, OpenAPI schema inconsistencies, and potential runtime issues with duplicate component rendering
  • Pay close attention to ShowPageContainer.tsx and components.utils.ts for the identified issues that need correction before merging

Sequence Diagram

sequenceDiagram
    participant User
    participant PageLayoutRenderer
    participant WidgetContentRenderer
    participant CalendarWidget
    participant EmailWidget
    participant FileWidget
    participant NoteWidget
    participant TaskWidget
    participant TimelineWidget
    participant FieldsWidget
    participant CalendarEventsCard
    participant EmailsCard
    participant FilesCard
    participant NotesCard
    participant TasksCard
    participant TimelineCard
    participant RecordFieldList

    User->>PageLayoutRenderer: "Load page layout"
    PageLayoutRenderer->>PageLayoutRenderer: "Get current page layout"
    PageLayoutRenderer->>PageLayoutRenderer: "Filter tabs by display mode"
    
    loop For each widget in active tab
        PageLayoutRenderer->>WidgetContentRenderer: "Render widget"
        
        alt Widget type is CALENDAR
            WidgetContentRenderer->>CalendarWidget: "Create calendar widget"
            CalendarWidget->>CalendarEventsCard: "Render calendar events"
            CalendarEventsCard-->>CalendarWidget: "Return calendar content"
            CalendarWidget-->>WidgetContentRenderer: "Return calendar widget"
            
        else Widget type is EMAILS
            WidgetContentRenderer->>EmailWidget: "Create email widget"
            EmailWidget->>EmailsCard: "Render emails"
            EmailsCard-->>EmailWidget: "Return email content"
            EmailWidget-->>WidgetContentRenderer: "Return email widget"
            
        else Widget type is FILES
            WidgetContentRenderer->>FileWidget: "Create file widget"
            FileWidget->>FilesCard: "Render files"
            FilesCard-->>FileWidget: "Return file content"
            FileWidget-->>WidgetContentRenderer: "Return file widget"
            
        else Widget type is NOTES
            WidgetContentRenderer->>NoteWidget: "Create note widget"
            NoteWidget->>NotesCard: "Render notes"
            NotesCard-->>NoteWidget: "Return note content"
            NoteWidget-->>WidgetContentRenderer: "Return note widget"
            
        else Widget type is TASKS
            WidgetContentRenderer->>TaskWidget: "Create task widget"
            TaskWidget->>TasksCard: "Render tasks"
            TasksCard-->>TaskWidget: "Return task content"
            TaskWidget-->>WidgetContentRenderer: "Return task widget"
            
        else Widget type is TIMELINE
            WidgetContentRenderer->>TimelineWidget: "Create timeline widget"
            TimelineWidget->>TimelineCard: "Render timeline"
            TimelineCard-->>TimelineWidget: "Return timeline content"
            TimelineWidget-->>WidgetContentRenderer: "Return timeline widget"
            
        else Widget type is FIELDS
            WidgetContentRenderer->>FieldsWidget: "Create fields widget"
            FieldsWidget->>RecordFieldList: "Render record fields"
            RecordFieldList-->>FieldsWidget: "Return field list content"
            FieldsWidget-->>WidgetContentRenderer: "Return fields widget"
        end
        
        WidgetContentRenderer-->>PageLayoutRenderer: "Return rendered widget"
    end
    
    PageLayoutRenderer-->>User: "Display page with all widgets"
Loading

Additional Comments (4)

  1. packages/twenty-front/src/modules/ui/layout/page/components/ShowPageContainer.tsx, line 9 (link)

    logic: calling useIsMobile() inside styled component definition violates rules of hooks - this will cause runtime errors

  2. packages/twenty-front/src/modules/ui/layout/page/components/ShowPageContainer.tsx, line 16 (link)

    logic: calling useIsMobile() inside styled component definition violates rules of hooks - this will cause runtime errors

  3. packages/twenty-server/src/engine/core-modules/open-api/utils/components.utils.ts, line 1173-1175 (link)

    logic: Missing new widget types in ForUpdate schema. Should include TIMELINE, TASKS, NOTES, FILES, EMAILS, CALENDAR to match the main schema

  4. packages/twenty-server/src/engine/core-modules/open-api/utils/components.utils.ts, line 1194-1196 (link)

    logic: Missing new widget types in ForResponse schema. Should include TIMELINE, TASKS, NOTES, FILES, EMAILS, CALENDAR to match the main schema

19 files reviewed, 11 comments

Edit Code Review Agent Settings | Greptile

widget: PageLayoutWidget;
};

export const TaskWidget = ({ widget: _widget }: TaskWidgetProps) => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

style: The widget parameter is prefixed with underscore indicating it's intentionally unused, but consider if widget configuration might be needed for future functionality

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/twenty-front/src/modules/page-layout/widgets/tasks/components/TaskWidget.tsx
Line: 17:17

Comment:
**style:** The widget parameter is prefixed with underscore indicating it's intentionally unused, but consider if widget configuration might be needed for future functionality

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

widget: PageLayoutWidget;
};

export const NoteWidget = ({ widget: _widget }: NoteWidgetProps) => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

style: Widget parameter is unused - consider removing underscore prefix once widget configuration is implemented

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/twenty-front/src/modules/page-layout/widgets/notes/components/NoteWidget.tsx
Line: 17:17

Comment:
**style:** Widget parameter is unused - consider removing underscore prefix once widget configuration is implemented

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

height: 100%;
justify-content: start;
width: 100%;
height: 100%;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

syntax: Duplicate height declaration - remove one of the height: 100% lines

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/twenty-front/src/modules/page-layout/components/PageLayoutRendererContent.tsx
Line: 33:33

Comment:
**syntax:** Duplicate height declaration - remove one of the `height: 100%` lines

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

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.

isInRightDrawer={isInRightDrawer}
/>

<PageLayoutGridLayout tabId={pinnedLeftTab.id} />
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

logic: Potential issue: PageLayoutGridLayout is rendered twice with different tabIds. Verify this doesn't cause conflicts or duplicate rendering.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/twenty-front/src/modules/page-layout/components/PageLayoutRendererContent.tsx
Line: 82:82

Comment:
**logic:** Potential issue: PageLayoutGridLayout is rendered twice with different tabIds. Verify this doesn't cause conflicts or duplicate rendering.

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

cursor[bot]

This comment was marked as outdated.

@FelixMalfait
Copy link
Copy Markdown
Member

Great work!
Many things left obviously like right drawer but you are aware of it!
Screenshot 2025-10-18 at 22 21 29

Copy link
Copy Markdown
Member

@FelixMalfait FelixMalfait left a comment

Choose a reason for hiding this comment

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

Great!

@FelixMalfait FelixMalfait merged commit f6d133f into main Oct 18, 2025
64 checks passed
@FelixMalfait FelixMalfait deleted the create-all-company-cards-as-widgets branch October 18, 2025 20:26
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.

2 participants