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