diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterFieldSelectDrodownButton.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterFieldSelectDropdownButton.tsx similarity index 58% rename from packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterFieldSelectDrodownButton.tsx rename to packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterFieldSelectDropdownButton.tsx index 319e492fe..647634ed9 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterFieldSelectDrodownButton.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterFieldSelectDropdownButton.tsx @@ -1,5 +1,5 @@ -import { AdvancedFilterFieldSelectDrodownContent } from '@/object-record/advanced-filter/components/AdvancedFilterFieldSelectDrodownContent'; -import { useAdvancedFilterDropdown } from '@/object-record/advanced-filter/hooks/useAdvancedFilterDropdown'; +import { AdvancedFilterFieldSelectDropdownContent } from '@/object-record/advanced-filter/components/AdvancedFilterFieldSelectDropdownContent'; +import { useAdvancedFilterFieldSelectDropdown } from '@/object-record/advanced-filter/hooks/useAdvancedFilterFieldSelectDropdown'; import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; import { SelectControl } from '@/ui/input/components/SelectControl'; @@ -11,15 +11,15 @@ const StyledContainer = styled.div` flex: 2; `; -type AdvancedFilterFieldSelectDrodownButtonProps = { +type AdvancedFilterFieldSelectDropdownButtonProps = { recordFilterId: string; }; -export const AdvancedFilterFieldSelectDrodownButton = ({ +export const AdvancedFilterFieldSelectDropdownButton = ({ recordFilterId, -}: AdvancedFilterFieldSelectDrodownButtonProps) => { - const { advancedFilterDropdownId } = - useAdvancedFilterDropdown(recordFilterId); +}: AdvancedFilterFieldSelectDropdownButtonProps) => { + const { advancedFilterFieldSelectDropdownId } = + useAdvancedFilterFieldSelectDropdown(recordFilterId); const currentRecordFilters = useRecoilComponentValueV2( currentRecordFiltersComponentState, @@ -34,7 +34,7 @@ export const AdvancedFilterFieldSelectDrodownButton = ({ return ( } - dropdownComponents={} - dropdownHotkeyScope={{ scope: advancedFilterDropdownId }} + dropdownComponents={ + + } + dropdownHotkeyScope={{ scope: advancedFilterFieldSelectDropdownId }} dropdownOffset={{ y: 8, x: 0 }} dropdownPlacement="bottom-start" /> diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterFieldSelectDrodownContent.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterFieldSelectDropdownContent.tsx similarity index 50% rename from packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterFieldSelectDrodownContent.tsx rename to packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterFieldSelectDropdownContent.tsx index 5372872d1..41711011f 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterFieldSelectDrodownContent.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterFieldSelectDropdownContent.tsx @@ -1,9 +1,15 @@ -import { ObjectFilterDropdownFilterSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect'; -import { ObjectFilterDropdownFilterSelectCompositeFieldSubMenu } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectCompositeFieldSubMenu'; +import { AdvancedFilterFieldSelectMenu } from '@/object-record/advanced-filter/components/AdvancedFilterFieldSelectMenu'; +import { AdvancedFilterSubFieldSelectMenu } from '@/object-record/advanced-filter/components/AdvancedFilterSubFieldSelectMenu'; import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState'; import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; -export const AdvancedFilterFieldSelectDrodownContent = () => { +type AdvancedFilterFieldSelectDropdownContentProps = { + recordFilterId: string; +}; + +export const AdvancedFilterFieldSelectDropdownContent = ({ + recordFilterId, +}: AdvancedFilterFieldSelectDropdownContentProps) => { const [objectFilterDropdownIsSelectingCompositeField] = useRecoilComponentStateV2( objectFilterDropdownIsSelectingCompositeFieldComponentState, @@ -13,8 +19,8 @@ export const AdvancedFilterFieldSelectDrodownContent = () => { objectFilterDropdownIsSelectingCompositeField; return shouldShowCompositeSelectionSubMenu ? ( - + ) : ( - + ); }; diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterFieldSelectMenu.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterFieldSelectMenu.tsx new file mode 100644 index 000000000..1ea4f94fe --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterFieldSelectMenu.tsx @@ -0,0 +1,182 @@ +import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; + +import { OBJECT_FILTER_DROPDOWN_ID } from '@/object-record/object-filter-dropdown/constants/ObjectFilterDropdownId'; + +import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState'; + +import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext'; +import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector'; +import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; +import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem'; +import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; +import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { isDefined } from 'twenty-shared/utils'; + +import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; +import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; +import { AdvancedFilterFieldSelectSearchInput } from '@/object-record/advanced-filter/components/AdvancedFilterFieldSelectSearchInput'; +import { useAdvancedFilterFieldSelectDropdown } from '@/object-record/advanced-filter/hooks/useAdvancedFilterFieldSelectDropdown'; +import { useSelectFieldUsedInAdvancedFilterDropdown } from '@/object-record/advanced-filter/hooks/useSelectFieldUsedInAdvancedFilterDropdown'; +import { ObjectFilterDropdownFilterSelectMenuItemV2 } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItemV2'; +import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState'; +import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState'; +import { objectFilterDropdownSubMenuFieldTypeComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSubMenuFieldTypeComponentState'; +import { FiltersHotkeyScope } from '@/object-record/object-filter-dropdown/types/FiltersHotkeyScope'; +import { isCompositeField } from '@/object-record/object-filter-dropdown/utils/isCompositeField'; +import { useFilterableFieldMetadataItemsInRecordIndexContext } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItemsInRecordIndexContext'; +import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; + +type AdvancedFilterFieldSelectMenuProps = { + recordFilterId: string; +}; + +export const AdvancedFilterFieldSelectMenu = ({ + recordFilterId, +}: AdvancedFilterFieldSelectMenuProps) => { + const { recordIndexId } = useRecordIndexContextOrThrow(); + + const { closeAdvancedFilterFieldSelectDropdown } = + useAdvancedFilterFieldSelectDropdown(recordFilterId); + + const [objectFilterDropdownSearchInput] = useRecoilComponentStateV2( + objectFilterDropdownSearchInputComponentState, + ); + + const { filterableFieldMetadataItems } = + useFilterableFieldMetadataItemsInRecordIndexContext(); + + const visibleTableColumns = useRecoilComponentValueV2( + visibleTableColumnsComponentSelector, + recordIndexId, + ); + const visibleColumnsIds = visibleTableColumns.map( + (column) => column.fieldMetadataId, + ); + + const filteredSearchInputFieldMetadataItems = + filterableFieldMetadataItems.filter((fieldMetadataItem) => + fieldMetadataItem.label + .toLocaleLowerCase() + .includes(objectFilterDropdownSearchInput.toLocaleLowerCase()), + ); + + const visibleColumnsFieldMetadataItems = filteredSearchInputFieldMetadataItems + .sort((a, b) => { + return visibleColumnsIds.indexOf(a.id) - visibleColumnsIds.indexOf(b.id); + }) + .filter((fieldMetadataItem) => + visibleColumnsIds.includes(fieldMetadataItem.id), + ); + + const hiddenColumnsFieldMetadataItems = filteredSearchInputFieldMetadataItems + .sort((a, b) => a.label.localeCompare(b.label)) + .filter( + (fieldMetadataItem) => !visibleColumnsIds.includes(fieldMetadataItem.id), + ); + + const selectableFieldMetadataItemIds = filterableFieldMetadataItems.map( + (fieldMetadataItem) => fieldMetadataItem.id, + ); + + const { resetSelectedItem } = useSelectableList(OBJECT_FILTER_DROPDOWN_ID); + + const { selectFieldUsedInAdvancedFilterDropdown } = + useSelectFieldUsedInAdvancedFilterDropdown(); + + const [, setObjectFilterDropdownSubMenuFieldType] = useRecoilComponentStateV2( + objectFilterDropdownSubMenuFieldTypeComponentState, + ); + + const [, setObjectFilterDropdownIsSelectingCompositeField] = + useRecoilComponentStateV2( + objectFilterDropdownIsSelectingCompositeFieldComponentState, + ); + + const setFieldMetadataItemIdUsedInDropdown = useSetRecoilComponentStateV2( + fieldMetadataItemIdUsedInDropdownComponentState, + ); + + const handleEnter = (fieldMetadataItemId: string) => { + const selectedFieldMetadataItem = filterableFieldMetadataItems.find( + (fieldMetadataItem) => fieldMetadataItem.id === fieldMetadataItemId, + ); + + if (!isDefined(selectedFieldMetadataItem)) { + return; + } + + handleFieldMetadataItemSelect(selectedFieldMetadataItem); + }; + + const handleFieldMetadataItemSelect = ( + selectedFieldMetadataItem: FieldMetadataItem, + ) => { + resetSelectedItem(); + + const filterType = getFilterTypeFromFieldType( + selectedFieldMetadataItem.type, + ); + + if (isCompositeField(filterType)) { + setObjectFilterDropdownSubMenuFieldType(filterType); + + setFieldMetadataItemIdUsedInDropdown(selectedFieldMetadataItem.id); + setObjectFilterDropdownIsSelectingCompositeField(true); + } else { + selectFieldUsedInAdvancedFilterDropdown({ + fieldMetadataItemId: selectedFieldMetadataItem.id, + recordFilterId, + }); + + closeAdvancedFilterFieldSelectDropdown(); + } + }; + + const shouldShowSeparator = + visibleColumnsFieldMetadataItems.length > 0 && + hiddenColumnsFieldMetadataItems.length > 0; + + return ( + <> + + + + {visibleColumnsFieldMetadataItems.map( + (visibleFieldMetadataItem, index) => ( + + + + ), + )} + {shouldShowSeparator && } + {hiddenColumnsFieldMetadataItems.map( + (hiddenFieldMetadataItem, index) => ( + + + + ), + )} + + + + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRecordFilterRow.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRecordFilterRow.tsx index 7540fdeb9..7c57f6c5d 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRecordFilterRow.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRecordFilterRow.tsx @@ -1,5 +1,5 @@ import { AdvancedFilterDropdownRow } from '@/object-record/advanced-filter/components/AdvancedFilterDropdownRow'; -import { AdvancedFilterFieldSelectDrodownButton } from '@/object-record/advanced-filter/components/AdvancedFilterFieldSelectDrodownButton'; +import { AdvancedFilterFieldSelectDropdownButton } from '@/object-record/advanced-filter/components/AdvancedFilterFieldSelectDropdownButton'; import { AdvancedFilterLogicalOperatorCell } from '@/object-record/advanced-filter/components/AdvancedFilterLogicalOperatorCell'; import { AdvancedFilterRecordFilterOperandSelect } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterOperandSelect'; import { AdvancedFilterRecordFilterOptionsDropdown } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterOptionsDropdown'; @@ -26,7 +26,7 @@ export const AdvancedFilterRecordFilterRow = ({ index={recordFilterIndex} recordFilterGroup={recordFilterGroup} /> - { + const { getIcon } = useIcons(); + + const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2( + fieldMetadataItemUsedInDropdownComponentSelector, + ); + + const [, setObjectFilterDropdownFilterIsSelected] = useRecoilComponentStateV2( + objectFilterDropdownFilterIsSelectedComponentState, + ); + + const [, setObjectFilterDropdownIsSelectingCompositeField] = + useRecoilComponentStateV2( + objectFilterDropdownIsSelectingCompositeFieldComponentState, + ); + + const [ + objectFilterDropdownSubMenuFieldType, + setObjectFilterDropdownSubMenuFieldType, + ] = useRecoilComponentStateV2( + objectFilterDropdownSubMenuFieldTypeComponentState, + ); + + const setFieldMetadataItemIdUsedInDropdown = useSetRecoilComponentStateV2( + fieldMetadataItemIdUsedInDropdownComponentState, + ); + + const { closeAdvancedFilterFieldSelectDropdown } = + useAdvancedFilterFieldSelectDropdown(recordFilterId); + + const { selectFieldUsedInAdvancedFilterDropdown } = + useSelectFieldUsedInAdvancedFilterDropdown(); + + const handleSelectFilter = ( + selectedFieldMetadataItem: FieldMetadataItem | null | undefined, + subFieldName?: string | null | undefined, + ) => { + if (!isDefined(selectedFieldMetadataItem)) { + return; + } + + selectFieldUsedInAdvancedFilterDropdown({ + fieldMetadataItemId: selectedFieldMetadataItem.id, + recordFilterId, + subFieldName, + }); + + closeAdvancedFilterFieldSelectDropdown(); + }; + + const handleSubMenuBack = () => { + setFieldMetadataItemIdUsedInDropdown(null); + setObjectFilterDropdownSubMenuFieldType(null); + setObjectFilterDropdownIsSelectingCompositeField(false); + setObjectFilterDropdownFilterIsSelected(false); + }; + + if (!isDefined(objectFilterDropdownSubMenuFieldType)) { + return null; + } + + const options = SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS[ + objectFilterDropdownSubMenuFieldType + ].filterableSubFields.sort((a, b) => a.localeCompare(b)); + + return ( + <> + + } + > + {getFilterableFieldTypeLabel(objectFilterDropdownSubMenuFieldType)} + + + { + handleSelectFilter(fieldMetadataItemUsedInDropdown); + }} + LeftIcon={IconApps} + text={`Any ${getFilterableFieldTypeLabel(objectFilterDropdownSubMenuFieldType)} field`} + /> + {/* TODO: fix this with a backend field on ViewFilter for composite field filter */} + {fieldMetadataItemUsedInDropdown?.type === 'ACTOR' && + options.map((subFieldName, index) => ( + { + if (isDefined(fieldMetadataItemUsedInDropdown)) { + handleSelectFilter( + fieldMetadataItemUsedInDropdown, + subFieldName, + ); + } + }} + text={getCompositeSubFieldLabel( + objectFilterDropdownSubMenuFieldType, + subFieldName, + )} + LeftIcon={getIcon(fieldMetadataItemUsedInDropdown?.icon)} + /> + ))} + + + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterViewFilterFieldSelect.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterViewFilterFieldSelect.tsx deleted file mode 100644 index 1a4348304..000000000 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterViewFilterFieldSelect.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import { useAdvancedFilterDropdown } from '@/object-record/advanced-filter/hooks/useAdvancedFilterDropdown'; - -import { ObjectFilterDropdownFilterSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect'; -import { ObjectFilterDropdownFilterSelectCompositeFieldSubMenu } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectCompositeFieldSubMenu'; -import { advancedFilterViewFilterGroupIdComponentState } from '@/object-record/object-filter-dropdown/states/advancedFilterViewFilterGroupIdComponentState'; -import { advancedFilterViewFilterIdComponentState } from '@/object-record/object-filter-dropdown/states/advancedFilterViewFilterIdComponentState'; -import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState'; -import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; -import { SelectControl } from '@/ui/input/components/SelectControl'; -import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; -import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; -import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; -import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; -import styled from '@emotion/styled'; - -const StyledContainer = styled.div` - flex: 2; -`; - -type AdvancedFilterViewFilterFieldSelectProps = { - viewFilterId: string; -}; - -export const AdvancedFilterViewFilterFieldSelect = ({ - viewFilterId, -}: AdvancedFilterViewFilterFieldSelectProps) => { - const { advancedFilterDropdownId } = useAdvancedFilterDropdown(viewFilterId); - - const currentRecordFilters = useRecoilComponentValueV2( - currentRecordFiltersComponentState, - ); - - const recordFilter = currentRecordFilters.find( - (recordFilter) => recordFilter.id === viewFilterId, - ); - - const selectedFieldLabel = recordFilter?.label ?? ''; - - const setAdvancedFilterViewFilterId = useSetRecoilComponentStateV2( - advancedFilterViewFilterIdComponentState, - ); - - const setAdvancedFilterViewFilterGroupId = useSetRecoilComponentStateV2( - advancedFilterViewFilterGroupIdComponentState, - ); - - const [objectFilterDropdownIsSelectingCompositeField] = - useRecoilComponentStateV2( - objectFilterDropdownIsSelectingCompositeFieldComponentState, - ); - - const shouldShowCompositeSelectionSubMenu = - objectFilterDropdownIsSelectingCompositeField; - - return ( - - - } - onOpen={() => { - setAdvancedFilterViewFilterId(recordFilter?.id); - setAdvancedFilterViewFilterGroupId(recordFilter?.recordFilterGroupId); - }} - dropdownComponents={ - shouldShowCompositeSelectionSubMenu ? ( - - ) : ( - - ) - } - dropdownHotkeyScope={{ scope: advancedFilterDropdownId }} - dropdownOffset={{ y: 8, x: 0 }} - dropdownPlacement="bottom-start" - /> - - ); -}; diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/hooks/useAdvancedFilterDropdown.ts b/packages/twenty-front/src/modules/object-record/advanced-filter/hooks/useAdvancedFilterDropdown.ts deleted file mode 100644 index 77cc3f1c0..000000000 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/hooks/useAdvancedFilterDropdown.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; - -export const useAdvancedFilterDropdown = (viewFilterId?: string) => { - const advancedFilterDropdownId = `advanced-filter-view-filter-field-${viewFilterId}`; - - const { closeDropdown: closeAdvancedFilterDropdown } = useDropdown( - advancedFilterDropdownId, - ); - - return { - closeAdvancedFilterDropdown, - advancedFilterDropdownId, - }; -}; diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/hooks/useAdvancedFilterFieldSelectDropdown.ts b/packages/twenty-front/src/modules/object-record/advanced-filter/hooks/useAdvancedFilterFieldSelectDropdown.ts new file mode 100644 index 000000000..11389c948 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/hooks/useAdvancedFilterFieldSelectDropdown.ts @@ -0,0 +1,14 @@ +import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; + +export const useAdvancedFilterFieldSelectDropdown = (viewFilterId?: string) => { + const advancedFilterFieldSelectDropdownId = `advanced-filter-view-filter-field-${viewFilterId}`; + + const { closeDropdown: closeAdvancedFilterFieldSelectDropdown } = useDropdown( + advancedFilterFieldSelectDropdownId, + ); + + return { + closeAdvancedFilterFieldSelectDropdown, + advancedFilterFieldSelectDropdownId, + }; +}; diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/hooks/useSelectFieldUsedInAdvancedFilterDropdown.ts b/packages/twenty-front/src/modules/object-record/advanced-filter/hooks/useSelectFieldUsedInAdvancedFilterDropdown.ts new file mode 100644 index 000000000..8437da734 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/hooks/useSelectFieldUsedInAdvancedFilterDropdown.ts @@ -0,0 +1,110 @@ +import { useGetFieldMetadataItemById } from '@/object-metadata/hooks/useGetFieldMetadataItemById'; +import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; +import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState'; +import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState'; +import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; +import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState'; +import { getInitialFilterValue } from '@/object-record/object-filter-dropdown/utils/getInitialFilterValue'; +import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter'; +import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; +import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands'; +import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope'; + +import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; +import { isDefined } from 'twenty-shared/utils'; + +type SelectFilterParams = { + fieldMetadataItemId: string; + recordFilterId: string; + subFieldName?: string | null | undefined; +}; + +export const useSelectFieldUsedInAdvancedFilterDropdown = () => { + const setSelectedOperandInDropdown = useSetRecoilComponentStateV2( + selectedOperandInDropdownComponentState, + ); + + const setFieldMetadataItemIdUsedInDropdown = useSetRecoilComponentStateV2( + fieldMetadataItemIdUsedInDropdownComponentState, + ); + + const setObjectFilterDropdownSearchInput = useSetRecoilComponentStateV2( + objectFilterDropdownSearchInputComponentState, + ); + + const currentRecordFilters = useRecoilComponentValueV2( + currentRecordFiltersComponentState, + ); + + const setHotkeyScope = useSetHotkeyScope(); + + const { applyRecordFilter } = useApplyRecordFilter(); + + const { getFieldMetadataItemById } = useGetFieldMetadataItemById(); + + const setSubFieldNameUsedInDropdown = useSetRecoilComponentStateV2( + subFieldNameUsedInDropdownComponentState, + ); + + const selectFieldUsedInAdvancedFilterDropdown = ({ + fieldMetadataItemId, + recordFilterId, + subFieldName, + }: SelectFilterParams) => { + setFieldMetadataItemIdUsedInDropdown(fieldMetadataItemId); + + const fieldMetadataItem = getFieldMetadataItemById(fieldMetadataItemId); + + if (!isDefined(fieldMetadataItem)) { + return; + } + + if ( + fieldMetadataItem.type === 'RELATION' || + fieldMetadataItem.type === 'SELECT' + ) { + setHotkeyScope(SingleRecordPickerHotkeyScope.SingleRecordPicker); + } + + const filterType = getFilterTypeFromFieldType(fieldMetadataItem.type); + + const firstOperand = getRecordFilterOperands({ + filterType, + })[0]; + + setSelectedOperandInDropdown(firstOperand); + + const { value, displayValue } = getInitialFilterValue( + filterType, + firstOperand, + ); + + const existingRecordFilter = currentRecordFilters.find( + (recordFilter) => recordFilter.id === recordFilterId, + ); + + applyRecordFilter({ + id: recordFilterId, + fieldMetadataId: fieldMetadataItem.id, + displayValue, + operand: firstOperand, + value, + recordFilterGroupId: existingRecordFilter?.recordFilterGroupId, + type: filterType, + label: fieldMetadataItem.label, + subFieldName, + }); + + if (isDefined(subFieldName)) { + setSubFieldNameUsedInDropdown(subFieldName); + } + + setObjectFilterDropdownSearchInput(''); + }; + + return { + selectFieldUsedInAdvancedFilterDropdown, + }; +}; diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/MultipleFiltersDropdownContent.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/MultipleFiltersDropdownContent.tsx index 67b613264..78d2cbeb3 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/MultipleFiltersDropdownContent.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/MultipleFiltersDropdownContent.tsx @@ -27,11 +27,11 @@ export const MultipleFiltersDropdownContent = ({ const shouldShowCompositeSelectionSubMenu = objectFilterDropdownIsSelectingCompositeField; - const shoudShowFilterInput = objectFilterDropdownFilterIsSelected; + const shouldShowFilterInput = objectFilterDropdownFilterIsSelected; return ( <> - {shoudShowFilterInput ? ( + {shouldShowFilterInput ? ( 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 b3fad1cbb..35bbcfa59 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 @@ -2,7 +2,6 @@ import styled from '@emotion/styled'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; -import { useAdvancedFilterDropdown } from '@/object-record/advanced-filter/hooks/useAdvancedFilterDropdown'; import { AdvancedFilterButton } from '@/object-record/object-filter-dropdown/components/AdvancedFilterButton'; import { ObjectFilterDropdownFilterSelectMenuItem } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItem'; import { OBJECT_FILTER_DROPDOWN_ID } from '@/object-record/object-filter-dropdown/constants/ObjectFilterDropdownId'; @@ -21,7 +20,6 @@ import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { FeatureFlagKey } from '~/generated/graphql'; import { useSelectFilterUsedInDropdown } from '@/object-record/object-filter-dropdown/hooks/useSelectFilterUsedInDropdown'; -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 { useFilterableFieldMetadataItemsInRecordIndexContext } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItemsInRecordIndexContext'; @@ -65,17 +63,9 @@ export const ObjectFilterDropdownFilterSelect = ({ }: ObjectFilterDropdownFilterSelectProps) => { const { recordIndexId } = useRecordIndexContextOrThrow(); - const advancedFilterViewFilterId = useRecoilComponentValueV2( - advancedFilterViewFilterIdComponentState, - ); - const [objectFilterDropdownSearchInput, setObjectFilterDropdownSearchInput] = useRecoilComponentStateV2(objectFilterDropdownSearchInputComponentState); - const { closeAdvancedFilterDropdown } = useAdvancedFilterDropdown( - advancedFilterViewFilterId, - ); - const { filterableFieldMetadataItems } = useFilterableFieldMetadataItemsInRecordIndexContext(); @@ -136,11 +126,9 @@ export const ObjectFilterDropdownFilterSelect = ({ }); setFieldMetadataItemIdUsedInDropdown(fieldMetadataItemId); - - closeAdvancedFilterDropdown(); }; - const shoudShowSeparator = + const shouldShowSeparator = visibleColumnsFieldMetadataItems.length > 0 && hiddenColumnsFieldMetadataItems.length > 0; @@ -186,7 +174,7 @@ export const ObjectFilterDropdownFilterSelect = ({ ), )} - {shoudShowSeparator && } + {shouldShowSeparator && } {hiddenColumnsFieldMetadataItems.map( (hiddenFieldMetadataItem, index) => ( { objectFilterDropdownSearchInputComponentState, ); - const advancedFilterViewFilterId = useRecoilComponentValueV2( - advancedFilterViewFilterIdComponentState, - ); - - const advancedFilterViewFilterGroupId = useRecoilComponentValueV2( - advancedFilterViewFilterGroupIdComponentState, - ); - - const { applyRecordFilter } = useApplyRecordFilter(); - - const { closeAdvancedFilterDropdown } = useAdvancedFilterDropdown( - advancedFilterViewFilterId, - ); - const currentRecordFilters = useRecoilComponentValueV2( currentRecordFiltersComponentState, ); @@ -109,30 +89,6 @@ export const ObjectFilterDropdownFilterSelectCompositeFieldSubMenu = () => { subFieldName: subFieldName, })[0]; - if ( - isDefined(advancedFilterViewFilterId) && - isDefined(advancedFilterViewFilterGroupId) - ) { - closeAdvancedFilterDropdown(); - - const { value, displayValue } = getInitialFilterValue( - type, - defaultOperand, - ); - - applyRecordFilter({ - id: advancedFilterViewFilterId, - fieldMetadataId: fieldMetadataItem.id, - value, - operand: defaultOperand, - displayValue, - type, - label: fieldMetadataItem.label, - recordFilterGroupId: advancedFilterViewFilterGroupId, - subFieldName: subFieldName, - }); - } - setFieldMetadataItemIdUsedInDropdown(fieldMetadataItem.id); setSubFieldNameUsedInDropdown(subFieldName); @@ -152,9 +108,7 @@ export const ObjectFilterDropdownFilterSelectCompositeFieldSubMenu = () => { duplicateFilterInCurrentRecordFilters, ); - const isSimpleFilter = !isDefined(advancedFilterViewFilterId); - - if (isSimpleFilter && filterIsAlreadyInCurrentRecordFilters) { + if (filterIsAlreadyInCurrentRecordFilters) { setSelectedFilter({ ...duplicateFilterInCurrentRecordFilters, }); 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 aaee2c6ba..09887c78d 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,6 +1,4 @@ -import { useAdvancedFilterDropdown } from '@/object-record/advanced-filter/hooks/useAdvancedFilterDropdown'; import { OBJECT_FILTER_DROPDOWN_ID } from '@/object-record/object-filter-dropdown/constants/ObjectFilterDropdownId'; -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'; @@ -61,16 +59,8 @@ export const ObjectFilterDropdownFilterSelectMenuItem = ({ selectedOperandInDropdownComponentState, ); - const advancedFilterViewFilterId = useRecoilComponentValueV2( - advancedFilterViewFilterIdComponentState, - ); - const setHotkeyScope = useSetHotkeyScope(); - const { closeAdvancedFilterDropdown } = useAdvancedFilterDropdown( - advancedFilterViewFilterId, - ); - const currentRecordFilters = useRecoilComponentValueV2( currentRecordFiltersComponentState, ); @@ -80,8 +70,6 @@ export const ObjectFilterDropdownFilterSelectMenuItem = ({ ); const handleSelectFilter = (fieldMetadataItem: FieldMetadataItem) => { - closeAdvancedFilterDropdown(); - setFieldMetadataItemIdUsedInDropdown(fieldMetadataItem.id); const filterType = getFilterTypeFromFieldType(fieldMetadataItem.type); @@ -106,9 +94,7 @@ export const ObjectFilterDropdownFilterSelectMenuItem = ({ duplicateFilterInCurrentRecordFilters, ); - const isSimpleFilter = !isDefined(advancedFilterViewFilterId); - - if (isSimpleFilter && filterIsAlreadyInCurrentRecordFilters) { + if (filterIsAlreadyInCurrentRecordFilters) { setSelectedFilter({ ...duplicateFilterInCurrentRecordFilters, }); diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItemV2.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItemV2.tsx new file mode 100644 index 000000000..431f81b9d --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectMenuItemV2.tsx @@ -0,0 +1,48 @@ +import { OBJECT_FILTER_DROPDOWN_ID } from '@/object-record/object-filter-dropdown/constants/ObjectFilterDropdownId'; + +import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; +import { isCompositeField } from '@/object-record/object-filter-dropdown/utils/isCompositeField'; +import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; +import { useRecoilValue } from 'recoil'; +import { MenuItemSelect, useIcons } from 'twenty-ui'; + +export type ObjectFilterDropdownFilterSelectMenuItemV2Props = { + fieldMetadataItemToSelect: FieldMetadataItem; + onClick: (selectedFieldMetadataItem: FieldMetadataItem) => void; +}; + +export const ObjectFilterDropdownFilterSelectMenuItemV2 = ({ + fieldMetadataItemToSelect, + onClick, +}: ObjectFilterDropdownFilterSelectMenuItemV2Props) => { + const { isSelectedItemIdSelector, resetSelectedItem } = useSelectableList( + OBJECT_FILTER_DROPDOWN_ID, + ); + + const isSelectedItem = useRecoilValue( + isSelectedItemIdSelector(fieldMetadataItemToSelect.id), + ); + + const { getIcon } = useIcons(); + + const Icon = getIcon(fieldMetadataItemToSelect.icon); + + const shouldShowSubMenu = isCompositeField(fieldMetadataItemToSelect.type); + + const handleClick = () => { + resetSelectedItem(); + + onClick(fieldMetadataItemToSelect); + }; + + return ( + + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useSelectFilterUsedInDropdown.ts b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useSelectFilterUsedInDropdown.ts index 8a417ad16..68ae3a723 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useSelectFilterUsedInDropdown.ts +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useSelectFilterUsedInDropdown.ts @@ -1,7 +1,5 @@ import { useGetFieldMetadataItemById } from '@/object-metadata/hooks/useGetFieldMetadataItemById'; import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; -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 { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState'; import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; @@ -11,7 +9,6 @@ import { getRecordFilterOperands } from '@/object-record/record-filter/utils/get import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope'; import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; -import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { v4 } from 'uuid'; import { isDefined } from 'twenty-shared/utils'; @@ -36,16 +33,6 @@ export const useSelectFilterUsedInDropdown = (componentInstanceId?: string) => { componentInstanceId, ); - const advancedFilterViewFilterGroupId = useRecoilComponentValueV2( - advancedFilterViewFilterGroupIdComponentState, - componentInstanceId, - ); - - const advancedFilterViewFilterId = useRecoilComponentValueV2( - advancedFilterViewFilterIdComponentState, - componentInstanceId, - ); - const setHotkeyScope = useSetHotkeyScope(); const { applyRecordFilter } = useApplyRecordFilter(componentInstanceId); @@ -83,16 +70,13 @@ export const useSelectFilterUsedInDropdown = (componentInstanceId?: string) => { firstOperand, ); - const isAdvancedFilter = isDefined(advancedFilterViewFilterId); - - if (isAdvancedFilter || value !== '') { + if (value !== '') { applyRecordFilter({ - id: advancedFilterViewFilterId ?? v4(), + id: v4(), fieldMetadataId: fieldMetadataItem.id, displayValue, operand: firstOperand, value, - recordFilterGroupId: advancedFilterViewFilterGroupId, type: filterType, label: fieldMetadataItem.label, }); diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/advancedFilterViewFilterGroupIdComponentState.ts b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/advancedFilterViewFilterGroupIdComponentState.ts deleted file mode 100644 index 64d8238bc..000000000 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/advancedFilterViewFilterGroupIdComponentState.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext'; -import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; - -export const advancedFilterViewFilterGroupIdComponentState = - createComponentStateV2({ - key: 'advancedFilterViewFilterGroupIdComponentState', - defaultValue: undefined, - componentInstanceContext: ObjectFilterDropdownComponentInstanceContext, - }); diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/advancedFilterViewFilterIdComponentState.ts b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/advancedFilterViewFilterIdComponentState.ts deleted file mode 100644 index cc3e3678d..000000000 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/advancedFilterViewFilterIdComponentState.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext'; -import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; - -export const advancedFilterViewFilterIdComponentState = createComponentStateV2< - string | undefined ->({ - key: 'advancedFilterViewFilterIdComponentState', - defaultValue: undefined, - componentInstanceContext: ObjectFilterDropdownComponentInstanceContext, -});