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
@@ -3,6 +3,7 @@ import { type PropsWithChildren, useEffect, useState } from 'react';
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 {
@@ -11,6 +12,7 @@ import {
} from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { type ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { type RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
@@ -18,6 +20,7 @@ export type JestContextStoreSetterMocks = {
contextStoreTargetedRecordsRule?: ContextStoreTargetedRecordsRule;
contextStoreNumberOfSelectedRecords?: number;
contextStoreFilters?: RecordFilter[];
contextStoreFilterGroups?: RecordFilterGroup[];
contextStoreCurrentObjectMetadataNameSingular?: string;
contextStoreCurrentViewId?: string;
contextStoreCurrentViewType?: ContextStoreViewType;
@@ -34,6 +37,7 @@ export const JestContextStoreSetter = ({
contextStoreNumberOfSelectedRecords = 0,
contextStoreCurrentObjectMetadataNameSingular = 'company',
contextStoreFilters = [],
contextStoreFilterGroups = [],
contextStoreCurrentViewType,
children,
}: JestContextStoreSetterProps) => {
@@ -53,6 +57,10 @@ export const JestContextStoreSetter = ({
contextStoreFiltersComponentState,
);
const setContextStoreFilterGroupsComponentState = useSetRecoilComponentState(
contextStoreFilterGroupsComponentState,
);
const setContextStoreCurrentViewId = useSetRecoilComponentState(
contextStoreCurrentViewIdComponentState,
);
@@ -74,6 +82,7 @@ export const JestContextStoreSetter = ({
setContextStoreCurrentObjectMetadataItemId(objectMetadataItem.id);
setContextStoreNumberOfSelectedRecords(contextStoreNumberOfSelectedRecords);
setcontextStoreFiltersComponentState(contextStoreFilters);
setContextStoreFilterGroupsComponentState(contextStoreFilterGroups);
setContextStoreCurrentViewType(contextStoreCurrentViewType ?? null);
setIsLoaded(true);
}, [
@@ -90,6 +99,8 @@ export const JestContextStoreSetter = ({
contextStoreCurrentViewId,
setContextStoreCurrentViewType,
contextStoreCurrentViewType,
setContextStoreFilterGroupsComponentState,
contextStoreFilterGroups,
]);
return isLoaded ? <>{children}</> : null;