diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowSingleRecordFieldChip.tsx b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormSingleRecordFieldChip.tsx
similarity index 87%
rename from packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowSingleRecordFieldChip.tsx
rename to packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormSingleRecordFieldChip.tsx
index 38168e0e5..5d290c4b5 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowSingleRecordFieldChip.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormSingleRecordFieldChip.tsx
@@ -1,11 +1,11 @@
import { RecordChip } from '@/object-record/components/RecordChip';
-import { VariableChipStandalone } from '@/object-record/record-field/form-types/components/VariableChipStandalone';
-import { ObjectRecord } from '@/object-record/types/ObjectRecord';
-import { isStandaloneVariableString } from '@/workflow/utils/isStandaloneVariableString';
import {
RecordId,
Variable,
-} from '@/workflow/workflow-steps/workflow-actions/components/WorkflowSingleRecordPicker';
+} from '@/object-record/record-field/form-types/components/FormSingleRecordPicker';
+import { VariableChipStandalone } from '@/object-record/record-field/form-types/components/VariableChipStandalone';
+import { ObjectRecord } from '@/object-record/types/ObjectRecord';
+import { isStandaloneVariableString } from '@/workflow/utils/isStandaloneVariableString';
import styled from '@emotion/styled';
const StyledRecordChip = styled(RecordChip)`
@@ -18,7 +18,7 @@ const StyledPlaceholder = styled.div`
margin: ${({ theme }) => theme.spacing(2)};
`;
-type WorkflowSingleRecordFieldChipProps = {
+type FormSingleRecordFieldChipProps = {
draftValue:
| {
type: 'static';
@@ -34,13 +34,13 @@ type WorkflowSingleRecordFieldChipProps = {
disabled?: boolean;
};
-export const WorkflowSingleRecordFieldChip = ({
+export const FormSingleRecordFieldChip = ({
draftValue,
selectedRecord,
objectNameSingular,
onRemove,
disabled,
-}: WorkflowSingleRecordFieldChipProps) => {
+}: FormSingleRecordFieldChipProps) => {
if (
!!draftValue &&
draftValue.type === 'variable' &&
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowSingleRecordPicker.tsx b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormSingleRecordPicker.tsx
similarity index 74%
rename from packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowSingleRecordPicker.tsx
rename to packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormSingleRecordPicker.tsx
index 7db091805..12f4507ca 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowSingleRecordPicker.tsx
+++ b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormSingleRecordPicker.tsx
@@ -2,6 +2,8 @@ import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
import { FormFieldInputContainer } from '@/object-record/record-field/form-types/components/FormFieldInputContainer';
import { FormFieldInputInputContainer } from '@/object-record/record-field/form-types/components/FormFieldInputInputContainer';
import { FormFieldInputRowContainer } from '@/object-record/record-field/form-types/components/FormFieldInputRowContainer';
+import { FormSingleRecordFieldChip } from '@/object-record/record-field/form-types/components/FormSingleRecordFieldChip';
+import { VariablePickerComponent } from '@/object-record/record-field/form-types/types/VariablePickerComponent';
import { SingleRecordPicker } from '@/object-record/record-picker/single-record-picker/components/SingleRecordPicker';
import { singleRecordPickerSearchFilterComponentState } from '@/object-record/record-picker/single-record-picker/states/singleRecordPickerSearchFilterComponentState';
import { singleRecordPickerSelectedIdComponentState } from '@/object-record/record-picker/single-record-picker/states/singleRecordPickerSelectedIdComponentState';
@@ -12,13 +14,10 @@ import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { isStandaloneVariableString } from '@/workflow/utils/isStandaloneVariableString';
-import { WorkflowSingleRecordFieldChip } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowSingleRecordFieldChip';
-import { WorkflowVariablesDropdown } from '@/workflow/workflow-variables/components/WorkflowVariablesDropdown';
-import { css } from '@emotion/react';
import styled from '@emotion/styled';
import { useCallback } from 'react';
-import { IconChevronDown, IconForbid, LightIconButton } from 'twenty-ui';
import { isDefined, isValidUuid } from 'twenty-shared/utils';
+import { IconChevronDown, IconForbid, LightIconButton } from 'twenty-ui';
const StyledFormSelectContainer = styled(FormFieldInputInputContainer)`
justify-content: space-between;
@@ -26,27 +25,10 @@ const StyledFormSelectContainer = styled(FormFieldInputInputContainer)`
padding-right: ${({ theme }) => theme.spacing(1)};
`;
-const StyledSearchVariablesDropdownContainer = styled.div`
- align-items: center;
- display: flex;
- justify-content: center;
- ${({ theme }) => css`
- :hover {
- background-color: ${theme.background.transparent.light};
- }
- `}
- ${({ theme }) => css`
- background-color: ${theme.background.transparent.lighter};
- border-top-right-radius: ${theme.border.radius.sm};
- border-bottom-right-radius: ${theme.border.radius.sm};
- border: 1px solid ${theme.border.color.medium};
- `}
-`;
-
export type RecordId = string;
export type Variable = string;
-type WorkflowSingleRecordPickerValue =
+type FormSingleRecordPickerValue =
| {
type: 'static';
value: RecordId;
@@ -56,33 +38,36 @@ type WorkflowSingleRecordPickerValue =
value: Variable;
};
-export type WorkflowSingleRecordPickerProps = {
+export type FormSingleRecordPickerProps = {
label?: string;
defaultValue: RecordId | Variable;
onChange: (value: RecordId | Variable) => void;
objectNameSingular: string;
disabled?: boolean;
testId?: string;
+ VariablePicker?: VariablePickerComponent;
};
-export const WorkflowSingleRecordPicker = ({
+export const FormSingleRecordPicker = ({
label,
defaultValue,
objectNameSingular,
onChange,
disabled,
testId,
-}: WorkflowSingleRecordPickerProps) => {
- const draftValue: WorkflowSingleRecordPickerValue =
- isStandaloneVariableString(defaultValue)
- ? {
- type: 'variable',
- value: defaultValue,
- }
- : {
- type: 'static',
- value: defaultValue || '',
- };
+ VariablePicker,
+}: FormSingleRecordPickerProps) => {
+ const draftValue: FormSingleRecordPickerValue = isStandaloneVariableString(
+ defaultValue,
+ )
+ ? {
+ type: 'variable',
+ value: defaultValue,
+ }
+ : {
+ type: 'static',
+ value: defaultValue || '',
+ };
const { record: selectedRecord } = useFindOneRecord({
objectRecordId:
@@ -94,8 +79,8 @@ export const WorkflowSingleRecordPicker = ({
skip: !isValidUuid(defaultValue),
});
- const dropdownId = `workflow-record-picker-${objectNameSingular}`;
- const variablesDropdownId = `workflow-record-picker-${objectNameSingular}-variables`;
+ const dropdownId = `form-record-picker-${objectNameSingular}`;
+ const variablesDropdownId = `form-record-picker-${objectNameSingular}-variables`;
const { closeDropdown } = useDropdown(dropdownId);
@@ -144,8 +129,10 @@ export const WorkflowSingleRecordPicker = ({
{label ? {label} : null}
-
-
+
)}
-
- {!disabled && (
-
-
-
+ {isDefined(VariablePicker) && !disabled && (
+
)}
diff --git a/packages/twenty-front/src/modules/object-record/record-field/form-types/types/VariablePickerComponent.ts b/packages/twenty-front/src/modules/object-record/record-field/form-types/types/VariablePickerComponent.ts
index 8e4d4371e..ad2d9cad8 100644
--- a/packages/twenty-front/src/modules/object-record/record-field/form-types/types/VariablePickerComponent.ts
+++ b/packages/twenty-front/src/modules/object-record/record-field/form-types/types/VariablePickerComponent.ts
@@ -3,4 +3,5 @@ export type VariablePickerComponent = React.FC<{
disabled?: boolean;
multiline?: boolean;
onVariableSelect: (variableName: string) => void;
+ objectNameSingularToSelect?: string;
}>;
diff --git a/packages/twenty-front/src/modules/workflow/validation-schemas/workflowSchema.ts b/packages/twenty-front/src/modules/workflow/validation-schemas/workflowSchema.ts
index e5a442d38..116034053 100644
--- a/packages/twenty-front/src/modules/workflow/validation-schemas/workflowSchema.ts
+++ b/packages/twenty-front/src/modules/workflow/validation-schemas/workflowSchema.ts
@@ -92,6 +92,7 @@ export const workflowFormActionSettingsSchema =
type: z.union([
z.literal(FieldMetadataType.TEXT),
z.literal(FieldMetadataType.NUMBER),
+ z.literal('RECORD'),
]),
placeholder: z.string().optional(),
settings: z.record(z.any()).optional(),
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionDeleteRecord.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionDeleteRecord.tsx
index e532bbcb2..af5f9e06d 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionDeleteRecord.tsx
+++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionDeleteRecord.tsx
@@ -1,14 +1,15 @@
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
+import { FormSingleRecordPicker } from '@/object-record/record-field/form-types/components/FormSingleRecordPicker';
import { Select } from '@/ui/input/components/Select';
import { WorkflowDeleteRecordAction } from '@/workflow/types/Workflow';
import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader';
-import { WorkflowSingleRecordPicker } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowSingleRecordPicker';
import { useEffect, useState } from 'react';
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
import { useActionHeaderTypeOrThrow } from '@/workflow/workflow-steps/workflow-actions/hooks/useActionHeaderTypeOrThrow';
import { useActionIconColorOrThrow } from '@/workflow/workflow-steps/workflow-actions/hooks/useActionIconColorOrThrow';
import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
+import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
import { isDefined } from 'twenty-shared/utils';
import { HorizontalSeparator, SelectOption, useIcons } from 'twenty-ui';
import { JsonValue } from 'type-fest';
@@ -154,7 +155,7 @@ export const WorkflowEditActionDeleteRecord = ({
-
handleFieldChange('objectRecordId', objectRecordId)
@@ -163,6 +164,7 @@ export const WorkflowEditActionDeleteRecord = ({
defaultValue={formData.objectRecordId}
testId="workflow-edit-action-record-delete-object-record-id"
disabled={isFormDisabled}
+ VariablePicker={WorkflowVariablePicker}
/>
>
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionUpdateRecord.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionUpdateRecord.tsx
index 4cf9414fc..2e6061323 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionUpdateRecord.tsx
+++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionUpdateRecord.tsx
@@ -6,9 +6,9 @@ import { useEffect, useState } from 'react';
import { formatFieldMetadataItemAsFieldDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsFieldDefinition';
import { FormFieldInput } from '@/object-record/record-field/components/FormFieldInput';
import { FormMultiSelectFieldInput } from '@/object-record/record-field/form-types/components/FormMultiSelectFieldInput';
+import { FormSingleRecordPicker } from '@/object-record/record-field/form-types/components/FormSingleRecordPicker';
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader';
-import { WorkflowSingleRecordPicker } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowSingleRecordPicker';
import { useActionHeaderTypeOrThrow } from '@/workflow/workflow-steps/workflow-actions/hooks/useActionHeaderTypeOrThrow';
import { useActionIconColorOrThrow } from '@/workflow/workflow-steps/workflow-actions/hooks/useActionIconColorOrThrow';
import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
@@ -208,7 +208,7 @@ export const WorkflowEditActionUpdateRecord = ({
-
@@ -217,6 +217,7 @@ export const WorkflowEditActionUpdateRecord = ({
objectNameSingular={formData.objectName}
defaultValue={formData.objectRecordId}
disabled={isFormDisabled}
+ VariablePicker={WorkflowVariablePicker}
/>
Type
{
if (newType === null) {
return;
}
- const type = newType as
- | FieldMetadataType.TEXT
- | FieldMetadataType.NUMBER;
- const { label, placeholder } = getDefaultFormFieldSettings(type);
+ const type = newType as WorkflowFormFieldType;
+ const { name, label, placeholder, settings } =
+ getDefaultFormFieldSettings(type);
onChange({
...field,
type,
+ name,
label,
placeholder,
+ settings,
});
}}
defaultValue={field.type}
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionFormFiller.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionFormFiller.tsx
index 80fc59082..37b8cc210 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionFormFiller.tsx
+++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionFormFiller.tsx
@@ -1,6 +1,7 @@
import { CmdEnterActionButton } from '@/action-menu/components/CmdEnterActionButton';
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
import { FormFieldInput } from '@/object-record/record-field/components/FormFieldInput';
+import { FormSingleRecordPicker } from '@/object-record/record-field/form-types/components/FormSingleRecordPicker';
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
import { RightDrawerFooter } from '@/ui/layout/right-drawer/components/RightDrawerFooter';
import { useWorkflowStepContextOrThrow } from '@/workflow/states/context/WorkflowStepContext';
@@ -114,28 +115,58 @@ export const WorkflowEditActionFormFiller = ({
disabled
/>
- {formData.map((field) => (
- {
- onFieldUpdate({
- fieldId: field.id,
- value,
- });
- }}
- defaultValue={field.value ?? ''}
- readonly={actionOptions.readonly}
- placeholder={
- field.placeholder ??
- getDefaultFormFieldSettings(field.type).placeholder
+ {formData.map((field) => {
+ if (field.type === 'RECORD') {
+ const objectNameSingular = field.settings?.objectName;
+
+ if (!isDefined(objectNameSingular)) {
+ return null;
}
- />
- ))}
+
+ const recordId = field.value?.id;
+
+ return (
+ {
+ onFieldUpdate({
+ fieldId: field.id,
+ value: {
+ id: recordId,
+ },
+ });
+ }}
+ objectNameSingular={objectNameSingular}
+ disabled={actionOptions.readonly}
+ />
+ );
+ }
+
+ return (
+ {
+ onFieldUpdate({
+ fieldId: field.id,
+ value,
+ });
+ }}
+ defaultValue={field.value ?? ''}
+ readonly={actionOptions.readonly}
+ placeholder={
+ field.placeholder ??
+ getDefaultFormFieldSettings(field.type).placeholder
+ }
+ />
+ );
+ })}
{!actionOptions.readonly && (
void;
+ onChange: (fieldName: string, value: unknown) => void;
}) => {
switch (field.type) {
case FieldMetadataType.TEXT:
@@ -32,6 +33,16 @@ export const WorkflowFormFieldSettingsByType = ({
}}
/>
);
+ case 'RECORD':
+ return (
+ {
+ onChange(fieldName, value);
+ }}
+ />
+ );
default:
return assertUnreachable(field.type, 'Unknown form field type');
}
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowFormFieldSettingsRecordPicker.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowFormFieldSettingsRecordPicker.tsx
new file mode 100644
index 000000000..b995b7908
--- /dev/null
+++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowFormFieldSettingsRecordPicker.tsx
@@ -0,0 +1,69 @@
+import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
+import { FormFieldInputContainer } from '@/object-record/record-field/form-types/components/FormFieldInputContainer';
+import { FormTextFieldInput } from '@/object-record/record-field/form-types/components/FormTextFieldInput';
+import { InputLabel } from '@/ui/input/components/InputLabel';
+import { Select } from '@/ui/input/components/Select';
+import { getDefaultFormFieldSettings } from '@/workflow/workflow-steps/workflow-actions/form-action/utils/getDefaultFormFieldSettings';
+import styled from '@emotion/styled';
+import { SelectOption, useIcons } from 'twenty-ui';
+
+type WorkflowFormFieldSettingsRecordPickerProps = {
+ label?: string;
+ settings?: Record;
+ onChange: (fieldName: string, value: unknown) => void;
+};
+
+const StyledContainer = styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: ${({ theme }) => theme.spacing(2)};
+`;
+
+export const WorkflowFormFieldSettingsRecordPicker = ({
+ label,
+ settings,
+ onChange,
+}: WorkflowFormFieldSettingsRecordPickerProps) => {
+ const { getIcon } = useIcons();
+
+ const { activeObjectMetadataItems } = useFilteredObjectMetadataItems();
+
+ const availableMetadata: Array> =
+ activeObjectMetadataItems.map((item) => ({
+ Icon: getIcon(item.icon),
+ label: item.labelPlural,
+ value: item.nameSingular,
+ }));
+
+ return (
+
+
+
+
+ Label
+ {
+ onChange('label', newLabel);
+ }}
+ defaultValue={label}
+ placeholder={getDefaultFormFieldSettings('RECORD').label}
+ />
+
+
+ );
+};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/__stories__/WorkflowEditActionFormFieldSettings.stories.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/__stories__/WorkflowEditActionFormFieldSettings.stories.tsx
index c92bd3d79..698c96b3d 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/__stories__/WorkflowEditActionFormFieldSettings.stories.tsx
+++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/__stories__/WorkflowEditActionFormFieldSettings.stories.tsx
@@ -1,11 +1,12 @@
import { WorkflowFormAction } from '@/workflow/types/Workflow';
import { Meta, StoryObj } from '@storybook/react';
import { expect, fn, userEvent, within } from '@storybook/test';
+import { FieldMetadataType } from 'twenty-shared/types';
import { ComponentDecorator } from 'twenty-ui';
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
+import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
import { WorkflowStepActionDrawerDecorator } from '~/testing/decorators/WorkflowStepActionDrawerDecorator';
import { WorkflowEditActionFormFieldSettings } from '../WorkflowEditActionFormFieldSettings';
-import { FieldMetadataType } from 'twenty-shared/types';
const meta: Meta = {
title: 'Modules/Workflow/Actions/Form/WorkflowEditActionFormFieldSettings',
@@ -14,6 +15,7 @@ const meta: Meta = {
WorkflowStepActionDrawerDecorator,
ComponentDecorator,
I18nFrontDecorator,
+ ObjectMetadataItemsDecorator,
],
};
@@ -90,3 +92,32 @@ export const NumberFieldSettings: Story = {
expect(args.onClose).toHaveBeenCalled();
},
};
+
+export const SingleRecordFieldSettings: Story = {
+ args: {
+ field: {
+ id: 'field-3',
+ name: 'record',
+ label: 'Record',
+ type: 'RECORD',
+ settings: {
+ objectName: 'company',
+ },
+ },
+ onClose: fn(),
+ },
+
+ play: async ({ canvasElement, args }) => {
+ const canvas = within(canvasElement);
+
+ const typeSelect = await canvas.findByText('Record');
+ expect(typeSelect).toBeVisible();
+
+ const objectSelect = await canvas.findByText('Companies');
+ expect(objectSelect).toBeVisible();
+
+ const closeButton = await canvas.findByTestId('close-button');
+ await userEvent.click(closeButton);
+ expect(args.onClose).toHaveBeenCalled();
+ },
+};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/__stories__/WorkflowEditActionFormFiller.stories.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/__stories__/WorkflowEditActionFormFiller.stories.tsx
index a5d4aadab..cc2ba8ed3 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/__stories__/WorkflowEditActionFormFiller.stories.tsx
+++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/__stories__/WorkflowEditActionFormFiller.stories.tsx
@@ -54,6 +54,16 @@ const mockAction: WorkflowFormAction = {
placeholder: 'Enter number',
settings: {},
},
+ {
+ id: 'field-3',
+ name: 'record',
+ label: 'Record',
+ type: 'RECORD',
+ placeholder: 'Select a record',
+ settings: {
+ objectName: 'company',
+ },
+ },
],
outputSchema: {},
errorHandlingOptions: {
@@ -78,6 +88,9 @@ export const Default: Story = {
const numberField = await canvas.findByText('Number Field');
expect(numberField).toBeVisible();
+
+ const recordField = await canvas.findByText('Record');
+ expect(recordField).toBeVisible();
},
};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/types/WorkflowFormActionField.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/types/WorkflowFormActionField.ts
index 34cd95720..fea86f612 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/types/WorkflowFormActionField.ts
+++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/types/WorkflowFormActionField.ts
@@ -1,12 +1,11 @@
-import { JsonValue } from 'type-fest';
-import { FieldMetadataType } from 'twenty-shared/types';
+import { WorkflowFormFieldType } from '@/workflow/workflow-steps/workflow-actions/form-action/types/WorkflowFormFieldType';
export type WorkflowFormActionField = {
id: string;
name: string;
label: string;
- type: FieldMetadataType.TEXT | FieldMetadataType.NUMBER;
+ type: WorkflowFormFieldType;
placeholder?: string;
- settings?: Record;
- value?: JsonValue;
+ settings?: Record;
+ value?: any;
};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/types/WorkflowFormFieldType.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/types/WorkflowFormFieldType.ts
new file mode 100644
index 000000000..61cc2c7a3
--- /dev/null
+++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/types/WorkflowFormFieldType.ts
@@ -0,0 +1,6 @@
+import { FieldMetadataType } from 'twenty-shared/types';
+
+export type WorkflowFormFieldType =
+ | FieldMetadataType.TEXT
+ | FieldMetadataType.NUMBER
+ | 'RECORD';
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/utils/getDefaultFormFieldSettings.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/utils/getDefaultFormFieldSettings.ts
index 23c957eff..58f607f55 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/utils/getDefaultFormFieldSettings.ts
+++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/utils/getDefaultFormFieldSettings.ts
@@ -1,7 +1,9 @@
-import { v4 } from 'uuid';
+import { WorkflowFormFieldType } from '@/workflow/workflow-steps/workflow-actions/form-action/types/WorkflowFormFieldType';
import { FieldMetadataType } from 'twenty-shared/types';
+import { assertUnreachable } from 'twenty-shared/utils';
+import { v4 } from 'uuid';
-export const getDefaultFormFieldSettings = (type: FieldMetadataType) => {
+export const getDefaultFormFieldSettings = (type: WorkflowFormFieldType) => {
switch (type) {
case FieldMetadataType.TEXT:
return {
@@ -17,12 +19,17 @@ export const getDefaultFormFieldSettings = (type: FieldMetadataType) => {
label: 'Number',
placeholder: '1000',
};
- default:
+ case 'RECORD':
return {
id: v4(),
- name: '',
- label: type.charAt(0).toUpperCase() + type.slice(1),
- placeholder: 'Enter your value',
+ name: 'record',
+ label: 'Record',
+ placeholder: 'Select a record',
+ settings: {
+ objectName: 'company',
+ },
};
+ default:
+ assertUnreachable(type);
}
};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablePicker.tsx b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablePicker.tsx
index 948f77f31..c704570a5 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablePicker.tsx
+++ b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablePicker.tsx
@@ -41,6 +41,7 @@ export const WorkflowVariablePicker: VariablePickerComponent = ({
disabled,
multiline,
onVariableSelect,
+ objectNameSingularToSelect,
}) => {
return (
);
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 c427aec74..c6f24cb03 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
@@ -1,10 +1,24 @@
import { FieldMetadataType } from 'twenty-shared/types';
+import { mockObjectMetadataItemsWithFieldMaps } from 'src/engine/core-modules/search/__mocks__/mockObjectMetadataItemsWithFieldMaps';
import { generateFakeFormResponse } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-form-response';
+import { FormFieldMetadata } from 'src/modules/workflow/workflow-executor/workflow-actions/form/types/workflow-form-action-settings.type';
+
+const companyMockObjectMetadataItem = mockObjectMetadataItemsWithFieldMaps.find(
+ (item) => item.nameSingular === 'company',
+)!;
describe('generateFakeFormResponse', () => {
- it('should generate fake responses for a form schema', () => {
- const schema = [
+ let objectMetadataRepository;
+
+ beforeEach(() => {
+ objectMetadataRepository = {
+ findOneOrFail: jest.fn().mockResolvedValue(companyMockObjectMetadataItem),
+ };
+ });
+
+ it('should generate fake responses for a form schema', async () => {
+ const schema: FormFieldMetadata[] = [
{
id: '96939213-49ac-4dee-949d-56e6c7be98e6',
name: 'name',
@@ -19,34 +33,22 @@ describe('generateFakeFormResponse', () => {
},
{
id: '96939213-49ac-4dee-949d-56e6c7be98e8',
- name: 'email',
- type: FieldMetadataType.EMAILS,
- label: 'Email',
+ name: 'company',
+ type: 'RECORD',
+ label: 'Company',
+ settings: {
+ objectName: 'company',
+ },
},
];
- const result = generateFakeFormResponse(schema);
+ const result = await generateFakeFormResponse({
+ formMetadata: schema,
+ workspaceId: '1',
+ objectMetadataRepository,
+ });
expect(result).toEqual({
- email: {
- isLeaf: false,
- label: 'Email',
- value: {
- additionalEmails: {
- isLeaf: true,
- label: ' Additional Emails',
- type: FieldMetadataType.RAW_JSON,
- value: null,
- },
- primaryEmail: {
- isLeaf: true,
- label: ' Primary Email',
- type: FieldMetadataType.TEXT,
- value: 'My text',
- },
- },
- icon: undefined,
- },
name: {
isLeaf: true,
label: 'Name',
@@ -61,6 +63,22 @@ describe('generateFakeFormResponse', () => {
value: 20,
icon: undefined,
},
+ company: {
+ isLeaf: false,
+ label: 'Company',
+ value: {
+ _outputSchemaType: 'RECORD',
+ fields: {},
+ object: {
+ isLeaf: true,
+ label: 'Company',
+ fieldIdName: 'id',
+ icon: undefined,
+ nameSingular: 'company',
+ value: 'A company',
+ },
+ },
+ },
});
});
});
diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-form-response.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-form-response.ts
index f48b390b6..ff5e7f1c6 100644
--- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-form-response.ts
+++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-form-response.ts
@@ -1,19 +1,58 @@
+import { isDefined } from 'twenty-shared/utils';
+import { Repository } from 'typeorm';
+
+import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import {
Leaf,
Node,
} from 'src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type';
import { generateFakeField } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-field';
+import { generateFakeObjectRecord } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-object-record';
import { FormFieldMetadata } from 'src/modules/workflow/workflow-executor/workflow-actions/form/types/workflow-form-action-settings.type';
-export const generateFakeFormResponse = (
- formMetadata: FormFieldMetadata[],
-): Record => {
- return formMetadata.reduce((acc, formFieldMetadata) => {
- acc[formFieldMetadata.name] = generateFakeField({
- type: formFieldMetadata.type,
- label: formFieldMetadata.label,
- });
+export const generateFakeFormResponse = async ({
+ formMetadata,
+ workspaceId,
+ objectMetadataRepository,
+}: {
+ formMetadata: FormFieldMetadata[];
+ workspaceId: string;
+ objectMetadataRepository: Repository;
+}): Promise> => {
+ const result = await Promise.all(
+ formMetadata.map(async (formFieldMetadata) => {
+ if (formFieldMetadata.type === 'RECORD') {
+ if (!formFieldMetadata?.settings?.objectName) {
+ return undefined;
+ }
- return acc;
+ const objectMetadata = await objectMetadataRepository.findOneOrFail({
+ where: {
+ nameSingular: formFieldMetadata?.settings?.objectName,
+ workspaceId,
+ },
+ relations: ['fields'],
+ });
+
+ return {
+ [formFieldMetadata.name]: {
+ isLeaf: false,
+ label: formFieldMetadata.label,
+ value: generateFakeObjectRecord(objectMetadata),
+ },
+ };
+ } else {
+ return {
+ [formFieldMetadata.name]: generateFakeField({
+ type: formFieldMetadata.type,
+ label: formFieldMetadata.label,
+ }),
+ };
+ }
+ }),
+ );
+
+ return result.filter(isDefined).reduce((acc, curr) => {
+ return { ...acc, ...curr };
}, {});
};
diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service.ts
index 7f8337d04..3582d2bec 100644
--- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service.ts
+++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service.ts
@@ -1,8 +1,8 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
-import { Repository } from 'typeorm';
import { isDefined } from 'twenty-shared/utils';
+import { Repository } from 'typeorm';
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
import { checkStringIsDatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/utils/check-string-is-database-event-action';
@@ -83,6 +83,8 @@ export class WorkflowSchemaWorkspaceService {
case WorkflowActionType.FORM:
return this.computeFormActionOutputSchema({
formMetadata: step.settings.input,
+ workspaceId,
+ objectMetadataRepository: this.objectMetadataRepository,
});
case WorkflowActionType.CODE: // StepOutput schema is computed on serverlessFunction draft execution
default:
@@ -182,11 +184,19 @@ export class WorkflowSchemaWorkspaceService {
return { success: { isLeaf: true, type: 'boolean', value: true } };
}
- private computeFormActionOutputSchema({
+ private async computeFormActionOutputSchema({
formMetadata,
+ workspaceId,
+ objectMetadataRepository,
}: {
formMetadata: FormFieldMetadata[];
- }): OutputSchema {
- return generateFakeFormResponse(formMetadata);
+ workspaceId: string;
+ objectMetadataRepository: Repository;
+ }): Promise {
+ return generateFakeFormResponse({
+ formMetadata,
+ workspaceId,
+ objectMetadataRepository,
+ });
}
}
diff --git a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/form/types/workflow-form-action-settings.type.ts b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/form/types/workflow-form-action-settings.type.ts
index bfaba15f0..6a2c5bb5b 100644
--- a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/form/types/workflow-form-action-settings.type.ts
+++ b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/form/types/workflow-form-action-settings.type.ts
@@ -1,12 +1,11 @@
-import { FieldMetadataType } from 'twenty-shared/types';
-
+import { WorkflowFormFieldType } from 'src/modules/workflow/workflow-executor/workflow-actions/form/types/workflow-form-field-type.type';
import { BaseWorkflowActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-settings.type';
export type FormFieldMetadata = {
id: string;
name: string;
label: string;
- type: FieldMetadataType;
+ type: WorkflowFormFieldType;
value?: any;
placeholder?: string;
settings?: Record;
diff --git a/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/form/types/workflow-form-field-type.type.ts b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/form/types/workflow-form-field-type.type.ts
new file mode 100644
index 000000000..61cc2c7a3
--- /dev/null
+++ b/packages/twenty-server/src/modules/workflow/workflow-executor/workflow-actions/form/types/workflow-form-field-type.type.ts
@@ -0,0 +1,6 @@
+import { FieldMetadataType } from 'twenty-shared/types';
+
+export type WorkflowFormFieldType =
+ | FieldMetadataType.TEXT
+ | FieldMetadataType.NUMBER
+ | 'RECORD';