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 3573d8367..a81a0d873 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 @@ -2,6 +2,9 @@ import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMeta import { availableFieldMetadataItemsForFilterFamilySelector } from '@/object-metadata/states/availableFieldMetadataItemsForFilterFamilySelector'; import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; import { useUpsertCombinedViewFilterGroup } from '@/object-record/advanced-filter/hooks/useUpsertCombinedViewFilterGroup'; +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'; import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter'; import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; @@ -31,6 +34,8 @@ export const AdvancedFilterAddFilterRuleSelect = ({ const { currentViewId } = useGetCurrentView(); const { upsertCombinedViewFilterGroup } = useUpsertCombinedViewFilterGroup(); + const { upsertRecordFilterGroup } = useUpsertRecordFilterGroup(); + const { upsertRecordFilter } = useUpsertRecordFilter(); const newPositionInViewFilterGroup = lastChildPosition + 1; @@ -108,15 +113,26 @@ export const AdvancedFilterAddFilterRuleSelect = ({ throw new Error('Missing view id'); } - const newViewFilterGroup = { - id: v4(), + const newRecordFilterGroupId = v4(); + + const newViewFilterGroup: ViewFilterGroup = { + __typename: 'ViewFilterGroup', + id: newRecordFilterGroupId, viewId: currentViewId, logicalOperator: ViewFilterGroupLogicalOperator.AND, parentViewFilterGroupId: viewFilterGroup.id, positionInViewFilterGroup: newPositionInViewFilterGroup, }; + const newRecordFilterGroup: RecordFilterGroup = { + id: newRecordFilterGroupId, + logicalOperator: RecordFilterGroupLogicalOperator.AND, + parentRecordFilterGroupId: viewFilterGroup.id, + positionInRecordFilterGroup: newPositionInViewFilterGroup, + }; + upsertCombinedViewFilterGroup(newViewFilterGroup); + upsertRecordFilterGroup(newRecordFilterGroup); const defaultFieldMetadataItem = getDefaultFieldMetadataItem(); diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterLogicalOperatorDropdown.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterLogicalOperatorDropdown.tsx index 80d0877d0..c6ffcfd58 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterLogicalOperatorDropdown.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterLogicalOperatorDropdown.tsx @@ -1,5 +1,7 @@ import { ADVANCED_FILTER_LOGICAL_OPERATOR_OPTIONS } from '@/object-record/advanced-filter/constants/AdvancedFilterLogicalOperatorOptions'; import { useUpsertCombinedViewFilterGroup } from '@/object-record/advanced-filter/hooks/useUpsertCombinedViewFilterGroup'; +import { useUpsertRecordFilterGroup } from '@/object-record/record-filter-group/hooks/useUpsertRecordFilterGroup'; +import { RecordFilterGroupLogicalOperator } from '@/object-record/record-filter-group/types/RecordFilterGroupLogicalOperator'; import { Select } from '@/ui/input/components/Select'; import { ViewFilterGroup } from '@/views/types/ViewFilterGroup'; import { ViewFilterGroupLogicalOperator } from '@/views/types/ViewFilterGroupLogicalOperator'; @@ -12,12 +14,23 @@ export const AdvancedFilterLogicalOperatorDropdown = ({ viewFilterGroup, }: AdvancedFilterLogicalOperatorDropdownProps) => { const { upsertCombinedViewFilterGroup } = useUpsertCombinedViewFilterGroup(); + const { upsertRecordFilterGroup } = useUpsertRecordFilterGroup(); const handleChange = (value: ViewFilterGroupLogicalOperator) => { upsertCombinedViewFilterGroup({ ...viewFilterGroup, logicalOperator: value, }); + + upsertRecordFilterGroup({ + id: viewFilterGroup.id, + parentRecordFilterGroupId: viewFilterGroup.parentViewFilterGroupId, + positionInRecordFilterGroup: viewFilterGroup.positionInViewFilterGroup, + logicalOperator: + value === ViewFilterGroupLogicalOperator.AND + ? RecordFilterGroupLogicalOperator.AND + : RecordFilterGroupLogicalOperator.OR, + }); }; return ( 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 2c18e3fb3..9b0d08d5b 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 @@ -2,6 +2,7 @@ import { AdvancedFilterRuleOptionsDropdownButton } from '@/object-record/advance import { useCurrentViewViewFilterGroup } from '@/object-record/advanced-filter/hooks/useCurrentViewViewFilterGroup'; import { useDeleteCombinedViewFilterGroup } from '@/object-record/advanced-filter/hooks/useDeleteCombinedViewFilterGroup'; +import { useRemoveRecordFilterGroup } from '@/object-record/record-filter-group/hooks/useRemoveRecordFilterGroup'; import { useRemoveRecordFilter } from '@/object-record/record-filter/hooks/useRemoveRecordFilter'; import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; @@ -30,6 +31,7 @@ export const AdvancedFilterRuleOptionsDropdown = ({ const { removeRecordFilter } = useRemoveRecordFilter(); const { deleteCombinedViewFilterGroup } = useDeleteCombinedViewFilterGroup(); + const { removeRecordFilterGroup } = useRemoveRecordFilterGroup(); const { currentViewFilterGroup, childViewFiltersAndViewFilterGroups } = useCurrentViewViewFilterGroup({ @@ -56,9 +58,11 @@ export const AdvancedFilterRuleOptionsDropdown = ({ isDefined(currentRecordFilter?.viewFilterGroupId) ) { deleteCombinedViewFilterGroup(currentRecordFilter.viewFilterGroupId); + removeRecordFilterGroup(currentRecordFilter.viewFilterGroupId); } } else if (isDefined(currentViewFilterGroup)) { deleteCombinedViewFilterGroup(currentViewFilterGroup.id); + removeRecordFilterGroup(currentViewFilterGroup.id); // TODO: This is a temporary fix view filter group will be removed soon. const childViewFilters = childViewFiltersAndViewFilterGroups.filter( 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 5302570c3..81c207b9a 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 @@ -3,6 +3,8 @@ import { availableFieldMetadataItemsForFilterFamilySelector } from '@/object-met import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; import { useUpsertCombinedViewFilterGroup } from '@/object-record/advanced-filter/hooks/useUpsertCombinedViewFilterGroup'; import { OBJECT_FILTER_DROPDOWN_ID } from '@/object-record/object-filter-dropdown/constants/ObjectFilterDropdownId'; +import { useUpsertRecordFilterGroup } from '@/object-record/record-filter-group/hooks/useUpsertRecordFilterGroup'; +import { RecordFilterGroupLogicalOperator } from '@/object-record/record-filter-group/types/RecordFilterGroupLogicalOperator'; import { useUpsertRecordFilter } from '@/object-record/record-filter/hooks/useUpsertRecordFilter'; import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands'; @@ -58,6 +60,7 @@ export const AdvancedFilterButton = () => { useGetCurrentView(); const { upsertCombinedViewFilterGroup } = useUpsertCombinedViewFilterGroup(); + const { upsertRecordFilterGroup } = useUpsertRecordFilterGroup(); const { upsertRecordFilter } = useUpsertRecordFilter(); @@ -96,6 +99,11 @@ export const AdvancedFilterButton = () => { upsertCombinedViewFilterGroup(newViewFilterGroup); + upsertRecordFilterGroup({ + id: newViewFilterGroup.id, + logicalOperator: RecordFilterGroupLogicalOperator.AND, + }); + const defaultFieldMetadataItem = availableFieldMetadataItemsForFilter.find( (fieldMetadataItem) => diff --git a/packages/twenty-front/src/modules/object-record/record-filter-group/hooks/__tests__/useRemoveRecordFilterGroup.test.tsx b/packages/twenty-front/src/modules/object-record/record-filter-group/hooks/__tests__/useRemoveRecordFilterGroup.test.tsx new file mode 100644 index 000000000..7a4b9fb17 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-filter-group/hooks/__tests__/useRemoveRecordFilterGroup.test.tsx @@ -0,0 +1,104 @@ +import { renderHook } from '@testing-library/react'; + +import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState'; +import { RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup'; +import { RecordFilterGroupLogicalOperator } from '@/object-record/record-filter-group/types/RecordFilterGroupLogicalOperator'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { act } from 'react'; +import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper'; +import { useRemoveRecordFilterGroup } from '../useRemoveRecordFilterGroup'; +import { useUpsertRecordFilterGroup } from '../useUpsertRecordFilterGroup'; + +const Wrapper = getJestMetadataAndApolloMocksWrapper({ + apolloMocks: [], +}); + +describe('useRemoveRecordFilterGroup', () => { + it('should remove an existing record filter group', () => { + const { result } = renderHook( + () => { + const currentRecordFilterGroups = useRecoilComponentValueV2( + currentRecordFilterGroupsComponentState, + ); + + const { upsertRecordFilterGroup } = useUpsertRecordFilterGroup(); + const { removeRecordFilterGroup } = useRemoveRecordFilterGroup(); + + return { + upsertRecordFilterGroup, + removeRecordFilterGroup, + currentRecordFilterGroups, + }; + }, + { + wrapper: Wrapper, + }, + ); + + const mockRecordFilterGroup: RecordFilterGroup = { + id: 'record-filter-group-1', + logicalOperator: RecordFilterGroupLogicalOperator.AND, + parentRecordFilterGroupId: null, + }; + + act(() => { + result.current.upsertRecordFilterGroup(mockRecordFilterGroup); + }); + + expect(result.current.currentRecordFilterGroups).toHaveLength(1); + expect(result.current.currentRecordFilterGroups[0]).toEqual( + mockRecordFilterGroup, + ); + + act(() => { + result.current.removeRecordFilterGroup(mockRecordFilterGroup.id); + }); + + expect(result.current.currentRecordFilterGroups).toHaveLength(0); + }); + + it('should not modify record filter groups when trying to remove a non-existent record filter group', () => { + const { result } = renderHook( + () => { + const currentRecordFilterGroups = useRecoilComponentValueV2( + currentRecordFilterGroupsComponentState, + ); + + const { upsertRecordFilterGroup } = useUpsertRecordFilterGroup(); + const { removeRecordFilterGroup } = useRemoveRecordFilterGroup(); + + return { + upsertRecordFilterGroup, + removeRecordFilterGroup, + currentRecordFilterGroups, + }; + }, + { + wrapper: Wrapper, + }, + ); + + const mockRecordFilterGroup: RecordFilterGroup = { + id: 'record-filter-group-1', + logicalOperator: RecordFilterGroupLogicalOperator.AND, + parentRecordFilterGroupId: null, + }; + + act(() => { + result.current.upsertRecordFilterGroup(mockRecordFilterGroup); + }); + + expect(result.current.currentRecordFilterGroups).toHaveLength(1); + + act(() => { + result.current.removeRecordFilterGroup( + 'non-existent-record-filter-group-id', + ); + }); + + expect(result.current.currentRecordFilterGroups).toHaveLength(1); + expect(result.current.currentRecordFilterGroups[0]).toEqual( + mockRecordFilterGroup, + ); + }); +}); diff --git a/packages/twenty-front/src/modules/object-record/record-filter-group/hooks/__tests__/useUpsertRecordFilterGroup.test.tsx b/packages/twenty-front/src/modules/object-record/record-filter-group/hooks/__tests__/useUpsertRecordFilterGroup.test.tsx new file mode 100644 index 000000000..36c138d3d --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-filter-group/hooks/__tests__/useUpsertRecordFilterGroup.test.tsx @@ -0,0 +1,94 @@ +import { renderHook } from '@testing-library/react'; +import { act } from 'react'; + +import { useUpsertRecordFilterGroup } from '@/object-record/record-filter-group/hooks/useUpsertRecordFilterGroup'; +import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState'; +import { RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup'; +import { RecordFilterGroupLogicalOperator } from '@/object-record/record-filter-group/types/RecordFilterGroupLogicalOperator'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper'; + +const Wrapper = getJestMetadataAndApolloMocksWrapper({ + apolloMocks: [], +}); + +describe('useUpsertRecordFilterGroup', () => { + it('should add a new record filter group', () => { + const { result } = renderHook( + () => { + const currentRecordFilterGroups = useRecoilComponentValueV2( + currentRecordFilterGroupsComponentState, + ); + + const { upsertRecordFilterGroup } = useUpsertRecordFilterGroup(); + + return { upsertRecordFilterGroup, currentRecordFilterGroups }; + }, + { + wrapper: Wrapper, + }, + ); + + const mockRecordFilterGroup: RecordFilterGroup = { + id: 'record-filter-group-1', + logicalOperator: RecordFilterGroupLogicalOperator.AND, + parentRecordFilterGroupId: null, + }; + + act(() => { + result.current.upsertRecordFilterGroup(mockRecordFilterGroup); + }); + + expect(result.current.currentRecordFilterGroups).toHaveLength(1); + expect(result.current.currentRecordFilterGroups[0]).toEqual( + mockRecordFilterGroup, + ); + }); + + it('should update an existing record filter group', () => { + const { result } = renderHook( + () => { + const currentRecordFilterGroups = useRecoilComponentValueV2( + currentRecordFilterGroupsComponentState, + ); + + const { upsertRecordFilterGroup } = useUpsertRecordFilterGroup(); + + return { upsertRecordFilterGroup, currentRecordFilterGroups }; + }, + { + wrapper: Wrapper, + }, + ); + + const mockInitialRecordFilterGroup: RecordFilterGroup = { + id: 'record-filter-group-1', + logicalOperator: RecordFilterGroupLogicalOperator.AND, + parentRecordFilterGroupId: null, + }; + + const mockUpdatedRecordFilterGroup: RecordFilterGroup = { + id: 'record-filter-group-1', + logicalOperator: RecordFilterGroupLogicalOperator.OR, + parentRecordFilterGroupId: null, + }; + + act(() => { + result.current.upsertRecordFilterGroup(mockInitialRecordFilterGroup); + }); + + expect(result.current.currentRecordFilterGroups).toHaveLength(1); + expect(result.current.currentRecordFilterGroups[0]).toEqual( + mockInitialRecordFilterGroup, + ); + + act(() => { + result.current.upsertRecordFilterGroup(mockUpdatedRecordFilterGroup); + }); + + expect(result.current.currentRecordFilterGroups).toHaveLength(1); + expect(result.current.currentRecordFilterGroups[0]).toEqual( + mockUpdatedRecordFilterGroup, + ); + }); +}); diff --git a/packages/twenty-front/src/modules/object-record/record-filter-group/hooks/useRemoveRecordFilterGroup.ts b/packages/twenty-front/src/modules/object-record/record-filter-group/hooks/useRemoveRecordFilterGroup.ts new file mode 100644 index 000000000..35de0eb7e --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-filter-group/hooks/useRemoveRecordFilterGroup.ts @@ -0,0 +1,59 @@ +import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState'; +import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; +import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; +import { useRecoilCallback } from 'recoil'; + +export const useRemoveRecordFilterGroup = () => { + const currentRecordFilterGroupsCallbackState = + useRecoilComponentCallbackStateV2(currentRecordFilterGroupsComponentState); + + const removeRecordFilterGroup = useRecoilCallback( + ({ set, snapshot }) => + (recordFilterGroupIdToRemove: string) => { + const currentRecordFilterGroups = getSnapshotValue( + snapshot, + currentRecordFilterGroupsCallbackState, + ); + + const hasFoundRecordFilterGroupInCurrentRecordFilterGroups = + currentRecordFilterGroups.some( + (existingRecordFilterGroup) => + existingRecordFilterGroup.id === recordFilterGroupIdToRemove, + ); + + if (hasFoundRecordFilterGroupInCurrentRecordFilterGroups) { + set( + currentRecordFilterGroupsCallbackState, + (currentRecordFilterGroups) => { + const newCurrentRecordFilterGroups = [ + ...currentRecordFilterGroups, + ]; + + const indexOfRecordFilterGroupToRemove = + newCurrentRecordFilterGroups.findIndex( + (existingRecordFilterGroup) => + existingRecordFilterGroup.id === + recordFilterGroupIdToRemove, + ); + + if (indexOfRecordFilterGroupToRemove === -1) { + return newCurrentRecordFilterGroups; + } + + newCurrentRecordFilterGroups.splice( + indexOfRecordFilterGroupToRemove, + 1, + ); + + return newCurrentRecordFilterGroups; + }, + ); + } + }, + [currentRecordFilterGroupsCallbackState], + ); + + return { + removeRecordFilterGroup, + }; +}; diff --git a/packages/twenty-front/src/modules/object-record/record-filter-group/hooks/useUpsertRecordFilterGroup.ts b/packages/twenty-front/src/modules/object-record/record-filter-group/hooks/useUpsertRecordFilterGroup.ts new file mode 100644 index 000000000..372972f3e --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-filter-group/hooks/useUpsertRecordFilterGroup.ts @@ -0,0 +1,63 @@ +import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState'; +import { RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup'; +import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; +import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; +import { useRecoilCallback } from 'recoil'; + +export const useUpsertRecordFilterGroup = () => { + const currentRecordFilterGroupsCallbackState = + useRecoilComponentCallbackStateV2(currentRecordFilterGroupsComponentState); + + const upsertRecordFilterGroup = useRecoilCallback( + ({ set, snapshot }) => + (recordFilterGroupToSet: RecordFilterGroup) => { + const currentRecordFilterGroups = getSnapshotValue( + snapshot, + currentRecordFilterGroupsCallbackState, + ); + + const hasFoundRecordFilterGroupInCurrentRecordFilterGroups = + currentRecordFilterGroups.some( + (existingRecordFilterGroup) => + existingRecordFilterGroup.id === recordFilterGroupToSet.id, + ); + + if (!hasFoundRecordFilterGroupInCurrentRecordFilterGroups) { + set(currentRecordFilterGroupsCallbackState, [ + ...currentRecordFilterGroups, + recordFilterGroupToSet, + ]); + } else { + set( + currentRecordFilterGroupsCallbackState, + (currentRecordFilterGroups) => { + const newCurrentRecordFilterGroups = [ + ...currentRecordFilterGroups, + ]; + + const indexOfRecordFilterGroupToUpdate = + newCurrentRecordFilterGroups.findIndex( + (existingRecordFilterGroup) => + existingRecordFilterGroup.id === recordFilterGroupToSet.id, + ); + + if (indexOfRecordFilterGroupToUpdate === -1) { + return newCurrentRecordFilterGroups; + } + + newCurrentRecordFilterGroups[indexOfRecordFilterGroupToUpdate] = { + ...recordFilterGroupToSet, + }; + + return newCurrentRecordFilterGroups; + }, + ); + } + }, + [currentRecordFilterGroupsCallbackState], + ); + + return { + upsertRecordFilterGroup, + }; +}; 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 c5a6083e1..00efe5de9 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 @@ -1,10 +1,10 @@ import { renderHook } from '@testing-library/react'; -import { act } from 'react-dom/test-utils'; import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; +import { act } from 'react'; import { FieldMetadataType } from '~/generated/graphql'; import { getJestMetadataAndApolloMocksWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksWrapper'; import { useRemoveRecordFilter } from '../useRemoveRecordFilter'; @@ -15,7 +15,7 @@ const Wrapper = getJestMetadataAndApolloMocksWrapper({ }); describe('useRemoveRecordFilter', () => { - it('should remove an existing filter', () => { + it('should remove an existing record filter', () => { const { result } = renderHook( () => { const currentRecordFilters = useRecoilComponentValueV2( @@ -36,7 +36,7 @@ describe('useRemoveRecordFilter', () => { }, ); - const filter: RecordFilter = { + const mockRecordFilter: RecordFilter = { id: 'filter-1', fieldMetadataId: 'field-1', value: 'test-value', @@ -46,17 +46,15 @@ describe('useRemoveRecordFilter', () => { type: FieldMetadataType.TEXT, }; - // First add a filter act(() => { - result.current.upsertRecordFilter(filter); + result.current.upsertRecordFilter(mockRecordFilter); }); expect(result.current.currentRecordFilters).toHaveLength(1); - expect(result.current.currentRecordFilters[0]).toEqual(filter); + expect(result.current.currentRecordFilters[0]).toEqual(mockRecordFilter); - // Then remove it act(() => { - result.current.removeRecordFilter(filter.fieldMetadataId); + result.current.removeRecordFilter(mockRecordFilter.fieldMetadataId); }); expect(result.current.currentRecordFilters).toHaveLength(0); @@ -81,7 +79,7 @@ describe('useRemoveRecordFilter', () => { }, ); - const filter: RecordFilter = { + const mockRecordFilter: RecordFilter = { id: 'filter-1', fieldMetadataId: 'field-1', value: 'test-value', @@ -91,20 +89,17 @@ describe('useRemoveRecordFilter', () => { type: FieldMetadataType.TEXT, }; - // Add a filter act(() => { - result.current.upsertRecordFilter(filter); + result.current.upsertRecordFilter(mockRecordFilter); }); expect(result.current.currentRecordFilters).toHaveLength(1); - // Try to remove a non-existent filter act(() => { - result.current.removeRecordFilter('non-existent-field'); + result.current.removeRecordFilter('non-existent-field-metadata-id'); }); - // Filter list should remain unchanged expect(result.current.currentRecordFilters).toHaveLength(1); - expect(result.current.currentRecordFilters[0]).toEqual(filter); + expect(result.current.currentRecordFilters[0]).toEqual(mockRecordFilter); }); }); 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 692cc3ec1..edaf73059 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 @@ -1,5 +1,5 @@ import { renderHook } from '@testing-library/react'; -import { act } from 'react-dom/test-utils'; +import { act } from 'react'; import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter'; @@ -30,7 +30,7 @@ describe('useUpsertRecordFilter', () => { }, ); - const newFilter: RecordFilter = { + const mockNewRecordFilter: RecordFilter = { id: 'filter-1', fieldMetadataId: 'field-1', value: 'test-value', @@ -41,11 +41,11 @@ describe('useUpsertRecordFilter', () => { }; act(() => { - result.current.upsertRecordFilter(newFilter); + result.current.upsertRecordFilter(mockNewRecordFilter); }); expect(result.current.currentRecordFilters).toHaveLength(1); - expect(result.current.currentRecordFilters[0]).toEqual(newFilter); + expect(result.current.currentRecordFilters[0]).toEqual(mockNewRecordFilter); }); it('should update an existing filter when fieldMetadataId exists', () => { @@ -64,7 +64,7 @@ describe('useUpsertRecordFilter', () => { }, ); - const initialFilter: RecordFilter = { + const mockInitialRecordFilter: RecordFilter = { id: 'filter-1', fieldMetadataId: 'field-1', value: 'initial-value', @@ -74,7 +74,7 @@ describe('useUpsertRecordFilter', () => { type: FieldMetadataType.TEXT, }; - const updatedFilter: RecordFilter = { + const mockUpdatedRecordFilter: RecordFilter = { id: 'filter-1', fieldMetadataId: 'field-1', value: 'updated-value', @@ -85,17 +85,21 @@ describe('useUpsertRecordFilter', () => { }; act(() => { - result.current.upsertRecordFilter(initialFilter); + result.current.upsertRecordFilter(mockInitialRecordFilter); }); expect(result.current.currentRecordFilters).toHaveLength(1); - expect(result.current.currentRecordFilters[0]).toEqual(initialFilter); + expect(result.current.currentRecordFilters[0]).toEqual( + mockInitialRecordFilter, + ); act(() => { - result.current.upsertRecordFilter(updatedFilter); + result.current.upsertRecordFilter(mockUpdatedRecordFilter); }); expect(result.current.currentRecordFilters).toHaveLength(1); - expect(result.current.currentRecordFilters[0]).toEqual(updatedFilter); + expect(result.current.currentRecordFilters[0]).toEqual( + mockUpdatedRecordFilter, + ); }); }); diff --git a/packages/twenty-front/src/modules/views/components/AdvancedFilterDropdownButton.tsx b/packages/twenty-front/src/modules/views/components/AdvancedFilterDropdownButton.tsx index f0b752833..f9ef88a2e 100644 --- a/packages/twenty-front/src/modules/views/components/AdvancedFilterDropdownButton.tsx +++ b/packages/twenty-front/src/modules/views/components/AdvancedFilterDropdownButton.tsx @@ -4,6 +4,7 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { AdvancedFilterRootLevelViewFilterGroup } from '@/object-record/advanced-filter/components/AdvancedFilterRootLevelViewFilterGroup'; import { useDeleteCombinedViewFilterGroup } from '@/object-record/advanced-filter/hooks/useDeleteCombinedViewFilterGroup'; +import { useRemoveRecordFilterGroup } from '@/object-record/record-filter-group/hooks/useRemoveRecordFilterGroup'; import { useRemoveRecordFilter } from '@/object-record/record-filter/hooks/useRemoveRecordFilter'; import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; @@ -14,6 +15,7 @@ import { isDefined } from 'twenty-shared'; export const AdvancedFilterDropdownButton = () => { const { deleteCombinedViewFilterGroup } = useDeleteCombinedViewFilterGroup(); + const { removeRecordFilterGroup } = useRemoveRecordFilterGroup(); const { currentViewWithCombinedFiltersAndSorts } = useGetCurrentView(); @@ -43,6 +45,7 @@ export const AdvancedFilterDropdownButton = () => { for (const viewFilterGroupId of viewFilterGroupIds) { await deleteCombinedViewFilterGroup(viewFilterGroupId); + removeRecordFilterGroup(viewFilterGroupId); } for (const recordFilterId of advancedRecordFilterIds) { @@ -50,6 +53,7 @@ export const AdvancedFilterDropdownButton = () => { } }, [ advancedRecordFilterIds, + removeRecordFilterGroup, removeRecordFilter, deleteCombinedViewFilterGroup, currentViewWithCombinedFiltersAndSorts?.viewFilterGroups,