From ed75fc8a250c7e14fbfdec38bd506bb8c16ff3ee Mon Sep 17 00:00:00 2001
From: Abdul Rahman <81605929+abdulrahmancodes@users.noreply.github.com>
Date: Mon, 11 May 2026 19:51:08 +0530
Subject: [PATCH] Use workflow inputSchema to render boolean, number, and enum
fields in code/logic function steps (#20439)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../components/WorkflowEditActionCode.tsx | 21 +++--
.../WorkflowEditActionCodeFields.tsx | 86 ++++++++++++++++++-
.../components/WorkflowReadonlyActionCode.tsx | 10 ++-
.../getInputSchemaPropertyAtPath.test.ts | 56 ++++++++++++
...orkflowCodeFieldsEnumSelectOptions.test.ts | 22 +++++
.../getWorkflowCodeFieldsLeafKind.test.ts | 36 ++++++++
.../utils/getInputSchemaPropertyAtPath.ts | 30 +++++++
.../getWorkflowCodeFieldsEnumSelectOptions.ts | 17 ++++
.../utils/getWorkflowCodeFieldsLeafKind.ts | 33 +++++++
.../WorkflowEditActionLogicFunction.tsx | 6 ++
.../json-schema-to-input-schema.test.ts | 27 ++++++
.../logic-function/input-json-schema.type.ts | 1 +
.../json-schema-to-input-schema.ts | 4 +
.../src/workflow/types/InputSchema.ts | 3 +-
14 files changed, 341 insertions(+), 11 deletions(-)
create mode 100644 packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/utils/__tests__/getInputSchemaPropertyAtPath.test.ts
create mode 100644 packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/utils/__tests__/getWorkflowCodeFieldsEnumSelectOptions.test.ts
create mode 100644 packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/utils/__tests__/getWorkflowCodeFieldsLeafKind.test.ts
create mode 100644 packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/utils/getInputSchemaPropertyAtPath.ts
create mode 100644 packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/utils/getWorkflowCodeFieldsEnumSelectOptions.ts
create mode 100644 packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/utils/getWorkflowCodeFieldsLeafKind.ts
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionCode.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionCode.tsx
index c8c7f5b9f1c..69b144d8155 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionCode.tsx
+++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionCode.tsx
@@ -8,10 +8,8 @@ import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/wo
import { type WorkflowCodeAction } from '@/workflow/types/Workflow';
import { setNestedValue } from '@/workflow/workflow-steps/workflow-actions/code-action/utils/setNestedValue';
-import { WorkflowStepCmdEnterButton } from '@/workflow/workflow-steps/components/WorkflowStepCmdEnterButton';
import { LogicFunctionExecutionResult } from '@/logic-functions/components/LogicFunctionExecutionResult';
import { LogicFunctionLogs } from '@/logic-functions/components/LogicFunctionLogs';
-import { mergeDefaultFunctionInputAndFunctionInput } from '@/workflow/workflow-steps/workflow-actions/code-action/utils/mergeDefaultFunctionInputAndFunctionInput';
import { InputLabel } from '@/ui/input/components/InputLabel';
import { TabList } from '@/ui/layout/tab-list/components/TabList';
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
@@ -19,15 +17,19 @@ import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotke
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
+import { WorkflowStepCmdEnterButton } from '@/workflow/workflow-steps/components/WorkflowStepCmdEnterButton';
import { WorkflowCodeEditor } from '@/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowCodeEditor';
import { WorkflowEditActionCodeFields } from '@/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionCodeFields';
import { WORKFLOW_LOGIC_FUNCTION_TAB_LIST_COMPONENT_ID } from '@/workflow/workflow-steps/workflow-actions/code-action/constants/WorkflowLogicFunctionTabListComponentId';
import { WorkflowLogicFunctionTabId } from '@/workflow/workflow-steps/workflow-actions/code-action/types/WorkflowLogicFunctionTabId';
import { getWrongExportedFunctionMarkers } from '@/workflow/workflow-steps/workflow-actions/code-action/utils/getWrongExportedFunctionMarkers';
+import { mergeDefaultFunctionInputAndFunctionInput } from '@/workflow/workflow-steps/workflow-actions/code-action/utils/mergeDefaultFunctionInputAndFunctionInput';
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
+import { LogicFunctionTestInputInitEffect } from '@/logic-functions/components/LogicFunctionTestInputInitEffect';
+import { useExecuteLogicFunction } from '@/logic-functions/hooks/useExecuteLogicFunction';
import { WorkflowStepFooter } from '@/workflow/workflow-steps/components/WorkflowStepFooter';
import { CODE_ACTION } from '@/workflow/workflow-steps/workflow-actions/constants/actions/CodeAction';
import { type Monaco } from '@monaco-editor/react';
@@ -35,20 +37,18 @@ import { type editor } from 'monaco-editor';
import { AutoTypings } from 'monaco-editor-auto-typings';
import { useState } from 'react';
import { Key } from 'ts-key-enum';
-import { isDefined } from 'twenty-shared/utils';
import {
getOutputSchemaFromValue,
jsonSchemaToInputSchema,
type InputJsonSchema,
} from 'twenty-shared/logic-function';
+import { isDefined } from 'twenty-shared/utils';
+import { getFunctionInputFromInputSchema } from 'twenty-shared/workflow';
import { IconCode, IconPlayerPlay } from 'twenty-ui/display';
import { CodeEditor } from 'twenty-ui/input';
+import { themeCssVariables } from 'twenty-ui/theme-constants';
import { useIsMobile } from 'twenty-ui/utilities';
import { useDebouncedCallback } from 'use-debounce';
-import { getFunctionInputFromInputSchema } from 'twenty-shared/workflow';
-import { themeCssVariables } from 'twenty-ui/theme-constants';
-import { LogicFunctionTestInputInitEffect } from '@/logic-functions/components/LogicFunctionTestInputInitEffect';
-import { useExecuteLogicFunction } from '@/logic-functions/hooks/useExecuteLogicFunction';
const StyledCodeEditorContainer = styled.div`
display: flex;
@@ -341,6 +341,7 @@ export const WorkflowEditActionCode = ({
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionCodeFields.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionCodeFields.tsx
index c423eacbce1..deb44af1785 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionCodeFields.tsx
+++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionCodeFields.tsx
@@ -1,11 +1,18 @@
+import { FormBooleanFieldInput } from '@/object-record/record-field/ui/form-types/components/FormBooleanFieldInput';
import { FormNestedFieldInputContainer } from '@/object-record/record-field/ui/form-types/components/FormNestedFieldInputContainer';
+import { FormNumberFieldInput } from '@/object-record/record-field/ui/form-types/components/FormNumberFieldInput';
+import { FormSelectFieldInput } from '@/object-record/record-field/ui/form-types/components/FormSelectFieldInput';
import { FormTextFieldInput } from '@/object-record/record-field/ui/form-types/components/FormTextFieldInput';
import { type VariablePickerComponent } from '@/object-record/record-field/ui/form-types/types/VariablePickerComponent';
import { InputLabel } from '@/ui/input/components/InputLabel';
-import { type FunctionInput } from 'twenty-shared/workflow';
+import { getInputSchemaPropertyAtPath } from '@/workflow/workflow-steps/workflow-actions/code-action/utils/getInputSchemaPropertyAtPath';
+import { getWorkflowCodeFieldsEnumSelectOptions } from '@/workflow/workflow-steps/workflow-actions/code-action/utils/getWorkflowCodeFieldsEnumSelectOptions';
+import { getWorkflowCodeFieldsLeafKind } from '@/workflow/workflow-steps/workflow-actions/code-action/utils/getWorkflowCodeFieldsLeafKind';
import { styled } from '@linaria/react';
import { t } from '@lingui/core/macro';
-import { isObject } from '@sniptt/guards';
+import { isNonEmptyArray, isObject } from '@sniptt/guards';
+import { isDefined } from 'twenty-shared/utils';
+import { type FunctionInput, type InputSchema } from 'twenty-shared/workflow';
import { themeCssVariables } from 'twenty-ui/theme-constants';
const StyledContainer = styled.div<{ fullWidth?: boolean }>`
@@ -26,6 +33,7 @@ type WorkflowEditActionCodeFieldsProps = {
onInputChange?: (value: any, path: string[]) => void | Promise;
VariablePicker?: VariablePickerComponent;
fullWidth?: boolean;
+ inputSchema?: InputSchema;
};
export const WorkflowEditActionCodeFields = ({
@@ -35,6 +43,7 @@ export const WorkflowEditActionCodeFields = ({
onInputChange,
VariablePicker,
fullWidth,
+ inputSchema,
}: WorkflowEditActionCodeFieldsProps) => {
return (
@@ -54,12 +63,84 @@ export const WorkflowEditActionCodeFields = ({
onInputChange={onInputChange}
VariablePicker={VariablePicker}
fullWidth={fullWidth}
+ inputSchema={inputSchema}
/>
);
}
+ const schemaProperty = getInputSchemaPropertyAtPath(
+ inputSchema,
+ currentPath,
+ );
+ const leafKind = getWorkflowCodeFieldsLeafKind(schemaProperty);
+
+ if (leafKind === 'boolean') {
+ return (
+ onInputChange?.(value, currentPath)}
+ VariablePicker={VariablePicker}
+ />
+ );
+ }
+
+ if (leafKind === 'number') {
+ return (
+ onInputChange?.(value, currentPath)}
+ VariablePicker={VariablePicker}
+ />
+ );
+ }
+
+ if (leafKind === 'enum' && isDefined(schemaProperty)) {
+ const enumOptions =
+ getWorkflowCodeFieldsEnumSelectOptions(schemaProperty);
+
+ if (isNonEmptyArray(enumOptions)) {
+ return (
+ onInputChange?.(value, currentPath)}
+ VariablePicker={VariablePicker}
+ options={enumOptions}
+ />
+ );
+ }
+ }
+
return (
onInputChange?.(value, currentPath)}
VariablePicker={VariablePicker}
+ multiline={schemaProperty?.multiline === true}
/>
);
})}
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowReadonlyActionCode.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowReadonlyActionCode.tsx
index 3b240a6b683..cf13f18afcd 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowReadonlyActionCode.tsx
+++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowReadonlyActionCode.tsx
@@ -1,6 +1,7 @@
import { useGetAvailablePackages } from '@/logic-functions/hooks/useGetAvailablePackages';
-import { type WorkflowCodeAction } from '@/workflow/types/Workflow';
import { useGetLogicFunctionSourceCode } from '@/logic-functions/hooks/useGetLogicFunctionSourceCode';
+import { useGetOneLogicFunction } from '@/logic-functions/hooks/useGetOneLogicFunction';
+import { type WorkflowCodeAction } from '@/workflow/types/Workflow';
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
import { WorkflowEditActionCodeFields } from '@/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionCodeFields';
@@ -29,6 +30,10 @@ export const WorkflowReadonlyActionCode = ({
id: logicFunctionId,
});
+ const { logicFunction } = useGetOneLogicFunction({
+ id: logicFunctionId,
+ });
+
const { sourceHandlerCode, loading } = useGetLogicFunctionSourceCode({
logicFunctionId,
});
@@ -55,6 +60,9 @@ export const WorkflowReadonlyActionCode = ({
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/utils/__tests__/getInputSchemaPropertyAtPath.test.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/utils/__tests__/getInputSchemaPropertyAtPath.test.ts
new file mode 100644
index 00000000000..668b8ede6fa
--- /dev/null
+++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/utils/__tests__/getInputSchemaPropertyAtPath.test.ts
@@ -0,0 +1,56 @@
+import { type InputSchema } from 'twenty-shared/workflow';
+
+import { getInputSchemaPropertyAtPath } from '@/workflow/workflow-steps/workflow-actions/code-action/utils/getInputSchemaPropertyAtPath';
+
+describe('getInputSchemaPropertyAtPath', () => {
+ const inputSchema = [
+ {
+ type: 'object' as const,
+ properties: {
+ slackChannelId: { type: 'string' as const },
+ messageFormat: {
+ type: 'string' as const,
+ enum: ['plain', 'markdown'],
+ },
+ count: { type: 'number' as const },
+ nested: {
+ type: 'object' as const,
+ properties: {
+ inner: { type: 'string' as const },
+ },
+ },
+ },
+ },
+ ] as InputSchema;
+
+ it('should return property at top-level path', () => {
+ expect(
+ getInputSchemaPropertyAtPath(inputSchema, ['messageFormat']),
+ ).toEqual({
+ type: 'string',
+ enum: ['plain', 'markdown'],
+ });
+ });
+
+ it('should return property at nested path', () => {
+ expect(
+ getInputSchemaPropertyAtPath(inputSchema, ['nested', 'inner']),
+ ).toEqual({
+ type: 'string',
+ });
+ });
+
+ it('should return undefined when path is invalid', () => {
+ expect(
+ getInputSchemaPropertyAtPath(inputSchema, ['missing']),
+ ).toBeUndefined();
+ expect(
+ getInputSchemaPropertyAtPath(inputSchema, ['slackChannelId', 'tooDeep']),
+ ).toBeUndefined();
+ });
+
+ it('should return undefined when inputSchema is missing or empty', () => {
+ expect(getInputSchemaPropertyAtPath(undefined, ['a'])).toBeUndefined();
+ expect(getInputSchemaPropertyAtPath([], ['a'])).toBeUndefined();
+ });
+});
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/utils/__tests__/getWorkflowCodeFieldsEnumSelectOptions.test.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/utils/__tests__/getWorkflowCodeFieldsEnumSelectOptions.test.ts
new file mode 100644
index 00000000000..83fe6497a84
--- /dev/null
+++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/utils/__tests__/getWorkflowCodeFieldsEnumSelectOptions.test.ts
@@ -0,0 +1,22 @@
+import { getWorkflowCodeFieldsEnumSelectOptions } from '@/workflow/workflow-steps/workflow-actions/code-action/utils/getWorkflowCodeFieldsEnumSelectOptions';
+
+describe('getWorkflowCodeFieldsEnumSelectOptions', () => {
+ it('should build select options from schema enum', () => {
+ expect(
+ getWorkflowCodeFieldsEnumSelectOptions({
+ type: 'string',
+ enum: ['plain', 'markdown'],
+ }),
+ ).toEqual([
+ { value: 'plain', label: 'plain' },
+ { value: 'markdown', label: 'markdown' },
+ ]);
+ });
+
+ it('should return empty array when property has no enum', () => {
+ expect(getWorkflowCodeFieldsEnumSelectOptions({ type: 'string' })).toEqual(
+ [],
+ );
+ expect(getWorkflowCodeFieldsEnumSelectOptions(undefined)).toEqual([]);
+ });
+});
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/utils/__tests__/getWorkflowCodeFieldsLeafKind.test.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/utils/__tests__/getWorkflowCodeFieldsLeafKind.test.ts
new file mode 100644
index 00000000000..4c68d33d869
--- /dev/null
+++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/utils/__tests__/getWorkflowCodeFieldsLeafKind.test.ts
@@ -0,0 +1,36 @@
+import { FieldMetadataType } from 'twenty-shared/types';
+
+import { getWorkflowCodeFieldsLeafKind } from '@/workflow/workflow-steps/workflow-actions/code-action/utils/getWorkflowCodeFieldsLeafKind';
+
+describe('getWorkflowCodeFieldsLeafKind', () => {
+ it('should map schema types to leaf editor kind', () => {
+ expect(getWorkflowCodeFieldsLeafKind({ type: 'boolean' })).toBe('boolean');
+ expect(
+ getWorkflowCodeFieldsLeafKind({ type: FieldMetadataType.BOOLEAN }),
+ ).toBe('boolean');
+ expect(getWorkflowCodeFieldsLeafKind({ type: 'number' })).toBe('number');
+ expect(
+ getWorkflowCodeFieldsLeafKind({ type: FieldMetadataType.NUMBER }),
+ ).toBe('number');
+ expect(
+ getWorkflowCodeFieldsLeafKind({ type: FieldMetadataType.NUMERIC }),
+ ).toBe('number');
+ expect(getWorkflowCodeFieldsLeafKind({ type: 'string' })).toBe('text');
+ expect(
+ getWorkflowCodeFieldsLeafKind({
+ type: 'string',
+ enum: ['plain', 'markdown'],
+ }),
+ ).toBe('enum');
+ expect(
+ getWorkflowCodeFieldsLeafKind({
+ type: FieldMetadataType.TEXT,
+ enum: ['a', 'b'],
+ }),
+ ).toBe('enum');
+ expect(getWorkflowCodeFieldsLeafKind({ type: 'string', enum: [] })).toBe(
+ 'text',
+ );
+ expect(getWorkflowCodeFieldsLeafKind(undefined)).toBe('text');
+ });
+});
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/utils/getInputSchemaPropertyAtPath.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/utils/getInputSchemaPropertyAtPath.ts
new file mode 100644
index 00000000000..7fa5fa1d1d2
--- /dev/null
+++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/utils/getInputSchemaPropertyAtPath.ts
@@ -0,0 +1,30 @@
+import {
+ type InputSchema,
+ type InputSchemaProperty,
+} from 'twenty-shared/workflow';
+import { isDefined } from 'twenty-shared/utils';
+
+export const getInputSchemaPropertyAtPath = (
+ inputSchema: InputSchema | undefined,
+ path: string[],
+): InputSchemaProperty | undefined => {
+ if (!isDefined(inputSchema) || inputSchema.length === 0) {
+ return undefined;
+ }
+
+ let current: InputSchemaProperty | undefined = inputSchema[0];
+
+ for (const segment of path) {
+ if (!isDefined(current)) {
+ return undefined;
+ }
+
+ if (current.type === 'object' && isDefined(current.properties?.[segment])) {
+ current = current.properties[segment];
+ } else {
+ return undefined;
+ }
+ }
+
+ return current;
+};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/utils/getWorkflowCodeFieldsEnumSelectOptions.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/utils/getWorkflowCodeFieldsEnumSelectOptions.ts
new file mode 100644
index 00000000000..6c2af84526e
--- /dev/null
+++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/utils/getWorkflowCodeFieldsEnumSelectOptions.ts
@@ -0,0 +1,17 @@
+import { isNonEmptyArray } from '@sniptt/guards';
+import { isDefined } from 'twenty-shared/utils';
+import { type InputSchemaProperty } from 'twenty-shared/workflow';
+import { type SelectOption } from 'twenty-ui/input';
+
+export const getWorkflowCodeFieldsEnumSelectOptions = (
+ property: InputSchemaProperty | undefined,
+): SelectOption[] => {
+ if (!isDefined(property) || !isNonEmptyArray(property.enum)) {
+ return [];
+ }
+
+ return property.enum.map((value) => ({
+ value,
+ label: value,
+ }));
+};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/utils/getWorkflowCodeFieldsLeafKind.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/utils/getWorkflowCodeFieldsLeafKind.ts
new file mode 100644
index 00000000000..247455ee2d3
--- /dev/null
+++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/utils/getWorkflowCodeFieldsLeafKind.ts
@@ -0,0 +1,33 @@
+import { isNonEmptyArray } from '@sniptt/guards';
+import { FieldMetadataType } from 'twenty-shared/types';
+import { isDefined } from 'twenty-shared/utils';
+import { type InputSchemaProperty } from 'twenty-shared/workflow';
+
+type WorkflowCodeFieldsLeafKind = 'boolean' | 'enum' | 'number' | 'text';
+
+export const getWorkflowCodeFieldsLeafKind = (
+ property: InputSchemaProperty | undefined,
+): WorkflowCodeFieldsLeafKind => {
+ if (!isDefined(property)) {
+ return 'text';
+ }
+
+ if (
+ (property.type === 'string' || property.type === FieldMetadataType.TEXT) &&
+ isNonEmptyArray(property.enum)
+ ) {
+ return 'enum';
+ }
+
+ switch (property.type) {
+ case 'boolean':
+ case FieldMetadataType.BOOLEAN:
+ return 'boolean';
+ case 'number':
+ case FieldMetadataType.NUMBER:
+ case FieldMetadataType.NUMERIC:
+ return 'number';
+ default:
+ return 'text';
+ }
+};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/logic-function-action/components/WorkflowEditActionLogicFunction.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/logic-function-action/components/WorkflowEditActionLogicFunction.tsx
index 741d914537f..4abb86e0147 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/logic-function-action/components/WorkflowEditActionLogicFunction.tsx
+++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/logic-function-action/components/WorkflowEditActionLogicFunction.tsx
@@ -213,6 +213,9 @@ export const WorkflowEditActionLogicFunction = ({
<>
@@ -237,6 +240,9 @@ export const WorkflowEditActionLogicFunction = ({
{hasInputFields ? (
{
enum: ['a', 'b'],
});
});
+
+ it('preserves multiline on string properties when true', () => {
+ const result = jsonSchemaToInputSchema({
+ type: 'object',
+ properties: {
+ body: { type: 'string', multiline: true },
+ },
+ });
+
+ expect(result[0].properties?.body).toEqual({
+ type: 'string',
+ multiline: true,
+ });
+ });
+
+ it('does not set multiline when false or omitted', () => {
+ const result = jsonSchemaToInputSchema({
+ type: 'object',
+ properties: {
+ title: { type: 'string', multiline: false },
+ other: { type: 'string' },
+ },
+ });
+
+ expect(result[0].properties?.title).toEqual({ type: 'string' });
+ expect(result[0].properties?.other).toEqual({ type: 'string' });
+ });
});
diff --git a/packages/twenty-shared/src/logic-function/input-json-schema.type.ts b/packages/twenty-shared/src/logic-function/input-json-schema.type.ts
index 7d3fecdf84d..ec0f8179862 100644
--- a/packages/twenty-shared/src/logic-function/input-json-schema.type.ts
+++ b/packages/twenty-shared/src/logic-function/input-json-schema.type.ts
@@ -15,4 +15,5 @@ export type InputJsonSchema = {
additionalProperties?: boolean | InputJsonSchema;
minimum?: number;
maximum?: number;
+ multiline?: boolean;
};
diff --git a/packages/twenty-shared/src/logic-function/json-schema-to-input-schema.ts b/packages/twenty-shared/src/logic-function/json-schema-to-input-schema.ts
index eea64416a74..dd2bb5cda18 100644
--- a/packages/twenty-shared/src/logic-function/json-schema-to-input-schema.ts
+++ b/packages/twenty-shared/src/logic-function/json-schema-to-input-schema.ts
@@ -46,6 +46,10 @@ const convertProperty = (jsonSchema: InputJsonSchema): InputSchemaProperty => {
);
}
+ if (jsonSchema.multiline === true) {
+ property.multiline = true;
+ }
+
return property;
};
diff --git a/packages/twenty-shared/src/workflow/types/InputSchema.ts b/packages/twenty-shared/src/workflow/types/InputSchema.ts
index 726019a14af..83cffd9a5c2 100644
--- a/packages/twenty-shared/src/workflow/types/InputSchema.ts
+++ b/packages/twenty-shared/src/workflow/types/InputSchema.ts
@@ -1,5 +1,5 @@
-import { type LeafType, type NodeType } from '@/workflow';
import { type FieldMetadataType } from '@/types';
+import { type LeafType, type NodeType } from '@/workflow';
export type InputSchemaPropertyType = LeafType | NodeType | FieldMetadataType;
@@ -8,6 +8,7 @@ export type InputSchemaProperty = {
enum?: string[];
items?: InputSchemaProperty;
properties?: Properties;
+ multiline?: boolean;
};
type Properties = {