Modified how template list is generated to be a function to allow checking whether the templates can be printed.

This commit is contained in:
Patrick Fic
2020-09-11 14:43:04 -07:00
parent 984c001bd8
commit f7e74c5043
20 changed files with 167 additions and 207 deletions

View File

@@ -1,36 +1,13 @@
body {
font-family: "Open Sans", sans-serif;
line-height: 1.25;
/* font-family: "Open Sans", sans-serif; */
/* line-height: 1.25; */
/* padding: 10mm 10mm 10mm 10mm !important; */
}
@page {
size: auto; /* auto is the initial value */
/* this affects the margin in the printer settings */
margin: 25mm 25mm 25mm 25mm;
color: tomato;
margin: 50px;
}
/* Unknown whether the below actually gets embedded */
/* @media print {
@page {
size: Letter;
margin: 0;
}
html,
body {
width: 210mm;
height: 282mm;
font-size: 11px;
background: #fff;
overflow: visible;
}
body {
padding-top: 15mm;
}
} */
table.imex-table {
border: 1px solid #ccc;
border-collapse: collapse;
@@ -38,27 +15,44 @@ table.imex-table {
padding: 0;
width: 100%;
table-layout: fixed;
font-size: inherit;
page-break-inside: auto;
}
table.imex-table caption {
font-size: 1.5em;
/* font-size: 1.5em; */
margin: 0.5em 0 0.75em;
font-size: inherit;
}
table.imex-table tr {
background-color: #f8f8f8;
/* background-color: #f8f8f8; */
border: 1px solid #ddd;
padding: 0.1rem;
padding: 0.2rem;
font-size: inherit;
page-break-inside: avoid;
page-break-after: auto;
}
table.imex-table th,
table.imex-table td {
padding: 0.3rem;
text-align: center;
font-size: inherit;
}
table.imex-table th.left,
table.imex-table td.left {
padding: 0.3rem;
text-align: left;
font-size: inherit;
}
table.imex-table th {
font-size: 0.85em;
/* font-size: 0.85em; */
letter-spacing: 0.1em;
text-transform: uppercase;
/* display: table-header-group; */
}

View File

@@ -1,6 +1,6 @@
import {
PaymentRequestButtonElement,
useStripe
useStripe,
} from "@stripe/react-stripe-js";
import React, { useEffect, useState } from "react";
import { connect } from "react-redux";
@@ -72,7 +72,7 @@ function Test({ bodyshop, setEmailOptions }) {
replyTo: bodyshop.email,
},
template: {
name: TemplateList.parts_order_confirmation.key,
name: TemplateList().parts_order_confirmation.key,
variables: {
id: "a7c2d4e1-f519-42a9-a071-c48cf0f22979",
},

View File

@@ -89,7 +89,7 @@ export function JobsDetailHeaderCsi({
replyTo: bodyshop.email,
},
template: {
name: TemplateList.csi_invitation.key,
name: TemplateList().csi_invitation.key,
variables: {
id: result.data.insert_csi.returning[0].id,
},
@@ -108,14 +108,16 @@ export function JobsDetailHeaderCsi({
<Menu.SubMenu title={t("jobs.actions.sendcsi")} {...props}>
<Menu.Item
onClick={handleCreateCsi}
key='email'
disabled={!!!job.ownr_ea}>
key="email"
disabled={!!!job.ownr_ea}
>
{t("general.labels.email")}
</Menu.Item>
<Menu.Item
onClick={handleCreateCsi}
key='text'
disabled={!!!job.ownr_ph1}>
key="text"
disabled={!!!job.ownr_ph1}
>
{t("general.labels.text")}
</Menu.Item>
<Menu.Divider />

View File

@@ -154,8 +154,8 @@ export function PartsOrderModalContainer({
},
template: {
name: isReturn
? TemplateList.parts_return_confirmation.key
: TemplateList.parts_order_confirmation.key,
? TemplateList().parts_return_confirmation.key
: TemplateList().parts_order_confirmation.key,
variables: {
id: insertResult.data.insert_parts_orders.returning[0].id,
},
@@ -166,8 +166,8 @@ export function PartsOrderModalContainer({
await RenderTemplate(
{
name: isReturn
? TemplateList.parts_return_confirmation.key
: TemplateList.parts_order_confirmation.key,
? TemplateList().parts_return_confirmation.key
: TemplateList().parts_order_confirmation.key,
variables: {
id: insertResult.data.insert_parts_orders.returning[0].id,
},

View File

@@ -123,7 +123,7 @@ function InvoiceEnterModalContainer({
replyTo: bodyshop.email,
},
template: {
name: TemplateList.payment_receipt.key,
name: TemplateList().payment_receipt.key,
variables: {
id: newPayment.data.insert_payments.returning[0].id,
},
@@ -133,7 +133,7 @@ function InvoiceEnterModalContainer({
displayTemplateInWindow(
await RenderTemplate(
{
name: TemplateList.payment_receipt.key,
name: TemplateList().payment_receipt.key,
variables: {
id: newPayment.data.insert_payments.returning[0].id,
},

View File

@@ -11,7 +11,8 @@ import {
} from "../../redux/user/user.selectors";
import PrintCenterItem from "../print-center-item/print-center-item.component";
import PrintCenterSpeedPrint from "../print-center-speed-print/print-center-speed-print.component";
import JobsReports from "./print-center-jobs.list";
import { TemplateList } from "../../utils/TemplateConstants";
import { useTranslation } from "react-i18next";
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
@@ -27,8 +28,13 @@ const mapDispatchToProps = (dispatch) => ({
const colSpan = { md: { span: 24 }, lg: { span: 12 } };
export function PrintCenterJobsComponent({ bodyshop, printCenterModal }) {
const { t } = useTranslation();
const { id: jobId } = printCenterModal.context;
const JobsReportsList = JobsReports(null);
const tempList = TemplateList();
const JobsReportsList = Object.keys(tempList).map((key) => {
return tempList[key];
});
console.log("PrintCenterJobsComponent -> JobsReportsList", JobsReportsList);
return (
<div>
@@ -38,20 +44,20 @@ export function PrintCenterJobsComponent({ bodyshop, printCenterModal }) {
</Col>
<Col {...colSpan}>
<Collapse accordion>
{JobsReportsList.map((section) => (
<Collapse.Panel key={section.key} header={section.title}>
<ul style={{ columns: "2 auto" }}>
{section.items.map((item) => (
<Collapse.Panel header={t("printcenter.labels.repairorder")}>
<ul style={{ columns: "2 auto" }}>
{JobsReportsList.filter((t) => t.drivingId === "job").map(
(item) => (
<PrintCenterItem
key={item.key}
item={item}
id={jobId}
disabled={item.disabled}
/>
))}
</ul>
</Collapse.Panel>
))}
)
)}{" "}
</ul>{" "}
</Collapse.Panel>
</Collapse>
</Col>
</Row>

View File

@@ -1,60 +0,0 @@
import i18n from "i18next";
export default function JobsReports(job) {
return [
{
title: i18n.t("printcenter.labels.repairorder"),
key: "printcenter.jobs.repairorder",
disabled: false,
items: [
{
key: "appointment_reminder",
title: i18n.t("printcenter.jobs.appointment_reminder"),
disabled: false,
},
{
key: "estimate_detail",
title: i18n.t("printcenter.jobs.estimate_detail"),
disabled: false,
},
{
key: "job_totals",
title: i18n.t("printcenter.jobs.job_totals"),
disabled: false,
},
{
key: "work_authorization",
title: i18n.t("printcenter.jobs.work_authorization"),
disabled: false,
},
{
key: "fippa_work_authorization",
title: i18n.t("printcenter.jobs.fippa_work_authorization"),
disabled: false,
},
{
key: "casl_work_authorization",
title: i18n.t("printcenter.jobs.casl_work_authorization"),
disabled: false,
},
{
key: "coversheet",
title: i18n.t("printcenter.jobs.coversheet"),
disabled: false,
},
],
},
{
title: i18n.t("printcenter.labels.misc"),
key: "printcenter.jobs.misc",
disabled: false,
items: [
{
key: "iou",
title: i18n.t("printcenter.jobs.iou"),
disabled: true,
},
],
},
];
}

View File

@@ -70,8 +70,8 @@ export function PrintCenterSpeedPrint({ bodyshop, jobId }) {
const renderTemplateList = (templates) => (
<span className="imex-flex-row__margin">
{templates.map((template, idx) => {
if (idx === templates.length - 1) return TemplateList[template].title;
return `${TemplateList[template].title}, `;
if (idx === templates.length - 1) return TemplateList()[template].title;
return `${TemplateList()[template].title}, `;
})}
</span>
);

View File

@@ -140,7 +140,7 @@ export function ScheduleJobModalContainer({
replyTo: bodyshop.email,
},
template: {
name: TemplateList.appointment_confirmation.key,
name: TemplateList().appointment_confirmation.key,
variables: {
id: appt.data.insert_appointments.returning[0].id,
},

View File

@@ -130,10 +130,10 @@ export default function ShopInfoIntakeChecklistComponent({ form }) {
<Select mode="multiple">
{Object.keys(TemplateList).map((i) => (
<Select.Option
key={TemplateList[i].key}
value={TemplateList[i].key}
key={TemplateList()[i].key}
value={TemplateList()[i].key}
>
{TemplateList[i].title}
{TemplateList()[i].title}
</Select.Option>
))}
</Select>
@@ -252,10 +252,10 @@ export default function ShopInfoIntakeChecklistComponent({ form }) {
<Select mode="multiple">
{Object.keys(TemplateList).map((i) => (
<Select.Option
key={TemplateList[i].key}
value={TemplateList[i].key}
key={TemplateList()[i].key}
value={TemplateList()[i].key}
>
{TemplateList[i].title}
{TemplateList()[i].title}
</Select.Option>
))}
</Select>

View File

@@ -60,8 +60,8 @@ export default function ShopInfoSpeedPrint({ bodyshop, form }) {
]}
>
<Select mode="multiple">
{Object.keys(TemplateList)
.map((key) => TemplateList[key])
{Object.keys(TemplateList())
.map((key) => TemplateList()[key])
.filter((template) => template.drivingId === "job")
.map((template, idx) => (
<Select.Option key={idx} value={template.key}>

View File

@@ -72,7 +72,7 @@ export function ShopTemplateAddComponent({
<Menu onClick={handleAdd}>
{availableTemplateKeys.length > 0 ? (
availableTemplateKeys.map((tkey) => (
<Menu.Item key={tkey}>{TemplateList[tkey].title}</Menu.Item>
<Menu.Item key={tkey}>{TemplateList()[tkey].title}</Menu.Item>
))
) : (
<div>{t("bodyshop.labels.notemplatesavailable")}</div>

View File

@@ -1,4 +1,4 @@
import React from "react";
import React, { useState } from "react";
import { Button, notification } from "antd";
import { useMutation } from "@apollo/react-hooks";
import { useTranslation } from "react-i18next";
@@ -12,15 +12,15 @@ export default function ShopTemplateSaveButton({
emailEditorRef,
}) {
const { t } = useTranslation();
const [loading, setLoading] = useState(false);
const [updateTemplate] = useMutation(UPDATE_TEMPLATE);
const handleSave = async () => {
logImEXEvent("shop_template_update");
setLoading(true);
emailEditorRef.current.exportHtml(async (data) => {
inlineCss(data.html, {
url: `${window.location.protocol}://${window.location.host}/`,
preserveMediaQueries: true,
}).then(async function (inlineHtml) {
const result = await updateTemplate({
variables: {
@@ -43,12 +43,13 @@ export default function ShopTemplateSaveButton({
}),
});
}
setLoading(false);
});
});
};
return (
<Button disabled={!!!templateId} onClick={handleSave}>
<Button disabled={!!!templateId} loading={loading} onClick={handleSave}>
{t("general.actions.save")}
</Button>
);

View File

@@ -38,11 +38,6 @@ export default function ShopTemplateEditorComponent({
return (
<div>
<ShopTemplateEditorSaveButton
templateId={templateId}
gql={editorContent.gql}
emailEditorRef={emailEditorRef}
/>
<EmailEditor
style={{ width: "100%" }}
ref={emailEditorRef}
@@ -63,7 +58,7 @@ export default function ShopTemplateEditorComponent({
],
}}
/>
<div style={{ display: "flex", width: "90vw" }}>
<div style={{ display: "flex", width: "90vw", margin: "2rem" }}>
<CmEditor
style={{ flex: 1 }}
value={editorContent.gql}
@@ -86,6 +81,11 @@ export default function ShopTemplateEditorComponent({
query={editorContent.gql}
emailEditorRef={emailEditorRef}
/>
<ShopTemplateEditorSaveButton
templateId={templateId}
gql={editorContent.gql}
emailEditorRef={emailEditorRef}
/>
</div>
</div>
);

View File

@@ -36,7 +36,6 @@ export function ShopTemplateTestRender({
emailEditorRef.current.exportHtml(async (data) => {
inlineCss(data.html, {
url: `${window.location.protocol}://${window.location.host}/`,
removeLinkTags: false,
}).then(async function (inlineHtml) {
try {
const { data: contextData } = await client.query({
@@ -65,8 +64,10 @@ export function ShopTemplateTestRender({
};
return (
<div style={style}>
<Editor value={variables} onChange={(e) => setVariables(e)} />
<div style={{ ...style, margin: "1rem", display: "flex" }}>
<div style={{ flex: 1 }}>
<Editor value={variables} onChange={(e) => setVariables(e)} />
</div>
<Button loading={loading} type="ghost" onClick={handleTestRender}>
{t("bodyshop.actions.testrender")}
</Button>

View File

@@ -62,9 +62,9 @@ export default function ShopTemplatesListContainer({ visibleState }) {
>
<Skeleton title={false} loading={item.loading} active>
<div style={{ display: "flex", flexDirection: "column" }}>
<div>{TemplateList[item.name].title}</div>
<div>{TemplateList[item.name].description}</div>
<div>{TemplateList[item.name].drivingid}</div>
<div>{TemplateList()[item.name].title}</div>
<div>{TemplateList()[item.name].description}</div>
<div>{TemplateList()[item.name].drivingid}</div>
</div>
</Skeleton>
</List.Item>

View File

@@ -70,7 +70,7 @@ export function TimeTicketsSummaryEmployees({
const handlePrintEmployeeTicket = async (empId) => {
const html = await RenderTemplate(
{
name: TemplateList.time_tickets_by_employee.key,
name: TemplateList().time_tickets_by_employee.key,
variables: { id: empId, start: startDate, end: endDate },
},
bodyshop

View File

@@ -1,71 +1,83 @@
import i18n from "i18next";
export const EmailSettings = {
fromNameDefault: "ImEX Online",
fromAddress: "noreply@imex.online",
};
export const TemplateList = {
//Verified Completed items
estimate_detail: {
title: "Estimate Detail",
description: "Est Detail",
drivingId: "job",
key: "estimate_detail",
},
//Non Completed Items
appointment_reminder: {
title: "Appointment Reminder",
description: "Sent to a customer as a reminder of an upcoming appointment.",
drivingId: "appointment",
key: "appointment_reminder",
},
appointment_confirmation: {
title: "Appointment Confirmation",
description:
"Sent to a customer as a Confirmation of an upcoming appointment.",
drivingId: "appointment",
key: "appointment_confirmation",
},
parts_order_confirmation: {
title: "Parts Order Confirmation",
description: "Parts order template including part details",
drivingId: "partsorder",
key: "parts_order_confirmation",
},
export const TemplateList = (type, object) => {
return {
//Verified Completed items
estimate_detail: {
title: i18n.t("printcenter.jobs.estimate_detail"),
description: "Est Detail",
drivingId: "job",
key: "estimate_detail",
},
all_job_notes: {
title: i18n.t("printcenter.jobs.all_job_notes"),
description: "All jobs Notes including Private",
drivingId: "job",
key: "all_job_notes",
},
cover_sheet_landscape: {
title: "Cover Sheet - Landscape",
description: "Cover sheet landscape",
drivingId: "job",
key: "cover_sheet_landscape",
},
cover_sheet_portrait: {
title: "Cover Sheet - portrait",
description: "Cover sheet portrait",
drivingId: "job",
key: "cover_sheet_portrait",
},
parts_return_confirmation: {
title: "Parts Return Confirmation",
description: "Parts Return template including part details",
drivingId: "partsorder",
key: "parts_return_confirmation",
},
csi_invitation: {
title: "Customer Survey Invitation",
description: "Customer Survey Invitation",
drivingId: "csi",
key: "csi_invitation",
},
payment_receipt: {
title: "Payment Receipt",
description: "Receipt of payment for customer",
drivingId: "payment",
key: "payment_receipt",
},
time_tickets_by_employee: {
title: "Time Tickets by Employee",
description: "Time tickets for employee with date range",
drivingId: "employee",
key: "time_tickets_by_employee",
},
//Non Completed Items
appointment_reminder: {
title: "Appointment Reminder",
description:
"Sent to a customer as a reminder of an upcoming appointment.",
drivingId: "appointment",
key: "appointment_reminder",
},
appointment_confirmation: {
title: "Appointment Confirmation",
description:
"Sent to a customer as a Confirmation of an upcoming appointment.",
drivingId: "appointment",
key: "appointment_confirmation",
},
parts_order_confirmation: {
title: "Parts Order Confirmation",
description: "Parts order template including part details",
drivingId: "partsorder",
key: "parts_order_confirmation",
},
cover_sheet_landscape: {
title: "Cover Sheet - Landscape",
description: "Cover sheet landscape",
drivingId: "job",
key: "cover_sheet_landscape",
},
cover_sheet_portrait: {
title: "Cover Sheet - portrait",
description: "Cover sheet portrait",
drivingId: "job",
key: "cover_sheet_portrait",
},
parts_return_confirmation: {
title: "Parts Return Confirmation",
description: "Parts Return template including part details",
drivingId: "partsorder",
key: "parts_return_confirmation",
},
csi_invitation: {
title: "Customer Survey Invitation",
description: "Customer Survey Invitation",
drivingId: "csi",
key: "csi_invitation",
},
payment_receipt: {
title: "Payment Receipt",
description: "Receipt of payment for customer",
drivingId: "payment",
key: "payment_receipt",
},
time_tickets_by_employee: {
title: "Time Tickets by Employee",
description: "Time tickets for employee with date range",
drivingId: "employee",
key: "time_tickets_by_employee",
},
};
};

View File

@@ -0,0 +1,3 @@
Table Styles
<link rel='stylesheet' href='http://localhost:3000/render-styles.css'/>

View File

@@ -202,6 +202,7 @@ exports.render = (req, res) => {
//console.log("[HJS Render] Context", req.body.context);
if (req.body.context.bodyshop.template_header) {
console.log("[HJS Render] Including Header");
//view = req.body.view;
view = `${req.body.context.bodyshop.template_header}${req.body.view}`;
} else {
console.log("[HJS Render] No header to include.");