diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index d31eb15cc7..8860029849 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -149,12 +149,12 @@ export type AggregateChartConfiguration = { __typename?: 'AggregateChartConfiguration'; aggregateFieldMetadataId: Scalars['UUID']; aggregateOperation: AggregateOperations; + configurationType: WidgetConfigurationType; description?: Maybe; displayDataLabel?: Maybe; filter?: Maybe; firstDayOfTheWeek?: Maybe; format?: Maybe; - graphType: GraphType; label?: Maybe; prefix?: Maybe; ratioAggregateConfig?: Maybe; @@ -351,14 +351,15 @@ export type BarChartConfiguration = { aggregateOperation: AggregateOperations; axisNameDisplay?: Maybe; color?: Maybe; + configurationType: WidgetConfigurationType; description?: Maybe; displayDataLabel?: Maybe; displayLegend?: Maybe; filter?: Maybe; firstDayOfTheWeek?: Maybe; - graphType: GraphType; groupMode?: Maybe; isCumulative?: Maybe; + layout: BarChartLayout; omitNullValues?: Maybe; primaryAxisDateGranularity?: Maybe; primaryAxisGroupByFieldMetadataId: Scalars['UUID']; @@ -379,6 +380,12 @@ export enum BarChartGroupMode { STACKED = 'STACKED' } +/** Layout orientation for bar charts */ +export enum BarChartLayout { + HORIZONTAL = 'HORIZONTAL', + VERTICAL = 'VERTICAL' +} + export type Billing = { __typename?: 'Billing'; billingUrl?: Maybe; @@ -899,12 +906,12 @@ export type CreatePageLayoutTabInput = { }; export type CreatePageLayoutWidgetInput = { - configuration?: InputMaybe; + configuration: Scalars['JSON']; gridPosition: GridPositionInput; objectMetadataId?: InputMaybe; pageLayoutTabId: Scalars['UUID']; title: Scalars['String']; - type?: InputMaybe; + type: WidgetType; }; export type CreateRemoteServerInput = { @@ -1476,11 +1483,11 @@ export type GaugeChartConfiguration = { aggregateFieldMetadataId: Scalars['UUID']; aggregateOperation: AggregateOperations; color?: Maybe; + configurationType: WidgetConfigurationType; description?: Maybe; displayDataLabel?: Maybe; filter?: Maybe; firstDayOfTheWeek?: Maybe; - graphType: GraphType; timezone?: Maybe; }; @@ -1519,16 +1526,6 @@ export enum GraphOrderBy { VALUE_DESC = 'VALUE_DESC' } -/** Type of graph widget */ -export enum GraphType { - AGGREGATE = 'AGGREGATE', - GAUGE = 'GAUGE', - HORIZONTAL_BAR = 'HORIZONTAL_BAR', - LINE = 'LINE', - PIE = 'PIE', - VERTICAL_BAR = 'VERTICAL_BAR' -} - export type GridPosition = { __typename?: 'GridPosition'; column: Scalars['Float']; @@ -1567,6 +1564,7 @@ export enum IdentityProviderType { export type IframeConfiguration = { __typename?: 'IframeConfiguration'; + configurationType: WidgetConfigurationType; url?: Maybe; }; @@ -1720,12 +1718,12 @@ export type LineChartConfiguration = { aggregateOperation: AggregateOperations; axisNameDisplay?: Maybe; color?: Maybe; + configurationType: WidgetConfigurationType; description?: Maybe; displayDataLabel?: Maybe; displayLegend?: Maybe; filter?: Maybe; firstDayOfTheWeek?: Maybe; - graphType: GraphType; isCumulative?: Maybe; isStacked?: Maybe; omitNullValues?: Maybe; @@ -3126,7 +3124,7 @@ export enum PageLayoutType { export type PageLayoutWidget = { __typename?: 'PageLayoutWidget'; - configuration?: Maybe; + configuration: WidgetConfiguration; createdAt: Scalars['DateTime']; deletedAt?: Maybe; gridPosition: GridPosition; @@ -3177,13 +3175,13 @@ export type PieChartConfiguration = { aggregateFieldMetadataId: Scalars['UUID']; aggregateOperation: AggregateOperations; color?: Maybe; + configurationType: WidgetConfigurationType; dateGranularity?: Maybe; description?: Maybe; displayDataLabel?: Maybe; displayLegend?: Maybe; filter?: Maybe; firstDayOfTheWeek?: Maybe; - graphType: GraphType; groupByFieldMetadataId: Scalars['UUID']; groupBySubFieldName?: Maybe; hideEmptyCategory?: Maybe; @@ -3729,11 +3727,6 @@ export type RatioAggregateConfig = { optionValue: Scalars['String']; }; -export type RatioAggregateConfigInput = { - fieldMetadataId: Scalars['UUID']; - optionValue: Scalars['String']; -}; - export type Relation = { __typename?: 'Relation'; sourceFieldMetadata: Field; @@ -4039,6 +4032,7 @@ export type SignedFile = { export type StandaloneRichTextConfiguration = { __typename?: 'StandaloneRichTextConfiguration'; body: RichTextV2Body; + configurationType: WidgetConfigurationType; }; export type StandardOverrides = { @@ -4797,6 +4791,29 @@ export type Webhook = { export type WidgetConfiguration = AggregateChartConfiguration | BarChartConfiguration | GaugeChartConfiguration | IframeConfiguration | LineChartConfiguration | PieChartConfiguration | StandaloneRichTextConfiguration; +export enum WidgetConfigurationType { + AGGREGATE_CHART = 'AGGREGATE_CHART', + BAR_CHART = 'BAR_CHART', + CALENDAR = 'CALENDAR', + EMAILS = 'EMAILS', + FIELD = 'FIELD', + FIELDS = 'FIELDS', + FIELD_RICH_TEXT = 'FIELD_RICH_TEXT', + FILES = 'FILES', + GAUGE_CHART = 'GAUGE_CHART', + IFRAME = 'IFRAME', + LINE_CHART = 'LINE_CHART', + NOTES = 'NOTES', + PIE_CHART = 'PIE_CHART', + STANDALONE_RICH_TEXT = 'STANDALONE_RICH_TEXT', + TASKS = 'TASKS', + TIMELINE = 'TIMELINE', + VIEW = 'VIEW', + WORKFLOW = 'WORKFLOW', + WORKFLOW_RUN = 'WORKFLOW_RUN', + WORKFLOW_VERSION = 'WORKFLOW_VERSION' +} + export enum WidgetType { CALENDAR = 'CALENDAR', EMAILS = 'EMAILS', diff --git a/packages/twenty-front/src/generated/graphql.ts b/packages/twenty-front/src/generated/graphql.ts index 81b5c3ce47..3cd326850d 100644 --- a/packages/twenty-front/src/generated/graphql.ts +++ b/packages/twenty-front/src/generated/graphql.ts @@ -149,12 +149,12 @@ export type AggregateChartConfiguration = { __typename?: 'AggregateChartConfiguration'; aggregateFieldMetadataId: Scalars['UUID']; aggregateOperation: AggregateOperations; + configurationType: WidgetConfigurationType; description?: Maybe; displayDataLabel?: Maybe; filter?: Maybe; firstDayOfTheWeek?: Maybe; format?: Maybe; - graphType: GraphType; label?: Maybe; prefix?: Maybe; ratioAggregateConfig?: Maybe; @@ -351,14 +351,15 @@ export type BarChartConfiguration = { aggregateOperation: AggregateOperations; axisNameDisplay?: Maybe; color?: Maybe; + configurationType: WidgetConfigurationType; description?: Maybe; displayDataLabel?: Maybe; displayLegend?: Maybe; filter?: Maybe; firstDayOfTheWeek?: Maybe; - graphType: GraphType; groupMode?: Maybe; isCumulative?: Maybe; + layout: BarChartLayout; omitNullValues?: Maybe; primaryAxisDateGranularity?: Maybe; primaryAxisGroupByFieldMetadataId: Scalars['UUID']; @@ -379,6 +380,12 @@ export enum BarChartGroupMode { STACKED = 'STACKED' } +/** Layout orientation for bar charts */ +export enum BarChartLayout { + HORIZONTAL = 'HORIZONTAL', + VERTICAL = 'VERTICAL' +} + export type Billing = { __typename?: 'Billing'; billingUrl?: Maybe; @@ -890,12 +897,12 @@ export type CreatePageLayoutTabInput = { }; export type CreatePageLayoutWidgetInput = { - configuration?: InputMaybe; + configuration: Scalars['JSON']; gridPosition: GridPositionInput; objectMetadataId?: InputMaybe; pageLayoutTabId: Scalars['UUID']; title: Scalars['String']; - type?: InputMaybe; + type: WidgetType; }; export type CreateRoleInput = { @@ -1452,11 +1459,11 @@ export type GaugeChartConfiguration = { aggregateFieldMetadataId: Scalars['UUID']; aggregateOperation: AggregateOperations; color?: Maybe; + configurationType: WidgetConfigurationType; description?: Maybe; displayDataLabel?: Maybe; filter?: Maybe; firstDayOfTheWeek?: Maybe; - graphType: GraphType; timezone?: Maybe; }; @@ -1495,16 +1502,6 @@ export enum GraphOrderBy { VALUE_DESC = 'VALUE_DESC' } -/** Type of graph widget */ -export enum GraphType { - AGGREGATE = 'AGGREGATE', - GAUGE = 'GAUGE', - HORIZONTAL_BAR = 'HORIZONTAL_BAR', - LINE = 'LINE', - PIE = 'PIE', - VERTICAL_BAR = 'VERTICAL_BAR' -} - export type GridPosition = { __typename?: 'GridPosition'; column: Scalars['Float']; @@ -1543,6 +1540,7 @@ export enum IdentityProviderType { export type IframeConfiguration = { __typename?: 'IframeConfiguration'; + configurationType: WidgetConfigurationType; url?: Maybe; }; @@ -1696,12 +1694,12 @@ export type LineChartConfiguration = { aggregateOperation: AggregateOperations; axisNameDisplay?: Maybe; color?: Maybe; + configurationType: WidgetConfigurationType; description?: Maybe; displayDataLabel?: Maybe; displayLegend?: Maybe; filter?: Maybe; firstDayOfTheWeek?: Maybe; - graphType: GraphType; isCumulative?: Maybe; isStacked?: Maybe; omitNullValues?: Maybe; @@ -3047,7 +3045,7 @@ export enum PageLayoutType { export type PageLayoutWidget = { __typename?: 'PageLayoutWidget'; - configuration?: Maybe; + configuration: WidgetConfiguration; createdAt: Scalars['DateTime']; deletedAt?: Maybe; gridPosition: GridPosition; @@ -3098,13 +3096,13 @@ export type PieChartConfiguration = { aggregateFieldMetadataId: Scalars['UUID']; aggregateOperation: AggregateOperations; color?: Maybe; + configurationType: WidgetConfigurationType; dateGranularity?: Maybe; description?: Maybe; displayDataLabel?: Maybe; displayLegend?: Maybe; filter?: Maybe; firstDayOfTheWeek?: Maybe; - graphType: GraphType; groupByFieldMetadataId: Scalars['UUID']; groupBySubFieldName?: Maybe; hideEmptyCategory?: Maybe; @@ -3580,11 +3578,6 @@ export type RatioAggregateConfig = { optionValue: Scalars['String']; }; -export type RatioAggregateConfigInput = { - fieldMetadataId: Scalars['UUID']; - optionValue: Scalars['String']; -}; - export type Relation = { __typename?: 'Relation'; sourceFieldMetadata: Field; @@ -3876,6 +3869,7 @@ export type SignedFile = { export type StandaloneRichTextConfiguration = { __typename?: 'StandaloneRichTextConfiguration'; body: RichTextV2Body; + configurationType: WidgetConfigurationType; }; export type StandardOverrides = { @@ -4616,6 +4610,29 @@ export type Webhook = { export type WidgetConfiguration = AggregateChartConfiguration | BarChartConfiguration | GaugeChartConfiguration | IframeConfiguration | LineChartConfiguration | PieChartConfiguration | StandaloneRichTextConfiguration; +export enum WidgetConfigurationType { + AGGREGATE_CHART = 'AGGREGATE_CHART', + BAR_CHART = 'BAR_CHART', + CALENDAR = 'CALENDAR', + EMAILS = 'EMAILS', + FIELD = 'FIELD', + FIELDS = 'FIELDS', + FIELD_RICH_TEXT = 'FIELD_RICH_TEXT', + FILES = 'FILES', + GAUGE_CHART = 'GAUGE_CHART', + IFRAME = 'IFRAME', + LINE_CHART = 'LINE_CHART', + NOTES = 'NOTES', + PIE_CHART = 'PIE_CHART', + STANDALONE_RICH_TEXT = 'STANDALONE_RICH_TEXT', + TASKS = 'TASKS', + TIMELINE = 'TIMELINE', + VIEW = 'VIEW', + WORKFLOW = 'WORKFLOW', + WORKFLOW_RUN = 'WORKFLOW_RUN', + WORKFLOW_VERSION = 'WORKFLOW_VERSION' +} + export enum WidgetType { CALENDAR = 'CALENDAR', EMAILS = 'EMAILS', @@ -4887,7 +4904,7 @@ export type SearchQueryVariables = Exact<{ export type SearchQuery = { __typename?: 'Query', search: { __typename?: 'SearchResultConnection', edges: Array<{ __typename?: 'SearchResultEdge', cursor: string, node: { __typename?: 'SearchRecord', recordId: any, objectNameSingular: string, label: string, imageUrl?: string | null, tsRankCD: number, tsRank: number } }>, pageInfo: { __typename?: 'SearchResultPageInfo', hasNextPage: boolean, endCursor?: string | null } } }; -export type PageLayoutWidgetFragmentFragment = { __typename?: 'PageLayoutWidget', id: any, title: string, type: WidgetType, objectMetadataId?: any | null, createdAt: string, updatedAt: string, deletedAt?: string | null, pageLayoutTabId: any, gridPosition: { __typename?: 'GridPosition', column: number, columnSpan: number, row: number, rowSpan: number }, configuration?: { __typename?: 'AggregateChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, label?: string | null, displayDataLabel?: boolean | null, format?: string | null, description?: string | null, filter?: any | null, prefix?: string | null, suffix?: string | null, timezone?: string | null, firstDayOfTheWeek?: number | null, ratioAggregateConfig?: { __typename?: 'RatioAggregateConfig', fieldMetadataId: any, optionValue: string } | null } | { __typename?: 'BarChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, primaryAxisGroupByFieldMetadataId: any, primaryAxisGroupBySubFieldName?: string | null, primaryAxisDateGranularity?: ObjectRecordGroupByDateGranularity | null, primaryAxisOrderBy?: GraphOrderBy | null, secondaryAxisGroupByFieldMetadataId?: any | null, secondaryAxisGroupBySubFieldName?: string | null, secondaryAxisGroupByDateGranularity?: ObjectRecordGroupByDateGranularity | null, secondaryAxisOrderBy?: GraphOrderBy | null, omitNullValues?: boolean | null, axisNameDisplay?: AxisNameDisplay | null, displayDataLabel?: boolean | null, displayLegend?: boolean | null, rangeMin?: number | null, rangeMax?: number | null, color?: string | null, description?: string | null, filter?: any | null, groupMode?: BarChartGroupMode | null, isCumulative?: boolean | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'GaugeChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, displayDataLabel?: boolean | null, color?: string | null, description?: string | null, filter?: any | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'IframeConfiguration', url?: string | null } | { __typename?: 'LineChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, primaryAxisGroupByFieldMetadataId: any, primaryAxisGroupBySubFieldName?: string | null, primaryAxisDateGranularity?: ObjectRecordGroupByDateGranularity | null, primaryAxisOrderBy?: GraphOrderBy | null, secondaryAxisGroupByFieldMetadataId?: any | null, secondaryAxisGroupBySubFieldName?: string | null, secondaryAxisGroupByDateGranularity?: ObjectRecordGroupByDateGranularity | null, secondaryAxisOrderBy?: GraphOrderBy | null, omitNullValues?: boolean | null, axisNameDisplay?: AxisNameDisplay | null, displayDataLabel?: boolean | null, displayLegend?: boolean | null, rangeMin?: number | null, rangeMax?: number | null, color?: string | null, description?: string | null, filter?: any | null, isStacked?: boolean | null, isCumulative?: boolean | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'PieChartConfiguration', graphType: GraphType, groupByFieldMetadataId: any, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, groupBySubFieldName?: string | null, dateGranularity?: ObjectRecordGroupByDateGranularity | null, orderBy?: GraphOrderBy | null, displayDataLabel?: boolean | null, showCenterMetric?: boolean | null, displayLegend?: boolean | null, color?: string | null, description?: string | null, filter?: any | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'StandaloneRichTextConfiguration', body: { __typename?: 'RichTextV2Body', blocknote?: string | null, markdown?: string | null } } | null }; +export type PageLayoutWidgetFragmentFragment = { __typename?: 'PageLayoutWidget', id: any, title: string, type: WidgetType, objectMetadataId?: any | null, createdAt: string, updatedAt: string, deletedAt?: string | null, pageLayoutTabId: any, gridPosition: { __typename?: 'GridPosition', column: number, columnSpan: number, row: number, rowSpan: number }, configuration: { __typename?: 'AggregateChartConfiguration', configurationType: WidgetConfigurationType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, label?: string | null, displayDataLabel?: boolean | null, format?: string | null, description?: string | null, filter?: any | null, prefix?: string | null, suffix?: string | null, timezone?: string | null, firstDayOfTheWeek?: number | null, ratioAggregateConfig?: { __typename?: 'RatioAggregateConfig', fieldMetadataId: any, optionValue: string } | null } | { __typename?: 'BarChartConfiguration', configurationType: WidgetConfigurationType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, primaryAxisGroupByFieldMetadataId: any, primaryAxisGroupBySubFieldName?: string | null, primaryAxisDateGranularity?: ObjectRecordGroupByDateGranularity | null, primaryAxisOrderBy?: GraphOrderBy | null, secondaryAxisGroupByFieldMetadataId?: any | null, secondaryAxisGroupBySubFieldName?: string | null, secondaryAxisGroupByDateGranularity?: ObjectRecordGroupByDateGranularity | null, secondaryAxisOrderBy?: GraphOrderBy | null, omitNullValues?: boolean | null, axisNameDisplay?: AxisNameDisplay | null, displayDataLabel?: boolean | null, displayLegend?: boolean | null, rangeMin?: number | null, rangeMax?: number | null, color?: string | null, description?: string | null, filter?: any | null, groupMode?: BarChartGroupMode | null, layout: BarChartLayout, isCumulative?: boolean | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'GaugeChartConfiguration', configurationType: WidgetConfigurationType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, displayDataLabel?: boolean | null, color?: string | null, description?: string | null, filter?: any | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'IframeConfiguration', configurationType: WidgetConfigurationType, url?: string | null } | { __typename?: 'LineChartConfiguration', configurationType: WidgetConfigurationType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, primaryAxisGroupByFieldMetadataId: any, primaryAxisGroupBySubFieldName?: string | null, primaryAxisDateGranularity?: ObjectRecordGroupByDateGranularity | null, primaryAxisOrderBy?: GraphOrderBy | null, secondaryAxisGroupByFieldMetadataId?: any | null, secondaryAxisGroupBySubFieldName?: string | null, secondaryAxisGroupByDateGranularity?: ObjectRecordGroupByDateGranularity | null, secondaryAxisOrderBy?: GraphOrderBy | null, omitNullValues?: boolean | null, axisNameDisplay?: AxisNameDisplay | null, displayDataLabel?: boolean | null, displayLegend?: boolean | null, rangeMin?: number | null, rangeMax?: number | null, color?: string | null, description?: string | null, filter?: any | null, isStacked?: boolean | null, isCumulative?: boolean | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'PieChartConfiguration', configurationType: WidgetConfigurationType, groupByFieldMetadataId: any, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, groupBySubFieldName?: string | null, dateGranularity?: ObjectRecordGroupByDateGranularity | null, orderBy?: GraphOrderBy | null, displayDataLabel?: boolean | null, showCenterMetric?: boolean | null, displayLegend?: boolean | null, color?: string | null, description?: string | null, filter?: any | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'StandaloneRichTextConfiguration', configurationType: WidgetConfigurationType, body: { __typename?: 'RichTextV2Body', blocknote?: string | null, markdown?: string | null } } }; export type UpdatePageLayoutWithTabsAndWidgetsMutationVariables = Exact<{ id: Scalars['String']; @@ -4895,7 +4912,7 @@ export type UpdatePageLayoutWithTabsAndWidgetsMutationVariables = Exact<{ }>; -export type UpdatePageLayoutWithTabsAndWidgetsMutation = { __typename?: 'Mutation', updatePageLayoutWithTabsAndWidgets: { __typename?: 'PageLayout', id: any, name: string, type: PageLayoutType, objectMetadataId?: any | null, createdAt: string, updatedAt: string, deletedAt?: string | null, tabs?: Array<{ __typename?: 'PageLayoutTab', id: any, title: string, position: number, pageLayoutId: any, createdAt: string, updatedAt: string, widgets?: Array<{ __typename?: 'PageLayoutWidget', id: any, title: string, type: WidgetType, objectMetadataId?: any | null, createdAt: string, updatedAt: string, deletedAt?: string | null, pageLayoutTabId: any, gridPosition: { __typename?: 'GridPosition', column: number, columnSpan: number, row: number, rowSpan: number }, configuration?: { __typename?: 'AggregateChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, label?: string | null, displayDataLabel?: boolean | null, format?: string | null, description?: string | null, filter?: any | null, prefix?: string | null, suffix?: string | null, timezone?: string | null, firstDayOfTheWeek?: number | null, ratioAggregateConfig?: { __typename?: 'RatioAggregateConfig', fieldMetadataId: any, optionValue: string } | null } | { __typename?: 'BarChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, primaryAxisGroupByFieldMetadataId: any, primaryAxisGroupBySubFieldName?: string | null, primaryAxisDateGranularity?: ObjectRecordGroupByDateGranularity | null, primaryAxisOrderBy?: GraphOrderBy | null, secondaryAxisGroupByFieldMetadataId?: any | null, secondaryAxisGroupBySubFieldName?: string | null, secondaryAxisGroupByDateGranularity?: ObjectRecordGroupByDateGranularity | null, secondaryAxisOrderBy?: GraphOrderBy | null, omitNullValues?: boolean | null, axisNameDisplay?: AxisNameDisplay | null, displayDataLabel?: boolean | null, displayLegend?: boolean | null, rangeMin?: number | null, rangeMax?: number | null, color?: string | null, description?: string | null, filter?: any | null, groupMode?: BarChartGroupMode | null, isCumulative?: boolean | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'GaugeChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, displayDataLabel?: boolean | null, color?: string | null, description?: string | null, filter?: any | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'IframeConfiguration', url?: string | null } | { __typename?: 'LineChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, primaryAxisGroupByFieldMetadataId: any, primaryAxisGroupBySubFieldName?: string | null, primaryAxisDateGranularity?: ObjectRecordGroupByDateGranularity | null, primaryAxisOrderBy?: GraphOrderBy | null, secondaryAxisGroupByFieldMetadataId?: any | null, secondaryAxisGroupBySubFieldName?: string | null, secondaryAxisGroupByDateGranularity?: ObjectRecordGroupByDateGranularity | null, secondaryAxisOrderBy?: GraphOrderBy | null, omitNullValues?: boolean | null, axisNameDisplay?: AxisNameDisplay | null, displayDataLabel?: boolean | null, displayLegend?: boolean | null, rangeMin?: number | null, rangeMax?: number | null, color?: string | null, description?: string | null, filter?: any | null, isStacked?: boolean | null, isCumulative?: boolean | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'PieChartConfiguration', graphType: GraphType, groupByFieldMetadataId: any, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, groupBySubFieldName?: string | null, dateGranularity?: ObjectRecordGroupByDateGranularity | null, orderBy?: GraphOrderBy | null, displayDataLabel?: boolean | null, showCenterMetric?: boolean | null, displayLegend?: boolean | null, color?: string | null, description?: string | null, filter?: any | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'StandaloneRichTextConfiguration', body: { __typename?: 'RichTextV2Body', blocknote?: string | null, markdown?: string | null } } | null }> | null }> | null } }; +export type UpdatePageLayoutWithTabsAndWidgetsMutation = { __typename?: 'Mutation', updatePageLayoutWithTabsAndWidgets: { __typename?: 'PageLayout', id: any, name: string, type: PageLayoutType, objectMetadataId?: any | null, createdAt: string, updatedAt: string, deletedAt?: string | null, tabs?: Array<{ __typename?: 'PageLayoutTab', id: any, title: string, position: number, pageLayoutId: any, createdAt: string, updatedAt: string, widgets?: Array<{ __typename?: 'PageLayoutWidget', id: any, title: string, type: WidgetType, objectMetadataId?: any | null, createdAt: string, updatedAt: string, deletedAt?: string | null, pageLayoutTabId: any, gridPosition: { __typename?: 'GridPosition', column: number, columnSpan: number, row: number, rowSpan: number }, configuration: { __typename?: 'AggregateChartConfiguration', configurationType: WidgetConfigurationType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, label?: string | null, displayDataLabel?: boolean | null, format?: string | null, description?: string | null, filter?: any | null, prefix?: string | null, suffix?: string | null, timezone?: string | null, firstDayOfTheWeek?: number | null, ratioAggregateConfig?: { __typename?: 'RatioAggregateConfig', fieldMetadataId: any, optionValue: string } | null } | { __typename?: 'BarChartConfiguration', configurationType: WidgetConfigurationType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, primaryAxisGroupByFieldMetadataId: any, primaryAxisGroupBySubFieldName?: string | null, primaryAxisDateGranularity?: ObjectRecordGroupByDateGranularity | null, primaryAxisOrderBy?: GraphOrderBy | null, secondaryAxisGroupByFieldMetadataId?: any | null, secondaryAxisGroupBySubFieldName?: string | null, secondaryAxisGroupByDateGranularity?: ObjectRecordGroupByDateGranularity | null, secondaryAxisOrderBy?: GraphOrderBy | null, omitNullValues?: boolean | null, axisNameDisplay?: AxisNameDisplay | null, displayDataLabel?: boolean | null, displayLegend?: boolean | null, rangeMin?: number | null, rangeMax?: number | null, color?: string | null, description?: string | null, filter?: any | null, groupMode?: BarChartGroupMode | null, layout: BarChartLayout, isCumulative?: boolean | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'GaugeChartConfiguration', configurationType: WidgetConfigurationType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, displayDataLabel?: boolean | null, color?: string | null, description?: string | null, filter?: any | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'IframeConfiguration', configurationType: WidgetConfigurationType, url?: string | null } | { __typename?: 'LineChartConfiguration', configurationType: WidgetConfigurationType, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, primaryAxisGroupByFieldMetadataId: any, primaryAxisGroupBySubFieldName?: string | null, primaryAxisDateGranularity?: ObjectRecordGroupByDateGranularity | null, primaryAxisOrderBy?: GraphOrderBy | null, secondaryAxisGroupByFieldMetadataId?: any | null, secondaryAxisGroupBySubFieldName?: string | null, secondaryAxisGroupByDateGranularity?: ObjectRecordGroupByDateGranularity | null, secondaryAxisOrderBy?: GraphOrderBy | null, omitNullValues?: boolean | null, axisNameDisplay?: AxisNameDisplay | null, displayDataLabel?: boolean | null, displayLegend?: boolean | null, rangeMin?: number | null, rangeMax?: number | null, color?: string | null, description?: string | null, filter?: any | null, isStacked?: boolean | null, isCumulative?: boolean | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'PieChartConfiguration', configurationType: WidgetConfigurationType, groupByFieldMetadataId: any, aggregateFieldMetadataId: any, aggregateOperation: AggregateOperations, groupBySubFieldName?: string | null, dateGranularity?: ObjectRecordGroupByDateGranularity | null, orderBy?: GraphOrderBy | null, displayDataLabel?: boolean | null, showCenterMetric?: boolean | null, displayLegend?: boolean | null, color?: string | null, description?: string | null, filter?: any | null, timezone?: string | null, firstDayOfTheWeek?: number | null } | { __typename?: 'StandaloneRichTextConfiguration', configurationType: WidgetConfigurationType, body: { __typename?: 'RichTextV2Body', blocknote?: string | null, markdown?: string | null } } }> | null }> | null } }; export type OnDbEventSubscriptionVariables = Exact<{ input: OnDbEventInput; @@ -5214,7 +5231,7 @@ export const PageLayoutWidgetFragmentFragmentDoc = gql` } configuration { ... on BarChartConfiguration { - graphType + configurationType aggregateFieldMetadataId aggregateOperation primaryAxisGroupByFieldMetadataId @@ -5235,12 +5252,13 @@ export const PageLayoutWidgetFragmentFragmentDoc = gql` description filter groupMode + layout isCumulative timezone firstDayOfTheWeek } ... on LineChartConfiguration { - graphType + configurationType aggregateFieldMetadataId aggregateOperation primaryAxisGroupByFieldMetadataId @@ -5266,7 +5284,7 @@ export const PageLayoutWidgetFragmentFragmentDoc = gql` firstDayOfTheWeek } ... on PieChartConfiguration { - graphType + configurationType groupByFieldMetadataId aggregateFieldMetadataId aggregateOperation @@ -5283,7 +5301,7 @@ export const PageLayoutWidgetFragmentFragmentDoc = gql` firstDayOfTheWeek } ... on AggregateChartConfiguration { - graphType + configurationType aggregateFieldMetadataId aggregateOperation label @@ -5301,7 +5319,7 @@ export const PageLayoutWidgetFragmentFragmentDoc = gql` } } ... on GaugeChartConfiguration { - graphType + configurationType aggregateFieldMetadataId aggregateOperation displayDataLabel @@ -5312,9 +5330,11 @@ export const PageLayoutWidgetFragmentFragmentDoc = gql` firstDayOfTheWeek } ... on IframeConfiguration { + configurationType url } ... on StandaloneRichTextConfiguration { + configurationType body { blocknote markdown diff --git a/packages/twenty-front/src/modules/command-menu/components/hooks/usePageLayoutHeaderInfo.ts b/packages/twenty-front/src/modules/command-menu/components/hooks/usePageLayoutHeaderInfo.ts index bbf43329d0..56e83a13d5 100644 --- a/packages/twenty-front/src/modules/command-menu/components/hooks/usePageLayoutHeaderInfo.ts +++ b/packages/twenty-front/src/modules/command-menu/components/hooks/usePageLayoutHeaderInfo.ts @@ -1,5 +1,6 @@ import { GRAPH_TYPE_INFORMATION } from '@/command-menu/pages/page-layout/constants/GraphTypeInformation'; -import { isChartWidget } from '@/command-menu/pages/page-layout/utils/isChartWidget'; +import { getCurrentGraphTypeFromConfig } from '@/command-menu/pages/page-layout/utils/getCurrentGraphTypeFromConfig'; +import { isWidgetConfigurationOfTypeGraph } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfTypeGraph'; import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { type PageLayoutTab } from '@/page-layout/types/PageLayoutTab'; import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget'; @@ -116,11 +117,13 @@ export const usePageLayoutHeaderInfo = ({ return null; } - if (!isChartWidget(widgetInEditMode)) { + if (!isWidgetConfigurationOfTypeGraph(widgetInEditMode.configuration)) { return null; } - const currentGraphType = widgetInEditMode.configuration.graphType; + const currentGraphType = getCurrentGraphTypeFromConfig( + widgetInEditMode.configuration, + ); const graphTypeInfo = GRAPH_TYPE_INFORMATION[currentGraphType]; const graphTypeLabel = t(graphTypeInfo.label); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/ChartLimitInfoBanner.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/ChartLimitInfoBanner.tsx index 345ab1b93d..8a9f5fe4bb 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/ChartLimitInfoBanner.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/ChartLimitInfoBanner.tsx @@ -1,11 +1,10 @@ import { getChartLimitMessage } from '@/command-menu/pages/page-layout/utils/getChartLimitMessage'; import { t } from '@lingui/core/macro'; import { SidePanelInformationBanner } from 'twenty-ui/display'; - -import { type GraphType } from '~/generated/graphql'; +import { type WidgetConfigurationType } from '~/generated/graphql'; type ChartLimitInfoBannerProps = { - graphType: GraphType; + widgetConfigurationType: WidgetConfigurationType; isPrimaryAxisDate: boolean; primaryAxisDateGranularity: Parameters< typeof getChartLimitMessage @@ -13,14 +12,14 @@ type ChartLimitInfoBannerProps = { }; export const ChartLimitInfoBanner = ({ - graphType, + widgetConfigurationType, isPrimaryAxisDate, primaryAxisDateGranularity, }: ChartLimitInfoBannerProps) => { return ( { assertChartWidgetOrThrow(widget); const configuration = widget.configuration; - const currentGraphType = configuration?.graphType; const { getChartSettingsValues } = useChartSettingsValues({ objectMetadataId: widget.objectMetadataId, @@ -81,6 +80,8 @@ export const ChartSettings = ({ widget }: { widget: PageLayoutWidget }) => { } }; + const currentGraphType = getCurrentGraphTypeFromConfig(configuration); + const chartSettings = GRAPH_TYPE_INFORMATION[currentGraphType].settings; const objectMetadataItem = objectMetadataItems.find( @@ -103,11 +104,18 @@ export const ChartSettings = ({ widget }: { widget: PageLayoutWidget }) => { .map((item) => item.id), ); - const primaryAxisFieldMetadataId = isBarOrLineChartConfiguration( + const isBarOrLineChart = + isWidgetConfigurationOfType(configuration, 'BarChartConfiguration') || + isWidgetConfigurationOfType(configuration, 'LineChartConfiguration'); + + const isPieChart = isWidgetConfigurationOfType( configuration, - ) + 'PieChartConfiguration', + ); + + const primaryAxisFieldMetadataId = isBarOrLineChart ? configuration.primaryAxisGroupByFieldMetadataId - : isPieChartConfiguration(configuration) + : isPieChart ? configuration.groupByFieldMetadataId : null; @@ -117,11 +125,9 @@ export const ChartSettings = ({ widget }: { widget: PageLayoutWidget }) => { const isPrimaryAxisDate = isFieldMetadataDateKind(primaryAxisField?.type); - const primaryAxisDateGranularity = isBarOrLineChartConfiguration( - configuration, - ) + const primaryAxisDateGranularity = isBarOrLineChart ? configuration.primaryAxisDateGranularity - : isPieChartConfiguration(configuration) + : isPieChart ? configuration.dateGranularity : null; @@ -156,7 +162,7 @@ export const ChartSettings = ({ widget }: { widget: PageLayoutWidget }) => { {shouldShowBanner && hasWidgetTooManyGroups && ( diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/ChartTypeSelectionSection.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/ChartTypeSelectionSection.tsx index 538a5020a7..7a0cef8dfe 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/ChartTypeSelectionSection.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/ChartTypeSelectionSection.tsx @@ -1,8 +1,9 @@ import { GRAPH_TYPE_INFORMATION } from '@/command-menu/pages/page-layout/constants/GraphTypeInformation'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import styled from '@emotion/styled'; -import { FeatureFlagKey, GraphType } from '~/generated-metadata/graphql'; +import { FeatureFlagKey } from '~/generated-metadata/graphql'; +import { GraphType } from '@/command-menu/pages/page-layout/types/GraphType'; import { t } from '@lingui/core/macro'; import { MenuPicker } from 'twenty-ui/navigation'; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/CommandMenuPageLayoutChartSettings.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/CommandMenuPageLayoutChartSettings.tsx index e83e15d0ba..491cb25feb 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/CommandMenuPageLayoutChartSettings.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/CommandMenuPageLayoutChartSettings.tsx @@ -1,6 +1,7 @@ import { ChartSettings } from '@/command-menu/pages/page-layout/components/ChartSettings'; import { WidgetSettingsFooter } from '@/command-menu/pages/page-layout/components/WidgetSettingsFooter'; import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/command-menu/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord'; +import { isChartWidget } from '@/command-menu/pages/page-layout/utils/isChartWidget'; import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState'; import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState'; import { WidgetComponentInstanceContext } from '@/page-layout/widgets/states/contexts/WidgetComponentInstanceContext'; @@ -31,11 +32,7 @@ export const CommandMenuPageLayoutChartSettings = () => { .flatMap((tab) => tab.widgets) .find((widget) => widget.id === pageLayoutEditingWidgetId); - if ( - !isDefined(widgetInEditMode) || - !isDefined(widgetInEditMode.configuration) || - !('graphType' in widgetInEditMode.configuration) - ) { + if (!isDefined(widgetInEditMode) || !isChartWidget(widgetInEditMode)) { return null; } diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/CommandMenuPageLayoutIframeSettings.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/CommandMenuPageLayoutIframeSettings.tsx index f5945938af..1bfb086140 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/CommandMenuPageLayoutIframeSettings.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/CommandMenuPageLayoutIframeSettings.tsx @@ -7,6 +7,7 @@ import { t } from '@lingui/core/macro'; import { isNonEmptyString, isString } from '@sniptt/guards'; import { useState } from 'react'; import { isDefined, isValidUrl } from 'twenty-shared/utils'; +import { WidgetConfigurationType } from '~/generated/graphql'; const StyledContainer = styled.div` display: flex; @@ -67,6 +68,7 @@ export const CommandMenuPageLayoutIframeSettings = () => { updatePageLayoutWidget(widgetInEditMode.id, { configuration: { __typename: 'IframeConfiguration', + configurationType: WidgetConfigurationType.IFRAME, url: isNonEmptyString(trimmedValue) ? trimmedValue : null, }, }); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/CommandMenuPageLayoutWidgetTypeSelect.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/CommandMenuPageLayoutWidgetTypeSelect.tsx index 457360b6f3..81b1af9c30 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/CommandMenuPageLayoutWidgetTypeSelect.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/CommandMenuPageLayoutWidgetTypeSelect.tsx @@ -19,7 +19,6 @@ import { IconChartPie, IconFrame, } from 'twenty-ui/display'; -import { GraphType } from '~/generated-metadata/graphql'; export const CommandMenuPageLayoutWidgetTypeSelect = () => { const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord(); @@ -49,7 +48,6 @@ export const CommandMenuPageLayoutWidgetTypeSelect = () => { if (!isDefined(pageLayoutEditingWidgetId)) { const fieldSelection = buildBarChartFieldSelection(); const newWidget = createPageLayoutGraphWidget({ - graphType: GraphType.VERTICAL_BAR, fieldSelection, }); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartAggregateOperationSelectableListItem.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartAggregateOperationSelectableListItem.tsx index a56579d93b..d44777641b 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartAggregateOperationSelectableListItem.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartAggregateOperationSelectableListItem.tsx @@ -1,7 +1,7 @@ import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/command-menu/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord'; import { useUpdateCurrentWidgetConfig } from '@/command-menu/pages/page-layout/hooks/useUpdateCurrentWidgetConfig'; import { useWidgetInEditMode } from '@/command-menu/pages/page-layout/hooks/useWidgetInEditMode'; -import { isAggregateChartConfiguration } from '@/command-menu/pages/page-layout/utils/isAggregateChartConfiguration'; +import { isWidgetConfigurationOfType } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfType'; import { type ExtendedAggregateOperations } from '@/object-record/record-table/types/ExtendedAggregateOperations'; import { convertExtendedAggregateOperationToAggregateOperation } from '@/object-record/utils/convertExtendedAggregateOperationToAggregateOperation'; import { DASHBOARD_AGGREGATE_OPERATION_RATIO } from '@/page-layout/widgets/graph/constants/DashboardAggregateOperationRatio'; @@ -53,7 +53,7 @@ export const ChartAggregateOperationSelectableListItem = ({ configuration.aggregateOperation; const isCurrentlyRatio = - isAggregateChartConfiguration(configuration) && + isWidgetConfigurationOfType(configuration, 'AggregateChartConfiguration') && isDefined(configuration.ratioAggregateConfig); if (!isExtendedAggregateOperation(operation)) { diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartAggregateOperationSelectionDropdownContent.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartAggregateOperationSelectionDropdownContent.tsx index f01c9e0820..dd8b15c94c 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartAggregateOperationSelectionDropdownContent.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartAggregateOperationSelectionDropdownContent.tsx @@ -3,9 +3,7 @@ import { ChartRatioAggregateOperationSelectableListItem } from '@/command-menu/p import { ChartRatioOptionValueSelectionDropdownContent } from '@/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent'; import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/command-menu/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord'; import { useWidgetInEditMode } from '@/command-menu/pages/page-layout/hooks/useWidgetInEditMode'; -import { isAggregateChartConfiguration } from '@/command-menu/pages/page-layout/utils/isAggregateChartConfiguration'; -import { isBarOrLineChartConfiguration } from '@/command-menu/pages/page-layout/utils/isBarOrLineChartConfiguration'; -import { isPieChartConfiguration } from '@/command-menu/pages/page-layout/utils/isPieChartConfiguration'; +import { isWidgetConfigurationOfType } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfType'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; import { DateAggregateOperations } from '@/object-record/record-table/constants/DateAggregateOperations'; import { getAvailableAggregateOperationsForFieldMetadataType } from '@/object-record/record-table/record-table-footer/utils/getAvailableAggregateOperationsForFieldMetadataType'; @@ -42,13 +40,19 @@ export const ChartAggregateOperationSelectionDropdownContent = ({ const configuration = widgetInEditMode?.configuration; - const isAggregateChart = isAggregateChartConfiguration(configuration); + const isAggregateChart = isWidgetConfigurationOfType( + configuration, + 'AggregateChartConfiguration', + ); + const isBarOrLineChart = + isWidgetConfigurationOfType(configuration, 'BarChartConfiguration') || + isWidgetConfigurationOfType(configuration, 'LineChartConfiguration'); + const isPieChart = isWidgetConfigurationOfType( + configuration, + 'PieChartConfiguration', + ); - if ( - !isBarOrLineChartConfiguration(configuration) && - !isAggregateChart && - !isPieChartConfiguration(configuration) - ) { + if (!isBarOrLineChart && !isAggregateChart && !isPieChart) { throw new Error('Invalid configuration type'); } diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartAxisNameSelectionDropdownContent.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartAxisNameSelectionDropdownContent.tsx index 85b38ab81e..5085052d13 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartAxisNameSelectionDropdownContent.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartAxisNameSelectionDropdownContent.tsx @@ -2,7 +2,7 @@ import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/command-menu/pa import { useUpdateCurrentWidgetConfig } from '@/command-menu/pages/page-layout/hooks/useUpdateCurrentWidgetConfig'; import { useWidgetInEditMode } from '@/command-menu/pages/page-layout/hooks/useWidgetInEditMode'; import { getChartAxisNameDisplayOptions } from '@/command-menu/pages/page-layout/utils/getChartAxisNameDisplayOptions'; -import { isBarOrLineChartConfiguration } from '@/command-menu/pages/page-layout/utils/isBarOrLineChartConfiguration'; +import { isWidgetConfigurationOfType } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfType'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext'; import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; @@ -18,11 +18,17 @@ export const ChartAxisNameSelectionDropdownContent = () => { const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord(); const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId); - if (!isBarOrLineChartConfiguration(widgetInEditMode?.configuration)) { + const configuration = widgetInEditMode?.configuration; + + const isBarOrLineChart = + isWidgetConfigurationOfType(configuration, 'BarChartConfiguration') || + isWidgetConfigurationOfType(configuration, 'LineChartConfiguration'); + + if (!isBarOrLineChart) { throw new Error('Invalid configuration type'); } - const currentAxisNameDisplay = widgetInEditMode.configuration.axisNameDisplay; + const currentAxisNameDisplay = configuration.axisNameDisplay; const dropdownId = useAvailableComponentInstanceIdOrThrow( DropdownComponentInstanceContext, diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartColorSelectionDropdownContent.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartColorSelectionDropdownContent.tsx index dfb228bf8e..d564e84735 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartColorSelectionDropdownContent.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartColorSelectionDropdownContent.tsx @@ -4,10 +4,7 @@ import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/command-menu/pa import { useUpdateCurrentWidgetConfig } from '@/command-menu/pages/page-layout/hooks/useUpdateCurrentWidgetConfig'; import { useWidgetInEditMode } from '@/command-menu/pages/page-layout/hooks/useWidgetInEditMode'; import { type ChartConfiguration } from '@/command-menu/pages/page-layout/types/ChartConfiguration'; -import { isBarOrLineChartConfiguration } from '@/command-menu/pages/page-layout/utils/isBarOrLineChartConfiguration'; -import { isGaugeChartConfiguration } from '@/command-menu/pages/page-layout/utils/isGaugeChartConfiguration'; -import { isIframeConfiguration } from '@/command-menu/pages/page-layout/utils/isIframeConfiguration'; -import { isPieChartConfiguration } from '@/command-menu/pages/page-layout/utils/isPieChartConfiguration'; +import { isWidgetConfigurationOfType } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfType'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; @@ -52,17 +49,30 @@ export const ChartColorSelectionDropdownContent = () => { return null; } - if (isIframeConfiguration(widgetInEditMode.configuration)) { + if ( + isWidgetConfigurationOfType( + widgetInEditMode.configuration, + 'IframeConfiguration', + ) + ) { throw new Error('Invalid configuration type'); } const configuration = widgetInEditMode.configuration as ChartConfiguration; - if ( - !isBarOrLineChartConfiguration(configuration) && - !isGaugeChartConfiguration(configuration) && - !isPieChartConfiguration(configuration) - ) { + const isBarOrLineChart = + isWidgetConfigurationOfType(configuration, 'BarChartConfiguration') || + isWidgetConfigurationOfType(configuration, 'LineChartConfiguration'); + const isGaugeChart = isWidgetConfigurationOfType( + configuration, + 'GaugeChartConfiguration', + ); + const isPieChart = isWidgetConfigurationOfType( + configuration, + 'PieChartConfiguration', + ); + + if (!isBarOrLineChart && !isGaugeChart && !isPieChart) { return null; } diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartDateGranularitySelectionDropdownContent.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartDateGranularitySelectionDropdownContent.tsx index ea02637ff2..ba93fede8c 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartDateGranularitySelectionDropdownContent.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartDateGranularitySelectionDropdownContent.tsx @@ -3,9 +3,9 @@ import { useUpdateCurrentWidgetConfig } from '@/command-menu/pages/page-layout/h import { useWidgetInEditMode } from '@/command-menu/pages/page-layout/hooks/useWidgetInEditMode'; import { type ChartConfiguration } from '@/command-menu/pages/page-layout/types/ChartConfiguration'; import { getDateGranularityLabel } from '@/command-menu/pages/page-layout/utils/getDateGranularityLabel'; -import { isBarOrLineChartConfiguration } from '@/command-menu/pages/page-layout/utils/isBarOrLineChartConfiguration'; -import { isChartConfiguration } from '@/command-menu/pages/page-layout/utils/isChartConfiguration'; -import { isPieChartConfiguration } from '@/command-menu/pages/page-layout/utils/isPieChartConfiguration'; +import { isWidgetConfigurationOfType } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfType'; +import { type FieldConfiguration } from '@/page-layout/types/FieldConfiguration'; +import { type FieldsConfiguration } from '@/page-layout/types/FieldsConfiguration'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext'; import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; @@ -17,6 +17,7 @@ import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/ho import { ObjectRecordGroupByDateGranularity } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; import { MenuItemSelect } from 'twenty-ui/navigation'; +import { type WidgetConfiguration } from '~/generated/graphql'; type ChartDateGranularitySelectionDropdownContentProps = { axis?: 'primary' | 'secondary'; @@ -31,11 +32,15 @@ const getCurrentDateGranularity = ({ }) => { const defaultGranularity = ObjectRecordGroupByDateGranularity.DAY; - if (isPieChartConfiguration(configuration)) { + if (isWidgetConfigurationOfType(configuration, 'PieChartConfiguration')) { return configuration.dateGranularity || defaultGranularity; } - if (!isBarOrLineChartConfiguration(configuration)) { + const isBarOrLineChart = + isWidgetConfigurationOfType(configuration, 'BarChartConfiguration') || + isWidgetConfigurationOfType(configuration, 'LineChartConfiguration'); + + if (!isBarOrLineChart) { return defaultGranularity; } @@ -48,6 +53,23 @@ const getCurrentDateGranularity = ({ ); }; +const isChartConfiguration = ( + configuration: + | WidgetConfiguration + | FieldsConfiguration + | FieldConfiguration + | null + | undefined, +): configuration is ChartConfiguration => { + return ( + isWidgetConfigurationOfType(configuration, 'BarChartConfiguration') || + isWidgetConfigurationOfType(configuration, 'LineChartConfiguration') || + isWidgetConfigurationOfType(configuration, 'PieChartConfiguration') || + isWidgetConfigurationOfType(configuration, 'AggregateChartConfiguration') || + isWidgetConfigurationOfType(configuration, 'GaugeChartConfiguration') + ); +}; + export const ChartDateGranularitySelectionDropdownContent = ({ axis, }: ChartDateGranularitySelectionDropdownContentProps) => { @@ -60,11 +82,19 @@ export const ChartDateGranularitySelectionDropdownContent = ({ throw new Error('Invalid configuration type'); } - if (!isDefined(axis) && !isPieChartConfiguration(configuration)) { + const isPieChart = isWidgetConfigurationOfType( + configuration, + 'PieChartConfiguration', + ); + const isBarOrLineChart = + isWidgetConfigurationOfType(configuration, 'BarChartConfiguration') || + isWidgetConfigurationOfType(configuration, 'LineChartConfiguration'); + + if (!isDefined(axis) && !isPieChart) { throw new Error('Invalid configuration type'); } - if (isDefined(axis) && !isBarOrLineChartConfiguration(configuration)) { + if (isDefined(axis) && !isBarOrLineChart) { throw new Error('Invalid configuration type'); } diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartFieldSelectionForAggregateOperationDropdownContent.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartFieldSelectionForAggregateOperationDropdownContent.tsx index fcf0e58015..1abd6564c6 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartFieldSelectionForAggregateOperationDropdownContent.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartFieldSelectionForAggregateOperationDropdownContent.tsx @@ -1,9 +1,7 @@ import { ChartAggregateOperationSelectionDropdownContent } from '@/command-menu/pages/page-layout/components/dropdown-content/ChartAggregateOperationSelectionDropdownContent'; import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/command-menu/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord'; import { useWidgetInEditMode } from '@/command-menu/pages/page-layout/hooks/useWidgetInEditMode'; -import { isAggregateChartConfiguration } from '@/command-menu/pages/page-layout/utils/isAggregateChartConfiguration'; -import { isBarOrLineChartConfiguration } from '@/command-menu/pages/page-layout/utils/isBarOrLineChartConfiguration'; -import { isPieChartConfiguration } from '@/command-menu/pages/page-layout/utils/isPieChartConfiguration'; +import { isWidgetConfigurationOfType } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfType'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; import { isFieldRelation } from '@/object-record/record-field/ui/types/guards/isFieldRelation'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; @@ -31,11 +29,19 @@ export const ChartFieldSelectionForAggregateOperationDropdownContent = () => { const configuration = widgetInEditMode?.configuration; - if ( - !isBarOrLineChartConfiguration(configuration) && - !isAggregateChartConfiguration(configuration) && - !isPieChartConfiguration(configuration) - ) { + const isBarOrLineChart = + isWidgetConfigurationOfType(configuration, 'BarChartConfiguration') || + isWidgetConfigurationOfType(configuration, 'LineChartConfiguration'); + const isAggregateChart = isWidgetConfigurationOfType( + configuration, + 'AggregateChartConfiguration', + ); + const isPieChart = isWidgetConfigurationOfType( + configuration, + 'PieChartConfiguration', + ); + + if (!isBarOrLineChart && !isAggregateChart && !isPieChart) { throw new Error('Invalid configuration type'); } diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionDropdownContent.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionDropdownContent.tsx index 78ba0be895..d06b3423bf 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionDropdownContent.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionDropdownContent.tsx @@ -1,13 +1,11 @@ import { ChartGroupByFieldSelectionDropdownContentBase } from '@/command-menu/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionDropdownContentBase'; -import { - type BarChartConfiguration, - type LineChartConfiguration, -} from '~/generated/graphql'; +import { type TypedBarChartConfiguration } from '@/command-menu/pages/page-layout/types/TypedBarChartConfiguration'; +import { type TypedLineChartConfiguration } from '@/command-menu/pages/page-layout/types/TypedLineChartConfiguration'; export const ChartGroupByFieldSelectionDropdownContent = () => { return ( fieldMetadataIdKey="secondaryAxisGroupByFieldMetadataId" subFieldNameKey="secondaryAxisGroupBySubFieldName" diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartPieFieldSelectionDropdownContent.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartPieFieldSelectionDropdownContent.tsx index 11d982606d..bf76377c8e 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartPieFieldSelectionDropdownContent.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartPieFieldSelectionDropdownContent.tsx @@ -1,9 +1,9 @@ import { ChartGroupByFieldSelectionDropdownContentBase } from '@/command-menu/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionDropdownContentBase'; -import { type PieChartConfiguration } from '~/generated/graphql'; +import { type TypedPieChartConfiguration } from '@/command-menu/pages/page-layout/types/TypedPieChartConfiguration'; export const ChartPieFieldSelectionDropdownContent = () => { return ( - + fieldMetadataIdKey="groupByFieldMetadataId" subFieldNameKey="groupBySubFieldName" /> diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioAggregateOperationSelectableListItem.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioAggregateOperationSelectableListItem.tsx index 3fffa19a0c..6f0d74701c 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioAggregateOperationSelectableListItem.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioAggregateOperationSelectableListItem.tsx @@ -1,6 +1,6 @@ import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/command-menu/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord'; import { useWidgetInEditMode } from '@/command-menu/pages/page-layout/hooks/useWidgetInEditMode'; -import { isAggregateChartConfiguration } from '@/command-menu/pages/page-layout/utils/isAggregateChartConfiguration'; +import { isWidgetConfigurationOfType } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfType'; import { DASHBOARD_AGGREGATE_OPERATION_RATIO } from '@/page-layout/widgets/graph/constants/DashboardAggregateOperationRatio'; import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; @@ -30,8 +30,10 @@ export const ChartRatioAggregateOperationSelectableListItem = ({ ); const isCurrentlyRatio = - isAggregateChartConfiguration(widgetInEditMode?.configuration) && - isDefined(widgetInEditMode.configuration.ratioAggregateConfig); + isWidgetConfigurationOfType( + widgetInEditMode?.configuration, + 'AggregateChartConfiguration', + ) && isDefined(widgetInEditMode.configuration.ratioAggregateConfig); const isFocused = selectedItemId === DASHBOARD_AGGREGATE_OPERATION_RATIO; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionBooleanSelectableListItem.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionBooleanSelectableListItem.tsx index c705ed1f33..b63a606720 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionBooleanSelectableListItem.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionBooleanSelectableListItem.tsx @@ -1,7 +1,7 @@ import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/command-menu/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord'; import { useUpdateCurrentWidgetConfig } from '@/command-menu/pages/page-layout/hooks/useUpdateCurrentWidgetConfig'; import { useWidgetInEditMode } from '@/command-menu/pages/page-layout/hooks/useWidgetInEditMode'; -import { isAggregateChartConfiguration } from '@/command-menu/pages/page-layout/utils/isAggregateChartConfiguration'; +import { isWidgetConfigurationOfType } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfType'; import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext'; import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; @@ -36,8 +36,9 @@ export const ChartRatioOptionBooleanSelectableListItem = ({ dropdownId, ); - const currentRatioConfig = isAggregateChartConfiguration( + const currentRatioConfig = isWidgetConfigurationOfType( widgetInEditMode?.configuration, + 'AggregateChartConfiguration', ) ? widgetInEditMode.configuration.ratioAggregateConfig : undefined; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionSelectSelectableListItem.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionSelectSelectableListItem.tsx index 62db3ed207..0d6f825603 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionSelectSelectableListItem.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionSelectSelectableListItem.tsx @@ -1,7 +1,7 @@ import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/command-menu/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord'; import { useUpdateCurrentWidgetConfig } from '@/command-menu/pages/page-layout/hooks/useUpdateCurrentWidgetConfig'; import { useWidgetInEditMode } from '@/command-menu/pages/page-layout/hooks/useWidgetInEditMode'; -import { isAggregateChartConfiguration } from '@/command-menu/pages/page-layout/utils/isAggregateChartConfiguration'; +import { isWidgetConfigurationOfType } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfType'; import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext'; import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; @@ -38,8 +38,9 @@ export const ChartRatioOptionSelectSelectableListItem = ({ dropdownId, ); - const currentRatioConfig = isAggregateChartConfiguration( + const currentRatioConfig = isWidgetConfigurationOfType( widgetInEditMode?.configuration, + 'AggregateChartConfiguration', ) ? widgetInEditMode.configuration.ratioAggregateConfig : undefined; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx index 7e6a34e7f6..6d3d33e66d 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionValueSelectionDropdownContent.tsx @@ -2,7 +2,7 @@ import { ChartRatioOptionBooleanSelectableListItem } from '@/command-menu/pages/ import { ChartRatioOptionSelectSelectableListItem } from '@/command-menu/pages/page-layout/components/dropdown-content/ChartRatioOptionSelectSelectableListItem'; import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/command-menu/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord'; import { useWidgetInEditMode } from '@/command-menu/pages/page-layout/hooks/useWidgetInEditMode'; -import { isAggregateChartConfiguration } from '@/command-menu/pages/page-layout/utils/isAggregateChartConfiguration'; +import { isWidgetConfigurationOfType } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfType'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader'; import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent'; @@ -42,7 +42,12 @@ export const ChartRatioOptionValueSelectionDropdownContent = ({ DropdownComponentInstanceContext, ); - if (!isAggregateChartConfiguration(widgetInEditMode?.configuration)) { + if ( + !isWidgetConfigurationOfType( + widgetInEditMode?.configuration, + 'AggregateChartConfiguration', + ) + ) { return null; } diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartSortByGroupByFieldDropdownContent.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartSortByGroupByFieldDropdownContent.tsx index b1ae508c73..69b55c8852 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartSortByGroupByFieldDropdownContent.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartSortByGroupByFieldDropdownContent.tsx @@ -3,7 +3,7 @@ import { useGraphGroupBySortOptionLabels } from '@/command-menu/pages/page-layou import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/command-menu/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord'; import { useUpdateCurrentWidgetConfig } from '@/command-menu/pages/page-layout/hooks/useUpdateCurrentWidgetConfig'; import { useWidgetInEditMode } from '@/command-menu/pages/page-layout/hooks/useWidgetInEditMode'; -import { isBarOrLineChartConfiguration } from '@/command-menu/pages/page-layout/utils/isBarOrLineChartConfiguration'; +import { isWidgetConfigurationOfType } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfType'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext'; import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; @@ -23,7 +23,9 @@ export const ChartSortByGroupByFieldDropdownContent = () => { const configuration = widgetInEditMode?.configuration; - const isBarOrLineChart = isBarOrLineChartConfiguration(configuration); + const isBarOrLineChart = + isWidgetConfigurationOfType(configuration, 'BarChartConfiguration') || + isWidgetConfigurationOfType(configuration, 'LineChartConfiguration'); if (!isBarOrLineChart) { throw new Error('Invalid configuration type'); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartSortBySelectionDropdownContent.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartSortBySelectionDropdownContent.tsx index f1e032ebd5..143eee5739 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartSortBySelectionDropdownContent.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartSortBySelectionDropdownContent.tsx @@ -3,9 +3,7 @@ import { useGraphXSortOptionLabels } from '@/command-menu/pages/page-layout/hook import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/command-menu/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord'; import { useUpdateCurrentWidgetConfig } from '@/command-menu/pages/page-layout/hooks/useUpdateCurrentWidgetConfig'; import { useWidgetInEditMode } from '@/command-menu/pages/page-layout/hooks/useWidgetInEditMode'; -import { isBarChartConfiguration } from '@/command-menu/pages/page-layout/utils/isBarChartConfiguration'; -import { isLineChartConfiguration } from '@/command-menu/pages/page-layout/utils/isLineChartConfiguration'; -import { isPieChartConfiguration } from '@/command-menu/pages/page-layout/utils/isPieChartConfiguration'; +import { isWidgetConfigurationOfType } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfType'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; import { isRelationNestedFieldDateKind } from '@/page-layout/widgets/graph/utils/isRelationNestedFieldDateKind'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; @@ -30,9 +28,18 @@ export const ChartSortBySelectionDropdownContent = () => { const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId); const configuration = widgetInEditMode?.configuration; - const isPieChart = isPieChartConfiguration(configuration); - const isLineChart = isLineChartConfiguration(configuration); - const isBarChart = isBarChartConfiguration(configuration); + const isPieChart = isWidgetConfigurationOfType( + configuration, + 'PieChartConfiguration', + ); + const isLineChart = isWidgetConfigurationOfType( + configuration, + 'LineChartConfiguration', + ); + const isBarChart = isWidgetConfigurationOfType( + configuration, + 'BarChartConfiguration', + ); if (!isBarChart && !isLineChart && !isPieChart) { throw new Error('Invalid configuration type'); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartXAxisFieldSelectionDropdownContent.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartXAxisFieldSelectionDropdownContent.tsx index 9f58fb6eaf..2c62b33340 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartXAxisFieldSelectionDropdownContent.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartXAxisFieldSelectionDropdownContent.tsx @@ -1,13 +1,11 @@ import { ChartGroupByFieldSelectionDropdownContentBase } from '@/command-menu/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionDropdownContentBase'; -import { - type BarChartConfiguration, - type LineChartConfiguration, -} from '~/generated/graphql'; +import { type TypedBarChartConfiguration } from '@/command-menu/pages/page-layout/types/TypedBarChartConfiguration'; +import { type TypedLineChartConfiguration } from '@/command-menu/pages/page-layout/types/TypedLineChartConfiguration'; export const ChartXAxisFieldSelectionDropdownContent = () => { return ( fieldMetadataIdKey="primaryAxisGroupByFieldMetadataId" subFieldNameKey="primaryAxisGroupBySubFieldName" diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/GraphConfigurationTypeToConfigTypename.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/GraphConfigurationTypeToConfigTypename.ts new file mode 100644 index 0000000000..18caa5ca71 --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/GraphConfigurationTypeToConfigTypename.ts @@ -0,0 +1,11 @@ +import { WidgetConfigurationType } from '~/generated/graphql'; + +export const GRAPH_CONFIGURATION_TYPE_TO_CONFIG_TYPENAME: Partial< + Record +> = { + [WidgetConfigurationType.BAR_CHART]: 'BarChartConfiguration', + [WidgetConfigurationType.LINE_CHART]: 'LineChartConfiguration', + [WidgetConfigurationType.PIE_CHART]: 'PieChartConfiguration', + [WidgetConfigurationType.AGGREGATE_CHART]: 'AggregateChartConfiguration', + [WidgetConfigurationType.GAUGE_CHART]: 'GaugeChartConfiguration', +} as const; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/GraphTypeInformation.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/GraphTypeInformation.ts index 3a06372484..0b27e82186 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/GraphTypeInformation.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/GraphTypeInformation.ts @@ -3,6 +3,7 @@ import { GAUGE_CHART_SETTINGS } from '@/command-menu/pages/page-layout/constants import { LINE_CHART_SETTINGS } from '@/command-menu/pages/page-layout/constants/LineChartSettings'; import { PIE_CHART_SETTINGS } from '@/command-menu/pages/page-layout/constants/PieChartSettings'; import { type ChartSettingsGroup } from '@/command-menu/pages/page-layout/types/ChartSettingsGroup'; +import { GraphType } from '@/command-menu/pages/page-layout/types/GraphType'; import { getBarChartSettings } from '@/command-menu/pages/page-layout/utils/getBarChartSettings'; import { type MessageDescriptor } from '@lingui/core'; import { msg } from '@lingui/core/macro'; @@ -15,7 +16,7 @@ import { IconGauge, IconSum, } from 'twenty-ui/display'; -import { GraphType } from '~/generated-metadata/graphql'; +import { BarChartLayout } from '~/generated/graphql'; export const GRAPH_TYPE_INFORMATION: Record< GraphType, @@ -28,12 +29,12 @@ export const GRAPH_TYPE_INFORMATION: Record< [GraphType.VERTICAL_BAR]: { label: msg`Vertical Bar Chart`, icon: IconChartBar, - settings: getBarChartSettings(GraphType.VERTICAL_BAR), + settings: getBarChartSettings(BarChartLayout.VERTICAL), }, [GraphType.HORIZONTAL_BAR]: { label: msg`Horizontal Bar Chart`, icon: IconChartBarHorizontal, - settings: getBarChartSettings(GraphType.HORIZONTAL_BAR), + settings: getBarChartSettings(BarChartLayout.HORIZONTAL), }, [GraphType.PIE]: { label: msg`Pie Chart`, diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/GraphTypeToConfigTypename.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/GraphTypeToConfigTypename.ts deleted file mode 100644 index 245fbfd8ed..0000000000 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/GraphTypeToConfigTypename.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { GraphType } from '~/generated-metadata/graphql'; - -export const GRAPH_TYPE_TO_CONFIG_TYPENAME = { - [GraphType.VERTICAL_BAR]: 'BarChartConfiguration', - [GraphType.HORIZONTAL_BAR]: 'BarChartConfiguration', - [GraphType.LINE]: 'LineChartConfiguration', - [GraphType.PIE]: 'PieChartConfiguration', - [GraphType.AGGREGATE]: 'AggregateChartConfiguration', - [GraphType.GAUGE]: 'GaugeChartConfiguration', -} as const; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/hooks/__tests__/useChartSettingsValues.test.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/hooks/__tests__/useChartSettingsValues.test.tsx index 7081bf1693..00906c8f64 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/hooks/__tests__/useChartSettingsValues.test.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/hooks/__tests__/useChartSettingsValues.test.tsx @@ -3,16 +3,17 @@ import { RecoilRoot } from 'recoil'; import { type ChartConfiguration } from '@/command-menu/pages/page-layout/types/ChartConfiguration'; import { CHART_CONFIGURATION_SETTING_IDS } from '@/command-menu/pages/page-layout/types/ChartConfigurationSettingIds'; +import { type TypedBarChartConfiguration } from '@/command-menu/pages/page-layout/types/TypedBarChartConfiguration'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { AggregateOperations, AxisNameDisplay, - type BarChartConfiguration, BarChartGroupMode, + BarChartLayout, FieldMetadataType, GraphOrderBy, - GraphType, + WidgetConfigurationType, } from '~/generated-metadata/graphql'; import { useChartSettingsValues } from '@/command-menu/pages/page-layout/hooks/useChartSettingsValues'; @@ -59,13 +60,12 @@ const mockObjectMetadataItem: ObjectMetadataItem = { } as ObjectMetadataItem; const buildBarChartConfiguration = ( - overrides: Partial, -): BarChartConfiguration => + overrides: Partial, +): TypedBarChartConfiguration => ({ __typename: 'BarChartConfiguration', aggregateFieldMetadataId: 'field-amount', aggregateOperation: AggregateOperations.SUM, - graphType: GraphType.VERTICAL_BAR, primaryAxisGroupByFieldMetadataId: 'field-company-name', primaryAxisGroupBySubFieldName: null, secondaryAxisGroupByFieldMetadataId: null, @@ -76,7 +76,7 @@ const buildBarChartConfiguration = ( displayDataLabel: false, groupMode: 'GROUPED', ...overrides, - }) as BarChartConfiguration; + }) as TypedBarChartConfiguration; const renderUseChartSettingsValues = (configuration: ChartConfiguration) => { return renderHook( @@ -102,7 +102,6 @@ const renderUseChartSettingsValues = (configuration: ChartConfiguration) => { describe('useChartSettingsValues', () => { describe('Vertical bar chart (1D - single grouping)', () => { const verticalBarConfig = buildBarChartConfiguration({ - graphType: GraphType.VERTICAL_BAR, primaryAxisGroupByFieldMetadataId: 'field-company-name', secondaryAxisGroupByFieldMetadataId: null, }); @@ -172,7 +171,7 @@ describe('useChartSettingsValues', () => { describe('Horizontal bar chart (1D - single grouping)', () => { const horizontalBarConfig = buildBarChartConfiguration({ - graphType: GraphType.HORIZONTAL_BAR, + layout: BarChartLayout.HORIZONTAL, primaryAxisGroupByFieldMetadataId: 'field-company-name', secondaryAxisGroupByFieldMetadataId: null, }); @@ -213,7 +212,7 @@ describe('useChartSettingsValues', () => { describe('Vertical bar chart (2D - with secondary grouping)', () => { const verticalBar2DConfig = buildBarChartConfiguration({ - graphType: GraphType.VERTICAL_BAR, + layout: BarChartLayout.VERTICAL, primaryAxisGroupByFieldMetadataId: 'field-company-name', secondaryAxisGroupByFieldMetadataId: 'field-stage', secondaryAxisOrderBy: GraphOrderBy.FIELD_DESC, @@ -253,7 +252,7 @@ describe('useChartSettingsValues', () => { describe('Horizontal bar chart (2D - with secondary grouping)', () => { const horizontalBar2DConfig = buildBarChartConfiguration({ - graphType: GraphType.HORIZONTAL_BAR, + layout: BarChartLayout.HORIZONTAL, primaryAxisGroupByFieldMetadataId: 'field-company-name', secondaryAxisGroupByFieldMetadataId: 'field-stage', secondaryAxisOrderBy: GraphOrderBy.FIELD_DESC, @@ -282,7 +281,7 @@ describe('useChartSettingsValues', () => { describe('Composite field with subfield', () => { const configWithSubField = buildBarChartConfiguration({ - graphType: GraphType.VERTICAL_BAR, + layout: BarChartLayout.VERTICAL, primaryAxisGroupByFieldMetadataId: 'field-full-name', primaryAxisGroupBySubFieldName: 'firstName', }); @@ -345,7 +344,7 @@ describe('useChartSettingsValues', () => { it('should return capitalized color when color is set', () => { const config = buildBarChartConfiguration({ color: 'blue', - } as Partial); + }); const { result } = renderUseChartSettingsValues(config); @@ -525,7 +524,7 @@ describe('useChartSettingsValues', () => { describe('Critical: Proves no swapping occurs', () => { it('horizontal bar with NULL secondary should still return primary for DATA_ON_DISPLAY_X', () => { const horizontalConfig = buildBarChartConfiguration({ - graphType: GraphType.HORIZONTAL_BAR, + layout: BarChartLayout.HORIZONTAL, primaryAxisGroupByFieldMetadataId: 'field-company-name', secondaryAxisGroupByFieldMetadataId: null, }); @@ -554,12 +553,14 @@ describe('useChartSettingsValues', () => { const verticalConfig = buildBarChartConfiguration({ ...baseConfig, - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, + layout: BarChartLayout.VERTICAL, }); const horizontalConfig = buildBarChartConfiguration({ ...baseConfig, - graphType: GraphType.HORIZONTAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, + layout: BarChartLayout.HORIZONTAL, }); const { result: verticalResult } = diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/hooks/useUpdateGraphTypeConfig.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/hooks/useUpdateGraphTypeConfig.ts index 1108961a52..8c5e02fec6 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/hooks/useUpdateGraphTypeConfig.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/hooks/useUpdateGraphTypeConfig.ts @@ -1,10 +1,12 @@ -import { GRAPH_TYPE_TO_CONFIG_TYPENAME } from '@/command-menu/pages/page-layout/constants/GraphTypeToConfigTypename'; +import { GRAPH_CONFIGURATION_TYPE_TO_CONFIG_TYPENAME } from '@/command-menu/pages/page-layout/constants/GraphConfigurationTypeToConfigTypename'; import { type ChartConfiguration } from '@/command-menu/pages/page-layout/types/ChartConfiguration'; +import { GraphType } from '@/command-menu/pages/page-layout/types/GraphType'; import { convertAggregateOperationForDateField } from '@/command-menu/pages/page-layout/utils/convertAggregateOperationForDateField'; import { convertBarOrLineChartConfigToPieChart } from '@/command-menu/pages/page-layout/utils/convertBarOrLineChartConfigToPieChart'; import { convertPieChartConfigToBarOrLineChart } from '@/command-menu/pages/page-layout/utils/convertPieChartConfigToBarOrLineChart'; -import { isBarOrLineChartConfiguration } from '@/command-menu/pages/page-layout/utils/isBarOrLineChartConfiguration'; -import { isPieChartConfiguration } from '@/command-menu/pages/page-layout/utils/isPieChartConfiguration'; +import { getConfigurationTypeFromGraphType } from '@/command-menu/pages/page-layout/utils/getConfigurationTypeFromGraphType'; +import { isWidgetConfigurationOfType } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfType'; +import { isWidgetConfigurationOfTypeGraph } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfTypeGraph'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState'; import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState'; @@ -16,7 +18,7 @@ import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTab import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; import { useRecoilCallback } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; -import { GraphType } from '~/generated/graphql'; +import { BarChartLayout } from '~/generated/graphql'; export const useGetConfigToUpdateAfterGraphTypeChange = ({ pageLayoutId, @@ -75,13 +77,20 @@ export const useGetConfigToUpdateAfterGraphTypeChange = ({ throw new Error('Widget configuration not found in draft state'); } - const currentConfiguration = - widgetInDraft.configuration as ChartConfiguration; + const currentConfiguration = widgetInDraft.configuration; - let configToUpdate: Record = { - __typename: GRAPH_TYPE_TO_CONFIG_TYPENAME[graphType], - graphType, - }; + if (!isWidgetConfigurationOfTypeGraph(currentConfiguration)) { + throw new Error('Widget configuration is not a chart configuration'); + } + + const newConfigurationType = + getConfigurationTypeFromGraphType(graphType); + + let configToUpdate = { + __typename: + GRAPH_CONFIGURATION_TYPE_TO_CONFIG_TYPENAME[newConfigurationType], + configurationType: newConfigurationType, + } as Partial; if ( graphType !== GraphType.AGGREGATE && @@ -106,13 +115,26 @@ export const useGetConfigToUpdateAfterGraphTypeChange = ({ } const isPieChart = graphType === GraphType.PIE; + const isVerticalBarChart = graphType === GraphType.VERTICAL_BAR; + const isHorizontalBarChart = graphType === GraphType.HORIZONTAL_BAR; + const isLineChart = graphType === GraphType.LINE; const isBarOrLineChart = - graphType === GraphType.VERTICAL_BAR || - graphType === GraphType.HORIZONTAL_BAR || - graphType === GraphType.LINE; + isVerticalBarChart || isHorizontalBarChart || isLineChart; + const wasBarOrLineChart = - isBarOrLineChartConfiguration(currentConfiguration); - const wasPieChart = isPieChartConfiguration(currentConfiguration); + isWidgetConfigurationOfType( + currentConfiguration, + 'BarChartConfiguration', + ) || + isWidgetConfigurationOfType( + currentConfiguration, + 'LineChartConfiguration', + ); + + const wasPieChart = isWidgetConfigurationOfType( + currentConfiguration, + 'PieChartConfiguration', + ); if (isPieChart && wasBarOrLineChart) { configToUpdate = { @@ -128,6 +150,20 @@ export const useGetConfigToUpdateAfterGraphTypeChange = ({ }; } + if (isHorizontalBarChart) { + configToUpdate = { + ...configToUpdate, + layout: BarChartLayout.HORIZONTAL, + }; + } + + if (isVerticalBarChart) { + configToUpdate = { + ...configToUpdate, + layout: BarChartLayout.VERTICAL, + }; + } + const activeTabId = snapshot.getLoadable(activeTabIdState).getValue(); if (isDefined(activeTabId) && isDefined(currentlyEditingWidgetId)) { @@ -135,12 +171,12 @@ export const useGetConfigToUpdateAfterGraphTypeChange = ({ .getLoadable(pageLayoutCurrentLayoutsState) .getValue(); - const updatedLayouts = updateWidgetMinimumSizeForGraphType( - graphType, - currentlyEditingWidgetId, - activeTabId, + const updatedLayouts = updateWidgetMinimumSizeForGraphType({ + configurationType: newConfigurationType, + widgetId: currentlyEditingWidgetId, + tabId: activeTabId, currentLayouts, - ); + }); set(pageLayoutCurrentLayoutsState, updatedLayouts); } diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/ChartConfiguration.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/ChartConfiguration.ts index 107a0c1c38..ca246be4d4 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/ChartConfiguration.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/ChartConfiguration.ts @@ -1,14 +1,12 @@ -import { - type AggregateChartConfiguration, - type BarChartConfiguration, - type GaugeChartConfiguration, - type LineChartConfiguration, - type PieChartConfiguration, -} from '~/generated/graphql'; +import { type TypedGaugeChartConfiguration } from '@/command-menu/pages/page-layout/types/TypedGaugeChartConfiguration'; +import { type TypedAggregateChartConfiguration } from './TypedAggregateChartConfiguration'; +import { type TypedBarChartConfiguration } from './TypedBarChartConfiguration'; +import { type TypedLineChartConfiguration } from './TypedLineChartConfiguration'; +import { type TypedPieChartConfiguration } from './TypedPieChartConfiguration'; export type ChartConfiguration = - | BarChartConfiguration - | LineChartConfiguration - | PieChartConfiguration - | AggregateChartConfiguration - | GaugeChartConfiguration; + | TypedBarChartConfiguration + | TypedLineChartConfiguration + | TypedPieChartConfiguration + | TypedAggregateChartConfiguration + | TypedGaugeChartConfiguration; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/ChartWidget.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/ChartWidget.ts index f84c9d6b74..251867f300 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/ChartWidget.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/ChartWidget.ts @@ -1,4 +1,3 @@ -import { type ChartFilters } from '@/command-menu/pages/page-layout/types/ChartFilters'; import { type ChartWidgetConfiguration } from '@/command-menu/pages/page-layout/types/ChartWidgetConfiguration'; import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget'; import { type ModifiedProperties } from 'twenty-shared/types'; @@ -8,9 +7,6 @@ export type ChartWidget = ModifiedProperties< PageLayoutWidget, { type: WidgetType.GRAPH; - configuration: ModifiedProperties< - ChartWidgetConfiguration, - { filter?: ChartFilters } - >; + configuration: ChartWidgetConfiguration; } >; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/ChartWidgetConfiguration.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/ChartWidgetConfiguration.ts index bb430eb9cf..2bbd8375a1 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/ChartWidgetConfiguration.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/ChartWidgetConfiguration.ts @@ -1,14 +1,12 @@ -import { - type AggregateChartConfiguration, - type BarChartConfiguration, - type GaugeChartConfiguration, - type LineChartConfiguration, - type PieChartConfiguration, -} from '~/generated/graphql'; +import { type TypedAggregateChartConfiguration } from '@/command-menu/pages/page-layout/types/TypedAggregateChartConfiguration'; +import { type TypedBarChartConfiguration } from '@/command-menu/pages/page-layout/types/TypedBarChartConfiguration'; +import { type TypedGaugeChartConfiguration } from '@/command-menu/pages/page-layout/types/TypedGaugeChartConfiguration'; +import { type TypedLineChartConfiguration } from '@/command-menu/pages/page-layout/types/TypedLineChartConfiguration'; +import { type TypedPieChartConfiguration } from '@/command-menu/pages/page-layout/types/TypedPieChartConfiguration'; export type ChartWidgetConfiguration = - | BarChartConfiguration - | GaugeChartConfiguration - | LineChartConfiguration - | AggregateChartConfiguration - | PieChartConfiguration; + | TypedBarChartConfiguration + | TypedGaugeChartConfiguration + | TypedLineChartConfiguration + | TypedAggregateChartConfiguration + | TypedPieChartConfiguration; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/GraphType.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/GraphType.ts new file mode 100644 index 0000000000..b52a7f70ff --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/GraphType.ts @@ -0,0 +1,8 @@ +export enum GraphType { + VERTICAL_BAR = 'VERTICAL_BAR', + HORIZONTAL_BAR = 'HORIZONTAL_BAR', + LINE = 'LINE', + PIE = 'PIE', + AGGREGATE = 'AGGREGATE', + GAUGE = 'GAUGE', +} diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/TypedAggregateChartConfiguration.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/TypedAggregateChartConfiguration.ts new file mode 100644 index 0000000000..3e97ce2755 --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/TypedAggregateChartConfiguration.ts @@ -0,0 +1,8 @@ +import { + type AggregateChartConfiguration, + type WidgetConfigurationType, +} from '~/generated/graphql'; + +export type TypedAggregateChartConfiguration = AggregateChartConfiguration & { + configurationType: WidgetConfigurationType.AGGREGATE_CHART; +}; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/TypedBarChartConfiguration.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/TypedBarChartConfiguration.ts new file mode 100644 index 0000000000..3204c323a4 --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/TypedBarChartConfiguration.ts @@ -0,0 +1,8 @@ +import { + type BarChartConfiguration, + type WidgetConfigurationType, +} from '~/generated/graphql'; + +export type TypedBarChartConfiguration = BarChartConfiguration & { + configurationType: WidgetConfigurationType.BAR_CHART; +}; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/TypedGaugeChartConfiguration.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/TypedGaugeChartConfiguration.ts new file mode 100644 index 0000000000..308065644d --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/TypedGaugeChartConfiguration.ts @@ -0,0 +1,8 @@ +import { + type GaugeChartConfiguration, + type WidgetConfigurationType, +} from '~/generated/graphql'; + +export type TypedGaugeChartConfiguration = GaugeChartConfiguration & { + configurationType: WidgetConfigurationType.GAUGE_CHART; +}; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/TypedLineChartConfiguration.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/TypedLineChartConfiguration.ts new file mode 100644 index 0000000000..3ab40c6ae4 --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/TypedLineChartConfiguration.ts @@ -0,0 +1,8 @@ +import { + type LineChartConfiguration, + type WidgetConfigurationType, +} from '~/generated/graphql'; + +export type TypedLineChartConfiguration = LineChartConfiguration & { + configurationType: WidgetConfigurationType.LINE_CHART; +}; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/TypedPieChartConfiguration.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/TypedPieChartConfiguration.ts new file mode 100644 index 0000000000..a89445f55f --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/TypedPieChartConfiguration.ts @@ -0,0 +1,8 @@ +import { + type PieChartConfiguration, + type WidgetConfigurationType, +} from '~/generated/graphql'; + +export type TypedPieChartConfiguration = PieChartConfiguration & { + configurationType: WidgetConfigurationType.PIE_CHART; +}; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/assertChartWidgetOrThrow.test.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/assertChartWidgetOrThrow.test.ts new file mode 100644 index 0000000000..91cbff87cd --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/assertChartWidgetOrThrow.test.ts @@ -0,0 +1,51 @@ +import { assertChartWidgetOrThrow } from '@/command-menu/pages/page-layout/utils/assertChartWidgetOrThrow'; +import { + ALL_CHART_CONFIGURATIONS, + createTestWidget, + TEST_BAR_CHART_CONFIGURATION, + TEST_IFRAME_CONFIGURATION, +} from '~/testing/mock-data/widget-configurations'; + +describe('assertChartWidgetOrThrow', () => { + it('does not throw for valid chart widget', () => { + const widget = createTestWidget({ + configuration: TEST_BAR_CHART_CONFIGURATION, + }); + + expect(() => assertChartWidgetOrThrow(widget)).not.toThrow(); + }); + + it('throws when objectMetadataId is missing', () => { + const widget = createTestWidget({ objectMetadataId: undefined }); + + expect(() => assertChartWidgetOrThrow(widget)).toThrow( + 'Widget objectMetadataId is required', + ); + }); + + it('throws when objectMetadataId is null', () => { + const widget = createTestWidget({ objectMetadataId: null }); + + expect(() => assertChartWidgetOrThrow(widget)).toThrow( + 'Widget objectMetadataId is required', + ); + }); + + it('throws when configuration is not a valid chart type', () => { + const widget = createTestWidget({ + configuration: TEST_IFRAME_CONFIGURATION, + }); + + expect(() => assertChartWidgetOrThrow(widget)).toThrow( + 'Expected chart configuration but got IframeConfiguration', + ); + }); + + it('passes for all valid chart types', () => { + ALL_CHART_CONFIGURATIONS.forEach((config) => { + const widget = createTestWidget({ configuration: config }); + + expect(() => assertChartWidgetOrThrow(widget)).not.toThrow(); + }); + }); +}); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/buildChartGroupByFieldConfigUpdate.test.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/buildChartGroupByFieldConfigUpdate.test.ts index cda2c4621e..16dd04191b 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/buildChartGroupByFieldConfigUpdate.test.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/buildChartGroupByFieldConfigUpdate.test.ts @@ -1,9 +1,10 @@ +import { type TypedBarChartConfiguration } from '@/command-menu/pages/page-layout/types/TypedBarChartConfiguration'; +import { type TypedPieChartConfiguration } from '@/command-menu/pages/page-layout/types/TypedPieChartConfiguration'; import { ObjectRecordGroupByDateGranularity } from 'twenty-shared/types'; import { - type BarChartConfiguration, BarChartGroupMode, GraphOrderBy, - type PieChartConfiguration, + WidgetConfigurationType, } from '~/generated/graphql'; import { buildChartGroupByFieldConfigUpdate } from '@/command-menu/pages/page-layout/utils/buildChartGroupByFieldConfigUpdate'; @@ -12,7 +13,8 @@ describe('buildChartGroupByFieldConfigUpdate', () => { const result = buildChartGroupByFieldConfigUpdate({ configuration: { __typename: 'BarChartConfiguration', - } as BarChartConfiguration, + configurationType: WidgetConfigurationType.BAR_CHART, + } as TypedBarChartConfiguration, fieldMetadataIdKey: 'primaryAxisGroupByFieldMetadataId', subFieldNameKey: 'primaryAxisGroupBySubFieldName', fieldId: 'field', @@ -29,7 +31,8 @@ describe('buildChartGroupByFieldConfigUpdate', () => { const result = buildChartGroupByFieldConfigUpdate({ configuration: { __typename: 'BarChartConfiguration', - } as BarChartConfiguration, + configurationType: WidgetConfigurationType.BAR_CHART, + } as TypedBarChartConfiguration, fieldMetadataIdKey: 'primaryAxisGroupByFieldMetadataId', subFieldNameKey: 'primaryAxisGroupBySubFieldName', fieldId: null, @@ -46,7 +49,8 @@ describe('buildChartGroupByFieldConfigUpdate', () => { const result = buildChartGroupByFieldConfigUpdate({ configuration: { __typename: 'BarChartConfiguration', - } as BarChartConfiguration, + configurationType: WidgetConfigurationType.BAR_CHART, + } as TypedBarChartConfiguration, fieldMetadataIdKey: 'secondaryAxisGroupByFieldMetadataId', subFieldNameKey: 'secondaryAxisGroupBySubFieldName', fieldId: 'field', @@ -62,7 +66,8 @@ describe('buildChartGroupByFieldConfigUpdate', () => { const result = buildChartGroupByFieldConfigUpdate({ configuration: { __typename: 'PieChartConfiguration', - } as PieChartConfiguration, + configurationType: WidgetConfigurationType.PIE_CHART, + } as TypedPieChartConfiguration, fieldMetadataIdKey: 'groupByFieldMetadataId', subFieldNameKey: 'groupBySubFieldName', fieldId: 'field', diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/convertAggregateOperationForDateField.test.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/convertAggregateOperationForDateField.test.ts new file mode 100644 index 0000000000..e2c69d3a21 --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/convertAggregateOperationForDateField.test.ts @@ -0,0 +1,122 @@ +import { type ChartConfiguration } from '@/command-menu/pages/page-layout/types/ChartConfiguration'; +import { convertAggregateOperationForDateField } from '@/command-menu/pages/page-layout/utils/convertAggregateOperationForDateField'; +import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; +import { AggregateOperations } from '@/object-record/record-table/constants/AggregateOperations'; +import { FieldMetadataType } from 'twenty-shared/types'; +import { TEST_BAR_CHART_CONFIGURATION } from '~/testing/mock-data/widget-configurations'; + +const createMockObjectMetadataItem = ( + fields: { id: string; type: FieldMetadataType }[], +): ObjectMetadataItem => + ({ + id: 'object-1', + fields: fields.map((f) => ({ ...f, name: 'field' })), + }) as ObjectMetadataItem; + +describe('convertAggregateOperationForDateField', () => { + it('returns COUNT when date field uses MIN operation', () => { + const config: ChartConfiguration = { + ...TEST_BAR_CHART_CONFIGURATION, + aggregateFieldMetadataId: 'date-field', + aggregateOperation: AggregateOperations.MIN, + }; + + const objectMetadataItem = createMockObjectMetadataItem([ + { id: 'date-field', type: FieldMetadataType.DATE }, + ]); + + const result = convertAggregateOperationForDateField( + config, + objectMetadataItem, + ); + + expect(result).toBe(AggregateOperations.COUNT); + }); + + it('returns COUNT when datetime field uses MAX operation', () => { + const config: ChartConfiguration = { + ...TEST_BAR_CHART_CONFIGURATION, + aggregateFieldMetadataId: 'datetime-field', + aggregateOperation: AggregateOperations.MAX, + }; + + const objectMetadataItem = createMockObjectMetadataItem([ + { id: 'datetime-field', type: FieldMetadataType.DATE_TIME }, + ]); + + const result = convertAggregateOperationForDateField( + config, + objectMetadataItem, + ); + + expect(result).toBe(AggregateOperations.COUNT); + }); + + it('returns undefined when field is not a date type', () => { + const config: ChartConfiguration = { + ...TEST_BAR_CHART_CONFIGURATION, + aggregateFieldMetadataId: 'number-field', + aggregateOperation: AggregateOperations.MIN, + }; + + const objectMetadataItem = createMockObjectMetadataItem([ + { id: 'number-field', type: FieldMetadataType.NUMBER }, + ]); + + const result = convertAggregateOperationForDateField( + config, + objectMetadataItem, + ); + + expect(result).toBeUndefined(); + }); + + it('returns undefined when operation is not MIN or MAX', () => { + const config: ChartConfiguration = { + ...TEST_BAR_CHART_CONFIGURATION, + aggregateFieldMetadataId: 'date-field', + aggregateOperation: AggregateOperations.SUM, + }; + + const objectMetadataItem = createMockObjectMetadataItem([ + { id: 'date-field', type: FieldMetadataType.DATE }, + ]); + + const result = convertAggregateOperationForDateField( + config, + objectMetadataItem, + ); + + expect(result).toBeUndefined(); + }); + + it('returns undefined when objectMetadataItem is undefined', () => { + const config: ChartConfiguration = { + ...TEST_BAR_CHART_CONFIGURATION, + aggregateOperation: AggregateOperations.MIN, + }; + + const result = convertAggregateOperationForDateField(config, undefined); + + expect(result).toBeUndefined(); + }); + + it('returns undefined when field is not found', () => { + const config: ChartConfiguration = { + ...TEST_BAR_CHART_CONFIGURATION, + aggregateFieldMetadataId: 'missing-field', + aggregateOperation: AggregateOperations.MIN, + }; + + const objectMetadataItem = createMockObjectMetadataItem([ + { id: 'other-field', type: FieldMetadataType.DATE }, + ]); + + const result = convertAggregateOperationForDateField( + config, + objectMetadataItem, + ); + + expect(result).toBeUndefined(); + }); +}); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/convertBarOrLineChartConfigToPieChart.test.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/convertBarOrLineChartConfigToPieChart.test.ts new file mode 100644 index 0000000000..91ba529448 --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/convertBarOrLineChartConfigToPieChart.test.ts @@ -0,0 +1,47 @@ +import { convertBarOrLineChartConfigToPieChart } from '@/command-menu/pages/page-layout/utils/convertBarOrLineChartConfigToPieChart'; +import { type BarChartConfiguration } from '~/generated/graphql'; +import { + TEST_BAR_CHART_CONFIGURATION, + TEST_LINE_CHART_CONFIGURATION, + TEST_PIE_CHART_CONFIGURATION, +} from '~/testing/mock-data/widget-configurations'; + +describe('convertBarOrLineChartConfigToPieChart', () => { + it('maps bar chart fields to pie chart equivalents', () => { + const result = convertBarOrLineChartConfigToPieChart( + TEST_BAR_CHART_CONFIGURATION, + ); + + expect(result).toEqual({ + groupByFieldMetadataId: + TEST_BAR_CHART_CONFIGURATION.primaryAxisGroupByFieldMetadataId, + groupBySubFieldName: + TEST_BAR_CHART_CONFIGURATION.primaryAxisGroupBySubFieldName, + dateGranularity: TEST_BAR_CHART_CONFIGURATION.primaryAxisDateGranularity, + orderBy: TEST_BAR_CHART_CONFIGURATION.primaryAxisOrderBy, + }); + }); + + it('maps line chart fields to pie chart equivalents', () => { + const result = convertBarOrLineChartConfigToPieChart( + TEST_LINE_CHART_CONFIGURATION, + ); + + expect(result).toEqual({ + groupByFieldMetadataId: + TEST_LINE_CHART_CONFIGURATION.primaryAxisGroupByFieldMetadataId, + groupBySubFieldName: + TEST_LINE_CHART_CONFIGURATION.primaryAxisGroupBySubFieldName, + dateGranularity: TEST_LINE_CHART_CONFIGURATION.primaryAxisDateGranularity, + orderBy: TEST_LINE_CHART_CONFIGURATION.primaryAxisOrderBy, + }); + }); + + it('returns empty object for non-bar/line chart configuration', () => { + const result = convertBarOrLineChartConfigToPieChart( + TEST_PIE_CHART_CONFIGURATION as unknown as BarChartConfiguration, + ); + + expect(result).toEqual({}); + }); +}); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/convertPieChartConfigToBarOrLineChart.test.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/convertPieChartConfigToBarOrLineChart.test.ts new file mode 100644 index 0000000000..4154ac3dee --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/convertPieChartConfigToBarOrLineChart.test.ts @@ -0,0 +1,50 @@ +import { convertPieChartConfigToBarOrLineChart } from '@/command-menu/pages/page-layout/utils/convertPieChartConfigToBarOrLineChart'; +import { type PieChartConfiguration } from '~/generated/graphql'; +import { + TEST_BAR_CHART_CONFIGURATION, + TEST_PIE_CHART_CONFIGURATION, +} from '~/testing/mock-data/widget-configurations'; + +describe('convertPieChartConfigToBarOrLineChart', () => { + it('maps pie chart fields to bar/line chart equivalents', () => { + const result = convertPieChartConfigToBarOrLineChart( + TEST_PIE_CHART_CONFIGURATION, + ); + + expect(result).toEqual({ + primaryAxisGroupByFieldMetadataId: + TEST_PIE_CHART_CONFIGURATION.groupByFieldMetadataId, + primaryAxisGroupBySubFieldName: + TEST_PIE_CHART_CONFIGURATION.groupBySubFieldName, + primaryAxisDateGranularity: TEST_PIE_CHART_CONFIGURATION.dateGranularity, + primaryAxisOrderBy: TEST_PIE_CHART_CONFIGURATION.orderBy, + }); + }); + + it('handles null/undefined fields', () => { + const minimalPieConfig: PieChartConfiguration = { + ...TEST_PIE_CHART_CONFIGURATION, + groupByFieldMetadataId: undefined as unknown as string, + groupBySubFieldName: null, + dateGranularity: null, + orderBy: null, + }; + + const result = convertPieChartConfigToBarOrLineChart(minimalPieConfig); + + expect(result).toEqual({ + primaryAxisGroupByFieldMetadataId: undefined, + primaryAxisGroupBySubFieldName: null, + primaryAxisDateGranularity: null, + primaryAxisOrderBy: null, + }); + }); + + it('returns empty object for non-pie chart configuration', () => { + const result = convertPieChartConfigToBarOrLineChart( + TEST_BAR_CHART_CONFIGURATION as unknown as PieChartConfiguration, + ); + + expect(result).toEqual({}); + }); +}); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/getBarChartSettings.test.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/getBarChartSettings.test.ts index dbf3fea6e0..9775f7f83a 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/getBarChartSettings.test.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/getBarChartSettings.test.ts @@ -17,14 +17,14 @@ import { RANGE_MIN_SETTING } from '@/command-menu/pages/page-layout/constants/se import { SHOW_LEGEND_SETTING } from '@/command-menu/pages/page-layout/constants/settings/ShowLegendSetting'; import { SORT_BY_GROUP_BY_FIELD_SETTING } from '@/command-menu/pages/page-layout/constants/settings/SortByGroupByFieldSetting'; import { STACKED_BARS_SETTING } from '@/command-menu/pages/page-layout/constants/settings/StackedBarsSetting'; -import { IconAxisX, IconAxisY } from 'twenty-ui/display'; -import { GraphType } from '~/generated-metadata/graphql'; import { getBarChartSettings } from '@/command-menu/pages/page-layout/utils/getBarChartSettings'; +import { IconAxisX, IconAxisY } from 'twenty-ui/display'; +import { BarChartLayout } from '~/generated/graphql'; describe('getBarChartSettings', () => { describe('Vertical bar chart', () => { it('should place primary axis items under "X axis" heading', () => { - const result = getBarChartSettings(GraphType.VERTICAL_BAR); + const result = getBarChartSettings(BarChartLayout.VERTICAL); const xAxisGroup = result.find( (group) => group.heading.id === CHART_SETTINGS_HEADINGS.X_AXIS.id, @@ -41,7 +41,7 @@ describe('getBarChartSettings', () => { }); it('should place secondary axis items under "Y axis" heading', () => { - const result = getBarChartSettings(GraphType.VERTICAL_BAR); + const result = getBarChartSettings(BarChartLayout.VERTICAL); const yAxisGroup = result.find( (group) => group.heading.id === CHART_SETTINGS_HEADINGS.Y_AXIS.id, @@ -61,7 +61,7 @@ describe('getBarChartSettings', () => { }); it('should have all expected groups in correct order', () => { - const result = getBarChartSettings(GraphType.VERTICAL_BAR); + const result = getBarChartSettings(BarChartLayout.VERTICAL); expect(result).toHaveLength(4); expect(result[0].heading).toBe(CHART_SETTINGS_HEADINGS.DATA); @@ -73,7 +73,7 @@ describe('getBarChartSettings', () => { describe('Horizontal bar chart', () => { it('should place SECONDARY axis items under "X axis" heading', () => { - const result = getBarChartSettings(GraphType.HORIZONTAL_BAR); + const result = getBarChartSettings(BarChartLayout.HORIZONTAL); const xAxisGroup = result.find( (group) => group.heading.id === CHART_SETTINGS_HEADINGS.X_AXIS.id, @@ -93,7 +93,7 @@ describe('getBarChartSettings', () => { }); it('should place PRIMARY axis items under "Y axis" heading', () => { - const result = getBarChartSettings(GraphType.HORIZONTAL_BAR); + const result = getBarChartSettings(BarChartLayout.HORIZONTAL); const yAxisGroup = result.find( (group) => group.heading.id === CHART_SETTINGS_HEADINGS.Y_AXIS.id, @@ -110,7 +110,7 @@ describe('getBarChartSettings', () => { }); it('should have all expected groups in correct order', () => { - const result = getBarChartSettings(GraphType.HORIZONTAL_BAR); + const result = getBarChartSettings(BarChartLayout.HORIZONTAL); expect(result).toHaveLength(4); expect(result[0].heading).toBe(CHART_SETTINGS_HEADINGS.DATA); @@ -122,8 +122,8 @@ describe('getBarChartSettings', () => { describe('Common groups', () => { it('should have consistent Data group for both orientations', () => { - const verticalResult = getBarChartSettings(GraphType.VERTICAL_BAR); - const horizontalResult = getBarChartSettings(GraphType.HORIZONTAL_BAR); + const verticalResult = getBarChartSettings(BarChartLayout.VERTICAL); + const horizontalResult = getBarChartSettings(BarChartLayout.HORIZONTAL); const verticalDataGroup = verticalResult.find( (group) => group.heading.id === CHART_SETTINGS_HEADINGS.DATA.id, @@ -140,8 +140,8 @@ describe('getBarChartSettings', () => { }); it('should have consistent Style group for both orientations', () => { - const verticalResult = getBarChartSettings(GraphType.VERTICAL_BAR); - const horizontalResult = getBarChartSettings(GraphType.HORIZONTAL_BAR); + const verticalResult = getBarChartSettings(BarChartLayout.VERTICAL); + const horizontalResult = getBarChartSettings(BarChartLayout.HORIZONTAL); const verticalStyleGroup = verticalResult.find( (group) => group.heading.id === CHART_SETTINGS_HEADINGS.STYLE.id, diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/getChartLimitMessage.test.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/getChartLimitMessage.test.ts new file mode 100644 index 0000000000..79666de152 --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/getChartLimitMessage.test.ts @@ -0,0 +1,67 @@ +import { getChartLimitMessage } from '@/command-menu/pages/page-layout/utils/getChartLimitMessage'; +import { ObjectRecordGroupByDateGranularity } from 'twenty-shared/types'; +import { WidgetConfigurationType } from '~/generated/graphql'; + +describe('getChartLimitMessage', () => { + it('returns date-based message for bar chart with date axis', () => { + const result = getChartLimitMessage({ + widgetConfigurationType: WidgetConfigurationType.BAR_CHART, + isPrimaryAxisDate: true, + primaryAxisDateGranularity: ObjectRecordGroupByDateGranularity.DAY, + }); + + expect(result).toContain('Undisplayed data'); + expect(result).toContain('days'); + expect(result).toContain('per chart'); + }); + + it('returns bars message for bar chart without date axis', () => { + const result = getChartLimitMessage({ + widgetConfigurationType: WidgetConfigurationType.BAR_CHART, + isPrimaryAxisDate: false, + primaryAxisDateGranularity: null, + }); + + expect(result).toContain('bars per chart'); + }); + + it('returns data points message for line chart without date axis', () => { + const result = getChartLimitMessage({ + widgetConfigurationType: WidgetConfigurationType.LINE_CHART, + isPrimaryAxisDate: false, + primaryAxisDateGranularity: null, + }); + + expect(result).toContain('data points per chart'); + }); + + it('returns slices message for pie chart', () => { + const result = getChartLimitMessage({ + widgetConfigurationType: WidgetConfigurationType.PIE_CHART, + isPrimaryAxisDate: false, + primaryAxisDateGranularity: null, + }); + + expect(result).toContain('slices per chart'); + }); + + it('returns date-based message with weeks granularity', () => { + const result = getChartLimitMessage({ + widgetConfigurationType: WidgetConfigurationType.LINE_CHART, + isPrimaryAxisDate: true, + primaryAxisDateGranularity: ObjectRecordGroupByDateGranularity.WEEK, + }); + + expect(result).toContain('weeks'); + }); + + it('returns slices message when isPrimaryAxisDate is true but granularity is null', () => { + const result = getChartLimitMessage({ + widgetConfigurationType: WidgetConfigurationType.PIE_CHART, + isPrimaryAxisDate: true, + primaryAxisDateGranularity: null, + }); + + expect(result).toContain('slices per chart'); + }); +}); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/getConfigurationTypeFromGraphType.test.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/getConfigurationTypeFromGraphType.test.ts new file mode 100644 index 0000000000..42579ce48c --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/getConfigurationTypeFromGraphType.test.ts @@ -0,0 +1,46 @@ +import { GraphType } from '@/command-menu/pages/page-layout/types/GraphType'; +import { getConfigurationTypeFromGraphType } from '@/command-menu/pages/page-layout/utils/getConfigurationTypeFromGraphType'; +import { WidgetConfigurationType } from '~/generated/graphql'; + +describe('getConfigurationTypeFromGraphType', () => { + it('returns BAR_CHART for VERTICAL_BAR', () => { + expect(getConfigurationTypeFromGraphType(GraphType.VERTICAL_BAR)).toBe( + WidgetConfigurationType.BAR_CHART, + ); + }); + + it('returns BAR_CHART for HORIZONTAL_BAR', () => { + expect(getConfigurationTypeFromGraphType(GraphType.HORIZONTAL_BAR)).toBe( + WidgetConfigurationType.BAR_CHART, + ); + }); + + it('returns LINE_CHART for LINE', () => { + expect(getConfigurationTypeFromGraphType(GraphType.LINE)).toBe( + WidgetConfigurationType.LINE_CHART, + ); + }); + + it('returns PIE_CHART for PIE', () => { + expect(getConfigurationTypeFromGraphType(GraphType.PIE)).toBe( + WidgetConfigurationType.PIE_CHART, + ); + }); + + it('returns AGGREGATE_CHART for AGGREGATE', () => { + expect(getConfigurationTypeFromGraphType(GraphType.AGGREGATE)).toBe( + WidgetConfigurationType.AGGREGATE_CHART, + ); + }); + + it('returns GAUGE_CHART for GAUGE', () => { + expect(getConfigurationTypeFromGraphType(GraphType.GAUGE)).toBe( + WidgetConfigurationType.GAUGE_CHART, + ); + }); + + it('throws for unknown graph type', () => { + const unknownGraphType = 'UNKNOWN' as never; + expect(() => getConfigurationTypeFromGraphType(unknownGraphType)).toThrow(); + }); +}); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/getCurrentGraphTypeFromConfig.test.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/getCurrentGraphTypeFromConfig.test.ts new file mode 100644 index 0000000000..95c1edbb2b --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/getCurrentGraphTypeFromConfig.test.ts @@ -0,0 +1,71 @@ +import { type ChartConfiguration } from '@/command-menu/pages/page-layout/types/ChartConfiguration'; +import { GraphType } from '@/command-menu/pages/page-layout/types/GraphType'; +import { getCurrentGraphTypeFromConfig } from '@/command-menu/pages/page-layout/utils/getCurrentGraphTypeFromConfig'; +import { + TEST_AGGREGATE_CHART_CONFIGURATION, + TEST_BAR_CHART_CONFIGURATION, + TEST_BAR_CHART_CONFIGURATION_HORIZONTAL, + TEST_GAUGE_CHART_CONFIGURATION, + TEST_LINE_CHART_CONFIGURATION, + TEST_PIE_CHART_CONFIGURATION, +} from '~/testing/mock-data/widget-configurations'; + +describe('getCurrentGraphTypeFromConfig', () => { + it('returns VERTICAL_BAR for bar chart with vertical layout', () => { + expect( + getCurrentGraphTypeFromConfig( + TEST_BAR_CHART_CONFIGURATION as ChartConfiguration, + ), + ).toBe(GraphType.VERTICAL_BAR); + }); + + it('returns HORIZONTAL_BAR for bar chart with horizontal layout', () => { + expect( + getCurrentGraphTypeFromConfig( + TEST_BAR_CHART_CONFIGURATION_HORIZONTAL as ChartConfiguration, + ), + ).toBe(GraphType.HORIZONTAL_BAR); + }); + + it('returns LINE for line chart', () => { + expect( + getCurrentGraphTypeFromConfig( + TEST_LINE_CHART_CONFIGURATION as ChartConfiguration, + ), + ).toBe(GraphType.LINE); + }); + + it('returns PIE for pie chart', () => { + expect( + getCurrentGraphTypeFromConfig( + TEST_PIE_CHART_CONFIGURATION as ChartConfiguration, + ), + ).toBe(GraphType.PIE); + }); + + it('returns AGGREGATE for aggregate chart', () => { + expect( + getCurrentGraphTypeFromConfig( + TEST_AGGREGATE_CHART_CONFIGURATION as ChartConfiguration, + ), + ).toBe(GraphType.AGGREGATE); + }); + + it('returns GAUGE for gauge chart', () => { + expect( + getCurrentGraphTypeFromConfig( + TEST_GAUGE_CHART_CONFIGURATION as ChartConfiguration, + ), + ).toBe(GraphType.GAUGE); + }); + + it('throws for unknown configuration type', () => { + const unknownConfig = { + __typename: 'UnknownConfiguration', + } as unknown as ChartConfiguration; + + expect(() => getCurrentGraphTypeFromConfig(unknownConfig)).toThrow( + 'Unknown chart configuration type', + ); + }); +}); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/getDateGranularityPluralLabel.test.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/getDateGranularityPluralLabel.test.ts new file mode 100644 index 0000000000..144a979acb --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/getDateGranularityPluralLabel.test.ts @@ -0,0 +1,64 @@ +import { getDateGranularityPluralLabel } from '@/command-menu/pages/page-layout/utils/getDateGranularityPluralLabel'; +import { ObjectRecordGroupByDateGranularity } from 'twenty-shared/types'; + +describe('getDateGranularityPluralLabel', () => { + it('returns "days" for DAY granularity', () => { + expect( + getDateGranularityPluralLabel(ObjectRecordGroupByDateGranularity.DAY), + ).toBe('days'); + }); + + it('returns "weeks" for WEEK granularity', () => { + expect( + getDateGranularityPluralLabel(ObjectRecordGroupByDateGranularity.WEEK), + ).toBe('weeks'); + }); + + it('returns "months" for MONTH granularity', () => { + expect( + getDateGranularityPluralLabel(ObjectRecordGroupByDateGranularity.MONTH), + ).toBe('months'); + }); + + it('returns "quarters" for QUARTER granularity', () => { + expect( + getDateGranularityPluralLabel(ObjectRecordGroupByDateGranularity.QUARTER), + ).toBe('quarters'); + }); + + it('returns "years" for YEAR granularity', () => { + expect( + getDateGranularityPluralLabel(ObjectRecordGroupByDateGranularity.YEAR), + ).toBe('years'); + }); + + it('returns "days" for DAY_OF_THE_WEEK granularity', () => { + expect( + getDateGranularityPluralLabel( + ObjectRecordGroupByDateGranularity.DAY_OF_THE_WEEK, + ), + ).toBe('days'); + }); + + it('returns "months" for MONTH_OF_THE_YEAR granularity', () => { + expect( + getDateGranularityPluralLabel( + ObjectRecordGroupByDateGranularity.MONTH_OF_THE_YEAR, + ), + ).toBe('months'); + }); + + it('returns "quarters" for QUARTER_OF_THE_YEAR granularity', () => { + expect( + getDateGranularityPluralLabel( + ObjectRecordGroupByDateGranularity.QUARTER_OF_THE_YEAR, + ), + ).toBe('quarters'); + }); + + it('returns "items" for NONE granularity', () => { + expect( + getDateGranularityPluralLabel(ObjectRecordGroupByDateGranularity.NONE), + ).toBe('items'); + }); +}); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isAggregateChartConfiguration.test.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isAggregateChartConfiguration.test.ts deleted file mode 100644 index 27d7d6fb47..0000000000 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isAggregateChartConfiguration.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { - type AggregateChartConfiguration, - type BarChartConfiguration, -} from '~/generated/graphql'; -import { isAggregateChartConfiguration } from '@/command-menu/pages/page-layout/utils/isAggregateChartConfiguration'; - -describe('isAggregateChartConfiguration', () => { - it('should return true for AggregateChartConfiguration', () => { - const configuration = { - __typename: 'AggregateChartConfiguration', - } as AggregateChartConfiguration; - - expect(isAggregateChartConfiguration(configuration)).toBe(true); - }); - - it('should return false for BarChartConfiguration', () => { - const configuration = { - __typename: 'BarChartConfiguration', - } as BarChartConfiguration; - - expect(isAggregateChartConfiguration(configuration)).toBe(false); - }); - - it('should return false for null', () => { - expect(isAggregateChartConfiguration(null)).toBe(false); - }); - - it('should return false for undefined', () => { - expect(isAggregateChartConfiguration(undefined)).toBe(false); - }); -}); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isBarChartConfiguration.test.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isBarChartConfiguration.test.ts deleted file mode 100644 index 44a6019855..0000000000 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isBarChartConfiguration.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { - type BarChartConfiguration, - type LineChartConfiguration, -} from '~/generated/graphql'; -import { isBarChartConfiguration } from '@/command-menu/pages/page-layout/utils/isBarChartConfiguration'; - -describe('isBarChartConfiguration', () => { - it('should return true for BarChartConfiguration', () => { - const configuration = { - __typename: 'BarChartConfiguration' as const, - } as BarChartConfiguration; - - expect(isBarChartConfiguration(configuration)).toBe(true); - }); - - it('should return false for LineChartConfiguration', () => { - const configuration = { - __typename: 'LineChartConfiguration' as const, - } as LineChartConfiguration; - - expect(isBarChartConfiguration(configuration)).toBe(false); - }); - - it('should return false for null', () => { - expect(isBarChartConfiguration(null)).toBe(false); - }); - - it('should return false for undefined', () => { - expect(isBarChartConfiguration(undefined)).toBe(false); - }); -}); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isBarOrLineChartConfiguration.test.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isBarOrLineChartConfiguration.test.ts deleted file mode 100644 index beb40a69db..0000000000 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isBarOrLineChartConfiguration.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { - type BarChartConfiguration, - type LineChartConfiguration, - type PieChartConfiguration, -} from '~/generated/graphql'; -import { isBarOrLineChartConfiguration } from '@/command-menu/pages/page-layout/utils/isBarOrLineChartConfiguration'; - -describe('isBarOrLineChartConfiguration', () => { - it('should return true for BarChartConfiguration', () => { - const configuration = { - __typename: 'BarChartConfiguration', - } as BarChartConfiguration; - - expect(isBarOrLineChartConfiguration(configuration)).toBe(true); - }); - - it('should return true for LineChartConfiguration', () => { - const configuration = { - __typename: 'LineChartConfiguration', - } as LineChartConfiguration; - - expect(isBarOrLineChartConfiguration(configuration)).toBe(true); - }); - - it('should return false for PieChartConfiguration', () => { - const configuration = { - __typename: 'PieChartConfiguration', - } as PieChartConfiguration; - - expect(isBarOrLineChartConfiguration(configuration)).toBe(false); - }); - - it('should return false for null', () => { - expect(isBarOrLineChartConfiguration(null)).toBe(false); - }); - - it('should return false for undefined', () => { - expect(isBarOrLineChartConfiguration(undefined)).toBe(false); - }); -}); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isChartConfiguration.test.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isChartConfiguration.test.ts deleted file mode 100644 index a397aa8e96..0000000000 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isChartConfiguration.test.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { - type AggregateChartConfiguration, - type BarChartConfiguration, - type GaugeChartConfiguration, - type IframeConfiguration, - type LineChartConfiguration, - type PieChartConfiguration, - type StandaloneRichTextConfiguration, -} from '~/generated/graphql'; -import { isChartConfiguration } from '@/command-menu/pages/page-layout/utils/isChartConfiguration'; - -describe('isChartConfiguration', () => { - it('should return true for BarChartConfiguration', () => { - const configuration = { - __typename: 'BarChartConfiguration', - } as BarChartConfiguration; - - expect(isChartConfiguration(configuration)).toBe(true); - }); - - it('should return true for LineChartConfiguration', () => { - const configuration = { - __typename: 'LineChartConfiguration', - } as LineChartConfiguration; - - expect(isChartConfiguration(configuration)).toBe(true); - }); - - it('should return true for PieChartConfiguration', () => { - const configuration = { - __typename: 'PieChartConfiguration', - } as PieChartConfiguration; - - expect(isChartConfiguration(configuration)).toBe(true); - }); - - it('should return true for AggregateChartConfiguration', () => { - const configuration = { - __typename: 'AggregateChartConfiguration', - } as AggregateChartConfiguration; - - expect(isChartConfiguration(configuration)).toBe(true); - }); - - it('should return true for GaugeChartConfiguration', () => { - const configuration = { - __typename: 'GaugeChartConfiguration', - } as GaugeChartConfiguration; - - expect(isChartConfiguration(configuration)).toBe(true); - }); - - it('should return false for IframeConfiguration', () => { - const configuration = { - __typename: 'IframeConfiguration', - } as IframeConfiguration; - - expect(isChartConfiguration(configuration)).toBe(false); - }); - - it('should return false for StandaloneRichTextConfiguration', () => { - const configuration = { - __typename: 'StandaloneRichTextConfiguration', - } as StandaloneRichTextConfiguration; - - expect(isChartConfiguration(configuration)).toBe(false); - }); - - it('should return false for null', () => { - expect(isChartConfiguration(null)).toBe(false); - }); - - it('should return false for undefined', () => { - expect(isChartConfiguration(undefined)).toBe(false); - }); -}); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isChartWidget.test.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isChartWidget.test.ts new file mode 100644 index 0000000000..6e0fb86064 --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isChartWidget.test.ts @@ -0,0 +1,51 @@ +import { isChartWidget } from '@/command-menu/pages/page-layout/utils/isChartWidget'; +import { + createTestWidget, + TEST_BAR_CHART_CONFIGURATION, + TEST_FIELDS_CONFIGURATION, + TEST_IFRAME_CONFIGURATION, + TEST_LINE_CHART_CONFIGURATION, + TEST_PIE_CHART_CONFIGURATION, +} from '~/testing/mock-data/widget-configurations'; + +describe('isChartWidget', () => { + it('returns true for widget with BarChartConfiguration', () => { + const widget = createTestWidget({ + configuration: TEST_BAR_CHART_CONFIGURATION, + }); + + expect(isChartWidget(widget)).toBe(true); + }); + + it('returns true for widget with LineChartConfiguration', () => { + const widget = createTestWidget({ + configuration: TEST_LINE_CHART_CONFIGURATION, + }); + + expect(isChartWidget(widget)).toBe(true); + }); + + it('returns true for widget with PieChartConfiguration', () => { + const widget = createTestWidget({ + configuration: TEST_PIE_CHART_CONFIGURATION, + }); + + expect(isChartWidget(widget)).toBe(true); + }); + + it('returns false for widget with IframeConfiguration', () => { + const widget = createTestWidget({ + configuration: TEST_IFRAME_CONFIGURATION, + }); + + expect(isChartWidget(widget)).toBe(false); + }); + + it('returns false for widget with FieldsConfiguration', () => { + const widget = createTestWidget({ + configuration: TEST_FIELDS_CONFIGURATION, + }); + + expect(isChartWidget(widget)).toBe(false); + }); +}); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isGaugeChartConfiguration.test.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isGaugeChartConfiguration.test.ts deleted file mode 100644 index 44885060c2..0000000000 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isGaugeChartConfiguration.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { - type BarChartConfiguration, - type GaugeChartConfiguration, -} from '~/generated/graphql'; -import { isGaugeChartConfiguration } from '@/command-menu/pages/page-layout/utils/isGaugeChartConfiguration'; - -describe('isGaugeChartConfiguration', () => { - it('should return true for GaugeChartConfiguration', () => { - const configuration = { - __typename: 'GaugeChartConfiguration', - } as GaugeChartConfiguration; - - expect(isGaugeChartConfiguration(configuration)).toBe(true); - }); - - it('should return false for BarChartConfiguration', () => { - const configuration = { - __typename: 'BarChartConfiguration', - } as BarChartConfiguration; - - expect(isGaugeChartConfiguration(configuration)).toBe(false); - }); - - it('should return false for null', () => { - expect(isGaugeChartConfiguration(null)).toBe(false); - }); - - it('should return false for undefined', () => { - expect(isGaugeChartConfiguration(undefined)).toBe(false); - }); -}); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isIframeConfiguration.test.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isIframeConfiguration.test.ts deleted file mode 100644 index 9ba3af45c6..0000000000 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isIframeConfiguration.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { - type BarChartConfiguration, - type IframeConfiguration, -} from '~/generated/graphql'; -import { isIframeConfiguration } from '@/command-menu/pages/page-layout/utils/isIframeConfiguration'; - -describe('isIframeConfiguration', () => { - it('should return true for IframeConfiguration', () => { - const configuration = { - __typename: 'IframeConfiguration', - } as IframeConfiguration; - - expect(isIframeConfiguration(configuration)).toBe(true); - }); - - it('should return false for BarChartConfiguration', () => { - const configuration = { - __typename: 'BarChartConfiguration', - } as BarChartConfiguration; - - expect(isIframeConfiguration(configuration)).toBe(false); - }); - - it('should return false for null', () => { - expect(isIframeConfiguration(null)).toBe(false); - }); - - it('should return false for undefined', () => { - expect(isIframeConfiguration(undefined)).toBe(false); - }); -}); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isLineChartConfiguration.test.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isLineChartConfiguration.test.ts deleted file mode 100644 index ad2e0a9c44..0000000000 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isLineChartConfiguration.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { - type BarChartConfiguration, - type LineChartConfiguration, -} from '~/generated/graphql'; -import { isLineChartConfiguration } from '@/command-menu/pages/page-layout/utils/isLineChartConfiguration'; - -describe('isLineChartConfiguration', () => { - it('should return true for LineChartConfiguration', () => { - const configuration = { - __typename: 'LineChartConfiguration', - } as LineChartConfiguration; - - expect(isLineChartConfiguration(configuration)).toBe(true); - }); - - it('should return false for BarChartConfiguration', () => { - const configuration = { - __typename: 'BarChartConfiguration', - } as BarChartConfiguration; - - expect(isLineChartConfiguration(configuration)).toBe(false); - }); - - it('should return false for null', () => { - expect(isLineChartConfiguration(null)).toBe(false); - }); - - it('should return false for undefined', () => { - expect(isLineChartConfiguration(undefined)).toBe(false); - }); -}); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isMinMaxRangeValid.test.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isMinMaxRangeValid.test.ts index bf1afb90de..9e8adbc28b 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isMinMaxRangeValid.test.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isMinMaxRangeValid.test.ts @@ -1,12 +1,13 @@ import { type ChartConfiguration } from '@/command-menu/pages/page-layout/types/ChartConfiguration'; import { CHART_CONFIGURATION_SETTING_IDS } from '@/command-menu/pages/page-layout/types/ChartConfigurationSettingIds'; import { isMinMaxRangeValid } from '@/command-menu/pages/page-layout/utils/isMinMaxRangeValid'; -import { GraphType } from '~/generated/graphql'; +import { BarChartLayout, WidgetConfigurationType } from '~/generated/graphql'; describe('isMinMaxRangeValid', () => { const mockConfiguration = { __typename: 'BarChartConfiguration', - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, + layout: BarChartLayout.VERTICAL, rangeMin: 10, rangeMax: 100, } as ChartConfiguration; @@ -78,7 +79,8 @@ describe('isMinMaxRangeValid', () => { it('should be valid when new rangeMin is negative and greater than existing rangeMax', () => { const configWithNegatives = { __typename: 'BarChartConfiguration', - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, + layout: BarChartLayout.VERTICAL, rangeMin: -100, rangeMax: -10, } as ChartConfiguration; @@ -101,7 +103,8 @@ describe('isMinMaxRangeValid', () => { it('should be valid when new rangeMin is zero and greater than existing rangeMax', () => { const configWithZero = { __typename: 'BarChartConfiguration', - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, + layout: BarChartLayout.VERTICAL, rangeMin: 0, rangeMax: 100, } as ChartConfiguration; @@ -119,7 +122,8 @@ describe('isMinMaxRangeValid', () => { it('should be valid when setting rangeMin on configuration without any range values', () => { const emptyConfig = { __typename: 'BarChartConfiguration', - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, + layout: BarChartLayout.VERTICAL, } as ChartConfiguration; const result = isMinMaxRangeValid( @@ -134,7 +138,8 @@ describe('isMinMaxRangeValid', () => { it('should be valid when setting rangeMax on configuration without any range values', () => { const emptyConfig = { __typename: 'BarChartConfiguration', - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, + layout: BarChartLayout.VERTICAL, } as ChartConfiguration; const result = isMinMaxRangeValid( @@ -149,7 +154,8 @@ describe('isMinMaxRangeValid', () => { it('should be valid when setting rangeMin without existing rangeMax', () => { const configWithOnlyMin = { __typename: 'BarChartConfiguration', - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, + layout: BarChartLayout.VERTICAL, rangeMin: 10, } as ChartConfiguration; @@ -165,7 +171,8 @@ describe('isMinMaxRangeValid', () => { it('should be valid when setting rangeMax without existing rangeMin', () => { const configWithOnlyMax = { __typename: 'BarChartConfiguration', - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, + layout: BarChartLayout.VERTICAL, rangeMax: 100, } as ChartConfiguration; @@ -181,7 +188,8 @@ describe('isMinMaxRangeValid', () => { it('should be invalid when setting rangeMin that would exceed existing rangeMax', () => { const configWithOnlyMax = { __typename: 'BarChartConfiguration', - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, + layout: BarChartLayout.VERTICAL, rangeMax: 100, } as ChartConfiguration; @@ -197,7 +205,8 @@ describe('isMinMaxRangeValid', () => { it('should be invalid when setting rangeMax that would be less than existing rangeMin', () => { const configWithOnlyMin = { __typename: 'BarChartConfiguration', - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, + layout: BarChartLayout.VERTICAL, rangeMin: 50, } as ChartConfiguration; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isPieChartConfiguration.test.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isPieChartConfiguration.test.ts deleted file mode 100644 index 52bb99b6c5..0000000000 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isPieChartConfiguration.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { - type BarChartConfiguration, - type PieChartConfiguration, -} from '~/generated/graphql'; -import { isPieChartConfiguration } from '@/command-menu/pages/page-layout/utils/isPieChartConfiguration'; - -describe('isPieChartConfiguration', () => { - it('should return true for PieChartConfiguration', () => { - const configuration = { - __typename: 'PieChartConfiguration', - } as PieChartConfiguration; - - expect(isPieChartConfiguration(configuration)).toBe(true); - }); - - it('should return false for BarChartConfiguration', () => { - const configuration = { - __typename: 'BarChartConfiguration', - } as BarChartConfiguration; - - expect(isPieChartConfiguration(configuration)).toBe(false); - }); - - it('should return false for null', () => { - expect(isPieChartConfiguration(null)).toBe(false); - }); - - it('should return false for undefined', () => { - expect(isPieChartConfiguration(undefined)).toBe(false); - }); -}); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isWidgetConfigurationOfType.test.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isWidgetConfigurationOfType.test.ts new file mode 100644 index 0000000000..924c6d001f --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isWidgetConfigurationOfType.test.ts @@ -0,0 +1,75 @@ +import { isWidgetConfigurationOfType } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfType'; +import { + ALL_CHART_CONFIGURATIONS, + TEST_BAR_CHART_CONFIGURATION, + TEST_IFRAME_CONFIGURATION, + TEST_PIE_CHART_CONFIGURATION, + TEST_STANDALONE_RICH_TEXT_CONFIGURATION, +} from '~/testing/mock-data/widget-configurations'; + +describe('isWidgetConfigurationOfType', () => { + it('returns true when __typename matches', () => { + expect( + isWidgetConfigurationOfType( + TEST_BAR_CHART_CONFIGURATION, + 'BarChartConfiguration', + ), + ).toBe(true); + }); + + it('returns false when __typename does not match', () => { + expect( + isWidgetConfigurationOfType( + TEST_BAR_CHART_CONFIGURATION, + 'PieChartConfiguration', + ), + ).toBe(false); + }); + + it('returns false for null configuration', () => { + expect(isWidgetConfigurationOfType(null, 'BarChartConfiguration')).toBe( + false, + ); + }); + + it('returns false for undefined configuration', () => { + expect( + isWidgetConfigurationOfType(undefined, 'BarChartConfiguration'), + ).toBe(false); + }); + + it('correctly identifies PieChartConfiguration', () => { + expect( + isWidgetConfigurationOfType( + TEST_PIE_CHART_CONFIGURATION, + 'PieChartConfiguration', + ), + ).toBe(true); + }); + + it('correctly identifies IframeConfiguration', () => { + expect( + isWidgetConfigurationOfType( + TEST_IFRAME_CONFIGURATION, + 'IframeConfiguration', + ), + ).toBe(true); + }); + + it('correctly identifies StandaloneRichTextConfiguration', () => { + expect( + isWidgetConfigurationOfType( + TEST_STANDALONE_RICH_TEXT_CONFIGURATION, + 'StandaloneRichTextConfiguration', + ), + ).toBe(true); + }); + + it('works with all chart configuration types', () => { + ALL_CHART_CONFIGURATIONS.forEach((config) => { + expect(isWidgetConfigurationOfType(config, config.__typename!)).toBe( + true, + ); + }); + }); +}); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isWidgetConfigurationOfTypeGraph.test.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isWidgetConfigurationOfTypeGraph.test.ts new file mode 100644 index 0000000000..99d63c897a --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/__tests__/isWidgetConfigurationOfTypeGraph.test.ts @@ -0,0 +1,41 @@ +import { isWidgetConfigurationOfTypeGraph } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfTypeGraph'; +import { + ALL_CHART_CONFIGURATIONS, + TEST_FIELDS_CONFIGURATION, + TEST_IFRAME_CONFIGURATION, + TEST_STANDALONE_RICH_TEXT_CONFIGURATION, +} from '~/testing/mock-data/widget-configurations'; + +describe('isWidgetConfigurationOfTypeGraph', () => { + it('returns true for all chart configurations', () => { + ALL_CHART_CONFIGURATIONS.forEach((config) => { + expect(isWidgetConfigurationOfTypeGraph(config)).toBe(true); + }); + }); + + it('returns false for IframeConfiguration', () => { + expect(isWidgetConfigurationOfTypeGraph(TEST_IFRAME_CONFIGURATION)).toBe( + false, + ); + }); + + it('returns false for StandaloneRichTextConfiguration', () => { + expect( + isWidgetConfigurationOfTypeGraph(TEST_STANDALONE_RICH_TEXT_CONFIGURATION), + ).toBe(false); + }); + + it('returns false for FieldsConfiguration', () => { + expect(isWidgetConfigurationOfTypeGraph(TEST_FIELDS_CONFIGURATION)).toBe( + false, + ); + }); + + it('returns false for null', () => { + expect(isWidgetConfigurationOfTypeGraph(null)).toBe(false); + }); + + it('returns false for undefined', () => { + expect(isWidgetConfigurationOfTypeGraph(undefined)).toBe(false); + }); +}); diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/buildChartGroupByFieldConfigUpdate.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/buildChartGroupByFieldConfigUpdate.ts index cf6de1c410..26cf7ee64e 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/buildChartGroupByFieldConfigUpdate.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/buildChartGroupByFieldConfigUpdate.ts @@ -1,8 +1,6 @@ import { type ChartConfiguration } from '@/command-menu/pages/page-layout/types/ChartConfiguration'; -import { isBarChartConfiguration } from '@/command-menu/pages/page-layout/utils/isBarChartConfiguration'; import { isFieldOrRelationNestedFieldDateKind } from '@/command-menu/pages/page-layout/utils/isFieldOrNestedFieldDateKind'; -import { isLineChartConfiguration } from '@/command-menu/pages/page-layout/utils/isLineChartConfiguration'; -import { isPieChartConfiguration } from '@/command-menu/pages/page-layout/utils/isPieChartConfiguration'; +import { isWidgetConfigurationOfType } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfType'; import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { ObjectRecordGroupByDateGranularity } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; @@ -40,9 +38,18 @@ export const buildChartGroupByFieldConfigUpdate = < [subFieldNameKey]: subFieldName, }; - const isBarChart = isBarChartConfiguration(configuration); - const isLineChart = isLineChartConfiguration(configuration); - const isPieChart = isPieChartConfiguration(configuration); + const isBarChart = isWidgetConfigurationOfType( + configuration, + 'BarChartConfiguration', + ); + const isLineChart = isWidgetConfigurationOfType( + configuration, + 'LineChartConfiguration', + ); + const isPieChart = isWidgetConfigurationOfType( + configuration, + 'PieChartConfiguration', + ); if (isPrimaryAxis) { const existingOrderBy = diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/convertPieChartConfigToBarOrLineChart.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/convertPieChartConfigToBarOrLineChart.ts index ad2c6cdff5..8d94272cea 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/convertPieChartConfigToBarOrLineChart.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/convertPieChartConfigToBarOrLineChart.ts @@ -1,11 +1,11 @@ import { type BarLineChartConvertibleFields } from '@/command-menu/pages/page-layout/types/BarLineChartConvertibleFields'; -import { isPieChartConfiguration } from '@/command-menu/pages/page-layout/utils/isPieChartConfiguration'; +import { isWidgetConfigurationOfType } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfType'; import { type PieChartConfiguration } from '~/generated/graphql'; export const convertPieChartConfigToBarOrLineChart = ( configuration: PieChartConfiguration, ): BarLineChartConvertibleFields => { - if (!isPieChartConfiguration(configuration)) { + if (!isWidgetConfigurationOfType(configuration, 'PieChartConfiguration')) { return {}; } diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getBarChartSettings.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getBarChartSettings.ts index 023c2561cf..4918a07c2c 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getBarChartSettings.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getBarChartSettings.ts @@ -19,12 +19,12 @@ import { SORT_BY_GROUP_BY_FIELD_SETTING } from '@/command-menu/pages/page-layout import { STACKED_BARS_SETTING } from '@/command-menu/pages/page-layout/constants/settings/StackedBarsSetting'; import { type ChartSettingsGroup } from '@/command-menu/pages/page-layout/types/ChartSettingsGroup'; import { IconAxisX, IconAxisY } from 'twenty-ui/display'; -import { GraphType } from '~/generated-metadata/graphql'; +import { BarChartLayout } from '~/generated/graphql'; export const getBarChartSettings = ( - graphType: GraphType.VERTICAL_BAR | GraphType.HORIZONTAL_BAR, + layout: BarChartLayout, ): ChartSettingsGroup[] => { - const isHorizontal = graphType === GraphType.HORIZONTAL_BAR; + const isHorizontal = layout === BarChartLayout.HORIZONTAL; const dataDisplayXIcon = isHorizontal ? IconAxisY : IconAxisX; const dataDisplayYIcon = isHorizontal ? IconAxisX : IconAxisY; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getChartLimitMessage.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getChartLimitMessage.ts index a8596ffc49..5878fb2021 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getChartLimitMessage.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getChartLimitMessage.ts @@ -5,11 +5,10 @@ import { PIE_CHART_MAXIMUM_NUMBER_OF_SLICES } from '@/page-layout/widgets/graph/ import { t } from '@lingui/core/macro'; import { type ObjectRecordGroupByDateGranularity } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; - -import { GraphType } from '~/generated/graphql'; +import { WidgetConfigurationType } from '~/generated/graphql'; type GetChartLimitMessageParams = { - graphType: GraphType; + widgetConfigurationType: WidgetConfigurationType; isPrimaryAxisDate: boolean; primaryAxisDateGranularity: | ObjectRecordGroupByDateGranularity @@ -18,15 +17,14 @@ type GetChartLimitMessageParams = { }; export const getChartLimitMessage = ({ - graphType, + widgetConfigurationType, isPrimaryAxisDate, primaryAxisDateGranularity, }: GetChartLimitMessageParams): string => { const maxItems = - graphType === GraphType.LINE + widgetConfigurationType === WidgetConfigurationType.LINE_CHART ? LINE_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_DATA_POINTS - : graphType === GraphType.VERTICAL_BAR || - graphType === GraphType.HORIZONTAL_BAR + : widgetConfigurationType === WidgetConfigurationType.BAR_CHART ? BAR_CHART_CONSTANTS.MAXIMUM_NUMBER_OF_BARS : PIE_CHART_MAXIMUM_NUMBER_OF_SLICES; @@ -37,14 +35,11 @@ export const getChartLimitMessage = ({ return t`Undisplayed data: max ${maxItems} ${granularityLabel} per chart.`; } - if (graphType === GraphType.LINE) { + if (widgetConfigurationType === WidgetConfigurationType.LINE_CHART) { return t`Undisplayed data: max ${maxItems} data points per chart.`; } - if ( - graphType === GraphType.VERTICAL_BAR || - graphType === GraphType.HORIZONTAL_BAR - ) { + if (widgetConfigurationType === WidgetConfigurationType.BAR_CHART) { return t`Undisplayed data: max ${maxItems} bars per chart.`; } diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getConfigurationTypeFromGraphType.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getConfigurationTypeFromGraphType.ts new file mode 100644 index 0000000000..11276533d3 --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getConfigurationTypeFromGraphType.ts @@ -0,0 +1,23 @@ +import { GraphType } from '@/command-menu/pages/page-layout/types/GraphType'; +import { assertUnreachable } from 'twenty-shared/utils'; +import { WidgetConfigurationType } from '~/generated/graphql'; + +export const getConfigurationTypeFromGraphType = ( + graphType: GraphType, +): WidgetConfigurationType => { + switch (graphType) { + case GraphType.VERTICAL_BAR: + case GraphType.HORIZONTAL_BAR: + return WidgetConfigurationType.BAR_CHART; + case GraphType.LINE: + return WidgetConfigurationType.LINE_CHART; + case GraphType.PIE: + return WidgetConfigurationType.PIE_CHART; + case GraphType.AGGREGATE: + return WidgetConfigurationType.AGGREGATE_CHART; + case GraphType.GAUGE: + return WidgetConfigurationType.GAUGE_CHART; + default: + assertUnreachable(graphType); + } +}; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getCurrentGraphTypeFromConfig.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getCurrentGraphTypeFromConfig.ts new file mode 100644 index 0000000000..1200226b88 --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getCurrentGraphTypeFromConfig.ts @@ -0,0 +1,35 @@ +import { type ChartConfiguration } from '@/command-menu/pages/page-layout/types/ChartConfiguration'; +import { GraphType } from '@/command-menu/pages/page-layout/types/GraphType'; +import { isWidgetConfigurationOfType } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfType'; +import { t } from '@lingui/core/macro'; +import { BarChartLayout } from '~/generated/graphql'; + +export const getCurrentGraphTypeFromConfig = ( + configuration: ChartConfiguration, +): GraphType => { + if (isWidgetConfigurationOfType(configuration, 'BarChartConfiguration')) { + return configuration.layout === BarChartLayout.HORIZONTAL + ? GraphType.HORIZONTAL_BAR + : GraphType.VERTICAL_BAR; + } + + if (isWidgetConfigurationOfType(configuration, 'LineChartConfiguration')) { + return GraphType.LINE; + } + + if (isWidgetConfigurationOfType(configuration, 'PieChartConfiguration')) { + return GraphType.PIE; + } + + if ( + isWidgetConfigurationOfType(configuration, 'AggregateChartConfiguration') + ) { + return GraphType.AGGREGATE; + } + + if (isWidgetConfigurationOfType(configuration, 'GaugeChartConfiguration')) { + return GraphType.GAUGE; + } + + throw new Error(t`Unknown chart configuration type`); +}; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isAggregateChartConfiguration.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isAggregateChartConfiguration.ts deleted file mode 100644 index 33ef0b49ad..0000000000 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isAggregateChartConfiguration.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { type FieldConfiguration } from '@/page-layout/types/FieldConfiguration'; -import { type FieldsConfiguration } from '@/page-layout/types/FieldsConfiguration'; -import { - type AggregateChartConfiguration, - type WidgetConfiguration, -} from '~/generated/graphql'; - -export const isAggregateChartConfiguration = ( - configuration: - | WidgetConfiguration - | FieldConfiguration - | FieldsConfiguration - | null - | undefined, -): configuration is AggregateChartConfiguration => { - return configuration?.__typename === 'AggregateChartConfiguration'; -}; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isBarChartConfiguration.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isBarChartConfiguration.ts deleted file mode 100644 index 07efbffb80..0000000000 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isBarChartConfiguration.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { type FieldConfiguration } from '@/page-layout/types/FieldConfiguration'; -import { type FieldsConfiguration } from '@/page-layout/types/FieldsConfiguration'; -import { - type BarChartConfiguration, - type WidgetConfiguration, -} from '~/generated/graphql'; - -export const isBarChartConfiguration = ( - configuration: - | WidgetConfiguration - | FieldConfiguration - | FieldsConfiguration - | null - | undefined, -): configuration is BarChartConfiguration => { - return configuration?.__typename === 'BarChartConfiguration'; -}; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isBarOrLineChartConfiguration.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isBarOrLineChartConfiguration.ts deleted file mode 100644 index 4b875c08cd..0000000000 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isBarOrLineChartConfiguration.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { - type BarChartConfiguration, - type LineChartConfiguration, - type WidgetConfiguration, -} from '~/generated/graphql'; - -import { type FieldConfiguration } from '@/page-layout/types/FieldConfiguration'; -import { type FieldsConfiguration } from '@/page-layout/types/FieldsConfiguration'; -import { isBarChartConfiguration } from './isBarChartConfiguration'; -import { isLineChartConfiguration } from './isLineChartConfiguration'; - -export const isBarOrLineChartConfiguration = ( - configuration: - | WidgetConfiguration - | FieldConfiguration - | FieldsConfiguration - | null - | undefined, -): configuration is BarChartConfiguration | LineChartConfiguration => { - return ( - isBarChartConfiguration(configuration) || - isLineChartConfiguration(configuration) - ); -}; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isChartConfiguration.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isChartConfiguration.ts deleted file mode 100644 index b049404036..0000000000 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isChartConfiguration.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { type WidgetConfiguration } from '~/generated/graphql'; - -import { type ChartConfiguration } from '@/command-menu/pages/page-layout/types/ChartConfiguration'; -import { isAggregateChartConfiguration } from '@/command-menu/pages/page-layout/utils/isAggregateChartConfiguration'; -import { isBarChartConfiguration } from '@/command-menu/pages/page-layout/utils/isBarChartConfiguration'; -import { isGaugeChartConfiguration } from '@/command-menu/pages/page-layout/utils/isGaugeChartConfiguration'; -import { isLineChartConfiguration } from '@/command-menu/pages/page-layout/utils/isLineChartConfiguration'; -import { isPieChartConfiguration } from '@/command-menu/pages/page-layout/utils/isPieChartConfiguration'; -import { type FieldConfiguration } from '@/page-layout/types/FieldConfiguration'; -import { type FieldsConfiguration } from '@/page-layout/types/FieldsConfiguration'; - -export const isChartConfiguration = ( - configuration: - | WidgetConfiguration - | FieldConfiguration - | FieldsConfiguration - | null - | undefined, -): configuration is ChartConfiguration => { - return ( - isBarChartConfiguration(configuration) || - isLineChartConfiguration(configuration) || - isPieChartConfiguration(configuration) || - isAggregateChartConfiguration(configuration) || - isGaugeChartConfiguration(configuration) - ); -}; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isChartWidget.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isChartWidget.ts index b7f0165c27..c9955387aa 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isChartWidget.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isChartWidget.ts @@ -1,8 +1,13 @@ import { type ChartWidget } from '@/command-menu/pages/page-layout/types/ChartWidget'; -import { WidgetType } from '~/generated/graphql'; +import { isWidgetConfigurationOfTypeGraph } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfTypeGraph'; +import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget'; +import { type WidgetConfiguration } from '~/generated/graphql'; export const isChartWidget = ( - pageLayoutWidget: any, + pageLayoutWidget: PageLayoutWidget, ): pageLayoutWidget is ChartWidget => { - return pageLayoutWidget.type === WidgetType.GRAPH; + return isWidgetConfigurationOfTypeGraph( + // TODO: Remove this cast when we FieldsConfiguration and FieldConfiguration are in the backend + pageLayoutWidget.configuration as WidgetConfiguration, + ); }; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isGaugeChartConfiguration.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isGaugeChartConfiguration.ts deleted file mode 100644 index 27821e23db..0000000000 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isGaugeChartConfiguration.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { type FieldConfiguration } from '@/page-layout/types/FieldConfiguration'; -import { type FieldsConfiguration } from '@/page-layout/types/FieldsConfiguration'; -import { - type GaugeChartConfiguration, - type WidgetConfiguration, -} from '~/generated/graphql'; - -export const isGaugeChartConfiguration = ( - configuration: - | WidgetConfiguration - | FieldConfiguration - | FieldsConfiguration - | null - | undefined, -): configuration is GaugeChartConfiguration => { - return configuration?.__typename === 'GaugeChartConfiguration'; -}; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isIframeConfiguration.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isIframeConfiguration.ts deleted file mode 100644 index a76c8761a0..0000000000 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isIframeConfiguration.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { type FieldConfiguration } from '@/page-layout/types/FieldConfiguration'; -import { type FieldsConfiguration } from '@/page-layout/types/FieldsConfiguration'; -import { - type IframeConfiguration, - type WidgetConfiguration, -} from '~/generated/graphql'; - -export const isIframeConfiguration = ( - configuration: - | WidgetConfiguration - | FieldConfiguration - | FieldsConfiguration - | null - | undefined, -): configuration is IframeConfiguration => { - return configuration?.__typename === 'IframeConfiguration'; -}; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isLineChartConfiguration.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isLineChartConfiguration.ts deleted file mode 100644 index 3ae16f6ac9..0000000000 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isLineChartConfiguration.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { type FieldConfiguration } from '@/page-layout/types/FieldConfiguration'; -import { type FieldsConfiguration } from '@/page-layout/types/FieldsConfiguration'; -import { - type LineChartConfiguration, - type WidgetConfiguration, -} from '~/generated/graphql'; - -export const isLineChartConfiguration = ( - configuration: - | WidgetConfiguration - | FieldConfiguration - | FieldsConfiguration - | null - | undefined, -): configuration is LineChartConfiguration => { - return configuration?.__typename === 'LineChartConfiguration'; -}; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isMinMaxRangeValid.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isMinMaxRangeValid.ts index c07b09ff9f..4f7b4410d0 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isMinMaxRangeValid.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isMinMaxRangeValid.ts @@ -1,6 +1,6 @@ import { type ChartConfiguration } from '@/command-menu/pages/page-layout/types/ChartConfiguration'; import { CHART_CONFIGURATION_SETTING_IDS } from '@/command-menu/pages/page-layout/types/ChartConfigurationSettingIds'; -import { isBarOrLineChartConfiguration } from '@/command-menu/pages/page-layout/utils/isBarOrLineChartConfiguration'; +import { isWidgetConfigurationOfType } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfType'; import { isDefined } from 'twenty-shared/utils'; export const isMinMaxRangeValid = ( @@ -10,7 +10,11 @@ export const isMinMaxRangeValid = ( newValue: number, configuration: ChartConfiguration, ): boolean => { - if (!isBarOrLineChartConfiguration(configuration)) { + const isBarOrLineChart = + isWidgetConfigurationOfType(configuration, 'BarChartConfiguration') || + isWidgetConfigurationOfType(configuration, 'LineChartConfiguration'); + + if (!isBarOrLineChart) { return true; } diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isPieChartConfiguration.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isPieChartConfiguration.ts deleted file mode 100644 index 65c835f71d..0000000000 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isPieChartConfiguration.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { type FieldConfiguration } from '@/page-layout/types/FieldConfiguration'; -import { type FieldsConfiguration } from '@/page-layout/types/FieldsConfiguration'; -import { - type PieChartConfiguration, - type WidgetConfiguration, -} from '~/generated/graphql'; - -export const isPieChartConfiguration = ( - configuration: - | WidgetConfiguration - | FieldConfiguration - | FieldsConfiguration - | null - | undefined, -): configuration is PieChartConfiguration => { - return configuration?.__typename === 'PieChartConfiguration'; -}; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isWidgetConfigurationOfType.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isWidgetConfigurationOfType.ts new file mode 100644 index 0000000000..c2e7951044 --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isWidgetConfigurationOfType.ts @@ -0,0 +1,65 @@ +import { type FieldConfiguration } from '@/page-layout/types/FieldConfiguration'; +import { type FieldsConfiguration } from '@/page-layout/types/FieldsConfiguration'; +import { + type AggregateChartConfiguration, + type BarChartConfiguration, + type GaugeChartConfiguration, + type IframeConfiguration, + type LineChartConfiguration, + type PieChartConfiguration, + type StandaloneRichTextConfiguration, + type WidgetConfiguration, + type WidgetConfigurationType, +} from '~/generated/graphql'; + +type WidgetConfigurationTypenameMap = { + AggregateChartConfiguration: Omit< + AggregateChartConfiguration, + 'configurationType' + > & { + configurationType: WidgetConfigurationType.AGGREGATE_CHART; + }; + BarChartConfiguration: Omit & { + configurationType: WidgetConfigurationType.BAR_CHART; + }; + GaugeChartConfiguration: Omit< + GaugeChartConfiguration, + 'configurationType' + > & { + configurationType: WidgetConfigurationType.GAUGE_CHART; + }; + IframeConfiguration: Omit & { + configurationType: WidgetConfigurationType.IFRAME; + }; + LineChartConfiguration: Omit & { + configurationType: WidgetConfigurationType.LINE_CHART; + }; + PieChartConfiguration: Omit & { + configurationType: WidgetConfigurationType.PIE_CHART; + }; + StandaloneRichTextConfiguration: Omit< + StandaloneRichTextConfiguration, + 'configurationType' + > & { + configurationType: WidgetConfigurationType.STANDALONE_RICH_TEXT; + }; +}; + +type WidgetConfigurationTypename = keyof WidgetConfigurationTypenameMap; + +export type WidgetConfigurationOfType = + WidgetConfigurationTypenameMap[T]; + +export const isWidgetConfigurationOfType = < + T extends WidgetConfigurationTypename, +>( + configuration: + | WidgetConfiguration + | FieldsConfiguration + | FieldConfiguration + | null + | undefined, + typename: T, +): configuration is WidgetConfigurationTypenameMap[T] => { + return configuration?.__typename === typename; +}; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isWidgetConfigurationOfTypeGraph.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isWidgetConfigurationOfTypeGraph.ts new file mode 100644 index 0000000000..9e66e4034b --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/isWidgetConfigurationOfTypeGraph.ts @@ -0,0 +1,22 @@ +import { type ChartConfiguration } from '@/command-menu/pages/page-layout/types/ChartConfiguration'; +import { isWidgetConfigurationOfType } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfType'; +import { type FieldConfiguration } from '@/page-layout/types/FieldConfiguration'; +import { type FieldsConfiguration } from '@/page-layout/types/FieldsConfiguration'; +import { type WidgetConfiguration } from '~/generated/graphql'; + +export const isWidgetConfigurationOfTypeGraph = ( + configuration: + | WidgetConfiguration + | FieldsConfiguration + | FieldConfiguration + | null + | undefined, +): configuration is ChartConfiguration => { + return ( + isWidgetConfigurationOfType(configuration, 'BarChartConfiguration') || + isWidgetConfigurationOfType(configuration, 'LineChartConfiguration') || + isWidgetConfigurationOfType(configuration, 'PieChartConfiguration') || + isWidgetConfigurationOfType(configuration, 'AggregateChartConfiguration') || + isWidgetConfigurationOfType(configuration, 'GaugeChartConfiguration') + ); +}; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/shouldHideChartSetting.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/shouldHideChartSetting.ts index d7fd902da9..a171fcb06a 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/shouldHideChartSetting.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/shouldHideChartSetting.ts @@ -1,8 +1,7 @@ import { type ChartConfiguration } from '@/command-menu/pages/page-layout/types/ChartConfiguration'; import { CHART_CONFIGURATION_SETTING_IDS } from '@/command-menu/pages/page-layout/types/ChartConfigurationSettingIds'; import { type ChartSettingsItem } from '@/command-menu/pages/page-layout/types/ChartSettingsGroup'; -import { isBarOrLineChartConfiguration } from '@/command-menu/pages/page-layout/utils/isBarOrLineChartConfiguration'; -import { isPieChartConfiguration } from '@/command-menu/pages/page-layout/utils/isPieChartConfiguration'; +import { isWidgetConfigurationOfType } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfType'; import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { isFieldRelation } from '@/object-record/record-field/ui/types/guards/isFieldRelation'; import { isRelationNestedFieldDateKind } from '@/page-layout/widgets/graph/utils/isRelationNestedFieldDateKind'; @@ -55,8 +54,12 @@ export const shouldHideChartSetting = ( ); if (isDefined(configuration) && isDefined(objectMetadataItem)) { + const isBarOrLineChart = + isWidgetConfigurationOfType(configuration, 'BarChartConfiguration') || + isWidgetConfigurationOfType(configuration, 'LineChartConfiguration'); + if (item.id === CHART_CONFIGURATION_SETTING_IDS.DATE_GRANULARITY_X) { - if (isBarOrLineChartConfiguration(configuration)) { + if (isBarOrLineChart) { return shouldHideDateGranularityBasedOnFieldType( configuration.primaryAxisGroupByFieldMetadataId, configuration.primaryAxisGroupBySubFieldName, @@ -67,7 +70,7 @@ export const shouldHideChartSetting = ( } if (item.id === CHART_CONFIGURATION_SETTING_IDS.DATE_GRANULARITY_Y) { - if (isBarOrLineChartConfiguration(configuration)) { + if (isBarOrLineChart) { return shouldHideDateGranularityBasedOnFieldType( configuration.secondaryAxisGroupByFieldMetadataId, configuration.secondaryAxisGroupBySubFieldName, @@ -78,7 +81,7 @@ export const shouldHideChartSetting = ( } if (item.id === CHART_CONFIGURATION_SETTING_IDS.DATE_GRANULARITY) { - if (isPieChartConfiguration(configuration)) { + if (isWidgetConfigurationOfType(configuration, 'PieChartConfiguration')) { return shouldHideDateGranularityBasedOnFieldType( configuration.groupByFieldMetadataId, configuration.groupBySubFieldName, @@ -89,7 +92,7 @@ export const shouldHideChartSetting = ( } if (item.id === CHART_CONFIGURATION_SETTING_IDS.CUMULATIVE) { - if (isBarOrLineChartConfiguration(configuration)) { + if (isBarOrLineChart) { return shouldHideDateGranularityBasedOnFieldType( configuration.primaryAxisGroupByFieldMetadataId, configuration.primaryAxisGroupBySubFieldName, @@ -100,7 +103,7 @@ export const shouldHideChartSetting = ( } if (item.id === CHART_CONFIGURATION_SETTING_IDS.SHOW_LEGEND) { - if (isPieChartConfiguration(configuration)) { + if (isWidgetConfigurationOfType(configuration, 'PieChartConfiguration')) { return false; } } diff --git a/packages/twenty-front/src/modules/page-layout/components/__stories__/PageLayoutRenderer.stories.tsx b/packages/twenty-front/src/modules/page-layout/components/__stories__/PageLayoutRenderer.stories.tsx index 4d7be2b061..7e82807687 100644 --- a/packages/twenty-front/src/modules/page-layout/components/__stories__/PageLayoutRenderer.stories.tsx +++ b/packages/twenty-front/src/modules/page-layout/components/__stories__/PageLayoutRenderer.stories.tsx @@ -14,17 +14,15 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi import { generateGroupByAggregateQuery } from '@/object-record/record-aggregate/utils/generateGroupByAggregateQuery'; import { PageLayoutRenderer } from '@/page-layout/components/PageLayoutRenderer'; import { LayoutRenderingProvider } from '@/ui/layout/contexts/LayoutRenderingContext'; -import { - GraphOrderBy, - GraphType, - WidgetType, -} from '~/generated-metadata/graphql'; +import { GraphOrderBy, WidgetType } from '~/generated-metadata/graphql'; import { AggregateOperations, AxisNameDisplay, type BarChartConfiguration, + BarChartLayout, PageLayoutType, type PageLayoutWidget, + WidgetConfigurationType, } from '~/generated/graphql'; import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator'; import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper'; @@ -93,14 +91,14 @@ const mixedGraphsPageLayoutMocks = { }, configuration: { __typename: 'AggregateChartConfiguration', - graphType: GraphType.AGGREGATE, + configurationType: WidgetConfigurationType.AGGREGATE_CHART, aggregateOperation: AggregateOperations.COUNT, aggregateFieldMetadataId: idField.id, }, createdAt: '2024-01-01T00:00:00Z', updatedAt: '2024-01-01T00:00:00Z', deletedAt: null, - } as PageLayoutWidget, + } satisfies PageLayoutWidget, { __typename: 'PageLayoutWidget', id: 'gauge-widget', @@ -117,7 +115,7 @@ const mixedGraphsPageLayoutMocks = { }, configuration: { __typename: 'GaugeChartConfiguration', - graphType: GraphType.GAUGE, + configurationType: WidgetConfigurationType.GAUGE_CHART, aggregateOperation: AggregateOperations.COUNT, aggregateFieldMetadataId: idField.id, displayDataLabel: false, @@ -125,7 +123,7 @@ const mixedGraphsPageLayoutMocks = { createdAt: '2024-01-01T00:00:00Z', updatedAt: '2024-01-01T00:00:00Z', deletedAt: null, - } as PageLayoutWidget, + } satisfies PageLayoutWidget, { __typename: 'PageLayoutWidget', id: 'pie-widget', @@ -142,7 +140,7 @@ const mixedGraphsPageLayoutMocks = { }, configuration: { __typename: 'PieChartConfiguration', - graphType: GraphType.PIE, + configurationType: WidgetConfigurationType.PIE_CHART, aggregateOperation: AggregateOperations.COUNT, aggregateFieldMetadataId: idField.id, groupByFieldMetadataId: createdAtField.id, @@ -151,7 +149,7 @@ const mixedGraphsPageLayoutMocks = { createdAt: '2024-01-01T00:00:00Z', updatedAt: '2024-01-01T00:00:00Z', deletedAt: null, - } as PageLayoutWidget, + } satisfies PageLayoutWidget, { __typename: 'PageLayoutWidget', id: 'bar-widget', @@ -168,7 +166,8 @@ const mixedGraphsPageLayoutMocks = { }, configuration: { __typename: 'BarChartConfiguration', - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, + layout: BarChartLayout.VERTICAL, aggregateOperation: AggregateOperations.COUNT, aggregateFieldMetadataId: nameField.id, primaryAxisGroupByFieldMetadataId: createdAtField.id, diff --git a/packages/twenty-front/src/modules/page-layout/constants/DefaultCompanyRecordPageLayout.ts b/packages/twenty-front/src/modules/page-layout/constants/DefaultCompanyRecordPageLayout.ts index 1e17068ef0..f0c007a088 100644 --- a/packages/twenty-front/src/modules/page-layout/constants/DefaultCompanyRecordPageLayout.ts +++ b/packages/twenty-front/src/modules/page-layout/constants/DefaultCompanyRecordPageLayout.ts @@ -1,6 +1,10 @@ import { DEFAULT_COMPANY_RECORD_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultCompanyRecordPageLayoutId'; import { type PageLayout } from '@/page-layout/types/PageLayout'; -import { PageLayoutType, WidgetType } from '~/generated-metadata/graphql'; +import { + PageLayoutType, + WidgetConfigurationType, + WidgetType, +} from '~/generated-metadata/graphql'; export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = { __typename: 'PageLayout', @@ -38,7 +42,11 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 12, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: WidgetConfigurationType.FIELDS, + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -57,7 +65,11 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 12, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: WidgetConfigurationType.FIELDS, + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -78,6 +90,7 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = { }, configuration: { __typename: 'FieldConfiguration', + configurationType: WidgetConfigurationType.FIELD, fieldMetadataId: 'accountOwner', layout: 'FIELD', }, @@ -101,6 +114,7 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = { }, configuration: { __typename: 'FieldConfiguration', + configurationType: WidgetConfigurationType.FIELD, fieldMetadataId: 'opportunities', layout: 'FIELD', }, @@ -124,6 +138,7 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = { }, configuration: { __typename: 'FieldConfiguration', + configurationType: WidgetConfigurationType.FIELD, fieldMetadataId: 'people', layout: 'FIELD', }, @@ -147,6 +162,7 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = { }, configuration: { __typename: 'FieldConfiguration', + configurationType: WidgetConfigurationType.FIELD, fieldMetadataId: 'people', layout: 'FIELD', }, @@ -170,6 +186,7 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = { }, configuration: { __typename: 'FieldConfiguration', + configurationType: WidgetConfigurationType.FIELD, fieldMetadataId: 'name', layout: 'FIELD', }, @@ -193,6 +210,7 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = { }, configuration: { __typename: 'FieldConfiguration', + configurationType: WidgetConfigurationType.FIELD, fieldMetadataId: 'name', layout: 'FIELD', }, @@ -216,6 +234,7 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = { }, configuration: { __typename: 'FieldConfiguration', + configurationType: WidgetConfigurationType.FIELD, fieldMetadataId: 'employees', layout: 'FIELD', }, @@ -239,6 +258,7 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = { }, configuration: { __typename: 'FieldConfiguration', + configurationType: WidgetConfigurationType.FIELD, fieldMetadataId: 'linkedinLink', layout: 'FIELD', }, @@ -274,7 +294,11 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: WidgetConfigurationType.FIELDS, + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -307,7 +331,11 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: WidgetConfigurationType.FIELDS, + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -340,7 +368,11 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: WidgetConfigurationType.FIELDS, + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -373,7 +405,11 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: WidgetConfigurationType.FIELDS, + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -406,7 +442,11 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: WidgetConfigurationType.FIELDS, + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -439,7 +479,11 @@ export const DEFAULT_COMPANY_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: WidgetConfigurationType.FIELDS, + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, diff --git a/packages/twenty-front/src/modules/page-layout/constants/DefaultNoteRecordPageLayout.ts b/packages/twenty-front/src/modules/page-layout/constants/DefaultNoteRecordPageLayout.ts index c0e04d5759..ee74f4ee1b 100644 --- a/packages/twenty-front/src/modules/page-layout/constants/DefaultNoteRecordPageLayout.ts +++ b/packages/twenty-front/src/modules/page-layout/constants/DefaultNoteRecordPageLayout.ts @@ -41,7 +41,11 @@ export const DEFAULT_NOTE_RECORD_PAGE_LAYOUT: PageLayout = { // Note: Configuration is null by default. For testing purposes, // use useTempNoteFieldsConfiguration() hook at runtime to get // a configuration with actual field metadata IDs from the backend. - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -60,7 +64,11 @@ export const DEFAULT_NOTE_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -99,7 +107,11 @@ export const DEFAULT_NOTE_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -139,7 +151,11 @@ export const DEFAULT_NOTE_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -172,7 +188,11 @@ export const DEFAULT_NOTE_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, diff --git a/packages/twenty-front/src/modules/page-layout/constants/DefaultOpportunityRecordPageLayout.ts b/packages/twenty-front/src/modules/page-layout/constants/DefaultOpportunityRecordPageLayout.ts index ccb15b81e8..21839d9b00 100644 --- a/packages/twenty-front/src/modules/page-layout/constants/DefaultOpportunityRecordPageLayout.ts +++ b/packages/twenty-front/src/modules/page-layout/constants/DefaultOpportunityRecordPageLayout.ts @@ -1,6 +1,10 @@ import { DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT_ID } from '@/page-layout/constants/DefaultOpportunityRecordPageLayoutId'; import { type PageLayout } from '@/page-layout/types/PageLayout'; -import { PageLayoutType, WidgetType } from '~/generated/graphql'; +import { + PageLayoutType, + WidgetConfigurationType, + WidgetType, +} from '~/generated/graphql'; export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = { __typename: 'PageLayout', @@ -38,7 +42,11 @@ export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 12, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: WidgetConfigurationType.FIELDS, + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -71,7 +79,11 @@ export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: WidgetConfigurationType.FIELDS, + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -104,7 +116,11 @@ export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: WidgetConfigurationType.FIELDS, + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -137,7 +153,11 @@ export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: WidgetConfigurationType.FIELDS, + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -170,7 +190,11 @@ export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: WidgetConfigurationType.FIELDS, + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -203,7 +227,11 @@ export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: WidgetConfigurationType.FIELDS, + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -236,7 +264,11 @@ export const DEFAULT_OPPORTUNITY_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: WidgetConfigurationType.FIELDS, + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, diff --git a/packages/twenty-front/src/modules/page-layout/constants/DefaultPersonRecordPageLayout.ts b/packages/twenty-front/src/modules/page-layout/constants/DefaultPersonRecordPageLayout.ts index 568d9fb062..a98fba91f5 100644 --- a/packages/twenty-front/src/modules/page-layout/constants/DefaultPersonRecordPageLayout.ts +++ b/packages/twenty-front/src/modules/page-layout/constants/DefaultPersonRecordPageLayout.ts @@ -38,7 +38,11 @@ export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 12, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -71,7 +75,11 @@ export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -104,7 +112,11 @@ export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -137,7 +149,11 @@ export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -170,7 +186,11 @@ export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -203,7 +223,11 @@ export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -236,7 +260,11 @@ export const DEFAULT_PERSON_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, diff --git a/packages/twenty-front/src/modules/page-layout/constants/DefaultRecordPageLayout.ts b/packages/twenty-front/src/modules/page-layout/constants/DefaultRecordPageLayout.ts index ad9b5efcf6..b4b3cc97a7 100644 --- a/packages/twenty-front/src/modules/page-layout/constants/DefaultRecordPageLayout.ts +++ b/packages/twenty-front/src/modules/page-layout/constants/DefaultRecordPageLayout.ts @@ -38,7 +38,11 @@ export const DEFAULT_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 12, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -71,7 +75,11 @@ export const DEFAULT_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -104,7 +112,11 @@ export const DEFAULT_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -137,7 +149,11 @@ export const DEFAULT_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -170,7 +186,11 @@ export const DEFAULT_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, diff --git a/packages/twenty-front/src/modules/page-layout/constants/DefaultTaskRecordPageLayout.ts b/packages/twenty-front/src/modules/page-layout/constants/DefaultTaskRecordPageLayout.ts index 6914ce9d1c..27713f9cab 100644 --- a/packages/twenty-front/src/modules/page-layout/constants/DefaultTaskRecordPageLayout.ts +++ b/packages/twenty-front/src/modules/page-layout/constants/DefaultTaskRecordPageLayout.ts @@ -38,7 +38,11 @@ export const DEFAULT_TASK_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 12, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -57,7 +61,11 @@ export const DEFAULT_TASK_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -96,7 +104,11 @@ export const DEFAULT_TASK_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -136,7 +148,11 @@ export const DEFAULT_TASK_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -169,7 +185,11 @@ export const DEFAULT_TASK_RECORD_PAGE_LAYOUT: PageLayout = { rowSpan: 6, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, diff --git a/packages/twenty-front/src/modules/page-layout/constants/DefaultWorkflowPageLayout.ts b/packages/twenty-front/src/modules/page-layout/constants/DefaultWorkflowPageLayout.ts index 8d2b81408a..a791df1927 100644 --- a/packages/twenty-front/src/modules/page-layout/constants/DefaultWorkflowPageLayout.ts +++ b/packages/twenty-front/src/modules/page-layout/constants/DefaultWorkflowPageLayout.ts @@ -43,7 +43,11 @@ export const DEFAULT_WORKFLOW_PAGE_LAYOUT: PageLayout = { rowSpan: 12, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, diff --git a/packages/twenty-front/src/modules/page-layout/constants/DefaultWorkflowRunPageLayout.ts b/packages/twenty-front/src/modules/page-layout/constants/DefaultWorkflowRunPageLayout.ts index d19be39f4f..8bc38cef00 100644 --- a/packages/twenty-front/src/modules/page-layout/constants/DefaultWorkflowRunPageLayout.ts +++ b/packages/twenty-front/src/modules/page-layout/constants/DefaultWorkflowRunPageLayout.ts @@ -44,7 +44,11 @@ export const DEFAULT_WORKFLOW_RUN_PAGE_LAYOUT: PageLayout = { rowSpan: 12, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -78,7 +82,11 @@ export const DEFAULT_WORKFLOW_RUN_PAGE_LAYOUT: PageLayout = { rowSpan: 12, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, diff --git a/packages/twenty-front/src/modules/page-layout/constants/DefaultWorkflowVersionPageLayout.ts b/packages/twenty-front/src/modules/page-layout/constants/DefaultWorkflowVersionPageLayout.ts index b2538e98b9..76249f98f6 100644 --- a/packages/twenty-front/src/modules/page-layout/constants/DefaultWorkflowVersionPageLayout.ts +++ b/packages/twenty-front/src/modules/page-layout/constants/DefaultWorkflowVersionPageLayout.ts @@ -44,7 +44,11 @@ export const DEFAULT_WORKFLOW_VERSION_PAGE_LAYOUT: PageLayout = { rowSpan: 12, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, @@ -78,7 +82,11 @@ export const DEFAULT_WORKFLOW_VERSION_PAGE_LAYOUT: PageLayout = { rowSpan: 12, columnSpan: 12, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', + sections: [], + }, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deletedAt: null, diff --git a/packages/twenty-front/src/modules/page-layout/constants/GraphWidgetSizes.ts b/packages/twenty-front/src/modules/page-layout/constants/GraphWidgetSizes.ts index 1dbf09832d..ac7cbfa119 100644 --- a/packages/twenty-front/src/modules/page-layout/constants/GraphWidgetSizes.ts +++ b/packages/twenty-front/src/modules/page-layout/constants/GraphWidgetSizes.ts @@ -1,28 +1,26 @@ import { type WidgetSizeConfig } from '@/page-layout/types/WidgetSizeConfig'; -import { GraphType } from '~/generated-metadata/graphql'; +import { WidgetConfigurationType } from '~/generated/graphql'; -export const GRAPH_WIDGET_SIZES: Record = { - [GraphType.AGGREGATE]: { +export const GRAPH_WIDGET_SIZES: Partial< + Record +> = { + [WidgetConfigurationType.AGGREGATE_CHART]: { default: { w: 2, h: 2 }, minimum: { w: 2, h: 2 }, }, - [GraphType.GAUGE]: { + [WidgetConfigurationType.GAUGE_CHART]: { default: { w: 3, h: 4 }, minimum: { w: 3, h: 4 }, }, - [GraphType.PIE]: { + [WidgetConfigurationType.PIE_CHART]: { default: { w: 4, h: 4 }, minimum: { w: 3, h: 4 }, }, - [GraphType.VERTICAL_BAR]: { + [WidgetConfigurationType.BAR_CHART]: { default: { w: 6, h: 6 }, minimum: { w: 4, h: 4 }, }, - [GraphType.HORIZONTAL_BAR]: { - default: { w: 6, h: 6 }, - minimum: { w: 4, h: 4 }, - }, - [GraphType.LINE]: { + [WidgetConfigurationType.LINE_CHART]: { default: { w: 6, h: 10 }, minimum: { w: 4, h: 4 }, }, diff --git a/packages/twenty-front/src/modules/page-layout/graphql/fragments/pageLayoutWidgetFragment.ts b/packages/twenty-front/src/modules/page-layout/graphql/fragments/pageLayoutWidgetFragment.ts index d99a5b11ac..5b349dd08f 100644 --- a/packages/twenty-front/src/modules/page-layout/graphql/fragments/pageLayoutWidgetFragment.ts +++ b/packages/twenty-front/src/modules/page-layout/graphql/fragments/pageLayoutWidgetFragment.ts @@ -17,7 +17,7 @@ export const PAGE_LAYOUT_WIDGET_FRAGMENT = gql` } configuration { ... on BarChartConfiguration { - graphType + configurationType aggregateFieldMetadataId aggregateOperation primaryAxisGroupByFieldMetadataId @@ -38,12 +38,13 @@ export const PAGE_LAYOUT_WIDGET_FRAGMENT = gql` description filter groupMode + layout isCumulative timezone firstDayOfTheWeek } ... on LineChartConfiguration { - graphType + configurationType aggregateFieldMetadataId aggregateOperation primaryAxisGroupByFieldMetadataId @@ -69,7 +70,7 @@ export const PAGE_LAYOUT_WIDGET_FRAGMENT = gql` firstDayOfTheWeek } ... on PieChartConfiguration { - graphType + configurationType groupByFieldMetadataId aggregateFieldMetadataId aggregateOperation @@ -86,7 +87,7 @@ export const PAGE_LAYOUT_WIDGET_FRAGMENT = gql` firstDayOfTheWeek } ... on AggregateChartConfiguration { - graphType + configurationType aggregateFieldMetadataId aggregateOperation label @@ -104,7 +105,7 @@ export const PAGE_LAYOUT_WIDGET_FRAGMENT = gql` } } ... on GaugeChartConfiguration { - graphType + configurationType aggregateFieldMetadataId aggregateOperation displayDataLabel @@ -115,9 +116,11 @@ export const PAGE_LAYOUT_WIDGET_FRAGMENT = gql` firstDayOfTheWeek } ... on IframeConfiguration { + configurationType url } ... on StandaloneRichTextConfiguration { + configurationType body { blocknote markdown diff --git a/packages/twenty-front/src/modules/page-layout/hooks/__tests__/useCreatePageLayoutGraphWidget.test.tsx b/packages/twenty-front/src/modules/page-layout/hooks/__tests__/useCreatePageLayoutGraphWidget.test.tsx index d20ade3ae9..6a753a5177 100644 --- a/packages/twenty-front/src/modules/page-layout/hooks/__tests__/useCreatePageLayoutGraphWidget.test.tsx +++ b/packages/twenty-front/src/modules/page-layout/hooks/__tests__/useCreatePageLayoutGraphWidget.test.tsx @@ -5,11 +5,12 @@ import { type GraphWidgetFieldSelection } from '@/page-layout/types/GraphWidgetF import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; +import { expect } from '@storybook/test'; import { act, renderHook } from '@testing-library/react'; import { useSetRecoilState } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; -import { GraphType, WidgetType } from '~/generated-metadata/graphql'; -import { PageLayoutType } from '~/generated/graphql'; +import { WidgetType } from '~/generated-metadata/graphql'; +import { PageLayoutType, WidgetConfigurationType } from '~/generated/graphql'; import { PAGE_LAYOUT_TEST_INSTANCE_ID, PageLayoutTestWrapper, @@ -85,7 +86,10 @@ describe('useCreatePageLayoutGraphWidget', () => { act(() => { result.current.createWidget.createPageLayoutGraphWidget({ - graphType: GraphType.VERTICAL_BAR, + fieldSelection: { + groupByFieldMetadataIdX: 'test-groupby-field-id', + aggregateFieldMetadataId: 'test-aggregate-field-id', + }, }); }); @@ -159,33 +163,34 @@ describe('useCreatePageLayoutGraphWidget', () => { result.current.setActiveTabId('tab-1'); }); - const graphTypes = [ - GraphType.AGGREGATE, - GraphType.GAUGE, - GraphType.PIE, - GraphType.VERTICAL_BAR, + const widgetConfigurationTypes = [ + WidgetConfigurationType.AGGREGATE_CHART, + WidgetConfigurationType.GAUGE_CHART, + WidgetConfigurationType.PIE_CHART, + WidgetConfigurationType.BAR_CHART, ]; const mockFieldSelections: Partial< - Record + Record > = { - [GraphType.AGGREGATE]: { + [WidgetConfigurationType.AGGREGATE_CHART]: { objectMetadataId: 'test-object-id', aggregateFieldMetadataId: 'test-aggregate-field-id', }, - [GraphType.VERTICAL_BAR]: { + [WidgetConfigurationType.BAR_CHART]: { objectMetadataId: 'test-object-id', groupByFieldMetadataIdX: 'test-groupby-field-id', aggregateFieldMetadataId: 'test-aggregate-field-id', }, }; - graphTypes.forEach((graphType) => { + widgetConfigurationTypes.forEach((widgetConfigurationType) => { act(() => { const fieldSelection = - mockFieldSelections[graphType as keyof typeof mockFieldSelections]; + mockFieldSelections[ + widgetConfigurationType as keyof typeof mockFieldSelections + ]; result.current.createWidget.createPageLayoutGraphWidget({ - graphType, fieldSelection, }); }); @@ -193,7 +198,7 @@ describe('useCreatePageLayoutGraphWidget', () => { expect(result.current.allWidgets).toHaveLength(4); - graphTypes.forEach((graphType, index) => { + widgetConfigurationTypes.forEach((widgetConfigurationType, index) => { const widget = result.current.allWidgets[index]; expect(widget.type).toBe(WidgetType.GRAPH); expect(widget.pageLayoutTabId).toBe('tab-1'); @@ -202,7 +207,9 @@ describe('useCreatePageLayoutGraphWidget', () => { isDefined(widget.configuration) && 'graphType' in widget.configuration ) { - expect(widget.configuration.graphType).toBe(graphType); + expect(widget.configuration.configurationType).toBe( + widgetConfigurationType, + ); } expect(widget.id).toBe('mock-uuid'); @@ -234,7 +241,10 @@ describe('useCreatePageLayoutGraphWidget', () => { expect(() => { result.current.createWidget.createPageLayoutGraphWidget({ - graphType: GraphType.VERTICAL_BAR, + fieldSelection: { + groupByFieldMetadataIdX: 'test-groupby-field-id', + aggregateFieldMetadataId: 'test-aggregate-field-id', + }, }); }).toThrow('A tab must be selected to create a new graph widget'); }); diff --git a/packages/twenty-front/src/modules/page-layout/hooks/__tests__/usePageLayoutDraftState.test.tsx b/packages/twenty-front/src/modules/page-layout/hooks/__tests__/usePageLayoutDraftState.test.tsx index e6d29f84e7..c88dd71a80 100644 --- a/packages/twenty-front/src/modules/page-layout/hooks/__tests__/usePageLayoutDraftState.test.tsx +++ b/packages/twenty-front/src/modules/page-layout/hooks/__tests__/usePageLayoutDraftState.test.tsx @@ -1,11 +1,12 @@ -import { act, renderHook } from '@testing-library/react'; -import { - GraphOrderBy, - GraphType, - WidgetType, -} from '~/generated-metadata/graphql'; -import { AggregateOperations, PageLayoutType } from '~/generated/graphql'; import { usePageLayoutDraftState } from '@/page-layout/hooks/usePageLayoutDraftState'; +import { act, renderHook } from '@testing-library/react'; +import { GraphOrderBy, WidgetType } from '~/generated-metadata/graphql'; +import { + AggregateOperations, + BarChartLayout, + PageLayoutType, + WidgetConfigurationType, +} from '~/generated/graphql'; import { PAGE_LAYOUT_TEST_INSTANCE_ID, PageLayoutTestWrapper, @@ -100,7 +101,8 @@ describe('usePageLayoutDraftState', () => { type: WidgetType.GRAPH, gridPosition: { row: 2, column: 2, rowSpan: 2, columnSpan: 2 }, configuration: { - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, + layout: BarChartLayout.VERTICAL, aggregateOperation: AggregateOperations.COUNT, aggregateFieldMetadataId: 'id', primaryAxisGroupByFieldMetadataId: 'createdAt', diff --git a/packages/twenty-front/src/modules/page-layout/hooks/useCreatePageLayoutGraphWidget.ts b/packages/twenty-front/src/modules/page-layout/hooks/useCreatePageLayoutGraphWidget.ts index ad29f1b73e..420f7b32cb 100644 --- a/packages/twenty-front/src/modules/page-layout/hooks/useCreatePageLayoutGraphWidget.ts +++ b/packages/twenty-front/src/modules/page-layout/hooks/useCreatePageLayoutGraphWidget.ts @@ -1,3 +1,4 @@ +import { isWidgetConfigurationOfType } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfType'; import { useDateTimeFormat } from '@/localization/hooks/useDateTimeFormat'; import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext'; import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState'; @@ -18,8 +19,11 @@ import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component- import { useRecoilCallback } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; import { v4 as uuidv4 } from 'uuid'; -import { type GraphType } from '~/generated-metadata/graphql'; -import { WidgetType } from '~/generated/graphql'; +import { + BarChartLayout, + WidgetConfigurationType, + WidgetType, +} from '~/generated/graphql'; export const useCreatePageLayoutGraphWidget = ( pageLayoutIdFromProps?: string, @@ -56,10 +60,8 @@ export const useCreatePageLayoutGraphWidget = ( const createPageLayoutGraphWidget = useRecoilCallback( ({ snapshot, set }) => ({ - graphType, fieldSelection, }: { - graphType: GraphType; fieldSelection?: GraphWidgetFieldSelection; }): PageLayoutWidget => { const activeTabId = snapshot.getLoadable(activeTabIdState).getValue(); @@ -83,18 +85,39 @@ export const useCreatePageLayoutGraphWidget = ( .getValue(); const allWidgets = pageLayoutDraft.tabs.flatMap((tab) => tab.widgets); - const existingWidgetCount = allWidgets.filter( - (w) => - w.type === WidgetType.GRAPH && - w.configuration && - 'graphType' in w.configuration && - w.configuration.graphType === graphType, - ).length; - const title = getWidgetTitle(graphType, existingWidgetCount); + const existingWidgetCount = allWidgets.filter((widget) => { + if (widget.type !== WidgetType.GRAPH) { + return false; + } + + if ( + !isWidgetConfigurationOfType( + widget.configuration, + 'BarChartConfiguration', + ) + ) { + return false; + } + return widget.configuration.layout === BarChartLayout.VERTICAL; + }).length; + + const title = getWidgetTitle( + { + configurationType: WidgetConfigurationType.BAR_CHART, + layout: BarChartLayout.VERTICAL, + }, + existingWidgetCount, + ); const widgetId = uuidv4(); - const defaultSize = getWidgetSize(graphType, 'default'); - const minimumSize = getWidgetSize(graphType, 'minimum'); + const defaultSize = getWidgetSize( + WidgetConfigurationType.BAR_CHART, + 'default', + ); + const minimumSize = getWidgetSize( + WidgetConfigurationType.BAR_CHART, + 'minimum', + ); const position = getDefaultWidgetPosition( pageLayoutDraggedArea, defaultSize, @@ -105,7 +128,6 @@ export const useCreatePageLayoutGraphWidget = ( id: widgetId, pageLayoutTabId: activeTabId, title, - graphType, gridPosition: { row: position.y, column: position.x, diff --git a/packages/twenty-front/src/modules/page-layout/hooks/useTemporaryFieldsConfiguration.ts b/packages/twenty-front/src/modules/page-layout/hooks/useTemporaryFieldsConfiguration.ts index 644b26066c..26d794ed09 100644 --- a/packages/twenty-front/src/modules/page-layout/hooks/useTemporaryFieldsConfiguration.ts +++ b/packages/twenty-front/src/modules/page-layout/hooks/useTemporaryFieldsConfiguration.ts @@ -36,6 +36,7 @@ export const useTemporaryFieldsConfiguration = ( return { __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', sections: [ { id: `${objectNameSingular}-section-general`, diff --git a/packages/twenty-front/src/modules/page-layout/states/pageLayoutCurrentLayoutsComponentState.ts b/packages/twenty-front/src/modules/page-layout/states/pageLayoutCurrentLayoutsComponentState.ts index 894ccb8415..1b43aa16c9 100644 --- a/packages/twenty-front/src/modules/page-layout/states/pageLayoutCurrentLayoutsComponentState.ts +++ b/packages/twenty-front/src/modules/page-layout/states/pageLayoutCurrentLayoutsComponentState.ts @@ -1,6 +1,6 @@ import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; -import { type TabLayouts } from '@/page-layout/types/tab-layouts'; +import { type TabLayouts } from '@/page-layout/types/TabLayouts'; import { PageLayoutComponentInstanceContext } from './contexts/PageLayoutComponentInstanceContext'; export const pageLayoutCurrentLayoutsComponentState = diff --git a/packages/twenty-front/src/modules/page-layout/states/pageLayoutDraftComponentState.ts b/packages/twenty-front/src/modules/page-layout/states/pageLayoutDraftComponentState.ts index 0d824eda53..bd8ab1439b 100644 --- a/packages/twenty-front/src/modules/page-layout/states/pageLayoutDraftComponentState.ts +++ b/packages/twenty-front/src/modules/page-layout/states/pageLayoutDraftComponentState.ts @@ -1,7 +1,7 @@ import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; import { PageLayoutType } from '~/generated/graphql'; -import { type DraftPageLayout } from '@/page-layout/types/draft-page-layout'; +import { type DraftPageLayout } from '@/page-layout/types/DraftPageLayout'; import { PageLayoutComponentInstanceContext } from './contexts/PageLayoutComponentInstanceContext'; export const pageLayoutDraftComponentState = diff --git a/packages/twenty-front/src/modules/page-layout/types/draft-page-layout.ts b/packages/twenty-front/src/modules/page-layout/types/DraftPageLayout.ts similarity index 100% rename from packages/twenty-front/src/modules/page-layout/types/draft-page-layout.ts rename to packages/twenty-front/src/modules/page-layout/types/DraftPageLayout.ts diff --git a/packages/twenty-front/src/modules/page-layout/types/FieldConfiguration.ts b/packages/twenty-front/src/modules/page-layout/types/FieldConfiguration.ts index 893ae46abb..3b37d3fba4 100644 --- a/packages/twenty-front/src/modules/page-layout/types/FieldConfiguration.ts +++ b/packages/twenty-front/src/modules/page-layout/types/FieldConfiguration.ts @@ -1,5 +1,8 @@ +import { type WidgetConfigurationType } from '~/generated/graphql'; + export type FieldConfiguration = { __typename: 'FieldConfiguration'; + configurationType: WidgetConfigurationType.FIELD; fieldMetadataId: string; layout: 'FIELD' | 'CARD' | 'VIEW'; }; diff --git a/packages/twenty-front/src/modules/page-layout/types/FieldsConfiguration.ts b/packages/twenty-front/src/modules/page-layout/types/FieldsConfiguration.ts index c5396e96b0..1f70d2aa7c 100644 --- a/packages/twenty-front/src/modules/page-layout/types/FieldsConfiguration.ts +++ b/packages/twenty-front/src/modules/page-layout/types/FieldsConfiguration.ts @@ -15,5 +15,6 @@ export type FieldsConfigurationSection = { export type FieldsConfiguration = { __typename: 'FieldsConfiguration'; + configurationType: 'FIELDS'; sections: FieldsConfigurationSection[]; }; diff --git a/packages/twenty-front/src/modules/page-layout/types/PageLayoutWidget.ts b/packages/twenty-front/src/modules/page-layout/types/PageLayoutWidget.ts index be0ac77a54..42e5c4c7d7 100644 --- a/packages/twenty-front/src/modules/page-layout/types/PageLayoutWidget.ts +++ b/packages/twenty-front/src/modules/page-layout/types/PageLayoutWidget.ts @@ -13,9 +13,5 @@ export type PageLayoutWidget = Omit< > & { objectMetadataId?: Nullable; conditionalDisplay?: RulesLogic; - configuration?: - | WidgetConfiguration - | FieldsConfiguration - | FieldConfiguration - | null; + configuration: WidgetConfiguration | FieldsConfiguration | FieldConfiguration; }; diff --git a/packages/twenty-front/src/modules/page-layout/types/tab-layouts.ts b/packages/twenty-front/src/modules/page-layout/types/TabLayouts.ts similarity index 100% rename from packages/twenty-front/src/modules/page-layout/types/tab-layouts.ts rename to packages/twenty-front/src/modules/page-layout/types/TabLayouts.ts diff --git a/packages/twenty-front/src/modules/page-layout/utils/__tests__/addWidgetToTab.test.ts b/packages/twenty-front/src/modules/page-layout/utils/__tests__/addWidgetToTab.test.ts index a40db74e5f..c921937ba1 100644 --- a/packages/twenty-front/src/modules/page-layout/utils/__tests__/addWidgetToTab.test.ts +++ b/packages/twenty-front/src/modules/page-layout/utils/__tests__/addWidgetToTab.test.ts @@ -1,6 +1,7 @@ -import { GraphType, WidgetType } from '~/generated-metadata/graphql'; +import { WidgetType } from '~/generated-metadata/graphql'; import { AggregateOperations, + WidgetConfigurationType, type PageLayoutWidget, } from '~/generated/graphql'; import { type PageLayoutTab } from '@/page-layout/types/PageLayoutTab'; @@ -14,7 +15,7 @@ describe('addWidgetToTab', () => { title: 'Test Widget', type: WidgetType.GRAPH, configuration: { - graphType: GraphType.AGGREGATE, + configurationType: WidgetConfigurationType.AGGREGATE_CHART, aggregateOperation: AggregateOperations.COUNT, aggregateFieldMetadataId: 'id', displayDataLabel: false, diff --git a/packages/twenty-front/src/modules/page-layout/utils/__tests__/convertLayoutsToWidgets.test.ts b/packages/twenty-front/src/modules/page-layout/utils/__tests__/convertLayoutsToWidgets.test.ts index c22fadc032..80fd78f756 100644 --- a/packages/twenty-front/src/modules/page-layout/utils/__tests__/convertLayoutsToWidgets.test.ts +++ b/packages/twenty-front/src/modules/page-layout/utils/__tests__/convertLayoutsToWidgets.test.ts @@ -1,7 +1,7 @@ import { AggregateOperations, GraphOrderBy, - GraphType, + WidgetConfigurationType, WidgetType, type PageLayoutWidget, } from '~/generated-metadata/graphql'; @@ -22,7 +22,7 @@ describe('convertLayoutsToWidgets', () => { columnSpan: 2, }, configuration: { - graphType: GraphType.AGGREGATE, + configurationType: WidgetConfigurationType.AGGREGATE_CHART, aggregateOperation: AggregateOperations.COUNT, aggregateFieldMetadataId: 'id', displayDataLabel: false, @@ -44,7 +44,7 @@ describe('convertLayoutsToWidgets', () => { columnSpan: 2, }, configuration: { - graphType: GraphType.PIE, + configurationType: WidgetConfigurationType.PIE_CHART, aggregateOperation: AggregateOperations.COUNT, aggregateFieldMetadataId: 'id', groupByFieldMetadataId: 'status', diff --git a/packages/twenty-front/src/modules/page-layout/utils/__tests__/convertPageLayoutToTabLayouts.test.ts b/packages/twenty-front/src/modules/page-layout/utils/__tests__/convertPageLayoutToTabLayouts.test.ts index 5598976fce..4582fd0719 100644 --- a/packages/twenty-front/src/modules/page-layout/utils/__tests__/convertPageLayoutToTabLayouts.test.ts +++ b/packages/twenty-front/src/modules/page-layout/utils/__tests__/convertPageLayoutToTabLayouts.test.ts @@ -4,8 +4,8 @@ import { convertPageLayoutToTabLayouts } from '@/page-layout/utils/convertPageLa import { AggregateOperations, GraphOrderBy, - GraphType, PageLayoutType, + WidgetConfigurationType, WidgetType, } from '~/generated/graphql'; @@ -30,7 +30,7 @@ describe('convertPageLayoutToTabLayouts', () => { title: 'Widget 1', type: WidgetType.GRAPH, configuration: { - graphType: GraphType.AGGREGATE, + configurationType: WidgetConfigurationType.AGGREGATE_CHART, aggregateOperation: AggregateOperations.COUNT, aggregateFieldMetadataId: 'id', displayDataLabel: false, @@ -48,7 +48,7 @@ describe('convertPageLayoutToTabLayouts', () => { title: 'Widget 2', type: WidgetType.GRAPH, configuration: { - graphType: GraphType.PIE, + configurationType: WidgetConfigurationType.PIE_CHART, aggregateOperation: AggregateOperations.COUNT, aggregateFieldMetadataId: 'id', groupByFieldMetadataId: 'status', @@ -108,6 +108,7 @@ describe('convertPageLayoutToTabLayouts', () => { title: 'Rich Text', type: WidgetType.STANDALONE_RICH_TEXT, configuration: { + configurationType: WidgetConfigurationType.STANDALONE_RICH_TEXT, body: { blocknote: '[]' }, }, gridPosition: { row: 0, column: 0, rowSpan: 4, columnSpan: 4 }, diff --git a/packages/twenty-front/src/modules/page-layout/utils/__tests__/createDefaultGraphWidget.test.ts b/packages/twenty-front/src/modules/page-layout/utils/__tests__/createDefaultGraphWidget.test.ts index 75fe8967ef..4273a10946 100644 --- a/packages/twenty-front/src/modules/page-layout/utils/__tests__/createDefaultGraphWidget.test.ts +++ b/packages/twenty-front/src/modules/page-layout/utils/__tests__/createDefaultGraphWidget.test.ts @@ -1,10 +1,9 @@ -import { isBarChartConfiguration } from '@/command-menu/pages/page-layout/utils/isBarChartConfiguration'; import { AggregateOperations, AxisNameDisplay, + BarChartLayout, GraphOrderBy, - GraphType, - WidgetType, + WidgetConfigurationType, } from '~/generated/graphql'; import { createDefaultGraphWidget } from '@/page-layout/utils/createDefaultGraphWidget'; @@ -17,41 +16,10 @@ describe('createDefaultGraphWidget', () => { objectMetadataId: 'object-1', }; - describe('AGGREGATE graph type', () => { - it('should create widget with aggregate configuration when field is provided', () => { - const widget = createDefaultGraphWidget({ - ...baseParams, - graphType: GraphType.AGGREGATE, - fieldSelection: { - aggregateFieldMetadataId: 'field-1', - }, - }); - - expect(widget.type).toBe(WidgetType.GRAPH); - expect(widget.configuration).toEqual({ - __typename: 'AggregateChartConfiguration', - graphType: GraphType.AGGREGATE, - aggregateFieldMetadataId: 'field-1', - aggregateOperation: AggregateOperations.COUNT, - displayDataLabel: true, - }); - }); - - it('should return null configuration when field is missing', () => { - const widget = createDefaultGraphWidget({ - ...baseParams, - graphType: GraphType.AGGREGATE, - }); - - expect(widget.configuration).toBeNull(); - }); - }); - describe('VERTICAL_BAR graph type', () => { it('should create widget with bar chart configuration when all fields are provided', () => { const widget = createDefaultGraphWidget({ ...baseParams, - graphType: GraphType.VERTICAL_BAR, fieldSelection: { aggregateFieldMetadataId: 'field-1', groupByFieldMetadataIdX: 'field-2', @@ -60,7 +28,8 @@ describe('createDefaultGraphWidget', () => { expect(widget.configuration).toEqual({ __typename: 'BarChartConfiguration', - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, + layout: BarChartLayout.VERTICAL, displayDataLabel: false, color: 'blue', primaryAxisGroupByFieldMetadataId: 'field-2', @@ -71,86 +40,35 @@ describe('createDefaultGraphWidget', () => { }); }); - it('should return null configuration when required fields are missing', () => { + it('should use objectMetadataId from fieldSelection over params', () => { const widget = createDefaultGraphWidget({ ...baseParams, - graphType: GraphType.VERTICAL_BAR, + objectMetadataId: 'object-1', fieldSelection: { aggregateFieldMetadataId: 'field-1', - // Missing groupByFieldMetadataIdX + objectMetadataId: 'object-2', }, }); - expect(widget.configuration).toBeNull(); + expect(widget.objectMetadataId).toBe('object-2'); }); - }); - describe('HORIZONTAL_BAR graph type', () => { - it('should create widget with horizontal bar configuration', () => { + it('should set timestamps and common widget properties', () => { const widget = createDefaultGraphWidget({ ...baseParams, - graphType: GraphType.HORIZONTAL_BAR, fieldSelection: { aggregateFieldMetadataId: 'field-1', - groupByFieldMetadataIdX: 'field-2', }, }); - expect(widget.configuration?.__typename).toBe('BarChartConfiguration'); - if (isBarChartConfiguration(widget.configuration)) { - expect(widget.configuration.graphType).toBe(GraphType.HORIZONTAL_BAR); - } + expect(widget.__typename).toBe('PageLayoutWidget'); + expect(widget.id).toBe('widget-1'); + expect(widget.pageLayoutTabId).toBe('tab-1'); + expect(widget.title).toBe('Test Widget'); + expect(widget.gridPosition).toEqual(baseParams.gridPosition); + expect(widget.createdAt).toBeDefined(); + expect(widget.updatedAt).toBeDefined(); + expect(widget.deletedAt).toBeNull(); }); }); - - describe('Unsupported graph types', () => { - it.each([GraphType.PIE, GraphType.LINE, GraphType.GAUGE])( - 'should return null configuration for %s graph type', - (graphType) => { - const widget = createDefaultGraphWidget({ - ...baseParams, - graphType, - fieldSelection: { - aggregateFieldMetadataId: 'field-1', - groupByFieldMetadataIdX: 'field-2', - }, - }); - - expect(widget.configuration).toBeNull(); - }, - ); - }); - - it('should use objectMetadataId from fieldSelection over params', () => { - const widget = createDefaultGraphWidget({ - ...baseParams, - graphType: GraphType.AGGREGATE, - objectMetadataId: 'object-1', - fieldSelection: { - aggregateFieldMetadataId: 'field-1', - objectMetadataId: 'object-2', - }, - }); - - expect(widget.objectMetadataId).toBe('object-2'); - }); - - it('should set timestamps and common widget properties', () => { - const widget = createDefaultGraphWidget({ - ...baseParams, - graphType: GraphType.AGGREGATE, - fieldSelection: { - aggregateFieldMetadataId: 'field-1', - }, - }); - - expect(widget.__typename).toBe('PageLayoutWidget'); - expect(widget.id).toBe('widget-1'); - expect(widget.pageLayoutTabId).toBe('tab-1'); - expect(widget.title).toBe('Test Widget'); - expect(widget.gridPosition).toEqual(baseParams.gridPosition); - expect(widget.createdAt).toBeDefined(); - expect(widget.updatedAt).toBeDefined(); - expect(widget.deletedAt).toBeNull(); - }); }); diff --git a/packages/twenty-front/src/modules/page-layout/utils/__tests__/extractFieldMetadataIdsFromWidget.test.ts b/packages/twenty-front/src/modules/page-layout/utils/__tests__/extractFieldMetadataIdsFromWidget.test.ts index 0cbb760d5a..e099d1bd95 100644 --- a/packages/twenty-front/src/modules/page-layout/utils/__tests__/extractFieldMetadataIdsFromWidget.test.ts +++ b/packages/twenty-front/src/modules/page-layout/utils/__tests__/extractFieldMetadataIdsFromWidget.test.ts @@ -1,22 +1,30 @@ +import { extractFieldMetadataIdsFromWidget } from '@/page-layout/utils/extractFieldMetadataIdsFromWidget'; import { type PageLayoutWidget, WidgetType, } from '~/generated-metadata/graphql'; +import { type WidgetConfiguration } from '~/generated/graphql'; import { - AggregateOperations, - AxisNameDisplay, - GraphOrderBy, - GraphType, -} from '~/generated/graphql'; -import { extractFieldMetadataIdsFromWidget } from '@/page-layout/utils/extractFieldMetadataIdsFromWidget'; + TEST_AGGREGATE_CHART_CONFIGURATION, + TEST_BAR_CHART_CONFIGURATION, + TEST_FIELD_METADATA_ID_1, + TEST_FIELD_METADATA_ID_2, + TEST_FIELD_METADATA_ID_3, + TEST_GAUGE_CHART_CONFIGURATION, + TEST_IFRAME_CONFIGURATION, + TEST_LINE_CHART_CONFIGURATION, + TEST_OBJECT_METADATA_ID, + TEST_PIE_CHART_CONFIGURATION, +} from '~/testing/mock-data/widget-configurations'; const createMockWidget = ( - overrides: Partial, + overrides: Partial> & { + configuration: WidgetConfiguration; + }, ): PageLayoutWidget => ({ id: 'widget-1', type: WidgetType.GRAPH, title: 'Test', - configuration: null, objectMetadataId: null, gridPosition: { row: 0, column: 0, rowSpan: 1, columnSpan: 1 }, pageLayoutTabId: 'tab-1', @@ -29,18 +37,7 @@ describe('extractFieldMetadataIdsFromWidget', () => { it('should return empty array for IFRAME widget', () => { const widget = createMockWidget({ type: WidgetType.IFRAME, - configuration: { - __typename: 'IframeConfiguration' as const, - url: 'https://example.com', - }, - }); - - expect(extractFieldMetadataIdsFromWidget(widget)).toEqual([]); - }); - - it('should return empty array for widget without configuration', () => { - const widget = createMockWidget({ - configuration: null, + configuration: TEST_IFRAME_CONFIGURATION, }); expect(extractFieldMetadataIdsFromWidget(widget)).toEqual([]); @@ -48,143 +45,91 @@ describe('extractFieldMetadataIdsFromWidget', () => { it('should extract field IDs from BarChartConfiguration', () => { const widget = createMockWidget({ - configuration: { - __typename: 'BarChartConfiguration' as const, - graphType: GraphType.VERTICAL_BAR, - aggregateFieldMetadataId: 'field-1', - aggregateOperation: AggregateOperations.COUNT, - primaryAxisGroupByFieldMetadataId: 'field-2', - primaryAxisOrderBy: GraphOrderBy.FIELD_ASC, - axisNameDisplay: AxisNameDisplay.BOTH, - displayDataLabel: false, - }, - objectMetadataId: 'object-1', + configuration: TEST_BAR_CHART_CONFIGURATION, + objectMetadataId: TEST_OBJECT_METADATA_ID, }); const result = extractFieldMetadataIdsFromWidget(widget); expect(result).toHaveLength(2); - expect(result).toContain('field-1'); - expect(result).toContain('field-2'); + expect(result).toContain(TEST_FIELD_METADATA_ID_1); + expect(result).toContain(TEST_FIELD_METADATA_ID_2); }); it('should extract field IDs from BarChartConfiguration with optional Y grouping', () => { const widget = createMockWidget({ configuration: { - __typename: 'BarChartConfiguration' as const, - graphType: GraphType.VERTICAL_BAR, - aggregateFieldMetadataId: 'field-1', - aggregateOperation: AggregateOperations.COUNT, - primaryAxisGroupByFieldMetadataId: 'field-2', - secondaryAxisGroupByFieldMetadataId: 'field-3', - primaryAxisOrderBy: GraphOrderBy.FIELD_ASC, - axisNameDisplay: AxisNameDisplay.BOTH, - displayDataLabel: false, + ...TEST_BAR_CHART_CONFIGURATION, + secondaryAxisGroupByFieldMetadataId: TEST_FIELD_METADATA_ID_3, }, - objectMetadataId: 'object-1', + objectMetadataId: TEST_OBJECT_METADATA_ID, }); const result = extractFieldMetadataIdsFromWidget(widget); expect(result).toHaveLength(3); - expect(result).toContain('field-1'); - expect(result).toContain('field-2'); - expect(result).toContain('field-3'); + expect(result).toContain(TEST_FIELD_METADATA_ID_1); + expect(result).toContain(TEST_FIELD_METADATA_ID_2); + expect(result).toContain(TEST_FIELD_METADATA_ID_3); }); it('should extract field IDs from LineChartConfiguration', () => { const widget = createMockWidget({ - configuration: { - __typename: 'LineChartConfiguration' as const, - graphType: GraphType.LINE, - aggregateFieldMetadataId: 'field-1', - aggregateOperation: AggregateOperations.SUM, - primaryAxisGroupByFieldMetadataId: 'field-2', - primaryAxisOrderBy: GraphOrderBy.FIELD_DESC, - axisNameDisplay: AxisNameDisplay.BOTH, - displayDataLabel: false, - }, - objectMetadataId: 'object-1', + configuration: TEST_LINE_CHART_CONFIGURATION, + objectMetadataId: TEST_OBJECT_METADATA_ID, }); const result = extractFieldMetadataIdsFromWidget(widget); expect(result).toHaveLength(2); - expect(result).toContain('field-1'); - expect(result).toContain('field-2'); + expect(result).toContain(TEST_FIELD_METADATA_ID_1); + expect(result).toContain(TEST_FIELD_METADATA_ID_2); }); it('should extract field IDs from PieChartConfiguration', () => { const widget = createMockWidget({ - configuration: { - __typename: 'PieChartConfiguration' as const, - graphType: GraphType.PIE, - aggregateFieldMetadataId: 'field-1', - aggregateOperation: AggregateOperations.COUNT, - groupByFieldMetadataId: 'field-2', - orderBy: GraphOrderBy.FIELD_ASC, - displayDataLabel: false, - }, - objectMetadataId: 'object-1', + configuration: TEST_PIE_CHART_CONFIGURATION, + objectMetadataId: TEST_OBJECT_METADATA_ID, }); const result = extractFieldMetadataIdsFromWidget(widget); expect(result).toHaveLength(2); - expect(result).toContain('field-1'); - expect(result).toContain('field-2'); + expect(result).toContain(TEST_FIELD_METADATA_ID_1); + expect(result).toContain(TEST_FIELD_METADATA_ID_2); }); it('should extract field IDs from AggregateChartConfiguration', () => { const widget = createMockWidget({ - configuration: { - __typename: 'AggregateChartConfiguration' as const, - graphType: GraphType.AGGREGATE, - aggregateFieldMetadataId: 'field-1', - aggregateOperation: AggregateOperations.AVG, - displayDataLabel: false, - }, - objectMetadataId: 'object-1', + configuration: TEST_AGGREGATE_CHART_CONFIGURATION, + objectMetadataId: TEST_OBJECT_METADATA_ID, }); const result = extractFieldMetadataIdsFromWidget(widget); expect(result).toHaveLength(1); - expect(result).toContain('field-1'); + expect(result).toContain(TEST_FIELD_METADATA_ID_1); }); it('should extract field IDs from GaugeChartConfiguration', () => { const widget = createMockWidget({ - configuration: { - __typename: 'GaugeChartConfiguration' as const, - graphType: GraphType.GAUGE, - aggregateFieldMetadataId: 'field-1', - aggregateOperation: AggregateOperations.SUM, - displayDataLabel: false, - }, - objectMetadataId: 'object-1', + configuration: TEST_GAUGE_CHART_CONFIGURATION, + objectMetadataId: TEST_OBJECT_METADATA_ID, }); const result = extractFieldMetadataIdsFromWidget(widget); expect(result).toHaveLength(1); - expect(result).toContain('field-1'); + expect(result).toContain(TEST_FIELD_METADATA_ID_1); }); it('should not include undefined optional fields', () => { const widget = createMockWidget({ configuration: { - __typename: 'BarChartConfiguration' as const, - graphType: GraphType.VERTICAL_BAR, - aggregateFieldMetadataId: 'field-1', - aggregateOperation: AggregateOperations.COUNT, - primaryAxisGroupByFieldMetadataId: 'field-2', + ...TEST_BAR_CHART_CONFIGURATION, secondaryAxisGroupByFieldMetadataId: undefined, - primaryAxisOrderBy: GraphOrderBy.FIELD_ASC, - axisNameDisplay: AxisNameDisplay.BOTH, - displayDataLabel: false, }, - objectMetadataId: 'object-1', + objectMetadataId: TEST_OBJECT_METADATA_ID, }); const result = extractFieldMetadataIdsFromWidget(widget); diff --git a/packages/twenty-front/src/modules/page-layout/utils/__tests__/filterVisibleWidgets.test.ts b/packages/twenty-front/src/modules/page-layout/utils/__tests__/filterVisibleWidgets.test.ts index 31c95fb60b..35f29137c3 100644 --- a/packages/twenty-front/src/modules/page-layout/utils/__tests__/filterVisibleWidgets.test.ts +++ b/packages/twenty-front/src/modules/page-layout/utils/__tests__/filterVisibleWidgets.test.ts @@ -20,7 +20,11 @@ describe('filterVisibleWidgets', () => { rowSpan: 1, columnSpan: 1, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', + sections: [], + }, createdAt: '2024-01-01T00:00:00.000Z', updatedAt: '2024-01-01T00:00:00.000Z', deletedAt: null, diff --git a/packages/twenty-front/src/modules/page-layout/utils/__tests__/getEmptyTabLayout.test.ts b/packages/twenty-front/src/modules/page-layout/utils/__tests__/getEmptyTabLayout.test.ts index 4c58e7cef8..9ed5957fca 100644 --- a/packages/twenty-front/src/modules/page-layout/utils/__tests__/getEmptyTabLayout.test.ts +++ b/packages/twenty-front/src/modules/page-layout/utils/__tests__/getEmptyTabLayout.test.ts @@ -1,4 +1,4 @@ -import { type TabLayouts } from '@/page-layout/types/tab-layouts'; +import { type TabLayouts } from '@/page-layout/types/TabLayouts'; import { getEmptyTabLayout } from '@/page-layout/utils/getEmptyTabLayout'; describe('getEmptyTabLayout', () => { diff --git a/packages/twenty-front/src/modules/page-layout/utils/__tests__/getTabsByDisplayMode.test.ts b/packages/twenty-front/src/modules/page-layout/utils/__tests__/getTabsByDisplayMode.test.ts index e08aa14578..de33c9551d 100644 --- a/packages/twenty-front/src/modules/page-layout/utils/__tests__/getTabsByDisplayMode.test.ts +++ b/packages/twenty-front/src/modules/page-layout/utils/__tests__/getTabsByDisplayMode.test.ts @@ -1,4 +1,4 @@ -import { type DraftPageLayout } from '@/page-layout/types/draft-page-layout'; +import { type DraftPageLayout } from '@/page-layout/types/DraftPageLayout'; import { type PageLayoutTab } from '@/page-layout/types/PageLayoutTab'; import { PageLayoutType } from '~/generated/graphql'; import { getTabsByDisplayMode } from '@/page-layout/utils/getTabsByDisplayMode'; diff --git a/packages/twenty-front/src/modules/page-layout/utils/__tests__/getTabsWithVisibleWidgets.test.ts b/packages/twenty-front/src/modules/page-layout/utils/__tests__/getTabsWithVisibleWidgets.test.ts index 6ce2141df8..16fcafdc82 100644 --- a/packages/twenty-front/src/modules/page-layout/utils/__tests__/getTabsWithVisibleWidgets.test.ts +++ b/packages/twenty-front/src/modules/page-layout/utils/__tests__/getTabsWithVisibleWidgets.test.ts @@ -20,7 +20,11 @@ describe('getTabsWithVisibleWidgets', () => { rowSpan: 1, columnSpan: 1, }, - configuration: null, + configuration: { + __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', + sections: [], + }, createdAt: '2024-01-01T00:00:00.000Z', updatedAt: '2024-01-01T00:00:00.000Z', deletedAt: null, diff --git a/packages/twenty-front/src/modules/page-layout/utils/__tests__/getUpdatedTabLayouts.test.ts b/packages/twenty-front/src/modules/page-layout/utils/__tests__/getUpdatedTabLayouts.test.ts index 60252bf799..7900253e5c 100644 --- a/packages/twenty-front/src/modules/page-layout/utils/__tests__/getUpdatedTabLayouts.test.ts +++ b/packages/twenty-front/src/modules/page-layout/utils/__tests__/getUpdatedTabLayouts.test.ts @@ -1,4 +1,4 @@ -import { type TabLayouts } from '@/page-layout/types/tab-layouts'; +import { type TabLayouts } from '@/page-layout/types/TabLayouts'; import { getUpdatedTabLayouts } from '@/page-layout/utils/getUpdatedTabLayouts'; describe('getUpdatedTabLayouts', () => { diff --git a/packages/twenty-front/src/modules/page-layout/utils/__tests__/getWidgetSize.test.ts b/packages/twenty-front/src/modules/page-layout/utils/__tests__/getWidgetSize.test.ts new file mode 100644 index 0000000000..b653995bce --- /dev/null +++ b/packages/twenty-front/src/modules/page-layout/utils/__tests__/getWidgetSize.test.ts @@ -0,0 +1,119 @@ +import { getWidgetSize } from '@/page-layout/utils/getWidgetSize'; +import { WidgetConfigurationType } from '~/generated/graphql'; + +describe('getWidgetSize', () => { + describe('chart types with specific sizes', () => { + it('returns correct default size for AGGREGATE_CHART', () => { + const result = getWidgetSize( + WidgetConfigurationType.AGGREGATE_CHART, + 'default', + ); + + expect(result).toEqual({ w: 2, h: 2 }); + }); + + it('returns correct minimum size for AGGREGATE_CHART', () => { + const result = getWidgetSize( + WidgetConfigurationType.AGGREGATE_CHART, + 'minimum', + ); + + expect(result).toEqual({ w: 2, h: 2 }); + }); + + it('returns correct default size for GAUGE_CHART', () => { + const result = getWidgetSize( + WidgetConfigurationType.GAUGE_CHART, + 'default', + ); + + expect(result).toEqual({ w: 3, h: 4 }); + }); + + it('returns correct minimum size for GAUGE_CHART', () => { + const result = getWidgetSize( + WidgetConfigurationType.GAUGE_CHART, + 'minimum', + ); + + expect(result).toEqual({ w: 3, h: 4 }); + }); + + it('returns correct default size for PIE_CHART', () => { + const result = getWidgetSize( + WidgetConfigurationType.PIE_CHART, + 'default', + ); + + expect(result).toEqual({ w: 4, h: 4 }); + }); + + it('returns correct minimum size for PIE_CHART', () => { + const result = getWidgetSize( + WidgetConfigurationType.PIE_CHART, + 'minimum', + ); + + expect(result).toEqual({ w: 3, h: 4 }); + }); + + it('returns correct default size for BAR_CHART', () => { + const result = getWidgetSize( + WidgetConfigurationType.BAR_CHART, + 'default', + ); + + expect(result).toEqual({ w: 6, h: 6 }); + }); + + it('returns correct minimum size for BAR_CHART', () => { + const result = getWidgetSize( + WidgetConfigurationType.BAR_CHART, + 'minimum', + ); + + expect(result).toEqual({ w: 4, h: 4 }); + }); + + it('returns correct default size for LINE_CHART', () => { + const result = getWidgetSize( + WidgetConfigurationType.LINE_CHART, + 'default', + ); + + expect(result).toEqual({ w: 6, h: 10 }); + }); + + it('returns correct minimum size for LINE_CHART', () => { + const result = getWidgetSize( + WidgetConfigurationType.LINE_CHART, + 'minimum', + ); + + expect(result).toEqual({ w: 4, h: 4 }); + }); + }); + + describe('fallback to default widget size', () => { + it('returns default widget size for IFRAME', () => { + const result = getWidgetSize(WidgetConfigurationType.IFRAME, 'default'); + + expect(result).toEqual({ w: 4, h: 4 }); + }); + + it('returns minimum widget size for IFRAME', () => { + const result = getWidgetSize(WidgetConfigurationType.IFRAME, 'minimum'); + + expect(result).toEqual({ w: 2, h: 2 }); + }); + + it('returns default widget size for STANDALONE_RICH_TEXT', () => { + const result = getWidgetSize( + WidgetConfigurationType.STANDALONE_RICH_TEXT, + 'default', + ); + + expect(result).toEqual({ w: 4, h: 4 }); + }); + }); +}); diff --git a/packages/twenty-front/src/modules/page-layout/utils/__tests__/getWidgetTitle.test.ts b/packages/twenty-front/src/modules/page-layout/utils/__tests__/getWidgetTitle.test.ts new file mode 100644 index 0000000000..ffaf9f9885 --- /dev/null +++ b/packages/twenty-front/src/modules/page-layout/utils/__tests__/getWidgetTitle.test.ts @@ -0,0 +1,61 @@ +import { getWidgetTitle } from '@/page-layout/utils/getWidgetTitle'; +import { + TEST_AGGREGATE_CHART_CONFIGURATION, + TEST_BAR_CHART_CONFIGURATION, + TEST_BAR_CHART_CONFIGURATION_HORIZONTAL, + TEST_GAUGE_CHART_CONFIGURATION, + TEST_IFRAME_CONFIGURATION, + TEST_LINE_CHART_CONFIGURATION, + TEST_PIE_CHART_CONFIGURATION, + TEST_STANDALONE_RICH_TEXT_CONFIGURATION, +} from '~/testing/mock-data/widget-configurations'; + +describe('getWidgetTitle', () => { + it('returns correct title for aggregate chart', () => { + const result = getWidgetTitle(TEST_AGGREGATE_CHART_CONFIGURATION, 0); + + expect(result).toBe('Aggregate Chart 1'); + }); + + it('returns correct title for gauge chart', () => { + const result = getWidgetTitle(TEST_GAUGE_CHART_CONFIGURATION, 2); + + expect(result).toBe('Gauge Chart 3'); + }); + + it('returns correct title for pie chart', () => { + const result = getWidgetTitle(TEST_PIE_CHART_CONFIGURATION, 0); + + expect(result).toBe('Pie Chart 1'); + }); + + it('returns correct title for vertical bar chart', () => { + const result = getWidgetTitle(TEST_BAR_CHART_CONFIGURATION, 0); + + expect(result).toBe('Vertical Bar Chart 1'); + }); + + it('returns correct title for horizontal bar chart', () => { + const result = getWidgetTitle(TEST_BAR_CHART_CONFIGURATION_HORIZONTAL, 1); + + expect(result).toBe('Horizontal Bar Chart 2'); + }); + + it('returns correct title for line chart', () => { + const result = getWidgetTitle(TEST_LINE_CHART_CONFIGURATION, 0); + + expect(result).toBe('Line Chart 1'); + }); + + it('returns correct title for iframe', () => { + const result = getWidgetTitle(TEST_IFRAME_CONFIGURATION, 0); + + expect(result).toBe('Iframe 1'); + }); + + it('returns correct title for standalone rich text', () => { + const result = getWidgetTitle(TEST_STANDALONE_RICH_TEXT_CONFIGURATION, 4); + + expect(result).toBe('Rich Text 5'); + }); +}); diff --git a/packages/twenty-front/src/modules/page-layout/utils/__tests__/prepareGridLayoutItemsWithPlaceholders.test.ts b/packages/twenty-front/src/modules/page-layout/utils/__tests__/prepareGridLayoutItemsWithPlaceholders.test.ts index cc68ae9c9f..c0d3d6ce15 100644 --- a/packages/twenty-front/src/modules/page-layout/utils/__tests__/prepareGridLayoutItemsWithPlaceholders.test.ts +++ b/packages/twenty-front/src/modules/page-layout/utils/__tests__/prepareGridLayoutItemsWithPlaceholders.test.ts @@ -1,7 +1,7 @@ import { PENDING_WIDGET_PLACEHOLDER_LAYOUT_KEY } from '@/page-layout/constants/PendingWidgetPlaceholderLayoutKey'; import { AggregateOperations, - GraphType, + WidgetConfigurationType, WidgetType, type PageLayoutWidget, } from '~/generated/graphql'; @@ -22,7 +22,7 @@ describe('prepareGridLayoutItemsWithPlaceholders', () => { }, configuration: { __typename: 'AggregateChartConfiguration', - graphType: GraphType.AGGREGATE, + configurationType: WidgetConfigurationType.AGGREGATE_CHART, aggregateOperation: AggregateOperations.COUNT, aggregateFieldMetadataId: 'field-id', displayDataLabel: false, diff --git a/packages/twenty-front/src/modules/page-layout/utils/__tests__/removeWidgetFromTab.test.ts b/packages/twenty-front/src/modules/page-layout/utils/__tests__/removeWidgetFromTab.test.ts index 1ab5e05e71..be70a5d79f 100644 --- a/packages/twenty-front/src/modules/page-layout/utils/__tests__/removeWidgetFromTab.test.ts +++ b/packages/twenty-front/src/modules/page-layout/utils/__tests__/removeWidgetFromTab.test.ts @@ -1,7 +1,7 @@ import { AggregateOperations, GraphOrderBy, - GraphType, + WidgetConfigurationType, WidgetType, } from '~/generated-metadata/graphql'; import { type PageLayoutTab } from '@/page-layout/types/PageLayoutTab'; @@ -22,7 +22,7 @@ describe('removeWidgetFromTab', () => { title: 'Widget 1', type: WidgetType.GRAPH, configuration: { - graphType: GraphType.AGGREGATE, + configurationType: WidgetConfigurationType.AGGREGATE_CHART, aggregateOperation: AggregateOperations.COUNT, aggregateFieldMetadataId: 'id', displayDataLabel: false, @@ -40,7 +40,7 @@ describe('removeWidgetFromTab', () => { title: 'Widget 2', type: WidgetType.GRAPH, configuration: { - graphType: GraphType.PIE, + configurationType: WidgetConfigurationType.PIE_CHART, aggregateOperation: AggregateOperations.COUNT, aggregateFieldMetadataId: 'id', groupByFieldMetadataId: 'status', @@ -71,6 +71,7 @@ describe('removeWidgetFromTab', () => { title: 'Widget 3', type: WidgetType.IFRAME, configuration: { + configurationType: WidgetConfigurationType.IFRAME, url: 'https://example.com', }, gridPosition: { row: 0, column: 0, rowSpan: 2, columnSpan: 2 }, diff --git a/packages/twenty-front/src/modules/page-layout/utils/__tests__/removeWidgetLayoutFromTab.test.ts b/packages/twenty-front/src/modules/page-layout/utils/__tests__/removeWidgetLayoutFromTab.test.ts index c107519e0f..fbd473b3df 100644 --- a/packages/twenty-front/src/modules/page-layout/utils/__tests__/removeWidgetLayoutFromTab.test.ts +++ b/packages/twenty-front/src/modules/page-layout/utils/__tests__/removeWidgetLayoutFromTab.test.ts @@ -1,4 +1,4 @@ -import { type TabLayouts } from '@/page-layout/types/tab-layouts'; +import { type TabLayouts } from '@/page-layout/types/TabLayouts'; import { removeWidgetLayoutFromTab } from '@/page-layout/utils/removeWidgetLayoutFromTab'; describe('removeWidgetLayoutFromTab', () => { diff --git a/packages/twenty-front/src/modules/page-layout/utils/__tests__/updateWidgetMinimumSizeForGraphType.test.ts b/packages/twenty-front/src/modules/page-layout/utils/__tests__/updateWidgetMinimumSizeForGraphType.test.ts index 0813bbbb13..673689cdac 100644 --- a/packages/twenty-front/src/modules/page-layout/utils/__tests__/updateWidgetMinimumSizeForGraphType.test.ts +++ b/packages/twenty-front/src/modules/page-layout/utils/__tests__/updateWidgetMinimumSizeForGraphType.test.ts @@ -1,8 +1,8 @@ -import { type Layouts } from 'react-grid-layout'; -import { GraphType } from '~/generated/graphql'; import { getWidgetSize } from '@/page-layout/utils/getWidgetSize'; import { updateLayoutItemConstraints } from '@/page-layout/utils/updateLayoutItemConstraints'; import { updateWidgetMinimumSizeForGraphType } from '@/page-layout/utils/updateWidgetMinimumSizeForGraphType'; +import { type Layouts } from 'react-grid-layout'; +import { WidgetConfigurationType } from '~/generated/graphql'; jest.mock('../getWidgetSize'); jest.mock('../updateLayoutItemConstraints'); @@ -29,14 +29,17 @@ describe('updateWidgetMinimumSizeForGraphType', () => { (getWidgetSize as jest.Mock).mockReturnValue({ w: 3, h: 2 }); (updateLayoutItemConstraints as jest.Mock).mockReturnValue(updatedLayouts); - const result = updateWidgetMinimumSizeForGraphType( - GraphType.PIE, - 'widget-1', - 'tab-1', - mockLayouts, - ); + const result = updateWidgetMinimumSizeForGraphType({ + configurationType: WidgetConfigurationType.PIE_CHART, + widgetId: 'widget-1', + tabId: 'tab-1', + currentLayouts: mockLayouts, + }); - expect(getWidgetSize).toHaveBeenCalledWith(GraphType.PIE, 'minimum'); + expect(getWidgetSize).toHaveBeenCalledWith( + WidgetConfigurationType.PIE_CHART, + 'minimum', + ); expect(updateLayoutItemConstraints).toHaveBeenCalledWith( mockLayouts['tab-1'], 'widget-1', @@ -57,15 +60,14 @@ describe('updateWidgetMinimumSizeForGraphType', () => { (getWidgetSize as jest.Mock).mockReturnValue({ w: 3, h: 2 }); - const result = updateWidgetMinimumSizeForGraphType( - GraphType.VERTICAL_BAR, - 'widget-2', - 'non-existent-tab', - mockLayouts, - ); - + const result = updateWidgetMinimumSizeForGraphType({ + configurationType: WidgetConfigurationType.BAR_CHART, + widgetId: 'widget-2', + tabId: 'non-existent-tab', + currentLayouts: mockLayouts, + }); expect(getWidgetSize).toHaveBeenCalledWith( - GraphType.VERTICAL_BAR, + WidgetConfigurationType.BAR_CHART, 'minimum', ); expect(updateLayoutItemConstraints).not.toHaveBeenCalled(); @@ -79,16 +81,15 @@ describe('updateWidgetMinimumSizeForGraphType', () => { }, }; - const graphTypes = [ - GraphType.AGGREGATE, - GraphType.GAUGE, - GraphType.PIE, - GraphType.VERTICAL_BAR, - GraphType.HORIZONTAL_BAR, - GraphType.LINE, + const configurationTypes = [ + WidgetConfigurationType.AGGREGATE_CHART, + WidgetConfigurationType.GAUGE_CHART, + WidgetConfigurationType.PIE_CHART, + WidgetConfigurationType.BAR_CHART, + WidgetConfigurationType.LINE_CHART, ]; - graphTypes.forEach((graphType, index) => { + configurationTypes.forEach((configurationType, index) => { const mockSize = { w: index + 2, h: index + 1 }; (getWidgetSize as jest.Mock).mockReturnValue(mockSize); (updateLayoutItemConstraints as jest.Mock).mockReturnValue({ @@ -105,14 +106,14 @@ describe('updateWidgetMinimumSizeForGraphType', () => { ], }); - const result = updateWidgetMinimumSizeForGraphType( - graphType, - 'widget-1', - 'tab-1', - mockLayouts, - ); + const result = updateWidgetMinimumSizeForGraphType({ + configurationType, + widgetId: 'widget-1', + tabId: 'tab-1', + currentLayouts: mockLayouts, + }); - expect(getWidgetSize).toHaveBeenCalledWith(graphType, 'minimum'); + expect(getWidgetSize).toHaveBeenCalledWith(configurationType, 'minimum'); expect(updateLayoutItemConstraints).toHaveBeenCalledWith( mockLayouts['tab-1'], 'widget-1', @@ -144,12 +145,12 @@ describe('updateWidgetMinimumSizeForGraphType', () => { updatedTab2Layouts, ); - const result = updateWidgetMinimumSizeForGraphType( - GraphType.LINE, - 'widget-2', - 'tab-2', - mockLayouts, - ); + const result = updateWidgetMinimumSizeForGraphType({ + configurationType: WidgetConfigurationType.LINE_CHART, + widgetId: 'widget-2', + tabId: 'tab-2', + currentLayouts: mockLayouts, + }); expect(result).toEqual({ 'tab-1': mockLayouts['tab-1'], @@ -163,12 +164,12 @@ describe('updateWidgetMinimumSizeForGraphType', () => { (getWidgetSize as jest.Mock).mockReturnValue({ w: 3, h: 2 }); - const result = updateWidgetMinimumSizeForGraphType( - GraphType.PIE, - 'widget-1', - 'tab-1', - mockLayouts, - ); + const result = updateWidgetMinimumSizeForGraphType({ + configurationType: WidgetConfigurationType.PIE_CHART, + widgetId: 'widget-1', + tabId: 'tab-1', + currentLayouts: mockLayouts, + }); expect(updateLayoutItemConstraints).not.toHaveBeenCalled(); expect(result).toEqual({}); @@ -190,12 +191,12 @@ describe('updateWidgetMinimumSizeForGraphType', () => { (getWidgetSize as jest.Mock).mockReturnValue({ w: 3, h: 2 }); (updateLayoutItemConstraints as jest.Mock).mockReturnValue(updatedLayouts); - const result = updateWidgetMinimumSizeForGraphType( - GraphType.GAUGE, - 'widget-1', - 'tab-1', - mockLayouts, - ); + const result = updateWidgetMinimumSizeForGraphType({ + configurationType: WidgetConfigurationType.GAUGE_CHART, + widgetId: 'widget-1', + tabId: 'tab-1', + currentLayouts: mockLayouts, + }); expect(result['tab-1']).toEqual(updatedLayouts); }); @@ -214,12 +215,12 @@ describe('updateWidgetMinimumSizeForGraphType', () => { unchangedLayouts, ); - const result = updateWidgetMinimumSizeForGraphType( - GraphType.PIE, - 'widget-non-existent', - 'tab-1', - mockLayouts, - ); + const result = updateWidgetMinimumSizeForGraphType({ + configurationType: WidgetConfigurationType.PIE_CHART, + widgetId: 'widget-non-existent', + tabId: 'tab-1', + currentLayouts: mockLayouts, + }); expect(updateLayoutItemConstraints).toHaveBeenCalledWith( mockLayouts['tab-1'], @@ -247,12 +248,12 @@ describe('updateWidgetMinimumSizeForGraphType', () => { ], }); - updateWidgetMinimumSizeForGraphType( - GraphType.HORIZONTAL_BAR, - 'widget-2', - 'tab-1', - mockLayouts, - ); + updateWidgetMinimumSizeForGraphType({ + configurationType: WidgetConfigurationType.BAR_CHART, + widgetId: 'widget-2', + tabId: 'tab-1', + currentLayouts: mockLayouts, + }); expect(updateLayoutItemConstraints).toHaveBeenCalledWith( mockLayouts['tab-1'], @@ -271,12 +272,12 @@ describe('updateWidgetMinimumSizeForGraphType', () => { (getWidgetSize as jest.Mock).mockReturnValue({ w: 3, h: 2 }); - const result = updateWidgetMinimumSizeForGraphType( - GraphType.PIE, - 'widget-1', - 'tab-2', - mockLayouts, - ); + const result = updateWidgetMinimumSizeForGraphType({ + configurationType: WidgetConfigurationType.PIE_CHART, + widgetId: 'widget-1', + tabId: 'tab-2', + currentLayouts: mockLayouts, + }); expect(updateLayoutItemConstraints).not.toHaveBeenCalled(); expect(result).toEqual(mockLayouts); diff --git a/packages/twenty-front/src/modules/page-layout/utils/convertPageLayoutDraftToUpdateInput.ts b/packages/twenty-front/src/modules/page-layout/utils/convertPageLayoutDraftToUpdateInput.ts index fb5811ccc2..fb6991fd76 100644 --- a/packages/twenty-front/src/modules/page-layout/utils/convertPageLayoutDraftToUpdateInput.ts +++ b/packages/twenty-front/src/modules/page-layout/utils/convertPageLayoutDraftToUpdateInput.ts @@ -1,4 +1,4 @@ -import { type DraftPageLayout } from '@/page-layout/types/draft-page-layout'; +import { type DraftPageLayout } from '@/page-layout/types/DraftPageLayout'; import { type UpdatePageLayoutWithTabsInput } from '~/generated/graphql'; export const convertPageLayoutDraftToUpdateInput = ( diff --git a/packages/twenty-front/src/modules/page-layout/utils/convertPageLayoutToTabLayouts.ts b/packages/twenty-front/src/modules/page-layout/utils/convertPageLayoutToTabLayouts.ts index fa93ecaceb..ffbe9a2e3f 100644 --- a/packages/twenty-front/src/modules/page-layout/utils/convertPageLayoutToTabLayouts.ts +++ b/packages/twenty-front/src/modules/page-layout/utils/convertPageLayoutToTabLayouts.ts @@ -1,10 +1,8 @@ import { DEFAULT_WIDGET_SIZE } from '@/page-layout/constants/DefaultWidgetSize'; -import { WIDGET_SIZES } from '@/page-layout/constants/WidgetSizes'; import { type PageLayout } from '@/page-layout/types/PageLayout'; -import { type TabLayouts } from '@/page-layout/types/tab-layouts'; +import { type TabLayouts } from '@/page-layout/types/TabLayouts'; import { getWidgetSize } from '@/page-layout/utils/getWidgetSize'; import { isDefined } from 'twenty-shared/utils'; -import { WidgetType } from '~/generated/graphql'; export const convertPageLayoutToTabLayouts = ( pageLayout: PageLayout, @@ -20,32 +18,20 @@ export const convertPageLayoutToTabLayouts = ( let minW = DEFAULT_WIDGET_SIZE.minimum.w; let minH = DEFAULT_WIDGET_SIZE.minimum.h; - if (widget.type === WidgetType.GRAPH && isDefined(widget.configuration)) { - const graphType = - 'graphType' in widget.configuration - ? widget.configuration.graphType - : undefined; - - if (isDefined(graphType)) { - const minimumSize = getWidgetSize(graphType, 'minimum'); + if (isDefined(widget.configuration)) { + if (widget.configuration.__typename === 'FieldsConfiguration') { + minW = DEFAULT_WIDGET_SIZE.minimum.w; + minH = DEFAULT_WIDGET_SIZE.minimum.h; + } else { + const minimumSize = getWidgetSize( + widget.configuration.configurationType, + 'minimum', + ); minW = minimumSize.w; minH = minimumSize.h; } } - if (widget.type === WidgetType.IFRAME) { - const iframeMinimumSize = WIDGET_SIZES[WidgetType.IFRAME]!.minimum; - minW = iframeMinimumSize.w; - minH = iframeMinimumSize.h; - } - - if (widget.type === WidgetType.STANDALONE_RICH_TEXT) { - const richTextMinimumSize = - WIDGET_SIZES[WidgetType.STANDALONE_RICH_TEXT]!.minimum; - minW = richTextMinimumSize.w; - minH = richTextMinimumSize.h; - } - return { i: widget.id, x: widget.gridPosition.column, diff --git a/packages/twenty-front/src/modules/page-layout/utils/createDefaultGraphWidget.ts b/packages/twenty-front/src/modules/page-layout/utils/createDefaultGraphWidget.ts index cab585481c..05a607e874 100644 --- a/packages/twenty-front/src/modules/page-layout/utils/createDefaultGraphWidget.ts +++ b/packages/twenty-front/src/modules/page-layout/utils/createDefaultGraphWidget.ts @@ -1,102 +1,43 @@ -import { assertUnreachable, isDefined } from 'twenty-shared/utils'; import { type ThemeColor } from 'twenty-ui/theme'; import { AggregateOperations, AxisNameDisplay, + BarChartLayout, GraphOrderBy, - GraphType, type GridPosition, type PageLayoutWidget, type WidgetConfiguration, + WidgetConfigurationType, WidgetType, } from '~/generated/graphql'; import { type GraphWidgetFieldSelection } from '@/page-layout/types/GraphWidgetFieldSelection'; const createDefaultGraphConfiguration = ( - graphType: GraphType, fieldSelection?: GraphWidgetFieldSelection, timezone?: string, firstDayOfTheWeek?: number, -): WidgetConfiguration | null => { - switch (graphType) { - case GraphType.AGGREGATE: - if (!isDefined(fieldSelection?.aggregateFieldMetadataId)) { - return null; - } - return { - __typename: 'AggregateChartConfiguration', - graphType: GraphType.AGGREGATE, - aggregateFieldMetadataId: fieldSelection.aggregateFieldMetadataId, - aggregateOperation: AggregateOperations.COUNT, - displayDataLabel: true, - timezone, - firstDayOfTheWeek, - }; - - case GraphType.PIE: - return null; - - case GraphType.VERTICAL_BAR: - if ( - !isDefined(fieldSelection?.aggregateFieldMetadataId) || - !isDefined(fieldSelection?.groupByFieldMetadataIdX) - ) { - return null; - } - return { - __typename: 'BarChartConfiguration', - graphType: GraphType.VERTICAL_BAR, - displayDataLabel: false, - color: 'blue' satisfies ThemeColor, - primaryAxisGroupByFieldMetadataId: - fieldSelection.groupByFieldMetadataIdX, - aggregateFieldMetadataId: fieldSelection.aggregateFieldMetadataId, - aggregateOperation: AggregateOperations.SUM, - primaryAxisOrderBy: GraphOrderBy.FIELD_ASC, - axisNameDisplay: AxisNameDisplay.NONE, - timezone, - firstDayOfTheWeek, - }; - - case GraphType.HORIZONTAL_BAR: - if ( - !isDefined(fieldSelection?.aggregateFieldMetadataId) || - !isDefined(fieldSelection?.groupByFieldMetadataIdX) - ) { - return null; - } - return { - __typename: 'BarChartConfiguration', - graphType: GraphType.HORIZONTAL_BAR, - displayDataLabel: false, - color: 'blue' satisfies ThemeColor, - primaryAxisGroupByFieldMetadataId: - fieldSelection.groupByFieldMetadataIdX, - aggregateFieldMetadataId: fieldSelection.aggregateFieldMetadataId, - aggregateOperation: AggregateOperations.SUM, - primaryAxisOrderBy: GraphOrderBy.FIELD_ASC, - axisNameDisplay: AxisNameDisplay.NONE, - timezone, - firstDayOfTheWeek, - }; - - case GraphType.LINE: - return null; - - case GraphType.GAUGE: - return null; - - default: - assertUnreachable(graphType); - } +): WidgetConfiguration => { + return { + __typename: 'BarChartConfiguration', + configurationType: WidgetConfigurationType.BAR_CHART, + layout: BarChartLayout.VERTICAL, + displayDataLabel: false, + color: 'blue' satisfies ThemeColor, + primaryAxisGroupByFieldMetadataId: fieldSelection?.groupByFieldMetadataIdX, + aggregateFieldMetadataId: fieldSelection?.aggregateFieldMetadataId, + aggregateOperation: AggregateOperations.SUM, + primaryAxisOrderBy: GraphOrderBy.FIELD_ASC, + axisNameDisplay: AxisNameDisplay.NONE, + timezone, + firstDayOfTheWeek, + }; }; type CreateDefaultGraphWidgetParams = { id: string; pageLayoutTabId: string; title: string; - graphType: GraphType; gridPosition: GridPosition; objectMetadataId?: string | null; fieldSelection?: GraphWidgetFieldSelection; @@ -108,7 +49,6 @@ export const createDefaultGraphWidget = ({ id, pageLayoutTabId, title, - graphType, gridPosition, objectMetadataId, fieldSelection, @@ -119,7 +59,6 @@ export const createDefaultGraphWidget = ({ fieldSelection?.objectMetadataId ?? objectMetadataId ?? null; const configuration = createDefaultGraphConfiguration( - graphType, fieldSelection, timezone, firstDayOfTheWeek, diff --git a/packages/twenty-front/src/modules/page-layout/utils/createDefaultIframeWidget.ts b/packages/twenty-front/src/modules/page-layout/utils/createDefaultIframeWidget.ts index 4d71c6bc05..0fce64132b 100644 --- a/packages/twenty-front/src/modules/page-layout/utils/createDefaultIframeWidget.ts +++ b/packages/twenty-front/src/modules/page-layout/utils/createDefaultIframeWidget.ts @@ -1,6 +1,7 @@ import { type GridPosition, type PageLayoutWidget, + WidgetConfigurationType, WidgetType, } from '~/generated/graphql'; @@ -19,6 +20,7 @@ export const createDefaultIframeWidget = ( title, type: WidgetType.IFRAME, configuration: { + configurationType: WidgetConfigurationType.IFRAME, url, }, gridPosition, diff --git a/packages/twenty-front/src/modules/page-layout/utils/createDefaultStandaloneRichTextWidget.ts b/packages/twenty-front/src/modules/page-layout/utils/createDefaultStandaloneRichTextWidget.ts index f5cb4d17de..80db57dde1 100644 --- a/packages/twenty-front/src/modules/page-layout/utils/createDefaultStandaloneRichTextWidget.ts +++ b/packages/twenty-front/src/modules/page-layout/utils/createDefaultStandaloneRichTextWidget.ts @@ -2,6 +2,7 @@ import { type GridPosition, type PageLayoutWidget, type RichTextV2Body, + WidgetConfigurationType, WidgetType, } from '~/generated/graphql'; @@ -19,6 +20,7 @@ export const createDefaultStandaloneRichTextWidget = ( title: 'Untitled Rich Text', type: WidgetType.STANDALONE_RICH_TEXT, configuration: { + configurationType: WidgetConfigurationType.STANDALONE_RICH_TEXT, body, }, gridPosition, diff --git a/packages/twenty-front/src/modules/page-layout/utils/getDefaultWidgetData.ts b/packages/twenty-front/src/modules/page-layout/utils/getDefaultWidgetData.ts deleted file mode 100644 index 6bc4f37fbf..0000000000 --- a/packages/twenty-front/src/modules/page-layout/utils/getDefaultWidgetData.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { GraphType } from '~/generated-metadata/graphql'; - -export const getDefaultWidgetData = (graphType: GraphType) => { - switch (graphType) { - case GraphType.AGGREGATE: - return { - value: '1,234', - trendPercentage: 15.2, - }; - - case GraphType.GAUGE: - return { - value: 0.7, - min: 0, - max: 1, - label: 'Progress', - }; - - case GraphType.PIE: - return { - items: [ - { id: 'segment1', value: 35, label: 'Segment A' }, - { id: 'segment2', value: 28, label: 'Segment B' }, - { id: 'segment3', value: 20, label: 'Segment C' }, - { id: 'segment4', value: 17, label: 'Segment D' }, - ], - }; - - case GraphType.VERTICAL_BAR: - return { - items: [ - { category: 'Jan', value: 45 }, - { category: 'Feb', value: 52 }, - { category: 'Mar', value: 48 }, - { category: 'Apr', value: 61 }, - { category: 'May', value: 55 }, - ], - indexBy: 'category', - keys: ['value'], - seriesLabels: { value: 'Value' }, - layout: 'vertical' as const, - }; - - case GraphType.HORIZONTAL_BAR: - return { - items: [ - { category: 'Jan', value: 45 }, - { category: 'Feb', value: 52 }, - { category: 'Mar', value: 48 }, - { category: 'Apr', value: 61 }, - { category: 'May', value: 55 }, - ], - indexBy: 'category', - keys: ['value'], - seriesLabels: { value: 'Value' }, - layout: 'horizontal' as const, - }; - - case GraphType.LINE: - return { - series: [ - { - id: 'revenue', - label: 'Revenue', - color: 'blue', - data: [ - { x: 0, y: 30 }, - { x: 1, y: 50 }, - { x: 2, y: 45 }, - { x: 3, y: 70 }, - { x: 4, y: 65 }, - { x: 5, y: 80 }, - { x: 6, y: 75 }, - { x: 7, y: 85 }, - ], - enableArea: true, - }, - { - id: 'costs', - label: 'Costs', - color: 'red', - data: [ - { x: 0, y: 60 }, - { x: 1, y: 45 }, - { x: 2, y: 55 }, - { x: 3, y: 40 }, - { x: 4, y: 60 }, - { x: 5, y: 50 }, - { x: 6, y: 70 }, - { x: 7, y: 65 }, - ], - enableArea: true, - }, - { - id: 'profit', - label: 'Profit', - color: 'turquoise', - data: [ - { x: 0, y: 45 }, - { x: 1, y: 60 }, - { x: 2, y: 35 }, - { x: 3, y: 55 }, - { x: 4, y: 50 }, - { x: 5, y: 65 }, - { x: 6, y: 40 }, - { x: 7, y: 75 }, - ], - enableArea: true, - }, - ], - enableArea: true, - showLegend: true, - showGrid: true, - enablePoints: false, - xAxisLabel: 'Time', - yAxisLabel: 'Value', - curve: 'monotoneX', - displayType: 'shortNumber', - prefix: '$', - suffix: '', - xScale: { type: 'linear' }, - yScale: { type: 'linear', min: 0, max: 'auto' }, - stackedArea: false, - enableSlices: false, - }; - - default: - return {}; - } -}; diff --git a/packages/twenty-front/src/modules/page-layout/utils/getEmptyTabLayout.ts b/packages/twenty-front/src/modules/page-layout/utils/getEmptyTabLayout.ts index 7c88665a92..1f61db65d1 100644 --- a/packages/twenty-front/src/modules/page-layout/utils/getEmptyTabLayout.ts +++ b/packages/twenty-front/src/modules/page-layout/utils/getEmptyTabLayout.ts @@ -1,4 +1,4 @@ -import { type TabLayouts } from '@/page-layout/types/tab-layouts'; +import { type TabLayouts } from '@/page-layout/types/TabLayouts'; export const getEmptyTabLayout = ( allTabLayouts: TabLayouts, diff --git a/packages/twenty-front/src/modules/page-layout/utils/getUpdatedTabLayouts.ts b/packages/twenty-front/src/modules/page-layout/utils/getUpdatedTabLayouts.ts index 5bc9cfe016..f449c6eb51 100644 --- a/packages/twenty-front/src/modules/page-layout/utils/getUpdatedTabLayouts.ts +++ b/packages/twenty-front/src/modules/page-layout/utils/getUpdatedTabLayouts.ts @@ -1,4 +1,4 @@ -import { type TabLayouts } from '@/page-layout/types/tab-layouts'; +import { type TabLayouts } from '@/page-layout/types/TabLayouts'; export const getUpdatedTabLayouts = ( allTabLayouts: TabLayouts, diff --git a/packages/twenty-front/src/modules/page-layout/utils/getWidgetSize.ts b/packages/twenty-front/src/modules/page-layout/utils/getWidgetSize.ts index e34ad644ab..1fc757b0d2 100644 --- a/packages/twenty-front/src/modules/page-layout/utils/getWidgetSize.ts +++ b/packages/twenty-front/src/modules/page-layout/utils/getWidgetSize.ts @@ -1,11 +1,12 @@ import { DEFAULT_WIDGET_SIZE } from '@/page-layout/constants/DefaultWidgetSize'; import { GRAPH_WIDGET_SIZES } from '@/page-layout/constants/GraphWidgetSizes'; -import { type GraphType } from '~/generated/graphql'; +import { type WidgetConfigurationType } from '~/generated/graphql'; export const getWidgetSize = ( - graphType: GraphType, + configurationType: WidgetConfigurationType, type: 'default' | 'minimum', ): { w: number; h: number } => { - const sizeConfig = GRAPH_WIDGET_SIZES[graphType] ?? DEFAULT_WIDGET_SIZE; + const sizeConfig = + GRAPH_WIDGET_SIZES[configurationType] ?? DEFAULT_WIDGET_SIZE; return sizeConfig[type]; }; diff --git a/packages/twenty-front/src/modules/page-layout/utils/getWidgetTitle.ts b/packages/twenty-front/src/modules/page-layout/utils/getWidgetTitle.ts index 0b63edc61e..6c3345588c 100644 --- a/packages/twenty-front/src/modules/page-layout/utils/getWidgetTitle.ts +++ b/packages/twenty-front/src/modules/page-layout/utils/getWidgetTitle.ts @@ -1,14 +1,50 @@ -import { GraphType } from '~/generated/graphql'; +import { t } from '@lingui/core/macro'; +import { assertUnreachable } from 'twenty-shared/utils'; +import { + BarChartLayout, + WidgetConfigurationType, + type WidgetConfiguration, +} from '~/generated/graphql'; -export const getWidgetTitle = (graphType: GraphType, index: number): string => { - const baseNames: Record = { - [GraphType.AGGREGATE]: 'Number', - [GraphType.GAUGE]: 'Gauge', - [GraphType.PIE]: 'Pie Chart', - [GraphType.VERTICAL_BAR]: 'Vertical Bar Chart', - [GraphType.HORIZONTAL_BAR]: 'Horizontal Bar Chart', - [GraphType.LINE]: 'Line Chart', - }; - - return `${baseNames[graphType] || 'Widget'} ${index + 1}`; +export const getWidgetTitle = ( + configuration: Pick & { + layout?: BarChartLayout; + }, + index: number, +): string => { + switch (configuration.configurationType) { + case WidgetConfigurationType.AGGREGATE_CHART: + return `${t`Aggregate Chart`} ${index + 1}`; + case WidgetConfigurationType.GAUGE_CHART: + return `${t`Gauge Chart`} ${index + 1}`; + case WidgetConfigurationType.PIE_CHART: + return `${t`Pie Chart`} ${index + 1}`; + case WidgetConfigurationType.BAR_CHART: + if (configuration.layout === BarChartLayout.VERTICAL) { + return `${t`Vertical Bar Chart`} ${index + 1}`; + } else { + return `${t`Horizontal Bar Chart`} ${index + 1}`; + } + case WidgetConfigurationType.LINE_CHART: + return `${t`Line Chart`} ${index + 1}`; + case WidgetConfigurationType.IFRAME: + return `${t`Iframe`} ${index + 1}`; + case WidgetConfigurationType.STANDALONE_RICH_TEXT: + return `${t`Rich Text`} ${index + 1}`; + case WidgetConfigurationType.FIELD: + case WidgetConfigurationType.FIELDS: + case WidgetConfigurationType.CALENDAR: + case WidgetConfigurationType.EMAILS: + case WidgetConfigurationType.TASKS: + case WidgetConfigurationType.NOTES: + case WidgetConfigurationType.FILES: + case WidgetConfigurationType.WORKFLOW: + case WidgetConfigurationType.WORKFLOW_VERSION: + case WidgetConfigurationType.WORKFLOW_RUN: + case WidgetConfigurationType.VIEW: + case WidgetConfigurationType.TIMELINE: + case WidgetConfigurationType.FIELD_RICH_TEXT: + default: + assertUnreachable(configuration.configurationType as never); + } }; diff --git a/packages/twenty-front/src/modules/page-layout/utils/removeTabLayouts.ts b/packages/twenty-front/src/modules/page-layout/utils/removeTabLayouts.ts index a3a427a584..73588afe19 100644 --- a/packages/twenty-front/src/modules/page-layout/utils/removeTabLayouts.ts +++ b/packages/twenty-front/src/modules/page-layout/utils/removeTabLayouts.ts @@ -1,4 +1,4 @@ -import { type TabLayouts } from '@/page-layout/types/tab-layouts'; +import { type TabLayouts } from '@/page-layout/types/TabLayouts'; export const removeTabLayouts = ( allTabLayouts: TabLayouts, diff --git a/packages/twenty-front/src/modules/page-layout/utils/removeWidgetLayoutFromTab.ts b/packages/twenty-front/src/modules/page-layout/utils/removeWidgetLayoutFromTab.ts index 1e2040933f..5316b630fe 100644 --- a/packages/twenty-front/src/modules/page-layout/utils/removeWidgetLayoutFromTab.ts +++ b/packages/twenty-front/src/modules/page-layout/utils/removeWidgetLayoutFromTab.ts @@ -1,4 +1,4 @@ -import { type TabLayouts } from '@/page-layout/types/tab-layouts'; +import { type TabLayouts } from '@/page-layout/types/TabLayouts'; export const removeWidgetLayoutFromTab = ( allTabLayouts: TabLayouts, diff --git a/packages/twenty-front/src/modules/page-layout/utils/updateWidgetMinimumSizeForGraphType.ts b/packages/twenty-front/src/modules/page-layout/utils/updateWidgetMinimumSizeForGraphType.ts index 69bbfc2f76..a6a2c997b2 100644 --- a/packages/twenty-front/src/modules/page-layout/utils/updateWidgetMinimumSizeForGraphType.ts +++ b/packages/twenty-front/src/modules/page-layout/utils/updateWidgetMinimumSizeForGraphType.ts @@ -1,16 +1,21 @@ import { getWidgetSize } from '@/page-layout/utils/getWidgetSize'; import { updateLayoutItemConstraints } from '@/page-layout/utils/updateLayoutItemConstraints'; -import { type GraphType } from '~/generated/graphql'; import { type Layouts } from 'react-grid-layout'; import { isDefined } from 'twenty-shared/utils'; +import { type WidgetConfigurationType } from '~/generated/graphql'; -export const updateWidgetMinimumSizeForGraphType = ( - graphType: GraphType, - widgetId: string, - tabId: string, - currentLayouts: Record, -): Record => { - const minimumSize = getWidgetSize(graphType, 'minimum'); +export const updateWidgetMinimumSizeForGraphType = ({ + configurationType, + widgetId, + tabId, + currentLayouts, +}: { + configurationType: WidgetConfigurationType; + widgetId: string; + tabId: string; + currentLayouts: Record; +}): Record => { + const minimumSize = getWidgetSize(configurationType, 'minimum'); const currentTabLayouts = currentLayouts[tabId]; if (!isDefined(currentTabLayouts)) { diff --git a/packages/twenty-front/src/modules/page-layout/widgets/components/__stories__/WidgetRenderer.stories.tsx b/packages/twenty-front/src/modules/page-layout/widgets/components/__stories__/WidgetRenderer.stories.tsx index bd7c5db3e3..2bcdbe8ef9 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/components/__stories__/WidgetRenderer.stories.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/components/__stories__/WidgetRenderer.stories.tsx @@ -1,7 +1,7 @@ import { + useApolloClient, type ApolloClient, type NormalizedCacheObject, - useApolloClient, } from '@apollo/client'; import { type MockedResponse } from '@apollo/client/testing'; import { type Meta, type StoryObj } from '@storybook/react'; @@ -36,15 +36,13 @@ import { WidgetComponentInstanceContext } from '@/page-layout/widgets/states/con import { widgetCardHoveredComponentFamilyState } from '@/page-layout/widgets/states/widgetCardHoveredComponentFamilyState'; import { type WidgetCardVariant } from '@/page-layout/widgets/types/WidgetCardVariant'; import { LayoutRenderingProvider } from '@/ui/layout/contexts/LayoutRenderingContext'; -import { - GraphOrderBy, - GraphType, - WidgetType, -} from '~/generated-metadata/graphql'; +import { GraphOrderBy, WidgetType } from '~/generated-metadata/graphql'; import { AggregateOperations, AxisNameDisplay, + BarChartLayout, PageLayoutType, + WidgetConfigurationType, } from '~/generated/graphql'; import { ChipGeneratorsDecorator } from '~/testing/decorators/ChipGeneratorsDecorator'; import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator'; @@ -268,7 +266,7 @@ export const WithNumberChart: Story = { }, configuration: { __typename: 'AggregateChartConfiguration', - graphType: GraphType.AGGREGATE, + configurationType: WidgetConfigurationType.AGGREGATE_CHART, aggregateOperation: AggregateOperations.COUNT, aggregateFieldMetadataId: idField.id, displayDataLabel: true, @@ -353,7 +351,7 @@ export const WithGaugeChart: Story = { }, configuration: { __typename: 'GaugeChartConfiguration', - graphType: GraphType.GAUGE, + configurationType: WidgetConfigurationType.GAUGE_CHART, aggregateOperation: AggregateOperations.COUNT, aggregateFieldMetadataId: idField.id, displayDataLabel: false, @@ -438,7 +436,8 @@ export const WithBarChart: Story = { }, configuration: { __typename: 'BarChartConfiguration', - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, + layout: BarChartLayout.VERTICAL, aggregateOperation: AggregateOperations.COUNT, aggregateFieldMetadataId: idField.id, primaryAxisGroupByFieldMetadataId: createdAtField.id, @@ -533,7 +532,7 @@ export const SmallWidget: Story = { }, configuration: { __typename: 'AggregateChartConfiguration', - graphType: GraphType.AGGREGATE, + configurationType: WidgetConfigurationType.AGGREGATE_CHART, aggregateOperation: AggregateOperations.COUNT, aggregateFieldMetadataId: idField.id, displayDataLabel: true, @@ -625,7 +624,8 @@ export const MediumWidget: Story = { }, configuration: { __typename: 'BarChartConfiguration', - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, + layout: BarChartLayout.VERTICAL, aggregateOperation: AggregateOperations.COUNT, aggregateFieldMetadataId: idField.id, primaryAxisGroupByFieldMetadataId: createdAtField.id, @@ -720,7 +720,8 @@ export const LargeWidget: Story = { }, configuration: { __typename: 'BarChartConfiguration', - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, + layout: BarChartLayout.VERTICAL, aggregateOperation: AggregateOperations.COUNT, aggregateFieldMetadataId: idField.id, primaryAxisGroupByFieldMetadataId: createdAtField.id, @@ -815,7 +816,7 @@ export const WideWidget: Story = { }, configuration: { __typename: 'AggregateChartConfiguration', - graphType: GraphType.AGGREGATE, + configurationType: WidgetConfigurationType.AGGREGATE_CHART, aggregateOperation: AggregateOperations.COUNT, aggregateFieldMetadataId: idField.id, displayDataLabel: true, @@ -907,7 +908,8 @@ export const TallWidget: Story = { }, configuration: { __typename: 'BarChartConfiguration', - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, + layout: BarChartLayout.VERTICAL, aggregateOperation: AggregateOperations.COUNT, aggregateFieldMetadataId: idField.id, primaryAxisGroupByFieldMetadataId: createdAtField.id, @@ -1003,6 +1005,7 @@ export const WithManyToOneRelationFieldWidget: Story = { }, configuration: { __typename: 'FieldConfiguration', + configurationType: WidgetConfigurationType.FIELD, fieldMetadataId: accountOwnerField.id, layout: 'FIELD', }, @@ -1116,6 +1119,7 @@ export const WithOneToManyRelationFieldWidget: Story = { }, configuration: { __typename: 'FieldConfiguration', + configurationType: WidgetConfigurationType.FIELD, fieldMetadataId: companyPeopleField.id, layout: 'FIELD', }, @@ -1224,7 +1228,7 @@ export const OnMobile: Story = { }, configuration: { __typename: 'AggregateChartConfiguration', - graphType: GraphType.AGGREGATE, + configurationType: WidgetConfigurationType.AGGREGATE_CHART, aggregateOperation: AggregateOperations.COUNT, aggregateFieldMetadataId: idField.id, displayDataLabel: true, @@ -1320,7 +1324,7 @@ export const InSidePanel: Story = { }, configuration: { __typename: 'AggregateChartConfiguration', - graphType: GraphType.AGGREGATE, + configurationType: WidgetConfigurationType.AGGREGATE_CHART, aggregateOperation: AggregateOperations.COUNT, aggregateFieldMetadataId: idField.id, displayDataLabel: true, @@ -1479,7 +1483,7 @@ export const Catalog: CatalogStory = { }, configuration: { __typename: 'AggregateChartConfiguration', - graphType: GraphType.AGGREGATE, + configurationType: WidgetConfigurationType.AGGREGATE_CHART, aggregateOperation: AggregateOperations.COUNT, aggregateFieldMetadataId: idField.id, displayDataLabel: true, diff --git a/packages/twenty-front/src/modules/page-layout/widgets/field/components/__stories__/FieldWidget.stories.tsx b/packages/twenty-front/src/modules/page-layout/widgets/field/components/__stories__/FieldWidget.stories.tsx index d17af24c1c..b61e5dd3bd 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/field/components/__stories__/FieldWidget.stories.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/field/components/__stories__/FieldWidget.stories.tsx @@ -26,7 +26,11 @@ import { FieldWidget } from '@/page-layout/widgets/field/components/FieldWidget' import { WidgetComponentInstanceContext } from '@/page-layout/widgets/states/contexts/WidgetComponentInstanceContext'; import { LayoutRenderingProvider } from '@/ui/layout/contexts/LayoutRenderingContext'; import { ComponentDecorator } from 'twenty-ui/testing'; -import { PageLayoutType, WidgetType } from '~/generated-metadata/graphql'; +import { + PageLayoutType, + WidgetConfigurationType, + WidgetType, +} from '~/generated-metadata/graphql'; import { ChipGeneratorsDecorator } from '~/testing/decorators/ChipGeneratorsDecorator'; import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator'; import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper'; @@ -336,6 +340,7 @@ export const TextFieldWidget: Story = { }, configuration: { __typename: 'FieldConfiguration', + configurationType: WidgetConfigurationType.FIELD, fieldMetadataId: nameField.id, layout: 'FIELD', }, @@ -427,6 +432,7 @@ export const AddressFieldWidget: Story = { }, configuration: { __typename: 'FieldConfiguration', + configurationType: WidgetConfigurationType.FIELD, fieldMetadataId: addressField.id, layout: 'FIELD', }, @@ -521,6 +527,7 @@ export const NumberFieldWidget: Story = { }, configuration: { __typename: 'FieldConfiguration', + configurationType: WidgetConfigurationType.FIELD, fieldMetadataId: employeesField.id, layout: 'FIELD', }, @@ -612,6 +619,7 @@ export const LinkFieldWidget: Story = { }, configuration: { __typename: 'FieldConfiguration', + configurationType: WidgetConfigurationType.FIELD, fieldMetadataId: linkedinField.id, layout: 'FIELD', }, @@ -703,6 +711,7 @@ export const ManyToOneRelationFieldWidget: Story = { }, configuration: { __typename: 'FieldConfiguration', + configurationType: WidgetConfigurationType.FIELD, fieldMetadataId: accountOwnerField.id, layout: 'FIELD', }, @@ -804,6 +813,7 @@ export const OneToManyRelationFieldWidget: Story = { }, configuration: { __typename: 'FieldConfiguration', + configurationType: WidgetConfigurationType.FIELD, fieldMetadataId: companyPeopleField.id, layout: 'FIELD', }, @@ -900,6 +910,7 @@ export const BooleanFieldWidget: Story = { }, configuration: { __typename: 'FieldConfiguration', + configurationType: WidgetConfigurationType.FIELD, fieldMetadataId: idealCustomerProfileField.id, layout: 'FIELD', }, @@ -990,6 +1001,7 @@ export const CurrencyFieldWidget: Story = { }, configuration: { __typename: 'FieldConfiguration', + configurationType: WidgetConfigurationType.FIELD, fieldMetadataId: annualRecurringRevenueField.id, layout: 'FIELD', }, @@ -1080,6 +1092,7 @@ export const EmailsFieldWidget: Story = { }, configuration: { __typename: 'FieldConfiguration', + configurationType: WidgetConfigurationType.FIELD, fieldMetadataId: personEmailsField.id, layout: 'FIELD', }, @@ -1174,6 +1187,7 @@ export const PhonesFieldWidget: Story = { }, configuration: { __typename: 'FieldConfiguration', + configurationType: WidgetConfigurationType.FIELD, fieldMetadataId: personPhonesField.id, layout: 'FIELD', }, @@ -1268,6 +1282,7 @@ export const SelectFieldWidget: Story = { }, configuration: { __typename: 'FieldConfiguration', + configurationType: WidgetConfigurationType.FIELD, fieldMetadataId: opportunityStageField.id, layout: 'FIELD', }, @@ -1363,6 +1378,7 @@ export const MultiSelectFieldWidget: Story = { }, configuration: { __typename: 'FieldConfiguration', + configurationType: WidgetConfigurationType.FIELD, fieldMetadataId: companyWorkPolicyField.id, layout: 'FIELD', }, @@ -1458,6 +1474,7 @@ export const TimelineActivityRelationFieldWidget: Story = { }, configuration: { __typename: 'FieldConfiguration', + configurationType: WidgetConfigurationType.FIELD, fieldMetadataId: timelineActivityWorkspaceMemberField.id, layout: 'FIELD', }, diff --git a/packages/twenty-front/src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetSectionsWithFields.ts b/packages/twenty-front/src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetSectionsWithFields.ts index d9d3c09419..ea719be779 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetSectionsWithFields.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetSectionsWithFields.ts @@ -21,6 +21,7 @@ export const useFieldsWidgetSectionsWithFields = ( const sectionsWithFields = filterAndOrderFieldsFromConfiguration({ configuration: temporaryConfiguration ?? { __typename: 'FieldsConfiguration', + configurationType: 'FIELDS', sections: [], }, availableFieldMetadataItems: fieldMetadataItems, diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/components/GraphWidget.tsx b/packages/twenty-front/src/modules/page-layout/widgets/graph/components/GraphWidget.tsx index 5ab753a8e9..d809ef6acb 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/components/GraphWidget.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/components/GraphWidget.tsx @@ -1,5 +1,4 @@ import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById'; -import { getDefaultWidgetData } from '@/page-layout/utils/getDefaultWidgetData'; import { PageLayoutWidgetNoDataDisplay } from '@/page-layout/widgets/components/PageLayoutWidgetNoDataDisplay'; import { ChartSkeletonLoader } from '@/page-layout/widgets/graph/components/ChartSkeletonLoader'; import { GraphWidgetAggregateChartRenderer } from '@/page-layout/widgets/graph/graphWidgetAggregateChart/components/GraphWidgetAggregateChartRenderer'; @@ -7,9 +6,10 @@ import { GraphWidgetBarChartRenderer } from '@/page-layout/widgets/graph/graphWi import { GraphWidgetLineChartRenderer } from '@/page-layout/widgets/graph/graphWidgetLineChart/components/GraphWidgetLineChartRenderer'; import { GraphWidgetPieChartRenderer } from '@/page-layout/widgets/graph/graphWidgetPieChart/components/GraphWidgetPieChartRenderer'; import { areChartConfigurationFieldsValidForQuery } from '@/page-layout/widgets/graph/utils/areChartConfigurationFieldsValidForQuery'; -import { lazy, Suspense } from 'react'; -import { GraphType } from '~/generated/graphql'; import { useCurrentWidget } from '@/page-layout/widgets/hooks/useCurrentWidget'; +import { lazy, Suspense } from 'react'; +import { isDefined } from 'twenty-shared/utils'; +import { WidgetConfigurationType } from '~/generated/graphql'; const GraphWidgetGaugeChart = lazy(() => import( @@ -21,13 +21,9 @@ const GraphWidgetGaugeChart = lazy(() => export type GraphWidgetProps = { objectMetadataId: string; - graphType: GraphType; }; -export const GraphWidget = ({ - objectMetadataId, - graphType, -}: GraphWidgetProps) => { +export const GraphWidget = ({ objectMetadataId }: GraphWidgetProps) => { const widget = useCurrentWidget(); const { objectMetadataItem } = useObjectMetadataItemById({ @@ -39,19 +35,24 @@ export const GraphWidget = ({ objectMetadataItem, ); - if (!hasValidConfiguration) { + if (!isDefined(widget.configuration) || !hasValidConfiguration) { return ; } - switch (graphType) { - case GraphType.AGGREGATE: + const configurationType = widget.configuration?.configurationType; + + switch (configurationType) { + case WidgetConfigurationType.AGGREGATE_CHART: return ; - case GraphType.GAUGE: { - const gaugeData: any = getDefaultWidgetData(graphType); - if (!gaugeData) { - return null; - } + case WidgetConfigurationType.GAUGE_CHART: { + const gaugeData = { + value: 0.7, + min: 0, + max: 1, + label: 'Progress', + }; + return ( }> ; - case GraphType.VERTICAL_BAR: - case GraphType.HORIZONTAL_BAR: + case WidgetConfigurationType.BAR_CHART: return ; - case GraphType.LINE: + case WidgetConfigurationType.LINE_CHART: return ; default: diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/components/GraphWidgetRenderer.tsx b/packages/twenty-front/src/modules/page-layout/widgets/graph/components/GraphWidgetRenderer.tsx index 09193fb572..2028b0fd6e 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/components/GraphWidgetRenderer.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/components/GraphWidgetRenderer.tsx @@ -2,30 +2,19 @@ import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget'; import { PageLayoutWidgetNoDataDisplay } from '@/page-layout/widgets/components/PageLayoutWidgetNoDataDisplay'; import { GraphWidget } from '@/page-layout/widgets/graph/components/GraphWidget'; import { isDefined } from 'twenty-shared/utils'; -import { GraphType } from '~/generated/graphql'; type GraphWidgetRendererProps = { widget: PageLayoutWidget; }; export const GraphWidgetRenderer = ({ widget }: GraphWidgetRendererProps) => { - if (!widget.configuration || !('graphType' in widget.configuration)) { + if (!isDefined(widget.configuration)) { return ; } - const graphType = widget.configuration.graphType; - - if ( - !Object.values(GraphType).includes(graphType) || - !isDefined(widget.objectMetadataId) - ) { + if (!isDefined(widget.objectMetadataId)) { return ; } - return ( - - ); + return ; }; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/components/__stories__/GraphWidgetBarChart.stories.tsx b/packages/twenty-front/src/modules/page-layout/widgets/graph/components/__stories__/GraphWidgetBarChart.stories.tsx index 642831098a..bc4e6a7fb0 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/components/__stories__/GraphWidgetBarChart.stories.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/components/__stories__/GraphWidgetBarChart.stories.tsx @@ -2,9 +2,9 @@ import { type Meta, type StoryObj } from '@storybook/react'; import { GraphWidgetTestWrapper } from '@/page-layout/widgets/graph/__tests__/GraphWidgetTestWrapper'; import { GraphWidgetBarChart } from '@/page-layout/widgets/graph/graphWidgetBarChart/components/GraphWidgetBarChart'; -import { BarChartLayout } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartLayout'; -import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator'; import { CatalogDecorator, ComponentDecorator } from 'twenty-ui/testing'; +import { BarChartLayout } from '~/generated/graphql'; +import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator'; const meta: Meta = { title: 'Modules/PageLayout/Widgets/GraphWidgetBarChart', diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/components/__stories__/GraphWidgetPieChart.stories.tsx b/packages/twenty-front/src/modules/page-layout/widgets/graph/components/__stories__/GraphWidgetPieChart.stories.tsx index 0c8f215a1d..715c2d98df 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/components/__stories__/GraphWidgetPieChart.stories.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/components/__stories__/GraphWidgetPieChart.stories.tsx @@ -6,7 +6,7 @@ import { GraphWidgetPieChart } from '@/page-layout/widgets/graph/graphWidgetPieC import { CatalogDecorator, ComponentDecorator } from 'twenty-ui/testing'; import { AggregateOperations, - GraphType, + WidgetConfigurationType, type PieChartConfiguration, } from '~/generated/graphql'; import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator'; @@ -26,7 +26,7 @@ const mockObjectMetadataItemId = companyObjectMetadataItem.id; const mockConfiguration: PieChartConfiguration = { aggregateFieldMetadataId: idField.id, aggregateOperation: AggregateOperations.COUNT, - graphType: GraphType.PIE, + configurationType: WidgetConfigurationType.PIE_CHART, groupByFieldMetadataId: idField.id, }; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/CustomBarItem.tsx b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/CustomBarItem.tsx index 8a066fa499..7ec4f93cfd 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/CustomBarItem.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/CustomBarItem.tsx @@ -1,6 +1,5 @@ import { LEGEND_HIGHLIGHT_DIMMED_OPACITY } from '@/page-layout/widgets/graph/constants/LegendHighlightDimmedOpacity.constant'; import { BAR_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartConstants'; -import { BarChartLayout } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartLayout'; import { graphWidgetHighlightedLegendIdComponentState } from '@/page-layout/widgets/graph/states/graphWidgetHighlightedLegendIdComponentState'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { type BarDatum, type BarItemProps } from '@nivo/bar'; @@ -9,6 +8,7 @@ import { isNumber } from '@sniptt/guards'; import { useCallback, useMemo, type MouseEvent } from 'react'; import styled from 'styled-components'; import { isDefined } from 'twenty-shared/utils'; +import { BarChartLayout } from '~/generated/graphql'; type CustomBarItemProps = BarItemProps & { keys?: string[]; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/CustomTotalsLayer.tsx b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/CustomTotalsLayer.tsx index 9aae1afeec..1c9911d5f7 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/CustomTotalsLayer.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/CustomTotalsLayer.tsx @@ -1,10 +1,10 @@ import { GraphDataLabel } from '@/page-layout/widgets/graph/components/GraphDataLabel'; import { type BarChartLabelData } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartLabelData'; -import { BarChartLayout } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartLayout'; import { computeBarChartGroupedLabels } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/computeBarChartGroupedLabels'; import { computeBarChartStackedLabels } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/computeBarChartStackedLabels'; import { type GraphLabelData } from '@/page-layout/widgets/graph/types/GraphLabelData'; import { type BarCustomLayerProps, type BarDatum } from '@nivo/bar'; +import { BarChartLayout } from '~/generated/graphql'; type CustomTotalsLayerProps = Pick, 'bars'> & { formatValue?: (value: number) => string; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/GraphWidgetBarChart.tsx b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/GraphWidgetBarChart.tsx index 0f172ca097..11c218f922 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/GraphWidgetBarChart.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/GraphWidgetBarChart.tsx @@ -8,7 +8,6 @@ import { BAR_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetBarC import { useBarChartData } from '@/page-layout/widgets/graph/graphWidgetBarChart/hooks/useBarChartData'; import { useBarChartTheme } from '@/page-layout/widgets/graph/graphWidgetBarChart/hooks/useBarChartTheme'; import { graphWidgetBarTooltipComponentState } from '@/page-layout/widgets/graph/graphWidgetBarChart/states/graphWidgetBarTooltipComponentState'; -import { BarChartLayout } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartLayout'; import { type BarChartSeries } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartSeries'; import { calculateStackedBarChartValueRange } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/calculateStackedBarChartValueRange'; import { calculateValueRangeFromBarChartKeys } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/calculateValueRangeFromBarChartKeys'; @@ -38,6 +37,7 @@ import { import { useCallback, useMemo, useRef, useState, type MouseEvent } from 'react'; import { isDefined } from 'twenty-shared/utils'; import { useDebouncedCallback } from 'use-debounce'; +import { BarChartLayout } from '~/generated/graphql'; type NoDataLayerWrapperProps = BarCustomLayerProps; @@ -279,7 +279,9 @@ export const GraphWidgetBarChart = ({ margin={margins} padding={BAR_CHART_CONSTANTS.OUTER_PADDING_RATIO} groupMode={groupMode} - layout={layout} + layout={ + layout === BarChartLayout.VERTICAL ? 'vertical' : 'horizontal' + } valueScale={{ type: 'linear', min: valueDomain.min, @@ -343,7 +345,7 @@ export const GraphWidgetBarChart = ({ enrichedKeys={enrichedKeys} formatOptions={formatOptions} enableGroupTooltip={groupMode === 'stacked'} - layout={layout} + layout={layout === BarChartLayout.VERTICAL ? 'vertical' : 'horizontal'} onBarClick={onBarClick} onMouseEnter={handleTooltipMouseEnter} onMouseLeave={handleTooltipMouseLeave} diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/hooks/useGraphBarChartWidgetData.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/hooks/useGraphBarChartWidgetData.ts index 4c085ea8ac..ac5610761b 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/hooks/useGraphBarChartWidgetData.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/hooks/useGraphBarChartWidgetData.ts @@ -1,6 +1,5 @@ import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; -import { type BarChartLayout } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartLayout'; import { type BarChartSeries } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartSeries'; import { getBarChartQueryLimit } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartQueryLimit'; import { transformGroupByDataToBarChartData } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/transformGroupByDataToBarChartData'; @@ -10,7 +9,10 @@ import { useUserFirstDayOfTheWeek } from '@/ui/input/components/internal/date/ho import { useUserTimezone } from '@/ui/input/components/internal/date/hooks/useUserTimezone'; import { type BarDatum } from '@nivo/bar'; import { useMemo } from 'react'; -import { type BarChartConfiguration } from '~/generated/graphql'; +import { + type BarChartConfiguration, + type BarChartLayout, +} from '~/generated/graphql'; type UseGraphBarChartWidgetDataProps = { objectMetadataItemId: string; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartLayout.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartLayout.ts deleted file mode 100644 index ff637662cb..0000000000 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartLayout.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum BarChartLayout { - VERTICAL = 'vertical', - HORIZONTAL = 'horizontal', -} diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/__tests__/calculateBarChartEndLineCoordinates.test.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/__tests__/calculateBarChartEndLineCoordinates.test.ts index 015569718e..136519bb7c 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/__tests__/calculateBarChartEndLineCoordinates.test.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/__tests__/calculateBarChartEndLineCoordinates.test.ts @@ -1,6 +1,7 @@ -import { BarChartLayout } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartLayout'; -import { type BarDatum, type ComputedBarDatum } from '@nivo/bar'; import { calculateBarChartEndLineCoordinates } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/calculateBarChartEndLineCoordinates'; +import { type BarDatum, type ComputedBarDatum } from '@nivo/bar'; +import { BarChartLayout } from '~/generated/graphql'; + describe('calculateBarChartEndLineCoordinates', () => { const createMockBar = ( overrides?: Partial>, diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/__tests__/transformGroupByDataToBarChartData.test.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/__tests__/transformGroupByDataToBarChartData.test.ts index 3fb50f6a59..46608b937f 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/__tests__/transformGroupByDataToBarChartData.test.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/__tests__/transformGroupByDataToBarChartData.test.ts @@ -4,8 +4,11 @@ import { FirstDayOfTheWeek, ObjectRecordGroupByDateGranularity, } from 'twenty-shared/types'; -import { GraphType } from '~/generated-metadata/graphql'; -import { AxisNameDisplay } from '~/generated/graphql'; +import { + AxisNameDisplay, + BarChartLayout, + WidgetConfigurationType, +} from '~/generated/graphql'; jest.mock( '@/page-layout/widgets/graph/graphWidgetBarChart/utils/fillDateGapsInBarChartData', @@ -73,7 +76,8 @@ describe('transformGroupByDataToBarChartData', () => { __typename: 'BarChartConfiguration', aggregateFieldMetadataId: aggregateField.id, aggregateOperation: 'COUNT', - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, + layout: BarChartLayout.VERTICAL, primaryAxisGroupByFieldMetadataId: groupByField.id, primaryAxisGroupBySubFieldName: 'createdAt', primaryAxisDateGranularity: ObjectRecordGroupByDateGranularity.MONTH, diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/__tests__/transformTwoDimensionalGroupByToBarChartData.test.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/__tests__/transformTwoDimensionalGroupByToBarChartData.test.ts index 57c96e7453..30452e615c 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/__tests__/transformTwoDimensionalGroupByToBarChartData.test.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/__tests__/transformTwoDimensionalGroupByToBarChartData.test.ts @@ -5,8 +5,9 @@ import { FirstDayOfTheWeek } from 'twenty-shared/types'; import { FieldMetadataType } from '~/generated-metadata/graphql'; import { AggregateOperations, + BarChartLayout, GraphOrderBy, - GraphType, + WidgetConfigurationType, type BarChartConfiguration, } from '~/generated/graphql'; import { transformTwoDimensionalGroupByToBarChartData } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/transformTwoDimensionalGroupByToBarChartData'; @@ -44,7 +45,8 @@ describe('transformTwoDimensionalGroupByToBarChartData', () => { const mockConfiguration: BarChartConfiguration = { __typename: 'BarChartConfiguration', - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, + layout: BarChartLayout.VERTICAL, aggregateFieldMetadataId: 'field-aggregate', aggregateOperation: AggregateOperations.SUM, primaryAxisGroupByFieldMetadataId: 'field-x', diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/calculateBarChartEndLineCoordinates.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/calculateBarChartEndLineCoordinates.ts index 81c5086214..d2ee6f46e5 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/calculateBarChartEndLineCoordinates.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/calculateBarChartEndLineCoordinates.ts @@ -1,5 +1,5 @@ -import { BarChartLayout } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartLayout'; import { type BarDatum, type ComputedBarDatum } from '@nivo/bar'; +import { BarChartLayout } from '~/generated/graphql'; export const calculateBarChartEndLineCoordinates = ( bar: ComputedBarDatum, diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/calculateWidthPerTick.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/calculateWidthPerTick.ts index 36bee52d1a..fc6f20dff7 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/calculateWidthPerTick.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/calculateWidthPerTick.ts @@ -1,4 +1,4 @@ -import { BarChartLayout } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartLayout'; +import { BarChartLayout } from '~/generated/graphql'; export const calculateWidthPerTick = ({ layout, diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/computeBarChartCategoryTickValues.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/computeBarChartCategoryTickValues.ts index acba82f045..a3d94c5274 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/computeBarChartCategoryTickValues.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/computeBarChartCategoryTickValues.ts @@ -1,9 +1,9 @@ import { BAR_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartConstants'; -import { BarChartLayout } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartLayout'; import { computeMinHeightPerTick } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/computeMinHeightPerTick'; import { getBarChartMargins } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartMargins'; import { computeChartCategoryTickValues } from '@/page-layout/widgets/graph/utils/computeChartCategoryTickValues'; import { type BarDatum } from '@nivo/bar'; +import { BarChartLayout } from '~/generated/graphql'; export const computeBarChartCategoryTickValues = ({ axisSize, diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/computeBarChartValueTickCount.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/computeBarChartValueTickCount.ts index 5d815a867d..7a49b0468b 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/computeBarChartValueTickCount.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/computeBarChartValueTickCount.ts @@ -1,6 +1,6 @@ import { BAR_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartConstants'; -import { BarChartLayout } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartLayout'; import { computeMinHeightPerTick } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/computeMinHeightPerTick'; +import { BarChartLayout } from '~/generated/graphql'; type ComputeBarChartValueTickCountProps = { axisSize: number; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartAxisConfigs.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartAxisConfigs.ts index fbc24731a8..844c46cebb 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartAxisConfigs.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartAxisConfigs.ts @@ -1,4 +1,3 @@ -import { BarChartLayout } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartLayout'; import { getBarChartMargins } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartMargins'; import { truncateTickLabel } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/truncateTickLabel'; import { @@ -9,6 +8,7 @@ import { import { COMMON_CHART_CONSTANTS } from '@/page-layout/widgets/graph/constants/CommonChartConstants'; import { BAR_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartConstants'; import { type BarChartTickConfig } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartTickConfig'; +import { BarChartLayout } from '~/generated/graphql'; type GetBarChartAxisConfigsProps = { layout: BarChartLayout; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartInnerPadding.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartInnerPadding.ts index 2c1e31f0e5..2799af0b94 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartInnerPadding.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartInnerPadding.ts @@ -1,5 +1,5 @@ import { BAR_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartConstants'; -import { BarChartLayout } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartLayout'; +import { BarChartLayout } from '~/generated/graphql'; type BarChartMargins = { top: number; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartMargins.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartMargins.ts index beac97f818..c28de17ee9 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartMargins.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartMargins.ts @@ -1,6 +1,6 @@ import { COMMON_CHART_CONSTANTS } from '@/page-layout/widgets/graph/constants/CommonChartConstants'; -import { BarChartLayout } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartLayout'; import { isDefined } from 'twenty-shared/utils'; +import { BarChartLayout } from '~/generated/graphql'; const BAR_CHART_MARGINS = { top: COMMON_CHART_CONSTANTS.MARGIN_TOP, diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartTickConfig.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartTickConfig.ts index ab9c1650be..841ff55480 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartTickConfig.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/getBarChartTickConfig.ts @@ -1,5 +1,4 @@ import { BAR_CHART_CONSTANTS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartConstants'; -import { BarChartLayout } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartLayout'; import { calculateWidthPerTick } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/calculateWidthPerTick'; import { computeBarChartCategoryTickValues } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/computeBarChartCategoryTickValues'; import { computeBarChartValueTickCount } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/computeBarChartValueTickCount'; @@ -7,6 +6,7 @@ import { getBarChartMargins } from '@/page-layout/widgets/graph/graphWidgetBarCh import { computeMaxLabelLengthForMargin } from '@/page-layout/widgets/graph/utils/computeMaxLabelLengthForMargin'; import { getTickRotationConfig } from '@/page-layout/widgets/graph/utils/getTickRotationConfig'; import { type BarDatum } from '@nivo/bar'; +import { BarChartLayout } from '~/generated/graphql'; export type BarChartTickConfig = { categoryTickValues: (string | number)[]; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/transformGroupByDataToBarChartData.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/transformGroupByDataToBarChartData.ts index 10f38ac648..4159416cfd 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/transformGroupByDataToBarChartData.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/utils/transformGroupByDataToBarChartData.ts @@ -4,7 +4,6 @@ import { getAggregateOperationLabel } from '@/object-record/record-board/record- import { type ExtendedAggregateOperations } from '@/object-record/record-table/types/ExtendedAggregateOperations'; import { getGroupByQueryResultGqlFieldName } from '@/page-layout/utils/getGroupByQueryResultGqlFieldName'; import { GRAPH_DEFAULT_DATE_GRANULARITY } from '@/page-layout/widgets/graph/constants/GraphDefaultDateGranularity'; -import { BarChartLayout } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartLayout'; import { type BarChartSeries } from '@/page-layout/widgets/graph/graphWidgetBarChart/types/BarChartSeries'; import { fillDateGapsInBarChartData } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/fillDateGapsInBarChartData'; import { transformOneDimensionalGroupByToBarChartData } from '@/page-layout/widgets/graph/graphWidgetBarChart/utils/transformOneDimensionalGroupByToBarChartData'; @@ -16,13 +15,13 @@ import { getFieldKey } from '@/page-layout/widgets/graph/utils/getFieldKey'; import { isRelationNestedFieldDateKind } from '@/page-layout/widgets/graph/utils/isRelationNestedFieldDateKind'; import { type BarDatum } from '@nivo/bar'; import { - type FirstDayOfTheWeek, isDefined, isFieldMetadataDateKind, + type FirstDayOfTheWeek, } from 'twenty-shared/utils'; -import { GraphType } from '~/generated-metadata/graphql'; import { AxisNameDisplay, + BarChartLayout, type BarChartConfiguration, } from '~/generated/graphql'; @@ -121,7 +120,7 @@ export const transformGroupByDataToBarChartData = ({ : undefined; const layout = - configuration.graphType === GraphType.HORIZONTAL_BAR + configuration.layout === BarChartLayout.HORIZONTAL ? BarChartLayout.HORIZONTAL : BarChartLayout.VERTICAL; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetPieChart/utils/__tests__/transformGroupByDataToPieChartData.test.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetPieChart/utils/__tests__/transformGroupByDataToPieChartData.test.ts index ee4d4b2a13..76af24c3e0 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetPieChart/utils/__tests__/transformGroupByDataToPieChartData.test.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetPieChart/utils/__tests__/transformGroupByDataToPieChartData.test.ts @@ -5,9 +5,11 @@ import { FirstDayOfTheWeek } from 'twenty-shared/types'; import { AggregateOperations, FieldMetadataType, - GraphType, } from '~/generated-metadata/graphql'; -import { type PieChartConfiguration } from '~/generated/graphql'; +import { + WidgetConfigurationType, + type PieChartConfiguration, +} from '~/generated/graphql'; jest.mock( '@/page-layout/widgets/graph/utils/formatPrimaryDimensionValues', @@ -49,7 +51,7 @@ describe('transformGroupByDataToPieChartData', () => { __typename: 'PieChartConfiguration', aggregateFieldMetadataId: aggregateField.id, aggregateOperation: AggregateOperations.COUNT, - graphType: GraphType.PIE, + configurationType: WidgetConfigurationType.PIE_CHART, groupByFieldMetadataId: groupByField.id, displayLegend: true, } as PieChartConfiguration; @@ -118,7 +120,7 @@ describe('transformGroupByDataToPieChartData', () => { __typename: 'PieChartConfiguration', aggregateFieldMetadataId: aggregateField.id, aggregateOperation: AggregateOperations.COUNT, - graphType: GraphType.PIE, + configurationType: WidgetConfigurationType.PIE_CHART, groupByFieldMetadataId: groupByField.id, displayLegend: false, color: 'red', diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/hooks/useGraphWidgetGroupByQuery.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/hooks/useGraphWidgetGroupByQuery.ts index c230d5662a..c3ec4daff9 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/hooks/useGraphWidgetGroupByQuery.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/hooks/useGraphWidgetGroupByQuery.ts @@ -1,4 +1,4 @@ -import { isPieChartConfiguration } from '@/command-menu/pages/page-layout/utils/isPieChartConfiguration'; +import { isWidgetConfigurationOfType } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfType'; import { useDateTimeFormat } from '@/localization/hooks/useDateTimeFormat'; import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; @@ -60,7 +60,10 @@ export const useGraphWidgetGroupByQuery = ({ throw new Error('Aggregate operation not found'); } - const groupByQueryVariables = isPieChartConfiguration(configuration) + const groupByQueryVariables = isWidgetConfigurationOfType( + configuration, + 'PieChartConfiguration', + ) ? generateGroupByQueryVariablesFromPieChartConfiguration({ objectMetadataItem, objectMetadataItems, diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/areChartConfigurationFieldsValidForQuery.test.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/areChartConfigurationFieldsValidForQuery.test.ts index 3aaafee314..d0625b30e7 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/areChartConfigurationFieldsValidForQuery.test.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/areChartConfigurationFieldsValidForQuery.test.ts @@ -2,7 +2,7 @@ import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataI import { FieldMetadataType } from '~/generated-metadata/graphql'; import { AggregateOperations, - GraphType, + WidgetConfigurationType, type AggregateChartConfiguration, } from '~/generated/graphql'; @@ -34,7 +34,7 @@ describe('areChartConfigurationFieldsValidForQuery', () => { optionValue: string; }): AggregateChartConfiguration => ({ __typename: 'AggregateChartConfiguration', - graphType: GraphType.AGGREGATE, + configurationType: WidgetConfigurationType.AGGREGATE_CHART, aggregateFieldMetadataId: 'aggregate-field', aggregateOperation: AggregateOperations.COUNT, ratioAggregateConfig: ratioAggregateConfig ?? null, diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/generateGroupByQueryVariablesFromBarOrLineChartConfiguration.test.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/generateGroupByQueryVariablesFromBarOrLineChartConfiguration.test.ts index 5f2643553c..1a2cd932d1 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/generateGroupByQueryVariablesFromBarOrLineChartConfiguration.test.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/generateGroupByQueryVariablesFromBarOrLineChartConfiguration.test.ts @@ -7,8 +7,8 @@ import { import { AggregateOperations, type BarChartConfiguration, - GraphType, type LineChartConfiguration, + WidgetConfigurationType, } from '~/generated/graphql'; describe('generateGroupByQueryVariablesFromBarOrLineChartConfiguration', () => { @@ -47,7 +47,7 @@ describe('generateGroupByQueryVariablesFromBarOrLineChartConfiguration', () => { __typename: 'BarChartConfiguration', aggregateFieldMetadataId: 'aggregate-field', aggregateOperation: AggregateOperations.COUNT, - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, primaryAxisGroupByFieldMetadataId: 'field-1', ...overrides, }) as BarChartConfiguration; @@ -59,7 +59,7 @@ describe('generateGroupByQueryVariablesFromBarOrLineChartConfiguration', () => { __typename: 'LineChartConfiguration', aggregateFieldMetadataId: 'aggregate-field', aggregateOperation: AggregateOperations.COUNT, - graphType: GraphType.LINE, + configurationType: WidgetConfigurationType.LINE_CHART, primaryAxisGroupByFieldMetadataId: 'field-1', ...overrides, }) as LineChartConfiguration; @@ -72,7 +72,7 @@ describe('generateGroupByQueryVariablesFromBarOrLineChartConfiguration', () => { objectMetadataItem: mockObjectMetadataItem, objectMetadataItems: [], chartConfiguration: buildBarChartConfiguration({ - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, primaryAxisGroupByFieldMetadataId: 'field-1', primaryAxisGroupBySubFieldName: null, }), @@ -87,7 +87,7 @@ describe('generateGroupByQueryVariablesFromBarOrLineChartConfiguration', () => { objectMetadataItem: mockObjectMetadataItem, objectMetadataItems: [], chartConfiguration: buildBarChartConfiguration({ - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, primaryAxisGroupByFieldMetadataId: 'field-1', primaryAxisGroupBySubFieldName: null, secondaryAxisGroupByFieldMetadataId: 'field-2', @@ -104,7 +104,7 @@ describe('generateGroupByQueryVariablesFromBarOrLineChartConfiguration', () => { objectMetadataItem: mockObjectMetadataItem, objectMetadataItems: [], chartConfiguration: buildBarChartConfiguration({ - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, primaryAxisGroupByFieldMetadataId: 'field-4', primaryAxisGroupBySubFieldName: 'firstName', }), @@ -119,7 +119,7 @@ describe('generateGroupByQueryVariablesFromBarOrLineChartConfiguration', () => { objectMetadataItem: mockObjectMetadataItem, objectMetadataItems: [], chartConfiguration: buildBarChartConfiguration({ - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, primaryAxisGroupByFieldMetadataId: 'field-3', primaryAxisGroupBySubFieldName: null, primaryAxisDateGranularity: 'MONTH' as any, @@ -138,7 +138,7 @@ describe('generateGroupByQueryVariablesFromBarOrLineChartConfiguration', () => { objectMetadataItem: mockObjectMetadataItem, objectMetadataItems: [], chartConfiguration: buildBarChartConfiguration({ - graphType: GraphType.HORIZONTAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, primaryAxisGroupByFieldMetadataId: 'field-1', primaryAxisGroupBySubFieldName: null, }), @@ -153,7 +153,7 @@ describe('generateGroupByQueryVariablesFromBarOrLineChartConfiguration', () => { objectMetadataItem: mockObjectMetadataItem, objectMetadataItems: [], chartConfiguration: buildBarChartConfiguration({ - graphType: GraphType.HORIZONTAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, primaryAxisGroupByFieldMetadataId: 'field-1', secondaryAxisGroupByFieldMetadataId: 'field-2', }), @@ -196,7 +196,7 @@ describe('generateGroupByQueryVariablesFromBarOrLineChartConfiguration', () => { objectMetadataItem, objectMetadataItems: [objectMetadataItem, targetObjectMetadata], chartConfiguration: buildBarChartConfiguration({ - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, primaryAxisGroupByFieldMetadataId: relationField.id, primaryAxisGroupBySubFieldName: 'createdAt', }), @@ -246,7 +246,7 @@ describe('generateGroupByQueryVariablesFromBarOrLineChartConfiguration', () => { objectMetadataItem, objectMetadataItems: [objectMetadataItem, targetObjectMetadata], chartConfiguration: buildLineChartConfiguration({ - graphType: GraphType.LINE, + configurationType: WidgetConfigurationType.LINE_CHART, primaryAxisGroupByFieldMetadataId: relationField.id, primaryAxisGroupBySubFieldName: 'createdAt', }), @@ -271,7 +271,7 @@ describe('generateGroupByQueryVariablesFromBarOrLineChartConfiguration', () => { objectMetadataItem: mockObjectMetadataItem, objectMetadataItems: [], chartConfiguration: buildLineChartConfiguration({ - graphType: GraphType.LINE, + configurationType: WidgetConfigurationType.LINE_CHART, primaryAxisGroupByFieldMetadataId: 'field-1', primaryAxisGroupBySubFieldName: null, }), @@ -286,7 +286,7 @@ describe('generateGroupByQueryVariablesFromBarOrLineChartConfiguration', () => { objectMetadataItem: mockObjectMetadataItem, objectMetadataItems: [], chartConfiguration: buildLineChartConfiguration({ - graphType: GraphType.LINE, + configurationType: WidgetConfigurationType.LINE_CHART, primaryAxisGroupByFieldMetadataId: 'field-1', primaryAxisGroupBySubFieldName: null, secondaryAxisGroupByFieldMetadataId: 'field-2', @@ -303,7 +303,7 @@ describe('generateGroupByQueryVariablesFromBarOrLineChartConfiguration', () => { objectMetadataItem: mockObjectMetadataItem, objectMetadataItems: [], chartConfiguration: buildLineChartConfiguration({ - graphType: GraphType.LINE, + configurationType: WidgetConfigurationType.LINE_CHART, primaryAxisGroupByFieldMetadataId: 'field-3', primaryAxisGroupBySubFieldName: null, primaryAxisDateGranularity: 'MONTH' as any, @@ -320,7 +320,7 @@ describe('generateGroupByQueryVariablesFromBarOrLineChartConfiguration', () => { objectMetadataItem: mockObjectMetadataItem, objectMetadataItems: [], chartConfiguration: buildLineChartConfiguration({ - graphType: GraphType.LINE, + configurationType: WidgetConfigurationType.LINE_CHART, primaryAxisGroupByFieldMetadataId: 'field-4', primaryAxisGroupBySubFieldName: 'firstName', }), @@ -335,7 +335,7 @@ describe('generateGroupByQueryVariablesFromBarOrLineChartConfiguration', () => { objectMetadataItem: mockObjectMetadataItem, objectMetadataItems: [], chartConfiguration: buildLineChartConfiguration({ - graphType: GraphType.LINE, + configurationType: WidgetConfigurationType.LINE_CHART, primaryAxisGroupByFieldMetadataId: 'field-1', secondaryAxisGroupByFieldMetadataId: 'field-2', }), diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/generateGroupByQueryVariablesFromPieChartConfiguration.test.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/generateGroupByQueryVariablesFromPieChartConfiguration.test.ts index c9dcde0e8c..47818aa29e 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/generateGroupByQueryVariablesFromPieChartConfiguration.test.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/generateGroupByQueryVariablesFromPieChartConfiguration.test.ts @@ -1,13 +1,13 @@ import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; +import { generateGroupByQueryVariablesFromPieChartConfiguration } from '@/page-layout/widgets/graph/utils/generateGroupByQueryVariablesFromPieChartConfiguration'; import { ObjectRecordGroupByDateGranularity } from 'twenty-shared/types'; import { AggregateOperations, FieldMetadataType, GraphOrderBy, - GraphType, + WidgetConfigurationType, type PieChartConfiguration, } from '~/generated-metadata/graphql'; -import { generateGroupByQueryVariablesFromPieChartConfiguration } from '@/page-layout/widgets/graph/utils/generateGroupByQueryVariablesFromPieChartConfiguration'; describe('generateGroupByQueryVariablesFromPieChartConfiguration', () => { const mockObjectMetadataItem: ObjectMetadataItem = { @@ -45,7 +45,7 @@ describe('generateGroupByQueryVariablesFromPieChartConfiguration', () => { __typename: 'PieChartConfiguration', aggregateFieldMetadataId: 'aggregate-field', aggregateOperation: AggregateOperations.COUNT, - graphType: GraphType.PIE, + configurationType: WidgetConfigurationType.PIE_CHART, groupByFieldMetadataId: 'field-1', ...overrides, }) as PieChartConfiguration; diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/normalizeChartConfigurationFields.test.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/normalizeChartConfigurationFields.test.ts index 3f72895344..b64c75747c 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/normalizeChartConfigurationFields.test.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/normalizeChartConfigurationFields.test.ts @@ -1,8 +1,8 @@ import { AggregateOperations, GraphOrderBy, - GraphType, ObjectRecordGroupByDateGranularity, + WidgetConfigurationType, } from '~/generated-metadata/graphql'; import { normalizeChartConfigurationFields } from '@/page-layout/widgets/graph/utils/normalizeChartConfigurationFields'; @@ -17,7 +17,7 @@ describe('normalizeChartConfigurationFields', () => { primaryAxisOrderBy: GraphOrderBy.FIELD_ASC, aggregateFieldMetadataId: 'aggregate-456', aggregateOperation: AggregateOperations.SUM, - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, } as any; const result = normalizeChartConfigurationFields(barConfig); @@ -37,7 +37,7 @@ describe('normalizeChartConfigurationFields', () => { primaryAxisOrderBy: GraphOrderBy.VALUE_DESC, aggregateFieldMetadataId: 'aggregate-012', aggregateOperation: AggregateOperations.COUNT, - graphType: GraphType.LINE, + configurationType: WidgetConfigurationType.LINE_CHART, } as any; const result = normalizeChartConfigurationFields(lineConfig); @@ -59,7 +59,7 @@ describe('normalizeChartConfigurationFields', () => { orderBy: GraphOrderBy.FIELD_DESC, aggregateFieldMetadataId: 'pie-aggregate', aggregateOperation: AggregateOperations.AVG, - graphType: GraphType.PIE, + configurationType: WidgetConfigurationType.PIE_CHART, } as any; const result = normalizeChartConfigurationFields(pieConfig); @@ -78,7 +78,7 @@ describe('normalizeChartConfigurationFields', () => { groupByFieldMetadataId: 'minimal-field', aggregateFieldMetadataId: 'minimal-aggregate', aggregateOperation: AggregateOperations.COUNT, - graphType: GraphType.PIE, + configurationType: WidgetConfigurationType.PIE_CHART, } as any; const result = normalizeChartConfigurationFields(pieConfig); diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/transformOneDimensionalGroupByToLineChartData.test.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/transformOneDimensionalGroupByToLineChartData.test.ts index 872159f171..89d9d555ac 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/transformOneDimensionalGroupByToLineChartData.test.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/transformOneDimensionalGroupByToLineChartData.test.ts @@ -5,7 +5,7 @@ import { FirstDayOfTheWeek } from 'twenty-shared/types'; import { AggregateOperations, FieldMetadataType, - GraphType, + WidgetConfigurationType, type LineChartConfiguration, } from '~/generated-metadata/graphql'; import { transformOneDimensionalGroupByToLineChartData } from '@/page-layout/widgets/graph/utils/transformOneDimensionalGroupByToLineChartData'; @@ -46,7 +46,7 @@ describe('transformOneDimensionalGroupByToLineChartData', () => { ): LineChartConfiguration => ({ __typename: 'LineChartConfiguration', - graphType: GraphType.LINE, + configurationType: WidgetConfigurationType.LINE_CHART, aggregateFieldMetadataId: 'amount-field', aggregateOperation: AggregateOperations.SUM, primaryAxisGroupByFieldMetadataId: 'stage-field', diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/transformTwoDimensionalGroupByToLineChartData.test.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/transformTwoDimensionalGroupByToLineChartData.test.ts index e0d11419a4..be575208cf 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/transformTwoDimensionalGroupByToLineChartData.test.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/__tests__/transformTwoDimensionalGroupByToLineChartData.test.ts @@ -5,7 +5,7 @@ import { FirstDayOfTheWeek } from 'twenty-shared/types'; import { AggregateOperations, FieldMetadataType, - GraphType, + WidgetConfigurationType, type LineChartConfiguration, } from '~/generated-metadata/graphql'; import { transformTwoDimensionalGroupByToLineChartData } from '@/page-layout/widgets/graph/utils/transformTwoDimensionalGroupByToLineChartData'; @@ -46,7 +46,7 @@ describe('transformTwoDimensionalGroupByToLineChartData', () => { ): LineChartConfiguration => ({ __typename: 'LineChartConfiguration', - graphType: GraphType.LINE, + configurationType: WidgetConfigurationType.LINE_CHART, aggregateFieldMetadataId: 'amount-field', aggregateOperation: AggregateOperations.SUM, primaryAxisGroupByFieldMetadataId: 'created-at-field', diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/assertAggregateChartWidget.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/assertAggregateChartWidget.ts index 7b05359467..9bb98926e1 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/assertAggregateChartWidget.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/assertAggregateChartWidget.ts @@ -1,4 +1,4 @@ -import { isAggregateChartConfiguration } from '@/command-menu/pages/page-layout/utils/isAggregateChartConfiguration'; +import { isWidgetConfigurationOfType } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfType'; import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget'; import { assertIsDefinedOrThrow } from 'twenty-shared/utils'; import { type AggregateChartConfiguration } from '~/generated/graphql'; @@ -17,7 +17,12 @@ export const assertAggregateChartWidgetOrThrow: AssertAggregateChartWidgetOrThro new Error('Widget objectMetadataId is required'), ); - if (!isAggregateChartConfiguration(widget.configuration)) { + if ( + !isWidgetConfigurationOfType( + widget.configuration, + 'AggregateChartConfiguration', + ) + ) { throw new Error( `Expected AggregateChartConfiguration but got ${widget.configuration?.__typename}`, ); diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/assertBarChartWidget.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/assertBarChartWidget.ts index ab2f702d58..4ac136da5a 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/assertBarChartWidget.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/assertBarChartWidget.ts @@ -1,4 +1,4 @@ -import { isBarChartConfiguration } from '@/command-menu/pages/page-layout/utils/isBarChartConfiguration'; +import { isWidgetConfigurationOfType } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfType'; import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget'; import { assertIsDefinedOrThrow } from 'twenty-shared/utils'; import { type BarChartConfiguration } from '~/generated/graphql'; @@ -18,7 +18,9 @@ export const assertBarChartWidgetOrThrow: AssertBarChartWidgetOrThrow = ( new Error('Widget objectMetadataId is required'), ); - if (!isBarChartConfiguration(widget.configuration)) { + if ( + !isWidgetConfigurationOfType(widget.configuration, 'BarChartConfiguration') + ) { throw new Error( `Expected BarChartConfiguration but got ${widget.configuration?.__typename}`, ); diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/assertPieChartWidget.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/assertPieChartWidget.ts index 55e61c9d85..165b48f5f8 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/assertPieChartWidget.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/assertPieChartWidget.ts @@ -1,4 +1,4 @@ -import { isPieChartConfiguration } from '@/command-menu/pages/page-layout/utils/isPieChartConfiguration'; +import { isWidgetConfigurationOfType } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfType'; import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget'; import { assertIsDefinedOrThrow } from 'twenty-shared/utils'; import { type PieChartConfiguration } from '~/generated/graphql'; @@ -18,7 +18,9 @@ export const assertPieChartWidgetOrThrow: AssertPieChartWidgetOrThrow = ( new Error('Widget objectMetadataId is required'), ); - if (!isPieChartConfiguration(widget.configuration)) { + if ( + !isWidgetConfigurationOfType(widget.configuration, 'PieChartConfiguration') + ) { throw new Error( `Expected PieChartConfiguration but got ${widget.configuration?.__typename}`, ); diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/normalizeChartConfigurationFields.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/normalizeChartConfigurationFields.ts index f96795c8f4..699e8dd39b 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/normalizeChartConfigurationFields.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/utils/normalizeChartConfigurationFields.ts @@ -1,5 +1,4 @@ -import { isBarOrLineChartConfiguration } from '@/command-menu/pages/page-layout/utils/isBarOrLineChartConfiguration'; -import { isPieChartConfiguration } from '@/command-menu/pages/page-layout/utils/isPieChartConfiguration'; +import { isWidgetConfigurationOfType } from '@/command-menu/pages/page-layout/utils/isWidgetConfigurationOfType'; import { type BarChartConfiguration, type GraphOrderBy, @@ -21,7 +20,11 @@ export const normalizeChartConfigurationFields = ( | LineChartConfiguration | PieChartConfiguration, ): NormalizedChartConfigurationFields => { - if (isBarOrLineChartConfiguration(configuration)) { + const isBarOrLineChart = + isWidgetConfigurationOfType(configuration, 'BarChartConfiguration') || + isWidgetConfigurationOfType(configuration, 'LineChartConfiguration'); + + if (isBarOrLineChart) { return { groupByFieldMetadataId: configuration.primaryAxisGroupByFieldMetadataId, groupBySubFieldName: configuration.primaryAxisGroupBySubFieldName, @@ -30,7 +33,7 @@ export const normalizeChartConfigurationFields = ( }; } - if (isPieChartConfiguration(configuration)) { + if (isWidgetConfigurationOfType(configuration, 'PieChartConfiguration')) { return { groupByFieldMetadataId: configuration.groupByFieldMetadataId, groupBySubFieldName: configuration.groupBySubFieldName, diff --git a/packages/twenty-front/src/modules/page-layout/widgets/standalone-rich-text/components/StandaloneRichTextWidget.tsx b/packages/twenty-front/src/modules/page-layout/widgets/standalone-rich-text/components/StandaloneRichTextWidget.tsx index 0d3d05790c..855182ebfd 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/standalone-rich-text/components/StandaloneRichTextWidget.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/standalone-rich-text/components/StandaloneRichTextWidget.tsx @@ -29,6 +29,7 @@ import { isDefined } from 'twenty-shared/utils'; import { useDebouncedCallback } from 'use-debounce'; import { PageLayoutType, + WidgetConfigurationType, type StandaloneRichTextConfiguration, } from '~/generated/graphql'; @@ -118,6 +119,7 @@ export const StandaloneRichTextWidget = ({ const handlePersistBody = useDebouncedCallback((blocknote: string) => { updatePageLayoutWidget(widget.id, { configuration: { + configurationType: WidgetConfigurationType.STANDALONE_RICH_TEXT, body: { blocknote, markdown: null, diff --git a/packages/twenty-front/src/testing/mock-data/widget-configurations.ts b/packages/twenty-front/src/testing/mock-data/widget-configurations.ts new file mode 100644 index 0000000000..996b98c841 --- /dev/null +++ b/packages/twenty-front/src/testing/mock-data/widget-configurations.ts @@ -0,0 +1,132 @@ +import { type TypedAggregateChartConfiguration } from '@/command-menu/pages/page-layout/types/TypedAggregateChartConfiguration'; +import { type TypedBarChartConfiguration } from '@/command-menu/pages/page-layout/types/TypedBarChartConfiguration'; +import { type TypedGaugeChartConfiguration } from '@/command-menu/pages/page-layout/types/TypedGaugeChartConfiguration'; +import { type TypedLineChartConfiguration } from '@/command-menu/pages/page-layout/types/TypedLineChartConfiguration'; +import { type TypedPieChartConfiguration } from '@/command-menu/pages/page-layout/types/TypedPieChartConfiguration'; +import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget'; +import { ObjectRecordGroupByDateGranularity } from 'twenty-shared/types'; +import { + AggregateOperations, + AxisNameDisplay, + BarChartLayout, + GraphOrderBy, + type IframeConfiguration, + type StandaloneRichTextConfiguration, + type WidgetConfiguration, + WidgetConfigurationType, + WidgetType, +} from '~/generated/graphql'; + +export const TEST_FIELD_METADATA_ID_1 = '20202020-1111-4111-a111-111111111111'; +export const TEST_FIELD_METADATA_ID_2 = '20202020-2222-4222-a222-222222222222'; +export const TEST_FIELD_METADATA_ID_3 = '20202020-3333-4333-a333-333333333333'; +export const TEST_OBJECT_METADATA_ID = '20202020-0000-4000-a000-000000000000'; + +export const TEST_BAR_CHART_CONFIGURATION: TypedBarChartConfiguration = { + __typename: 'BarChartConfiguration', + configurationType: WidgetConfigurationType.BAR_CHART, + layout: BarChartLayout.VERTICAL, + aggregateFieldMetadataId: TEST_FIELD_METADATA_ID_1, + aggregateOperation: AggregateOperations.SUM, + primaryAxisGroupByFieldMetadataId: TEST_FIELD_METADATA_ID_2, + primaryAxisOrderBy: GraphOrderBy.FIELD_ASC, + displayDataLabel: true, + axisNameDisplay: AxisNameDisplay.NONE, +}; + +export const TEST_BAR_CHART_CONFIGURATION_HORIZONTAL: TypedBarChartConfiguration = + { + ...TEST_BAR_CHART_CONFIGURATION, + layout: BarChartLayout.HORIZONTAL, + }; + +export const TEST_LINE_CHART_CONFIGURATION: TypedLineChartConfiguration = { + __typename: 'LineChartConfiguration', + configurationType: WidgetConfigurationType.LINE_CHART, + aggregateFieldMetadataId: TEST_FIELD_METADATA_ID_1, + aggregateOperation: AggregateOperations.AVG, + primaryAxisGroupByFieldMetadataId: TEST_FIELD_METADATA_ID_2, + primaryAxisOrderBy: GraphOrderBy.FIELD_ASC, + primaryAxisDateGranularity: ObjectRecordGroupByDateGranularity.DAY, + displayDataLabel: true, + axisNameDisplay: AxisNameDisplay.NONE, +}; + +export const TEST_PIE_CHART_CONFIGURATION: TypedPieChartConfiguration = { + __typename: 'PieChartConfiguration', + configurationType: WidgetConfigurationType.PIE_CHART, + aggregateFieldMetadataId: TEST_FIELD_METADATA_ID_1, + aggregateOperation: AggregateOperations.COUNT, + groupByFieldMetadataId: TEST_FIELD_METADATA_ID_2, + groupBySubFieldName: null, + dateGranularity: ObjectRecordGroupByDateGranularity.MONTH, + orderBy: GraphOrderBy.VALUE_DESC, + displayDataLabel: true, +}; + +export const TEST_AGGREGATE_CHART_CONFIGURATION: TypedAggregateChartConfiguration = + { + __typename: 'AggregateChartConfiguration', + configurationType: WidgetConfigurationType.AGGREGATE_CHART, + aggregateFieldMetadataId: TEST_FIELD_METADATA_ID_1, + aggregateOperation: AggregateOperations.COUNT, + displayDataLabel: false, + }; + +export const TEST_GAUGE_CHART_CONFIGURATION: TypedGaugeChartConfiguration = { + __typename: 'GaugeChartConfiguration', + configurationType: WidgetConfigurationType.GAUGE_CHART, + aggregateFieldMetadataId: TEST_FIELD_METADATA_ID_1, + aggregateOperation: AggregateOperations.SUM, + displayDataLabel: false, +}; + +export const TEST_IFRAME_CONFIGURATION: IframeConfiguration = { + __typename: 'IframeConfiguration', + configurationType: WidgetConfigurationType.IFRAME, + url: 'https://example.com/dashboard', +}; + +export const TEST_STANDALONE_RICH_TEXT_CONFIGURATION: StandaloneRichTextConfiguration = + { + __typename: 'StandaloneRichTextConfiguration', + configurationType: WidgetConfigurationType.STANDALONE_RICH_TEXT, + body: { markdown: 'Hello world' }, + }; + +export const TEST_FIELDS_CONFIGURATION = { + __typename: 'FieldsConfiguration' as const, + configurationType: 'FIELDS' as const, + sections: [], +}; + +export const ALL_CHART_CONFIGURATIONS: WidgetConfiguration[] = [ + TEST_BAR_CHART_CONFIGURATION, + TEST_LINE_CHART_CONFIGURATION, + TEST_PIE_CHART_CONFIGURATION, + TEST_AGGREGATE_CHART_CONFIGURATION, + TEST_GAUGE_CHART_CONFIGURATION, +]; + +export const ALL_NON_CHART_CONFIGURATIONS = [ + TEST_IFRAME_CONFIGURATION, + TEST_STANDALONE_RICH_TEXT_CONFIGURATION, + TEST_FIELDS_CONFIGURATION, +] as const; + +export const createTestWidget = ( + overrides: Partial = {}, +): PageLayoutWidget => ({ + __typename: 'PageLayoutWidget', + id: 'widget-1', + pageLayoutTabId: 'tab-1', + title: 'Test Widget', + type: WidgetType.GRAPH, + gridPosition: { row: 0, column: 0, rowSpan: 2, columnSpan: 2 }, + objectMetadataId: TEST_OBJECT_METADATA_ID, + configuration: TEST_BAR_CHART_CONFIGURATION, + createdAt: '2024-01-01', + updatedAt: '2024-01-01', + deletedAt: null, + ...overrides, +}); diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-14/1-14-upgrade-version-command.module.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-14/1-14-upgrade-version-command.module.ts index e9b3fd8c03..92b6b9a326 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-14/1-14-upgrade-version-command.module.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-14/1-14-upgrade-version-command.module.ts @@ -6,10 +6,10 @@ import { UpdateCreatedByEnumCommand } from 'src/database/commands/upgrade-versio import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { AiAgentModule } from 'src/engine/metadata-modules/ai/ai-agent/ai-agent.module'; import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module'; +import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; import { RoleModule } from 'src/engine/metadata-modules/role/role.module'; import { WorkspaceSchemaManagerModule } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.module'; import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module'; -import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity'; @Module({ imports: [ diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-15/1-15-migrate-page-layout-widget-configuration.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-15/1-15-migrate-page-layout-widget-configuration.command.ts new file mode 100644 index 0000000000..2e40b9d351 --- /dev/null +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-15/1-15-migrate-page-layout-widget-configuration.command.ts @@ -0,0 +1,288 @@ +import { InjectRepository } from '@nestjs/typeorm'; + +import { Command } from 'nest-commander'; +import { assertUnreachable, isDefined } from 'twenty-shared/utils'; +import { Repository } from 'typeorm'; + +import { ActiveOrSuspendedWorkspacesMigrationCommandRunner } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner'; +import { RunOnWorkspaceArgs } from 'src/database/commands/command-runners/workspaces-migration.command-runner'; +import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; +import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; +import { PageLayoutWidgetEntity } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity'; +import { BarChartLayout } from 'src/engine/metadata-modules/page-layout-widget/enums/bar-chart-layout.enum'; +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 { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager'; +import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service'; + +enum DeprecatedGraphType { + VERTICAL_BAR = 'VERTICAL_BAR', + HORIZONTAL_BAR = 'HORIZONTAL_BAR', + PIE = 'PIE', + LINE = 'LINE', + AGGREGATE = 'AGGREGATE', + GAUGE = 'GAUGE', +} + +const migrateConfiguration = ( + widgetType: WidgetType, + configuration: Record, +): Record => { + const existingConfigurationType = configuration.configurationType as + | string + | undefined; + + if (isDefined(existingConfigurationType)) { + return configuration; + } + + switch (widgetType) { + case WidgetType.GRAPH: + return migrateGraphConfiguration(configuration); + case WidgetType.IFRAME: + return { + ...configuration, + configurationType: WidgetConfigurationType.IFRAME, + }; + case WidgetType.STANDALONE_RICH_TEXT: + return { + ...configuration, + configurationType: WidgetConfigurationType.STANDALONE_RICH_TEXT, + }; + case WidgetType.VIEW: + return { + ...configuration, + configurationType: WidgetConfigurationType.VIEW, + }; + case WidgetType.FIELDS: + return { + ...configuration, + configurationType: WidgetConfigurationType.FIELDS, + }; + case WidgetType.TIMELINE: + return { + ...configuration, + configurationType: WidgetConfigurationType.TIMELINE, + }; + case WidgetType.TASKS: + return { + ...configuration, + configurationType: WidgetConfigurationType.TASKS, + }; + case WidgetType.NOTES: + return { + ...configuration, + configurationType: WidgetConfigurationType.NOTES, + }; + case WidgetType.FILES: + return { + ...configuration, + configurationType: WidgetConfigurationType.FILES, + }; + case WidgetType.EMAILS: + return { + ...configuration, + configurationType: WidgetConfigurationType.EMAILS, + }; + case WidgetType.CALENDAR: + return { + ...configuration, + configurationType: WidgetConfigurationType.CALENDAR, + }; + case WidgetType.FIELD_RICH_TEXT: + return { + ...configuration, + configurationType: WidgetConfigurationType.FIELD_RICH_TEXT, + }; + case WidgetType.WORKFLOW: + return { + ...configuration, + configurationType: WidgetConfigurationType.WORKFLOW, + }; + case WidgetType.WORKFLOW_VERSION: + return { + ...configuration, + configurationType: WidgetConfigurationType.WORKFLOW_VERSION, + }; + case WidgetType.WORKFLOW_RUN: + return { + ...configuration, + configurationType: WidgetConfigurationType.WORKFLOW_RUN, + }; + case WidgetType.FIELD: + return { + ...configuration, + configurationType: WidgetConfigurationType.FIELD, + }; + default: + assertUnreachable(widgetType); + } +}; + +const migrateGraphConfiguration = ( + configuration: Record, +): Record => { + const { graphType: _, ...restConfig } = configuration; + const graphType = configuration.graphType as DeprecatedGraphType | undefined; + + if (!isDefined(graphType)) { + return configuration; + } + + switch (graphType) { + case DeprecatedGraphType.VERTICAL_BAR: + return { + ...restConfig, + configurationType: WidgetConfigurationType.BAR_CHART, + layout: BarChartLayout.VERTICAL, + }; + case DeprecatedGraphType.HORIZONTAL_BAR: + return { + ...restConfig, + configurationType: WidgetConfigurationType.BAR_CHART, + layout: BarChartLayout.HORIZONTAL, + }; + case DeprecatedGraphType.PIE: + return { + ...restConfig, + configurationType: WidgetConfigurationType.PIE_CHART, + }; + case DeprecatedGraphType.LINE: + return { + ...restConfig, + configurationType: WidgetConfigurationType.LINE_CHART, + }; + case DeprecatedGraphType.AGGREGATE: + return { + ...restConfig, + configurationType: WidgetConfigurationType.AGGREGATE_CHART, + }; + case DeprecatedGraphType.GAUGE: + return { + ...restConfig, + configurationType: WidgetConfigurationType.GAUGE_CHART, + }; + default: + assertUnreachable(graphType); + } +}; + +const needsMigration = ( + widgetType: WidgetType, + configuration: Record, +): boolean => { + const graphType = configuration.graphType as DeprecatedGraphType | undefined; + const configurationType = configuration.configurationType as + | WidgetConfigurationType + | undefined; + + if (isDefined(graphType)) { + return true; + } + + if ( + !isDefined(configurationType) && + (widgetType === WidgetType.IFRAME || + widgetType === WidgetType.STANDALONE_RICH_TEXT) + ) { + return true; + } + + return false; +}; + +@Command({ + name: 'upgrade:1-15:migrate-page-layout-widget-configuration', + description: + 'Migrate page layout widget configurations to use configurationType', +}) +export class MigratePageLayoutWidgetConfigurationCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner { + constructor( + @InjectRepository(WorkspaceEntity) + protected readonly workspaceRepository: Repository, + @InjectRepository(PageLayoutWidgetEntity) + private readonly pageLayoutWidgetRepository: Repository, + protected readonly twentyORMGlobalManager: GlobalWorkspaceOrmManager, + protected readonly dataSourceService: DataSourceService, + private readonly workspaceCacheService: WorkspaceCacheService, + ) { + super(workspaceRepository, twentyORMGlobalManager, dataSourceService); + } + + override async runOnWorkspace({ + workspaceId, + options, + }: RunOnWorkspaceArgs): Promise { + this.logger.log( + `Starting migration of page layout widget configurations for workspace ${workspaceId}`, + ); + + const widgets = await this.pageLayoutWidgetRepository.find({ + where: { workspaceId }, + select: ['id', 'type', 'configuration'], + }); + + const widgetsWithNullConfiguration = widgets.filter( + (widget) => !isDefined(widget.configuration), + ); + + for (const widget of widgetsWithNullConfiguration) { + this.logger.log( + `Widget ${widget.id} has a corrupted configuration (null). Skipping migration.`, + ); + } + + const widgetsToMigrate = widgets.filter( + (widget) => + isDefined(widget.configuration) && + needsMigration( + widget.type, + widget.configuration as unknown as Record, + ), + ); + + this.logger.log( + `Found ${widgetsToMigrate.length} widget(s) needing migration out of ${widgets.length} total`, + ); + + if (widgetsToMigrate.length === 0) { + return; + } + + for (const widget of widgetsToMigrate) { + const migratedConfiguration = migrateConfiguration( + widget.type, + widget.configuration as unknown as Record, + ); + + if (options.dryRun) { + this.logger.log( + `[DRY RUN] Would update widget ${widget.id} configuration from ${JSON.stringify(widget.configuration)} to ${JSON.stringify(migratedConfiguration)}`, + ); + } else { + await this.pageLayoutWidgetRepository.update( + { id: widget.id }, + { configuration: migratedConfiguration }, + ); + + this.logger.log(`Updated widget ${widget.id} configuration`); + } + } + + this.logger.log( + `${options.dryRun ? '[DRY RUN] Would have migrated' : 'Successfully migrated'} ${widgetsToMigrate.length} widget(s)`, + ); + + if (!options.dryRun && widgetsToMigrate.length > 0) { + this.logger.log( + `Invalidating and recomputing cache for workspace ${workspaceId}`, + ); + + await this.workspaceCacheService.invalidateAndRecompute(workspaceId, [ + 'flatPageLayoutWidgetMaps', + ]); + + this.logger.log(`Cache invalidated and recomputed successfully`); + } + } +} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/1-15/1-15-upgrade-version-command.module.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/1-15/1-15-upgrade-version-command.module.ts index d307e34a10..30a98f8fa2 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/1-15/1-15-upgrade-version-command.module.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/1-15/1-15-upgrade-version-command.module.ts @@ -2,12 +2,27 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { FixNanPositionValuesInNotesCommand } from 'src/database/commands/upgrade-version-command/1-15/1-15-fix-nan-position-values-in-notes.command'; +import { MigratePageLayoutWidgetConfigurationCommand } from 'src/database/commands/upgrade-version-command/1-15/1-15-migrate-page-layout-widget-configuration.command'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module'; +import { PageLayoutWidgetEntity } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity'; +import { WorkspaceSchemaManagerModule } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.module'; +import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module'; @Module({ - imports: [TypeOrmModule.forFeature([WorkspaceEntity]), DataSourceModule], - providers: [FixNanPositionValuesInNotesCommand], - exports: [FixNanPositionValuesInNotesCommand], + imports: [ + TypeOrmModule.forFeature([WorkspaceEntity, PageLayoutWidgetEntity]), + DataSourceModule, + WorkspaceSchemaManagerModule, + WorkspaceCacheModule, + ], + providers: [ + MigratePageLayoutWidgetConfigurationCommand, + FixNanPositionValuesInNotesCommand, + ], + exports: [ + MigratePageLayoutWidgetConfigurationCommand, + FixNanPositionValuesInNotesCommand, + ], }) export class V1_15_UpgradeVersionCommandModule {} diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts index a88fe866f0..18e391df9f 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/upgrade.command.ts @@ -19,6 +19,7 @@ import { UpdateRoleTargetsUniqueConstraintMigrationCommand } from 'src/database/ import { DeleteRemovedAgentsCommand } from 'src/database/commands/upgrade-version-command/1-14/1-14-delete-removed-agents.command'; import { UpdateCreatedByEnumCommand } from 'src/database/commands/upgrade-version-command/1-14/1-14-update-created-by-enum.command'; import { FixNanPositionValuesInNotesCommand } from 'src/database/commands/upgrade-version-command/1-15/1-15-fix-nan-position-values-in-notes.command'; +import { MigratePageLayoutWidgetConfigurationCommand } from 'src/database/commands/upgrade-version-command/1-15/1-15-migrate-page-layout-widget-configuration.command'; import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service'; import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; @@ -52,6 +53,7 @@ export class UpgradeCommand extends UpgradeCommandRunner { protected readonly deleteRemovedAgentsCommand: DeleteRemovedAgentsCommand, // 1.15 Commands + protected readonly migratePageLayoutWidgetConfigurationCommand: MigratePageLayoutWidgetConfigurationCommand, protected readonly fixNanPositionValuesInNotesCommand: FixNanPositionValuesInNotesCommand, ) { super( @@ -80,6 +82,7 @@ export class UpgradeCommand extends UpgradeCommandRunner { ]; const commands_1150: VersionCommands = [ + this.migratePageLayoutWidgetConfigurationCommand, this.fixNanPositionValuesInNotesCommand, ]; diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/common/1766069735219-set-page-layout-widget-configuration-not-nullable.ts b/packages/twenty-server/src/database/typeorm/core/migrations/common/1766069735219-set-page-layout-widget-configuration-not-nullable.ts new file mode 100644 index 0000000000..8807e36ae0 --- /dev/null +++ b/packages/twenty-server/src/database/typeorm/core/migrations/common/1766069735219-set-page-layout-widget-configuration-not-nullable.ts @@ -0,0 +1,17 @@ +import { type MigrationInterface, type QueryRunner } from 'typeorm'; + +export class CoreMigrationCheck1766069735219 implements MigrationInterface { + name = 'CoreMigrationCheck1766069735219'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."pageLayoutWidget" ALTER COLUMN "configuration" SET NOT NULL`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."pageLayoutWidget" ALTER COLUMN "configuration" DROP NOT NULL`, + ); + } +} diff --git a/packages/twenty-server/src/engine/metadata-modules/flat-page-layout-widget/utils/from-create-page-layout-widget-input-to-flat-page-layout-widget-to-create.util.ts b/packages/twenty-server/src/engine/metadata-modules/flat-page-layout-widget/utils/from-create-page-layout-widget-input-to-flat-page-layout-widget-to-create.util.ts index 094d38e850..3ea3d51d2a 100644 --- a/packages/twenty-server/src/engine/metadata-modules/flat-page-layout-widget/utils/from-create-page-layout-widget-input-to-flat-page-layout-widget-to-create.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/flat-page-layout-widget/utils/from-create-page-layout-widget-input-to-flat-page-layout-widget-to-create.util.ts @@ -38,7 +38,7 @@ export const fromCreatePageLayoutWidgetInputToFlatPageLayoutWidgetToCreate = ({ type: createPageLayoutWidgetInput.type ?? WidgetType.VIEW, objectMetadataId: createPageLayoutWidgetInput.objectMetadataId ?? null, gridPosition: createPageLayoutWidgetInput.gridPosition, - configuration: createPageLayoutWidgetInput.configuration ?? null, + configuration: createPageLayoutWidgetInput.configuration, applicationId: workspaceCustomApplicationId, }; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/constants/all-widget-configuration-type-validator-by-widget-configuration-type.constant.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/constants/all-widget-configuration-type-validator-by-widget-configuration-type.constant.ts new file mode 100644 index 0000000000..41d2f503d1 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/constants/all-widget-configuration-type-validator-by-widget-configuration-type.constant.ts @@ -0,0 +1,24 @@ +import { AggregateChartConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/aggregate-chart-configuration.dto'; +import { BarChartConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/bar-chart-configuration.dto'; +import { GaugeChartConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/gauge-chart-configuration.dto'; +import { IframeConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/iframe-configuration.dto'; +import { LineChartConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/line-chart-configuration.dto'; +import { PieChartConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/pie-chart-configuration.dto'; +import { StandaloneRichTextConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/standalone-rich-text-configuration.dto'; +import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type'; +import { type PageLayoutWidgetConfigurationBase } from 'src/engine/metadata-modules/page-layout-widget/types/page-layout-widget-configurationt-base.type'; + +export const ALL_WIDGET_CONFIGURATION_TYPE_VALIDATOR_BY_WIDGET_CONFIGURATION_TYPE = + { + [WidgetConfigurationType.AGGREGATE_CHART]: AggregateChartConfigurationDTO, + [WidgetConfigurationType.BAR_CHART]: BarChartConfigurationDTO, + [WidgetConfigurationType.GAUGE_CHART]: GaugeChartConfigurationDTO, + [WidgetConfigurationType.IFRAME]: IframeConfigurationDTO, + [WidgetConfigurationType.LINE_CHART]: LineChartConfigurationDTO, + [WidgetConfigurationType.PIE_CHART]: PieChartConfigurationDTO, + [WidgetConfigurationType.STANDALONE_RICH_TEXT]: + StandaloneRichTextConfigurationDTO, + // TODO: Remove the partial when we have all the widget configuration types in the backend + } as const satisfies Partial<{ + [P in WidgetConfigurationType]: new () => PageLayoutWidgetConfigurationBase; + }>; diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/aggregate-chart-configuration.dto.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/aggregate-chart-configuration.dto.ts index cdf9b3ae8f..d42387a9d0 100644 --- a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/aggregate-chart-configuration.dto.ts +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/aggregate-chart-configuration.dto.ts @@ -4,6 +4,7 @@ import { Type } from 'class-transformer'; import { IsBoolean, IsEnum, + IsIn, IsNotEmpty, IsObject, IsOptional, @@ -22,14 +23,17 @@ import { ObjectRecordFilter } from 'src/engine/api/graphql/workspace-query-build import { AggregateOperations } from 'src/engine/api/graphql/graphql-query-runner/constants/aggregate-operations.constant'; import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; import { RatioAggregateConfigDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/ratio-aggregate-config.dto'; -import { GraphType } from 'src/engine/metadata-modules/page-layout-widget/enums/graph-type.enum'; +import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type'; +import { PageLayoutWidgetConfigurationBase } from 'src/engine/metadata-modules/page-layout-widget/types/page-layout-widget-configurationt-base.type'; @ObjectType('AggregateChartConfiguration') -export class AggregateChartConfigurationDTO { - @Field(() => GraphType) - @IsEnum(GraphType) +export class AggregateChartConfigurationDTO + implements PageLayoutWidgetConfigurationBase +{ + @Field(() => WidgetConfigurationType) + @IsIn([WidgetConfigurationType.AGGREGATE_CHART]) @IsNotEmpty() - graphType: GraphType.AGGREGATE; + configurationType: WidgetConfigurationType.AGGREGATE_CHART; @Field(() => UUIDScalarType) @IsUUID() diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/bar-chart-configuration.dto.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/bar-chart-configuration.dto.ts index e0dcda2159..a3a6571b4e 100644 --- a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/bar-chart-configuration.dto.ts +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/bar-chart-configuration.dto.ts @@ -23,16 +23,20 @@ import { AggregateOperations } from 'src/engine/api/graphql/graphql-query-runner import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; import { AxisNameDisplay } from 'src/engine/metadata-modules/page-layout-widget/enums/axis-name-display.enum'; import { BarChartGroupMode } from 'src/engine/metadata-modules/page-layout-widget/enums/bar-chart-group-mode.enum'; +import { BarChartLayout } from 'src/engine/metadata-modules/page-layout-widget/enums/bar-chart-layout.enum'; import { ObjectRecordGroupByDateGranularity } from 'src/engine/metadata-modules/page-layout-widget/enums/date-granularity.enum'; import { GraphOrderBy } from 'src/engine/metadata-modules/page-layout-widget/enums/graph-order-by.enum'; -import { GraphType } from 'src/engine/metadata-modules/page-layout-widget/enums/graph-type.enum'; +import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type'; +import { PageLayoutWidgetConfigurationBase } from 'src/engine/metadata-modules/page-layout-widget/types/page-layout-widget-configurationt-base.type'; @ObjectType('BarChartConfiguration') -export class BarChartConfigurationDTO { - @Field(() => GraphType) - @IsIn([GraphType.VERTICAL_BAR, GraphType.HORIZONTAL_BAR]) +export class BarChartConfigurationDTO + implements PageLayoutWidgetConfigurationBase +{ + @Field(() => WidgetConfigurationType) + @IsIn([WidgetConfigurationType.BAR_CHART]) @IsNotEmpty() - graphType: GraphType.VERTICAL_BAR | GraphType.HORIZONTAL_BAR; + configurationType: WidgetConfigurationType.BAR_CHART; @Field(() => UUIDScalarType) @IsUUID() @@ -145,6 +149,11 @@ export class BarChartConfigurationDTO { @IsOptional() groupMode?: BarChartGroupMode; + @Field(() => BarChartLayout) + @IsEnum(BarChartLayout) + @IsNotEmpty() + layout: BarChartLayout; + @Field(() => Boolean, { nullable: true, }) diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/gauge-chart-configuration.dto.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/gauge-chart-configuration.dto.ts index a55fc06795..d565afeb46 100644 --- a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/gauge-chart-configuration.dto.ts +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/gauge-chart-configuration.dto.ts @@ -3,6 +3,7 @@ import { Field, Int, ObjectType } from '@nestjs/graphql'; import { IsBoolean, IsEnum, + IsIn, IsNotEmpty, IsObject, IsOptional, @@ -19,14 +20,17 @@ import { ObjectRecordFilter } from 'src/engine/api/graphql/workspace-query-build import { AggregateOperations } from 'src/engine/api/graphql/graphql-query-runner/constants/aggregate-operations.constant'; import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; -import { GraphType } from 'src/engine/metadata-modules/page-layout-widget/enums/graph-type.enum'; +import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type'; +import { PageLayoutWidgetConfigurationBase } from 'src/engine/metadata-modules/page-layout-widget/types/page-layout-widget-configurationt-base.type'; @ObjectType('GaugeChartConfiguration') -export class GaugeChartConfigurationDTO { - @Field(() => GraphType) - @IsEnum(GraphType) +export class GaugeChartConfigurationDTO + implements PageLayoutWidgetConfigurationBase +{ + @Field(() => WidgetConfigurationType) + @IsIn([WidgetConfigurationType.GAUGE_CHART]) @IsNotEmpty() - graphType: GraphType.GAUGE; + configurationType: WidgetConfigurationType.GAUGE_CHART; @Field(() => UUIDScalarType) @IsUUID() diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/iframe-configuration.dto.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/iframe-configuration.dto.ts index 4b840b4e34..f1e7c81b20 100644 --- a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/iframe-configuration.dto.ts +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/iframe-configuration.dto.ts @@ -1,9 +1,19 @@ import { Field, ObjectType } from '@nestjs/graphql'; -import { IsOptional, IsString, IsUrl } from 'class-validator'; +import { IsIn, IsNotEmpty, IsOptional, IsString, IsUrl } from 'class-validator'; + +import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type'; +import { PageLayoutWidgetConfigurationBase } from 'src/engine/metadata-modules/page-layout-widget/types/page-layout-widget-configurationt-base.type'; @ObjectType('IframeConfiguration') -export class IframeConfigurationDTO { +export class IframeConfigurationDTO + implements PageLayoutWidgetConfigurationBase +{ + @Field(() => WidgetConfigurationType) + @IsIn([WidgetConfigurationType.IFRAME]) + @IsNotEmpty() + configurationType: WidgetConfigurationType.IFRAME; + @Field(() => String, { nullable: true }) @IsString() @IsOptional() diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/inputs/create-page-layout-widget.input.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/inputs/create-page-layout-widget.input.ts index bd050e5e18..aa0dd7a6da 100644 --- a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/inputs/create-page-layout-widget.input.ts +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/inputs/create-page-layout-widget.input.ts @@ -15,6 +15,7 @@ import { GraphQLJSON } from 'graphql-type-json'; 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'; import { WidgetType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-type.enum'; +import { AllPageLayoutWidgetConfiguration } from 'src/engine/metadata-modules/page-layout-widget/types/all-page-layout-widget-configuration.type'; @InputType() export class CreatePageLayoutWidgetInput { @@ -28,10 +29,10 @@ export class CreatePageLayoutWidgetInput { @IsNotEmpty() title: string; - @Field(() => WidgetType, { nullable: true, defaultValue: WidgetType.VIEW }) + @Field(() => WidgetType, { nullable: false }) @IsEnum(WidgetType) @IsOptional() - type?: WidgetType; + type: WidgetType; @Field(() => UUIDScalarType, { nullable: true }) @IsUUID() @@ -43,8 +44,8 @@ export class CreatePageLayoutWidgetInput { @Type(() => GridPositionInput) gridPosition: GridPositionInput; - @Field(() => GraphQLJSON, { nullable: true }) + @Field(() => GraphQLJSON, { nullable: false }) @IsObject() @IsOptional() - configuration?: Record | null; + configuration: AllPageLayoutWidgetConfiguration; } diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/inputs/update-page-layout-widget-with-id.input.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/inputs/update-page-layout-widget-with-id.input.ts index 74a32b46ef..e7ee7d4838 100644 --- a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/inputs/update-page-layout-widget-with-id.input.ts +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/inputs/update-page-layout-widget-with-id.input.ts @@ -15,6 +15,7 @@ import { GraphQLJSON } from 'graphql-type-json'; 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'; import { WidgetType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-type.enum'; +import { AllPageLayoutWidgetConfiguration } from 'src/engine/metadata-modules/page-layout-widget/types/all-page-layout-widget-configuration.type'; @InputType() export class UpdatePageLayoutWidgetWithIdInput { @@ -52,5 +53,5 @@ export class UpdatePageLayoutWidgetWithIdInput { @Field(() => GraphQLJSON, { nullable: true }) @IsObject() @IsOptional() - configuration: Record | null; + configuration: AllPageLayoutWidgetConfiguration; } diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/inputs/update-page-layout-widget.input.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/inputs/update-page-layout-widget.input.ts index c66199ea07..86463ef0f6 100644 --- a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/inputs/update-page-layout-widget.input.ts +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/inputs/update-page-layout-widget.input.ts @@ -14,6 +14,7 @@ import { GraphQLJSON } from 'graphql-type-json'; 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'; import { WidgetType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-type.enum'; +import { AllPageLayoutWidgetConfiguration } from 'src/engine/metadata-modules/page-layout-widget/types/all-page-layout-widget-configuration.type'; @InputType() export class UpdatePageLayoutWidgetInput { @@ -41,5 +42,5 @@ export class UpdatePageLayoutWidgetInput { @Field(() => GraphQLJSON, { nullable: true }) @IsObject() @IsOptional() - configuration?: Record | null; + configuration?: AllPageLayoutWidgetConfiguration; } diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/line-chart-configuration.dto.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/line-chart-configuration.dto.ts index 672b512dcf..fcdac43ea8 100644 --- a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/line-chart-configuration.dto.ts +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/line-chart-configuration.dto.ts @@ -3,6 +3,7 @@ import { Field, Int, ObjectType } from '@nestjs/graphql'; import { IsBoolean, IsEnum, + IsIn, IsNotEmpty, IsNumber, IsObject, @@ -23,14 +24,17 @@ import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/ import { AxisNameDisplay } from 'src/engine/metadata-modules/page-layout-widget/enums/axis-name-display.enum'; import { ObjectRecordGroupByDateGranularity } from 'src/engine/metadata-modules/page-layout-widget/enums/date-granularity.enum'; import { GraphOrderBy } from 'src/engine/metadata-modules/page-layout-widget/enums/graph-order-by.enum'; -import { GraphType } from 'src/engine/metadata-modules/page-layout-widget/enums/graph-type.enum'; +import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type'; +import { PageLayoutWidgetConfigurationBase } from 'src/engine/metadata-modules/page-layout-widget/types/page-layout-widget-configurationt-base.type'; @ObjectType('LineChartConfiguration') -export class LineChartConfigurationDTO { - @Field(() => GraphType) - @IsEnum(GraphType) +export class LineChartConfigurationDTO + implements PageLayoutWidgetConfigurationBase +{ + @Field(() => WidgetConfigurationType) + @IsIn([WidgetConfigurationType.LINE_CHART]) @IsNotEmpty() - graphType: GraphType.LINE; + configurationType: WidgetConfigurationType.LINE_CHART; @Field(() => UUIDScalarType) @IsUUID() diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/page-layout-widget.dto.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/page-layout-widget.dto.ts index 8e808f1427..db394a0ba8 100644 --- a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/page-layout-widget.dto.ts +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/page-layout-widget.dto.ts @@ -3,11 +3,9 @@ import { Field, ObjectType, registerEnumType } from '@nestjs/graphql'; import { IDField } from '@ptc-org/nestjs-query-graphql'; import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; -import { - WidgetConfiguration, - WidgetConfigurationInterface, -} from 'src/engine/metadata-modules/page-layout-widget/dtos/widget-configuration.interface'; +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'; +import { AllPageLayoutWidgetConfiguration } from 'src/engine/metadata-modules/page-layout-widget/types/all-page-layout-widget-configuration.type'; registerEnumType(WidgetType, { name: 'WidgetType' }); @@ -41,13 +39,13 @@ export class PageLayoutWidgetDTO { type: WidgetType; @Field(() => UUIDScalarType, { nullable: true }) - objectMetadataId: string | null; + objectMetadataId?: string; @Field(() => GridPositionDTO, { nullable: false }) gridPosition: GridPositionDTO; - @Field(() => WidgetConfiguration, { nullable: true }) - configuration: WidgetConfigurationInterface | null; + @Field(() => WidgetConfiguration, { nullable: false }) + configuration: AllPageLayoutWidgetConfiguration; @Field() createdAt: Date; @@ -56,5 +54,5 @@ export class PageLayoutWidgetDTO { updatedAt: Date; @Field(() => Date, { nullable: true }) - deletedAt?: Date | null; + deletedAt?: Date; } diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/pie-chart-configuration.dto.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/pie-chart-configuration.dto.ts index 409cfea92a..8f1ec1d483 100644 --- a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/pie-chart-configuration.dto.ts +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/pie-chart-configuration.dto.ts @@ -3,6 +3,7 @@ import { Field, Int, ObjectType } from '@nestjs/graphql'; import { IsBoolean, IsEnum, + IsIn, IsNotEmpty, IsObject, IsOptional, @@ -21,14 +22,17 @@ import { AggregateOperations } from 'src/engine/api/graphql/graphql-query-runner import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; import { ObjectRecordGroupByDateGranularity } from 'src/engine/metadata-modules/page-layout-widget/enums/date-granularity.enum'; import { GraphOrderBy } from 'src/engine/metadata-modules/page-layout-widget/enums/graph-order-by.enum'; -import { GraphType } from 'src/engine/metadata-modules/page-layout-widget/enums/graph-type.enum'; +import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type'; +import { PageLayoutWidgetConfigurationBase } from 'src/engine/metadata-modules/page-layout-widget/types/page-layout-widget-configurationt-base.type'; @ObjectType('PieChartConfiguration') -export class PieChartConfigurationDTO { - @Field(() => GraphType) - @IsEnum(GraphType) +export class PieChartConfigurationDTO + implements PageLayoutWidgetConfigurationBase +{ + @Field(() => WidgetConfigurationType) + @IsIn([WidgetConfigurationType.PIE_CHART]) @IsNotEmpty() - graphType: GraphType.PIE; + configurationType: WidgetConfigurationType.PIE_CHART; @Field(() => UUIDScalarType) @IsUUID() diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/ratio-aggregate-config.dto.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/ratio-aggregate-config.dto.ts index fe2cb5e128..5ee17c1e1c 100644 --- a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/ratio-aggregate-config.dto.ts +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/ratio-aggregate-config.dto.ts @@ -1,11 +1,10 @@ -import { Field, InputType, ObjectType } from '@nestjs/graphql'; +import { Field, ObjectType } from '@nestjs/graphql'; import { IsNotEmpty, IsString, IsUUID } from 'class-validator'; import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; @ObjectType('RatioAggregateConfig') -@InputType('RatioAggregateConfigInput') export class RatioAggregateConfigDTO { @Field(() => UUIDScalarType) @IsUUID() diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/standalone-rich-text-configuration.dto.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/standalone-rich-text-configuration.dto.ts index ec39e45bb3..7efb2db7cc 100644 --- a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/standalone-rich-text-configuration.dto.ts +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/standalone-rich-text-configuration.dto.ts @@ -1,12 +1,21 @@ import { Field, ObjectType } from '@nestjs/graphql'; import { Type } from 'class-transformer'; -import { IsNotEmpty, ValidateNested } from 'class-validator'; +import { IsIn, IsNotEmpty, ValidateNested } from 'class-validator'; import { RichTextV2BodyDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/rich-text-v2-body.dto'; +import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type'; +import { PageLayoutWidgetConfigurationBase } from 'src/engine/metadata-modules/page-layout-widget/types/page-layout-widget-configurationt-base.type'; @ObjectType('StandaloneRichTextConfiguration') -export class StandaloneRichTextConfigurationDTO { +export class StandaloneRichTextConfigurationDTO + implements PageLayoutWidgetConfigurationBase +{ + @Field(() => WidgetConfigurationType) + @IsIn([WidgetConfigurationType.STANDALONE_RICH_TEXT]) + @IsNotEmpty() + configurationType: WidgetConfigurationType.STANDALONE_RICH_TEXT; + @Field(() => RichTextV2BodyDTO) @ValidateNested() @Type(() => RichTextV2BodyDTO) diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/widget-configuration.interface.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/widget-configuration.interface.ts index 1699f776f1..c3cdabca21 100644 --- a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/widget-configuration.interface.ts +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/dtos/widget-configuration.interface.ts @@ -1,77 +1,17 @@ import { createUnionType } from '@nestjs/graphql'; -import { AggregateChartConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/aggregate-chart-configuration.dto'; -import { BarChartConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/bar-chart-configuration.dto'; -import { GaugeChartConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/gauge-chart-configuration.dto'; -import { IframeConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/iframe-configuration.dto'; -import { LineChartConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/line-chart-configuration.dto'; -import { PieChartConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/pie-chart-configuration.dto'; -import { StandaloneRichTextConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/standalone-rich-text-configuration.dto'; -import { GraphType } from 'src/engine/metadata-modules/page-layout-widget/enums/graph-type.enum'; -import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.enum'; +import { ALL_WIDGET_CONFIGURATION_TYPE_VALIDATOR_BY_WIDGET_CONFIGURATION_TYPE } from 'src/engine/metadata-modules/page-layout-widget/constants/all-widget-configuration-type-validator-by-widget-configuration-type.constant'; +import { type AllPageLayoutWidgetConfiguration } from 'src/engine/metadata-modules/page-layout-widget/types/all-page-layout-widget-configuration.type'; export const WidgetConfiguration = createUnionType({ name: 'WidgetConfiguration', - types: () => [ - BarChartConfigurationDTO, - LineChartConfigurationDTO, - PieChartConfigurationDTO, - AggregateChartConfigurationDTO, - GaugeChartConfigurationDTO, - IframeConfigurationDTO, - StandaloneRichTextConfigurationDTO, - ], - resolveType(configuration: Record) { - if (!('configurationType' in configuration)) { - throw new Error( - 'Widget configuration missing configurationType discriminator. This indicates a validation bug or data corruption.', - ); - } - - if ( - configuration.configurationType === WidgetConfigurationType.CHART_CONFIG - ) { - switch (configuration.graphType) { - case GraphType.VERTICAL_BAR: - case GraphType.HORIZONTAL_BAR: - return BarChartConfigurationDTO; - case GraphType.LINE: - return LineChartConfigurationDTO; - case GraphType.PIE: - return PieChartConfigurationDTO; - case GraphType.AGGREGATE: - return AggregateChartConfigurationDTO; - case GraphType.GAUGE: - return GaugeChartConfigurationDTO; - default: - throw new Error(`Unknown graph type: ${configuration.graphType}`); - } - } - - if ( - configuration.configurationType === WidgetConfigurationType.IFRAME_CONFIG - ) { - return IframeConfigurationDTO; - } - - if ( - configuration.configurationType === - WidgetConfigurationType.STANDALONE_RICH_TEXT_CONFIG - ) { - return StandaloneRichTextConfigurationDTO; - } - - throw new Error( - `Unknown widget configuration type: ${configuration.configurationType}`, - ); + types: () => + Object.values( + ALL_WIDGET_CONFIGURATION_TYPE_VALIDATOR_BY_WIDGET_CONFIGURATION_TYPE, + ), + resolveType({ configurationType }: AllPageLayoutWidgetConfiguration) { + return ALL_WIDGET_CONFIGURATION_TYPE_VALIDATOR_BY_WIDGET_CONFIGURATION_TYPE[ + configurationType + ]; }, }); - -export type WidgetConfigurationInterface = - | BarChartConfigurationDTO - | LineChartConfigurationDTO - | PieChartConfigurationDTO - | AggregateChartConfigurationDTO - | GaugeChartConfigurationDTO - | IframeConfigurationDTO - | StandaloneRichTextConfigurationDTO; diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity.ts index 078e2080c3..4a94e6920d 100644 --- a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity.ts +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity.ts @@ -18,8 +18,8 @@ import { SyncableEntity } from 'src/engine/workspace-manager/workspace-sync/inte import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { PageLayoutTabEntity } from 'src/engine/metadata-modules/page-layout-tab/entities/page-layout-tab.entity'; -import { WidgetConfigurationInterface } 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'; +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'; @Entity({ name: 'pageLayoutWidget', schema: 'core' }) @@ -78,8 +78,8 @@ export class PageLayoutWidgetEntity @Column({ type: 'jsonb', nullable: false }) gridPosition: GridPosition; - @Column({ type: 'jsonb', nullable: true }) - configuration: WidgetConfigurationInterface | null; + @Column({ type: 'jsonb', nullable: false }) + configuration: AllPageLayoutWidgetConfiguration; @CreateDateColumn({ type: 'timestamptz' }) createdAt: Date; diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/enums/bar-chart-layout.enum.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/enums/bar-chart-layout.enum.ts new file mode 100644 index 0000000000..d17e76c878 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/enums/bar-chart-layout.enum.ts @@ -0,0 +1,11 @@ +import { registerEnumType } from '@nestjs/graphql'; + +export enum BarChartLayout { + VERTICAL = 'VERTICAL', + HORIZONTAL = 'HORIZONTAL', +} + +registerEnumType(BarChartLayout, { + name: 'BarChartLayout', + description: 'Layout orientation for bar charts', +}); diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/enums/graph-type.enum.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/enums/graph-type.enum.ts index af1307da7f..8a4101e7c2 100644 --- a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/enums/graph-type.enum.ts +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/enums/graph-type.enum.ts @@ -1,12 +1,11 @@ import { registerEnumType } from '@nestjs/graphql'; export enum GraphType { - AGGREGATE = 'AGGREGATE', - GAUGE = 'GAUGE', - PIE = 'PIE', - VERTICAL_BAR = 'VERTICAL_BAR', - HORIZONTAL_BAR = 'HORIZONTAL_BAR', - LINE = 'LINE', + AGGREGATE_CHART = 'AGGREGATE_CHART', + GAUGE_CHART = 'GAUGE_CHART', + PIE_CHART = 'PIE_CHART', + BAR_CHART = 'BAR_CHART', + LINE_CHART = 'LINE_CHART', } registerEnumType(GraphType, { diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.enum.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.enum.ts deleted file mode 100644 index e8c7bee55b..0000000000 --- a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.enum.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum WidgetConfigurationType { - CHART_CONFIG = 'CHART_CONFIG', - IFRAME_CONFIG = 'IFRAME_CONFIG', - STANDALONE_RICH_TEXT_CONFIG = 'STANDALONE_RICH_TEXT_CONFIG', -} diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type.ts new file mode 100644 index 0000000000..f53724ce62 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type.ts @@ -0,0 +1,37 @@ +import { registerEnumType } from '@nestjs/graphql'; + +import { type Expect } from 'twenty-shared/testing'; + +import { GraphType } from 'src/engine/metadata-modules/page-layout-widget/enums/graph-type.enum'; + +export enum WidgetConfigurationType { + AGGREGATE_CHART = GraphType.AGGREGATE_CHART, + GAUGE_CHART = GraphType.GAUGE_CHART, + PIE_CHART = GraphType.PIE_CHART, + BAR_CHART = GraphType.BAR_CHART, + LINE_CHART = GraphType.LINE_CHART, + IFRAME = 'IFRAME', + STANDALONE_RICH_TEXT = 'STANDALONE_RICH_TEXT', + VIEW = 'VIEW', + FIELD = 'FIELD', + FIELDS = 'FIELDS', + TIMELINE = 'TIMELINE', + TASKS = 'TASKS', + NOTES = 'NOTES', + FILES = 'FILES', + EMAILS = 'EMAILS', + CALENDAR = 'CALENDAR', + FIELD_RICH_TEXT = 'FIELD_RICH_TEXT', + WORKFLOW = 'WORKFLOW', + WORKFLOW_VERSION = 'WORKFLOW_VERSION', + WORKFLOW_RUN = 'WORKFLOW_RUN', +} + +registerEnumType(WidgetConfigurationType, { + name: 'WidgetConfigurationType', +}); + +// eslint-disable-next-line unused-imports/no-unused-vars +type Assertion = Expect< + `${GraphType}` extends `${WidgetConfigurationType}` ? true : false +>; diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/resolvers/page-layout-widget.resolver.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/resolvers/page-layout-widget.resolver.ts index 03b8a17781..11c611052b 100644 --- a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/resolvers/page-layout-widget.resolver.ts +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/resolvers/page-layout-widget.resolver.ts @@ -26,7 +26,6 @@ import { UpdatePageLayoutWidgetInput } from 'src/engine/metadata-modules/page-la import { PageLayoutWidgetDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/page-layout-widget.dto'; import { WidgetConfiguration } from 'src/engine/metadata-modules/page-layout-widget/dtos/widget-configuration.interface'; import { PageLayoutWidgetService } from 'src/engine/metadata-modules/page-layout-widget/services/page-layout-widget.service'; -import { injectWidgetConfigurationDiscriminator } from 'src/engine/metadata-modules/page-layout-widget/utils/inject-widget-configuration-discriminator.util'; import { PageLayoutGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/page-layout/utils/page-layout-graphql-api-exception.filter'; import { WorkspaceMigrationBuilderGraphqlApiExceptionInterceptor } from 'src/engine/workspace-manager/workspace-migration-v2/interceptors/workspace-migration-builder-graphql-api-exception.interceptor'; @@ -109,9 +108,6 @@ export class PageLayoutWidgetResolver { @ResolveField(() => WidgetConfiguration, { nullable: true }) configuration(@Parent() widget: PageLayoutWidgetDTO) { - return injectWidgetConfigurationDiscriminator( - widget.type, - widget.configuration, - ); + return widget.configuration; } } diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/services/page-layout-widget.service.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/services/page-layout-widget.service.ts index a57c68b238..65a45f47b8 100644 --- a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/services/page-layout-widget.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/services/page-layout-widget.service.ts @@ -20,7 +20,6 @@ import { import { CreatePageLayoutWidgetInput } from 'src/engine/metadata-modules/page-layout-widget/dtos/inputs/create-page-layout-widget.input'; import { UpdatePageLayoutWidgetInput } from 'src/engine/metadata-modules/page-layout-widget/dtos/inputs/update-page-layout-widget.input'; import { type PageLayoutWidgetDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/page-layout-widget.dto'; -import { WidgetConfigurationInterface } 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'; import { PageLayoutWidgetException, @@ -28,6 +27,7 @@ import { PageLayoutWidgetExceptionMessageKey, generatePageLayoutWidgetExceptionMessage, } 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 { fromFlatPageLayoutWidgetToPageLayoutWidgetDto } from 'src/engine/metadata-modules/page-layout-widget/utils/from-flat-page-layout-widget-to-page-layout-widget-dto.util'; import { validateAndTransformWidgetConfiguration } from 'src/engine/metadata-modules/page-layout-widget/utils/validate-and-transform-widget-configuration.util'; import { validateWidgetGridPosition } from 'src/engine/metadata-modules/page-layout-widget/utils/validate-widget-grid-position.util'; @@ -70,16 +70,16 @@ export class PageLayoutWidgetService { titleForError, }: { type: WidgetType; - configuration: Record; + configuration: AllPageLayoutWidgetConfiguration; workspaceId: string; titleForError: string; - }): Promise { + }): Promise { const isDashboardV2Enabled = await this.featureFlagService.isFeatureEnabled( FeatureFlagKey.IS_DASHBOARD_V2_ENABLED, workspaceId, ); - let validatedConfig: WidgetConfigurationInterface | null = null; + let validatedConfig: AllPageLayoutWidgetConfiguration | null = null; try { validatedConfig = await validateAndTransformWidgetConfiguration({ @@ -210,9 +210,7 @@ export class PageLayoutWidgetService { fromCreatePageLayoutWidgetInputToFlatPageLayoutWidgetToCreate({ createPageLayoutWidgetInput: { ...createPageLayoutWidgetInput, - ...(validatedConfig && { - configuration: validatedConfig as Record, - }), + configuration: validatedConfig, }, workspaceId, workspaceCustomApplicationId: workspaceCustomFlatApplication.id, @@ -271,12 +269,8 @@ export class PageLayoutWidgetService { private async getValidatedConfigurationForCreate( input: CreatePageLayoutWidgetInput, workspaceId: string, - ): Promise { - if (!input.configuration || !input.type) { - return null; - } - - return this.validateWidgetConfigurationOrThrow({ + ): Promise { + return await this.validateWidgetConfigurationOrThrow({ type: input.type, configuration: input.configuration, workspaceId, @@ -313,9 +307,11 @@ export class PageLayoutWidgetService { id, update: { ...updateData, - ...(validatedConfig && { - configuration: validatedConfig as Record, - }), + ...(isDefined(validatedConfig) + ? { + configuration: validatedConfig, + } + : {}), }, }; @@ -369,20 +365,15 @@ export class PageLayoutWidgetService { updateData: UpdatePageLayoutWidgetInput, existingWidget: FlatPageLayoutWidget, workspaceId: string, - ): Promise { - if (!updateData.configuration) { - return null; + ): Promise { + if (!isDefined(updateData.configuration)) { + return undefined; } const typeForValidation = updateData.type ?? existingWidget.type; - - if (!typeForValidation) { - return null; - } - const titleForError = updateData.title ?? existingWidget.title; - return this.validateWidgetConfigurationOrThrow({ + return await this.validateWidgetConfigurationOrThrow({ type: typeForValidation, configuration: updateData.configuration, workspaceId, diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/types/all-page-layout-widget-configuration.type.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/types/all-page-layout-widget-configuration.type.ts new file mode 100644 index 0000000000..1df41a884f --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/types/all-page-layout-widget-configuration.type.ts @@ -0,0 +1,5 @@ +import { type ALL_WIDGET_CONFIGURATION_TYPE_VALIDATOR_BY_WIDGET_CONFIGURATION_TYPE } from 'src/engine/metadata-modules/page-layout-widget/constants/all-widget-configuration-type-validator-by-widget-configuration-type.constant'; + +export type AllPageLayoutWidgetConfiguration = InstanceType< + (typeof ALL_WIDGET_CONFIGURATION_TYPE_VALIDATOR_BY_WIDGET_CONFIGURATION_TYPE)[keyof typeof ALL_WIDGET_CONFIGURATION_TYPE_VALIDATOR_BY_WIDGET_CONFIGURATION_TYPE] +>; diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/types/page-layout-widget-configurationt-base.type.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/types/page-layout-widget-configurationt-base.type.ts new file mode 100644 index 0000000000..c0bfd72172 --- /dev/null +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/types/page-layout-widget-configurationt-base.type.ts @@ -0,0 +1,5 @@ +import { type WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type'; + +export type PageLayoutWidgetConfigurationBase = { + configurationType: WidgetConfigurationType; +}; diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/utils/from-flat-page-layout-widget-to-page-layout-widget-dto.util.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/utils/from-flat-page-layout-widget-to-page-layout-widget-dto.util.ts index 98dcb6679d..4f1234528b 100644 --- a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/utils/from-flat-page-layout-widget-to-page-layout-widget-dto.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/utils/from-flat-page-layout-widget-to-page-layout-widget-dto.util.ts @@ -4,12 +4,14 @@ import { type PageLayoutWidgetDTO } from 'src/engine/metadata-modules/page-layou export const fromFlatPageLayoutWidgetToPageLayoutWidgetDto = ( flatPageLayoutWidget: FlatPageLayoutWidget, ): PageLayoutWidgetDTO => { - const { createdAt, updatedAt, deletedAt, ...rest } = flatPageLayoutWidget; + const { createdAt, updatedAt, deletedAt, objectMetadataId, ...rest } = + flatPageLayoutWidget; return { ...rest, + objectMetadataId: objectMetadataId ?? undefined, createdAt: new Date(createdAt), updatedAt: new Date(updatedAt), - deletedAt: deletedAt ? new Date(deletedAt) : null, + deletedAt: deletedAt ? new Date(deletedAt) : undefined, }; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/utils/inject-widget-configuration-discriminator.util.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/utils/inject-widget-configuration-discriminator.util.ts deleted file mode 100644 index f45a522a17..0000000000 --- a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/utils/inject-widget-configuration-discriminator.util.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { type WidgetConfigurationInterface } from 'src/engine/metadata-modules/page-layout-widget/dtos/widget-configuration.interface'; -import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.enum'; -import { WidgetType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-type.enum'; - -type ConfigurationWithDiscriminator = WidgetConfigurationInterface & { - configurationType: WidgetConfigurationType; -}; - -export const injectWidgetConfigurationDiscriminator = ( - widgetType: WidgetType, - configuration: WidgetConfigurationInterface | null, -): ConfigurationWithDiscriminator | null => { - if (!configuration) { - return null; - } - - if (widgetType === WidgetType.IFRAME) { - return { - ...configuration, - configurationType: WidgetConfigurationType.IFRAME_CONFIG, - } satisfies ConfigurationWithDiscriminator; - } - - if (widgetType === WidgetType.GRAPH && 'graphType' in configuration) { - return { - ...configuration, - configurationType: WidgetConfigurationType.CHART_CONFIG, - } satisfies ConfigurationWithDiscriminator; - } - - if (widgetType === WidgetType.STANDALONE_RICH_TEXT) { - return { - ...configuration, - configurationType: WidgetConfigurationType.STANDALONE_RICH_TEXT_CONFIG, - } satisfies ConfigurationWithDiscriminator; - } - - return configuration as ConfigurationWithDiscriminator; -}; diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/utils/validate-and-transform-widget-configuration.util.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/utils/validate-and-transform-widget-configuration.util.ts index d1c2731b8d..206fd3407f 100644 --- a/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/utils/validate-and-transform-widget-configuration.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout-widget/utils/validate-and-transform-widget-configuration.util.ts @@ -10,10 +10,10 @@ import { IframeConfigurationDTO } from 'src/engine/metadata-modules/page-layout- import { LineChartConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/line-chart-configuration.dto'; import { PieChartConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/pie-chart-configuration.dto'; import { StandaloneRichTextConfigurationDTO } from 'src/engine/metadata-modules/page-layout-widget/dtos/standalone-rich-text-configuration.dto'; -import { type WidgetConfigurationInterface } from 'src/engine/metadata-modules/page-layout-widget/dtos/widget-configuration.interface'; import { BarChartGroupMode } from 'src/engine/metadata-modules/page-layout-widget/enums/bar-chart-group-mode.enum'; import { GraphType } from 'src/engine/metadata-modules/page-layout-widget/enums/graph-type.enum'; import { WidgetType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-type.enum'; +import { type AllPageLayoutWidgetConfiguration } from 'src/engine/metadata-modules/page-layout-widget/types/all-page-layout-widget-configuration.type'; const formatValidationErrors = (errors: ValidationError[]): string => { return errors @@ -33,22 +33,24 @@ const validateGraphConfiguration = ({ }: { configuration: Record; isDashboardV2Enabled: boolean; -}): WidgetConfigurationInterface | null => { - const graphType = configuration.graphType as GraphType; +}): AllPageLayoutWidgetConfiguration | null => { + const configurationType = configuration.configurationType as GraphType; - if (!graphType || !Object.values(GraphType).includes(graphType)) { + if ( + !configurationType || + !Object.values(GraphType).includes(configurationType) + ) { return null; } - if (graphType === GraphType.GAUGE && !isDashboardV2Enabled) { + if (configurationType === GraphType.GAUGE_CHART && !isDashboardV2Enabled) { throw new Error( - `Chart type ${graphType} requires IS_DASHBOARD_V2_ENABLED feature flag`, + `Chart type ${configurationType} requires IS_DASHBOARD_V2_ENABLED feature flag`, ); } - switch (graphType) { - case GraphType.VERTICAL_BAR: - case GraphType.HORIZONTAL_BAR: { + switch (configurationType) { + case GraphType.BAR_CHART: { const instance = plainToInstance(BarChartConfigurationDTO, configuration); const errors = validateSync(instance, { @@ -69,7 +71,7 @@ const validateGraphConfiguration = ({ return instance; } - case GraphType.LINE: { + case GraphType.LINE_CHART: { const instance = plainToInstance( LineChartConfigurationDTO, configuration, @@ -93,7 +95,7 @@ const validateGraphConfiguration = ({ return instance; } - case GraphType.PIE: { + case GraphType.PIE_CHART: { const instance = plainToInstance(PieChartConfigurationDTO, configuration); const errors = validateSync(instance, { @@ -107,7 +109,7 @@ const validateGraphConfiguration = ({ return instance; } - case GraphType.AGGREGATE: { + case GraphType.AGGREGATE_CHART: { const instance = plainToInstance( AggregateChartConfigurationDTO, configuration, @@ -124,7 +126,7 @@ const validateGraphConfiguration = ({ return instance; } - case GraphType.GAUGE: { + case GraphType.GAUGE_CHART: { const instance = plainToInstance( GaugeChartConfigurationDTO, configuration, @@ -148,7 +150,7 @@ const validateGraphConfiguration = ({ const validateIframeConfiguration = ( configuration: unknown, -): WidgetConfigurationInterface | null => { +): AllPageLayoutWidgetConfiguration | null => { const instance = plainToInstance(IframeConfigurationDTO, configuration); const errors = validateSync(instance, { @@ -165,7 +167,7 @@ const validateIframeConfiguration = ( const validateStandaloneRichTextConfiguration = async ( configuration: unknown, -): Promise => { +): Promise => { const instance = plainToInstance( StandaloneRichTextConfigurationDTO, configuration, @@ -195,7 +197,7 @@ export const validateAndTransformWidgetConfiguration = async ({ type: WidgetType; configuration: unknown; isDashboardV2Enabled: boolean; -}): Promise => { +}): Promise => { if (!configuration || typeof configuration !== 'object') { throw new Error('Invalid configuration: not an object'); } diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout/services/page-layout-duplication.service.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout/services/page-layout-duplication.service.ts index abe63e1a6d..901c479f75 100644 --- a/packages/twenty-server/src/engine/metadata-modules/page-layout/services/page-layout-duplication.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout/services/page-layout-duplication.service.ts @@ -272,10 +272,7 @@ export class PageLayoutDuplicationService { gridPosition: originalWidget.gridPosition, type: originalWidget.type, objectMetadataId: originalWidget.objectMetadataId, - configuration: originalWidget.configuration as Record< - string, - unknown - > | null, + configuration: originalWidget.configuration, pageLayoutTabId: newTabId, }, workspaceId, diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout/services/page-layout-update.service.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout/services/page-layout-update.service.ts index 74b4109f69..eee034a575 100644 --- a/packages/twenty-server/src/engine/metadata-modules/page-layout/services/page-layout-update.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout/services/page-layout-update.service.ts @@ -6,8 +6,10 @@ import { v4 } from 'uuid'; import { ApplicationService } from 'src/engine/core-modules/application/application.service'; import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service'; import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util'; +import { FLAT_PAGE_LAYOUT_TAB_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-page-layout-tab/constants/flat-page-layout-tab-editable-properties.constant'; import { type FlatPageLayoutTabMaps } from 'src/engine/metadata-modules/flat-page-layout-tab/types/flat-page-layout-tab-maps.type'; import { type FlatPageLayoutTab } from 'src/engine/metadata-modules/flat-page-layout-tab/types/flat-page-layout-tab.type'; +import { FLAT_PAGE_LAYOUT_WIDGET_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-page-layout-widget/constants/flat-page-layout-widget-editable-properties.constant'; import { type FlatPageLayoutWidgetMaps } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget-maps.type'; import { type FlatPageLayoutWidget } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget.type'; import { type FlatPageLayout } from 'src/engine/metadata-modules/flat-page-layout/types/flat-page-layout.type'; @@ -63,6 +65,7 @@ export class PageLayoutUpdateService { const existingPageLayout = flatPageLayoutMaps.byId[id]; + // TODO move in validator if ( !isDefined(existingPageLayout) || isDefined(existingPageLayout.deletedAt) @@ -75,6 +78,7 @@ export class PageLayoutUpdateService { PageLayoutExceptionCode.PAGE_LAYOUT_NOT_FOUND, ); } + /// const { workspaceCustomFlatApplication } = await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow( @@ -201,7 +205,7 @@ export class PageLayoutUpdateService { >({ existingObjects: existingTabs, receivedObjects: tabs, - propertiesToCompare: ['title', 'position'], + propertiesToCompare: FLAT_PAGE_LAYOUT_TAB_EDITABLE_PROPERTIES, }); const now = new Date(); @@ -228,10 +232,13 @@ export class PageLayoutUpdateService { const tabsToUpdate: FlatPageLayoutTab[] = entitiesToUpdate.map( (tabInput) => { - const existingTab = flatPageLayoutTabMaps.byId[tabInput.id]; + const existingTab = findFlatEntityByIdInFlatEntityMapsOrThrow({ + flatEntityId: tabInput.id, + flatEntityMaps: flatPageLayoutTabMaps, + }); return { - ...existingTab!, + ...existingTab, title: tabInput.title, position: tabInput.position, updatedAt: now.toISOString(), @@ -241,10 +248,13 @@ export class PageLayoutUpdateService { const tabsToRestoreAndUpdate: FlatPageLayoutTab[] = entitiesToRestoreAndUpdate.map((tabInput) => { - const existingTab = flatPageLayoutTabMaps.byId[tabInput.id]; + const existingTab = findFlatEntityByIdInFlatEntityMapsOrThrow({ + flatEntityId: tabInput.id, + flatEntityMaps: flatPageLayoutTabMaps, + }); return { - ...existingTab!, + ...existingTab, title: tabInput.title, position: tabInput.position, deletedAt: null, @@ -349,14 +359,7 @@ export class PageLayoutUpdateService { >({ existingObjects: existingWidgets, receivedObjects: widgets, - propertiesToCompare: [ - 'pageLayoutTabId', - 'objectMetadataId', - 'title', - 'type', - 'gridPosition', - 'configuration', - ], + propertiesToCompare: FLAT_PAGE_LAYOUT_WIDGET_EDITABLE_PROPERTIES, }); const now = new Date(); @@ -385,10 +388,13 @@ export class PageLayoutUpdateService { const widgetsToUpdate: FlatPageLayoutWidget[] = entitiesToUpdate.map( (widgetInput) => { - const existingWidget = flatPageLayoutWidgetMaps.byId[widgetInput.id]; + const existingWidget = findFlatEntityByIdInFlatEntityMapsOrThrow({ + flatEntityId: widgetInput.id, + flatEntityMaps: flatPageLayoutWidgetMaps, + }); return { - ...existingWidget!, + ...existingWidget, pageLayoutTabId: widgetInput.pageLayoutTabId, title: widgetInput.title, type: widgetInput.type, @@ -402,10 +408,13 @@ export class PageLayoutUpdateService { const widgetsToRestoreAndUpdate: FlatPageLayoutWidget[] = entitiesToRestoreAndUpdate.map((widgetInput) => { - const existingWidget = flatPageLayoutWidgetMaps.byId[widgetInput.id]; + const existingWidget = findFlatEntityByIdInFlatEntityMapsOrThrow({ + flatEntityId: widgetInput.id, + flatEntityMaps: flatPageLayoutWidgetMaps, + }); return { - ...existingWidget!, + ...existingWidget, pageLayoutTabId: widgetInput.pageLayoutTabId, title: widgetInput.title, type: widgetInput.type, diff --git a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/types/seeder-flat-page-layout-widget.type.ts b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/types/seeder-flat-page-layout-widget.type.ts new file mode 100644 index 0000000000..9485b2af12 --- /dev/null +++ b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/types/seeder-flat-page-layout-widget.type.ts @@ -0,0 +1,12 @@ +import { type FlatPageLayoutWidget } from 'src/engine/metadata-modules/flat-page-layout-widget/types/flat-page-layout-widget.type'; + +export type SeederFlatPageLayoutWidget = Pick< + FlatPageLayoutWidget, + | 'id' + | 'pageLayoutTabId' + | 'title' + | 'type' + | 'gridPosition' + | 'configuration' + | 'objectMetadataId' +>; diff --git a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/get-page-layout-widget-data-seeds-v2.util.ts b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/get-page-layout-widget-data-seeds-v2.util.ts index fed493fd03..0ee06d777c 100644 --- a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/get-page-layout-widget-data-seeds-v2.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/get-page-layout-widget-data-seeds-v2.util.ts @@ -5,26 +5,14 @@ import { isDefined } from 'twenty-shared/utils'; import { AggregateOperations } from 'src/engine/api/graphql/graphql-query-runner/constants/aggregate-operations.constant'; import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { AxisNameDisplay } from 'src/engine/metadata-modules/page-layout-widget/enums/axis-name-display.enum'; +import { GraphOrderBy } from 'src/engine/metadata-modules/page-layout-widget/enums/graph-order-by.enum'; +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 { PAGE_LAYOUT_TAB_SEEDS } from 'src/engine/workspace-manager/dev-seeder/core/constants/page-layout-tab-seeds.constant'; import { PAGE_LAYOUT_WIDGET_SEEDS } from 'src/engine/workspace-manager/dev-seeder/core/constants/page-layout-widget-seeds.constant'; +import { type SeederFlatPageLayoutWidget } from 'src/engine/workspace-manager/dev-seeder/core/types/seeder-flat-page-layout-widget.type'; import { generateSeedId } from 'src/engine/workspace-manager/dev-seeder/core/utils/generate-seed-id.util'; -type PageLayoutWidgetDataSeed = { - id: string; - pageLayoutTabId: string; - title: string; - type: WidgetType; - gridPosition: { - row: number; - column: number; - rowSpan: number; - columnSpan: number; - }; - configuration: Record | null; - objectMetadataId: string | null; -}; - const getFieldId = ( object: ObjectMetadataEntity | undefined, fieldName: string, @@ -35,7 +23,7 @@ const getFieldId = ( export const getPageLayoutWidgetDataSeedsV2 = ( workspaceId: string, objectMetadataItems: ObjectMetadataEntity[], -): PageLayoutWidgetDataSeed[] => { +): SeederFlatPageLayoutWidget[] => { const opportunityObject = objectMetadataItems.find( (obj) => obj.standardId === STANDARD_OBJECT_IDS.opportunity, ); @@ -65,7 +53,7 @@ export const getPageLayoutWidgetDataSeedsV2 = ( // LINE chart: Revenue Forecast (Sales Overview) isDefined(opportunityAmountFieldId) && isDefined(opportunityCloseDateFieldId) - ? { + ? ({ id: generateSeedId( workspaceId, PAGE_LAYOUT_WIDGET_SEEDS.SALES_REVENUE_FORECAST, @@ -78,23 +66,23 @@ export const getPageLayoutWidgetDataSeedsV2 = ( type: WidgetType.GRAPH, gridPosition: { row: 0, column: 7, rowSpan: 8, columnSpan: 5 }, configuration: { - graphType: 'LINE', + configurationType: WidgetConfigurationType.LINE_CHART, aggregateFieldMetadataId: opportunityAmountFieldId, aggregateOperation: AggregateOperations.SUM, primaryAxisGroupByFieldMetadataId: opportunityCloseDateFieldId, - primaryAxisOrderBy: 'FIELD_ASC', + primaryAxisOrderBy: GraphOrderBy.FIELD_ASC, axisNameDisplay: AxisNameDisplay.NONE, displayDataLabel: false, timezone: 'UTC', firstDayOfTheWeek: CalendarStartDay.MONDAY, }, objectMetadataId: opportunityObject?.id ?? null, - } + } satisfies SeederFlatPageLayoutWidget) : null, // LINE chart: New Customers Over Time (Customer Overview) isDefined(companyIdFieldId) && isDefined(companyCreatedAtFieldId) - ? { + ? ({ id: generateSeedId( workspaceId, PAGE_LAYOUT_WIDGET_SEEDS.CUSTOMER_NEW_OVER_TIME, @@ -107,23 +95,23 @@ export const getPageLayoutWidgetDataSeedsV2 = ( type: WidgetType.GRAPH, gridPosition: { row: 0, column: 3, rowSpan: 6, columnSpan: 5 }, configuration: { - graphType: 'LINE', + configurationType: WidgetConfigurationType.LINE_CHART, aggregateFieldMetadataId: companyIdFieldId, aggregateOperation: AggregateOperations.COUNT, primaryAxisGroupByFieldMetadataId: companyCreatedAtFieldId, - primaryAxisOrderBy: 'FIELD_ASC', + primaryAxisOrderBy: GraphOrderBy.FIELD_ASC, axisNameDisplay: AxisNameDisplay.NONE, displayDataLabel: false, timezone: 'UTC', firstDayOfTheWeek: CalendarStartDay.MONDAY, }, objectMetadataId: companyObject?.id ?? null, - } + } satisfies SeederFlatPageLayoutWidget) : null, // PIE chart: Revenue Distribution (Customer Analytics) isDefined(companyArrFieldId) && isDefined(companyNameFieldId) - ? { + ? ({ id: generateSeedId( workspaceId, PAGE_LAYOUT_WIDGET_SEEDS.CUSTOMER_REVENUE_DISTRIBUTION, @@ -136,22 +124,22 @@ export const getPageLayoutWidgetDataSeedsV2 = ( type: WidgetType.GRAPH, gridPosition: { row: 0, column: 4, rowSpan: 2, columnSpan: 3 }, configuration: { - graphType: 'PIE', + configurationType: WidgetConfigurationType.PIE_CHART, aggregateFieldMetadataId: companyArrFieldId, aggregateOperation: AggregateOperations.SUM, groupByFieldMetadataId: companyNameFieldId, - orderBy: 'VALUE_DESC', + orderBy: GraphOrderBy.VALUE_DESC, displayDataLabel: true, timezone: 'UTC', firstDayOfTheWeek: CalendarStartDay.MONDAY, }, objectMetadataId: companyObject?.id ?? null, - } + } satisfies SeederFlatPageLayoutWidget) : null, // GAUGE chart: Average ARR (Customer Analytics) isDefined(companyArrFieldId) - ? { + ? ({ id: generateSeedId( workspaceId, PAGE_LAYOUT_WIDGET_SEEDS.CUSTOMER_AVERAGE_ARR, @@ -164,7 +152,7 @@ export const getPageLayoutWidgetDataSeedsV2 = ( type: WidgetType.GRAPH, gridPosition: { row: 0, column: 7, rowSpan: 6, columnSpan: 5 }, configuration: { - graphType: 'GAUGE', + configurationType: WidgetConfigurationType.GAUGE_CHART, aggregateFieldMetadataId: companyArrFieldId, aggregateOperation: AggregateOperations.AVG, displayDataLabel: true, @@ -172,12 +160,12 @@ export const getPageLayoutWidgetDataSeedsV2 = ( firstDayOfTheWeek: CalendarStartDay.MONDAY, }, objectMetadataId: companyObject?.id ?? null, - } + } satisfies SeederFlatPageLayoutWidget) : null, // PIE chart: Companies by LinkedIn (Customer Overview) isDefined(companyIdFieldId) && isDefined(companyLinkedinLinkFieldId) - ? { + ? ({ id: generateSeedId( workspaceId, PAGE_LAYOUT_WIDGET_SEEDS.CUSTOMER_LINKEDIN_DISTRIBUTION, @@ -190,22 +178,22 @@ export const getPageLayoutWidgetDataSeedsV2 = ( type: WidgetType.GRAPH, gridPosition: { row: 6, column: 0, rowSpan: 4, columnSpan: 6 }, configuration: { - graphType: 'PIE', + configurationType: WidgetConfigurationType.PIE_CHART, aggregateFieldMetadataId: companyIdFieldId, aggregateOperation: AggregateOperations.COUNT, groupByFieldMetadataId: companyLinkedinLinkFieldId, - orderBy: 'VALUE_DESC', + orderBy: GraphOrderBy.VALUE_DESC, displayDataLabel: true, timezone: 'UTC', firstDayOfTheWeek: CalendarStartDay.MONDAY, }, objectMetadataId: companyObject?.id ?? null, - } + } satisfies SeederFlatPageLayoutWidget) : null, // PIE chart: Contact Roles (Team Metrics) isDefined(personIdFieldId) && isDefined(personJobTitleFieldId) - ? { + ? ({ id: generateSeedId( workspaceId, PAGE_LAYOUT_WIDGET_SEEDS.TEAM_CONTACT_ROLES, @@ -218,17 +206,17 @@ export const getPageLayoutWidgetDataSeedsV2 = ( type: WidgetType.GRAPH, gridPosition: { row: 0, column: 0, rowSpan: 4, columnSpan: 6 }, configuration: { - graphType: 'PIE', + configurationType: WidgetConfigurationType.PIE_CHART, aggregateFieldMetadataId: personIdFieldId, aggregateOperation: AggregateOperations.COUNT, groupByFieldMetadataId: personJobTitleFieldId, - orderBy: 'VALUE_DESC', + orderBy: GraphOrderBy.VALUE_DESC, displayDataLabel: true, timezone: 'UTC', firstDayOfTheWeek: CalendarStartDay.MONDAY, }, objectMetadataId: personObject?.id ?? null, - } + } satisfies SeederFlatPageLayoutWidget) : null, ].filter(isDefined); }; diff --git a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/get-page-layout-widget-data-seeds.util.ts b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/get-page-layout-widget-data-seeds.util.ts index 3e3ce4b22a..a85925d80d 100644 --- a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/get-page-layout-widget-data-seeds.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/get-page-layout-widget-data-seeds.util.ts @@ -5,28 +5,17 @@ import { isDefined } from 'twenty-shared/utils'; import { AggregateOperations } from 'src/engine/api/graphql/graphql-query-runner/constants/aggregate-operations.constant'; import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { AxisNameDisplay } from 'src/engine/metadata-modules/page-layout-widget/enums/axis-name-display.enum'; -import { GraphType } from 'src/engine/metadata-modules/page-layout-widget/enums/graph-type.enum'; +import { BarChartLayout } from 'src/engine/metadata-modules/page-layout-widget/enums/bar-chart-layout.enum'; +import { ObjectRecordGroupByDateGranularity } from 'src/engine/metadata-modules/page-layout-widget/enums/date-granularity.enum'; +import { GraphOrderBy } from 'src/engine/metadata-modules/page-layout-widget/enums/graph-order-by.enum'; +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 { PAGE_LAYOUT_TAB_SEEDS } from 'src/engine/workspace-manager/dev-seeder/core/constants/page-layout-tab-seeds.constant'; import { PAGE_LAYOUT_WIDGET_SEEDS } from 'src/engine/workspace-manager/dev-seeder/core/constants/page-layout-widget-seeds.constant'; +import { type SeederFlatPageLayoutWidget } from 'src/engine/workspace-manager/dev-seeder/core/types/seeder-flat-page-layout-widget.type'; import { generateSeedId } from 'src/engine/workspace-manager/dev-seeder/core/utils/generate-seed-id.util'; import { getPageLayoutWidgetDataSeedsV2 } from 'src/engine/workspace-manager/dev-seeder/core/utils/get-page-layout-widget-data-seeds-v2.util'; -type PageLayoutWidgetDataSeed = { - id: string; - pageLayoutTabId: string; - title: string; - type: WidgetType; - gridPosition: { - row: number; - column: number; - rowSpan: number; - columnSpan: number; - }; - configuration: Record | null; - objectMetadataId: string | null; -}; - const getFieldId = ( object: ObjectMetadataEntity | undefined, fieldName: string, @@ -38,7 +27,7 @@ export const getPageLayoutWidgetDataSeeds = ( workspaceId: string, objectMetadataItems: ObjectMetadataEntity[], isDashboardV2Enabled: boolean, -): PageLayoutWidgetDataSeed[] => { +): SeederFlatPageLayoutWidget[] => { const opportunityObject = objectMetadataItems.find( (obj) => obj.standardId === STANDARD_OBJECT_IDS.opportunity, ); @@ -78,10 +67,10 @@ export const getPageLayoutWidgetDataSeeds = ( const rocketIdFieldId = getFieldId(rocketObject, 'id'); const rocketCreatedAtFieldId = getFieldId(rocketObject, 'createdAt'); - const v1Widgets = [ + const v1Widgets: SeederFlatPageLayoutWidget[] = [ // Sales Overview Tab Widgets isDefined(opportunityAmountFieldId) - ? { + ? ({ id: generateSeedId( workspaceId, PAGE_LAYOUT_WIDGET_SEEDS.SALES_PIPELINE_VALUE, @@ -94,7 +83,7 @@ export const getPageLayoutWidgetDataSeeds = ( type: WidgetType.GRAPH, gridPosition: { row: 0, column: 0, rowSpan: 2, columnSpan: 3 }, configuration: { - graphType: 'AGGREGATE', + configurationType: WidgetConfigurationType.AGGREGATE_CHART, aggregateFieldMetadataId: opportunityAmountFieldId, aggregateOperation: AggregateOperations.SUM, displayDataLabel: true, @@ -102,10 +91,10 @@ export const getPageLayoutWidgetDataSeeds = ( firstDayOfTheWeek: CalendarStartDay.MONDAY, }, objectMetadataId: opportunityObject?.id ?? null, - } + } satisfies SeederFlatPageLayoutWidget) : null, isDefined(rocketIdFieldId) - ? { + ? ({ id: generateSeedId( workspaceId, PAGE_LAYOUT_WIDGET_SEEDS.SALES_AVERAGE_DEAL_SIZE, @@ -118,7 +107,7 @@ export const getPageLayoutWidgetDataSeeds = ( type: WidgetType.GRAPH, gridPosition: { row: 0, column: 3, rowSpan: 4, columnSpan: 4 }, configuration: { - graphType: 'AGGREGATE', + configurationType: WidgetConfigurationType.AGGREGATE_CHART, aggregateFieldMetadataId: rocketIdFieldId, aggregateOperation: AggregateOperations.COUNT, displayDataLabel: true, @@ -126,12 +115,12 @@ export const getPageLayoutWidgetDataSeeds = ( firstDayOfTheWeek: CalendarStartDay.MONDAY, }, objectMetadataId: rocketObject?.id ?? null, - } + } satisfies SeederFlatPageLayoutWidget) : null, isDefined(opportunityAmountFieldId) && isDefined(opportunityCloseDateFieldId) && isDefined(opportunityStageFieldId) - ? { + ? ({ id: generateSeedId( workspaceId, PAGE_LAYOUT_WIDGET_SEEDS.SALES_DEALS_BY_STAGE, @@ -144,25 +133,26 @@ export const getPageLayoutWidgetDataSeeds = ( type: WidgetType.GRAPH, gridPosition: { row: 4, column: 0, rowSpan: 8, columnSpan: 6 }, configuration: { - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, aggregateFieldMetadataId: opportunityAmountFieldId, aggregateOperation: AggregateOperations.SUM, primaryAxisGroupByFieldMetadataId: opportunityCloseDateFieldId, secondaryAxisGroupByFieldMetadataId: opportunityStageFieldId, - primaryAxisOrderBy: 'FIELD_ASC', + primaryAxisOrderBy: GraphOrderBy.FIELD_ASC, axisNameDisplay: AxisNameDisplay.NONE, displayDataLabel: false, color: 'auto', + layout: BarChartLayout.VERTICAL, timezone: 'UTC', firstDayOfTheWeek: CalendarStartDay.MONDAY, }, objectMetadataId: opportunityObject?.id ?? null, - } + } satisfies SeederFlatPageLayoutWidget) : null, // Sales Details Tab Widgets isDefined(rocketIdFieldId) && isDefined(rocketCreatedAtFieldId) - ? { + ? ({ id: generateSeedId( workspaceId, PAGE_LAYOUT_WIDGET_SEEDS.SALES_DEAL_DISTRIBUTION, @@ -175,22 +165,23 @@ export const getPageLayoutWidgetDataSeeds = ( type: WidgetType.GRAPH, gridPosition: { row: 0, column: 0, rowSpan: 5, columnSpan: 5 }, configuration: { - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, aggregateFieldMetadataId: rocketIdFieldId, aggregateOperation: AggregateOperations.COUNT, primaryAxisGroupByFieldMetadataId: rocketCreatedAtFieldId, - primaryAxisOrderBy: 'FIELD_ASC', + primaryAxisOrderBy: GraphOrderBy.FIELD_ASC, axisNameDisplay: AxisNameDisplay.NONE, displayDataLabel: false, color: 'auto', + layout: BarChartLayout.VERTICAL, timezone: 'UTC', firstDayOfTheWeek: CalendarStartDay.MONDAY, }, objectMetadataId: rocketObject?.id ?? null, - } + } satisfies SeederFlatPageLayoutWidget) : null, isDefined(opportunityIdFieldId) - ? { + ? ({ id: generateSeedId( workspaceId, PAGE_LAYOUT_WIDGET_SEEDS.SALES_OPPORTUNITY_COUNT, @@ -203,7 +194,7 @@ export const getPageLayoutWidgetDataSeeds = ( type: WidgetType.GRAPH, gridPosition: { row: 0, column: 5, rowSpan: 5, columnSpan: 7 }, configuration: { - graphType: 'AGGREGATE', + configurationType: WidgetConfigurationType.AGGREGATE_CHART, aggregateFieldMetadataId: opportunityIdFieldId, aggregateOperation: AggregateOperations.COUNT, displayDataLabel: true, @@ -211,12 +202,12 @@ export const getPageLayoutWidgetDataSeeds = ( firstDayOfTheWeek: CalendarStartDay.MONDAY, }, objectMetadataId: opportunityObject?.id ?? null, - } + } satisfies SeederFlatPageLayoutWidget) : null, // Customer Overview Tab Widgets isDefined(companyIdFieldId) - ? { + ? ({ id: generateSeedId( workspaceId, PAGE_LAYOUT_WIDGET_SEEDS.CUSTOMER_TOTAL_COUNT, @@ -229,7 +220,7 @@ export const getPageLayoutWidgetDataSeeds = ( type: WidgetType.GRAPH, gridPosition: { row: 0, column: 0, rowSpan: 2, columnSpan: 3 }, configuration: { - graphType: 'AGGREGATE', + configurationType: WidgetConfigurationType.AGGREGATE_CHART, aggregateFieldMetadataId: companyIdFieldId, aggregateOperation: AggregateOperations.COUNT, displayDataLabel: true, @@ -237,12 +228,12 @@ export const getPageLayoutWidgetDataSeeds = ( firstDayOfTheWeek: CalendarStartDay.MONDAY, }, objectMetadataId: companyObject?.id ?? null, - } + } satisfies SeederFlatPageLayoutWidget) : null, isDefined(companyIdFieldId) && isDefined(companyEmployeesFieldId) && isDefined(companyAddressFieldId) - ? { + ? ({ id: generateSeedId( workspaceId, PAGE_LAYOUT_WIDGET_SEEDS.CUSTOMER_COMPANIES_BY_SIZE, @@ -255,27 +246,29 @@ export const getPageLayoutWidgetDataSeeds = ( type: WidgetType.GRAPH, gridPosition: { row: 0, column: 8, rowSpan: 10, columnSpan: 8 }, configuration: { - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, aggregateFieldMetadataId: companyIdFieldId, aggregateOperation: AggregateOperations.COUNT, primaryAxisGroupByFieldMetadataId: companyEmployeesFieldId, secondaryAxisGroupByFieldMetadataId: companyAddressFieldId, secondaryAxisGroupBySubFieldName: 'addressCity', - secondaryAxisGroupByDateGranularity: 'DAY', - primaryAxisOrderBy: 'FIELD_ASC', + secondaryAxisGroupByDateGranularity: + ObjectRecordGroupByDateGranularity.DAY, + primaryAxisOrderBy: GraphOrderBy.FIELD_ASC, axisNameDisplay: AxisNameDisplay.NONE, displayDataLabel: false, color: 'auto', + layout: BarChartLayout.VERTICAL, timezone: 'UTC', firstDayOfTheWeek: CalendarStartDay.MONDAY, }, objectMetadataId: companyObject?.id ?? null, - } + } satisfies SeederFlatPageLayoutWidget) : null, // Customer Analytics Tab Widgets isDefined(companyArrFieldId) - ? { + ? ({ id: generateSeedId( workspaceId, PAGE_LAYOUT_WIDGET_SEEDS.CUSTOMER_ANNUAL_RECURRING_REVENUE, @@ -288,7 +281,7 @@ export const getPageLayoutWidgetDataSeeds = ( type: WidgetType.GRAPH, gridPosition: { row: 0, column: 0, rowSpan: 4, columnSpan: 4 }, configuration: { - graphType: 'AGGREGATE', + configurationType: WidgetConfigurationType.AGGREGATE_CHART, aggregateFieldMetadataId: companyArrFieldId, aggregateOperation: AggregateOperations.SUM, displayDataLabel: true, @@ -296,10 +289,10 @@ export const getPageLayoutWidgetDataSeeds = ( firstDayOfTheWeek: CalendarStartDay.MONDAY, }, objectMetadataId: companyObject?.id ?? null, - } + } satisfies SeederFlatPageLayoutWidget) : null, isDefined(companyLinkedinLinkFieldId) - ? { + ? ({ id: generateSeedId( workspaceId, PAGE_LAYOUT_WIDGET_SEEDS.CUSTOMER_LINKEDIN_COUNT, @@ -312,7 +305,7 @@ export const getPageLayoutWidgetDataSeeds = ( type: WidgetType.GRAPH, gridPosition: { row: 2, column: 0, rowSpan: 4, columnSpan: 3 }, configuration: { - graphType: 'AGGREGATE', + configurationType: WidgetConfigurationType.AGGREGATE_CHART, aggregateFieldMetadataId: companyLinkedinLinkFieldId, aggregateOperation: AggregateOperations.COUNT, displayDataLabel: true, @@ -320,12 +313,12 @@ export const getPageLayoutWidgetDataSeeds = ( firstDayOfTheWeek: CalendarStartDay.MONDAY, }, objectMetadataId: companyObject?.id ?? null, - } + } satisfies SeederFlatPageLayoutWidget) : null, // Team Overview Tab Widgets isDefined(personIdFieldId) - ? { + ? ({ id: generateSeedId(workspaceId, PAGE_LAYOUT_WIDGET_SEEDS.TEAM_SIZE), pageLayoutTabId: generateSeedId( workspaceId, @@ -335,7 +328,7 @@ export const getPageLayoutWidgetDataSeeds = ( type: WidgetType.GRAPH, gridPosition: { row: 0, column: 0, rowSpan: 5, columnSpan: 6 }, configuration: { - graphType: 'AGGREGATE', + configurationType: WidgetConfigurationType.AGGREGATE_CHART, aggregateFieldMetadataId: personIdFieldId, aggregateOperation: AggregateOperations.COUNT, displayDataLabel: true, @@ -343,10 +336,10 @@ export const getPageLayoutWidgetDataSeeds = ( firstDayOfTheWeek: CalendarStartDay.MONDAY, }, objectMetadataId: personObject?.id ?? null, - } + } satisfies SeederFlatPageLayoutWidget) : null, isDefined(personIdFieldId) && isDefined(personCityFieldId) - ? { + ? ({ id: generateSeedId( workspaceId, PAGE_LAYOUT_WIDGET_SEEDS.TEAM_GEOGRAPHIC_DISTRIBUTION, @@ -359,24 +352,25 @@ export const getPageLayoutWidgetDataSeeds = ( type: WidgetType.GRAPH, gridPosition: { row: 0, column: 6, rowSpan: 5, columnSpan: 6 }, configuration: { - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, aggregateFieldMetadataId: personIdFieldId, aggregateOperation: AggregateOperations.COUNT, primaryAxisGroupByFieldMetadataId: personCityFieldId, - primaryAxisOrderBy: 'VALUE_DESC', + primaryAxisOrderBy: GraphOrderBy.VALUE_DESC, axisNameDisplay: AxisNameDisplay.NONE, displayDataLabel: false, color: 'auto', + layout: BarChartLayout.VERTICAL, timezone: 'UTC', firstDayOfTheWeek: CalendarStartDay.MONDAY, }, objectMetadataId: personObject?.id ?? null, - } + } satisfies SeederFlatPageLayoutWidget) : null, // Team Metrics Tab Widgets isDefined(taskIdFieldId) - ? { + ? ({ id: generateSeedId( workspaceId, PAGE_LAYOUT_WIDGET_SEEDS.TEAM_OPEN_TASKS, @@ -389,7 +383,7 @@ export const getPageLayoutWidgetDataSeeds = ( type: WidgetType.GRAPH, gridPosition: { row: 0, column: 6, rowSpan: 6, columnSpan: 6 }, configuration: { - graphType: 'AGGREGATE', + configurationType: WidgetConfigurationType.AGGREGATE_CHART, aggregateFieldMetadataId: taskIdFieldId, aggregateOperation: AggregateOperations.COUNT, displayDataLabel: true, @@ -397,7 +391,7 @@ export const getPageLayoutWidgetDataSeeds = ( firstDayOfTheWeek: CalendarStartDay.MONDAY, }, objectMetadataId: taskObject?.id ?? null, - } + } satisfies SeederFlatPageLayoutWidget) : null, ].filter(isDefined); diff --git a/packages/twenty-server/src/modules/dashboard/tools/add-dashboard-widget.tool.ts b/packages/twenty-server/src/modules/dashboard/tools/add-dashboard-widget.tool.ts index 49c566d681..47e393cc89 100644 --- a/packages/twenty-server/src/modules/dashboard/tools/add-dashboard-widget.tool.ts +++ b/packages/twenty-server/src/modules/dashboard/tools/add-dashboard-widget.tool.ts @@ -1,6 +1,8 @@ import { z } from 'zod'; +import { type CreatePageLayoutWidgetInput } from 'src/engine/metadata-modules/page-layout-widget/dtos/inputs/create-page-layout-widget.input'; import { type WidgetType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-type.enum'; +import { type AllPageLayoutWidgetConfiguration } from 'src/engine/metadata-modules/page-layout-widget/types/all-page-layout-widget-configuration.type'; import { gridPositionSchema, widgetConfigurationSchema, @@ -47,11 +49,11 @@ See create_complete_dashboard for configuration examples.`, columnSpan: number; }; objectMetadataId?: string; - configuration?: Record; + configuration?: AllPageLayoutWidgetConfiguration; }) => { try { const widget = await deps.pageLayoutWidgetService.create( - parameters, + parameters as CreatePageLayoutWidgetInput, context.workspaceId, ); diff --git a/packages/twenty-server/src/modules/dashboard/tools/create-complete-dashboard.tool.ts b/packages/twenty-server/src/modules/dashboard/tools/create-complete-dashboard.tool.ts index ccac71a4d2..f9315cb3ad 100644 --- a/packages/twenty-server/src/modules/dashboard/tools/create-complete-dashboard.tool.ts +++ b/packages/twenty-server/src/modules/dashboard/tools/create-complete-dashboard.tool.ts @@ -1,8 +1,10 @@ import { v4 as uuidv4 } from 'uuid'; import { z } from 'zod'; -import { PageLayoutType } from 'src/engine/metadata-modules/page-layout/enums/page-layout-type.enum'; +import { type CreatePageLayoutWidgetInput } from 'src/engine/metadata-modules/page-layout-widget/dtos/inputs/create-page-layout-widget.input'; import { type WidgetType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-type.enum'; +import { type AllPageLayoutWidgetConfiguration } from 'src/engine/metadata-modules/page-layout-widget/types/all-page-layout-widget-configuration.type'; +import { PageLayoutType } from 'src/engine/metadata-modules/page-layout/enums/page-layout-type.enum'; import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util'; import { gridPositionSchema, @@ -92,7 +94,7 @@ AGGREGATION OPERATIONS: COUNT, SUM, AVG, MIN, MAX, COUNT_EMPTY, COUNT_NOT_EMPTY` columnSpan: number; }; objectMetadataId?: string; - configuration?: Record; + configuration?: AllPageLayoutWidgetConfiguration; }>; }) => { try { @@ -115,7 +117,10 @@ AGGREGATION OPERATIONS: COUNT, SUM, AVG, MIN, MAX, COUNT_EMPTY, COUNT_NOT_EMPTY` for (const widget of widgets) { try { const createdWidget = await deps.pageLayoutWidgetService.create( - { ...widget, pageLayoutTabId: pageLayoutTab.id }, + { + ...widget, + pageLayoutTabId: pageLayoutTab.id, + } as CreatePageLayoutWidgetInput, context.workspaceId, ); diff --git a/packages/twenty-server/src/modules/dashboard/tools/schemas/widget.schema.ts b/packages/twenty-server/src/modules/dashboard/tools/schemas/widget.schema.ts index 1e1ee4481d..ccd80905d5 100644 --- a/packages/twenty-server/src/modules/dashboard/tools/schemas/widget.schema.ts +++ b/packages/twenty-server/src/modules/dashboard/tools/schemas/widget.schema.ts @@ -28,7 +28,7 @@ export const widgetTypeSchema = z.enum([ // Graph configuration schema for AGGREGATE type (KPI numbers) const aggregateChartConfigSchema = z.object({ - graphType: z.literal(GraphType.AGGREGATE), + graphType: z.literal(GraphType.AGGREGATE_CHART), aggregateFieldMetadataId: z .string() .uuid() @@ -47,7 +47,7 @@ const aggregateChartConfigSchema = z.object({ // Graph configuration schema for BAR charts const barChartConfigSchema = z.object({ - graphType: z.enum([GraphType.VERTICAL_BAR, GraphType.HORIZONTAL_BAR]), + graphType: z.literal(GraphType.BAR_CHART), aggregateFieldMetadataId: z .string() .uuid() @@ -63,12 +63,17 @@ const barChartConfigSchema = z.object({ .optional(), displayDataLabel: z.boolean().optional().default(false), displayLegend: z.boolean().optional().default(true), + layout: z + .enum(['VERTICAL', 'HORIZONTAL']) + .optional() + .default('VERTICAL') + .describe('Layout orientation for bar charts'), filter: z.record(z.string(), z.unknown()).optional(), }); // Graph configuration schema for LINE charts const lineChartConfigSchema = z.object({ - graphType: z.literal(GraphType.LINE), + graphType: z.literal(GraphType.LINE_CHART), aggregateFieldMetadataId: z.string().uuid(), aggregateOperation: z.nativeEnum(AggregateOperations), primaryAxisGroupByFieldMetadataId: z.string().uuid(), @@ -82,7 +87,7 @@ const lineChartConfigSchema = z.object({ // Graph configuration schema for PIE charts const pieChartConfigSchema = z.object({ - graphType: z.literal(GraphType.PIE), + graphType: z.literal(GraphType.PIE_CHART), aggregateFieldMetadataId: z.string().uuid(), aggregateOperation: z.nativeEnum(AggregateOperations), groupByFieldMetadataId: z.string().uuid().describe('Field UUID to slice by'), @@ -116,4 +121,4 @@ export const widgetConfigurationSchema = z .describe('Widget configuration - structure depends on widget type'); // Export enums for documentation -export { AggregateOperations, GraphType }; +export { AggregateOperations, GraphType as GraphType }; diff --git a/packages/twenty-server/test/integration/constants/widget-configuration-test-data.constants.ts b/packages/twenty-server/test/integration/constants/widget-configuration-test-data.constants.ts index af2f451a8d..824716bd0a 100644 --- a/packages/twenty-server/test/integration/constants/widget-configuration-test-data.constants.ts +++ b/packages/twenty-server/test/integration/constants/widget-configuration-test-data.constants.ts @@ -1,7 +1,8 @@ import { AggregateOperations } from 'src/engine/api/graphql/graphql-query-runner/constants/aggregate-operations.constant'; import { AxisNameDisplay } from 'src/engine/metadata-modules/page-layout-widget/enums/axis-name-display.enum'; +import { BarChartLayout } from 'src/engine/metadata-modules/page-layout-widget/enums/bar-chart-layout.enum'; import { GraphOrderBy } from 'src/engine/metadata-modules/page-layout-widget/enums/graph-order-by.enum'; -import { GraphType } from 'src/engine/metadata-modules/page-layout-widget/enums/graph-type.enum'; +import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type'; export const TEST_FIELD_METADATA_ID_1 = '20202020-1111-4111-a111-111111111111'; export const TEST_FIELD_METADATA_ID_2 = '20202020-2222-4222-a222-222222222222'; @@ -9,22 +10,25 @@ export const TEST_FIELD_METADATA_ID_3 = '20202020-3333-4333-a333-333333333333'; export const TEST_FIELD_METADATA_ID_4 = '20202020-4444-4444-a444-444444444444'; export const TEST_IFRAME_CONFIG = { + configurationType: WidgetConfigurationType.IFRAME, url: 'https://example.com/dashboard', -}; +} as const; export const TEST_STANDALONE_RICH_TEXT_CONFIG = { + configurationType: WidgetConfigurationType.STANDALONE_RICH_TEXT, body: { blocknote: '{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"Hello world"}]}]}', markdown: '# Hello world', }, -}; +} as const; export const TEST_STANDALONE_RICH_TEXT_CONFIG_MINIMAL = { + configurationType: WidgetConfigurationType.STANDALONE_RICH_TEXT, body: { markdown: 'Simple text', }, -}; +} as const; export const INVALID_STANDALONE_RICH_TEXT_CONFIG_MISSING_BODY = {}; @@ -33,6 +37,7 @@ export const INVALID_STANDALONE_RICH_TEXT_CONFIG_BODY_WRONG_TYPE = { }; export const INVALID_STANDALONE_RICH_TEXT_CONFIG_INVALID_SUBFIELDS = { + configurationType: WidgetConfigurationType.STANDALONE_RICH_TEXT, body: { blocknote: 'valid', markdown: 'valid', @@ -41,11 +46,12 @@ export const INVALID_STANDALONE_RICH_TEXT_CONFIG_INVALID_SUBFIELDS = { }; export const TEST_IFRAME_CONFIG_ALTERNATIVE = { + configurationType: WidgetConfigurationType.IFRAME, url: 'https://app.twenty.com/analytics', -}; +} as const; export const TEST_NUMBER_CHART_CONFIG = { - graphType: GraphType.AGGREGATE, + configurationType: WidgetConfigurationType.AGGREGATE_CHART, aggregateFieldMetadataId: TEST_FIELD_METADATA_ID_1, aggregateOperation: AggregateOperations.COUNT, label: 'Total Records', @@ -55,14 +61,15 @@ export const TEST_NUMBER_CHART_CONFIG = { }; export const TEST_NUMBER_CHART_CONFIG_MINIMAL = { - graphType: GraphType.AGGREGATE, + configurationType: WidgetConfigurationType.AGGREGATE_CHART, aggregateFieldMetadataId: TEST_FIELD_METADATA_ID_1, aggregateOperation: AggregateOperations.SUM, displayDataLabel: false, }; export const TEST_VERTICAL_BAR_CHART_CONFIG = { - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, + layout: BarChartLayout.VERTICAL, aggregateFieldMetadataId: TEST_FIELD_METADATA_ID_1, aggregateOperation: AggregateOperations.SUM, primaryAxisGroupByFieldMetadataId: TEST_FIELD_METADATA_ID_2, @@ -77,7 +84,8 @@ export const TEST_VERTICAL_BAR_CHART_CONFIG = { }; export const TEST_VERTICAL_BAR_CHART_CONFIG_MINIMAL = { - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, + layout: BarChartLayout.VERTICAL, aggregateFieldMetadataId: TEST_FIELD_METADATA_ID_1, aggregateOperation: AggregateOperations.COUNT, primaryAxisGroupByFieldMetadataId: TEST_FIELD_METADATA_ID_2, @@ -87,7 +95,8 @@ export const TEST_VERTICAL_BAR_CHART_CONFIG_MINIMAL = { }; export const TEST_HORIZONTAL_BAR_CHART_CONFIG = { - graphType: GraphType.HORIZONTAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, + layout: BarChartLayout.HORIZONTAL, aggregateFieldMetadataId: TEST_FIELD_METADATA_ID_1, aggregateOperation: AggregateOperations.SUM, primaryAxisGroupByFieldMetadataId: TEST_FIELD_METADATA_ID_2, @@ -102,7 +111,8 @@ export const TEST_HORIZONTAL_BAR_CHART_CONFIG = { }; export const TEST_HORIZONTAL_BAR_CHART_CONFIG_MINIMAL = { - graphType: GraphType.HORIZONTAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, + layout: BarChartLayout.HORIZONTAL, aggregateFieldMetadataId: TEST_FIELD_METADATA_ID_1, aggregateOperation: AggregateOperations.COUNT, primaryAxisGroupByFieldMetadataId: TEST_FIELD_METADATA_ID_2, @@ -112,7 +122,7 @@ export const TEST_HORIZONTAL_BAR_CHART_CONFIG_MINIMAL = { }; export const TEST_LINE_CHART_CONFIG = { - graphType: GraphType.LINE, + configurationType: WidgetConfigurationType.LINE_CHART, aggregateFieldMetadataId: TEST_FIELD_METADATA_ID_1, aggregateOperation: AggregateOperations.AVG, primaryAxisGroupByFieldMetadataId: TEST_FIELD_METADATA_ID_2, @@ -129,7 +139,7 @@ export const TEST_LINE_CHART_CONFIG = { }; export const TEST_LINE_CHART_CONFIG_MINIMAL = { - graphType: GraphType.LINE, + configurationType: WidgetConfigurationType.LINE_CHART, aggregateFieldMetadataId: TEST_FIELD_METADATA_ID_1, aggregateOperation: AggregateOperations.MAX, primaryAxisGroupByFieldMetadataId: TEST_FIELD_METADATA_ID_2, @@ -139,7 +149,7 @@ export const TEST_LINE_CHART_CONFIG_MINIMAL = { }; export const TEST_PIE_CHART_CONFIG = { - graphType: GraphType.PIE, + configurationType: WidgetConfigurationType.PIE_CHART, groupByFieldMetadataId: TEST_FIELD_METADATA_ID_1, aggregateFieldMetadataId: TEST_FIELD_METADATA_ID_2, aggregateOperation: AggregateOperations.SUM, @@ -161,7 +171,7 @@ export const TEST_PIE_CHART_CONFIG = { }; export const TEST_PIE_CHART_CONFIG_MINIMAL = { - graphType: GraphType.PIE, + configurationType: WidgetConfigurationType.PIE_CHART, groupByFieldMetadataId: TEST_FIELD_METADATA_ID_1, aggregateFieldMetadataId: TEST_FIELD_METADATA_ID_2, aggregateOperation: AggregateOperations.COUNT, @@ -170,7 +180,7 @@ export const TEST_PIE_CHART_CONFIG_MINIMAL = { }; export const TEST_GAUGE_CHART_CONFIG = { - graphType: GraphType.GAUGE, + configurationType: WidgetConfigurationType.GAUGE_CHART, aggregateFieldMetadataId: TEST_FIELD_METADATA_ID_1, aggregateOperation: AggregateOperations.SUM, description: 'Completion percentage', @@ -187,7 +197,7 @@ export const TEST_GAUGE_CHART_CONFIG = { }; export const TEST_GAUGE_CHART_CONFIG_MINIMAL = { - graphType: GraphType.GAUGE, + configurationType: WidgetConfigurationType.GAUGE_CHART, aggregateFieldMetadataId: TEST_FIELD_METADATA_ID_1, aggregateOperation: AggregateOperations.COUNT_TRUE, displayDataLabel: false, @@ -202,29 +212,29 @@ export const INVALID_IFRAME_CONFIG_EMPTY_URL = { }; export const INVALID_NUMBER_CHART_CONFIG_MISSING_FIELDS = { - graphType: GraphType.AGGREGATE, + configurationType: WidgetConfigurationType.AGGREGATE_CHART, }; export const INVALID_NUMBER_CHART_CONFIG_BAD_UUID = { - graphType: GraphType.AGGREGATE, + configurationType: WidgetConfigurationType.AGGREGATE_CHART, aggregateFieldMetadataId: 'not-a-uuid', aggregateOperation: AggregateOperations.COUNT, }; export const INVALID_NUMBER_CHART_CONFIG_INVALID_OPERATION = { - graphType: GraphType.AGGREGATE, + configurationType: WidgetConfigurationType.AGGREGATE_CHART, aggregateFieldMetadataId: TEST_FIELD_METADATA_ID_1, aggregateOperation: 'INVALID_OP' as any, }; export const INVALID_VERTICAL_BAR_CHART_CONFIG_MISSING_GROUP_BY = { - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, aggregateFieldMetadataId: TEST_FIELD_METADATA_ID_1, aggregateOperation: AggregateOperations.SUM, }; export const INVALID_VERTICAL_BAR_CHART_CONFIG_BAD_ORDER_BY = { - graphType: GraphType.VERTICAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, aggregateFieldMetadataId: TEST_FIELD_METADATA_ID_1, aggregateOperation: AggregateOperations.SUM, primaryAxisGroupByFieldMetadataId: TEST_FIELD_METADATA_ID_2, @@ -232,13 +242,13 @@ export const INVALID_VERTICAL_BAR_CHART_CONFIG_BAD_ORDER_BY = { }; export const INVALID_HORIZONTAL_BAR_CHART_CONFIG_MISSING_GROUP_BY = { - graphType: GraphType.HORIZONTAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, aggregateFieldMetadataId: TEST_FIELD_METADATA_ID_1, aggregateOperation: AggregateOperations.SUM, }; export const INVALID_HORIZONTAL_BAR_CHART_CONFIG_BAD_ORDER_BY = { - graphType: GraphType.HORIZONTAL_BAR, + configurationType: WidgetConfigurationType.BAR_CHART, aggregateFieldMetadataId: TEST_FIELD_METADATA_ID_1, aggregateOperation: AggregateOperations.SUM, primaryAxisGroupByFieldMetadataId: TEST_FIELD_METADATA_ID_2, @@ -246,7 +256,7 @@ export const INVALID_HORIZONTAL_BAR_CHART_CONFIG_BAD_ORDER_BY = { }; export const CONFIG_TYPE_MISMATCH_IFRAME_WITH_GRAPH = { - graphType: GraphType.AGGREGATE, + configurationType: WidgetConfigurationType.AGGREGATE_CHART, url: 'https://example.com', }; diff --git a/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/__snapshots__/successful-page-layout-widget-creation.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/__snapshots__/successful-page-layout-widget-creation.integration-spec.ts.snap index 91d73f263d..422d2cade3 100644 --- a/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/__snapshots__/successful-page-layout-widget-creation.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/__snapshots__/successful-page-layout-widget-creation.integration-spec.ts.snap @@ -2,7 +2,10 @@ exports[`Page layout widget creation should succeed should create a page layout widget 1`] = ` { - "configuration": null, + "configuration": { + "configurationType": "IFRAME", + "url": null, + }, "createdAt": Any, "deletedAt": null, "gridPosition": { @@ -15,14 +18,17 @@ exports[`Page layout widget creation should succeed should create a page layout "objectMetadataId": null, "pageLayoutTabId": Any, "title": "Test Widget", - "type": "VIEW", + "type": "IFRAME", "updatedAt": Any, } `; exports[`Page layout widget creation should succeed should create a page layout widget with specific type 1`] = ` { - "configuration": null, + "configuration": { + "configurationType": "IFRAME", + "url": "https://example.com", + }, "createdAt": Any, "deletedAt": null, "gridPosition": { @@ -34,8 +40,8 @@ exports[`Page layout widget creation should succeed should create a page layout "id": Any, "objectMetadataId": null, "pageLayoutTabId": Any, - "title": "Graph Widget", - "type": "GRAPH", + "title": "Iframe Widget", + "type": "IFRAME", "updatedAt": Any, } `; diff --git a/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/__snapshots__/successful-page-layout-widget-deletion.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/__snapshots__/successful-page-layout-widget-deletion.integration-spec.ts.snap index f191d4b46b..73eb821b88 100644 --- a/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/__snapshots__/successful-page-layout-widget-deletion.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/__snapshots__/successful-page-layout-widget-deletion.integration-spec.ts.snap @@ -2,7 +2,10 @@ exports[`Page layout widget deletion should succeed should soft delete and restore a page layout widget 1`] = ` { - "configuration": null, + "configuration": { + "configurationType": "IFRAME", + "url": null, + }, "createdAt": Any, "deletedAt": Any, "gridPosition": { @@ -15,14 +18,17 @@ exports[`Page layout widget deletion should succeed should soft delete and resto "objectMetadataId": null, "pageLayoutTabId": Any, "title": "Widget To Delete", - "type": "VIEW", + "type": "IFRAME", "updatedAt": Any, } `; exports[`Page layout widget deletion should succeed should soft delete and restore a page layout widget 2`] = ` { - "configuration": null, + "configuration": { + "configurationType": "IFRAME", + "url": null, + }, "createdAt": Any, "deletedAt": null, "gridPosition": { @@ -35,7 +41,7 @@ exports[`Page layout widget deletion should succeed should soft delete and resto "objectMetadataId": null, "pageLayoutTabId": Any, "title": "Widget To Delete", - "type": "VIEW", + "type": "IFRAME", "updatedAt": Any, } `; diff --git a/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/__snapshots__/successful-page-layout-widget-update.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/__snapshots__/successful-page-layout-widget-update.integration-spec.ts.snap index c7dd3dc9f7..0e49c0d12c 100644 --- a/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/__snapshots__/successful-page-layout-widget-update.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/__snapshots__/successful-page-layout-widget-update.integration-spec.ts.snap @@ -2,7 +2,10 @@ exports[`Page layout widget update should succeed should update page layout widget grid position 1`] = ` { - "configuration": null, + "configuration": { + "configurationType": "IFRAME", + "url": null, + }, "createdAt": Any, "deletedAt": null, "gridPosition": { @@ -15,14 +18,17 @@ exports[`Page layout widget update should succeed should update page layout widg "objectMetadataId": null, "pageLayoutTabId": Any, "title": "Original Widget Title", - "type": "VIEW", + "type": "IFRAME", "updatedAt": Any, } `; exports[`Page layout widget update should succeed should update page layout widget title 1`] = ` { - "configuration": null, + "configuration": { + "configurationType": "IFRAME", + "url": null, + }, "createdAt": Any, "deletedAt": null, "gridPosition": { @@ -35,14 +41,17 @@ exports[`Page layout widget update should succeed should update page layout widg "objectMetadataId": null, "pageLayoutTabId": Any, "title": "Updated Widget Title", - "type": "VIEW", + "type": "IFRAME", "updatedAt": Any, } `; exports[`Page layout widget update should succeed should update page layout widget type 1`] = ` { - "configuration": null, + "configuration": { + "configurationType": "IFRAME", + "url": null, + }, "createdAt": Any, "deletedAt": null, "gridPosition": { diff --git a/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/constants/widget-configuration-gql-fields.constant.ts b/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/constants/widget-configuration-gql-fields.constant.ts index d67e921031..acd48b9465 100644 --- a/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/constants/widget-configuration-gql-fields.constant.ts +++ b/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/constants/widget-configuration-gql-fields.constant.ts @@ -1,6 +1,6 @@ export const WIDGET_CONFIGURATION_GQL_FIELDS = ` ... on BarChartConfiguration { - graphType + configurationType aggregateFieldMetadataId aggregateOperation primaryAxisGroupByFieldMetadataId @@ -21,12 +21,13 @@ export const WIDGET_CONFIGURATION_GQL_FIELDS = ` description filter groupMode + layout isCumulative timezone firstDayOfTheWeek } ... on LineChartConfiguration { - graphType + configurationType aggregateFieldMetadataId aggregateOperation primaryAxisGroupByFieldMetadataId @@ -52,7 +53,7 @@ export const WIDGET_CONFIGURATION_GQL_FIELDS = ` firstDayOfTheWeek } ... on PieChartConfiguration { - graphType + configurationType groupByFieldMetadataId aggregateFieldMetadataId aggregateOperation @@ -69,7 +70,7 @@ export const WIDGET_CONFIGURATION_GQL_FIELDS = ` firstDayOfTheWeek } ... on AggregateChartConfiguration { - graphType + configurationType aggregateFieldMetadataId aggregateOperation label @@ -83,7 +84,7 @@ export const WIDGET_CONFIGURATION_GQL_FIELDS = ` firstDayOfTheWeek } ... on GaugeChartConfiguration { - graphType + configurationType aggregateFieldMetadataId aggregateOperation displayDataLabel @@ -94,9 +95,11 @@ export const WIDGET_CONFIGURATION_GQL_FIELDS = ` firstDayOfTheWeek } ... on IframeConfiguration { + configurationType url } ... on StandaloneRichTextConfiguration { + configurationType body { blocknote markdown diff --git a/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/failing-page-layout-widget-creation.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/failing-page-layout-widget-creation.integration-spec.ts index 1d1fbf1124..865b9fb69e 100644 --- a/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/failing-page-layout-widget-creation.integration-spec.ts +++ b/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/failing-page-layout-widget-creation.integration-spec.ts @@ -1,4 +1,5 @@ import { faker } from '@faker-js/faker'; +import { TEST_IFRAME_CONFIG } from 'test/integration/constants/widget-configuration-test-data.constants'; import { expectOneNotInternalServerErrorSnapshot } from 'test/integration/graphql/utils/expect-one-not-internal-server-error-snapshot.util'; import { createOnePageLayoutTab } from 'test/integration/metadata/suites/page-layout-tab/utils/create-one-page-layout-tab.util'; import { destroyOnePageLayoutTab } from 'test/integration/metadata/suites/page-layout-tab/utils/destroy-one-page-layout-tab.util'; @@ -7,6 +8,7 @@ import { createOnePageLayout } from 'test/integration/metadata/suites/page-layou import { destroyOnePageLayout } from 'test/integration/metadata/suites/page-layout/utils/destroy-one-page-layout.util'; import { type CreatePageLayoutWidgetInput } from 'src/engine/metadata-modules/page-layout-widget/dtos/inputs/create-page-layout-widget.input'; +import { WidgetType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-type.enum'; describe('Page layout widget creation should fail', () => { let testPageLayoutId: string; @@ -47,12 +49,14 @@ describe('Page layout widget creation should fail', () => { expectToFail: true, input: { pageLayoutTabId: testPageLayoutTabId, + type: WidgetType.IFRAME, gridPosition: { row: 0, column: 0, rowSpan: 1, columnSpan: 1, }, + configuration: TEST_IFRAME_CONFIG, } as CreatePageLayoutWidgetInput, }); @@ -65,6 +69,8 @@ describe('Page layout widget creation should fail', () => { input: { title: 'Widget With Non-Existent Tab', pageLayoutTabId: faker.string.uuid(), + type: WidgetType.IFRAME, + configuration: TEST_IFRAME_CONFIG, gridPosition: { row: 0, column: 0, @@ -83,6 +89,8 @@ describe('Page layout widget creation should fail', () => { input: { title: 'Widget With Invalid Grid Position', pageLayoutTabId: testPageLayoutTabId, + type: WidgetType.IFRAME, + configuration: TEST_IFRAME_CONFIG, gridPosition: { row: -1, column: 0, diff --git a/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/successful-page-layout-widget-creation.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/successful-page-layout-widget-creation.integration-spec.ts index fb29c6bb17..8a65c32695 100644 --- a/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/successful-page-layout-widget-creation.integration-spec.ts +++ b/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/successful-page-layout-widget-creation.integration-spec.ts @@ -10,12 +10,15 @@ import { eachTestingContextFilter, } from 'twenty-shared/testing'; +import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type'; import { WidgetType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-type.enum'; +import { type AllPageLayoutWidgetConfiguration } from 'src/engine/metadata-modules/page-layout-widget/types/all-page-layout-widget-configuration.type'; type TestContext = { input: { title: string; - type?: WidgetType; + type: WidgetType; + configuration: AllPageLayoutWidgetConfiguration; gridPosition: { row: number; column: number; @@ -31,6 +34,10 @@ const SUCCESSFUL_TEST_CASES: EachTestingContext[] = [ context: { input: { title: 'Test Widget', + type: WidgetType.IFRAME, + configuration: { + configurationType: WidgetConfigurationType.IFRAME, + }, gridPosition: { row: 0, column: 0, @@ -44,8 +51,12 @@ const SUCCESSFUL_TEST_CASES: EachTestingContext[] = [ title: 'create a page layout widget with specific type', context: { input: { - title: 'Graph Widget', - type: WidgetType.GRAPH, + title: 'Iframe Widget', + type: WidgetType.IFRAME, + configuration: { + configurationType: WidgetConfigurationType.IFRAME, + url: 'https://example.com', + }, gridPosition: { row: 0, column: 0, diff --git a/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/successful-page-layout-widget-deletion.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/successful-page-layout-widget-deletion.integration-spec.ts index f05b950477..8b3bbbe63b 100644 --- a/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/successful-page-layout-widget-deletion.integration-spec.ts +++ b/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/successful-page-layout-widget-deletion.integration-spec.ts @@ -12,6 +12,9 @@ import { eachTestingContextFilter, } from 'twenty-shared/testing'; +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'; + type TestContext = { title: string; operation: 'soft-delete-restore' | 'hard-delete'; @@ -76,6 +79,10 @@ describe('Page layout widget deletion should succeed', () => { input: { title, pageLayoutTabId: testPageLayoutTabId, + type: WidgetType.IFRAME, + configuration: { + configurationType: WidgetConfigurationType.IFRAME, + }, gridPosition: { row: 0, column: 0, diff --git a/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/successful-page-layout-widget-update.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/successful-page-layout-widget-update.integration-spec.ts index 4076c9d03e..7623248f83 100644 --- a/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/successful-page-layout-widget-update.integration-spec.ts +++ b/packages/twenty-server/test/integration/metadata/suites/page-layout-widget/successful-page-layout-widget-update.integration-spec.ts @@ -11,6 +11,7 @@ import { eachTestingContextFilter, } from 'twenty-shared/testing'; +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'; type TestContext = { @@ -99,6 +100,10 @@ describe('Page layout widget update should succeed', () => { input: { title: 'Original Widget Title', pageLayoutTabId: testPageLayoutTabId, + type: WidgetType.IFRAME, + configuration: { + configurationType: WidgetConfigurationType.IFRAME, + }, gridPosition: { row: 0, column: 0, diff --git a/packages/twenty-server/test/integration/metadata/suites/page-layout/__snapshots__/successful-page-layout-with-tabs-update.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/page-layout/__snapshots__/successful-page-layout-with-tabs-update.integration-spec.ts.snap index fa55e8481d..684ab827e3 100644 --- a/packages/twenty-server/test/integration/metadata/suites/page-layout/__snapshots__/successful-page-layout-with-tabs-update.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/page-layout/__snapshots__/successful-page-layout-with-tabs-update.integration-spec.ts.snap @@ -22,13 +22,13 @@ exports[`Page layout with tabs update should succeed should update page layout w "aggregateFieldMetadataId": Any, "aggregateOperation": "COUNT", "color": null, + "configurationType": "PIE_CHART", "dateGranularity": "DAY", "description": null, "displayDataLabel": false, "displayLegend": true, "filter": null, "firstDayOfTheWeek": 1, - "graphType": "PIE", "groupByFieldMetadataId": Any, "groupBySubFieldName": null, "orderBy": "VALUE_DESC", @@ -63,6 +63,7 @@ exports[`Page layout with tabs update should succeed should update page layout w "widgets": [ { "configuration": { + "configurationType": "IFRAME", "url": "https://example.com", }, "createdAt": Any, @@ -110,13 +111,13 @@ exports[`Page layout with tabs update should succeed should update page layout w "aggregateFieldMetadataId": Any, "aggregateOperation": "COUNT", "color": null, + "configurationType": "PIE_CHART", "dateGranularity": "DAY", "description": null, "displayDataLabel": false, "displayLegend": true, "filter": null, "firstDayOfTheWeek": 1, - "graphType": "PIE", "groupByFieldMetadataId": Any, "groupBySubFieldName": null, "orderBy": "VALUE_DESC", diff --git a/packages/twenty-server/test/integration/metadata/suites/page-layout/failing-page-layout-with-tabs-update.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/page-layout/failing-page-layout-with-tabs-update.integration-spec.ts index 3883ef8fa4..d598c9f60f 100644 --- a/packages/twenty-server/test/integration/metadata/suites/page-layout/failing-page-layout-with-tabs-update.integration-spec.ts +++ b/packages/twenty-server/test/integration/metadata/suites/page-layout/failing-page-layout-with-tabs-update.integration-spec.ts @@ -1,8 +1,9 @@ import { expectOneNotInternalServerErrorSnapshot } from 'test/integration/graphql/utils/expect-one-not-internal-server-error-snapshot.util'; import { updateOnePageLayoutWithTabsAndWidgets } from 'test/integration/metadata/suites/page-layout/utils/update-one-page-layout-with-tabs-and-widgets.util'; -import { PageLayoutType } from 'src/engine/metadata-modules/page-layout/enums/page-layout-type.enum'; +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 { PageLayoutType } from 'src/engine/metadata-modules/page-layout/enums/page-layout-type.enum'; describe('Page layout with tabs update should fail', () => { it('when updating a non-existent page layout', async () => { @@ -29,7 +30,9 @@ describe('Page layout with tabs update should fail', () => { type: WidgetType.FIELDS, objectMetadataId: null, gridPosition: { row: 0, column: 0, rowSpan: 1, columnSpan: 1 }, - configuration: null, + configuration: { + configurationType: WidgetConfigurationType.IFRAME, + }, }, ], }, diff --git a/packages/twenty-server/test/integration/metadata/suites/page-layout/successful-page-layout-with-tabs-update.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/page-layout/successful-page-layout-with-tabs-update.integration-spec.ts index 729f0d1871..6d36200f9c 100644 --- a/packages/twenty-server/test/integration/metadata/suites/page-layout/successful-page-layout-with-tabs-update.integration-spec.ts +++ b/packages/twenty-server/test/integration/metadata/suites/page-layout/successful-page-layout-with-tabs-update.integration-spec.ts @@ -11,20 +11,22 @@ import { import { v4 } from 'uuid'; import { AggregateOperations } from 'src/engine/api/graphql/graphql-query-runner/constants/aggregate-operations.constant'; -import { GraphType } from 'src/engine/metadata-modules/page-layout-widget/enums/graph-type.enum'; +import { WidgetConfigurationType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-configuration-type.type'; import { WidgetType } from 'src/engine/metadata-modules/page-layout-widget/enums/widget-type.enum'; +import { type AllPageLayoutWidgetConfiguration } from 'src/engine/metadata-modules/page-layout-widget/types/all-page-layout-widget-configuration.type'; import { PageLayoutType } from 'src/engine/metadata-modules/page-layout/enums/page-layout-type.enum'; const MOCK_PIE_CHART_CONFIGURATION = { - graphType: GraphType.PIE, + configurationType: WidgetConfigurationType.PIE_CHART, aggregateFieldMetadataId: '20202020-77d3-42ff-adc6-cffdad2792c7', aggregateOperation: AggregateOperations.COUNT, groupByFieldMetadataId: '20202020-a9fd-4071-9082-db4870ed2430', -}; +} as const satisfies AllPageLayoutWidgetConfiguration; const MOCK_IFRAME_CONFIGURATION = { + configurationType: WidgetConfigurationType.IFRAME, url: 'https://example.com', -}; +} as const satisfies AllPageLayoutWidgetConfiguration; type TestContext = { layoutName: string; @@ -49,9 +51,7 @@ type TestContext = { rowSpan: number; columnSpan: number; }; - configuration: - | typeof MOCK_PIE_CHART_CONFIGURATION - | typeof MOCK_IFRAME_CONFIGURATION; + configuration: AllPageLayoutWidgetConfiguration; }>; }>; };