Support primitive types in filters (#15402)

When using primitive types such as array, number and boolean, we display
a text field in filters because fieldmetadataId is empty. We should
instead support these as we would do for our own fields.

Adding also a fix for https://github.com/twentyhq/twenty/issues/15282

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
This commit is contained in:
Thomas Trompette
2025-10-28 15:13:10 +01:00
committed by GitHub
parent 98b4e8431b
commit da0e5ba342
7 changed files with 75 additions and 12 deletions
@@ -2,7 +2,9 @@ import { useFieldMetadataItemById } from '@/object-metadata/hooks/useFieldMetada
import { configurableViewFilterOperands } from '@/object-record/object-filter-dropdown/utils/configurableViewFilterOperands';
import { FormFieldInput } from '@/object-record/record-field/ui/components/FormFieldInput';
import { FormArrayFieldInput } from '@/object-record/record-field/ui/form-types/components/FormArrayFieldInput';
import { FormBooleanFieldInput } from '@/object-record/record-field/ui/form-types/components/FormBooleanFieldInput';
import { FormMultiSelectFieldInput } from '@/object-record/record-field/ui/form-types/components/FormMultiSelectFieldInput';
import { FormNumberFieldInput } from '@/object-record/record-field/ui/form-types/components/FormNumberFieldInput';
import { FormRelativeDatePicker } from '@/object-record/record-field/ui/form-types/components/FormRelativeDatePicker';
import { FormSingleRecordPicker } from '@/object-record/record-field/ui/form-types/components/FormSingleRecordPicker';
import { FormTextFieldInput } from '@/object-record/record-field/ui/form-types/components/FormTextFieldInput';
@@ -21,6 +23,7 @@ import {
type StepFilter,
} from 'twenty-shared/types';
import { isDefined, parseJson } from 'twenty-shared/utils';
import { parseBooleanFromStringValue } from 'twenty-shared/workflow';
import { type JsonValue } from 'type-fest';
type WorkflowStepFilterValueInputProps = {
@@ -35,9 +38,9 @@ const COMPOSITE_FIELD_METADATA_TYPES = [
FieldMetadataType.CURRENCY,
];
const isFilterableFieldMetadataType = (
const isFilterableFieldType = (
type: string,
): type is FieldMetadataType => {
): type is FieldMetadataType | 'array' | 'boolean' | 'number' => {
return [
FieldMetadataType.TEXT,
FieldMetadataType.NUMBER,
@@ -53,6 +56,9 @@ const isFilterableFieldMetadataType = (
FieldMetadataType.UUID,
FieldMetadataType.RELATION,
...COMPOSITE_FIELD_METADATA_TYPES,
'array',
'boolean',
'number',
].includes(type as FieldMetadataType);
};
@@ -123,11 +129,7 @@ export const WorkflowStepFilterValueInput = ({
);
}
if (
!isDefined(variableType) ||
!isFilterableFieldMetadataType(variableType) ||
!isDefined(selectedFieldMetadataItem)
) {
if (!isDefined(variableType) || !isFilterableFieldType(variableType)) {
return (
<FormTextFieldInput
defaultValue={stepFilter.value}
@@ -174,7 +176,7 @@ export const WorkflowStepFilterValueInput = ({
);
}
if (variableType === FieldMetadataType.ARRAY) {
if (variableType === FieldMetadataType.ARRAY || variableType === 'array') {
const arrayValue = parseJson<string[]>(stepFilter.value) ?? [];
return (
@@ -186,6 +188,36 @@ export const WorkflowStepFilterValueInput = ({
);
}
if (
variableType === FieldMetadataType.BOOLEAN ||
variableType === 'boolean'
) {
const parsedValue = parseBooleanFromStringValue(stepFilter.value) as
| boolean
| undefined
| string;
return (
<FormBooleanFieldInput
defaultValue={parsedValue}
onChange={handleValueChange}
readonly={readonly}
VariablePicker={WorkflowVariablePicker}
/>
);
}
if (variableType === 'number') {
return (
<FormNumberFieldInput
defaultValue={stepFilter.value}
onChange={handleValueChange}
readonly={readonly}
VariablePicker={WorkflowVariablePicker}
/>
);
}
const field = {
type: variableType as FieldMetadataType,
label: '',
@@ -67,6 +67,6 @@ export const NumberInput: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await expect(await canvas.findByText('100')).toBeVisible();
await expect(await canvas.findByDisplayValue(100)).toBeVisible();
},
};
@@ -60,7 +60,7 @@ export const FILTER_OPERANDS_MAP = {
...emptyOperands,
],
BOOLEAN: [ViewFilterOperand.IS],
UUID: [ViewFilterOperand.IS],
UUID: [ViewFilterOperand.IS, ViewFilterOperand.IS_NOT],
NUMERIC: [
ViewFilterOperand.GREATER_THAN_OR_EQUAL,
ViewFilterOperand.LESS_THAN_OR_EQUAL,
@@ -108,6 +108,7 @@ export const getStepFilterOperands = ({
}
}
case 'NUMBER':
case 'number':
return FILTER_OPERANDS_MAP.NUMBER;
case 'RAW_JSON':
return FILTER_OPERANDS_MAP.RAW_JSON;
@@ -123,8 +124,10 @@ export const getStepFilterOperands = ({
case 'SELECT':
return FILTER_OPERANDS_MAP.SELECT;
case 'ARRAY':
case 'array':
return FILTER_OPERANDS_MAP.ARRAY;
case 'BOOLEAN':
case 'boolean':
return FILTER_OPERANDS_MAP.BOOLEAN;
case 'UUID':
return FILTER_OPERANDS_MAP.UUID;