diff --git a/src/api/test/protectedApiClient.test.ts b/src/api/test/protectedApiClient.test.ts index 20027d2b..6dbc005e 100644 --- a/src/api/test/protectedApiClient.test.ts +++ b/src/api/test/protectedApiClient.test.ts @@ -2105,6 +2105,7 @@ describe('Admin Protected Client Routes', () => { emails: ['email@email.com'], emailSubject: 'Some subject', emailBody: 'Some body', + attachments: [], }); expect(result).toEqual(response); @@ -2119,6 +2120,7 @@ describe('Admin Protected Client Routes', () => { emails: ['notAnEmail'], emailSubject: 'Some subject', emailBody: 'Some body', + attachments: [], }).catch((err) => err.response.data); expect(result).toEqual(response); @@ -2133,6 +2135,7 @@ describe('Admin Protected Client Routes', () => { emails: [], emailSubject: 'Subject', emailBody: 'Body', + attachments: [], }).catch((err) => err.response.data); expect(result).toEqual(response); diff --git a/src/components/forms/ducks/types.ts b/src/components/forms/ducks/types.ts index 9b3dd06e..57c2462e 100644 --- a/src/components/forms/ducks/types.ts +++ b/src/components/forms/ducks/types.ts @@ -129,6 +129,12 @@ export interface SendEmailFormValues { export interface SendEmailRequest extends SendEmailFormValues { readonly emails: string[]; + readonly attachments: EmailAttachment[]; +} + +interface EmailAttachment { + readonly name: string; + readonly data: string; } export interface AddTemplateRequest { diff --git a/src/components/forms/sendEmailForm/index.tsx b/src/components/forms/sendEmailForm/index.tsx index 6583e84d..2eb76f90 100644 --- a/src/components/forms/sendEmailForm/index.tsx +++ b/src/components/forms/sendEmailForm/index.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react'; import styled from 'styled-components'; -import { Form, Input, Switch, message } from 'antd'; +import { Collapse, Form, Input, Switch, UploadProps, message } from 'antd'; import { Flex, SubmitButton, @@ -19,6 +19,10 @@ import { n } from '../../../utils/stringFormat'; import DOMPurify from 'isomorphic-dompurify'; import SaveMenu from '../../saveMenu'; import templateContent from './content'; +import Dragger from 'antd/lib/upload/Dragger'; +import { InboxOutlined } from '@ant-design/icons'; +import { LIGHT_GREEN } from '../../../utils/colors'; +import { RcFile } from 'antd/lib/upload'; const PreviewSwitch = styled(Switch)` display: flex; @@ -46,6 +50,20 @@ const EmailFlex = styled(Flex)` gap: 4px; `; +function readAndPreview(f: RcFile): Promise<[string, string]> { + return new Promise((resolve) => { + const reader = new FileReader(); + reader.addEventListener( + 'loadend', + () => { + resolve([f.name, reader.result as string]); + }, + false, + ); + reader.readAsDataURL(f); + }); +} + interface SendEmailFormProps { readonly emails: string[]; readonly sendEmailForm: FormInstance; @@ -63,6 +81,7 @@ const SendEmailForm: React.FC = ({ const [showPreview, setShowPreview] = useState(false); const [showSave, setShowSave] = useState(false); const [sanitizedBodyContent, setSanitizedBodyContent] = useState(''); + const [attachments, setAttachments] = useState>({}); const togglePreview = (isShowPreview: boolean) => { setShowPreview(isShowPreview); @@ -78,10 +97,15 @@ const SendEmailForm: React.FC = ({ return; } + const attachmentData = Object.entries(attachments).map(([name, data]) => { + return { name, data }; + }); + const sendEmailRequest: SendEmailRequest = { emailSubject: values.emailSubject, emailBody: DOMPurify.sanitize(values.emailBody), emails, + attachments: attachmentData, }; ProtectedApiClient.sendEmail(sendEmailRequest) .then(() => { @@ -92,6 +116,32 @@ const SendEmailForm: React.FC = ({ ); }; + const attachmentFormProps: UploadProps = { + name: 'file', + multiple: true, + beforeUpload: (_, fileList) => { + if (fileList.length === 0) { + return false; + } + + const uploadPromises = fileList.map((f) => readAndPreview(f)); + Promise.all(uploadPromises).then((uploadedAttachments) => { + setAttachments({ + ...attachments, + ...Object.fromEntries(uploadedAttachments), + }); + }); + + return false; + }, + onRemove(file) { + const attachmentsCopy = attachments; + delete attachmentsCopy[file.name]; + + setAttachments(attachmentsCopy); + }, + }; + return (
= ({ }} /> )} + + + +

+ +

+

{t('upload_header')}

+

{t('upload_description')}

+
+
+
{t('send')} diff --git a/src/i18n/en/forms.json b/src/i18n/en/forms.json index 0beb5985..ffde4211 100644 --- a/src/i18n/en/forms.json +++ b/src/i18n/en/forms.json @@ -146,6 +146,9 @@ }, "body_required": "The email body is required", "body_placeholder": "Email Body", + "upload_collapse_title": "Upload Attachments", + "upload_header": "Click or drag a file to this area to upload", + "upload_description": "Add attachments to send to users!", "send": "Send Email", "save": "Save as Template", "name_template": "Name your template",