diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterFieldSelect.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterFieldSelect.tsx
index fd082749c..53763ec73 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterFieldSelect.tsx
+++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterFieldSelect.tsx
@@ -1,3 +1,4 @@
+import { useGetFieldMetadataItemById } from '@/object-metadata/hooks/useGetFieldMetadataItemById';
import { SelectControl } from '@/ui/input/components/SelectControl';
import { useWorkflowStepContextOrThrow } from '@/workflow/states/context/WorkflowStepContext';
import { stepsOutputSchemaFamilySelector } from '@/workflow/states/selectors/stepsOutputSchemaFamilySelector';
@@ -38,6 +39,8 @@ export const WorkflowStepFilterFieldSelect = ({
}),
);
+ const { getFieldMetadataItemById } = useGetFieldMetadataItemById();
+
const handleChange = useRecoilCallback(
({ snapshot }) =>
(variableName: string) => {
@@ -54,24 +57,39 @@ export const WorkflowStepFilterFieldSelect = ({
)
.getValue();
- const { variableLabel, variableType } =
- searchVariableThroughOutputSchema({
- stepOutputSchema: currentStepOutputSchema?.[0],
- rawVariableName: variableName,
- isFullRecord: false,
- });
+ const {
+ variableLabel,
+ variableType,
+ fieldMetadataId,
+ compositeFieldSubFieldName,
+ } = searchVariableThroughOutputSchema({
+ stepOutputSchema: currentStepOutputSchema?.[0],
+ rawVariableName: variableName,
+ isFullRecord: false,
+ });
+
+ const filterType = isDefined(fieldMetadataId)
+ ? getFieldMetadataItemById(fieldMetadataId).type
+ : variableType;
upsertStepFilterSettings({
stepFilterToUpsert: {
...stepFilter,
stepOutputKey: variableName,
displayValue: variableLabel ?? '',
- type: variableType ?? 'unknown',
+ type: filterType ?? 'unknown',
value: '',
+ fieldMetadataId,
+ compositeFieldSubFieldName,
},
});
},
- [upsertStepFilterSettings, stepFilter, workflowVersionId],
+ [
+ upsertStepFilterSettings,
+ stepFilter,
+ workflowVersionId,
+ getFieldMetadataItemById,
+ ],
);
if (!isDefined(stepId)) {
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterValueCompositeInput.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterValueCompositeInput.tsx
new file mode 100644
index 000000000..26ad2913e
--- /dev/null
+++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterValueCompositeInput.tsx
@@ -0,0 +1,84 @@
+import { FormCountryMultiSelectInput } from '@/object-record/record-field/form-types/components/FormCountryMultiSelectInput';
+import { FormMultiSelectFieldInput } from '@/object-record/record-field/form-types/components/FormMultiSelectFieldInput';
+import { FormNumberFieldInput } from '@/object-record/record-field/form-types/components/FormNumberFieldInput';
+import { FormTextFieldInput } from '@/object-record/record-field/form-types/components/FormTextFieldInput';
+import { CURRENCIES } from '@/settings/data-model/constants/Currencies';
+import { WorkflowStepFilterContext } from '@/workflow/workflow-steps/workflow-actions/filter-action/states/context/WorkflowStepFilterContext';
+import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
+import { useContext } from 'react';
+import { StepFilter } from 'twenty-shared/types';
+import { JsonValue } from 'type-fest';
+
+export const WorkflowStepFilterValueCompositeInput = ({
+ stepFilter,
+ onChange,
+}: {
+ stepFilter: StepFilter;
+ onChange: (newValue: JsonValue) => void;
+}) => {
+ const { readonly } = useContext(WorkflowStepFilterContext);
+ const { type: filterType, compositeFieldSubFieldName: subFieldName } =
+ stepFilter;
+
+ return (
+ <>
+ {filterType === 'ADDRESS' ? (
+ subFieldName === 'addressCountry' ? (
+
+ ) : (
+
+ )
+ ) : filterType === 'CURRENCY' ? (
+ subFieldName === 'currencyCode' ? (
+
+ ) : subFieldName === 'amountMicros' ? (
+
+ ) : null
+ ) : filterType === 'PHONES' ? (
+ subFieldName === 'primaryPhoneNumber' ? (
+
+ ) : (
+
+ )
+ ) : (
+
+ )}
+ >
+ );
+};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterValueInput.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterValueInput.tsx
index e05359fff..a309fde68 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterValueInput.tsx
+++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterValueInput.tsx
@@ -3,17 +3,13 @@ import { configurableViewFilterOperands } from '@/object-record/object-filter-dr
import { FormFieldInput } from '@/object-record/record-field/components/FormFieldInput';
import { FormTextFieldInput } from '@/object-record/record-field/form-types/components/FormTextFieldInput';
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
-import { useWorkflowStepContextOrThrow } from '@/workflow/states/context/WorkflowStepContext';
-import { stepsOutputSchemaFamilySelector } from '@/workflow/states/selectors/stepsOutputSchemaFamilySelector';
+import { WorkflowStepFilterValueCompositeInput } from '@/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterValueCompositeInput';
import { useUpsertStepFilterSettings } from '@/workflow/workflow-steps/workflow-actions/filter-action/hooks/useUpsertStepFilterSettings';
import { WorkflowStepFilterContext } from '@/workflow/workflow-steps/workflow-actions/filter-action/states/context/WorkflowStepFilterContext';
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
-import { extractRawVariableNamePart } from '@/workflow/workflow-variables/utils/extractRawVariableNamePart';
-import { searchVariableThroughOutputSchema } from '@/workflow/workflow-variables/utils/searchVariableThroughOutputSchema';
import { useLingui } from '@lingui/react/macro';
import { isObject, isString } from '@sniptt/guards';
import { useContext } from 'react';
-import { useRecoilValue } from 'recoil';
import { FieldMetadataType, StepFilter } from 'twenty-shared/src/types';
import { isDefined } from 'twenty-shared/utils';
import { JsonValue } from 'type-fest';
@@ -22,6 +18,14 @@ type WorkflowStepFilterValueInputProps = {
stepFilter: StepFilter;
};
+const COMPOSITE_FIELD_METADATA_TYPES = [
+ FieldMetadataType.ADDRESS,
+ FieldMetadataType.PHONES,
+ FieldMetadataType.EMAILS,
+ FieldMetadataType.LINKS,
+ FieldMetadataType.CURRENCY,
+];
+
const isFilterableFieldMetadataType = (
type: string,
): type is FieldMetadataType => {
@@ -37,6 +41,8 @@ const isFilterableFieldMetadataType = (
FieldMetadataType.RAW_JSON,
FieldMetadataType.RICH_TEXT_V2,
FieldMetadataType.ARRAY,
+ FieldMetadataType.UUID,
+ ...COMPOSITE_FIELD_METADATA_TYPES,
].includes(type as FieldMetadataType);
};
@@ -47,24 +53,6 @@ export const WorkflowStepFilterValueInput = ({
const { readonly } = useContext(WorkflowStepFilterContext);
const { upsertStepFilterSettings } = useUpsertStepFilterSettings();
- const { workflowVersionId } = useWorkflowStepContextOrThrow();
-
- const stepId = extractRawVariableNamePart({
- rawVariableName: stepFilter.stepOutputKey,
- part: 'stepId',
- });
-
- const stepsOutputSchema = useRecoilValue(
- stepsOutputSchemaFamilySelector({
- workflowVersionId,
- stepIds: [stepId],
- }),
- );
- const { variableType, fieldMetadataId } = searchVariableThroughOutputSchema({
- stepOutputSchema: stepsOutputSchema?.[0],
- rawVariableName: stepFilter.stepOutputKey,
- isFullRecord: false,
- });
const handleValueChange = (value: JsonValue) => {
const valueToUpsert = isString(value)
@@ -92,23 +80,23 @@ export const WorkflowStepFilterValueInput = ({
return null;
}
- if (isDefined(variableType) && isFilterableFieldMetadataType(variableType)) {
- const selectedFieldMetadataItem = isDefined(fieldMetadataId)
- ? getFieldMetadataItemById(fieldMetadataId)
- : undefined;
+ const {
+ fieldMetadataId,
+ type: variableType,
+ compositeFieldSubFieldName,
+ } = stepFilter;
- const field = {
- type: variableType as FieldMetadataType,
- label: '',
- metadata: {
- fieldName: selectedFieldMetadataItem?.name ?? '',
- options: selectedFieldMetadataItem?.options ?? [],
- } as FieldMetadata,
- };
+ const selectedFieldMetadataItem = isDefined(fieldMetadataId)
+ ? getFieldMetadataItemById(fieldMetadataId)
+ : undefined;
+ if (
+ !isDefined(variableType) ||
+ !isFilterableFieldMetadataType(variableType) ||
+ !isDefined(selectedFieldMetadataItem)
+ ) {
return (
-
+ );
+ }
+
+ const field = {
+ type: variableType as FieldMetadataType,
+ label: '',
+ metadata: {
+ fieldName: selectedFieldMetadataItem?.name ?? '',
+ options: selectedFieldMetadataItem?.options ?? [],
+ } as FieldMetadata,
+ };
+
return (
- {
if (!isRecordOutputSchema(outputSchema)) {
return;
@@ -54,6 +62,17 @@ const getFieldMetadataId = (
return undefined;
};
+const isCompositeSubField = (
+ key: string,
+ outputSchema: OutputSchema,
+): boolean => {
+ if (isBaseOutputSchema(outputSchema) && outputSchema[key]?.isLeaf) {
+ return outputSchema[key]?.isCompositeSubField ?? false;
+ }
+
+ return false;
+};
+
const searchCurrentStepOutputSchema = ({
stepOutputSchema,
path,
@@ -70,6 +89,7 @@ const searchCurrentStepOutputSchema = ({
let nextKey = path[nextKeyIndex];
let variablePathLabel = stepOutputSchema.name;
let isSelectedFieldInNextKey = false;
+ let parentFieldMetadataId: string | undefined;
const handleFieldNotFound = () => {
if (nextKeyIndex + 1 < path.length) {
@@ -94,6 +114,7 @@ const searchCurrentStepOutputSchema = ({
currentSubStep = currentField.value;
nextKey = path[nextKeyIndex + 1];
variablePathLabel = `${variablePathLabel} > ${currentField.label}`;
+ parentFieldMetadataId = currentField.fieldMetadataId;
} else {
handleFieldNotFound();
}
@@ -103,6 +124,7 @@ const searchCurrentStepOutputSchema = ({
currentSubStep = currentField.value;
nextKey = path[nextKeyIndex + 1];
variablePathLabel = `${variablePathLabel} > ${currentField.label}`;
+ parentFieldMetadataId = currentField.fieldMetadataId;
} else {
handleFieldNotFound();
}
@@ -118,23 +140,24 @@ const searchCurrentStepOutputSchema = ({
};
}
+ const variableName = isSelectedFieldInNextKey ? nextKey : selectedField;
+ const variableLabel =
+ isFullRecord && isRecordOutputSchema(currentSubStep)
+ ? getDisplayedSubStepObjectLabel(currentSubStep)
+ : getDisplayedSubStepFieldLabel(variableName, currentSubStep);
+
return {
- variableLabel:
- isFullRecord && isRecordOutputSchema(currentSubStep)
- ? getDisplayedSubStepObjectLabel(currentSubStep)
- : getDisplayedSubStepFieldLabel(
- isSelectedFieldInNextKey ? nextKey : selectedField,
- currentSubStep,
- ),
- variablePathLabel,
- variableType: getVariableType(
- isSelectedFieldInNextKey ? nextKey : selectedField,
+ variableLabel,
+ variablePathLabel: `${variablePathLabel} > ${variableLabel}`,
+ variableType: getVariableType(variableName, currentSubStep),
+ fieldMetadataId:
+ getFieldMetadataId(variableName, currentSubStep) ?? parentFieldMetadataId,
+ compositeFieldSubFieldName: isCompositeSubField(
+ variableName,
currentSubStep,
- ),
- fieldMetadataId: getFieldMetadataId(
- isSelectedFieldInNextKey ? nextKey : selectedField,
- currentSubStep,
- ),
+ )
+ ? variableName
+ : undefined,
};
};
@@ -146,7 +169,7 @@ export const searchVariableThroughOutputSchema = ({
stepOutputSchema: StepOutputSchema;
rawVariableName: string;
isFullRecord?: boolean;
-}) => {
+}): VariableInfo => {
if (!isDefined(stepOutputSchema)) {
return {
variableLabel: undefined,
@@ -175,18 +198,10 @@ export const searchVariableThroughOutputSchema = ({
};
}
- const { variableLabel, variablePathLabel, variableType, fieldMetadataId } =
- searchCurrentStepOutputSchema({
- stepOutputSchema,
- path,
- isFullRecord,
- selectedField,
- });
-
- return {
- variableLabel,
- variablePathLabel: `${variablePathLabel} > ${variableLabel}`,
- variableType,
- fieldMetadataId,
- };
+ return searchCurrentStepOutputSchema({
+ stepOutputSchema,
+ path,
+ isFullRecord,
+ selectedField,
+ });
};
diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type.ts
index ba5a9a1a7..a52d9db7e 100644
--- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type.ts
+++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type.ts
@@ -8,6 +8,7 @@ export type Leaf = {
description?: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
value: any;
+ isCompositeSubField?: boolean;
};
export type Node = {
@@ -29,7 +30,9 @@ type Link = {
export type BaseOutputSchema = Record;
export type FieldOutputSchema =
- | ((Leaf | Node) & { fieldMetadataId?: string })
+ | ((Leaf | Node) & {
+ fieldMetadataId?: string;
+ })
| RecordOutputSchema;
export type RecordOutputSchema = {
diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-fake-field.spec.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-fake-field.spec.ts
index d3ed8dfe4..c153b21f0 100644
--- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-fake-field.spec.ts
+++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-fake-field.spec.ts
@@ -158,12 +158,16 @@ describe('generateFakeField', () => {
type: FieldMetadataType.LINKS,
value: {
label: {
+ fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000',
+ isCompositeSubField: true,
isLeaf: true,
type: FieldMetadataType.TEXT,
label: 'Label',
value: 'Fake Label',
},
url: {
+ fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000',
+ isCompositeSubField: true,
isLeaf: true,
type: FieldMetadataType.TEXT,
label: 'Url',
@@ -199,12 +203,16 @@ describe('generateFakeField', () => {
type: FieldMetadataType.CURRENCY,
value: {
amount: {
+ fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000',
+ isCompositeSubField: true,
isLeaf: true,
type: FieldMetadataType.NUMBER,
label: 'Amount',
value: 100,
},
currencyCode: {
+ fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000',
+ isCompositeSubField: true,
isLeaf: true,
type: FieldMetadataType.TEXT,
label: 'Currency Code',
diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-fake-form-response.spec.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-fake-form-response.spec.ts
index 47462cd05..f49fed0b9 100644
--- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-fake-form-response.spec.ts
+++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-fake-form-response.spec.ts
@@ -80,18 +80,24 @@ describe('generateFakeFormResponse', () => {
"type": "LINKS",
"value": {
"primaryLinkLabel": {
+ "fieldMetadataId": "domainNameFieldMetadataId",
+ "isCompositeSubField": true,
"isLeaf": true,
"label": "Primary Link Label",
"type": "TEXT",
"value": "My text",
},
"primaryLinkUrl": {
+ "fieldMetadataId": "domainNameFieldMetadataId",
+ "isCompositeSubField": true,
"isLeaf": true,
"label": "Primary Link Url",
"type": "TEXT",
"value": "My text",
},
"secondaryLinks": {
+ "fieldMetadataId": "domainNameFieldMetadataId",
+ "isCompositeSubField": true,
"isLeaf": true,
"label": "Secondary Links",
"type": "RAW_JSON",
diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-field.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-field.ts
index 480808288..d3a3f2a3c 100644
--- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-field.ts
+++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-field.ts
@@ -37,6 +37,8 @@ export const generateFakeField = ({
type: property.type,
label: camelToTitleCase(property.name),
value: value || generateFakeValue(property.type, 'FieldMetadataType'),
+ fieldMetadataId,
+ isCompositeSubField: true,
};
return acc;
diff --git a/packages/twenty-shared/src/types/StepFilters.ts b/packages/twenty-shared/src/types/StepFilters.ts
index 6011ac68e..20d534f23 100644
--- a/packages/twenty-shared/src/types/StepFilters.ts
+++ b/packages/twenty-shared/src/types/StepFilters.ts
@@ -16,10 +16,12 @@ export type StepFilter = {
id: string;
type: string;
label: string;
+ stepOutputKey: string;
operand: ViewFilterOperand;
value: string;
displayValue: string;
stepFilterGroupId: string;
- stepOutputKey: string;
positionInStepFilterGroup?: number;
+ fieldMetadataId?: string;
+ compositeFieldSubFieldName?: string;
};