Email Improvements.
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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": ""
|
||||||
|
|||||||
@@ -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": ""
|
||||||
|
|||||||
@@ -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,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user