Fixed error with previous filters on ACTOR with new sub-field filtering (#12050)
Fixes https://github.com/twentyhq/core-team-issues/issues/969 that appeared since the new ACTOR sub-field filtering that changed the default sub-field filtering from name to source. Now we apply any existing filter value on an ACTOR field, whether for a sub-field or not, to the name sub-field by default. If the user wants to create a sub-field filter on source, he has to create a new advanced filter. Fixes https://github.com/twentyhq/core-team-issues/issues/967 --------- Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
@ -10,7 +10,6 @@ import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
|||||||
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions';
|
||||||
import { ObjectFilterDropdownBooleanSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownBooleanSelect';
|
import { ObjectFilterDropdownBooleanSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownBooleanSelect';
|
||||||
import { ObjectFilterDropdownCurrencySelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownCurrencySelect';
|
import { ObjectFilterDropdownCurrencySelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownCurrencySelect';
|
||||||
import { ObjectFilterDropdownSourceSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownSourceSelect';
|
|
||||||
import { ObjectFilterDropdownTextInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownTextInput';
|
import { ObjectFilterDropdownTextInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownTextInput';
|
||||||
import { DATE_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/DateFilterTypes';
|
import { DATE_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/DateFilterTypes';
|
||||||
import { NUMBER_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/NumberFilterTypes';
|
import { NUMBER_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/NumberFilterTypes';
|
||||||
@ -97,7 +96,7 @@ export const ObjectFilterDropdownFilterInput = ({
|
|||||||
)}
|
)}
|
||||||
{filterType === 'ACTOR' && (
|
{filterType === 'ACTOR' && (
|
||||||
<>
|
<>
|
||||||
<ObjectFilterDropdownSourceSelect />
|
<ObjectFilterDropdownTextInput />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{filterType === 'ADDRESS' &&
|
{filterType === 'ADDRESS' &&
|
||||||
|
|||||||
@ -59,7 +59,7 @@ describe('getOperandsForFilterType', () => {
|
|||||||
['LINKS', [...containsOperands, ...emptyOperands], 'secondaryLinks'],
|
['LINKS', [...containsOperands, ...emptyOperands], 'secondaryLinks'],
|
||||||
['ACTOR', [...containsOperands, ...emptyOperands], 'name'],
|
['ACTOR', [...containsOperands, ...emptyOperands], 'name'],
|
||||||
['ACTOR', [...actorSourceOperands, ...emptyOperands], 'source'],
|
['ACTOR', [...actorSourceOperands, ...emptyOperands], 'source'],
|
||||||
['ACTOR', [...actorSourceOperands, ...emptyOperands]],
|
['ACTOR', [...containsOperands, ...emptyOperands]],
|
||||||
[
|
[
|
||||||
'CURRENCY',
|
'CURRENCY',
|
||||||
[...currencyCurrencyCodeOperands, ...emptyOperands],
|
[...currencyCurrencyCodeOperands, ...emptyOperands],
|
||||||
|
|||||||
@ -886,139 +886,81 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 'ACTOR': {
|
case 'ACTOR': {
|
||||||
if (isSubFieldFilter) {
|
if (subFieldName === 'source') {
|
||||||
switch (subFieldName) {
|
|
||||||
case 'source': {
|
|
||||||
switch (recordFilter.operand) {
|
|
||||||
case RecordFilterOperand.Is: {
|
|
||||||
if (recordFilter.value === '[]') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const parsedSources = JSON.parse(
|
|
||||||
recordFilter.value,
|
|
||||||
) as string[];
|
|
||||||
|
|
||||||
return {
|
|
||||||
[correspondingFieldMetadataItem.name]: {
|
|
||||||
source: {
|
|
||||||
in: parsedSources,
|
|
||||||
} satisfies RelationFilter,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
case RecordFilterOperand.IsNot: {
|
|
||||||
if (recordFilter.value === '[]') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const parsedSources = JSON.parse(
|
|
||||||
recordFilter.value,
|
|
||||||
) as string[];
|
|
||||||
|
|
||||||
if (parsedSources.length === 0) return;
|
|
||||||
|
|
||||||
return {
|
|
||||||
not: {
|
|
||||||
[correspondingFieldMetadataItem.name]: {
|
|
||||||
source: {
|
|
||||||
in: parsedSources,
|
|
||||||
} satisfies RelationFilter,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
throw new Error(
|
|
||||||
`Unknown operand ${recordFilter.operand} for ${recordFilter.label} filter`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case 'name': {
|
|
||||||
switch (recordFilter.operand) {
|
|
||||||
case RecordFilterOperand.Contains:
|
|
||||||
return {
|
|
||||||
or: [
|
|
||||||
{
|
|
||||||
[correspondingFieldMetadataItem.name]: {
|
|
||||||
name: {
|
|
||||||
ilike: `%${recordFilter.value}%`,
|
|
||||||
},
|
|
||||||
} satisfies ActorFilter,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
case RecordFilterOperand.DoesNotContain:
|
|
||||||
return {
|
|
||||||
and: [
|
|
||||||
{
|
|
||||||
not: {
|
|
||||||
[correspondingFieldMetadataItem.name]: {
|
|
||||||
name: {
|
|
||||||
ilike: `%${recordFilter.value}%`,
|
|
||||||
},
|
|
||||||
} satisfies ActorFilter,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
default:
|
|
||||||
throw new Error(
|
|
||||||
`Unknown operand ${recordFilter.operand} for ${recordFilter.label} filter`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
if (recordFilter.value === '[]') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const parsedSources = JSON.parse(recordFilter.value) as string[];
|
|
||||||
|
|
||||||
if (parsedSources.length === 0) return;
|
|
||||||
|
|
||||||
switch (recordFilter.operand) {
|
switch (recordFilter.operand) {
|
||||||
case RecordFilterOperand.Is:
|
case RecordFilterOperand.Is: {
|
||||||
|
if (recordFilter.value === '[]') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedSources = JSON.parse(recordFilter.value) as string[];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
[correspondingFieldMetadataItem.name]: {
|
[correspondingFieldMetadataItem.name]: {
|
||||||
source: {
|
source: {
|
||||||
in: parsedSources,
|
in: parsedSources,
|
||||||
},
|
} satisfies RelationFilter,
|
||||||
} satisfies ActorFilter,
|
},
|
||||||
};
|
};
|
||||||
case RecordFilterOperand.IsNot:
|
}
|
||||||
|
case RecordFilterOperand.IsNot: {
|
||||||
|
if (recordFilter.value === '[]') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedSources = JSON.parse(recordFilter.value) as string[];
|
||||||
|
|
||||||
|
if (parsedSources.length === 0) return;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
and: [
|
not: {
|
||||||
{
|
[correspondingFieldMetadataItem.name]: {
|
||||||
or: [
|
source: {
|
||||||
{
|
in: parsedSources,
|
||||||
not: {
|
} satisfies RelationFilter,
|
||||||
[correspondingFieldMetadataItem.name]: {
|
|
||||||
source: {
|
|
||||||
in: parsedSources,
|
|
||||||
},
|
|
||||||
} satisfies ActorFilter,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
[correspondingFieldMetadataItem.name]: {
|
|
||||||
source: {
|
|
||||||
is: 'NULL',
|
|
||||||
},
|
|
||||||
} satisfies ActorFilter,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
};
|
};
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unknown operand ${recordFilter.operand} for ${recordFilter.label} filter`,
|
`Unknown operand ${recordFilter.operand} for ${recordFilter.label} filter`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (recordFilter.operand) {
|
||||||
|
case RecordFilterOperand.Contains:
|
||||||
|
return {
|
||||||
|
or: [
|
||||||
|
{
|
||||||
|
[correspondingFieldMetadataItem.name]: {
|
||||||
|
name: {
|
||||||
|
ilike: `%${recordFilter.value}%`,
|
||||||
|
},
|
||||||
|
} satisfies ActorFilter,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
case RecordFilterOperand.DoesNotContain:
|
||||||
|
return {
|
||||||
|
and: [
|
||||||
|
{
|
||||||
|
not: {
|
||||||
|
[correspondingFieldMetadataItem.name]: {
|
||||||
|
name: {
|
||||||
|
ilike: `%${recordFilter.value}%`,
|
||||||
|
},
|
||||||
|
} satisfies ActorFilter,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
throw new Error(
|
||||||
|
`Unknown operand ${recordFilter.operand} for ${recordFilter.label} filter`,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case 'EMAILS': {
|
case 'EMAILS': {
|
||||||
return computeGqlOperationFilterForEmails({
|
return computeGqlOperationFilterForEmails({
|
||||||
|
|||||||
@ -25,7 +25,7 @@ export const getDefaultSubFieldNameForCompositeFilterableFieldType = (
|
|||||||
case 'ADDRESS':
|
case 'ADDRESS':
|
||||||
return undefined;
|
return undefined;
|
||||||
case 'ACTOR':
|
case 'ACTOR':
|
||||||
return 'source';
|
return 'name';
|
||||||
case 'FULL_NAME':
|
case 'FULL_NAME':
|
||||||
return undefined;
|
return undefined;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import { isFilterOnActorSourceSubField } from '@/object-record/object-filter-dro
|
|||||||
import { FilterableFieldType } from '@/object-record/record-filter/types/FilterableFieldType';
|
import { FilterableFieldType } from '@/object-record/record-filter/types/FilterableFieldType';
|
||||||
import { CompositeFieldSubFieldName } from '@/settings/data-model/types/CompositeFieldSubFieldName';
|
import { CompositeFieldSubFieldName } from '@/settings/data-model/types/CompositeFieldSubFieldName';
|
||||||
import { ViewFilterOperand as RecordFilterOperand } from '@/views/types/ViewFilterOperand';
|
import { ViewFilterOperand as RecordFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||||
import { isNonEmptyString } from '@sniptt/guards';
|
|
||||||
import { FieldMetadataType } from 'twenty-shared/types';
|
import { FieldMetadataType } from 'twenty-shared/types';
|
||||||
import { assertUnreachable } from 'twenty-shared/utils';
|
import { assertUnreachable } from 'twenty-shared/utils';
|
||||||
|
|
||||||
@ -148,8 +147,6 @@ export const getRecordFilterOperands = ({
|
|||||||
filterType,
|
filterType,
|
||||||
subFieldName,
|
subFieldName,
|
||||||
}: GetRecordFilterOperandsParams) => {
|
}: GetRecordFilterOperandsParams) => {
|
||||||
const isFilterOnSubField = isNonEmptyString(subFieldName);
|
|
||||||
|
|
||||||
switch (filterType) {
|
switch (filterType) {
|
||||||
case 'TEXT':
|
case 'TEXT':
|
||||||
case 'EMAILS':
|
case 'EMAILS':
|
||||||
@ -187,7 +184,7 @@ export const getRecordFilterOperands = ({
|
|||||||
case 'SELECT':
|
case 'SELECT':
|
||||||
return FILTER_OPERANDS_MAP.SELECT;
|
return FILTER_OPERANDS_MAP.SELECT;
|
||||||
case 'ACTOR': {
|
case 'ACTOR': {
|
||||||
if (isFilterOnActorSourceSubField(subFieldName) || !isFilterOnSubField) {
|
if (isFilterOnActorSourceSubField(subFieldName)) {
|
||||||
return [
|
return [
|
||||||
RecordFilterOperand.Is,
|
RecordFilterOperand.Is,
|
||||||
RecordFilterOperand.IsNot,
|
RecordFilterOperand.IsNot,
|
||||||
|
|||||||
Reference in New Issue
Block a user