diff --git a/packages/twenty-front/src/loading/components/__stories__/PrefetchLoading.stories.tsx b/packages/twenty-front/src/loading/components/__stories__/PrefetchLoading.stories.tsx index 3e84058d3..0f94fc9a0 100644 --- a/packages/twenty-front/src/loading/components/__stories__/PrefetchLoading.stories.tsx +++ b/packages/twenty-front/src/loading/components/__stories__/PrefetchLoading.stories.tsx @@ -36,6 +36,6 @@ export const Default: Story = { await canvas.findByText('Search'); await canvas.findByText('Settings'); await canvas.findByText('Linkedin'); - await canvas.findByText('All'); + await canvas.findByText('All companies'); }, }; diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/useFieldMetadataItemById.ts b/packages/twenty-front/src/modules/object-metadata/hooks/useFieldMetadataItemById.ts new file mode 100644 index 000000000..1daffe5f9 --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/hooks/useFieldMetadataItemById.ts @@ -0,0 +1,17 @@ +import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; +import { useRecoilValue } from 'recoil'; +import { isDefined } from 'twenty-ui'; + +export const useFieldMetadataItemById = (fieldMetadataId: string) => { + const objectMetadataItems = useRecoilValue(objectMetadataItemsState); + + const fieldMetadataItem = objectMetadataItems + .flatMap((objectMetadataItem) => objectMetadataItem.fields) + .find((field) => field.id === fieldMetadataId); + + if (!isDefined(fieldMetadataItem)) { + throw new Error(`Field metadata item not found for id ${fieldMetadataId}`); + } + + return { fieldMetadataItem }; +}; diff --git a/packages/twenty-front/src/modules/object-metadata/hooks/useGetFieldMetadataItemById.ts b/packages/twenty-front/src/modules/object-metadata/hooks/useGetFieldMetadataItemById.ts new file mode 100644 index 000000000..c1827d284 --- /dev/null +++ b/packages/twenty-front/src/modules/object-metadata/hooks/useGetFieldMetadataItemById.ts @@ -0,0 +1,23 @@ +import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; +import { useRecoilValue } from 'recoil'; +import { isDefined } from 'twenty-ui'; + +export const useGetFieldMetadataItemById = () => { + const objectMetadataItems = useRecoilValue(objectMetadataItemsState); + + const getFieldMetadataItemById = (fieldMetadataId: string) => { + const fieldMetadataItem = objectMetadataItems + .flatMap((objectMetadataItem) => objectMetadataItem.fields) + .find((field) => field.id === fieldMetadataId); + + if (!isDefined(fieldMetadataItem)) { + throw new Error( + `Field metadata item not found for id ${fieldMetadataId}`, + ); + } + + return fieldMetadataItem; + }; + + return { getFieldMetadataItemById }; +}; diff --git a/packages/twenty-front/src/modules/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions.ts b/packages/twenty-front/src/modules/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions.ts index b817bdf53..35eaa9130 100644 --- a/packages/twenty-front/src/modules/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions.ts +++ b/packages/twenty-front/src/modules/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions.ts @@ -60,13 +60,25 @@ export const formatFieldMetadataItemAsFilterDefinition = ({ fieldMetadataId: field.id, label: field.label, iconName: field.icon ?? 'Icon123', - relationObjectMetadataNamePlural: - field.relationDefinition?.targetObjectMetadata.namePlural, - relationObjectMetadataNameSingular: - field.relationDefinition?.targetObjectMetadata.nameSingular, type: getFilterTypeFromFieldType(field.type), }); +export const getRelationObjectMetadataNameSingular = ({ + field, +}: { + field: ObjectMetadataItem['fields'][0]; +}): string | undefined => { + return field.relationDefinition?.targetObjectMetadata.nameSingular; +}; + +export const getRelationObjectMetadataNamePlural = ({ + field, +}: { + field: ObjectMetadataItem['fields'][0]; +}): string | undefined => { + return field.relationDefinition?.targetObjectMetadata.namePlural; +}; + export const getFilterTypeFromFieldType = (fieldType: FieldMetadataType) => { switch (fieldType) { case FieldMetadataType.DATE_TIME: diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterViewFilterValueInput.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterViewFilterValueInput.tsx index e40191418..48c8c0a7b 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterViewFilterValueInput.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterViewFilterValueInput.tsx @@ -1,5 +1,6 @@ import { useCurrentViewFilter } from '@/object-record/advanced-filter/hooks/useCurrentViewFilter'; import { ObjectFilterDropdownFilterInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterInput'; +import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState'; import { filterDefinitionUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/filterDefinitionUsedInDropdownComponentState'; import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; @@ -26,6 +27,10 @@ export const AdvancedFilterViewFilterValueInput = ({ filterDefinitionUsedInDropdownComponentState, ); + const setFieldMetadataItemIdUsedInDropdown = useSetRecoilComponentStateV2( + fieldMetadataItemIdUsedInDropdownComponentState, + ); + const setSelectedOperandInDropdown = useSetRecoilComponentStateV2( selectedOperandInDropdownComponentState, ); @@ -58,6 +63,7 @@ export const AdvancedFilterViewFilterValueInput = ({ /> } onOpen={() => { + setFieldMetadataItemIdUsedInDropdown(filter.fieldMetadataId); setFilterDefinitionUsedInDropdown(filter.definition); setSelectedOperandInDropdown(filter.operand); setSelectedFilter(filter); diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect.tsx index 69a87b395..efcc83ba5 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect.tsx @@ -25,6 +25,7 @@ import { isDefined } from 'twenty-ui'; import { FeatureFlagKey } from '~/generated/graphql'; import { advancedFilterViewFilterIdComponentState } from '@/object-record/object-filter-dropdown/states/advancedFilterViewFilterIdComponentState'; +import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState'; import { FiltersHotkeyScope } from '@/object-record/object-filter-dropdown/types/FiltersHotkeyScope'; import { useLingui } from '@lingui/react/macro'; @@ -126,11 +127,15 @@ export const ObjectFilterDropdownFilterSelect = ({ const { selectFilterDefinitionUsedInDropdown } = useSelectFilterDefinitionUsedInDropdown(); + const setFieldMetadataItemIdUsedInDropdown = useSetRecoilComponentStateV2( + fieldMetadataItemIdUsedInDropdownComponentState, + ); + const { resetSelectedItem } = useSelectableList(OBJECT_FILTER_DROPDOWN_ID); - const handleEnter = (itemId: string) => { + const handleEnter = (fieldMetadataItemId: string) => { const selectedFilterDefinition = availableFilterDefinitions.find( - (item) => item.fieldMetadataId === itemId, + (item) => item.fieldMetadataId === fieldMetadataItemId, ); if (!isDefined(selectedFilterDefinition)) { @@ -143,6 +148,10 @@ export const ObjectFilterDropdownFilterSelect = ({ filterDefinition: selectedFilterDefinition, }); + setFieldMetadataItemIdUsedInDropdown( + selectedFilterDefinition.fieldMetadataId, + ); + closeAdvancedFilterDropdown(); }; diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectCompositeFieldSubMenu.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectCompositeFieldSubMenu.tsx index 5f1fa512d..71b3b356c 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectCompositeFieldSubMenu.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectCompositeFieldSubMenu.tsx @@ -1,6 +1,7 @@ import { useAdvancedFilterDropdown } from '@/object-record/advanced-filter/hooks/useAdvancedFilterDropdown'; import { advancedFilterViewFilterGroupIdComponentState } from '@/object-record/object-filter-dropdown/states/advancedFilterViewFilterGroupIdComponentState'; import { advancedFilterViewFilterIdComponentState } from '@/object-record/object-filter-dropdown/states/advancedFilterViewFilterIdComponentState'; +import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState'; import { filterDefinitionUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/filterDefinitionUsedInDropdownComponentState'; import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState'; import { objectFilterDropdownFirstLevelFilterDefinitionComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFirstLevelFilterDefinitionComponentState'; @@ -61,6 +62,10 @@ export const ObjectFilterDropdownFilterSelectCompositeFieldSubMenu = () => { filterDefinitionUsedInDropdownComponentState, ); + const setFieldMetadataItemIdUsedInDropdown = useSetRecoilComponentStateV2( + fieldMetadataItemIdUsedInDropdownComponentState, + ); + const setSelectedOperandInDropdown = useSetRecoilComponentStateV2( selectedOperandInDropdownComponentState, ); @@ -110,6 +115,7 @@ export const ObjectFilterDropdownFilterSelectCompositeFieldSubMenu = () => { } setFilterDefinitionUsedInDropdown(definition); + setFieldMetadataItemIdUsedInDropdown(definition.fieldMetadataId); setSelectedOperandInDropdown( getRecordFilterOperandsForRecordFilterDefinition(definition)[0], @@ -122,6 +128,7 @@ export const ObjectFilterDropdownFilterSelectCompositeFieldSubMenu = () => { }; const handleSubMenuBack = () => { + setFieldMetadataItemIdUsedInDropdown(null); setFilterDefinitionUsedInDropdown(null); setObjectFilterDropdownSubMenuFieldType(null); setObjectFilterDropdownFirstLevelFilterDefinition(null); diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItem.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItem.tsx index 6e5ef654d..fcccd4b6e 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItem.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItem.tsx @@ -1,15 +1,16 @@ import { useAdvancedFilterDropdown } from '@/object-record/advanced-filter/hooks/useAdvancedFilterDropdown'; import { OBJECT_FILTER_DROPDOWN_ID } from '@/object-record/object-filter-dropdown/constants/ObjectFilterDropdownId'; + import { useSelectFilterDefinitionUsedInDropdown } from '@/object-record/object-filter-dropdown/hooks/useSelectFilterDefinitionUsedInDropdown'; import { advancedFilterViewFilterIdComponentState } from '@/object-record/object-filter-dropdown/states/advancedFilterViewFilterIdComponentState'; +import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState'; import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState'; import { objectFilterDropdownFirstLevelFilterDefinitionComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFirstLevelFilterDefinitionComponentState'; import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState'; import { objectFilterDropdownSubMenuFieldTypeComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSubMenuFieldTypeComponentState'; import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; -import { CompositeFilterableFieldType } from '@/object-record/record-filter/types/CompositeFilterableFieldType'; - import { isCompositeField } from '@/object-record/object-filter-dropdown/utils/isCompositeField'; +import { CompositeFilterableFieldType } from '@/object-record/record-filter/types/CompositeFilterableFieldType'; import { RecordFilterDefinition } from '@/object-record/record-filter/types/RecordFilterDefinition'; import { getRecordFilterOperandsForRecordFilterDefinition } from '@/object-record/record-filter/utils/getRecordFilterOperandsForRecordFilterDefinition'; import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope'; @@ -31,6 +32,10 @@ export const ObjectFilterDropdownFilterSelectMenuItem = ({ const { selectFilterDefinitionUsedInDropdown } = useSelectFilterDefinitionUsedInDropdown(); + const setFieldMetadataItemIdUsedInDropdown = useSetRecoilComponentStateV2( + fieldMetadataItemIdUsedInDropdownComponentState, + ); + const [, setObjectFilterDropdownFirstLevelFilterDefinition] = useRecoilComponentStateV2( objectFilterDropdownFirstLevelFilterDefinitionComponentState, @@ -82,6 +87,10 @@ export const ObjectFilterDropdownFilterSelectMenuItem = ({ filterDefinition: availableFilterDefinition, }); + setFieldMetadataItemIdUsedInDropdown( + availableFilterDefinition.fieldMetadataId, + ); + if ( availableFilterDefinition.type === 'RELATION' || availableFilterDefinition.type === 'SELECT' diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordSelect.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordSelect.tsx index d8cdad553..b63a8817e 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordSelect.tsx @@ -2,8 +2,10 @@ import { useState } from 'react'; import { v4 } from 'uuid'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; +import { getRelationObjectMetadataNameSingular } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; import { ObjectFilterDropdownRecordPinnedItems } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownRecordPinnedItems'; import { CURRENT_WORKSPACE_MEMBER_SELECTABLE_ITEM_ID } from '@/object-record/object-filter-dropdown/constants/CurrentWorkspaceMemberSelectableItemId'; +import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector'; import { filterDefinitionUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/filterDefinitionUsedInDropdownComponentState'; import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState'; import { objectFilterDropdownSelectedRecordIdsComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSelectedRecordIdsComponentState'; @@ -41,6 +43,10 @@ export const ObjectFilterDropdownRecordSelect = ({ filterDefinitionUsedInDropdownComponentState, ); + const fieldMetadataItemUsedInFilterDropdown = useRecoilComponentValueV2( + fieldMetadataItemUsedInDropdownComponentSelector, + ); + const selectedOperandInDropdown = useRecoilComponentValueV2( selectedOperandInDropdownComponentState, ); @@ -73,15 +79,20 @@ export const ObjectFilterDropdownRecordSelect = ({ }) .parse(selectedFilter?.value); - const objectNameSingular = - filterDefinitionUsedInDropdown?.relationObjectMetadataNameSingular; + if (!isDefined(fieldMetadataItemUsedInFilterDropdown)) { + throw new Error('fieldMetadataItemUsedInFilterDropdown is not defined'); + } + + const objectNameSingular = getRelationObjectMetadataNameSingular({ + field: fieldMetadataItemUsedInFilterDropdown, + }); if (!isDefined(objectNameSingular)) { throw new Error('relationObjectMetadataNameSingular is not defined'); } const { objectMetadataItem } = useObjectMetadataItem({ - objectNameSingular: objectNameSingular, + objectNameSingular, }); const objectLabelPlural = objectMetadataItem?.labelPlural; diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/SingleEntityObjectFilterDropdownButton.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/SingleEntityObjectFilterDropdownButton.tsx index 20b57d9b0..4be9678ab 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/SingleEntityObjectFilterDropdownButton.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/SingleEntityObjectFilterDropdownButton.tsx @@ -7,8 +7,8 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; -import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; +import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState'; import { filterDefinitionUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/filterDefinitionUsedInDropdownComponentState'; import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; @@ -36,6 +36,10 @@ export const SingleEntityObjectFilterDropdownButton = ({ filterDefinitionUsedInDropdownComponentState, ); + const setFieldMetadataItemIdUsedInDropdown = useSetRecoilComponentStateV2( + fieldMetadataItemIdUsedInDropdownComponentState, + ); + const setSelectedOperandInDropdown = useSetRecoilComponentStateV2( selectedOperandInDropdownComponentState, ); @@ -47,6 +51,9 @@ export const SingleEntityObjectFilterDropdownButton = ({ const availableFilterDefinition = availableFilterDefinitions[0]; React.useEffect(() => { + setFieldMetadataItemIdUsedInDropdown( + availableFilterDefinition.fieldMetadataId, + ); setFilterDefinitionUsedInDropdown(availableFilterDefinition); const defaultOperand = getRecordFilterOperandsForRecordFilterDefinition( availableFilterDefinition, @@ -56,6 +63,7 @@ export const SingleEntityObjectFilterDropdownButton = ({ availableFilterDefinition, setFilterDefinitionUsedInDropdown, setSelectedOperandInDropdown, + setFieldMetadataItemIdUsedInDropdown, ]); const theme = useTheme(); @@ -69,14 +77,7 @@ export const SingleEntityObjectFilterDropdownButton = ({ clickableComponent={ {selectedFilter ? ( - + ) : ( t`Filter` )} diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/__stories__/MultipleFiltersDropdownButton.stories.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/__stories__/MultipleFiltersDropdownButton.stories.tsx index b209520ec..ad980c620 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/__stories__/MultipleFiltersDropdownButton.stories.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/__stories__/MultipleFiltersDropdownButton.stories.tsx @@ -3,13 +3,14 @@ import { Meta, StoryObj } from '@storybook/react'; import { TaskGroups } from '@/activities/tasks/components/TaskGroups'; import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition'; +import { formatFieldMetadataItemAsFilterDefinition } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; import { MultipleFiltersDropdownButton } from '@/object-record/object-filter-dropdown/components/MultipleFiltersDropdownButton'; import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext'; import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext'; import { RecordIndexContextProvider } from '@/object-record/record-index/contexts/RecordIndexContext'; import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; import { tableColumnsComponentState } from '@/object-record/record-table/states/tableColumnsComponentState'; -import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { availableFilterDefinitionsComponentState } from '@/views/states/availableFilterDefinitionsComponentState'; import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; @@ -18,7 +19,6 @@ import { ComponentDecorator, getCanvasElementForDropdownTesting, } from 'twenty-ui'; -import { FieldMetadataType } from '~/generated/graphql'; import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator'; import { IconsProviderDecorator } from '~/testing/decorators/IconsProviderDecorator'; import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator'; @@ -45,59 +45,26 @@ const meta: Meta = { instanceId, ); - setTableColumns([ - { - fieldMetadataId: '1', - iconName: 'IconUser', - label: 'Text', - type: FieldMetadataType.TEXT, - isVisible: true, - metadata: { - fieldName: 'text', - }, - } as ColumnDefinition, - { - fieldMetadataId: '3', - iconName: 'IconNumber', - label: 'Number', - type: FieldMetadataType.NUMBER, - isVisible: true, - metadata: { - fieldName: 'number', - }, - } as ColumnDefinition, - { - fieldMetadataId: '4', - iconName: 'IconCalendar', - label: 'Date', - type: FieldMetadataType.DATE_TIME, - isVisible: true, - metadata: { - fieldName: 'date', - }, - } as ColumnDefinition, - ]); + const columns = companyObjectMetadataItem.fields.map( + (fieldMetadataItem, index) => + formatFieldMetadataItemAsColumnDefinition({ + field: fieldMetadataItem, + objectMetadataItem: companyObjectMetadataItem, + position: index, + }), + ); + + const filterDefinitions = companyObjectMetadataItem.fields.map( + (fieldMetadataItem) => + formatFieldMetadataItemAsFilterDefinition({ + field: fieldMetadataItem, + }), + ); + + setTableColumns(columns); + + setAvailableFilterDefinitions(filterDefinitions); - setAvailableFilterDefinitions([ - { - fieldMetadataId: '1', - iconName: 'IconUser', - label: 'Text', - type: FieldMetadataType.TEXT, - }, - { - fieldMetadataId: '3', - iconName: 'IconNumber', - label: 'Number', - type: FieldMetadataType.NUMBER, - }, - { - fieldMetadataId: '3', - iconName: 'IconCalendar', - label: 'Date', - type: FieldMetadataType.DATE_TIME, - }, - ]); return ( { setFilterDefinitionUsedInDropdown(filterDefinition); + setFieldMetadataItemIdUsedInDropdown(filterDefinition.fieldMetadataId); if ( filterDefinition.type === 'RELATION' || diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState.ts b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState.ts new file mode 100644 index 000000000..cefeb2b73 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState.ts @@ -0,0 +1,9 @@ +import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext'; +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; + +export const fieldMetadataItemIdUsedInDropdownComponentState = + createComponentStateV2({ + key: 'fieldMetadataItemIdUsedInDropdownComponentState', + defaultValue: null, + componentInstanceContext: ObjectFilterDropdownComponentInstanceContext, + }); diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector.ts b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector.ts new file mode 100644 index 000000000..bf3e6622e --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector.ts @@ -0,0 +1,31 @@ +import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; +import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; +import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext'; +import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState'; +import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; + +export const fieldMetadataItemUsedInDropdownComponentSelector = + createComponentSelectorV2({ + key: 'fieldMetadataItemUsedInDropdownComponentSelector', + get: + ({ instanceId }) => + ({ get }) => { + const fieldMetadataItemIdUsedInDropdown = get( + fieldMetadataItemIdUsedInDropdownComponentState.atomFamily({ + instanceId, + }), + ); + + const objectMetadataItems = get(objectMetadataItemsState); + + const correspondingFieldMetadataItem = objectMetadataItems + .flatMap((objectMetadataItem) => objectMetadataItem.fields) + .find( + (fieldMetadataItem) => + fieldMetadataItem.id === fieldMetadataItemIdUsedInDropdown, + ); + + return correspondingFieldMetadataItem; + }, + componentInstanceContext: ObjectFilterDropdownComponentInstanceContext, + }); diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState.ts b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState.ts new file mode 100644 index 000000000..db286d739 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState.ts @@ -0,0 +1,10 @@ +import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext'; +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; + +export const subFieldNameUsedInDropdownComponentState = createComponentStateV2< + string | null +>({ + key: 'subFieldNameUsedInDropdownComponentState', + defaultValue: null, + componentInstanceContext: ObjectFilterDropdownComponentInstanceContext, +}); diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/RelationToOneFieldInput.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/RelationToOneFieldInput.stories.tsx index 262d00fca..ce70f5d51 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/RelationToOneFieldInput.stories.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/RelationToOneFieldInput.stories.tsx @@ -140,7 +140,10 @@ export const Submit: Story = { }); await userEvent.click(item); - await waitFor(() => expect(submitJestFn).toHaveBeenCalledTimes(1)); + + await waitFor(() => { + expect(submitJestFn).toHaveBeenCalledTimes(1); + }); }, }; diff --git a/packages/twenty-front/src/modules/object-record/record-filter/types/RecordFilter.ts b/packages/twenty-front/src/modules/object-record/record-filter/types/RecordFilter.ts index b993ea318..d6760f92f 100644 --- a/packages/twenty-front/src/modules/object-record/record-filter/types/RecordFilter.ts +++ b/packages/twenty-front/src/modules/object-record/record-filter/types/RecordFilter.ts @@ -12,4 +12,6 @@ export type RecordFilter = { operand: ViewFilterOperand; positionInViewFilterGroup?: number | null; definition: RecordFilterDefinition; + label?: string; + subFieldName?: string; }; diff --git a/packages/twenty-front/src/modules/object-record/record-filter/types/RecordFilterDefinition.ts b/packages/twenty-front/src/modules/object-record/record-filter/types/RecordFilterDefinition.ts index d549f1038..820195f68 100644 --- a/packages/twenty-front/src/modules/object-record/record-filter/types/RecordFilterDefinition.ts +++ b/packages/twenty-front/src/modules/object-record/record-filter/types/RecordFilterDefinition.ts @@ -1,5 +1,3 @@ -import { IconComponent } from 'twenty-ui'; - import { FilterableFieldType } from './FilterableFieldType'; export type RecordFilterDefinition = { @@ -7,9 +5,5 @@ export type RecordFilterDefinition = { label: string; iconName: string; type: FilterableFieldType; - relationObjectMetadataNamePlural?: string; - relationObjectMetadataNameSingular?: string; - selectAllLabel?: string; - SelectAllIcon?: IconComponent; compositeFieldName?: string; }; diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleToggleColumnFilter.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleToggleColumnFilter.ts index daa0bb001..bcc7d25be 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleToggleColumnFilter.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleToggleColumnFilter.ts @@ -3,13 +3,15 @@ import { v4 } from 'uuid'; import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromFieldMetadata'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; -import { useSelectFilterDefinitionUsedInDropdown } from '@/object-record/object-filter-dropdown/hooks/useSelectFilterDefinitionUsedInDropdown'; +import { useSelectFilterDefinitionUsedInDropdown } from '@/object-record/object-filter-dropdown/hooks/useSelectFilterDefinitionUsedInDropdown'; +import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState'; import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter'; import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter'; import { getRecordFilterOperandsForRecordFilterDefinition } from '@/object-record/record-filter/utils/getRecordFilterOperandsForRecordFilterDefinition'; import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState'; import { useGetCurrentView } from '@/views/hooks/useGetCurrentView'; import { useUpsertCombinedViewFilters } from '@/views/hooks/useUpsertCombinedViewFilters'; @@ -56,6 +58,11 @@ export const useHandleToggleColumnFilter = ({ const { selectFilterDefinitionUsedInDropdown } = useSelectFilterDefinitionUsedInDropdown(viewBarId); + const setFieldMetadataItemIdUsedInDropdown = useSetRecoilComponentStateV2( + fieldMetadataItemIdUsedInDropdownComponentState, + viewBarId, + ); + const handleToggleColumnFilter = useCallback( async (fieldMetadataId: string) => { const correspondingColumnDefinition = columnDefinitions.find( @@ -100,6 +107,7 @@ export const useHandleToggleColumnFilter = ({ await upsertCombinedViewFilter(newFilter); selectFilterDefinitionUsedInDropdown({ filterDefinition }); + setFieldMetadataItemIdUsedInDropdown(fieldMetadataId); } openDropdown(existingViewFilter?.id ?? newFilterId); @@ -112,6 +120,7 @@ export const useHandleToggleColumnFilter = ({ currentViewWithCombinedFiltersAndSorts, availableFilterDefinitions, upsertRecordFilter, + setFieldMetadataItemIdUsedInDropdown, ], ); diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleRecordSelectMenuItems.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleRecordSelectMenuItems.tsx index c657ba9a9..77782e550 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleRecordSelectMenuItems.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleRecordSelectMenuItems.tsx @@ -24,11 +24,6 @@ export type SingleRecordSelectMenuItemsProps = { onCancel?: () => void; onRecordSelected: (entity?: RecordForSelect) => void; selectedRecord?: RecordForSelect; - SelectAllIcon?: IconComponent; - selectAllLabel?: string; - isAllRecordsSelected?: boolean; - isAllRecordsSelectShown?: boolean; - onAllRecordsSelected?: () => void; hotkeyScope?: string; isFiltered: boolean; shouldSelectEmptyOption?: boolean; @@ -42,11 +37,6 @@ export const SingleRecordSelectMenuItems = ({ onCancel, onRecordSelected, selectedRecord, - SelectAllIcon, - selectAllLabel, - isAllRecordsSelected, - isAllRecordsSelectShown, - onAllRecordsSelected, hotkeyScope = RelationPickerHotkeyScope.RelationPicker, isFiltered, shouldSelectEmptyOption, @@ -61,16 +51,7 @@ export const SingleRecordSelectMenuItems = ({ } : null; - const selectAll = isAllRecordsSelectShown - ? { - __typename: '', - id: 'select-all', - name: selectAllLabel, - } - : null; - const recordsInDropdown = [ - selectAll, selectNone, selectedRecord, ...recordsToSelect, @@ -87,10 +68,6 @@ export const SingleRecordSelectMenuItems = ({ isSelectedItemIdSelector('select-none'), ); - const isSelectedSelectAllButton = useRecoilValue( - isSelectedItemIdSelector('select-all'), - ); - useScopedHotkeys( [Key.Escape], () => { @@ -120,9 +97,7 @@ export const SingleRecordSelectMenuItems = ({ {loading && !isFiltered ? ( - ) : recordsInDropdown.length === 0 && - !isAllRecordsSelectShown && - !loading ? ( + ) : recordsInDropdown.length === 0 && !loading ? ( <> ) : ( recordsInDropdown?.map((record) => { @@ -141,22 +116,6 @@ export const SingleRecordSelectMenuItems = ({ ) ); } - case 'select-all': { - return ( - isAllRecordsSelectShown && - selectAllLabel && - onAllRecordsSelected && ( - onAllRecordsSelected()} - LeftIcon={SelectAllIcon} - text={selectAllLabel} - selected={!!isAllRecordsSelected} - hovered={isSelectedSelectAllButton} - /> - ) - ); - } default: { return (