diff --git a/packages/twenty-front/src/modules/workflow/components/WorkflowDiagramStepNodeBase.tsx b/packages/twenty-front/src/modules/workflow/components/WorkflowDiagramStepNodeBase.tsx
index 03d0921ca..859d22ee4 100644
--- a/packages/twenty-front/src/modules/workflow/components/WorkflowDiagramStepNodeBase.tsx
+++ b/packages/twenty-front/src/modules/workflow/components/WorkflowDiagramStepNodeBase.tsx
@@ -76,6 +76,7 @@ export const WorkflowDiagramStepNodeBase = ({
);
}
+ case 'RECORD_CRUD.UPDATE':
case 'RECORD_CRUD.CREATE': {
return (
@@ -87,8 +88,8 @@ export const WorkflowDiagramStepNodeBase = ({
);
}
- case 'RECORD_CRUD.DELETE':
- case 'RECORD_CRUD.UPDATE': {
+
+ case 'RECORD_CRUD.DELETE': {
return null;
}
}
diff --git a/packages/twenty-front/src/modules/workflow/components/WorkflowSingleRecordFieldChip.tsx b/packages/twenty-front/src/modules/workflow/components/WorkflowSingleRecordFieldChip.tsx
new file mode 100644
index 000000000..b1d1fbf92
--- /dev/null
+++ b/packages/twenty-front/src/modules/workflow/components/WorkflowSingleRecordFieldChip.tsx
@@ -0,0 +1,62 @@
+import { RecordChip } from '@/object-record/components/RecordChip';
+import { VariableChip } from '@/object-record/record-field/form-types/components/VariableChip';
+import { ObjectRecord } from '@/object-record/types/ObjectRecord';
+import {
+ RecordId,
+ Variable,
+} from '@/workflow/components/WorkflowSingleRecordPicker';
+import { isStandaloneVariableString } from '@/workflow/utils/isStandaloneVariableString';
+import styled from '@emotion/styled';
+
+const StyledRecordChip = styled(RecordChip)`
+ margin: ${({ theme }) => theme.spacing(2)};
+`;
+
+const StyledPlaceholder = styled.div`
+ color: ${({ theme }) => theme.font.color.tertiary};
+ font-size: ${({ theme }) => theme.font.size.md};
+ margin: ${({ theme }) => theme.spacing(2)};
+`;
+
+type WorkflowSingleRecordFieldChipProps = {
+ draftValue:
+ | {
+ type: 'static';
+ value: RecordId;
+ }
+ | {
+ type: 'variable';
+ value: Variable;
+ };
+ selectedRecord?: ObjectRecord;
+ objectNameSingular: string;
+ onRemove: () => void;
+};
+
+export const WorkflowSingleRecordFieldChip = ({
+ draftValue,
+ selectedRecord,
+ objectNameSingular,
+ onRemove,
+}: WorkflowSingleRecordFieldChipProps) => {
+ if (
+ !!draftValue &&
+ draftValue.type === 'variable' &&
+ isStandaloneVariableString(draftValue.value)
+ ) {
+ return (
+
+ );
+ }
+
+ if (!!draftValue && draftValue.type === 'static' && !!selectedRecord) {
+ return (
+
+ );
+ }
+
+ return Select a {objectNameSingular};
+};
diff --git a/packages/twenty-front/src/modules/workflow/components/WorkflowSingleRecordPicker.tsx b/packages/twenty-front/src/modules/workflow/components/WorkflowSingleRecordPicker.tsx
new file mode 100644
index 000000000..c97167aa6
--- /dev/null
+++ b/packages/twenty-front/src/modules/workflow/components/WorkflowSingleRecordPicker.tsx
@@ -0,0 +1,219 @@
+import {
+ IconChevronDown,
+ IconForbid,
+ isDefined,
+ LightIconButton,
+} from 'twenty-ui';
+
+import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord';
+import { StyledFormFieldInputContainer } from '@/object-record/record-field/form-types/components/StyledFormFieldInputContainer';
+import { StyledFormFieldInputRowContainer } from '@/object-record/record-field/form-types/components/StyledFormFieldInputRowContainer';
+import { SingleRecordSelect } from '@/object-record/relation-picker/components/SingleRecordSelect';
+import { useRecordPicker } from '@/object-record/relation-picker/hooks/useRecordPicker';
+import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext';
+import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect';
+import { ObjectRecord } from '@/object-record/types/ObjectRecord';
+import { InputLabel } from '@/ui/input/components/InputLabel';
+import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
+import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
+import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope';
+import { WorkflowSingleRecordFieldChip } from '@/workflow/components/WorkflowSingleRecordFieldChip';
+import SearchVariablesDropdown from '@/workflow/search-variables/components/SearchVariablesDropdown';
+import { isStandaloneVariableString } from '@/workflow/utils/isStandaloneVariableString';
+import { css } from '@emotion/react';
+import styled from '@emotion/styled';
+import { useCallback, useState } from 'react';
+import { isValidUuid } from '~/utils/isValidUuid';
+
+const StyledFormSelectContainer = styled.div`
+ background-color: ${({ theme }) => theme.background.transparent.lighter};
+ border: 1px solid ${({ theme }) => theme.border.color.medium};
+ border-top-left-radius: ${({ theme }) => theme.border.radius.sm};
+ border-bottom-left-radius: ${({ theme }) => theme.border.radius.sm};
+ border-right: none;
+ border-bottom-right-radius: none;
+ border-top-right-radius: none;
+ box-sizing: border-box;
+ display: flex;
+ overflow: 'hidden';
+ width: 100%;
+ justify-content: space-between;
+ align-items: center;
+ 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;
+
+export type WorkflowSingleRecordPickerProps = {
+ label?: string;
+ defaultValue: RecordId | Variable;
+ onChange: (value: RecordId | Variable) => void;
+ objectNameSingular: string;
+};
+
+export const WorkflowSingleRecordPicker = ({
+ label,
+ defaultValue,
+ objectNameSingular,
+ onChange,
+}: WorkflowSingleRecordPickerProps) => {
+ const [draftValue, setDraftValue] = useState<
+ | {
+ type: 'static';
+ value: RecordId;
+ }
+ | {
+ type: 'variable';
+ value: Variable;
+ }
+ >(
+ isStandaloneVariableString(defaultValue)
+ ? {
+ type: 'variable',
+ value: defaultValue,
+ }
+ : {
+ type: 'static',
+ value: defaultValue || '',
+ },
+ );
+
+ const { record } = useFindOneRecord({
+ objectRecordId:
+ isDefined(defaultValue) && !isStandaloneVariableString(defaultValue)
+ ? defaultValue
+ : '',
+ objectNameSingular,
+ withSoftDeleted: true,
+ skip: !isValidUuid(defaultValue),
+ });
+
+ const [selectedRecord, setSelectedRecord] = useState<
+ ObjectRecord | undefined
+ >(record);
+
+ const dropdownId = `workflow-record-picker-${objectNameSingular}`;
+ const variablesDropdownId = `workflow-record-picker-${objectNameSingular}-variables`;
+
+ const { closeDropdown } = useDropdown(dropdownId);
+
+ const { setRecordPickerSearchFilter } = useRecordPicker({
+ recordPickerInstanceId: dropdownId,
+ });
+
+ const handleCloseRelationPickerDropdown = useCallback(() => {
+ setRecordPickerSearchFilter('');
+ }, [setRecordPickerSearchFilter]);
+
+ const handleRecordSelected = (
+ selectedEntity: RecordForSelect | null | undefined,
+ ) => {
+ setDraftValue({
+ type: 'static',
+ value: selectedEntity?.record?.id ?? '',
+ });
+ setSelectedRecord(selectedEntity?.record);
+ closeDropdown();
+
+ onChange?.(selectedEntity?.record?.id ?? '');
+ };
+
+ const handleVariableTagInsert = (variable: string) => {
+ setDraftValue({
+ type: 'variable',
+ value: variable,
+ });
+ setSelectedRecord(undefined);
+ closeDropdown();
+
+ onChange?.(variable);
+ };
+
+ const handleUnlinkVariable = () => {
+ setDraftValue({
+ type: 'static',
+ value: '',
+ });
+ closeDropdown();
+
+ onChange('');
+ };
+
+ return (
+
+ {label ? {label} : null}
+
+
+
+
+
+ }
+ dropdownComponents={
+
+ closeDropdown()}
+ onRecordSelected={handleRecordSelected}
+ objectNameSingular={objectNameSingular}
+ recordPickerInstanceId={dropdownId}
+ selectedRecordIds={
+ draftValue?.value &&
+ !isStandaloneVariableString(draftValue.value)
+ ? [draftValue.value]
+ : []
+ }
+ />
+
+ }
+ dropdownHotkeyScope={{
+ scope: dropdownId,
+ }}
+ />
+
+
+
+
+
+
+
+ );
+};
diff --git a/packages/twenty-front/src/modules/workflow/components/WorkflowStepDetail.tsx b/packages/twenty-front/src/modules/workflow/components/WorkflowStepDetail.tsx
index 00914401b..41fe04c16 100644
--- a/packages/twenty-front/src/modules/workflow/components/WorkflowStepDetail.tsx
+++ b/packages/twenty-front/src/modules/workflow/components/WorkflowStepDetail.tsx
@@ -1,6 +1,3 @@
-import { WorkflowEditActionFormRecordCreate } from '@/workflow/components/WorkflowEditActionFormRecordCreate';
-import { WorkflowEditActionFormSendEmail } from '@/workflow/components/WorkflowEditActionFormSendEmail';
-import { WorkflowEditActionFormServerlessFunction } from '@/workflow/components/WorkflowEditActionFormServerlessFunction';
import { WorkflowEditTriggerDatabaseEventForm } from '@/workflow/components/WorkflowEditTriggerDatabaseEventForm';
import { WorkflowEditTriggerManualForm } from '@/workflow/components/WorkflowEditTriggerManualForm';
import {
@@ -11,6 +8,11 @@ import {
import { assertUnreachable } from '@/workflow/utils/assertUnreachable';
import { getStepDefinitionOrThrow } from '@/workflow/utils/getStepDefinitionOrThrow';
import { isWorkflowRecordCreateAction } from '@/workflow/utils/isWorkflowRecordCreateAction';
+import { isWorkflowRecordUpdateAction } from '@/workflow/utils/isWorkflowRecordUpdateAction';
+import { WorkflowEditActionFormRecordCreate } from '@/workflow/workflow-actions/components/WorkflowEditActionFormRecordCreate';
+import { WorkflowEditActionFormRecordUpdate } from '@/workflow/workflow-actions/components/WorkflowEditActionFormRecordUpdate';
+import { WorkflowEditActionFormSendEmail } from '@/workflow/workflow-actions/components/WorkflowEditActionFormSendEmail';
+import { WorkflowEditActionFormServerlessFunction } from '@/workflow/workflow-actions/components/WorkflowEditActionFormServerlessFunction';
import { isDefined } from 'twenty-ui';
type WorkflowStepDetailProps =
@@ -102,6 +104,15 @@ export const WorkflowStepDetail = ({
);
}
+ if (isWorkflowRecordUpdateAction(stepDefinition.definition)) {
+ return (
+
+ );
+ }
+
return null;
}
}
diff --git a/packages/twenty-front/src/modules/workflow/constants/Actions.ts b/packages/twenty-front/src/modules/workflow/constants/Actions.ts
index c4a9bae48..e71d71ea2 100644
--- a/packages/twenty-front/src/modules/workflow/constants/Actions.ts
+++ b/packages/twenty-front/src/modules/workflow/constants/Actions.ts
@@ -25,4 +25,9 @@ export const ACTIONS: Array<{
type: 'RECORD_CRUD.CREATE',
icon: IconAddressBook,
},
+ {
+ label: 'Update Record',
+ type: 'RECORD_CRUD.UPDATE',
+ icon: IconAddressBook,
+ },
];
diff --git a/packages/twenty-front/src/modules/workflow/utils/__tests__/getStepDefaultDefinition.test.ts b/packages/twenty-front/src/modules/workflow/utils/__tests__/getStepDefaultDefinition.test.ts
index 8d8515e65..cd7b54308 100644
--- a/packages/twenty-front/src/modules/workflow/utils/__tests__/getStepDefaultDefinition.test.ts
+++ b/packages/twenty-front/src/modules/workflow/utils/__tests__/getStepDefaultDefinition.test.ts
@@ -92,6 +92,37 @@ it('returns a valid definition for RECORD_CRUD.CREATE actions', () => {
});
});
+it('returns a valid definition for RECORD_CRUD.UPDATE actions', () => {
+ expect(
+ getStepDefaultDefinition({
+ type: 'RECORD_CRUD.UPDATE',
+ activeObjectMetadataItems: generatedMockObjectMetadataItems,
+ }),
+ ).toStrictEqual({
+ id: expect.any(String),
+ name: 'Update Record',
+ type: 'RECORD_CRUD',
+ valid: false,
+ settings: {
+ input: {
+ type: 'UPDATE',
+ objectName: generatedMockObjectMetadataItems[0].nameSingular,
+ objectRecord: {},
+ objectRecordId: '',
+ },
+ outputSchema: {},
+ errorHandlingOptions: {
+ continueOnFailure: {
+ value: false,
+ },
+ retryOnFailure: {
+ value: false,
+ },
+ },
+ },
+ });
+});
+
it("throws for RECORD_CRUD.DELETE actions as it's not implemented yet", () => {
expect(() => {
getStepDefaultDefinition({
@@ -101,15 +132,6 @@ it("throws for RECORD_CRUD.DELETE actions as it's not implemented yet", () => {
}).toThrow('Not implemented yet');
});
-it("throws for RECORD_CRUD.UPDATE actions as it's not implemented yet", () => {
- expect(() => {
- getStepDefaultDefinition({
- type: 'RECORD_CRUD.UPDATE',
- activeObjectMetadataItems: generatedMockObjectMetadataItems,
- });
- }).toThrow('Not implemented yet');
-});
-
it('throws when providing an unknown type', () => {
expect(() => {
getStepDefaultDefinition({
diff --git a/packages/twenty-front/src/modules/workflow/utils/getStepDefaultDefinition.ts b/packages/twenty-front/src/modules/workflow/utils/getStepDefaultDefinition.ts
index 20a33f8b9..ddb95c326 100644
--- a/packages/twenty-front/src/modules/workflow/utils/getStepDefaultDefinition.ts
+++ b/packages/twenty-front/src/modules/workflow/utils/getStepDefaultDefinition.ts
@@ -3,6 +3,18 @@ import { WorkflowStep, WorkflowStepType } from '@/workflow/types/Workflow';
import { assertUnreachable } from '@/workflow/utils/assertUnreachable';
import { v4 } from 'uuid';
+const BASE_DEFAULT_STEP_SETTINGS = {
+ outputSchema: {},
+ errorHandlingOptions: {
+ continueOnFailure: {
+ value: false,
+ },
+ retryOnFailure: {
+ value: false,
+ },
+ },
+};
+
export const getStepDefaultDefinition = ({
type,
activeObjectMetadataItems,
@@ -25,15 +37,7 @@ export const getStepDefaultDefinition = ({
serverlessFunctionVersion: '',
serverlessFunctionInput: {},
},
- outputSchema: {},
- errorHandlingOptions: {
- continueOnFailure: {
- value: false,
- },
- retryOnFailure: {
- value: false,
- },
- },
+ ...BASE_DEFAULT_STEP_SETTINGS,
},
};
}
@@ -50,15 +54,7 @@ export const getStepDefaultDefinition = ({
subject: '',
body: '',
},
- outputSchema: {},
- errorHandlingOptions: {
- continueOnFailure: {
- value: false,
- },
- retryOnFailure: {
- value: false,
- },
- },
+ ...BASE_DEFAULT_STEP_SETTINGS,
},
};
}
@@ -74,20 +70,27 @@ export const getStepDefaultDefinition = ({
objectName: activeObjectMetadataItems[0].nameSingular,
objectRecord: {},
},
- outputSchema: {},
- errorHandlingOptions: {
- continueOnFailure: {
- value: false,
- },
- retryOnFailure: {
- value: false,
- },
- },
+ ...BASE_DEFAULT_STEP_SETTINGS,
},
};
}
- case 'RECORD_CRUD.DELETE':
- case 'RECORD_CRUD.UPDATE': {
+ case 'RECORD_CRUD.UPDATE':
+ return {
+ id: newStepId,
+ name: 'Update Record',
+ type: 'RECORD_CRUD',
+ valid: false,
+ settings: {
+ input: {
+ type: 'UPDATE',
+ objectName: activeObjectMetadataItems[0].nameSingular,
+ objectRecordId: '',
+ objectRecord: {},
+ },
+ ...BASE_DEFAULT_STEP_SETTINGS,
+ },
+ };
+ case 'RECORD_CRUD.DELETE': {
throw new Error('Not implemented yet');
}
default: {
diff --git a/packages/twenty-front/src/modules/workflow/utils/isWorkflowRecordUpdateAction.ts b/packages/twenty-front/src/modules/workflow/utils/isWorkflowRecordUpdateAction.ts
new file mode 100644
index 000000000..732ba25a6
--- /dev/null
+++ b/packages/twenty-front/src/modules/workflow/utils/isWorkflowRecordUpdateAction.ts
@@ -0,0 +1,12 @@
+import {
+ WorkflowAction,
+ WorkflowRecordUpdateAction,
+} from '@/workflow/types/Workflow';
+
+export const isWorkflowRecordUpdateAction = (
+ action: WorkflowAction,
+): action is WorkflowRecordUpdateAction => {
+ return (
+ action.type === 'RECORD_CRUD' && action.settings.input.type === 'UPDATE'
+ );
+};
diff --git a/packages/twenty-front/src/modules/workflow/components/WorkflowEditActionFormRecordCreate.tsx b/packages/twenty-front/src/modules/workflow/workflow-actions/components/WorkflowEditActionFormRecordCreate.tsx
similarity index 94%
rename from packages/twenty-front/src/modules/workflow/components/WorkflowEditActionFormRecordCreate.tsx
rename to packages/twenty-front/src/modules/workflow/workflow-actions/components/WorkflowEditActionFormRecordCreate.tsx
index 7408e2465..3c181c7b7 100644
--- a/packages/twenty-front/src/modules/workflow/components/WorkflowEditActionFormRecordCreate.tsx
+++ b/packages/twenty-front/src/modules/workflow/workflow-actions/components/WorkflowEditActionFormRecordCreate.tsx
@@ -28,7 +28,7 @@ type WorkflowEditActionFormRecordCreateProps = {
};
};
-type SendEmailFormData = {
+type CreateRecordFormData = {
objectName: string;
[field: string]: unknown;
};
@@ -49,17 +49,17 @@ export const WorkflowEditActionFormRecordCreate = ({
value: item.nameSingular,
}));
- const [formData, setFormData] = useState({
+ const [formData, setFormData] = useState({
objectName: action.settings.input.objectName,
...action.settings.input.objectRecord,
});
const isFormDisabled = actionOptions.readonly;
const handleFieldChange = (
- fieldName: keyof SendEmailFormData,
+ fieldName: keyof CreateRecordFormData,
updatedValue: JsonValue,
) => {
- const newFormData: SendEmailFormData = {
+ const newFormData: CreateRecordFormData = {
...formData,
[fieldName]: updatedValue,
};
@@ -93,7 +93,7 @@ export const WorkflowEditActionFormRecordCreate = ({
);
const saveAction = useDebouncedCallback(
- async (formData: SendEmailFormData) => {
+ async (formData: CreateRecordFormData) => {
if (actionOptions.readonly === true) {
return;
}
@@ -153,7 +153,7 @@ export const WorkflowEditActionFormRecordCreate = ({
emptyOption={{ label: 'Select an option', value: '' }}
options={availableMetadata}
onChange={(updatedObjectName) => {
- const newFormData: SendEmailFormData = {
+ const newFormData: CreateRecordFormData = {
objectName: updatedObjectName,
};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-actions/components/WorkflowEditActionFormRecordUpdate.tsx b/packages/twenty-front/src/modules/workflow/workflow-actions/components/WorkflowEditActionFormRecordUpdate.tsx
new file mode 100644
index 000000000..bb30074cc
--- /dev/null
+++ b/packages/twenty-front/src/modules/workflow/workflow-actions/components/WorkflowEditActionFormRecordUpdate.tsx
@@ -0,0 +1,179 @@
+import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
+import { Select, SelectOption } from '@/ui/input/components/Select';
+import { WorkflowEditGenericFormBase } from '@/workflow/components/WorkflowEditGenericFormBase';
+import { WorkflowSingleRecordPicker } from '@/workflow/components/WorkflowSingleRecordPicker';
+import { WorkflowRecordUpdateAction } from '@/workflow/types/Workflow';
+import { useTheme } from '@emotion/react';
+import { useEffect, useState } from 'react';
+import {
+ HorizontalSeparator,
+ IconAddressBook,
+ isDefined,
+ useIcons,
+} from 'twenty-ui';
+
+import { JsonValue } from 'type-fest';
+import { useDebouncedCallback } from 'use-debounce';
+
+type WorkflowEditActionFormRecordUpdateProps = {
+ action: WorkflowRecordUpdateAction;
+ actionOptions:
+ | {
+ readonly: true;
+ }
+ | {
+ readonly?: false;
+ onActionUpdate: (action: WorkflowRecordUpdateAction) => void;
+ };
+};
+
+type UpdateRecordFormData = {
+ objectName: string;
+ objectRecordId: string;
+ [field: string]: unknown;
+};
+
+export const WorkflowEditActionFormRecordUpdate = ({
+ action,
+ actionOptions,
+}: WorkflowEditActionFormRecordUpdateProps) => {
+ const theme = useTheme();
+ const { getIcon } = useIcons();
+
+ const { activeObjectMetadataItems } = useFilteredObjectMetadataItems();
+
+ const availableMetadata: Array> =
+ activeObjectMetadataItems.map((item) => ({
+ Icon: getIcon(item.icon),
+ label: item.labelPlural,
+ value: item.nameSingular,
+ }));
+
+ const [formData, setFormData] = useState({
+ objectName: action.settings.input.objectName,
+ objectRecordId: action.settings.input.objectRecordId,
+ ...action.settings.input.objectRecord,
+ });
+ const isFormDisabled = actionOptions.readonly;
+
+ const handleFieldChange = (
+ fieldName: keyof UpdateRecordFormData,
+ updatedValue: JsonValue,
+ ) => {
+ const newFormData: UpdateRecordFormData = {
+ ...formData,
+ [fieldName]: updatedValue,
+ };
+
+ setFormData(newFormData);
+
+ saveAction(newFormData);
+ };
+
+ useEffect(() => {
+ setFormData({
+ objectName: action.settings.input.objectName,
+ objectRecordId: action.settings.input.objectRecordId,
+ ...action.settings.input.objectRecord,
+ });
+ }, [action.settings.input]);
+
+ const selectedObjectMetadataItemNameSingular = formData.objectName;
+
+ const selectedObjectMetadataItem = activeObjectMetadataItems.find(
+ (item) => item.nameSingular === selectedObjectMetadataItemNameSingular,
+ );
+ if (!isDefined(selectedObjectMetadataItem)) {
+ throw new Error('Should have found the metadata item');
+ }
+
+ const saveAction = useDebouncedCallback(
+ async (formData: UpdateRecordFormData) => {
+ if (actionOptions.readonly === true) {
+ return;
+ }
+
+ const {
+ objectName: updatedObjectName,
+ objectRecordId: updatedObjectRecordId,
+ ...updatedOtherFields
+ } = formData;
+
+ actionOptions.onActionUpdate({
+ ...action,
+ settings: {
+ ...action.settings,
+ input: {
+ type: 'UPDATE',
+ objectName: updatedObjectName,
+ objectRecordId: updatedObjectRecordId ?? '',
+ objectRecord: updatedOtherFields,
+ },
+ },
+ });
+ },
+ 1_000,
+ );
+
+ useEffect(() => {
+ return () => {
+ saveAction.flush();
+ };
+ }, [saveAction]);
+
+ const headerTitle = isDefined(action.name) ? action.name : `Update Record`;
+
+ return (
+ {
+ if (actionOptions.readonly === true) {
+ return;
+ }
+
+ actionOptions.onActionUpdate({
+ ...action,
+ name: newName,
+ });
+ }}
+ HeaderIcon={
+
+ }
+ headerTitle={headerTitle}
+ headerType="Action"
+ >
+
+ );
+};
diff --git a/packages/twenty-front/src/modules/workflow/components/WorkflowEditActionFormSendEmail.tsx b/packages/twenty-front/src/modules/workflow/workflow-actions/components/WorkflowEditActionFormSendEmail.tsx
similarity index 100%
rename from packages/twenty-front/src/modules/workflow/components/WorkflowEditActionFormSendEmail.tsx
rename to packages/twenty-front/src/modules/workflow/workflow-actions/components/WorkflowEditActionFormSendEmail.tsx
diff --git a/packages/twenty-front/src/modules/workflow/components/WorkflowEditActionFormServerlessFunction.tsx b/packages/twenty-front/src/modules/workflow/workflow-actions/components/WorkflowEditActionFormServerlessFunction.tsx
similarity index 100%
rename from packages/twenty-front/src/modules/workflow/components/WorkflowEditActionFormServerlessFunction.tsx
rename to packages/twenty-front/src/modules/workflow/workflow-actions/components/WorkflowEditActionFormServerlessFunction.tsx