Compare commits

...
Author SHA1 Message Date
Weiko b5595909fa fix 2026-05-05 16:51:26 +02:00
Weiko d230389cdc Merge branch 'main' into c--deprecate-grid-position-2 2026-05-05 15:13:57 +02:00
Weiko 731d0256d1 lint 2026-04-30 16:00:39 +02:00
Weiko abf3dc49bd Merge branch 'main' into c--deprecate-grid-position-2 2026-04-30 15:43:07 +02:00
Weiko 91ab638b30 fix 2026-04-30 15:39:43 +02:00
Weiko d15625984c fix 2026-04-30 15:14:00 +02:00
Weiko 046bc6f475 fix 2026-04-30 14:44:43 +02:00
Weiko 94a1502774 fix 2026-04-30 14:33:47 +02:00
Weiko a3c2599b2f codegen 2026-04-29 18:19:23 +02:00
Weiko 3dc80f91c6 fix 2026-04-29 18:15:51 +02:00
Weiko 406b535193 Merge branch 'main' into c--deprecate-grid-position-2 2026-04-29 18:14:45 +02:00
Weiko 520d8d0be1 fix 2026-04-29 18:05:59 +02:00
Weiko 42bc5d4485 fix 2026-04-29 16:09:27 +02:00
Weiko d23aa660c3 Merge branch 'main' into c--deprecate-grid-position-2 2026-04-29 15:51:55 +02:00
Weiko aba3aeb8c6 remove-page-layout-widget-grid-position 2026-04-24 19:50:39 +02:00
156 changed files with 1730 additions and 1546 deletions
@@ -56,7 +56,7 @@ export default definePageLayout({
title: 'PRs Merged This Week',
type: 'GRAPH',
objectUniversalIdentifier: PULL_REQUEST_UNIVERSAL_IDENTIFIER,
gridPosition: { row: 0, ...COL_1, rowSpan: 3 },
position: { layoutMode: PageLayoutTabLayoutMode.GRID, row: 0, ...COL_1, rowSpan: 3 },
configuration: {
configurationType: 'AGGREGATE_CHART',
aggregateFieldMetadataUniversalIdentifier:
@@ -84,7 +84,7 @@ export default definePageLayout({
title: 'PR Reviews This Week',
type: 'GRAPH',
objectUniversalIdentifier: PULL_REQUEST_REVIEW_UNIVERSAL_IDENTIFIER,
gridPosition: { row: 3, ...COL_1, rowSpan: 3 },
position: { layoutMode: PageLayoutTabLayoutMode.GRID, row: 3, ...COL_1, rowSpan: 3 },
configuration: {
configurationType: 'AGGREGATE_CHART',
aggregateFieldMetadataUniversalIdentifier:
@@ -112,7 +112,7 @@ export default definePageLayout({
title: 'PRs Opened This Week',
type: 'GRAPH',
objectUniversalIdentifier: PULL_REQUEST_UNIVERSAL_IDENTIFIER,
gridPosition: { row: 6, ...COL_1, rowSpan: 3 },
position: { layoutMode: PageLayoutTabLayoutMode.GRID, row: 6, ...COL_1, rowSpan: 3 },
configuration: {
configurationType: 'AGGREGATE_CHART',
aggregateFieldMetadataUniversalIdentifier:
@@ -140,7 +140,7 @@ export default definePageLayout({
title: 'Issues Opened This Week',
type: 'GRAPH',
objectUniversalIdentifier: ISSUE_UNIVERSAL_IDENTIFIER,
gridPosition: { row: 9, ...COL_1, rowSpan: 3 },
position: { layoutMode: PageLayoutTabLayoutMode.GRID, row: 9, ...COL_1, rowSpan: 3 },
configuration: {
configurationType: 'AGGREGATE_CHART',
aggregateFieldMetadataUniversalIdentifier:
@@ -168,7 +168,7 @@ export default definePageLayout({
title: 'PRs by State',
type: 'GRAPH',
objectUniversalIdentifier: PULL_REQUEST_UNIVERSAL_IDENTIFIER,
gridPosition: { row: 12, ...COL_1, rowSpan: 6 },
position: { layoutMode: PageLayoutTabLayoutMode.GRID, row: 12, ...COL_1, rowSpan: 6 },
configuration: {
configurationType: 'PIE_CHART',
aggregateFieldMetadataUniversalIdentifier:
@@ -187,7 +187,7 @@ export default definePageLayout({
title: 'PRs Merged per Week',
type: 'GRAPH',
objectUniversalIdentifier: PULL_REQUEST_UNIVERSAL_IDENTIFIER,
gridPosition: { row: 0, ...COL_2, rowSpan: 6 },
position: { layoutMode: PageLayoutTabLayoutMode.GRID, row: 0, ...COL_2, rowSpan: 6 },
configuration: {
configurationType: 'BAR_CHART',
aggregateFieldMetadataUniversalIdentifier:
@@ -210,7 +210,7 @@ export default definePageLayout({
title: 'PR Reviews per Week',
type: 'GRAPH',
objectUniversalIdentifier: PULL_REQUEST_REVIEW_UNIVERSAL_IDENTIFIER,
gridPosition: { row: 6, ...COL_2, rowSpan: 6 },
position: { layoutMode: PageLayoutTabLayoutMode.GRID, row: 6, ...COL_2, rowSpan: 6 },
configuration: {
configurationType: 'BAR_CHART',
aggregateFieldMetadataUniversalIdentifier:
@@ -233,7 +233,7 @@ export default definePageLayout({
title: 'Issues per Week',
type: 'GRAPH',
objectUniversalIdentifier: ISSUE_UNIVERSAL_IDENTIFIER,
gridPosition: { row: 12, ...COL_2, rowSpan: 6 },
position: { layoutMode: PageLayoutTabLayoutMode.GRID, row: 12, ...COL_2, rowSpan: 6 },
configuration: {
configurationType: 'BAR_CHART',
aggregateFieldMetadataUniversalIdentifier:
@@ -257,7 +257,7 @@ export default definePageLayout({
universalIdentifier: '7b3e9c4a-1d52-4f8b-ac76-3e5b8d2f1a9c',
title: 'Top PR Authors',
type: 'FRONT_COMPONENT',
gridPosition: { row: 0, ...COL_3, rowSpan: 9 },
position: { layoutMode: PageLayoutTabLayoutMode.GRID, row: 0, ...COL_3, rowSpan: 9 },
configuration: {
configurationType: 'FRONT_COMPONENT',
frontComponentUniversalIdentifier:
@@ -268,7 +268,7 @@ export default definePageLayout({
universalIdentifier: '5e4a8c1d-7f93-4b2e-9d6c-3a8f1b5e7d4c',
title: 'Top Reviewers',
type: 'FRONT_COMPONENT',
gridPosition: { row: 9, ...COL_3, rowSpan: 9 },
position: { layoutMode: PageLayoutTabLayoutMode.GRID, row: 9, ...COL_3, rowSpan: 9 },
configuration: {
configurationType: 'FRONT_COMPONENT',
frontComponentUniversalIdentifier:
@@ -28,7 +28,7 @@ export default definePageLayout({
type: 'GRAPH',
objectUniversalIdentifier:
CALL_RECORDING_OBJECT_UNIVERSAL_IDENTIFIER,
gridPosition: { row: 0, column: 0, rowSpan: 2, columnSpan: 3 },
position: { layoutMode: PageLayoutTabLayoutMode.CANVAS },
configuration: {
configurationType: 'AGGREGATE_CHART',
aggregateFieldMetadataUniversalIdentifier:
@@ -43,7 +43,7 @@ export default definePageLayout({
type: 'GRAPH',
objectUniversalIdentifier:
CALL_RECORDING_OBJECT_UNIVERSAL_IDENTIFIER,
gridPosition: { row: 0, column: 3, rowSpan: 6, columnSpan: 4 },
position: { layoutMode: PageLayoutTabLayoutMode.CANVAS },
configuration: {
configurationType: 'PIE_CHART',
aggregateFieldMetadataUniversalIdentifier:
@@ -64,7 +64,7 @@ export default definePageLayout({
type: 'GRAPH',
objectUniversalIdentifier:
CALL_RECORDING_OBJECT_UNIVERSAL_IDENTIFIER,
gridPosition: { row: 0, column: 7, rowSpan: 6, columnSpan: 5 },
position: { layoutMode: PageLayoutTabLayoutMode.CANVAS },
configuration: {
configurationType: 'BAR_CHART',
aggregateFieldMetadataUniversalIdentifier:
@@ -85,7 +85,7 @@ export default definePageLayout({
type: 'GRAPH',
objectUniversalIdentifier:
CALL_RECORDING_OBJECT_UNIVERSAL_IDENTIFIER,
gridPosition: { row: 6, column: 0, rowSpan: 6, columnSpan: 12 },
position: { layoutMode: PageLayoutTabLayoutMode.CANVAS },
configuration: {
configurationType: 'LINE_CHART',
aggregateFieldMetadataUniversalIdentifier:
@@ -20,6 +20,7 @@ export default definePageLayout({
universalIdentifier: 'e5c93fce-76b4-41e9-9c5d-9b17e034366c',
title: 'Summary',
type: 'FRONT_COMPONENT',
position: { layoutMode: PageLayoutTabLayoutMode.CANVAS },
configuration: {
configurationType: 'FRONT_COMPONENT',
frontComponentUniversalIdentifier:
@@ -39,6 +40,7 @@ export default definePageLayout({
universalIdentifier: 'd5a9f3b7-4e82-4c06-9f1d-2b0a7e6c8d45',
title: 'Media Player',
type: 'FRONT_COMPONENT',
position: { layoutMode: PageLayoutTabLayoutMode.CANVAS },
configuration: {
configurationType: 'FRONT_COMPONENT',
frontComponentUniversalIdentifier:
@@ -58,6 +60,7 @@ export default definePageLayout({
universalIdentifier: '23c87a9c-25e3-4e83-84d9-02fb1a6fde76',
title: 'Timeline',
type: 'TIMELINE',
position: { layoutMode: PageLayoutTabLayoutMode.CANVAS },
configuration: {
configurationType: 'TIMELINE',
},
@@ -75,6 +78,7 @@ export default definePageLayout({
universalIdentifier: 'ae93482f-384f-42a8-9c06-6bc14b10da6a',
title: 'Tasks',
type: 'TASKS',
position: { layoutMode: PageLayoutTabLayoutMode.CANVAS },
configuration: {
configurationType: 'TASKS',
},
@@ -92,6 +96,7 @@ export default definePageLayout({
universalIdentifier: 'e2b6a0c4-1f59-4d73-a684-9c7b4f3d5e12',
title: 'Notes',
type: 'NOTES',
position: { layoutMode: PageLayoutTabLayoutMode.CANVAS },
configuration: {
configurationType: 'NOTES',
},
@@ -109,6 +114,7 @@ export default definePageLayout({
universalIdentifier: 'a17bf74a-a7ff-48a0-8628-3fd905539c8d',
title: 'Files',
type: 'FILES',
position: { layoutMode: PageLayoutTabLayoutMode.CANVAS },
configuration: {
configurationType: 'FILES',
},
@@ -29,12 +29,7 @@ export default definePageLayout({
frontComponentUniversalIdentifier:
RESEND_SYNC_STATUS_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER,
},
gridPosition: {
row: 0,
column: 0,
rowSpan: 12,
columnSpan: 12,
},
position: { layoutMode: PageLayoutTabLayoutMode.CANVAS },
},
],
},
@@ -925,7 +925,7 @@ type PageLayoutWidget {
title: String!
type: WidgetType!
objectMetadataId: UUID
gridPosition: GridPosition! @deprecated(reason: "Use `position` instead. Will be removed in a future release.")
gridPosition: GridPosition @deprecated(reason: "Use `position` instead. Will be removed in a future release.")
position: PageLayoutWidgetPosition
configuration: WidgetConfiguration!
conditionalDisplay: JSON
@@ -3737,7 +3737,7 @@ input UpdatePageLayoutWidgetWithIdInput {
title: String!
type: WidgetType!
objectMetadataId: UUID
gridPosition: GridPositionInput!
gridPosition: GridPositionInput
position: JSON
configuration: JSON
conditionalDisplay: JSON
@@ -3756,7 +3756,7 @@ input CreatePageLayoutWidgetInput {
title: String!
type: WidgetType!
objectMetadataId: UUID
gridPosition: GridPositionInput!
gridPosition: GridPositionInput
position: JSON
configuration: JSON!
}
@@ -703,7 +703,7 @@ export interface PageLayoutWidget {
type: WidgetType
objectMetadataId?: Scalars['UUID']
/** @deprecated Use `position` instead. Will be removed in a future release. */
gridPosition: GridPosition
gridPosition?: GridPosition
position?: PageLayoutWidgetPosition
configuration: WidgetConfiguration
conditionalDisplay?: Scalars['JSON']
@@ -6068,11 +6068,11 @@ export interface UpdatePageLayoutWithTabsInput {name: Scalars['String'],type: Pa
export interface UpdatePageLayoutTabWithWidgetsInput {id: Scalars['UUID'],title: Scalars['String'],position: Scalars['Float'],icon?: (Scalars['String'] | null),layoutMode?: (PageLayoutTabLayoutMode | null),widgets: UpdatePageLayoutWidgetWithIdInput[]}
export interface UpdatePageLayoutWidgetWithIdInput {id: Scalars['UUID'],pageLayoutTabId: Scalars['UUID'],title: Scalars['String'],type: WidgetType,objectMetadataId?: (Scalars['UUID'] | null),gridPosition: GridPositionInput,position?: (Scalars['JSON'] | null),configuration?: (Scalars['JSON'] | null),conditionalDisplay?: (Scalars['JSON'] | null),conditionalAvailabilityExpression?: (Scalars['String'] | null)}
export interface UpdatePageLayoutWidgetWithIdInput {id: Scalars['UUID'],pageLayoutTabId: Scalars['UUID'],title: Scalars['String'],type: WidgetType,objectMetadataId?: (Scalars['UUID'] | null),gridPosition?: (GridPositionInput | null),position?: (Scalars['JSON'] | null),configuration?: (Scalars['JSON'] | null),conditionalDisplay?: (Scalars['JSON'] | null),conditionalAvailabilityExpression?: (Scalars['String'] | null)}
export interface GridPositionInput {row: Scalars['Float'],column: Scalars['Float'],rowSpan: Scalars['Float'],columnSpan: Scalars['Float']}
export interface CreatePageLayoutWidgetInput {pageLayoutTabId: Scalars['UUID'],title: Scalars['String'],type: WidgetType,objectMetadataId?: (Scalars['UUID'] | null),gridPosition: GridPositionInput,position?: (Scalars['JSON'] | null),configuration: Scalars['JSON']}
export interface CreatePageLayoutWidgetInput {pageLayoutTabId: Scalars['UUID'],title: Scalars['String'],type: WidgetType,objectMetadataId?: (Scalars['UUID'] | null),gridPosition?: (GridPositionInput | null),position?: (Scalars['JSON'] | null),configuration: Scalars['JSON']}
export interface UpdatePageLayoutWidgetInput {pageLayoutTabId?: (Scalars['UUID'] | null),title?: (Scalars['String'] | null),type?: (WidgetType | null),objectMetadataId?: (Scalars['UUID'] | null),gridPosition?: (GridPositionInput | null),position?: (Scalars['JSON'] | null),configuration?: (Scalars['JSON'] | null),conditionalDisplay?: (Scalars['JSON'] | null),conditionalAvailabilityExpression?: (Scalars['String'] | null)}
File diff suppressed because one or more lines are too long
@@ -17,6 +17,7 @@ import {
BarChartLayout,
FindOnePageLayoutDocument,
GraphOrderBy,
PageLayoutTabLayoutMode,
PageLayoutType,
type PageLayoutWidget,
WidgetConfigurationType,
@@ -82,8 +83,9 @@ const mixedGraphsPageLayoutMocks = {
type: WidgetType.GRAPH,
title: 'Revenue',
objectMetadataId: mockPersonObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 2,
@@ -108,8 +110,9 @@ const mixedGraphsPageLayoutMocks = {
type: WidgetType.GRAPH,
title: 'Goal Progress',
objectMetadataId: mockPersonObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 3,
rowSpan: 4,
@@ -135,8 +138,9 @@ const mixedGraphsPageLayoutMocks = {
type: WidgetType.GRAPH,
title: 'Revenue Sources',
objectMetadataId: mockPersonObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 6,
rowSpan: 4,
@@ -163,8 +167,9 @@ const mixedGraphsPageLayoutMocks = {
type: WidgetType.GRAPH,
title: 'Quarterly Comparison',
objectMetadataId: mockPersonObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 2,
column: 0,
rowSpan: 4,
@@ -40,13 +40,6 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Fields',
type: WidgetType.FIELDS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 12,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetVerticalListPosition',
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
@@ -86,13 +79,6 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Timeline',
type: WidgetType.TIMELINE,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
@@ -131,13 +117,6 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Tasks',
type: WidgetType.TASKS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
@@ -176,13 +155,6 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Notes',
type: WidgetType.NOTES,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
@@ -221,13 +193,6 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Files',
type: WidgetType.FILES,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
@@ -266,13 +231,6 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Emails',
type: WidgetType.EMAILS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
@@ -311,13 +269,6 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Calendar',
type: WidgetType.CALENDAR,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
@@ -40,13 +40,6 @@ export const DEFAULT_MESSAGE_THREAD_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Thread',
type: WidgetType.EMAIL_THREAD,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 12,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetVerticalListPosition',
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
@@ -40,13 +40,6 @@ export const DEFAULT_NOTE_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Fields',
type: WidgetType.FIELDS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 12,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetVerticalListPosition',
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
@@ -73,13 +66,6 @@ export const DEFAULT_NOTE_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Note',
type: WidgetType.FIELD_RICH_TEXT,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 12,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetVerticalListPosition',
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
@@ -126,13 +112,6 @@ export const DEFAULT_NOTE_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Note',
type: WidgetType.FIELD_RICH_TEXT,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 12,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetVerticalListPosition',
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
@@ -179,13 +158,6 @@ export const DEFAULT_NOTE_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Timeline',
type: WidgetType.TIMELINE,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
@@ -224,13 +196,6 @@ export const DEFAULT_NOTE_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Files',
type: WidgetType.FILES,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
@@ -40,13 +40,6 @@ export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Fields',
type: WidgetType.FIELDS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 12,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetVerticalListPosition',
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
@@ -86,13 +79,6 @@ export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Timeline',
type: WidgetType.TIMELINE,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
@@ -131,13 +117,6 @@ export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Tasks',
type: WidgetType.TASKS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
@@ -176,13 +155,6 @@ export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Notes',
type: WidgetType.NOTES,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
@@ -221,13 +193,6 @@ export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Files',
type: WidgetType.FILES,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
@@ -266,13 +231,6 @@ export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Emails',
type: WidgetType.EMAILS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
@@ -311,13 +269,6 @@ export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Calendar',
type: WidgetType.CALENDAR,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
@@ -40,13 +40,6 @@ export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Fields',
type: WidgetType.FIELDS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 12,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetVerticalListPosition',
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
@@ -86,13 +79,6 @@ export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Timeline',
type: WidgetType.TIMELINE,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
@@ -131,13 +117,6 @@ export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Tasks',
type: WidgetType.TASKS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
@@ -176,13 +155,6 @@ export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Notes',
type: WidgetType.NOTES,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
@@ -221,13 +193,6 @@ export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Files',
type: WidgetType.FILES,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
@@ -266,13 +231,6 @@ export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Emails',
type: WidgetType.EMAILS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
@@ -311,13 +269,6 @@ export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Calendar',
type: WidgetType.CALENDAR,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
@@ -40,13 +40,6 @@ export const DEFAULT_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Fields',
type: WidgetType.FIELDS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 12,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetVerticalListPosition',
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
@@ -86,13 +79,6 @@ export const DEFAULT_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Timeline',
type: WidgetType.TIMELINE,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
@@ -131,13 +117,6 @@ export const DEFAULT_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Tasks',
type: WidgetType.TASKS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
@@ -176,13 +155,6 @@ export const DEFAULT_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Notes',
type: WidgetType.NOTES,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
@@ -221,13 +193,6 @@ export const DEFAULT_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Files',
type: WidgetType.FILES,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
@@ -40,13 +40,6 @@ export const DEFAULT_TASK_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Fields',
type: WidgetType.FIELDS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 12,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetVerticalListPosition',
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
@@ -70,13 +63,6 @@ export const DEFAULT_TASK_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Note',
type: WidgetType.FIELD_RICH_TEXT,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 12,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetVerticalListPosition',
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
@@ -123,13 +109,6 @@ export const DEFAULT_TASK_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Note',
type: WidgetType.FIELD_RICH_TEXT,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 12,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetVerticalListPosition',
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
@@ -176,13 +155,6 @@ export const DEFAULT_TASK_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Timeline',
type: WidgetType.TIMELINE,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
@@ -221,13 +193,6 @@ export const DEFAULT_TASK_RECORD_PAGE_LAYOUT: PageLayout = {
title: 'Files',
type: WidgetType.FILES,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
@@ -45,13 +45,6 @@ export const DEFAULT_WORKFLOW_PAGE_LAYOUT: PageLayout = {
title: 'Flow',
type: WidgetType.WORKFLOW,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 12,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
@@ -46,13 +46,6 @@ export const DEFAULT_WORKFLOW_RUN_PAGE_LAYOUT: PageLayout = {
title: 'Fields',
type: WidgetType.FIELDS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 12,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetVerticalListPosition',
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
@@ -93,13 +86,6 @@ export const DEFAULT_WORKFLOW_RUN_PAGE_LAYOUT: PageLayout = {
title: 'Flow',
type: WidgetType.WORKFLOW_RUN,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 12,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
@@ -46,13 +46,6 @@ export const DEFAULT_WORKFLOW_VERSION_PAGE_LAYOUT: PageLayout = {
title: 'Fields',
type: WidgetType.FIELDS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 12,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetVerticalListPosition',
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
@@ -93,13 +86,6 @@ export const DEFAULT_WORKFLOW_VERSION_PAGE_LAYOUT: PageLayout = {
title: 'Flow',
type: WidgetType.WORKFLOW_VERSION,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 12,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
@@ -13,12 +13,6 @@ export const PAGE_LAYOUT_WIDGET_FRAGMENT = gql`
deletedAt
conditionalDisplay
conditionalAvailabilityExpression
gridPosition {
column
columnSpan
row
rowSpan
}
position {
... on PageLayoutWidgetGridPosition {
layoutMode
@@ -26,7 +26,6 @@ const makeWidget = (
title: id,
isActive: true,
type: WidgetType.FIELDS,
gridPosition: { column: 0, columnSpan: 1, row: 0, rowSpan: 1 },
configuration: { __typename: 'FieldsConfiguration' as const },
position: {
__typename: 'PageLayoutWidgetVerticalListPosition' as const,
@@ -26,7 +26,6 @@ const makeWidget = (
title: id,
isActive: true,
type: WidgetType.FIELDS,
gridPosition: { column: 0, columnSpan: 1, row: 0, rowSpan: 1 },
configuration: { __typename: 'FieldsConfiguration' as const },
position: {
__typename: 'PageLayoutWidgetVerticalListPosition' as const,
@@ -27,7 +27,6 @@ const makeWidget = (
title: id,
isActive: true,
type: WidgetType.FIELDS,
gridPosition: { column: 0, columnSpan: 1, row: 0, rowSpan: 1 },
configuration: { __typename: 'FieldsConfiguration' as const },
position: {
__typename: 'PageLayoutWidgetVerticalListPosition' as const,
@@ -26,7 +26,6 @@ const makeWidget = (
title: id,
isActive: true,
type: WidgetType.FIELDS,
gridPosition: { column: 0, columnSpan: 1, row: 0, rowSpan: 1 },
configuration: { __typename: 'FieldsConfiguration' as const },
position: {
__typename: 'PageLayoutWidgetVerticalListPosition' as const,
@@ -26,7 +26,6 @@ const makeWidget = (
title: id,
isActive: true,
type: WidgetType.FIELDS,
gridPosition: { column: 0, columnSpan: 1, row: 0, rowSpan: 1 },
configuration: { __typename: 'FieldsConfiguration' as const },
position: {
__typename: 'PageLayoutWidgetVerticalListPosition' as const,
@@ -5,6 +5,7 @@ import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget';
import { act, renderHook } from '@testing-library/react';
import { createStore } from 'jotai';
import { type ReactNode } from 'react';
import { isDefined } from 'twenty-shared/utils';
import {
PageLayoutTabLayoutMode,
PageLayoutType,
@@ -26,7 +27,6 @@ const makeWidget = (
title: id,
isActive: true,
type: WidgetType.FIELDS,
gridPosition: { column: 0, columnSpan: 1, row: 0, rowSpan: 1 },
configuration: { __typename: 'FieldsConfiguration' as const },
position: {
__typename: 'PageLayoutWidgetVerticalListPosition' as const,
@@ -142,12 +142,9 @@ describe('useMoveWidgetToTab', () => {
const draft = store.get(getDraftAtom());
const tab1Positions = draft.tabs[0].widgets.map((w) => {
if (w.position && 'index' in w.position) {
return w.position.index;
}
return -1;
});
const tab1Positions = draft.tabs[0].widgets.map((w) =>
isDefined(w.position) && 'index' in w.position ? w.position.index : -1,
);
expect(tab1Positions).toEqual([0, 1]);
});
@@ -4,6 +4,7 @@ import {
AggregateOperations,
BarChartLayout,
GraphOrderBy,
PageLayoutTabLayoutMode,
PageLayoutType,
WidgetConfigurationType,
WidgetType,
@@ -104,7 +105,14 @@ describe('usePageLayoutDraftState', () => {
title: 'New Widget',
isActive: true,
type: WidgetType.GRAPH,
gridPosition: { row: 2, column: 2, rowSpan: 2, columnSpan: 2 },
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 2,
column: 2,
rowSpan: 2,
columnSpan: 2,
},
configuration: {
configurationType: WidgetConfigurationType.BAR_CHART,
layout: BarChartLayout.VERTICAL,
@@ -53,8 +53,9 @@ describe('usePageLayoutWithRelationWidgets', () => {
isActive: true,
type: WidgetType.FIELDS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 1,
@@ -78,8 +79,9 @@ describe('usePageLayoutWithRelationWidgets', () => {
isActive: true,
type: WidgetType.NOTES,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 1,
column: 0,
rowSpan: 1,
@@ -102,8 +104,9 @@ describe('usePageLayoutWithRelationWidgets', () => {
isActive: true,
type: WidgetType.GRAPH,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 2,
column: 0,
rowSpan: 1,
@@ -228,8 +231,9 @@ describe('usePageLayoutWithRelationWidgets', () => {
isActive: true,
type: WidgetType.GRAPH,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 1,
@@ -121,7 +121,7 @@ export const useCreatePageLayoutGraphWidget = ({
id: widgetId,
pageLayoutTabId: activeTabId,
title,
gridPosition: {
position: {
row: position.y,
column: position.x,
rowSpan: position.h,
@@ -77,7 +77,7 @@ export const useCreatePageLayoutRecordTableWidget = (
id: widgetId,
pageLayoutTabId: activeTabId,
title: objectMetadata?.labelPlural ?? 'Record Table',
gridPosition: {
position: {
row: position.y,
column: position.x,
rowSpan: position.h,
@@ -2,6 +2,7 @@ import { type PageLayoutTab } from '@/page-layout/types/PageLayoutTab';
import { addWidgetToTab } from '@/page-layout/utils/addWidgetToTab';
import {
AggregateOperations,
PageLayoutTabLayoutMode,
WidgetConfigurationType,
WidgetType,
type PageLayoutWidget,
@@ -22,7 +23,14 @@ describe('addWidgetToTab', () => {
aggregateFieldMetadataId: 'id',
displayDataLabel: false,
},
gridPosition: { row: 0, column: 0, rowSpan: 2, columnSpan: 2 },
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 2,
columnSpan: 2,
},
objectMetadataId: null,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
@@ -18,7 +18,9 @@ describe('convertLayoutsToWidgets', () => {
title: 'Widget 1',
type: WidgetType.GRAPH,
objectMetadataId: null,
gridPosition: {
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 2,
@@ -42,7 +44,9 @@ describe('convertLayoutsToWidgets', () => {
title: 'Widget 2',
type: WidgetType.GRAPH,
objectMetadataId: null,
gridPosition: {
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 2,
rowSpan: 2,
@@ -72,12 +76,6 @@ describe('convertLayoutsToWidgets', () => {
const result = convertLayoutsToWidgets(mockWidgets, layouts);
expect(result[0].gridPosition).toEqual({
column: 2,
row: 3,
columnSpan: 4,
rowSpan: 5,
});
expect(result[0].position).toEqual({
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
@@ -86,12 +84,6 @@ describe('convertLayoutsToWidgets', () => {
columnSpan: 4,
rowSpan: 5,
});
expect(result[1].gridPosition).toEqual({
column: 6,
row: 7,
columnSpan: 8,
rowSpan: 9,
});
expect(result[1].position).toEqual({
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
@@ -109,7 +101,9 @@ describe('convertLayoutsToWidgets', () => {
const result = convertLayoutsToWidgets(mockWidgets, layouts);
expect(result[1].gridPosition).toEqual({
expect(result[1].position).toEqual({
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
column: 0,
row: 0,
columnSpan: 2,
@@ -124,7 +118,9 @@ describe('convertLayoutsToWidgets', () => {
const result = convertLayoutsToWidgets(mockWidgets, layouts);
expect(result[0].gridPosition).toEqual({
expect(result[0].position).toEqual({
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
column: 0,
row: 4,
columnSpan: 1,
@@ -15,7 +15,14 @@ const makeWidget = (
title: 'Widget',
type: WidgetType.FIELDS,
pageLayoutTabId: 'tab-1',
gridPosition: { row: 0, column: 0, rowSpan: 1, columnSpan: 1 },
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 1,
columnSpan: 1,
},
configuration: null,
objectMetadataId: null,
...overrides,
@@ -78,23 +85,24 @@ describe('convertPageLayoutDraftToUpdateInput', () => {
expect(result.tabs[0].widgets.map((w) => w.id)).toEqual(['w1', 'w2']);
});
it('should map gridPosition correctly', () => {
it('should map grid position correctly', () => {
const widget = makeWidget({
id: 'w1',
gridPosition: { row: 2, column: 3, rowSpan: 4, columnSpan: 5 },
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 2,
column: 3,
rowSpan: 4,
columnSpan: 5,
},
});
const draft = makeDraft([makeTab('tab-1', [widget])]);
const result = convertPageLayoutDraftToUpdateInput(draft);
expect(result.tabs[0].widgets[0].gridPosition).toEqual({
row: 2,
column: 3,
rowSpan: 4,
columnSpan: 5,
});
expect(result.tabs[0].widgets[0].position).toEqual({
layoutMode: 'GRID',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 2,
column: 3,
rowSpan: 4,
@@ -245,11 +253,17 @@ describe('convertPageLayoutDraftToUpdateInput', () => {
});
});
it('should produce GRID position from gridPosition when tab layoutMode is undefined', () => {
it('should default to GRID position when tab layoutMode is undefined', () => {
const widget = makeWidget({
id: 'w1',
position: null,
gridPosition: { row: 1, column: 2, rowSpan: 3, columnSpan: 4 },
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 1,
column: 2,
rowSpan: 3,
columnSpan: 4,
},
});
const draft = makeDraft([makeTab('tab-1', [widget])]);
@@ -267,7 +281,14 @@ describe('convertPageLayoutDraftToUpdateInput', () => {
it('should produce GRID position when tab layoutMode is explicitly GRID', () => {
const widget = makeWidget({
id: 'w1',
gridPosition: { row: 5, column: 6, rowSpan: 7, columnSpan: 8 },
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 5,
column: 6,
rowSpan: 7,
columnSpan: 8,
},
});
const draft = makeDraft([
makeTab('tab-1', [widget], PageLayoutTabLayoutMode.GRID),
@@ -288,10 +309,13 @@ describe('convertPageLayoutDraftToUpdateInput', () => {
const widget = makeWidget({
id: 'w1',
position: {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 1,
column: 2,
rowSpan: 3,
columnSpan: 4,
},
gridPosition: { row: 1, column: 2, rowSpan: 3, columnSpan: 4 },
});
const draft = makeDraft([
makeTab('tab-1', [widget], PageLayoutTabLayoutMode.GRID),
@@ -320,7 +344,14 @@ describe('convertPageLayoutDraftToUpdateInput', () => {
const canvasWidget = makeWidget({ id: 'w2' });
const gridWidget = makeWidget({
id: 'w3',
gridPosition: { row: 1, column: 0, rowSpan: 2, columnSpan: 6 },
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 1,
column: 0,
rowSpan: 2,
columnSpan: 6,
},
});
const draft = makeDraft([
@@ -4,6 +4,7 @@ import { convertPageLayoutToTabLayouts } from '@/page-layout/utils/convertPageLa
import {
AggregateOperations,
GraphOrderBy,
PageLayoutTabLayoutMode,
PageLayoutType,
WidgetConfigurationType,
WidgetType,
@@ -39,7 +40,14 @@ describe('convertPageLayoutToTabLayouts', () => {
aggregateFieldMetadataId: 'id',
displayDataLabel: false,
},
gridPosition: { row: 0, column: 0, rowSpan: 2, columnSpan: 2 },
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 2,
columnSpan: 2,
},
objectMetadataId: 'object-metadata-1',
createdAt: '2025-01-01T00:00:00.000Z',
updatedAt: '2025-01-01T00:00:00.000Z',
@@ -61,7 +69,14 @@ describe('convertPageLayoutToTabLayouts', () => {
orderBy: GraphOrderBy.VALUE_DESC,
displayDataLabel: false,
},
gridPosition: { row: 2, column: 0, rowSpan: 2, columnSpan: 2 },
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 2,
column: 0,
rowSpan: 2,
columnSpan: 2,
},
objectMetadataId: 'object-metadata-1',
createdAt: '2025-01-01T00:00:00.000Z',
updatedAt: '2025-01-01T00:00:00.000Z',
@@ -121,7 +136,14 @@ describe('convertPageLayoutToTabLayouts', () => {
configurationType: WidgetConfigurationType.STANDALONE_RICH_TEXT,
body: { blocknote: '[]' },
},
gridPosition: { row: 0, column: 0, rowSpan: 4, columnSpan: 4 },
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 4,
columnSpan: 4,
},
objectMetadataId: null,
createdAt: '2025-01-01T00:00:00.000Z',
updatedAt: '2025-01-01T00:00:00.000Z',
@@ -27,13 +27,6 @@ describe('createDefaultFieldsWidget', () => {
configurationType: WidgetConfigurationType.FIELDS,
viewId: 'view-1',
},
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 1,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetVerticalListPosition',
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
@@ -1,6 +1,5 @@
import { createDefaultFrontComponentWidget } from '@/page-layout/utils/createDefaultFrontComponentWidget';
import {
type GridPosition,
PageLayoutTabLayoutMode,
WidgetConfigurationType,
WidgetType,
@@ -8,8 +7,7 @@ import {
describe('createDefaultFrontComponentWidget', () => {
it('should return a FRONT_COMPONENT widget with grid position mapped to position', () => {
const gridPosition: GridPosition = {
__typename: 'GridPosition',
const position = {
row: 2,
column: 3,
rowSpan: 4,
@@ -21,7 +19,7 @@ describe('createDefaultFrontComponentWidget', () => {
'tab-1',
'My Component',
'front-comp-1',
gridPosition,
position,
);
expect(widget).toMatchObject({
@@ -36,7 +34,6 @@ describe('createDefaultFrontComponentWidget', () => {
configurationType: WidgetConfigurationType.FRONT_COMPONENT,
frontComponentId: 'front-comp-1',
},
gridPosition,
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
@@ -13,7 +13,7 @@ describe('createDefaultGraphWidget', () => {
id: 'widget-1',
pageLayoutTabId: 'tab-1',
title: 'Test Widget',
gridPosition: { row: 0, column: 0, rowSpan: 2, columnSpan: 2 },
position: { row: 0, column: 0, rowSpan: 2, columnSpan: 2 },
objectMetadataId: 'object-1',
};
@@ -67,7 +67,6 @@ describe('createDefaultGraphWidget', () => {
expect(widget.id).toBe('widget-1');
expect(widget.pageLayoutTabId).toBe('tab-1');
expect(widget.title).toBe('Test Widget');
expect(widget.gridPosition).toEqual(baseParams.gridPosition);
expect(widget.position).toEqual({
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
@@ -9,13 +9,13 @@ describe('createDefaultRecordTableWidget', () => {
const widgetId = 'widget-id-1';
const pageLayoutTabId = 'tab-id-1';
const title = 'My Record Table';
const gridPosition = { row: 1, column: 2, rowSpan: 3, columnSpan: 4 };
const position = { row: 1, column: 2, rowSpan: 3, columnSpan: 4 };
const widget = createDefaultRecordTableWidget({
id: widgetId,
pageLayoutTabId,
title,
gridPosition,
position,
});
it('should return correct shape with all required fields', () => {
@@ -38,15 +38,14 @@ describe('createDefaultRecordTableWidget', () => {
});
});
it('should correctly map grid position values to both gridPosition and position fields', () => {
expect(widget.gridPosition).toEqual(gridPosition);
it('should populate the position field with a GRID layoutMode', () => {
expect(widget.position).toEqual({
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: gridPosition.row,
column: gridPosition.column,
rowSpan: gridPosition.rowSpan,
columnSpan: gridPosition.columnSpan,
row: position.row,
column: position.column,
rowSpan: position.rowSpan,
columnSpan: position.columnSpan,
});
});
@@ -22,7 +22,6 @@ describe('createDefaultStandaloneRichTextWidget', () => {
configuration: {
body: { blocknote: '[{"type":"paragraph","content":"Test"}]' },
},
gridPosition: { row: 0, column: 0, rowSpan: 4, columnSpan: 4 },
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
@@ -1,7 +1,8 @@
import { extractFieldMetadataIdsFromWidget } from '@/page-layout/utils/extractFieldMetadataIdsFromWidget';
import {
type PageLayoutWidget,
PageLayoutTabLayoutMode,
WidgetType,
type PageLayoutWidget,
type WidgetConfiguration,
} from '~/generated-metadata/graphql';
import {
@@ -28,7 +29,14 @@ const createMockWidget = (
type: WidgetType.GRAPH,
title: 'Test',
objectMetadataId: null,
gridPosition: { row: 0, column: 0, rowSpan: 1, columnSpan: 1 },
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 1,
columnSpan: 1,
},
pageLayoutTabId: 'tab-1',
createdAt: '2024-01-01',
updatedAt: '2024-01-01',
@@ -1,6 +1,7 @@
import { type PageLayoutTab } from '@/page-layout/types/PageLayoutTab';
import { filterVisibleWidgets } from '@/page-layout/utils/filterVisibleWidgets';
import {
PageLayoutTabLayoutMode,
WidgetConfigurationType,
WidgetType,
} from '~/generated-metadata/graphql';
@@ -18,8 +19,9 @@ describe('filterVisibleWidgets', () => {
title: `Widget ${id}`,
type: WidgetType.FIELDS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 1,
@@ -1,6 +1,7 @@
import { type PageLayoutTab } from '@/page-layout/types/PageLayoutTab';
import { getTabsWithVisibleWidgets } from '@/page-layout/utils/getTabsWithVisibleWidgets';
import {
PageLayoutTabLayoutMode,
WidgetConfigurationType,
WidgetType,
} from '~/generated-metadata/graphql';
@@ -18,8 +19,9 @@ describe('getTabsWithVisibleWidgets', () => {
title: `Widget ${id}`,
type: WidgetType.FIELDS,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 1,
@@ -5,6 +5,7 @@ import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget';
import { injectRelationWidgetsIntoLayout } from '@/page-layout/utils/injectRelationWidgetsIntoLayout';
import {
FieldDisplayMode,
PageLayoutTabLayoutMode,
WidgetConfigurationType,
WidgetType,
} from '~/generated-metadata/graphql';
@@ -20,16 +21,9 @@ const createMockWidget = (
title: `Widget ${id}`,
type,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 1,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: 'GRID',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 1,
@@ -0,0 +1,39 @@
import { isCanvasPosition } from '@/page-layout/utils/isCanvasPosition';
import {
type PageLayoutWidgetPosition,
PageLayoutTabLayoutMode,
} from '~/generated-metadata/graphql';
describe('isCanvasPosition', () => {
it('should return true for CANVAS layout mode', () => {
const position: PageLayoutWidgetPosition = {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
} as PageLayoutWidgetPosition;
expect(isCanvasPosition(position)).toBe(true);
});
it('should return false for GRID layout mode', () => {
const position: PageLayoutWidgetPosition = {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 1,
columnSpan: 1,
};
expect(isCanvasPosition(position)).toBe(false);
});
it('should return false for VERTICAL_LIST layout mode', () => {
const position: PageLayoutWidgetPosition = {
__typename: 'PageLayoutWidgetVerticalListPosition',
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
index: 0,
};
expect(isCanvasPosition(position)).toBe(false);
});
});
@@ -3,6 +3,7 @@ import {
DYNAMIC_RELATION_WIDGET_ID_PREFIX,
isDynamicRelationWidget,
} from '@/page-layout/utils/isDynamicRelationWidget';
import { PageLayoutTabLayoutMode } from '~/generated-metadata/graphql';
const makeWidget = (id: string): PageLayoutWidget =>
({
@@ -10,7 +11,14 @@ const makeWidget = (id: string): PageLayoutWidget =>
title: 'Widget',
type: 'FIELDS',
pageLayoutTabId: 'tab-1',
gridPosition: { row: 0, column: 0, rowSpan: 1, columnSpan: 1 },
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 1,
columnSpan: 1,
},
configuration: {},
}) as PageLayoutWidget;
@@ -0,0 +1,39 @@
import { isGridPosition } from '@/page-layout/utils/isGridPosition';
import {
type PageLayoutWidgetPosition,
PageLayoutTabLayoutMode,
} from '~/generated-metadata/graphql';
describe('isGridPosition', () => {
it('should return true for GRID layout mode', () => {
const position: PageLayoutWidgetPosition = {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 1,
columnSpan: 1,
};
expect(isGridPosition(position)).toBe(true);
});
it('should return false for CANVAS layout mode', () => {
const position: PageLayoutWidgetPosition = {
__typename: 'PageLayoutWidgetCanvasPosition',
layoutMode: PageLayoutTabLayoutMode.CANVAS,
} as PageLayoutWidgetPosition;
expect(isGridPosition(position)).toBe(false);
});
it('should return false for VERTICAL_LIST layout mode', () => {
const position: PageLayoutWidgetPosition = {
__typename: 'PageLayoutWidgetVerticalListPosition',
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
index: 0,
};
expect(isGridPosition(position)).toBe(false);
});
});
@@ -2,6 +2,7 @@ import { PENDING_WIDGET_PLACEHOLDER_LAYOUT_KEY } from '@/page-layout/constants/P
import { prepareGridLayoutItemsWithPlaceholders } from '@/page-layout/utils/prepareGridLayoutItemsWithPlaceholders';
import {
AggregateOperations,
PageLayoutTabLayoutMode,
WidgetConfigurationType,
WidgetType,
type PageLayoutWidget,
@@ -16,7 +17,9 @@ describe('prepareGridLayoutItemsWithPlaceholders', () => {
title: `Test Widget ${id}`,
type: WidgetType.GRAPH,
objectMetadataId: null,
gridPosition: {
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 2,
@@ -212,7 +215,7 @@ describe('prepareGridLayoutItemsWithPlaceholders', () => {
expect(resultWidget.title).toBe(widget.title);
expect(resultWidget.type).toBe(widget.type);
expect(resultWidget.objectMetadataId).toBe(widget.objectMetadataId);
expect(resultWidget.gridPosition).toEqual(widget.gridPosition);
expect(resultWidget.position).toEqual(widget.position);
expect(resultWidget.configuration).toEqual(widget.configuration);
expect(resultWidget.createdAt).toBe(widget.createdAt);
expect(resultWidget.updatedAt).toBe(widget.updatedAt);
@@ -3,7 +3,10 @@ import { type PageLayout } from '@/page-layout/types/PageLayout';
import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget';
import { DYNAMIC_RELATION_WIDGET_ID_PREFIX } from '@/page-layout/utils/isDynamicRelationWidget';
import { reInjectDynamicRelationWidgetsFromDraft } from '@/page-layout/utils/reInjectDynamicRelationWidgetsFromDraft';
import { WidgetType } from '~/generated-metadata/graphql';
import {
PageLayoutTabLayoutMode,
WidgetType,
} from '~/generated-metadata/graphql';
const makeWidget = (
overrides: Partial<PageLayoutWidget> & { id: string },
@@ -12,7 +15,14 @@ const makeWidget = (
title: 'Widget',
type: WidgetType.FIELDS,
pageLayoutTabId: 'tab-1',
gridPosition: { row: 0, column: 0, rowSpan: 1, columnSpan: 1 },
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 1,
columnSpan: 1,
},
configuration: {},
...overrides,
}) as PageLayoutWidget;
@@ -3,6 +3,7 @@ import { removeWidgetFromTab } from '@/page-layout/utils/removeWidgetFromTab';
import {
AggregateOperations,
GraphOrderBy,
PageLayoutTabLayoutMode,
WidgetConfigurationType,
WidgetType,
} from '~/generated-metadata/graphql';
@@ -31,7 +32,14 @@ describe('removeWidgetFromTab', () => {
aggregateFieldMetadataId: 'id',
displayDataLabel: false,
},
gridPosition: { row: 0, column: 0, rowSpan: 2, columnSpan: 2 },
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 2,
columnSpan: 2,
},
objectMetadataId: null,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
@@ -53,7 +61,14 @@ describe('removeWidgetFromTab', () => {
orderBy: GraphOrderBy.VALUE_DESC,
displayDataLabel: false,
},
gridPosition: { row: 2, column: 0, rowSpan: 2, columnSpan: 2 },
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 2,
column: 0,
rowSpan: 2,
columnSpan: 2,
},
objectMetadataId: null,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
@@ -84,7 +99,14 @@ describe('removeWidgetFromTab', () => {
configurationType: WidgetConfigurationType.IFRAME,
url: 'https://example.com',
},
gridPosition: { row: 0, column: 0, rowSpan: 2, columnSpan: 2 },
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 2,
columnSpan: 2,
},
objectMetadataId: null,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
@@ -13,7 +13,6 @@ const makeWidget = (id: string, index: number): PageLayoutWidget =>
pageLayoutTabId: 'tab-1',
title: id,
type: WidgetType.FIELDS,
gridPosition: { row: 0, column: 0, rowSpan: 1, columnSpan: 1 },
configuration: {
__typename: 'FieldsConfiguration',
configurationType: WidgetConfigurationType.FIELDS,
@@ -18,12 +18,6 @@ export const convertLayoutsToWidgets = (
return {
...widget,
gridPosition: {
row,
column,
rowSpan,
columnSpan,
},
position: {
__typename: 'PageLayoutWidgetGridPosition' as const,
layoutMode: PageLayoutTabLayoutMode.GRID,
@@ -1,6 +1,9 @@
import { PAGE_LAYOUT_CONFIG } from '@/page-layout/constants/PageLayoutBreakpoints';
import { type DraftPageLayout } from '@/page-layout/types/DraftPageLayout';
import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget';
import { isDynamicRelationWidget } from '@/page-layout/utils/isDynamicRelationWidget';
import { isGridPosition } from '@/page-layout/utils/isGridPosition';
import { isVerticalListPosition } from '@/page-layout/utils/isVerticalListPosition';
import {
PageLayoutTabLayoutMode,
type UpdatePageLayoutWithTabsInput,
@@ -14,7 +17,7 @@ const buildWidgetPosition = (
switch (tabLayoutMode) {
case PageLayoutTabLayoutMode.VERTICAL_LIST: {
const index =
widget.position?.__typename === 'PageLayoutWidgetVerticalListPosition'
widget.position && isVerticalListPosition(widget.position)
? widget.position.index
: widgetIndex;
@@ -27,14 +30,27 @@ const buildWidgetPosition = (
return {
layoutMode: PageLayoutTabLayoutMode.CANVAS,
};
case PageLayoutTabLayoutMode.GRID:
case PageLayoutTabLayoutMode.GRID: {
if (widget.position && isGridPosition(widget.position)) {
return {
layoutMode: PageLayoutTabLayoutMode.GRID,
row: widget.position.row,
column: widget.position.column,
rowSpan: widget.position.rowSpan,
columnSpan: widget.position.columnSpan,
};
}
const columns = PAGE_LAYOUT_CONFIG.columns.desktop;
return {
layoutMode: PageLayoutTabLayoutMode.GRID,
row: widget.gridPosition.row,
column: widget.gridPosition.column,
rowSpan: widget.gridPosition.rowSpan,
columnSpan: widget.gridPosition.columnSpan,
row: Math.floor(widgetIndex / columns),
column: widgetIndex % columns,
rowSpan: 1,
columnSpan: 1,
};
}
}
};
@@ -67,12 +83,6 @@ export const convertPageLayoutDraftToUpdateInput = (
title: widget.title,
type: widget.type,
objectMetadataId: widget.objectMetadataId ?? null,
gridPosition: {
row: widget.gridPosition.row,
column: widget.gridPosition.column,
rowSpan: widget.gridPosition.rowSpan,
columnSpan: widget.gridPosition.columnSpan,
},
position: buildWidgetPosition(
widget,
widgetIndex,
@@ -2,6 +2,7 @@ import { DEFAULT_WIDGET_SIZE } from '@/page-layout/constants/DefaultWidgetSize';
import { type PageLayout } from '@/page-layout/types/PageLayout';
import { type TabLayouts } from '@/page-layout/types/TabLayouts';
import { getWidgetSize } from '@/page-layout/utils/getWidgetSize';
import { isGridPosition } from '@/page-layout/utils/isGridPosition';
import { isDefined } from 'twenty-shared/utils';
export const convertPageLayoutToTabLayouts = (
@@ -32,23 +33,24 @@ export const convertPageLayoutToTabLayouts = (
}
}
const gridPos =
isDefined(widget.position) &&
widget.position.__typename === 'PageLayoutWidgetGridPosition'
? {
row: widget.position.row,
column: widget.position.column,
rowSpan: widget.position.rowSpan,
columnSpan: widget.position.columnSpan,
}
: widget.gridPosition;
if (!widget.position || !isGridPosition(widget.position)) {
return {
i: widget.id,
x: 0,
y: 0,
w: 1,
h: 1,
minW,
minH,
};
}
return {
i: widget.id,
x: gridPos.column,
y: gridPos.row,
w: gridPos.columnSpan,
h: gridPos.rowSpan,
x: widget.position.column,
y: widget.position.row,
w: widget.position.columnSpan,
h: widget.position.rowSpan,
minW,
minH,
};
@@ -37,13 +37,6 @@ export const createDefaultFieldWidget = ({
fieldMetadataId,
fieldDisplayMode,
},
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 1,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetVerticalListPosition',
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
@@ -31,13 +31,6 @@ export const createDefaultFieldsWidget = ({
configurationType: WidgetConfigurationType.FIELDS,
viewId,
},
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 1,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetVerticalListPosition',
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
@@ -1,6 +1,6 @@
import {
type GridPosition,
PageLayoutTabLayoutMode,
type PageLayoutWidgetGridPosition,
WidgetConfigurationType,
WidgetType,
} from '~/generated-metadata/graphql';
@@ -11,7 +11,7 @@ export const createDefaultFrontComponentWidget = (
pageLayoutTabId: string,
title: string,
frontComponentId: string,
gridPosition: GridPosition,
position: Omit<PageLayoutWidgetGridPosition, '__typename' | 'layoutMode'>,
): PageLayoutWidget => {
return {
__typename: 'PageLayoutWidget',
@@ -26,14 +26,13 @@ export const createDefaultFrontComponentWidget = (
configurationType: WidgetConfigurationType.FRONT_COMPONENT,
frontComponentId,
},
gridPosition,
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: gridPosition.row,
column: gridPosition.column,
rowSpan: gridPosition.rowSpan,
columnSpan: gridPosition.columnSpan,
row: position.row,
column: position.column,
rowSpan: position.rowSpan,
columnSpan: position.columnSpan,
},
objectMetadataId: null,
createdAt: new Date().toISOString(),
@@ -3,9 +3,9 @@ import {
AxisNameDisplay,
BarChartLayout,
GraphOrderBy,
type GridPosition,
PageLayoutTabLayoutMode,
type PageLayoutWidget,
type PageLayoutWidgetGridPosition,
type WidgetConfiguration,
WidgetConfigurationType,
WidgetType,
@@ -41,7 +41,7 @@ type CreateDefaultGraphWidgetParams = {
id: string;
pageLayoutTabId: string;
title: string;
gridPosition: GridPosition;
position: Omit<PageLayoutWidgetGridPosition, '__typename' | 'layoutMode'>;
objectMetadataId?: string | null;
fieldSelection?: GraphWidgetFieldSelection;
timezone?: string;
@@ -52,7 +52,7 @@ export const createDefaultGraphWidget = ({
id,
pageLayoutTabId,
title,
gridPosition,
position,
objectMetadataId,
fieldSelection,
timezone,
@@ -76,14 +76,13 @@ export const createDefaultGraphWidget = ({
isActive: true,
type: WidgetType.GRAPH,
configuration,
gridPosition,
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: gridPosition.row,
column: gridPosition.column,
rowSpan: gridPosition.rowSpan,
columnSpan: gridPosition.columnSpan,
row: position.row,
column: position.column,
rowSpan: position.rowSpan,
columnSpan: position.columnSpan,
},
objectMetadataId: resolvedObjectMetadataId,
createdAt: new Date().toISOString(),
@@ -1,7 +1,7 @@
import {
type GridPosition,
PageLayoutTabLayoutMode,
type PageLayoutWidget,
type PageLayoutWidgetGridPosition,
WidgetConfigurationType,
WidgetType,
} from '~/generated-metadata/graphql';
@@ -11,7 +11,7 @@ export const createDefaultIframeWidget = (
pageLayoutTabId: string,
title: string,
url: string | null,
gridPosition: GridPosition,
position: Omit<PageLayoutWidgetGridPosition, '__typename' | 'layoutMode'>,
objectMetadataId?: string | null,
): PageLayoutWidget => {
return {
@@ -26,14 +26,13 @@ export const createDefaultIframeWidget = (
configurationType: WidgetConfigurationType.IFRAME,
url,
},
gridPosition,
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: gridPosition.row,
column: gridPosition.column,
rowSpan: gridPosition.rowSpan,
columnSpan: gridPosition.columnSpan,
row: position.row,
column: position.column,
rowSpan: position.rowSpan,
columnSpan: position.columnSpan,
},
objectMetadataId: objectMetadataId ?? null,
createdAt: new Date().toISOString(),
@@ -1,7 +1,7 @@
import {
type GridPosition,
PageLayoutTabLayoutMode,
type PageLayoutWidget,
type PageLayoutWidgetGridPosition,
WidgetConfigurationType,
WidgetType,
} from '~/generated-metadata/graphql';
@@ -10,13 +10,13 @@ export const createDefaultRecordTableWidget = ({
id,
pageLayoutTabId,
title,
gridPosition,
position,
objectMetadataId,
}: {
id: string;
pageLayoutTabId: string;
title: string;
gridPosition: GridPosition;
position: Omit<PageLayoutWidgetGridPosition, '__typename' | 'layoutMode'>;
objectMetadataId?: string;
}): PageLayoutWidget => {
return {
@@ -30,14 +30,13 @@ export const createDefaultRecordTableWidget = ({
configuration: {
configurationType: WidgetConfigurationType.RECORD_TABLE,
},
gridPosition,
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: gridPosition.row,
column: gridPosition.column,
rowSpan: gridPosition.rowSpan,
columnSpan: gridPosition.columnSpan,
row: position.row,
column: position.column,
rowSpan: position.rowSpan,
columnSpan: position.columnSpan,
},
objectMetadataId: objectMetadataId ?? null,
isOverridden: false,
@@ -1,7 +1,7 @@
import {
type GridPosition,
PageLayoutTabLayoutMode,
type PageLayoutWidget,
type PageLayoutWidgetGridPosition,
type RichTextBody,
WidgetConfigurationType,
WidgetType,
@@ -11,7 +11,7 @@ export const createDefaultStandaloneRichTextWidget = (
id: string,
pageLayoutTabId: string,
body: RichTextBody,
gridPosition: GridPosition,
position: Omit<PageLayoutWidgetGridPosition, '__typename' | 'layoutMode'>,
objectMetadataId?: string | null,
): PageLayoutWidget => {
return {
@@ -26,14 +26,13 @@ export const createDefaultStandaloneRichTextWidget = (
configurationType: WidgetConfigurationType.STANDALONE_RICH_TEXT,
body,
},
gridPosition,
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: gridPosition.row,
column: gridPosition.column,
rowSpan: gridPosition.rowSpan,
columnSpan: gridPosition.columnSpan,
row: position.row,
column: position.column,
rowSpan: position.rowSpan,
columnSpan: position.columnSpan,
},
objectMetadataId: objectMetadataId ?? null,
createdAt: new Date().toISOString(),
@@ -22,13 +22,6 @@ const getRelationFieldWidgetToInsert = (
isActive: true,
type: WidgetType.FIELD,
objectMetadataId: null,
gridPosition: {
__typename: 'GridPosition' as const,
row: 0,
column: 0,
rowSpan: 1,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetGridPosition' as const,
layoutMode: PageLayoutTabLayoutMode.GRID,
@@ -0,0 +1,10 @@
import {
type PageLayoutWidgetCanvasPosition,
type PageLayoutWidgetPosition,
PageLayoutTabLayoutMode,
} from '~/generated-metadata/graphql';
export const isCanvasPosition = (
position: PageLayoutWidgetPosition,
): position is PageLayoutWidgetCanvasPosition =>
position.layoutMode === PageLayoutTabLayoutMode.CANVAS;
@@ -0,0 +1,10 @@
import {
type PageLayoutWidgetGridPosition,
type PageLayoutWidgetPosition,
PageLayoutTabLayoutMode,
} from '~/generated-metadata/graphql';
export const isGridPosition = (
position: PageLayoutWidgetPosition,
): position is PageLayoutWidgetGridPosition =>
position.layoutMode === PageLayoutTabLayoutMode.GRID;
@@ -260,8 +260,9 @@ export const WithNumberChart: Story = {
type: WidgetType.GRAPH,
title: 'Sales Pipeline',
objectMetadataId: companyObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 2,
@@ -348,8 +349,9 @@ export const WithGaugeChart: Story = {
type: WidgetType.GRAPH,
title: 'Conversion Rate',
objectMetadataId: companyObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 5,
@@ -436,8 +438,9 @@ export const WithBarChart: Story = {
type: WidgetType.GRAPH,
title: 'Monthly Trends',
objectMetadataId: companyObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 5,
@@ -535,8 +538,9 @@ export const SmallWidget: Story = {
type: WidgetType.GRAPH,
title: 'Small Widget (2x2 grid)',
objectMetadataId: companyObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 2,
@@ -630,8 +634,9 @@ export const MediumWidget: Story = {
type: WidgetType.GRAPH,
title: 'Medium Widget (4x3 grid)',
objectMetadataId: companyObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 3,
@@ -729,8 +734,9 @@ export const LargeWidget: Story = {
type: WidgetType.GRAPH,
title: 'Large Widget (6x4 grid)',
objectMetadataId: companyObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 4,
@@ -828,8 +834,9 @@ export const WideWidget: Story = {
type: WidgetType.GRAPH,
title: 'Wide Widget (8x2 grid)',
objectMetadataId: companyObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 2,
@@ -923,8 +930,9 @@ export const TallWidget: Story = {
type: WidgetType.GRAPH,
title: 'Tall Widget (3x6 grid)',
objectMetadataId: companyObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 6,
@@ -1023,8 +1031,9 @@ export const WithManyToOneRelationFieldWidget: Story = {
type: WidgetType.FIELD,
title: 'Account Owner',
objectMetadataId: companyObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 1,
@@ -1143,8 +1152,9 @@ export const WithOneToManyRelationFieldWidget: Story = {
type: WidgetType.FIELD,
title: 'People',
objectMetadataId: companyObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 1,
@@ -1255,8 +1265,9 @@ export const OneToManyRelationFieldWidgetWithSeeAllButton: Story = {
type: WidgetType.FIELD,
title: 'People',
objectMetadataId: companyObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 1,
@@ -1393,8 +1404,9 @@ export const OnMobile: Story = {
type: WidgetType.GRAPH,
title: 'Mobile Widget',
objectMetadataId: companyObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 2,
@@ -1492,8 +1504,9 @@ export const InSidePanel: Story = {
type: WidgetType.GRAPH,
title: 'Side Panel Widget',
objectMetadataId: companyObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 2,
@@ -1652,8 +1665,9 @@ export const Catalog: CatalogStory<Story, typeof WidgetRenderer> = {
type: WidgetType.GRAPH,
title: 'Widget name',
objectMetadataId: companyObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 2,
@@ -340,8 +340,9 @@ export const TextFieldWidget: Story = {
type: WidgetType.FIELD,
title: 'Company Name',
objectMetadataId: companyObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 1,
@@ -435,8 +436,9 @@ export const AddressFieldWidget: Story = {
type: WidgetType.FIELD,
title: 'Address',
objectMetadataId: companyObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 1,
column: 0,
rowSpan: 1,
@@ -533,8 +535,9 @@ export const NumberFieldWidget: Story = {
type: WidgetType.FIELD,
title: 'Employees',
objectMetadataId: companyObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 2,
column: 0,
rowSpan: 1,
@@ -628,8 +631,9 @@ export const LinkFieldWidget: Story = {
type: WidgetType.FIELD,
title: 'LinkedIn',
objectMetadataId: companyObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 3,
column: 0,
rowSpan: 1,
@@ -723,8 +727,9 @@ export const ManyToOneRelationFieldWidget: Story = {
type: WidgetType.FIELD,
title: 'Account Owner',
objectMetadataId: companyObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 4,
column: 0,
rowSpan: 1,
@@ -828,8 +833,9 @@ export const OneToManyRelationFieldWidget: Story = {
type: WidgetType.FIELD,
title: 'People',
objectMetadataId: companyObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 11,
column: 0,
rowSpan: 1,
@@ -925,8 +931,9 @@ export const BooleanFieldWidget: Story = {
type: WidgetType.FIELD,
title: 'Ideal Customer Profile',
objectMetadataId: companyObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 5,
column: 0,
rowSpan: 1,
@@ -1019,8 +1026,9 @@ export const CurrencyFieldWidget: Story = {
type: WidgetType.FIELD,
title: 'Annual Recurring Revenue',
objectMetadataId: companyObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 6,
column: 0,
rowSpan: 1,
@@ -1113,8 +1121,9 @@ export const EmailsFieldWidget: Story = {
type: WidgetType.FIELD,
title: 'Emails',
objectMetadataId: personObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 7,
column: 0,
rowSpan: 1,
@@ -1208,8 +1217,9 @@ export const PhonesFieldWidget: Story = {
type: WidgetType.FIELD,
title: 'Phones',
objectMetadataId: personObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 8,
column: 0,
rowSpan: 1,
@@ -1303,8 +1313,9 @@ export const SelectFieldWidget: Story = {
type: WidgetType.FIELD,
title: 'Stage',
objectMetadataId: opportunityObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 9,
column: 0,
rowSpan: 1,
@@ -1399,8 +1410,9 @@ export const MultiSelectFieldWidget: Story = {
type: WidgetType.FIELD,
title: 'Work Policy',
objectMetadataId: companyObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 10,
column: 0,
rowSpan: 1,
@@ -1498,8 +1510,9 @@ export const TimelineActivityRelationFieldWidget: Story = {
type: WidgetType.FIELD,
title: 'Workspace Member',
objectMetadataId: timelineActivityObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 12,
column: 0,
rowSpan: 1,
@@ -1598,8 +1611,9 @@ export const ManyToOneRelationCardWidget: Story = {
type: WidgetType.FIELD,
title: 'Account Owner',
objectMetadataId: companyObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 4,
column: 0,
rowSpan: 1,
@@ -1711,8 +1725,9 @@ export const OneToManyRelationCardWidget: Story = {
type: WidgetType.FIELD,
title: 'People',
objectMetadataId: companyObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 11,
column: 0,
rowSpan: 1,
@@ -1807,8 +1822,9 @@ export const TimelineActivityRelationCardWidget: Story = {
type: WidgetType.FIELD,
title: 'Workspace Member',
objectMetadataId: timelineActivityObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 12,
column: 0,
rowSpan: 1,
@@ -1969,8 +1985,9 @@ export const OneToManyRelationCardWidgetWithProgressiveLoading: Story = {
type: WidgetType.FIELD,
title: 'People',
objectMetadataId: companyObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 11,
column: 0,
rowSpan: 1,
@@ -167,8 +167,9 @@ const createFieldsWidget = (viewId: string | null): PageLayoutWidget => ({
type: WidgetType.FIELDS,
title: 'Fields',
objectMetadataId: companyObjectMetadataItem.id,
gridPosition: {
__typename: 'GridPosition',
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 4,
@@ -286,13 +286,6 @@ export const SidePanelPageLayoutRecordPageWidgetTypeSelect = () => {
configurationType: WidgetConfigurationType.FRONT_COMPONENT,
frontComponentId: frontComponent.id,
},
gridPosition: {
__typename: 'GridPosition',
row: 0,
column: 0,
rowSpan: 1,
columnSpan: 12,
},
position: {
__typename: 'PageLayoutWidgetVerticalListPosition',
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
@@ -12,6 +12,7 @@ import {
type FieldsConfiguration,
GraphOrderBy,
type IframeConfiguration,
PageLayoutTabLayoutMode,
type StandaloneRichTextConfiguration,
type WidgetConfiguration,
WidgetConfigurationType,
@@ -127,7 +128,14 @@ export const createTestWidget = (
pageLayoutTabId: 'tab-1',
title: 'Test Widget',
type: WidgetType.GRAPH,
gridPosition: { row: 0, column: 0, rowSpan: 2, columnSpan: 2 },
position: {
__typename: 'PageLayoutWidgetGridPosition',
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 2,
columnSpan: 2,
},
objectMetadataId: TEST_OBJECT_METADATA_ID,
configuration: TEST_BAR_CHART_CONFIGURATION,
createdAt: '2024-01-01',
@@ -119,7 +119,6 @@ export {
ViewVisibility,
} from 'twenty-shared/types';
export type {
GridPosition,
PageLayoutWidgetConditionalDisplay,
PageLayoutWidgetUniversalConfiguration,
} from 'twenty-shared/types';
@@ -0,0 +1,21 @@
import { QueryRunner } from 'typeorm';
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
import { FastInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/fast-instance-command.interface';
@RegisteredInstanceCommand('2.2.0', 1777473592000)
export class MakePageLayoutWidgetGridPositionNullableFastInstanceCommand
implements FastInstanceCommand
{
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "core"."pageLayoutWidget" ALTER COLUMN "gridPosition" DROP NOT NULL`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "core"."pageLayoutWidget" ALTER COLUMN "gridPosition" SET NOT NULL`,
);
}
}
@@ -0,0 +1,32 @@
import { DataSource, QueryRunner } from 'typeorm';
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
import { SlowInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/slow-instance-command.interface';
@RegisteredInstanceCommand('2.2.0', 1795000003000, { type: 'slow' })
export class BackfillPageLayoutWidgetPositionAgainSlowInstanceCommand
implements SlowInstanceCommand
{
async runDataMigration(dataSource: DataSource): Promise<void> {
await dataSource.query(
`UPDATE "core"."pageLayoutWidget"
SET "position" = jsonb_build_object(
'layoutMode', 'GRID',
'row', "gridPosition"->'row',
'column', "gridPosition"->'column',
'rowSpan', "gridPosition"->'rowSpan',
'columnSpan', "gridPosition"->'columnSpan'
)
WHERE "position" IS NULL
AND "gridPosition" IS NOT NULL`,
);
}
public async up(_queryRunner: QueryRunner): Promise<void> {
return;
}
public async down(_queryRunner: QueryRunner): Promise<void> {
return;
}
}
@@ -18,9 +18,11 @@ import { DropWorkspaceVersionColumnFastInstanceCommand } from 'src/database/comm
import { AddIsPreInstalledToApplicationRegistrationFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-1/2-1-instance-command-fast-1776886452831-add-is-pre-installed-to-application-registration';
import { AddProviderExecutedToAgentMessagePartFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-1/2-1-instance-command-fast-1777012800000-add-provider-executed-to-agent-message-part';
import { BackfillPageLayoutWidgetPositionSlowInstanceCommand } from 'src/database/commands/upgrade-version-command/2-1/2-1-instance-command-slow-1795000002000-backfill-page-layout-widget-position';
import { AddUpgradeMigrationWorkspaceIdIndexFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-3/2-3-instance-command-fast-1777308014234-add-upgrade-migration-workspace-id-index';
import { AddCacheTokensToAgentChatThreadFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-2/2-2-instance-command-fast-1777455269302-add-cache-tokens-to-agent-chat-thread';
import { MakePageLayoutWidgetGridPositionNullableFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-2/2-2-instance-command-fast-1777473592000-make-page-layout-widget-grid-position-nullable';
import { AddLogoToApplicationFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-2/2-2-instance-command-fast-1777539664664-add-logo-to-application';
import { BackfillPageLayoutWidgetPositionAgainSlowInstanceCommand } from 'src/database/commands/upgrade-version-command/2-2/2-2-instance-command-slow-1795000003000-backfill-page-layout-widget-position-again';
import { AddUpgradeMigrationWorkspaceIdIndexFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-3/2-3-instance-command-fast-1777308014234-add-upgrade-migration-workspace-id-index';
import { AddDeletedAtToAgentChatThreadFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-3/2-3-instance-command-fast-1777682000000-add-deleted-at-to-agent-chat-thread';
import { ConnectionProviderSyncableEntityFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-3/2-3-instance-command-fast-1777896012579-connection-provider-syncable-entity';
import { RemoveUserDefaultAvatarUrlFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-3/2-3-instance-command-fast-1777915958318-remove-user-default-avatar-url';
@@ -45,6 +47,8 @@ export const INSTANCE_COMMANDS = [
AddIsPreInstalledToApplicationRegistrationFastInstanceCommand,
AddProviderExecutedToAgentMessagePartFastInstanceCommand,
BackfillPageLayoutWidgetPositionSlowInstanceCommand,
MakePageLayoutWidgetGridPositionNullableFastInstanceCommand,
BackfillPageLayoutWidgetPositionAgainSlowInstanceCommand,
AddCacheTokensToAgentChatThreadFastInstanceCommand,
AddLogoToApplicationFastInstanceCommand,
AddDeletedAtToAgentChatThreadFastInstanceCommand,
@@ -1,3 +1,5 @@
import { PageLayoutTabLayoutMode } from 'twenty-shared/types';
import { WidgetType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-type.enum';
import { fromPageLayoutWidgetManifestToUniversalFlatPageLayoutWidget } from 'src/engine/core-modules/application/application-manifest/converters/from-page-layout-widget-manifest-to-universal-flat-page-layout-widget.util';
@@ -12,9 +14,12 @@ describe('fromPageLayoutWidgetManifestToUniversalFlatPageLayoutWidget', () => {
universalIdentifier: 'widget-uuid-1',
title: 'My Widget',
type: WidgetType.VIEW,
position: { layoutMode: PageLayoutTabLayoutMode.CANVAS },
configuration: { configurationType: 'VIEW' },
},
pageLayoutTabUniversalIdentifier,
pageLayoutTabLayoutMode: PageLayoutTabLayoutMode.CANVAS,
widgetIndexInTab: 0,
applicationUniversalIdentifier,
now,
});
@@ -30,13 +35,9 @@ describe('fromPageLayoutWidgetManifestToUniversalFlatPageLayoutWidget', () => {
expect(result.type).toBe(WidgetType.VIEW);
expect(result.objectMetadataUniversalIdentifier).toBeNull();
expect(result.conditionalDisplay).toBeNull();
expect(result.gridPosition).toEqual({
row: 0,
column: 0,
rowSpan: 1,
columnSpan: 1,
expect(result.position).toEqual({
layoutMode: PageLayoutTabLayoutMode.CANVAS,
});
expect(result.position).toBeNull();
expect(result.universalConfiguration).toEqual({
configurationType: 'VIEW',
});
@@ -49,12 +50,21 @@ describe('fromPageLayoutWidgetManifestToUniversalFlatPageLayoutWidget', () => {
title: 'Iframe Widget',
type: 'IFRAME',
objectUniversalIdentifier: 'obj-uuid-1',
position: {
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 1,
columnSpan: 1,
},
configuration: {
configurationType: 'IFRAME',
url: 'https://example.com',
},
},
pageLayoutTabUniversalIdentifier,
pageLayoutTabLayoutMode: PageLayoutTabLayoutMode.GRID,
widgetIndexInTab: 0,
applicationUniversalIdentifier,
now,
});
@@ -62,7 +72,8 @@ describe('fromPageLayoutWidgetManifestToUniversalFlatPageLayoutWidget', () => {
expect(result.title).toBe('Iframe Widget');
expect(result.type).toBe('IFRAME');
expect(result.objectMetadataUniversalIdentifier).toBe('obj-uuid-1');
expect(result.gridPosition).toEqual({
expect(result.position).toEqual({
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 1,
@@ -74,25 +85,143 @@ describe('fromPageLayoutWidgetManifestToUniversalFlatPageLayoutWidget', () => {
});
});
it('should use manifest gridPosition when provided', () => {
it('should preserve a vertical-list position from the manifest', () => {
const result = fromPageLayoutWidgetManifestToUniversalFlatPageLayoutWidget({
pageLayoutWidgetManifest: {
universalIdentifier: 'widget-uuid-3',
title: 'Positioned Widget',
type: WidgetType.GRAPH,
gridPosition: { row: 2, column: 6, rowSpan: 4, columnSpan: 6 },
position: {
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
index: 2,
},
configuration: { configurationType: 'VIEW' },
},
pageLayoutTabUniversalIdentifier,
pageLayoutTabLayoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
widgetIndexInTab: 0,
applicationUniversalIdentifier,
now,
});
expect(result.gridPosition).toEqual({
row: 2,
column: 6,
rowSpan: 4,
columnSpan: 6,
expect(result.position).toEqual({
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
index: 2,
});
});
it('should default position to a CANVAS shape when manifest position is missing on a CANVAS tab', () => {
const result = fromPageLayoutWidgetManifestToUniversalFlatPageLayoutWidget({
pageLayoutWidgetManifest: {
universalIdentifier: 'widget-uuid-4',
title: 'Front Component Widget',
type: 'FRONT_COMPONENT',
configuration: {
configurationType: 'FRONT_COMPONENT',
frontComponentUniversalIdentifier: 'fc-uuid-1',
},
},
pageLayoutTabUniversalIdentifier,
pageLayoutTabLayoutMode: PageLayoutTabLayoutMode.CANVAS,
widgetIndexInTab: 0,
applicationUniversalIdentifier,
now,
});
expect(result.position).toEqual({
layoutMode: PageLayoutTabLayoutMode.CANVAS,
});
});
it('should default position to a VERTICAL_LIST shape using the widget index when missing', () => {
const result = fromPageLayoutWidgetManifestToUniversalFlatPageLayoutWidget({
pageLayoutWidgetManifest: {
universalIdentifier: 'widget-uuid-5',
title: 'Vertical List Widget',
type: WidgetType.VIEW,
configuration: { configurationType: 'VIEW' },
},
pageLayoutTabUniversalIdentifier,
pageLayoutTabLayoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
widgetIndexInTab: 3,
applicationUniversalIdentifier,
now,
});
expect(result.position).toEqual({
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
index: 3,
});
});
it('should default position to a GRID shape when missing and tab layoutMode is unspecified', () => {
const result = fromPageLayoutWidgetManifestToUniversalFlatPageLayoutWidget({
pageLayoutWidgetManifest: {
universalIdentifier: 'widget-uuid-6',
title: 'Grid Default Widget',
type: WidgetType.VIEW,
configuration: { configurationType: 'VIEW' },
},
pageLayoutTabUniversalIdentifier,
widgetIndexInTab: 0,
applicationUniversalIdentifier,
now,
});
expect(result.position).toEqual({
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 1,
columnSpan: 1,
});
});
it('should advance the GRID default column by widgetIndexInTab', () => {
const result = fromPageLayoutWidgetManifestToUniversalFlatPageLayoutWidget({
pageLayoutWidgetManifest: {
universalIdentifier: 'widget-uuid-7',
title: 'Grid Default Widget Index 5',
type: WidgetType.VIEW,
configuration: { configurationType: 'VIEW' },
},
pageLayoutTabUniversalIdentifier,
pageLayoutTabLayoutMode: PageLayoutTabLayoutMode.GRID,
widgetIndexInTab: 5,
applicationUniversalIdentifier,
now,
});
expect(result.position).toEqual({
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 5,
rowSpan: 1,
columnSpan: 1,
});
});
it('should wrap GRID defaults to the next row past the column limit', () => {
const result = fromPageLayoutWidgetManifestToUniversalFlatPageLayoutWidget({
pageLayoutWidgetManifest: {
universalIdentifier: 'widget-uuid-8',
title: 'Grid Default Widget Wrapping',
type: WidgetType.VIEW,
configuration: { configurationType: 'VIEW' },
},
pageLayoutTabUniversalIdentifier,
pageLayoutTabLayoutMode: PageLayoutTabLayoutMode.GRID,
widgetIndexInTab: 13,
applicationUniversalIdentifier,
now,
});
expect(result.position).toEqual({
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 1,
column: 1,
rowSpan: 1,
columnSpan: 1,
});
});
});
@@ -1,19 +1,32 @@
import { type PageLayoutWidgetManifest } from 'twenty-shared/application';
import { PageLayoutTabLayoutMode } from 'twenty-shared/types';
import { type WidgetType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-type.enum';
import { getDefaultPageLayoutWidgetPosition } from 'src/engine/metadata-modules/page-layout-widget/utils/get-default-page-layout-widget-position.util';
import { type UniversalFlatPageLayoutWidget } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-page-layout-widget.type';
export const fromPageLayoutWidgetManifestToUniversalFlatPageLayoutWidget = ({
pageLayoutWidgetManifest,
pageLayoutTabUniversalIdentifier,
pageLayoutTabLayoutMode,
widgetIndexInTab,
applicationUniversalIdentifier,
now,
}: {
pageLayoutWidgetManifest: PageLayoutWidgetManifest;
pageLayoutTabUniversalIdentifier: string;
pageLayoutTabLayoutMode?: PageLayoutTabLayoutMode;
widgetIndexInTab: number;
applicationUniversalIdentifier: string;
now: string;
}): UniversalFlatPageLayoutWidget => {
const resolvedLayoutMode =
pageLayoutTabLayoutMode ?? PageLayoutTabLayoutMode.GRID;
const position =
pageLayoutWidgetManifest.position ??
getDefaultPageLayoutWidgetPosition(resolvedLayoutMode, widgetIndexInTab);
return {
universalIdentifier: pageLayoutWidgetManifest.universalIdentifier,
applicationUniversalIdentifier,
@@ -24,13 +37,8 @@ export const fromPageLayoutWidgetManifestToUniversalFlatPageLayoutWidget = ({
objectMetadataUniversalIdentifier:
pageLayoutWidgetManifest.objectUniversalIdentifier ?? null,
conditionalDisplay: pageLayoutWidgetManifest.conditionalDisplay ?? null,
gridPosition: pageLayoutWidgetManifest.gridPosition ?? {
row: 0,
column: 0,
rowSpan: 1,
columnSpan: 1,
},
position: null,
gridPosition: null,
position,
universalConfiguration:
pageLayoutWidgetManifest.configuration as UniversalFlatPageLayoutWidget['universalConfiguration'],
createdAt: now,
@@ -393,14 +393,23 @@ export const computeApplicationManifestAllUniversalFlatEntityMaps = ({
allUniversalFlatEntityMaps.flatPageLayoutTabMaps,
});
for (const pageLayoutWidgetManifest of pageLayoutTabManifest.widgets ??
[]) {
const widgets = pageLayoutTabManifest.widgets ?? [];
for (
let widgetIndexInTab = 0;
widgetIndexInTab < widgets.length;
widgetIndexInTab++
) {
const pageLayoutWidgetManifest = widgets[widgetIndexInTab];
addUniversalFlatEntityToUniversalFlatEntityMapsThroughMutationOrThrow({
universalFlatEntity:
fromPageLayoutWidgetManifestToUniversalFlatPageLayoutWidget({
pageLayoutWidgetManifest,
pageLayoutTabUniversalIdentifier:
pageLayoutTabManifest.universalIdentifier,
pageLayoutTabLayoutMode: pageLayoutTabManifest.layoutMode,
widgetIndexInTab,
applicationUniversalIdentifier,
now,
}),
@@ -431,14 +440,23 @@ export const computeApplicationManifestAllUniversalFlatEntityMaps = ({
allUniversalFlatEntityMaps.flatPageLayoutTabMaps,
});
for (const pageLayoutWidgetManifest of pageLayoutTabManifest.widgets ??
[]) {
const widgets = pageLayoutTabManifest.widgets ?? [];
for (
let widgetIndexInTab = 0;
widgetIndexInTab < widgets.length;
widgetIndexInTab++
) {
const pageLayoutWidgetManifest = widgets[widgetIndexInTab];
addUniversalFlatEntityToUniversalFlatEntityMapsThroughMutationOrThrow({
universalFlatEntity:
fromPageLayoutWidgetManifestToUniversalFlatPageLayoutWidget({
pageLayoutWidgetManifest,
pageLayoutTabUniversalIdentifier:
pageLayoutTabManifest.universalIdentifier,
pageLayoutTabLayoutMode: pageLayoutTabManifest.layoutMode,
widgetIndexInTab,
applicationUniversalIdentifier,
now,
}),
@@ -1225,16 +1225,22 @@ export const computeMetadataSchemaComponents = (
return schemas;
}
case 'pageLayoutWidget': {
schemas['GridPosition'] = {
schemas['PageLayoutWidgetPosition'] = {
type: 'object',
description: 'Grid position for widget placement',
description:
'Widget position. Shape depends on the parent tab layoutMode: GRID requires row/column/rowSpan/columnSpan, VERTICAL_LIST requires index, CANVAS has no extra fields.',
properties: {
layoutMode: {
type: 'string',
enum: ['GRID', 'VERTICAL_LIST', 'CANVAS'],
},
row: { type: 'number', minimum: 0 },
column: { type: 'number', minimum: 0 },
rowSpan: { type: 'number', minimum: 1 },
columnSpan: { type: 'number', minimum: 1 },
index: { type: 'number', minimum: 0 },
},
required: ['row', 'column', 'rowSpan', 'columnSpan'],
required: ['layoutMode'],
};
schemas[`${capitalize(item.nameSingular)}`] = {
@@ -1260,15 +1266,15 @@ export const computeMetadataSchemaComponents = (
default: 'VIEW',
},
objectMetadataId: { type: 'string', format: 'uuid' },
gridPosition: {
$ref: '#/components/schemas/GridPosition',
position: {
$ref: '#/components/schemas/PageLayoutWidgetPosition',
},
configuration: {
type: 'object',
description: 'Widget-specific configuration',
},
},
required: ['pageLayoutTabId', 'title', 'gridPosition'],
required: ['pageLayoutTabId', 'title', 'position'],
};
schemas[`${capitalize(item.namePlural)}`] = {
type: 'array',
@@ -1287,8 +1293,8 @@ export const computeMetadataSchemaComponents = (
enum: ['VIEW', 'IFRAME', 'FIELDS', 'GRAPH'],
},
objectMetadataId: { type: 'string', format: 'uuid' },
gridPosition: {
$ref: '#/components/schemas/GridPosition',
position: {
$ref: '#/components/schemas/PageLayoutWidgetPosition',
},
configuration: {
type: 'object',
@@ -1308,8 +1314,8 @@ export const computeMetadataSchemaComponents = (
enum: ['VIEW', 'IFRAME', 'FIELDS', 'GRAPH'],
},
objectMetadataId: { type: 'string', format: 'uuid' },
gridPosition: {
$ref: '#/components/schemas/GridPosition',
position: {
$ref: '#/components/schemas/PageLayoutWidgetPosition',
},
configuration: {
type: 'object',
@@ -201,7 +201,6 @@ exports[`ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY should ma
"title",
"type",
"objectMetadataUniversalIdentifier",
"gridPosition",
"position",
"universalConfiguration",
"deletedAt",
@@ -211,7 +210,6 @@ exports[`ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY should ma
"universalOverrides",
],
"propertiesToStringify": [
"gridPosition",
"position",
"universalConfiguration",
"conditionalDisplay",
@@ -4,7 +4,6 @@ export const FLAT_PAGE_LAYOUT_WIDGET_EDITABLE_PROPERTIES = [
'title',
'type',
'objectMetadataId',
'gridPosition',
'position',
'configuration',
'conditionalDisplay',
@@ -1,10 +1,15 @@
import { isDefined } from 'twenty-shared/utils';
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
import { resolveEntityRelationUniversalIdentifiers } from 'src/engine/metadata-modules/flat-entity/utils/resolve-entity-relation-universal-identifiers.util';
import { type FlatPageLayoutWidget } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget.type';
import { type CreatePageLayoutWidgetInput } from 'src/engine/metadata-modules/page-layout-widget/dtos/inputs/create-page-layout-widget.input';
import { getDefaultPageLayoutWidgetPosition } from 'src/engine/metadata-modules/page-layout-widget/utils/get-default-page-layout-widget-position.util';
export const buildFlatPageLayoutWidgetCommonProperties = ({
widgetInput,
widgetIndexInTab,
flatPageLayoutTabMaps,
flatObjectMetadataMaps,
}: {
@@ -17,6 +22,7 @@ export const buildFlatPageLayoutWidgetCommonProperties = ({
| 'gridPosition'
| 'position'
>;
widgetIndexInTab?: number;
} & Pick<
AllFlatEntityMaps,
'flatPageLayoutTabMaps' | 'flatObjectMetadataMaps'
@@ -43,6 +49,22 @@ export const buildFlatPageLayoutWidgetCommonProperties = ({
flatEntityMaps: { flatPageLayoutTabMaps, flatObjectMetadataMaps },
});
let position = widgetInput.position;
if (!isDefined(position)) {
const parentTab = findFlatEntityByIdInFlatEntityMaps({
flatEntityId: widgetInput.pageLayoutTabId,
flatEntityMaps: flatPageLayoutTabMaps,
});
if (isDefined(parentTab)) {
position = getDefaultPageLayoutWidgetPosition(
parentTab.layoutMode,
widgetIndexInTab ?? 0,
);
}
}
return {
pageLayoutTabId: widgetInput.pageLayoutTabId,
pageLayoutTabUniversalIdentifier,
@@ -50,7 +72,7 @@ export const buildFlatPageLayoutWidgetCommonProperties = ({
type: widgetInput.type,
objectMetadataId: widgetInput.objectMetadataId ?? null,
objectMetadataUniversalIdentifier,
gridPosition: widgetInput.gridPosition,
position: widgetInput.position ?? null,
gridPosition: widgetInput.gridPosition ?? null,
position: position ?? null,
};
};
@@ -14,6 +14,7 @@ export type FromCreatePageLayoutWidgetInputToFlatPageLayoutWidgetToCreateArgs =
createPageLayoutWidgetInput: CreatePageLayoutWidgetInput;
workspaceId: string;
flatApplication: FlatApplication;
widgetIndexInTab?: number;
} & Pick<
AllFlatEntityMaps,
| 'flatPageLayoutTabMaps'
@@ -28,6 +29,7 @@ export const fromCreatePageLayoutWidgetInputToFlatPageLayoutWidgetToCreate = ({
createPageLayoutWidgetInput: rawCreatePageLayoutWidgetInput,
workspaceId,
flatApplication,
widgetIndexInTab,
flatPageLayoutTabMaps,
flatObjectMetadataMaps,
flatFieldMetadataMaps,
@@ -57,6 +59,7 @@ export const fromCreatePageLayoutWidgetInputToFlatPageLayoutWidgetToCreate = ({
gridPosition: createPageLayoutWidgetInput.gridPosition,
position: createPageLayoutWidgetInput.position,
},
widgetIndexInTab,
flatPageLayoutTabMaps,
flatObjectMetadataMaps,
});
@@ -108,7 +108,6 @@ export const computeFlatDefaultRecordPageLayoutToCreate = ({
pageLayoutTabUniversalIdentifier: tabUniversalIdentifier,
title: widgetProps.title,
type: widgetProps.type,
gridPosition: widgetProps.gridPosition,
position: widgetProps.position,
// @ts-expect-error - configurationType is validated but TS can't match to discriminated union
configuration,
@@ -40,13 +40,14 @@ export class CreatePageLayoutWidgetInput {
objectMetadataId?: string | null;
@Field(() => GridPositionInput, {
nullable: false,
nullable: true,
deprecationReason:
'Use `position` instead. Will be removed in a future release.',
})
@ValidateNested()
@Type(() => GridPositionInput)
gridPosition: GridPositionInput;
@IsOptional()
gridPosition?: GridPositionInput;
@Field(() => GraphQLJSON, { nullable: true })
@IsObject()
@@ -1,10 +1,9 @@
import { Field, InputType } from '@nestjs/graphql';
import { IsInt, IsNotEmpty, Min } from 'class-validator';
import { type GridPosition } from 'twenty-shared/types';
@InputType('GridPositionInput')
export class GridPositionInput implements GridPosition {
export class GridPositionInput {
@Field()
@IsInt()
@Min(0)
@@ -49,13 +49,14 @@ export class UpdatePageLayoutWidgetWithIdInput {
objectMetadataId: string | null;
@Field(() => GridPositionInput, {
nullable: true,
deprecationReason:
'Use `position` instead. Will be removed in a future release.',
})
@ValidateNested()
@Type(() => GridPositionInput)
@IsNotEmpty()
gridPosition: GridPositionInput;
@IsOptional()
gridPosition?: GridPositionInput;
@Field(() => GraphQLJSON, { nullable: true })
@IsObject()
@@ -57,11 +57,11 @@ export class PageLayoutWidgetDTO {
objectMetadataId?: string;
@Field(() => GridPositionDTO, {
nullable: false,
nullable: true,
deprecationReason:
'Use `position` instead. Will be removed in a future release.',
})
gridPosition: GridPositionDTO;
gridPosition?: GridPositionDTO;
@Field(() => PageLayoutWidgetPositionUnion, { nullable: true })
position?: PageLayoutWidgetPosition | null;
@@ -3,7 +3,6 @@ import { ObjectType } from '@nestjs/graphql';
import {
PageLayoutWidgetConditionalDisplay,
PageLayoutWidgetPosition,
type GridPosition,
type SerializedRelation,
} from 'twenty-shared/types';
import {
@@ -23,6 +22,7 @@ import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadat
import { PageLayoutTabEntity } from 'src/engine/metadata-modules/page-layout-tab/entities/page-layout-tab.entity';
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { WidgetType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-type.enum';
import { type GridPosition } from 'src/engine/metadata-modules/page-layout-widget/types/grid-position.type';
import { PageLayoutWidgetConfigurationTypeSettings } from 'src/engine/metadata-modules/page-layout-widget/types/page-layout-widget-configuration.type';
import { OverridableEntity } from 'src/engine/workspace-manager/types/overridable-entity';
import { type JsonbProperty } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/jsonb-property.type';
@@ -89,8 +89,8 @@ export class PageLayoutWidgetEntity<
@Column({ type: 'varchar', nullable: true })
conditionalAvailabilityExpression: string | null;
@Column({ type: 'jsonb', nullable: false })
gridPosition: JsonbProperty<GridPosition>;
@Column({ type: 'jsonb', nullable: true })
gridPosition: JsonbProperty<GridPosition> | null;
@Column({ type: 'jsonb', nullable: true })
position: JsonbProperty<PageLayoutWidgetPosition | null>;
@@ -14,20 +14,24 @@ import {
} from '@nestjs/graphql';
import { PermissionFlagType } from 'twenty-shared/constants';
import { PageLayoutTabLayoutMode } from 'twenty-shared/types';
import { MetadataResolver } from 'src/engine/api/graphql/graphql-config/decorators/metadata-resolver.decorator';
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe';
import { I18nService } from 'src/engine/core-modules/i18n/i18n.service';
import { type I18nContext } from 'src/engine/core-modules/i18n/types/i18n-context.type';
import { type WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
import { MetadataResolver } from 'src/engine/api/graphql/graphql-config/decorators/metadata-resolver.decorator';
import { NoPermissionGuard } from 'src/engine/guards/no-permission.guard';
import { SettingsPermissionGuard } from 'src/engine/guards/settings-permission.guard';
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
import { CreatePageLayoutWidgetInput } from 'src/engine/metadata-modules/page-layout-widget/dtos/inputs/create-page-layout-widget.input';
import { UpdatePageLayoutWidgetInput } from 'src/engine/metadata-modules/page-layout-widget/dtos/inputs/update-page-layout-widget.input';
import { PageLayoutWidgetDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/page-layout-widget.dto';
import {
GridPositionDTO,
PageLayoutWidgetDTO,
} from 'src/engine/metadata-modules/page-layout-widget/dtos/page-layout-widget.dto';
import { WidgetConfiguration } from 'src/engine/metadata-modules/page-layout-widget/dtos/widget-configuration.interface';
import { PageLayoutWidgetService } from 'src/engine/metadata-modules/page-layout-widget/services/page-layout-widget.service';
import { resolvePageLayoutWidgetTitle } from 'src/engine/metadata-modules/page-layout-widget/utils/resolve-page-layout-widget-title.util';
@@ -134,4 +138,18 @@ export class PageLayoutWidgetResolver {
configuration(@Parent() widget: PageLayoutWidgetDTO) {
return widget.configuration;
}
@ResolveField(() => GridPositionDTO)
gridPosition(@Parent() widget: PageLayoutWidgetDTO): GridPositionDTO {
if (widget.position?.layoutMode === PageLayoutTabLayoutMode.GRID) {
return {
row: widget.position.row,
column: widget.position.column,
rowSpan: widget.position.rowSpan,
columnSpan: widget.position.columnSpan,
};
}
return { row: 0, column: 0, rowSpan: 1, columnSpan: 1 };
}
}
@@ -28,6 +28,7 @@ import {
} from 'src/engine/metadata-modules/page-layout-widget/exceptions/page-layout-widget.exception';
import { type AllPageLayoutWidgetConfiguration } from 'src/engine/metadata-modules/page-layout-widget/types/all-page-layout-widget-configuration.type';
import { fromFlatPageLayoutWidgetToPageLayoutWidgetDto } from 'src/engine/metadata-modules/page-layout-widget/utils/from-flat-page-layout-widget-to-page-layout-widget-dto.util';
import { normalizePageLayoutWidgetInputPosition } from 'src/engine/metadata-modules/page-layout-widget/utils/normalize-page-layout-widget-input-position.util';
import { validateChartConfigurationFieldReferencesOrThrow } from 'src/engine/metadata-modules/page-layout-widget/utils/validate-chart-configuration-field-references.util';
import { WorkspaceMigrationBuilderException } from 'src/engine/workspace-manager/workspace-migration/exceptions/workspace-migration-builder-exception';
import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service';
@@ -209,14 +210,16 @@ export class PageLayoutWidgetService {
input: CreatePageLayoutWidgetInput;
workspaceId: string;
}): Promise<PageLayoutWidgetDTO> {
const createInput = isDefined(input.configuration)
const normalizedInput = normalizePageLayoutWidgetInputPosition(input);
const createInput = isDefined(normalizedInput.configuration)
? {
...input,
...normalizedInput,
configuration: await this.enrichRichTextConfigurationBody(
input.configuration,
normalizedInput.configuration,
),
}
: input;
: normalizedInput;
const { workspaceCustomFlatApplication } =
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
@@ -340,20 +343,24 @@ export class PageLayoutWidgetService {
),
]);
const normalizedUpdateData =
normalizePageLayoutWidgetInputPosition(updateData);
const isConfigurationBeingUpdated = Object.prototype.hasOwnProperty.call(
updateData,
normalizedUpdateData,
'configuration',
);
const processedUpdateData =
isConfigurationBeingUpdated && isDefined(updateData.configuration)
isConfigurationBeingUpdated &&
isDefined(normalizedUpdateData.configuration)
? {
...updateData,
...normalizedUpdateData,
configuration: await this.enrichRichTextConfigurationBody(
updateData.configuration,
normalizedUpdateData.configuration,
),
}
: updateData;
: normalizedUpdateData;
const updatePageLayoutWidgetInput: UpdatePageLayoutWidgetInputWithId = {
id,
@@ -0,0 +1,47 @@
import { PageLayoutTabLayoutMode } from 'twenty-shared/types';
import { getDefaultPageLayoutWidgetPosition } from 'src/engine/metadata-modules/page-layout-widget/utils/get-default-page-layout-widget-position.util';
describe('getDefaultPageLayoutWidgetPosition', () => {
it('returns a CANVAS shape for CANVAS layoutMode', () => {
expect(
getDefaultPageLayoutWidgetPosition(PageLayoutTabLayoutMode.CANVAS, 0),
).toEqual({ layoutMode: PageLayoutTabLayoutMode.CANVAS });
});
it('returns a VERTICAL_LIST shape using the widget index', () => {
expect(
getDefaultPageLayoutWidgetPosition(
PageLayoutTabLayoutMode.VERTICAL_LIST,
4,
),
).toEqual({
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
index: 4,
});
});
it('returns a GRID cell at the index column on row 0 below the wrap point', () => {
expect(
getDefaultPageLayoutWidgetPosition(PageLayoutTabLayoutMode.GRID, 5),
).toEqual({
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 5,
rowSpan: 1,
columnSpan: 1,
});
});
it('wraps GRID defaults onto the next row past the column limit', () => {
expect(
getDefaultPageLayoutWidgetPosition(PageLayoutTabLayoutMode.GRID, 13),
).toEqual({
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 1,
column: 1,
rowSpan: 1,
columnSpan: 1,
});
});
});
@@ -0,0 +1,50 @@
import { PageLayoutTabLayoutMode } from 'twenty-shared/types';
import { normalizePageLayoutWidgetInputPosition } from 'src/engine/metadata-modules/page-layout-widget/utils/normalize-page-layout-widget-input-position.util';
describe('normalizePageLayoutWidgetInputPosition', () => {
it('passes through when only position is provided', () => {
const input = {
position: {
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 2,
column: 3,
rowSpan: 1,
columnSpan: 1,
},
} as const;
expect(normalizePageLayoutWidgetInputPosition(input)).toEqual(input);
});
it('derives position from gridPosition when position is missing', () => {
const result = normalizePageLayoutWidgetInputPosition({
gridPosition: { row: 4, column: 5, rowSpan: 2, columnSpan: 3 },
});
expect(result.position).toEqual({
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 4,
column: 5,
rowSpan: 2,
columnSpan: 3,
});
});
it('keeps position when both are provided (position wins)', () => {
const result = normalizePageLayoutWidgetInputPosition({
position: { layoutMode: PageLayoutTabLayoutMode.CANVAS },
gridPosition: { row: 1, column: 1, rowSpan: 1, columnSpan: 1 },
});
expect(result.position).toEqual({
layoutMode: PageLayoutTabLayoutMode.CANVAS,
});
});
it('returns input unchanged when neither field is provided', () => {
const input: { position?: undefined; gridPosition?: undefined } = {};
expect(normalizePageLayoutWidgetInputPosition(input)).toEqual(input);
});
});
@@ -1,224 +0,0 @@
import { WIDGET_GRID_MAX_COLUMNS } from 'src/engine/metadata-modules/page-layout-widget/constants/widget-grid-max-columns.constant';
import { WIDGET_GRID_MAX_ROWS } from 'src/engine/metadata-modules/page-layout-widget/constants/widget-grid-max-rows.constant';
import { PageLayoutWidgetExceptionCode } from 'src/engine/metadata-modules/page-layout-widget/exceptions/page-layout-widget.exception';
import { validateWidgetGridPosition } from 'src/engine/metadata-modules/page-layout-widget/utils/validate-widget-grid-position.util';
describe('validateWidgetGridPosition', () => {
const validGridPosition = {
row: 0,
column: 0,
rowSpan: 2,
columnSpan: 3,
};
describe('Valid grid positions', () => {
it('should return empty array for valid grid position', () => {
const errors = validateWidgetGridPosition(
validGridPosition,
'Test Widget',
);
expect(errors).toEqual([]);
});
it('should return empty array for widget at max column boundary', () => {
const errors = validateWidgetGridPosition(
{
row: 0,
column: WIDGET_GRID_MAX_COLUMNS - 1,
rowSpan: 1,
columnSpan: 1,
},
'Test Widget',
);
expect(errors).toEqual([]);
});
it('should return empty array for widget at max row boundary', () => {
const errors = validateWidgetGridPosition(
{
row: WIDGET_GRID_MAX_ROWS - 1,
column: 0,
rowSpan: 1,
columnSpan: 1,
},
'Test Widget',
);
expect(errors).toEqual([]);
});
it('should return empty array for widget spanning to column grid edge', () => {
const errors = validateWidgetGridPosition(
{
row: 0,
column: 8,
rowSpan: 1,
columnSpan: 4,
},
'Test Widget',
);
expect(errors).toEqual([]);
});
it('should return empty array for widget spanning to row grid edge', () => {
const errors = validateWidgetGridPosition(
{
row: WIDGET_GRID_MAX_ROWS - 5,
column: 0,
rowSpan: 5,
columnSpan: 6,
},
'Test Widget',
);
expect(errors).toEqual([]);
});
});
describe('Invalid row positions', () => {
it('should return error for row exceeding max rows', () => {
const errors = validateWidgetGridPosition(
{ ...validGridPosition, row: WIDGET_GRID_MAX_ROWS },
'Test Widget',
);
expect(errors.length).toBeGreaterThan(0);
expect(errors[0].code).toBe(
PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
);
});
it('should return error when widget extends beyond grid height', () => {
const errors = validateWidgetGridPosition(
{
row: WIDGET_GRID_MAX_ROWS - 2,
column: 0,
rowSpan: 5,
columnSpan: 6,
},
'Test Widget',
);
expect(errors.length).toBeGreaterThan(0);
expect(
errors.some((error) =>
error.message.includes('extends beyond grid height'),
),
).toBe(true);
});
});
describe('Invalid column positions', () => {
it('should return error for column exceeding max columns', () => {
const errors = validateWidgetGridPosition(
{ ...validGridPosition, column: WIDGET_GRID_MAX_COLUMNS },
'Test Widget',
);
expect(errors.length).toBeGreaterThan(0);
expect(errors[0].code).toBe(
PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
);
});
});
describe('Widget extending beyond grid', () => {
it('should return error when widget extends beyond grid width', () => {
const errors = validateWidgetGridPosition(
{
row: 0,
column: 10,
rowSpan: 1,
columnSpan: 3,
},
'Test Widget',
);
expect(errors.length).toBeGreaterThan(0);
expect(
errors.some((error) =>
error.message.includes('extends beyond grid width'),
),
).toBe(true);
});
});
describe('Error messages', () => {
it('should include max columns value in error', () => {
const errors = validateWidgetGridPosition(
{
row: 0,
column: 10,
rowSpan: 1,
columnSpan: 5,
},
'Test Widget',
);
expect(errors.length).toBeGreaterThan(0);
expect(
errors.some((error) =>
error.message.includes(WIDGET_GRID_MAX_COLUMNS.toString()),
),
).toBe(true);
});
it('should include max rows value in error for row start', () => {
const errors = validateWidgetGridPosition(
{
row: WIDGET_GRID_MAX_ROWS + 10,
column: 0,
rowSpan: 1,
columnSpan: 1,
},
'Test Widget',
);
expect(errors.length).toBeGreaterThan(0);
expect(
errors.some((error) =>
error.message.includes(WIDGET_GRID_MAX_ROWS.toString()),
),
).toBe(true);
});
it('should include max rows value in error for row extension', () => {
const errors = validateWidgetGridPosition(
{
row: 95,
column: 0,
rowSpan: 10,
columnSpan: 6,
},
'Test Widget',
);
expect(errors.length).toBeGreaterThan(0);
expect(
errors.some((error) =>
error.message.includes(WIDGET_GRID_MAX_ROWS.toString()),
),
).toBe(true);
});
it('should include widget title in error message', () => {
const errors = validateWidgetGridPosition(
{
row: WIDGET_GRID_MAX_ROWS,
column: 0,
rowSpan: 1,
columnSpan: 1,
},
'My Custom Widget',
);
expect(errors.length).toBeGreaterThan(0);
expect(
errors.some((error) => error.message.includes('My Custom Widget')),
).toBe(true);
});
});
});
@@ -10,6 +10,7 @@ export const fromFlatPageLayoutWidgetToPageLayoutWidgetDto = (
deletedAt,
objectMetadataId,
overrides,
gridPosition: _gridPosition,
...rest
} = flatPageLayoutWidget;
@@ -0,0 +1,29 @@
import {
PageLayoutTabLayoutMode,
type PageLayoutWidgetPosition,
} from 'twenty-shared/types';
import { WIDGET_GRID_MAX_COLUMNS } from 'src/engine/metadata-modules/page-layout-widget/constants/widget-grid-max-columns.constant';
export const getDefaultPageLayoutWidgetPosition = (
layoutMode: PageLayoutTabLayoutMode,
widgetIndexInTab: number,
): PageLayoutWidgetPosition => {
switch (layoutMode) {
case PageLayoutTabLayoutMode.CANVAS:
return { layoutMode: PageLayoutTabLayoutMode.CANVAS };
case PageLayoutTabLayoutMode.VERTICAL_LIST:
return {
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
index: widgetIndexInTab,
};
case PageLayoutTabLayoutMode.GRID:
return {
layoutMode: PageLayoutTabLayoutMode.GRID,
row: Math.floor(widgetIndexInTab / WIDGET_GRID_MAX_COLUMNS),
column: widgetIndexInTab % WIDGET_GRID_MAX_COLUMNS,
rowSpan: 1,
columnSpan: 1,
};
}
};
@@ -0,0 +1,35 @@
import {
PageLayoutTabLayoutMode,
type PageLayoutWidgetPosition,
} from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { type GridPositionInput } from 'src/engine/metadata-modules/page-layout-widget/dtos/inputs/grid-position.input';
export const normalizePageLayoutWidgetInputPosition = <
TInput extends {
position?: PageLayoutWidgetPosition | null;
gridPosition?: GridPositionInput | null;
},
>(
input: TInput,
): TInput & { position?: PageLayoutWidgetPosition | null } => {
if (isDefined(input.position)) {
return input;
}
if (!isDefined(input.gridPosition)) {
return input;
}
return {
...input,
position: {
layoutMode: PageLayoutTabLayoutMode.GRID,
row: input.gridPosition.row,
column: input.gridPosition.column,
rowSpan: input.gridPosition.rowSpan,
columnSpan: input.gridPosition.columnSpan,
},
};
};
@@ -18,6 +18,58 @@ export const validatePageLayoutWidgetGridPosition = (
const { row, column, rowSpan, columnSpan } = position;
if (row < 0) {
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: generatePageLayoutWidgetExceptionMessage(
PageLayoutWidgetExceptionMessageKey.INVALID_WIDGET_POSITION,
widgetTitle,
undefined,
`row ${row} must be non-negative`,
),
userFriendlyMessage: msg`Widget row must be non-negative`,
});
}
if (column < 0) {
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: generatePageLayoutWidgetExceptionMessage(
PageLayoutWidgetExceptionMessageKey.INVALID_WIDGET_POSITION,
widgetTitle,
undefined,
`column ${column} must be non-negative`,
),
userFriendlyMessage: msg`Widget column must be non-negative`,
});
}
if (rowSpan < 1) {
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: generatePageLayoutWidgetExceptionMessage(
PageLayoutWidgetExceptionMessageKey.INVALID_WIDGET_POSITION,
widgetTitle,
undefined,
`rowSpan ${rowSpan} must be at least 1`,
),
userFriendlyMessage: msg`Widget rowSpan must be at least 1`,
});
}
if (columnSpan < 1) {
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: generatePageLayoutWidgetExceptionMessage(
PageLayoutWidgetExceptionMessageKey.INVALID_WIDGET_POSITION,
widgetTitle,
undefined,
`columnSpan ${columnSpan} must be at least 1`,
),
userFriendlyMessage: msg`Widget columnSpan must be at least 1`,
});
}
if (column >= WIDGET_GRID_MAX_COLUMNS) {
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
@@ -1,75 +0,0 @@
import { msg } from '@lingui/core/macro';
import { type GridPosition } from 'twenty-shared/types';
import { WIDGET_GRID_MAX_COLUMNS } from 'src/engine/metadata-modules/page-layout-widget/constants/widget-grid-max-columns.constant';
import { WIDGET_GRID_MAX_ROWS } from 'src/engine/metadata-modules/page-layout-widget/constants/widget-grid-max-rows.constant';
import {
PageLayoutWidgetExceptionCode,
PageLayoutWidgetExceptionMessageKey,
generatePageLayoutWidgetExceptionMessage,
} from 'src/engine/metadata-modules/page-layout-widget/exceptions/page-layout-widget.exception';
import { type FlatEntityValidationError } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/types/failed-flat-entity-validation.type';
// TODO: remove in favor of validatePageLayoutWidgetGridPosition once gridPosition is deprecated
export const validateWidgetGridPosition = (
gridPosition: GridPosition,
widgetTitle: string,
): FlatEntityValidationError[] => {
const errors: FlatEntityValidationError[] = [];
const { row, column, rowSpan, columnSpan } = gridPosition;
if (column >= WIDGET_GRID_MAX_COLUMNS) {
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: generatePageLayoutWidgetExceptionMessage(
PageLayoutWidgetExceptionMessageKey.INVALID_WIDGET_POSITION,
widgetTitle,
undefined,
`column ${column} exceeds grid width (max column is ${WIDGET_GRID_MAX_COLUMNS - 1})`,
),
userFriendlyMessage: msg`Widget extends beyond grid width`,
});
}
if (column + columnSpan > WIDGET_GRID_MAX_COLUMNS) {
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: generatePageLayoutWidgetExceptionMessage(
PageLayoutWidgetExceptionMessageKey.INVALID_WIDGET_POSITION,
widgetTitle,
undefined,
`widget extends beyond grid width (column ${column} + columnSpan ${columnSpan} > ${WIDGET_GRID_MAX_COLUMNS})`,
),
userFriendlyMessage: msg`Widget extends beyond grid width`,
});
}
if (row >= WIDGET_GRID_MAX_ROWS) {
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: generatePageLayoutWidgetExceptionMessage(
PageLayoutWidgetExceptionMessageKey.INVALID_WIDGET_POSITION,
widgetTitle,
undefined,
`row ${row} exceeds maximum allowed rows (${WIDGET_GRID_MAX_ROWS})`,
),
userFriendlyMessage: msg`Widget row exceeds grid height`,
});
}
if (row + rowSpan > WIDGET_GRID_MAX_ROWS) {
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: generatePageLayoutWidgetExceptionMessage(
PageLayoutWidgetExceptionMessageKey.INVALID_WIDGET_POSITION,
widgetTitle,
undefined,
`widget extends beyond grid height (row ${row} + rowSpan ${rowSpan} > ${WIDGET_GRID_MAX_ROWS})`,
),
userFriendlyMessage: msg`Widget extends beyond grid height`,
});
}
return errors;
};
@@ -324,7 +324,7 @@ export class PageLayoutDuplicationService {
fromCreatePageLayoutWidgetInputToFlatPageLayoutWidgetToCreate({
createPageLayoutWidgetInput: {
title: originalWidget.title,
gridPosition: originalWidget.gridPosition,
gridPosition: originalWidget.gridPosition ?? undefined,
position: originalWidget.position ?? undefined,
type: originalWidget.type,
objectMetadataId: originalWidget.objectMetadataId,
@@ -25,6 +25,7 @@ import { reconstructFlatPageLayoutWithTabsAndWidgets } from 'src/engine/metadata
import { UpdatePageLayoutTabWithWidgetsInput } from 'src/engine/metadata-modules/page-layout-tab/dtos/inputs/update-page-layout-tab-with-widgets.input';
import { UpdatePageLayoutWidgetWithIdInput } from 'src/engine/metadata-modules/page-layout-widget/dtos/inputs/update-page-layout-widget-with-id.input';
import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type';
import { normalizePageLayoutWidgetInputPosition } from 'src/engine/metadata-modules/page-layout-widget/utils/normalize-page-layout-widget-input-position.util';
import { validateChartConfigurationFieldReferencesOrThrow } from 'src/engine/metadata-modules/page-layout-widget/utils/validate-chart-configuration-field-references.util';
import { UpdatePageLayoutWithTabsInput } from 'src/engine/metadata-modules/page-layout/dtos/inputs/update-page-layout-with-tabs.input';
import { PageLayoutDTO } from 'src/engine/metadata-modules/page-layout/dtos/page-layout.dto';
@@ -476,10 +477,17 @@ export class PageLayoutUpdateService {
);
for (const tabInput of tabs) {
const normalizedWidgets = tabInput.widgets.map(
(widget) =>
normalizePageLayoutWidgetInputPosition(
widget,
) as UpdatePageLayoutWidgetWithIdInput,
);
const { widgetsToCreate, widgetsToUpdate, widgetsToDelete } =
this.computeWidgetOperationsForTab({
tabId: tabInput.id,
widgets: tabInput.widgets,
widgets: normalizedWidgets,
widgetIdsAcrossAllTabs,
flatPageLayoutWidgetMaps,
flatPageLayoutTabMaps,
@@ -580,13 +588,14 @@ export class PageLayoutUpdateService {
const now = new Date();
const widgetsToCreate: FlatPageLayoutWidget[] = entitiesToCreate.map(
(widgetInput) => {
(widgetInput, widgetIndexInTab) => {
const widgetId = widgetInput.id ?? v4();
return {
id: widgetId,
...buildFlatPageLayoutWidgetCommonProperties({
widgetInput,
widgetIndexInTab,
flatPageLayoutTabMaps,
flatObjectMetadataMaps,
}),
@@ -729,12 +738,18 @@ export class PageLayoutUpdateService {
title: widgetInput.title,
type: widgetInput.type,
objectMetadataId: widgetInput.objectMetadataId ?? null,
gridPosition: widgetInput.gridPosition,
position: widgetInput.position ?? null,
configuration,
pageLayoutTabId: widgetInput.pageLayoutTabId,
};
if (isDefined(widgetInput.position)) {
editableProperties.position = widgetInput.position;
}
if (isDefined(widgetInput.gridPosition)) {
editableProperties.gridPosition = widgetInput.gridPosition;
}
if (widgetInput.conditionalDisplay !== undefined) {
editableProperties.conditionalDisplay =
widgetInput.conditionalDisplay ?? null;
@@ -210,7 +210,6 @@ describe('resolveOverridableEntityEventBatchOverrides', () => {
title: 'Original',
type: 'FIELDS',
position: '{}',
gridPosition: '{}',
configuration: '{}',
conditionalDisplay: null,
pageLayoutTabId: 'plt-1',
@@ -6,7 +6,6 @@ export type SeederFlatPageLayoutWidget = Pick<
| 'pageLayoutTabId'
| 'title'
| 'type'
| 'gridPosition'
| 'position'
| 'configuration'
| 'objectMetadataId'

Some files were not shown because too many files have changed in this diff Show More