Add position to page layout widgets (#17643)

## Context
Introducing a new position column in PageLayoutWidget. This is because
we already have gridPosition but all widgets are not supposed to fit in
a grid (see pageLayoutTab layoutMode (GRID, VERTICAL_LIST, CANVAS...),
position will now support the different layoutMode

## Implementation
- Adding the new column (not renaming to avoid breaking changes in the
dashboards)
- Add proper validation for each layout mode
- Update standard page layout to not always use GRID but rely on the tab
layout mode

<details>

<summary>Graphql Query</summary>

```graphql
{
  "data": {
    "getPageLayouts": [
      {
        "tabs": [
          {
            "title": "Tab 1",
            "layoutMode": "GRID",
            "widgets": [
              {
                "title": "Untitled Rich Text",
                "position": {
                  "column": 0,
                  "columnSpan": 6,
                  "layoutMode": "GRID",
                  "row": 0,
                  "rowSpan": 6
                }
              },
              {
                "title": "Deals by Company",
                "position": {
                  "column": 6,
                  "columnSpan": 6,
                  "layoutMode": "GRID",
                  "row": 0,
                  "rowSpan": 6
                }
              },
              {
                "title": "Pipeline Value by Stage",
                "position": {
                  "column": 0,
                  "columnSpan": 6,
                  "layoutMode": "GRID",
                  "row": 6,
                  "rowSpan": 6
                }
              },
              {
                "title": "Revenue Timeline",
                "position": {
                  "column": 6,
                  "columnSpan": 6,
                  "layoutMode": "GRID",
                  "row": 6,
                  "rowSpan": 6
                }
              },
              {
                "title": "Opportunities by Owner",
                "position": {
                  "column": 0,
                  "columnSpan": 6,
                  "layoutMode": "GRID",
                  "row": 12,
                  "rowSpan": 6
                }
              },
              {
                "title": "Stock market (Iframe)",
                "position": {
                  "column": 6,
                  "columnSpan": 6,
                  "layoutMode": "GRID",
                  "row": 12,
                  "rowSpan": 8
                }
              },
              {
                "title": "Deals created this month",
                "position": {
                  "column": 0,
                  "columnSpan": 3,
                  "layoutMode": "GRID",
                  "row": 18,
                  "rowSpan": 2
                }
              },
              {
                "title": "Deal value created this month",
                "position": {
                  "column": 3,
                  "columnSpan": 3,
                  "layoutMode": "GRID",
                  "row": 18,
                  "rowSpan": 2
                }
              }
            ]
          }
        ]
      },
      {
        "tabs": [
          {
            "title": "Home",
            "layoutMode": "VERTICAL_LIST",
            "widgets": [
              {
                "title": "Fields",
                "position": {
                  "layoutMode": "VERTICAL_LIST",
                  "index": 0
                }
              }
            ]
          },
          {
            "title": "Timeline",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Timeline",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Tasks",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Tasks",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Notes",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Notes",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Files",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Files",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Emails",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Emails",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Calendar",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Calendar",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          }
        ]
      },
      {
        "tabs": [
          {
            "title": "Home",
            "layoutMode": "VERTICAL_LIST",
            "widgets": [
              {
                "title": "Fields",
                "position": {
                  "layoutMode": "VERTICAL_LIST",
                  "index": 0
                }
              }
            ]
          },
          {
            "title": "Timeline",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Timeline",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Tasks",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Tasks",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Notes",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Notes",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Files",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Files",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Emails",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Emails",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Calendar",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Calendar",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          }
        ]
      },
      {
        "tabs": [
          {
            "title": "Home",
            "layoutMode": "VERTICAL_LIST",
            "widgets": [
              {
                "title": "Fields",
                "position": {
                  "layoutMode": "VERTICAL_LIST",
                  "index": 0
                }
              }
            ]
          },
          {
            "title": "Timeline",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Timeline",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Tasks",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Tasks",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Notes",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Notes",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Files",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Files",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Emails",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Emails",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Calendar",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Calendar",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          }
        ]
      },
      {
        "tabs": [
          {
            "title": "Home",
            "layoutMode": "VERTICAL_LIST",
            "widgets": [
              {
                "title": "Fields",
                "position": {
                  "layoutMode": "VERTICAL_LIST",
                  "index": 0
                }
              },
              {
                "title": "Note",
                "position": {
                  "layoutMode": "VERTICAL_LIST",
                  "index": 1
                }
              }
            ]
          },
          {
            "title": "Note",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Note",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Timeline",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Timeline",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Files",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Files",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          }
        ]
      },
      {
        "tabs": [
          {
            "title": "Home",
            "layoutMode": "VERTICAL_LIST",
            "widgets": [
              {
                "title": "Fields",
                "position": {
                  "layoutMode": "VERTICAL_LIST",
                  "index": 0
                }
              },
              {
                "title": "Note",
                "position": {
                  "layoutMode": "VERTICAL_LIST",
                  "index": 1
                }
              }
            ]
          },
          {
            "title": "Note",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Note",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Timeline",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Timeline",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          },
          {
            "title": "Files",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Files",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          }
        ]
      },
      {
        "tabs": [
          {
            "title": "Flow",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Flow",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          }
        ]
      },
      {
        "tabs": [
          {
            "title": "Home",
            "layoutMode": "VERTICAL_LIST",
            "widgets": [
              {
                "title": "Fields",
                "position": {
                  "layoutMode": "VERTICAL_LIST",
                  "index": 0
                }
              }
            ]
          },
          {
            "title": "Flow",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Flow",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          }
        ]
      },
      {
        "tabs": [
          {
            "title": "Home",
            "layoutMode": "VERTICAL_LIST",
            "widgets": [
              {
                "title": "Fields",
                "position": {
                  "layoutMode": "VERTICAL_LIST",
                  "index": 0
                }
              }
            ]
          },
          {
            "title": "Flow",
            "layoutMode": "CANVAS",
            "widgets": [
              {
                "title": "Flow",
                "position": {
                  "layoutMode": "CANVAS"
                }
              }
            ]
          }
        ]
      }
    ]
  }
}
```

</details>
This commit is contained in:
Weiko
2026-02-03 16:54:01 +01:00
committed by GitHub
parent f009913cbe
commit 5c91a96e84
31 changed files with 577 additions and 28 deletions
@@ -1,4 +1,9 @@
import { PageLayoutTabLayoutMode } from 'twenty-shared/types';
import {
PageLayoutTabLayoutMode,
type PageLayoutWidgetCanvasPosition,
type PageLayoutWidgetGridPosition,
type PageLayoutWidgetVerticalListPosition,
} from 'twenty-shared/types';
import { WidgetType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-type.enum';
import { type GridPosition } from 'src/engine/metadata-modules/page-layout-widget/types/grid-position.type';
@@ -24,6 +29,43 @@ export const GRID_POSITIONS = {
},
} as const satisfies Record<string, GridPosition>;
export const GRID_LAYOUT_POSITIONS = {
FULL_WIDTH: {
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 12,
columnSpan: 12,
},
HALF_HEIGHT: {
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
RICH_TEXT: {
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 12,
column: 0,
rowSpan: 6,
columnSpan: 12,
},
} as const satisfies Record<string, PageLayoutWidgetGridPosition>;
export const VERTICAL_LIST_LAYOUT_POSITIONS = {
FIRST: {
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
index: 0,
},
} as const satisfies Record<string, PageLayoutWidgetVerticalListPosition>;
export const CANVAS_LAYOUT_POSITIONS = {
DEFAULT: {
layoutMode: PageLayoutTabLayoutMode.CANVAS,
},
} as const satisfies Record<string, PageLayoutWidgetCanvasPosition>;
export const TAB_PROPS = {
home: {
title: 'Home',
@@ -92,55 +134,66 @@ export const WIDGET_PROPS = {
title: 'Fields',
type: WidgetType.FIELDS,
gridPosition: GRID_POSITIONS.FULL_WIDTH,
position: VERTICAL_LIST_LAYOUT_POSITIONS.FIRST,
},
timeline: {
title: 'Timeline',
type: WidgetType.TIMELINE,
gridPosition: GRID_POSITIONS.HALF_HEIGHT,
position: CANVAS_LAYOUT_POSITIONS.DEFAULT,
},
tasks: {
title: 'Tasks',
type: WidgetType.TASKS,
gridPosition: GRID_POSITIONS.HALF_HEIGHT,
position: CANVAS_LAYOUT_POSITIONS.DEFAULT,
},
notes: {
title: 'Notes',
type: WidgetType.NOTES,
gridPosition: GRID_POSITIONS.HALF_HEIGHT,
position: CANVAS_LAYOUT_POSITIONS.DEFAULT,
},
files: {
title: 'Files',
type: WidgetType.FILES,
gridPosition: GRID_POSITIONS.HALF_HEIGHT,
position: CANVAS_LAYOUT_POSITIONS.DEFAULT,
},
emails: {
title: 'Emails',
type: WidgetType.EMAILS,
gridPosition: GRID_POSITIONS.HALF_HEIGHT,
position: CANVAS_LAYOUT_POSITIONS.DEFAULT,
},
calendar: {
title: 'Calendar',
type: WidgetType.CALENDAR,
gridPosition: GRID_POSITIONS.HALF_HEIGHT,
position: CANVAS_LAYOUT_POSITIONS.DEFAULT,
},
richText: {
title: 'Note',
type: WidgetType.FIELD_RICH_TEXT,
gridPosition: GRID_POSITIONS.RICH_TEXT,
position: CANVAS_LAYOUT_POSITIONS.DEFAULT,
},
workflow: {
title: 'Flow',
type: WidgetType.WORKFLOW,
gridPosition: GRID_POSITIONS.FULL_WIDTH,
position: CANVAS_LAYOUT_POSITIONS.DEFAULT,
},
workflowVersion: {
title: 'Flow',
type: WidgetType.WORKFLOW_VERSION,
gridPosition: GRID_POSITIONS.FULL_WIDTH,
position: CANVAS_LAYOUT_POSITIONS.DEFAULT,
},
workflowRun: {
title: 'Flow',
type: WidgetType.WORKFLOW_RUN,
gridPosition: GRID_POSITIONS.FULL_WIDTH,
position: CANVAS_LAYOUT_POSITIONS.DEFAULT,
},
} as const;
@@ -1,4 +1,7 @@
import { type PageLayoutTabLayoutMode } from 'twenty-shared/types';
import {
type PageLayoutTabLayoutMode,
type PageLayoutWidgetPosition,
} from 'twenty-shared/types';
import { type WidgetType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-type.enum';
import { type GridPosition } from 'src/engine/metadata-modules/page-layout-widget/types/grid-position.type';
@@ -8,6 +11,7 @@ export type StandardPageLayoutWidgetDefinition = {
title?: string;
type?: WidgetType;
gridPosition?: GridPosition;
position?: PageLayoutWidgetPosition;
};
export type StandardPageLayoutTabDefinition = {
@@ -24,6 +28,7 @@ export type StandardRecordPageWidgetDefinition = {
title: string;
type: WidgetType;
gridPosition: GridPosition;
position?: PageLayoutWidgetPosition;
};
export type StandardRecordPageTabDefinition = {
@@ -21,7 +21,10 @@ const NOTE_PAGE_TABS = {
},
richText: {
universalIdentifier: '20202020-ac04-4004-8004-a0be5a11a412',
...WIDGET_PROPS.richText,
title: WIDGET_PROPS.richText.title,
type: WIDGET_PROPS.richText.type,
gridPosition: WIDGET_PROPS.richText.gridPosition,
position: { layoutMode: TAB_PROPS.home.layoutMode, index: 1 },
},
},
},
@@ -1,14 +1,18 @@
import { type PageLayoutTabLayoutMode } from 'twenty-shared/types';
import {
type PageLayoutTabLayoutMode,
type PageLayoutWidgetPosition,
} from 'twenty-shared/types';
import { type PageLayoutType } from 'src/engine/metadata-modules/page-layout/enums/page-layout-type.enum';
import { type WidgetType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-type.enum';
import { type GridPosition } from 'src/engine/metadata-modules/page-layout-widget/types/grid-position.type';
import { type PageLayoutType } from 'src/engine/metadata-modules/page-layout/enums/page-layout-type.enum';
export type StandardPageLayoutWidgetConfig = {
universalIdentifier: string;
title?: string;
type?: WidgetType;
gridPosition?: GridPosition;
position?: PageLayoutWidgetPosition;
};
export type StandardPageLayoutTabConfig = {
@@ -21,7 +21,10 @@ const TASK_PAGE_TABS = {
},
richText: {
universalIdentifier: '20202020-ac05-4005-8005-ba5ca11a5512',
...WIDGET_PROPS.richText,
title: WIDGET_PROPS.richText.title,
type: WIDGET_PROPS.richText.type,
gridPosition: WIDGET_PROPS.richText.gridPosition,
position: { layoutMode: TAB_PROPS.home.layoutMode, index: 1 },
},
},
},
@@ -88,6 +88,7 @@ const computeRecordPageWidgets = ({
title: widget.title,
type: widget.type,
gridPosition: widget.gridPosition,
position: widget.position ?? null,
configuration: {
configurationType: WidgetConfigurationType.FIELDS,
},
@@ -1,4 +1,5 @@
import { CalendarStartDay } from 'twenty-shared/constants';
import { PageLayoutTabLayoutMode } from 'twenty-shared/types';
import { v4 } from 'uuid';
import { AggregateOperations } from 'src/engine/api/graphql/graphql-query-runner/constants/aggregate-operations.constant';
@@ -65,6 +66,13 @@ const createWelcomeRichText = ({
title: 'Untitled Rich Text',
type: WidgetType.STANDALONE_RICH_TEXT,
gridPosition: { row: 0, column: 0, rowSpan: 6, columnSpan: 6 },
position: {
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 6,
columnSpan: 6,
},
configuration: {
configurationType:
WidgetConfigurationType.STANDALONE_RICH_TEXT as const,
@@ -232,6 +240,13 @@ const createDealsByCompany = ({
title: 'Deals by Company',
type: WidgetType.GRAPH,
gridPosition: { row: 0, column: 6, rowSpan: 6, columnSpan: 6 },
position: {
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 6,
rowSpan: 6,
columnSpan: 6,
},
configuration: {
configurationType: WidgetConfigurationType.PIE_CHART,
groupByFieldMetadataId: opportunityFields.company.id,
@@ -270,6 +285,13 @@ const createPipelineValueByStage = ({
title: 'Pipeline Value by Stage',
type: WidgetType.GRAPH,
gridPosition: { row: 6, column: 0, rowSpan: 6, columnSpan: 6 },
position: {
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 6,
column: 0,
rowSpan: 6,
columnSpan: 6,
},
configuration: {
configurationType: WidgetConfigurationType.BAR_CHART,
aggregateFieldMetadataId: opportunityFields.amount.id,
@@ -314,6 +336,13 @@ const createRevenueTimeline = ({
title: 'Revenue Timeline',
type: WidgetType.GRAPH,
gridPosition: { row: 6, column: 6, rowSpan: 6, columnSpan: 6 },
position: {
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 6,
column: 6,
rowSpan: 6,
columnSpan: 6,
},
configuration: {
configurationType: WidgetConfigurationType.LINE_CHART,
aggregateFieldMetadataId: opportunityFields.amount.id,
@@ -353,6 +382,13 @@ const createOpportunitiesByOwner = ({
title: 'Opportunities by Owner',
type: WidgetType.GRAPH,
gridPosition: { row: 12, column: 0, rowSpan: 6, columnSpan: 6 },
position: {
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 12,
column: 0,
rowSpan: 6,
columnSpan: 6,
},
configuration: {
configurationType: WidgetConfigurationType.BAR_CHART,
aggregateFieldMetadataId: opportunityFields.id.id,
@@ -394,6 +430,13 @@ const createStockMarketIframe = ({
title: 'Stock market (Iframe)',
type: WidgetType.IFRAME,
gridPosition: { row: 12, column: 6, rowSpan: 8, columnSpan: 6 },
position: {
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 12,
column: 6,
rowSpan: 8,
columnSpan: 6,
},
configuration: {
configurationType: WidgetConfigurationType.IFRAME as const,
url: 'https://www.tradingview.com/embed-widget/hotlists/?locale=en',
@@ -421,6 +464,13 @@ const createDealsCreatedThisMonth = ({
title: 'Deals created this month',
type: WidgetType.GRAPH,
gridPosition: { row: 18, column: 0, rowSpan: 2, columnSpan: 3 },
position: {
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 18,
column: 0,
rowSpan: 2,
columnSpan: 3,
},
configuration: {
configurationType: WidgetConfigurationType.AGGREGATE_CHART,
aggregateFieldMetadataId: opportunityFields.id.id,
@@ -464,6 +514,13 @@ const createDealValueCreatedThisMonth = ({
title: 'Deal value created this month',
type: WidgetType.GRAPH,
gridPosition: { row: 18, column: 3, rowSpan: 2, columnSpan: 3 },
position: {
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 18,
column: 3,
rowSpan: 2,
columnSpan: 3,
},
configuration: {
configurationType: WidgetConfigurationType.AGGREGATE_CHART,
aggregateFieldMetadataId: opportunityFields.amount.id,
@@ -1,3 +1,4 @@
import { type PageLayoutWidgetPosition } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { type FlatPageLayoutWidget } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget.type';
@@ -19,6 +20,7 @@ export type CreateStandardPageLayoutWidgetContext = {
title: string;
type: WidgetType;
gridPosition: GridPosition;
position: PageLayoutWidgetPosition | null;
configuration: AllPageLayoutWidgetConfiguration;
objectMetadataId: string | null;
};
@@ -40,6 +42,7 @@ export const createStandardPageLayoutWidgetFlatMetadata = ({
title,
type,
gridPosition,
position,
configuration,
objectMetadataId,
},
@@ -73,6 +76,7 @@ export const createStandardPageLayoutWidgetFlatMetadata = ({
title,
type,
gridPosition,
position,
configuration,
objectMetadataId,
createdAt: now,
@@ -2,10 +2,15 @@ import { Injectable } from '@nestjs/common';
import { msg, t } from '@lingui/core/macro';
import { ALL_METADATA_NAME } from 'twenty-shared/metadata';
import {
PageLayoutTabLayoutMode,
PageLayoutWidgetPosition,
} from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
import { type FlatPageLayoutTab } from 'src/engine/metadata-modules/flat-page-layout-tab/types/flat-page-layout-tab.type';
import { FlatPageLayoutWidgetTypeValidatorService } from 'src/engine/metadata-modules/flat-page-layout-widget/services/flat-page-layout-widget-type-validator.service';
import { PageLayoutTabExceptionCode } from 'src/engine/metadata-modules/page-layout-tab/exceptions/page-layout-tab.exception';
import { GraphType } from 'src/engine/metadata-modules/page-layout-widget/enums/graph-type.enum';
@@ -13,6 +18,8 @@ import { WidgetType } from 'src/engine/metadata-modules/page-layout-widget/enums
import { PageLayoutWidgetExceptionCode } from 'src/engine/metadata-modules/page-layout-widget/exceptions/page-layout-widget.exception';
import { AllPageLayoutWidgetConfiguration } from 'src/engine/metadata-modules/page-layout-widget/types/all-page-layout-widget-configuration.type';
import { GridPosition } from 'src/engine/metadata-modules/page-layout-widget/types/grid-position.type';
import { validatePageLayoutWidgetGridPosition } from 'src/engine/metadata-modules/page-layout-widget/utils/validate-page-layout-widget-grid-position.util';
import { validatePageLayoutWidgetVerticalListPosition } from 'src/engine/metadata-modules/page-layout-widget/utils/validate-page-layout-widget-vertical-list-position.util';
import { validateWidgetGridPosition } from 'src/engine/metadata-modules/page-layout-widget/utils/validate-widget-grid-position.util';
import {
FailedFlatEntityValidation,
@@ -80,6 +87,12 @@ export class FlatPageLayoutWidgetValidatorService {
pageLayoutTabId: updatedFlatPageLayoutWidget.pageLayoutTabId,
};
const referencedPageLayoutTab = findFlatEntityByIdInFlatEntityMaps({
flatEntityId: updatedFlatPageLayoutWidget.pageLayoutTabId,
flatEntityMaps:
optimisticFlatEntityMapsAndRelatedFlatEntityMaps.flatPageLayoutTabMaps,
});
const gridPositionErrors = this.validateGridPosition({
gridPosition: updatedFlatPageLayoutWidget.gridPosition,
widgetTitle: updatedFlatPageLayoutWidget.title,
@@ -87,6 +100,14 @@ export class FlatPageLayoutWidgetValidatorService {
validationResult.errors.push(...gridPositionErrors);
const positionErrors = this.validatePosition({
position: updatedFlatPageLayoutWidget.position,
pageLayoutTab: referencedPageLayoutTab,
widgetTitle: updatedFlatPageLayoutWidget.title,
});
validationResult.errors.push(...positionErrors);
const featureFlagErrors = this.validateFeatureFlags({
type: updatedFlatPageLayoutWidget.type,
configuration: updatedFlatPageLayoutWidget.configuration,
@@ -209,6 +230,14 @@ export class FlatPageLayoutWidgetValidatorService {
validationResult.errors.push(...gridPositionErrors);
const positionErrors = this.validatePosition({
position: flatPageLayoutWidgetToValidate.position,
pageLayoutTab: referencedPageLayoutTab,
widgetTitle: flatPageLayoutWidgetToValidate.title,
});
validationResult.errors.push(...positionErrors);
const featureFlagErrors = this.validateFeatureFlags({
type: flatPageLayoutWidgetToValidate.type,
configuration: flatPageLayoutWidgetToValidate.configuration,
@@ -295,4 +324,54 @@ export class FlatPageLayoutWidgetValidatorService {
return [];
}
private validatePosition({
position,
pageLayoutTab,
widgetTitle,
}: {
position: PageLayoutWidgetPosition | null | undefined;
pageLayoutTab: FlatPageLayoutTab | undefined;
widgetTitle: string;
}): FlatEntityValidationError[] {
if (!isDefined(position)) {
return [];
}
const errors: FlatEntityValidationError[] = [];
if (
isDefined(pageLayoutTab) &&
position.layoutMode !== pageLayoutTab.layoutMode
) {
const layoutMode = position.layoutMode;
const tabLayoutMode = pageLayoutTab.layoutMode;
errors.push({
code: PageLayoutWidgetExceptionCode.INVALID_PAGE_LAYOUT_WIDGET_DATA,
message: t`Position layoutMode "${layoutMode}" does not match tab layoutMode "${tabLayoutMode}"`,
userFriendlyMessage: msg`Widget position type must match the tab layout mode`,
});
}
switch (position.layoutMode) {
case PageLayoutTabLayoutMode.GRID:
errors.push(
...validatePageLayoutWidgetGridPosition(position, widgetTitle),
);
break;
case PageLayoutTabLayoutMode.VERTICAL_LIST:
errors.push(
...validatePageLayoutWidgetVerticalListPosition(
position,
widgetTitle,
),
);
break;
case PageLayoutTabLayoutMode.CANVAS:
break;
}
return errors;
}
}