diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 3162dae43c..ed181f1c7c 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -3187,6 +3187,7 @@ export type PieChartConfiguration = { graphType: GraphType; groupByFieldMetadataId: Scalars['UUID']; groupBySubFieldName?: Maybe; + hideEmptyCategory?: Maybe; orderBy?: Maybe; showCenterMetric?: Maybe; timezone?: Maybe; diff --git a/packages/twenty-front/src/generated/graphql.ts b/packages/twenty-front/src/generated/graphql.ts index 8918532ad1..175fa6e7e5 100644 --- a/packages/twenty-front/src/generated/graphql.ts +++ b/packages/twenty-front/src/generated/graphql.ts @@ -3108,6 +3108,7 @@ export type PieChartConfiguration = { graphType: GraphType; groupByFieldMetadataId: Scalars['UUID']; groupBySubFieldName?: Maybe; + hideEmptyCategory?: Maybe; orderBy?: Maybe; showCenterMetric?: Maybe; timezone?: Maybe; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/PieChartSettings.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/PieChartSettings.ts index d80c5a326f..1749f99130 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/PieChartSettings.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/PieChartSettings.ts @@ -7,6 +7,7 @@ import { DATA_LABELS_SETTING } from '@/command-menu/pages/page-layout/constants/ import { DATE_GRANULARITY_SETTING } from '@/command-menu/pages/page-layout/constants/settings/DateGranularitySetting'; import { EACH_SLICE_REPRESENTS_SETTING } from '@/command-menu/pages/page-layout/constants/settings/EachSliceRepresentsSetting'; import { FILTER_SETTING } from '@/command-menu/pages/page-layout/constants/settings/FilterSetting'; +import { HIDE_EMPTY_CATEGORY_SETTING } from '@/command-menu/pages/page-layout/constants/settings/HideEmptyCategorySetting'; import { PRIMARY_SORT_BY_SETTING } from '@/command-menu/pages/page-layout/constants/settings/PrimarySortBySetting'; import { SHOW_LEGEND_SETTING } from '@/command-menu/pages/page-layout/constants/settings/ShowLegendSetting'; import { type ChartSettingsGroup } from '@/command-menu/pages/page-layout/types/ChartSettingsGroup'; @@ -21,6 +22,7 @@ export const PIE_CHART_SETTINGS: ChartSettingsGroup[] = [ DATE_GRANULARITY_SETTING, EACH_SLICE_REPRESENTS_SETTING, PRIMARY_SORT_BY_SETTING, + HIDE_EMPTY_CATEGORY_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 6e0ddbbd98..5cd33ebfb2 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 @@ -18,6 +18,7 @@ export const CHART_CONFIGURATION_SETTING_LABELS = { STACKED_BARS: msg`Stacked bars`, STACKED_LINES: msg`Stacked lines`, OMIT_NULL_VALUES: msg`Omit zero values`, + HIDE_EMPTY_CATEGORY: msg`Hide empty category`, MIN_RANGE: msg`Min range`, MAX_RANGE: msg`Max range`, DATE_GRANULARITY_X: msg`Date granularity`, diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/HideEmptyCategorySetting.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/HideEmptyCategorySetting.ts new file mode 100644 index 0000000000..ec879e5d7a --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/HideEmptyCategorySetting.ts @@ -0,0 +1,11 @@ +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 { IconEyeOff } from 'twenty-ui/display'; + +export const HIDE_EMPTY_CATEGORY_SETTING: ChartSettingsItem = { + isBoolean: true, + Icon: IconEyeOff, + label: CHART_CONFIGURATION_SETTING_LABELS.HIDE_EMPTY_CATEGORY, + id: CHART_CONFIGURATION_SETTING_IDS.HIDE_EMPTY_CATEGORY, +}; 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 0ace503ea2..3f8cf30579 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 @@ -255,6 +255,8 @@ export const useChartSettingsValues = ({ return isBarOrLineChart ? (configuration.omitNullValues ?? false) : false; + case CHART_CONFIGURATION_SETTING_IDS.HIDE_EMPTY_CATEGORY: + return isPieChart ? (configuration.hideEmptyCategory ?? false) : false; case CHART_CONFIGURATION_SETTING_IDS.MIN_RANGE: return isBarOrLineChart ? (configuration.rangeMin?.toString() ?? '') 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 8c86cb7d44..0e3e220b0d 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 @@ -17,6 +17,7 @@ export enum CHART_CONFIGURATION_SETTING_IDS { STACKED_BARS = 'STACKED_BARS', STACKED_LINES = 'STACKED_LINES', OMIT_NULL_VALUES = 'OMIT_NULL_VALUES', + HIDE_EMPTY_CATEGORY = 'HIDE_EMPTY_CATEGORY', MIN_RANGE = 'MIN_RANGE', MAX_RANGE = 'MAX_RANGE', DATE_GRANULARITY_X = 'DATE_GRANULARITY_X', @@ -34,6 +35,7 @@ export const CHART_CONFIGURATION_SETTING_TO_CONFIG_KEY_MAP = { [CHART_CONFIGURATION_SETTING_IDS.STACKED_BARS]: 'groupMode', [CHART_CONFIGURATION_SETTING_IDS.STACKED_LINES]: 'isStacked', [CHART_CONFIGURATION_SETTING_IDS.OMIT_NULL_VALUES]: 'omitNullValues', + [CHART_CONFIGURATION_SETTING_IDS.HIDE_EMPTY_CATEGORY]: 'hideEmptyCategory', [CHART_CONFIGURATION_SETTING_IDS.MIN_RANGE]: 'rangeMin', [CHART_CONFIGURATION_SETTING_IDS.MAX_RANGE]: 'rangeMax', [CHART_CONFIGURATION_SETTING_IDS.DATE_GRANULARITY_X]: diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetPieChart/hooks/usePieChartCenterMetricData.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetPieChart/hooks/usePieChartCenterMetricData.ts index d8b215e9ba..7d67703c5f 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetPieChart/hooks/usePieChartCenterMetricData.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetPieChart/hooks/usePieChartCenterMetricData.ts @@ -39,6 +39,22 @@ export const usePieChartCenterMetricData = ({ configuration, }); + const groupByField = objectMetadataItem.fields.find( + (field) => field.id === configuration.groupByFieldMetadataId, + ); + + const centerMetricFilter = useMemo(() => { + if (!configuration.hideEmptyCategory || !groupByField) { + return gqlOperationFilter; + } + + const notNullFilter = { [groupByField.name]: { is: 'NOT_NULL' as const } }; + + return isDefined(gqlOperationFilter) + ? { and: [gqlOperationFilter, notNullFilter] } + : notNullFilter; + }, [gqlOperationFilter, configuration.hideEmptyCategory, groupByField]); + const centerMetricAggregateOperation = configuration.aggregateOperation as AggregateOperations; @@ -58,7 +74,7 @@ export const usePieChartCenterMetricData = ({ const { data: centerMetricData } = useAggregateRecords({ objectNameSingular: objectMetadataItem.nameSingular, recordGqlFieldsAggregate, - filter: gqlOperationFilter, + filter: centerMetricFilter, skip, }); diff --git a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetPieChart/utils/transformGroupByDataToPieChartData.ts b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetPieChart/utils/transformGroupByDataToPieChartData.ts index 6a5bc908c2..d23fbad99d 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetPieChart/utils/transformGroupByDataToPieChartData.ts +++ b/packages/twenty-front/src/modules/page-layout/widgets/graph/graphWidgetPieChart/utils/transformGroupByDataToPieChartData.ts @@ -82,8 +82,14 @@ export const transformGroupByDataToPieChartData = ({ ? (configuration.dateGranularity ?? undefined) : undefined; + const filteredResults = configuration.hideEmptyCategory + ? rawResults.filter((result) => + isDefined(result.groupByDimensionValues?.[0]), + ) + : rawResults; + // TODO: Add a limit to the query instead of slicing here (issue: twentyhq/core-team-issues#1600) - const limitedResults = rawResults.slice( + const limitedResults = filteredResults.slice( 0, PIE_CHART_MAXIMUM_NUMBER_OF_SLICES, ); @@ -122,7 +128,8 @@ export const transformGroupByDataToPieChartData = ({ return { data, showLegend, - hasTooManyGroups: rawResults.length > PIE_CHART_MAXIMUM_NUMBER_OF_SLICES, + hasTooManyGroups: + filteredResults.length > PIE_CHART_MAXIMUM_NUMBER_OF_SLICES, formattedToRawLookup, }; }; diff --git a/packages/twenty-server/src/engine/metadata-modules/page-layout/dtos/pie-chart-configuration.dto.ts b/packages/twenty-server/src/engine/metadata-modules/page-layout/dtos/pie-chart-configuration.dto.ts index 1011bd1bd8..ff3366a485 100644 --- a/packages/twenty-server/src/engine/metadata-modules/page-layout/dtos/pie-chart-configuration.dto.ts +++ b/packages/twenty-server/src/engine/metadata-modules/page-layout/dtos/pie-chart-configuration.dto.ts @@ -81,6 +81,11 @@ export class PieChartConfigurationDTO { @IsOptional() displayLegend?: boolean; + @Field(() => Boolean, { nullable: true, defaultValue: false }) + @IsBoolean() + @IsOptional() + hideEmptyCategory?: boolean; + @Field(() => String, { nullable: true }) @IsString() @IsOptional()