From 5fec6a9afce779405069378f9e55bd19e11b4821 Mon Sep 17 00:00:00 2001 From: nitin <142569587+ehconitin@users.noreply.github.com> Date: Tue, 14 Oct 2025 18:36:50 +0530 Subject: [PATCH] Fix group by y axis "stacked" behaviour (#15081) also improved seeds --- .../src/generated-metadata/graphql.ts | 7 ++++ .../twenty-front/src/generated/graphql.ts | 12 +++++- .../page-layout/components/ChartSettings.tsx | 37 +++++++++++++++---- .../page-layout/constants/BarChartSettings.ts | 8 +++- .../ChartConfigurationSettingLabels.ts | 1 + .../constants/settings/StackedBarsSetting.ts | 12 ++++++ .../hooks/useChartSettingsValues.ts | 4 ++ .../types/ChartConfigurationSettingIds.ts | 2 + .../fragments/pageLayoutWidgetFragment.ts | 1 + .../GraphWidgetBarChartRenderer.tsx | 5 +++ .../dtos/bar-chart-configuration.dto.ts | 9 +++++ .../enums/bar-chart-group-mode.enum.ts | 11 ++++++ .../get-page-layout-widget-data-seeds.util.ts | 27 +++++++++----- .../display/icon/components/TablerIcons.ts | 1 + packages/twenty-ui/src/display/index.ts | 1 + 15 files changed, 118 insertions(+), 20 deletions(-) create mode 100644 packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/StackedBarsSetting.ts create mode 100644 packages/twenty-server/src/engine/core-modules/page-layout/enums/bar-chart-group-mode.enum.ts diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 0b11eb1685..0b23d36e0d 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -291,6 +291,7 @@ export type BarChartConfiguration = { groupByFieldMetadataIdY?: Maybe; groupBySubFieldNameX?: Maybe; groupBySubFieldNameY?: Maybe; + groupMode?: Maybe; omitNullValues?: Maybe; orderByX?: Maybe; orderByY?: Maybe; @@ -298,6 +299,12 @@ export type BarChartConfiguration = { rangeMin?: Maybe; }; +/** Display mode for bar charts with secondary grouping */ +export enum BarChartGroupMode { + GROUPED = 'GROUPED', + STACKED = 'STACKED' +} + export type Billing = { __typename?: 'Billing'; billingUrl?: Maybe; diff --git a/packages/twenty-front/src/generated/graphql.ts b/packages/twenty-front/src/generated/graphql.ts index a5bd78405e..78bf5f1a8d 100644 --- a/packages/twenty-front/src/generated/graphql.ts +++ b/packages/twenty-front/src/generated/graphql.ts @@ -291,6 +291,7 @@ export type BarChartConfiguration = { groupByFieldMetadataIdY?: Maybe; groupBySubFieldNameX?: Maybe; groupBySubFieldNameY?: Maybe; + groupMode?: Maybe; omitNullValues?: Maybe; orderByX?: Maybe; orderByY?: Maybe; @@ -298,6 +299,12 @@ export type BarChartConfiguration = { rangeMin?: Maybe; }; +/** Display mode for bar charts with secondary grouping */ +export enum BarChartGroupMode { + GROUPED = 'GROUPED', + STACKED = 'STACKED' +} + export type Billing = { __typename?: 'Billing'; billingUrl?: Maybe; @@ -4464,7 +4471,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?: 'BarChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: ExtendedAggregateOperations, groupByFieldMetadataIdX: any, groupBySubFieldNameX?: string | null, orderByX?: GraphOrderBy | null, groupByFieldMetadataIdY?: any | null, groupBySubFieldNameY?: string | null, orderByY?: GraphOrderBy | null, omitNullValues?: boolean | null, axisNameDisplay?: AxisNameDisplay | null, displayDataLabel?: boolean | null, rangeMin?: number | null, rangeMax?: number | null, color?: string | null, description?: string | null, filter?: any | null } | { __typename?: 'GaugeChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: ExtendedAggregateOperations, displayDataLabel?: boolean | null, color?: string | null, description?: string | null, filter?: any | null } | { __typename?: 'IframeConfiguration', url: string } | { __typename?: 'LineChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: ExtendedAggregateOperations, groupByFieldMetadataIdX: any, groupBySubFieldNameX?: string | null, orderByX?: GraphOrderBy | null, groupByFieldMetadataIdY?: any | null, groupBySubFieldNameY?: string | null, orderByY?: GraphOrderBy | null, omitNullValues?: boolean | null, axisNameDisplay?: AxisNameDisplay | null, displayDataLabel?: boolean | null, rangeMin?: number | null, rangeMax?: number | null, color?: string | null, description?: string | null, filter?: any | null } | { __typename?: 'NumberChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: ExtendedAggregateOperations, displayDataLabel?: boolean | null, description?: string | null, filter?: any | null } | { __typename?: 'PieChartConfiguration', graphType: GraphType, groupByFieldMetadataId: any, aggregateFieldMetadataId: any, aggregateOperation: ExtendedAggregateOperations, groupBySubFieldName?: string | null, orderBy?: GraphOrderBy | null, displayDataLabel?: boolean | null, color?: string | null, description?: string | null, filter?: any | 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?: 'BarChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: ExtendedAggregateOperations, groupByFieldMetadataIdX: any, groupBySubFieldNameX?: string | null, orderByX?: GraphOrderBy | null, groupByFieldMetadataIdY?: any | null, groupBySubFieldNameY?: string | null, orderByY?: GraphOrderBy | null, omitNullValues?: boolean | null, axisNameDisplay?: AxisNameDisplay | null, displayDataLabel?: boolean | null, rangeMin?: number | null, rangeMax?: number | null, color?: string | null, description?: string | null, filter?: any | null, groupMode?: BarChartGroupMode | null } | { __typename?: 'GaugeChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: ExtendedAggregateOperations, displayDataLabel?: boolean | null, color?: string | null, description?: string | null, filter?: any | null } | { __typename?: 'IframeConfiguration', url: string } | { __typename?: 'LineChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: ExtendedAggregateOperations, groupByFieldMetadataIdX: any, groupBySubFieldNameX?: string | null, orderByX?: GraphOrderBy | null, groupByFieldMetadataIdY?: any | null, groupBySubFieldNameY?: string | null, orderByY?: GraphOrderBy | null, omitNullValues?: boolean | null, axisNameDisplay?: AxisNameDisplay | null, displayDataLabel?: boolean | null, rangeMin?: number | null, rangeMax?: number | null, color?: string | null, description?: string | null, filter?: any | null } | { __typename?: 'NumberChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: ExtendedAggregateOperations, displayDataLabel?: boolean | null, description?: string | null, filter?: any | null } | { __typename?: 'PieChartConfiguration', graphType: GraphType, groupByFieldMetadataId: any, aggregateFieldMetadataId: any, aggregateOperation: ExtendedAggregateOperations, groupBySubFieldName?: string | null, orderBy?: GraphOrderBy | null, displayDataLabel?: boolean | null, color?: string | null, description?: string | null, filter?: any | null } | null }; export type UpdatePageLayoutWithTabsAndWidgetsMutationVariables = Exact<{ id: Scalars['String']; @@ -4472,7 +4479,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?: 'BarChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: ExtendedAggregateOperations, groupByFieldMetadataIdX: any, groupBySubFieldNameX?: string | null, orderByX?: GraphOrderBy | null, groupByFieldMetadataIdY?: any | null, groupBySubFieldNameY?: string | null, orderByY?: GraphOrderBy | null, omitNullValues?: boolean | null, axisNameDisplay?: AxisNameDisplay | null, displayDataLabel?: boolean | null, rangeMin?: number | null, rangeMax?: number | null, color?: string | null, description?: string | null, filter?: any | null } | { __typename?: 'GaugeChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: ExtendedAggregateOperations, displayDataLabel?: boolean | null, color?: string | null, description?: string | null, filter?: any | null } | { __typename?: 'IframeConfiguration', url: string } | { __typename?: 'LineChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: ExtendedAggregateOperations, groupByFieldMetadataIdX: any, groupBySubFieldNameX?: string | null, orderByX?: GraphOrderBy | null, groupByFieldMetadataIdY?: any | null, groupBySubFieldNameY?: string | null, orderByY?: GraphOrderBy | null, omitNullValues?: boolean | null, axisNameDisplay?: AxisNameDisplay | null, displayDataLabel?: boolean | null, rangeMin?: number | null, rangeMax?: number | null, color?: string | null, description?: string | null, filter?: any | null } | { __typename?: 'NumberChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: ExtendedAggregateOperations, displayDataLabel?: boolean | null, description?: string | null, filter?: any | null } | { __typename?: 'PieChartConfiguration', graphType: GraphType, groupByFieldMetadataId: any, aggregateFieldMetadataId: any, aggregateOperation: ExtendedAggregateOperations, groupBySubFieldName?: string | null, orderBy?: GraphOrderBy | null, displayDataLabel?: boolean | null, color?: string | null, description?: string | null, filter?: any | 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?: 'BarChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: ExtendedAggregateOperations, groupByFieldMetadataIdX: any, groupBySubFieldNameX?: string | null, orderByX?: GraphOrderBy | null, groupByFieldMetadataIdY?: any | null, groupBySubFieldNameY?: string | null, orderByY?: GraphOrderBy | null, omitNullValues?: boolean | null, axisNameDisplay?: AxisNameDisplay | null, displayDataLabel?: boolean | null, rangeMin?: number | null, rangeMax?: number | null, color?: string | null, description?: string | null, filter?: any | null, groupMode?: BarChartGroupMode | null } | { __typename?: 'GaugeChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: ExtendedAggregateOperations, displayDataLabel?: boolean | null, color?: string | null, description?: string | null, filter?: any | null } | { __typename?: 'IframeConfiguration', url: string } | { __typename?: 'LineChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: ExtendedAggregateOperations, groupByFieldMetadataIdX: any, groupBySubFieldNameX?: string | null, orderByX?: GraphOrderBy | null, groupByFieldMetadataIdY?: any | null, groupBySubFieldNameY?: string | null, orderByY?: GraphOrderBy | null, omitNullValues?: boolean | null, axisNameDisplay?: AxisNameDisplay | null, displayDataLabel?: boolean | null, rangeMin?: number | null, rangeMax?: number | null, color?: string | null, description?: string | null, filter?: any | null } | { __typename?: 'NumberChartConfiguration', graphType: GraphType, aggregateFieldMetadataId: any, aggregateOperation: ExtendedAggregateOperations, displayDataLabel?: boolean | null, description?: string | null, filter?: any | null } | { __typename?: 'PieChartConfiguration', graphType: GraphType, groupByFieldMetadataId: any, aggregateFieldMetadataId: any, aggregateOperation: ExtendedAggregateOperations, groupBySubFieldName?: string | null, orderBy?: GraphOrderBy | null, displayDataLabel?: boolean | null, color?: string | null, description?: string | null, filter?: any | null } | null }> | null }> | null } }; export type OnDbEventSubscriptionVariables = Exact<{ input: OnDbEventInput; @@ -4788,6 +4795,7 @@ export const PageLayoutWidgetFragmentFragmentDoc = gql` color description filter + groupMode } ... on LineChartConfiguration { graphType diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/ChartSettings.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/ChartSettings.tsx index 4245e57d61..5c51809bba 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/ChartSettings.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/ChartSettings.tsx @@ -22,7 +22,11 @@ import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectab import { t } from '@lingui/core/macro'; import { isNonEmptyString } from '@sniptt/guards'; -import { type GraphType, type PageLayoutWidget } from '~/generated/graphql'; +import { + BarChartGroupMode, + type GraphType, + type PageLayoutWidget, +} from '~/generated/graphql'; export const ChartSettings = ({ widget }: { widget: PageLayoutWidget }) => { const { updateCommandMenuPageInfo } = useUpdateCommandMenuPageInfo(); @@ -101,15 +105,34 @@ export const ChartSettings = ({ widget }: { widget: PageLayoutWidget }) => { ? CHART_CONFIGURATION_SETTING_TO_CONFIG_KEY_MAP[ CHART_CONFIGURATION_SETTING_IDS.DATA_LABELS ] - : item.id; + : item.id === CHART_CONFIGURATION_SETTING_IDS.STACKED_BARS + ? CHART_CONFIGURATION_SETTING_TO_CONFIG_KEY_MAP[ + CHART_CONFIGURATION_SETTING_IDS.STACKED_BARS + ] + : item.id; setSelectedItemId(item.id); - updateCurrentWidgetConfig({ - configToUpdate: { - [configKey]: !getChartSettingsValues(item.id), - }, - }); + if (item.id === CHART_CONFIGURATION_SETTING_IDS.STACKED_BARS) { + const isCurrentlyStacked = getChartSettingsValues(item.id); + const newGroupMode = isCurrentlyStacked + ? BarChartGroupMode.GROUPED + : BarChartGroupMode.STACKED; + + updateCurrentWidgetConfig({ + configToUpdate: { + groupMode: newGroupMode, + }, + }); + } else { + const newValue = !getChartSettingsValues(item.id); + + updateCurrentWidgetConfig({ + configToUpdate: { + [configKey]: newValue, + }, + }); + } }; const handleDropdownOpen = () => { diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/BarChartSettings.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/BarChartSettings.ts index d79a3a7206..9e951200d8 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/BarChartSettings.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/BarChartSettings.ts @@ -8,6 +8,7 @@ import { FILTER_SETTING } from '@/command-menu/pages/page-layout/constants/setti import { GROUP_BY_SETTING } from '@/command-menu/pages/page-layout/constants/settings/GroupBySetting'; import { SORT_BY_GROUP_BY_FIELD_SETTING } from '@/command-menu/pages/page-layout/constants/settings/SortByGroupByFieldSetting'; import { SORT_BY_X_SETTING } from '@/command-menu/pages/page-layout/constants/settings/SortByXSetting'; +import { STACKED_BARS_SETTING } from '@/command-menu/pages/page-layout/constants/settings/StackedBarsSetting'; import { type ChartSettingsGroup } from '@/command-menu/pages/page-layout/types/ChartSettingsGroup'; export const BAR_CHART_SETTINGS: ChartSettingsGroup[] = [ @@ -29,6 +30,11 @@ export const BAR_CHART_SETTINGS: ChartSettingsGroup[] = [ }, { heading: 'Style', - items: [COLORS_SETTING, AXIS_NAME_SETTING, DATA_LABELS_SETTING], + items: [ + COLORS_SETTING, + AXIS_NAME_SETTING, + STACKED_BARS_SETTING, + DATA_LABELS_SETTING, + ], }, ]; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/ChartConfigurationSettingLabels.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/ChartConfigurationSettingLabels.ts index b3b606f3f4..cba4f8c376 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/ChartConfigurationSettingLabels.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/ChartConfigurationSettingLabels.ts @@ -13,4 +13,5 @@ export const CHART_CONFIGURATION_SETTING_LABELS = { DATA_ON_DISPLAY_PIE_CHART: msg`Data on display`, EACH_SLICE_REPRESENTS: msg`Each slice represents`, AXIS_NAME: msg`Axis name`, + STACKED_BARS: msg`Stacked bars`, }; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/StackedBarsSetting.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/StackedBarsSetting.ts new file mode 100644 index 0000000000..2d886cafa1 --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/StackedBarsSetting.ts @@ -0,0 +1,12 @@ +import { CHART_CONFIGURATION_SETTING_LABELS } from '@/command-menu/pages/page-layout/constants/settings/ChartConfigurationSettingLabels'; +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 { IconStack2 } from 'twenty-ui/display'; + +export const STACKED_BARS_SETTING: ChartSettingsItem = { + isBoolean: true, + Icon: IconStack2, + label: CHART_CONFIGURATION_SETTING_LABELS.STACKED_BARS, + id: CHART_CONFIGURATION_SETTING_IDS.STACKED_BARS, + dependsOn: [CHART_CONFIGURATION_SETTING_IDS.GROUP_BY], +}; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts index 0d193fe26b..7931607475 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/hooks/useChartSettingsValues.ts @@ -144,6 +144,10 @@ export const useChartSettingsValues = ({ return groupByOrderByLabel; case CHART_CONFIGURATION_SETTING_IDS.DATA_LABELS: return configuration.displayDataLabel ?? undefined; + case CHART_CONFIGURATION_SETTING_IDS.STACKED_BARS: + return 'groupMode' in configuration + ? configuration.groupMode !== 'GROUPED' + : true; default: return ''; } diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/ChartConfigurationSettingIds.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/ChartConfigurationSettingIds.ts index 5ddd62d3c2..4290ac1b22 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/ChartConfigurationSettingIds.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/ChartConfigurationSettingIds.ts @@ -12,8 +12,10 @@ export enum CHART_CONFIGURATION_SETTING_IDS { DATA_ON_DISPLAY_PIE_CHART = 'DATA_ON_DISPLAY_PIE_CHART', EACH_SLICE_REPRESENTS = 'EACH_SLICE_REPRESENTS', AXIS_NAME = 'AXIS_NAME', + STACKED_BARS = 'STACKED_BARS', } export const CHART_CONFIGURATION_SETTING_TO_CONFIG_KEY_MAP = { [CHART_CONFIGURATION_SETTING_IDS.DATA_LABELS]: 'displayDataLabel', + [CHART_CONFIGURATION_SETTING_IDS.STACKED_BARS]: 'groupMode', } as const; 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 29afebfc4e..9fd4f90c44 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 @@ -34,6 +34,7 @@ export const PAGE_LAYOUT_WIDGET_FRAGMENT = gql` color description filter + groupMode } ... on LineChartConfiguration { graphType diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/GraphWidgetBarChartRenderer.tsx b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/GraphWidgetBarChartRenderer.tsx index 27478f7bf8..41db1536dc 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/GraphWidgetBarChartRenderer.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetBarChart/components/GraphWidgetBarChartRenderer.tsx @@ -43,6 +43,10 @@ export const GraphWidgetBarChartRenderer = ({ return
Error: {error.message}
; } + const configuration = widget.configuration as BarChartConfiguration; + const groupMode = + configuration.groupMode === 'GROUPED' ? 'grouped' : 'stacked'; + return ( }> diff --git a/packages/twenty-server/src/engine/core-modules/page-layout/dtos/bar-chart-configuration.dto.ts b/packages/twenty-server/src/engine/core-modules/page-layout/dtos/bar-chart-configuration.dto.ts index d994a3d264..d34d08eac1 100644 --- a/packages/twenty-server/src/engine/core-modules/page-layout/dtos/bar-chart-configuration.dto.ts +++ b/packages/twenty-server/src/engine/core-modules/page-layout/dtos/bar-chart-configuration.dto.ts @@ -17,6 +17,7 @@ import { ObjectRecordFilter } from 'src/engine/api/graphql/workspace-query-build import { ExtendedAggregateOperations } from 'src/engine/api/graphql/graphql-query-runner/constants/extended-aggregate-operations.constant'; import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; import { AxisNameDisplay } from 'src/engine/core-modules/page-layout/enums/axis-name-display.enum'; +import { BarChartGroupMode } from 'src/engine/core-modules/page-layout/enums/bar-chart-group-mode.enum'; import { GraphOrderBy } from 'src/engine/core-modules/page-layout/enums/graph-order-by.enum'; import { GraphType } from 'src/engine/core-modules/page-layout/enums/graph-type.enum'; @@ -109,4 +110,12 @@ export class BarChartConfigurationDTO { @IsObject() @IsOptional() filter?: ObjectRecordFilter; + + @Field(() => BarChartGroupMode, { + nullable: true, + defaultValue: BarChartGroupMode.STACKED, + }) + @IsEnum(BarChartGroupMode) + @IsOptional() + groupMode?: BarChartGroupMode; } diff --git a/packages/twenty-server/src/engine/core-modules/page-layout/enums/bar-chart-group-mode.enum.ts b/packages/twenty-server/src/engine/core-modules/page-layout/enums/bar-chart-group-mode.enum.ts new file mode 100644 index 0000000000..f80113fcc9 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/page-layout/enums/bar-chart-group-mode.enum.ts @@ -0,0 +1,11 @@ +import { registerEnumType } from '@nestjs/graphql'; + +export enum BarChartGroupMode { + STACKED = 'STACKED', + GROUPED = 'GROUPED', +} + +registerEnumType(BarChartGroupMode, { + name: 'BarChartGroupMode', + description: 'Display mode for bar charts with secondary grouping', +}); 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 d28b88d9c2..fbce7315d0 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 @@ -64,6 +64,7 @@ export const getPageLayoutWidgetDataSeeds = ( const companyArrFieldId = getFieldId(companyObject, 'annualRecurringRevenue'); const companyNameFieldId = getFieldId(companyObject, 'name'); const companyLinkedinLinkFieldId = getFieldId(companyObject, 'linkedinLink'); + const companyAddressFieldId = getFieldId(companyObject, 'address'); const personIdFieldId = getFieldId(personObject, 'id'); const personCityFieldId = getFieldId(personObject, 'city'); @@ -158,19 +159,21 @@ export const getPageLayoutWidgetDataSeeds = ( workspaceId, PAGE_LAYOUT_TAB_SEEDS.SALES_OVERVIEW, ), - title: 'Deals by Stage', + title: 'Pipeline Value by Close Date (Stacked by Stage)', type: WidgetType.GRAPH, - gridPosition: { row: 4, column: 0, rowSpan: 4, columnSpan: 6 }, + gridPosition: { row: 4, column: 0, rowSpan: 8, columnSpan: 6 }, configuration: isDefined(opportunityAmountFieldId) && + isDefined(opportunityCloseDateFieldId) && isDefined(opportunityStageFieldId) ? { graphType: 'BAR', aggregateFieldMetadataId: opportunityAmountFieldId, aggregateOperation: AggregateOperations.SUM, - groupByFieldMetadataIdX: opportunityStageFieldId, - orderByX: 'FIELD_DESC', - axisNameDisplay: AxisNameDisplay.NONE, + groupByFieldMetadataIdX: opportunityCloseDateFieldId, + groupByFieldMetadataIdY: opportunityStageFieldId, + orderByX: 'FIELD_ASC', + axisNameDisplay: AxisNameDisplay.BOTH, displayDataLabel: false, } : null, @@ -285,18 +288,22 @@ export const getPageLayoutWidgetDataSeeds = ( workspaceId, PAGE_LAYOUT_TAB_SEEDS.CUSTOMER_OVERVIEW, ), - title: 'Companies by Size', + title: 'Companies by Size (Stacked by City)', type: WidgetType.GRAPH, - gridPosition: { row: 0, column: 8, rowSpan: 6, columnSpan: 4 }, + gridPosition: { row: 0, column: 8, rowSpan: 10, columnSpan: 8 }, configuration: - isDefined(companyIdFieldId) && isDefined(companyEmployeesFieldId) + isDefined(companyIdFieldId) && + isDefined(companyEmployeesFieldId) && + isDefined(companyAddressFieldId) ? { graphType: 'BAR', aggregateFieldMetadataId: companyIdFieldId, aggregateOperation: AggregateOperations.COUNT, groupByFieldMetadataIdX: companyEmployeesFieldId, + groupByFieldMetadataIdY: companyAddressFieldId, + groupBySubFieldNameY: 'addressCity', orderByX: 'FIELD_ASC', - axisNameDisplay: AxisNameDisplay.NONE, + axisNameDisplay: AxisNameDisplay.BOTH, displayDataLabel: false, } : null, @@ -460,7 +467,7 @@ export const getPageLayoutWidgetDataSeeds = ( aggregateFieldMetadataId: personIdFieldId, aggregateOperation: AggregateOperations.COUNT, groupByFieldMetadataIdX: personCityFieldId, - orderByX: 'FIELD_DESC', + orderByX: 'VALUE_DESC', axisNameDisplay: AxisNameDisplay.NONE, displayDataLabel: false, } diff --git a/packages/twenty-ui/src/display/icon/components/TablerIcons.ts b/packages/twenty-ui/src/display/icon/components/TablerIcons.ts index 531f8a4b76..a6f62a76ed 100644 --- a/packages/twenty-ui/src/display/icon/components/TablerIcons.ts +++ b/packages/twenty-ui/src/display/icon/components/TablerIcons.ts @@ -315,6 +315,7 @@ export { IconSquareNumber9, IconSquareRoundedCheck, IconSquareRoundedX, + IconStack2, IconStatusChange, IconStepInto, IconStrikethrough, diff --git a/packages/twenty-ui/src/display/index.ts b/packages/twenty-ui/src/display/index.ts index 5f4788e23b..5c7c674f53 100644 --- a/packages/twenty-ui/src/display/index.ts +++ b/packages/twenty-ui/src/display/index.ts @@ -377,6 +377,7 @@ export { IconSquareNumber9, IconSquareRoundedCheck, IconSquareRoundedX, + IconStack2, IconStatusChange, IconStepInto, IconStrikethrough,