Added image upload to email and multiple recipients BOD-103
This commit is contained in:
@@ -4,17 +4,21 @@ import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { auth, logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { setEmailOptions } from "../../redux/email/email.actions";
|
||||
import { TemplateList } from "../../utils/TemplateConstants";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({});
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setEmailOptions: (e) => dispatch(setEmailOptions(e)),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(function Test({ bodyshop }) {
|
||||
)(function Test({ bodyshop, setEmailOptions }) {
|
||||
const handle = async () => {
|
||||
const response = await axios.post(
|
||||
"/accounting/iif/receivables",
|
||||
@@ -94,6 +98,24 @@ export default connect(
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
onClick={() => {
|
||||
setEmailOptions({
|
||||
messageOptions: {
|
||||
to: ["patrickwf@gmail.com"],
|
||||
replyTo: bodyshop.email,
|
||||
},
|
||||
template: {
|
||||
name: TemplateList.parts_order_confirmation.key,
|
||||
variables: {
|
||||
id: "a7c2d4e1-f519-42a9-a071-c48cf0f22979",
|
||||
},
|
||||
},
|
||||
});
|
||||
}}
|
||||
>
|
||||
send email
|
||||
</button>
|
||||
<button onClick={handle}>Hit with Header.</button>
|
||||
<button onClick={handleLocal}>Hit Localhost with Header.</button>
|
||||
<button onClick={handleQbxml}>Qbxml</button>
|
||||
|
||||
@@ -1,18 +1,26 @@
|
||||
import { UploadOutlined } from "@ant-design/icons";
|
||||
import { Editor } from "@tinymce/tinymce-react";
|
||||
import { Input } from "antd";
|
||||
import { Button, Input, Upload, Select } from "antd";
|
||||
import React from "react";
|
||||
|
||||
export default function EmailOverlayComponent({
|
||||
messageOptions,
|
||||
handleConfigChange,
|
||||
handleToChange,
|
||||
handleHtmlChange,
|
||||
handleUpload,
|
||||
handleFileRemove,
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
To:
|
||||
<Input
|
||||
value={messageOptions.to}
|
||||
onChange={handleConfigChange}
|
||||
<Select
|
||||
name="to"
|
||||
mode="tags"
|
||||
value={messageOptions.to}
|
||||
style={{ width: "100%" }}
|
||||
onChange={handleToChange}
|
||||
tokenSeparators={[",", ";"]}
|
||||
/>
|
||||
CC:
|
||||
<Input
|
||||
@@ -45,6 +53,17 @@ export default function EmailOverlayComponent({
|
||||
}}
|
||||
onEditorChange={handleHtmlChange}
|
||||
/>
|
||||
<Upload
|
||||
fileList={messageOptions.fileList}
|
||||
beforeUpload={handleUpload}
|
||||
onRemove={handleFileRemove}
|
||||
multiple
|
||||
listType="picture-card"
|
||||
>
|
||||
<Button>
|
||||
<UploadOutlined /> Upload
|
||||
</Button>
|
||||
</Upload>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,17 +4,17 @@ import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
import { toggleEmailOverlayVisible } from "../../redux/email/email.actions";
|
||||
import {
|
||||
selectEmailConfig,
|
||||
selectEmailVisible,
|
||||
} from "../../redux/email/email.selectors.js";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import { EmailSettings } from "../../utils/TemplateConstants";
|
||||
import RenderTemplate from "../../utils/RenderTemplate";
|
||||
import { EmailSettings } from "../../utils/TemplateConstants";
|
||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||
import EmailOverlayComponent from "./email-overlay.component";
|
||||
import { logImEXEvent } from "../../firebase/firebase.utils";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
modalVisible: selectEmailVisible,
|
||||
@@ -33,6 +33,7 @@ export function EmailOverlayContainer({
|
||||
bodyshop,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [sending, setSending] = useState(false);
|
||||
const defaultEmailFrom = {
|
||||
@@ -45,14 +46,27 @@ export function EmailOverlayContainer({
|
||||
const [messageOptions, setMessageOptions] = useState({
|
||||
...defaultEmailFrom,
|
||||
html: "",
|
||||
fileList: [],
|
||||
});
|
||||
|
||||
const handleOk = async () => {
|
||||
logImEXEvent("email_send_from_modal");
|
||||
|
||||
const attachments = [];
|
||||
|
||||
await asyncForEach(messageOptions.fileList, async (f) => {
|
||||
const t = {
|
||||
ContentType: f.type,
|
||||
Filename: f.name,
|
||||
Base64Content: (await toBase64(f)).split(",")[1],
|
||||
};
|
||||
attachments.push(t);
|
||||
});
|
||||
console.log("messageOptions", messageOptions);
|
||||
|
||||
setSending(true);
|
||||
try {
|
||||
await axios.post("/sendemail", messageOptions);
|
||||
await axios.post("/sendemail", { ...messageOptions, attachments });
|
||||
notification["success"]({ message: t("emails.successes.sent") });
|
||||
toggleEmailOverlayVisible();
|
||||
} catch (error) {
|
||||
@@ -71,17 +85,37 @@ export function EmailOverlayContainer({
|
||||
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 () => {
|
||||
logImEXEvent("email_render_template", { template: emailConfig.template });
|
||||
|
||||
setLoading(true);
|
||||
console.log("emailConfig", emailConfig);
|
||||
let html = await RenderTemplate(emailConfig.template, bodyshop);
|
||||
setMessageOptions({
|
||||
...emailConfig.messageOptions,
|
||||
...defaultEmailFrom,
|
||||
html: html,
|
||||
fileList: [],
|
||||
});
|
||||
setLoading(false);
|
||||
};
|
||||
@@ -99,17 +133,22 @@ export function EmailOverlayContainer({
|
||||
onCancel={() => {
|
||||
toggleEmailOverlayVisible();
|
||||
}}
|
||||
okButtonProps={{ loading: sending }}>
|
||||
okButtonProps={{ loading: sending }}
|
||||
>
|
||||
<LoadingSpinner loading={loading}>
|
||||
<EmailOverlayComponent
|
||||
handleConfigChange={handleConfigChange}
|
||||
messageOptions={messageOptions}
|
||||
handleHtmlChange={handleHtmlChange}
|
||||
handleUpload={handleUpload}
|
||||
handleFileRemove={handleFileRemove}
|
||||
handleToChange={handleToChange}
|
||||
/>
|
||||
<button
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(messageOptions.html);
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
Copy HTML
|
||||
</button>
|
||||
</LoadingSpinner>
|
||||
@@ -120,3 +159,17 @@ export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(EmailOverlayContainer);
|
||||
|
||||
const toBase64 = (file) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => resolve(reader.result);
|
||||
reader.onerror = (error) => reject(error);
|
||||
});
|
||||
|
||||
const asyncForEach = async (array, callback) => {
|
||||
for (let index = 0; index < array.length; index++) {
|
||||
await callback(array[index], index, array);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -83,7 +83,7 @@ export function JobsDetailHeaderCsi({
|
||||
if (e.key === "email")
|
||||
setEmailOptions({
|
||||
messageOptions: {
|
||||
to: job.ownr_ea,
|
||||
to: [job.ownr_ea],
|
||||
replyTo: bodyshop.email,
|
||||
},
|
||||
template: {
|
||||
|
||||
@@ -148,7 +148,7 @@ export function PartsOrderModalContainer({
|
||||
if (sendType === "e") {
|
||||
setEmailOptions({
|
||||
messageOptions: {
|
||||
to: data.vendors.filter((item) => item.id === values.id)[0] || null,
|
||||
to: [data.vendors.filter((item) => item.id === values.id)[0]] || null,
|
||||
replyTo: bodyshop.email,
|
||||
},
|
||||
template: {
|
||||
@@ -216,17 +216,19 @@ export function PartsOrderModalContainer({
|
||||
: t("parts_orders.labels.newpartsorder")
|
||||
}
|
||||
onCancel={() => toggleModalVisible()}
|
||||
width='90%'
|
||||
width="90%"
|
||||
onOk={() => form.submit()}
|
||||
destroyOnClose
|
||||
forceRender>
|
||||
{error ? <AlertComponent message={error.message} type='error' /> : null}
|
||||
forceRender
|
||||
>
|
||||
{error ? <AlertComponent message={error.message} type="error" /> : null}
|
||||
<LoadingSpinner loading={loading}>
|
||||
<Form
|
||||
form={form}
|
||||
autoComplete='no'
|
||||
autoComplete="no"
|
||||
onFinish={handleFinish}
|
||||
initialValues={initialValues}>
|
||||
initialValues={initialValues}
|
||||
>
|
||||
<PartsOrderModalComponent
|
||||
vendorList={(data && data.vendors) || []}
|
||||
sendTypeState={sendTypeState}
|
||||
|
||||
@@ -120,7 +120,7 @@ function InvoiceEnterModalContainer({
|
||||
if (sendby === "email") {
|
||||
setEmailOptions({
|
||||
messageOptions: {
|
||||
// to: appData.email,
|
||||
// to: [appData.email],
|
||||
replyTo: bodyshop.email,
|
||||
},
|
||||
template: {
|
||||
|
||||
@@ -135,7 +135,7 @@ export function ScheduleJobModalContainer({
|
||||
if (appData.notifyCustomer) {
|
||||
setEmailOptions({
|
||||
messageOptions: {
|
||||
to: appData.email,
|
||||
to: [appData.email],
|
||||
replyTo: bodyshop.email,
|
||||
},
|
||||
template: {
|
||||
|
||||
Reference in New Issue
Block a user