feat: Attachement for Send Email workflow node (#15044)

## Description

- This PR addresses the one issue out of
https://github.com/twentyhq/core-team-issues/issues/1685
-  Added backend support for workflow node to support attachement
- updated send email schema, core utility 
- added workflowattachmentRow and workflowsendEmailAttachment file to
handle file attachment in email workflow
- updated Google and Microsoft to use MailComposer which unifies with
SMTP provider and improves mail structure

## Visual Appearance



https://github.com/user-attachments/assets/16478569-0a83-417e-a85e-70e41fe83343

---------

Co-authored-by: martmull <martmull@hotmail.fr>
This commit is contained in:
Harshit Singh
2025-10-22 22:15:28 +05:30
committed by GitHub
parent e9ab54766c
commit 4b846a42a2
16 changed files with 567 additions and 117 deletions
@@ -7,7 +7,6 @@ import { SidePanelHeader } from '@/command-menu/components/SidePanelHeader';
import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { FormAdvancedTextFieldInput } from '@/object-record/record-field/ui/form-types/components/FormAdvancedTextFieldInput';
import { FormTextFieldInput } from '@/object-record/record-field/ui/form-types/components/FormTextFieldInput';
import { useTriggerApisOAuth } from '@/settings/accounts/hooks/useTriggerApiOAuth';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
@@ -17,7 +16,6 @@ import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/ho
import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState';
import { type WorkflowSendEmailAction } from '@/workflow/types/Workflow';
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
import { WorkflowStepFooter } from '@/workflow/workflow-steps/components/WorkflowStepFooter';
import { SEND_EMAIL_ACTION } from '@/workflow/workflow-steps/workflow-actions/constants/actions/SendEmailAction';
import { useWorkflowActionHeader } from '@/workflow/workflow-steps/workflow-actions/hooks/useWorkflowActionHeader';
@@ -33,8 +31,12 @@ import { type SelectOption } from 'twenty-ui/input';
import { type JsonValue } from 'type-fest';
import { useDebouncedCallback } from 'use-debounce';
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
import { WorkflowSendEmailAttachments } from '@/advanced-text-editor/components/WorkflowSendEmailAttachments';
import { FormAdvancedTextFieldInput } from '@/object-record/record-field/ui/form-types/components/FormAdvancedTextFieldInput';
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
const EMAIL_EDITOR_MIN_HEIGHT = 340;
const EMAIL_EDITOR_MAX_WIDTH = 600;
type WorkflowEditActionSendEmailProps = {
@@ -49,11 +51,20 @@ type WorkflowEditActionSendEmailProps = {
};
};
type WorkflowFile = {
id: string;
name: string;
size: number;
type: string;
createdAt: string;
};
type SendEmailFormData = {
connectedAccountId: string;
email: string;
subject: string;
body: string;
files: WorkflowFile[];
};
export const WorkflowEditActionSendEmail = ({
@@ -80,6 +91,7 @@ export const WorkflowEditActionSendEmail = ({
email: action.settings.input.email,
subject: action.settings.input.subject ?? '',
body: action.settings.input.body ?? '',
files: action.settings.input.files ?? [],
});
const checkConnectedAccountScopes = async (
@@ -138,6 +150,7 @@ export const WorkflowEditActionSendEmail = ({
email: formData.email,
subject: formData.subject,
body: formData.body,
files: formData.files,
},
},
});
@@ -180,7 +193,7 @@ export const WorkflowEditActionSendEmail = ({
return attachmentAbsoluteURL;
};
const handleImageUploadError = (error: Error, file: File) => {
const handleImageUploadError = (_: Error, file: File) => {
enqueueErrorSnackBar({
message: t`Failed to upload image: `.concat(file.name),
});
@@ -318,6 +331,13 @@ export const WorkflowEditActionSendEmail = ({
}}
VariablePicker={WorkflowVariablePicker}
/>
<WorkflowSendEmailAttachments
label="Attachments"
files={formData.files}
onChange={(files) => {
handleFieldChange('files', files);
}}
/>
<FormAdvancedTextFieldInput
label="Body"
placeholder="Enter email body"
@@ -28,6 +28,7 @@ const DEFAULT_ACTION: WorkflowSendEmailAction = {
email: '',
subject: '',
body: '',
files: [],
},
outputSchema: {},
errorHandlingOptions: {
@@ -52,6 +53,7 @@ const CONFIGURED_ACTION: WorkflowSendEmailAction = {
email: 'test@twenty.com',
subject: 'Welcome to Twenty!',
body: 'Dear Tim,\n\nWelcome to Twenty! We are excited to have you on board.\n\nBest regards,\nThe Team',
files: [],
},
outputSchema: {},
errorHandlingOptions: {
@@ -0,0 +1,7 @@
export type WorkflowAttachmentType = {
id: string;
name: string;
size: number;
type: string;
createdAt: string;
};