diff --git a/packages/twenty-front/src/modules/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions.ts b/packages/twenty-front/src/modules/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions.ts index 7038bebab..76c818999 100644 --- a/packages/twenty-front/src/modules/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions.ts +++ b/packages/twenty-front/src/modules/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions.ts @@ -51,6 +51,8 @@ export const getFilterTypeFromFieldType = ( return 'BOOLEAN'; case FieldMetadataType.TS_VECTOR: return 'TS_VECTOR'; + case FieldMetadataType.UUID: + return 'UUID'; default: return 'TEXT'; } diff --git a/packages/twenty-front/src/modules/object-metadata/utils/getFilterFilterableFieldMetadataItems.ts b/packages/twenty-front/src/modules/object-metadata/utils/getFilterFilterableFieldMetadataItems.ts index e5dd52d4d..503a0b8f0 100644 --- a/packages/twenty-front/src/modules/object-metadata/utils/getFilterFilterableFieldMetadataItems.ts +++ b/packages/twenty-front/src/modules/object-metadata/utils/getFilterFilterableFieldMetadataItems.ts @@ -9,6 +9,7 @@ export const getFilterFilterableFieldMetadataItems = ({ return (field: FieldMetadataItem) => { const isSystemField = field.isSystem; const isFieldActive = field.isActive; + const isIdField = field.name === 'id'; const isRelationFieldHandled = !( field.type === FieldMetadataType.RELATION && @@ -33,11 +34,12 @@ export const getFilterFilterableFieldMetadataItems = ({ FieldMetadataType.ACTOR, FieldMetadataType.PHONES, FieldMetadataType.ARRAY, + FieldMetadataType.UUID, ...(isJsonFilterEnabled ? [FieldMetadataType.RAW_JSON] : []), ].includes(field.type); const isFieldFilterable = - !isSystemField && + (!isSystemField || isIdField) && isFieldActive && isRelationFieldHandled && isFieldTypeFilterable; diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/constants/TextFilterTypes.ts b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/constants/TextFilterTypes.ts index 519326b99..f8eb69a50 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/constants/TextFilterTypes.ts +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/constants/TextFilterTypes.ts @@ -7,4 +7,5 @@ export const TEXT_FILTER_TYPES = [ 'LINKS', 'ARRAY', 'RAW_JSON', + 'UUID', ]; diff --git a/packages/twenty-front/src/modules/object-record/record-filter/types/FilterableFieldType.ts b/packages/twenty-front/src/modules/object-record/record-filter/types/FilterableFieldType.ts index 2d6c2d7af..157678465 100644 --- a/packages/twenty-front/src/modules/object-record/record-filter/types/FilterableFieldType.ts +++ b/packages/twenty-front/src/modules/object-record/record-filter/types/FilterableFieldType.ts @@ -20,6 +20,7 @@ export const FILTERABLE_FIELD_TYPES = [ 'ARRAY', 'RAW_JSON', 'BOOLEAN', + 'UUID', ] as const; type FilterableFieldTypeBaseLiteral = (typeof FILTERABLE_FIELD_TYPES)[number]; diff --git a/packages/twenty-front/src/modules/object-record/record-filter/utils/compute-record-gql-operation-filter/turnRecordFilterIntoGqlOperationFilter.ts b/packages/twenty-front/src/modules/object-record/record-filter/utils/compute-record-gql-operation-filter/turnRecordFilterIntoGqlOperationFilter.ts index 82a53e4d5..cabf6e83c 100644 --- a/packages/twenty-front/src/modules/object-record/record-filter/utils/compute-record-gql-operation-filter/turnRecordFilterIntoGqlOperationFilter.ts +++ b/packages/twenty-front/src/modules/object-record/record-filter/utils/compute-record-gql-operation-filter/turnRecordFilterIntoGqlOperationFilter.ts @@ -17,6 +17,7 @@ import { SelectFilter, StringFilter, TSVectorFilter, + UUIDFilter, } from '@/object-record/graphql/types/RecordGqlOperationFilter'; import { Field } from '~/generated/graphql'; import { generateILikeFiltersForCompositeFields } from '~/utils/array/generateILikeFiltersForCompositeFields'; @@ -1176,6 +1177,24 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({ } as BooleanFilter, }; } + case 'UUID': { + const recordIds = arrayOfUuidOrVariableSchema.parse(recordFilter.value); + + if (recordIds.length === 0) return; + + switch (recordFilter.operand) { + case RecordFilterOperand.Is: + return { + [correspondingFieldMetadataItem.name]: { + in: recordIds, + } as UUIDFilter, + }; + default: + throw new Error( + `Unknown operand ${recordFilter.operand} for ${filterType} filter`, + ); + } + } default: throw new Error('Unknown filter type'); } diff --git a/packages/twenty-front/src/modules/object-record/record-filter/utils/getRecordFilterOperands.ts b/packages/twenty-front/src/modules/object-record/record-filter/utils/getRecordFilterOperands.ts index ab8585598..315708315 100644 --- a/packages/twenty-front/src/modules/object-record/record-filter/utils/getRecordFilterOperands.ts +++ b/packages/twenty-front/src/modules/object-record/record-filter/utils/getRecordFilterOperands.ts @@ -128,6 +128,7 @@ export const FILTER_OPERANDS_MAP = { ], BOOLEAN: [RecordFilterOperand.Is], TS_VECTOR: [RecordFilterOperand.VectorSearch], + UUID: [RecordFilterOperand.Is], } as const satisfies FilterOperandMap; export const COMPOSITE_FIELD_FILTER_OPERANDS_MAP = { @@ -204,6 +205,8 @@ export const getRecordFilterOperands = ({ return FILTER_OPERANDS_MAP.BOOLEAN; case 'TS_VECTOR': return FILTER_OPERANDS_MAP.TS_VECTOR; + case 'UUID': + return FILTER_OPERANDS_MAP.UUID; default: assertUnreachable(filterType, `Unknown filter type ${filterType}`); } diff --git a/packages/twenty-front/src/modules/object-record/record-table/utils/buildRecordInputFromFilter.spec.ts b/packages/twenty-front/src/modules/object-record/record-table/utils/buildRecordInputFromFilter.spec.ts index 139febaad..c4a623ca4 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/utils/buildRecordInputFromFilter.spec.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/utils/buildRecordInputFromFilter.spec.ts @@ -497,4 +497,15 @@ describe('buildValueFromFilter', () => { expect(buildValueFromFilter({ filter })).toBeUndefined(); }); }); + + describe('UUID field type', () => { + it('should return the value', () => { + const filter = createTestFilter( + ViewFilterOperand.Is, + 'test-uuid', + 'UUID', + ); + expect(buildValueFromFilter({ filter })).toBe('test-uuid'); + }); + }); }); diff --git a/packages/twenty-front/src/modules/object-record/record-table/utils/buildRecordInputFromFilter.ts b/packages/twenty-front/src/modules/object-record/record-table/utils/buildRecordInputFromFilter.ts index 611f9a8b0..24d814a2b 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/utils/buildRecordInputFromFilter.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/utils/buildRecordInputFromFilter.ts @@ -96,6 +96,11 @@ export const buildValueFromFilter = ({ label, ); } + case 'UUID': + return computeValueFromFilterUUID( + filter.operand as (typeof FILTER_OPERANDS_MAP)['UUID'][number], + filter.value, + ); default: assertUnreachable(filter.type); } @@ -310,3 +315,15 @@ const computeValueFromFilterTSVector = ( assertUnreachable(operand); } }; + +const computeValueFromFilterUUID = ( + operand: RecordFilterToRecordInputOperand<'UUID'>, + value: string, +) => { + switch (operand) { + case ViewFilterOperand.Is: + return value; + default: + assertUnreachable(operand); + } +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/utils/shouldDisplayFormField.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/utils/shouldDisplayFormField.ts index 45f428dae..d05d2c0f0 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/utils/shouldDisplayFormField.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/utils/shouldDisplayFormField.ts @@ -34,6 +34,7 @@ export const shouldDisplayFormField = ({ actionType: WorkflowActionType; }) => { let isTypeAllowedForAction = false; + const isIdField = fieldMetadataItem.name === 'id'; switch (actionType) { case 'CREATE_RECORD': @@ -57,7 +58,7 @@ export const shouldDisplayFormField = ({ return ( isTypeAllowedForAction && - !fieldMetadataItem.isSystem && + (!fieldMetadataItem.isSystem || isIdField) && fieldMetadataItem.isActive ); };