Add the support of Empty and Non-Empty filter (#5773)
This commit is contained in:
committed by
GitHub
parent
9e08445bff
commit
9228667a57
@ -9,6 +9,11 @@ export type UUIDFilter = {
|
||||
is?: IsFilter;
|
||||
};
|
||||
|
||||
export type RelationFilter = {
|
||||
is?: IsFilter;
|
||||
in?: UUIDFilterValue[];
|
||||
};
|
||||
|
||||
export type BooleanFilter = {
|
||||
eq?: boolean;
|
||||
is?: IsFilter;
|
||||
|
||||
@ -3,6 +3,7 @@ import { useRecoilValue } from 'recoil';
|
||||
import { ObjectFilterDropdownSearchInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownSearchInput';
|
||||
import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown';
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
|
||||
import { MultipleFiltersDropdownFilterOnFilterChangedEffect } from './MultipleFiltersDropdownFilterOnFilterChangedEffect';
|
||||
import { ObjectFilterDropdownDateInput } from './ObjectFilterDropdownDateInput';
|
||||
@ -36,6 +37,11 @@ export const MultipleFiltersDropdownContent = ({
|
||||
const selectedOperandInDropdown = useRecoilValue(
|
||||
selectedOperandInDropdownState,
|
||||
);
|
||||
const isEmptyOperand =
|
||||
selectedOperandInDropdown &&
|
||||
[ViewFilterOperand.IsEmpty, ViewFilterOperand.IsNotEmpty].includes(
|
||||
selectedOperandInDropdown,
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -43,6 +49,8 @@ export const MultipleFiltersDropdownContent = ({
|
||||
<ObjectFilterDropdownFilterSelect />
|
||||
) : isObjectFilterDropdownOperandSelectUnfolded ? (
|
||||
<ObjectFilterDropdownOperandSelect />
|
||||
) : isEmptyOperand ? (
|
||||
<ObjectFilterDropdownOperandButton />
|
||||
) : (
|
||||
selectedOperandInDropdown && (
|
||||
<>
|
||||
|
||||
@ -2,6 +2,7 @@ import { useRecoilValue } from 'recoil';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown';
|
||||
import { FilterDefinition } from '@/object-record/object-filter-dropdown/types/FilterDefinition';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
@ -34,10 +35,27 @@ export const ObjectFilterDropdownOperandSelect = () => {
|
||||
filterDefinitionUsedInDropdown?.type,
|
||||
);
|
||||
|
||||
const handleOperangeChange = (newOperand: ViewFilterOperand) => {
|
||||
const handleOperandChange = (newOperand: ViewFilterOperand) => {
|
||||
const isEmptyOperand = [
|
||||
ViewFilterOperand.IsEmpty,
|
||||
ViewFilterOperand.IsNotEmpty,
|
||||
].includes(newOperand);
|
||||
|
||||
setSelectedOperandInDropdown(newOperand);
|
||||
setIsObjectFilterDropdownOperandSelectUnfolded(false);
|
||||
|
||||
if (isEmptyOperand) {
|
||||
selectFilter?.({
|
||||
id: v4(),
|
||||
fieldMetadataId: filterDefinitionUsedInDropdown?.fieldMetadataId ?? '',
|
||||
displayValue: '',
|
||||
operand: newOperand,
|
||||
value: '',
|
||||
definition: filterDefinitionUsedInDropdown as FilterDefinition,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
isDefined(filterDefinitionUsedInDropdown) &&
|
||||
isDefined(selectedFilter)
|
||||
@ -63,7 +81,7 @@ export const ObjectFilterDropdownOperandSelect = () => {
|
||||
<MenuItem
|
||||
key={`select-filter-operand-${index}`}
|
||||
onClick={() => {
|
||||
handleOperangeChange(filterOperand);
|
||||
handleOperandChange(filterOperand);
|
||||
}}
|
||||
text={getOperandLabel(filterOperand)}
|
||||
/>
|
||||
|
||||
@ -4,20 +4,34 @@ import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import { getOperandsForFilterType } from '../getOperandsForFilterType';
|
||||
|
||||
describe('getOperandsForFilterType', () => {
|
||||
const emptyOperands = [
|
||||
ViewFilterOperand.IsEmpty,
|
||||
ViewFilterOperand.IsNotEmpty,
|
||||
];
|
||||
|
||||
const containsOperands = [
|
||||
ViewFilterOperand.Contains,
|
||||
ViewFilterOperand.DoesNotContain,
|
||||
];
|
||||
|
||||
const numberOperands = [
|
||||
ViewFilterOperand.GreaterThan,
|
||||
ViewFilterOperand.LessThan,
|
||||
];
|
||||
|
||||
const relationOperand = [ViewFilterOperand.Is, ViewFilterOperand.IsNot];
|
||||
|
||||
const testCases = [
|
||||
['TEXT', [ViewFilterOperand.Contains, ViewFilterOperand.DoesNotContain]],
|
||||
['EMAIL', [ViewFilterOperand.Contains, ViewFilterOperand.DoesNotContain]],
|
||||
[
|
||||
'FULL_NAME',
|
||||
[ViewFilterOperand.Contains, ViewFilterOperand.DoesNotContain],
|
||||
],
|
||||
['ADDRESS', [ViewFilterOperand.Contains, ViewFilterOperand.DoesNotContain]],
|
||||
['LINK', [ViewFilterOperand.Contains, ViewFilterOperand.DoesNotContain]],
|
||||
['LINKS', [ViewFilterOperand.Contains, ViewFilterOperand.DoesNotContain]],
|
||||
['CURRENCY', [ViewFilterOperand.GreaterThan, ViewFilterOperand.LessThan]],
|
||||
['NUMBER', [ViewFilterOperand.GreaterThan, ViewFilterOperand.LessThan]],
|
||||
['DATE_TIME', [ViewFilterOperand.GreaterThan, ViewFilterOperand.LessThan]],
|
||||
['RELATION', [ViewFilterOperand.Is, ViewFilterOperand.IsNot]],
|
||||
['TEXT', [...containsOperands, ...emptyOperands]],
|
||||
['EMAIL', [...containsOperands, ...emptyOperands]],
|
||||
['FULL_NAME', [...containsOperands, ...emptyOperands]],
|
||||
['ADDRESS', [...containsOperands, ...emptyOperands]],
|
||||
['LINK', [...containsOperands, ...emptyOperands]],
|
||||
['LINKS', [...containsOperands, ...emptyOperands]],
|
||||
['CURRENCY', [...numberOperands, ...emptyOperands]],
|
||||
['NUMBER', [...numberOperands, ...emptyOperands]],
|
||||
['DATE_TIME', [...numberOperands, ...emptyOperands]],
|
||||
['RELATION', [...relationOperand, ...emptyOperands]],
|
||||
[undefined, []],
|
||||
[null, []],
|
||||
['UNKNOWN_TYPE', []],
|
||||
|
||||
@ -18,6 +18,10 @@ export const getOperandLabel = (
|
||||
return 'Is not';
|
||||
case ViewFilterOperand.IsNotNull:
|
||||
return 'Is not null';
|
||||
case ViewFilterOperand.IsEmpty:
|
||||
return 'Is empty';
|
||||
case ViewFilterOperand.IsNotEmpty:
|
||||
return 'Is not empty';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
@ -35,6 +39,10 @@ export const getOperandLabelShort = (
|
||||
return ': Not';
|
||||
case ViewFilterOperand.IsNotNull:
|
||||
return ': NotNull';
|
||||
case ViewFilterOperand.IsNotEmpty:
|
||||
return ': NotEmpty';
|
||||
case ViewFilterOperand.IsEmpty:
|
||||
return ': Empty';
|
||||
case ViewFilterOperand.GreaterThan:
|
||||
return '\u00A0> ';
|
||||
case ViewFilterOperand.LessThan:
|
||||
|
||||
@ -5,6 +5,13 @@ import { FilterType } from '../types/FilterType';
|
||||
export const getOperandsForFilterType = (
|
||||
filterType: FilterType | null | undefined,
|
||||
): ViewFilterOperand[] => {
|
||||
const emptyOperands = [
|
||||
ViewFilterOperand.IsEmpty,
|
||||
ViewFilterOperand.IsNotEmpty,
|
||||
];
|
||||
|
||||
const relationOperands = [ViewFilterOperand.Is, ViewFilterOperand.IsNot];
|
||||
|
||||
switch (filterType) {
|
||||
case 'TEXT':
|
||||
case 'EMAIL':
|
||||
@ -12,17 +19,25 @@ export const getOperandsForFilterType = (
|
||||
case 'ADDRESS':
|
||||
case 'PHONE':
|
||||
case 'LINK':
|
||||
return [ViewFilterOperand.Contains, ViewFilterOperand.DoesNotContain];
|
||||
case 'LINKS':
|
||||
return [ViewFilterOperand.Contains, ViewFilterOperand.DoesNotContain];
|
||||
return [
|
||||
ViewFilterOperand.Contains,
|
||||
ViewFilterOperand.DoesNotContain,
|
||||
...emptyOperands,
|
||||
];
|
||||
case 'CURRENCY':
|
||||
case 'NUMBER':
|
||||
case 'DATE_TIME':
|
||||
case 'DATE':
|
||||
return [ViewFilterOperand.GreaterThan, ViewFilterOperand.LessThan];
|
||||
return [
|
||||
ViewFilterOperand.GreaterThan,
|
||||
ViewFilterOperand.LessThan,
|
||||
...emptyOperands,
|
||||
];
|
||||
case 'RELATION':
|
||||
return [...relationOperands, ...emptyOperands];
|
||||
case 'SELECT':
|
||||
return [ViewFilterOperand.Is, ViewFilterOperand.IsNot];
|
||||
return [...relationOperands];
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
|
||||
@ -6,10 +6,12 @@ import {
|
||||
DateFilter,
|
||||
FloatFilter,
|
||||
RecordGqlOperationFilter,
|
||||
RelationFilter,
|
||||
StringFilter,
|
||||
URLFilter,
|
||||
UUIDFilter,
|
||||
} from '@/object-record/graphql/types/RecordGqlOperationFilter';
|
||||
import { FilterType } from '@/object-record/object-filter-dropdown/types/FilterType';
|
||||
import { makeAndFilterVariables } from '@/object-record/utils/makeAndFilterVariables';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import { Field } from '~/generated/graphql';
|
||||
@ -24,6 +26,200 @@ export type ObjectDropdownFilter = Omit<Filter, 'definition'> & {
|
||||
};
|
||||
};
|
||||
|
||||
const applyEmptyFilters = (
|
||||
operand: ViewFilterOperand,
|
||||
correspondingField: Pick<Field, 'id' | 'name'>,
|
||||
objectRecordFilters: RecordGqlOperationFilter[],
|
||||
filterType: FilterType,
|
||||
) => {
|
||||
let emptyRecordFilter: RecordGqlOperationFilter = {};
|
||||
|
||||
switch (filterType) {
|
||||
case 'TEXT':
|
||||
case 'EMAIL':
|
||||
case 'PHONE':
|
||||
emptyRecordFilter = {
|
||||
or: [
|
||||
{ [correspondingField.name]: { ilike: '' } as StringFilter },
|
||||
{ [correspondingField.name]: { is: 'NULL' } as StringFilter },
|
||||
],
|
||||
};
|
||||
break;
|
||||
case 'CURRENCY':
|
||||
emptyRecordFilter = {
|
||||
or: [
|
||||
{
|
||||
[correspondingField.name]: {
|
||||
amountMicros: { is: 'NULL' },
|
||||
} as CurrencyFilter,
|
||||
},
|
||||
],
|
||||
};
|
||||
break;
|
||||
case 'FULL_NAME': {
|
||||
const fullNameFilters = generateILikeFiltersForCompositeFields(
|
||||
'',
|
||||
correspondingField.name,
|
||||
['firstName', 'lastName'],
|
||||
true,
|
||||
);
|
||||
|
||||
emptyRecordFilter = {
|
||||
and: fullNameFilters,
|
||||
};
|
||||
break;
|
||||
}
|
||||
case 'LINK':
|
||||
emptyRecordFilter = {
|
||||
or: [
|
||||
{ [correspondingField.name]: { url: { ilike: '' } } as URLFilter },
|
||||
{
|
||||
[correspondingField.name]: { url: { is: 'NULL' } } as URLFilter,
|
||||
},
|
||||
],
|
||||
};
|
||||
break;
|
||||
case 'LINKS': {
|
||||
const linksFilters = generateILikeFiltersForCompositeFields(
|
||||
'',
|
||||
correspondingField.name,
|
||||
['primaryLinkLabel', 'primaryLinkUrl'],
|
||||
true,
|
||||
);
|
||||
|
||||
emptyRecordFilter = {
|
||||
and: linksFilters,
|
||||
};
|
||||
break;
|
||||
}
|
||||
case 'ADDRESS':
|
||||
emptyRecordFilter = {
|
||||
and: [
|
||||
{
|
||||
or: [
|
||||
{
|
||||
[correspondingField.name]: {
|
||||
addressStreet1: { ilike: '' },
|
||||
} as AddressFilter,
|
||||
},
|
||||
{
|
||||
[correspondingField.name]: {
|
||||
addressStreet1: { is: 'NULL' },
|
||||
} as AddressFilter,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
or: [
|
||||
{
|
||||
[correspondingField.name]: {
|
||||
addressStreet2: { ilike: '' },
|
||||
} as AddressFilter,
|
||||
},
|
||||
{
|
||||
[correspondingField.name]: {
|
||||
addressStreet2: { is: 'NULL' },
|
||||
} as AddressFilter,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
or: [
|
||||
{
|
||||
[correspondingField.name]: {
|
||||
addressCity: { ilike: '' },
|
||||
} as AddressFilter,
|
||||
},
|
||||
{
|
||||
[correspondingField.name]: {
|
||||
addressCity: { is: 'NULL' },
|
||||
} as AddressFilter,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
or: [
|
||||
{
|
||||
[correspondingField.name]: {
|
||||
addressState: { ilike: '' },
|
||||
} as AddressFilter,
|
||||
},
|
||||
{
|
||||
[correspondingField.name]: {
|
||||
addressState: { is: 'NULL' },
|
||||
} as AddressFilter,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
or: [
|
||||
{
|
||||
[correspondingField.name]: {
|
||||
addressCountry: { ilike: '' },
|
||||
} as AddressFilter,
|
||||
},
|
||||
{
|
||||
[correspondingField.name]: {
|
||||
addressCountry: { is: 'NULL' },
|
||||
} as AddressFilter,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
or: [
|
||||
{
|
||||
[correspondingField.name]: {
|
||||
addressPostcode: { ilike: '' },
|
||||
} as AddressFilter,
|
||||
},
|
||||
{
|
||||
[correspondingField.name]: {
|
||||
addressPostcode: { is: 'NULL' },
|
||||
} as AddressFilter,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
break;
|
||||
case 'NUMBER':
|
||||
emptyRecordFilter = {
|
||||
[correspondingField.name]: { is: 'NULL' } as FloatFilter,
|
||||
};
|
||||
break;
|
||||
case 'DATE_TIME':
|
||||
emptyRecordFilter = {
|
||||
[correspondingField.name]: { is: 'NULL' } as DateFilter,
|
||||
};
|
||||
break;
|
||||
case 'SELECT':
|
||||
emptyRecordFilter = {
|
||||
[correspondingField.name]: { is: 'NULL' } as UUIDFilter,
|
||||
};
|
||||
break;
|
||||
case 'RELATION':
|
||||
emptyRecordFilter = {
|
||||
[correspondingField.name + 'Id']: { is: 'NULL' } as RelationFilter,
|
||||
};
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unsupported empty filter type ${filterType}`);
|
||||
}
|
||||
|
||||
switch (operand) {
|
||||
case ViewFilterOperand.IsEmpty:
|
||||
objectRecordFilters.push(emptyRecordFilter);
|
||||
break;
|
||||
case ViewFilterOperand.IsNotEmpty:
|
||||
objectRecordFilters.push({
|
||||
not: emptyRecordFilter,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown operand ${operand} for ${filterType} filter`);
|
||||
}
|
||||
};
|
||||
|
||||
export const turnObjectDropdownFilterIntoQueryFilter = (
|
||||
rawUIFilters: ObjectDropdownFilter[],
|
||||
fields: Pick<Field, 'id' | 'name'>[],
|
||||
@ -35,12 +231,19 @@ export const turnObjectDropdownFilterIntoQueryFilter = (
|
||||
(field) => field.id === rawUIFilter.fieldMetadataId,
|
||||
);
|
||||
|
||||
const isEmptyOperand = [
|
||||
ViewFilterOperand.IsEmpty,
|
||||
ViewFilterOperand.IsNotEmpty,
|
||||
].includes(rawUIFilter.operand);
|
||||
|
||||
if (!correspondingField) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isDefined(rawUIFilter.value) || rawUIFilter.value === '') {
|
||||
continue;
|
||||
if (!isEmptyOperand) {
|
||||
if (!isDefined(rawUIFilter.value) || rawUIFilter.value === '') {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
switch (rawUIFilter.definition.type) {
|
||||
@ -64,6 +267,15 @@ export const turnObjectDropdownFilterIntoQueryFilter = (
|
||||
},
|
||||
});
|
||||
break;
|
||||
case ViewFilterOperand.IsEmpty:
|
||||
case ViewFilterOperand.IsNotEmpty:
|
||||
applyEmptyFilters(
|
||||
rawUIFilter.operand,
|
||||
correspondingField,
|
||||
objectRecordFilters,
|
||||
rawUIFilter.definition.type,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`,
|
||||
@ -86,6 +298,15 @@ export const turnObjectDropdownFilterIntoQueryFilter = (
|
||||
} as DateFilter,
|
||||
});
|
||||
break;
|
||||
case ViewFilterOperand.IsEmpty:
|
||||
case ViewFilterOperand.IsNotEmpty:
|
||||
applyEmptyFilters(
|
||||
rawUIFilter.operand,
|
||||
correspondingField,
|
||||
objectRecordFilters,
|
||||
rawUIFilter.definition.type,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`,
|
||||
@ -108,6 +329,15 @@ export const turnObjectDropdownFilterIntoQueryFilter = (
|
||||
} as FloatFilter,
|
||||
});
|
||||
break;
|
||||
case ViewFilterOperand.IsEmpty:
|
||||
case ViewFilterOperand.IsNotEmpty:
|
||||
applyEmptyFilters(
|
||||
rawUIFilter.operand,
|
||||
correspondingField,
|
||||
objectRecordFilters,
|
||||
rawUIFilter.definition.type,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`,
|
||||
@ -115,39 +345,57 @@ export const turnObjectDropdownFilterIntoQueryFilter = (
|
||||
}
|
||||
break;
|
||||
case 'RELATION': {
|
||||
try {
|
||||
JSON.parse(rawUIFilter.value);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Cannot parse filter value for RELATION filter : "${rawUIFilter.value}"`,
|
||||
);
|
||||
}
|
||||
if (!isEmptyOperand) {
|
||||
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[];
|
||||
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 UUIDFilter,
|
||||
});
|
||||
break;
|
||||
case ViewFilterOperand.IsNot:
|
||||
if (parsedRecordIds.length > 0) {
|
||||
if (parsedRecordIds.length > 0) {
|
||||
switch (rawUIFilter.operand) {
|
||||
case ViewFilterOperand.Is:
|
||||
objectRecordFilters.push({
|
||||
not: {
|
||||
[correspondingField.name + 'Id']: {
|
||||
in: parsedRecordIds,
|
||||
} as UUIDFilter,
|
||||
},
|
||||
[correspondingField.name + 'Id']: {
|
||||
in: parsedRecordIds,
|
||||
} as RelationFilter,
|
||||
});
|
||||
}
|
||||
break;
|
||||
case ViewFilterOperand.IsNot:
|
||||
if (parsedRecordIds.length > 0) {
|
||||
objectRecordFilters.push({
|
||||
not: {
|
||||
[correspondingField.name + 'Id']: {
|
||||
in: parsedRecordIds,
|
||||
} as RelationFilter,
|
||||
},
|
||||
});
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`,
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch (rawUIFilter.operand) {
|
||||
case ViewFilterOperand.IsEmpty:
|
||||
case ViewFilterOperand.IsNotEmpty:
|
||||
applyEmptyFilters(
|
||||
rawUIFilter.operand,
|
||||
correspondingField,
|
||||
objectRecordFilters,
|
||||
rawUIFilter.definition.type,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`,
|
||||
`Unknown empty operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -169,6 +417,15 @@ export const turnObjectDropdownFilterIntoQueryFilter = (
|
||||
} as CurrencyFilter,
|
||||
});
|
||||
break;
|
||||
case ViewFilterOperand.IsEmpty:
|
||||
case ViewFilterOperand.IsNotEmpty:
|
||||
applyEmptyFilters(
|
||||
rawUIFilter.operand,
|
||||
correspondingField,
|
||||
objectRecordFilters,
|
||||
rawUIFilter.definition.type,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`,
|
||||
@ -197,6 +454,15 @@ export const turnObjectDropdownFilterIntoQueryFilter = (
|
||||
},
|
||||
});
|
||||
break;
|
||||
case ViewFilterOperand.IsEmpty:
|
||||
case ViewFilterOperand.IsNotEmpty:
|
||||
applyEmptyFilters(
|
||||
rawUIFilter.operand,
|
||||
correspondingField,
|
||||
objectRecordFilters,
|
||||
rawUIFilter.definition.type,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`,
|
||||
@ -224,6 +490,15 @@ export const turnObjectDropdownFilterIntoQueryFilter = (
|
||||
}),
|
||||
});
|
||||
break;
|
||||
case ViewFilterOperand.IsEmpty:
|
||||
case ViewFilterOperand.IsNotEmpty:
|
||||
applyEmptyFilters(
|
||||
rawUIFilter.operand,
|
||||
correspondingField,
|
||||
objectRecordFilters,
|
||||
rawUIFilter.definition.type,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`,
|
||||
@ -231,7 +506,6 @@ export const turnObjectDropdownFilterIntoQueryFilter = (
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'FULL_NAME': {
|
||||
const fullNameFilters = generateILikeFiltersForCompositeFields(
|
||||
rawUIFilter.value,
|
||||
@ -253,6 +527,15 @@ export const turnObjectDropdownFilterIntoQueryFilter = (
|
||||
}),
|
||||
});
|
||||
break;
|
||||
case ViewFilterOperand.IsEmpty:
|
||||
case ViewFilterOperand.IsNotEmpty:
|
||||
applyEmptyFilters(
|
||||
rawUIFilter.operand,
|
||||
correspondingField,
|
||||
objectRecordFilters,
|
||||
rawUIFilter.definition.type,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`,
|
||||
@ -286,6 +569,27 @@ export const turnObjectDropdownFilterIntoQueryFilter = (
|
||||
},
|
||||
} as AddressFilter,
|
||||
},
|
||||
{
|
||||
[correspondingField.name]: {
|
||||
addressState: {
|
||||
ilike: `%${rawUIFilter.value}%`,
|
||||
},
|
||||
} as AddressFilter,
|
||||
},
|
||||
{
|
||||
[correspondingField.name]: {
|
||||
addressCountry: {
|
||||
ilike: `%${rawUIFilter.value}%`,
|
||||
},
|
||||
} as AddressFilter,
|
||||
},
|
||||
{
|
||||
[correspondingField.name]: {
|
||||
addressPostcode: {
|
||||
ilike: `%${rawUIFilter.value}%`,
|
||||
},
|
||||
} as AddressFilter,
|
||||
},
|
||||
],
|
||||
});
|
||||
break;
|
||||
@ -322,6 +626,15 @@ export const turnObjectDropdownFilterIntoQueryFilter = (
|
||||
],
|
||||
});
|
||||
break;
|
||||
case ViewFilterOperand.IsEmpty:
|
||||
case ViewFilterOperand.IsNotEmpty:
|
||||
applyEmptyFilters(
|
||||
rawUIFilter.operand,
|
||||
correspondingField,
|
||||
objectRecordFilters,
|
||||
rawUIFilter.definition.type,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`,
|
||||
@ -329,6 +642,15 @@ export const turnObjectDropdownFilterIntoQueryFilter = (
|
||||
}
|
||||
break;
|
||||
case 'SELECT': {
|
||||
if (isEmptyOperand) {
|
||||
applyEmptyFilters(
|
||||
rawUIFilter.operand,
|
||||
correspondingField,
|
||||
objectRecordFilters,
|
||||
rawUIFilter.definition.type,
|
||||
);
|
||||
break;
|
||||
}
|
||||
const stringifiedSelectValues = rawUIFilter.value;
|
||||
let parsedOptionValues: string[] = [];
|
||||
|
||||
|
||||
@ -49,7 +49,7 @@ describe('generateCsv', () => {
|
||||
},
|
||||
];
|
||||
const csv = generateCsv({ columns, rows });
|
||||
expect(csv).toEqual(`id,Foo,Empty,Nested Foo,Nested Nested,Relation
|
||||
expect(csv).toEqual(`Id,Foo,Empty,Nested Foo,Nested Nested,Relation
|
||||
1,some field,,foo,nested,a relation`);
|
||||
});
|
||||
});
|
||||
|
||||
@ -6,4 +6,6 @@ export enum ViewFilterOperand {
|
||||
GreaterThan = 'greaterThan',
|
||||
Contains = 'contains',
|
||||
DoesNotContain = 'doesNotContain',
|
||||
IsEmpty = 'isEmpty',
|
||||
IsNotEmpty = 'isNotEmpty',
|
||||
}
|
||||
|
||||
@ -4,7 +4,31 @@ export const generateILikeFiltersForCompositeFields = (
|
||||
filterString: string,
|
||||
baseFieldName: string,
|
||||
subFields: string[],
|
||||
emptyCheck = false,
|
||||
) => {
|
||||
if (emptyCheck) {
|
||||
return subFields.map((subField) => {
|
||||
return {
|
||||
or: [
|
||||
{
|
||||
[baseFieldName]: {
|
||||
[subField]: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
[baseFieldName]: {
|
||||
[subField]: {
|
||||
ilike: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return filterString
|
||||
.split(' ')
|
||||
.reduce((previousValue: RecordGqlOperationFilter[], currentValue) => {
|
||||
|
||||
@ -199,14 +199,20 @@ export class QueryRunnerArgsFactory {
|
||||
if (!fieldMetadata) {
|
||||
return value;
|
||||
}
|
||||
|
||||
switch (fieldMetadata.type) {
|
||||
case 'NUMBER':
|
||||
return Object.fromEntries(
|
||||
Object.entries(value).map(([filterKey, filterValue]) => [
|
||||
filterKey,
|
||||
Number(filterValue),
|
||||
]),
|
||||
);
|
||||
case 'NUMBER': {
|
||||
if (value?.is === 'NULL') {
|
||||
return value;
|
||||
} else {
|
||||
return Object.fromEntries(
|
||||
Object.entries(value).map(([filterKey, filterValue]) => [
|
||||
filterKey,
|
||||
Number(filterValue),
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user