feat(twenty-front): relation traversal in filter dropdown (stacked) (#20533)
**Stacked on #20527** https://github.com/user-attachments/assets/48995655-401a-4c35-8094-e88da8408bdd ## Summary Surfaces the one-hop relation traversal added in #20527 through the existing **composite sub-field dropdown pattern**. Clicking a MANY_TO_ONE relation field in the "+ Filter" picker now opens the same second-level dropdown that composite fields (FULL_NAME, ADDRESS, CURRENCY, etc.) already use — populated with the target object's filterable fields. Picking one (e.g. `Company → Name`) builds a filter that serializes to the nested GraphQL filter the backend now accepts: `{ company: { name: { ilike: "%X%" } } }`. No new components. The whole feature reuses `AdvancedFilterSubFieldSelectMenu` + the existing `subFieldNameUsedInDropdownComponentState` + the existing `MenuItem hasSubMenu` indicator. Only the conditions that gate the sub-menu (and the sub-menu's content for relations) were broadened. ## What landed | File | Change | |---|---| | `ObjectFilterDropdownFilterSelectMenuItem` | Sub-menu chevron now shows on MANY_TO_ONE relations (`isManyToOneRelationField` util). | | `AdvancedFilterFieldSelectMenu` | Relation clicks open the sub-menu alongside composite clicks. | | `AdvancedFilterSubFieldSelectMenu` | New branch: when the sub-menu type is `'RELATION'`, render the target object's filterable fields via `useFilterableFieldMetadataItems(targetObjectMetadataId)`. Composite logic untouched. | | `objectFilterDropdownSubMenuFieldType` state | Widened to accept a `'RELATION'` sentinel. Role-permissions sub-field menu narrows it back out (it doesn't traverse relations). | | `useSelectFieldUsedInAdvancedFilterDropdown` | New optional `targetFieldMetadataItem` arg. When present, the stored RecordFilter's `type` is the target field's type so the operand picker and value input render the target's operands (`'TEXT'` operators when filtering `company.name`, etc.). | | `turnRecordFilterIntoGqlOperationFilter` (shared) | When the filter targets a `RELATION` field with a `subFieldName`, synthesize a field-metadata for the target, recurse to build the inner filter, then wrap it under the relation field's name → `{ relationName: { targetFieldName: { ...operator } } }`. | `RecordFilter.subFieldName` stays narrowly typed as `CompositeFieldSubFieldName` so the wide downstream consumers (`shouldShowFilterTextInput`, composite handlers in the serializer, etc.) don't change. The relation target field's name is stored through a narrowly-scoped cast at the dropdown's storage point — the serializer checks `filter.type === 'RELATION'` before interpreting it as a target field name, so the cast can't be mis-read by composite-only code paths. ## Test plan - [ ] Open a table view on People, click "+ Filter", click "Company" → sub-menu opens with Company's filterable fields - [ ] Pick "Name" → operand picker shows TEXT operators (Contains, Equals, …) - [ ] Type "Airbnb" → filter applies, table shows people whose company name contains "Airbnb" - [ ] Verify network tab: the GraphQL filter variable is `{ company: { name: { ilike: "%Airbnb%" } } }` - [ ] Same flow with a composite target field (e.g. `Company → annualRecurringRevenue → amountMicros`) — should work end-to-end (backend supports composite-within-relation; #20527 has an integration test covering this) - [ ] Composite fields (FULL_NAME, ADDRESS) still open their normal sub-menu and filter correctly — no regression - [ ] Role-permissions field-select sub-field menu is unaffected (it bails out early on the RELATION sentinel) ## Out of scope - ONE_TO_MANY traversal (no backend support yet) - Aggregates (`people.count > 5`) - Persisting relation-traversal filters into a saved view (ViewFilter has no `relationPath` column yet; that's a separate slice) - REST API DSL changes - AI Tools 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+88
@@ -919,4 +919,92 @@ describe('turnRecordFilterIntoRecordGqlOperationFilter', () => {
|
||||
expect(result).toHaveProperty('recordId.in');
|
||||
});
|
||||
});
|
||||
|
||||
describe('relation traversal', () => {
|
||||
// The dispatcher should wrap the inner filter under the relation source
|
||||
// field's GraphQL key and build it against the target field's type
|
||||
// (not the relation FK's type).
|
||||
it('should nest filter under source field name when target field is set', () => {
|
||||
const result = turnRecordFilterIntoRecordGqlOperationFilter({
|
||||
filterValueDependencies,
|
||||
recordFilter: {
|
||||
...makeFilter('f-relation', RecordFilterOperand.CONTAINS, 'Acme'),
|
||||
relationTargetFieldMetadataId: 'f-text',
|
||||
} as RecordFilter,
|
||||
fieldMetadataItems: fields,
|
||||
});
|
||||
|
||||
expect(result).toEqual({ company: { name: { ilike: '%Acme%' } } });
|
||||
});
|
||||
|
||||
// If the target field is no longer in fieldMetadataItems (e.g. it was
|
||||
// deleted from the workspace), dropping the filter is the safe path —
|
||||
// the alternative would silently interpret the text value as a UUID
|
||||
// list against the relation FK.
|
||||
it('should return undefined when target field is not found', () => {
|
||||
const result = turnRecordFilterIntoRecordGqlOperationFilter({
|
||||
filterValueDependencies,
|
||||
recordFilter: {
|
||||
...makeFilter('f-relation', RecordFilterOperand.CONTAINS, 'Acme'),
|
||||
relationTargetFieldMetadataId: 'nonexistent-target',
|
||||
} as RecordFilter,
|
||||
fieldMetadataItems: fields,
|
||||
});
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
// Emptiness operands must check the target column on the related
|
||||
// object rather than the FK column on the source record.
|
||||
it('should apply IS_EMPTY against the target field, not the relation FK', () => {
|
||||
const result = turnRecordFilterIntoRecordGqlOperationFilter({
|
||||
filterValueDependencies,
|
||||
recordFilter: {
|
||||
...makeFilter('f-relation', RecordFilterOperand.IS_EMPTY, ''),
|
||||
relationTargetFieldMetadataId: 'f-text',
|
||||
} as RecordFilter,
|
||||
fieldMetadataItems: fields,
|
||||
});
|
||||
|
||||
// Without traversal, the RELATION case would have produced a filter
|
||||
// on `companyId`; here we expect the emptiness check to fall on
|
||||
// `company.name` instead.
|
||||
expect(result).toEqual({
|
||||
company: { or: [{ name: { ilike: '' } }, { name: { is: 'NULL' } }] },
|
||||
});
|
||||
});
|
||||
|
||||
// The target field's per-type switch must run, so number/select/etc.
|
||||
// targets behave like a direct filter on that field — verified here
|
||||
// with a SELECT target.
|
||||
it('should dispatch to target type switch (SELECT target)', () => {
|
||||
const result = turnRecordFilterIntoRecordGqlOperationFilter({
|
||||
filterValueDependencies,
|
||||
recordFilter: {
|
||||
...makeFilter('f-relation', RecordFilterOperand.IS, '["ACTIVE"]'),
|
||||
relationTargetFieldMetadataId: 'f-select',
|
||||
} as RecordFilter,
|
||||
fieldMetadataItems: fields,
|
||||
});
|
||||
|
||||
expect(result).toEqual({ company: { status: { in: ['ACTIVE'] } } });
|
||||
});
|
||||
|
||||
// Without a relationTargetFieldMetadataId the dispatcher must fall
|
||||
// through to the direct builder — preserving the filter-by-record-id
|
||||
// behaviour on the relation FK.
|
||||
it('should keep relation filter-by-id behaviour when no target field is set', () => {
|
||||
const result = turnRecordFilterIntoRecordGqlOperationFilter({
|
||||
filterValueDependencies,
|
||||
recordFilter: makeFilter(
|
||||
'f-relation',
|
||||
RecordFilterOperand.IS,
|
||||
'["550e8400-e29b-41d4-a716-446655440000"]',
|
||||
),
|
||||
fieldMetadataItems: fields,
|
||||
});
|
||||
|
||||
expect(result).toHaveProperty('companyId.in');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,6 +19,7 @@ export type RecordFilter = {
|
||||
recordFilterGroupId?: string | null;
|
||||
operand: ViewFilterOperand;
|
||||
subFieldName?: CompositeFieldSubFieldName | null | undefined;
|
||||
relationTargetFieldMetadataId?: string | null | undefined;
|
||||
};
|
||||
|
||||
export type RecordFilterGroup = {
|
||||
|
||||
+164
-127
@@ -75,11 +75,11 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
}: TurnRecordFilterIntoRecordGqlOperationFilterParams):
|
||||
| RecordGqlOperationFilter
|
||||
| undefined => {
|
||||
const correspondingFieldMetadataItem = fieldMetadataItems.find(
|
||||
const sourceFieldMetadataItem = fieldMetadataItems.find(
|
||||
(field) => field.id === recordFilter.fieldMetadataId,
|
||||
);
|
||||
|
||||
if (!isDefined(correspondingFieldMetadataItem)) {
|
||||
if (!isDefined(sourceFieldMetadataItem)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -87,15 +87,66 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
sourceFieldMetadataItem.type === FieldMetadataType.RELATION &&
|
||||
isDefined(recordFilter.relationTargetFieldMetadataId)
|
||||
) {
|
||||
const targetFieldMetadataItem = fieldMetadataItems.find(
|
||||
(field) => field.id === recordFilter.relationTargetFieldMetadataId,
|
||||
);
|
||||
|
||||
if (!isDefined(targetFieldMetadataItem)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const innerFilter = buildDirectFieldGqlOperationFilter({
|
||||
recordFilter: {
|
||||
...recordFilter,
|
||||
fieldMetadataId: targetFieldMetadataItem.id,
|
||||
relationTargetFieldMetadataId: null,
|
||||
},
|
||||
fieldMetadataItem: targetFieldMetadataItem,
|
||||
filterValueDependencies,
|
||||
});
|
||||
|
||||
if (!isDefined(innerFilter)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
[sourceFieldMetadataItem.name]: innerFilter,
|
||||
} as RecordGqlOperationFilter;
|
||||
}
|
||||
|
||||
return buildDirectFieldGqlOperationFilter({
|
||||
recordFilter,
|
||||
fieldMetadataItem: sourceFieldMetadataItem,
|
||||
filterValueDependencies,
|
||||
});
|
||||
};
|
||||
|
||||
type BuildDirectFieldGqlOperationFilterParams = {
|
||||
filterValueDependencies: RecordFilterValueDependencies;
|
||||
recordFilter: Omit<RecordFilter, 'id'>;
|
||||
fieldMetadataItem: FieldShared;
|
||||
};
|
||||
|
||||
const buildDirectFieldGqlOperationFilter = ({
|
||||
recordFilter,
|
||||
fieldMetadataItem,
|
||||
filterValueDependencies,
|
||||
}: BuildDirectFieldGqlOperationFilterParams):
|
||||
| RecordGqlOperationFilter
|
||||
| undefined => {
|
||||
const shouldComputeEmptinessFilter = checkIfShouldComputeEmptinessFilter({
|
||||
recordFilterOperand: recordFilter.operand,
|
||||
correspondingFieldMetadataItem,
|
||||
correspondingFieldMetadataItem: fieldMetadataItem,
|
||||
});
|
||||
|
||||
if (shouldComputeEmptinessFilter) {
|
||||
const emptinessFilter = getEmptyRecordGqlOperationFilter({
|
||||
operand: recordFilter.operand,
|
||||
correspondingField: correspondingFieldMetadataItem,
|
||||
correspondingField: fieldMetadataItem,
|
||||
recordFilter: recordFilter,
|
||||
});
|
||||
|
||||
@@ -106,23 +157,21 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
|
||||
const isSubFieldFilter = isNonEmptyString(subFieldName);
|
||||
|
||||
const filterType = getFilterTypeFromFieldType(
|
||||
correspondingFieldMetadataItem.type,
|
||||
);
|
||||
const filterType = getFilterTypeFromFieldType(fieldMetadataItem.type);
|
||||
|
||||
switch (filterType) {
|
||||
case 'TEXT':
|
||||
switch (recordFilter.operand) {
|
||||
case RecordFilterOperand.CONTAINS:
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
} as StringFilter,
|
||||
};
|
||||
case RecordFilterOperand.DOES_NOT_CONTAIN:
|
||||
return {
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
} as StringFilter,
|
||||
},
|
||||
@@ -137,7 +186,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
switch (recordFilter.operand) {
|
||||
case RecordFilterOperand.VECTOR_SEARCH:
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
search: recordFilter.value,
|
||||
} as TSVectorFilter,
|
||||
};
|
||||
@@ -150,14 +199,14 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
switch (recordFilter.operand) {
|
||||
case RecordFilterOperand.CONTAINS:
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
like: `%${recordFilter.value}%`,
|
||||
} as RawJsonFilter,
|
||||
};
|
||||
case RecordFilterOperand.DOES_NOT_CONTAIN:
|
||||
return {
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
like: `%${recordFilter.value}%`,
|
||||
} as RawJsonFilter,
|
||||
},
|
||||
@@ -171,14 +220,14 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
switch (recordFilter.operand) {
|
||||
case RecordFilterOperand.CONTAINS:
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
like: `%${recordFilter.value}%`,
|
||||
} as FilesFilter,
|
||||
};
|
||||
case RecordFilterOperand.DOES_NOT_CONTAIN:
|
||||
return {
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
like: `%${recordFilter.value}%`,
|
||||
} as FilesFilter,
|
||||
},
|
||||
@@ -215,12 +264,12 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
return {
|
||||
and: [
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
gte: start,
|
||||
} as DateFilter,
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
lt: end,
|
||||
} as DateFilter,
|
||||
},
|
||||
@@ -241,19 +290,19 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
switch (recordFilter.operand) {
|
||||
case RecordFilterOperand.IS_IN_PAST:
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
lt: nowAsPlainDate,
|
||||
} as DateFilter,
|
||||
};
|
||||
case RecordFilterOperand.IS_IN_FUTURE:
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
gte: nowAsPlainDate,
|
||||
} as DateFilter,
|
||||
};
|
||||
case RecordFilterOperand.IS_TODAY: {
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
eq: nowAsPlainDate,
|
||||
} as DateFilter,
|
||||
};
|
||||
@@ -265,14 +314,14 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
switch (recordFilter.operand) {
|
||||
case RecordFilterOperand.IS_AFTER: {
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
gte: plainDateFilter,
|
||||
} as DateFilter,
|
||||
};
|
||||
}
|
||||
case RecordFilterOperand.IS_BEFORE: {
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
lt: plainDateFilter,
|
||||
} as DateFilter,
|
||||
};
|
||||
@@ -280,7 +329,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
|
||||
case RecordFilterOperand.IS: {
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
eq: plainDateFilter,
|
||||
} as DateFilter,
|
||||
};
|
||||
@@ -331,12 +380,12 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
return {
|
||||
and: [
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
gte: start.toInstant().toString(),
|
||||
} as DateTimeFilter,
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
lt: end.toInstant().toString(),
|
||||
} as DateTimeFilter,
|
||||
},
|
||||
@@ -357,13 +406,13 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
switch (recordFilter.operand) {
|
||||
case RecordFilterOperand.IS_IN_PAST:
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
lt: now.toInstant().round('minute').toString(),
|
||||
} as DateTimeFilter,
|
||||
};
|
||||
case RecordFilterOperand.IS_IN_FUTURE:
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
gt: now.toInstant().round('minute').toString(),
|
||||
} as DateTimeFilter,
|
||||
};
|
||||
@@ -371,12 +420,12 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
return {
|
||||
and: [
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
gte: getPeriodStart(now, 'DAY').toInstant().toString(),
|
||||
} as DateTimeFilter,
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
lt: getNextPeriodStart(now, 'DAY').toInstant().toString(),
|
||||
} as DateTimeFilter,
|
||||
},
|
||||
@@ -413,12 +462,12 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
return {
|
||||
and: [
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
gte: start.toString(),
|
||||
} as DateTimeFilter,
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
lt: end.toString(),
|
||||
} as DateTimeFilter,
|
||||
},
|
||||
@@ -431,14 +480,14 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
switch (recordFilter.operand) {
|
||||
case RecordFilterOperand.IS_AFTER: {
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
gte: resolvedDateTime.toString(),
|
||||
} as DateTimeFilter,
|
||||
};
|
||||
}
|
||||
case RecordFilterOperand.IS_BEFORE: {
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
lt: resolvedDateTime.toString(),
|
||||
} as DateTimeFilter,
|
||||
};
|
||||
@@ -454,13 +503,13 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
switch (recordFilter.operand) {
|
||||
case RecordFilterOperand.IS:
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
eq: convertRatingToRatingValue(parseFloat(recordFilter.value)),
|
||||
} as RatingFilter,
|
||||
};
|
||||
case RecordFilterOperand.GREATER_THAN_OR_EQUAL:
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
in: convertGreaterThanOrEqualRatingToArrayOfRatingValues(
|
||||
parseFloat(recordFilter.value),
|
||||
),
|
||||
@@ -468,7 +517,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
};
|
||||
case RecordFilterOperand.LESS_THAN_OR_EQUAL:
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
in: convertLessThanOrEqualRatingToArrayOfRatingValues(
|
||||
parseFloat(recordFilter.value),
|
||||
),
|
||||
@@ -483,26 +532,26 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
switch (recordFilter.operand) {
|
||||
case RecordFilterOperand.GREATER_THAN_OR_EQUAL:
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
gte: parseFloat(recordFilter.value),
|
||||
} as FloatFilter,
|
||||
};
|
||||
case RecordFilterOperand.LESS_THAN_OR_EQUAL:
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
lte: parseFloat(recordFilter.value),
|
||||
} as FloatFilter,
|
||||
};
|
||||
case RecordFilterOperand.IS:
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
eq: parseFloat(recordFilter.value),
|
||||
} as FloatFilter,
|
||||
};
|
||||
case RecordFilterOperand.IS_NOT:
|
||||
return {
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
eq: parseFloat(recordFilter.value),
|
||||
} as FloatFilter,
|
||||
},
|
||||
@@ -535,7 +584,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
switch (recordFilter.operand) {
|
||||
case RecordFilterOperand.IS:
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name + 'Id']: {
|
||||
[fieldMetadataItem.name + 'Id']: {
|
||||
in: recordIds,
|
||||
} as RelationFilter,
|
||||
};
|
||||
@@ -545,13 +594,13 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
or: [
|
||||
{
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name + 'Id']: {
|
||||
[fieldMetadataItem.name + 'Id']: {
|
||||
in: recordIds,
|
||||
} as RelationFilter,
|
||||
},
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name + 'Id']: {
|
||||
[fieldMetadataItem.name + 'Id']: {
|
||||
is: 'NULL',
|
||||
} as RelationFilter,
|
||||
},
|
||||
@@ -579,7 +628,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
if (parsedCurrencyCodes.length === 0) return undefined;
|
||||
|
||||
const gqlFilter: RecordGqlOperationFilter = {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
currencyCode: { in: parsedCurrencyCodes },
|
||||
} as CurrencyFilter,
|
||||
};
|
||||
@@ -607,26 +656,26 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
switch (recordFilter.operand) {
|
||||
case RecordFilterOperand.GREATER_THAN_OR_EQUAL:
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
amountMicros: { gte: parseFloat(recordFilter.value) * 1000000 },
|
||||
} as CurrencyFilter,
|
||||
};
|
||||
case RecordFilterOperand.LESS_THAN_OR_EQUAL:
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
amountMicros: { lte: parseFloat(recordFilter.value) * 1000000 },
|
||||
} as CurrencyFilter,
|
||||
};
|
||||
case RecordFilterOperand.IS:
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
amountMicros: { eq: parseFloat(recordFilter.value) * 1000000 },
|
||||
} as CurrencyFilter,
|
||||
};
|
||||
case RecordFilterOperand.IS_NOT:
|
||||
return {
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
amountMicros: {
|
||||
eq: parseFloat(recordFilter.value) * 1000000,
|
||||
},
|
||||
@@ -646,7 +695,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
}
|
||||
case 'LINKS': {
|
||||
return computeGqlOperationFilterForLinks({
|
||||
correspondingFieldMetadataItem,
|
||||
correspondingFieldMetadataItem: fieldMetadataItem,
|
||||
recordFilter,
|
||||
subFieldName,
|
||||
});
|
||||
@@ -654,7 +703,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
case 'FULL_NAME': {
|
||||
const fullNameFilters = generateILikeFiltersForCompositeFields(
|
||||
recordFilter.value,
|
||||
correspondingFieldMetadataItem.name,
|
||||
fieldMetadataItem.name,
|
||||
['firstName', 'lastName'],
|
||||
);
|
||||
switch (recordFilter.operand) {
|
||||
@@ -665,7 +714,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
[subFieldName]: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
},
|
||||
@@ -684,7 +733,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
} else {
|
||||
return {
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
[subFieldName]: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
},
|
||||
@@ -705,42 +754,42 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
return {
|
||||
or: [
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
addressStreet1: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
},
|
||||
} as AddressFilter,
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
addressStreet2: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
},
|
||||
} as AddressFilter,
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
addressCity: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
},
|
||||
} as AddressFilter,
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
addressState: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
},
|
||||
} as AddressFilter,
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
addressCountry: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
},
|
||||
} as AddressFilter,
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
addressPostcode: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
},
|
||||
@@ -759,7 +808,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
}
|
||||
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
[subFieldName]: {
|
||||
in: parsedCountryCodes,
|
||||
} as AddressFilter,
|
||||
@@ -768,7 +817,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
}
|
||||
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
[subFieldName]: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
} as AddressFilter,
|
||||
@@ -783,7 +832,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
or: [
|
||||
{
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
addressStreet1: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
},
|
||||
@@ -791,7 +840,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
},
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
addressStreet1: {
|
||||
is: 'NULL',
|
||||
},
|
||||
@@ -803,7 +852,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
or: [
|
||||
{
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
addressStreet2: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
},
|
||||
@@ -811,7 +860,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
},
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
addressStreet2: {
|
||||
is: 'NULL',
|
||||
},
|
||||
@@ -823,7 +872,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
or: [
|
||||
{
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
addressCity: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
},
|
||||
@@ -831,7 +880,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
},
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
addressCity: {
|
||||
is: 'NULL',
|
||||
},
|
||||
@@ -843,7 +892,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
or: [
|
||||
{
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
addressState: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
},
|
||||
@@ -851,7 +900,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
},
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
addressState: {
|
||||
is: 'NULL',
|
||||
},
|
||||
@@ -863,7 +912,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
or: [
|
||||
{
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
addressPostcode: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
},
|
||||
@@ -871,7 +920,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
},
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
addressPostcode: {
|
||||
is: 'NULL',
|
||||
},
|
||||
@@ -883,7 +932,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
or: [
|
||||
{
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
addressCountry: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
},
|
||||
@@ -891,7 +940,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
},
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
addressCountry: {
|
||||
is: 'NULL',
|
||||
},
|
||||
@@ -918,7 +967,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
or: [
|
||||
{
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
addressCountry: {
|
||||
in: JSON.parse(recordFilter.value),
|
||||
} as AddressFilter,
|
||||
@@ -926,7 +975,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
},
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
addressCountry: {
|
||||
is: 'NULL',
|
||||
} as AddressFilter,
|
||||
@@ -940,7 +989,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
or: [
|
||||
{
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
[subFieldName]: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
} as AddressFilter,
|
||||
@@ -948,7 +997,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
},
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
[subFieldName]: {
|
||||
is: 'NULL',
|
||||
} as AddressFilter,
|
||||
@@ -976,7 +1025,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
|
||||
if (nonEmptyOptions.length > 0) {
|
||||
conditions.push({
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
containsAny: nonEmptyOptions,
|
||||
} as MultiSelectFilter,
|
||||
});
|
||||
@@ -984,7 +1033,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
|
||||
if (emptyOptions.length > 0) {
|
||||
conditions.push({
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
isEmptyArray: true,
|
||||
} as MultiSelectFilter,
|
||||
});
|
||||
@@ -997,18 +1046,18 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
or: [
|
||||
{
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
containsAny: nonEmptyOptions,
|
||||
} as MultiSelectFilter,
|
||||
},
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
isEmptyArray: true,
|
||||
} as MultiSelectFilter,
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
is: 'NULL',
|
||||
} as MultiSelectFilter,
|
||||
},
|
||||
@@ -1034,7 +1083,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
|
||||
if (nonEmptyOptions.length > 0) {
|
||||
conditions.push({
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
in: nonEmptyOptions,
|
||||
} as SelectFilter,
|
||||
});
|
||||
@@ -1042,7 +1091,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
|
||||
if (emptyOptions.length > 0) {
|
||||
conditions.push({
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
is: 'NULL',
|
||||
} as SelectFilter,
|
||||
});
|
||||
@@ -1056,7 +1105,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
if (nonEmptyOptions.length > 0) {
|
||||
conditions.push({
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
in: nonEmptyOptions,
|
||||
} as SelectFilter,
|
||||
},
|
||||
@@ -1066,7 +1115,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
if (emptyOptions.length > 0) {
|
||||
conditions.push({
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
is: 'NULL',
|
||||
} as SelectFilter,
|
||||
},
|
||||
@@ -1085,14 +1134,14 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
switch (recordFilter.operand) {
|
||||
case RecordFilterOperand.CONTAINS:
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
containsIlike: `%${recordFilter.value}%`,
|
||||
} as ArrayFilter,
|
||||
};
|
||||
case RecordFilterOperand.DOES_NOT_CONTAIN:
|
||||
return {
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
containsIlike: `%${recordFilter.value}%`,
|
||||
} as ArrayFilter,
|
||||
},
|
||||
@@ -1114,7 +1163,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
const parsedSources = JSON.parse(recordFilter.value) as string[];
|
||||
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
source: {
|
||||
in: parsedSources,
|
||||
} satisfies RelationFilter,
|
||||
@@ -1132,7 +1181,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
|
||||
return {
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
source: {
|
||||
in: parsedSources,
|
||||
} satisfies RelationFilter,
|
||||
@@ -1141,12 +1190,8 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
};
|
||||
}
|
||||
default: {
|
||||
const fieldForRecordFilter = fieldMetadataItems.find(
|
||||
(field) => field.id === recordFilter.fieldMetadataId,
|
||||
);
|
||||
|
||||
throw new Error(
|
||||
`Unknown operand ${recordFilter.operand} for ${fieldForRecordFilter?.label ?? ''} filter`,
|
||||
`Unknown operand ${recordFilter.operand} for ${fieldMetadataItem.label} filter`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1177,7 +1222,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
switch (recordFilter.operand) {
|
||||
case RecordFilterOperand.IS:
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
workspaceMemberId: {
|
||||
in: workspaceMemberIds,
|
||||
} satisfies UUIDFilter,
|
||||
@@ -1188,7 +1233,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
or: [
|
||||
{
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
workspaceMemberId: {
|
||||
in: workspaceMemberIds,
|
||||
} satisfies UUIDFilter,
|
||||
@@ -1196,7 +1241,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
},
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
workspaceMemberId: {
|
||||
is: 'NULL',
|
||||
} satisfies UUIDFilter,
|
||||
@@ -1206,12 +1251,8 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
};
|
||||
}
|
||||
default: {
|
||||
const fieldForRecordFilter = fieldMetadataItems.find(
|
||||
(field) => field.id === recordFilter.fieldMetadataId,
|
||||
);
|
||||
|
||||
throw new Error(
|
||||
`Unknown operand ${recordFilter.operand} for ${fieldForRecordFilter?.label ?? ''} filter`,
|
||||
`Unknown operand ${recordFilter.operand} for ${fieldMetadataItem.label} filter`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1227,7 +1268,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
return {
|
||||
or: [
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
name: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
},
|
||||
@@ -1236,7 +1277,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
...(matchingSourceValues.length > 0
|
||||
? [
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
source: {
|
||||
in: matchingSourceValues,
|
||||
},
|
||||
@@ -1252,7 +1293,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
and: [
|
||||
{
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
name: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
},
|
||||
@@ -1263,7 +1304,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
? [
|
||||
{
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
source: {
|
||||
in: matchingSourceValues,
|
||||
},
|
||||
@@ -1276,19 +1317,15 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
};
|
||||
}
|
||||
default: {
|
||||
const fieldForRecordFilter = fieldMetadataItems.find(
|
||||
(field) => field.id === recordFilter.fieldMetadataId,
|
||||
);
|
||||
|
||||
throw new Error(
|
||||
`Unknown operand ${recordFilter.operand} for ${fieldForRecordFilter?.label ?? ''} filter`,
|
||||
`Unknown operand ${recordFilter.operand} for ${fieldMetadataItem.label} filter`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
case 'EMAILS': {
|
||||
return computeGqlOperationFilterForEmails({
|
||||
correspondingFieldMetadataItem,
|
||||
correspondingFieldMetadataItem: fieldMetadataItem,
|
||||
recordFilter,
|
||||
subFieldName,
|
||||
});
|
||||
@@ -1306,21 +1343,21 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
return {
|
||||
or: [
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
primaryPhoneNumber: {
|
||||
ilike: `%${filterValue}%`,
|
||||
},
|
||||
} as PhonesFilter,
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
primaryPhoneCallingCode: {
|
||||
ilike: `%${filterValue}%`,
|
||||
},
|
||||
} as PhonesFilter,
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
additionalPhones: {
|
||||
like: `%${filterValue}%`,
|
||||
},
|
||||
@@ -1333,7 +1370,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
and: [
|
||||
{
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
primaryPhoneNumber: {
|
||||
ilike: `%${filterValue}%`,
|
||||
},
|
||||
@@ -1342,7 +1379,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
},
|
||||
{
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
primaryPhoneCallingCode: {
|
||||
ilike: `%${filterValue}%`,
|
||||
},
|
||||
@@ -1353,7 +1390,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
or: [
|
||||
{
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
additionalPhones: {
|
||||
like: `%${filterValue}%`,
|
||||
},
|
||||
@@ -1361,7 +1398,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
},
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
additionalPhones: {
|
||||
is: 'NULL',
|
||||
} as PhonesFilter,
|
||||
@@ -1387,7 +1424,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
return {
|
||||
or: [
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
additionalPhones: {
|
||||
like: `%${filterValue}%`,
|
||||
},
|
||||
@@ -1400,7 +1437,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
or: [
|
||||
{
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
additionalPhones: {
|
||||
like: `%${filterValue}%`,
|
||||
},
|
||||
@@ -1408,7 +1445,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
},
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
additionalPhones: {
|
||||
is: 'NULL',
|
||||
} as PhonesFilter,
|
||||
@@ -1426,7 +1463,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
switch (recordFilter.operand) {
|
||||
case RecordFilterOperand.CONTAINS:
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
primaryPhoneNumber: {
|
||||
ilike: `%${filterValue}%`,
|
||||
},
|
||||
@@ -1435,7 +1472,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
case RecordFilterOperand.DOES_NOT_CONTAIN:
|
||||
return {
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
primaryPhoneNumber: {
|
||||
ilike: `%${filterValue}%`,
|
||||
},
|
||||
@@ -1452,7 +1489,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
switch (recordFilter.operand) {
|
||||
case RecordFilterOperand.CONTAINS:
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
primaryPhoneCallingCode: {
|
||||
ilike: `%${filterValue}%`,
|
||||
},
|
||||
@@ -1461,7 +1498,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
case RecordFilterOperand.DOES_NOT_CONTAIN:
|
||||
return {
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
primaryPhoneCallingCode: {
|
||||
ilike: `%${filterValue}%`,
|
||||
},
|
||||
@@ -1482,7 +1519,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
}
|
||||
case 'BOOLEAN': {
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
eq: recordFilter.value === 'true',
|
||||
} as BooleanFilter,
|
||||
};
|
||||
@@ -1495,7 +1532,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
||||
switch (recordFilter.operand) {
|
||||
case RecordFilterOperand.IS:
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[fieldMetadataItem.name]: {
|
||||
in: recordIds,
|
||||
} as UUIDFilter,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user