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 29218a8b2..55159cc69 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 @@ -8,6 +8,7 @@ import { selectedOperandInDropdownComponentState } from '@/object-record/object- 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'; @@ -71,6 +72,11 @@ export const ObjectFilterDropdownFilterSelectMenuItem = ({ selectedFilterComponentState, ); + const setObjectFilterDropdownCurrentRecordFilter = + useSetRecoilComponentStateV2( + objectFilterDropdownCurrentRecordFilterComponentState, + ); + const handleSelectFilter = (fieldMetadataItem: FieldMetadataItem) => { setFieldMetadataItemIdUsedInDropdown(fieldMetadataItem.id); @@ -97,9 +103,11 @@ export const ObjectFilterDropdownFilterSelectMenuItem = ({ ); if (filterIsAlreadyInCurrentRecordFilters) { - setSelectedFilter({ - ...duplicateFilterInCurrentRecordFilters, - }); + setSelectedFilter(duplicateFilterInCurrentRecordFilters); + + setObjectFilterDropdownCurrentRecordFilter( + duplicateFilterInCurrentRecordFilters, + ); setSelectedOperandInDropdown( duplicateFilterInCurrentRecordFilters.operand, 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 32c4331eb..efedcdbe1 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 @@ -4,6 +4,7 @@ import { StyledInput } from '@/object-record/object-filter-dropdown/components/O import { FILTER_FIELD_LIST_ID } from '@/object-record/object-filter-dropdown/constants/FilterFieldListId'; import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState'; import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector'; +import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState'; import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState'; import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState'; import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState'; @@ -84,6 +85,11 @@ export const ObjectFilterDropdownSubFieldSelect = () => { selectedFilterComponentState, ); + const setObjectFilterDropdownCurrentRecordFilter = + useSetRecoilComponentStateV2( + objectFilterDropdownCurrentRecordFilterComponentState, + ); + const handleSelectFilter = ( fieldMetadataItem: FieldMetadataItem | null | undefined, subFieldName?: string | null | undefined, @@ -119,9 +125,11 @@ export const ObjectFilterDropdownSubFieldSelect = () => { ); if (filterIsAlreadyInCurrentRecordFilters) { - setSelectedFilter({ - ...duplicateFilterInCurrentRecordFilters, - }); + setSelectedFilter(duplicateFilterInCurrentRecordFilters); + + setObjectFilterDropdownCurrentRecordFilter( + duplicateFilterInCurrentRecordFilters, + ); setSelectedOperandInDropdown( duplicateFilterInCurrentRecordFilters.operand, 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 eaba93073..1629318e3 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 @@ -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 ObjectFilterDropdownTextInput = () => { - const selectedOperandInDropdown = useRecoilComponentValueV2( - selectedOperandInDropdownComponentState, - ); - const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2( fieldMetadataItemUsedInDropdownComponentSelector, ); - const subFieldNameUsedInDropdown = useRecoilComponentValueV2( - subFieldNameUsedInDropdownComponentState, - ); + const { objectFilterDropdownFilterValue } = + useObjectFilterDropdownFilterValue(); - const selectedFilter = useRecoilComponentValueV2( - selectedFilterComponentState, - ); - - 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) { @@ -48,38 +32,19 @@ export const ObjectFilterDropdownTextInput = () => { ); return ( - fieldMetadataItemUsedInDropdown && - selectedOperandInDropdown && ( - - ) => { - const newValue = event.target.value; + + ) => { + 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, - }); - }} - /> - - ) + applyObjectFilterDropdownFilterValue(newValue); + }} + /> + ); }; 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 new file mode 100644 index 000000000..69f83a828 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue.ts @@ -0,0 +1,62 @@ +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 { useCreateRecordFilterFromObjectFilterDropdownCurrentStates } from '@/object-record/record-filter/hooks/useCreateRecordFilterFromObjectFilterDropdownCurrentStates'; +import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { isDefined } from 'twenty-shared/utils'; + +export const useApplyObjectFilterDropdownFilterValue = () => { + const objectFilterDropdownCurrentRecordFilter = useRecoilComponentValueV2( + objectFilterDropdownCurrentRecordFilterComponentState, + ); + + const objectFilterDropdownFilterNotYetCreated = !isDefined( + objectFilterDropdownCurrentRecordFilter, + ); + + const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2( + fieldMetadataItemUsedInDropdownComponentSelector, + ); + + const { createRecordFilterFromObjectFilterDropdownCurrentStates } = + useCreateRecordFilterFromObjectFilterDropdownCurrentStates(); + + const { upsertObjectFilterDropdownCurrentFilter } = + useUpsertObjectFilterDropdownCurrentFilter(); + + const applyObjectFilterDropdownFilterValue = (newFilterValue: string) => { + if (objectFilterDropdownFilterNotYetCreated) { + if (!isDefined(fieldMetadataItemUsedInDropdown)) { + throw new Error( + `Field metadata item is not defined in object filter dropdown when setting a filter value to create it, this should not happen.`, + ); + } + + const { newRecordFilterFromObjectFilterDropdownStates } = + createRecordFilterFromObjectFilterDropdownCurrentStates( + fieldMetadataItemUsedInDropdown, + ); + + const newCurrentRecordFilter = { + ...newRecordFilterFromObjectFilterDropdownStates, + value: newFilterValue, + displayValue: newFilterValue, + } satisfies RecordFilter; + + upsertObjectFilterDropdownCurrentFilter(newCurrentRecordFilter); + } else { + const newCurrentRecordFilter = { + ...objectFilterDropdownCurrentRecordFilter, + value: newFilterValue, + displayValue: newFilterValue, + } satisfies RecordFilter; + + upsertObjectFilterDropdownCurrentFilter(newCurrentRecordFilter); + } + }; + + return { + applyObjectFilterDropdownFilterValue, + }; +}; diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useObjectFilterDropdownFilterValue.ts b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useObjectFilterDropdownFilterValue.ts new file mode 100644 index 000000000..54c721cbd --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useObjectFilterDropdownFilterValue.ts @@ -0,0 +1,12 @@ +import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; + +export const useObjectFilterDropdownFilterValue = () => { + const currentRecordFilter = useRecoilComponentValueV2( + objectFilterDropdownCurrentRecordFilterComponentState, + ); + + const objectFilterDropdownFilterValue = currentRecordFilter?.value; + + return { objectFilterDropdownFilterValue }; +}; 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 209ffda50..5c239d59e 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 @@ -1,4 +1,5 @@ import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState'; +import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState'; import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState'; import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState'; import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState'; @@ -50,6 +51,12 @@ export const useResetFilterDropdown = (componentInstanceId?: string) => { componentInstanceId, ); + const objectFilterDropdownCurrentRecordFilterCallbackState = + useRecoilComponentCallbackStateV2( + objectFilterDropdownCurrentRecordFilterComponentState, + componentInstanceId, + ); + const resetFilterDropdown = useRecoilCallback( ({ set }) => () => { @@ -60,6 +67,7 @@ export const useResetFilterDropdown = (componentInstanceId?: string) => { set(objectFilterDropdownFilterIsSelectedCallbackState, false); set(objectFilterDropdownIsSelectingCompositeFieldCallbackState, false); set(fieldMetadataItemIdUsedInDropdownCallbackState, null); + set(objectFilterDropdownCurrentRecordFilterCallbackState, null); }, [ objectFilterDropdownSearchInputCallbackState, @@ -69,6 +77,7 @@ export const useResetFilterDropdown = (componentInstanceId?: string) => { objectFilterDropdownFilterIsSelectedCallbackState, objectFilterDropdownIsSelectingCompositeFieldCallbackState, fieldMetadataItemIdUsedInDropdownCallbackState, + objectFilterDropdownCurrentRecordFilterCallbackState, ], ); 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 new file mode 100644 index 000000000..52a45b438 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/hooks/useUpsertObjectFilterDropdownCurrentFilter.ts @@ -0,0 +1,31 @@ +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'; + +export const useUpsertObjectFilterDropdownCurrentFilter = () => { + const setObjectFilterDropdownCurrentRecordFilter = + useSetRecoilComponentStateV2( + objectFilterDropdownCurrentRecordFilterComponentState, + ); + + const setSelectedFilter = useSetRecoilComponentStateV2( + selectedFilterComponentState, + ); + + const { upsertRecordFilter } = useUpsertRecordFilter(); + + const upsertObjectFilterDropdownCurrentFilter = ( + recordFilterToUpsert: RecordFilter, + ) => { + upsertRecordFilter(recordFilterToUpsert); + + setObjectFilterDropdownCurrentRecordFilter(recordFilterToUpsert); + setSelectedFilter(recordFilterToUpsert); + }; + + return { + upsertObjectFilterDropdownCurrentFilter, + }; +}; diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState.ts b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState.ts new file mode 100644 index 000000000..f275d9d77 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState.ts @@ -0,0 +1,10 @@ +import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext'; +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; +import { RecordFilter } from '../../record-filter/types/RecordFilter'; + +export const objectFilterDropdownCurrentRecordFilterComponentState = + createComponentStateV2({ + key: 'objectFilterDropdownCurrentRecordFilterComponentState', + defaultValue: undefined, + componentInstanceContext: ObjectFilterDropdownComponentInstanceContext, + }); diff --git a/packages/twenty-front/src/modules/object-record/record-filter/hooks/useCreateRecordFilterFromObjectFilterDropdownCurrentStates.ts b/packages/twenty-front/src/modules/object-record/record-filter/hooks/useCreateRecordFilterFromObjectFilterDropdownCurrentStates.ts new file mode 100644 index 000000000..7020e0f38 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-filter/hooks/useCreateRecordFilterFromObjectFilterDropdownCurrentStates.ts @@ -0,0 +1,61 @@ +import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; +import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; +import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector'; +import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; +import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState'; +import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { isDefined } from 'twenty-shared/utils'; +import { v4 } from 'uuid'; + +export const useCreateRecordFilterFromObjectFilterDropdownCurrentStates = + () => { + const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2( + fieldMetadataItemUsedInDropdownComponentSelector, + ); + + const selectedOperandInDropdown = useRecoilComponentValueV2( + selectedOperandInDropdownComponentState, + ); + + const subFieldNameUsedInDropdown = useRecoilComponentValueV2( + subFieldNameUsedInDropdownComponentState, + ); + + const createRecordFilterFromObjectFilterDropdownCurrentStates = ( + fieldMetadataItem: FieldMetadataItem, + ) => { + if (!isDefined(fieldMetadataItemUsedInDropdown)) { + throw new Error( + `Field metadata item used in dropdown is not defined when creating a record filter from object filter dropdown current states, this should not happen.`, + ); + } + + const filterType = getFilterTypeFromFieldType( + fieldMetadataItemUsedInDropdown.type, + ); + + if (!isDefined(selectedOperandInDropdown)) { + throw new Error( + `Selected operand in dropdown is not defined when creating a record filter from object filter dropdown current states, this should not happen.`, + ); + } + + const newRecordFilterFromObjectFilterDropdownStates: RecordFilter = { + id: v4(), + fieldMetadataId: fieldMetadataItemUsedInDropdown?.id, + operand: selectedOperandInDropdown, + displayValue: '', + label: fieldMetadataItem.label, + type: filterType, + value: '', + subFieldName: subFieldNameUsedInDropdown, + }; + + return { newRecordFilterFromObjectFilterDropdownStates }; + }; + + return { + createRecordFilterFromObjectFilterDropdownCurrentStates, + }; + }; diff --git a/packages/twenty-front/src/modules/views/hooks/useSetEditableFilterChipDropdownStates.ts b/packages/twenty-front/src/modules/views/hooks/useSetEditableFilterChipDropdownStates.ts index 4dd5645d3..8427b7813 100644 --- a/packages/twenty-front/src/modules/views/hooks/useSetEditableFilterChipDropdownStates.ts +++ b/packages/twenty-front/src/modules/views/hooks/useSetEditableFilterChipDropdownStates.ts @@ -1,4 +1,5 @@ import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState'; +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'; @@ -48,6 +49,13 @@ export const useSetEditableFilterChipDropdownStates = () => { recordFilter, ); + set( + objectFilterDropdownCurrentRecordFilterComponentState.atomFamily({ + instanceId: recordFilter.id, + }), + recordFilter, + ); + set( subFieldNameUsedInDropdownComponentState.atomFamily({ instanceId: recordFilter.id,