Implement page layout override (#18472)

This commit is contained in:
Weiko
2026-03-09 18:21:08 +01:00
committed by GitHub
parent 309fd7a526
commit c433b2b73f
90 changed files with 1270 additions and 57 deletions
File diff suppressed because one or more lines are too long
@@ -10,6 +10,7 @@ export const PAGE_LAYOUT_TAB_FRAGMENT = gql`
icon
position
layoutMode
isOverridden
widgets {
...PageLayoutWidgetFragment
}
@@ -71,6 +71,7 @@ const mixedGraphsPageLayoutMocks = {
title: 'Mixed Graphs',
position: 0,
pageLayoutId: 'mixed-graphs-layout',
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -95,6 +96,7 @@ const mixedGraphsPageLayoutMocks = {
aggregateOperation: AggregateOperations.COUNT,
aggregateFieldMetadataId: idField.id,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -120,6 +122,7 @@ const mixedGraphsPageLayoutMocks = {
aggregateFieldMetadataId: idField.id,
displayDataLabel: false,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -146,6 +149,7 @@ const mixedGraphsPageLayoutMocks = {
groupByFieldMetadataId: createdAtField.id,
orderBy: GraphOrderBy.VALUE_DESC,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -175,6 +179,7 @@ const mixedGraphsPageLayoutMocks = {
axisNameDisplay: AxisNameDisplay.BOTH,
displayDataLabel: false,
} satisfies BarChartConfiguration,
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -29,6 +29,7 @@ const createInitialTabs = (): PageLayoutTab[] => [
icon: 'IconPlus',
pageLayoutId: 'test-layout',
widgets: [],
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -41,6 +42,7 @@ const createInitialTabs = (): PageLayoutTab[] => [
position: 1,
pageLayoutId: 'test-layout',
widgets: [],
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -53,6 +55,7 @@ const createInitialTabs = (): PageLayoutTab[] => [
position: 2,
pageLayoutId: 'test-layout',
widgets: [],
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -82,6 +85,7 @@ const PageLayoutTabListPlayground = ({
position: nextIndex,
pageLayoutId: 'test-layout',
widgets: [],
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -26,6 +26,7 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = {
position: 100,
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
pageLayoutId: DEFAULT_COMPANY_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -49,6 +50,7 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -64,6 +66,7 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = {
position: 200,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
pageLayoutId: DEFAULT_COMPANY_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -87,6 +90,7 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -102,6 +106,7 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = {
position: 300,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
pageLayoutId: DEFAULT_COMPANY_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -125,6 +130,7 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -140,6 +146,7 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = {
position: 400,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
pageLayoutId: DEFAULT_COMPANY_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -163,6 +170,7 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -178,6 +186,7 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = {
position: 500,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
pageLayoutId: DEFAULT_COMPANY_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -201,6 +210,7 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -216,6 +226,7 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = {
position: 600,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
pageLayoutId: DEFAULT_COMPANY_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -239,6 +250,7 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -254,6 +266,7 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = {
position: 700,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
pageLayoutId: DEFAULT_COMPANY_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -277,6 +290,7 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -26,6 +26,7 @@ export const DEFAULT_NOTE_RECORD_PAGE_LAYOUT: PageLayout = {
position: 100,
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
pageLayoutId: DEFAULT_NOTE_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -52,6 +53,7 @@ export const DEFAULT_NOTE_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -75,6 +77,7 @@ export const DEFAULT_NOTE_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -97,6 +100,7 @@ export const DEFAULT_NOTE_RECORD_PAGE_LAYOUT: PageLayout = {
position: 150,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
pageLayoutId: DEFAULT_NOTE_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -120,6 +124,7 @@ export const DEFAULT_NOTE_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -142,6 +147,7 @@ export const DEFAULT_NOTE_RECORD_PAGE_LAYOUT: PageLayout = {
position: 200,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
pageLayoutId: DEFAULT_NOTE_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -165,6 +171,7 @@ export const DEFAULT_NOTE_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -180,6 +187,7 @@ export const DEFAULT_NOTE_RECORD_PAGE_LAYOUT: PageLayout = {
position: 300,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
pageLayoutId: DEFAULT_NOTE_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -203,6 +211,7 @@ export const DEFAULT_NOTE_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -26,6 +26,7 @@ export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = {
position: 100,
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
pageLayoutId: DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -49,6 +50,7 @@ export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -64,6 +66,7 @@ export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = {
position: 200,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
pageLayoutId: DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -87,6 +90,7 @@ export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -102,6 +106,7 @@ export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = {
position: 300,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
pageLayoutId: DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -125,6 +130,7 @@ export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -140,6 +146,7 @@ export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = {
position: 400,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
pageLayoutId: DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -163,6 +170,7 @@ export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -178,6 +186,7 @@ export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = {
position: 500,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
pageLayoutId: DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -201,6 +210,7 @@ export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -216,6 +226,7 @@ export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = {
position: 600,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
pageLayoutId: DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -239,6 +250,7 @@ export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -254,6 +266,7 @@ export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = {
position: 700,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
pageLayoutId: DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -277,6 +290,7 @@ export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -26,6 +26,7 @@ export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT: PageLayout = {
position: 100,
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
pageLayoutId: DEFAULT_PERSON_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -49,6 +50,7 @@ export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -64,6 +66,7 @@ export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT: PageLayout = {
position: 200,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
pageLayoutId: DEFAULT_PERSON_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -87,6 +90,7 @@ export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -102,6 +106,7 @@ export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT: PageLayout = {
position: 300,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
pageLayoutId: DEFAULT_PERSON_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -125,6 +130,7 @@ export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -140,6 +146,7 @@ export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT: PageLayout = {
position: 400,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
pageLayoutId: DEFAULT_PERSON_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -163,6 +170,7 @@ export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -178,6 +186,7 @@ export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT: PageLayout = {
position: 500,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
pageLayoutId: DEFAULT_PERSON_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -201,6 +210,7 @@ export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -216,6 +226,7 @@ export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT: PageLayout = {
position: 600,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
pageLayoutId: DEFAULT_PERSON_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -239,6 +250,7 @@ export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -254,6 +266,7 @@ export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT: PageLayout = {
position: 700,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
pageLayoutId: DEFAULT_PERSON_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -277,6 +290,7 @@ export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -26,6 +26,7 @@ export const DEFAULT_RECORD_PAGE_LAYOUT: PageLayout = {
position: 100,
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
pageLayoutId: DEFAULT_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -49,6 +50,7 @@ export const DEFAULT_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -64,6 +66,7 @@ export const DEFAULT_RECORD_PAGE_LAYOUT: PageLayout = {
position: 200,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
pageLayoutId: DEFAULT_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -87,6 +90,7 @@ export const DEFAULT_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -102,6 +106,7 @@ export const DEFAULT_RECORD_PAGE_LAYOUT: PageLayout = {
position: 300,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
pageLayoutId: DEFAULT_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -125,6 +130,7 @@ export const DEFAULT_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -140,6 +146,7 @@ export const DEFAULT_RECORD_PAGE_LAYOUT: PageLayout = {
position: 400,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
pageLayoutId: DEFAULT_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -163,6 +170,7 @@ export const DEFAULT_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -178,6 +186,7 @@ export const DEFAULT_RECORD_PAGE_LAYOUT: PageLayout = {
position: 500,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
pageLayoutId: DEFAULT_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -201,6 +210,7 @@ export const DEFAULT_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -26,6 +26,7 @@ export const DEFAULT_TASK_RECORD_PAGE_LAYOUT: PageLayout = {
position: 100,
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
pageLayoutId: DEFAULT_TASK_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -49,6 +50,7 @@ export const DEFAULT_TASK_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -72,6 +74,7 @@ export const DEFAULT_TASK_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -94,6 +97,7 @@ export const DEFAULT_TASK_RECORD_PAGE_LAYOUT: PageLayout = {
position: 150,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
pageLayoutId: DEFAULT_TASK_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -117,6 +121,7 @@ export const DEFAULT_TASK_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -139,6 +144,7 @@ export const DEFAULT_TASK_RECORD_PAGE_LAYOUT: PageLayout = {
position: 200,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
pageLayoutId: DEFAULT_TASK_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -162,6 +168,7 @@ export const DEFAULT_TASK_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -177,6 +184,7 @@ export const DEFAULT_TASK_RECORD_PAGE_LAYOUT: PageLayout = {
position: 300,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
pageLayoutId: DEFAULT_TASK_RECORD_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -200,6 +208,7 @@ export const DEFAULT_TASK_RECORD_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -31,6 +31,7 @@ export const DEFAULT_WORKFLOW_PAGE_LAYOUT: PageLayout = {
layoutMode: PageLayoutTabLayoutMode.CANVAS,
icon: 'IconSettings',
pageLayoutId: DEFAULT_WORKFLOW_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -54,6 +55,7 @@ export const DEFAULT_WORKFLOW_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -32,6 +32,7 @@ export const DEFAULT_WORKFLOW_RUN_PAGE_LAYOUT: PageLayout = {
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
icon: 'IconHome',
pageLayoutId: DEFAULT_WORKFLOW_RUN_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -55,6 +56,7 @@ export const DEFAULT_WORKFLOW_RUN_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -71,6 +73,7 @@ export const DEFAULT_WORKFLOW_RUN_PAGE_LAYOUT: PageLayout = {
layoutMode: PageLayoutTabLayoutMode.CANVAS,
icon: 'IconSettings',
pageLayoutId: DEFAULT_WORKFLOW_RUN_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -94,6 +97,7 @@ export const DEFAULT_WORKFLOW_RUN_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -32,6 +32,7 @@ export const DEFAULT_WORKFLOW_VERSION_PAGE_LAYOUT: PageLayout = {
position: 100,
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
pageLayoutId: DEFAULT_WORKFLOW_VERSION_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -55,6 +56,7 @@ export const DEFAULT_WORKFLOW_VERSION_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -71,6 +73,7 @@ export const DEFAULT_WORKFLOW_VERSION_PAGE_LAYOUT: PageLayout = {
position: 200,
layoutMode: PageLayoutTabLayoutMode.CANVAS,
pageLayoutId: DEFAULT_WORKFLOW_VERSION_PAGE_LAYOUT_ID,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -94,6 +97,7 @@ export const DEFAULT_WORKFLOW_VERSION_PAGE_LAYOUT: PageLayout = {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -6,6 +6,7 @@ export const PAGE_LAYOUT_WIDGET_FRAGMENT = gql`
title
type
objectMetadataId
isOverridden
createdAt
updatedAt
deletedAt
@@ -78,6 +78,7 @@ describe('useCreatePageLayoutGraphWidget', () => {
position: 0,
pageLayoutId: '',
widgets: [],
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -158,6 +159,7 @@ describe('useCreatePageLayoutGraphWidget', () => {
position: 0,
pageLayoutId: '',
widgets: [],
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -233,6 +233,7 @@ describe('useCreatePageLayoutTab', () => {
position: 0,
pageLayoutId: 'test-layout',
widgets: [],
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -92,6 +92,7 @@ describe('usePageLayoutDraftState', () => {
title: 'Tab 1',
position: 0,
pageLayoutId: '',
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -112,6 +113,7 @@ describe('usePageLayoutDraftState', () => {
displayDataLabel: false,
},
objectMetadataId: null,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -39,6 +39,7 @@ describe('usePageLayoutWithRelationWidgets', () => {
position: 100,
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
pageLayoutId: 'test-layout',
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -62,6 +63,7 @@ describe('usePageLayoutWithRelationWidgets', () => {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -84,6 +86,7 @@ describe('usePageLayoutWithRelationWidgets', () => {
__typename: 'NotesConfiguration',
configurationType: WidgetConfigurationType.NOTES,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -112,6 +115,7 @@ describe('usePageLayoutWithRelationWidgets', () => {
primaryAxisOrderBy: GraphOrderBy.FIELD_ASC,
displayDataLabel: false,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -236,6 +240,7 @@ describe('usePageLayoutWithRelationWidgets', () => {
primaryAxisOrderBy: GraphOrderBy.FIELD_ASC,
displayDataLabel: false,
},
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -53,6 +53,7 @@ export const useCreatePageLayoutTab = (pageLayoutIdFromProps?: string) => {
position: maxPosition + 1,
pageLayoutId: pageLayoutId,
widgets: [],
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -22,6 +22,7 @@ describe('addWidgetToTab', () => {
},
gridPosition: { row: 0, column: 0, rowSpan: 2, columnSpan: 2 },
objectMetadataId: null,
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -35,6 +36,7 @@ describe('addWidgetToTab', () => {
position: 0,
pageLayoutId: 'layout-1',
widgets: [],
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -46,6 +48,7 @@ describe('addWidgetToTab', () => {
position: 1,
pageLayoutId: 'layout-1',
widgets: [],
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -28,6 +28,7 @@ describe('convertLayoutsToWidgets', () => {
aggregateFieldMetadataId: 'id',
displayDataLabel: false,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -52,6 +53,7 @@ describe('convertLayoutsToWidgets', () => {
orderBy: GraphOrderBy.VALUE_DESC,
displayDataLabel: false,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -38,6 +38,7 @@ describe('convertPageLayoutToTabLayouts', () => {
},
gridPosition: { row: 0, column: 0, rowSpan: 2, columnSpan: 2 },
objectMetadataId: 'object-metadata-1',
isOverridden: false,
createdAt: '2025-01-01T00:00:00.000Z',
updatedAt: '2025-01-01T00:00:00.000Z',
deletedAt: null,
@@ -58,11 +59,13 @@ describe('convertPageLayoutToTabLayouts', () => {
},
gridPosition: { row: 2, column: 0, rowSpan: 2, columnSpan: 2 },
objectMetadataId: 'object-metadata-1',
isOverridden: false,
createdAt: '2025-01-01T00:00:00.000Z',
updatedAt: '2025-01-01T00:00:00.000Z',
deletedAt: null,
},
],
isOverridden: false,
createdAt: '2025-01-01T00:00:00.000Z',
updatedAt: '2025-01-01T00:00:00.000Z',
deletedAt: null,
@@ -115,11 +118,13 @@ describe('convertPageLayoutToTabLayouts', () => {
},
gridPosition: { row: 0, column: 0, rowSpan: 4, columnSpan: 4 },
objectMetadataId: null,
isOverridden: false,
createdAt: '2025-01-01T00:00:00.000Z',
updatedAt: '2025-01-01T00:00:00.000Z',
deletedAt: null,
},
],
isOverridden: false,
createdAt: '2025-01-01T00:00:00.000Z',
updatedAt: '2025-01-01T00:00:00.000Z',
deletedAt: null,
@@ -28,6 +28,7 @@ const createMockWidget = (
objectMetadataId: null,
gridPosition: { row: 0, column: 0, rowSpan: 1, columnSpan: 1 },
pageLayoutTabId: 'tab-1',
isOverridden: false,
createdAt: '2024-01-01',
updatedAt: '2024-01-01',
...overrides,
@@ -28,6 +28,7 @@ describe('filterVisibleWidgets', () => {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00.000Z',
updatedAt: '2024-01-01T00:00:00.000Z',
deletedAt: null,
@@ -9,6 +9,7 @@ describe('getPageLayoutTabListInitialActiveTabId', () => {
title: `Tab ${id}`,
position: 0,
widgets: [],
isOverridden: false,
createdAt: '2024-01-01T00:00:00.000Z',
updatedAt: '2024-01-01T00:00:00.000Z',
});
@@ -14,6 +14,7 @@ describe('getTabsByDisplayMode', () => {
title: `Tab ${id}`,
position: 0,
widgets: [],
isOverridden: false,
createdAt: '2024-01-01T00:00:00.000Z',
updatedAt: '2024-01-01T00:00:00.000Z',
});
@@ -28,6 +28,7 @@ describe('getTabsWithVisibleWidgets', () => {
configurationType: WidgetConfigurationType.FIELDS,
viewId: null,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00.000Z',
updatedAt: '2024-01-01T00:00:00.000Z',
deletedAt: null,
@@ -45,6 +46,7 @@ describe('getTabsWithVisibleWidgets', () => {
title: `Tab ${id}`,
position: 0,
widgets,
isOverridden: false,
createdAt: '2024-01-01T00:00:00.000Z',
updatedAt: '2024-01-01T00:00:00.000Z',
deletedAt: null,
@@ -27,6 +27,7 @@ describe('prepareGridLayoutItemsWithPlaceholders', () => {
aggregateFieldMetadataId: 'field-id',
displayDataLabel: false,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -30,6 +30,7 @@ describe('removeWidgetFromTab', () => {
},
gridPosition: { row: 0, column: 0, rowSpan: 2, columnSpan: 2 },
objectMetadataId: null,
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -50,11 +51,13 @@ describe('removeWidgetFromTab', () => {
},
gridPosition: { row: 2, column: 0, rowSpan: 2, columnSpan: 2 },
objectMetadataId: null,
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
},
],
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -78,11 +81,13 @@ describe('removeWidgetFromTab', () => {
},
gridPosition: { row: 0, column: 0, rowSpan: 2, columnSpan: 2 },
objectMetadataId: null,
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
},
],
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -34,6 +34,7 @@ export const createDefaultFrontComponentWidget = (
columnSpan: gridPosition.columnSpan,
},
objectMetadataId: null,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -84,6 +84,7 @@ export const createDefaultGraphWidget = ({
columnSpan: gridPosition.columnSpan,
},
objectMetadataId: resolvedObjectMetadataId,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -34,6 +34,7 @@ export const createDefaultIframeWidget = (
columnSpan: gridPosition.columnSpan,
},
objectMetadataId: objectMetadataId ?? null,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -34,6 +34,7 @@ export const createDefaultStandaloneRichTextWidget = (
columnSpan: gridPosition.columnSpan,
},
objectMetadataId: objectMetadataId ?? null,
isOverridden: false,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
deletedAt: null,
@@ -40,6 +40,7 @@ const getRelationFieldWidgetToInsert = (
fieldMetadataId: field.id,
layout: 'CARD' as const,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00.000Z',
updatedAt: '2024-01-01T00:00:00.000Z',
deletedAt: null,
@@ -151,6 +151,7 @@ const createPageLayoutWithWidget = (
position: 0,
pageLayoutId: PAGE_LAYOUT_TEST_INSTANCE_ID,
widgets: [widget],
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -273,6 +274,7 @@ export const WithNumberChart: Story = {
aggregateFieldMetadataId: idField.id,
displayDataLabel: true,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -359,6 +361,7 @@ export const WithGaugeChart: Story = {
aggregateFieldMetadataId: idField.id,
displayDataLabel: false,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -449,6 +452,7 @@ export const WithBarChart: Story = {
axisNameDisplay: AxisNameDisplay.BOTH,
displayDataLabel: false,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -542,6 +546,7 @@ export const SmallWidget: Story = {
aggregateFieldMetadataId: idField.id,
displayDataLabel: true,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -639,6 +644,7 @@ export const MediumWidget: Story = {
axisNameDisplay: AxisNameDisplay.BOTH,
displayDataLabel: false,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -736,6 +742,7 @@ export const LargeWidget: Story = {
axisNameDisplay: AxisNameDisplay.BOTH,
displayDataLabel: false,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -829,6 +836,7 @@ export const WideWidget: Story = {
aggregateFieldMetadataId: idField.id,
displayDataLabel: true,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -926,6 +934,7 @@ export const TallWidget: Story = {
axisNameDisplay: AxisNameDisplay.BOTH,
displayDataLabel: false,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -1019,6 +1028,7 @@ export const WithManyToOneRelationFieldWidget: Story = {
fieldMetadataId: accountOwnerField.id,
layout: 'FIELD',
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -1137,6 +1147,7 @@ export const WithOneToManyRelationFieldWidget: Story = {
fieldMetadataId: companyPeopleField.id,
layout: 'FIELD',
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -1247,6 +1258,7 @@ export const OneToManyRelationFieldWidgetWithSeeAllButton: Story = {
fieldMetadataId: companyPeopleField.id,
layout: 'FIELD',
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -1384,6 +1396,7 @@ export const OnMobile: Story = {
aggregateFieldMetadataId: idField.id,
displayDataLabel: true,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -1481,6 +1494,7 @@ export const InSidePanel: Story = {
aggregateFieldMetadataId: idField.id,
displayDataLabel: true,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -1639,6 +1653,7 @@ export const Catalog: CatalogStory<Story, typeof WidgetRenderer> = {
aggregateFieldMetadataId: idField.id,
displayDataLabel: true,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -1731,6 +1746,7 @@ export const Catalog: CatalogStory<Story, typeof WidgetRenderer> = {
position: 0,
pageLayoutId: PAGE_LAYOUT_TEST_INSTANCE_ID,
widgets: [widget],
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -1743,6 +1759,7 @@ export const Catalog: CatalogStory<Story, typeof WidgetRenderer> = {
position: 1,
pageLayoutId: PAGE_LAYOUT_TEST_INSTANCE_ID,
widgets: [],
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -1757,6 +1774,7 @@ export const Catalog: CatalogStory<Story, typeof WidgetRenderer> = {
position: 0,
pageLayoutId: PAGE_LAYOUT_TEST_INSTANCE_ID,
widgets: [widget],
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -299,6 +299,7 @@ const createPageLayoutWithWidget = (
position: 0,
pageLayoutId: PAGE_LAYOUT_TEST_INSTANCE_ID,
widgets: [widget],
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -352,6 +353,7 @@ export const TextFieldWidget: Story = {
fieldMetadataId: nameField.id,
layout: 'FIELD',
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -445,6 +447,7 @@ export const AddressFieldWidget: Story = {
fieldMetadataId: addressField.id,
layout: 'FIELD',
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -541,6 +544,7 @@ export const NumberFieldWidget: Story = {
fieldMetadataId: employeesField.id,
layout: 'FIELD',
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -634,6 +638,7 @@ export const LinkFieldWidget: Story = {
fieldMetadataId: linkedinField.id,
layout: 'FIELD',
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -727,6 +732,7 @@ export const ManyToOneRelationFieldWidget: Story = {
fieldMetadataId: accountOwnerField.id,
layout: 'FIELD',
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -830,6 +836,7 @@ export const OneToManyRelationFieldWidget: Story = {
fieldMetadataId: companyPeopleField.id,
layout: 'FIELD',
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -925,6 +932,7 @@ export const BooleanFieldWidget: Story = {
fieldMetadataId: idealCustomerProfileField.id,
layout: 'FIELD',
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -1017,6 +1025,7 @@ export const CurrencyFieldWidget: Story = {
fieldMetadataId: annualRecurringRevenueField.id,
layout: 'FIELD',
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -1109,6 +1118,7 @@ export const EmailsFieldWidget: Story = {
fieldMetadataId: personEmailsField.id,
layout: 'FIELD',
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -1202,6 +1212,7 @@ export const PhonesFieldWidget: Story = {
fieldMetadataId: personPhonesField.id,
layout: 'FIELD',
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -1295,6 +1306,7 @@ export const SelectFieldWidget: Story = {
fieldMetadataId: opportunityStageField.id,
layout: 'FIELD',
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -1389,6 +1401,7 @@ export const MultiSelectFieldWidget: Story = {
fieldMetadataId: companyWorkPolicyField.id,
layout: 'FIELD',
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -1486,6 +1499,7 @@ export const TimelineActivityRelationFieldWidget: Story = {
fieldMetadataId: timelineActivityWorkspaceMemberField.id,
layout: 'FIELD',
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -1584,6 +1598,7 @@ export const ManyToOneRelationCardWidget: Story = {
fieldMetadataId: accountOwnerField.id,
layout: 'CARD',
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -1695,6 +1710,7 @@ export const OneToManyRelationCardWidget: Story = {
fieldMetadataId: companyPeopleField.id,
layout: 'CARD',
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -1789,6 +1805,7 @@ export const TimelineActivityRelationCardWidget: Story = {
fieldMetadataId: timelineActivityWorkspaceMemberField.id,
layout: 'CARD',
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -1949,6 +1966,7 @@ export const OneToManyRelationCardWidgetWithProgressiveLoading: Story = {
fieldMetadataId: companyPeopleField.id,
layout: 'CARD',
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -150,6 +150,7 @@ const createPageLayoutWithWidget = (
position: 0,
pageLayoutId: PAGE_LAYOUT_TEST_INSTANCE_ID,
widgets: [widget],
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -179,6 +180,7 @@ const createFieldsWidget = (viewId: string | null): PageLayoutWidget => ({
configurationType: WidgetConfigurationType.FIELDS,
viewId,
},
isOverridden: false,
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-01T00:00:00Z',
deletedAt: null,
@@ -129,6 +129,7 @@ export const createTestWidget = (
gridPosition: { row: 0, column: 0, rowSpan: 2, columnSpan: 2 },
objectMetadataId: TEST_OBJECT_METADATA_ID,
configuration: TEST_BAR_CHART_CONFIGURATION,
isOverridden: false,
createdAt: '2024-01-01',
updatedAt: '2024-01-01',
deletedAt: null,
@@ -904,6 +904,7 @@ type PageLayoutWidget {
createdAt: DateTime!
updatedAt: DateTime!
deletedAt: DateTime
isOverridden: Boolean!
}
enum WidgetType {
@@ -1214,6 +1215,7 @@ type PageLayoutTab {
createdAt: DateTime!
updatedAt: DateTime!
deletedAt: DateTime
isOverridden: Boolean!
}
type PageLayout {
@@ -3251,6 +3253,7 @@ input UpdatePageLayoutWidgetInput {
gridPosition: GridPositionInput
position: JSON
configuration: JSON
conditionalDisplay: JSON
}
input CreatePageLayoutTabInput {
@@ -3262,6 +3265,7 @@ input CreatePageLayoutTabInput {
input UpdatePageLayoutTabInput {
title: String
position: Float
icon: String
}
input CreatePageLayoutInput {
@@ -3287,6 +3291,7 @@ input UpdatePageLayoutTabWithWidgetsInput {
id: UUID!
title: String!
position: Float!
icon: String
widgets: [UpdatePageLayoutWidgetWithIdInput!]!
}
@@ -3299,6 +3304,7 @@ input UpdatePageLayoutWidgetWithIdInput {
gridPosition: GridPositionInput!
position: JSON
configuration: JSON
conditionalDisplay: JSON
}
input CreateLogicFunctionFromSourceInput {
@@ -677,6 +677,7 @@ export interface PageLayoutWidget {
createdAt: Scalars['DateTime']
updatedAt: Scalars['DateTime']
deletedAt?: Scalars['DateTime']
isOverridden: Scalars['Boolean']
__typename: 'PageLayoutWidget'
}
@@ -942,6 +943,7 @@ export interface PageLayoutTab {
createdAt: Scalars['DateTime']
updatedAt: Scalars['DateTime']
deletedAt?: Scalars['DateTime']
isOverridden: Scalars['Boolean']
__typename: 'PageLayoutTab'
}
@@ -3521,6 +3523,7 @@ export interface PageLayoutWidgetGenqlSelection{
createdAt?: boolean | number
updatedAt?: boolean | number
deletedAt?: boolean | number
isOverridden?: boolean | number
__typename?: boolean | number
__scalar?: boolean | number
}
@@ -3813,6 +3816,7 @@ export interface PageLayoutTabGenqlSelection{
createdAt?: boolean | number
updatedAt?: boolean | number
deletedAt?: boolean | number
isOverridden?: boolean | number
__typename?: boolean | number
__scalar?: boolean | number
}
@@ -5839,11 +5843,11 @@ export interface CreatePageLayoutWidgetInput {pageLayoutTabId: Scalars['UUID'],t
export interface GridPositionInput {row: Scalars['Float'],column: Scalars['Float'],rowSpan: Scalars['Float'],columnSpan: Scalars['Float']}
export interface UpdatePageLayoutWidgetInput {title?: (Scalars['String'] | null),type?: (WidgetType | null),objectMetadataId?: (Scalars['UUID'] | null),gridPosition?: (GridPositionInput | null),position?: (Scalars['JSON'] | null),configuration?: (Scalars['JSON'] | null)}
export interface UpdatePageLayoutWidgetInput {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)}
export interface CreatePageLayoutTabInput {title: Scalars['String'],position?: (Scalars['Float'] | null),pageLayoutId: Scalars['UUID']}
export interface UpdatePageLayoutTabInput {title?: (Scalars['String'] | null),position?: (Scalars['Float'] | null)}
export interface UpdatePageLayoutTabInput {title?: (Scalars['String'] | null),position?: (Scalars['Float'] | null),icon?: (Scalars['String'] | null)}
export interface CreatePageLayoutInput {name: Scalars['String'],type?: (PageLayoutType | null),objectMetadataId?: (Scalars['UUID'] | null)}
@@ -5851,9 +5855,9 @@ export interface UpdatePageLayoutInput {name?: (Scalars['String'] | null),type?:
export interface UpdatePageLayoutWithTabsInput {name: Scalars['String'],type: PageLayoutType,objectMetadataId?: (Scalars['UUID'] | null),tabs: UpdatePageLayoutTabWithWidgetsInput[]}
export interface UpdatePageLayoutTabWithWidgetsInput {id: Scalars['UUID'],title: Scalars['String'],position: Scalars['Float'],widgets: UpdatePageLayoutWidgetWithIdInput[]}
export interface UpdatePageLayoutTabWithWidgetsInput {id: Scalars['UUID'],title: Scalars['String'],position: Scalars['Float'],icon?: (Scalars['String'] | 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)}
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)}
export interface CreateLogicFunctionFromSourceInput {id?: (Scalars['UUID'] | null),universalIdentifier?: (Scalars['UUID'] | null),name: Scalars['String'],description?: (Scalars['String'] | null),timeoutSeconds?: (Scalars['Float'] | null),toolInputSchema?: (Scalars['JSON'] | null),isTool?: (Scalars['Boolean'] | null),source?: (Scalars['JSON'] | null),cronTriggerSettings?: (Scalars['JSON'] | null),databaseEventTriggerSettings?: (Scalars['JSON'] | null),httpRouteTriggerSettings?: (Scalars['JSON'] | null)}
@@ -1897,6 +1897,9 @@ export default {
"deletedAt": [
4
],
"isOverridden": [
6
],
"__typename": [
1
]
@@ -2517,6 +2520,9 @@ export default {
"deletedAt": [
4
],
"isOverridden": [
6
],
"__typename": [
1
]
@@ -8561,6 +8567,9 @@ export default {
"configuration": [
15
],
"conditionalDisplay": [
15
],
"__typename": [
1
]
@@ -8586,6 +8595,9 @@ export default {
"position": [
11
],
"icon": [
1
],
"__typename": [
1
]
@@ -8645,6 +8657,9 @@ export default {
"position": [
11
],
"icon": [
1
],
"widgets": [
349
],
@@ -8677,6 +8692,9 @@ export default {
"configuration": [
15
],
"conditionalDisplay": [
15
],
"__typename": [
1
]
@@ -0,0 +1,27 @@
import { type MigrationInterface, type QueryRunner } from 'typeorm';
export class AddOverridesToPageLayoutTabAndWidget1772267875870
implements MigrationInterface
{
name = 'AddOverridesToPageLayoutTabAndWidget1772267875870';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "core"."pageLayoutTab" ADD "overrides" jsonb`,
);
await queryRunner.query(
`ALTER TABLE "core"."pageLayoutWidget" ADD "overrides" jsonb`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "core"."pageLayoutWidget" DROP COLUMN "overrides"`,
);
await queryRunner.query(
`ALTER TABLE "core"."pageLayoutTab" DROP COLUMN "overrides"`,
);
}
}
@@ -27,5 +27,6 @@ export const fromPageLayoutTabManifestToUniversalFlatPageLayoutTab = ({
createdAt: now,
updatedAt: now,
deletedAt: null,
overrides: null,
};
};
@@ -35,5 +35,6 @@ export const fromPageLayoutWidgetManifestToUniversalFlatPageLayoutWidget = ({
createdAt: now,
updatedAt: now,
deletedAt: null,
overrides: null,
};
};
@@ -145,8 +145,12 @@ exports[`ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY should ma
"title",
"position",
"deletedAt",
"icon",
"overrides",
],
"propertiesToStringify": [
"overrides",
],
"propertiesToStringify": [],
},
"pageLayoutWidget": {
"propertiesToCompare": [
@@ -157,11 +161,15 @@ exports[`ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY should ma
"position",
"universalConfiguration",
"deletedAt",
"conditionalDisplay",
"overrides",
],
"propertiesToStringify": [
"gridPosition",
"position",
"universalConfiguration",
"conditionalDisplay",
"overrides",
],
},
"role": {
@@ -34,6 +34,7 @@ type MetadataEntityPropertyConfiguration<
: HasObjectInUnion<MetadataEntity<TMetadataName>[K]>
: boolean;
toCompare: boolean;
isOverridable?: boolean;
};
};
@@ -834,6 +835,7 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
toCompare: true,
toStringify: false,
universalProperty: undefined,
isOverridable: true,
},
type: { toCompare: true, toStringify: false, universalProperty: undefined },
objectMetadataId: {
@@ -850,6 +852,7 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
toCompare: true,
toStringify: true,
universalProperty: undefined,
isOverridable: true,
},
configuration: {
toCompare: true,
@@ -877,7 +880,13 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
universalProperty: 'pageLayoutTabUniversalIdentifier',
},
conditionalDisplay: {
toCompare: false,
toCompare: true,
toStringify: true,
universalProperty: undefined,
isOverridable: true,
},
overrides: {
toCompare: true,
toStringify: true,
universalProperty: undefined,
},
@@ -887,11 +896,13 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
toCompare: true,
toStringify: false,
universalProperty: undefined,
isOverridable: true,
},
position: {
toCompare: true,
toStringify: false,
universalProperty: undefined,
isOverridable: true,
},
deletedAt: {
toCompare: true,
@@ -899,9 +910,10 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
universalProperty: undefined,
},
icon: {
toCompare: false,
toCompare: true,
toStringify: false,
universalProperty: undefined,
isOverridable: true,
},
createdAt: {
toCompare: false,
@@ -923,6 +935,11 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
toStringify: false,
universalProperty: undefined,
},
overrides: {
toCompare: true,
toStringify: true,
universalProperty: undefined,
},
},
skill: {
name: { toCompare: true, toStringify: false, universalProperty: undefined },
@@ -1341,3 +1358,12 @@ export type MetadataEntityComparablePropertyName<T extends AllMetadataName> =
FilterComparableKeys<
(typeof ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME)[T]
>;
type FilterOverridableKeys<TConfig> = {
[P in keyof TConfig]: TConfig[P] extends { isOverridable: true } ? P : never;
}[keyof TConfig];
export type MetadataEntityOverridablePropertyName<T extends AllMetadataName> =
FilterOverridableKeys<
(typeof ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME)[T]
>;
@@ -0,0 +1,32 @@
import {
ALL_METADATA_NAME,
type AllMetadataName,
} from 'twenty-shared/metadata';
import {
ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME,
type MetadataEntityOverridablePropertyName,
} from 'src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant';
const computeOverridableProperties = <T extends AllMetadataName>(
metadataName: T,
): MetadataEntityOverridablePropertyName<T>[] => {
const config =
ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME[metadataName];
return (Object.entries(config) as [string, { isOverridable?: boolean }][])
.filter(([_, conf]) => conf.isOverridable === true)
.map(([property]) => property as MetadataEntityOverridablePropertyName<T>);
};
export const ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME = Object.values(
ALL_METADATA_NAME,
).reduce(
(acc, metadataName) => ({
...acc,
[metadataName]: computeOverridableProperties(metadataName),
}),
{} as {
[P in AllMetadataName]: MetadataEntityOverridablePropertyName<P>[];
},
);
@@ -3,4 +3,5 @@ import { type MetadataEntityPropertyName } from 'src/engine/metadata-modules/fla
export const FLAT_PAGE_LAYOUT_TAB_EDITABLE_PROPERTIES = [
'title',
'position',
'icon',
] as const satisfies MetadataEntityPropertyName<'pageLayoutTab'>[];
@@ -57,5 +57,6 @@ export const fromCreatePageLayoutTabInputToFlatPageLayoutTabToCreate = ({
widgetUniversalIdentifiers: [],
icon: null,
layoutMode: PageLayoutTabLayoutMode.GRID,
overrides: null,
};
};
@@ -13,6 +13,8 @@ import {
PageLayoutTabException,
PageLayoutTabExceptionCode,
} from 'src/engine/metadata-modules/page-layout-tab/exceptions/page-layout-tab.exception';
import { isCallerOverridingEntity } from 'src/engine/metadata-modules/utils/is-caller-overriding-entity.util';
import { sanitizeOverridableEntityInput } from 'src/engine/metadata-modules/utils/sanitize-overridable-entity-input.util';
import { mergeUpdateInExistingRecord } from 'src/utils/merge-update-in-existing-record.util';
export type UpdatePageLayoutTabInputWithId = {
@@ -23,9 +25,13 @@ export type UpdatePageLayoutTabInputWithId = {
export const fromUpdatePageLayoutTabInputToFlatPageLayoutTabToUpdateOrThrow = ({
updatePageLayoutTabInput: rawUpdatePageLayoutTabInput,
flatPageLayoutTabMaps,
callerApplicationUniversalIdentifier,
workspaceCustomApplicationUniversalIdentifier,
}: {
updatePageLayoutTabInput: UpdatePageLayoutTabInputWithId;
flatPageLayoutTabMaps: FlatPageLayoutTabMaps;
callerApplicationUniversalIdentifier: string;
workspaceCustomApplicationUniversalIdentifier: string;
}): FlatPageLayoutTab => {
const { id: pageLayoutTabToUpdateId } = extractAndSanitizeObjectStringFields(
rawUpdatePageLayoutTabInput,
@@ -44,14 +50,32 @@ export const fromUpdatePageLayoutTabInputToFlatPageLayoutTabToUpdateOrThrow = ({
);
}
const updatedEditableFieldProperties = extractAndSanitizeObjectStringFields(
const editableProperties = extractAndSanitizeObjectStringFields(
rawUpdatePageLayoutTabInput.update,
FLAT_PAGE_LAYOUT_TAB_EDITABLE_PROPERTIES,
);
return mergeUpdateInExistingRecord({
existing: existingFlatPageLayoutTabToUpdate,
properties: [...FLAT_PAGE_LAYOUT_TAB_EDITABLE_PROPERTIES],
update: updatedEditableFieldProperties,
const shouldOverride = isCallerOverridingEntity({
callerApplicationUniversalIdentifier,
entityApplicationUniversalIdentifier:
existingFlatPageLayoutTabToUpdate.applicationUniversalIdentifier,
workspaceCustomApplicationUniversalIdentifier,
});
const { overrides, updatedEditableProperties } =
sanitizeOverridableEntityInput({
metadataName: 'pageLayoutTab',
existingFlatEntity: existingFlatPageLayoutTabToUpdate,
updatedEditableProperties: editableProperties,
shouldOverride,
});
return {
...mergeUpdateInExistingRecord({
existing: existingFlatPageLayoutTabToUpdate,
properties: [...FLAT_PAGE_LAYOUT_TAB_EDITABLE_PROPERTIES],
update: updatedEditableProperties,
}),
overrides,
} as FlatPageLayoutTab;
};
@@ -53,5 +53,6 @@ export const transformPageLayoutTabEntityToFlatPageLayoutTab = ({
widgetUniversalIdentifiers: pageLayoutTabEntity.widgets.map(
(widget) => widget.universalIdentifier,
),
overrides: pageLayoutTabEntity.overrides ?? null,
};
};
@@ -7,4 +7,5 @@ export const FLAT_PAGE_LAYOUT_WIDGET_EDITABLE_PROPERTIES = [
'gridPosition',
'position',
'configuration',
'conditionalDisplay',
] as const satisfies MetadataEntityPropertyName<'pageLayoutWidget'>[];
@@ -73,6 +73,7 @@ export const fromCreatePageLayoutWidgetInputToFlatPageLayoutWidgetToCreate = ({
applicationId: flatApplication.id,
applicationUniversalIdentifier: flatApplication.universalIdentifier,
conditionalDisplay: null,
overrides: null,
universalConfiguration:
fromPageLayoutWidgetConfigurationToUniversalConfiguration({
configuration: createPageLayoutWidgetInput.configuration,
@@ -17,6 +17,8 @@ import {
PageLayoutWidgetExceptionCode,
} from 'src/engine/metadata-modules/page-layout-widget/exceptions/page-layout-widget.exception';
import { validateWidgetConfigurationInput } from 'src/engine/metadata-modules/page-layout-widget/utils/validate-widget-configuration-input.util';
import { isCallerOverridingEntity } from 'src/engine/metadata-modules/utils/is-caller-overriding-entity.util';
import { sanitizeOverridableEntityInput } from 'src/engine/metadata-modules/utils/sanitize-overridable-entity-input.util';
import { mergeUpdateInExistingRecord } from 'src/utils/merge-update-in-existing-record.util';
export type UpdatePageLayoutWidgetInputWithId = {
@@ -33,9 +35,13 @@ export const fromUpdatePageLayoutWidgetInputToFlatPageLayoutWidgetToUpdateOrThro
flatFrontComponentMaps,
flatViewFieldGroupMaps,
flatViewMaps,
callerApplicationUniversalIdentifier,
workspaceCustomApplicationUniversalIdentifier,
}: {
updatePageLayoutWidgetInput: UpdatePageLayoutWidgetInputWithId;
flatPageLayoutWidgetMaps: FlatPageLayoutWidgetMaps;
callerApplicationUniversalIdentifier: string;
workspaceCustomApplicationUniversalIdentifier: string;
} & Pick<
AllFlatEntityMaps,
| 'flatObjectMetadataMaps'
@@ -62,29 +68,44 @@ export const fromUpdatePageLayoutWidgetInputToFlatPageLayoutWidgetToUpdateOrThro
);
}
const updatedEditableFieldProperties = extractAndSanitizeObjectStringFields(
const editableProperties = extractAndSanitizeObjectStringFields(
rawUpdatePageLayoutWidgetInput.update,
FLAT_PAGE_LAYOUT_WIDGET_EDITABLE_PROPERTIES,
);
if (
Object.prototype.hasOwnProperty.call(
updatedEditableFieldProperties,
'configuration',
)
Object.prototype.hasOwnProperty.call(editableProperties, 'configuration')
) {
validateWidgetConfigurationInput({
configuration: updatedEditableFieldProperties.configuration,
configuration: editableProperties.configuration,
});
}
const flatPageLayoutWidgetToUpdate = mergeUpdateInExistingRecord({
existing: existingFlatPageLayoutWidgetToUpdate,
properties: FLAT_PAGE_LAYOUT_WIDGET_EDITABLE_PROPERTIES,
update: updatedEditableFieldProperties,
const shouldOverride = isCallerOverridingEntity({
callerApplicationUniversalIdentifier,
entityApplicationUniversalIdentifier:
existingFlatPageLayoutWidgetToUpdate.applicationUniversalIdentifier,
workspaceCustomApplicationUniversalIdentifier,
});
if (updatedEditableFieldProperties.objectMetadataId !== undefined) {
const { overrides, updatedEditableProperties } =
sanitizeOverridableEntityInput({
metadataName: 'pageLayoutWidget',
existingFlatEntity: existingFlatPageLayoutWidgetToUpdate,
updatedEditableProperties: editableProperties,
shouldOverride,
});
const flatPageLayoutWidgetToUpdate = {
...mergeUpdateInExistingRecord({
existing: existingFlatPageLayoutWidgetToUpdate,
properties: FLAT_PAGE_LAYOUT_WIDGET_EDITABLE_PROPERTIES,
update: updatedEditableProperties,
}),
overrides,
};
if (updatedEditableProperties.objectMetadataId !== undefined) {
const { objectMetadataUniversalIdentifier } =
resolveEntityRelationUniversalIdentifiers({
metadataName: 'pageLayoutWidget',
@@ -98,7 +119,7 @@ export const fromUpdatePageLayoutWidgetInputToFlatPageLayoutWidgetToUpdateOrThro
objectMetadataUniversalIdentifier;
}
if (isDefined(updatedEditableFieldProperties.configuration)) {
if (isDefined(updatedEditableProperties.configuration)) {
flatPageLayoutWidgetToUpdate.universalConfiguration =
fromPageLayoutWidgetConfigurationToUniversalConfiguration({
configuration: flatPageLayoutWidgetToUpdate.configuration,
@@ -97,6 +97,7 @@ const buildFieldsWidget = ({
},
},
universalConfiguration: null,
overrides: null,
});
const buildFlatPageLayoutWidgetMaps = (
@@ -70,6 +70,7 @@ export const computeFlatDefaultRecordPageLayoutToCreate = ({
deletedAt: null,
icon: tabProps.icon,
layoutMode: tabProps.layoutMode,
overrides: null,
});
const isFieldsWidget = widgetKey === 'fields';
@@ -120,6 +121,7 @@ export const computeFlatDefaultRecordPageLayoutToCreate = ({
updatedAt: now,
deletedAt: null,
conditionalDisplay: null,
overrides: null,
});
}
@@ -5,6 +5,7 @@ import {
IsArray,
IsNotEmpty,
IsNumber,
IsOptional,
IsString,
IsUUID,
ValidateNested,
@@ -29,6 +30,11 @@ export class UpdatePageLayoutTabWithWidgetsInput {
@IsNotEmpty()
position: number;
@Field(() => String, { nullable: true })
@IsString()
@IsOptional()
icon?: string | null;
@Field(() => [UpdatePageLayoutWidgetWithIdInput])
@IsArray()
@ValidateNested({ each: true })
@@ -13,4 +13,9 @@ export class UpdatePageLayoutTabInput {
@IsNumber()
@IsOptional()
position?: number;
@Field(() => String, { nullable: true })
@IsString()
@IsOptional()
icon?: string | null;
}
@@ -1,9 +1,16 @@
import { Field, Float, ObjectType, registerEnumType } from '@nestjs/graphql';
import {
Field,
Float,
HideField,
ObjectType,
registerEnumType,
} from '@nestjs/graphql';
import { IDField } from '@ptc-org/nestjs-query-graphql';
import { PageLayoutTabLayoutMode } from 'twenty-shared/types';
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
import { type PageLayoutTabOverrides } from 'src/engine/metadata-modules/page-layout-tab/entities/page-layout-tab.entity';
import { PageLayoutWidgetDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/page-layout-widget.dto';
registerEnumType(PageLayoutTabLayoutMode, {
@@ -47,4 +54,10 @@ export class PageLayoutTabDTO {
@Field(() => Date, { nullable: true })
deletedAt?: Date | null;
@Field(() => Boolean, { nullable: false })
isOverridden: boolean;
@HideField()
overrides?: PageLayoutTabOverrides | null;
}
@@ -17,7 +17,13 @@ import {
import { PageLayoutWidgetEntity } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity';
import { PageLayoutEntity } from 'src/engine/metadata-modules/page-layout/entities/page-layout.entity';
import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface';
import { OverridableEntity } from 'src/engine/workspace-manager/types/overridable-entity';
export type PageLayoutTabOverrides = {
title?: string;
position?: number;
icon?: string | null;
};
@Entity({ name: 'pageLayoutTab', schema: 'core' })
@ObjectType('PageLayoutTab')
@@ -27,7 +33,7 @@ import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-enti
{ where: '"deletedAt" IS NULL' },
)
export class PageLayoutTabEntity
extends SyncableEntity
extends OverridableEntity<PageLayoutTabOverrides>
implements Required<PageLayoutTabEntity>
{
@PrimaryGeneratedColumn('uuid')
@@ -7,6 +7,7 @@ import {
import {
Args,
Context,
Float,
Mutation,
Parent,
Query,
@@ -30,6 +31,7 @@ import { PageLayoutTabDTO } from 'src/engine/metadata-modules/page-layout-tab/dt
import { PageLayoutTabService } from 'src/engine/metadata-modules/page-layout-tab/services/page-layout-tab.service';
import { resolvePageLayoutTabTitle } from 'src/engine/metadata-modules/page-layout-tab/utils/resolve-page-layout-tab-title.util';
import { PageLayoutGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/page-layout/utils/page-layout-graphql-api-exception.filter';
import { resolveOverridableEntityProperty } from 'src/engine/metadata-modules/utils/resolve-overridable-entity-property.util';
import { WorkspaceMigrationGraphqlApiExceptionInterceptor } from 'src/engine/workspace-manager/workspace-migration/interceptors/workspace-migration-graphql-api-exception.interceptor';
@MetadataResolver(() => PageLayoutTabDTO)
@@ -48,15 +50,26 @@ export class PageLayoutTabResolver {
@Parent() tab: PageLayoutTabDTO,
@Context() context: I18nContext,
): Promise<string> {
const resolvedTitle = resolveOverridableEntityProperty(tab, 'title');
const i18n = this.i18nService.getI18nInstance(context.req.locale);
return resolvePageLayoutTabTitle({
title: tab.title,
title: resolvedTitle,
applicationId: tab.applicationId,
i18nInstance: i18n,
});
}
@ResolveField(() => Float)
position(@Parent() tab: PageLayoutTabDTO): number {
return resolveOverridableEntityProperty(tab, 'position');
}
@ResolveField(() => String, { nullable: true })
icon(@Parent() tab: PageLayoutTabDTO): string | null | undefined {
return resolveOverridableEntityProperty(tab, 'icon');
}
@Query(() => [PageLayoutTabDTO])
@UseGuards(NoPermissionGuard)
async getPageLayoutTabs(
@@ -233,6 +233,10 @@ export class PageLayoutTabService {
fromUpdatePageLayoutTabInputToFlatPageLayoutTabToUpdateOrThrow({
updatePageLayoutTabInput,
flatPageLayoutTabMaps: existingFlatPageLayoutTabMaps,
callerApplicationUniversalIdentifier:
workspaceCustomFlatApplication.universalIdentifier,
workspaceCustomApplicationUniversalIdentifier:
workspaceCustomFlatApplication.universalIdentifier,
});
const validateAndBuildResult =
@@ -1,3 +1,5 @@
import { isDefined } from 'twenty-shared/utils';
import { type FlatPageLayoutTab } from 'src/engine/metadata-modules/flat-page-layout-tab/types/flat-page-layout-tab.type';
import { type PageLayoutTabDTO } from 'src/engine/metadata-modules/page-layout-tab/dtos/page-layout-tab.dto';
@@ -14,6 +16,8 @@ export const fromFlatPageLayoutTabToPageLayoutTabDto = (
return {
...rest,
isOverridden:
isDefined(rest.overrides) && Object.keys(rest.overrides).length > 0,
createdAt: new Date(createdAt),
updatedAt: new Date(updatedAt),
deletedAt: deletedAt ? new Date(deletedAt) : null,
@@ -11,7 +11,10 @@ import {
ValidateNested,
} from 'class-validator';
import { GraphQLJSON } from 'graphql-type-json';
import { PageLayoutWidgetPosition } from 'twenty-shared/types';
import {
PageLayoutWidgetConditionalDisplay,
PageLayoutWidgetPosition,
} from 'twenty-shared/types';
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
import { GridPositionInput } from 'src/engine/metadata-modules/page-layout-widget/dtos/inputs/grid-position.input';
@@ -60,4 +63,9 @@ export class UpdatePageLayoutWidgetWithIdInput {
@IsObject()
@IsOptional()
configuration: AllPageLayoutWidgetConfiguration;
@Field(() => GraphQLJSON, { nullable: true })
@IsObject()
@IsOptional()
conditionalDisplay?: PageLayoutWidgetConditionalDisplay | null;
}
@@ -10,7 +10,10 @@ import {
ValidateNested,
} from 'class-validator';
import { GraphQLJSON } from 'graphql-type-json';
import { PageLayoutWidgetPosition } from 'twenty-shared/types';
import {
PageLayoutWidgetConditionalDisplay,
PageLayoutWidgetPosition,
} from 'twenty-shared/types';
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
import { GridPositionInput } from 'src/engine/metadata-modules/page-layout-widget/dtos/inputs/grid-position.input';
@@ -49,4 +52,9 @@ export class UpdatePageLayoutWidgetInput {
@IsObject()
@IsOptional()
configuration?: AllPageLayoutWidgetConfiguration;
@Field(() => GraphQLJSON, { nullable: true })
@IsObject()
@IsOptional()
conditionalDisplay?: PageLayoutWidgetConditionalDisplay | null;
}
@@ -1,4 +1,9 @@
import { Field, ObjectType, registerEnumType } from '@nestjs/graphql';
import {
Field,
HideField,
ObjectType,
registerEnumType,
} from '@nestjs/graphql';
import { IDField } from '@ptc-org/nestjs-query-graphql';
import GraphQLJSON from 'graphql-type-json';
@@ -8,6 +13,7 @@ import {
} from 'twenty-shared/types';
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
import { type PageLayoutWidgetOverrides } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity';
import { PageLayoutWidgetPositionUnion } from 'src/engine/metadata-modules/page-layout-widget/dtos/page-layout-widget-position.union';
import { WidgetConfiguration } from 'src/engine/metadata-modules/page-layout-widget/dtos/widget-configuration.interface';
import { WidgetType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-type.enum';
@@ -67,4 +73,10 @@ export class PageLayoutWidgetDTO {
@Field(() => Date, { nullable: true })
deletedAt?: Date;
@Field(() => Boolean, { nullable: false })
isOverridden: boolean;
@HideField()
overrides?: PageLayoutWidgetOverrides | null;
}
@@ -23,9 +23,15 @@ import { PageLayoutTabEntity } from 'src/engine/metadata-modules/page-layout-tab
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 { PageLayoutWidgetConfigurationTypeSettings } from 'src/engine/metadata-modules/page-layout-widget/types/page-layout-widget-configuration.type';
import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface';
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';
export type PageLayoutWidgetOverrides = {
title?: string;
position?: PageLayoutWidgetPosition | null;
conditionalDisplay?: PageLayoutWidgetConditionalDisplay | null;
};
@Entity({ name: 'pageLayoutWidget', schema: 'core' })
@ObjectType('PageLayoutWidget')
@Index(
@@ -38,7 +44,7 @@ export class PageLayoutWidgetEntity<
TWidgetConfigurationType extends
WidgetConfigurationType = WidgetConfigurationType,
>
extends SyncableEntity
extends OverridableEntity<PageLayoutWidgetOverrides>
implements Required<PageLayoutWidgetEntity>
{
@PrimaryGeneratedColumn('uuid')
@@ -6,6 +6,7 @@ import {
} from '@nestjs/common';
import { Args, Mutation, Parent, Query, ResolveField } from '@nestjs/graphql';
import GraphQLJSON from 'graphql-type-json';
import { PermissionFlagType } from 'twenty-shared/constants';
import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe';
@@ -21,6 +22,7 @@ import { PageLayoutWidgetDTO } from 'src/engine/metadata-modules/page-layout-wid
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 { PageLayoutGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/page-layout/utils/page-layout-graphql-api-exception.filter';
import { resolveOverridableEntityProperty } from 'src/engine/metadata-modules/utils/resolve-overridable-entity-property.util';
import { WorkspaceMigrationGraphqlApiExceptionInterceptor } from 'src/engine/workspace-manager/workspace-migration/interceptors/workspace-migration-graphql-api-exception.interceptor';
@MetadataResolver(() => PageLayoutWidgetDTO)
@@ -95,6 +97,21 @@ export class PageLayoutWidgetResolver {
});
}
@ResolveField(() => String)
title(@Parent() widget: PageLayoutWidgetDTO): string {
return resolveOverridableEntityProperty(widget, 'title');
}
@ResolveField(() => GraphQLJSON, { nullable: true })
position(@Parent() widget: PageLayoutWidgetDTO) {
return resolveOverridableEntityProperty(widget, 'position');
}
@ResolveField(() => GraphQLJSON, { nullable: true })
conditionalDisplay(@Parent() widget: PageLayoutWidgetDTO) {
return resolveOverridableEntityProperty(widget, 'conditionalDisplay');
}
@ResolveField(() => WidgetConfiguration, { nullable: true })
configuration(@Parent() widget: PageLayoutWidgetDTO) {
return widget.configuration;
@@ -327,24 +327,32 @@ export class PageLayoutWidgetService {
existingFlatPageLayoutWidgetMaps,
);
const {
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
flatFieldMetadataMaps: existingFlatFieldMetadataMaps,
flatFrontComponentMaps: existingFlatFrontComponentMaps,
flatViewFieldGroupMaps: existingFlatViewFieldGroupMaps,
flatViewMaps: existingFlatViewMaps,
} = await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
const [
{
workspaceId,
flatMapsKeys: [
'flatObjectMetadataMaps',
'flatFieldMetadataMaps',
'flatFrontComponentMaps',
'flatViewFieldGroupMaps',
'flatViewMaps',
],
flatObjectMetadataMaps: existingFlatObjectMetadataMaps,
flatFieldMetadataMaps: existingFlatFieldMetadataMaps,
flatFrontComponentMaps: existingFlatFrontComponentMaps,
flatViewFieldGroupMaps: existingFlatViewFieldGroupMaps,
flatViewMaps: existingFlatViewMaps,
},
);
{ workspaceCustomFlatApplication },
] = await Promise.all([
this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
{
workspaceId,
flatMapsKeys: [
'flatObjectMetadataMaps',
'flatFieldMetadataMaps',
'flatFrontComponentMaps',
'flatViewFieldGroupMaps',
'flatViewMaps',
],
},
),
this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
{ workspaceId },
),
]);
const isConfigurationBeingUpdated = Object.prototype.hasOwnProperty.call(
updateData,
@@ -377,6 +385,10 @@ export class PageLayoutWidgetService {
flatFrontComponentMaps: existingFlatFrontComponentMaps,
flatViewFieldGroupMaps: existingFlatViewFieldGroupMaps,
flatViewMaps: existingFlatViewMaps,
callerApplicationUniversalIdentifier:
workspaceCustomFlatApplication.universalIdentifier,
workspaceCustomApplicationUniversalIdentifier:
workspaceCustomFlatApplication.universalIdentifier,
});
const shouldValidateChartFields =
@@ -1,3 +1,5 @@
import { isDefined } from 'twenty-shared/utils';
import { type FlatPageLayoutWidget } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget.type';
import { type PageLayoutWidgetDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/page-layout-widget.dto';
@@ -9,6 +11,8 @@ export const fromFlatPageLayoutWidgetToPageLayoutWidgetDto = (
return {
...rest,
isOverridden:
isDefined(rest.overrides) && Object.keys(rest.overrides).length > 0,
objectMetadataId: objectMetadataId ?? undefined,
createdAt: new Date(createdAt),
updatedAt: new Date(updatedAt),
@@ -295,6 +295,7 @@ export class PageLayoutUpdateService {
widgetUniversalIdentifiers: [],
icon: null,
layoutMode: PageLayoutTabLayoutMode.GRID,
overrides: null,
};
},
);
@@ -498,6 +499,7 @@ export class PageLayoutUpdateService {
applicationUniversalIdentifier:
workspaceCustomApplicationUniversalIdentifier,
conditionalDisplay: null,
overrides: null,
universalConfiguration:
fromPageLayoutWidgetConfigurationToUniversalConfiguration({
configuration: widgetInput.configuration,
@@ -0,0 +1,37 @@
import { isCallerOverridingEntity } from 'src/engine/metadata-modules/utils/is-caller-overriding-entity.util';
const CUSTOM_APP_ID = 'custom-app-universal-id';
const STANDARD_APP_ID = 'standard-app-universal-id';
const OTHER_APP_ID = 'other-app-universal-id';
describe('isCallerOverridingEntity', () => {
it('should return true when custom app updates a standard-app entity', () => {
expect(
isCallerOverridingEntity({
callerApplicationUniversalIdentifier: CUSTOM_APP_ID,
entityApplicationUniversalIdentifier: STANDARD_APP_ID,
workspaceCustomApplicationUniversalIdentifier: CUSTOM_APP_ID,
}),
).toBe(true);
});
it('should return false when custom app updates its own entity', () => {
expect(
isCallerOverridingEntity({
callerApplicationUniversalIdentifier: CUSTOM_APP_ID,
entityApplicationUniversalIdentifier: CUSTOM_APP_ID,
workspaceCustomApplicationUniversalIdentifier: CUSTOM_APP_ID,
}),
).toBe(false);
});
it('should return false when a non-custom app updates another app entity', () => {
expect(
isCallerOverridingEntity({
callerApplicationUniversalIdentifier: OTHER_APP_ID,
entityApplicationUniversalIdentifier: STANDARD_APP_ID,
workspaceCustomApplicationUniversalIdentifier: CUSTOM_APP_ID,
}),
).toBe(false);
});
});
@@ -0,0 +1,73 @@
import { resolveOverridableEntityProperty } from 'src/engine/metadata-modules/utils/resolve-overridable-entity-property.util';
type TestEntity = {
title: string;
position: number;
icon: string | null;
overrides?: Partial<TestEntity> | null;
};
describe('resolveOverridableEntityProperty', () => {
it('should return override value when override exists for the property', () => {
const entity: TestEntity = {
title: 'Base Title',
position: 0,
icon: null,
overrides: { title: 'Overridden Title' },
};
expect(resolveOverridableEntityProperty(entity, 'title')).toBe(
'Overridden Title',
);
});
it('should return base value when overrides is null', () => {
const entity: TestEntity = {
title: 'Base Title',
position: 0,
icon: null,
overrides: null,
};
expect(resolveOverridableEntityProperty(entity, 'title')).toBe(
'Base Title',
);
});
it('should return base value when overrides exist but not for the requested property', () => {
const entity: TestEntity = {
title: 'Base Title',
position: 0,
icon: null,
overrides: { position: 5 },
};
expect(resolveOverridableEntityProperty(entity, 'title')).toBe(
'Base Title',
);
});
it('should return base value when overrides is undefined', () => {
const entity: TestEntity = {
title: 'Base Title',
position: 0,
icon: null,
overrides: undefined,
};
expect(resolveOverridableEntityProperty(entity, 'title')).toBe(
'Base Title',
);
});
it('should return null when override explicitly sets a nullable property to null', () => {
const entity: TestEntity = {
title: 'Base Title',
position: 0,
icon: 'IconStar',
overrides: { icon: null },
};
expect(resolveOverridableEntityProperty(entity, 'icon')).toBeNull();
});
});
@@ -0,0 +1,136 @@
import { sanitizeOverridableEntityInput } from 'src/engine/metadata-modules/utils/sanitize-overridable-entity-input.util';
describe('sanitizeOverridableEntityInput', () => {
describe('when shouldOverride is false', () => {
it('should pass through properties unchanged and preserve existing overrides', () => {
const existingOverrides = { title: 'Previous Override' };
const result = sanitizeOverridableEntityInput({
metadataName: 'pageLayoutTab',
existingFlatEntity: {
title: 'Base Title',
position: 0,
icon: null,
overrides: existingOverrides,
},
updatedEditableProperties: { title: 'New Title' },
shouldOverride: false,
});
expect(result.updatedEditableProperties).toEqual({ title: 'New Title' });
expect(result.overrides).toBe(existingOverrides);
});
});
describe('when shouldOverride is true', () => {
it('should move overridable property to overrides and remove it from editableProperties', () => {
const result = sanitizeOverridableEntityInput({
metadataName: 'pageLayoutTab',
existingFlatEntity: {
title: 'Base Title',
position: 0,
icon: null,
overrides: null,
},
updatedEditableProperties: { title: 'Overridden Title' },
shouldOverride: true,
});
expect(result.overrides).toEqual({ title: 'Overridden Title' });
expect(result.updatedEditableProperties).not.toHaveProperty('title');
});
it('should implicitly restore when new value matches base value by removing the key from overrides', () => {
const result = sanitizeOverridableEntityInput({
metadataName: 'pageLayoutTab',
existingFlatEntity: {
title: 'Base Title',
position: 0,
icon: null,
overrides: { title: 'Old Override', position: 5 },
},
updatedEditableProperties: { title: 'Base Title' },
shouldOverride: true,
});
expect(result.overrides).toEqual({ position: 5 });
expect(result.updatedEditableProperties).not.toHaveProperty('title');
});
it('should return null overrides when removing the last override key', () => {
const result = sanitizeOverridableEntityInput({
metadataName: 'pageLayoutTab',
existingFlatEntity: {
title: 'Base Title',
position: 0,
icon: null,
overrides: { title: 'Old Override' },
},
updatedEditableProperties: { title: 'Base Title' },
shouldOverride: true,
});
expect(result.overrides).toBeNull();
expect(result.updatedEditableProperties).not.toHaveProperty('title');
});
it('should not move non-overridable properties to overrides', () => {
const result = sanitizeOverridableEntityInput({
metadataName: 'pageLayoutTab',
existingFlatEntity: {
title: 'Base Title',
position: 0,
icon: null,
pageLayoutId: 'layout-1',
overrides: null,
},
updatedEditableProperties: {
title: 'Overridden Title',
pageLayoutId: 'layout-2',
},
shouldOverride: true,
});
expect(result.overrides).toEqual({ title: 'Overridden Title' });
expect(result.updatedEditableProperties).toEqual({
pageLayoutId: 'layout-2',
});
});
it('should preserve existing override keys when updating a different overridable property', () => {
const result = sanitizeOverridableEntityInput({
metadataName: 'pageLayoutTab',
existingFlatEntity: {
title: 'Base Title',
position: 0,
icon: null,
overrides: { title: 'Overridden Title' },
},
updatedEditableProperties: { position: 5 },
shouldOverride: true,
});
expect(result.overrides).toEqual({
title: 'Overridden Title',
position: 5,
});
expect(result.updatedEditableProperties).not.toHaveProperty('position');
});
it('should create new overrides object when no existing overrides', () => {
const result = sanitizeOverridableEntityInput({
metadataName: 'pageLayoutTab',
existingFlatEntity: {
title: 'Base Title',
position: 0,
icon: null,
overrides: null,
},
updatedEditableProperties: { icon: 'IconStar' },
shouldOverride: true,
});
expect(result.overrides).toEqual({ icon: 'IconStar' });
expect(result.updatedEditableProperties).not.toHaveProperty('icon');
});
});
});
@@ -0,0 +1,16 @@
export const isCallerOverridingEntity = ({
callerApplicationUniversalIdentifier,
entityApplicationUniversalIdentifier,
workspaceCustomApplicationUniversalIdentifier,
}: {
callerApplicationUniversalIdentifier: string;
entityApplicationUniversalIdentifier: string;
workspaceCustomApplicationUniversalIdentifier: string;
}): boolean => {
return (
callerApplicationUniversalIdentifier ===
workspaceCustomApplicationUniversalIdentifier &&
entityApplicationUniversalIdentifier !==
workspaceCustomApplicationUniversalIdentifier
);
};
@@ -0,0 +1,11 @@
export const resolveOverridableEntityProperty = <
TEntity extends { overrides?: Partial<TEntity> | null },
K extends string & keyof TEntity,
>(
entity: TEntity,
property: K,
): TEntity[K] => {
const overrideValue = entity.overrides?.[property];
return overrideValue !== undefined ? overrideValue : entity[property];
};
@@ -0,0 +1,88 @@
import isEqual from 'lodash.isequal';
import { type AllMetadataName } from 'twenty-shared/metadata';
import { isDefined } from 'twenty-shared/utils';
import { ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME } from 'src/engine/metadata-modules/flat-entity/constant/all-overridable-properties-by-metadata-name.constant';
type FlatEntityWithOverrides = {
[key: string]: unknown;
overrides: Record<string, unknown> | null;
};
export const sanitizeOverridableEntityInput = <
T extends AllMetadataName,
TProperties extends Record<string, unknown>,
>({
metadataName,
existingFlatEntity,
updatedEditableProperties,
shouldOverride,
}: {
metadataName: T;
existingFlatEntity: FlatEntityWithOverrides;
updatedEditableProperties: TProperties;
shouldOverride: boolean;
}): {
overrides: Record<string, unknown> | null;
updatedEditableProperties: TProperties;
} => {
const existingOverrides = existingFlatEntity.overrides;
if (!shouldOverride) {
return {
overrides: existingOverrides,
updatedEditableProperties,
};
}
const sanitizedEditableProperties = {
...updatedEditableProperties,
} as TProperties;
const overridableProperties = ALL_OVERRIDABLE_PROPERTIES_BY_METADATA_NAME[
metadataName
] as string[];
const overrides = overridableProperties.reduce<Record<
string,
unknown
> | null>((acc, property) => {
const isPropertyUpdated =
sanitizedEditableProperties[property] !== undefined;
if (!isPropertyUpdated) {
return acc;
}
const propertyValue = sanitizedEditableProperties[property];
delete sanitizedEditableProperties[property];
if (isEqual(propertyValue, existingFlatEntity[property])) {
if (
isDefined(acc) &&
Object.prototype.hasOwnProperty.call(acc, property)
) {
const { [property]: _, ...restOverrides } = acc;
return restOverrides;
}
return acc;
}
return {
...acc,
[property]: propertyValue,
};
}, existingOverrides);
if (isDefined(overrides) && Object.keys(overrides).length === 0) {
return {
overrides: null,
updatedEditableProperties: sanitizedEditableProperties,
};
}
return { overrides, updatedEditableProperties: sanitizedEditableProperties };
};
@@ -10,4 +10,5 @@ export type SeederFlatPageLayoutWidget = Pick<
| 'position'
| 'configuration'
| 'objectMetadataId'
| 'overrides'
>;
@@ -11,7 +11,10 @@ type PageLayoutTabSeed = {
position: number;
pageLayoutId: string;
workspaceId: string;
} & Pick<FlatPageLayoutTab, 'applicationId' | 'universalIdentifier'>;
} & Pick<
FlatPageLayoutTab,
'applicationId' | 'universalIdentifier' | 'overrides'
>;
export const getPageLayoutTabDataSeeds = ({
applicationId,
@@ -31,6 +34,7 @@ export const getPageLayoutTabDataSeeds = ({
workspaceId,
universalIdentifier: v4(),
applicationId,
overrides: null,
},
{
id: generateSeedId(workspaceId, PAGE_LAYOUT_TAB_SEEDS.SALES_DETAILS),
@@ -43,6 +47,7 @@ export const getPageLayoutTabDataSeeds = ({
workspaceId,
universalIdentifier: v4(),
applicationId,
overrides: null,
},
{
id: generateSeedId(workspaceId, PAGE_LAYOUT_TAB_SEEDS.CUSTOMER_OVERVIEW),
@@ -55,6 +60,7 @@ export const getPageLayoutTabDataSeeds = ({
workspaceId,
universalIdentifier: v4(),
applicationId,
overrides: null,
},
{
id: generateSeedId(workspaceId, PAGE_LAYOUT_TAB_SEEDS.CUSTOMER_ANALYTICS),
@@ -67,6 +73,7 @@ export const getPageLayoutTabDataSeeds = ({
workspaceId,
universalIdentifier: v4(),
applicationId,
overrides: null,
},
{
id: generateSeedId(workspaceId, PAGE_LAYOUT_TAB_SEEDS.TEAM_OVERVIEW),
@@ -76,6 +83,7 @@ export const getPageLayoutTabDataSeeds = ({
workspaceId,
universalIdentifier: v4(),
applicationId,
overrides: null,
},
{
id: generateSeedId(workspaceId, PAGE_LAYOUT_TAB_SEEDS.TEAM_METRICS),
@@ -85,5 +93,6 @@ export const getPageLayoutTabDataSeeds = ({
workspaceId,
universalIdentifier: v4(),
applicationId,
overrides: null,
},
];
@@ -91,6 +91,7 @@ export const getPageLayoutWidgetDataSeedsV2 = (
firstDayOfTheWeek: CalendarStartDay.MONDAY,
},
objectMetadataId: opportunityObject?.id ?? null,
overrides: null,
} satisfies SeederFlatPageLayoutWidget)
: null,
@@ -127,6 +128,7 @@ export const getPageLayoutWidgetDataSeedsV2 = (
firstDayOfTheWeek: CalendarStartDay.MONDAY,
},
objectMetadataId: companyObject?.id ?? null,
overrides: null,
} satisfies SeederFlatPageLayoutWidget)
: null,
@@ -162,6 +164,7 @@ export const getPageLayoutWidgetDataSeedsV2 = (
firstDayOfTheWeek: CalendarStartDay.MONDAY,
},
objectMetadataId: companyObject?.id ?? null,
overrides: null,
} satisfies SeederFlatPageLayoutWidget)
: null,
@@ -195,6 +198,7 @@ export const getPageLayoutWidgetDataSeedsV2 = (
firstDayOfTheWeek: CalendarStartDay.MONDAY,
},
objectMetadataId: companyObject?.id ?? null,
overrides: null,
} satisfies SeederFlatPageLayoutWidget)
: null,
@@ -231,6 +235,7 @@ export const getPageLayoutWidgetDataSeedsV2 = (
firstDayOfTheWeek: CalendarStartDay.MONDAY,
},
objectMetadataId: companyObject?.id ?? null,
overrides: null,
} satisfies SeederFlatPageLayoutWidget)
: null,
@@ -266,6 +271,7 @@ export const getPageLayoutWidgetDataSeedsV2 = (
firstDayOfTheWeek: CalendarStartDay.MONDAY,
},
objectMetadataId: personObject?.id ?? null,
overrides: null,
} satisfies SeederFlatPageLayoutWidget)
: null,
].filter(isDefined);
@@ -106,6 +106,7 @@ export const getPageLayoutWidgetDataSeeds = (
firstDayOfTheWeek: CalendarStartDay.MONDAY,
},
objectMetadataId: opportunityObject?.id ?? null,
overrides: null,
} satisfies SeederFlatPageLayoutWidget)
: null,
isDefined(rocketIdFieldId)
@@ -137,6 +138,7 @@ export const getPageLayoutWidgetDataSeeds = (
firstDayOfTheWeek: CalendarStartDay.MONDAY,
},
objectMetadataId: rocketObject?.id ?? null,
overrides: null,
} satisfies SeederFlatPageLayoutWidget)
: null,
isDefined(opportunityAmountFieldId) &&
@@ -176,6 +178,7 @@ export const getPageLayoutWidgetDataSeeds = (
firstDayOfTheWeek: CalendarStartDay.MONDAY,
},
objectMetadataId: opportunityObject?.id ?? null,
overrides: null,
} satisfies SeederFlatPageLayoutWidget)
: null,
@@ -214,6 +217,7 @@ export const getPageLayoutWidgetDataSeeds = (
firstDayOfTheWeek: CalendarStartDay.MONDAY,
},
objectMetadataId: rocketObject?.id ?? null,
overrides: null,
} satisfies SeederFlatPageLayoutWidget)
: null,
isDefined(opportunityIdFieldId)
@@ -245,6 +249,7 @@ export const getPageLayoutWidgetDataSeeds = (
firstDayOfTheWeek: CalendarStartDay.MONDAY,
},
objectMetadataId: opportunityObject?.id ?? null,
overrides: null,
} satisfies SeederFlatPageLayoutWidget)
: null,
@@ -278,6 +283,7 @@ export const getPageLayoutWidgetDataSeeds = (
firstDayOfTheWeek: CalendarStartDay.MONDAY,
},
objectMetadataId: companyObject?.id ?? null,
overrides: null,
} satisfies SeederFlatPageLayoutWidget)
: null,
isDefined(companyIdFieldId) &&
@@ -320,6 +326,7 @@ export const getPageLayoutWidgetDataSeeds = (
firstDayOfTheWeek: CalendarStartDay.MONDAY,
},
objectMetadataId: companyObject?.id ?? null,
overrides: null,
} satisfies SeederFlatPageLayoutWidget)
: null,
@@ -353,6 +360,7 @@ export const getPageLayoutWidgetDataSeeds = (
firstDayOfTheWeek: CalendarStartDay.MONDAY,
},
objectMetadataId: companyObject?.id ?? null,
overrides: null,
} satisfies SeederFlatPageLayoutWidget)
: null,
isDefined(companyLinkedinLinkFieldId)
@@ -384,6 +392,7 @@ export const getPageLayoutWidgetDataSeeds = (
firstDayOfTheWeek: CalendarStartDay.MONDAY,
},
objectMetadataId: companyObject?.id ?? null,
overrides: null,
} satisfies SeederFlatPageLayoutWidget)
: null,
@@ -414,6 +423,7 @@ export const getPageLayoutWidgetDataSeeds = (
firstDayOfTheWeek: CalendarStartDay.MONDAY,
},
objectMetadataId: personObject?.id ?? null,
overrides: null,
} satisfies SeederFlatPageLayoutWidget)
: null,
isDefined(personIdFieldId) && isDefined(personCityFieldId)
@@ -450,6 +460,7 @@ export const getPageLayoutWidgetDataSeeds = (
firstDayOfTheWeek: CalendarStartDay.MONDAY,
},
objectMetadataId: personObject?.id ?? null,
overrides: null,
} satisfies SeederFlatPageLayoutWidget)
: null,
@@ -483,6 +494,7 @@ export const getPageLayoutWidgetDataSeeds = (
firstDayOfTheWeek: CalendarStartDay.MONDAY,
},
objectMetadataId: taskObject?.id ?? null,
overrides: null,
} satisfies SeederFlatPageLayoutWidget)
: null,
@@ -508,6 +520,7 @@ export const getPageLayoutWidgetDataSeeds = (
frontComponentId: '6cdf2607-4b28-40e6-8c53-cc06799ddc88',
} as const,
objectMetadataId: null,
overrides: null,
} satisfies SeederFlatPageLayoutWidget,
].filter(isDefined);
@@ -30,6 +30,7 @@ export const seedPageLayoutTabs = async ({
'workspaceId',
'universalIdentifier',
'applicationId',
'overrides',
])
.values(pageLayoutTabs)
.orIgnore()
@@ -42,6 +42,7 @@ export const seedPageLayoutWidgets = async ({
configuration: widget.configuration,
universalIdentifier: v4(),
applicationId: workspaceCustomApplicationId,
overrides: widget.overrides ?? null,
};
});
@@ -61,6 +62,7 @@ export const seedPageLayoutWidgets = async ({
'workspaceId',
'universalIdentifier',
'applicationId',
'overrides',
])
.values(pageLayoutWidgets)
.orIgnore()
@@ -73,5 +73,6 @@ export const createStandardPageLayoutTabFlatMetadata = ({
deletedAt: null,
icon,
layoutMode,
overrides: null,
};
};
@@ -108,5 +108,6 @@ export const createStandardPageLayoutWidgetFlatMetadata = ({
updatedAt: now,
deletedAt: null,
conditionalDisplay: conditionalDisplay ?? null,
overrides: null,
};
};
@@ -0,0 +1,11 @@
import { Column } from 'typeorm';
import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface';
import { type JsonbProperty } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/jsonb-property.type';
export abstract class OverridableEntity<
TOverrides = Record<string, unknown>,
> extends SyncableEntity {
@Column({ type: 'jsonb', nullable: true })
overrides: JsonbProperty<TOverrides> | null;
}
@@ -0,0 +1,161 @@
import { findPageLayoutTabs } from 'test/integration/metadata/suites/page-layout-tab/utils/find-page-layout-tabs.util';
import { updateOnePageLayoutTab } from 'test/integration/metadata/suites/page-layout-tab/utils/update-one-page-layout-tab.util';
import { findPageLayouts } from 'test/integration/metadata/suites/page-layout/utils/find-page-layouts.util';
import { PageLayoutType } from 'src/engine/metadata-modules/page-layout/enums/page-layout-type.enum';
const TAB_OVERRIDE_GQL_FIELDS = `
id
title
position
icon
isOverridden
pageLayoutId
createdAt
updatedAt
deletedAt
`;
describe('Page layout tab override behavior', () => {
let seededTabId: string;
let seededTabOriginalTitle: string;
let seededTabOriginalPosition: number;
let seededTabOriginalIcon: string | null;
let seededPageLayoutId: string;
beforeAll(async () => {
const { data: layoutsData } = await findPageLayouts({
expectToFail: false,
input: undefined,
});
const recordPageLayout = layoutsData.getPageLayouts.find(
(layout) => layout.type === PageLayoutType.RECORD_PAGE,
);
expect(recordPageLayout).toBeDefined();
seededPageLayoutId = recordPageLayout!.id;
const { data: tabsData } = await findPageLayoutTabs({
expectToFail: false,
input: { pageLayoutId: seededPageLayoutId },
gqlFields: TAB_OVERRIDE_GQL_FIELDS,
});
expect(tabsData.getPageLayoutTabs.length).toBeGreaterThanOrEqual(1);
const firstTab = tabsData.getPageLayoutTabs[0];
seededTabId = firstTab.id;
seededTabOriginalTitle = firstTab.title;
seededTabOriginalPosition = firstTab.position;
seededTabOriginalIcon = firstTab.icon ?? null;
});
afterAll(async () => {
await updateOnePageLayoutTab({
expectToFail: false,
input: {
id: seededTabId,
title: seededTabOriginalTitle,
position: seededTabOriginalPosition,
icon: seededTabOriginalIcon,
},
gqlFields: TAB_OVERRIDE_GQL_FIELDS,
});
});
it('should override a seeded tab title and return the overridden value', async () => {
const overriddenTitle = `Override Test ${Date.now()}`;
const { data } = await updateOnePageLayoutTab({
expectToFail: false,
input: {
id: seededTabId,
title: overriddenTitle,
},
gqlFields: TAB_OVERRIDE_GQL_FIELDS,
});
expect(data.updatePageLayoutTab.title).toBe(overriddenTitle);
expect(data.updatePageLayoutTab.isOverridden).toBe(true);
});
it('should return the overridden title when querying the tab', async () => {
const { data: tabsData } = await findPageLayoutTabs({
expectToFail: false,
input: { pageLayoutId: seededPageLayoutId },
gqlFields: TAB_OVERRIDE_GQL_FIELDS,
});
const tab = tabsData.getPageLayoutTabs.find(
(tabItem) => tabItem.id === seededTabId,
);
expect(tab).toBeDefined();
expect(tab!.title).not.toBe(seededTabOriginalTitle);
});
it('should implicitly restore when updating title back to original value', async () => {
const { data } = await updateOnePageLayoutTab({
expectToFail: false,
input: {
id: seededTabId,
title: seededTabOriginalTitle,
},
gqlFields: TAB_OVERRIDE_GQL_FIELDS,
});
expect(data.updatePageLayoutTab.title).toBe(seededTabOriginalTitle);
expect(data.updatePageLayoutTab.isOverridden).toBe(false);
});
it('should override position on a seeded tab', async () => {
const overriddenPosition = 999;
const { data } = await updateOnePageLayoutTab({
expectToFail: false,
input: {
id: seededTabId,
position: overriddenPosition,
},
gqlFields: TAB_OVERRIDE_GQL_FIELDS,
});
expect(data.updatePageLayoutTab.position).toBe(overriddenPosition);
await updateOnePageLayoutTab({
expectToFail: false,
input: {
id: seededTabId,
position: seededTabOriginalPosition,
},
gqlFields: TAB_OVERRIDE_GQL_FIELDS,
});
});
it('should override icon on a seeded tab', async () => {
const overriddenIcon = 'IconStar';
const { data } = await updateOnePageLayoutTab({
expectToFail: false,
input: {
id: seededTabId,
icon: overriddenIcon,
},
gqlFields: TAB_OVERRIDE_GQL_FIELDS,
});
expect(data.updatePageLayoutTab.icon).toBe(overriddenIcon);
await updateOnePageLayoutTab({
expectToFail: false,
input: {
id: seededTabId,
icon: seededTabOriginalIcon,
},
gqlFields: TAB_OVERRIDE_GQL_FIELDS,
});
});
});
@@ -33,6 +33,7 @@ export const updateOnePageLayoutTabQueryFactory = ({
input: {
title: input.title,
position: input.position,
icon: input.icon,
},
},
});
@@ -0,0 +1,172 @@
import { findPageLayoutTabs } from 'test/integration/metadata/suites/page-layout-tab/utils/find-page-layout-tabs.util';
import { findPageLayoutWidgets } from 'test/integration/metadata/suites/page-layout-widget/utils/find-page-layout-widgets.util';
import { updateOnePageLayoutWidget } from 'test/integration/metadata/suites/page-layout-widget/utils/update-one-page-layout-widget.util';
import { findPageLayouts } from 'test/integration/metadata/suites/page-layout/utils/find-page-layouts.util';
import { PageLayoutType } from 'src/engine/metadata-modules/page-layout/enums/page-layout-type.enum';
const WIDGET_OVERRIDE_GQL_FIELDS = `
id
title
type
pageLayoutTabId
conditionalDisplay
isOverridden
createdAt
updatedAt
deletedAt
`;
describe('Page layout widget override behavior', () => {
let seededWidgetId: string;
let seededWidgetOriginalTitle: string;
beforeAll(async () => {
const { data: layoutsData } = await findPageLayouts({
expectToFail: false,
input: undefined,
});
const recordPageLayout = layoutsData.getPageLayouts.find(
(layout) => layout.type === PageLayoutType.RECORD_PAGE,
);
expect(recordPageLayout).toBeDefined();
const { data: tabsData } = await findPageLayoutTabs({
expectToFail: false,
input: { pageLayoutId: recordPageLayout!.id },
});
expect(tabsData.getPageLayoutTabs.length).toBeGreaterThanOrEqual(1);
const firstTabId = tabsData.getPageLayoutTabs[0].id;
const { data: widgetsData } = await findPageLayoutWidgets({
expectToFail: false,
input: { pageLayoutTabId: firstTabId },
gqlFields: WIDGET_OVERRIDE_GQL_FIELDS,
});
expect(widgetsData.getPageLayoutWidgets.length).toBeGreaterThanOrEqual(1);
const firstWidget = widgetsData.getPageLayoutWidgets[0];
seededWidgetId = firstWidget.id;
seededWidgetOriginalTitle = firstWidget.title;
});
afterAll(async () => {
await updateOnePageLayoutWidget({
expectToFail: false,
input: {
id: seededWidgetId,
title: seededWidgetOriginalTitle,
},
gqlFields: WIDGET_OVERRIDE_GQL_FIELDS,
});
});
it('should override a seeded widget title and return the overridden value', async () => {
const overriddenTitle = `Widget Override ${Date.now()}`;
const { data } = await updateOnePageLayoutWidget({
expectToFail: false,
input: {
id: seededWidgetId,
title: overriddenTitle,
},
gqlFields: WIDGET_OVERRIDE_GQL_FIELDS,
});
expect(data.updatePageLayoutWidget.title).toBe(overriddenTitle);
expect(data.updatePageLayoutWidget.isOverridden).toBe(true);
});
it('should return the overridden title when querying the widget', async () => {
const { data: tabsData } = await findPageLayoutTabs({
expectToFail: false,
input: {
pageLayoutId: (
await findPageLayouts({
expectToFail: false,
input: undefined,
})
).data.getPageLayouts.find(
(layout) => layout.type === PageLayoutType.RECORD_PAGE,
)!.id,
},
});
const { data: widgetsData } = await findPageLayoutWidgets({
expectToFail: false,
input: { pageLayoutTabId: tabsData.getPageLayoutTabs[0].id },
gqlFields: WIDGET_OVERRIDE_GQL_FIELDS,
});
const widget = widgetsData.getPageLayoutWidgets.find(
(widgetItem) => widgetItem.id === seededWidgetId,
);
expect(widget).toBeDefined();
expect(widget!.title).not.toBe(seededWidgetOriginalTitle);
});
it('should implicitly restore when updating title back to original value', async () => {
const { data } = await updateOnePageLayoutWidget({
expectToFail: false,
input: {
id: seededWidgetId,
title: seededWidgetOriginalTitle,
},
gqlFields: WIDGET_OVERRIDE_GQL_FIELDS,
});
expect(data.updatePageLayoutWidget.title).toBe(seededWidgetOriginalTitle);
expect(data.updatePageLayoutWidget.isOverridden).toBe(false);
});
it('should override conditionalDisplay independently from title', async () => {
const overriddenTitle = `Multi Override ${Date.now()}`;
const overriddenConditionalDisplay = {
fieldMetadataId: '00000000-0000-0000-0000-000000000001',
operator: 'is',
value: 'test',
};
const { data: titleData } = await updateOnePageLayoutWidget({
expectToFail: false,
input: {
id: seededWidgetId,
title: overriddenTitle,
},
gqlFields: WIDGET_OVERRIDE_GQL_FIELDS,
});
expect(titleData.updatePageLayoutWidget.title).toBe(overriddenTitle);
const { data: conditionalData } = await updateOnePageLayoutWidget({
expectToFail: false,
input: {
id: seededWidgetId,
conditionalDisplay: overriddenConditionalDisplay,
},
gqlFields: WIDGET_OVERRIDE_GQL_FIELDS,
});
expect(conditionalData.updatePageLayoutWidget.title).toBe(overriddenTitle);
expect(
conditionalData.updatePageLayoutWidget.conditionalDisplay,
).toEqual(overriddenConditionalDisplay);
await updateOnePageLayoutWidget({
expectToFail: false,
input: {
id: seededWidgetId,
title: seededWidgetOriginalTitle,
conditionalDisplay: null,
},
gqlFields: WIDGET_OVERRIDE_GQL_FIELDS,
});
});
});
@@ -47,6 +47,7 @@ export const updateOnePageLayoutWidgetQueryFactory = ({
objectMetadataId: input.objectMetadataId,
gridPosition: input.gridPosition,
configuration: input.configuration,
conditionalDisplay: input.conditionalDisplay,
},
},
});