[DASHBOARDS] Pie Chart (#16048)
## Description - Connect Pie Chart to the backend - Update from the old design to the new design - Switch seamlessly from/to other types of chart from the Pie Chart by converting the configuration Note: There are a couple things to implement before the pie chart is ready to go live: - Add a new setting on Bar, Line and Pie chart to hide and display legends (toggle) - Add data labels next to each slice ## Video QA https://github.com/user-attachments/assets/b1561e32-0434-43ad-8855-d617b209ce27
This commit is contained in:
+15
-9
@@ -11,7 +11,7 @@ import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/command-menu/pa
|
||||
import { useUpdateChartSettingInput } from '@/command-menu/pages/page-layout/hooks/useUpdateChartSettingInput';
|
||||
import { useUpdateChartSettingToggle } from '@/command-menu/pages/page-layout/hooks/useUpdateChartSettingToggle';
|
||||
import { useUpdateCurrentWidgetConfig } from '@/command-menu/pages/page-layout/hooks/useUpdateCurrentWidgetConfig';
|
||||
import { useUpdateGraphTypeConfig } from '@/command-menu/pages/page-layout/hooks/useUpdateGraphTypeConfig';
|
||||
import { useGetConfigToUpdateAfterGraphTypeChange } from '@/command-menu/pages/page-layout/hooks/useUpdateGraphTypeConfig';
|
||||
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 { shouldHideChartSetting } from '@/command-menu/pages/page-layout/utils/shouldHideChartSetting';
|
||||
@@ -19,6 +19,7 @@ import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages';
|
||||
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { BAR_CHART_MAXIMUM_NUMBER_OF_BARS } from '@/page-layout/widgets/graph/graphWidgetBarChart/constants/BarChartMaximumNumberOfBars.constant';
|
||||
import { LINE_CHART_MAXIMUM_NUMBER_OF_DATA_POINTS } from '@/page-layout/widgets/graph/graphWidgetLineChart/constants/LineChartMaximumNumberOfDataPoints.constant';
|
||||
import { PIE_CHART_MAXIMUM_NUMBER_OF_SLICES } from '@/page-layout/widgets/graph/graphWidgetPieChart/constants/PieChartMaximumNumberOfSlices.constant';
|
||||
import { hasWidgetTooManyGroupsComponentState } from '@/page-layout/widgets/graph/states/hasWidgetTooManyGroupsComponentState';
|
||||
import { useOpenDropdown } from '@/ui/layout/dropdown/hooks/useOpenDropdown';
|
||||
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
|
||||
@@ -76,11 +77,11 @@ export const ChartSettings = ({ widget }: { widget: PageLayoutWidget }) => {
|
||||
|
||||
const { updateChartSettingInput } = useUpdateChartSettingInput(pageLayoutId);
|
||||
|
||||
const { updateGraphTypeConfig } = useUpdateGraphTypeConfig({
|
||||
pageLayoutId,
|
||||
widget,
|
||||
configuration,
|
||||
});
|
||||
const { getConfigToUpdateAfterGraphTypeChange } =
|
||||
useGetConfigToUpdateAfterGraphTypeChange({
|
||||
pageLayoutId,
|
||||
widget,
|
||||
});
|
||||
|
||||
const isGroupByEnabled = getChartSettingsValues(
|
||||
CHART_CONFIGURATION_SETTING_IDS.GROUP_BY,
|
||||
@@ -89,7 +90,7 @@ export const ChartSettings = ({ widget }: { widget: PageLayoutWidget }) => {
|
||||
useRecoilComponentState(hasWidgetTooManyGroupsComponentState);
|
||||
|
||||
const handleGraphTypeChange = (graphType: GraphType) => {
|
||||
const configToUpdate = updateGraphTypeConfig(graphType);
|
||||
const configToUpdate = getConfigToUpdateAfterGraphTypeChange(graphType);
|
||||
|
||||
updateCurrentWidgetConfig({
|
||||
configToUpdate,
|
||||
@@ -133,7 +134,9 @@ export const ChartSettings = ({ widget }: { widget: PageLayoutWidget }) => {
|
||||
configuration.__typename === 'BarChartConfiguration' ||
|
||||
configuration.__typename === 'LineChartConfiguration'
|
||||
? configuration.primaryAxisGroupByFieldMetadataId
|
||||
: null;
|
||||
: configuration.__typename === 'PieChartConfiguration'
|
||||
? configuration.groupByFieldMetadataId
|
||||
: null;
|
||||
|
||||
const primaryAxisField = objectMetadataItem?.fields?.find(
|
||||
(field) => field.id === primaryAxisFieldMetadataId,
|
||||
@@ -155,7 +158,10 @@ export const ChartSettings = ({ widget }: { widget: PageLayoutWidget }) => {
|
||||
message={
|
||||
currentGraphType === GraphType.LINE
|
||||
? t`Undisplayed data: max ${LINE_CHART_MAXIMUM_NUMBER_OF_DATA_POINTS} data points per chart.`
|
||||
: t`Undisplayed data: max ${BAR_CHART_MAXIMUM_NUMBER_OF_BARS} bars per chart.`
|
||||
: currentGraphType === GraphType.VERTICAL_BAR ||
|
||||
currentGraphType === GraphType.HORIZONTAL_BAR
|
||||
? t`Undisplayed data: max ${BAR_CHART_MAXIMUM_NUMBER_OF_BARS} bars per chart.`
|
||||
: t`Undisplayed data: max ${PIE_CHART_MAXIMUM_NUMBER_OF_SLICES} slices per chart.`
|
||||
}
|
||||
tooltipMessage={
|
||||
isPrimaryAxisDate
|
||||
|
||||
+2
-1
@@ -42,7 +42,8 @@ export const ChartAggregateOperationSelectionDropdownContent = ({
|
||||
widgetInEditMode?.configuration?.__typename !== 'BarChartConfiguration' &&
|
||||
widgetInEditMode?.configuration?.__typename !== 'LineChartConfiguration' &&
|
||||
widgetInEditMode?.configuration?.__typename !==
|
||||
'AggregateChartConfiguration'
|
||||
'AggregateChartConfiguration' &&
|
||||
widgetInEditMode?.configuration?.__typename !== 'PieChartConfiguration'
|
||||
) {
|
||||
throw new Error('Invalid configuration type');
|
||||
}
|
||||
|
||||
+5
-1
@@ -24,10 +24,14 @@ const getCurrentDateGranularity = ({
|
||||
axis,
|
||||
}: {
|
||||
configuration: ChartConfiguration;
|
||||
axis: 'primary' | 'secondary';
|
||||
axis?: 'primary' | 'secondary';
|
||||
}) => {
|
||||
const defaultGranularity = ObjectRecordGroupByDateGranularity.DAY;
|
||||
|
||||
if (configuration?.__typename === 'PieChartConfiguration') {
|
||||
return configuration.dateGranularity || defaultGranularity;
|
||||
}
|
||||
|
||||
const isBarOrLineChart =
|
||||
configuration?.__typename === 'BarChartConfiguration' ||
|
||||
configuration?.__typename === 'LineChartConfiguration';
|
||||
|
||||
+2
-1
@@ -31,7 +31,8 @@ export const ChartFieldSelectionForAggregateOperationDropdownContent = () => {
|
||||
if (
|
||||
configuration?.__typename !== 'BarChartConfiguration' &&
|
||||
configuration?.__typename !== 'LineChartConfiguration' &&
|
||||
configuration?.__typename !== 'AggregateChartConfiguration'
|
||||
configuration?.__typename !== 'AggregateChartConfiguration' &&
|
||||
configuration?.__typename !== 'PieChartConfiguration'
|
||||
) {
|
||||
throw new Error('Invalid configuration type');
|
||||
}
|
||||
|
||||
+45
-14
@@ -14,16 +14,21 @@ import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/ho
|
||||
import { type CompositeFieldSubFieldName } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { MenuItemSelect } from 'twenty-ui/navigation';
|
||||
import { type GraphOrderBy } from '~/generated/graphql';
|
||||
import {
|
||||
type BarChartConfiguration,
|
||||
type GraphOrderBy,
|
||||
type LineChartConfiguration,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
export const ChartXAxisSortBySelectionDropdownContent = () => {
|
||||
export const ChartSortBySelectionDropdownContent = () => {
|
||||
const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord();
|
||||
const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId);
|
||||
const configuration = widgetInEditMode?.configuration;
|
||||
|
||||
if (
|
||||
configuration?.__typename !== 'BarChartConfiguration' &&
|
||||
configuration?.__typename !== 'LineChartConfiguration'
|
||||
configuration?.__typename !== 'LineChartConfiguration' &&
|
||||
configuration?.__typename !== 'PieChartConfiguration'
|
||||
) {
|
||||
throw new Error('Invalid configuration type');
|
||||
}
|
||||
@@ -49,14 +54,22 @@ export const ChartXAxisSortBySelectionDropdownContent = () => {
|
||||
objectMetadataId: widgetInEditMode.objectMetadataId,
|
||||
});
|
||||
|
||||
const isPieChart = configuration.__typename === 'PieChartConfiguration';
|
||||
const isLineChart = configuration.__typename === 'LineChartConfiguration';
|
||||
|
||||
const handleSelect = (orderBy: GraphOrderBy) => {
|
||||
updateCurrentWidgetConfig({
|
||||
configToUpdate: { primaryAxisOrderBy: orderBy },
|
||||
});
|
||||
if (isPieChart) {
|
||||
updateCurrentWidgetConfig({
|
||||
configToUpdate: { orderBy },
|
||||
});
|
||||
} else {
|
||||
updateCurrentWidgetConfig({
|
||||
configToUpdate: { primaryAxisOrderBy: orderBy },
|
||||
});
|
||||
}
|
||||
closeDropdown();
|
||||
};
|
||||
|
||||
const isLineChart = configuration.__typename === 'LineChartConfiguration';
|
||||
const availableOptions = X_SORT_BY_OPTIONS.filter((option) => {
|
||||
if (isLineChart) {
|
||||
return option.value !== 'VALUE_ASC' && option.value !== 'VALUE_DESC';
|
||||
@@ -64,6 +77,26 @@ export const ChartXAxisSortBySelectionDropdownContent = () => {
|
||||
return true;
|
||||
});
|
||||
|
||||
let currentOrderBy: GraphOrderBy | undefined;
|
||||
let groupByFieldMetadataId: string | undefined;
|
||||
let groupBySubFieldName: string | null | undefined;
|
||||
|
||||
if (configuration.__typename === 'PieChartConfiguration') {
|
||||
currentOrderBy = configuration.orderBy ?? undefined;
|
||||
groupByFieldMetadataId = configuration.groupByFieldMetadataId;
|
||||
groupBySubFieldName = configuration.groupBySubFieldName;
|
||||
} else {
|
||||
const barOrLineChartConfiguration = configuration as
|
||||
| BarChartConfiguration
|
||||
| LineChartConfiguration;
|
||||
currentOrderBy =
|
||||
barOrLineChartConfiguration.primaryAxisOrderBy ?? undefined;
|
||||
groupByFieldMetadataId =
|
||||
barOrLineChartConfiguration.primaryAxisGroupByFieldMetadataId;
|
||||
groupBySubFieldName =
|
||||
barOrLineChartConfiguration.primaryAxisGroupBySubFieldName;
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownMenuItemsContainer>
|
||||
<SelectableList
|
||||
@@ -82,18 +115,16 @@ export const ChartXAxisSortBySelectionDropdownContent = () => {
|
||||
<MenuItemSelect
|
||||
text={getXSortOptionLabel({
|
||||
graphOrderBy: sortOption.value,
|
||||
groupByFieldMetadataIdX:
|
||||
configuration.primaryAxisGroupByFieldMetadataId,
|
||||
groupBySubFieldNameX:
|
||||
configuration.primaryAxisGroupBySubFieldName as
|
||||
| CompositeFieldSubFieldName
|
||||
| undefined,
|
||||
groupByFieldMetadataIdX: groupByFieldMetadataId ?? '',
|
||||
groupBySubFieldNameX: groupBySubFieldName as
|
||||
| CompositeFieldSubFieldName
|
||||
| undefined,
|
||||
aggregateFieldMetadataId:
|
||||
configuration.aggregateFieldMetadataId ?? undefined,
|
||||
aggregateOperation:
|
||||
configuration.aggregateOperation ?? undefined,
|
||||
})}
|
||||
selected={configuration.primaryAxisOrderBy === sortOption.value}
|
||||
selected={currentOrderBy === sortOption.value}
|
||||
focused={selectedItemId === sortOption.value}
|
||||
LeftIcon={sortOption.icon}
|
||||
onClick={() => {
|
||||
+2
-2
@@ -9,10 +9,10 @@ import { DATE_GRANULARITY_Y_SETTING } from '@/command-menu/pages/page-layout/con
|
||||
import { FILTER_SETTING } from '@/command-menu/pages/page-layout/constants/settings/FilterSetting';
|
||||
import { GROUP_BY_SETTING } from '@/command-menu/pages/page-layout/constants/settings/GroupBySetting';
|
||||
import { OMIT_NULL_VALUES_SETTING } from '@/command-menu/pages/page-layout/constants/settings/OmitNullValuesSetting';
|
||||
import { PRIMARY_SORT_BY_SETTING } from '@/command-menu/pages/page-layout/constants/settings/PrimarySortBySetting';
|
||||
import { RANGE_MAX_SETTING } from '@/command-menu/pages/page-layout/constants/settings/RangeMaxSetting';
|
||||
import { RANGE_MIN_SETTING } from '@/command-menu/pages/page-layout/constants/settings/RangeMinSetting';
|
||||
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_LINES_SETTING } from '@/command-menu/pages/page-layout/constants/settings/StackedLineSettings';
|
||||
import { type ChartSettingsGroup } from '@/command-menu/pages/page-layout/types/ChartSettingsGroup';
|
||||
|
||||
@@ -26,7 +26,7 @@ export const LINE_CHART_SETTINGS: ChartSettingsGroup[] = [
|
||||
items: [
|
||||
DATA_DISPLAY_X_SETTING,
|
||||
DATE_GRANULARITY_X_SETTING,
|
||||
SORT_BY_X_SETTING,
|
||||
PRIMARY_SORT_BY_SETTING,
|
||||
OMIT_NULL_VALUES_SETTING,
|
||||
],
|
||||
},
|
||||
|
||||
+2
-2
@@ -5,7 +5,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 { SORT_BY_X_SETTING } from '@/command-menu/pages/page-layout/constants/settings/SortByXSetting';
|
||||
import { PRIMARY_SORT_BY_SETTING } from '@/command-menu/pages/page-layout/constants/settings/PrimarySortBySetting';
|
||||
import { type ChartSettingsGroup } from '@/command-menu/pages/page-layout/types/ChartSettingsGroup';
|
||||
|
||||
export const PIE_CHART_SETTINGS: ChartSettingsGroup[] = [
|
||||
@@ -17,7 +17,7 @@ export const PIE_CHART_SETTINGS: ChartSettingsGroup[] = [
|
||||
DATA_DISPLAY_PIE_CHART_SETTING,
|
||||
DATE_GRANULARITY_SETTING,
|
||||
EACH_SLICE_REPRESENTS_SETTING,
|
||||
SORT_BY_X_SETTING,
|
||||
PRIMARY_SORT_BY_SETTING,
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ export const CHART_CONFIGURATION_SETTING_LABELS = {
|
||||
COLORS: msg`Colors`,
|
||||
FILTER: msg`Filter`,
|
||||
GROUP_BY: msg`Group by`,
|
||||
SORT_BY_X: msg`Sort by`,
|
||||
PRIMARY_SORT_BY: msg`Sort by`,
|
||||
SORT_BY_GROUP_BY_FIELD: msg`Sort by`,
|
||||
DATA_ON_DISPLAY_X: msg`Data on display`,
|
||||
DATA_ON_DISPLAY_Y: msg`Data on display`,
|
||||
|
||||
+2
@@ -1,3 +1,4 @@
|
||||
import { ChartPieFieldSelectionDropdownContent } from '@/command-menu/pages/page-layout/components/dropdown-content/ChartPieFieldSelectionDropdownContent';
|
||||
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';
|
||||
@@ -9,4 +10,5 @@ export const DATA_DISPLAY_PIE_CHART_SETTING: ChartSettingsItem = {
|
||||
label: CHART_CONFIGURATION_SETTING_LABELS.DATA_ON_DISPLAY_PIE_CHART,
|
||||
id: CHART_CONFIGURATION_SETTING_IDS.DATA_ON_DISPLAY_PIE_CHART,
|
||||
dependsOn: [CHART_CONFIGURATION_SETTING_IDS.SOURCE],
|
||||
DropdownContent: ChartPieFieldSelectionDropdownContent,
|
||||
};
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import { ChartPieFieldSelectionDropdownContent } from '@/command-menu/pages/page-layout/components/dropdown-content/ChartPieFieldSelectionDropdownContent';
|
||||
import { ChartFieldSelectionForAggregateOperationDropdownContent } from '@/command-menu/pages/page-layout/components/dropdown-content/ChartFieldSelectionForAggregateOperationDropdownContent';
|
||||
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';
|
||||
@@ -9,5 +9,5 @@ export const EACH_SLICE_REPRESENTS_SETTING: ChartSettingsItem = {
|
||||
Icon: IconChartPie,
|
||||
label: CHART_CONFIGURATION_SETTING_LABELS.EACH_SLICE_REPRESENTS,
|
||||
id: CHART_CONFIGURATION_SETTING_IDS.EACH_SLICE_REPRESENTS,
|
||||
DropdownContent: ChartPieFieldSelectionDropdownContent,
|
||||
DropdownContent: ChartFieldSelectionForAggregateOperationDropdownContent,
|
||||
};
|
||||
|
||||
+5
-5
@@ -1,13 +1,13 @@
|
||||
import { ChartXAxisSortBySelectionDropdownContent } from '@/command-menu/pages/page-layout/components/dropdown-content/ChartXAxisSortBySelectionDropdownContent';
|
||||
import { ChartSortBySelectionDropdownContent } from '@/command-menu/pages/page-layout/components/dropdown-content/ChartSortBySelectionDropdownContent';
|
||||
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 { IconArrowsSort } from 'twenty-ui/display';
|
||||
|
||||
export const SORT_BY_X_SETTING: ChartSettingsItem = {
|
||||
export const PRIMARY_SORT_BY_SETTING: ChartSettingsItem = {
|
||||
isBoolean: false,
|
||||
Icon: IconArrowsSort,
|
||||
label: CHART_CONFIGURATION_SETTING_LABELS.SORT_BY_X,
|
||||
id: CHART_CONFIGURATION_SETTING_IDS.SORT_BY_X,
|
||||
DropdownContent: ChartXAxisSortBySelectionDropdownContent,
|
||||
label: CHART_CONFIGURATION_SETTING_LABELS.PRIMARY_SORT_BY,
|
||||
id: CHART_CONFIGURATION_SETTING_IDS.PRIMARY_SORT_BY,
|
||||
DropdownContent: ChartSortBySelectionDropdownContent,
|
||||
};
|
||||
+4
-4
@@ -141,7 +141,7 @@ describe('useChartSettingsValues', () => {
|
||||
const { result } = renderUseChartSettingsValues(verticalBarConfig);
|
||||
|
||||
const value = result.current.getChartSettingsValues(
|
||||
CHART_CONFIGURATION_SETTING_IDS.SORT_BY_X,
|
||||
CHART_CONFIGURATION_SETTING_IDS.PRIMARY_SORT_BY,
|
||||
);
|
||||
|
||||
expect(value).toBeDefined();
|
||||
@@ -203,7 +203,7 @@ describe('useChartSettingsValues', () => {
|
||||
const { result } = renderUseChartSettingsValues(horizontalBarConfig);
|
||||
|
||||
const value = result.current.getChartSettingsValues(
|
||||
CHART_CONFIGURATION_SETTING_IDS.SORT_BY_X,
|
||||
CHART_CONFIGURATION_SETTING_IDS.PRIMARY_SORT_BY,
|
||||
);
|
||||
|
||||
expect(value).toBeDefined();
|
||||
@@ -451,7 +451,7 @@ describe('useChartSettingsValues', () => {
|
||||
const { result } = renderUseChartSettingsValues(config);
|
||||
|
||||
const value = result.current.getChartSettingsValues(
|
||||
CHART_CONFIGURATION_SETTING_IDS.SORT_BY_X,
|
||||
CHART_CONFIGURATION_SETTING_IDS.PRIMARY_SORT_BY,
|
||||
);
|
||||
|
||||
expect(value).toBeUndefined();
|
||||
@@ -573,7 +573,7 @@ describe('useChartSettingsValues', () => {
|
||||
CHART_CONFIGURATION_SETTING_IDS.DATA_ON_DISPLAY_X,
|
||||
CHART_CONFIGURATION_SETTING_IDS.DATA_ON_DISPLAY_Y,
|
||||
CHART_CONFIGURATION_SETTING_IDS.GROUP_BY,
|
||||
CHART_CONFIGURATION_SETTING_IDS.SORT_BY_X,
|
||||
CHART_CONFIGURATION_SETTING_IDS.PRIMARY_SORT_BY,
|
||||
CHART_CONFIGURATION_SETTING_IDS.SORT_BY_GROUP_BY_FIELD,
|
||||
CHART_CONFIGURATION_SETTING_IDS.AXIS_NAME,
|
||||
CHART_CONFIGURATION_SETTING_IDS.DATA_LABELS,
|
||||
|
||||
+38
-3
@@ -102,12 +102,28 @@ export const useChartSettingsValues = ({
|
||||
})
|
||||
: undefined;
|
||||
|
||||
let pieChartSortByLabel: string | undefined;
|
||||
|
||||
if (configuration.__typename === 'PieChartConfiguration') {
|
||||
groupByOrderBy = configuration.orderBy;
|
||||
groupByFieldYId = configuration.groupByFieldMetadataId;
|
||||
groupBySubFieldNameY = configuration.groupBySubFieldName as
|
||||
| CompositeFieldSubFieldName
|
||||
| undefined;
|
||||
|
||||
pieChartSortByLabel =
|
||||
isDefined(configuration.orderBy) &&
|
||||
isDefined(configuration.groupByFieldMetadataId)
|
||||
? getXSortOptionLabel({
|
||||
graphOrderBy: configuration.orderBy,
|
||||
groupByFieldMetadataIdX: configuration.groupByFieldMetadataId,
|
||||
groupBySubFieldNameX: configuration.groupBySubFieldName as
|
||||
| CompositeFieldSubFieldName
|
||||
| undefined,
|
||||
aggregateFieldMetadataId: configuration.aggregateFieldMetadataId,
|
||||
aggregateOperation: configuration.aggregateOperation ?? undefined,
|
||||
})
|
||||
: undefined;
|
||||
}
|
||||
|
||||
const finalGroupByFieldYId = groupByFieldYId;
|
||||
@@ -135,7 +151,8 @@ export const useChartSettingsValues = ({
|
||||
? capitalize(configuration.color)
|
||||
: undefined;
|
||||
case CHART_CONFIGURATION_SETTING_IDS.DATA_ON_DISPLAY_Y:
|
||||
case CHART_CONFIGURATION_SETTING_IDS.DATA_ON_DISPLAY_AGGREGATE: {
|
||||
case CHART_CONFIGURATION_SETTING_IDS.DATA_ON_DISPLAY_AGGREGATE:
|
||||
case CHART_CONFIGURATION_SETTING_IDS.EACH_SLICE_REPRESENTS: {
|
||||
const hasAggregateLabel = isDefined(aggregateField?.label);
|
||||
const hasAggregateOperation = isDefined(aggregateOperation);
|
||||
|
||||
@@ -145,6 +162,24 @@ export const useChartSettingsValues = ({
|
||||
: ''
|
||||
}`;
|
||||
}
|
||||
case CHART_CONFIGURATION_SETTING_IDS.DATA_ON_DISPLAY_PIE_CHART: {
|
||||
const pieChartGroupByField = isDefined(finalGroupByFieldYId)
|
||||
? objectMetadataItem?.fields.find(
|
||||
(field) => field.id === finalGroupByFieldYId,
|
||||
)
|
||||
: undefined;
|
||||
const pieChartGroupBySubFieldNameLabel =
|
||||
isDefined(finalGroupBySubFieldNameY) &&
|
||||
isDefined(pieChartGroupByField)
|
||||
? getFieldLabelWithSubField({
|
||||
field: pieChartGroupByField,
|
||||
subFieldName: finalGroupBySubFieldNameY,
|
||||
})
|
||||
: undefined;
|
||||
|
||||
return pieChartGroupBySubFieldNameLabel ?? pieChartGroupByField?.label;
|
||||
}
|
||||
|
||||
case CHART_CONFIGURATION_SETTING_IDS.GROUP_BY:
|
||||
return groupByFieldY?.label;
|
||||
case CHART_CONFIGURATION_SETTING_IDS.AXIS_NAME:
|
||||
@@ -152,8 +187,8 @@ export const useChartSettingsValues = ({
|
||||
isDefined(configuration.axisNameDisplay)
|
||||
? getChartAxisNameDisplayOptions(configuration.axisNameDisplay)
|
||||
: undefined;
|
||||
case CHART_CONFIGURATION_SETTING_IDS.SORT_BY_X:
|
||||
return xAxisOrderByLabel;
|
||||
case CHART_CONFIGURATION_SETTING_IDS.PRIMARY_SORT_BY:
|
||||
return pieChartSortByLabel ?? xAxisOrderByLabel;
|
||||
case CHART_CONFIGURATION_SETTING_IDS.SORT_BY_GROUP_BY_FIELD:
|
||||
return groupByOrderByLabel;
|
||||
case CHART_CONFIGURATION_SETTING_IDS.DATA_LABELS:
|
||||
|
||||
+75
-26
@@ -1,25 +1,26 @@
|
||||
import { GRAPH_TYPE_TO_CONFIG_TYPENAME } from '@/command-menu/pages/page-layout/constants/GraphTypeToConfigTypename';
|
||||
import { type ChartConfiguration } from '@/command-menu/pages/page-layout/types/ChartConfiguration';
|
||||
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 { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
|
||||
import { AggregateOperations } from '@/object-record/record-table/constants/AggregateOperations';
|
||||
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
|
||||
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
|
||||
import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState';
|
||||
import { getTabListInstanceIdFromPageLayoutId } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutId';
|
||||
import { updateWidgetMinimumSizeForGraphType } from '@/page-layout/utils/updateWidgetMinimumSizeForGraphType';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { isDefined, isFieldMetadataDateKind } from 'twenty-shared/utils';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { GraphType, type PageLayoutWidget } from '~/generated/graphql';
|
||||
|
||||
export const useUpdateGraphTypeConfig = ({
|
||||
export const useGetConfigToUpdateAfterGraphTypeChange = ({
|
||||
pageLayoutId,
|
||||
widget,
|
||||
configuration,
|
||||
}: {
|
||||
pageLayoutId: string;
|
||||
widget: PageLayoutWidget;
|
||||
configuration: ChartConfiguration;
|
||||
}) => {
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
|
||||
@@ -40,10 +41,41 @@ export const useUpdateGraphTypeConfig = ({
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const updateGraphTypeConfig = useRecoilCallback(
|
||||
const pageLayoutDraftState = useRecoilComponentCallbackState(
|
||||
pageLayoutDraftComponentState,
|
||||
pageLayoutId,
|
||||
);
|
||||
|
||||
const getConfigToUpdateAfterGraphTypeChange = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
(graphType: GraphType) => {
|
||||
const configToUpdate: Record<string, any> = {
|
||||
const currentlyEditingWidgetId = snapshot
|
||||
.getLoadable(currentlyEditingWidgetIdState)
|
||||
.getValue();
|
||||
|
||||
if (!isDefined(currentlyEditingWidgetId)) {
|
||||
throw new Error('No widget is currently being edited');
|
||||
}
|
||||
|
||||
const draftPageLayout = snapshot
|
||||
.getLoadable(pageLayoutDraftState)
|
||||
.getValue();
|
||||
|
||||
const widgetInDraft = draftPageLayout.tabs
|
||||
.flatMap((tab) => tab.widgets)
|
||||
.find((w) => w.id === currentlyEditingWidgetId);
|
||||
|
||||
if (
|
||||
!isDefined(widgetInDraft) ||
|
||||
!isDefined(widgetInDraft.configuration)
|
||||
) {
|
||||
throw new Error('Widget configuration not found in draft state');
|
||||
}
|
||||
|
||||
const currentConfiguration =
|
||||
widgetInDraft.configuration as ChartConfiguration;
|
||||
|
||||
let configToUpdate: Record<string, any> = {
|
||||
__typename: GRAPH_TYPE_TO_CONFIG_TYPENAME[graphType],
|
||||
graphType,
|
||||
};
|
||||
@@ -52,33 +84,50 @@ export const useUpdateGraphTypeConfig = ({
|
||||
graphType !== GraphType.AGGREGATE &&
|
||||
graphType !== GraphType.GAUGE
|
||||
) {
|
||||
const currentAggregateFieldMetadataId =
|
||||
configuration.aggregateFieldMetadataId;
|
||||
|
||||
const objectMetadataItem = objectMetadataItems.find(
|
||||
(item) => item.id === widget.objectMetadataId,
|
||||
);
|
||||
|
||||
if (isDefined(objectMetadataItem)) {
|
||||
const aggregateField = objectMetadataItem.fields.find(
|
||||
(field) => field.id === currentAggregateFieldMetadataId,
|
||||
const convertedAggregateOperation =
|
||||
convertAggregateOperationForDateField(
|
||||
currentConfiguration,
|
||||
objectMetadataItem,
|
||||
);
|
||||
|
||||
if (
|
||||
isDefined(aggregateField) &&
|
||||
isFieldMetadataDateKind(aggregateField.type) &&
|
||||
(configuration.aggregateOperation === AggregateOperations.MIN ||
|
||||
configuration.aggregateOperation === AggregateOperations.MAX)
|
||||
) {
|
||||
configToUpdate.aggregateOperation = AggregateOperations.COUNT;
|
||||
}
|
||||
if (isDefined(convertedAggregateOperation)) {
|
||||
configToUpdate = {
|
||||
...configToUpdate,
|
||||
aggregateOperation: convertedAggregateOperation,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const isPieChart = graphType === GraphType.PIE;
|
||||
const isBarOrLineChart =
|
||||
graphType === GraphType.VERTICAL_BAR ||
|
||||
graphType === GraphType.HORIZONTAL_BAR ||
|
||||
graphType === GraphType.LINE;
|
||||
const wasBarOrLineChart =
|
||||
currentConfiguration.__typename === 'BarChartConfiguration' ||
|
||||
currentConfiguration.__typename === 'LineChartConfiguration';
|
||||
const wasPieChart =
|
||||
currentConfiguration.__typename === 'PieChartConfiguration';
|
||||
|
||||
if (isPieChart && wasBarOrLineChart) {
|
||||
configToUpdate = {
|
||||
...configToUpdate,
|
||||
...convertBarOrLineChartConfigToPieChart(currentConfiguration),
|
||||
};
|
||||
}
|
||||
|
||||
if (isBarOrLineChart && wasPieChart) {
|
||||
configToUpdate = {
|
||||
...configToUpdate,
|
||||
...convertPieChartConfigToBarOrLineChart(currentConfiguration),
|
||||
};
|
||||
}
|
||||
|
||||
const activeTabId = snapshot.getLoadable(activeTabIdState).getValue();
|
||||
const currentlyEditingWidgetId = snapshot
|
||||
.getLoadable(currentlyEditingWidgetIdState)
|
||||
.getValue();
|
||||
|
||||
if (isDefined(activeTabId) && isDefined(currentlyEditingWidgetId)) {
|
||||
const currentLayouts = snapshot
|
||||
@@ -99,13 +148,13 @@ export const useUpdateGraphTypeConfig = ({
|
||||
},
|
||||
[
|
||||
activeTabIdState,
|
||||
configuration,
|
||||
currentlyEditingWidgetIdState,
|
||||
objectMetadataItems,
|
||||
pageLayoutCurrentLayoutsState,
|
||||
pageLayoutDraftState,
|
||||
widget.objectMetadataId,
|
||||
],
|
||||
);
|
||||
|
||||
return { updateGraphTypeConfig };
|
||||
return { getConfigToUpdateAfterGraphTypeChange };
|
||||
};
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ export enum CHART_CONFIGURATION_SETTING_IDS {
|
||||
COLORS = 'COLORS',
|
||||
FILTER = 'FILTER',
|
||||
GROUP_BY = 'GROUP_BY',
|
||||
SORT_BY_X = 'SORT_BY_X',
|
||||
PRIMARY_SORT_BY = 'PRIMARY_SORT_BY',
|
||||
SORT_BY_GROUP_BY_FIELD = 'SORT_BY_GROUP_BY_FIELD',
|
||||
SORT_BY = 'SORT_BY',
|
||||
DATA_ON_DISPLAY_X = 'DATA_ON_DISPLAY_X',
|
||||
|
||||
+3
-3
@@ -9,10 +9,10 @@ import { DATE_GRANULARITY_Y_SETTING } from '@/command-menu/pages/page-layout/con
|
||||
import { FILTER_SETTING } from '@/command-menu/pages/page-layout/constants/settings/FilterSetting';
|
||||
import { GROUP_BY_SETTING } from '@/command-menu/pages/page-layout/constants/settings/GroupBySetting';
|
||||
import { OMIT_NULL_VALUES_SETTING } from '@/command-menu/pages/page-layout/constants/settings/OmitNullValuesSetting';
|
||||
import { PRIMARY_SORT_BY_SETTING } from '@/command-menu/pages/page-layout/constants/settings/PrimarySortBySetting';
|
||||
import { RANGE_MAX_SETTING } from '@/command-menu/pages/page-layout/constants/settings/RangeMaxSetting';
|
||||
import { RANGE_MIN_SETTING } from '@/command-menu/pages/page-layout/constants/settings/RangeMinSetting';
|
||||
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 { IconAxisX, IconAxisY } from 'twenty-ui/display';
|
||||
import { GraphType } from '~/generated-metadata/graphql';
|
||||
@@ -31,7 +31,7 @@ describe('getBarChartSettings', () => {
|
||||
expect(xAxisGroup?.items[0].label).toBe(DATA_DISPLAY_X_SETTING.label);
|
||||
expect(xAxisGroup?.items[0].Icon).toBe(IconAxisX);
|
||||
expect(xAxisGroup?.items[1]).toEqual(DATE_GRANULARITY_X_SETTING);
|
||||
expect(xAxisGroup?.items[2]).toEqual(SORT_BY_X_SETTING);
|
||||
expect(xAxisGroup?.items[2]).toEqual(PRIMARY_SORT_BY_SETTING);
|
||||
expect(xAxisGroup?.items[3]).toEqual(OMIT_NULL_VALUES_SETTING);
|
||||
});
|
||||
|
||||
@@ -92,7 +92,7 @@ describe('getBarChartSettings', () => {
|
||||
expect(yAxisGroup?.items[0].label).toBe(DATA_DISPLAY_X_SETTING.label);
|
||||
expect(yAxisGroup?.items[0].Icon).toBe(IconAxisY);
|
||||
expect(yAxisGroup?.items[1]).toEqual(DATE_GRANULARITY_X_SETTING);
|
||||
expect(yAxisGroup?.items[2]).toEqual(SORT_BY_X_SETTING);
|
||||
expect(yAxisGroup?.items[2]).toEqual(PRIMARY_SORT_BY_SETTING);
|
||||
expect(yAxisGroup?.items[3]).toEqual(OMIT_NULL_VALUES_SETTING);
|
||||
});
|
||||
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import { type ChartConfiguration } from '@/command-menu/pages/page-layout/types/ChartConfiguration';
|
||||
import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { AggregateOperations } from '@/object-record/record-table/constants/AggregateOperations';
|
||||
import { isDefined, isFieldMetadataDateKind } from 'twenty-shared/utils';
|
||||
|
||||
export const convertAggregateOperationForDateField = (
|
||||
configuration: ChartConfiguration,
|
||||
objectMetadataItem: ObjectMetadataItem | undefined,
|
||||
): AggregateOperations | undefined => {
|
||||
if (!isDefined(objectMetadataItem)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const currentAggregateFieldMetadataId =
|
||||
configuration.aggregateFieldMetadataId;
|
||||
|
||||
const aggregateField = objectMetadataItem.fields.find(
|
||||
(field) => field.id === currentAggregateFieldMetadataId,
|
||||
);
|
||||
|
||||
if (
|
||||
isDefined(aggregateField) &&
|
||||
isFieldMetadataDateKind(aggregateField.type) &&
|
||||
(configuration.aggregateOperation === AggregateOperations.MIN ||
|
||||
configuration.aggregateOperation === AggregateOperations.MAX)
|
||||
) {
|
||||
return AggregateOperations.COUNT;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
import {
|
||||
type BarChartConfiguration,
|
||||
type LineChartConfiguration,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
export const convertBarOrLineChartConfigToPieChart = (
|
||||
configuration: BarChartConfiguration | LineChartConfiguration,
|
||||
): Record<string, any> => {
|
||||
const configToUpdate: Record<string, any> = {};
|
||||
|
||||
if ('primaryAxisGroupByFieldMetadataId' in configuration) {
|
||||
configToUpdate.groupByFieldMetadataId =
|
||||
configuration.primaryAxisGroupByFieldMetadataId;
|
||||
}
|
||||
|
||||
if ('primaryAxisGroupBySubFieldName' in configuration) {
|
||||
configToUpdate.groupBySubFieldName =
|
||||
configuration.primaryAxisGroupBySubFieldName;
|
||||
}
|
||||
|
||||
if ('primaryAxisDateGranularity' in configuration) {
|
||||
configToUpdate.dateGranularity = configuration.primaryAxisDateGranularity;
|
||||
}
|
||||
|
||||
if ('primaryAxisOrderBy' in configuration) {
|
||||
configToUpdate.orderBy = configuration.primaryAxisOrderBy;
|
||||
}
|
||||
|
||||
return configToUpdate;
|
||||
};
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
import { type PieChartConfiguration } from '~/generated/graphql';
|
||||
|
||||
export const convertPieChartConfigToBarOrLineChart = (
|
||||
configuration: PieChartConfiguration,
|
||||
): Record<string, any> => {
|
||||
const configToUpdate: Record<string, any> = {};
|
||||
|
||||
if ('groupByFieldMetadataId' in configuration) {
|
||||
configToUpdate.primaryAxisGroupByFieldMetadataId =
|
||||
configuration.groupByFieldMetadataId;
|
||||
}
|
||||
|
||||
if ('groupBySubFieldName' in configuration) {
|
||||
configToUpdate.primaryAxisGroupBySubFieldName =
|
||||
configuration.groupBySubFieldName;
|
||||
}
|
||||
|
||||
if ('dateGranularity' in configuration) {
|
||||
configToUpdate.primaryAxisDateGranularity = configuration.dateGranularity;
|
||||
}
|
||||
|
||||
if ('orderBy' in configuration) {
|
||||
configToUpdate.primaryAxisOrderBy = configuration.orderBy;
|
||||
}
|
||||
|
||||
return configToUpdate;
|
||||
};
|
||||
+2
-2
@@ -9,10 +9,10 @@ import { DATE_GRANULARITY_Y_SETTING } from '@/command-menu/pages/page-layout/con
|
||||
import { FILTER_SETTING } from '@/command-menu/pages/page-layout/constants/settings/FilterSetting';
|
||||
import { GROUP_BY_SETTING } from '@/command-menu/pages/page-layout/constants/settings/GroupBySetting';
|
||||
import { OMIT_NULL_VALUES_SETTING } from '@/command-menu/pages/page-layout/constants/settings/OmitNullValuesSetting';
|
||||
import { PRIMARY_SORT_BY_SETTING } from '@/command-menu/pages/page-layout/constants/settings/PrimarySortBySetting';
|
||||
import { RANGE_MAX_SETTING } from '@/command-menu/pages/page-layout/constants/settings/RangeMaxSetting';
|
||||
import { RANGE_MIN_SETTING } from '@/command-menu/pages/page-layout/constants/settings/RangeMinSetting';
|
||||
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';
|
||||
import { IconAxisX, IconAxisY } from 'twenty-ui/display';
|
||||
@@ -29,7 +29,7 @@ export const getBarChartSettings = (
|
||||
const primaryAxisItems = [
|
||||
{ ...DATA_DISPLAY_X_SETTING, Icon: dataDisplayXIcon },
|
||||
DATE_GRANULARITY_X_SETTING,
|
||||
SORT_BY_X_SETTING,
|
||||
PRIMARY_SORT_BY_SETTING,
|
||||
OMIT_NULL_VALUES_SETTING,
|
||||
];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user