Baseline email wrapper
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
import React from "react";
|
||||
import { Input } from "antd";
|
||||
import CKEditor from "@ckeditor/ckeditor5-react";
|
||||
import ClassicEditor from "@ckeditor/ckeditor5-build-classic";
|
||||
|
||||
export default function SendEmailButtonComponent({
|
||||
emailConfig,
|
||||
handleConfigChange,
|
||||
handleHtmlChange
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
THis is where the text editing will happen To
|
||||
<Input
|
||||
defaultValue={emailConfig.to}
|
||||
onChange={handleConfigChange}
|
||||
name='to'
|
||||
/>
|
||||
CC
|
||||
<Input
|
||||
defaultValue={emailConfig.cc}
|
||||
onChange={handleConfigChange}
|
||||
name='cc'
|
||||
/>
|
||||
Subject
|
||||
<Input
|
||||
defaultValue={emailConfig.subject}
|
||||
onChange={handleConfigChange}
|
||||
name='subject'
|
||||
/>
|
||||
<CKEditor
|
||||
editor={ClassicEditor}
|
||||
data={emailConfig.html}
|
||||
onInit={editor => {
|
||||
// You can store the "editor" and use when it is needed.
|
||||
console.log("Editor is ready to use!", editor);
|
||||
}}
|
||||
onChange={(event, editor) => {
|
||||
const data = editor.getData();
|
||||
console.log({ event, editor, data });
|
||||
handleHtmlChange(data);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import { Button, Modal } from "antd";
|
||||
import axios from "axios";
|
||||
import React, { useState } from "react";
|
||||
import { useQuery } from "react-apollo";
|
||||
//Message options has the to & from details
|
||||
//Query Config is what can get popped into UseQuery(QUERY_NAME, {variables: {vars}, fetchonly})
|
||||
//Template Which template should be used to send the email.
|
||||
import ReactDOMServer from "react-dom/server";
|
||||
import { renderEmail } from "react-html-email";
|
||||
import LoadingSpinner from "../loading-spinner/loading-spinner.component";
|
||||
import SendEmailButtonComponent from "./send-email-button.component";
|
||||
export default function SendEmail({
|
||||
MessageOptions,
|
||||
QueryConfig,
|
||||
Template,
|
||||
...otherProps
|
||||
}) {
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
const [emailConfig, setEmailConfig] = useState({ ...MessageOptions });
|
||||
const [gqlQuery, vars] = QueryConfig;
|
||||
|
||||
const { loading, data } = useQuery(gqlQuery, {
|
||||
...vars,
|
||||
fetchPolicy: "network-only",
|
||||
skip: !modalVisible
|
||||
});
|
||||
if (data && !emailConfig.html) {
|
||||
console.log(ReactDOMServer.renderToStaticMarkup(<Template data={data} />));
|
||||
setEmailConfig({
|
||||
...emailConfig,
|
||||
//html: ReactDOMServer.renderToStaticMarkup(<Template data={data} />)
|
||||
html: renderEmail(<Template data={data} />)
|
||||
});
|
||||
}
|
||||
|
||||
const handleConfigChange = event => {
|
||||
const { name, value } = event.target;
|
||||
setEmailConfig({ ...emailConfig, [name]: value });
|
||||
};
|
||||
|
||||
const handleHtmlChange = text => {
|
||||
setEmailConfig({ ...emailConfig, html: text });
|
||||
};
|
||||
|
||||
const sendEmail = () => {
|
||||
axios.post("/sendemail", emailConfig).then(response => {
|
||||
alert(JSON.stringify(response));
|
||||
});
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<Modal
|
||||
destroyOnClose={true}
|
||||
visible={modalVisible}
|
||||
width={"80%"}
|
||||
onOk={sendEmail}
|
||||
onCancel={() => setModalVisible(false)}>
|
||||
<LoadingSpinner loading={loading}>
|
||||
<SendEmailButtonComponent
|
||||
handleConfigChange={handleConfigChange}
|
||||
emailConfig={emailConfig}
|
||||
handleHtmlChange={handleHtmlChange}
|
||||
/>
|
||||
</LoadingSpinner>
|
||||
</Modal>
|
||||
|
||||
<Button onClick={() => setModalVisible(true)}>
|
||||
{otherProps.children}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
46
client/src/emails/components/grid/grid.component.jsx
Normal file
46
client/src/emails/components/grid/grid.component.jsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import React from "react";
|
||||
|
||||
function Cell({ children }) {
|
||||
return <td>{children}</td>;
|
||||
}
|
||||
|
||||
function Row({ children }) {
|
||||
return (
|
||||
<tr>
|
||||
{React.Children.map(children, el => {
|
||||
if (el.type === Cell) return el;
|
||||
|
||||
return <td>{el}</td>;
|
||||
})}
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
function Grid({ children }) {
|
||||
return (
|
||||
<table>
|
||||
<tbody>
|
||||
{React.Children.map(children, el => {
|
||||
if (!el) return;
|
||||
|
||||
if (el.type === Row) return el;
|
||||
|
||||
if (el.type === Cell) {
|
||||
return <tr>{el}</tr>;
|
||||
}
|
||||
|
||||
return (
|
||||
<tr>
|
||||
<td>{el}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
|
||||
Grid.Row = Row;
|
||||
Grid.Cell = Cell;
|
||||
|
||||
export default Grid;
|
||||
30
client/src/emails/parts-order/parts-order.email.jsx
Normal file
30
client/src/emails/parts-order/parts-order.email.jsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import React from "react";
|
||||
import { A, Box, Email, Item, Span } from "react-html-email";
|
||||
|
||||
export default function PartsOrderEmail({ data }) {
|
||||
console.log("Data", data);
|
||||
return (
|
||||
<Email title='Hello World!'>
|
||||
<Box>
|
||||
<Item align='center'>
|
||||
<Span fontSize={20}>
|
||||
This is an example email made with:
|
||||
<A href='https://github.com/chromakode/react-html-email'>
|
||||
react-html-email
|
||||
</A>
|
||||
.
|
||||
</Span>
|
||||
</Item>
|
||||
<Item align='center'>
|
||||
<Span fontSize={20}>
|
||||
This is an example email made with:
|
||||
<A href='https://github.com/chromakode/react-html-email'>
|
||||
react-html-email
|
||||
</A>
|
||||
.
|
||||
</Span>
|
||||
</Item>
|
||||
</Box>
|
||||
</Email>
|
||||
);
|
||||
}
|
||||
45
client/src/emails/parts-order/parts-order.query.js
Normal file
45
client/src/emails/parts-order/parts-order.query.js
Normal file
@@ -0,0 +1,45 @@
|
||||
import { gql } from "apollo-boost";
|
||||
|
||||
export const REPORT_QUERY_PARTS_ORDER_BY_PK = gql`
|
||||
query REPORT_QUERY_PARTS_ORDER_BY_PK($id: uuid!) {
|
||||
parts_orders_by_pk(id: $id) {
|
||||
job {
|
||||
id
|
||||
vehicle {
|
||||
id
|
||||
v_model_desc
|
||||
v_make_desc
|
||||
v_model_yr
|
||||
v_vin
|
||||
}
|
||||
ro_number
|
||||
est_number
|
||||
}
|
||||
id
|
||||
deliver_by
|
||||
parts_order_lines {
|
||||
id
|
||||
db_price
|
||||
act_price
|
||||
line_desc
|
||||
line_remarks
|
||||
oem_partno
|
||||
status
|
||||
}
|
||||
status
|
||||
user_email
|
||||
}
|
||||
bodyshops(where: { associations: { active: { _eq: true } } }) {
|
||||
id
|
||||
address1
|
||||
address2
|
||||
city
|
||||
email
|
||||
federal_tax_id
|
||||
state
|
||||
shopname
|
||||
zip_post
|
||||
logo_img_path
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -9,47 +9,3 @@ export const INSERT_NEW_PARTS_ORDERS = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const REPORT_QUERY_PARTS_ORDER_BY_PK = gql`
|
||||
query REPORT_QUERY_PARTS_ORDER_BY_PK($id: uuid!) {
|
||||
parts_orders_by_pk(id: $id) {
|
||||
job {
|
||||
id
|
||||
vehicle {
|
||||
id
|
||||
v_model_desc
|
||||
v_make_desc
|
||||
v_model_yr
|
||||
v_vin
|
||||
}
|
||||
ro_number
|
||||
est_number
|
||||
}
|
||||
id
|
||||
deliver_by
|
||||
parts_order_lines {
|
||||
id
|
||||
db_price
|
||||
act_price
|
||||
line_desc
|
||||
line_remarks
|
||||
oem_partno
|
||||
status
|
||||
}
|
||||
status
|
||||
user_email
|
||||
}
|
||||
bodyshops(where: { associations: { active: { _eq: true } } }) {
|
||||
id
|
||||
address1
|
||||
address2
|
||||
city
|
||||
email
|
||||
federal_tax_id
|
||||
state
|
||||
shopname
|
||||
zip_post
|
||||
logo_img_path
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -1,42 +1,29 @@
|
||||
import { Button } from "antd";
|
||||
import axios from "axios";
|
||||
import React from "react";
|
||||
import { useApolloClient } from "react-apollo";
|
||||
import ReactDOMServer from "react-dom/server";
|
||||
import { REPORT_QUERY_PARTS_ORDER_BY_PK } from "../../graphql/parts-orders.queries";
|
||||
import ReportPartsOrder from "../../reports/pages/parts-order/parts-order.component";
|
||||
import SendEmailButton from "../../components/send-email-button/send-email-button.container";
|
||||
import PartsOrderEmail from "../../emails/parts-order/parts-order.email";
|
||||
import { REPORT_QUERY_PARTS_ORDER_BY_PK } from "../../emails/parts-order/parts-order.query";
|
||||
|
||||
export default function ManageRootPageComponent() {
|
||||
const client = useApolloClient();
|
||||
//const client = useApolloClient();
|
||||
return (
|
||||
<div>
|
||||
<Button
|
||||
onClick={() => {
|
||||
client
|
||||
.query({
|
||||
query: REPORT_QUERY_PARTS_ORDER_BY_PK,
|
||||
variables: { id: "ebe0fb6b-6ec4-4ae0-8fdc-49bdf1e37ff3" },
|
||||
fetchPolicy: "network-only"
|
||||
})
|
||||
.then(response => {
|
||||
axios.post("/sendemail", {
|
||||
from: { name: "Kavia Autobody" },
|
||||
to: "snaptsoft@gmail.com",
|
||||
replyTo: "patrickwf@gmail.com",
|
||||
subject: "Sending Email using Node.js",
|
||||
text: "Plaintext version of the message",
|
||||
html: ReactDOMServer.renderToStaticMarkup(
|
||||
<ReportPartsOrder
|
||||
order={response.data.parts_orders_by_pk}
|
||||
bodyshop={response.data.bodyshops[0]}
|
||||
/>
|
||||
)
|
||||
});
|
||||
});
|
||||
}}>
|
||||
Test Email
|
||||
</Button>
|
||||
<div>Testing Section-Report</div>
|
||||
<SendEmailButton
|
||||
MessageOptions={{
|
||||
from: {
|
||||
name: "Kavia"
|
||||
},
|
||||
to: "patrickwf@gmail.com",
|
||||
replyTo: "patrickwf@gmail.com"
|
||||
}}
|
||||
Template={PartsOrderEmail}
|
||||
QueryConfig={[
|
||||
REPORT_QUERY_PARTS_ORDER_BY_PK,
|
||||
{
|
||||
variables: { id: "ebe0fb6b-6ec4-4ae0-8fdc-49bdf1e37ff3" }
|
||||
}
|
||||
]}>
|
||||
Send an Email in new Window
|
||||
</SendEmailButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -60,15 +60,13 @@ export default function Manage({ match }) {
|
||||
</Header>
|
||||
<Layout>
|
||||
<Content
|
||||
className="content-container"
|
||||
style={{ padding: "0em 4em 4em" }}
|
||||
>
|
||||
className='content-container'
|
||||
style={{ padding: "0em 4em 4em" }}>
|
||||
<ErrorBoundary>
|
||||
<Suspense
|
||||
fallback={
|
||||
<LoadingSpinner message={t("general.labels.loadingapp")} />
|
||||
}
|
||||
>
|
||||
}>
|
||||
<Route exact path={`${match.path}`} component={ManageRootPage} />
|
||||
|
||||
<Route exact path={`${match.path}/jobs`} component={JobsPage} />
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
import React from "react";
|
||||
import { ApolloProvider } from "react-apollo";
|
||||
|
||||
export default function ReportEmailWrapper({ client, children }) {
|
||||
return <ApolloProvider client={client}>{children}</ApolloProvider>;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
import React from "react";
|
||||
import { Row, Col, Typography } from "antd";
|
||||
export default function RcShopHeaderComponent({ bodyshop }) {
|
||||
if (!bodyshop) return null;
|
||||
return (
|
||||
<Row>
|
||||
<Col span={8}>
|
||||
{bodyshop.logo_img_path ? (
|
||||
<img
|
||||
alt='Shop Logo'
|
||||
src={bodyshop.logo_img_path}
|
||||
style={{ height: "80px" }}
|
||||
/>
|
||||
) : null}
|
||||
</Col>
|
||||
<Col span={16}>
|
||||
<Row>
|
||||
<Typography.Title>{`${bodyshop.shopname}`}</Typography.Title>
|
||||
</Row>
|
||||
<Row>{`${bodyshop.address1 || ""} ${bodyshop.address2 ||
|
||||
""}, ${bodyshop.city || ""}, ${bodyshop.state ||
|
||||
""} ${bodyshop.zip_post || ""}`}</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
import React from "react";
|
||||
import { Row, Col, Table } from "antd";
|
||||
import CurrencyFormatter from "../../../utils/CurrencyFormatter";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export default function ReportPartsOrderComponent({ order, bodyshop }) {
|
||||
const { t } = useTranslation();
|
||||
const columns = [
|
||||
{
|
||||
title: t("joblines.fields.line_desc"),
|
||||
dataIndex: "line_desc",
|
||||
key: "line_desc"
|
||||
},
|
||||
{
|
||||
title: t("joblines.fields.oem_partno"),
|
||||
dataIndex: "oem_partno",
|
||||
key: "oem_partno",
|
||||
render: (text, record) => (
|
||||
<span>
|
||||
{record.oem_partno ? record.oem_partno : record.op_code_desc}
|
||||
</span>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: t("joblines.fields.line_remarks"),
|
||||
dataIndex: "line_remarks",
|
||||
key: "line_remarks"
|
||||
},
|
||||
{
|
||||
title: t("joblines.fields.db_price"),
|
||||
dataIndex: "db_price",
|
||||
key: "db_price",
|
||||
render: (text, record) => (
|
||||
<CurrencyFormatter>{record.db_price}</CurrencyFormatter>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: t("joblines.fields.act_price"),
|
||||
dataIndex: "act_price",
|
||||
key: "act_price",
|
||||
render: (text, record) => (
|
||||
<CurrencyFormatter>{record.act_price}</CurrencyFormatter>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Row>
|
||||
<Col span={4}>
|
||||
<div>{`PO Number: ${order.po_number || ""}`}</div>
|
||||
<div>{`Delivery By: ${order.deliver_by || ""}`}</div>
|
||||
<div>{`Ordered By: ${order.user_email || ""}`}</div>
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
<div>{`Vehicle Description: ${order.job.vehicle.v_model_yr ||
|
||||
""} ${order.job.vehicle.v_make_desc || ""} ${order.job.vehicle
|
||||
.v_model_desc || ""}`}</div>
|
||||
<div>{`VIN: ${order.job.vehicle.v_vin || ""}`}</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Table
|
||||
size='small'
|
||||
columns={columns.map(item => ({ ...item }))}
|
||||
pagination={false}
|
||||
rowKey='id'
|
||||
dataSource={order.parts_order_lines ? order.parts_order_lines : null}
|
||||
/>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import React from "react";
|
||||
import { useQuery } from "react-apollo";
|
||||
import AlertComponent from "../../../components/alert/alert.component";
|
||||
import LoadingSpinner from "../../../components/loading-spinner/loading-spinner.component";
|
||||
import { REPORT_QUERY_PARTS_ORDER_BY_PK } from "../../../graphql/parts-orders.queries";
|
||||
import RcShopHeaderComponent from "../../components/shop-header/shop-header.component";
|
||||
import ReportPartsOrderComponent from "./parts-order.component";
|
||||
|
||||
export default function ReportPartsOrderContainer({ orderId }) {
|
||||
const { loading, error, data } = useQuery(REPORT_QUERY_PARTS_ORDER_BY_PK, {
|
||||
variables: { id: orderId },
|
||||
fetchPolicy: "network-only",
|
||||
});
|
||||
if (loading) return <LoadingSpinner />;
|
||||
if (error) return <AlertComponent message={error.message} type='error' />;
|
||||
return (
|
||||
<div>
|
||||
<RcShopHeaderComponent bodyshop={data ? data.bodyshops[0] : null} />
|
||||
<ReportPartsOrderComponent
|
||||
order={data ? data.parts_orders_by_pk : null}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user