diff --git a/packages/twenty-front/src/modules/object-record/record-filter/utils/turnObjectDropdownFilterIntoQueryFilter.ts b/packages/twenty-front/src/modules/object-record/record-filter/utils/computeViewRecordGqlOperationFilter.ts similarity index 78% rename from packages/twenty-front/src/modules/object-record/record-filter/utils/turnObjectDropdownFilterIntoQueryFilter.ts rename to packages/twenty-front/src/modules/object-record/record-filter/utils/computeViewRecordGqlOperationFilter.ts index bf6f8a6137d0..56c81a6ddfca 100644 --- a/packages/twenty-front/src/modules/object-record/record-filter/utils/turnObjectDropdownFilterIntoQueryFilter.ts +++ b/packages/twenty-front/src/modules/object-record/record-filter/utils/computeViewRecordGqlOperationFilter.ts @@ -30,19 +30,15 @@ import { resolveFilterValue } from '@/views/utils/view-filter-value/resolveFilte import { endOfDay, roundToNearestMinutes, startOfDay } from 'date-fns'; import { z } from 'zod'; -// TODO: Rename file before merging - -// TODO: break this down into smaller functions and make the whole thing immutable -// Especially applyEmptyFilters -export const filterToRecordGqlOperationFilter = ( - rawUIFilter: Filter, +const computeFilterRecordGqlOperationFilter = ( + filter: Filter, fields: Pick[], ): RecordGqlOperationFilter | undefined => { const correspondingField = fields.find( - (field) => field.id === rawUIFilter.fieldMetadataId, + (field) => field.id === filter.fieldMetadataId, ); - const compositeFieldName = rawUIFilter.definition.compositeFieldName; + const compositeFieldName = filter.definition.compositeFieldName; const isCompositeFieldFiter = isNonEmptyString(compositeFieldName); @@ -52,55 +48,55 @@ export const filterToRecordGqlOperationFilter = ( ViewFilterOperand.IsInPast, ViewFilterOperand.IsInFuture, ViewFilterOperand.IsToday, - ].includes(rawUIFilter.operand); + ].includes(filter.operand); if (!correspondingField) { return; } if (!isEmptyOperand) { - if (!isDefined(rawUIFilter.value) || rawUIFilter.value === '') { + if (!isDefined(filter.value) || filter.value === '') { return; } } - switch (rawUIFilter.definition.type) { + switch (filter.definition.type) { case 'TEXT': - switch (rawUIFilter.operand) { + switch (filter.operand) { case ViewFilterOperand.Contains: return { [correspondingField.name]: { - ilike: `%${rawUIFilter.value}%`, + ilike: `%${filter.value}%`, } as StringFilter, }; case ViewFilterOperand.DoesNotContain: return { not: { [correspondingField.name]: { - ilike: `%${rawUIFilter.value}%`, + ilike: `%${filter.value}%`, } as StringFilter, }, }; case ViewFilterOperand.IsEmpty: case ViewFilterOperand.IsNotEmpty: return getEmptyRecordGqlOperationFilter( - rawUIFilter.operand, + filter.operand, correspondingField, - rawUIFilter.definition, + filter.definition, ); default: throw new Error( - `Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`, + `Unknown operand ${filter.operand} for ${filter.definition.type} filter`, ); } case 'DATE': case 'DATE_TIME': { - const resolvedFilterValue = resolveFilterValue(rawUIFilter); + const resolvedFilterValue = resolveFilterValue(filter); const now = roundToNearestMinutes(new Date()); const date = resolvedFilterValue instanceof Date ? resolvedFilterValue : now; - switch (rawUIFilter.operand) { + switch (filter.operand) { case ViewFilterOperand.IsAfter: { return { [correspondingField.name]: { @@ -118,9 +114,9 @@ export const filterToRecordGqlOperationFilter = ( case ViewFilterOperand.IsEmpty: case ViewFilterOperand.IsNotEmpty: { return getEmptyRecordGqlOperationFilter( - rawUIFilter.operand, + filter.operand, correspondingField, - rawUIFilter.definition, + filter.definition, ); } case ViewFilterOperand.IsRelative: { @@ -206,23 +202,23 @@ export const filterToRecordGqlOperationFilter = ( } default: throw new Error( - `Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`, // + `Unknown operand ${filter.operand} for ${filter.definition.type} filter`, // ); } } case 'RATING': - switch (rawUIFilter.operand) { + switch (filter.operand) { case ViewFilterOperand.Is: return { [correspondingField.name]: { - eq: convertRatingToRatingValue(parseFloat(rawUIFilter.value)), + eq: convertRatingToRatingValue(parseFloat(filter.value)), } as StringFilter, }; case ViewFilterOperand.GreaterThan: return { [correspondingField.name]: { in: convertGreaterThanRatingToArrayOfRatingValues( - parseFloat(rawUIFilter.value), + parseFloat(filter.value), ), } as StringFilter, }; @@ -230,62 +226,62 @@ export const filterToRecordGqlOperationFilter = ( return { [correspondingField.name]: { in: convertLessThanRatingToArrayOfRatingValues( - parseFloat(rawUIFilter.value), + parseFloat(filter.value), ), } as StringFilter, }; case ViewFilterOperand.IsEmpty: case ViewFilterOperand.IsNotEmpty: return getEmptyRecordGqlOperationFilter( - rawUIFilter.operand, + filter.operand, correspondingField, - rawUIFilter.definition, + filter.definition, ); default: throw new Error( - `Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`, + `Unknown operand ${filter.operand} for ${filter.definition.type} filter`, ); } case 'NUMBER': - switch (rawUIFilter.operand) { + switch (filter.operand) { case ViewFilterOperand.GreaterThan: return { [correspondingField.name]: { - gte: parseFloat(rawUIFilter.value), + gte: parseFloat(filter.value), } as FloatFilter, }; case ViewFilterOperand.LessThan: return { [correspondingField.name]: { - lte: parseFloat(rawUIFilter.value), + lte: parseFloat(filter.value), } as FloatFilter, }; case ViewFilterOperand.IsEmpty: case ViewFilterOperand.IsNotEmpty: return getEmptyRecordGqlOperationFilter( - rawUIFilter.operand, + filter.operand, correspondingField, - rawUIFilter.definition, + filter.definition, ); default: throw new Error( - `Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`, + `Unknown operand ${filter.operand} for ${filter.definition.type} filter`, ); } case 'RELATION': { if (!isEmptyOperand) { try { - JSON.parse(rawUIFilter.value); + JSON.parse(filter.value); } catch (e) { throw new Error( - `Cannot parse filter value for RELATION filter : "${rawUIFilter.value}"`, + `Cannot parse filter value for RELATION filter : "${filter.value}"`, ); } - const parsedRecordIds = JSON.parse(rawUIFilter.value) as string[]; + const parsedRecordIds = JSON.parse(filter.value) as string[]; if (parsedRecordIds.length > 0) { - switch (rawUIFilter.operand) { + switch (filter.operand) { case ViewFilterOperand.Is: return { [correspondingField.name + 'Id']: { @@ -305,61 +301,61 @@ export const filterToRecordGqlOperationFilter = ( break; default: throw new Error( - `Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`, + `Unknown operand ${filter.operand} for ${filter.definition.type} filter`, ); } } } else { - switch (rawUIFilter.operand) { + switch (filter.operand) { case ViewFilterOperand.IsEmpty: case ViewFilterOperand.IsNotEmpty: return getEmptyRecordGqlOperationFilter( - rawUIFilter.operand, + filter.operand, correspondingField, - rawUIFilter.definition, + filter.definition, ); default: throw new Error( - `Unknown empty operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`, + `Unknown empty operand ${filter.operand} for ${filter.definition.type} filter`, ); } } break; } case 'CURRENCY': - switch (rawUIFilter.operand) { + switch (filter.operand) { case ViewFilterOperand.GreaterThan: return { [correspondingField.name]: { - amountMicros: { gte: parseFloat(rawUIFilter.value) * 1000000 }, + amountMicros: { gte: parseFloat(filter.value) * 1000000 }, } as CurrencyFilter, }; case ViewFilterOperand.LessThan: return { [correspondingField.name]: { - amountMicros: { lte: parseFloat(rawUIFilter.value) * 1000000 }, + amountMicros: { lte: parseFloat(filter.value) * 1000000 }, } as CurrencyFilter, }; case ViewFilterOperand.IsEmpty: case ViewFilterOperand.IsNotEmpty: return getEmptyRecordGqlOperationFilter( - rawUIFilter.operand, + filter.operand, correspondingField, - rawUIFilter.definition, + filter.definition, ); default: throw new Error( - `Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`, + `Unknown operand ${filter.operand} for ${filter.definition.type} filter`, ); } case 'LINKS': { const linksFilters = generateILikeFiltersForCompositeFields( - rawUIFilter.value, + filter.value, correspondingField.name, ['primaryLinkLabel', 'primaryLinkUrl'], ); - switch (rawUIFilter.operand) { + switch (filter.operand) { case ViewFilterOperand.Contains: if (!isCompositeFieldFiter) { return { @@ -369,7 +365,7 @@ export const filterToRecordGqlOperationFilter = ( return { [correspondingField.name]: { [compositeFieldName]: { - ilike: `%${rawUIFilter.value}%`, + ilike: `%${filter.value}%`, }, }, }; @@ -388,7 +384,7 @@ export const filterToRecordGqlOperationFilter = ( not: { [correspondingField.name]: { [compositeFieldName]: { - ilike: `%${rawUIFilter.value}%`, + ilike: `%${filter.value}%`, }, }, }, @@ -397,23 +393,23 @@ export const filterToRecordGqlOperationFilter = ( case ViewFilterOperand.IsEmpty: case ViewFilterOperand.IsNotEmpty: return getEmptyRecordGqlOperationFilter( - rawUIFilter.operand, + filter.operand, correspondingField, - rawUIFilter.definition, + filter.definition, ); default: throw new Error( - `Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`, + `Unknown operand ${filter.operand} for ${filter.definition.type} filter`, ); } } case 'FULL_NAME': { const fullNameFilters = generateILikeFiltersForCompositeFields( - rawUIFilter.value, + filter.value, correspondingField.name, ['firstName', 'lastName'], ); - switch (rawUIFilter.operand) { + switch (filter.operand) { case ViewFilterOperand.Contains: if (!isCompositeFieldFiter) { return { @@ -423,7 +419,7 @@ export const filterToRecordGqlOperationFilter = ( return { [correspondingField.name]: { [compositeFieldName]: { - ilike: `%${rawUIFilter.value}%`, + ilike: `%${filter.value}%`, }, }, }; @@ -442,7 +438,7 @@ export const filterToRecordGqlOperationFilter = ( not: { [correspondingField.name]: { [compositeFieldName]: { - ilike: `%${rawUIFilter.value}%`, + ilike: `%${filter.value}%`, }, }, }, @@ -451,18 +447,18 @@ export const filterToRecordGqlOperationFilter = ( case ViewFilterOperand.IsEmpty: case ViewFilterOperand.IsNotEmpty: return getEmptyRecordGqlOperationFilter( - rawUIFilter.operand, + filter.operand, correspondingField, - rawUIFilter.definition, + filter.definition, ); default: throw new Error( - `Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`, + `Unknown operand ${filter.operand} for ${filter.definition.type} filter`, ); } } case 'ADDRESS': - switch (rawUIFilter.operand) { + switch (filter.operand) { case ViewFilterOperand.Contains: if (!isCompositeFieldFiter) { return { @@ -470,42 +466,42 @@ export const filterToRecordGqlOperationFilter = ( { [correspondingField.name]: { addressStreet1: { - ilike: `%${rawUIFilter.value}%`, + ilike: `%${filter.value}%`, }, } as AddressFilter, }, { [correspondingField.name]: { addressStreet2: { - ilike: `%${rawUIFilter.value}%`, + ilike: `%${filter.value}%`, }, } as AddressFilter, }, { [correspondingField.name]: { addressCity: { - ilike: `%${rawUIFilter.value}%`, + ilike: `%${filter.value}%`, }, } as AddressFilter, }, { [correspondingField.name]: { addressState: { - ilike: `%${rawUIFilter.value}%`, + ilike: `%${filter.value}%`, }, } as AddressFilter, }, { [correspondingField.name]: { addressCountry: { - ilike: `%${rawUIFilter.value}%`, + ilike: `%${filter.value}%`, }, } as AddressFilter, }, { [correspondingField.name]: { addressPostcode: { - ilike: `%${rawUIFilter.value}%`, + ilike: `%${filter.value}%`, }, } as AddressFilter, }, @@ -515,7 +511,7 @@ export const filterToRecordGqlOperationFilter = ( return { [correspondingField.name]: { [compositeFieldName]: { - ilike: `%${rawUIFilter.value}%`, + ilike: `%${filter.value}%`, } as AddressFilter, }, }; @@ -528,7 +524,7 @@ export const filterToRecordGqlOperationFilter = ( not: { [correspondingField.name]: { addressStreet1: { - ilike: `%${rawUIFilter.value}%`, + ilike: `%${filter.value}%`, }, } as AddressFilter, }, @@ -537,7 +533,7 @@ export const filterToRecordGqlOperationFilter = ( not: { [correspondingField.name]: { addressStreet2: { - ilike: `%${rawUIFilter.value}%`, + ilike: `%${filter.value}%`, }, } as AddressFilter, }, @@ -546,7 +542,7 @@ export const filterToRecordGqlOperationFilter = ( not: { [correspondingField.name]: { addressCity: { - ilike: `%${rawUIFilter.value}%`, + ilike: `%${filter.value}%`, }, } as AddressFilter, }, @@ -558,7 +554,7 @@ export const filterToRecordGqlOperationFilter = ( not: { [correspondingField.name]: { [compositeFieldName]: { - ilike: `%${rawUIFilter.value}%`, + ilike: `%${filter.value}%`, } as AddressFilter, }, }, @@ -567,24 +563,24 @@ export const filterToRecordGqlOperationFilter = ( case ViewFilterOperand.IsEmpty: case ViewFilterOperand.IsNotEmpty: return getEmptyRecordGqlOperationFilter( - rawUIFilter.operand, + filter.operand, correspondingField, - rawUIFilter.definition, + filter.definition, ); default: throw new Error( - `Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`, + `Unknown operand ${filter.operand} for ${filter.definition.type} filter`, ); } case 'SELECT': { if (isEmptyOperand) { return getEmptyRecordGqlOperationFilter( - rawUIFilter.operand, + filter.operand, correspondingField, - rawUIFilter.definition, + filter.definition, ); } - const stringifiedSelectValues = rawUIFilter.value; + const stringifiedSelectValues = filter.value; let parsedOptionValues: string[] = []; if (!isNonEmptyString(stringifiedSelectValues)) { @@ -600,7 +596,7 @@ export const filterToRecordGqlOperationFilter = ( } if (parsedOptionValues.length > 0) { - switch (rawUIFilter.operand) { + switch (filter.operand) { case ViewFilterOperand.Is: return { [correspondingField.name]: { @@ -617,7 +613,7 @@ export const filterToRecordGqlOperationFilter = ( }; default: throw new Error( - `Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`, + `Unknown operand ${filter.operand} for ${filter.definition.type} filter`, ); } } @@ -625,9 +621,9 @@ export const filterToRecordGqlOperationFilter = ( } // TODO: fix this with a new composite field in ViewFilter entity case 'ACTOR': { - switch (rawUIFilter.operand) { + switch (filter.operand) { case ViewFilterOperand.Is: { - const parsedRecordIds = JSON.parse(rawUIFilter.value) as string[]; + const parsedRecordIds = JSON.parse(filter.value) as string[]; return { [correspondingField.name]: { @@ -638,7 +634,7 @@ export const filterToRecordGqlOperationFilter = ( }; } case ViewFilterOperand.IsNot: { - const parsedRecordIds = JSON.parse(rawUIFilter.value) as string[]; + const parsedRecordIds = JSON.parse(filter.value) as string[]; if (parsedRecordIds.length > 0) { return { @@ -659,7 +655,7 @@ export const filterToRecordGqlOperationFilter = ( { [correspondingField.name]: { name: { - ilike: `%${rawUIFilter.value}%`, + ilike: `%${filter.value}%`, }, } as ActorFilter, }, @@ -672,7 +668,7 @@ export const filterToRecordGqlOperationFilter = ( not: { [correspondingField.name]: { name: { - ilike: `%${rawUIFilter.value}%`, + ilike: `%${filter.value}%`, }, } as ActorFilter, }, @@ -682,26 +678,26 @@ export const filterToRecordGqlOperationFilter = ( case ViewFilterOperand.IsEmpty: case ViewFilterOperand.IsNotEmpty: return getEmptyRecordGqlOperationFilter( - rawUIFilter.operand, + filter.operand, correspondingField, - rawUIFilter.definition, + filter.definition, ); default: throw new Error( - `Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.label} filter`, + `Unknown operand ${filter.operand} for ${filter.definition.label} filter`, ); } break; } case 'EMAILS': - switch (rawUIFilter.operand) { + switch (filter.operand) { case ViewFilterOperand.Contains: return { or: [ { [correspondingField.name]: { primaryEmail: { - ilike: `%${rawUIFilter.value}%`, + ilike: `%${filter.value}%`, }, } as EmailsFilter, }, @@ -714,7 +710,7 @@ export const filterToRecordGqlOperationFilter = ( not: { [correspondingField.name]: { primaryEmail: { - ilike: `%${rawUIFilter.value}%`, + ilike: `%${filter.value}%`, }, } as EmailsFilter, }, @@ -724,22 +720,22 @@ export const filterToRecordGqlOperationFilter = ( case ViewFilterOperand.IsEmpty: case ViewFilterOperand.IsNotEmpty: return getEmptyRecordGqlOperationFilter( - rawUIFilter.operand, + filter.operand, correspondingField, - rawUIFilter.definition, + filter.definition, ); default: throw new Error( - `Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`, + `Unknown operand ${filter.operand} for ${filter.definition.type} filter`, ); } case 'PHONES': { const phonesFilters = generateILikeFiltersForCompositeFields( - rawUIFilter.value, + filter.value, correspondingField.name, ['primaryPhoneNumber', 'primaryPhoneCountryCode'], ); - switch (rawUIFilter.operand) { + switch (filter.operand) { case ViewFilterOperand.Contains: return { or: phonesFilters, @@ -755,13 +751,13 @@ export const filterToRecordGqlOperationFilter = ( case ViewFilterOperand.IsEmpty: case ViewFilterOperand.IsNotEmpty: return getEmptyRecordGqlOperationFilter( - rawUIFilter.operand, + filter.operand, correspondingField, - rawUIFilter.definition, + filter.definition, ); default: throw new Error( - `Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`, + `Unknown operand ${filter.operand} for ${filter.definition.type} filter`, ); } } @@ -770,7 +766,7 @@ export const filterToRecordGqlOperationFilter = ( } }; -const viewFilterGroupToRecordGqlOperationFilter = ( +const computeViewFilterGroupRecordGqlOperationFilter = ( filters: Filter[], fields: Pick[], viewFilterGroups: ViewFilterGroup[], @@ -791,7 +787,7 @@ const viewFilterGroupToRecordGqlOperationFilter = ( ); const groupRecordGqlOperationFilters = groupFilters - .map((filter) => filterToRecordGqlOperationFilter(filter, fields)) + .map((filter) => computeFilterRecordGqlOperationFilter(filter, fields)) .filter(isDefined); const subGroupRecordGqlOperationFilters = viewFilterGroups @@ -800,7 +796,7 @@ const viewFilterGroupToRecordGqlOperationFilter = ( viewFilterGroup.parentViewFilterGroupId === viewFilterGroupId, ) .map((subViewFilterGroup) => - viewFilterGroupToRecordGqlOperationFilter( + computeViewFilterGroupRecordGqlOperationFilter( filters, fields, viewFilterGroups, @@ -832,10 +828,10 @@ const viewFilterGroupToRecordGqlOperationFilter = ( } }; -export const filtersToRecordGqlOperationFilter = ( +export const computeViewRecordGqlOperationFilter = ( filters: Filter[], fields: Pick[], - viewFilterGroups?: ViewFilterGroup[], // TODO: Make required + viewFilterGroups: ViewFilterGroup[], ): RecordGqlOperationFilter => { if (!viewFilterGroups) { throw new Error('viewFilterGroups are required'); @@ -844,12 +840,12 @@ export const filtersToRecordGqlOperationFilter = ( const regularRecordGqlOperationFilter: RecordGqlOperationFilter[] = filters .filter((filter) => !filter.viewFilterGroupId) .map((regularFilter) => - filterToRecordGqlOperationFilter(regularFilter, fields), + computeFilterRecordGqlOperationFilter(regularFilter, fields), ) .filter(isDefined); const advancedRecordGqlOperationFilter = - viewFilterGroupToRecordGqlOperationFilter( + computeViewFilterGroupRecordGqlOperationFilter( filters, fields, viewFilterGroups, diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainer.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainer.tsx index 5b5381a921ba..28901ebd219c 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainer.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainer.tsx @@ -28,6 +28,7 @@ import { ActionMenuConfirmationModals } from '@/action-menu/components/ActionMen import { ActionMenuDropdown } from '@/action-menu/components/ActionMenuDropdown'; import { ActionMenuEffect } from '@/action-menu/components/ActionMenuEffect'; import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext'; +import { recordIndexViewFilterGroupsState } from '@/object-record/record-index/states/recordIndexViewFilterGroupsState'; import { ViewBar } from '@/views/components/ViewBar'; import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; import { ViewField } from '@/views/types/ViewField'; @@ -67,6 +68,9 @@ export const RecordIndexContainer = () => { const { columnDefinitions, filterDefinitions, sortDefinitions } = useColumnDefinitionsFromFieldMetadata(objectMetadataItem); + const setRecordIndexViewFilterGroups = useSetRecoilState( + recordIndexViewFilterGroupsState, + ); const setRecordIndexFilters = useSetRecoilState(recordIndexFiltersState); const setRecordIndexSorts = useSetRecoilState(recordIndexSortsState); const setRecordIndexIsCompactModeActive = useSetRecoilState( @@ -138,6 +142,7 @@ export const RecordIndexContainer = () => { setTableFilters( mapViewFiltersToFilters(view.viewFilters, filterDefinitions), ); + setRecordIndexViewFilterGroups(view.viewFilterGroups ?? []); setRecordIndexFilters( mapViewFiltersToFilters(view.viewFilters, filterDefinitions), ); diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoard.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoard.ts index d726f9a7a150..d1e598c8e121 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoard.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoard.ts @@ -5,12 +5,13 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy'; import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard'; -import { filtersToRecordGqlOperationFilter } from '@/object-record/record-filter/utils/turnObjectDropdownFilterIntoQueryFilter'; +import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter'; import { useRecordBoardRecordGqlFields } from '@/object-record/record-index/hooks/useRecordBoardRecordGqlFields'; import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState'; import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState'; import { recordIndexIsCompactModeActiveState } from '@/object-record/record-index/states/recordIndexIsCompactModeActiveState'; import { recordIndexSortsState } from '@/object-record/record-index/states/recordIndexSortsState'; +import { recordIndexViewFilterGroupsState } from '@/object-record/record-index/states/recordIndexViewFilterGroupsState'; import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore'; import { useSetRecordCountInCurrentView } from '@/views/hooks/useSetRecordCountInCurrentView'; @@ -42,11 +43,15 @@ export const useLoadRecordIndexBoard = ({ setFieldDefinitions(recordIndexFieldDefinitions); }, [recordIndexFieldDefinitions, setFieldDefinitions]); + const recordIndexViewFilterGroups = useRecoilValue( + recordIndexViewFilterGroupsState, + ); const recordIndexFilters = useRecoilValue(recordIndexFiltersState); const recordIndexSorts = useRecoilValue(recordIndexSortsState); - const requestFilters = filtersToRecordGqlOperationFilter( + const requestFilters = computeViewRecordGqlOperationFilter( recordIndexFilters, objectMetadataItem?.fields ?? [], + recordIndexViewFilterGroups, ); const orderBy = turnSortsIntoOrderBy(objectMetadataItem, recordIndexSorts); 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 ca58004ad44d..40cf9c9bb7a2 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 @@ -5,10 +5,11 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy'; import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard'; -import { filtersToRecordGqlOperationFilter } from '@/object-record/record-filter/utils/turnObjectDropdownFilterIntoQueryFilter'; +import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter'; import { useRecordBoardRecordGqlFields } from '@/object-record/record-index/hooks/useRecordBoardRecordGqlFields'; import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState'; import { recordIndexSortsState } from '@/object-record/record-index/states/recordIndexSortsState'; +import { recordIndexViewFilterGroupsState } from '@/object-record/record-index/states/recordIndexViewFilterGroupsState'; import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore'; import { isDefined } from '~/utils/isDefined'; @@ -33,11 +34,15 @@ export const useLoadRecordIndexBoardColumn = ({ const { setRecordIdsForColumn } = useRecordBoard(recordBoardId); const { upsertRecords: upsertRecordsInStore } = useUpsertRecordsInStore(); + const recordIndexViewFilterGroups = useRecoilValue( + recordIndexViewFilterGroupsState, + ); const recordIndexFilters = useRecoilValue(recordIndexFiltersState); const recordIndexSorts = useRecoilValue(recordIndexSortsState); - const requestFilters = filtersToRecordGqlOperationFilter( + const requestFilters = computeViewRecordGqlOperationFilter( recordIndexFilters, objectMetadataItem?.fields ?? [], + recordIndexViewFilterGroups, ); const orderBy = turnSortsIntoOrderBy(objectMetadataItem, recordIndexSorts); diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexTable.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexTable.ts index 0453113ba971..92ec0290997d 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexTable.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexTable.ts @@ -5,7 +5,7 @@ import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy'; -import { filtersToRecordGqlOperationFilter } from '@/object-record/record-filter/utils/turnObjectDropdownFilterIntoQueryFilter'; +import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter'; import { useRecordTableRecordGqlFields } from '@/object-record/record-index/hooks/useRecordTableRecordGqlFields'; import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates'; import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable'; @@ -28,7 +28,7 @@ export const useFindManyParams = ( const tableFilters = useRecoilValue(tableFiltersState); const tableSorts = useRecoilValue(tableSortsState); - const filter = filtersToRecordGqlOperationFilter( + const filter = computeViewRecordGqlOperationFilter( tableFilters, objectMetadataItem?.fields ?? [], tableViewFilterGroups, diff --git a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexViewFilterGroupsState.ts b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexViewFilterGroupsState.ts new file mode 100644 index 000000000000..978614756ca7 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexViewFilterGroupsState.ts @@ -0,0 +1,7 @@ +import { ViewFilterGroup } from '@/views/types/ViewFilterGroup'; +import { createState } from 'twenty-ui'; + +export const recordIndexViewFilterGroupsState = createState({ + key: 'recordIndexViewFilterGroupsState', + defaultValue: [], +}); diff --git a/packages/twenty-front/src/modules/views/utils/getQueryVariablesFromView.ts b/packages/twenty-front/src/modules/views/utils/getQueryVariablesFromView.ts index c67d2dafbc53..1f7bc82743a9 100644 --- a/packages/twenty-front/src/modules/views/utils/getQueryVariablesFromView.ts +++ b/packages/twenty-front/src/modules/views/utils/getQueryVariablesFromView.ts @@ -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 { filtersToRecordGqlOperationFilter } from '@/object-record/record-filter/utils/turnObjectDropdownFilterIntoQueryFilter'; +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'; @@ -25,7 +25,7 @@ export const getQueryVariablesFromView = ({ }; } - const { viewFilters, viewSorts } = view; + const { viewFilterGroups, viewFilters, viewSorts } = view; const filterDefinitions = formatFieldMetadataItemsAsFilterDefinitions({ fields: fieldMetadataItems, @@ -35,9 +35,10 @@ export const getQueryVariablesFromView = ({ fields: fieldMetadataItems, }); - const filter = filtersToRecordGqlOperationFilter( + const filter = computeViewRecordGqlOperationFilter( mapViewFiltersToFilters(viewFilters, filterDefinitions), objectMetadataItem?.fields ?? [], + viewFilterGroups ?? [], ); const orderBy = turnSortsIntoOrderBy(