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)) {