diff --git a/client/src/components/job-detail-lines/job-lines.component.jsx b/client/src/components/job-detail-lines/job-lines.component.jsx
index 3f61cfdd7..0b11301b3 100644
--- a/client/src/components/job-detail-lines/job-lines.component.jsx
+++ b/client/src/components/job-detail-lines/job-lines.component.jsx
@@ -1,4 +1,4 @@
-import { Input, Table, Button } from "antd";
+import { Button, Input, Table } from "antd";
import React, { useState } from "react";
import { useTranslation } from "react-i18next";
import CurrencyFormatter from "../../utils/CurrencyFormatter";
@@ -123,8 +123,9 @@ export default function JobLinesComponent({
{record.allocations && record.allocations.length > 0
? record.allocations.map(item => (
{`${item.employee.first_name} ${item.employee.last_name} (${item.hours})`}
+ key={
+ item.id
+ }>{`${item.employee.first_name} ${item.employee.last_name} (${item.hours})`}
))
: null}
@@ -193,7 +193,7 @@ export default function JobLinesComponent({
}}
{...formItemLayout}
loading={loading}
- size="small"
+ size='small'
pagination={{ position: "bottom", defaultPageSize: 50 }}
rowSelection={{
// selectedRowKeys: selectedLines,
@@ -201,7 +201,7 @@ export default function JobLinesComponent({
setSelectedLines(selectedRows)
}}
columns={columns.map(item => ({ ...item }))}
- rowKey="id"
+ rowKey='id'
dataSource={jobLines}
onChange={handleTableChange}
/>
diff --git a/client/src/components/parts-order-modal/parts-order-modal.container.jsx b/client/src/components/parts-order-modal/parts-order-modal.container.jsx
index 902e8e25b..06cccf380 100644
--- a/client/src/components/parts-order-modal/parts-order-modal.container.jsx
+++ b/client/src/components/parts-order-modal/parts-order-modal.container.jsx
@@ -56,6 +56,10 @@ export default connect(
data: linesToOrder.reduce((acc, value) => {
acc.push({
line_desc: value.line_desc,
+ oem_partno: value.oem_partno,
+ db_price: value.db_price,
+ act_price: value.act_price,
+ line_remarks: "Alalala",
joblineid: value.joblineid,
status:
bodyshop.md_order_statuses.default_ordered || "Ordered*"
diff --git a/client/src/graphql/parts-orders.queries.js b/client/src/graphql/parts-orders.queries.js
index b2124383c..34eb515e1 100644
--- a/client/src/graphql/parts-orders.queries.js
+++ b/client/src/graphql/parts-orders.queries.js
@@ -9,3 +9,47 @@ 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
+ }
+ }
+`;
diff --git a/client/src/pages/manage-root/manage-root.page.component.jsx b/client/src/pages/manage-root/manage-root.page.component.jsx
index a7ff94d11..4d2ba2c2c 100644
--- a/client/src/pages/manage-root/manage-root.page.component.jsx
+++ b/client/src/pages/manage-root/manage-root.page.component.jsx
@@ -1,9 +1,42 @@
-import React from 'react'
+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";
export default function ManageRootPageComponent() {
- return (
-
- Temporary Home Page.
-
- )
+ const client = useApolloClient();
+ return (
+
+
+
Testing Section-Report
+
+ );
}
diff --git a/client/src/reports/components/email-wrapper/email-wrapper.component.jsx b/client/src/reports/components/email-wrapper/email-wrapper.component.jsx
new file mode 100644
index 000000000..3e84c11bf
--- /dev/null
+++ b/client/src/reports/components/email-wrapper/email-wrapper.component.jsx
@@ -0,0 +1,6 @@
+import React from "react";
+import { ApolloProvider } from "react-apollo";
+
+export default function ReportEmailWrapper({ client, children }) {
+ return {children};
+}
diff --git a/client/src/reports/components/shop-header/shop-header.component.jsx b/client/src/reports/components/shop-header/shop-header.component.jsx
new file mode 100644
index 000000000..9561f1853
--- /dev/null
+++ b/client/src/reports/components/shop-header/shop-header.component.jsx
@@ -0,0 +1,26 @@
+import React from "react";
+import { Row, Col, Typography } from "antd";
+export default function RcShopHeaderComponent({ bodyshop }) {
+ if (!bodyshop) return null;
+ return (
+
+
+ {bodyshop.logo_img_path ? (
+
+ ) : null}
+
+
+
+ {`${bodyshop.shopname}`}
+
+ {`${bodyshop.address1 || ""} ${bodyshop.address2 ||
+ ""}, ${bodyshop.city || ""}, ${bodyshop.state ||
+ ""} ${bodyshop.zip_post || ""}`}
+
+
+ );
+}
diff --git a/client/src/reports/pages/parts-order/parts-order.component.jsx b/client/src/reports/pages/parts-order/parts-order.component.jsx
new file mode 100644
index 000000000..94864df10
--- /dev/null
+++ b/client/src/reports/pages/parts-order/parts-order.component.jsx
@@ -0,0 +1,73 @@
+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) => (
+
+ {record.oem_partno ? record.oem_partno : record.op_code_desc}
+
+ )
+ },
+ {
+ 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) => (
+ {record.db_price}
+ )
+ },
+ {
+ title: t("joblines.fields.act_price"),
+ dataIndex: "act_price",
+ key: "act_price",
+ render: (text, record) => (
+ {record.act_price}
+ )
+ }
+ ];
+
+ return (
+
+
+
+ {`PO Number: ${order.po_number || ""}`}
+ {`Delivery By: ${order.deliver_by || ""}`}
+ {`Ordered By: ${order.user_email || ""}`}
+
+
+ {`Vehicle Description: ${order.job.vehicle.v_model_yr ||
+ ""} ${order.job.vehicle.v_make_desc || ""} ${order.job.vehicle
+ .v_model_desc || ""}`}
+ {`VIN: ${order.job.vehicle.v_vin || ""}`}
+
+
+
+ ({ ...item }))}
+ pagination={false}
+ rowKey='id'
+ dataSource={order.parts_order_lines ? order.parts_order_lines : null}
+ />
+
+
+ );
+}
diff --git a/client/src/reports/pages/parts-order/parts-order.container.jsx b/client/src/reports/pages/parts-order/parts-order.container.jsx
new file mode 100644
index 000000000..5ccfc8893
--- /dev/null
+++ b/client/src/reports/pages/parts-order/parts-order.container.jsx
@@ -0,0 +1,24 @@
+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 ;
+ if (error) return ;
+ return (
+
+
+
+
+ );
+}
diff --git a/client/yarn.lock b/client/yarn.lock
index ed969110b..d0b1a823a 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -11196,8 +11196,6 @@ rxjs@^6.5.3:
version "6.5.4"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c"
integrity sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==
- dependencies:
- tslib "^1.9.0"
safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
diff --git a/package.json b/package.json
index f2140eed1..ebb9e31f7 100644
--- a/package.json
+++ b/package.json
@@ -24,7 +24,8 @@
"dotenv": "8.2.0",
"express": "^4.16.4",
"express-sslify": "^1.2.0",
- "firebase-tools": "^7.12.1"
+ "firebase-tools": "^7.12.1",
+ "nodemailer": "^6.4.2"
},
"devDependencies": {
"concurrently": "^4.0.1",
diff --git a/sendemail.js b/sendemail.js
new file mode 100644
index 000000000..868c6b65b
--- /dev/null
+++ b/sendemail.js
@@ -0,0 +1,45 @@
+var nodemailer = require("nodemailer");
+require("dotenv").config();
+
+var transporter = nodemailer.createTransport({
+ host: process.env.email_server,
+ port: 465,
+ secure: true, // upgrade later with STARTTLS
+ auth: {
+ user: process.env.email_api,
+ pass: process.env.email_secret
+ }
+});
+
+// verify connection configuration
+transporter.verify(function(error, success) {
+ if (error) {
+ console.log(error);
+ } else {
+ console.log("[EMAIL] Succesfully connected to SMTP server.");
+ }
+});
+
+exports.sendEmail = (req, res) => {
+ if (process.env.NODE_ENV !== "production") {
+ console.log("[EMAIL] Incoming Message Body", req.body);
+ }
+ transporter.sendMail(
+ {
+ ...req.body,
+ from: {
+ name: req.body.from.name || "No Reply @ Bodyshop.app",
+ address: "noreply@bodyshop.app"
+ }
+ },
+ function(error, info) {
+ if (error) {
+ console.log("[EMAIL] Email send failed. ", error);
+ res.json({ success: false, error: error });
+ } else {
+ console.log("[EMAIL] Email sent: " + info.response);
+ res.json({ success: true, response: info.response });
+ }
+ }
+ );
+};
diff --git a/server.js b/server.js
index 7acc0a49d..258d70662 100644
--- a/server.js
+++ b/server.js
@@ -23,6 +23,8 @@ app.post("/sign_s3", s3upload.sign_s3);
app.get("/sign_s3", s3upload.get_s3);
app.post("/delete_s3", s3upload.delete_s3);
+var sendEmail = require("./sendemail.js");
+app.post("/sendemail", sendEmail.sendEmail);
// app.get("/test", function(req, res) {
// res.json({ success: true });
// });
diff --git a/yarn.lock b/yarn.lock
index 48163623a..c72d7c808 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2872,6 +2872,11 @@ node-forge@^0.9.0:
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.1.tgz#775368e6846558ab6676858a4d8c6e8d16c677b5"
integrity sha512-G6RlQt5Sb4GMBzXvhfkeFmbqR6MzhtnT7VTHuLadjkii3rdYHNdw0m8zA4BTxVIh68FicCQ2NSUANpsqkr9jvQ==
+nodemailer@^6.4.2:
+ version "6.4.2"
+ resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.4.2.tgz#7147550e32cdc37453380ab78d2074533966090a"
+ integrity sha512-g0n4nH1ONGvqYo1v72uSWvF/MRNnnq1LzmSzXb/6EPF3LFb51akOhgG3K2+aETAsJx90/Q5eFNTntu4vBCwyQQ==
+
normalize-package-data@^2.3.2:
version "2.5.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"