Email Improvements.

This commit is contained in:
Patrick Fic
2021-04-08 13:55:24 -07:00
parent 54e5129fd4
commit 7f30439868
8 changed files with 179 additions and 119 deletions

View File

@@ -10669,6 +10669,74 @@
</concept_node> </concept_node>
</children> </children>
</folder_node> </folder_node>
<folder_node>
<name>fields</name>
<children>
<concept_node>
<name>cc</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>subject</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>to</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
<folder_node> <folder_node>
<name>labels</name> <name>labels</name>
<children> <children>

View File

@@ -1,68 +1,82 @@
import { UploadOutlined } from "@ant-design/icons"; import { UploadOutlined } from "@ant-design/icons";
import { Button, Card, Divider, Input, Select, Upload } from "antd"; import { Card, Divider, Form, Input, Select, Upload } from "antd";
import React from "react"; import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
export default function EmailOverlayComponent({ export default function EmailOverlayComponent({ form }) {
messageOptions,
handleConfigChange,
handleToChange,
handleHtmlChange,
handleUpload,
handleFileRemove,
}) {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<div> <div>
To: <Form.Item
<Select label={t("emails.fields.to")}
name="to" name="to"
mode="tags" rules={[
value={messageOptions.to} {
//style={{ width: "100%" }} required: true,
onChange={handleToChange} message: t("general.validation.required"),
tokenSeparators={[",", ";"]} },
/> ]}
CC: >
<Select <Select mode="tags" tokenSeparators={[",", ";"]} />
value={messageOptions.cc} </Form.Item>
mode="tags" <Form.Item label={t("emails.fields.cc")} name="cc">
onChange={(value) => handleConfigChange("cc", value)} <Select mode="tags" tokenSeparators={[",", ";"]} />
name="cc" </Form.Item>
tokenSeparators={[",", ";"]} <Form.Item
/> label={t("emails.fields.subject")}
Subject:
<Input
value={messageOptions.subject}
onChange={(e) => handleConfigChange("subject", e.target.value)}
name="subject" name="subject"
/> rules={[
<Divider>{t("emails.labels.preview")}</Divider> {
<div required: true,
style={{ message: t("general.validation.required"),
padding: "1rem", },
]}
>
<Input />
</Form.Item>
backgroundColor: "lightgray", <Divider>{t("emails.labels.preview")}</Divider>
borderLeft: "6px solid #2196F3", <Form.Item shouldUpdate>
{() => {
return (
<div
style={{
padding: "1rem",
backgroundColor: "lightgray",
borderLeft: "6px solid #2196F3",
}}
dangerouslySetInnerHTML={{ __html: form.getFieldValue("html") }}
/>
);
}} }}
dangerouslySetInnerHTML={{ __html: messageOptions.html }} </Form.Item>
/>
<Divider>
<Divider>{t("emails.labels.preview")}</Divider>
</Divider>
<Card title={t("emails.labels.attachments")}> <Card title={t("emails.labels.attachments")}>
<Upload <Form.Item
fileList={messageOptions.fileList} name="fileList"
beforeUpload={handleUpload} valuePropName="fileList"
onRemove={handleFileRemove} getValueFromEvent={(e) => {
multiple console.log("Upload event:", e);
listType="picture-card"
style={{ width: "100%" }} if (Array.isArray(e)) {
return e;
}
return e && e.fileList;
}}
> >
<Button> <Upload.Dragger multiple listType="picture-card">
<UploadOutlined /> Upload <>
</Button> <p className="ant-upload-drag-icon">
</Upload> <UploadOutlined />
</p>
<p className="ant-upload-text">
Click or drag files to this area to upload.
</p>
</>
</Upload.Dragger>
</Form.Item>
</Card> </Card>
</div> </div>
); );

View File

@@ -1,4 +1,4 @@
import { Modal, notification } from "antd"; import { Form, Modal, notification } from "antd";
import axios from "axios"; import axios from "axios";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
@@ -10,7 +10,10 @@ import {
selectEmailConfig, selectEmailConfig,
selectEmailVisible, selectEmailVisible,
} from "../../redux/email/email.selectors.js"; } from "../../redux/email/email.selectors.js";
import { selectBodyshop } from "../../redux/user/user.selectors"; import {
selectBodyshop,
selectCurrentUser,
} from "../../redux/user/user.selectors";
import RenderTemplate from "../../utils/RenderTemplate"; import RenderTemplate from "../../utils/RenderTemplate";
import { EmailSettings } from "../../utils/TemplateConstants"; import { EmailSettings } from "../../utils/TemplateConstants";
import LoadingSpinner from "../loading-spinner/loading-spinner.component"; import LoadingSpinner from "../loading-spinner/loading-spinner.component";
@@ -20,6 +23,7 @@ const mapStateToProps = createStructuredSelector({
modalVisible: selectEmailVisible, modalVisible: selectEmailVisible,
emailConfig: selectEmailConfig, emailConfig: selectEmailConfig,
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
currentUser: selectCurrentUser,
}); });
const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
@@ -31,31 +35,27 @@ export function EmailOverlayContainer({
modalVisible, modalVisible,
toggleEmailOverlayVisible, toggleEmailOverlayVisible,
bodyshop, bodyshop,
currentUser,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const [form] = Form.useForm();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [sending, setSending] = useState(false); const [sending, setSending] = useState(false);
const [rawHtml, setRawHtml] = useState(""); const [rawHtml, setRawHtml] = useState("");
const defaultEmailFrom = { const defaultEmailFrom = {
from: { from: {
name: bodyshop.shopname || EmailSettings.fromNameDefault, name: `${currentUser.displayName} @ ${bodyshop.shopname}`,
address: EmailSettings.fromAddress, address: EmailSettings.fromAddress,
}, },
replyTo: bodyshop.email, ReplyTo: { Email: currentUser.email, Name: currentUser.displayName },
}; };
const [messageOptions, setMessageOptions] = useState({
...defaultEmailFrom,
html: "",
fileList: [],
});
const handleOk = async () => { const handleFinish = async (values) => {
logImEXEvent("email_send_from_modal"); logImEXEvent("email_send_from_modal");
console.log(`values`, values);
const attachments = []; const attachments = [];
await asyncForEach(messageOptions.fileList, async (f) => { await asyncForEach(values.fileList, async (f) => {
const t = { const t = {
ContentType: f.type, ContentType: f.type,
Filename: f.name, Filename: f.name,
@@ -67,7 +67,8 @@ export function EmailOverlayContainer({
setSending(true); setSending(true);
try { try {
await axios.post("/sendemail", { await axios.post("/sendemail", {
...messageOptions, ...defaultEmailFrom,
...values,
html: rawHtml, html: rawHtml,
attachments, attachments,
}); });
@@ -82,34 +83,6 @@ export function EmailOverlayContainer({
setSending(false); setSending(false);
}; };
const handleConfigChange = (name, value) => {
setMessageOptions({ ...messageOptions, [name]: value });
};
const handleHtmlChange = (text) => {
setMessageOptions({ ...messageOptions, html: text });
};
const handleToChange = (recipients) => {
setMessageOptions({ ...messageOptions, to: recipients });
};
const handleUpload = (file) => {
setMessageOptions({
...messageOptions,
fileList: [...messageOptions.fileList, file],
});
return false;
};
const handleFileRemove = (file) => {
setMessageOptions((state) => {
const index = state.fileList.indexOf(file);
const newfileList = state.fileList.slice();
newfileList.splice(index, 1);
return {
fileList: newfileList,
};
});
};
const render = async () => { const render = async () => {
logImEXEvent("email_render_template", { template: emailConfig.template }); logImEXEvent("email_render_template", { template: emailConfig.template });
setLoading(true); setLoading(true);
@@ -120,11 +93,9 @@ export function EmailOverlayContainer({
url: `${window.location.protocol}://${window.location.host}/`, url: `${window.location.protocol}://${window.location.host}/`,
}); });
setRawHtml(response.data); setRawHtml(response.data);
form.setFieldsValue({
console.log("response", response);
setMessageOptions({
...emailConfig.messageOptions, ...emailConfig.messageOptions,
...defaultEmailFrom,
html: response.data, html: response.data,
fileList: [], fileList: [],
}); });
@@ -140,29 +111,16 @@ export function EmailOverlayContainer({
destroyOnClose={true} destroyOnClose={true}
visible={modalVisible} visible={modalVisible}
width={"80%"} width={"80%"}
onOk={handleOk} onOk={() => form.submit()}
onCancel={() => { onCancel={() => {
toggleEmailOverlayVisible(); toggleEmailOverlayVisible();
}} }}
okButtonProps={{ loading: sending }} okButtonProps={{ loading: sending }}
> >
<LoadingSpinner loading={loading}> <Form layout="vertical" form={form} onFinish={handleFinish}>
<EmailOverlayComponent {loading && <LoadingSpinner />}
handleConfigChange={handleConfigChange} <EmailOverlayComponent form={form} />
messageOptions={messageOptions} </Form>
handleHtmlChange={handleHtmlChange}
handleUpload={handleUpload}
handleFileRemove={handleFileRemove}
handleToChange={handleToChange}
/>
<button
onClick={() => {
navigator.clipboard.writeText(messageOptions.html);
}}
>
Copy HTML
</button>
</LoadingSpinner>
</Modal> </Modal>
); );
} }

View File

@@ -686,6 +686,11 @@
"errors": { "errors": {
"notsent": "Email not sent. Error encountered while sending {{message}}" "notsent": "Email not sent. Error encountered while sending {{message}}"
}, },
"fields": {
"cc": "CC",
"subject": "Subject",
"to": "To"
},
"labels": { "labels": {
"attachments": "Attachments", "attachments": "Attachments",
"preview": "Email Preview" "preview": "Email Preview"

View File

@@ -686,6 +686,11 @@
"errors": { "errors": {
"notsent": "Correo electrónico no enviado Se encontró un error al enviar {{message}}" "notsent": "Correo electrónico no enviado Se encontró un error al enviar {{message}}"
}, },
"fields": {
"cc": "",
"subject": "",
"to": ""
},
"labels": { "labels": {
"attachments": "", "attachments": "",
"preview": "" "preview": ""

View File

@@ -686,6 +686,11 @@
"errors": { "errors": {
"notsent": "Courriel non envoyé. Erreur rencontrée lors de l'envoi de {{message}}" "notsent": "Courriel non envoyé. Erreur rencontrée lors de l'envoi de {{message}}"
}, },
"fields": {
"cc": "",
"subject": "",
"to": ""
},
"labels": { "labels": {
"attachments": "", "attachments": "",
"preview": "" "preview": ""

View File

@@ -136,7 +136,12 @@ export const GenerateDocument = async (template, messageOptions, sendType) => {
if (sendType === "e") { if (sendType === "e") {
store.dispatch( store.dispatch(
setEmailOptions({ setEmailOptions({
messageOptions, messageOptions: {
...messageOptions,
to: Array.isArray(messageOptions.to)
? messageOptions.to
: [messageOptions.to],
},
template, template,
}) })
); );

View File

@@ -42,8 +42,8 @@ exports.sendEmail = async (req, res) => {
return { Email: i }; return { Email: i };
}), }),
ReplyTo: { ReplyTo: {
Email: req.body.from.address, Email: req.body.ReplyTo.Email,
Name: req.body.from.name, Name: req.body.ReplyTo.Name,
}, },
Subject: req.body.subject, Subject: req.body.subject,
// TextPart: // TextPart: