From 94546d7c094f56a0ff366c0689ff5cc94de74809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Bosi?= <71827178+bosiraphael@users.noreply.github.com> Date: Tue, 7 Oct 2025 18:33:07 +0200 Subject: [PATCH] Add composites subfield selection in the graph editor (#14931) Closes https://github.com/twentyhq/core-team-issues/issues/1638 Closes https://github.com/twentyhq/core-team-issues/issues/1623 https://github.com/user-attachments/assets/2dddadaa-781b-497d-b454-0066d599bda3 --------- Co-authored-by: Lucas Bordeau --- .../src/generated-metadata/graphql.ts | 5 + .../twenty-front/src/generated/graphql.ts | 5 + .../CommandMenuDefaultSelectionEffect.tsx | 7 +- .../components/CommandMenuItemDropdown.tsx | 9 + .../components/CommandMenuList.tsx | 3 +- .../CommandMenuListSelectableListId.ts | 1 + ...ommandMenuCloseAnimationCompleteCleanup.ts | 5 +- .../page-layout/components/ChartSettings.tsx | 14 +- ...egateOperationSelectionDropdownContent.tsx | 3 +- .../ChartAxisNameSelectionDropdownContent.tsx | 5 - .../ChartColorSelectionDropdownContent.tsx | 5 - .../ChartDataSourceDropdownContent.tsx | 5 - .../ChartFieldSelectionDropdownContent.tsx | 123 ------------ ...onForAggregateOperationDropdownContent.tsx | 9 +- ...roupByFieldSelectionCompositeFieldView.tsx | 94 +++++++++ ...rtGroupByFieldSelectionDropdownContent.tsx | 131 ++---------- ...oupByFieldSelectionDropdownContentBase.tsx | 187 ++++++++++++++++++ .../ChartPieFieldSelectionDropdownContent.tsx | 11 ++ ...ChartSortByGroupByFieldDropdownContent.tsx | 10 +- ...hartXAxisFieldSelectionDropdownContent.tsx | 16 ++ ...artXAxisSortBySelectionDropdownContent.tsx | 72 +++---- .../constants/settings/AxisNameSetting.ts | 2 + .../settings/ChartDataSourceSetting.ts | 2 + .../constants/settings/ColorsSetting.ts | 2 + .../constants/settings/DataDisplayXSetting.ts | 2 + .../constants/settings/DataDisplayYSetting.ts | 2 + .../settings/EachSliceRepresentsSetting.ts | 2 + .../constants/settings/GroupBySetting.ts | 2 + .../settings/SortByGroupByFieldSetting.ts | 2 + .../constants/settings/SortByXSetting.ts | 2 + .../hooks/useChartSettingsValues.ts | 21 +- .../hooks/useGraphGroupBySortOptionLabels.ts | 9 +- .../hooks/useGraphXSortOptionLabels.ts | 9 +- .../page-layout/types/ChartSettingsGroup.ts | 2 + .../utils/getChartSettingsDropdownContent.tsx | 35 ---- .../utils/getFieldLabelWithSubField.ts | 27 +++ .../dtos/bar-chart-configuration.dto.ts | 10 + .../dtos/line-chart-configuration.dto.ts | 10 + .../dtos/pie-chart-configuration.dto.ts | 5 + 39 files changed, 503 insertions(+), 363 deletions(-) create mode 100644 packages/twenty-front/src/modules/command-menu/constants/CommandMenuListSelectableListId.ts delete mode 100644 packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartFieldSelectionDropdownContent.tsx create mode 100644 packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionCompositeFieldView.tsx create mode 100644 packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionDropdownContentBase.tsx create mode 100644 packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartPieFieldSelectionDropdownContent.tsx create mode 100644 packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartXAxisFieldSelectionDropdownContent.tsx delete mode 100644 packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getChartSettingsDropdownContent.tsx create mode 100644 packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getFieldLabelWithSubField.ts diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 4a37d3de0dc..79b9df8ccbc 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -289,6 +289,8 @@ export type BarChartConfiguration = { graphType: GraphType; groupByFieldMetadataIdX: Scalars['UUID']; groupByFieldMetadataIdY?: Maybe; + groupBySubFieldNameX?: Maybe; + groupBySubFieldNameY?: Maybe; omitNullValues?: Maybe; orderByX: GraphOrderBy; orderByY?: Maybe; @@ -1598,6 +1600,8 @@ export type LineChartConfiguration = { graphType: GraphType; groupByFieldMetadataIdX: Scalars['UUID']; groupByFieldMetadataIdY?: Maybe; + groupBySubFieldNameX?: Maybe; + groupBySubFieldNameY?: Maybe; omitNullValues?: Maybe; orderByX: GraphOrderBy; orderByY?: Maybe; @@ -2955,6 +2959,7 @@ export type PieChartConfiguration = { filter?: Maybe; graphType: GraphType; groupByFieldMetadataId: Scalars['UUID']; + groupBySubFieldName?: Maybe; orderBy: GraphOrderBy; }; diff --git a/packages/twenty-front/src/generated/graphql.ts b/packages/twenty-front/src/generated/graphql.ts index 4e7ac5bdbf7..35b494a8056 100644 --- a/packages/twenty-front/src/generated/graphql.ts +++ b/packages/twenty-front/src/generated/graphql.ts @@ -289,6 +289,8 @@ export type BarChartConfiguration = { graphType: GraphType; groupByFieldMetadataIdX: Scalars['UUID']; groupByFieldMetadataIdY?: Maybe; + groupBySubFieldNameX?: Maybe; + groupBySubFieldNameY?: Maybe; omitNullValues?: Maybe; orderByX: GraphOrderBy; orderByY?: Maybe; @@ -1555,6 +1557,8 @@ export type LineChartConfiguration = { graphType: GraphType; groupByFieldMetadataIdX: Scalars['UUID']; groupByFieldMetadataIdY?: Maybe; + groupBySubFieldNameX?: Maybe; + groupBySubFieldNameY?: Maybe; omitNullValues?: Maybe; orderByX: GraphOrderBy; orderByY?: Maybe; @@ -2866,6 +2870,7 @@ export type PieChartConfiguration = { filter?: Maybe; graphType: GraphType; groupByFieldMetadataId: Scalars['UUID']; + groupBySubFieldName?: Maybe; orderBy: GraphOrderBy; }; diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuDefaultSelectionEffect.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuDefaultSelectionEffect.tsx index d46e17253ba..282fca12b34 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuDefaultSelectionEffect.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuDefaultSelectionEffect.tsx @@ -1,3 +1,4 @@ +import { COMMAND_MENU_LIST_SELECTABLE_LIST_ID } from '@/command-menu/constants/CommandMenuListSelectableListId'; import { hasUserSelectedCommandState } from '@/command-menu/states/hasUserSelectedCommandState'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; @@ -11,11 +12,13 @@ export const CommandMenuDefaultSelectionEffect = ({ }: { selectableItemIds: string[]; }) => { - const { setSelectedItemId } = useSelectableList('command-menu-list'); + const { setSelectedItemId } = useSelectableList( + COMMAND_MENU_LIST_SELECTABLE_LIST_ID, + ); const selectedItemId = useRecoilComponentValue( selectedItemIdComponentState, - 'command-menu-list', + COMMAND_MENU_LIST_SELECTABLE_LIST_ID, ); const hasUserSelectedCommand = useRecoilValue(hasUserSelectedCommandState); diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuItemDropdown.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuItemDropdown.tsx index 31d8bd4426e..68c0989c620 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuItemDropdown.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuItemDropdown.tsx @@ -2,11 +2,13 @@ import { CommandMenuItem, type CommandMenuItemProps, } from '@/command-menu/components/CommandMenuItem'; +import { COMMAND_MENU_LIST_SELECTABLE_LIST_ID } from '@/command-menu/constants/CommandMenuListSelectableListId'; import { Dropdown, type DropdownProps, } from '@/ui/layout/dropdown/components/Dropdown'; import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState'; +import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; export type CommandMenuItemDropdownProps = CommandMenuItemProps & @@ -34,6 +36,10 @@ export const CommandMenuItemDropdown = ({ dropdownId, ); + const { setSelectedItemId } = useSelectableList( + COMMAND_MENU_LIST_SELECTABLE_LIST_ID, + ); + return ( { + setSelectedItemId(id); + }} /> ); }; diff --git a/packages/twenty-front/src/modules/command-menu/components/CommandMenuList.tsx b/packages/twenty-front/src/modules/command-menu/components/CommandMenuList.tsx index 8f8b568e206..1151b550a97 100644 --- a/packages/twenty-front/src/modules/command-menu/components/CommandMenuList.tsx +++ b/packages/twenty-front/src/modules/command-menu/components/CommandMenuList.tsx @@ -2,6 +2,7 @@ import { ActionComponent } from '@/action-menu/actions/display/components/Action import { CommandGroup } from '@/command-menu/components/CommandGroup'; import { type ActionGroupConfig } from '@/command-menu/components/CommandMenu'; import { CommandMenuDefaultSelectionEffect } from '@/command-menu/components/CommandMenuDefaultSelectionEffect'; +import { COMMAND_MENU_LIST_SELECTABLE_LIST_ID } from '@/command-menu/constants/CommandMenuListSelectableListId'; import { COMMAND_MENU_SEARCH_BAR_HEIGHT } from '@/command-menu/constants/CommandMenuSearchBarHeight'; import { COMMAND_MENU_SEARCH_BAR_PADDING } from '@/command-menu/constants/CommandMenuSearchBarPadding'; import { SIDE_PANEL_FOCUS_ID } from '@/command-menu/constants/SidePanelFocusId'; @@ -74,7 +75,7 @@ export const CommandMenuList = ({ { diff --git a/packages/twenty-front/src/modules/command-menu/constants/CommandMenuListSelectableListId.ts b/packages/twenty-front/src/modules/command-menu/constants/CommandMenuListSelectableListId.ts new file mode 100644 index 00000000000..794415147b0 --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/constants/CommandMenuListSelectableListId.ts @@ -0,0 +1 @@ +export const COMMAND_MENU_LIST_SELECTABLE_LIST_ID = 'command-menu-list'; diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuCloseAnimationCompleteCleanup.ts b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuCloseAnimationCompleteCleanup.ts index a4d3685c68c..dea70b558e6 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuCloseAnimationCompleteCleanup.ts +++ b/packages/twenty-front/src/modules/command-menu/hooks/useCommandMenuCloseAnimationCompleteCleanup.ts @@ -1,5 +1,6 @@ import { COMMAND_MENU_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/CommandMenuComponentInstanceId'; import { COMMAND_MENU_CONTEXT_CHIP_GROUPS_DROPDOWN_ID } from '@/command-menu/constants/CommandMenuContextChipGroupsDropdownId'; +import { COMMAND_MENU_LIST_SELECTABLE_LIST_ID } from '@/command-menu/constants/CommandMenuListSelectableListId'; import { COMMAND_MENU_PREVIOUS_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/CommandMenuPreviousComponentInstanceId'; import { useResetContextStoreStates } from '@/command-menu/hooks/useResetContextStoreStates'; import { commandMenuNavigationMorphItemByPageState } from '@/command-menu/states/commandMenuNavigationMorphItemsState'; @@ -27,7 +28,9 @@ import { useRecoilCallback } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; export const useCommandMenuCloseAnimationCompleteCleanup = () => { - const { resetSelectedItem } = useSelectableList('command-menu-list'); + const { resetSelectedItem } = useSelectableList( + COMMAND_MENU_LIST_SELECTABLE_LIST_ID, + ); const { resetContextStoreStates } = useResetContextStoreStates(); 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 7068d5e3821..b687d2ef8fd 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 @@ -2,6 +2,7 @@ import { CommandGroup } from '@/command-menu/components/CommandGroup'; import { CommandMenuItemDropdown } from '@/command-menu/components/CommandMenuItemDropdown'; import { CommandMenuItemToggle } from '@/command-menu/components/CommandMenuItemToggle'; import { CommandMenuList } from '@/command-menu/components/CommandMenuList'; +import { COMMAND_MENU_LIST_SELECTABLE_LIST_ID } from '@/command-menu/constants/CommandMenuListSelectableListId'; import { useUpdateCommandMenuPageInfo } from '@/command-menu/hooks/useUpdateCommandMenuPageInfo'; import { ChartTypeSelectionSection } from '@/command-menu/pages/page-layout/components/ChartTypeSelectionSection'; import { GRAPH_TYPE_INFORMATION } from '@/command-menu/pages/page-layout/constants/GraphTypeInformation'; @@ -14,11 +15,10 @@ import { CHART_CONFIGURATION_SETTING_IDS, CHART_CONFIGURATION_SETTING_TO_CONFIG_KEY_MAP, } from '@/command-menu/pages/page-layout/types/ChartConfigurationSettingIds'; -import { getChartSettingsDropdownContent } from '@/command-menu/pages/page-layout/utils/getChartSettingsDropdownContent'; import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; -import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useOpenDropdown } from '@/ui/layout/dropdown/hooks/useOpenDropdown'; import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; +import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; import { t } from '@lingui/core/macro'; import { isNonEmptyString } from '@sniptt/guards'; @@ -62,6 +62,10 @@ export const ChartSettings = ({ widget }: { widget: PageLayoutWidget }) => { const chartSettings = GRAPH_TYPE_INFORMATION[currentGraphType].settings; + const { setSelectedItemId } = useSelectableList( + COMMAND_MENU_LIST_SELECTABLE_LIST_ID, + ); + return ( { ] : item.id; + setSelectedItemId(item.id); + updateCurrentWidgetConfig({ configToUpdate: { [configKey]: !getChartSettingsValues(item.id), @@ -131,9 +137,7 @@ export const ChartSettings = ({ widget }: { widget: PageLayoutWidget }) => { dropdownId={item.id} dropdownComponents={ - - {getChartSettingsDropdownContent(item.id)} - + {item.DropdownContent && } } dropdownPlacement="bottom-end" 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 562c3e7843e..de41305641c 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 @@ -19,7 +19,6 @@ import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { t } from '@lingui/core/macro'; -import { Trans } from '@lingui/react/macro'; import { useState } from 'react'; import { isDefined } from 'twenty-shared/utils'; import { IconChevronLeft } from 'twenty-ui/display'; @@ -116,7 +115,7 @@ export const ChartAggregateOperationSelectionDropdownContent = ({ /> } > - Y-Axis Aggregate Operation + {`${selectedField.label}`} { return ( <> - - Axis Name - { return ( <> - - Color - { return ( <> - - Source - { - const [searchQuery, setSearchQuery] = useState(''); - const { objectMetadataItems } = useObjectMetadataItems(); - const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord(); - const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId); - - if ( - widgetInEditMode?.configuration?.__typename !== 'BarChartConfiguration' && - widgetInEditMode?.configuration?.__typename !== 'LineChartConfiguration' - ) { - throw new Error('Invalid configuration type'); - } - - const currentXAxisFieldMetadataId = - widgetInEditMode.configuration.groupByFieldMetadataIdX; - - const sourceObjectMetadataItem = objectMetadataItems.find( - (item) => item.id === widgetInEditMode.objectMetadataId, - ); - - const dropdownId = useAvailableComponentInstanceIdOrThrow( - DropdownComponentInstanceContext, - ); - - const selectedItemId = useRecoilComponentValue( - selectedItemIdComponentState, - dropdownId, - ); - - const availableFieldMetadataItems = filterBySearchQuery({ - items: sourceObjectMetadataItem?.fields || [], - searchQuery, - getSearchableValues: (item) => [item.label, item.name], - }); - - const { updateCurrentWidgetConfig } = - useUpdateCurrentWidgetConfig(pageLayoutId); - - const { closeDropdown } = useCloseDropdown(); - - const { getIcon } = useIcons(); - - if (!isDefined(sourceObjectMetadataItem)) { - return null; - } - - const handleSelectField = (fieldMetadataId: string) => { - updateCurrentWidgetConfig({ - configToUpdate: { - groupByFieldMetadataIdX: fieldMetadataId, - }, - }); - closeDropdown(); - }; - - return ( - <> - - X-Axis Field - - setSearchQuery(event.target.value)} - value={searchQuery} - /> - - - item.id, - )} - > - {availableFieldMetadataItems.map((fieldMetadataItem) => ( - { - handleSelectField(fieldMetadataItem.id); - }} - > - { - handleSelectField(fieldMetadataItem.id); - }} - /> - - ))} - - - - ); -}; 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 e3c2d0440f5..1c6e3dc9b0d 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 @@ -2,7 +2,7 @@ import { ChartAggregateOperationSelectionDropdownContent } from '@/command-menu/ import { usePageLayoutIdFromContextStoreTargetedRecord } from '@/command-menu/pages/page-layout/hooks/usePageLayoutFromContextStoreTargetedRecord'; import { useWidgetInEditMode } from '@/command-menu/pages/page-layout/hooks/useWidgetInEditMode'; import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; -import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader'; +import { isFieldRelation } from '@/object-record/record-field/ui/types/guards/isFieldRelation'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; @@ -13,7 +13,6 @@ import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { t } from '@lingui/core/macro'; -import { Trans } from '@lingui/react/macro'; import { useState } from 'react'; import { isDefined } from 'twenty-shared/utils'; import { useIcons } from 'twenty-ui/display'; @@ -59,7 +58,8 @@ export const ChartFieldSelectionForAggregateOperationDropdownContent = () => { items: sourceObjectMetadataItem?.fields || [], searchQuery, getSearchableValues: (item) => [item.label, item.name], - }); + // TODO: remove this once group by is supported for relation fields + }).filter((field) => !isFieldRelation(field)); const { getIcon } = useIcons(); @@ -78,9 +78,6 @@ export const ChartFieldSelectionForAggregateOperationDropdownContent = () => { return ( <> - - Y-Axis Field - void; + onSelectSubField: (subFieldName: string) => void; +}; + +export const ChartGroupByFieldSelectionCompositeFieldView = ({ + compositeField, + currentSubFieldName, + onBack, + onSelectSubField, +}: ChartGroupByFieldSelectionCompositeFieldViewProps) => { + const { getIcon } = useIcons(); + + const dropdownId = useAvailableComponentInstanceIdOrThrow( + DropdownComponentInstanceContext, + ); + + const selectedItemId = useRecoilComponentValue( + selectedItemIdComponentState, + dropdownId, + ); + + const compositeFieldType = compositeField.type as CompositeFieldType; + + const subFieldNames = SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS[ + compositeFieldType + ].subFields.map((subField) => subField.subFieldName); + + return ( + <> + + } + > + {compositeField.label} + + + + {subFieldNames.map((subFieldName) => ( + { + onSelectSubField(subFieldName); + }} + > + { + onSelectSubField(subFieldName); + }} + LeftIcon={getIcon( + ICON_NAME_BY_SUB_FIELD[subFieldName] ?? compositeField.icon, + )} + /> + + ))} + + + + ); +}; 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 643f068d0cb..48d173f44a1 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,125 +1,16 @@ -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 { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; -import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader'; -import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; -import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput'; -import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; -import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext'; -import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; -import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; -import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; -import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; -import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; -import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; -import { t } from '@lingui/core/macro'; -import { Trans } from '@lingui/react/macro'; -import { useState } from 'react'; -import { isDefined } from 'twenty-shared/utils'; -import { useIcons } from 'twenty-ui/display'; -import { MenuItemSelect } from 'twenty-ui/navigation'; -import { filterBySearchQuery } from '~/utils/filterBySearchQuery'; +import { ChartGroupByFieldSelectionDropdownContentBase } from '@/command-menu/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionDropdownContentBase'; +import { + type BarChartConfiguration, + type LineChartConfiguration, +} from '~/generated/graphql'; export const ChartGroupByFieldSelectionDropdownContent = () => { - const [searchQuery, setSearchQuery] = useState(''); - const { objectMetadataItems } = useObjectMetadataItems(); - const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord(); - const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId); - - if ( - widgetInEditMode?.configuration?.__typename !== 'BarChartConfiguration' && - widgetInEditMode?.configuration?.__typename !== 'LineChartConfiguration' - ) { - throw new Error('Invalid configuration type'); - } - - const currentGroupByFieldMetadataId = - widgetInEditMode.configuration.groupByFieldMetadataIdY; - - const sourceObjectMetadataItem = objectMetadataItems.find( - (item) => item.id === widgetInEditMode.objectMetadataId, - ); - - const dropdownId = useAvailableComponentInstanceIdOrThrow( - DropdownComponentInstanceContext, - ); - - const selectedItemId = useRecoilComponentValue( - selectedItemIdComponentState, - dropdownId, - ); - - const availableFieldMetadataItems = filterBySearchQuery({ - items: sourceObjectMetadataItem?.fields || [], - searchQuery, - getSearchableValues: (item) => [item.label, item.name], - }); - - const { updateCurrentWidgetConfig } = - useUpdateCurrentWidgetConfig(pageLayoutId); - - const { closeDropdown } = useCloseDropdown(); - - const { getIcon } = useIcons(); - - if (!isDefined(sourceObjectMetadataItem)) { - return null; - } - - const handleSelectField = (fieldMetadataId: string) => { - updateCurrentWidgetConfig({ - configToUpdate: { - groupByFieldMetadataIdY: fieldMetadataId, - }, - }); - closeDropdown(); - }; - return ( - <> - - Y-Axis Group By Field - - setSearchQuery(event.target.value)} - value={searchQuery} - /> - - - item.id, - )} - > - {availableFieldMetadataItems.map((fieldMetadataItem) => ( - { - handleSelectField(fieldMetadataItem.id); - }} - > - { - handleSelectField(fieldMetadataItem.id); - }} - /> - - ))} - - - + + fieldMetadataIdKey="groupByFieldMetadataIdY" + subFieldNameKey="groupBySubFieldNameY" + /> ); }; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionDropdownContentBase.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionDropdownContentBase.tsx new file mode 100644 index 00000000000..a2fa2a68601 --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionDropdownContentBase.tsx @@ -0,0 +1,187 @@ +import { ChartGroupByFieldSelectionCompositeFieldView } from '@/command-menu/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionCompositeFieldView'; +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 { type ChartConfiguration } from '@/command-menu/pages/page-layout/types/ChartConfiguration'; +import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; +import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; +import { isCompositeFieldType } from '@/object-record/object-filter-dropdown/utils/isCompositeFieldType'; +import { isFieldRelation } from '@/object-record/record-field/ui/types/guards/isFieldRelation'; +import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; +import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput'; +import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; +import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext'; +import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; +import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; +import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; +import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; +import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; +import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { t } from '@lingui/core/macro'; +import { useMemo, useState } from 'react'; +import { isDefined } from 'twenty-shared/utils'; +import { useIcons } from 'twenty-ui/display'; +import { MenuItemSelect } from 'twenty-ui/navigation'; +import { filterBySearchQuery } from '~/utils/filterBySearchQuery'; + +type ChartGroupByFieldSelectionDropdownContentBaseProps< + T extends ChartConfiguration, +> = { + fieldMetadataIdKey: keyof T; + subFieldNameKey: keyof T; +}; + +export const ChartGroupByFieldSelectionDropdownContentBase = < + T extends ChartConfiguration, +>({ + fieldMetadataIdKey, + subFieldNameKey, +}: ChartGroupByFieldSelectionDropdownContentBaseProps) => { + const [searchQuery, setSearchQuery] = useState(''); + + const [selectedCompositeField, setSelectedCompositeField] = + useState(null); + + const { objectMetadataItems } = useObjectMetadataItems(); + + const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord(); + + const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId); + + const configuration = widgetInEditMode?.configuration as T; + + const currentGroupByFieldMetadataId = configuration?.[fieldMetadataIdKey] as + | string + | undefined; + const currentSubFieldName = configuration?.[subFieldNameKey] as + | string + | undefined; + + const sourceObjectMetadataItem = objectMetadataItems.find( + (item) => item.id === widgetInEditMode?.objectMetadataId, + ); + + const dropdownId = useAvailableComponentInstanceIdOrThrow( + DropdownComponentInstanceContext, + ); + + const selectedItemId = useRecoilComponentValue( + selectedItemIdComponentState, + dropdownId, + ); + + const availableFieldMetadataItems = useMemo( + () => + filterBySearchQuery({ + items: sourceObjectMetadataItem?.fields || [], + searchQuery, + getSearchableValues: (item) => [item.label, item.name], + // TODO: remove this once group by is supported for relation fields + }).filter((field) => !isFieldRelation(field)), + [sourceObjectMetadataItem?.fields, searchQuery], + ); + + const { updateCurrentWidgetConfig } = + useUpdateCurrentWidgetConfig(pageLayoutId); + + const { closeDropdown } = useCloseDropdown(); + + const { getIcon } = useIcons(); + + if (!isDefined(sourceObjectMetadataItem)) { + return null; + } + + const handleSelectField = (fieldMetadataItem: FieldMetadataItem) => { + if (isCompositeFieldType(fieldMetadataItem.type)) { + setSelectedCompositeField(fieldMetadataItem); + } else { + updateCurrentWidgetConfig({ + configToUpdate: { + [fieldMetadataIdKey]: fieldMetadataItem.id, + [subFieldNameKey]: null, + }, + }); + closeDropdown(); + } + }; + + const handleBack = () => { + setSelectedCompositeField(null); + }; + + const handleSelectSubField = (subFieldName: string) => { + if (!isDefined(selectedCompositeField)) { + return; + } + + updateCurrentWidgetConfig({ + configToUpdate: { + [fieldMetadataIdKey]: selectedCompositeField.id, + [subFieldNameKey]: subFieldName, + }, + }); + closeDropdown(); + }; + + if (isDefined(selectedCompositeField)) { + return ( + + ); + } + + return ( + <> + setSearchQuery(event.target.value)} + value={searchQuery} + /> + + + {availableFieldMetadataItems.length === 0 ? ( + + ) : ( + item.id, + )} + > + {availableFieldMetadataItems.map((fieldMetadataItem) => ( + { + handleSelectField(fieldMetadataItem); + }} + > + { + handleSelectField(fieldMetadataItem); + }} + /> + + ))} + + )} + + + ); +}; 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 new file mode 100644 index 00000000000..11d982606d7 --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartPieFieldSelectionDropdownContent.tsx @@ -0,0 +1,11 @@ +import { ChartGroupByFieldSelectionDropdownContentBase } from '@/command-menu/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionDropdownContentBase'; +import { type PieChartConfiguration } from '~/generated/graphql'; + +export const ChartPieFieldSelectionDropdownContent = () => { + return ( + + fieldMetadataIdKey="groupByFieldMetadataId" + subFieldNameKey="groupBySubFieldName" + /> + ); +}; 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 39a3ac7d38d..2f14a549761 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,6 @@ 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 { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext'; import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; @@ -21,13 +20,7 @@ import { type NumberChartConfiguration, } from '~/generated/graphql'; -type ChartSortByGroupByFieldDropdownContentProps = { - title: string; -}; - -export const ChartSortByGroupByFieldDropdownContent = ({ - title, -}: ChartSortByGroupByFieldDropdownContentProps) => { +export const ChartSortByGroupByFieldDropdownContent = () => { const { pageLayoutId } = usePageLayoutIdFromContextStoreTargetedRecord(); const { widgetInEditMode } = useWidgetInEditMode(pageLayoutId); @@ -74,7 +67,6 @@ export const ChartSortByGroupByFieldDropdownContent = ({ return ( <> - {title} { + return ( + + fieldMetadataIdKey="groupByFieldMetadataIdX" + subFieldNameKey="groupBySubFieldNameX" + /> + ); +}; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartXAxisSortBySelectionDropdownContent.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartXAxisSortBySelectionDropdownContent.tsx index 628dd070361..90c19d0704a 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartXAxisSortBySelectionDropdownContent.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/components/dropdown-content/ChartXAxisSortBySelectionDropdownContent.tsx @@ -3,7 +3,6 @@ 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 { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponentInstanceContext'; import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; @@ -12,7 +11,6 @@ import { SelectableListItem } from '@/ui/layout/selectable-list/components/Selec import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; -import { Trans } from '@lingui/react/macro'; import { MenuItemSelect } from 'twenty-ui/navigation'; import { type GraphOrderBy } from '~/generated/graphql'; @@ -59,46 +57,38 @@ export const ChartXAxisSortBySelectionDropdownContent = () => { }); return ( - <> - - X-Axis Sort By - - - option.value, - )} - > - {X_SORT_BY_OPTIONS.map((sortOption) => ( - { + + option.value)} + > + {X_SORT_BY_OPTIONS.map((sortOption) => ( + { + handleSelectSortOption(sortOption.value); + }} + > + { handleSelectSortOption(sortOption.value); }} - > - { - handleSelectSortOption(sortOption.value); - }} - /> - - ))} - - - + /> + + ))} + + ); }; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/AxisNameSetting.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/AxisNameSetting.ts index 847c42cf2a0..f9aee4a4ce9 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/AxisNameSetting.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/AxisNameSetting.ts @@ -1,3 +1,4 @@ +import { ChartAxisNameSelectionDropdownContent } from '@/command-menu/pages/page-layout/components/dropdown-content/ChartAxisNameSelectionDropdownContent'; 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'; @@ -8,4 +9,5 @@ export const AXIS_NAME_SETTING: ChartSettingsItem = { Icon: IconGizmo, label: CHART_CONFIGURATION_SETTING_LABELS.AXIS_NAME, id: CHART_CONFIGURATION_SETTING_IDS.AXIS_NAME, + DropdownContent: ChartAxisNameSelectionDropdownContent, }; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/ChartDataSourceSetting.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/ChartDataSourceSetting.ts index 734ffa61251..eced94ec349 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/ChartDataSourceSetting.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/ChartDataSourceSetting.ts @@ -1,3 +1,4 @@ +import { ChartDataSourceDropdownContent } from '@/command-menu/pages/page-layout/components/dropdown-content/ChartDataSourceDropdownContent'; 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'; @@ -8,4 +9,5 @@ export const CHART_DATA_SOURCE_SETTING: ChartSettingsItem = { Icon: IconDatabase, label: CHART_CONFIGURATION_SETTING_LABELS.SOURCE, id: CHART_CONFIGURATION_SETTING_IDS.SOURCE, + DropdownContent: ChartDataSourceDropdownContent, }; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/ColorsSetting.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/ColorsSetting.ts index 6988b0804f7..5c663567b8e 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/ColorsSetting.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/ColorsSetting.ts @@ -1,3 +1,4 @@ +import { ChartColorSelectionDropdownContent } from '@/command-menu/pages/page-layout/components/dropdown-content/ChartColorSelectionDropdownContent'; 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'; @@ -8,4 +9,5 @@ export const COLORS_SETTING: ChartSettingsItem = { Icon: IconColorSwatch, label: CHART_CONFIGURATION_SETTING_LABELS.COLORS, id: CHART_CONFIGURATION_SETTING_IDS.COLORS, + DropdownContent: ChartColorSelectionDropdownContent, }; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/DataDisplayXSetting.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/DataDisplayXSetting.ts index d61e8ffeb98..f11ed0ca4a5 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/DataDisplayXSetting.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/DataDisplayXSetting.ts @@ -1,3 +1,4 @@ +import { ChartXAxisFieldSelectionDropdownContent } from '@/command-menu/pages/page-layout/components/dropdown-content/ChartXAxisFieldSelectionDropdownContent'; 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_X_SETTING: ChartSettingsItem = { label: CHART_CONFIGURATION_SETTING_LABELS.DATA_ON_DISPLAY_X, id: CHART_CONFIGURATION_SETTING_IDS.DATA_ON_DISPLAY_X, dependsOn: [CHART_CONFIGURATION_SETTING_IDS.SOURCE], + DropdownContent: ChartXAxisFieldSelectionDropdownContent, }; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/DataDisplayYSetting.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/DataDisplayYSetting.ts index d0bb10269cc..fe0b163a6f2 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/DataDisplayYSetting.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/DataDisplayYSetting.ts @@ -1,3 +1,4 @@ +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,4 +10,5 @@ export const DATA_DISPLAY_Y_SETTING: ChartSettingsItem = { label: CHART_CONFIGURATION_SETTING_LABELS.DATA_ON_DISPLAY_Y, id: CHART_CONFIGURATION_SETTING_IDS.DATA_ON_DISPLAY_Y, dependsOn: [CHART_CONFIGURATION_SETTING_IDS.SOURCE], + DropdownContent: ChartFieldSelectionForAggregateOperationDropdownContent, }; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/EachSliceRepresentsSetting.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/EachSliceRepresentsSetting.ts index 3e745706ea9..a98ef99d9a1 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/EachSliceRepresentsSetting.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/EachSliceRepresentsSetting.ts @@ -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'; @@ -8,4 +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, }; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/GroupBySetting.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/GroupBySetting.ts index 0f8caae90d0..77562010014 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/GroupBySetting.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/GroupBySetting.ts @@ -1,3 +1,4 @@ +import { ChartGroupByFieldSelectionDropdownContent } from '@/command-menu/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionDropdownContent'; 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 GROUP_BY_SETTING: ChartSettingsItem = { label: CHART_CONFIGURATION_SETTING_LABELS.GROUP_BY, id: CHART_CONFIGURATION_SETTING_IDS.GROUP_BY, dependsOn: [CHART_CONFIGURATION_SETTING_IDS.SOURCE], + DropdownContent: ChartGroupByFieldSelectionDropdownContent, }; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/SortByGroupByFieldSetting.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/SortByGroupByFieldSetting.ts index 5cb95057dc5..1a08e6108b3 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/SortByGroupByFieldSetting.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/SortByGroupByFieldSetting.ts @@ -1,3 +1,4 @@ +import { ChartSortByGroupByFieldDropdownContent } from '@/command-menu/pages/page-layout/components/dropdown-content/ChartSortByGroupByFieldDropdownContent'; 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'; @@ -8,4 +9,5 @@ export const SORT_BY_GROUP_BY_FIELD_SETTING: ChartSettingsItem = { Icon: IconArrowsSort, label: CHART_CONFIGURATION_SETTING_LABELS.SORT_BY_GROUP_BY_FIELD, id: CHART_CONFIGURATION_SETTING_IDS.SORT_BY_GROUP_BY_FIELD, + DropdownContent: ChartSortByGroupByFieldDropdownContent, }; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/SortByXSetting.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/SortByXSetting.ts index 258df0caafe..13dd5fd283b 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/SortByXSetting.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/constants/settings/SortByXSetting.ts @@ -1,3 +1,4 @@ +import { ChartXAxisSortBySelectionDropdownContent } from '@/command-menu/pages/page-layout/components/dropdown-content/ChartXAxisSortBySelectionDropdownContent'; 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'; @@ -8,4 +9,5 @@ export const SORT_BY_X_SETTING: ChartSettingsItem = { Icon: IconArrowsSort, label: CHART_CONFIGURATION_SETTING_LABELS.SORT_BY_X, id: CHART_CONFIGURATION_SETTING_IDS.SORT_BY_X, + DropdownContent: ChartXAxisSortBySelectionDropdownContent, }; 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 3b87490d77b..78b109e2ea5 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 @@ -3,9 +3,11 @@ import { useGraphXSortOptionLabels } from '@/command-menu/pages/page-layout/hook 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 { getChartAxisNameDisplayOptions } from '@/command-menu/pages/page-layout/utils/getChartAxisNameDisplayOptions'; +import { getFieldLabelWithSubField } from '@/command-menu/pages/page-layout/utils/getFieldLabelWithSubField'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { getAggregateOperationLabel } from '@/object-record/record-board/record-board-column/utils/getAggregateOperationLabel'; import { useRecoilValue } from 'recoil'; +import { type CompositeFieldSubFieldName } from 'twenty-shared/types'; import { capitalize, isDefined } from 'twenty-shared/utils'; export const useChartSettingsValues = ({ @@ -43,6 +45,15 @@ export const useChartSettingsValues = ({ ) : undefined; + const groupBySubFieldNameXLabel = + 'groupBySubFieldNameX' in configuration && isDefined(groupByFieldX) + ? getFieldLabelWithSubField({ + field: groupByFieldX, + subFieldName: + configuration.groupBySubFieldNameX as CompositeFieldSubFieldName, + }) + : undefined; + const aggregateField = objectMetadataItem?.fields.find( (fieldMetadataItem) => fieldMetadataItem.id === configuration.aggregateFieldMetadataId, @@ -66,6 +77,8 @@ export const useChartSettingsValues = ({ ? getXSortOptionLabel({ graphOrderBy: xAxisOrderBy, groupByFieldMetadataIdX: configuration.groupByFieldMetadataIdX, + groupBySubFieldNameX: + configuration.groupBySubFieldNameX as CompositeFieldSubFieldName, aggregateFieldMetadataId: configuration.aggregateFieldMetadataId, aggregateOperation: configuration.aggregateOperation, }) @@ -88,6 +101,12 @@ export const useChartSettingsValues = ({ : 'groupByFieldMetadataId' in configuration ? configuration.groupByFieldMetadataId : undefined, + groupBySubFieldName: + 'groupBySubFieldNameY' in configuration + ? (configuration.groupBySubFieldNameY as CompositeFieldSubFieldName) + : 'groupBySubFieldName' in configuration + ? (configuration.groupBySubFieldName as CompositeFieldSubFieldName) + : undefined, }); const getChartSettingsValues = ( @@ -97,7 +116,7 @@ export const useChartSettingsValues = ({ case CHART_CONFIGURATION_SETTING_IDS.SOURCE: return objectMetadataItem?.labelPlural; case CHART_CONFIGURATION_SETTING_IDS.DATA_ON_DISPLAY_X: - return groupByFieldX?.label; + return groupBySubFieldNameXLabel; case CHART_CONFIGURATION_SETTING_IDS.COLORS: return isDefined(configuration.color) && 'color' in configuration ? capitalize(configuration.color) diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/hooks/useGraphGroupBySortOptionLabels.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/hooks/useGraphGroupBySortOptionLabels.ts index fc433f9afdb..7005c192f78 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/hooks/useGraphGroupBySortOptionLabels.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/hooks/useGraphGroupBySortOptionLabels.ts @@ -1,6 +1,8 @@ +import { getFieldLabelWithSubField } from '@/command-menu/pages/page-layout/utils/getFieldLabelWithSubField'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { t } from '@lingui/core/macro'; import { useRecoilValue } from 'recoil'; +import { type CompositeFieldSubFieldName } from 'twenty-shared/types'; import { GraphOrderBy } from '~/generated/graphql'; export const useGraphGroupBySortOptionLabels = ({ @@ -17,15 +19,20 @@ export const useGraphGroupBySortOptionLabels = ({ const getGroupBySortOptionLabel = ({ graphOrderBy, groupByFieldMetadataId, + groupBySubFieldName, }: { graphOrderBy: GraphOrderBy; groupByFieldMetadataId: string; + groupBySubFieldName?: CompositeFieldSubFieldName; }): string => { const field = objectMetadataItem?.fields.find( (fieldMetadataItem) => fieldMetadataItem.id === groupByFieldMetadataId, ); - const fieldLabel = field?.label || t`Field`; + const fieldLabel = getFieldLabelWithSubField({ + field, + subFieldName: groupBySubFieldName, + }); switch (graphOrderBy) { case GraphOrderBy.FIELD_ASC: diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/hooks/useGraphXSortOptionLabels.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/hooks/useGraphXSortOptionLabels.ts index cefed01a8f2..6dcce7b6f3a 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/hooks/useGraphXSortOptionLabels.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/hooks/useGraphXSortOptionLabels.ts @@ -1,7 +1,9 @@ +import { getFieldLabelWithSubField } from '@/command-menu/pages/page-layout/utils/getFieldLabelWithSubField'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { getAggregateOperationLabel } from '@/object-record/record-board/record-board-column/utils/getAggregateOperationLabel'; import { t } from '@lingui/core/macro'; import { useRecoilValue } from 'recoil'; +import { type CompositeFieldSubFieldName } from 'twenty-shared/types'; import { assertUnreachable, isDefined } from 'twenty-shared/utils'; import { type ExtendedAggregateOperations, @@ -22,11 +24,13 @@ export const useGraphXSortOptionLabels = ({ const getXSortOptionLabel = ({ graphOrderBy, groupByFieldMetadataIdX, + groupBySubFieldNameX, aggregateFieldMetadataId, aggregateOperation, }: { graphOrderBy: GraphOrderBy; groupByFieldMetadataIdX: string; + groupBySubFieldNameX?: CompositeFieldSubFieldName; aggregateFieldMetadataId?: string; aggregateOperation?: ExtendedAggregateOperations; }): string => { @@ -34,7 +38,10 @@ export const useGraphXSortOptionLabels = ({ (fieldMetadataItem) => fieldMetadataItem.id === groupByFieldMetadataIdX, ); - const fieldLabel = groupByField?.label || t`Field`; + const fieldLabel = getFieldLabelWithSubField({ + field: groupByField, + subFieldName: groupBySubFieldNameX, + }); const aggregateField = objectMetadataItem?.fields.find( (fieldMetadataItem) => diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/ChartSettingsGroup.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/ChartSettingsGroup.ts index cb054ea70f1..20aa3fed415 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/ChartSettingsGroup.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/types/ChartSettingsGroup.ts @@ -1,5 +1,6 @@ import { type CHART_CONFIGURATION_SETTING_IDS } from '@/command-menu/pages/page-layout/types/ChartConfigurationSettingIds'; import { type MessageDescriptor } from '@lingui/core'; +import { type ComponentType } from 'react'; import { type IconComponent } from 'twenty-ui/display'; export type ChartSettingsGroup = { @@ -14,4 +15,5 @@ export type ChartSettingsItem = { description?: string; isBoolean: boolean; dependsOn?: CHART_CONFIGURATION_SETTING_IDS[]; + DropdownContent?: ComponentType; }; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getChartSettingsDropdownContent.tsx b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getChartSettingsDropdownContent.tsx deleted file mode 100644 index d6fc3c7ad03..00000000000 --- a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getChartSettingsDropdownContent.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { ChartAxisNameSelectionDropdownContent } from '@/command-menu/pages/page-layout/components/dropdown-content/ChartAxisNameSelectionDropdownContent'; -import { ChartColorSelectionDropdownContent } from '@/command-menu/pages/page-layout/components/dropdown-content/ChartColorSelectionDropdownContent'; -import { ChartDataSourceDropdownContent } from '@/command-menu/pages/page-layout/components/dropdown-content/ChartDataSourceDropdownContent'; -import { ChartFieldSelectionDropdownContent } from '@/command-menu/pages/page-layout/components/dropdown-content/ChartFieldSelectionDropdownContent'; -import { ChartFieldSelectionForAggregateOperationDropdownContent } from '@/command-menu/pages/page-layout/components/dropdown-content/ChartFieldSelectionForAggregateOperationDropdownContent'; -import { ChartGroupByFieldSelectionDropdownContent } from '@/command-menu/pages/page-layout/components/dropdown-content/ChartGroupByFieldSelectionDropdownContent'; -import { ChartSortByGroupByFieldDropdownContent } from '@/command-menu/pages/page-layout/components/dropdown-content/ChartSortByGroupByFieldDropdownContent'; -import { ChartXAxisSortBySelectionDropdownContent } from '@/command-menu/pages/page-layout/components/dropdown-content/ChartXAxisSortBySelectionDropdownContent'; -import { CHART_CONFIGURATION_SETTING_IDS } from '@/command-menu/pages/page-layout/types/ChartConfigurationSettingIds'; -import { t } from '@lingui/core/macro'; - -export const getChartSettingsDropdownContent = (itemId: string) => { - switch (itemId) { - case CHART_CONFIGURATION_SETTING_IDS.SOURCE: - return ; - case CHART_CONFIGURATION_SETTING_IDS.DATA_ON_DISPLAY_X: - return ; - case CHART_CONFIGURATION_SETTING_IDS.SORT_BY_X: - return ; - case CHART_CONFIGURATION_SETTING_IDS.DATA_ON_DISPLAY_Y: - return ; - case CHART_CONFIGURATION_SETTING_IDS.SORT_BY_GROUP_BY_FIELD: - return ( - - ); - case CHART_CONFIGURATION_SETTING_IDS.GROUP_BY: - return ; - case CHART_CONFIGURATION_SETTING_IDS.COLORS: - return ; - case CHART_CONFIGURATION_SETTING_IDS.AXIS_NAME: - return ; - default: - return
Configuration options will be implemented here
; - } -}; diff --git a/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getFieldLabelWithSubField.ts b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getFieldLabelWithSubField.ts new file mode 100644 index 00000000000..90a218f25ef --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/page-layout/utils/getFieldLabelWithSubField.ts @@ -0,0 +1,27 @@ +import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; +import { getCompositeSubFieldLabel } from '@/object-record/object-filter-dropdown/utils/getCompositeSubFieldLabel'; +import { type CompositeFieldType } from '@/settings/data-model/types/CompositeFieldType'; +import { t } from '@lingui/core/macro'; +import { type CompositeFieldSubFieldName } from 'twenty-shared/types'; +import { isDefined } from 'twenty-shared/utils'; + +export const getFieldLabelWithSubField = ({ + field, + subFieldName, +}: { + field: FieldMetadataItem | undefined; + subFieldName?: CompositeFieldSubFieldName; +}): string => { + const subFieldNameLabel = isDefined(subFieldName) + ? getCompositeSubFieldLabel(field?.type as CompositeFieldType, subFieldName) + : undefined; + + const fieldLabel = + isDefined(subFieldNameLabel) && isDefined(field?.label) + ? `${field.label} ${subFieldNameLabel}` + : isDefined(field?.label) + ? field.label + : t`Field`; + + return fieldLabel; +}; 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 42a83fa012b..38db3217644 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 @@ -42,6 +42,11 @@ export class BarChartConfigurationDTO { @IsNotEmpty() groupByFieldMetadataIdX: string; + @Field(() => String, { nullable: true }) + @IsString() + @IsOptional() + groupBySubFieldNameX?: string; + @Field(() => GraphOrderBy) @IsEnum(GraphOrderBy) @IsNotEmpty() @@ -52,6 +57,11 @@ export class BarChartConfigurationDTO { @IsOptional() groupByFieldMetadataIdY?: string; + @Field(() => String, { nullable: true }) + @IsString() + @IsOptional() + groupBySubFieldNameY?: string; + @Field(() => GraphOrderBy, { nullable: true }) @IsEnum(GraphOrderBy) @IsOptional() diff --git a/packages/twenty-server/src/engine/core-modules/page-layout/dtos/line-chart-configuration.dto.ts b/packages/twenty-server/src/engine/core-modules/page-layout/dtos/line-chart-configuration.dto.ts index d4f6f4d52da..8e4cc909c56 100644 --- a/packages/twenty-server/src/engine/core-modules/page-layout/dtos/line-chart-configuration.dto.ts +++ b/packages/twenty-server/src/engine/core-modules/page-layout/dtos/line-chart-configuration.dto.ts @@ -42,6 +42,11 @@ export class LineChartConfigurationDTO { @IsNotEmpty() groupByFieldMetadataIdX: string; + @Field(() => String, { nullable: true }) + @IsString() + @IsOptional() + groupBySubFieldNameX?: string; + @Field(() => GraphOrderBy) @IsEnum(GraphOrderBy) @IsNotEmpty() @@ -52,6 +57,11 @@ export class LineChartConfigurationDTO { @IsOptional() groupByFieldMetadataIdY?: string; + @Field(() => String, { nullable: true }) + @IsString() + @IsOptional() + groupBySubFieldNameY?: string; + @Field(() => GraphOrderBy, { nullable: true }) @IsEnum(GraphOrderBy) @IsOptional() diff --git a/packages/twenty-server/src/engine/core-modules/page-layout/dtos/pie-chart-configuration.dto.ts b/packages/twenty-server/src/engine/core-modules/page-layout/dtos/pie-chart-configuration.dto.ts index 141db21f125..57648692268 100644 --- a/packages/twenty-server/src/engine/core-modules/page-layout/dtos/pie-chart-configuration.dto.ts +++ b/packages/twenty-server/src/engine/core-modules/page-layout/dtos/pie-chart-configuration.dto.ts @@ -40,6 +40,11 @@ export class PieChartConfigurationDTO { @IsNotEmpty() groupByFieldMetadataId: string; + @Field(() => String, { nullable: true }) + @IsString() + @IsOptional() + groupBySubFieldName?: string; + @Field(() => GraphOrderBy) @IsEnum(GraphOrderBy) @IsNotEmpty()