From 442255ebaf91fcc5c1a27dd21b1f8db29de88ef5 Mon Sep 17 00:00:00 2001 From: popovspiridon99 Date: Wed, 26 Mar 2025 10:31:00 +0900 Subject: [PATCH] Form from template --- client/package-lock.json | 38 ++- client/package.json | 2 + client/public/template_table.docx | Bin 14300 -> 14405 bytes client/src/pages/PrintReport.tsx | 507 ++++++++++++++++++++++-------- client/yarn.lock | 26 +- 5 files changed, 432 insertions(+), 141 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 30b439d..09c7289 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -42,8 +42,10 @@ "buffer": "^6.0.3", "dayjs": "^1.11.13", "docx-templates": "^4.13.0", + "easy-template-x": "^5.1.0", "embla-carousel-react": "^8.3.0", "file-type": "^19.0.0", + "html2canvas": "^1.4.1", "jspdf": "^2.5.2", "ol": "^10.0.0", "ol-ext": "^4.0.23", @@ -4257,6 +4259,15 @@ "vite": "^4 || ^5" } }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/@zeit/schemas": { "version": "2.36.0", "resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.36.0.tgz", @@ -4643,7 +4654,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", - "optional": true, "engines": { "node": ">= 0.6.0" } @@ -5560,7 +5570,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", - "optional": true, "dependencies": { "utrie": "^1.0.2" } @@ -5963,6 +5972,18 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, + "node_modules/easy-template-x": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/easy-template-x/-/easy-template-x-5.1.0.tgz", + "integrity": "sha512-vypMbIMLWLXoooA9rsL3SVN2oQtZwmmx1m4H8gi6JfbEXQQ5VLHGOUHYi9APbvN9R8Gx93r1fphdSFRHxozeYw==", + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "0.8.10", + "json5": "2.2.3", + "jszip": "3.10.1", + "lodash.get": "4.4.2" + } + }, "node_modules/ejs": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", @@ -7150,7 +7171,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", - "optional": true, + "license": "MIT", "dependencies": { "css-line-break": "^2.1.0", "text-segmentation": "^1.0.3" @@ -7851,7 +7872,6 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, "bin": { "json5": "lib/cli.js" }, @@ -7884,6 +7904,7 @@ "version": "2.5.2", "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-2.5.2.tgz", "integrity": "sha512-myeX9c+p7znDWPk0eTrujCzNjT+CXdXyk7YmJq5nD5V7uLLKmSXnlQ/Jn/kuo3X09Op70Apm0rQSnFWyGK8uEQ==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.23.2", "atob": "^2.1.2", @@ -8046,6 +8067,13 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -11280,7 +11308,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", - "optional": true, "dependencies": { "utrie": "^1.0.2" } @@ -11801,7 +11828,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", - "optional": true, "dependencies": { "base64-arraybuffer": "^1.0.2" } diff --git a/client/package.json b/client/package.json index f14d041..3c5cddd 100644 --- a/client/package.json +++ b/client/package.json @@ -45,8 +45,10 @@ "buffer": "^6.0.3", "dayjs": "^1.11.13", "docx-templates": "^4.13.0", + "easy-template-x": "^5.1.0", "embla-carousel-react": "^8.3.0", "file-type": "^19.0.0", + "html2canvas": "^1.4.1", "jspdf": "^2.5.2", "ol": "^10.0.0", "ol-ext": "^4.0.23", diff --git a/client/public/template_table.docx b/client/public/template_table.docx index b88ddad27526e5e1ba9e8dbe2ccb132ac83b1d89..18c9cb4961147e2ae3ab2516bd518fdc2ac32fb4 100644 GIT binary patch delta 3829 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 delta 3707 zcmZ9PXHXN2lg1NDlrFtR={1;ymV_=Ky+~J@B2^&tB8U>2bOaKRDo95_dI>Gkd+$X+ zB1r!Og0D2;yt$csZ*D(5v-3RrL;{pKy&HT7w5Ibhj{VRfv zz`89*4QeNEEVbt7p;aF)1X;{SqI_%E5EC7)7G@-CbH`cH!m)GEQ{NeZEtF3$Ap6m;#dIh8yNCj< zC-3@>6lGF<4}S_1H|cRIJ@})%(@8VDJ@ULdg37%m?=2ZL1Ac3!(1w;m4M`LEmGfa5 zdTXNsDtuzKNJPr987U8b`X}EN|3*EvEgH-BY8W1B*x4ccru7z|peY}}%K~R-3IjO* z2@QnlwS-lYugMVC?9c3r(j-`%1xfEpvBUHtLrb2QKE!;g*Z(d4u^`KH>6nk{i}WSu z@Fq! zV*s1XfR~y_Yy5v5!LW+Ayo3Up9EP4f~i-0PmoS;owvBH|7bI2C&s*% zs*B2qNnFkyNWB=F1Gy!Z`&qJrr2!D+(>#^*{yK44Pn7U<2+24vzIwthkDwFJzL#Q zggMw|IUF;AxqyjN7<9+0ksqIUSWo1=eMC(i25%XlIRwcU=*uTr&KuD7;N zl>l|@G(HrR0ucyY0vQ01LxX$IB7o^{SvH$D?`aZ@!&~;4pJrf%vu=w}@;mffp03Fi zJ{(>wjSFB6tSOa|bc)=t+MwggWj{EmNs3^dCIoGs2H`aW)ngCpZ#KQ8*?tEZtD(h4F#Z5c4#H}s{&%E06tJQBg~O#qiST2 zu_>+5n0BG2im+jS-WJO6OWyi)us|$6j}4Bcga%gDYmJ;_7vck-P#v>;X&4MEVeWn_ zaW9I*I69QoTSMDT$JN*u`HaR9W+*-id>vtsVgp zN3f3>s?G9gBVyA&)@xLcCrehY`$myzGqYqd6m=lB-ZErcw8!5J&VrW;Lqy%hbf?je zvs!@RYN2fVS8B%Am=o%~0a4}EV6~-=fS27qX=}1XIGk+os6g>JLDr}(>>F|eLJ+Ba zlxjl8rsw)6<~D&90IlB|PvT4LXQ+Z3F@vY&WF(O_`Gr=DJa{}rVj$tAkNQtAmFENQ z`MD&;hezQuTbA(>>C=ZBP1rLJgGh#7Ts-%49j!#h+!Hr3v||@r?o%lfNAhphUHNeS z#~sbGpJWS!v(NM1fTMWfE6Jj4d#mtW-AEB>C@zq*= z`&zNX5#u(z{32Rk+-7QX zI9haliv2uqlhfJ8Aqf+nl_NcHoE(ye$W5Po9`SGpO(p*BT5M%4@w8e!FdRy2r4o z9|;IGjKRqr_55{LFT)V~2T-UUaT+`M0b5=7^uzoqWK?4DRtvVQ-8;FU= zTDykBnIaEwUD9EL^w3_^hE&Vjvy|cf=zXY;{&GcT1pnt>YcLmA$X;(p+{tk^vL89< z*k49XYg1t$q2cw=0Du*NZU|}|mIt0ilkAml)SEDP&xEMe5wD?kvJOKXS%w_(j5efI z{BA?O?;6K;nCyld4GcL0RW5#5U@~H5b2Dk?QVzC3M-fT4tU?R2Wn(Qn4d`OG`3D!d zKm9J=Cqs1`sE|*YgEhnn)wH?}9<;t6AhisYlb(zU$XgdLdI58OcW0+*e_i&U`5d?G z2TL>6{)z>PN{kr^Sy8A(!txULuP<+n^j!9dmDh&FD( zj7QeV->_(!y`b<{~H`3zO7HuHJh`#rNsZ^geXCgD3%cdASK3t(E1rd-~nQqJ* zm2v~?vrzaUds^%1GowxW5*Q)%)}z;8ZFD<5#kp~lv*1t>%R-3C4i;YIp8SknUh0U~ zCH^&K1?<}jIrBLhSW#s&9nEc@4>*Gf-0Pcl!(*OPFNDz<6fHVLAPJsrD^FoM{An9;kbgZ~~#2wCm z4>$lo{}8}QG6hB>Hlv27X^slg8%>k diff --git a/client/src/pages/PrintReport.tsx b/client/src/pages/PrintReport.tsx index 5b658e0..f310f8f 100644 --- a/client/src/pages/PrintReport.tsx +++ b/client/src/pages/PrintReport.tsx @@ -1,6 +1,11 @@ -import { Button, Flex } from "@mantine/core"; -import { useState } from "react"; -import createReport from 'docx-templates' +import { ActionIcon, Button, Flex, Group, Input, Stack, Text, TextInput } from "@mantine/core" +import { useEffect, useState } from "react" +import createReport, { listCommands } from 'docx-templates' +import { Dropzone, DropzoneProps, IMAGE_MIME_TYPE, MS_WORD_MIME_TYPE } from '@mantine/dropzone' +import { IconFile, IconFileTypeDocx, IconPhoto, IconPlus, IconUpload, IconX } from "@tabler/icons-react" +import { CommandSummary } from "docx-templates/lib/types" +import { Control, Controller, FieldValues, SubmitHandler, useFieldArray, useForm, UseFormRegister } from "react-hook-form" +import { TemplateHandler } from 'easy-template-x' const xslTemplate = ` @@ -905,165 +910,403 @@ const xslTemplate = ` ` -const PrintReport = () => { - const [loading, setLoading] = useState(false); +const handleGenerateExcel = () => { + // Define the example XML data + const xmlData = ` + + + 1 + Region 1 + City 1 + 10 + 1000 + 500 + 200 + 1000 + 300 + 1500 + 400 + 2000 + 500 + 2500 + 300 + + + ` + + // Parse the XSL template and XML data + const parser = new DOMParser() + const xslDoc = parser.parseFromString(xslTemplate, "application/xml") + const xmlDoc = parser.parseFromString(xmlData, "application/xml") + + // Apply the transformation + const xsltProcessor = new XSLTProcessor() + xsltProcessor.importStylesheet(xslDoc) + const resultDocument = xsltProcessor.transformToDocument(xmlDoc) + + // Serialize the result to a string + const serializer = new XMLSerializer() + const resultXml = serializer.serializeToString(resultDocument) + + // Add missing Excel-specific headers if needed + const correctedXml = `\n` + resultXml - const generateDocx = async () => { - setLoading(true); + // Convert to Blob and trigger download + const blob = new Blob([correctedXml], { type: "application/vnd.ms-excel" }) + const url = URL.createObjectURL(blob) + const link = document.createElement("a") + link.href = url + link.download = "template.xls" + link.click() + URL.revokeObjectURL(url) +} + +const TemplateFormET = ({ + templateUrl +}: { + templateUrl: string +}) => { + const [templateUint8Array, setTemplateUint8Array] = useState(null) + const [loading, setLoading] = useState(false) + + const loadTemplate = async (templateUrl: string) => { + setLoading(true) try { - // Fetch the DOCX template from the public folder - const response = await fetch("/template.docx"); - const response_table = await fetch("/template_table.docx"); - const templateArrayBuffer = await response.arrayBuffer(); - const templateArrayBuffer_table = await response_table.arrayBuffer(); + const response = await fetch(templateUrl) + const templateArrayBuffer = await response.arrayBuffer() + + setTemplateUint8Array(new Uint8Array(templateArrayBuffer)) + } catch (error) { + console.error("Error generating DOCX:", error) + } finally { + setLoading(false) + } + } + + const loadTags = async (templateUint8Array: Uint8Array) => { + const handler = new TemplateHandler() + const tags = await handler.parseTags(templateUint8Array) + + console.log(tags) + } + + useEffect(() => { + if (templateUint8Array) { + loadTags(templateUint8Array) + } + }, [templateUint8Array]) + + useEffect(() => { + if (templateUrl) { + loadTemplate(templateUrl) + } + }, [templateUrl]) - // Convert ArrayBuffer to Uint8Array (Fix TypeScript error) - const templateUint8Array = new Uint8Array(templateArrayBuffer); - const templateUint8Array_table = new Uint8Array(templateArrayBuffer_table); + return ( +
+ +
+ ) +} + +interface TemplateCommand extends CommandSummary { + children?: CommandSummary[] +} + +export function parseCommandList(commands: CommandSummary[]): TemplateCommand[] { + function parseBlock(startIndex: number, currentElement?: string): [TemplateCommand[], number] { + const block: TemplateCommand[] = [] + let i = startIndex + + while (i < commands.length) { + const command = commands[i] + + if (command.type === "FOR") { + const [elementName, , arrayName] = command.code.split(" ") + const forCommand: TemplateCommand = { + raw: command.raw, + type: command.type, + code: arrayName, + children: [], + } + + const [children, nextIndex] = parseBlock(i + 1, elementName) + forCommand.children = children + i = nextIndex + block.push(forCommand) + } else if (command.type === "END-FOR") { + return [block, i + 1] + } else { + let code = command.code + if (currentElement && (code.startsWith(`${currentElement}.`) || code.startsWith(`$${currentElement}.`))) { + code = code.replace(`$${currentElement}.`, "").replace(`${currentElement}.`, "") + } - // Fetch the image (Example: Load from public folder) - const imageResponse = await fetch("/test.png"); // Change this to your image path - const imageBlob = await imageResponse.blob(); - const imageArrayBuffer = await imageBlob.arrayBuffer(); - const imageUint8Array = new Uint8Array(imageArrayBuffer); + block.push({ + ...command, + code, + }) + i++ + } + } + + return [block, i] + } + + const [parsed] = parseBlock(0) + return parsed +} + +const FormLoop = ({ + control, + register, + command, +}: { + control: Control, + register: UseFormRegister, + command: TemplateCommand, +}) => { + const { fields, append, remove } = useFieldArray({ + name: command.code, + control + }) + + return ( + + + { + fields.map((field, index) => ( + + {command.children && + command.children.map(c => + renderCommand( + control, + register, + c, + `${c.code}`, + `${command.code}.${index}.${c.code}`, + `${command.code}.${index}.${c.code}` + ) + )} + + + )) + } + + { + if (command.children) { + append(command.children.map(c => c.code).reduce((acc, key) => { + acc[key] = ''; + return acc; + }, {} as Record)) + } + }}> + + + + ) +} +const renderCommand = ( + control: Control, + register: UseFormRegister, + command: CommandSummary, + label: string, + key: string, + name: string, +) => { + if (command.type === 'INS') { + return ( + + ) + } + + if (command.type === 'IMAGE') { + return ( + ( + console.log('rejected files', files)} + onDrop={(files) => { + console.log(files[0]) + files[0].arrayBuffer().then(res => { + onChange({ + width: 6, + height: 6, + data: new Uint8Array(res), + extension: files[0]?.path?.match(/\.[^.]+$/)?.[0] || "" + }) + }) + }} + maxFiles={1} + > + + + + + + + + + + + +
+ + Drag files here or click to select files + + + Attach as many files as you like, each file should not exceed 5mb + +
+
+
+ )} + /> + ) + } +} + +const TemplateForm = ({ + templateUrl, +}: { + templateUrl: string, +}) => { + const { register, control, handleSubmit, reset, watch, formState } = useForm({ + mode: 'onChange', + }) + + const [loading, setLoading] = useState(false) + const [saving, setSaving] = useState(false) + + const [templateUint8Array, setTemplateUint8Array] = useState(null) + + const [commandList, setCommandList] = useState([]) + + const saveTest = async (templateUint8Array: Uint8Array, data: any) => { + setSaving(true) + + try { // Generate the DOCX file with the replacement const report = await createReport({ template: templateUint8Array, // Ensure it's Uint8Array - data: { - test: "Hello World", - myImage: { - width: 6, // Width in cm - height: 6, // Height in cm - data: imageUint8Array, // Image binary data - extension: ".png", // Specify the image format - }, - }, - }); - - const report_table = await createReport({ - template: templateUint8Array_table, // Ensure it's Uint8Array - data: { - test: "Hello World", - rows: [ - { - first: 'A', - second: 'B', - third: 'C', - fourth: 'D', - }, - { - first: 'E', - second: 'F', - third: 'G', - fourth: 'H', - }, - { - first: 'I', - second: 'J', - third: 'K', - fourth: 'L', - } - ] - }, - }); + data: data, + }) // Convert Uint8Array to a Blob const blob = new Blob([report], { type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", - }); - - const blob_table = new Blob([report_table], { - type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document", - }); - - // Create a download link and trigger the download - const url = URL.createObjectURL(blob); - const a = document.createElement("a"); - a.href = url; - a.download = "report.docx"; - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); + }) // Create a download link and trigger the download - const url_table = URL.createObjectURL(blob_table); - const a_table = document.createElement("a"); - a_table.href = url_table; - a_table.download = "report_table.docx"; - document.body.appendChild(a_table); - a_table.click(); - document.body.removeChild(a_table); + const url = URL.createObjectURL(blob) + const a = document.createElement("a") + a.href = url + a.download = "report.docx" + document.body.appendChild(a) + a.click() + document.body.removeChild(a) - // Revoke the object URL after download - URL.revokeObjectURL(url); - URL.revokeObjectURL(url_table); + URL.revokeObjectURL(url) } catch (error) { - console.error("Error generating DOCX:", error); + console.error("Error generating DOCX:", error) } finally { - setLoading(false); + setSaving(false) } } - const handleGenerateExcel = () => { - // Define the example XML data - const xmlData = ` - - - 1 - Region 1 - City 1 - 10 - 1000 - 500 - 200 - 1000 - 300 - 1500 - 400 - 2000 - 500 - 2500 - 300 - - - `; + const loadTemplate = async () => { + setLoading(true) - // Parse the XSL template and XML data - const parser = new DOMParser(); - const xslDoc = parser.parseFromString(xslTemplate, "application/xml"); - const xmlDoc = parser.parseFromString(xmlData, "application/xml"); + try { + const response = await fetch(templateUrl) + const templateArrayBuffer = await response.arrayBuffer() - // Apply the transformation - const xsltProcessor = new XSLTProcessor(); - xsltProcessor.importStylesheet(xslDoc); - const resultDocument = xsltProcessor.transformToDocument(xmlDoc); + setTemplateUint8Array(new Uint8Array(templateArrayBuffer)) + } catch (error) { + console.error("Error generating DOCX:", error) + } finally { + setLoading(false) + } + } - // Serialize the result to a string - const serializer = new XMLSerializer(); - const resultXml = serializer.serializeToString(resultDocument); + const loadCommands = async (templateUint8Array: Uint8Array) => { + try { + await listCommands(templateUint8Array).then(l => { + setCommandList(parseCommandList(l)) + }) + } catch (error) { + console.error("Error loading commands from DOCX:", error) + } + } - // Add missing Excel-specific headers if needed - const correctedXml = `\n` + resultXml + useEffect(() => { + if (templateUint8Array) { + loadCommands(templateUint8Array) + } + }, [templateUint8Array]) - // Convert to Blob and trigger download - const blob = new Blob([correctedXml], { type: "application/vnd.ms-excel" }); - const url = URL.createObjectURL(blob); - const link = document.createElement("a"); - link.href = url; - link.download = "template.xls"; - link.click(); + const onSubmit: SubmitHandler = async (data) => { + try { + if (templateUint8Array && data) { + saveTest(templateUint8Array, data) + } + } catch (error) { + console.error(error) + } + } - // Clean up - URL.revokeObjectURL(url); + useEffect(() => { + if (templateUrl) { + loadTemplate() + } + }, [templateUrl]) + + if (commandList) { + return ( +
+ + {commandList.map(command => { + if (command.type === 'FOR') { + return ( + + ) + } else { + return renderCommand(control, register, command, command.code, command.code, command.code) + } + })} + + +
+ ) } +} + +const PrintReport = () => { + const [loading, setLoading] = useState(false) return ( - - - - + + + + + + + ) } diff --git a/client/yarn.lock b/client/yarn.lock index c965585..e35451d 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -1869,6 +1869,11 @@ dependencies: "@swc/core" "^1.5.7" +"@xmldom/xmldom@0.8.10": + version "0.8.10" + resolved "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz" + integrity sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw== + "@zeit/schemas@2.36.0": version "2.36.0" resolved "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.36.0.tgz" @@ -2932,6 +2937,16 @@ eastasianwidth@^0.2.0: resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== +easy-template-x@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/easy-template-x/-/easy-template-x-5.1.0.tgz" + integrity sha512-vypMbIMLWLXoooA9rsL3SVN2oQtZwmmx1m4H8gi6JfbEXQQ5VLHGOUHYi9APbvN9R8Gx93r1fphdSFRHxozeYw== + dependencies: + "@xmldom/xmldom" "0.8.10" + json5 "2.2.3" + jszip "3.10.1" + lodash.get "4.4.2" + ejs@^3.1.6: version "3.1.10" resolved "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz" @@ -3665,7 +3680,7 @@ hmac-drbg@^1.0.1: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -html2canvas@^1.0.0-rc.5: +html2canvas@^1.0.0-rc.5, html2canvas@^1.4.1: version "1.4.1" resolved "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz" integrity sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA== @@ -4056,7 +4071,7 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== -json5@^2.2.0, json5@^2.2.3: +json5@^2.2.0, json5@^2.2.3, json5@2.2.3: version "2.2.3" resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== @@ -4090,7 +4105,7 @@ jspdf@^2.5.1, jspdf@^2.5.2: dompurify "^2.5.4" html2canvas "^1.0.0-rc.5" -jszip@^3.10.1: +jszip@^3.10.1, jszip@3.10.1: version "3.10.1" resolved "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz" integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g== @@ -4176,6 +4191,11 @@ lodash.debounce@^4.0.8: resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz" integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== +lodash.get@4.4.2: + version "4.4.2" + resolved "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz" + integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz"