Feat/complete filter order by types (#2943)

* Fixed orderBy bug

* Fixed gitch select multiple record filter

* Fixed RelationPicker search

* Fixed OrderBy type

* WIP

* Finished RequestFilter typing

* Finished RequestFilter type

* Fixed missing import

* Changed naming
This commit is contained in:
Lucas Bordeau
2023-12-12 15:56:21 +01:00
committed by GitHub
parent 8381869c7f
commit 2a4ab2ffd3
10 changed files with 334 additions and 354 deletions

View File

@ -1,4 +1,5 @@
import { Note } from '@/activities/types/Note'; import { Note } from '@/activities/types/Note';
import { OrderByField } from '@/object-metadata/types/OrderByField';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { ActivityTargetableEntity } from '../../types/ActivityTargetableEntity'; import { ActivityTargetableEntity } from '../../types/ActivityTargetableEntity';
@ -19,7 +20,7 @@ export const useNotes = (entity: ActivityTargetableEntity) => {
}; };
const orderBy = { const orderBy = {
createdAt: 'AscNullsFirst', createdAt: 'AscNullsFirst',
} as any; // TODO: finish typing } as OrderByField;
const { records: notes } = useFindManyRecords({ const { records: notes } = useFindManyRecords({
skip: !activityTargets?.length, skip: !activityTargets?.length,

View File

@ -25,6 +25,7 @@ import {
PaginatedRecordTypeResults, PaginatedRecordTypeResults,
} from '../types/PaginatedRecordTypeResults'; } from '../types/PaginatedRecordTypeResults';
import { mapPaginatedRecordsToRecords } from '../utils/mapPaginatedRecordsToRecords'; import { mapPaginatedRecordsToRecords } from '../utils/mapPaginatedRecordsToRecords';
import { ObjectRecordFilter } from '@/object-record/types/ObjectRecordFilter';
export const useFindManyRecords = < export const useFindManyRecords = <
RecordType extends { id: string } & Record<string, any>, RecordType extends { id: string } & Record<string, any>,
@ -36,7 +37,7 @@ export const useFindManyRecords = <
onCompleted, onCompleted,
skip, skip,
}: ObjectMetadataItemIdentifier & { }: ObjectMetadataItemIdentifier & {
filter?: any; filter?: ObjectRecordFilter;
orderBy?: OrderByField; orderBy?: OrderByField;
limit?: number; limit?: number;
onCompleted?: (data: PaginatedRecordTypeResults<RecordType>) => void; onCompleted?: (data: PaginatedRecordTypeResults<RecordType>) => void;

View File

@ -1,104 +0,0 @@
import { useCallback } from 'react';
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
import { Company } from '@/companies/types/Company';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { turnFiltersIntoWhereClause } from '@/object-record/object-filter-dropdown/utils/turnFiltersIntoWhereClause';
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
import { useRecordBoardScopedStates } from '@/object-record/record-board/hooks/internal/useRecordBoardScopedStates';
import { PaginatedRecordTypeResults } from '@/object-record/types/PaginatedRecordTypeResults';
import { Opportunity } from '@/pipeline/types/Opportunity';
import { PipelineStep } from '@/pipeline/types/PipelineStep';
import { useFindManyRecords } from './useFindManyRecords';
export const useObjectRecordBoard = () => {
const objectNameSingular = 'opportunity';
const { objectMetadataItem: foundObjectMetadataItem } = useObjectMetadataItem(
{
objectNameSingular,
},
);
const {
isBoardLoadedState,
boardFiltersState,
boardSortsState,
savedCompaniesState,
savedOpportunitiesState,
savedPipelineStepsState,
} = useRecordBoardScopedStates();
const setIsBoardLoaded = useSetRecoilState(isBoardLoadedState);
const boardFilters = useRecoilValue(boardFiltersState);
const boardSorts = useRecoilValue(boardSortsState);
const setSavedCompanies = useSetRecoilState(savedCompaniesState);
const [savedOpportunities] = useRecoilState(savedOpportunitiesState);
const [savedPipelineSteps, setSavedPipelineSteps] = useRecoilState(
savedPipelineStepsState,
);
const filter = turnFiltersIntoWhereClause(
boardFilters,
foundObjectMetadataItem?.fields ?? [],
);
const orderBy = turnSortsIntoOrderBy(
boardSorts,
foundObjectMetadataItem?.fields ?? [],
);
useFindManyRecords({
objectNameSingular: 'pipelineStep',
filter: {},
onCompleted: useCallback(
(data: PaginatedRecordTypeResults<PipelineStep>) => {
setSavedPipelineSteps(data.edges.map((edge) => edge.node));
},
[setSavedPipelineSteps],
),
});
const {
records: opportunities,
loading,
fetchMoreRecords: fetchMoreOpportunities,
} = useFindManyRecords<Opportunity>({
skip: !savedPipelineSteps.length,
objectNameSingular: 'opportunity',
filter: filter,
orderBy: orderBy as any, // TODO: finish typing
onCompleted: useCallback(() => {
setIsBoardLoaded(true);
}, [setIsBoardLoaded]),
});
const { fetchMoreRecords: fetchMoreCompanies } = useFindManyRecords({
skip: !savedOpportunities.length,
objectNameSingular: 'company',
filter: {
id: {
in: savedOpportunities.map(
(opportunity) => opportunity.companyId || '',
),
},
},
onCompleted: useCallback(
(data: PaginatedRecordTypeResults<Company>) => {
setSavedCompanies(data.edges.map((edge) => edge.node));
},
[setSavedCompanies],
),
});
return {
opportunities,
loading,
fetchMoreOpportunities,
fetchMoreCompanies,
};
};

View File

@ -3,7 +3,7 @@ import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
import { Company } from '@/companies/types/Company'; import { Company } from '@/companies/types/Company';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { turnFiltersIntoWhereClause } from '@/object-record/object-filter-dropdown/utils/turnFiltersIntoWhereClause'; import { turnFiltersIntoObjectRecordFilters } from '@/object-record/utils/turnFiltersIntoWhereClause';
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy'; import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
import { useRecordBoardScopedStates } from '@/object-record/record-board/hooks/internal/useRecordBoardScopedStates'; import { useRecordBoardScopedStates } from '@/object-record/record-board/hooks/internal/useRecordBoardScopedStates';
import { PaginatedRecordTypeResults } from '@/object-record/types/PaginatedRecordTypeResults'; import { PaginatedRecordTypeResults } from '@/object-record/types/PaginatedRecordTypeResults';
@ -43,7 +43,7 @@ export const useObjectRecordBoard = () => {
savedPipelineStepsState, savedPipelineStepsState,
); );
const filter = turnFiltersIntoWhereClause( const filter = turnFiltersIntoObjectRecordFilters(
boardFilters, boardFilters,
foundObjectMetadataItem?.fields ?? [], foundObjectMetadataItem?.fields ?? [],
); );
@ -70,8 +70,8 @@ export const useObjectRecordBoard = () => {
} = useFindManyRecords<Opportunity>({ } = useFindManyRecords<Opportunity>({
skip: !savedPipelineSteps.length, skip: !savedPipelineSteps.length,
objectNameSingular: 'opportunity', objectNameSingular: 'opportunity',
filter: filter, filter,
orderBy: orderBy as any, // TODO: finish typing orderBy,
onCompleted: useCallback(() => { onCompleted: useCallback(() => {
setIsBoardLoaded(true); setIsBoardLoaded(true);
}, [setIsBoardLoaded]), }, [setIsBoardLoaded]),

View File

@ -3,7 +3,7 @@ import { useRecoilValue, useSetRecoilState } from 'recoil';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural'; import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
import { turnFiltersIntoWhereClause } from '@/object-record/object-filter-dropdown/utils/turnFiltersIntoWhereClause'; import { turnFiltersIntoObjectRecordFilters } from '@/object-record/utils/turnFiltersIntoWhereClause';
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy'; import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
import { useRecordTableScopedStates } from '@/object-record/record-table/hooks/internal/useRecordTableScopedStates'; import { useRecordTableScopedStates } from '@/object-record/record-table/hooks/internal/useRecordTableScopedStates';
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable'; import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
@ -31,21 +31,20 @@ export const useObjectRecordTable = () => {
const tableSorts = useRecoilValue(tableSortsState); const tableSorts = useRecoilValue(tableSortsState);
const setLastRowVisible = useSetRecoilState(tableLastRowVisibleState); const setLastRowVisible = useSetRecoilState(tableLastRowVisibleState);
const filter = turnFiltersIntoWhereClause( const requestFilters = turnFiltersIntoObjectRecordFilters(
tableFilters, tableFilters,
foundObjectMetadataItem?.fields ?? [], foundObjectMetadataItem?.fields ?? [],
); );
// TODO: finish typing
const orderBy = turnSortsIntoOrderBy( const orderBy = turnSortsIntoOrderBy(
tableSorts, tableSorts,
foundObjectMetadataItem?.fields ?? [], foundObjectMetadataItem?.fields ?? [],
) as any; );
const { records, loading, fetchMoreRecords, queryStateIdentifier } = const { records, loading, fetchMoreRecords, queryStateIdentifier } =
useFindManyRecords({ useFindManyRecords({
objectNameSingular, objectNameSingular,
filter, filter: requestFilters,
orderBy, orderBy,
onCompleted: () => { onCompleted: () => {
setLastRowVisible(false); setLastRowVisible(false);

View File

@ -1,237 +0,0 @@
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { Field } from '~/generated/graphql';
import { Filter } from '../types/Filter';
type FilterToTurnIntoWhereClause = Omit<Filter, 'definition'> & {
definition: {
type: Filter['definition']['type'];
};
};
export const turnFiltersIntoWhereClause = (
filters: FilterToTurnIntoWhereClause[],
fields: Pick<Field, 'id' | 'name'>[],
) => {
const whereClause: any[] = [];
filters.forEach((filter) => {
const correspondingField = fields.find(
(field) => field.id === filter.fieldMetadataId,
);
if (!correspondingField) {
throw new Error(
`Could not find field ${filter.fieldMetadataId} in metadata object`,
);
}
switch (filter.definition.type) {
case 'EMAIL':
case 'PHONE':
case 'TEXT':
switch (filter.operand) {
case ViewFilterOperand.Contains:
whereClause.push({
[correspondingField.name]: {
ilike: `%${filter.value}%`,
},
});
return;
case ViewFilterOperand.DoesNotContain:
whereClause.push({
not: {
[correspondingField.name]: {
ilike: `%${filter.value}%`,
},
},
});
return;
default:
throw new Error(
`Unknown operand ${filter.operand} for ${filter.definition.type} filter`,
);
}
case 'DATE_TIME':
switch (filter.operand) {
case ViewFilterOperand.GreaterThan:
whereClause.push({
[correspondingField.name]: {
gte: filter.value,
},
});
return;
case ViewFilterOperand.LessThan:
whereClause.push({
[correspondingField.name]: {
lte: filter.value,
},
});
return;
default:
throw new Error(
`Unknown operand ${filter.operand} for ${filter.definition.type} filter`,
);
}
case 'NUMBER':
switch (filter.operand) {
case ViewFilterOperand.GreaterThan:
whereClause.push({
[correspondingField.name]: {
gte: parseFloat(filter.value),
},
});
return;
case ViewFilterOperand.LessThan:
whereClause.push({
[correspondingField.name]: {
lte: parseFloat(filter.value),
},
});
return;
default:
throw new Error(
`Unknown operand ${filter.operand} for ${filter.definition.type} filter`,
);
}
case 'RELATION':
try {
JSON.parse(filter.value);
} catch (e) {
throw new Error(
`Cannot parse filter value for RELATION filter : "${filter.value}"`,
);
}
const parsedRecordIds = JSON.parse(filter.value) as string[];
if (parsedRecordIds.length > 0) {
switch (filter.operand) {
case ViewFilterOperand.Is:
whereClause.push({
[correspondingField.name + 'Id']: {
in: parsedRecordIds,
},
});
return;
case ViewFilterOperand.IsNot:
whereClause.push({
not: {
[correspondingField.name + 'Id']: {
in: parsedRecordIds,
},
},
});
return;
default:
throw new Error(
`Unknown operand ${filter.operand} for ${filter.definition.type} filter`,
);
}
}
break;
case 'CURRENCY':
switch (filter.operand) {
case ViewFilterOperand.GreaterThan:
whereClause.push({
[correspondingField.name]: {
amountMicros: { gte: parseFloat(filter.value) * 1000000 },
},
});
return;
case ViewFilterOperand.LessThan:
whereClause.push({
[correspondingField.name]: {
amountMicros: { lte: parseFloat(filter.value) * 1000000 },
},
});
return;
default:
throw new Error(
`Unknown operand ${filter.operand} for ${filter.definition.type} filter`,
);
}
case 'LINK':
switch (filter.operand) {
case ViewFilterOperand.Contains:
whereClause.push({
[correspondingField.name]: {
url: {
ilike: `%${filter.value}%`,
},
},
});
return;
case ViewFilterOperand.DoesNotContain:
whereClause.push({
not: {
[correspondingField.name]: {
url: {
ilike: `%${filter.value}%`,
},
},
},
});
return;
default:
throw new Error(
`Unknown operand ${filter.operand} for ${filter.definition.type} filter`,
);
}
case 'FULL_NAME':
switch (filter.operand) {
case ViewFilterOperand.Contains:
whereClause.push({
or: [
{
[correspondingField.name]: {
firstName: {
ilike: `%${filter.value}%`,
},
},
},
{
[correspondingField.name]: {
firstName: {
ilike: `%${filter.value}%`,
},
},
},
],
});
return;
case ViewFilterOperand.DoesNotContain:
whereClause.push({
and: [
{
not: {
[correspondingField.name]: {
firstName: {
ilike: `%${filter.value}%`,
},
},
},
},
{
not: {
[correspondingField.name]: {
lastName: {
ilike: `%${filter.value}%`,
},
},
},
},
],
});
return;
default:
throw new Error(
`Unknown operand ${filter.operand} for ${filter.definition.type} filter`,
);
}
default:
throw new Error('Unknown filter type');
}
});
return { and: whereClause };
};

View File

@ -1,3 +1,4 @@
import { OrderByField } from '@/object-metadata/types/OrderByField';
import { Field } from '~/generated/graphql'; import { Field } from '~/generated/graphql';
import { Sort } from '../types/Sort'; import { Sort } from '../types/Sort';
@ -5,8 +6,9 @@ import { Sort } from '../types/Sort';
export const turnSortsIntoOrderBy = ( export const turnSortsIntoOrderBy = (
sorts: Sort[], sorts: Sort[],
fields: Pick<Field, 'id' | 'name'>[], fields: Pick<Field, 'id' | 'name'>[],
) => { ): OrderByField => {
const sortsObject: Record<string, 'AscNullsFirst' | 'DescNullsLast'> = {}; const sortsObject: Record<string, 'AscNullsFirst' | 'DescNullsLast'> = {};
if (!sorts.length) { if (!sorts.length) {
const createdAtField = fields.find((field) => field.name === 'createdAt'); const createdAtField = fields.find((field) => field.name === 'createdAt');
if (createdAtField) { if (createdAtField) {
@ -23,6 +25,7 @@ export const turnSortsIntoOrderBy = (
[fields[0].name]: 'DescNullsFirst', [fields[0].name]: 'DescNullsFirst',
}; };
} }
sorts.forEach((sort) => { sorts.forEach((sort) => {
const correspondingField = fields.find( const correspondingField = fields.find(
(field) => field.id === sort.fieldMetadataId, (field) => field.id === sort.fieldMetadataId,

View File

@ -1,7 +1,7 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useRecoilState, useRecoilValue } from 'recoil'; import { useRecoilState, useRecoilValue } from 'recoil';
import { useObjectRecordBoard } from '@/object-record/hooks/useObjectRecordBoard.1'; import { useObjectRecordBoard } from '@/object-record/hooks/useObjectRecordBoard';
import { useRecordBoardActionBarEntriesInternal } from '@/object-record/record-board/hooks/internal/useRecordBoardActionBarEntriesInternal'; import { useRecordBoardActionBarEntriesInternal } from '@/object-record/record-board/hooks/internal/useRecordBoardActionBarEntriesInternal';
import { useRecordBoardContextMenuEntriesInternal } from '@/object-record/record-board/hooks/internal/useRecordBoardContextMenuEntriesInternal'; import { useRecordBoardContextMenuEntriesInternal } from '@/object-record/record-board/hooks/internal/useRecordBoardContextMenuEntriesInternal';
import { useRecordBoardScopedStates } from '@/object-record/record-board/hooks/internal/useRecordBoardScopedStates'; import { useRecordBoardScopedStates } from '@/object-record/record-board/hooks/internal/useRecordBoardScopedStates';

View File

@ -0,0 +1,72 @@
export type UUIDFilterValue = string;
export type IsFilter = 'NULL' | 'NOT_NULL';
export type UUIDFilter = {
eq?: UUIDFilterValue;
in?: UUIDFilterValue[];
neq?: UUIDFilterValue;
is?: IsFilter;
};
export type StringFilter = {
eq?: string;
gt?: string;
gte?: string;
in?: string[];
lt?: string;
lte?: string;
neq?: string;
startsWith?: string;
like?: string;
ilike?: string;
regex?: string;
iregex?: string;
is?: IsFilter;
};
export type FloatFilter = {
eq?: number;
gt?: number;
gte?: number;
in?: number[];
lt?: number;
lte?: number;
neq?: number;
is?: IsFilter;
};
export type DateFilter = {
eq?: string;
gt?: string;
gte?: string;
in?: string[];
lt?: string;
lte?: string;
neq?: string;
is?: IsFilter;
};
export type CurrencyFilter = {
amountMicros?: FloatFilter;
};
export type URLFilter = {
url?: StringFilter;
};
export type FullNameFilter = {
firstName?: StringFilter;
lastName?: StringFilter;
};
export type LeafFilter = UUIDFilter | StringFilter | FloatFilter | DateFilter | CurrencyFilter | URLFilter | FullNameFilter
export type ObjectRecordFilter = {
and?: ObjectRecordFilter[];
or?: ObjectRecordFilter[];
not?: ObjectRecordFilter;
} | {
[fieldName: string]: LeafFilter
}

View File

@ -0,0 +1,245 @@
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { Field } from '~/generated/graphql';
import { Filter } from '../object-filter-dropdown/types/Filter';
import { CurrencyFilter, DateFilter, FloatFilter, FullNameFilter, ObjectRecordFilter, StringFilter, URLFilter } from '@/object-record/types/ObjectRecordFilter';
export type RawUIFilter = Omit<Filter, 'definition'> & {
definition: {
type: Filter['definition']['type'];
};
};
export const turnFiltersIntoObjectRecordFilters = (
rawUIFilters: RawUIFilter[],
fields: Pick<Field, 'id' | 'name'>[],
): ObjectRecordFilter => {
const objectRecordFilters: ObjectRecordFilter[] = [];
for(const rawUIFilter of rawUIFilters) {
const correspondingField = fields.find(
(field) => field.id === rawUIFilter.fieldMetadataId,
);
if (!correspondingField) {
throw new Error(
`Could not find field ${rawUIFilter.fieldMetadataId} in metadata object`,
);
}
switch (rawUIFilter.definition.type) {
case 'EMAIL':
case 'PHONE':
case 'TEXT':
switch (rawUIFilter.operand) {
case ViewFilterOperand.Contains:
objectRecordFilters.push({
[correspondingField.name]: {
ilike: `%${rawUIFilter.value}%`,
} as StringFilter,
});
break
case ViewFilterOperand.DoesNotContain:
objectRecordFilters.push({
not: {
[correspondingField.name]: {
ilike: `%${rawUIFilter.value}%`,
} as StringFilter,
},
});
break
default:
throw new Error(
`Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`,
);
}
break;
case 'DATE_TIME':
switch (rawUIFilter.operand) {
case ViewFilterOperand.GreaterThan:
objectRecordFilters.push({
[correspondingField.name]: {
gte: rawUIFilter.value,
} as DateFilter,
});
break;
case ViewFilterOperand.LessThan:
objectRecordFilters.push({
[correspondingField.name]: {
lte: rawUIFilter.value,
} as DateFilter,
});
break;
default:
throw new Error(
`Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`,
);
}
break;
case 'NUMBER':
switch (rawUIFilter.operand) {
case ViewFilterOperand.GreaterThan:
objectRecordFilters.push({
[correspondingField.name]: {
gte: parseFloat(rawUIFilter.value),
} as FloatFilter,
});
break;
case ViewFilterOperand.LessThan:
objectRecordFilters.push({
[correspondingField.name]: {
lte: parseFloat(rawUIFilter.value),
} as FloatFilter,
});
break;
default:
throw new Error(
`Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`,
);
}
break;
case 'RELATION':
try {
JSON.parse(rawUIFilter.value);
} catch (e) {
throw new Error(
`Cannot parse filter value for RELATION filter : "${rawUIFilter.value}"`,
);
}
const parsedRecordIds = JSON.parse(rawUIFilter.value) as string[];
if (parsedRecordIds.length > 0) {
switch (rawUIFilter.operand) {
case ViewFilterOperand.Is:
objectRecordFilters.push({
[correspondingField.name + 'Id']: {
in: parsedRecordIds,
} as StringFilter,
});
break;
case ViewFilterOperand.IsNot:
objectRecordFilters.push({
not: {
[correspondingField.name + 'Id']: {
in: parsedRecordIds,
} as StringFilter,
},
});
break;
default:
throw new Error(
`Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`,
);
}
}
break;
case 'CURRENCY':
switch (rawUIFilter.operand) {
case ViewFilterOperand.GreaterThan:
objectRecordFilters.push({
[correspondingField.name]: {
amountMicros: { gte: parseFloat(rawUIFilter.value) * 1000000 },
} as CurrencyFilter,
});
break;
case ViewFilterOperand.LessThan:
objectRecordFilters.push({
[correspondingField.name]: {
amountMicros: { lte: parseFloat(rawUIFilter.value) * 1000000 },
} as CurrencyFilter,
});
break;
default:
throw new Error(
`Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`,
);
}
break;
case 'LINK':
switch (rawUIFilter.operand) {
case ViewFilterOperand.Contains:
objectRecordFilters.push({
[correspondingField.name]: {
url: {
ilike: `%${rawUIFilter.value}%`,
},
} as URLFilter,
});
break;
case ViewFilterOperand.DoesNotContain:
objectRecordFilters.push({
not: {
[correspondingField.name]: {
url: {
ilike: `%${rawUIFilter.value}%`,
},
} as URLFilter,
},
});
break;
default:
throw new Error(
`Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`,
);
}
break;
case 'FULL_NAME':
switch (rawUIFilter.operand) {
case ViewFilterOperand.Contains:
objectRecordFilters.push({
or: [
{
[correspondingField.name]: {
firstName: {
ilike: `%${rawUIFilter.value}%`,
},
} as FullNameFilter,
},
{
[correspondingField.name]: {
lastName: {
ilike: `%${rawUIFilter.value}%`,
},
} as FullNameFilter,
},
],
});
break;
case ViewFilterOperand.DoesNotContain:
objectRecordFilters.push({
and: [
{
not: {
[correspondingField.name]: {
firstName: {
ilike: `%${rawUIFilter.value}%`,
},
} as FullNameFilter,
},
},
{
not: {
[correspondingField.name]: {
lastName: {
ilike: `%${rawUIFilter.value}%`,
},
} as FullNameFilter,
},
},
],
});
break;
default:
throw new Error(
`Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`,
);
}
break;
default:
throw new Error('Unknown filter type');
}
}
return { and: objectRecordFilters };
};