Feat: Advanced filter (#7700)

Design:


![twenty-advanced-filters-design](https://github.com/user-attachments/assets/7d99971c-9ee1-4a78-a2fb-7ae5a9b3a836)

Not ready to be merged yet!

---------

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
ad-elias
2024-10-24 16:59:59 +02:00
committed by GitHub
parent 1dfeba39eb
commit 315820ec86
99 changed files with 3349 additions and 1079 deletions

View File

@ -0,0 +1,38 @@
import { ViewFilterGroup } from '@/views/types/ViewFilterGroup';
export const getCombinedViewFilterGroups = (
viewFilterGroups: ViewFilterGroup[],
unsavedToUpsertViewFilterGroups: ViewFilterGroup[],
unsavedToDeleteViewFilterGroupIds: string[],
): ViewFilterGroup[] => {
const toCreateViewFilterGroups = unsavedToUpsertViewFilterGroups.filter(
(toUpsertViewFilterGroup) =>
!viewFilterGroups.some(
(viewFilterGroup) => viewFilterGroup.id === toUpsertViewFilterGroup.id,
),
);
const toUpdateViewFilterGroups = unsavedToUpsertViewFilterGroups.filter(
(toUpsertViewFilterGroup) =>
viewFilterGroups.some(
(viewFilterGroup) => viewFilterGroup.id === toUpsertViewFilterGroup.id,
),
);
const combinedViewFilterGroups = viewFilterGroups
.filter(
(viewFilterGroup) =>
!unsavedToDeleteViewFilterGroupIds.includes(viewFilterGroup.id),
)
.map((viewFilterGroup) => {
const toUpdateViewFilterGroup = toUpdateViewFilterGroups.find(
(toUpdateViewFilterGroup) =>
toUpdateViewFilterGroup.id === viewFilterGroup.id,
);
return toUpdateViewFilterGroup ?? viewFilterGroup;
})
.concat(toCreateViewFilterGroups);
return combinedViewFilterGroups;
};

View File

@ -8,24 +8,19 @@ export const getCombinedViewFilters = (
const toCreateViewFilters = toUpsertViewFilters.filter(
(toUpsertViewFilter) =>
!viewFilters.some(
(viewFilter) =>
viewFilter.fieldMetadataId === toUpsertViewFilter.fieldMetadataId,
(viewFilter) => viewFilter.id === toUpsertViewFilter.id,
),
);
const toUpdateViewFilters = toUpsertViewFilters.filter((toUpsertViewFilter) =>
viewFilters.some(
(viewFilter) =>
viewFilter.fieldMetadataId === toUpsertViewFilter.fieldMetadataId,
),
viewFilters.some((viewFilter) => viewFilter.id === toUpsertViewFilter.id),
);
const combinedViewFilters = viewFilters
.filter((viewFilter) => !toDeleteViewFilterIds.includes(viewFilter.id))
.map((viewFilter) => {
const toUpdateViewFilter = toUpdateViewFilters.find(
(toUpdateViewFilter) =>
toUpdateViewFilter.fieldMetadataId === viewFilter.fieldMetadataId,
(toUpdateViewFilter) => toUpdateViewFilter.id === viewFilter.id,
);
return toUpdateViewFilter ?? viewFilter;

View File

@ -3,7 +3,7 @@ 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 { turnFiltersIntoQueryFilter } from '@/object-record/record-filter/utils/turnFiltersIntoQueryFilter';
import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
import { View } from '@/views/types/View';
import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
import { mapViewSortsToSorts } from '@/views/utils/mapViewSortsToSorts';
@ -27,7 +27,7 @@ export const getQueryVariablesFromView = ({
};
}
const { viewFilters, viewSorts } = view;
const { viewFilterGroups, viewFilters, viewSorts } = view;
const filterDefinitions = formatFieldMetadataItemsAsFilterDefinitions({
fields: fieldMetadataItems,
@ -38,9 +38,10 @@ export const getQueryVariablesFromView = ({
fields: fieldMetadataItems,
});
const filter = turnFiltersIntoQueryFilter(
const filter = computeViewRecordGqlOperationFilter(
mapViewFiltersToFilters(viewFilters, filterDefinitions),
objectMetadataItem?.fields ?? [],
viewFilterGroups ?? [],
);
const orderBy = turnSortsIntoOrderBy(

View File

@ -23,6 +23,8 @@ export const mapViewFiltersToFilters = (
value: viewFilter.value,
displayValue: viewFilter.displayValue,
operand: viewFilter.operand,
viewFilterGroupId: viewFilter.viewFilterGroupId,
positionInViewFilterGroup: viewFilter.positionInViewFilterGroup,
definition: viewFilter.definition ?? availableFilterDefinition,
};
})

View File

@ -0,0 +1,18 @@
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
import { isDefined } from 'twenty-ui';
export const shouldReplaceFilter = (
oldFilter: Pick<Filter, 'id' | 'fieldMetadataId' | 'viewFilterGroupId'>,
newFilter: Pick<Filter, 'id' | 'fieldMetadataId' | 'viewFilterGroupId'>,
) => {
const isNewFilterAdvancedFilter = isDefined(newFilter.viewFilterGroupId);
if (isNewFilterAdvancedFilter) {
return newFilter.id === oldFilter.id;
} else {
return (
newFilter.fieldMetadataId === oldFilter.fieldMetadataId &&
!oldFilter.viewFilterGroupId
);
}
};

View File

@ -0,0 +1,15 @@
import { ViewFilterGroup } from '@/views/types/ViewFilterGroup';
export const sortViewFilterGroupsOutermostFirst = (
viewFilterGroups: ViewFilterGroup[],
parentViewFilterGroupId?: string,
): ViewFilterGroup[] => {
const childGroups = viewFilterGroups.filter(
(group) => group.parentViewFilterGroupId === parentViewFilterGroupId,
);
return childGroups.flatMap((group) => [
group,
...sortViewFilterGroupsOutermostFirst(viewFilterGroups, group.id),
]);
};