Resolved bulk printing & status for printing items. IO-690

This commit is contained in:
Patrick Fic
2021-02-23 10:34:31 -08:00
parent fad4ee4ef5
commit 666931aacd
6 changed files with 197 additions and 120 deletions

View File

@@ -1,5 +1,6 @@
import { MailOutlined, PrinterOutlined } from "@ant-design/icons"; import { MailOutlined, PrinterOutlined } from "@ant-design/icons";
import React from "react"; import { Spin } from "antd";
import React, { useState } from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { setEmailOptions } from "../../redux/email/email.actions"; import { setEmailOptions } from "../../redux/email/email.actions";
@@ -22,8 +23,10 @@ export function PrintCenterItemComponent({
bodyshop, bodyshop,
disabled, disabled,
}) { }) {
const [loading, setLoading] = useState(false);
const { context } = printCenterModal; const { context } = printCenterModal;
const renderToNewWindow = async () => { const renderToNewWindow = async () => {
setLoading(true);
await GenerateDocument( await GenerateDocument(
{ {
name: item.key, name: item.key,
@@ -32,6 +35,7 @@ export function PrintCenterItemComponent({
{}, {},
"p" "p"
); );
setLoading(false);
}; };
if (disabled) return <li className="print-center-item">{item.title} </li>; if (disabled) return <li className="print-center-item">{item.title} </li>;
@@ -51,6 +55,7 @@ export function PrintCenterItemComponent({
); );
}} }}
/> />
{loading && <Spin size="small" />}
</li> </li>
); );
} }

View File

@@ -1,11 +1,11 @@
import { Button, List, Typography } from "antd"; import { Button, List, Typography } from "antd";
import React from "react"; import React, { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { logImEXEvent } from "../../firebase/firebase.utils"; import { logImEXEvent } from "../../firebase/firebase.utils";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import { GenerateDocument } from "../../utils/RenderTemplate"; import { GenerateDocuments } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants"; import { TemplateList } from "../../utils/TemplateConstants";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
@@ -16,26 +16,19 @@ const mapDispatchToProps = (dispatch) => ({
}); });
export function PrintCenterSpeedPrint({ bodyshop, jobId }) { export function PrintCenterSpeedPrint({ bodyshop, jobId }) {
const [loading, setLoading] = useState(false);
const { speedprint } = bodyshop; const { speedprint } = bodyshop;
const { t } = useTranslation(); const { t } = useTranslation();
const renderTemplate = async (templateKey) => { const renderAllTemplates = async (templateKeys) => {
logImEXEvent("speed_print_template_render");
GenerateDocument(
{
name: templateKey,
variables: { id: jobId },
},
{},
"p"
);
};
const renderAllTemplates = (templateKeys) => {
logImEXEvent("speed_print_render_all_templates"); logImEXEvent("speed_print_render_all_templates");
setLoading(true);
templateKeys.forEach((templateKey) => renderTemplate(templateKey)); await GenerateDocuments(
templateKeys.map((key) => {
return { name: key, variables: { id: jobId } };
})
);
setLoading(false);
}; };
return ( return (
@@ -50,7 +43,10 @@ export function PrintCenterSpeedPrint({ bodyshop, jobId }) {
renderItem={(sp) => ( renderItem={(sp) => (
<List.Item <List.Item
actions={[ actions={[
<Button onClick={() => renderAllTemplates(sp.templates)}> <Button
loading={loading}
onClick={() => renderAllTemplates(sp.templates)}
>
Print All Print All
</Button>, </Button>,
]} ]}

View File

@@ -50,7 +50,7 @@ export function ShopTemplateTestRender({
view: inlineHtml.data, view: inlineHtml.data,
context: { ...contextData, bodyshop: bodyshop }, context: { ...contextData, bodyshop: bodyshop },
}); });
displayTemplateInWindowNoprint(renderResponse.data); // displayTemplateInWindowNoprint(renderResponse.data);
setLoading(false); setLoading(false);
} catch (error) { } catch (error) {

View File

@@ -1,16 +1,14 @@
import React from "react"; import { Button, List, Space, Statistic, Typography } from "antd";
import { Statistic, Space, List, Button, Typography } from "antd";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
import { useTranslation } from "react-i18next";
import moment from "moment"; import moment from "moment";
import RenderTemplate, { import React from "react";
displayTemplateInWindow, import { useTranslation } from "react-i18next";
} from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { createStructuredSelector } from "reselect"; import { createStructuredSelector } from "reselect";
import { selectBodyshop } from "../../redux/user/user.selectors"; import { selectBodyshop } from "../../redux/user/user.selectors";
import { onlyUnique } from "../../utils/arrayHelper"; import { onlyUnique } from "../../utils/arrayHelper";
import { GenerateDocument } from "../../utils/RenderTemplate";
import { TemplateList } from "../../utils/TemplateConstants";
import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component";
const mapStateToProps = createStructuredSelector({ const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop, bodyshop: selectBodyshop,
@@ -68,15 +66,14 @@ export function TimeTicketsSummaryEmployees({
}); });
const handlePrintEmployeeTicket = async (empId) => { const handlePrintEmployeeTicket = async (empId) => {
alert("Missing Key!"); GenerateDocument(
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 }, variables: { id: empId, start: startDate, end: endDate },
}, },
bodyshop {},
"p"
); );
displayTemplateInWindow(html);
}; };
return ( return (

View File

@@ -1,3 +1,4 @@
import { notification } from "antd";
import axios from "axios"; import axios from "axios";
import gql from "graphql-tag"; import gql from "graphql-tag";
import jsreport from "jsreport-browser-client-dist"; import jsreport from "jsreport-browser-client-dist";
@@ -5,16 +6,149 @@ import { auth } from "../firebase/firebase.utils";
import { setEmailOptions } from "../redux/email/email.actions"; import { setEmailOptions } from "../redux/email/email.actions";
import { store } from "../redux/store"; import { store } from "../redux/store";
import client from "../utils/GraphQLClient"; import client from "../utils/GraphQLClient";
import { TemplateList } from "./TemplateConstants";
const server = process.env.REACT_APP_REPORTS_SERVER_URL; const server = process.env.REACT_APP_REPORTS_SERVER_URL;
jsreport.serverUrl = server; jsreport.serverUrl = server;
const Templates = TemplateList();
export default async function RenderTemplate( export default async function RenderTemplate(
templateObject, templateObject,
bodyshop, bodyshop,
renderAsHtml = false renderAsHtml = false
) { ) {
//Query assets that match the template name. Must be in format <<templateName>>.query //Query assets that match the template name. Must be in format <<templateName>>.query
let { contextData, useShopSpecificTemplate } = await fetchContextData(
templateObject
);
let reportRequest = {
template: {
name: useShopSpecificTemplate
? `/${bodyshop.imexshopid}/${templateObject.name}`
: `/${templateObject.name}`,
...(renderAsHtml ? {} : { recipe: "chrome-pdf" }),
},
data: {
...contextData,
...templateObject.variables,
...templateObject.context,
headerpath: `/${bodyshop.imexshopid}/header.html`,
bodyshop: bodyshop,
},
};
try {
const render = await jsreport.renderAsync(reportRequest);
if (!renderAsHtml) {
render.download(Templates[templateObject.name].title || "");
} else {
return new Promise((resolve, reject) => {
resolve(render.toString());
});
}
} catch (error) {
notification["error"]({ message: JSON.stringify(error) });
}
}
export async function RenderTemplates(
templateObjects,
bodyshop,
renderAsHtml = false
) {
//Query assets that match the template name. Must be in format <<templateName>>.query
let templateAndData = [];
let proms = [];
templateObjects.forEach((template) => {
proms.push(
(async () => {
let { contextData, useShopSpecificTemplate } = await fetchContextData(
template
);
templateAndData.push({
templateObject: template,
contextData,
useShopSpecificTemplate,
});
})()
);
});
await Promise.all(proms);
let rootTemplate = templateAndData.shift();
let reportRequest = {
template: {
name: rootTemplate.useShopSpecificTemplate
? `/${bodyshop.imexshopid}/${rootTemplate.templateObject.name}`
: `/${rootTemplate.templateObject.name}`,
...(renderAsHtml ? {} : { recipe: "chrome-pdf" }),
pdfOperations: templateAndData.map((template) => {
return {
template: {
name: template.useShopSpecificTemplate
? `/${bodyshop.imexshopid}/${template.templateObject.name}`
: `/${template.templateObject.name}`,
...(renderAsHtml ? {} : { recipe: "chrome-pdf" }),
},
data: {
...template.contextData,
...template.templateObject.variables,
...template.templateObject.context,
headerpath: `/${bodyshop.imexshopid}/header.html`,
bodyshop: bodyshop,
},
type: "append",
mergeWholeDocument: true,
renderForEveryPage: true,
};
}),
},
data: {
...rootTemplate.contextData,
...rootTemplate.templateObject.variables,
...rootTemplate.templateObject.context,
headerpath: `/${bodyshop.imexshopid}/header.html`,
bodyshop: bodyshop,
},
};
console.log("reportRequest", reportRequest);
try {
const render = await jsreport.renderAsync(reportRequest);
if (!renderAsHtml) {
render.download("Speed Print");
} else {
return new Promise((resolve, reject) => {
resolve(render.toString());
});
}
} catch (error) {
notification["error"]({ message: JSON.stringify(error) });
}
}
export const GenerateDocument = async (template, messageOptions, sendType) => {
const bodyshop = store.getState().user.bodyshop;
if (sendType === "e") {
store.dispatch(
setEmailOptions({
messageOptions,
template,
})
);
} else {
await RenderTemplate(template, bodyshop);
}
};
export const GenerateDocuments = async (templates) => {
const bodyshop = store.getState().user.bodyshop;
await RenderTemplates(templates, bodyshop);
};
const fetchContextData = async (templateObject) => {
jsreport.headers["Authorization"] = jsreport.headers["Authorization"] =
"Bearer " + (await auth.currentUser.getIdToken()); "Bearer " + (await auth.currentUser.getIdToken());
@@ -44,7 +178,7 @@ export default async function RenderTemplate(
"There are too many queries to choose from. Please ensure there are no conflicting keys." "There are too many queries to choose from. Please ensure there are no conflicting keys."
); );
} }
let contextData; let contextData = {};
if (templateQueryToExecute) { if (templateQueryToExecute) {
const { data } = await client.query({ const { data } = await client.query({
query: gql(templateQueryToExecute), query: gql(templateQueryToExecute),
@@ -54,83 +188,37 @@ export default async function RenderTemplate(
contextData = data; contextData = data;
} }
let reportRequest = { return { contextData, useShopSpecificTemplate };
template: {
name: useShopSpecificTemplate
? `/${bodyshop.imexshopid}/${templateObject.name}`
: `/${templateObject.name}`,
...(renderAsHtml ? {} : { recipe: "chrome-pdf" }),
},
data: {
...(templateQueryToExecute ? contextData : {}),
...templateObject.variables,
...templateObject.context,
headerpath: `/${bodyshop.imexshopid}/header.html`,
bodyshop: bodyshop,
},
};
const render = await jsreport.renderAsync(reportRequest);
if (!renderAsHtml) {
render.download();
// var html =
// "<html>" +
// "<style>html,body {padding:0;margin:0;} iframe {width:100%;height:100%;border:0}</style>" +
// "<body>" +
// '<iframe type="application/pdf" src="' +
// render.toDataURI() +
// '"></iframe>' +
// "</body></html>";
// displayTemplateInWindowNoprint(html);
} else {
return new Promise((resolve, reject) => {
resolve(render.toString());
});
}
}
export const displayTemplateInWindow = (html) => {
try {
var newWin = window.open("", "_blank", "toolbar=0,location=0,menubar=0");
newWin.document.write(html);
setTimeout(function () {
newWin.document.close();
newWin.focus();
newWin.print();
newWin.close();
}, 500);
} catch (error) {
console.log("Unable to write to new window.", error);
}
}; };
export const displayTemplateInWindowNoprint = (html) => { //export const displayTemplateInWindow = (html) => {
try { // try {
var newWin = window.open("", "_blank", "toolbar=0,location=0,menubar=0"); // var newWin = window.open("", "_blank", "toolbar=0,location=0,menubar=0");
newWin.document.write(html); // newWin.document.write(html);
setTimeout(function () { // setTimeout(function () {
newWin.document.close(); // newWin.document.close();
newWin.focus(); // newWin.focus();
//newWin.print(); // newWin.print();
//newWin.close(); // newWin.close();
}, 500); // }, 500);
} catch (error) { // } catch (error) {
console.log("Unable to write to new window.", error); // console.log("Unable to write to new window.", error);
} // }
}; // };
export const GenerateDocument = async (template, messageOptions, sendType) => { // export const displayTemplateInWindowNoprint = (html) => {
const bodyshop = store.getState().user.bodyshop; // try {
if (sendType === "e") { // var newWin = window.open("", "_blank", "toolbar=0,location=0,menubar=0");
store.dispatch( // newWin.document.write(html);
setEmailOptions({
messageOptions, // setTimeout(function () {
template, // newWin.document.close();
}) // newWin.focus();
); // //newWin.print();
} else { // //newWin.close();
await RenderTemplate(template, bodyshop); // }, 500);
} // } catch (error) {
}; // console.log("Unable to write to new window.", error);
// }
// };

View File

@@ -12,15 +12,6 @@ export const TemplateList = (type, context) => {
//If there's no type or the type is job, send it back. //If there's no type or the type is job, send it back.
...(!type || type === "job" ...(!type || type === "job"
? { ? {
estimate_detail: {
title: i18n.t("printcenter.jobs.estimate_detail"),
description: "Est Detail",
subject: `${i18n.t("printcenter.jobs.estimate_detail")} - ${
context && context.job && context.job.ro_number
}`,
key: "estimate_detail",
disabled: false,
},
casl_authorization: { casl_authorization: {
title: i18n.t("printcenter.jobs.casl_authorization"), title: i18n.t("printcenter.jobs.casl_authorization"),
description: "CASL Authorization", description: "CASL Authorization",
@@ -209,7 +200,7 @@ export const TemplateList = (type, context) => {
title: i18n.t("printcenter.jobs.parts_order"), title: i18n.t("printcenter.jobs.parts_order"),
description: "Parts Order", description: "Parts Order",
key: "parts_order", key: "parts_order",
subject: `${bodyshop.shopname} Parts Order ${ subject: `${bodyshop && bodyshop.shopname} Parts Order ${
(context && (context &&
context && context &&
context.job && context.job &&