fix(filters): unify combinedFilter for queries and bulk delete (#13952)

Issue: https://github.com/twentyhq/twenty/issues/13913

Motivation/Problem:
Bulk delete and query paths were composing filters differently, causing
mismatches. In some cases this led to invalid or empty GraphQL filters
(e.g. {"and":[{}]}), breaking delete operations.

Fix:
Unify filter composition (combinedFilter) across queries and bulk
delete, ensuring consistent handling of base filters + soft-deleted
clause. Also adjusted record filter grouping logic to avoid dropping
filters when no groups exist.

Result:
Filtered queries and bulk deletes now behave consistently and reliably
without producing broken filters.

---------

Co-authored-by: root <root@DESKTOP-E2VOJGE>
Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Puranjay Mishra
2025-08-18 06:01:47 -04:00
committed by GitHub
parent 1598e5590a
commit 483c1e2214
16 changed files with 185 additions and 53 deletions
@@ -17,6 +17,7 @@ import { recordStoreFamilyState } from '@/object-record/record-store/states/reco
import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndActionMenuWrapper';
import { getPeopleRecordConnectionMock } from '~/testing/mock-data/people';
import { generatedMockObjectMetadataItems } from '~/testing/utils/generatedMockObjectMetadataItems';
import { contextStoreFilterGroupsComponentState } from '@/context-store/states/contextStoreFilterGroupsComponentState';
const mockCopyContextStoreStates = jest.fn();
jest.mock(
@@ -81,6 +82,12 @@ describe('useSetGlobalCommandMenuContext', () => {
}),
);
const filterGroups = useRecoilValue(
contextStoreFilterGroupsComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
}),
);
const anyFieldFilterValue = useRecoilValue(
contextStoreAnyFieldFilterValueComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
@@ -104,6 +111,7 @@ describe('useSetGlobalCommandMenuContext', () => {
targetedRecordsRule,
numberOfSelectedRecords,
filters,
filterGroups,
currentViewType,
commandMenuPageInfo,
hasUserSelectedCommand,
@@ -2,6 +2,7 @@ import { contextStoreAnyFieldFilterValueComponentState } from '@/context-store/s
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
import { contextStoreFilterGroupsComponentState } from '@/context-store/states/contextStoreFilterGroupsComponentState';
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
@@ -77,6 +78,21 @@ export const useCopyContextStoreStates = () => {
contextStoreFilters,
);
const contextStoreFilterGroups = snapshot
.getLoadable(
contextStoreFilterGroupsComponentState.atomFamily({
instanceId: instanceIdToCopyFrom,
}),
)
.getValue();
set(
contextStoreFilterGroupsComponentState.atomFamily({
instanceId: instanceIdToCopyTo,
}),
contextStoreFilterGroups,
);
const contextStoreAnyFieldFilterValue = snapshot
.getLoadable(
contextStoreAnyFieldFilterValueComponentState.atomFamily({
@@ -1,6 +1,7 @@
import { contextStoreAnyFieldFilterValueComponentState } from '@/context-store/states/contextStoreAnyFieldFilterValueComponentState';
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
import { contextStoreFilterGroupsComponentState } from '@/context-store/states/contextStoreFilterGroupsComponentState';
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
@@ -40,6 +41,13 @@ export const useResetContextStoreStates = () => {
[],
);
set(
contextStoreFilterGroupsComponentState.atomFamily({
instanceId,
}),
[],
);
set(
contextStoreAnyFieldFilterValueComponentState.atomFamily({
instanceId,
@@ -5,6 +5,7 @@ import { commandMenuPageInfoState } from '@/command-menu/states/commandMenuPageI
import { hasUserSelectedCommandState } from '@/command-menu/states/hasUserSelectedCommandState';
import { contextStoreAnyFieldFilterValueComponentState } from '@/context-store/states/contextStoreAnyFieldFilterValueComponentState';
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
import { contextStoreFilterGroupsComponentState } from '@/context-store/states/contextStoreFilterGroupsComponentState';
import { contextStoreFiltersComponentState } from '@/context-store/states/contextStoreFiltersComponentState';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
@@ -46,6 +47,13 @@ export const useSetGlobalCommandMenuContext = () => {
[],
);
set(
contextStoreFilterGroupsComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,
}),
[],
);
set(
contextStoreAnyFieldFilterValueComponentState.atomFamily({
instanceId: COMMAND_MENU_COMPONENT_INSTANCE_ID,