import { gql } from "@apollo/client"; import jsreport from "@jsreport/browser-client"; import axios from "axios"; import _ from "lodash"; import { auth } from "../firebase/firebase.utils"; import { setEmailOptions } from "../redux/email/email.actions"; import { store } from "../redux/store"; import client from "../utils/GraphQLClient"; import cleanAxios from "./CleanAxios"; import { TemplateList } from "./TemplateConstants"; import { generateTemplate } from "./graphQLmodifier"; const server = import.meta.env.VITE_APP_REPORTS_SERVER_URL; jsreport.serverUrl = server; let Templates; export function GenerateTemplates() { //Required as a part of the transition to Vite. //Previous method had the template hash generating before translations loaded, resulting in empty files. Templates = TemplateList(); } export default async function RenderTemplate( templateObject, bodyshop, renderAsHtml = false, renderAsExcel = false, renderAsText = false, notification ) { if (window.jsr3) { jsreport.serverUrl = "https://reports3.test.imex.online/"; } const jsrAuth = (await axios.post("/utils/jsr")).data; jsreport.headers["Authorization"] = jsrAuth; //Query assets that match the template name. Must be in format <>.query let { contextData, useShopSpecificTemplate, shopSpecificFolder } = await fetchContextData(templateObject, jsrAuth); const { ignoreCustomMargins } = Templates[templateObject.name]; let reportRequest = { template: { name: useShopSpecificTemplate ? `/${bodyshop.imexshopid}/${templateObject.name}` : `/${templateObject.name}`, ...(renderAsHtml ? {} : { recipe: "chrome-pdf", ...(!ignoreCustomMargins && { chrome: { marginTop: bodyshop.logo_img_path && bodyshop.logo_img_path.headerMargin && bodyshop.logo_img_path.headerMargin > 36 ? bodyshop.logo_img_path.headerMargin : "36px", marginBottom: bodyshop.logo_img_path && bodyshop.logo_img_path.footerMargin && bodyshop.logo_img_path.footerMargin > 50 ? bodyshop.logo_img_path.footerMargin : "50px" } }) }), ...(renderAsExcel ? { recipe: "html-to-xlsx" } : {}), ...(renderAsText ? { recipe: "text" } : {}) }, data: { ...contextData, ...templateObject.variables, ...templateObject.context, headerpath: shopSpecificFolder ? `/${bodyshop.imexshopid}/header.html` : `/GENERIC/header.html`, footerpath: shopSpecificFolder ? `/${bodyshop.imexshopid}/footer.html` : `/GENERIC/footer.html`, bodyshop: bodyshop, filters: templateObject?.filters, sorters: templateObject?.sorters, offset: bodyshop.timezone, //dayjs().utcOffset(), defaultSorters: templateObject?.defaultSorters } }; try { const render = await jsreport.render(reportRequest); if (!renderAsHtml) { render.download((Templates[templateObject.name] && Templates[templateObject.name].title) || ""); } else { let pdf; if (bodyshop.attach_pdf_to_email) { const pdfRequest = _.cloneDeep(reportRequest); //Updates to spread in the header details. pdfRequest.template = { ...pdfRequest.template, ...{ recipe: "chrome-pdf", ...(!ignoreCustomMargins && { chrome: { marginTop: bodyshop.logo_img_path && bodyshop.logo_img_path.headerMargin && bodyshop.logo_img_path.headerMargin > 36 ? bodyshop.logo_img_path.headerMargin : "36px", marginBottom: bodyshop.logo_img_path && bodyshop.logo_img_path.footerMargin && bodyshop.logo_img_path.footerMargin > 50 ? bodyshop.logo_img_path.footerMargin : "50px" } }) } }; const pdfRender = await jsreport.render(pdfRequest); pdf = await pdfRender.toDataURI(); } const html = await render.toString(); return new Promise((resolve, reject) => { resolve({ pdf, filename: Templates[templateObject.name] && Templates[templateObject.name].title, html }); }); } } catch (error) { notification["error"]({ message: JSON.stringify(error) }); } } export async function RenderTemplates(templateObjects, bodyshop, renderAsHtml = false, notification) { //Query assets that match the template name. Must be in format <>.query let unsortedTemplatesAndData = []; let proms = []; const jsrAuth = (await axios.post("/utils/jsr")).data; jsreport.headers["Authorization"] = jsrAuth; templateObjects.forEach((template) => { proms.push( (async () => { let { contextData, useShopSpecificTemplate, shopSpecificFolder } = await fetchContextData(template, jsrAuth); unsortedTemplatesAndData.push({ templateObject: template, contextData, useShopSpecificTemplate, shopSpecificFolder }); })() ); }); await Promise.all(proms); //Re-order list to follow speedprint. // var templateAndData = _.sortBy(unsortedTemplatesAndData, function (item) { // return templateObjects.findIndex( // (template) => template.name === item.templateObject.name // ); // }); if (window.jsr3) { jsreport.serverUrl = "https://reports3.test.imex.online/"; } unsortedTemplatesAndData.sort(function (a, b) { return ( templateObjects.findIndex((x) => x.name === a.templateObject.name) - templateObjects.findIndex((x) => x.name === b.templateObject.name) ); }); const templateAndData = unsortedTemplatesAndData; let rootTemplate = templateAndData.shift(); let reportRequest = { template: { name: rootTemplate.useShopSpecificTemplate ? `/${bodyshop.imexshopid}/${rootTemplate.templateObject.name}` : `/${rootTemplate.templateObject.name}`, ...(renderAsHtml ? {} : { recipe: "chrome-pdf", chrome: { marginTop: bodyshop.logo_img_path && bodyshop.logo_img_path.headerMargin && bodyshop.logo_img_path.headerMargin > 36 ? bodyshop.logo_img_path.headerMargin : "36px", marginBottom: bodyshop.logo_img_path && bodyshop.logo_img_path.footerMargin && bodyshop.logo_img_path.footerMargin > 50 ? bodyshop.logo_img_path.footerMargin : "50px" } }), pdfOperations: [ { template: { name: "/components/Header-Footer", recipe: "chrome-pdf", engine: "handlebars" }, type: "merge" }, ...templateAndData.map((template) => { return { template: { chrome: { marginTop: bodyshop.logo_img_path && bodyshop.logo_img_path.headerMargin && bodyshop.logo_img_path.headerMargin > 36 ? bodyshop.logo_img_path.headerMargin : "36px", marginBottom: bodyshop.logo_img_path && bodyshop.logo_img_path.footerMargin && bodyshop.logo_img_path.footerMargin > 50 ? bodyshop.logo_img_path.footerMargin : "50px" }, name: template.useShopSpecificTemplate ? `/${bodyshop.imexshopid}/${template.templateObject.name}` : `/${template.templateObject.name}`, ...(renderAsHtml ? {} : { recipe: "chrome-pdf" }) }, type: "append" // mergeWholeDocument: true, // renderForEveryPage: true, }; }) ] }, data: { ...extend(rootTemplate.contextData, ...templateAndData.map((temp) => temp.contextData)), // ...rootTemplate.templateObject.variables, // ...rootTemplate.templateObject.context, headerpath: rootTemplate.shopSpecificFolder ? `/${bodyshop.imexshopid}/header.html` : `/GENERIC/header.html`, footerpath: rootTemplate.shopSpecificFolder ? `/${bodyshop.imexshopid}/footer.html` : `/GENERIC/footer.html`, bodyshop: bodyshop, offset: bodyshop.timezone } }; try { const render = await jsreport.render(reportRequest); if (!renderAsHtml) { render.download("Speed Print"); } else { return render.toString(); } } catch (error) { notification["error"]({ message: JSON.stringify(error) }); } } export const GenerateDocument = async (template, messageOptions, sendType, jobid, notification) => { const bodyshop = store.getState().user.bodyshop; if (sendType === "e") { store.dispatch( setEmailOptions({ jobid, messageOptions: { ...messageOptions, to: Array.isArray(messageOptions.to) ? messageOptions.to : [messageOptions.to] }, template }) ); } else if (sendType === "x") { await RenderTemplate(template, bodyshop, false, true, false, notification); } else if (sendType === "text") { await RenderTemplate(template, bodyshop, false, false, true, notification); } else { await RenderTemplate(template, bodyshop, false, false, false, notification); } }; export const GenerateDocuments = async (templates, notification) => { const bodyshop = store.getState().user.bodyshop; await RenderTemplates(templates, bodyshop, false, notification); }; export const fetchFilterData = async ({ name }) => { try { const bodyshop = store.getState().user.bodyshop; const jsrAuth = (await axios.post("/utils/jsr")).data; jsreport.headers["FirebaseAuthorization"] = "Bearer " + (await auth.currentUser.getIdToken()); const folders = await cleanAxios.get(`${server}/odata/folders`, { headers: { Authorization: jsrAuth } }); const shopSpecificFolder = folders.data.value.find((f) => f.name === bodyshop.imexshopid); const jsReportFilters = await cleanAxios.get(`${server}/odata/assets?$filter=name eq '${name}.filters'`, { headers: { Authorization: jsrAuth } }); let parsedFilterData; let useShopSpecificTemplate = false; // let shopSpecificTemplate; if (shopSpecificFolder) { let shopSpecificTemplate = jsReportFilters.data.value.find( (f) => f?.folder?.shortid === shopSpecificFolder.shortid ); if (shopSpecificTemplate) { useShopSpecificTemplate = true; parsedFilterData = atob(shopSpecificTemplate.content); } } if (!parsedFilterData) { const generalTemplate = jsReportFilters.data.value.find((f) => !f.folder); useShopSpecificTemplate = false; if (generalTemplate) parsedFilterData = atob(generalTemplate.content); } const data = JSON.parse(parsedFilterData); return { data, useShopSpecificTemplate, success: true }; } catch { return { success: false }; } }; const fetchContextData = async (templateObject, jsrAuth) => { const bodyshop = store.getState().user.bodyshop; jsreport.headers["FirebaseAuthorization"] = "Bearer " + (await auth.currentUser.getIdToken()); const folders = await cleanAxios.get(`${server}/odata/folders`, { headers: { Authorization: jsrAuth } }); const shopSpecificFolder = folders.data.value.find((f) => f.name === bodyshop.imexshopid); const jsReportQueries = await cleanAxios.get( `${server}/odata/assets?$filter=name eq '${templateObject.name}.query'`, { headers: { Authorization: jsrAuth } } ); let templateQueryToExecute; let useShopSpecificTemplate = false; // let shopSpecificTemplate; if (shopSpecificFolder) { let shopSpecificTemplate = jsReportQueries.data.value.find( (f) => f?.folder?.shortid === shopSpecificFolder.shortid ); if (shopSpecificTemplate) { useShopSpecificTemplate = true; templateQueryToExecute = atob(shopSpecificTemplate.content); } } if (!templateQueryToExecute) { const generalTemplate = jsReportQueries.data.value.find((f) => !f.folder); useShopSpecificTemplate = false; templateQueryToExecute = atob(generalTemplate.content); } // Commented out for future revision debugging // console.log('Template Object'); // console.dir(templateObject); // console.log('Unmodified Query'); // console.dir(templateQueryToExecute); const hasFilters = templateObject?.filters?.length > 0; const hasSorters = templateObject?.sorters?.length > 0; const hasDefaultSorters = templateObject?.defaultSorters?.length > 0; // We have no template filters or sorters, so we can just execute the query and return the data if (!hasFilters && !hasSorters && !hasDefaultSorters) { let contextData = {}; if (templateQueryToExecute) { const { data } = await client.query({ query: gql(templateQueryToExecute), variables: { ...templateObject.variables } }); contextData = data; } return { contextData, useShopSpecificTemplate, shopSpecificFolder }; } return await generateTemplate(templateQueryToExecute, templateObject, useShopSpecificTemplate, shopSpecificFolder); }; //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) => { // 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); // } // }; function extend(o1, o2, o3) { var result = {}, obj; for (var i = 0; i < arguments.length; i++) { obj = arguments[i]; for (var key in obj) { if (Object.prototype.toString.call(obj[key]) === "[object Object]") { if (typeof result[key] === "undefined") { result[key] = {}; } result[key] = extend(result[key], obj[key]); } else { result[key] = obj[key]; } } } return result; }