diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterDropdownDateInput.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterDropdownDateInput.tsx deleted file mode 100644 index 1a3f4005f..000000000 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterDropdownDateInput.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import { v4 } from 'uuid'; - -import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; -import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector'; -import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; -import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; -import { getRelativeDateDisplayValue } from '@/object-record/object-filter-dropdown/utils/getRelativeDateDisplayValue'; -import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter'; -import { DateTimePicker } from '@/ui/input/components/internal/date/components/InternalDatePicker'; -import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; -import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; -import { computeVariableDateViewFilterValue } from '@/views/view-filter-value/utils/computeVariableDateViewFilterValue'; -import { - resolveDateViewFilterValue, - VariableDateViewFilterValueDirection, - VariableDateViewFilterValueUnit, -} from '@/views/view-filter-value/utils/resolveDateViewFilterValue'; -import { useState } from 'react'; -import { isDefined } from 'twenty-shared/utils'; -import { FieldMetadataType } from '~/generated-metadata/graphql'; - -export const AdvancedFilterDropdownDateInput = () => { - const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2( - fieldMetadataItemUsedInDropdownComponentSelector, - ); - - const selectedOperandInDropdown = useRecoilComponentValueV2( - selectedOperandInDropdownComponentState, - ); - - const selectedFilter = useRecoilComponentValueV2( - selectedFilterComponentState, - ); - - const { applyRecordFilter } = useApplyRecordFilter(); - - const initialFilterValue = selectedFilter - ? resolveDateViewFilterValue(selectedFilter) - : null; - const [internalDate, setInternalDate] = useState( - initialFilterValue instanceof Date ? initialFilterValue : null, - ); - - const isDateTimeInput = - fieldMetadataItemUsedInDropdown?.type === FieldMetadataType.DATE_TIME; - - const handleAbsoluteDateChange = (newDate: Date | null) => { - setInternalDate(newDate); - - if (!fieldMetadataItemUsedInDropdown || !selectedOperandInDropdown) return; - - const newDisplayValue = isDefined(newDate) - ? newDate.toLocaleDateString() - : ''; - - applyRecordFilter({ - id: selectedFilter?.id ? selectedFilter.id : v4(), - fieldMetadataId: fieldMetadataItemUsedInDropdown.id, - value: newDate?.toISOString() ?? '', - operand: selectedOperandInDropdown, - displayValue: newDisplayValue, - recordFilterGroupId: selectedFilter?.recordFilterGroupId, - positionInRecordFilterGroup: selectedFilter?.positionInRecordFilterGroup, - type: getFilterTypeFromFieldType(fieldMetadataItemUsedInDropdown.type), - label: fieldMetadataItemUsedInDropdown.label, - }); - }; - - const handleRelativeDateChange = ( - relativeDate: { - direction: VariableDateViewFilterValueDirection; - amount?: number; - unit: VariableDateViewFilterValueUnit; - } | null, - ) => { - if (!fieldMetadataItemUsedInDropdown || !selectedOperandInDropdown) return; - - const value = relativeDate - ? computeVariableDateViewFilterValue( - relativeDate.direction, - relativeDate.amount, - relativeDate.unit, - ) - : ''; - - applyRecordFilter({ - id: selectedFilter?.id ? selectedFilter.id : v4(), - fieldMetadataId: fieldMetadataItemUsedInDropdown.id, - value, - operand: selectedOperandInDropdown, - displayValue: getRelativeDateDisplayValue(relativeDate), - recordFilterGroupId: selectedFilter?.recordFilterGroupId, - positionInRecordFilterGroup: selectedFilter?.positionInRecordFilterGroup, - type: getFilterTypeFromFieldType(fieldMetadataItemUsedInDropdown.type), - label: fieldMetadataItemUsedInDropdown.label, - }); - }; - - const isRelativeOperand = - selectedOperandInDropdown === ViewFilterOperand.IsRelative; - - const resolvedValue = selectedFilter - ? resolveDateViewFilterValue(selectedFilter) - : null; - - const relativeDate = - resolvedValue && !(resolvedValue instanceof Date) - ? resolvedValue - : undefined; - - return ( - - ); -}; diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterDropdownFilterInput.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterDropdownFilterInput.tsx index 9c2de5a91..7afb23afb 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterDropdownFilterInput.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterDropdownFilterInput.tsx @@ -5,9 +5,9 @@ import { ObjectFilterDropdownSearchInput } from '@/object-record/object-filter-d import { ObjectFilterDropdownSourceSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownSourceSelect'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; -import { AdvancedFilterDropdownDateInput } from '@/object-record/advanced-filter/components/AdvancedFilterDropdownDateInput'; import { ObjectFilterDropdownBooleanSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownBooleanSelect'; import { ObjectFilterDropdownCurrencySelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownCurrencySelect'; +import { ObjectFilterDropdownDateInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownDateInput'; import { ObjectFilterDropdownTextInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownTextInput'; import { DATE_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/DateFilterTypes'; import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState'; @@ -41,7 +41,7 @@ export const AdvancedFilterDropdownFilterInput = ({ <> {filterType === 'RATING' && } {DATE_FILTER_TYPES.includes(filterType) && ( - + )} {filterType === 'RELATION' && ( <> diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterDropdownNumberInput.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterDropdownNumberInput.tsx index e8cf61cae..d20eade9c 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterDropdownNumberInput.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterDropdownNumberInput.tsx @@ -1,13 +1,9 @@ -import { useState } from 'react'; - -import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; +import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue'; +import { useObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useObjectFilterDropdownFilterValue'; import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector'; -import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; -import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter'; import { TextInputV2 } from '@/ui/input/components/TextInputV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; -import { v4 } from 'uuid'; export const AdvancedFilterDropdownNumberInput = () => { const selectedOperandInDropdown = useRecoilComponentValueV2( @@ -18,34 +14,14 @@ export const AdvancedFilterDropdownNumberInput = () => { fieldMetadataItemUsedInDropdownComponentSelector, ); - const selectedFilter = useRecoilComponentValueV2( - selectedFilterComponentState, - ); + const { objectFilterDropdownFilterValue } = + useObjectFilterDropdownFilterValue(); - const { applyRecordFilter } = useApplyRecordFilter(); - - const [inputValue, setInputValue] = useState( - () => selectedFilter?.value || '', - ); + const { applyObjectFilterDropdownFilterValue } = + useApplyObjectFilterDropdownFilterValue(); const handleChange = (newValue: string) => { - if (!fieldMetadataItemUsedInDropdown || !selectedOperandInDropdown) { - return; - } - - setInputValue(newValue); - - applyRecordFilter({ - id: selectedFilter?.id ? selectedFilter.id : v4(), - fieldMetadataId: fieldMetadataItemUsedInDropdown?.id ?? '', - value: newValue, - operand: selectedOperandInDropdown, - displayValue: newValue, - type: getFilterTypeFromFieldType(fieldMetadataItemUsedInDropdown.type), - label: fieldMetadataItemUsedInDropdown.label, - recordFilterGroupId: selectedFilter?.recordFilterGroupId, - positionInRecordFilterGroup: selectedFilter?.positionInRecordFilterGroup, - }); + applyObjectFilterDropdownFilterValue(newValue); }; if (!selectedOperandInDropdown || !fieldMetadataItemUsedInDropdown) { @@ -54,7 +30,7 @@ export const AdvancedFilterDropdownNumberInput = () => { return ( { - const { applyRecordFilter } = useApplyRecordFilter(); + const { applyObjectFilterDropdownFilterValue } = + useApplyObjectFilterDropdownFilterValue(); const handleChange = (newValue: string) => { - applyRecordFilter({ - id: recordFilter.id, - fieldMetadataId: recordFilter.fieldMetadataId, - value: newValue, - operand: recordFilter.operand, - displayValue: newValue, - type: recordFilter.type, - label: recordFilter.label, - recordFilterGroupId: recordFilter.recordFilterGroupId, - positionInRecordFilterGroup: recordFilter.positionInRecordFilterGroup, - subFieldName: recordFilter.subFieldName, - }); + applyObjectFilterDropdownFilterValue(newValue); }; return ( diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterValueInput.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterValueInput.tsx index bd482bfab..9327afb69 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterValueInput.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterValueInput.tsx @@ -4,6 +4,8 @@ import { AdvancedFilterValueInputDropdownButtonClickableSelect } from '@/object- import { DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET } from '@/object-record/advanced-filter/constants/DefaultAdvancedFilterDropdownOffset'; import { NUMBER_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/NumberFilterTypes'; import { TEXT_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/TextFilterTypes'; +import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState'; +import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState'; import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState'; import { configurableViewFilterOperands } from '@/object-record/object-filter-dropdown/utils/configurableViewFilterOperands'; import { isExpectedSubFieldName } from '@/object-record/object-filter-dropdown/utils/isExpectedSubFieldName'; @@ -44,6 +46,15 @@ export const AdvancedFilterValueInput = ({ objectFilterDropdownSearchInputComponentState, ); + const setFieldMetadataItemIdUsedInDropdown = useSetRecoilComponentStateV2( + fieldMetadataItemIdUsedInDropdownComponentState, + ); + + const setObjectFilterDropdownCurrentRecordFilter = + useSetRecoilComponentStateV2( + objectFilterDropdownCurrentRecordFilterComponentState, + ); + const operandHasNoInput = recordFilter && !configurableViewFilterOperands.has(recordFilter.operand); @@ -55,6 +66,11 @@ export const AdvancedFilterValueInput = ({ setObjectFilterDropdownSearchInput(''); }; + const handleFilterValueDropdownOpen = () => { + setObjectFilterDropdownCurrentRecordFilter(recordFilter); + setFieldMetadataItemIdUsedInDropdown(recordFilter.fieldMetadataId); + }; + const filterType = recordFilter.type; const dropdownContentOffset = @@ -98,6 +114,7 @@ export const AdvancedFilterValueInput = ({ dropdownPlacement="bottom-start" dropdownWidth={280} onClose={handleFilterValueDropdownClose} + onOpen={handleFilterValueDropdownOpen} /> )} 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 index 4c97d3312..a913d88b9 100644 --- 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 @@ -1,12 +1,14 @@ 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 { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState'; 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 { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter'; import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; +import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter'; import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands'; import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope'; @@ -40,14 +42,19 @@ export const useSelectFieldUsedInAdvancedFilterDropdown = () => { const setHotkeyScope = useSetHotkeyScope(); - const { applyRecordFilter } = useApplyRecordFilter(); - const { getFieldMetadataItemById } = useGetFieldMetadataItemById(); const setSubFieldNameUsedInDropdown = useSetRecoilComponentStateV2( subFieldNameUsedInDropdownComponentState, ); + const setObjectFilterDropdownCurrentRecordFilter = + useSetRecoilComponentStateV2( + objectFilterDropdownCurrentRecordFilterComponentState, + ); + + const { upsertRecordFilter } = useUpsertRecordFilter(); + const selectFieldUsedInAdvancedFilterDropdown = ({ fieldMetadataItemId, recordFilterId, @@ -86,7 +93,7 @@ export const useSelectFieldUsedInAdvancedFilterDropdown = () => { (recordFilter) => recordFilter.id === recordFilterId, ); - applyRecordFilter({ + const newAdvancedFilter = { id: recordFilterId, fieldMetadataId: fieldMetadataItem.id, displayValue, @@ -97,12 +104,15 @@ export const useSelectFieldUsedInAdvancedFilterDropdown = () => { existingRecordFilter?.positionInRecordFilterGroup, type: filterType, label: fieldMetadataItem.label, - subFieldName: subFieldName ?? null, - }); + subFieldName, + } satisfies RecordFilter; setSubFieldNameUsedInDropdown(subFieldName); setObjectFilterDropdownSearchInput(''); + + setObjectFilterDropdownCurrentRecordFilter(newAdvancedFilter); + upsertRecordFilter(newAdvancedFilter); }; return { diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/hooks/useSetAdvancedFilterDropdownAllRowsStates.ts b/packages/twenty-front/src/modules/object-record/advanced-filter/hooks/useSetAdvancedFilterDropdownAllRowsStates.ts new file mode 100644 index 000000000..cd0ca0865 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/hooks/useSetAdvancedFilterDropdownAllRowsStates.ts @@ -0,0 +1,98 @@ +import { rootLevelRecordFilterGroupComponentSelector } from '@/object-record/advanced-filter/states/rootLevelRecordFilterGroupComponentSelector'; +import { getAdvancedFilterObjectFilterDropdownComponentInstanceId } from '@/object-record/advanced-filter/utils/getAdvancedFilterObjectFilterDropdownComponentInstanceId'; +import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState'; +import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState'; +import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState'; +import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { useRecoilCallback } from 'recoil'; + +export const useSetAdvancedFilterDropdownStates = () => { + const rootLevelRecordFilterGroup = useRecoilComponentValueV2( + rootLevelRecordFilterGroupComponentSelector, + ); + + const currentRecordFilters = useRecoilComponentValueV2( + currentRecordFiltersComponentState, + ); + + const currentRecordFilterGroups = useRecoilComponentValueV2( + currentRecordFilterGroupsComponentState, + ); + + const setAdvancedFilterDropdownStates = useRecoilCallback( + ({ set }) => + () => { + const rootLevelRecordFilters = currentRecordFilters.filter( + (recordFilter) => + recordFilter.recordFilterGroupId === rootLevelRecordFilterGroup?.id, + ); + + for (const rootLevelRecordFilter of rootLevelRecordFilters) { + set( + objectFilterDropdownCurrentRecordFilterComponentState.atomFamily({ + instanceId: + getAdvancedFilterObjectFilterDropdownComponentInstanceId( + rootLevelRecordFilter.id, + ), + }), + rootLevelRecordFilter, + ); + + set( + fieldMetadataItemIdUsedInDropdownComponentState.atomFamily({ + instanceId: + getAdvancedFilterObjectFilterDropdownComponentInstanceId( + rootLevelRecordFilter.id, + ), + }), + rootLevelRecordFilter.fieldMetadataId, + ); + } + + const childRecordFilterGroups = currentRecordFilterGroups.filter( + (currentRecordGroupToFilter) => + currentRecordGroupToFilter.parentRecordFilterGroupId === + rootLevelRecordFilterGroup?.id, + ); + + for (const childRecordFilterGroup of childRecordFilterGroups) { + const recordFiltersInThisGroup = currentRecordFilters.filter( + (recordFilter) => + recordFilter.recordFilterGroupId === childRecordFilterGroup.id, + ); + + for (const recordFilterInThisGroup of recordFiltersInThisGroup) { + set( + objectFilterDropdownCurrentRecordFilterComponentState.atomFamily({ + instanceId: + getAdvancedFilterObjectFilterDropdownComponentInstanceId( + recordFilterInThisGroup.id, + ), + }), + recordFilterInThisGroup, + ); + + set( + fieldMetadataItemIdUsedInDropdownComponentState.atomFamily({ + instanceId: + getAdvancedFilterObjectFilterDropdownComponentInstanceId( + recordFilterInThisGroup.id, + ), + }), + recordFilterInThisGroup.fieldMetadataId, + ); + } + } + }, + [ + currentRecordFilterGroups, + currentRecordFilters, + rootLevelRecordFilterGroup, + ], + ); + + return { + setAdvancedFilterDropdownStates, + }; +}; diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/hooks/useSetRecordFilterUsedInAdvancedFilterDropdownRow.ts b/packages/twenty-front/src/modules/object-record/advanced-filter/hooks/useSetRecordFilterUsedInAdvancedFilterDropdownRow.ts index f9a00b8fd..3eddb61cd 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/hooks/useSetRecordFilterUsedInAdvancedFilterDropdownRow.ts +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/hooks/useSetRecordFilterUsedInAdvancedFilterDropdownRow.ts @@ -1,6 +1,6 @@ import { getAdvancedFilterObjectFilterDropdownComponentInstanceId } from '@/object-record/advanced-filter/utils/getAdvancedFilterObjectFilterDropdownComponentInstanceId'; import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState'; -import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; +import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState'; import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter'; import { useRecoilCallback } from 'recoil'; @@ -31,7 +31,7 @@ export const useSetRecordFilterUsedInAdvancedFilterDropdownRow = () => { ); set( - selectedFilterComponentState.atomFamily({ + objectFilterDropdownCurrentRecordFilterComponentState.atomFamily({ instanceId: advancedFilterRowObjectFilterDropdownComponentInstanceId, }), diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownBooleanSelect.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownBooleanSelect.tsx index f17f0a66e..168a7be92 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownBooleanSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownBooleanSelect.tsx @@ -1,20 +1,13 @@ import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; -import { useEffect, useState } from 'react'; -import { v4 } from 'uuid'; -import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; -import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector'; -import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; -import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; -import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter'; +import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue'; +import { useObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useObjectFilterDropdownFilterValue'; import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope'; import { BooleanDisplay } from '@/ui/field/display/components/BooleanDisplay'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; -import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; -import { isDefined } from 'twenty-shared/utils'; import { IconCheck } from 'twenty-ui/display'; const StyledBooleanSelectContainer = styled.div<{ selected?: boolean }>` @@ -40,52 +33,20 @@ export const ObjectFilterDropdownBooleanSelect = () => { const theme = useTheme(); const options = [true, false]; - const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2( - fieldMetadataItemUsedInDropdownComponentSelector, - ); + const { objectFilterDropdownFilterValue } = + useObjectFilterDropdownFilterValue(); - const selectedOperandInDropdown = useRecoilComponentValueV2( - selectedOperandInDropdownComponentState, - ); - - const selectedFilter = useRecoilComponentValueV2( - selectedFilterComponentState, - ); - - const { applyRecordFilter } = useApplyRecordFilter(); + const { applyObjectFilterDropdownFilterValue } = + useApplyObjectFilterDropdownFilterValue(); const { closeDropdown } = useDropdown(); - const [selectedValue, setSelectedValue] = useState( - selectedFilter?.value === 'true', - ); + const handleOptionSelect = (newValue: boolean) => { + applyObjectFilterDropdownFilterValue( + newValue.toString(), + newValue ? 'True' : 'False', + ); - useEffect(() => { - setSelectedValue(selectedFilter?.value === 'true'); - }, [selectedFilter?.value]); - - const handleOptionSelect = (value: boolean) => { - if ( - !isDefined(fieldMetadataItemUsedInDropdown) || - !isDefined(selectedOperandInDropdown) - ) { - return; - } - - applyRecordFilter({ - id: selectedFilter?.id ?? v4(), - operand: selectedOperandInDropdown, - displayValue: value ? 'True' : 'False', - fieldMetadataId: fieldMetadataItemUsedInDropdown.id, - value: value.toString(), - recordFilterGroupId: selectedFilter?.recordFilterGroupId, - positionInRecordFilterGroup: selectedFilter?.positionInRecordFilterGroup, - type: getFilterTypeFromFieldType(fieldMetadataItemUsedInDropdown.type), - label: fieldMetadataItemUsedInDropdown.label, - subFieldName: selectedFilter?.subFieldName, - }); - - setSelectedValue(value); closeDropdown(); }; @@ -100,10 +61,10 @@ export const ObjectFilterDropdownBooleanSelect = () => { handleOptionSelect(option)} - selected={selectedValue === option} + selected={objectFilterDropdownFilterValue === option.toString()} > - {selectedFilter?.value === option.toString() && ( + {objectFilterDropdownFilterValue === option.toString() && ( diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownCurrencySelect.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownCurrencySelect.tsx index 0893eead3..d10f7fcfa 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownCurrencySelect.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownCurrencySelect.tsx @@ -1,12 +1,7 @@ -import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; +import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue'; import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector'; -import { objectFilterDropdownSelectedRecordIdsComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSelectedRecordIdsComponentState'; -import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; -import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; +import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState'; import { turnCurrencyIntoSelectableItem } from '@/object-record/object-filter-dropdown/utils/turnCurrencyIntoSelectableItem'; -import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter'; -import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; -import { findDuplicateRecordFilterInNonAdvancedRecordFilters } from '@/object-record/record-filter/utils/findDuplicateRecordFilterInNonAdvancedRecordFilters'; import { StyledMultipleSelectDropdownAvatarChip } from '@/object-record/select/components/StyledMultipleSelectDropdownAvatarChip'; import { SelectableItem } from '@/object-record/select/types/SelectableItem'; import { CURRENCIES } from '@/settings/data-model/constants/Currencies'; @@ -14,70 +9,55 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; -import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; -import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; import { useLingui } from '@lingui/react/macro'; +import { isNonEmptyString } from '@sniptt/guards'; import { ChangeEvent, useState } from 'react'; import { isDefined } from 'twenty-shared/utils'; import { MenuItem, MenuItemMultiSelectAvatar } from 'twenty-ui/navigation'; -import { v4 } from 'uuid'; export const EMPTY_FILTER_VALUE = '[]'; export const MAX_ITEMS_TO_DISPLAY = 3; type ObjectFilterDropdownCurrencySelectProps = { - viewComponentId?: string; dropdownWidth?: number; }; export const ObjectFilterDropdownCurrencySelect = ({ - viewComponentId, dropdownWidth, }: ObjectFilterDropdownCurrencySelectProps) => { const [searchText, setSearchText] = useState(''); - const selectedFilter = useRecoilComponentValueV2( - selectedFilterComponentState, + const objectFilterDropdownCurrentRecordFilter = useRecoilComponentValueV2( + objectFilterDropdownCurrentRecordFilterComponentState, ); - const setObjectFilterDropdownSelectedRecordIds = useSetRecoilComponentStateV2( - objectFilterDropdownSelectedRecordIdsComponentState, - selectedFilter?.id, - ); - - const objectFilterDropdownSelectedRecordIds = useRecoilComponentValueV2( - objectFilterDropdownSelectedRecordIdsComponentState, - selectedFilter?.id, - ); - - const selectedOperandInDropdown = useRecoilComponentValueV2( - selectedOperandInDropdownComponentState, - ); + const { applyObjectFilterDropdownFilterValue } = + useApplyObjectFilterDropdownFilterValue(); const fieldMetadataItemUsedInFilterDropdown = useRecoilComponentValueV2( fieldMetadataItemUsedInDropdownComponentSelector, ); - const { applyRecordFilter } = useApplyRecordFilter(viewComponentId); - const currenciesAsSelectableItems = CURRENCIES.map( turnCurrencyIntoSelectableItem, ); + const selectedCurrencies = isNonEmptyString( + objectFilterDropdownCurrentRecordFilter?.value, + ) + ? (JSON.parse(objectFilterDropdownCurrentRecordFilter.value) as string[]) // TODO: replace by a safe parse + : []; + const filteredSelectableItems = currenciesAsSelectableItems.filter( (selectableItem) => selectableItem.name.toLowerCase().includes(searchText.toLowerCase()) && - !objectFilterDropdownSelectedRecordIds.includes(selectableItem.id), + !selectedCurrencies.includes(selectableItem.id), ); const filteredSelectedItems = currenciesAsSelectableItems.filter( (selectableItem) => selectableItem.name.toLowerCase().includes(searchText.toLowerCase()) && - objectFilterDropdownSelectedRecordIds.includes(selectableItem.id), - ); - - const currentRecordFilters = useRecoilComponentValueV2( - currentRecordFiltersComponentState, + selectedCurrencies.includes(selectableItem.id), ); const handleMultipleItemSelectChange = ( @@ -85,10 +65,8 @@ export const ObjectFilterDropdownCurrencySelect = ({ newSelectedValue: boolean, ) => { const newSelectedItemIds = newSelectedValue - ? [...objectFilterDropdownSelectedRecordIds, itemToSelect.id] - : objectFilterDropdownSelectedRecordIds.filter( - (id) => id !== itemToSelect.id, - ); + ? [...selectedCurrencies, itemToSelect.id] + : selectedCurrencies.filter((id) => id !== itemToSelect.id); if (!isDefined(fieldMetadataItemUsedInFilterDropdown)) { throw new Error( @@ -96,8 +74,6 @@ export const ObjectFilterDropdownCurrencySelect = ({ ); } - setObjectFilterDropdownSelectedRecordIds(newSelectedItemIds); - const selectedItemNames = currenciesAsSelectableItems .filter((option) => newSelectedItemIds.includes(option.id)) .map((option) => option.name); @@ -107,46 +83,12 @@ export const ObjectFilterDropdownCurrencySelect = ({ ? `${selectedItemNames.length} currencies` : selectedItemNames.join(', '); - if ( - isDefined(fieldMetadataItemUsedInFilterDropdown) && - isDefined(selectedOperandInDropdown) - ) { - const newFilterValue = - newSelectedItemIds.length > 0 - ? JSON.stringify(newSelectedItemIds) - : EMPTY_FILTER_VALUE; + const newFilterValue = + newSelectedItemIds.length > 0 + ? JSON.stringify(newSelectedItemIds) + : EMPTY_FILTER_VALUE; - const duplicateFilterInCurrentRecordFilters = - findDuplicateRecordFilterInNonAdvancedRecordFilters({ - recordFilters: currentRecordFilters, - fieldMetadataItemId: fieldMetadataItemUsedInFilterDropdown.id, - subFieldName: 'currencyCode', - }); - - const filterIsAlreadyInCurrentRecordFilters = isDefined( - duplicateFilterInCurrentRecordFilters, - ); - - const filterId = filterIsAlreadyInCurrentRecordFilters - ? duplicateFilterInCurrentRecordFilters?.id - : v4(); - - applyRecordFilter({ - id: selectedFilter?.id ? selectedFilter.id : filterId, - type: getFilterTypeFromFieldType( - fieldMetadataItemUsedInFilterDropdown.type, - ), - label: fieldMetadataItemUsedInFilterDropdown.label, - operand: selectedOperandInDropdown || ViewFilterOperand.Is, - displayValue: filterDisplayValue, - fieldMetadataId: fieldMetadataItemUsedInFilterDropdown.id, - value: newFilterValue, - recordFilterGroupId: selectedFilter?.recordFilterGroupId, - subFieldName: 'currencyCode', - positionInRecordFilterGroup: - selectedFilter?.positionInRecordFilterGroup, - }); - } + applyObjectFilterDropdownFilterValue(newFilterValue, filterDisplayValue); }; const showNoResult = diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownDateInput.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownDateInput.tsx index 9d0ebc99e..921164ccd 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownDateInput.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownDateInput.tsx @@ -1,11 +1,8 @@ -import { v4 } from 'uuid'; - -import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; +import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue'; import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector'; -import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; +import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState'; import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; import { getRelativeDateDisplayValue } from '@/object-record/object-filter-dropdown/utils/getRelativeDateDisplayValue'; -import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter'; import { DateTimePicker } from '@/ui/input/components/internal/date/components/InternalDatePicker'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; @@ -28,15 +25,17 @@ export const ObjectFilterDropdownDateInput = () => { selectedOperandInDropdownComponentState, ); - const selectedFilter = useRecoilComponentValueV2( - selectedFilterComponentState, + const objectFilterDropdownCurrentRecordFilter = useRecoilComponentValueV2( + objectFilterDropdownCurrentRecordFilterComponentState, ); - const { applyRecordFilter } = useApplyRecordFilter(); + const { applyObjectFilterDropdownFilterValue } = + useApplyObjectFilterDropdownFilterValue(); - const initialFilterValue = selectedFilter - ? resolveDateViewFilterValue(selectedFilter) + const initialFilterValue = isDefined(objectFilterDropdownCurrentRecordFilter) + ? resolveDateViewFilterValue(objectFilterDropdownCurrentRecordFilter) : null; + const [internalDate, setInternalDate] = useState( initialFilterValue instanceof Date ? initialFilterValue : null, ); @@ -47,24 +46,14 @@ export const ObjectFilterDropdownDateInput = () => { const handleAbsoluteDateChange = (newDate: Date | null) => { setInternalDate(newDate); - if (!fieldMetadataItemUsedInDropdown || !selectedOperandInDropdown) return; + const newFilterValue = newDate?.toISOString() ?? ''; + const newDisplayValue = isDefined(newDate) + ? isDateTimeInput + ? newDate.toLocaleString() + : newDate.toLocaleDateString() + : ''; - applyRecordFilter({ - id: selectedFilter?.id ? selectedFilter.id : v4(), - fieldMetadataId: fieldMetadataItemUsedInDropdown.id, - value: newDate?.toISOString() ?? '', - operand: selectedOperandInDropdown, - displayValue: isDefined(newDate) - ? isDateTimeInput - ? newDate.toLocaleString() - : newDate.toLocaleDateString() - : '', - recordFilterGroupId: selectedFilter?.recordFilterGroupId, - positionInRecordFilterGroup: selectedFilter?.positionInRecordFilterGroup, - type: getFilterTypeFromFieldType(fieldMetadataItemUsedInDropdown.type), - label: fieldMetadataItemUsedInDropdown.label, - subFieldName: selectedFilter?.subFieldName, - }); + applyObjectFilterDropdownFilterValue(newFilterValue, newDisplayValue); }; const handleRelativeDateChange = ( @@ -74,9 +63,7 @@ export const ObjectFilterDropdownDateInput = () => { unit: VariableDateViewFilterValueUnit; } | null, ) => { - if (!fieldMetadataItemUsedInDropdown || !selectedOperandInDropdown) return; - - const value = relativeDate + const newFilterValue = relativeDate ? computeVariableDateViewFilterValue( relativeDate.direction, relativeDate.amount, @@ -84,24 +71,18 @@ export const ObjectFilterDropdownDateInput = () => { ) : ''; - applyRecordFilter({ - id: selectedFilter?.id ? selectedFilter.id : v4(), - fieldMetadataId: fieldMetadataItemUsedInDropdown.id, - value, - operand: selectedOperandInDropdown, - displayValue: getRelativeDateDisplayValue(relativeDate), - recordFilterGroupId: selectedFilter?.recordFilterGroupId, - positionInRecordFilterGroup: selectedFilter?.positionInRecordFilterGroup, - type: getFilterTypeFromFieldType(fieldMetadataItemUsedInDropdown.type), - label: fieldMetadataItemUsedInDropdown.label, - }); + const newDisplayValue = relativeDate + ? getRelativeDateDisplayValue(relativeDate) + : ''; + + applyObjectFilterDropdownFilterValue(newFilterValue, newDisplayValue); }; const isRelativeOperand = selectedOperandInDropdown === ViewFilterOperand.IsRelative; - const resolvedValue = selectedFilter - ? resolveDateViewFilterValue(selectedFilter) + const resolvedValue = objectFilterDropdownCurrentRecordFilter + ? resolveDateViewFilterValue(objectFilterDropdownCurrentRecordFilter) : null; const relativeDate = 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 55159cc69..ddb73212d 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 @@ -9,7 +9,6 @@ import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; import { FILTER_FIELD_LIST_ID } from '@/object-record/object-filter-dropdown/constants/FilterFieldListId'; import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState'; -import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; import { isCompositeFieldType } from '@/object-record/object-filter-dropdown/utils/isCompositeFieldType'; import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; import { findDuplicateRecordFilterInNonAdvancedRecordFilters } from '@/object-record/record-filter/utils/findDuplicateRecordFilterInNonAdvancedRecordFilters'; @@ -68,10 +67,6 @@ export const ObjectFilterDropdownFilterSelectMenuItem = ({ currentRecordFiltersComponentState, ); - const setSelectedFilter = useSetRecoilComponentStateV2( - selectedFilterComponentState, - ); - const setObjectFilterDropdownCurrentRecordFilter = useSetRecoilComponentStateV2( objectFilterDropdownCurrentRecordFilterComponentState, @@ -103,8 +98,6 @@ export const ObjectFilterDropdownFilterSelectMenuItem = ({ ); if (filterIsAlreadyInCurrentRecordFilters) { - setSelectedFilter(duplicateFilterInCurrentRecordFilters); - setObjectFilterDropdownCurrentRecordFilter( duplicateFilterInCurrentRecordFilters, ); diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownNumberInput.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownNumberInput.tsx index 3f69de42e..a0a81b8b5 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownNumberInput.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownNumberInput.tsx @@ -1,41 +1,25 @@ import { ChangeEvent, useCallback, useState } from 'react'; -import { v4 } from 'uuid'; -import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; +import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue'; +import { useObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useObjectFilterDropdownFilterValue'; import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector'; -import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; -import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; -import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState'; -import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter'; import { DropdownMenuInput } from '@/ui/layout/dropdown/components/DropdownMenuInput'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; export const ObjectFilterDropdownNumberInput = () => { - const selectedOperandInDropdown = useRecoilComponentValueV2( - selectedOperandInDropdownComponentState, - ); - const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2( fieldMetadataItemUsedInDropdownComponentSelector, ); - const selectedFilter = useRecoilComponentValueV2( - selectedFilterComponentState, - ); + const { objectFilterDropdownFilterValue } = + useObjectFilterDropdownFilterValue(); - const subFieldNameUsedInDropdown = useRecoilComponentValueV2( - subFieldNameUsedInDropdownComponentState, - ); - - const { applyRecordFilter } = useApplyRecordFilter(); + const { applyObjectFilterDropdownFilterValue } = + useApplyObjectFilterDropdownFilterValue(); const [hasFocused, setHasFocused] = useState(false); - const [inputValue, setInputValue] = useState( - () => selectedFilter?.value || '', - ); - const handleInputRef = useCallback( (node: HTMLInputElement | null) => { if (Boolean(node) && !hasFocused) { @@ -47,39 +31,22 @@ export const ObjectFilterDropdownNumberInput = () => { [hasFocused], ); + const handleInputChange = (event: ChangeEvent) => { + const newValue = event.target.value; + + applyObjectFilterDropdownFilterValue(newValue); + }; + return ( - fieldMetadataItemUsedInDropdown && - selectedOperandInDropdown && ( - - ) => { - const newValue = event.target.value; - - setInputValue(newValue); - - applyRecordFilter({ - id: selectedFilter?.id ? selectedFilter.id : v4(), - fieldMetadataId: fieldMetadataItemUsedInDropdown?.id ?? '', - value: newValue, - operand: selectedOperandInDropdown, - displayValue: newValue, - type: getFilterTypeFromFieldType( - fieldMetadataItemUsedInDropdown.type, - ), - label: fieldMetadataItemUsedInDropdown.label, - recordFilterGroupId: selectedFilter?.recordFilterGroupId, - positionInRecordFilterGroup: - selectedFilter?.positionInRecordFilterGroup, - subFieldName: subFieldNameUsedInDropdown, - }); - }} - /> - - ) + + + ); }; diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownOperandSelect.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownOperandSelect.tsx index c2ee9f99a..28c2a20de 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownOperandSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownOperandSelect.tsx @@ -1,22 +1,16 @@ -import { v4 } from 'uuid'; - import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; +import { useApplyObjectFilterDropdownOperand } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownOperand'; import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector'; -import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; -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 { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; -import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; import styled from '@emotion/styled'; import { isDefined } from 'twenty-shared/utils'; -import { getOperandLabel } from '../utils/getOperandLabel'; import { MenuItem } from 'twenty-ui/navigation'; +import { getOperandLabel } from '../utils/getOperandLabel'; const StyledDropdownMenuItemsContainer = styled(DropdownMenuItemsContainer)` background-color: ${({ theme }) => theme.background.primary}; @@ -28,19 +22,12 @@ export const ObjectFilterDropdownOperandSelect = () => { fieldMetadataItemUsedInDropdownComponentSelector, ); - const setSelectedOperandInDropdown = useSetRecoilComponentStateV2( - selectedOperandInDropdownComponentState, - ); - - const selectedFilter = useRecoilComponentValueV2( - selectedFilterComponentState, - ); - const subFieldNameUsedInDropdown = useRecoilComponentValueV2( subFieldNameUsedInDropdownComponentState, ); - const { applyRecordFilter } = useApplyRecordFilter(); + const { applyObjectFilterDropdownOperand } = + useApplyObjectFilterDropdownOperand(); const { closeDropdown } = useDropdown(); @@ -54,56 +41,7 @@ export const ObjectFilterDropdownOperandSelect = () => { : []; const handleOperandChange = (newOperand: ViewFilterOperand) => { - const isValuelessOperand = [ - ViewFilterOperand.IsEmpty, - ViewFilterOperand.IsNotEmpty, - ViewFilterOperand.IsInPast, - ViewFilterOperand.IsInFuture, - ViewFilterOperand.IsToday, - ].includes(newOperand); - - setSelectedOperandInDropdown(newOperand); - - if (isValuelessOperand && isDefined(fieldMetadataItemUsedInDropdown)) { - applyRecordFilter({ - id: selectedFilter?.id ? selectedFilter.id : v4(), - fieldMetadataId: fieldMetadataItemUsedInDropdown.id, - displayValue: '', - operand: newOperand, - value: '', - type: getFilterTypeFromFieldType(fieldMetadataItemUsedInDropdown.type), - label: fieldMetadataItemUsedInDropdown.label, - subFieldName: subFieldNameUsedInDropdown, - }); - return; - } - - if ( - isDefined(fieldMetadataItemUsedInDropdown) && - isDefined(selectedFilter) - ) { - const filterType = getFilterTypeFromFieldType( - fieldMetadataItemUsedInDropdown.type, - ); - - const { value, displayValue } = getInitialFilterValue( - filterType, - newOperand, - selectedFilter.value, - selectedFilter.displayValue, - ); - - applyRecordFilter({ - id: selectedFilter.id ? selectedFilter.id : v4(), - fieldMetadataId: selectedFilter.fieldMetadataId, - displayValue, - operand: newOperand, - value, - type: filterType, - label: fieldMetadataItemUsedInDropdown.label, - subFieldName: subFieldNameUsedInDropdown, - }); - } + applyObjectFilterDropdownOperand(newOperand); }; return ( diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownOptionSelect.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownOptionSelect.tsx index 4c2318d39..755f5ebe4 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownOptionSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownOptionSelect.tsx @@ -1,6 +1,5 @@ -import { useEffect, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { Key } from 'ts-key-enum'; -import { v4 } from 'uuid'; import { FieldMetadataItemOption } from '@/object-metadata/types/FieldMetadataItem'; import { useOptionsForSelect } from '@/object-record/object-filter-dropdown/hooks/useOptionsForSelect'; @@ -10,19 +9,17 @@ import { SelectableList } from '@/ui/layout/selectable-list/components/Selectabl import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; -import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; +import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue'; import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext'; import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector'; +import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState'; import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState'; -import { objectFilterDropdownSelectedOptionValuesComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSelectedOptionValuesComponentState'; -import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; -import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; -import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter'; import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope'; import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { isNonEmptyString } from '@sniptt/guards'; import { isDefined } from 'twenty-shared/utils'; import { MenuItem, MenuItemMultiSelect } from 'twenty-ui/navigation'; @@ -38,27 +35,30 @@ export const ObjectFilterDropdownOptionSelect = () => { fieldMetadataItemUsedInDropdownComponentSelector, ); - const objectFilterDropdownSelectedOptionValues = useRecoilComponentValueV2( - objectFilterDropdownSelectedOptionValuesComponentState, - ); - - const selectedFilter = useRecoilComponentValueV2( - selectedFilterComponentState, - ); - const objectFilterDropdownSearchInput = useRecoilComponentValueV2( objectFilterDropdownSearchInputComponentState, ); - const selectedOperandInDropdown = useRecoilComponentValueV2( - selectedOperandInDropdownComponentState, - ); - const componentInstanceId = useAvailableComponentInstanceIdOrThrow( ObjectFilterDropdownComponentInstanceContext, ); - const { applyRecordFilter } = useApplyRecordFilter(); + const objectFilterDropdownCurrentRecordFilter = useRecoilComponentValueV2( + objectFilterDropdownCurrentRecordFilterComponentState, + ); + + const { applyObjectFilterDropdownFilterValue } = + useApplyObjectFilterDropdownFilterValue(); + + const selectedOptions = useMemo( + () => + isNonEmptyString(objectFilterDropdownCurrentRecordFilter?.value) + ? (JSON.parse( + objectFilterDropdownCurrentRecordFilter.value, + ) as string[]) // TODO: replace by a safe parse + : [], + [objectFilterDropdownCurrentRecordFilter?.value], + ); const { closeDropdown } = useDropdown(); @@ -80,9 +80,7 @@ export const ObjectFilterDropdownOptionSelect = () => { useEffect(() => { if (isDefined(selectOptions)) { const options = selectOptions.map((option) => { - const isSelected = - objectFilterDropdownSelectedOptionValues?.includes(option.value) ?? - false; + const isSelected = selectedOptions?.includes(option.value) ?? false; return { ...option, @@ -92,7 +90,7 @@ export const ObjectFilterDropdownOptionSelect = () => { setSelectableOptions(options); } - }, [objectFilterDropdownSelectedOptionValues, selectOptions]); + }, [selectedOptions, selectOptions]); useScopedHotkeys( [Key.Escape], @@ -127,29 +125,13 @@ export const ObjectFilterDropdownOptionSelect = () => { ? `${selectedOptions.length} options` : selectedOptions.map((option) => option.label).join(', '); - if ( - isDefined(fieldMetadataItemUsedInDropdown) && - isDefined(selectedOperandInDropdown) - ) { - const newFilterValue = - selectedOptions.length > 0 - ? JSON.stringify(selectedOptions.map((option) => option.value)) - : EMPTY_FILTER_VALUE; + const newFilterValue = + selectedOptions.length > 0 + ? JSON.stringify(selectedOptions.map((option) => option.value)) + : EMPTY_FILTER_VALUE; + + applyObjectFilterDropdownFilterValue(newFilterValue, filterDisplayValue); - applyRecordFilter({ - id: selectedFilter?.id ? selectedFilter.id : v4(), - type: getFilterTypeFromFieldType(fieldMetadataItemUsedInDropdown.type), - label: fieldMetadataItemUsedInDropdown.label, - operand: selectedOperandInDropdown, - displayValue: filterDisplayValue, - fieldMetadataId: fieldMetadataItemUsedInDropdown.id, - value: newFilterValue, - recordFilterGroupId: selectedFilter?.recordFilterGroupId, - positionInRecordFilterGroup: - selectedFilter?.positionInRecordFilterGroup, - subFieldName: selectedFilter?.subFieldName, - }); - } resetSelectedItem(); }; diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownRatingInput.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownRatingInput.tsx index 85590df54..7378eb993 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownRatingInput.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownRatingInput.tsx @@ -1,15 +1,9 @@ -import { v4 } from 'uuid'; - -import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; import { RATING_VALUES } from '@/object-record/record-field/meta-types/constants/RatingValues'; import { FieldRatingValue } from '@/object-record/record-field/types/FieldMetadata'; -import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter'; import { RatingInput } from '@/ui/field/input/components/RatingInput'; -import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; -import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; -import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector'; -import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; +import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue'; +import { useObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useObjectFilterDropdownFilterValue'; import styled from '@emotion/styled'; const StyledRatingInputContainer = styled.div` @@ -35,53 +29,37 @@ export const convertLessThanRatingToArrayOfRatingValues = ( }; export const convertRatingToRatingValue = (rating: number) => { - return `RATING_${rating}`; + return `RATING_${rating}` as FieldRatingValue; }; export const ObjectFilterDropdownRatingInput = () => { - const selectedOperandInDropdown = useRecoilComponentValueV2( - selectedOperandInDropdownComponentState, - ); + const { applyObjectFilterDropdownFilterValue } = + useApplyObjectFilterDropdownFilterValue(); - const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2( - fieldMetadataItemUsedInDropdownComponentSelector, - ); + const { objectFilterDropdownFilterValue } = + useObjectFilterDropdownFilterValue(); - const selectedFilter = useRecoilComponentValueV2( - selectedFilterComponentState, - ); + const handleInputChange = (newRatingValue: FieldRatingValue) => { + if (!newRatingValue) { + return; + } - const { applyRecordFilter } = useApplyRecordFilter(); + const ratingValueConverted = + convertFieldRatingValueToNumber(newRatingValue); + + applyObjectFilterDropdownFilterValue(ratingValueConverted); + }; + + const currentFilterValueConvertedToRatingValue = convertRatingToRatingValue( + Number(objectFilterDropdownFilterValue), + ); return ( - fieldMetadataItemUsedInDropdown && - selectedOperandInDropdown && ( - - { - if (!newValue) { - return; - } - - applyRecordFilter?.({ - id: selectedFilter?.id ? selectedFilter.id : v4(), - fieldMetadataId: fieldMetadataItemUsedInDropdown.id, - value: convertFieldRatingValueToNumber(newValue), - operand: selectedOperandInDropdown, - displayValue: convertFieldRatingValueToNumber(newValue), - recordFilterGroupId: selectedFilter?.recordFilterGroupId, - positionInRecordFilterGroup: - selectedFilter?.positionInRecordFilterGroup, - type: getFilterTypeFromFieldType( - fieldMetadataItemUsedInDropdown.type, - ), - label: fieldMetadataItemUsedInDropdown.label, - subFieldName: selectedFilter?.subFieldName, - }); - }} - /> - - ) + + + ); }; 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 85f419456..14baaa92d 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 @@ -1,17 +1,13 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; -import { - getFilterTypeFromFieldType, - getRelationObjectMetadataNameSingular, -} from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; +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 { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue'; +import { useObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useObjectFilterDropdownFilterValue'; import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector'; import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState'; -import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; -import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter'; import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; -import { findDuplicateRecordFilterInNonAdvancedRecordFilters } from '@/object-record/record-filter/utils/findDuplicateRecordFilterInNonAdvancedRecordFilters'; import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope'; import { MultipleSelectDropdown } from '@/object-record/select/components/MultipleSelectDropdown'; import { useRecordsForSelect } from '@/object-record/select/hooks/useRecordsForSelect'; @@ -23,7 +19,6 @@ import { jsonRelationFilterValueSchema } from '@/views/view-filter-value/validat import { simpleRelationFilterValueSchema } from '@/views/view-filter-value/validation-schemas/simpleRelationFilterValueSchema'; import { isDefined } from 'twenty-shared/utils'; import { IconUserCircle } from 'twenty-ui/display'; -import { v4 } from 'uuid'; export const EMPTY_FILTER_VALUE: string = JSON.stringify({ isCurrentWorkspaceMemberSelected: false, @@ -33,26 +28,26 @@ export const EMPTY_FILTER_VALUE: string = JSON.stringify({ export const MAX_RECORDS_TO_DISPLAY = 3; type ObjectFilterDropdownRecordSelectProps = { - viewComponentId?: string; recordFilterId?: string; }; export const ObjectFilterDropdownRecordSelect = ({ - viewComponentId, recordFilterId, }: ObjectFilterDropdownRecordSelectProps) => { const fieldMetadataItemUsedInFilterDropdown = useRecoilComponentValueV2( fieldMetadataItemUsedInDropdownComponentSelector, ); + const { objectFilterDropdownFilterValue } = + useObjectFilterDropdownFilterValue(); + + const { applyObjectFilterDropdownFilterValue } = + useApplyObjectFilterDropdownFilterValue(); + const selectedOperandInDropdown = useRecoilComponentValueV2( selectedOperandInDropdownComponentState, ); - const selectedFilter = useRecoilComponentValueV2( - selectedFilterComponentState, - ); - const objectFilterDropdownSearchInput = useRecoilComponentValueV2( objectFilterDropdownSearchInputComponentState, ); @@ -61,16 +56,14 @@ export const ObjectFilterDropdownRecordSelect = ({ currentRecordFiltersComponentState, ); - const { applyRecordFilter } = useApplyRecordFilter(viewComponentId); - const { isCurrentWorkspaceMemberSelected } = jsonRelationFilterValueSchema .catch({ isCurrentWorkspaceMemberSelected: false, selectedRecordIds: simpleRelationFilterValueSchema.parse( - selectedFilter?.value, + objectFilterDropdownFilterValue, ), }) - .parse(selectedFilter?.value); + .parse(objectFilterDropdownFilterValue); if (!isDefined(fieldMetadataItemUsedInFilterDropdown)) { throw new Error('fieldMetadataItemUsedInFilterDropdown is not defined'); @@ -210,50 +203,7 @@ export const ObjectFilterDropdownRecordSelect = ({ } satisfies RelationFilterValue) : ''; - const duplicateFilterInCurrentRecordFilters = - findDuplicateRecordFilterInNonAdvancedRecordFilters({ - recordFilters: currentRecordFilters, - fieldMetadataItemId: fieldMetadataItemUsedInFilterDropdown.id, - }); - - const filterIsAlreadyInCurrentRecordFilters = isDefined( - duplicateFilterInCurrentRecordFilters, - ); - - if (filterIsAlreadyInCurrentRecordFilters && !isDefined(recordFilterId)) { - applyRecordFilter({ - id: duplicateFilterInCurrentRecordFilters.id, - type: getFilterTypeFromFieldType( - fieldMetadataItemUsedInFilterDropdown.type, - ), - label: fieldMetadataItemUsedInFilterDropdown.label, - operand: selectedOperandInDropdown, - displayValue: filterDisplayValue, - fieldMetadataId: fieldMetadataItemUsedInFilterDropdown.id, - value: newFilterValue, - recordFilterGroupId: - duplicateFilterInCurrentRecordFilters.recordFilterGroupId, - positionInRecordFilterGroup: - duplicateFilterInCurrentRecordFilters.positionInRecordFilterGroup, - subFieldName: duplicateFilterInCurrentRecordFilters.subFieldName, - }); - } else { - applyRecordFilter({ - id: selectedFilter?.id ? selectedFilter.id : v4(), - type: getFilterTypeFromFieldType( - fieldMetadataItemUsedInFilterDropdown.type, - ), - label: fieldMetadataItemUsedInFilterDropdown.label, - operand: selectedOperandInDropdown, - displayValue: filterDisplayValue, - fieldMetadataId: fieldMetadataItemUsedInFilterDropdown.id, - value: newFilterValue, - recordFilterGroupId: selectedFilter?.recordFilterGroupId, - positionInRecordFilterGroup: - selectedFilter?.positionInRecordFilterGroup, - subFieldName: selectedFilter?.subFieldName, - }); - } + applyObjectFilterDropdownFilterValue(newFilterValue, filterDisplayValue); } }; diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownSourceSelect.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownSourceSelect.tsx index a710e1907..85bf0578c 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownSourceSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownSourceSelect.tsx @@ -1,68 +1,44 @@ -import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; +import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue'; import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector'; +import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState'; import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState'; -import { objectFilterDropdownSelectedRecordIdsComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSelectedRecordIdsComponentState'; -import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; -import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; import { getActorSourceMultiSelectOptions } from '@/object-record/object-filter-dropdown/utils/getActorSourceMultiSelectOptions'; -import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter'; -import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; -import { findDuplicateRecordFilterInNonAdvancedRecordFilters } from '@/object-record/record-filter/utils/findDuplicateRecordFilterInNonAdvancedRecordFilters'; import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope'; import { MultipleSelectDropdown } from '@/object-record/select/components/MultipleSelectDropdown'; import { SelectableItem } from '@/object-record/select/types/SelectableItem'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; -import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; -import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; +import { isNonEmptyString } from '@sniptt/guards'; import { isDefined } from 'twenty-shared/utils'; -import { v4 } from 'uuid'; export const EMPTY_FILTER_VALUE = '[]'; export const MAX_ITEMS_TO_DISPLAY = 3; -type ObjectFilterDropdownSourceSelectProps = { - viewComponentId?: string; -}; - -export const ObjectFilterDropdownSourceSelect = ({ - viewComponentId, -}: ObjectFilterDropdownSourceSelectProps) => { +export const ObjectFilterDropdownSourceSelect = () => { const objectFilterDropdownSearchInput = useRecoilComponentValueV2( objectFilterDropdownSearchInputComponentState, ); - const setObjectFilterDropdownSelectedRecordIds = useSetRecoilComponentStateV2( - objectFilterDropdownSelectedRecordIdsComponentState, - ); - - const objectFilterDropdownSelectedRecordIds = useRecoilComponentValueV2( - objectFilterDropdownSelectedRecordIdsComponentState, - ); - - const selectedFilter = useRecoilComponentValueV2( - selectedFilterComponentState, - ); - - const selectedOperandInDropdown = useRecoilComponentValueV2( - selectedOperandInDropdownComponentState, - ); - const fieldMetadataItemUsedInFilterDropdown = useRecoilComponentValueV2( fieldMetadataItemUsedInDropdownComponentSelector, ); - const { applyRecordFilter } = useApplyRecordFilter(viewComponentId); - - const sourceTypes = getActorSourceMultiSelectOptions( - objectFilterDropdownSelectedRecordIds, + const objectFilterDropdownCurrentRecordFilter = useRecoilComponentValueV2( + objectFilterDropdownCurrentRecordFilterComponentState, ); + const { applyObjectFilterDropdownFilterValue } = + useApplyObjectFilterDropdownFilterValue(); + + const selectedSources = isNonEmptyString( + objectFilterDropdownCurrentRecordFilter?.value, + ) + ? (JSON.parse(objectFilterDropdownCurrentRecordFilter.value) as string[]) // TODO: replace by a safe parse + : []; + + const sourceTypes = getActorSourceMultiSelectOptions(selectedSources); + const filteredSelectedItems = sourceTypes.filter((option) => - objectFilterDropdownSelectedRecordIds.includes(option.id), - ); - - const currentRecordFilters = useRecoilComponentValueV2( - currentRecordFiltersComponentState, + selectedSources.includes(option.id), ); const handleMultipleItemSelectChange = ( @@ -70,10 +46,8 @@ export const ObjectFilterDropdownSourceSelect = ({ newSelectedValue: boolean, ) => { const newSelectedItemIds = newSelectedValue - ? [...objectFilterDropdownSelectedRecordIds, itemToSelect.id] - : objectFilterDropdownSelectedRecordIds.filter( - (id) => id !== itemToSelect.id, - ); + ? [...selectedSources, itemToSelect.id] + : selectedSources.filter((id) => id !== itemToSelect.id); if (!isDefined(fieldMetadataItemUsedInFilterDropdown)) { throw new Error( @@ -81,8 +55,6 @@ export const ObjectFilterDropdownSourceSelect = ({ ); } - setObjectFilterDropdownSelectedRecordIds(newSelectedItemIds); - const selectedItemNames = sourceTypes .filter((option) => newSelectedItemIds.includes(option.id)) .map((option) => option.name); @@ -92,46 +64,12 @@ export const ObjectFilterDropdownSourceSelect = ({ ? `${selectedItemNames.length} source types` : selectedItemNames.join(', '); - if ( - isDefined(fieldMetadataItemUsedInFilterDropdown) && - isDefined(selectedOperandInDropdown) - ) { - const newFilterValue = - newSelectedItemIds.length > 0 - ? JSON.stringify(newSelectedItemIds) - : EMPTY_FILTER_VALUE; + const newFilterValue = + newSelectedItemIds.length > 0 + ? JSON.stringify(newSelectedItemIds) + : EMPTY_FILTER_VALUE; - const duplicateFilterInCurrentRecordFilters = - findDuplicateRecordFilterInNonAdvancedRecordFilters({ - recordFilters: currentRecordFilters, - fieldMetadataItemId: fieldMetadataItemUsedInFilterDropdown.id, - subFieldName: 'source', - }); - - const filterIsAlreadyInCurrentRecordFilters = isDefined( - duplicateFilterInCurrentRecordFilters, - ); - - const filterId = filterIsAlreadyInCurrentRecordFilters - ? duplicateFilterInCurrentRecordFilters?.id - : v4(); - - applyRecordFilter({ - id: selectedFilter?.id ? selectedFilter.id : filterId, - type: getFilterTypeFromFieldType( - fieldMetadataItemUsedInFilterDropdown.type, - ), - label: fieldMetadataItemUsedInFilterDropdown.label, - operand: selectedOperandInDropdown || ViewFilterOperand.Is, - displayValue: filterDisplayValue, - fieldMetadataId: fieldMetadataItemUsedInFilterDropdown.id, - value: newFilterValue, - recordFilterGroupId: selectedFilter?.recordFilterGroupId, - subFieldName: 'source', - positionInRecordFilterGroup: - selectedFilter?.positionInRecordFilterGroup, - }); - } + applyObjectFilterDropdownFilterValue(newFilterValue, filterDisplayValue); }; return ( diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownSubFieldSelect.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownSubFieldSelect.tsx index efedcdbe1..e6eff0d30 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownSubFieldSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownSubFieldSelect.tsx @@ -9,7 +9,6 @@ import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-rec import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState'; import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState'; import { objectFilterDropdownSubMenuFieldTypeComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSubMenuFieldTypeComponentState'; -import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState'; import { FiltersHotkeyScope } from '@/object-record/object-filter-dropdown/types/FiltersHotkeyScope'; @@ -81,10 +80,6 @@ export const ObjectFilterDropdownSubFieldSelect = () => { currentRecordFiltersComponentState, ); - const setSelectedFilter = useSetRecoilComponentStateV2( - selectedFilterComponentState, - ); - const setObjectFilterDropdownCurrentRecordFilter = useSetRecoilComponentStateV2( objectFilterDropdownCurrentRecordFilterComponentState, @@ -125,8 +120,6 @@ export const ObjectFilterDropdownSubFieldSelect = () => { ); if (filterIsAlreadyInCurrentRecordFilters) { - setSelectedFilter(duplicateFilterInCurrentRecordFilters); - setObjectFilterDropdownCurrentRecordFilter( duplicateFilterInCurrentRecordFilters, ); diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownTextInput.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownTextInput.tsx index 1629318e3..4c39c6892 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownTextInput.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownTextInput.tsx @@ -31,6 +31,12 @@ export const ObjectFilterDropdownTextInput = () => { [hasFocused], ); + const handleInputChange = (event: ChangeEvent) => { + const newValue = event.target.value; + + applyObjectFilterDropdownFilterValue(newValue); + }; + return ( { autoFocus type="text" placeholder={fieldMetadataItemUsedInDropdown?.label} - onChange={(event: ChangeEvent) => { - const newValue = event.target.value; - - applyObjectFilterDropdownFilterValue(newValue); - }} + onChange={handleInputChange} /> ); diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownTextSearchInput.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownTextSearchInput.tsx deleted file mode 100644 index 46871720c..000000000 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownTextSearchInput.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { ChangeEvent, useCallback, useState } from 'react'; - -import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; -import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector'; -import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState'; -import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; -import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; -import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter'; -import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput'; -import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; -import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; -import { v4 } from 'uuid'; - -export const ObjectFilterDropdownTextSearchInput = () => { - const [hasFocused, setHasFocused] = useState(false); - - const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2( - fieldMetadataItemUsedInDropdownComponentSelector, - ); - - const selectedOperandInDropdown = useRecoilComponentValueV2( - selectedOperandInDropdownComponentState, - ); - - const objectFilterDropdownSearchInput = useRecoilComponentValueV2( - objectFilterDropdownSearchInputComponentState, - ); - - const selectedFilter = useRecoilComponentValueV2( - selectedFilterComponentState, - ); - - const setObjectFilterDropdownSearchInput = useSetRecoilComponentStateV2( - objectFilterDropdownSearchInputComponentState, - ); - - const { applyRecordFilter } = useApplyRecordFilter(); - - const handleInputRef = useCallback( - (node: HTMLInputElement | null) => { - if (Boolean(node) && !hasFocused) { - node?.focus(); - node?.select(); - setHasFocused(true); - } - }, - [hasFocused], - ); - return ( - fieldMetadataItemUsedInDropdown && - selectedOperandInDropdown && ( - ) => { - setObjectFilterDropdownSearchInput(event.target.value); - - applyRecordFilter({ - id: selectedFilter?.id ?? v4(), - fieldMetadataId: fieldMetadataItemUsedInDropdown.id, - value: event.target.value, - operand: selectedOperandInDropdown, - displayValue: event.target.value, - recordFilterGroupId: selectedFilter?.recordFilterGroupId, - type: getFilterTypeFromFieldType( - fieldMetadataItemUsedInDropdown.type, - ), - label: fieldMetadataItemUsedInDropdown.label, - positionInRecordFilterGroup: - selectedFilter?.positionInRecordFilterGroup, - subFieldName: selectedFilter?.subFieldName, - }); - }} - /> - ) - ); -}; diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue.ts b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue.ts index 69f83a828..c94d6a961 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue.ts +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue.ts @@ -25,7 +25,10 @@ export const useApplyObjectFilterDropdownFilterValue = () => { const { upsertObjectFilterDropdownCurrentFilter } = useUpsertObjectFilterDropdownCurrentFilter(); - const applyObjectFilterDropdownFilterValue = (newFilterValue: string) => { + const applyObjectFilterDropdownFilterValue = ( + newFilterValue: string, + newDisplayValue?: string, + ) => { if (objectFilterDropdownFilterNotYetCreated) { if (!isDefined(fieldMetadataItemUsedInDropdown)) { throw new Error( @@ -41,7 +44,7 @@ export const useApplyObjectFilterDropdownFilterValue = () => { const newCurrentRecordFilter = { ...newRecordFilterFromObjectFilterDropdownStates, value: newFilterValue, - displayValue: newFilterValue, + displayValue: newDisplayValue ?? newFilterValue, } satisfies RecordFilter; upsertObjectFilterDropdownCurrentFilter(newCurrentRecordFilter); @@ -49,7 +52,7 @@ export const useApplyObjectFilterDropdownFilterValue = () => { const newCurrentRecordFilter = { ...objectFilterDropdownCurrentRecordFilter, value: newFilterValue, - displayValue: newFilterValue, + displayValue: newDisplayValue ?? newFilterValue, } satisfies RecordFilter; upsertObjectFilterDropdownCurrentFilter(newCurrentRecordFilter); diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownOperand.ts b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownOperand.ts new file mode 100644 index 000000000..98601c32e --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownOperand.ts @@ -0,0 +1,79 @@ +import { useUpsertObjectFilterDropdownCurrentFilter } from '@/object-record/object-filter-dropdown/hooks/useUpsertObjectFilterDropdownCurrentFilter'; +import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector'; +import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState'; +import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; +import { useCreateEmptyRecordFilterFromFieldMetadataItem } from '@/object-record/record-filter/hooks/useCreateEmptyRecordFilterFromFieldMetadataItem'; +import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter'; +import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand'; +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'; + +export const useApplyObjectFilterDropdownOperand = () => { + const objectFilterDropdownCurrentRecordFilter = useRecoilComponentValueV2( + objectFilterDropdownCurrentRecordFilterComponentState, + ); + + const setSelectedOperandInDropdown = useSetRecoilComponentStateV2( + selectedOperandInDropdownComponentState, + ); + + const objectFilterDropdownFilterIsCreated = isDefined( + objectFilterDropdownCurrentRecordFilter, + ); + + const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2( + fieldMetadataItemUsedInDropdownComponentSelector, + ); + + const { upsertObjectFilterDropdownCurrentFilter } = + useUpsertObjectFilterDropdownCurrentFilter(); + + const { createEmptyRecordFilterFromFieldMetadataItem } = + useCreateEmptyRecordFilterFromFieldMetadataItem(); + + const applyObjectFilterDropdownOperand = ( + newOperand: RecordFilterOperand, + ) => { + const isValuelessOperand = [ + RecordFilterOperand.IsEmpty, + RecordFilterOperand.IsNotEmpty, + RecordFilterOperand.IsInPast, + RecordFilterOperand.IsInFuture, + RecordFilterOperand.IsToday, + ].includes(newOperand); + + if (objectFilterDropdownFilterIsCreated) { + const newCurrentRecordFilter = { + ...objectFilterDropdownCurrentRecordFilter, + operand: newOperand, + } satisfies RecordFilter; + + upsertObjectFilterDropdownCurrentFilter(newCurrentRecordFilter); + } else if (isValuelessOperand) { + if (!isDefined(fieldMetadataItemUsedInDropdown)) { + throw new Error( + 'FieldMetadataItemUsedInDropdown is not defined, cannot create empty record filter, this should not happen', + ); + } + + const { newRecordFilter: emptyRecordFilter } = + createEmptyRecordFilterFromFieldMetadataItem( + fieldMetadataItemUsedInDropdown, + ); + + const recordFilterToCreate = { + ...emptyRecordFilter, + operand: newOperand, + } satisfies RecordFilter; + + upsertObjectFilterDropdownCurrentFilter(recordFilterToCreate); + } + + setSelectedOperandInDropdown(newOperand); + }; + + return { + applyObjectFilterDropdownOperand, + }; +}; diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useEmptyRecordFilter.ts b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useEmptyRecordFilter.ts index 93e70009c..f1094fa70 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useEmptyRecordFilter.ts +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useEmptyRecordFilter.ts @@ -1,6 +1,6 @@ +import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState'; import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState'; import { objectFilterDropdownSelectedRecordIdsComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSelectedRecordIdsComponentState'; -import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; import { useRecoilCallback } from 'recoil'; @@ -17,22 +17,23 @@ export const useEmptyRecordFilter = (componentInstanceId?: string) => { componentInstanceId, ); - const selectedFilterCallbackState = useRecoilComponentCallbackStateV2( - selectedFilterComponentState, - componentInstanceId, - ); + const objectFilterDropdownCurrentRecordFilter = + useRecoilComponentCallbackStateV2( + objectFilterDropdownCurrentRecordFilterComponentState, + componentInstanceId, + ); const emptyRecordFilter = useRecoilCallback( ({ set }) => () => { set(objectFilterDropdownSearchInputCallbackState, ''); set(objectFilterDropdownSelectedRecordIdsCallbackState, []); - set(selectedFilterCallbackState, undefined); + set(objectFilterDropdownCurrentRecordFilter, undefined); }, [ objectFilterDropdownSearchInputCallbackState, objectFilterDropdownSelectedRecordIdsCallbackState, - selectedFilterCallbackState, + objectFilterDropdownCurrentRecordFilter, ], ); diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useResetFilterDropdown.ts b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useResetFilterDropdown.ts index 5c239d59e..280fc3f4b 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useResetFilterDropdown.ts +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useResetFilterDropdown.ts @@ -4,7 +4,6 @@ import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-rec import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState'; import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState'; import { objectFilterDropdownSelectedRecordIdsComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSelectedRecordIdsComponentState'; -import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; import { useRecoilCallback } from 'recoil'; @@ -28,11 +27,6 @@ export const useResetFilterDropdown = (componentInstanceId?: string) => { componentInstanceId, ); - const selectedFilterCallbackState = useRecoilComponentCallbackStateV2( - selectedFilterComponentState, - componentInstanceId, - ); - const selectedOperandInDropdownCallbackState = useRecoilComponentCallbackStateV2( selectedOperandInDropdownComponentState, @@ -62,7 +56,6 @@ export const useResetFilterDropdown = (componentInstanceId?: string) => { () => { set(objectFilterDropdownSearchInputCallbackState, ''); set(objectFilterDropdownSelectedRecordIdsCallbackState, []); - set(selectedFilterCallbackState, undefined); set(selectedOperandInDropdownCallbackState, null); set(objectFilterDropdownFilterIsSelectedCallbackState, false); set(objectFilterDropdownIsSelectingCompositeFieldCallbackState, false); @@ -72,7 +65,6 @@ export const useResetFilterDropdown = (componentInstanceId?: string) => { [ objectFilterDropdownSearchInputCallbackState, objectFilterDropdownSelectedRecordIdsCallbackState, - selectedFilterCallbackState, selectedOperandInDropdownCallbackState, objectFilterDropdownFilterIsSelectedCallbackState, objectFilterDropdownIsSelectingCompositeFieldCallbackState, 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 deleted file mode 100644 index a7c8ed06f..000000000 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useSelectFilterUsedInDropdown.ts +++ /dev/null @@ -1,97 +0,0 @@ -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 { getInitialFilterValue } from '@/object-record/object-filter-dropdown/utils/getInitialFilterValue'; -import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter'; -import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands'; -import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope'; - -import { getDefaultSubFieldNameForCompositeFilterableFieldType } from '@/object-record/record-filter/utils/getDefaultSubFieldNameForCompositeFilterableFieldType'; -import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; -import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; -import { isDefined } from 'twenty-shared/utils'; -import { v4 } from 'uuid'; - -type SelectFilterParams = { - fieldMetadataItemId: string; -}; - -export const useSelectFilterUsedInDropdown = (componentInstanceId?: string) => { - const setSelectedOperandInDropdown = useSetRecoilComponentStateV2( - selectedOperandInDropdownComponentState, - componentInstanceId, - ); - - const setFieldMetadataItemIdUsedInDropdown = useSetRecoilComponentStateV2( - fieldMetadataItemIdUsedInDropdownComponentState, - componentInstanceId, - ); - - const setObjectFilterDropdownSearchInput = useSetRecoilComponentStateV2( - objectFilterDropdownSearchInputComponentState, - componentInstanceId, - ); - - const setHotkeyScope = useSetHotkeyScope(); - - const { applyRecordFilter } = useApplyRecordFilter(componentInstanceId); - - const { getFieldMetadataItemById } = useGetFieldMetadataItemById(); - - const selectFilterUsedInDropdown = ({ - fieldMetadataItemId, - }: 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 defaultSubFieldName = - getDefaultSubFieldNameForCompositeFilterableFieldType(filterType); - - const firstOperand = getRecordFilterOperands({ - filterType, - subFieldName: defaultSubFieldName, - })[0]; - - setSelectedOperandInDropdown(firstOperand); - - const { value, displayValue } = getInitialFilterValue( - filterType, - firstOperand, - ); - - if (value !== '') { - applyRecordFilter({ - id: v4(), - fieldMetadataId: fieldMetadataItem.id, - displayValue, - operand: firstOperand, - value, - type: filterType, - label: fieldMetadataItem.label, - subFieldName: defaultSubFieldName, - }); - } - - setObjectFilterDropdownSearchInput(''); - }; - - return { - selectFilterUsedInDropdown, - }; -}; diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useUpsertObjectFilterDropdownCurrentFilter.ts b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useUpsertObjectFilterDropdownCurrentFilter.ts index 52a45b438..e3009382f 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useUpsertObjectFilterDropdownCurrentFilter.ts +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useUpsertObjectFilterDropdownCurrentFilter.ts @@ -1,5 +1,4 @@ import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState'; -import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter'; import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; @@ -10,10 +9,6 @@ export const useUpsertObjectFilterDropdownCurrentFilter = () => { objectFilterDropdownCurrentRecordFilterComponentState, ); - const setSelectedFilter = useSetRecoilComponentStateV2( - selectedFilterComponentState, - ); - const { upsertRecordFilter } = useUpsertRecordFilter(); const upsertObjectFilterDropdownCurrentFilter = ( @@ -22,7 +17,6 @@ export const useUpsertObjectFilterDropdownCurrentFilter = () => { upsertRecordFilter(recordFilterToUpsert); setObjectFilterDropdownCurrentRecordFilter(recordFilterToUpsert); - setSelectedFilter(recordFilterToUpsert); }; return { diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/onFilterSelectComponentState.ts b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/onFilterSelectComponentState.ts deleted file mode 100644 index 9511acc13..000000000 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/onFilterSelectComponentState.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext'; -import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; -import { RecordFilter } from '../../record-filter/types/RecordFilter'; - -export const onFilterSelectComponentState = createComponentStateV2< - ((filter: RecordFilter | null) => void) | undefined ->({ - key: 'onFilterSelectComponentState', - defaultValue: undefined, - componentInstanceContext: ObjectFilterDropdownComponentInstanceContext, -}); diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/selectedFilterComponentState.ts b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/selectedFilterComponentState.ts deleted file mode 100644 index f1d207d45..000000000 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/selectedFilterComponentState.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext'; -import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; -import { RecordFilter } from '../../record-filter/types/RecordFilter'; - -export const selectedFilterComponentState = createComponentStateV2< - RecordFilter | undefined | null ->({ - key: 'selectedFilterComponentState', - defaultValue: undefined, - componentInstanceContext: ObjectFilterDropdownComponentInstanceContext, -}); diff --git a/packages/twenty-front/src/modules/object-record/record-filter/hooks/useApplyRecordFilter.ts b/packages/twenty-front/src/modules/object-record/record-filter/hooks/useApplyRecordFilter.ts deleted file mode 100644 index 5fabc9cbf..000000000 --- a/packages/twenty-front/src/modules/object-record/record-filter/hooks/useApplyRecordFilter.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; -import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter'; -import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter'; -import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; - -import { useRecoilCallback } from 'recoil'; -import { isDefined } from 'twenty-shared/utils'; - -export const useApplyRecordFilter = (componentInstanceId?: string) => { - const selectedFilterCallbackState = useRecoilComponentCallbackStateV2( - selectedFilterComponentState, - componentInstanceId, - ); - - const { upsertRecordFilter } = useUpsertRecordFilter(); - - const applyRecordFilter = useRecoilCallback( - ({ set }) => - (filter: RecordFilter | null) => { - set(selectedFilterCallbackState, filter); - - if (isDefined(filter)) { - upsertRecordFilter(filter); - } - }, - [selectedFilterCallbackState, upsertRecordFilter], - ); - - return { - applyRecordFilter, - }; -}; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/hooks/useOpenRecordFilterChipFromTableHeader.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/hooks/useOpenRecordFilterChipFromTableHeader.ts index 9aef27c50..c888d755e 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-header/hooks/useOpenRecordFilterChipFromTableHeader.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-header/hooks/useOpenRecordFilterChipFromTableHeader.ts @@ -1,23 +1,16 @@ -import { useSelectFilterUsedInDropdown } from '@/object-record/object-filter-dropdown/hooks/useSelectFilterUsedInDropdown'; import { useCreateEmptyRecordFilterFromFieldMetadataItem } from '@/object-record/record-filter/hooks/useCreateEmptyRecordFilterFromFieldMetadataItem'; import { useFilterableFieldMetadataItemsInRecordIndexContext } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItemsInRecordIndexContext'; import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter'; import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; -import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext'; import { useOpenDropdownFromOutside } from '@/ui/layout/dropdown/hooks/useOpenDropdownFromOutside'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useSetEditableFilterChipDropdownStates } from '@/views/hooks/useSetEditableFilterChipDropdownStates'; import { isDefined } from 'twenty-shared/utils'; export const useOpenRecordFilterChipFromTableHeader = () => { - const { recordIndexId } = useRecordIndexContextOrThrow(); - const { filterableFieldMetadataItems } = useFilterableFieldMetadataItemsInRecordIndexContext(); - const { selectFilterUsedInDropdown } = - useSelectFilterUsedInDropdown(recordIndexId); - const currentRecordFilters = useRecoilComponentValueV2( currentRecordFiltersComponentState, ); @@ -60,8 +53,6 @@ export const useOpenRecordFilterChipFromTableHeader = () => { upsertRecordFilter(newRecordFilter); - selectFilterUsedInDropdown({ fieldMetadataItemId }); - setEditableFilterChipDropdownStates(newRecordFilter); openDropdownFromOutside(newRecordFilter.id); }; diff --git a/packages/twenty-front/src/modules/ui/input/components/internal/date/components/RelativeDatePickerHeader.tsx b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/RelativeDatePickerHeader.tsx index 70643934d..0c7a15744 100644 --- a/packages/twenty-front/src/modules/ui/input/components/internal/date/components/RelativeDatePickerHeader.tsx +++ b/packages/twenty-front/src/modules/ui/input/components/internal/date/components/RelativeDatePickerHeader.tsx @@ -69,8 +69,10 @@ export const RelativeDatePickerHeader = ( }); }} options={RELATIVE_DATE_DIRECTION_SELECT_OPTIONS} + fullWidth /> { const amountString = text.replace(/[^0-9]|^0+/g, ''); @@ -106,6 +108,7 @@ export const RelativeDatePickerHeader = ( }); }} options={unitSelectOptions} + fullWidth /> ); diff --git a/packages/twenty-front/src/modules/views/components/AdvancedFilterDropdownButton.tsx b/packages/twenty-front/src/modules/views/components/AdvancedFilterDropdownButton.tsx index da51bc55c..d01ac7204 100644 --- a/packages/twenty-front/src/modules/views/components/AdvancedFilterDropdownButton.tsx +++ b/packages/twenty-front/src/modules/views/components/AdvancedFilterDropdownButton.tsx @@ -1,6 +1,7 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { AdvancedFilterRootRecordFilterGroup } from '@/object-record/advanced-filter/components/AdvancedFilterRootRecordFilterGroup'; +import { useSetAdvancedFilterDropdownStates } from '@/object-record/advanced-filter/hooks/useSetAdvancedFilterDropdownAllRowsStates'; import { rootLevelRecordFilterGroupComponentSelector } from '@/object-record/advanced-filter/states/rootLevelRecordFilterGroupComponentSelector'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { AdvancedFilterChip } from '@/views/components/AdvancedFilterChip'; @@ -12,6 +13,13 @@ export const AdvancedFilterDropdownButton = () => { rootLevelRecordFilterGroupComponentSelector, ); + const { setAdvancedFilterDropdownStates } = + useSetAdvancedFilterDropdownStates(); + + const handleOpenAdvancedFilterDropdown = () => { + setAdvancedFilterDropdownStates(); + }; + if (!isDefined(rootLevelRecordFilterGroup)) { return null; } @@ -25,6 +33,7 @@ export const AdvancedFilterDropdownButton = () => { dropdownOffset={{ y: 8, x: 0 }} dropdownPlacement="bottom-start" dropdownWidth="100%" + onOpen={handleOpenAdvancedFilterDropdown} /> ); }; diff --git a/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdown.tsx b/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdown.tsx index 3c13dc749..450417761 100644 --- a/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdown.tsx +++ b/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdown.tsx @@ -3,7 +3,7 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; import { VIEW_BAR_FILTER_DROPDOWN_ID } from '@/views/constants/ViewBarFilterDropdownId'; -import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; +import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState'; import { useRemoveRecordFilter } from '@/object-record/record-filter/hooks/useRemoveRecordFilter'; import { isRecordFilterConsideredEmpty } from '@/object-record/record-filter/utils/isRecordFilterConsideredEmpty'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; @@ -22,17 +22,19 @@ export const ViewBarFilterDropdown = ({ const { removeRecordFilter } = useRemoveRecordFilter(); - const selectedFilter = useRecoilComponentValueV2( - selectedFilterComponentState, + const objectFilterDropdownCurrentRecordFilter = useRecoilComponentValueV2( + objectFilterDropdownCurrentRecordFilterComponentState, ); const handleDropdownClickOutside = () => { const recordFilterIsEmpty = - isDefined(selectedFilter) && - isRecordFilterConsideredEmpty(selectedFilter); + isDefined(objectFilterDropdownCurrentRecordFilter) && + isRecordFilterConsideredEmpty(objectFilterDropdownCurrentRecordFilter); if (recordFilterIsEmpty) { - removeRecordFilter({ recordFilterId: selectedFilter.id }); + removeRecordFilter({ + recordFilterId: objectFilterDropdownCurrentRecordFilter.id, + }); } }; diff --git a/packages/twenty-front/src/modules/views/hooks/useSetEditableFilterChipDropdownStates.ts b/packages/twenty-front/src/modules/views/hooks/useSetEditableFilterChipDropdownStates.ts index 8427b7813..bc8cd0376 100644 --- a/packages/twenty-front/src/modules/views/hooks/useSetEditableFilterChipDropdownStates.ts +++ b/packages/twenty-front/src/modules/views/hooks/useSetEditableFilterChipDropdownStates.ts @@ -2,7 +2,6 @@ import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState'; import { objectFilterDropdownSelectedOptionValuesComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSelectedOptionValuesComponentState'; import { objectFilterDropdownSelectedRecordIdsComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSelectedRecordIdsComponentState'; -import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState'; import { useFilterableFieldMetadataItemsInRecordIndexContext } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItemsInRecordIndexContext'; @@ -42,13 +41,6 @@ export const useSetEditableFilterChipDropdownStates = () => { recordFilter.operand, ); - set( - selectedFilterComponentState.atomFamily({ - instanceId: recordFilter.id, - }), - recordFilter, - ); - set( objectFilterDropdownCurrentRecordFilterComponentState.atomFamily({ instanceId: recordFilter.id,