Remove filter definition (#10227)

This PR removes completely filter definition usage and states,
everything is now derived from fieldMetadataItem.
This commit is contained in:
Lucas Bordeau
2025-02-14 18:37:34 +01:00
committed by GitHub
parent 68db9a7a8c
commit 70e5cdfe83
51 changed files with 250 additions and 766 deletions

View File

@ -1,8 +1,7 @@
import { useEffect } from 'react';
import { formatFieldMetadataItemAsFilterDefinition } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
import { filterDefinitionUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/filterDefinitionUsedInDropdownComponentState';
import { selectedFilterComponentState } from '@/object-record/object-filter-dropdown/states/selectedFilterComponentState';
import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState';
import { useFilterableFieldMetadataItemsInRecordIndexContext } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItemsInRecordIndexContext';
@ -19,11 +18,6 @@ export const EditableFilterDropdownButtonEffect = ({
viewFilterDropdownId,
viewFilter,
}: EditableFilterDropdownButtonEffectProps) => {
const setFilterDefinitionUsedInDropdown = useSetRecoilComponentStateV2(
filterDefinitionUsedInDropdownComponentState,
viewFilterDropdownId,
);
const setFieldMetadataItemIdUsedInDropdown = useSetRecoilComponentStateV2(
fieldMetadataItemIdUsedInDropdownComponentState,
);
@ -51,19 +45,11 @@ export const EditableFilterDropdownButtonEffect = ({
return;
}
const filterDefinition = formatFieldMetadataItemAsFilterDefinition({
field: fieldMetadataItem,
});
if (isDefined(filterDefinition)) {
setFilterDefinitionUsedInDropdown(filterDefinition);
setFieldMetadataItemIdUsedInDropdown(filterDefinition.fieldMetadataId);
setSelectedOperandInDropdown(viewFilter.operand);
setSelectedFilter(viewFilter);
}
setFieldMetadataItemIdUsedInDropdown(fieldMetadataItem.id);
setSelectedOperandInDropdown(viewFilter.operand);
setSelectedFilter(viewFilter);
}, [
filterableFieldMetadataItems,
setFilterDefinitionUsedInDropdown,
setFieldMetadataItemIdUsedInDropdown,
viewFilter,
setSelectedOperandInDropdown,

View File

@ -1,4 +1,3 @@
import { formatFieldMetadataItemAsFilterDefinition } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { useFilterableFieldMetadataItemsInRecordIndexContext } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItemsInRecordIndexContext';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
@ -45,16 +44,12 @@ export const ViewBarRecordFilterEffect = () => {
if (isDataPrefetched && !hasInitializedCurrentRecordFilters) {
const currentView = views.find((view) => view.id === currentViewId);
const filterDefinitions = filterableFieldMetadataItems.map(
(fieldMetadataItem) =>
formatFieldMetadataItemAsFilterDefinition({
field: fieldMetadataItem,
}),
);
if (isDefined(currentView)) {
setCurrentRecordFilters(
mapViewFiltersToFilters(currentView.viewFilters, filterDefinitions),
mapViewFiltersToFilters(
currentView.viewFilters,
filterableFieldMetadataItems,
),
);
setHasInitializedCurrentRecordFilters(true);
}

View File

@ -1,12 +1,8 @@
import { act, renderHook } from '@testing-library/react';
import {
formatFieldMetadataItemAsFilterDefinition,
getFilterTypeFromFieldType,
} from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import { RecordFilterDefinition } from '@/object-record/record-filter/types/RecordFilterDefinition';
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { currentViewIdComponentState } from '@/views/states/currentViewIdComponentState';
@ -34,11 +30,6 @@ describe('useApplyCurrentViewFiltersToCurrentRecordFilters', () => {
const mockFieldMetadataItem = mockObjectMetadataItem.fields[0];
const mockFilterDefinition: RecordFilterDefinition =
formatFieldMetadataItemAsFilterDefinition({
field: mockFieldMetadataItem,
});
const mockViewFilter: ViewFilter = {
__typename: 'ViewFilter',
id: 'filter-1',
@ -48,7 +39,6 @@ describe('useApplyCurrentViewFiltersToCurrentRecordFilters', () => {
displayValue: mockFieldMetadataItem.label,
viewFilterGroupId: 'group-1',
positionInViewFilterGroup: 0,
definition: mockFilterDefinition,
};
const mockView = {
@ -108,8 +98,7 @@ describe('useApplyCurrentViewFiltersToCurrentRecordFilters', () => {
operand: mockViewFilter.operand,
viewFilterGroupId: mockViewFilter.viewFilterGroupId,
positionInViewFilterGroup: mockViewFilter.positionInViewFilterGroup,
definition: mockFilterDefinition,
label: mockViewFilter.displayValue,
label: mockFieldMetadataItem.label,
type: getFilterTypeFromFieldType(mockFieldMetadataItem.type),
} satisfies RecordFilter,
]);

View File

@ -1,12 +1,8 @@
import { act, renderHook } from '@testing-library/react';
import {
formatFieldMetadataItemAsFilterDefinition,
getFilterTypeFromFieldType,
} from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import { RecordFilterDefinition } from '@/object-record/record-filter/types/RecordFilterDefinition';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { ViewFilter } from '@/views/types/ViewFilter';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
@ -30,11 +26,6 @@ describe('useApplyViewFiltersToCurrentRecordFilters', () => {
const mockFieldMetadataItem = mockObjectMetadataItem.fields[0];
const mockAvailableFilterDefinition: RecordFilterDefinition =
formatFieldMetadataItemAsFilterDefinition({
field: mockFieldMetadataItem,
});
const mockViewFilter: ViewFilter = {
__typename: 'ViewFilter',
id: 'filter-1',
@ -44,7 +35,6 @@ describe('useApplyViewFiltersToCurrentRecordFilters', () => {
displayValue: mockFieldMetadataItem.label,
viewFilterGroupId: 'group-1',
positionInViewFilterGroup: 0,
definition: mockAvailableFilterDefinition,
};
it('should apply view filters to current record filters', () => {
@ -82,8 +72,7 @@ describe('useApplyViewFiltersToCurrentRecordFilters', () => {
operand: mockViewFilter.operand,
viewFilterGroupId: mockViewFilter.viewFilterGroupId,
positionInViewFilterGroup: mockViewFilter.positionInViewFilterGroup,
definition: mockAvailableFilterDefinition,
label: mockViewFilter.displayValue,
label: mockFieldMetadataItem.label,
type: getFilterTypeFromFieldType(mockFieldMetadataItem.type),
} satisfies RecordFilter,
]);

View File

@ -10,7 +10,6 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
import { objectMetadataItemFamilySelector } from '@/object-metadata/states/objectMetadataItemFamilySelector';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { formatFieldMetadataItemAsFilterDefinition } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { getObjectRecordIdentifier } from '@/object-metadata/utils/getObjectRecordIdentifier';
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { generateFindManyRecordsQuery } from '@/object-record/utils/generateFindManyRecordsQuery';
@ -18,7 +17,6 @@ import { ViewFilter } from '@/views/types/ViewFilter';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { relationFilterValueSchemaObject } from '@/views/view-filter-value/validation-schemas/jsonRelationFilterValueSchema';
import { isDefined } from 'twenty-shared';
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
const filterQueryParamsSchema = z.object({
viewId: z.string().optional(),
@ -85,13 +83,6 @@ export const useViewFromQueryParams = () => {
if (!fieldMetadataItem) return null;
const filterDefinition =
formatFieldMetadataItemAsFilterDefinition({
field: fieldMetadataItem,
});
if (isUndefinedOrNull(filterDefinition)) return null;
const relationObjectMetadataNameSingular =
fieldMetadataItem.relationDefinition?.targetObjectMetadata
?.nameSingular;
@ -178,7 +169,6 @@ export const useViewFromQueryParams = () => {
value: filterValueAsString,
displayValue:
relationRecordNames?.join(', ') ?? filterValueAsString,
definition: filterDefinition,
persistAction: 'NONE',
};
},

View File

@ -1,4 +1,4 @@
import { useFilterDefinitionsFromFilterableFieldMetadataItems } from '@/object-record/record-filter/hooks/useFilterDefinitionsFromFilterableFieldMetadataItems';
import { useFilterableFieldMetadataItemsInRecordIndexContext } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItemsInRecordIndexContext';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData';
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
@ -19,15 +19,18 @@ export const useApplyCurrentViewFiltersToCurrentRecordFilters = () => {
currentRecordFiltersComponentState,
);
const { filterDefinitions } =
useFilterDefinitionsFromFilterableFieldMetadataItems();
const { filterableFieldMetadataItems } =
useFilterableFieldMetadataItemsInRecordIndexContext();
const applyCurrentViewFiltersToCurrentRecordFilters = () => {
const currentView = views.find((view) => view.id === currentViewId);
if (isDefined(currentView)) {
setCurrentRecordFilters(
mapViewFiltersToFilters(currentView.viewFilters, filterDefinitions),
mapViewFiltersToFilters(
currentView.viewFilters,
filterableFieldMetadataItems,
),
);
}
};

View File

@ -1,4 +1,4 @@
import { useFilterDefinitionsFromFilterableFieldMetadataItems } from '@/object-record/record-filter/hooks/useFilterDefinitionsFromFilterableFieldMetadataItems';
import { useFilterableFieldMetadataItemsInRecordIndexContext } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItemsInRecordIndexContext';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { ViewFilter } from '@/views/types/ViewFilter';
@ -9,15 +9,15 @@ export const useApplyViewFiltersToCurrentRecordFilters = () => {
currentRecordFiltersComponentState,
);
const { filterDefinitions } =
useFilterDefinitionsFromFilterableFieldMetadataItems();
const { filterableFieldMetadataItems } =
useFilterableFieldMetadataItemsInRecordIndexContext();
const applyViewFiltersToCurrentRecordFilters = (
viewFilters: ViewFilter[],
) => {
const recordFiltersToApply = mapViewFiltersToFilters(
viewFilters,
filterDefinitions,
filterableFieldMetadataItems,
);
setCurrentRecordFilters(recordFiltersToApply);

View File

@ -3,8 +3,6 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
import { useViewOrDefaultViewFromPrefetchedViews } from '@/views/hooks/useViewOrDefaultViewFromPrefetchedViews';
import { getQueryVariablesFromView } from '@/views/utils/getQueryVariablesFromView';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { FeatureFlagKey } from '~/generated/graphql';
export const useQueryVariablesFromActiveFieldsOfViewOrDefaultView = ({
objectMetadataItem,
@ -22,17 +20,12 @@ export const useQueryVariablesFromActiveFieldsOfViewOrDefaultView = ({
objectMetadataItem,
});
const isJsonFilterEnabled = useIsFeatureEnabled(
FeatureFlagKey.IsJsonFilterEnabled,
);
const { filterValueDependencies } = useFilterValueDependencies();
const { filter, orderBy } = getQueryVariablesFromView({
fieldMetadataItems: activeFieldMetadataItems,
objectMetadataItem,
view,
isJsonFilterEnabled,
filterValueDependencies,
});

View File

@ -1,4 +1,3 @@
import { RecordFilterDefinition } from '@/object-record/record-filter/types/RecordFilterDefinition';
import { ViewFilterOperand } from './ViewFilterOperand';
export type ViewFilter = {
@ -14,5 +13,4 @@ export type ViewFilter = {
viewId?: string;
viewFilterGroupId?: string;
positionInViewFilterGroup?: number | null;
definition?: RecordFilterDefinition;
};

View File

@ -20,6 +20,15 @@ const baseDefinition = {
fieldName: 'fieldName',
};
const baseFieldMetadataItem = {
id: '05731f68-6e7a-4903-8374-c0b6a9063482',
createdAt: '2021-01-01',
updatedAt: '2021-01-01',
name: 'name',
label: 'Name',
type: FieldMetadataType.FULL_NAME,
};
describe('mapViewSortsToSorts', () => {
it('should map each ViewSort object to a corresponding Sort object', () => {
const viewSorts: ViewSort[] = [
@ -62,23 +71,12 @@ describe('mapViewFiltersToFilters', () => {
value: 'testValue',
displayValue: 'Test Display Value',
operand: ViewFilterOperand.Is,
definition: {
...baseDefinition,
type: 'FULL_NAME',
},
label: baseDefinition.label,
type: 'FULL_NAME',
positionInViewFilterGroup: undefined,
viewFilterGroupId: undefined,
label: baseFieldMetadataItem.label,
type: FieldMetadataType.FULL_NAME,
},
];
expect(
mapViewFiltersToFilters(viewFilters, [
{
...baseDefinition,
type: 'FULL_NAME',
},
]),
mapViewFiltersToFilters(viewFilters, [baseFieldMetadataItem]),
).toEqual(expectedFilters);
});
});

View File

@ -1,6 +1,6 @@
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { formatFieldMetadataItemsAsFilterDefinitions } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { formatFieldMetadataItemsAsSortDefinitions } from '@/object-metadata/utils/formatFieldMetadataItemsAsSortDefinitions';
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
import { RecordFilterValueDependencies } from '@/object-record/record-filter/types/RecordFilterValueDependencies';
@ -15,13 +15,11 @@ export const getQueryVariablesFromView = ({
view,
fieldMetadataItems,
objectMetadataItem,
isJsonFilterEnabled,
filterValueDependencies,
}: {
view: View | null | undefined;
fieldMetadataItems: FieldMetadataItem[];
objectMetadataItem: ObjectMetadataItem;
isJsonFilterEnabled: boolean;
filterValueDependencies: RecordFilterValueDependencies;
}) => {
if (!isDefined(view)) {
@ -33,18 +31,13 @@ export const getQueryVariablesFromView = ({
const { viewFilterGroups, viewFilters, viewSorts } = view;
const filterDefinitions = formatFieldMetadataItemsAsFilterDefinitions({
fields: fieldMetadataItems,
isJsonFilterEnabled,
});
const sortDefinitions = formatFieldMetadataItemsAsSortDefinitions({
fields: fieldMetadataItems,
});
const filter = computeViewRecordGqlOperationFilter(
filterValueDependencies,
mapViewFiltersToFilters(viewFilters, filterDefinitions),
mapViewFiltersToFilters(viewFilters, fieldMetadataItems),
objectMetadataItem?.fields ?? [],
viewFilterGroups ?? [],
);

View File

@ -1,21 +1,31 @@
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import { isDefined } from 'twenty-shared';
import { RecordFilterDefinition } from '@/object-record/record-filter/types/RecordFilterDefinition';
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
import { ViewFilter } from '../types/ViewFilter';
export const mapViewFiltersToFilters = (
viewFilters: ViewFilter[],
availableFilterDefinitions: RecordFilterDefinition[],
availableFieldMetadataItems: FieldMetadataItem[],
): RecordFilter[] => {
return viewFilters
.map((viewFilter) => {
const availableFilterDefinition = availableFilterDefinitions.find(
(filterDefinition) =>
filterDefinition.fieldMetadataId === viewFilter.fieldMetadataId,
const availableFieldMetadataItem = availableFieldMetadataItems.find(
(fieldMetadataItem) =>
fieldMetadataItem.id === viewFilter.fieldMetadataId,
);
if (!availableFilterDefinition) return null;
if (!isDefined(availableFieldMetadataItem)) {
throw new Error(
`Field metadata item not found for view filter ${viewFilter.id} and field metadata id ${viewFilter.fieldMetadataId}`,
);
}
const filterType = getFilterTypeFromFieldType(
availableFieldMetadataItem.type,
);
return {
id: viewFilter.id,
@ -25,9 +35,8 @@ export const mapViewFiltersToFilters = (
operand: viewFilter.operand,
viewFilterGroupId: viewFilter.viewFilterGroupId,
positionInViewFilterGroup: viewFilter.positionInViewFilterGroup,
definition: viewFilter.definition ?? availableFilterDefinition,
label: viewFilter.definition?.label ?? availableFilterDefinition.label,
type: viewFilter.definition?.type ?? availableFilterDefinition.type,
label: availableFieldMetadataItem.label,
type: filterType,
};
})
.filter(isDefined);