Implemented LINKS and EMAILS sub-field fitering (#11984)
This PR introduces LINKS and EMAILS sub-field filtering. It's mainly about the implementation of secondaryLinks and additionalEmails sub-fields, which are treated like additionalPhones. There's also a refactor on the computeViewRecordGqlOperationFilter, a big file that becomes very difficult to read and maintain. This PR breaks it down into multiple smaller utils. There's still work to be done to clean it as it is a central part of the record filter module, this PR lays the foundation.
This commit is contained in:
@ -3,7 +3,7 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { RecordGqlOperationFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
|
||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
import { RecordFilterValueDependencies } from '@/object-record/record-filter/types/RecordFilterValueDependencies';
|
||||
import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
||||
import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeRecordGqlOperationFilter';
|
||||
import { makeAndFilterVariables } from '@/object-record/utils/makeAndFilterVariables';
|
||||
|
||||
export const computeContextStoreFilters = (
|
||||
|
||||
@ -90,9 +90,9 @@ export const AdvancedFilterSubFieldSelectMenu = ({
|
||||
return null;
|
||||
}
|
||||
|
||||
const subFieldNames = SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS[
|
||||
objectFilterDropdownSubMenuFieldType
|
||||
].filterableSubFields.sort((a, b) => a.localeCompare(b));
|
||||
const subFieldNames =
|
||||
SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS[objectFilterDropdownSubMenuFieldType]
|
||||
.filterableSubFields;
|
||||
|
||||
const subFieldsAreFilterable =
|
||||
isDefined(fieldMetadataItemUsedInDropdown) &&
|
||||
|
||||
@ -91,6 +91,7 @@ export type AddressFilter = {
|
||||
export type LinksFilter = {
|
||||
primaryLinkUrl?: StringFilter;
|
||||
primaryLinkLabel?: StringFilter;
|
||||
secondaryLinks?: RawJsonFilter;
|
||||
};
|
||||
|
||||
export type ActorFilter = {
|
||||
@ -100,6 +101,7 @@ export type ActorFilter = {
|
||||
|
||||
export type EmailsFilter = {
|
||||
primaryEmail?: StringFilter;
|
||||
additionalEmails?: RawJsonFilter;
|
||||
};
|
||||
|
||||
export type PhonesFilter = {
|
||||
|
||||
@ -38,7 +38,7 @@ export const ObjectFilterDropdownTextInput = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<DropdownMenuItemsContainer>
|
||||
<DropdownMenuItemsContainer width="auto">
|
||||
<DropdownMenuInput
|
||||
ref={handleInputRef}
|
||||
value={objectFilterDropdownFilterValue}
|
||||
|
||||
@ -32,6 +32,11 @@ describe('getOperandsForFilterType', () => {
|
||||
RecordFilterOperand.IsNot,
|
||||
];
|
||||
|
||||
const actorSourceOperands = [
|
||||
RecordFilterOperand.Is,
|
||||
RecordFilterOperand.IsNot,
|
||||
];
|
||||
|
||||
const dateOperands = [
|
||||
RecordFilterOperand.Is,
|
||||
RecordFilterOperand.IsRelative,
|
||||
@ -49,7 +54,12 @@ describe('getOperandsForFilterType', () => {
|
||||
['FULL_NAME', [...containsOperands, ...emptyOperands]],
|
||||
['ADDRESS', [...containsOperands, ...emptyOperands]],
|
||||
['LINKS', [...containsOperands, ...emptyOperands]],
|
||||
['ACTOR', [...containsOperands, ...emptyOperands]],
|
||||
['LINKS', [...containsOperands, ...emptyOperands], 'primaryLinkUrl'],
|
||||
['LINKS', [...containsOperands, ...emptyOperands], 'primaryLinkLabel'],
|
||||
['LINKS', [...containsOperands, ...emptyOperands], 'secondaryLinks'],
|
||||
['ACTOR', [...containsOperands, ...emptyOperands], 'name'],
|
||||
['ACTOR', [...actorSourceOperands, ...emptyOperands], 'source'],
|
||||
['ACTOR', [...actorSourceOperands, ...emptyOperands]],
|
||||
[
|
||||
'CURRENCY',
|
||||
[...currencyCurrencyCodeOperands, ...emptyOperands],
|
||||
|
||||
@ -7,4 +7,11 @@ export const ICON_NAME_BY_SUB_FIELD: Partial<
|
||||
amountMicros: 'IconNumber95Small',
|
||||
name: 'IconAlignJustified',
|
||||
source: 'IconFileArrowLeft',
|
||||
primaryEmail: 'IconMail',
|
||||
additionalEmails: 'IconList',
|
||||
primaryLinkLabel: 'IconLabel',
|
||||
primaryLinkUrl: 'IconLink',
|
||||
secondaryLinks: 'IconList',
|
||||
primaryPhoneCallingCode: 'IconPlus',
|
||||
additionalPhones: 'IconList',
|
||||
};
|
||||
|
||||
@ -2,7 +2,7 @@ import { FieldCurrencyValue } from '@/object-record/record-field/types/FieldMeta
|
||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand';
|
||||
import { RecordFilterValueDependencies } from '@/object-record/record-filter/types/RecordFilterValueDependencies';
|
||||
import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
||||
import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeRecordGqlOperationFilter';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import { FieldMetadataType } from '~/generated/graphql';
|
||||
import { getCompaniesMock } from '~/testing/mock-data/companies';
|
||||
@ -682,7 +682,7 @@ describe('should work as expected for the different field types', () => {
|
||||
not: {
|
||||
phones: {
|
||||
additionalPhones: {
|
||||
like: `%1234567890%`,
|
||||
like: '%1234567890%',
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -865,6 +865,13 @@ describe('should work as expected for the different field types', () => {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
emails: {
|
||||
additionalEmails: {
|
||||
like: '%test@test.com%',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -878,42 +885,106 @@ describe('should work as expected for the different field types', () => {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
or: [
|
||||
{
|
||||
not: {
|
||||
emails: {
|
||||
additionalEmails: {
|
||||
like: '%test@test.com%',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
emails: {
|
||||
additionalEmails: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
or: [
|
||||
and: [
|
||||
{
|
||||
emails: {
|
||||
primaryEmail: {
|
||||
ilike: '',
|
||||
or: [
|
||||
{
|
||||
emails: {
|
||||
primaryEmail: {
|
||||
eq: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
emails: {
|
||||
primaryEmail: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
emails: {
|
||||
primaryEmail: {
|
||||
is: 'NULL',
|
||||
or: [
|
||||
{
|
||||
emails: {
|
||||
additionalEmails: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
emails: {
|
||||
additionalEmails: {
|
||||
like: '[]',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
not: {
|
||||
or: [
|
||||
and: [
|
||||
{
|
||||
emails: {
|
||||
primaryEmail: {
|
||||
ilike: '',
|
||||
or: [
|
||||
{
|
||||
emails: {
|
||||
primaryEmail: {
|
||||
eq: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
emails: {
|
||||
primaryEmail: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
emails: {
|
||||
primaryEmail: {
|
||||
is: 'NULL',
|
||||
or: [
|
||||
{
|
||||
emails: {
|
||||
additionalEmails: {
|
||||
is: 'NULL',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
emails: {
|
||||
additionalEmails: {
|
||||
like: '[]',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@ -6,6 +6,8 @@ const COMPOSITE_TYPES_FILTERABLE = [
|
||||
'CURRENCY',
|
||||
'ADDRESS',
|
||||
'PHONES',
|
||||
'LINKS',
|
||||
'EMAILS',
|
||||
] satisfies FieldType[];
|
||||
|
||||
type FilterableCompositeFieldType = (typeof COMPOSITE_TYPES_FILTERABLE)[number];
|
||||
|
||||
@ -0,0 +1,92 @@
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import {
|
||||
EmailsFilter,
|
||||
RecordGqlOperationFilter,
|
||||
} from '@/object-record/graphql/types/RecordGqlOperationFilter';
|
||||
|
||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
|
||||
export const computeEmptyGqlOperationFilterForEmails = ({
|
||||
recordFilter,
|
||||
correspondingFieldMetadataItem,
|
||||
}: {
|
||||
recordFilter: RecordFilter;
|
||||
correspondingFieldMetadataItem: Pick<FieldMetadataItem, 'name' | 'type'>;
|
||||
}): RecordGqlOperationFilter => {
|
||||
const subFieldName = recordFilter.subFieldName;
|
||||
const isSubFieldFilter = isNonEmptyString(subFieldName);
|
||||
|
||||
if (isSubFieldFilter) {
|
||||
switch (subFieldName) {
|
||||
case 'primaryEmail': {
|
||||
return {
|
||||
or: [
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
primaryEmail: { eq: '' },
|
||||
} satisfies EmailsFilter,
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
primaryEmail: { is: 'NULL' },
|
||||
} satisfies EmailsFilter,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
case 'additionalEmails': {
|
||||
return {
|
||||
or: [
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
additionalEmails: { is: 'NULL' },
|
||||
} satisfies EmailsFilter,
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
additionalEmails: { like: '[]' },
|
||||
} satisfies EmailsFilter,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
default: {
|
||||
throw new Error(`Unknown subfield name ${subFieldName}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
and: [
|
||||
{
|
||||
or: [
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
primaryEmail: { eq: '' },
|
||||
} satisfies EmailsFilter,
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
primaryEmail: { is: 'NULL' },
|
||||
} satisfies EmailsFilter,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
or: [
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
additionalEmails: { is: 'NULL' },
|
||||
} satisfies EmailsFilter,
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
additionalEmails: { like: '[]' },
|
||||
} satisfies EmailsFilter,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,122 @@
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import {
|
||||
LinksFilter,
|
||||
RecordGqlOperationFilter,
|
||||
} from '@/object-record/graphql/types/RecordGqlOperationFilter';
|
||||
|
||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
|
||||
export const computeEmptyGqlOperationFilterForLinks = ({
|
||||
recordFilter,
|
||||
correspondingFieldMetadataItem,
|
||||
}: {
|
||||
recordFilter: RecordFilter;
|
||||
correspondingFieldMetadataItem: Pick<FieldMetadataItem, 'name' | 'type'>;
|
||||
}): RecordGqlOperationFilter => {
|
||||
const subFieldName = recordFilter.subFieldName;
|
||||
const isSubFieldFilter = isNonEmptyString(subFieldName);
|
||||
|
||||
if (isSubFieldFilter) {
|
||||
switch (subFieldName) {
|
||||
case 'primaryLinkLabel': {
|
||||
return {
|
||||
or: [
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
primaryLinkLabel: { eq: '' },
|
||||
} satisfies LinksFilter,
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
primaryLinkLabel: { is: 'NULL' },
|
||||
} satisfies LinksFilter,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
case 'primaryLinkUrl': {
|
||||
return {
|
||||
or: [
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
primaryLinkUrl: { eq: '' },
|
||||
} satisfies LinksFilter,
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
primaryLinkUrl: { is: 'NULL' },
|
||||
} satisfies LinksFilter,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
case 'secondaryLinks': {
|
||||
return {
|
||||
or: [
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
secondaryLinks: { is: 'NULL' },
|
||||
} satisfies LinksFilter,
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
secondaryLinks: { like: '[]' },
|
||||
} satisfies LinksFilter,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
default: {
|
||||
throw new Error(`Unknown subfield name ${subFieldName}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
and: [
|
||||
{
|
||||
or: [
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
primaryLinkLabel: { eq: '' },
|
||||
} satisfies LinksFilter,
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
primaryLinkLabel: { is: 'NULL' },
|
||||
} satisfies LinksFilter,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
or: [
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
primaryLinkUrl: { eq: '' },
|
||||
} satisfies LinksFilter,
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
primaryLinkUrl: { is: 'NULL' },
|
||||
} satisfies LinksFilter,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
or: [
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
secondaryLinks: { is: 'NULL' },
|
||||
} satisfies LinksFilter,
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
secondaryLinks: { like: '[]' },
|
||||
} satisfies LinksFilter,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,31 @@
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
||||
import { FilterableFieldType } from '@/object-record/record-filter/types/FilterableFieldType';
|
||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
import { isEmptinessOperand } from '@/object-record/record-filter/utils/isEmptinessOperand';
|
||||
|
||||
export const checkIfShouldComputeEmptinessFilter = ({
|
||||
recordFilter,
|
||||
correspondingFieldMetadataItem,
|
||||
}: {
|
||||
recordFilter: RecordFilter;
|
||||
correspondingFieldMetadataItem: Pick<FieldMetadataItem, 'type'>;
|
||||
}) => {
|
||||
const isAnEmptinessOperand = isEmptinessOperand(recordFilter.operand);
|
||||
|
||||
const filterTypesThatHaveNoEmptinessOperand: FilterableFieldType[] = [
|
||||
'BOOLEAN',
|
||||
];
|
||||
|
||||
const filterType = getFilterTypeFromFieldType(
|
||||
correspondingFieldMetadataItem.type,
|
||||
);
|
||||
|
||||
const filterHasEmptinessOperands =
|
||||
!filterTypesThatHaveNoEmptinessOperand.includes(filterType);
|
||||
|
||||
const shouldComputeEmptinessFilter =
|
||||
filterHasEmptinessOperands && isAnEmptinessOperand;
|
||||
|
||||
return shouldComputeEmptinessFilter;
|
||||
};
|
||||
@ -0,0 +1,26 @@
|
||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand';
|
||||
import { isEmptinessOperand } from '@/object-record/record-filter/utils/isEmptinessOperand';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const checkIfShouldSkipFiltering = ({
|
||||
recordFilter,
|
||||
}: {
|
||||
recordFilter: RecordFilter;
|
||||
}) => {
|
||||
const isAnEmptinessOperand = isEmptinessOperand(recordFilter.operand);
|
||||
|
||||
const isDateOperandWithoutValue = [
|
||||
RecordFilterOperand.IsInPast,
|
||||
RecordFilterOperand.IsInFuture,
|
||||
RecordFilterOperand.IsToday,
|
||||
].includes(recordFilter.operand);
|
||||
|
||||
const isFilterValueEmpty =
|
||||
!isDefined(recordFilter.value) || recordFilter.value === '';
|
||||
|
||||
const shouldSkipFiltering =
|
||||
!isAnEmptinessOperand && !isDateOperandWithoutValue && isFilterValueEmpty;
|
||||
|
||||
return shouldSkipFiltering;
|
||||
};
|
||||
@ -0,0 +1,153 @@
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import {
|
||||
EmailsFilter,
|
||||
RecordGqlOperationFilter,
|
||||
} from '@/object-record/graphql/types/RecordGqlOperationFilter';
|
||||
|
||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand';
|
||||
import { CompositeFieldSubFieldName } from '@/settings/data-model/types/CompositeFieldSubFieldName';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
|
||||
export const computeGqlOperationFilterForEmails = ({
|
||||
recordFilter,
|
||||
correspondingFieldMetadataItem,
|
||||
subFieldName,
|
||||
}: {
|
||||
recordFilter: RecordFilter;
|
||||
correspondingFieldMetadataItem: Pick<FieldMetadataItem, 'name' | 'type'>;
|
||||
subFieldName: CompositeFieldSubFieldName | null | undefined;
|
||||
}): RecordGqlOperationFilter => {
|
||||
const isSubFieldFilter = isNonEmptyString(subFieldName);
|
||||
|
||||
if (isSubFieldFilter) {
|
||||
switch (subFieldName) {
|
||||
case 'primaryEmail': {
|
||||
switch (recordFilter.operand) {
|
||||
case RecordFilterOperand.Contains:
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
primaryEmail: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
},
|
||||
} satisfies EmailsFilter,
|
||||
};
|
||||
case RecordFilterOperand.DoesNotContain:
|
||||
return {
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
primaryEmail: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
},
|
||||
} satisfies EmailsFilter,
|
||||
},
|
||||
};
|
||||
default:
|
||||
throw new Error(
|
||||
`Unknown operand ${recordFilter.operand} for ${correspondingFieldMetadataItem.type} filter`,
|
||||
);
|
||||
}
|
||||
}
|
||||
case 'additionalEmails': {
|
||||
switch (recordFilter.operand) {
|
||||
case RecordFilterOperand.Contains:
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
additionalEmails: {
|
||||
like: `%${recordFilter.value}%`,
|
||||
},
|
||||
} satisfies EmailsFilter,
|
||||
};
|
||||
case RecordFilterOperand.DoesNotContain:
|
||||
return {
|
||||
or: [
|
||||
{
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
additionalEmails: {
|
||||
like: `%${recordFilter.value}%`,
|
||||
},
|
||||
} satisfies EmailsFilter,
|
||||
},
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
additionalEmails: {
|
||||
is: 'NULL',
|
||||
},
|
||||
} satisfies EmailsFilter,
|
||||
},
|
||||
],
|
||||
};
|
||||
default:
|
||||
throw new Error(
|
||||
`Unknown operand ${recordFilter.operand} for ${correspondingFieldMetadataItem.type} filter`,
|
||||
);
|
||||
}
|
||||
}
|
||||
default: {
|
||||
throw new Error(`Unknown subfield name ${subFieldName}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (recordFilter.operand) {
|
||||
case RecordFilterOperand.Contains:
|
||||
return {
|
||||
or: [
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
primaryEmail: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
},
|
||||
} satisfies EmailsFilter,
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
additionalEmails: {
|
||||
like: `%${recordFilter.value}%`,
|
||||
},
|
||||
} satisfies EmailsFilter,
|
||||
},
|
||||
],
|
||||
};
|
||||
case RecordFilterOperand.DoesNotContain:
|
||||
return {
|
||||
and: [
|
||||
{
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
primaryEmail: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
},
|
||||
} satisfies EmailsFilter,
|
||||
},
|
||||
},
|
||||
{
|
||||
or: [
|
||||
{
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
additionalEmails: {
|
||||
like: `%${recordFilter.value}%`,
|
||||
},
|
||||
} satisfies EmailsFilter,
|
||||
},
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
additionalEmails: {
|
||||
is: 'NULL',
|
||||
},
|
||||
} satisfies EmailsFilter,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
default:
|
||||
throw new Error(
|
||||
`Unknown operand ${recordFilter.operand} for ${correspondingFieldMetadataItem.type} filter`,
|
||||
);
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,166 @@
|
||||
import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { LinksFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
|
||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand';
|
||||
import { CompositeFieldSubFieldName } from '@/settings/data-model/types/CompositeFieldSubFieldName';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
|
||||
export const computeGqlOperationFilterForLinks = ({
|
||||
recordFilter,
|
||||
correspondingFieldMetadataItem,
|
||||
subFieldName,
|
||||
}: {
|
||||
recordFilter: RecordFilter;
|
||||
correspondingFieldMetadataItem: Pick<FieldMetadataItem, 'name' | 'type'>;
|
||||
subFieldName: CompositeFieldSubFieldName | null | undefined;
|
||||
}) => {
|
||||
const isSubFieldFilter = isNonEmptyString(subFieldName);
|
||||
|
||||
if (isSubFieldFilter) {
|
||||
switch (subFieldName) {
|
||||
case 'primaryLinkLabel':
|
||||
case 'primaryLinkUrl': {
|
||||
switch (recordFilter.operand) {
|
||||
case RecordFilterOperand.Contains:
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[subFieldName]: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
},
|
||||
} satisfies LinksFilter,
|
||||
};
|
||||
case RecordFilterOperand.DoesNotContain:
|
||||
return {
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
[subFieldName]: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
},
|
||||
} satisfies LinksFilter,
|
||||
},
|
||||
};
|
||||
default:
|
||||
throw new Error(
|
||||
`Unknown operand ${recordFilter.operand} for ${correspondingFieldMetadataItem.type} filter`,
|
||||
);
|
||||
}
|
||||
}
|
||||
case 'secondaryLinks': {
|
||||
switch (recordFilter.operand) {
|
||||
case RecordFilterOperand.Contains:
|
||||
return {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
secondaryLinks: {
|
||||
like: `%${recordFilter.value}%`,
|
||||
},
|
||||
} satisfies LinksFilter,
|
||||
};
|
||||
case RecordFilterOperand.DoesNotContain:
|
||||
return {
|
||||
or: [
|
||||
{
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
secondaryLinks: {
|
||||
like: `%${recordFilter.value}%`,
|
||||
},
|
||||
} satisfies LinksFilter,
|
||||
},
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
secondaryLinks: {
|
||||
is: 'NULL',
|
||||
},
|
||||
} satisfies LinksFilter,
|
||||
},
|
||||
],
|
||||
};
|
||||
default:
|
||||
throw new Error(
|
||||
`Unknown operand ${recordFilter.operand} for ${correspondingFieldMetadataItem.type} filter`,
|
||||
);
|
||||
}
|
||||
}
|
||||
default: {
|
||||
throw new Error(`Unknown subfield name ${subFieldName}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (recordFilter.operand) {
|
||||
case RecordFilterOperand.Contains:
|
||||
return {
|
||||
or: [
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
primaryLinkUrl: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
},
|
||||
} satisfies LinksFilter,
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
primaryLinkLabel: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
},
|
||||
} satisfies LinksFilter,
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
secondaryLinks: {
|
||||
like: `%${recordFilter.value}%`,
|
||||
},
|
||||
} satisfies LinksFilter,
|
||||
},
|
||||
],
|
||||
};
|
||||
case RecordFilterOperand.DoesNotContain:
|
||||
return {
|
||||
and: [
|
||||
{
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
primaryLinkLabel: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
},
|
||||
} satisfies LinksFilter,
|
||||
},
|
||||
},
|
||||
{
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
primaryLinkUrl: {
|
||||
ilike: `%${recordFilter.value}%`,
|
||||
},
|
||||
} satisfies LinksFilter,
|
||||
},
|
||||
},
|
||||
{
|
||||
or: [
|
||||
{
|
||||
not: {
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
secondaryLinks: {
|
||||
like: `%${recordFilter.value}%`,
|
||||
},
|
||||
} satisfies LinksFilter,
|
||||
},
|
||||
},
|
||||
{
|
||||
[correspondingFieldMetadataItem.name]: {
|
||||
secondaryLinks: {
|
||||
is: 'NULL',
|
||||
},
|
||||
} satisfies LinksFilter,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
default:
|
||||
throw new Error(
|
||||
`Unknown operand ${recordFilter.operand} for ${correspondingFieldMetadataItem.type} filter`,
|
||||
);
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,83 @@
|
||||
import { RecordGqlOperationFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
|
||||
import { Field } from '~/generated/graphql';
|
||||
|
||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
import { RecordFilterValueDependencies } from '@/object-record/record-filter/types/RecordFilterValueDependencies';
|
||||
|
||||
import { RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
|
||||
import { RecordFilterGroupLogicalOperator } from '@/object-record/record-filter-group/types/RecordFilterGroupLogicalOperator';
|
||||
import { turnRecordFilterIntoRecordGqlOperationFilter } from '@/object-record/record-filter/utils/compute-record-gql-operation-filter/turnRecordFilterIntoGqlOperationFilter';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const turnRecordFilterGroupsIntoGqlOperationFilter = (
|
||||
filterValueDependencies: RecordFilterValueDependencies,
|
||||
filters: RecordFilter[],
|
||||
fields: Pick<Field, 'id' | 'name' | 'type'>[],
|
||||
recordFilterGroups: RecordFilterGroup[],
|
||||
currentRecordFilterGroupId?: string,
|
||||
): RecordGqlOperationFilter | undefined => {
|
||||
const currentRecordFilterGroup = recordFilterGroups.find(
|
||||
(recordFilterGroup) => recordFilterGroup.id === currentRecordFilterGroupId,
|
||||
);
|
||||
|
||||
if (!isDefined(currentRecordFilterGroup)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const recordFiltersInGroup = filters.filter(
|
||||
(filter) => filter.recordFilterGroupId === currentRecordFilterGroupId,
|
||||
);
|
||||
|
||||
const groupRecordGqlOperationFilters = recordFiltersInGroup
|
||||
.map((recordFilter) =>
|
||||
turnRecordFilterIntoRecordGqlOperationFilter({
|
||||
filterValueDependencies,
|
||||
recordFilter: recordFilter,
|
||||
fieldMetadataItems: fields,
|
||||
}),
|
||||
)
|
||||
.filter(isDefined);
|
||||
|
||||
const subGroupRecordGqlOperationFilters = recordFilterGroups
|
||||
.filter(
|
||||
(recordFilterGroup) =>
|
||||
recordFilterGroup.parentRecordFilterGroupId ===
|
||||
currentRecordFilterGroupId,
|
||||
)
|
||||
.map((subRecordFilterGroup) =>
|
||||
turnRecordFilterGroupsIntoGqlOperationFilter(
|
||||
filterValueDependencies,
|
||||
filters,
|
||||
fields,
|
||||
recordFilterGroups,
|
||||
subRecordFilterGroup.id,
|
||||
),
|
||||
)
|
||||
.filter(isDefined);
|
||||
|
||||
if (
|
||||
currentRecordFilterGroup.logicalOperator ===
|
||||
RecordFilterGroupLogicalOperator.AND
|
||||
) {
|
||||
return {
|
||||
and: [
|
||||
...groupRecordGqlOperationFilters,
|
||||
...subGroupRecordGqlOperationFilters,
|
||||
],
|
||||
};
|
||||
} else if (
|
||||
currentRecordFilterGroup.logicalOperator ===
|
||||
RecordFilterGroupLogicalOperator.OR
|
||||
) {
|
||||
return {
|
||||
or: [
|
||||
...groupRecordGqlOperationFilters,
|
||||
...subGroupRecordGqlOperationFilters,
|
||||
],
|
||||
};
|
||||
} else {
|
||||
throw new Error(
|
||||
`Unknown logical operator ${currentRecordFilterGroup.logicalOperator}`,
|
||||
);
|
||||
}
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,66 @@
|
||||
import { RecordGqlOperationFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
|
||||
import { Field } from '~/generated/graphql';
|
||||
|
||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
import { RecordFilterValueDependencies } from '@/object-record/record-filter/types/RecordFilterValueDependencies';
|
||||
|
||||
import { RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
|
||||
import { turnRecordFilterGroupsIntoGqlOperationFilter } from '@/object-record/record-filter/utils/compute-record-gql-operation-filter/turnRecordFilterGroupIntoGqlOperationFilter';
|
||||
import { turnRecordFilterIntoRecordGqlOperationFilter } from '@/object-record/record-filter/utils/compute-record-gql-operation-filter/turnRecordFilterIntoGqlOperationFilter';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const computeRecordGqlOperationFilter = ({
|
||||
fields,
|
||||
filterValueDependencies,
|
||||
recordFilters,
|
||||
recordFilterGroups,
|
||||
}: {
|
||||
filterValueDependencies: RecordFilterValueDependencies;
|
||||
recordFilters: RecordFilter[];
|
||||
fields: Pick<Field, 'id' | 'name' | 'type'>[];
|
||||
recordFilterGroups: RecordFilterGroup[];
|
||||
}): RecordGqlOperationFilter => {
|
||||
const regularRecordGqlOperationFilter: RecordGqlOperationFilter[] =
|
||||
recordFilters
|
||||
.filter((filter) => !isDefined(filter.recordFilterGroupId))
|
||||
.map((regularFilter) =>
|
||||
turnRecordFilterIntoRecordGqlOperationFilter({
|
||||
filterValueDependencies,
|
||||
recordFilter: regularFilter,
|
||||
fieldMetadataItems: fields,
|
||||
}),
|
||||
)
|
||||
.filter(isDefined);
|
||||
|
||||
const outermostFilterGroupId = recordFilterGroups.find(
|
||||
(recordFilterGroup) => !recordFilterGroup.parentRecordFilterGroupId,
|
||||
)?.id;
|
||||
|
||||
const advancedRecordGqlOperationFilter =
|
||||
turnRecordFilterGroupsIntoGqlOperationFilter(
|
||||
filterValueDependencies,
|
||||
recordFilters,
|
||||
fields,
|
||||
recordFilterGroups,
|
||||
outermostFilterGroupId,
|
||||
);
|
||||
|
||||
const recordGqlOperationFilters = [
|
||||
...regularRecordGqlOperationFilter,
|
||||
advancedRecordGqlOperationFilter,
|
||||
].filter(isDefined);
|
||||
|
||||
if (recordGqlOperationFilters.length === 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (recordGqlOperationFilters.length === 1) {
|
||||
return recordGqlOperationFilters[0];
|
||||
}
|
||||
|
||||
const recordGqlOperationFilter = {
|
||||
and: recordGqlOperationFilters,
|
||||
};
|
||||
|
||||
return recordGqlOperationFilter;
|
||||
};
|
||||
@ -5,7 +5,6 @@ import {
|
||||
ArrayFilter,
|
||||
CurrencyFilter,
|
||||
DateFilter,
|
||||
EmailsFilter,
|
||||
FloatFilter,
|
||||
MultiSelectFilter,
|
||||
PhonesFilter,
|
||||
@ -15,9 +14,10 @@ import {
|
||||
RelationFilter,
|
||||
SelectFilter,
|
||||
StringFilter,
|
||||
URLFilter,
|
||||
} from '@/object-record/graphql/types/RecordGqlOperationFilter';
|
||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||
import { computeEmptyGqlOperationFilterForEmails } from '@/object-record/record-filter/utils/compute-empty-record-gql-operation-filter/for-composite-field/computeEmptyGqlOperationFilterForEmails';
|
||||
import { computeEmptyGqlOperationFilterForLinks } from '@/object-record/record-filter/utils/compute-empty-record-gql-operation-filter/for-composite-field/computeEmptyGqlOperationFilterForLinks';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { Field } from '~/generated/graphql';
|
||||
@ -187,33 +187,10 @@ export const getEmptyRecordGqlOperationFilter = ({
|
||||
break;
|
||||
}
|
||||
case 'LINKS': {
|
||||
if (!isSubFieldFilter) {
|
||||
const linksFilters = generateILikeFiltersForCompositeFields(
|
||||
'',
|
||||
correspondingField.name,
|
||||
['primaryLinkLabel', 'primaryLinkUrl'],
|
||||
true,
|
||||
);
|
||||
|
||||
emptyRecordFilter = {
|
||||
and: linksFilters,
|
||||
};
|
||||
} else {
|
||||
emptyRecordFilter = {
|
||||
or: [
|
||||
{
|
||||
[correspondingField.name]: {
|
||||
[compositeFieldName]: { ilike: '' },
|
||||
} as URLFilter,
|
||||
},
|
||||
{
|
||||
[correspondingField.name]: {
|
||||
[compositeFieldName]: { is: 'NULL' },
|
||||
} as URLFilter,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
emptyRecordFilter = computeEmptyGqlOperationFilterForLinks({
|
||||
correspondingFieldMetadataItem: correspondingField,
|
||||
recordFilter,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'ADDRESS':
|
||||
@ -401,20 +378,10 @@ export const getEmptyRecordGqlOperationFilter = ({
|
||||
};
|
||||
break;
|
||||
case 'EMAILS':
|
||||
emptyRecordFilter = {
|
||||
or: [
|
||||
{
|
||||
[correspondingField.name]: {
|
||||
primaryEmail: { ilike: '' },
|
||||
} as EmailsFilter,
|
||||
},
|
||||
{
|
||||
[correspondingField.name]: {
|
||||
primaryEmail: { is: 'NULL' },
|
||||
} as EmailsFilter,
|
||||
},
|
||||
],
|
||||
};
|
||||
emptyRecordFilter = computeEmptyGqlOperationFilterForEmails({
|
||||
correspondingFieldMetadataItem: correspondingField,
|
||||
recordFilter,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unsupported empty filter type ${filterType}`);
|
||||
|
||||
@ -3,6 +3,7 @@ import { isFilterOnActorSourceSubField } from '@/object-record/object-filter-dro
|
||||
import { FilterableFieldType } from '@/object-record/record-filter/types/FilterableFieldType';
|
||||
import { CompositeFieldSubFieldName } from '@/settings/data-model/types/CompositeFieldSubFieldName';
|
||||
import { ViewFilterOperand as RecordFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
import { assertUnreachable } from 'twenty-shared/utils';
|
||||
|
||||
@ -147,6 +148,8 @@ export const getRecordFilterOperands = ({
|
||||
filterType,
|
||||
subFieldName,
|
||||
}: GetRecordFilterOperandsParams) => {
|
||||
const isFilterOnSubField = isNonEmptyString(subFieldName);
|
||||
|
||||
switch (filterType) {
|
||||
case 'TEXT':
|
||||
case 'EMAILS':
|
||||
@ -184,7 +187,7 @@ export const getRecordFilterOperands = ({
|
||||
case 'SELECT':
|
||||
return FILTER_OPERANDS_MAP.SELECT;
|
||||
case 'ACTOR': {
|
||||
if (isFilterOnActorSourceSubField(subFieldName)) {
|
||||
if (isFilterOnActorSourceSubField(subFieldName) || !isFilterOnSubField) {
|
||||
return [
|
||||
RecordFilterOperand.Is,
|
||||
RecordFilterOperand.IsNot,
|
||||
|
||||
@ -3,7 +3,7 @@ import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils
|
||||
import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||
import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
||||
import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeRecordGqlOperationFilter';
|
||||
import { useCurrentRecordGroupDefinition } from '@/object-record/record-group/hooks/useCurrentRecordGroupDefinition';
|
||||
import { useRecordGroupFilter } from '@/object-record/record-group/hooks/useRecordGroupFilter';
|
||||
import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState';
|
||||
|
||||
@ -8,7 +8,7 @@ import { useSetRecordIdsForColumn } from '@/object-record/record-board/hooks/use
|
||||
import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||
import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
||||
import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeRecordGqlOperationFilter';
|
||||
import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState';
|
||||
import { useRecordBoardRecordGqlFields } from '@/object-record/record-index/hooks/useRecordBoardRecordGqlFields';
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ import { computeAggregateValueAndLabel } from '@/object-record/record-board/reco
|
||||
import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||
import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
||||
import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeRecordGqlOperationFilter';
|
||||
import { recordIndexKanbanAggregateOperationState } from '@/object-record/record-index/states/recordIndexKanbanAggregateOperationState';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { UserContext } from '@/users/contexts/UserContext';
|
||||
|
||||
@ -3,7 +3,7 @@ import { computeAggregateValueAndLabel } from '@/object-record/record-board/reco
|
||||
import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||
import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
||||
import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeRecordGqlOperationFilter';
|
||||
import { useRecordGroupFilter } from '@/object-record/record-group/hooks/useRecordGroupFilter';
|
||||
import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations';
|
||||
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
|
||||
|
||||
@ -56,7 +56,7 @@ export const SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS = {
|
||||
label: 'Emails',
|
||||
Icon: IllustrationIconMail,
|
||||
subFields: ['primaryEmail', 'additionalEmails'],
|
||||
filterableSubFields: ['primaryEmail'],
|
||||
filterableSubFields: ['primaryEmail', 'additionalEmails'],
|
||||
labelBySubField: {
|
||||
primaryEmail: 'Primary Email',
|
||||
additionalEmails: 'Additional Emails',
|
||||
@ -81,7 +81,11 @@ export const SETTINGS_COMPOSITE_FIELD_TYPE_CONFIGS = {
|
||||
},
|
||||
category: 'Basic',
|
||||
subFields: ['primaryLinkUrl', 'primaryLinkLabel', 'secondaryLinks'],
|
||||
filterableSubFields: ['primaryLinkUrl', 'primaryLinkLabel'],
|
||||
filterableSubFields: [
|
||||
'primaryLinkUrl',
|
||||
'primaryLinkLabel',
|
||||
'secondaryLinks',
|
||||
],
|
||||
labelBySubField: {
|
||||
primaryLinkUrl: 'Link URL',
|
||||
primaryLinkLabel: 'Link Label',
|
||||
|
||||
@ -3,7 +3,7 @@ import { useAggregateRecords } from '@/object-record/hooks/useAggregateRecords';
|
||||
import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState';
|
||||
import { useFilterValueDependencies } from '@/object-record/record-filter/hooks/useFilterValueDependencies';
|
||||
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||
import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
||||
import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeRecordGqlOperationFilter';
|
||||
import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useGetViewGroupsFilters } from '@/views/hooks/useGetViewGroupsFilters';
|
||||
|
||||
@ -4,7 +4,7 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';
|
||||
import { RecordFilterValueDependencies } from '@/object-record/record-filter/types/RecordFilterValueDependencies';
|
||||
|
||||
import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter';
|
||||
import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeRecordGqlOperationFilter';
|
||||
import { View } from '@/views/types/View';
|
||||
import { mapViewFilterGroupsToRecordFilterGroups } from '@/views/utils/mapViewFilterGroupsToRecordFilterGroups';
|
||||
import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
|
||||
|
||||
Reference in New Issue
Block a user