diff --git a/packages/twenty-front/src/modules/context-store/utils/computeContextStoreFilters.ts b/packages/twenty-front/src/modules/context-store/utils/computeContextStoreFilters.ts index a86b7905f..3e888aa6d 100644 --- a/packages/twenty-front/src/modules/context-store/utils/computeContextStoreFilters.ts +++ b/packages/twenty-front/src/modules/context-store/utils/computeContextStoreFilters.ts @@ -3,7 +3,7 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { RecordGqlOperationFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter'; import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter'; import { RecordFilterValueDependencies } from '@/object-record/record-filter/types/RecordFilterValueDependencies'; -import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter'; +import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter'; import { makeAndFilterVariables } from '@/object-record/utils/makeAndFilterVariables'; export const computeContextStoreFilters = ( @@ -16,12 +16,12 @@ export const computeContextStoreFilters = ( if (contextStoreTargetedRecordsRule.mode === 'exclusion') { queryFilter = makeAndFilterVariables([ - computeViewRecordGqlOperationFilter( + computeRecordGqlOperationFilter({ filterValueDependencies, - contextStoreFilters, - objectMetadataItem?.fields ?? [], - [], - ), + fields: objectMetadataItem?.fields ?? [], + recordFilters: contextStoreFilters, + recordFilterGroups: [], + }), contextStoreTargetedRecordsRule.excludedRecordIds.length > 0 ? { not: { @@ -41,12 +41,12 @@ export const computeContextStoreFilters = ( in: contextStoreTargetedRecordsRule.selectedRecordIds, }, } - : computeViewRecordGqlOperationFilter( + : computeRecordGqlOperationFilter({ filterValueDependencies, - contextStoreFilters, - objectMetadataItem?.fields ?? [], - [], - ); + fields: objectMetadataItem?.fields ?? [], + recordFilters: contextStoreFilters, + recordFilterGroups: [], + }); } return queryFilter; diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterAddFilterRuleSelect.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterAddFilterRuleSelect.tsx index ab5cad4a9..7680af8c6 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterAddFilterRuleSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterAddFilterRuleSelect.tsx @@ -1,6 +1,7 @@ import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById'; import { availableFieldMetadataItemsForFilterFamilySelector } from '@/object-metadata/states/availableFieldMetadataItemsForFilterFamilySelector'; import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; +import { getAdvancedFilterAddFilterRuleSelectDropdownId } from '@/object-record/advanced-filter/utils/getAdvancedFilterAddFilterRuleSelectDropdownId'; import { useUpsertRecordFilterGroup } from '@/object-record/record-filter-group/hooks/useUpsertRecordFilterGroup'; import { RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup'; import { RecordFilterGroupLogicalOperator } from '@/object-record/record-filter-group/types/RecordFilterGroupLogicalOperator'; @@ -27,7 +28,9 @@ export const AdvancedFilterAddFilterRuleSelect = ({ recordFilterGroup, lastChildPosition = 0, }: AdvancedFilterAddFilterRuleSelectProps) => { - const dropdownId = `advanced-filter-add-filter-rule-${recordFilterGroup.id}`; + const dropdownId = getAdvancedFilterAddFilterRuleSelectDropdownId( + recordFilterGroup.id, + ); const { currentViewId } = useGetCurrentView(); diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRuleOptionsDropdown.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRuleOptionsDropdown.tsx index df45cd041..3441144f5 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRuleOptionsDropdown.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRuleOptionsDropdown.tsx @@ -8,7 +8,6 @@ import { currentRecordFiltersComponentState } from '@/object-record/record-filte import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; -import { ADVANCED_FILTER_DROPDOWN_ID } from '@/views/constants/AdvancedFilterDropdownId'; import { isDefined } from 'twenty-shared'; import { MenuItem } from 'twenty-ui'; @@ -46,7 +45,7 @@ export const AdvancedFilterRuleOptionsDropdown = ({ const handleRemove = async () => { if (isDefined(viewFilterId)) { - removeRecordFilter(viewFilterId); + removeRecordFilter({ recordFilterId: viewFilterId }); const isOnlyViewFilterInGroup = childViewFiltersAndViewFilterGroups.length === 1; @@ -66,7 +65,7 @@ export const AdvancedFilterRuleOptionsDropdown = ({ ); for (const childViewFilter of childViewFilters) { - removeRecordFilter(childViewFilter.id); + removeRecordFilter({ recordFilterId: childViewFilter.id }); } } else { throw new Error('No view filter or view filter group to remove'); @@ -86,7 +85,7 @@ export const AdvancedFilterRuleOptionsDropdown = ({ } - dropdownHotkeyScope={{ scope: ADVANCED_FILTER_DROPDOWN_ID }} + dropdownHotkeyScope={{ scope: dropdownId }} dropdownOffset={{ y: 8, x: 0 }} dropdownPlacement="bottom-start" /> diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/utils/getAdvancedFilterAddFilterRuleSelectDropdownId.ts b/packages/twenty-front/src/modules/object-record/advanced-filter/utils/getAdvancedFilterAddFilterRuleSelectDropdownId.ts new file mode 100644 index 000000000..497dae8fc --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/utils/getAdvancedFilterAddFilterRuleSelectDropdownId.ts @@ -0,0 +1,5 @@ +export const getAdvancedFilterAddFilterRuleSelectDropdownId = ( + recordFilterGroupId: string, +) => { + return `advanced-filter-add-filter-rule-${recordFilterGroupId}`; +}; diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/AdvancedFilterButton.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/AdvancedFilterButton.tsx index 1527662af..99dcefc23 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/AdvancedFilterButton.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/AdvancedFilterButton.tsx @@ -133,8 +133,8 @@ export const AdvancedFilterButton = () => { }); } - openAdvancedFilterDropdown(); closeObjectFilterDropdown(); + openAdvancedFilterDropdown(); }; return ( diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectCompositeFieldSubMenu.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectCompositeFieldSubMenu.tsx index 7ac9491ef..95f00a1d7 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectCompositeFieldSubMenu.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelectCompositeFieldSubMenu.tsx @@ -9,12 +9,15 @@ 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 { getCompositeSubFieldLabel } from '@/object-record/object-filter-dropdown/utils/getCompositeSubFieldLabel'; import { getFilterableFieldTypeLabel } from '@/object-record/object-filter-dropdown/utils/getFilterableFieldTypeLabel'; import { getInitialFilterValue } from '@/object-record/object-filter-dropdown/utils/getInitialFilterValue'; import { useApplyRecordFilter } from '@/object-record/record-filter/hooks/useApplyRecordFilter'; +import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; +import { findDuplicateRecordFilterInNonAdvancedRecordFilters } from '@/object-record/record-filter/utils/findDuplicateRecordFilterInNonAdvancedRecordFilters'; import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands'; import { SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS } from '@/settings/data-model/constants/SettingsCompositeFieldTypeConfigs'; import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; @@ -81,55 +84,84 @@ export const ObjectFilterDropdownFilterSelectCompositeFieldSubMenu = () => { advancedFilterViewFilterId, ); + const currentRecordFilters = useRecoilComponentValueV2( + currentRecordFiltersComponentState, + ); + + const setSelectedFilter = useSetRecoilComponentStateV2( + selectedFilterComponentState, + ); + const handleSelectFilter = ( fieldMetadataItem: FieldMetadataItem | null | undefined, subFieldName?: string | null | undefined, ) => { - if (isDefined(fieldMetadataItem)) { - if ( - isDefined(advancedFilterViewFilterId) && - isDefined(advancedFilterViewFilterGroupId) - ) { - closeAdvancedFilterDropdown(); + if (!isDefined(fieldMetadataItem)) { + return; + } - const type = getFilterTypeFromFieldType(fieldMetadataItem.type); + const type = getFilterTypeFromFieldType(fieldMetadataItem.type); - const operand = getRecordFilterOperands({ - filterType: type, - subFieldName: subFieldName, - })[0]; + const defaultOperand = getRecordFilterOperands({ + filterType: type, + subFieldName: subFieldName, + })[0]; - const { value, displayValue } = getInitialFilterValue(type, operand); + if ( + isDefined(advancedFilterViewFilterId) && + isDefined(advancedFilterViewFilterGroupId) + ) { + closeAdvancedFilterDropdown(); - applyRecordFilter({ - id: advancedFilterViewFilterId, - fieldMetadataId: fieldMetadataItem.id, - value, - operand, - displayValue, - type: getFilterTypeFromFieldType(fieldMetadataItem.type), - label: fieldMetadataItem.label, - recordFilterGroupId: advancedFilterViewFilterGroupId, - subFieldName: subFieldName, - }); - } - - setFieldMetadataItemIdUsedInDropdown(fieldMetadataItem.id); - - const type = getFilterTypeFromFieldType(fieldMetadataItem.type); - - setSelectedOperandInDropdown( - getRecordFilterOperands({ - filterType: type, - subFieldName: subFieldName, - })[0], + const { value, displayValue } = getInitialFilterValue( + type, + defaultOperand, ); - setSubFieldNameUsedInDropdown(subFieldName); + applyRecordFilter({ + id: advancedFilterViewFilterId, + fieldMetadataId: fieldMetadataItem.id, + value, + operand: defaultOperand, + displayValue, + type, + label: fieldMetadataItem.label, + recordFilterGroupId: advancedFilterViewFilterGroupId, + subFieldName: subFieldName, + }); + } - setObjectFilterDropdownSearchInput(''); + setFieldMetadataItemIdUsedInDropdown(fieldMetadataItem.id); - setObjectFilterDropdownFilterIsSelected(true); + setSubFieldNameUsedInDropdown(subFieldName); + + setObjectFilterDropdownSearchInput(''); + + setObjectFilterDropdownFilterIsSelected(true); + + const duplicateFilterInCurrentRecordFilters = + findDuplicateRecordFilterInNonAdvancedRecordFilters({ + recordFilters: currentRecordFilters, + fieldMetadataItemId: fieldMetadataItem.id, + subFieldName, + }); + + const filterIsAlreadyInCurrentRecordFilters = isDefined( + duplicateFilterInCurrentRecordFilters, + ); + + const isSimpleFilter = !isDefined(advancedFilterViewFilterId); + + if (isSimpleFilter && filterIsAlreadyInCurrentRecordFilters) { + setSelectedFilter({ + ...duplicateFilterInCurrentRecordFilters, + }); + + setSelectedOperandInDropdown( + duplicateFilterInCurrentRecordFilters.operand, + ); + } else { + setSelectedOperandInDropdown(defaultOperand); } }; 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 36934f8ac..b4e5c6b09 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 @@ -10,7 +10,10 @@ import { selectedOperandInDropdownComponentState } from '@/object-record/object- import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; +import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState'; import { isCompositeField } from '@/object-record/object-filter-dropdown/utils/isCompositeField'; +import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; +import { findDuplicateRecordFilterInNonAdvancedRecordFilters } from '@/object-record/record-filter/utils/findDuplicateRecordFilterInNonAdvancedRecordFilters'; import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands'; import { RecordPickerHotkeyScope } from '@/object-record/record-picker/types/RecordPickerHotkeyScope'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; @@ -19,6 +22,7 @@ import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/ import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { useRecoilValue } from 'recoil'; +import { isDefined } from 'twenty-shared'; import { MenuItemSelect, useIcons } from 'twenty-ui'; export type ObjectFilterDropdownFilterSelectMenuItemProps = { @@ -67,6 +71,14 @@ export const ObjectFilterDropdownFilterSelectMenuItem = ({ advancedFilterViewFilterId, ); + const currentRecordFilters = useRecoilComponentValueV2( + currentRecordFiltersComponentState, + ); + + const setSelectedFilter = useSetRecoilComponentStateV2( + selectedFilterComponentState, + ); + const handleSelectFilter = (fieldMetadataItem: FieldMetadataItem) => { closeAdvancedFilterDropdown(); @@ -78,13 +90,35 @@ export const ObjectFilterDropdownFilterSelectMenuItem = ({ setHotkeyScope(RecordPickerHotkeyScope.RecordPicker); } - setSelectedOperandInDropdown( - getRecordFilterOperands({ - filterType, - })[0], - ); + const defaultOperand = getRecordFilterOperands({ + filterType, + })[0]; setObjectFilterDropdownFilterIsSelected(true); + + const duplicateFilterInCurrentRecordFilters = + findDuplicateRecordFilterInNonAdvancedRecordFilters({ + recordFilters: currentRecordFilters, + fieldMetadataItemId: fieldMetadataItem.id, + }); + + const filterIsAlreadyInCurrentRecordFilters = isDefined( + duplicateFilterInCurrentRecordFilters, + ); + + const isSimpleFilter = !isDefined(advancedFilterViewFilterId); + + if (isSimpleFilter && filterIsAlreadyInCurrentRecordFilters) { + setSelectedFilter({ + ...duplicateFilterInCurrentRecordFilters, + }); + + setSelectedOperandInDropdown( + duplicateFilterInCurrentRecordFilters.operand, + ); + } else { + setSelectedOperandInDropdown(defaultOperand); + } }; const { getIcon } = useIcons(); diff --git a/packages/twenty-front/src/modules/object-record/record-filter/hooks/__tests__/useRemoveRecordFilter.test.tsx b/packages/twenty-front/src/modules/object-record/record-filter/hooks/__tests__/useRemoveRecordFilter.test.tsx index 00efe5de9..cd778c190 100644 --- a/packages/twenty-front/src/modules/object-record/record-filter/hooks/__tests__/useRemoveRecordFilter.test.tsx +++ b/packages/twenty-front/src/modules/object-record/record-filter/hooks/__tests__/useRemoveRecordFilter.test.tsx @@ -54,7 +54,9 @@ describe('useRemoveRecordFilter', () => { expect(result.current.currentRecordFilters[0]).toEqual(mockRecordFilter); act(() => { - result.current.removeRecordFilter(mockRecordFilter.fieldMetadataId); + result.current.removeRecordFilter({ + recordFilterId: mockRecordFilter.id, + }); }); expect(result.current.currentRecordFilters).toHaveLength(0); @@ -96,7 +98,9 @@ describe('useRemoveRecordFilter', () => { expect(result.current.currentRecordFilters).toHaveLength(1); act(() => { - result.current.removeRecordFilter('non-existent-field-metadata-id'); + result.current.removeRecordFilter({ + recordFilterId: 'non-existent-field-metadata-id', + }); }); expect(result.current.currentRecordFilters).toHaveLength(1); diff --git a/packages/twenty-front/src/modules/object-record/record-filter/hooks/__tests__/useUpsertRecordFilter.test.tsx b/packages/twenty-front/src/modules/object-record/record-filter/hooks/__tests__/useUpsertRecordFilter.test.tsx index edaf73059..00e6b0be0 100644 --- a/packages/twenty-front/src/modules/object-record/record-filter/hooks/__tests__/useUpsertRecordFilter.test.tsx +++ b/packages/twenty-front/src/modules/object-record/record-filter/hooks/__tests__/useUpsertRecordFilter.test.tsx @@ -14,7 +14,7 @@ const Wrapper = getJestMetadataAndApolloMocksWrapper({ }); describe('useUpsertRecordFilter', () => { - it('should add a new filter when fieldMetadataId does not exist', () => { + it('should add a new filter when record filter id does not exist', () => { const { result } = renderHook( () => { const currentRecordFilters = useRecoilComponentValueV2( @@ -48,7 +48,7 @@ describe('useUpsertRecordFilter', () => { expect(result.current.currentRecordFilters[0]).toEqual(mockNewRecordFilter); }); - it('should update an existing filter when fieldMetadataId exists', () => { + it('should update an existing filter when record filter id exists', () => { const { result } = renderHook( () => { const currentRecordFilters = useRecoilComponentValueV2( diff --git a/packages/twenty-front/src/modules/object-record/record-filter/hooks/useRemoveRecordFilter.ts b/packages/twenty-front/src/modules/object-record/record-filter/hooks/useRemoveRecordFilter.ts index a41f5b3f8..0c0d20798 100644 --- a/packages/twenty-front/src/modules/object-record/record-filter/hooks/useRemoveRecordFilter.ts +++ b/packages/twenty-front/src/modules/object-record/record-filter/hooks/useRemoveRecordFilter.ts @@ -10,7 +10,7 @@ export const useRemoveRecordFilter = () => { const removeRecordFilter = useRecoilCallback( ({ set, snapshot }) => - (fieldMetadataId: string) => { + ({ recordFilterId }: { recordFilterId: string }) => { const currentRecordFilters = getSnapshotValue( snapshot, currentRecordFiltersCallbackState, @@ -18,8 +18,7 @@ export const useRemoveRecordFilter = () => { const foundRecordFilterInCurrentRecordFilters = currentRecordFilters.some( - (existingFilter) => - existingFilter.fieldMetadataId === fieldMetadataId, + (existingFilter) => existingFilter.id === recordFilterId, ); if (foundRecordFilterInCurrentRecordFilters) { @@ -27,8 +26,7 @@ export const useRemoveRecordFilter = () => { const newCurrentRecordFilters = [...currentRecordFilters]; const indexOfFilterToRemove = newCurrentRecordFilters.findIndex( - (existingFilter) => - existingFilter.fieldMetadataId === fieldMetadataId, + (existingFilter) => existingFilter.id === recordFilterId, ); newCurrentRecordFilters.splice(indexOfFilterToRemove, 1); diff --git a/packages/twenty-front/src/modules/object-record/record-filter/hooks/useUpsertRecordFilter.ts b/packages/twenty-front/src/modules/object-record/record-filter/hooks/useUpsertRecordFilter.ts index 6f63ec614..e12a676bb 100644 --- a/packages/twenty-front/src/modules/object-record/record-filter/hooks/useUpsertRecordFilter.ts +++ b/packages/twenty-front/src/modules/object-record/record-filter/hooks/useUpsertRecordFilter.ts @@ -19,9 +19,7 @@ export const useUpsertRecordFilter = () => { const foundRecordFilterInCurrentRecordFilters = currentRecordFilters.some( - (existingFilter) => - existingFilter.fieldMetadataId === - recordFilterToSet.fieldMetadataId, + (existingFilter) => existingFilter.id === recordFilterToSet.id, ); if (!foundRecordFilterInCurrentRecordFilters) { @@ -34,9 +32,7 @@ export const useUpsertRecordFilter = () => { const newCurrentRecordFilters = [...currentRecordFilters]; const indexOfFilterToUpdate = newCurrentRecordFilters.findIndex( - (existingFilter) => - existingFilter.fieldMetadataId === - recordFilterToSet.fieldMetadataId, + (existingFilter) => existingFilter.id === recordFilterToSet.id, ); newCurrentRecordFilters[indexOfFilterToUpdate] = { diff --git a/packages/twenty-front/src/modules/object-record/record-filter/utils/__tests__/computeViewRecordGqlOperationFilter.test.ts b/packages/twenty-front/src/modules/object-record/record-filter/utils/__tests__/computeViewRecordGqlOperationFilter.test.ts index f0277f78b..484e078d5 100644 --- a/packages/twenty-front/src/modules/object-record/record-filter/utils/__tests__/computeViewRecordGqlOperationFilter.test.ts +++ b/packages/twenty-front/src/modules/object-record/record-filter/utils/__tests__/computeViewRecordGqlOperationFilter.test.ts @@ -1,7 +1,7 @@ import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter'; import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand'; import { RecordFilterValueDependencies } from '@/object-record/record-filter/types/RecordFilterValueDependencies'; -import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter'; +import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; import { FieldMetadataType } from '~/generated/graphql'; import { getCompaniesMock } from '~/testing/mock-data/companies'; @@ -40,12 +40,12 @@ describe('computeViewRecordGqlOperationFilter', () => { label: 'Name', }; - const result = computeViewRecordGqlOperationFilter( - mockFilterValueDependencies, - [nameFilter], - companyMockObjectMetadataItem.fields, - [], - ); + const result = computeRecordGqlOperationFilter({ + filterValueDependencies: mockFilterValueDependencies, + recordFilters: [nameFilter], + recordFilterGroups: [], + fields: companyMockObjectMetadataItem.fields, + }); expect(result).toEqual({ name: { @@ -85,12 +85,12 @@ describe('computeViewRecordGqlOperationFilter', () => { label: 'Employees', }; - const result = computeViewRecordGqlOperationFilter( - mockFilterValueDependencies, - [nameFilter, employeesFilter], - companyMockObjectMetadataItem.fields, - [], - ); + const result = computeRecordGqlOperationFilter({ + filterValueDependencies: mockFilterValueDependencies, + recordFilters: [nameFilter, employeesFilter], + recordFilterGroups: [], + fields: companyMockObjectMetadataItem.fields, + }); expect(result).toEqual({ and: [ @@ -156,17 +156,17 @@ describe('should work as expected for the different field types', () => { type: FieldMetadataType.ADDRESS, }; - const result = computeViewRecordGqlOperationFilter( - mockFilterValueDependencies, - [ + const result = computeRecordGqlOperationFilter({ + filterValueDependencies: mockFilterValueDependencies, + recordFilters: [ addressFilterContains, addressFilterDoesNotContain, addressFilterIsEmpty, addressFilterIsNotEmpty, ], - companyMockObjectMetadataItem.fields, - [], - ); + recordFilterGroups: [], + fields: companyMockObjectMetadataItem.fields, + }); expect(result).toEqual({ and: [ @@ -523,17 +523,17 @@ describe('should work as expected for the different field types', () => { type: FieldMetadataType.PHONES, }; - const result = computeViewRecordGqlOperationFilter( - mockFilterValueDependencies, - [ + const result = computeRecordGqlOperationFilter({ + filterValueDependencies: mockFilterValueDependencies, + recordFilters: [ phonesFilterContains, phonesFilterDoesNotContain, phonesFilterIsEmpty, phonesFilterIsNotEmpty, ], - personMockObjectMetadataItem.fields, - [], - ); + recordFilterGroups: [], + fields: personMockObjectMetadataItem.fields, + }); expect(result).toEqual({ and: [ @@ -657,17 +657,17 @@ describe('should work as expected for the different field types', () => { type: FieldMetadataType.EMAILS, }; - const result = computeViewRecordGqlOperationFilter( - mockFilterValueDependencies, - [ + const result = computeRecordGqlOperationFilter({ + filterValueDependencies: mockFilterValueDependencies, + recordFilters: [ emailsFilterContains, emailsFilterDoesNotContain, emailsFilterIsEmpty, emailsFilterIsNotEmpty, ], - personMockObjectMetadataItem.fields, - [], - ); + recordFilterGroups: [], + fields: personMockObjectMetadataItem.fields, + }); expect(result).toEqual({ and: [ @@ -793,18 +793,18 @@ describe('should work as expected for the different field types', () => { type: FieldMetadataType.DATE_TIME, }; - const result = computeViewRecordGqlOperationFilter( - mockFilterValueDependencies, - [ + const result = computeRecordGqlOperationFilter({ + filterValueDependencies: mockFilterValueDependencies, + recordFilters: [ dateFilterIsAfter, dateFilterIsBefore, dateFilterIs, dateFilterIsEmpty, dateFilterIsNotEmpty, ], - companyMockObjectMetadataItem.fields, - [], - ); + recordFilterGroups: [], + fields: companyMockObjectMetadataItem.fields, + }); expect(result).toEqual({ and: [ @@ -894,17 +894,17 @@ describe('should work as expected for the different field types', () => { type: FieldMetadataType.NUMBER, }; - const result = computeViewRecordGqlOperationFilter( - mockFilterValueDependencies, - [ + const result = computeRecordGqlOperationFilter({ + filterValueDependencies: mockFilterValueDependencies, + recordFilters: [ employeesFilterIsGreaterThan, employeesFilterIsLessThan, employeesFilterIsEmpty, employeesFilterIsNotEmpty, ], - companyMockObjectMetadataItem.fields, - [], - ); + recordFilterGroups: [], + fields: companyMockObjectMetadataItem.fields, + }); expect(result).toEqual({ and: [ diff --git a/packages/twenty-front/src/modules/object-record/record-filter/utils/computeViewRecordGqlOperationFilter.ts b/packages/twenty-front/src/modules/object-record/record-filter/utils/computeViewRecordGqlOperationFilter.ts index 16fb47b1d..5a1f39766 100644 --- a/packages/twenty-front/src/modules/object-record/record-filter/utils/computeViewRecordGqlOperationFilter.ts +++ b/packages/twenty-front/src/modules/object-record/record-filter/utils/computeViewRecordGqlOperationFilter.ts @@ -32,8 +32,7 @@ import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter'; import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand'; import { RecordFilterValueDependencies } from '@/object-record/record-filter/types/RecordFilterValueDependencies'; import { getEmptyRecordGqlOperationFilter } from '@/object-record/record-filter/utils/getEmptyRecordGqlOperationFilter'; -import { ViewFilterGroup } from '@/views/types/ViewFilterGroup'; -import { ViewFilterGroupLogicalOperator } from '@/views/types/ViewFilterGroupLogicalOperator'; + import { resolveDateViewFilterValue } from '@/views/view-filter-value/utils/resolveDateViewFilterValue'; import { resolveSelectViewFilterValue } from '@/views/view-filter-value/utils/resolveSelectViewFilterValue'; import { jsonRelationFilterValueSchema } from '@/views/view-filter-value/validation-schemas/jsonRelationFilterValueSchema'; @@ -41,6 +40,8 @@ import { simpleRelationFilterValueSchema } from '@/views/view-filter-value/valid import { endOfDay, roundToNearestMinutes, startOfDay } from 'date-fns'; import { z } from 'zod'; +import { RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup'; +import { RecordFilterGroupLogicalOperator } from '@/object-record/record-filter-group/types/RecordFilterGroupLogicalOperator'; import { FilterableFieldType } from '@/object-record/record-filter/types/FilterableFieldType'; type ComputeFilterRecordGqlOperationFilterParams = { @@ -808,54 +809,55 @@ export const computeFilterRecordGqlOperationFilter = ({ } }; -const computeViewFilterGroupRecordGqlOperationFilter = ( +const computeRecordFilterGroupRecordGqlOperationFilter = ( filterValueDependencies: RecordFilterValueDependencies, filters: RecordFilter[], fields: Pick[], - viewFilterGroups: ViewFilterGroup[], - currentViewFilterGroupId?: string, + recordFilterGroups: RecordFilterGroup[], + currentRecordFilterGroupId?: string, ): RecordGqlOperationFilter | undefined => { - const currentViewFilterGroup = viewFilterGroups.find( - (viewFilterGroup) => viewFilterGroup.id === currentViewFilterGroupId, + const currentRecordFilterGroup = recordFilterGroups.find( + (recordFilterGroup) => recordFilterGroup.id === currentRecordFilterGroupId, ); - if (!currentViewFilterGroup) { + if (!currentRecordFilterGroup) { return; } - const groupFilters = filters.filter( - (filter) => filter.recordFilterGroupId === currentViewFilterGroupId, + const recordFiltersInGroup = filters.filter( + (filter) => filter.recordFilterGroupId === currentRecordFilterGroupId, ); - const groupRecordGqlOperationFilters = groupFilters - .map((filter) => + const groupRecordGqlOperationFilters = recordFiltersInGroup + .map((recordFilter) => computeFilterRecordGqlOperationFilter({ filterValueDependencies, - filter, + filter: recordFilter, fieldMetadataItems: fields, }), ) .filter(isDefined); - const subGroupRecordGqlOperationFilters = viewFilterGroups + const subGroupRecordGqlOperationFilters = recordFilterGroups .filter( - (viewFilterGroup) => - viewFilterGroup.parentViewFilterGroupId === currentViewFilterGroupId, + (recordFilterGroup) => + recordFilterGroup.parentRecordFilterGroupId === + currentRecordFilterGroupId, ) .map((subViewFilterGroup) => - computeViewFilterGroupRecordGqlOperationFilter( + computeRecordFilterGroupRecordGqlOperationFilter( filterValueDependencies, filters, fields, - viewFilterGroups, + recordFilterGroups, subViewFilterGroup.id, ), ) .filter(isDefined); if ( - currentViewFilterGroup.logicalOperator === - ViewFilterGroupLogicalOperator.AND + currentRecordFilterGroup.logicalOperator === + RecordFilterGroupLogicalOperator.AND ) { return { and: [ @@ -864,7 +866,8 @@ const computeViewFilterGroupRecordGqlOperationFilter = ( ], }; } else if ( - currentViewFilterGroup.logicalOperator === ViewFilterGroupLogicalOperator.OR + currentRecordFilterGroup.logicalOperator === + RecordFilterGroupLogicalOperator.OR ) { return { or: [ @@ -874,38 +877,44 @@ const computeViewFilterGroupRecordGqlOperationFilter = ( }; } else { throw new Error( - `Unknown logical operator ${currentViewFilterGroup.logicalOperator}`, + `Unknown logical operator ${currentRecordFilterGroup.logicalOperator}`, ); } }; -export const computeViewRecordGqlOperationFilter = ( - filterValueDependencies: RecordFilterValueDependencies, - filters: RecordFilter[], - fields: Pick[], - viewFilterGroups: ViewFilterGroup[], -): RecordGqlOperationFilter => { - const regularRecordGqlOperationFilter: RecordGqlOperationFilter[] = filters - .filter((filter) => !filter.recordFilterGroupId) - .map((regularFilter) => - computeFilterRecordGqlOperationFilter({ - filterValueDependencies, - filter: regularFilter, - fieldMetadataItems: fields, - }), - ) - .filter(isDefined); +export const computeRecordGqlOperationFilter = ({ + fields, + filterValueDependencies, + recordFilters, + recordFilterGroups, +}: { + filterValueDependencies: RecordFilterValueDependencies; + recordFilters: RecordFilter[]; + fields: Pick[]; + recordFilterGroups: RecordFilterGroup[]; +}): RecordGqlOperationFilter => { + const regularRecordGqlOperationFilter: RecordGqlOperationFilter[] = + recordFilters + .filter((filter) => !isDefined(filter.recordFilterGroupId)) + .map((regularFilter) => + computeFilterRecordGqlOperationFilter({ + filterValueDependencies, + filter: regularFilter, + fieldMetadataItems: fields, + }), + ) + .filter(isDefined); - const outermostFilterGroupId = viewFilterGroups.find( - (viewFilterGroup) => !viewFilterGroup.parentViewFilterGroupId, + const outermostFilterGroupId = recordFilterGroups.find( + (recordFilterGroup) => !recordFilterGroup.parentRecordFilterGroupId, )?.id; const advancedRecordGqlOperationFilter = - computeViewFilterGroupRecordGqlOperationFilter( + computeRecordFilterGroupRecordGqlOperationFilter( filterValueDependencies, - filters, + recordFilters, fields, - viewFilterGroups, + recordFilterGroups, outermostFilterGroupId, ); diff --git a/packages/twenty-front/src/modules/object-record/record-filter/utils/findDuplicateRecordFilterInNonAdvancedRecordFilters.ts b/packages/twenty-front/src/modules/object-record/record-filter/utils/findDuplicateRecordFilterInNonAdvancedRecordFilters.ts new file mode 100644 index 000000000..3a8a178aa --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-filter/utils/findDuplicateRecordFilterInNonAdvancedRecordFilters.ts @@ -0,0 +1,26 @@ +import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter'; +import { compareStrictlyExceptForNullAndUndefined } from '~/utils/compareStrictlyExceptForNullAndUndefined'; + +export const findDuplicateRecordFilterInNonAdvancedRecordFilters = ({ + recordFilters, + fieldMetadataItemId, + subFieldName, +}: { + recordFilters: RecordFilter[]; + fieldMetadataItemId: string; + subFieldName?: string | null | undefined; +}): RecordFilter | undefined => { + const duplicateFilterInCurrentRecordFilters = recordFilters.find( + (recordFilter) => + compareStrictlyExceptForNullAndUndefined( + recordFilter.fieldMetadataId, + fieldMetadataItemId, + ) && + compareStrictlyExceptForNullAndUndefined( + recordFilter.subFieldName, + subFieldName, + ), + ); + + return duplicateFilterInCurrentRecordFilters; +}; diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect.tsx index 284453511..3859e5b79 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect.tsx @@ -34,7 +34,6 @@ export const RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect = const findManyRecordsParams = useFindManyRecordIndexTableParams( objectMetadataItem?.nameSingular ?? '', - objectMetadataItem?.namePlural ?? '', ); const contextStoreFilters = useRecoilComponentValueV2( diff --git a/packages/twenty-front/src/modules/object-record/record-index/export/hooks/useExportFetchRecords.ts b/packages/twenty-front/src/modules/object-record/record-index/export/hooks/useExportFetchRecords.ts index 8404c6c0c..2f8c0d90e 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/export/hooks/useExportFetchRecords.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/export/hooks/useExportFetchRecords.ts @@ -83,7 +83,6 @@ export const useExportFetchRecords = ({ const findManyRecordsParams = useFindManyRecordIndexTableParams( objectMetadataItem.nameSingular, - recordIndexId, ); const finalColumns = [ diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useFindManyRecordIndexTableParams.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useFindManyRecordIndexTableParams.ts index cd1315708..853d20971 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useFindManyRecordIndexTableParams.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useFindManyRecordIndexTableParams.ts @@ -1,17 +1,16 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy'; +import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState'; import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies'; import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; -import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter'; +import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter'; import { useCurrentRecordGroupDefinition } from '@/object-record/record-group/hooks/useCurrentRecordGroupDefinition'; import { useRecordGroupFilter } from '@/object-record/record-group/hooks/useRecordGroupFilter'; import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState'; -import { tableViewFilterGroupsComponentState } from '@/object-record/record-table/states/tableViewFilterGroupsComponentState'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; export const useFindManyRecordIndexTableParams = ( objectNameSingular: string, - recordTableId?: string, ) => { const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular, @@ -23,9 +22,8 @@ export const useFindManyRecordIndexTableParams = ( const currentRecordGroupDefinition = useCurrentRecordGroupDefinition(); - const tableViewFilterGroups = useRecoilComponentValueV2( - tableViewFilterGroupsComponentState, - recordTableId, + const currentRecordFilterGroups = useRecoilComponentValueV2( + currentRecordFilterGroupsComponentState, ); const currentRecordSorts = useRecoilComponentValueV2( @@ -38,12 +36,12 @@ export const useFindManyRecordIndexTableParams = ( const { filterValueDependencies } = useFilterValueDependencies(); - const stateFilter = computeViewRecordGqlOperationFilter( + const stateFilter = computeRecordGqlOperationFilter({ + fields: objectMetadataItem?.fields ?? [], filterValueDependencies, - currentRecordFilters, - objectMetadataItem?.fields ?? [], - tableViewFilterGroups, - ); + recordFilterGroups: currentRecordFilterGroups, + recordFilters: currentRecordFilters, + }); const orderBy = turnSortsIntoOrderBy(objectMetadataItem, currentRecordSorts); diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoardColumn.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoardColumn.ts index 5cd530706..ae273510c 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoardColumn.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoardColumn.ts @@ -7,13 +7,14 @@ import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils import { useSetRecordIdsForColumn } from '@/object-record/record-board/hooks/useSetRecordIdsForColumn'; import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies'; import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; -import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter'; +import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter'; import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState'; import { useRecordBoardRecordGqlFields } from '@/object-record/record-index/hooks/useRecordBoardRecordGqlFields'; import { recordIndexViewFilterGroupsState } from '@/object-record/record-index/states/recordIndexViewFilterGroupsState'; import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState'; import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { mapViewFilterGroupsToRecordFilterGroups } from '@/views/utils/mapViewFilterGroupsToRecordFilterGroups'; import { isDefined } from 'twenty-shared'; type UseLoadRecordIndexBoardProps = { @@ -43,6 +44,10 @@ export const useLoadRecordIndexBoardColumn = ({ recordIndexViewFilterGroupsState, ); + const recordFilterGroups = mapViewFilterGroupsToRecordFilterGroups( + recordIndexViewFilterGroups, + ); + const currentRecordFilters = useRecoilComponentValueV2( currentRecordFiltersComponentState, ); @@ -53,12 +58,12 @@ export const useLoadRecordIndexBoardColumn = ({ const { filterValueDependencies } = useFilterValueDependencies(); - const requestFilters = computeViewRecordGqlOperationFilter( + const requestFilters = computeRecordGqlOperationFilter({ filterValueDependencies, - currentRecordFilters, - objectMetadataItem?.fields ?? [], - recordIndexViewFilterGroups, - ); + recordFilters: currentRecordFilters, + recordFilterGroups, + fields: objectMetadataItem.fields, + }); const orderBy = turnSortsIntoOrderBy(objectMetadataItem, currentRecordSorts); diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexStates.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexStates.ts index 1bfc27209..3d3a9b0fd 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexStates.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexStates.ts @@ -18,7 +18,6 @@ import { useSetTableColumns } from '@/object-record/record-table/hooks/useSetTab import { viewFieldAggregateOperationState } from '@/object-record/record-table/record-table-footer/states/viewFieldAggregateOperationState'; import { tableFiltersComponentState } from '@/object-record/record-table/states/tableFiltersComponentState'; import { tableSortsComponentState } from '@/object-record/record-table/states/tableSortsComponentState'; -import { tableViewFilterGroupsComponentState } from '@/object-record/record-table/states/tableViewFilterGroupsComponentState'; import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition'; import { convertAggregateOperationToExtendedAggregateOperation } from '@/object-record/utils/convertAggregateOperationToExtendedAggregateOperation'; import { filterAvailableTableColumns } from '@/object-record/utils/filterAvailableTableColumns'; @@ -205,12 +204,7 @@ export const useLoadRecordIndexStates = () => { onViewFieldsChange(view.viewFields, objectMetadataItem, recordIndexId); onViewGroupsChange(view.viewGroups, objectMetadataItem, recordIndexId); - set( - tableViewFilterGroupsComponentState.atomFamily({ - instanceId: recordIndexId, - }), - view.viewFilterGroups ?? [], - ); + set( tableFiltersComponentState.atomFamily({ instanceId: recordIndexId, diff --git a/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateSoftDelete.tsx b/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateSoftDelete.tsx index 45d922334..42d761f8f 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateSoftDelete.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyStateSoftDelete.tsx @@ -36,7 +36,7 @@ export const RecordTableEmptyStateSoftDelete = () => { throw new Error('Deleted filter not found'); } - removeRecordFilter(deletedFilter.fieldMetadataId); + removeRecordFilter({ recordFilterId: deletedFilter.id }); toggleSoftDeleteFilterState(false); }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/hooks/useAggregateRecordsForHeader.ts b/packages/twenty-front/src/modules/object-record/record-table/hooks/useAggregateRecordsForHeader.ts index b85b14257..684b7bc4c 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/hooks/useAggregateRecordsForHeader.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/hooks/useAggregateRecordsForHeader.ts @@ -3,11 +3,12 @@ import { useAggregateRecords } from '@/object-record/hooks/useAggregateRecords'; import { buildRecordGqlFieldsAggregateForView } from '@/object-record/record-board/record-board-column/utils/buildRecordGqlFieldsAggregateForView'; import { computeAggregateValueAndLabel } from '@/object-record/record-board/record-board-column/utils/computeAggregateValueAndLabel'; import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies'; -import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter'; +import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter'; import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState'; import { recordIndexKanbanAggregateOperationState } from '@/object-record/record-index/states/recordIndexKanbanAggregateOperationState'; import { recordIndexViewFilterGroupsState } from '@/object-record/record-index/states/recordIndexViewFilterGroupsState'; import { UserContext } from '@/users/contexts/UserContext'; +import { mapViewFilterGroupsToRecordFilterGroups } from '@/views/utils/mapViewFilterGroupsToRecordFilterGroups'; import { useContext } from 'react'; import { useRecoilValue } from 'recoil'; import { isDefined } from 'twenty-shared'; @@ -26,6 +27,10 @@ export const useAggregateRecordsForHeader = ({ recordIndexViewFilterGroupsState, ); + const recordFilterGroups = mapViewFilterGroupsToRecordFilterGroups( + recordIndexViewFilterGroups, + ); + const recordIndexFilters = useRecoilValue(recordIndexFiltersState); const recordIndexKanbanAggregateOperation = useRecoilValue( @@ -36,12 +41,12 @@ export const useAggregateRecordsForHeader = ({ const { dateFormat, timeFormat, timeZone } = useContext(UserContext); - const requestFilters = computeViewRecordGqlOperationFilter( + const requestFilters = computeRecordGqlOperationFilter({ filterValueDependencies, - recordIndexFilters, - objectMetadataItem.fields, - recordIndexViewFilterGroups, - ); + recordFilters: recordIndexFilters, + recordFilterGroups, + fields: objectMetadataItem.fields, + }); const recordGqlFieldsAggregate = buildRecordGqlFieldsAggregateForView({ objectMetadataItem, diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/hooks/useAggregateRecordsForRecordTableColumnFooter.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/hooks/useAggregateRecordsForRecordTableColumnFooter.tsx index 07ab95fbf..1c481becc 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/hooks/useAggregateRecordsForRecordTableColumnFooter.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-footer/hooks/useAggregateRecordsForRecordTableColumnFooter.tsx @@ -2,7 +2,7 @@ import { useAggregateRecords } from '@/object-record/hooks/useAggregateRecords'; import { computeAggregateValueAndLabel } from '@/object-record/record-board/record-board-column/utils/computeAggregateValueAndLabel'; import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies'; import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; -import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter'; +import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter'; import { useRecordGroupFilter } from '@/object-record/record-group/hooks/useRecordGroupFilter'; import { recordIndexViewFilterGroupsState } from '@/object-record/record-index/states/recordIndexViewFilterGroupsState'; import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations'; @@ -13,6 +13,7 @@ import { ExtendedAggregateOperations } from '@/object-record/record-table/types/ import { convertAggregateOperationToExtendedAggregateOperation } from '@/object-record/utils/convertAggregateOperationToExtendedAggregateOperation'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { UserContext } from '@/users/contexts/UserContext'; +import { mapViewFilterGroupsToRecordFilterGroups } from '@/views/utils/mapViewFilterGroupsToRecordFilterGroups'; import { useContext } from 'react'; import { useRecoilValue } from 'recoil'; import { isDefined, isFieldMetadataDateKind } from 'twenty-shared'; @@ -33,13 +34,17 @@ export const useAggregateRecordsForRecordTableColumnFooter = ( const { filterValueDependencies } = useFilterValueDependencies(); - const requestFilters = computeViewRecordGqlOperationFilter( - filterValueDependencies, - currentRecordFilters, - objectMetadataItem.fields, + const recordFilterGroups = mapViewFilterGroupsToRecordFilterGroups( recordIndexViewFilterGroups, ); + const requestFilters = computeRecordGqlOperationFilter({ + fields: objectMetadataItem.fields, + filterValueDependencies, + recordFilterGroups, + recordFilters: currentRecordFilters, + }); + const { viewFieldId } = useContext( RecordTableColumnAggregateFooterCellContext, ); diff --git a/packages/twenty-front/src/modules/object-record/record-table/states/tableViewFilterGroupsComponentState.ts b/packages/twenty-front/src/modules/object-record/record-table/states/tableViewFilterGroupsComponentState.ts deleted file mode 100644 index c3f13977b..000000000 --- a/packages/twenty-front/src/modules/object-record/record-table/states/tableViewFilterGroupsComponentState.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; -import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; -import { ViewFilterGroup } from '@/views/types/ViewFilterGroup'; - -export const tableViewFilterGroupsComponentState = createComponentStateV2< - ViewFilterGroup[] ->({ - key: 'tableViewFilterGroupsComponentState', - defaultValue: [], - componentInstanceContext: RecordTableComponentInstanceContext, -}); diff --git a/packages/twenty-front/src/modules/views/components/AdvancedFilterDropdownButton.tsx b/packages/twenty-front/src/modules/views/components/AdvancedFilterDropdownButton.tsx index 9cdcf427f..d40f7ba1e 100644 --- a/packages/twenty-front/src/modules/views/components/AdvancedFilterDropdownButton.tsx +++ b/packages/twenty-front/src/modules/views/components/AdvancedFilterDropdownButton.tsx @@ -29,10 +29,6 @@ export const AdvancedFilterDropdownButton = () => { const { removeRecordFilter } = useRemoveRecordFilter(); - const handleDropdownClickOutside = useCallback(() => {}, []); - - const handleDropdownClose = () => {}; - const removeAdvancedFilter = useCallback(async () => { if (!advancedRecordFilterIds) { throw new Error('No advanced view filters to remove'); @@ -48,7 +44,7 @@ export const AdvancedFilterDropdownButton = () => { } for (const recordFilterId of advancedRecordFilterIds) { - removeRecordFilter(recordFilterId); + removeRecordFilter({ recordFilterId }); } }, [ advancedRecordFilterIds, @@ -83,8 +79,6 @@ export const AdvancedFilterDropdownButton = () => { dropdownOffset={{ y: 8, x: 0 }} dropdownPlacement="bottom-start" dropdownMenuWidth={800} - onClickOutside={handleDropdownClickOutside} - onClose={handleDropdownClose} /> ); }; diff --git a/packages/twenty-front/src/modules/views/components/EditableFilterDropdownButton.tsx b/packages/twenty-front/src/modules/views/components/EditableFilterDropdownButton.tsx index 264314cc1..461b061df 100644 --- a/packages/twenty-front/src/modules/views/components/EditableFilterDropdownButton.tsx +++ b/packages/twenty-front/src/modules/views/components/EditableFilterDropdownButton.tsx @@ -29,11 +29,11 @@ export const EditableFilterDropdownButton = ({ const handleRemove = () => { closeDropdown(); - removeRecordFilter(viewFilter.fieldMetadataId); + removeRecordFilter({ recordFilterId: viewFilter.id }); }; const handleDropdownClickOutside = useCallback(() => { - const { value, operand, fieldMetadataId } = viewFilter; + const { value, operand } = viewFilter; if ( !value && ![ @@ -44,7 +44,7 @@ export const EditableFilterDropdownButton = ({ RecordFilterOperand.IsToday, ].includes(operand) ) { - removeRecordFilter(fieldMetadataId); + removeRecordFilter({ recordFilterId: viewFilter.id }); } }, [viewFilter, removeRecordFilter]); diff --git a/packages/twenty-front/src/modules/views/components/RecordFilterChip.tsx b/packages/twenty-front/src/modules/views/components/RecordFilterChip.tsx index 15b8c9d2a..9c5382236 100644 --- a/packages/twenty-front/src/modules/views/components/RecordFilterChip.tsx +++ b/packages/twenty-front/src/modules/views/components/RecordFilterChip.tsx @@ -21,7 +21,7 @@ export const RecordFilterChip = ({ recordFilter }: RecordFilterChipProps) => { const FieldMetadataItemIcon = getIcon(fieldMetadataItem.icon); const handleRemoveClick = () => { - removeRecordFilter(recordFilter.fieldMetadataId); + removeRecordFilter({ recordFilterId: recordFilter.id }); }; return ( diff --git a/packages/twenty-front/src/modules/views/components/SoftDeleteFilterChip.tsx b/packages/twenty-front/src/modules/views/components/SoftDeleteFilterChip.tsx index 94172dfb8..c49fdd3b0 100644 --- a/packages/twenty-front/src/modules/views/components/SoftDeleteFilterChip.tsx +++ b/packages/twenty-front/src/modules/views/components/SoftDeleteFilterChip.tsx @@ -25,7 +25,7 @@ export const SoftDeleteFilterChip = ({ const { getIcon } = useIcons(); const handleRemoveClick = () => { - removeRecordFilter(recordFilter.fieldMetadataId); + removeRecordFilter({ recordFilterId: recordFilter.id }); setIsSoftDeleteFilterActive(false); }; diff --git a/packages/twenty-front/src/modules/views/hooks/internal/usePersistViewFilterRecords.ts b/packages/twenty-front/src/modules/views/hooks/internal/usePersistViewFilterRecords.ts index 03c915204..38cda8932 100644 --- a/packages/twenty-front/src/modules/views/hooks/internal/usePersistViewFilterRecords.ts +++ b/packages/twenty-front/src/modules/views/hooks/internal/usePersistViewFilterRecords.ts @@ -14,7 +14,6 @@ import { useUpdateOneRecordMutation } from '@/object-record/hooks/useUpdateOneRe import { GraphQLView } from '@/views/types/GraphQLView'; import { ViewFilter } from '@/views/types/ViewFilter'; import { isDefined } from 'twenty-shared'; -import { v4 } from 'uuid'; export const usePersistViewFilterRecords = () => { const { objectMetadataItem } = useObjectMetadataItem({ @@ -51,7 +50,7 @@ export const usePersistViewFilterRecords = () => { mutation: createOneRecordMutation, variables: { input: { - id: v4(), + id: viewFilter.id, fieldMetadataId: viewFilter.fieldMetadataId, viewId: view.id, value: viewFilter.value, diff --git a/packages/twenty-front/src/modules/views/hooks/internal/usePersistViewSortRecords.ts b/packages/twenty-front/src/modules/views/hooks/internal/usePersistViewSortRecords.ts index 7ef3948a8..066c33651 100644 --- a/packages/twenty-front/src/modules/views/hooks/internal/usePersistViewSortRecords.ts +++ b/packages/twenty-front/src/modules/views/hooks/internal/usePersistViewSortRecords.ts @@ -52,6 +52,7 @@ export const usePersistViewSortRecords = () => { fieldMetadataId: viewSort.fieldMetadataId, viewId: view.id, direction: viewSort.direction, + id: viewSort.id, }, }, update: (cache, { data }) => { diff --git a/packages/twenty-front/src/modules/views/hooks/useChangeView.ts b/packages/twenty-front/src/modules/views/hooks/useChangeView.ts index e12d0f7b7..18bb2b230 100644 --- a/packages/twenty-front/src/modules/views/hooks/useChangeView.ts +++ b/packages/twenty-front/src/modules/views/hooks/useChangeView.ts @@ -3,7 +3,7 @@ import { useSetViewInUrl } from '@/views/hooks/useSetViewInUrl'; export const useChangeView = () => { const { setViewInUrl } = useSetViewInUrl(); - const changeView = async (viewId: string) => { + const changeView = (viewId: string) => { setViewInUrl(viewId); }; diff --git a/packages/twenty-front/src/modules/views/hooks/useSaveRecordFilterGroupsToViewFilterGroups.ts b/packages/twenty-front/src/modules/views/hooks/useSaveRecordFilterGroupsToViewFilterGroups.ts index 17aa248ec..7e5478ae4 100644 --- a/packages/twenty-front/src/modules/views/hooks/useSaveRecordFilterGroupsToViewFilterGroups.ts +++ b/packages/twenty-front/src/modules/views/hooks/useSaveRecordFilterGroupsToViewFilterGroups.ts @@ -59,8 +59,8 @@ export const useSaveRecordFilterGroupsToViewFilterGroups = () => { newViewFilterGroups, ); - const viewFilterIdsToDelete = viewFilterGroupsToDelete.map( - (viewFilter) => viewFilter.id, + const viewFilterGroupIdsToDelete = viewFilterGroupsToDelete.map( + (viewFilterGroup) => viewFilterGroup.id, ); await createViewFilterGroupRecords( @@ -68,7 +68,7 @@ export const useSaveRecordFilterGroupsToViewFilterGroups = () => { currentView, ); await updateViewFilterGroupRecords(viewFilterGroupsToUpdate); - await deleteViewFilterGroupRecords(viewFilterIdsToDelete); + await deleteViewFilterGroupRecords(viewFilterGroupIdsToDelete); }, [ createViewFilterGroupRecords, diff --git a/packages/twenty-front/src/modules/views/utils/__tests__/getViewFiltersToCreate.test.ts b/packages/twenty-front/src/modules/views/utils/__tests__/getViewFiltersToCreate.test.ts index 5b87e142e..66d3ee231 100644 --- a/packages/twenty-front/src/modules/views/utils/__tests__/getViewFiltersToCreate.test.ts +++ b/packages/twenty-front/src/modules/views/utils/__tests__/getViewFiltersToCreate.test.ts @@ -17,8 +17,11 @@ describe('getViewFiltersToCreate', () => { it('should return all filters when current filters array is empty', () => { const currentViewFilters: ViewFilter[] = []; const newViewFilters: ViewFilter[] = [ - { ...baseFilter }, - { ...baseFilter, id: 'filter-2', fieldMetadataId: 'field-2' }, + { ...baseFilter } satisfies ViewFilter, + { + ...baseFilter, + id: 'filter-2', + } satisfies ViewFilter, ]; const result = getViewFiltersToCreate(currentViewFilters, newViewFilters); @@ -40,8 +43,7 @@ describe('getViewFiltersToCreate', () => { const newFilterWithDifferentFieldMetadata = { ...baseFilter, id: 'filter-2', - fieldMetadataId: 'field-2', - }; + } satisfies ViewFilter; const currentViewFilters: ViewFilter[] = [existingFilter]; @@ -55,25 +57,6 @@ describe('getViewFiltersToCreate', () => { expect(result).toEqual([newFilterWithDifferentFieldMetadata]); }); - it('should handle filters with different viewFilterGroupIds', () => { - const existingFilter = { ...baseFilter }; - const filterWithDifferentGroup = { - ...baseFilter, - viewFilterGroupId: 'group-2', - }; - - const currentViewFilters: ViewFilter[] = [existingFilter]; - - const newViewFilters: ViewFilter[] = [ - existingFilter, - filterWithDifferentGroup, - ]; - - const result = getViewFiltersToCreate(currentViewFilters, newViewFilters); - - expect(result).toEqual([filterWithDifferentGroup]); - }); - it('should handle empty arrays for both inputs', () => { const currentViewFilters: ViewFilter[] = []; const newViewFilters: ViewFilter[] = []; @@ -82,36 +65,4 @@ describe('getViewFiltersToCreate', () => { expect(result).toEqual([]); }); - - it('should consider filters with same fieldMetadataId but different viewFilterGroupId as new', () => { - const currentViewFilters: ViewFilter[] = [baseFilter]; - const filterWithSameFieldMetadataIdButDifferentGroup = { - ...baseFilter, - id: 'filter-2', - viewFilterGroupId: 'group-2', - }; - const newViewFilters: ViewFilter[] = [ - filterWithSameFieldMetadataIdButDifferentGroup, - ]; - - const result = getViewFiltersToCreate(currentViewFilters, newViewFilters); - - expect(result).toEqual([filterWithSameFieldMetadataIdButDifferentGroup]); - }); - - it('should consider filters with same viewFilterGroupId but different fieldMetadataId as new', () => { - const currentViewFilters: ViewFilter[] = [baseFilter]; - const filterWithSameGroupButDifferentFieldMetadata = { - ...baseFilter, - id: 'filter-2', - fieldMetadataId: 'field-2', - }; - const newViewFilters: ViewFilter[] = [ - filterWithSameGroupButDifferentFieldMetadata, - ]; - - const result = getViewFiltersToCreate(currentViewFilters, newViewFilters); - - expect(result).toEqual([filterWithSameGroupButDifferentFieldMetadata]); - }); }); diff --git a/packages/twenty-front/src/modules/views/utils/__tests__/getViewFiltersToDelete.test.ts b/packages/twenty-front/src/modules/views/utils/__tests__/getViewFiltersToDelete.test.ts index 0440b5236..07d3bee0e 100644 --- a/packages/twenty-front/src/modules/views/utils/__tests__/getViewFiltersToDelete.test.ts +++ b/packages/twenty-front/src/modules/views/utils/__tests__/getViewFiltersToDelete.test.ts @@ -38,8 +38,7 @@ describe('getViewFiltersToDelete', () => { const filterToKeep = { ...baseFilter, id: 'filter-2', - fieldMetadataId: 'field-2', - }; + } satisfies ViewFilter; const currentViewFilters: ViewFilter[] = [filterToDelete, filterToKeep]; const newViewFilters: ViewFilter[] = [filterToKeep]; @@ -57,43 +56,4 @@ describe('getViewFiltersToDelete', () => { expect(result).toEqual([]); }); - - it('should identify filters to delete based on fieldMetadataId and viewFilterGroupId', () => { - const filterInGroup1 = { ...baseFilter }; - const filterInGroup2 = { - ...baseFilter, - viewFilterGroupId: 'group-2', - }; - const filterWithDifferentField = { - ...baseFilter, - fieldMetadataId: 'field-2', - }; - - const currentViewFilters: ViewFilter[] = [ - filterInGroup1, - filterInGroup2, - filterWithDifferentField, - ]; - const newViewFilters: ViewFilter[] = [filterInGroup1]; - - const result = getViewFiltersToDelete(currentViewFilters, newViewFilters); - - expect(result).toEqual([filterInGroup2, filterWithDifferentField]); - }); - - it('should not delete filters that match in both fieldMetadataId and viewFilterGroupId', () => { - const existingFilter = { ...baseFilter }; - const matchingFilter = { - ...baseFilter, - value: 'different-value', - displayValue: 'different-value', - }; - - const currentViewFilters: ViewFilter[] = [existingFilter]; - const newViewFilters: ViewFilter[] = [matchingFilter]; - - const result = getViewFiltersToDelete(currentViewFilters, newViewFilters); - - expect(result).toEqual([]); - }); }); diff --git a/packages/twenty-front/src/modules/views/utils/__tests__/getViewFiltersToUpdate.test.ts b/packages/twenty-front/src/modules/views/utils/__tests__/getViewFiltersToUpdate.test.ts index 275f0d6c0..8bdc845b9 100644 --- a/packages/twenty-front/src/modules/views/utils/__tests__/getViewFiltersToUpdate.test.ts +++ b/packages/twenty-front/src/modules/views/utils/__tests__/getViewFiltersToUpdate.test.ts @@ -33,12 +33,12 @@ describe('getViewFiltersToUpdate', () => { }); it('should return filters that exist in both arrays but have different values', () => { - const existingFilter = { ...baseFilter }; + const existingFilter = { ...baseFilter } satisfies ViewFilter; const updatedFilter = { ...baseFilter, value: 'updated-value', displayValue: 'updated-value', - }; + } satisfies ViewFilter; const currentViewFilters: ViewFilter[] = [existingFilter]; const newViewFilters: ViewFilter[] = [updatedFilter]; @@ -49,8 +49,8 @@ describe('getViewFiltersToUpdate', () => { }); it('should not return filters that exist in both arrays with same values', () => { - const existingFilter = { ...baseFilter }; - const sameFilter = { ...baseFilter }; + const existingFilter = { ...baseFilter } satisfies ViewFilter; + const sameFilter = { ...baseFilter } satisfies ViewFilter; const currentViewFilters: ViewFilter[] = [existingFilter]; const newViewFilters: ViewFilter[] = [sameFilter]; @@ -69,44 +69,12 @@ describe('getViewFiltersToUpdate', () => { expect(result).toEqual([]); }); - it('should not update filters with same fieldMetadataId but different viewFilterGroupId', () => { - const existingFilter = { ...baseFilter }; - const filterInDifferentGroup = { - ...baseFilter, - viewFilterGroupId: 'group-2', - value: 'updated-value', - }; - - const currentViewFilters: ViewFilter[] = [existingFilter]; - const newViewFilters: ViewFilter[] = [filterInDifferentGroup]; - - const result = getViewFiltersToUpdate(currentViewFilters, newViewFilters); - - expect(result).toEqual([]); - }); - - it('should not update filters with same viewFilterGroupId but different fieldMetadataId', () => { - const existingFilter = { ...baseFilter }; - const filterWithDifferentField = { - ...baseFilter, - fieldMetadataId: 'field-2', - value: 'updated-value', - }; - - const currentViewFilters: ViewFilter[] = [existingFilter]; - const newViewFilters: ViewFilter[] = [filterWithDifferentField]; - - const result = getViewFiltersToUpdate(currentViewFilters, newViewFilters); - - expect(result).toEqual([]); - }); - it('should update filter when operand changes', () => { - const existingFilter = { ...baseFilter }; + const existingFilter = { ...baseFilter } satisfies ViewFilter; const filterWithNewOperand = { ...baseFilter, operand: ViewFilterOperand.DoesNotContain, - }; + } satisfies ViewFilter; const currentViewFilters: ViewFilter[] = [existingFilter]; const newViewFilters: ViewFilter[] = [filterWithNewOperand]; @@ -117,11 +85,11 @@ describe('getViewFiltersToUpdate', () => { }); it('should update filter when position changes', () => { - const existingFilter = { ...baseFilter }; + const existingFilter = { ...baseFilter } satisfies ViewFilter; const filterWithNewPosition = { ...baseFilter, positionInViewFilterGroup: 1, - }; + } satisfies ViewFilter; const currentViewFilters: ViewFilter[] = [existingFilter]; const newViewFilters: ViewFilter[] = [filterWithNewPosition]; diff --git a/packages/twenty-front/src/modules/views/utils/getQueryVariablesFromView.ts b/packages/twenty-front/src/modules/views/utils/getQueryVariablesFromView.ts index d67978a5e..49457a24f 100644 --- a/packages/twenty-front/src/modules/views/utils/getQueryVariablesFromView.ts +++ b/packages/twenty-front/src/modules/views/utils/getQueryVariablesFromView.ts @@ -4,8 +4,9 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy'; import { RecordFilterValueDependencies } from '@/object-record/record-filter/types/RecordFilterValueDependencies'; -import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter'; +import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter'; import { View } from '@/views/types/View'; +import { mapViewFilterGroupsToRecordFilterGroups } from '@/views/utils/mapViewFilterGroupsToRecordFilterGroups'; import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters'; import { mapViewSortsToSorts } from '@/views/utils/mapViewSortsToSorts'; import { isDefined } from 'twenty-shared'; @@ -30,13 +31,22 @@ export const getQueryVariablesFromView = ({ const { viewFilterGroups, viewFilters, viewSorts } = view; - const filter = computeViewRecordGqlOperationFilter( - filterValueDependencies, - mapViewFiltersToFilters(viewFilters, fieldMetadataItems), - objectMetadataItem?.fields ?? [], + const recordFilterGroups = mapViewFilterGroupsToRecordFilterGroups( viewFilterGroups ?? [], ); + const recordFilters = mapViewFiltersToFilters( + viewFilters, + fieldMetadataItems, + ); + + const filter = computeRecordGqlOperationFilter({ + fields: objectMetadataItem?.fields ?? [], + filterValueDependencies, + recordFilterGroups, + recordFilters, + }); + const orderBy = turnSortsIntoOrderBy( objectMetadataItem, mapViewSortsToSorts(viewSorts), diff --git a/packages/twenty-front/src/modules/views/utils/getViewFiltersToCreate.ts b/packages/twenty-front/src/modules/views/utils/getViewFiltersToCreate.ts index aff45654a..cfc803f78 100644 --- a/packages/twenty-front/src/modules/views/utils/getViewFiltersToCreate.ts +++ b/packages/twenty-front/src/modules/views/utils/getViewFiltersToCreate.ts @@ -1,5 +1,6 @@ import { ViewFilter } from '@/views/types/ViewFilter'; import { isDefined } from 'twenty-shared'; +import { compareStrictlyExceptForNullAndUndefined } from '~/utils/compareStrictlyExceptForNullAndUndefined'; export const getViewFiltersToCreate = ( currentViewFilters: ViewFilter[], @@ -8,8 +9,10 @@ export const getViewFiltersToCreate = ( return newViewFilters.filter((newViewFilter) => { const correspondingViewFilter = currentViewFilters.find( (currentViewFilter) => - currentViewFilter.fieldMetadataId === newViewFilter.fieldMetadataId && - currentViewFilter.viewFilterGroupId === newViewFilter.viewFilterGroupId, + compareStrictlyExceptForNullAndUndefined( + currentViewFilter.id, + newViewFilter.id, + ), ); const shouldCreateBecauseViewFilterIsNew = !isDefined( diff --git a/packages/twenty-front/src/modules/views/utils/getViewFiltersToDelete.ts b/packages/twenty-front/src/modules/views/utils/getViewFiltersToDelete.ts index d6ee34cff..59de70b93 100644 --- a/packages/twenty-front/src/modules/views/utils/getViewFiltersToDelete.ts +++ b/packages/twenty-front/src/modules/views/utils/getViewFiltersToDelete.ts @@ -1,4 +1,5 @@ import { ViewFilter } from '@/views/types/ViewFilter'; +import { compareStrictlyExceptForNullAndUndefined } from '~/utils/compareStrictlyExceptForNullAndUndefined'; export const getViewFiltersToDelete = ( currentViewFilters: ViewFilter[], @@ -6,11 +7,11 @@ export const getViewFiltersToDelete = ( ) => { return currentViewFilters.filter( (currentViewFilter) => - !newViewFilters.some( - (newViewFilter) => - newViewFilter.fieldMetadataId === currentViewFilter.fieldMetadataId && - newViewFilter.viewFilterGroupId === - currentViewFilter.viewFilterGroupId, + !newViewFilters.some((newViewFilter) => + compareStrictlyExceptForNullAndUndefined( + currentViewFilter.id, + newViewFilter.id, + ), ), ); }; diff --git a/packages/twenty-front/src/modules/views/utils/getViewFiltersToUpdate.ts b/packages/twenty-front/src/modules/views/utils/getViewFiltersToUpdate.ts index b0776841e..ebf34e1b0 100644 --- a/packages/twenty-front/src/modules/views/utils/getViewFiltersToUpdate.ts +++ b/packages/twenty-front/src/modules/views/utils/getViewFiltersToUpdate.ts @@ -1,6 +1,7 @@ import { ViewFilter } from '@/views/types/ViewFilter'; import { areViewFiltersEqual } from '@/views/utils/areViewFiltersEqual'; import { isDefined } from 'twenty-shared'; +import { compareStrictlyExceptForNullAndUndefined } from '~/utils/compareStrictlyExceptForNullAndUndefined'; export const getViewFiltersToUpdate = ( currentViewFilters: ViewFilter[], @@ -9,8 +10,10 @@ export const getViewFiltersToUpdate = ( return newViewFilters.filter((newViewFilter) => { const correspondingViewFilter = currentViewFilters.find( (currentViewFilter) => - currentViewFilter.fieldMetadataId === newViewFilter.fieldMetadataId && - currentViewFilter.viewFilterGroupId === newViewFilter.viewFilterGroupId, + compareStrictlyExceptForNullAndUndefined( + currentViewFilter.id, + newViewFilter.id, + ), ); if (!isDefined(correspondingViewFilter)) {