From fbbd3535a797e17589eaa5a85dbe5399c1fb5327 Mon Sep 17 00:00:00 2001 From: Don Xavier <mail.donxavier@gmail.com> Date: Tue, 7 Jan 2025 12:25:19 +0530 Subject: [PATCH 01/26] Implement multiple File uploading --- src/hooks/useFileUpload.tsx | 41 ++++++++++++++++++- .../Encounters/tabs/EncounterFilesTab.tsx | 9 +++- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/hooks/useFileUpload.tsx b/src/hooks/useFileUpload.tsx index d8aea1fa1b1..d570e164dbb 100644 --- a/src/hooks/useFileUpload.tsx +++ b/src/hooks/useFileUpload.tsx @@ -1,6 +1,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query"; import imageCompression from "browser-image-compression"; import { t } from "i18next"; +import jsPDF from "jspdf"; import { ChangeEvent, DetailedHTMLProps, @@ -97,6 +98,33 @@ export default function useFileUpload( const [files, setFiles] = useState<File[]>([]); const queryClient = useQueryClient(); + const generatePDF = async (files: File[]): Promise<File | null> => { + try { + const pdf = new jsPDF(); + for (const [index, file] of files.entries()) { + const imgData = await new Promise<string>((resolve, reject) => { + const reader = new FileReader(); + reader.onload = () => resolve(reader.result as string); + reader.onerror = () => reject("Error reading file"); + reader.readAsDataURL(file); + }); + + pdf.addImage(imgData, "JPEG", 10, 10, 190, 0); + if (index < files.length - 1) pdf.addPage(); + } + + const pdfBlob = pdf.output("blob"); + const pdfFile = new File([pdfBlob], "combined.pdf", { + type: "application/pdf", + }); + + console.log("Generated PDF file:", pdfFile); // Log the generated file + return pdfFile; + } catch (error) { + console.error("Error generating PDF:", error); + return null; + } + }; const onFileChange = (e: ChangeEvent<HTMLInputElement>): any => { if (!e.target.files?.length) { return; @@ -238,8 +266,19 @@ export default function useFileUpload( if (!validateFileUpload()) return; setProgress(0); + let filesToUpload = files; + + if (files.length > 1) { + const pdfFile = await generatePDF(files); + if (pdfFile) { + filesToUpload = [pdfFile]; + } else { + console.error("Failed to generate PDF from multiple files."); + return; + } + } - for (const [index, file] of files.entries()) { + for (const [index, file] of filesToUpload.entries()) { const filename = allowNameFallback && uploadFileNames[index] === "" && file ? file.name diff --git a/src/pages/Encounters/tabs/EncounterFilesTab.tsx b/src/pages/Encounters/tabs/EncounterFilesTab.tsx index 053637b75d8..8d979bc912e 100644 --- a/src/pages/Encounters/tabs/EncounterFilesTab.tsx +++ b/src/pages/Encounters/tabs/EncounterFilesTab.tsx @@ -136,6 +136,7 @@ export const EncounterFilesTab = (props: EncounterTabProps) => { ], allowNameFallback: false, onUpload: () => refetch(), + multiple: true, }); useEffect(() => { @@ -543,8 +544,12 @@ const FileUploadDialog = ({ </DialogHeader> <div className="mb-1 flex items-center justify-between gap-2 rounded-md bg-secondary-300 px-4 py-2"> <span> - <CareIcon icon="l-paperclip" className="mr-2" /> - {fileUpload.files?.[0]?.name} + {fileUpload.files?.map((file, index) => ( + <div key={index} className="flex items-center mb-2"> + <CareIcon icon="l-paperclip" className="mr-2" /> + {file.name} + </div> + ))} </span> </div> <TextFormField From f1364650a0daa2ecc97e020c236159508e258f09 Mon Sep 17 00:00:00 2001 From: Don Xavier <mail.donxavier@gmail.com> Date: Tue, 7 Jan 2025 12:32:57 +0530 Subject: [PATCH 02/26] Added jspdf to package.json --- package-lock.json | 146 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 1 + 2 files changed, 145 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index aae20921b8a..7d8532b6234 100644 --- a/package-lock.json +++ b/package-lock.json @@ -63,6 +63,7 @@ "i18next-browser-languagedetector": "^8.0.2", "i18next-http-backend": "^3.0.1", "input-otp": "^1.4.1", + "jspdf": "^2.5.2", "lodash-es": "^4.17.21", "lucide-react": "^0.469.0", "markdown-it": "^14.1.0", @@ -146,8 +147,8 @@ "node": ">=22.8.0" }, "optionalDependencies": { - "@esbuild/linux-arm64": "*", - "@esbuild/linux-x64": "*", + "@esbuild/linux-arm64": "latest", + "@esbuild/linux-x64": "latest", "@rollup/rollup-linux-arm64-gnu": "4.29.1", "@rollup/rollup-linux-x64-gnu": "4.29.1" } @@ -6729,6 +6730,13 @@ "devOptional": true, "license": "MIT" }, + "node_modules/@types/raf": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", + "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==", + "license": "MIT", + "optional": true + }, "node_modules/@types/react": { "version": "18.3.18", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", @@ -7613,6 +7621,18 @@ "node": ">= 4.0.0" } }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "license": "(MIT OR Apache-2.0)", + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, "node_modules/autoprefixer": { "version": "10.4.20", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", @@ -7938,6 +7958,18 @@ "browserslist": ">=4.0.0" } }, + "node_modules/btoa": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", + "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==", + "license": "(MIT OR Apache-2.0)", + "bin": { + "btoa": "bin/btoa.js" + }, + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -8107,6 +8139,33 @@ "node": ">=6" } }, + "node_modules/canvg": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.10.tgz", + "integrity": "sha512-qwR2FRNO9NlzTeKIPIKpnTY6fqwuYSequ8Ru8c0YkYU7U0oW+hLUvWadLvAu1Rl72OMNiFhoLu4f8eUjQ7l/+Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@types/raf": "^3.4.0", + "core-js": "^3.8.3", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.7", + "rgbcolor": "^1.0.1", + "stackblur-canvas": "^2.0.0", + "svg-pathdata": "^6.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/canvg/node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT", + "optional": true + }, "node_modules/caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -8546,6 +8605,18 @@ "toggle-selection": "^1.0.6" } }, + "node_modules/core-js": { + "version": "3.39.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz", + "integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/core-js-compat": { "version": "3.39.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz", @@ -10711,6 +10782,12 @@ "pend": "~1.2.0" } }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -12884,6 +12961,31 @@ "node": ">=0.10.0" } }, + "node_modules/jspdf": { + "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", + "btoa": "^1.2.1", + "fflate": "^0.8.1" + }, + "optionalDependencies": { + "canvg": "^3.0.6", + "core-js": "^3.6.0", + "dompurify": "^2.5.4", + "html2canvas": "^1.0.0-rc.5" + } + }, + "node_modules/jspdf/node_modules/dompurify": { + "version": "2.5.8", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.5.8.tgz", + "integrity": "sha512-o1vSNgrmYMQObbSSvF/1brBYEQPHhV1+gsmrusO7/GXtp1T9rCS8cXFqVxK/9crT1jA6Ccv+5MTSjBNqr7Sovw==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optional": true + }, "node_modules/jsprim": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", @@ -17003,6 +17105,16 @@ "dev": true, "license": "MIT" }, + "node_modules/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "license": "MIT", + "optional": true, + "dependencies": { + "performance-now": "^2.1.0" + } + }, "node_modules/raf-schd": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", @@ -18017,6 +18129,16 @@ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "license": "MIT" }, + "node_modules/rgbcolor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", + "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", + "license": "MIT OR SEE LICENSE IN FEEL-FREE.md", + "optional": true, + "engines": { + "node": ">= 0.8.15" + } + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -18842,6 +18964,16 @@ "node": ">=0.10.0" } }, + "node_modules/stackblur-canvas": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz", + "integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.14" + } + }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", @@ -19298,6 +19430,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svg-pathdata": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", + "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", diff --git a/package.json b/package.json index e66c7f8a6aa..9d18368f6a1 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,7 @@ "i18next-browser-languagedetector": "^8.0.2", "i18next-http-backend": "^3.0.1", "input-otp": "^1.4.1", + "jspdf": "^2.5.2", "lodash-es": "^4.17.21", "lucide-react": "^0.469.0", "markdown-it": "^14.1.0", From 21cd51d451bf0af1e437119c7ce60d664da4949c Mon Sep 17 00:00:00 2001 From: Don Xavier <mail.donxavier@gmail.com> Date: Tue, 7 Jan 2025 12:25:19 +0530 Subject: [PATCH 03/26] Implement multiple File uploading --- src/hooks/useFileUpload.tsx | 41 ++++++++++++++++++- .../Encounters/tabs/EncounterFilesTab.tsx | 9 +++- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/hooks/useFileUpload.tsx b/src/hooks/useFileUpload.tsx index d8aea1fa1b1..d570e164dbb 100644 --- a/src/hooks/useFileUpload.tsx +++ b/src/hooks/useFileUpload.tsx @@ -1,6 +1,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query"; import imageCompression from "browser-image-compression"; import { t } from "i18next"; +import jsPDF from "jspdf"; import { ChangeEvent, DetailedHTMLProps, @@ -97,6 +98,33 @@ export default function useFileUpload( const [files, setFiles] = useState<File[]>([]); const queryClient = useQueryClient(); + const generatePDF = async (files: File[]): Promise<File | null> => { + try { + const pdf = new jsPDF(); + for (const [index, file] of files.entries()) { + const imgData = await new Promise<string>((resolve, reject) => { + const reader = new FileReader(); + reader.onload = () => resolve(reader.result as string); + reader.onerror = () => reject("Error reading file"); + reader.readAsDataURL(file); + }); + + pdf.addImage(imgData, "JPEG", 10, 10, 190, 0); + if (index < files.length - 1) pdf.addPage(); + } + + const pdfBlob = pdf.output("blob"); + const pdfFile = new File([pdfBlob], "combined.pdf", { + type: "application/pdf", + }); + + console.log("Generated PDF file:", pdfFile); // Log the generated file + return pdfFile; + } catch (error) { + console.error("Error generating PDF:", error); + return null; + } + }; const onFileChange = (e: ChangeEvent<HTMLInputElement>): any => { if (!e.target.files?.length) { return; @@ -238,8 +266,19 @@ export default function useFileUpload( if (!validateFileUpload()) return; setProgress(0); + let filesToUpload = files; + + if (files.length > 1) { + const pdfFile = await generatePDF(files); + if (pdfFile) { + filesToUpload = [pdfFile]; + } else { + console.error("Failed to generate PDF from multiple files."); + return; + } + } - for (const [index, file] of files.entries()) { + for (const [index, file] of filesToUpload.entries()) { const filename = allowNameFallback && uploadFileNames[index] === "" && file ? file.name diff --git a/src/pages/Encounters/tabs/EncounterFilesTab.tsx b/src/pages/Encounters/tabs/EncounterFilesTab.tsx index 053637b75d8..8d979bc912e 100644 --- a/src/pages/Encounters/tabs/EncounterFilesTab.tsx +++ b/src/pages/Encounters/tabs/EncounterFilesTab.tsx @@ -136,6 +136,7 @@ export const EncounterFilesTab = (props: EncounterTabProps) => { ], allowNameFallback: false, onUpload: () => refetch(), + multiple: true, }); useEffect(() => { @@ -543,8 +544,12 @@ const FileUploadDialog = ({ </DialogHeader> <div className="mb-1 flex items-center justify-between gap-2 rounded-md bg-secondary-300 px-4 py-2"> <span> - <CareIcon icon="l-paperclip" className="mr-2" /> - {fileUpload.files?.[0]?.name} + {fileUpload.files?.map((file, index) => ( + <div key={index} className="flex items-center mb-2"> + <CareIcon icon="l-paperclip" className="mr-2" /> + {file.name} + </div> + ))} </span> </div> <TextFormField From a757ad57f797c20a5d64231903e0b9bf0ca85e38 Mon Sep 17 00:00:00 2001 From: Don Xavier <mail.donxavier@gmail.com> Date: Tue, 7 Jan 2025 12:32:57 +0530 Subject: [PATCH 04/26] Added jspdf to package.json --- package-lock.json | 148 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 3 +- 2 files changed, 147 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index edfd7250ef1..f5a9e517dc0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -62,7 +62,8 @@ "i18next": "^24.2.1", "i18next-browser-languagedetector": "^8.0.2", "i18next-http-backend": "^3.0.1", - "input-otp": "^1.4.2", + "input-otp": "^1.4.1", + "jspdf": "^2.5.2", "lodash-es": "^4.17.21", "lucide-react": "^0.469.0", "markdown-it": "^14.1.0", @@ -146,8 +147,8 @@ "node": ">=22.8.0" }, "optionalDependencies": { - "@esbuild/linux-arm64": "*", - "@esbuild/linux-x64": "*", + "@esbuild/linux-arm64": "latest", + "@esbuild/linux-x64": "latest", "@rollup/rollup-linux-arm64-gnu": "4.29.1", "@rollup/rollup-linux-x64-gnu": "4.29.1" } @@ -6729,6 +6730,13 @@ "devOptional": true, "license": "MIT" }, + "node_modules/@types/raf": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", + "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==", + "license": "MIT", + "optional": true + }, "node_modules/@types/react": { "version": "18.3.18", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", @@ -7613,6 +7621,18 @@ "node": ">= 4.0.0" } }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "license": "(MIT OR Apache-2.0)", + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, "node_modules/autoprefixer": { "version": "10.4.20", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", @@ -7938,6 +7958,18 @@ "browserslist": ">=4.0.0" } }, + "node_modules/btoa": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", + "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==", + "license": "(MIT OR Apache-2.0)", + "bin": { + "btoa": "bin/btoa.js" + }, + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -8107,6 +8139,33 @@ "node": ">=6" } }, + "node_modules/canvg": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.10.tgz", + "integrity": "sha512-qwR2FRNO9NlzTeKIPIKpnTY6fqwuYSequ8Ru8c0YkYU7U0oW+hLUvWadLvAu1Rl72OMNiFhoLu4f8eUjQ7l/+Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@types/raf": "^3.4.0", + "core-js": "^3.8.3", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.7", + "rgbcolor": "^1.0.1", + "stackblur-canvas": "^2.0.0", + "svg-pathdata": "^6.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/canvg/node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT", + "optional": true + }, "node_modules/caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -8546,6 +8605,18 @@ "toggle-selection": "^1.0.6" } }, + "node_modules/core-js": { + "version": "3.39.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz", + "integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/core-js-compat": { "version": "3.39.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz", @@ -10711,6 +10782,12 @@ "pend": "~1.2.0" } }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -12884,6 +12961,31 @@ "node": ">=0.10.0" } }, + "node_modules/jspdf": { + "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", + "btoa": "^1.2.1", + "fflate": "^0.8.1" + }, + "optionalDependencies": { + "canvg": "^3.0.6", + "core-js": "^3.6.0", + "dompurify": "^2.5.4", + "html2canvas": "^1.0.0-rc.5" + } + }, + "node_modules/jspdf/node_modules/dompurify": { + "version": "2.5.8", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.5.8.tgz", + "integrity": "sha512-o1vSNgrmYMQObbSSvF/1brBYEQPHhV1+gsmrusO7/GXtp1T9rCS8cXFqVxK/9crT1jA6Ccv+5MTSjBNqr7Sovw==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optional": true + }, "node_modules/jsprim": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", @@ -17003,6 +17105,16 @@ "dev": true, "license": "MIT" }, + "node_modules/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "license": "MIT", + "optional": true, + "dependencies": { + "performance-now": "^2.1.0" + } + }, "node_modules/raf-schd": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", @@ -18017,6 +18129,16 @@ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "license": "MIT" }, + "node_modules/rgbcolor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", + "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", + "license": "MIT OR SEE LICENSE IN FEEL-FREE.md", + "optional": true, + "engines": { + "node": ">= 0.8.15" + } + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -18842,6 +18964,16 @@ "node": ">=0.10.0" } }, + "node_modules/stackblur-canvas": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz", + "integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.14" + } + }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", @@ -19298,6 +19430,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svg-pathdata": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", + "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", diff --git a/package.json b/package.json index cd0f1948a36..3ae8a1761a9 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,8 @@ "i18next": "^24.2.1", "i18next-browser-languagedetector": "^8.0.2", "i18next-http-backend": "^3.0.1", - "input-otp": "^1.4.2", + "input-otp": "^1.4.1", + "jspdf": "^2.5.2", "lodash-es": "^4.17.21", "lucide-react": "^0.469.0", "markdown-it": "^14.1.0", From 1dbf18f075c8e2b85592e15d5b897f1cbaec0506 Mon Sep 17 00:00:00 2001 From: Don Xavier <mail.donxavier@gmail.com> Date: Sat, 11 Jan 2025 14:08:31 +0530 Subject: [PATCH 05/26] Add jspdf to package.json --- package-lock.json | 146 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 1 + 2 files changed, 145 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index edfd7250ef1..25b3b0c7e12 100644 --- a/package-lock.json +++ b/package-lock.json @@ -63,6 +63,7 @@ "i18next-browser-languagedetector": "^8.0.2", "i18next-http-backend": "^3.0.1", "input-otp": "^1.4.2", + "jspdf": "^2.5.2", "lodash-es": "^4.17.21", "lucide-react": "^0.469.0", "markdown-it": "^14.1.0", @@ -146,8 +147,8 @@ "node": ">=22.8.0" }, "optionalDependencies": { - "@esbuild/linux-arm64": "*", - "@esbuild/linux-x64": "*", + "@esbuild/linux-arm64": "latest", + "@esbuild/linux-x64": "latest", "@rollup/rollup-linux-arm64-gnu": "4.29.1", "@rollup/rollup-linux-x64-gnu": "4.29.1" } @@ -6729,6 +6730,13 @@ "devOptional": true, "license": "MIT" }, + "node_modules/@types/raf": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", + "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==", + "license": "MIT", + "optional": true + }, "node_modules/@types/react": { "version": "18.3.18", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", @@ -7613,6 +7621,18 @@ "node": ">= 4.0.0" } }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "license": "(MIT OR Apache-2.0)", + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, "node_modules/autoprefixer": { "version": "10.4.20", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", @@ -7938,6 +7958,18 @@ "browserslist": ">=4.0.0" } }, + "node_modules/btoa": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", + "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==", + "license": "(MIT OR Apache-2.0)", + "bin": { + "btoa": "bin/btoa.js" + }, + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -8107,6 +8139,33 @@ "node": ">=6" } }, + "node_modules/canvg": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.10.tgz", + "integrity": "sha512-qwR2FRNO9NlzTeKIPIKpnTY6fqwuYSequ8Ru8c0YkYU7U0oW+hLUvWadLvAu1Rl72OMNiFhoLu4f8eUjQ7l/+Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@types/raf": "^3.4.0", + "core-js": "^3.8.3", + "raf": "^3.4.1", + "regenerator-runtime": "^0.13.7", + "rgbcolor": "^1.0.1", + "stackblur-canvas": "^2.0.0", + "svg-pathdata": "^6.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/canvg/node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT", + "optional": true + }, "node_modules/caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -8546,6 +8605,18 @@ "toggle-selection": "^1.0.6" } }, + "node_modules/core-js": { + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.40.0.tgz", + "integrity": "sha512-7vsMc/Lty6AGnn7uFpYT56QesI5D2Y/UkgKounk87OP9Z2H9Z8kj6jzcSGAxFmUtDOS0ntK6lbQz+Nsa0Jj6mQ==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/core-js-compat": { "version": "3.39.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz", @@ -10711,6 +10782,12 @@ "pend": "~1.2.0" } }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "license": "MIT" + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -12884,6 +12961,31 @@ "node": ">=0.10.0" } }, + "node_modules/jspdf": { + "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", + "btoa": "^1.2.1", + "fflate": "^0.8.1" + }, + "optionalDependencies": { + "canvg": "^3.0.6", + "core-js": "^3.6.0", + "dompurify": "^2.5.4", + "html2canvas": "^1.0.0-rc.5" + } + }, + "node_modules/jspdf/node_modules/dompurify": { + "version": "2.5.8", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.5.8.tgz", + "integrity": "sha512-o1vSNgrmYMQObbSSvF/1brBYEQPHhV1+gsmrusO7/GXtp1T9rCS8cXFqVxK/9crT1jA6Ccv+5MTSjBNqr7Sovw==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optional": true + }, "node_modules/jsprim": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", @@ -17003,6 +17105,16 @@ "dev": true, "license": "MIT" }, + "node_modules/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "license": "MIT", + "optional": true, + "dependencies": { + "performance-now": "^2.1.0" + } + }, "node_modules/raf-schd": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", @@ -18017,6 +18129,16 @@ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "license": "MIT" }, + "node_modules/rgbcolor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", + "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", + "license": "MIT OR SEE LICENSE IN FEEL-FREE.md", + "optional": true, + "engines": { + "node": ">= 0.8.15" + } + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -18842,6 +18964,16 @@ "node": ">=0.10.0" } }, + "node_modules/stackblur-canvas": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz", + "integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.14" + } + }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", @@ -19298,6 +19430,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svg-pathdata": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", + "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", diff --git a/package.json b/package.json index cd0f1948a36..bfe88062249 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,7 @@ "i18next-browser-languagedetector": "^8.0.2", "i18next-http-backend": "^3.0.1", "input-otp": "^1.4.2", + "jspdf": "^2.5.2", "lodash-es": "^4.17.21", "lucide-react": "^0.469.0", "markdown-it": "^14.1.0", From ece5d709aca5783ad669ae064de7e10398c929ca Mon Sep 17 00:00:00 2001 From: Don Xavier <mail.donxavier@gmail.com> Date: Sat, 11 Jan 2025 14:13:11 +0530 Subject: [PATCH 06/26] resolve package-lock.json --- package-lock.json | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 10343c4edc1..b3e4b75b088 100644 --- a/package-lock.json +++ b/package-lock.json @@ -147,10 +147,8 @@ "node": ">=22.8.0" }, "optionalDependencies": { - "@esbuild/linux-arm64": "latest", - "@esbuild/linux-x64": "latest", - "@esbuild/linux-arm64": "latest", - "@esbuild/linux-x64": "latest", + "@esbuild/linux-arm64": "*", + "@esbuild/linux-x64": "*", "@rollup/rollup-linux-arm64-gnu": "4.29.1", "@rollup/rollup-linux-x64-gnu": "4.29.1" } From 8014bf81187e6904235ab339789737c3fb418841 Mon Sep 17 00:00:00 2001 From: Don Xavier <mail.donxavier@gmail.com> Date: Sat, 11 Jan 2025 15:28:15 +0530 Subject: [PATCH 07/26] Remove duplicates package-lock.json --- package-lock.json | 130 ---------------------------------------------- package.json | 2 +- 2 files changed, 1 insertion(+), 131 deletions(-) diff --git a/package-lock.json b/package-lock.json index 48d61f2960c..ecfddcb61af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,7 +38,6 @@ "@radix-ui/react-tabs": "^1.1.1", "@radix-ui/react-toast": "^1.2.4", "@radix-ui/react-tooltip": "^1.1.6", - "@rollup/rollup-linux-x64-gnu": "4.30.1", "@sentry/browser": "^8.48.0", "@tanstack/react-query": "^5.62.8", "@tanstack/react-query-devtools": "^5.63.0", @@ -6736,13 +6735,6 @@ "license": "MIT", "optional": true }, - "node_modules/@types/raf": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", - "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==", - "license": "MIT", - "optional": true - }, "node_modules/@types/react": { "version": "18.3.18", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", @@ -7639,18 +7631,6 @@ "node": ">= 4.5.0" } }, - "node_modules/atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "license": "(MIT OR Apache-2.0)", - "bin": { - "atob": "bin/atob.js" - }, - "engines": { - "node": ">= 4.5.0" - } - }, "node_modules/autoprefixer": { "version": "10.4.20", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", @@ -7988,18 +7968,6 @@ "node": ">= 0.4.0" } }, - "node_modules/btoa": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", - "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==", - "license": "(MIT OR Apache-2.0)", - "bin": { - "btoa": "bin/btoa.js" - }, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -8196,33 +8164,6 @@ "license": "MIT", "optional": true }, - "node_modules/canvg": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.10.tgz", - "integrity": "sha512-qwR2FRNO9NlzTeKIPIKpnTY6fqwuYSequ8Ru8c0YkYU7U0oW+hLUvWadLvAu1Rl72OMNiFhoLu4f8eUjQ7l/+Q==", - "license": "MIT", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.12.5", - "@types/raf": "^3.4.0", - "core-js": "^3.8.3", - "raf": "^3.4.1", - "regenerator-runtime": "^0.13.7", - "rgbcolor": "^1.0.1", - "stackblur-canvas": "^2.0.0", - "svg-pathdata": "^6.0.3" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/canvg/node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "license": "MIT", - "optional": true - }, "node_modules/caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -10845,12 +10786,6 @@ "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", "license": "MIT" }, - "node_modules/fflate": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", - "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", - "license": "MIT" - }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -13049,31 +12984,6 @@ "license": "(MPL-2.0 OR Apache-2.0)", "optional": true }, - "node_modules/jspdf": { - "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", - "btoa": "^1.2.1", - "fflate": "^0.8.1" - }, - "optionalDependencies": { - "canvg": "^3.0.6", - "core-js": "^3.6.0", - "dompurify": "^2.5.4", - "html2canvas": "^1.0.0-rc.5" - } - }, - "node_modules/jspdf/node_modules/dompurify": { - "version": "2.5.8", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.5.8.tgz", - "integrity": "sha512-o1vSNgrmYMQObbSSvF/1brBYEQPHhV1+gsmrusO7/GXtp1T9rCS8cXFqVxK/9crT1jA6Ccv+5MTSjBNqr7Sovw==", - "license": "(MPL-2.0 OR Apache-2.0)", - "optional": true - }, "node_modules/jsprim": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", @@ -17203,16 +17113,6 @@ "performance-now": "^2.1.0" } }, - "node_modules/raf": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", - "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", - "license": "MIT", - "optional": true, - "dependencies": { - "performance-now": "^2.1.0" - } - }, "node_modules/raf-schd": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", @@ -18237,16 +18137,6 @@ "node": ">= 0.8.15" } }, - "node_modules/rgbcolor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", - "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", - "license": "MIT OR SEE LICENSE IN FEEL-FREE.md", - "optional": true, - "engines": { - "node": ">= 0.8.15" - } - }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -19108,16 +18998,6 @@ "node": ">=0.1.14" } }, - "node_modules/stackblur-canvas": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz", - "integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.1.14" - } - }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", @@ -19584,16 +19464,6 @@ "node": ">=12.0.0" } }, - "node_modules/svg-pathdata": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", - "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", diff --git a/package.json b/package.json index f90ac36fb69..49648689311 100644 --- a/package.json +++ b/package.json @@ -213,4 +213,4 @@ "node": ">=22.8.0" }, "packageManager": "npm@10.9.0" -} \ No newline at end of file +} From 74186e60005341fe4eb08cc5015bac7a21f9d2c1 Mon Sep 17 00:00:00 2001 From: Don Xavier <mail.donxavier@gmail.com> Date: Sat, 11 Jan 2025 15:31:32 +0530 Subject: [PATCH 08/26] remove extra space package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 49648689311..f90ac36fb69 100644 --- a/package.json +++ b/package.json @@ -213,4 +213,4 @@ "node": ">=22.8.0" }, "packageManager": "npm@10.9.0" -} +} \ No newline at end of file From 18bb454bb1534f4524037d4177399e2a392422d4 Mon Sep 17 00:00:00 2001 From: Don Xavier <mail.donxavier@gmail.com> Date: Sat, 11 Jan 2025 15:33:02 +0530 Subject: [PATCH 09/26] package-lock.json mistake solved --- package-lock.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package-lock.json b/package-lock.json index ecfddcb61af..ee7896e955d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,6 +38,7 @@ "@radix-ui/react-tabs": "^1.1.1", "@radix-ui/react-toast": "^1.2.4", "@radix-ui/react-tooltip": "^1.1.6", + "@rollup/rollup-linux-x64-gnu": "4.30.1", "@sentry/browser": "^8.48.0", "@tanstack/react-query": "^5.62.8", "@tanstack/react-query-devtools": "^5.63.0", From 127acde0fcb1ca81eafd21df95199d0b351c7968 Mon Sep 17 00:00:00 2001 From: Don Xavier <mail.donxavier@gmail.com> Date: Thu, 16 Jan 2025 21:58:11 +0530 Subject: [PATCH 10/26] Add PDF combination feature to file upload --- public/locale/en.json | 1 + src/components/Files/FilesTab.tsx | 18 ++++++++++++++++++ src/hooks/useFileUpload.tsx | 13 ++++++++++++- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/public/locale/en.json b/public/locale/en.json index 65a010592db..589a52e412a 100644 --- a/public/locale/en.json +++ b/public/locale/en.json @@ -588,6 +588,7 @@ "close": "Close", "close_scanner": "Close Scanner", "collapse_sidebar": "Collapse Sidebar", + "combine_files_pdf": "Combine Files To PDF", "comment_added_successfully": "Comment added successfully", "comment_min_length": "Comment Should Contain At Least 1 Character", "comments": "Comments", diff --git a/src/components/Files/FilesTab.tsx b/src/components/Files/FilesTab.tsx index fadb4358883..485475ea403 100644 --- a/src/components/Files/FilesTab.tsx +++ b/src/components/Files/FilesTab.tsx @@ -66,6 +66,7 @@ export const FilesTab = (props: FilesTabProps) => { }); const { t } = useTranslation(); const [openUploadDialog, setOpenUploadDialog] = useState(false); + const [isPdf, setIsPdf] = useState(false); const [selectedAudioFile, setSelectedAudioFile] = useState<FileUploadModel | null>(null); const [openAudioPlayerDialog, setOpenAudioPlayerDialog] = useState(false); @@ -126,6 +127,7 @@ export const FilesTab = (props: FilesTabProps) => { const fileUpload = useFileUpload({ type: type, multiple: true, + CombineToPDF: isPdf, allowedExtensions: [ "jpg", "jpeg", @@ -360,6 +362,22 @@ export const FilesTab = (props: FilesTabProps) => { </label> {fileUpload.Input({ className: "hidden" })} </DropdownMenuItem> + <DropdownMenuItem + className="flex flex-row items-center" + onSelect={(e) => { + e.preventDefault(); + setIsPdf(true); + }} + > + <label + htmlFor="file_upload_patient" + className="flex flex-row items-center cursor-pointer text-primary-900 font-normal w-full" + > + <CareIcon icon="l-file-upload-alt" className="mr-1" /> + <span>{t("combine_files_pdf")}</span> + </label> + {fileUpload.Input({ className: "hidden" })} + </DropdownMenuItem> <DropdownMenuItem className="flex flex-row items-center text-primary-900"> <button onClick={() => fileUpload.handleCameraCapture()} diff --git a/src/hooks/useFileUpload.tsx b/src/hooks/useFileUpload.tsx index 804ef036100..c93d68a8832 100644 --- a/src/hooks/useFileUpload.tsx +++ b/src/hooks/useFileUpload.tsx @@ -29,6 +29,7 @@ export type FileUploadOptions = { multiple?: boolean; type: string; category?: FileCategory; + CombineToPDF?: boolean; onUpload?: (file: FileUploadModel) => void; // if allowed, will fallback to the name of the file if a seperate filename is not defined. allowNameFallback?: boolean; @@ -86,6 +87,7 @@ export default function useFileUpload( category = "unspecified", multiple, allowNameFallback = true, + CombineToPDF, } = options; const [uploadFileNames, setUploadFileNames] = useState<string[]>([]); @@ -281,7 +283,16 @@ export default function useFileUpload( setProgress(0); const errors: File[] = []; - for (const [index, file] of filesToUpload.entries()) { + if (CombineToPDF && files.length > 1) { + const pdfFile = await generatePDF(files); + if (pdfFile) { + files.splice(0, files.length, pdfFile); + } else { + console.error("Failed to generate PDF from multiple files."); + return; + } + } + for (const [index, file] of files.entries()) { const filename = allowNameFallback && uploadFileNames[index] === "" && file ? file.name From 14d397fd0aa8e490984559cd778b107b796ac76c Mon Sep 17 00:00:00 2001 From: Don Xavier <mail.donxavier@gmail.com> Date: Sat, 18 Jan 2025 15:03:46 +0530 Subject: [PATCH 11/26] Add error handling for PDF generation + Reset isPdf wherever necessary --- public/locale/en.json | 1 + src/components/Files/FilesTab.tsx | 59 +++++++++++++++++-------------- src/hooks/useFileUpload.tsx | 5 ++- 3 files changed, 38 insertions(+), 27 deletions(-) diff --git a/public/locale/en.json b/public/locale/en.json index 589a52e412a..f4d2fb0d4ee 100644 --- a/public/locale/en.json +++ b/public/locale/en.json @@ -1025,6 +1025,7 @@ "file_error__file_name": "Please give a name for all files!", "file_error__file_size": "Maximum size of files is 100 MB", "file_error__file_type": "Invalid file type \".{{extension}}\" Allowed types: {{allowedExtensions}}", + "file_error__generate_pdf": "Failed to generate PDF", "file_error__mark_complete_failed": "Error while marking file upload as complete", "file_error__network": "Error Uploading File: Network Error", "file_error__single_file_name": "Please give a name for the file", diff --git a/src/components/Files/FilesTab.tsx b/src/components/Files/FilesTab.tsx index 485475ea403..e0340e160eb 100644 --- a/src/components/Files/FilesTab.tsx +++ b/src/components/Files/FilesTab.tsx @@ -128,33 +128,37 @@ export const FilesTab = (props: FilesTabProps) => { type: type, multiple: true, CombineToPDF: isPdf, - allowedExtensions: [ - "jpg", - "jpeg", - "png", - "gif", - "bmp", - "tiff", - "mp4", - "mov", - "avi", - "wmv", - "mp3", - "wav", - "ogg", - "txt", - "csv", - "rtf", - "doc", - "odt", - "pdf", - "xls", - "xlsx", - "ods", - "pdf", - ], + allowedExtensions: isPdf + ? ["jpg", "jpeg", "png"] + : [ + "jpg", + "jpeg", + "png", + "gif", + "bmp", + "tiff", + "mp4", + "mov", + "avi", + "wmv", + "mp3", + "wav", + "ogg", + "txt", + "csv", + "rtf", + "doc", + "odt", + "pdf", + "xls", + "xlsx", + "ods", + ], allowNameFallback: false, - onUpload: () => refetch(), + onUpload: () => { + refetch(); + setIsPdf(false); + }, }); useEffect(() => { @@ -162,12 +166,14 @@ export const FilesTab = (props: FilesTabProps) => { setOpenUploadDialog(true); } else { setOpenUploadDialog(false); + setIsPdf(false); } }, [fileUpload.files]); useEffect(() => { if (!openUploadDialog) { fileUpload.clearFiles(); + setIsPdf(false); } }, [openUploadDialog]); @@ -350,6 +356,7 @@ export const FilesTab = (props: FilesTabProps) => { <DropdownMenuItem className="flex flex-row items-center" onSelect={(e) => { + setIsPdf(false); e.preventDefault(); }} > diff --git a/src/hooks/useFileUpload.tsx b/src/hooks/useFileUpload.tsx index c93d68a8832..52c7b97831e 100644 --- a/src/hooks/useFileUpload.tsx +++ b/src/hooks/useFileUpload.tsx @@ -124,6 +124,8 @@ export default function useFileUpload( return pdfFile; } catch (error) { console.error("Error generating PDF:", error); + toast.error(t("file_error__generate_pdf")); + setError(t("file_error__generate_pdf")); return null; } }; @@ -288,7 +290,8 @@ export default function useFileUpload( if (pdfFile) { files.splice(0, files.length, pdfFile); } else { - console.error("Failed to generate PDF from multiple files."); + clearFiles(); + setError(t("file_error__generate_pdf")); return; } } From f1c77d2a1aea90087fe3beffe5f88e671d37b0c9 Mon Sep 17 00:00:00 2001 From: Don Xavier <mail.donxavier@gmail.com> Date: Tue, 21 Jan 2025 16:23:17 +0530 Subject: [PATCH 12/26] Add checkbox For image to pdf conversion. --- src/components/Files/FilesTab.tsx | 92 +++++++++++++++---------------- src/hooks/useFileUpload.tsx | 17 ++++-- 2 files changed, 56 insertions(+), 53 deletions(-) diff --git a/src/components/Files/FilesTab.tsx b/src/components/Files/FilesTab.tsx index e0340e160eb..2e582b121ad 100644 --- a/src/components/Files/FilesTab.tsx +++ b/src/components/Files/FilesTab.tsx @@ -66,7 +66,6 @@ export const FilesTab = (props: FilesTabProps) => { }); const { t } = useTranslation(); const [openUploadDialog, setOpenUploadDialog] = useState(false); - const [isPdf, setIsPdf] = useState(false); const [selectedAudioFile, setSelectedAudioFile] = useState<FileUploadModel | null>(null); const [openAudioPlayerDialog, setOpenAudioPlayerDialog] = useState(false); @@ -127,37 +126,34 @@ export const FilesTab = (props: FilesTabProps) => { const fileUpload = useFileUpload({ type: type, multiple: true, - CombineToPDF: isPdf, - allowedExtensions: isPdf - ? ["jpg", "jpeg", "png"] - : [ - "jpg", - "jpeg", - "png", - "gif", - "bmp", - "tiff", - "mp4", - "mov", - "avi", - "wmv", - "mp3", - "wav", - "ogg", - "txt", - "csv", - "rtf", - "doc", - "odt", - "pdf", - "xls", - "xlsx", - "ods", - ], + allowedExtensions: [ + "jpg", + "jpeg", + "png", + "gif", + "bmp", + "tiff", + "mp4", + "mov", + "avi", + "wmv", + "mp3", + "wav", + "ogg", + "txt", + "csv", + "rtf", + "doc", + "odt", + "pdf", + "xls", + "xlsx", + "ods", + "pdf", + ], allowNameFallback: false, onUpload: () => { refetch(); - setIsPdf(false); }, }); @@ -166,14 +162,12 @@ export const FilesTab = (props: FilesTabProps) => { setOpenUploadDialog(true); } else { setOpenUploadDialog(false); - setIsPdf(false); } }, [fileUpload.files]); useEffect(() => { if (!openUploadDialog) { fileUpload.clearFiles(); - setIsPdf(false); } }, [openUploadDialog]); @@ -356,7 +350,6 @@ export const FilesTab = (props: FilesTabProps) => { <DropdownMenuItem className="flex flex-row items-center" onSelect={(e) => { - setIsPdf(false); e.preventDefault(); }} > @@ -369,22 +362,6 @@ export const FilesTab = (props: FilesTabProps) => { </label> {fileUpload.Input({ className: "hidden" })} </DropdownMenuItem> - <DropdownMenuItem - className="flex flex-row items-center" - onSelect={(e) => { - e.preventDefault(); - setIsPdf(true); - }} - > - <label - htmlFor="file_upload_patient" - className="flex flex-row items-center cursor-pointer text-primary-900 font-normal w-full" - > - <CareIcon icon="l-file-upload-alt" className="mr-1" /> - <span>{t("combine_files_pdf")}</span> - </label> - {fileUpload.Input({ className: "hidden" })} - </DropdownMenuItem> <DropdownMenuItem className="flex flex-row items-center text-primary-900"> <button onClick={() => fileUpload.handleCameraCapture()} @@ -594,6 +571,7 @@ const FileUploadDialog = ({ associatingId: string; }) => { const { t } = useTranslation(); + const [isPdf, setIsPdf] = useState(false); return ( <Dialog open={open} @@ -642,10 +620,26 @@ const FileUploadDialog = ({ </div> ))} </div> + {fileUpload.files.length > 1 && ( + <div className="flex items-center gap-2 mt-4"> + <input + type="checkbox" + id="combine_as_pdf" + checked={isPdf} + onChange={(e) => setIsPdf(e.target.checked)} + disabled={fileUpload.uploading} + className="cursor-pointer" + /> + <label htmlFor="file_upload_patient"> + {t("combine_files_pdf")} + </label> + </div> + )} + <div className="flex items-center gap-2 mt-4"> <Button variant="outline_primary" - onClick={() => fileUpload.handleFileUpload(associatingId)} + onClick={() => fileUpload.handleFileUpload(associatingId, isPdf)} disabled={fileUpload.uploading} className="w-full" id="upload_file_button" diff --git a/src/hooks/useFileUpload.tsx b/src/hooks/useFileUpload.tsx index 52c7b97831e..7745784f591 100644 --- a/src/hooks/useFileUpload.tsx +++ b/src/hooks/useFileUpload.tsx @@ -29,7 +29,6 @@ export type FileUploadOptions = { multiple?: boolean; type: string; category?: FileCategory; - CombineToPDF?: boolean; onUpload?: (file: FileUploadModel) => void; // if allowed, will fallback to the name of the file if a seperate filename is not defined. allowNameFallback?: boolean; @@ -54,7 +53,10 @@ export type FileUploadReturn = { validateFiles: () => boolean; handleCameraCapture: () => void; handleAudioCapture: () => void; - handleFileUpload: (associating_id: string) => Promise<void>; + handleFileUpload: ( + associating_id: string, + CombineToPDF?: boolean, + ) => Promise<void>; Dialogues: JSX.Element; Input: (_: FileInputProps) => JSX.Element; fileNames: string[]; @@ -87,7 +89,6 @@ export default function useFileUpload( category = "unspecified", multiple, allowNameFallback = true, - CombineToPDF, } = options; const [uploadFileNames, setUploadFileNames] = useState<string[]>([]); @@ -104,6 +105,11 @@ export default function useFileUpload( try { const pdf = new jsPDF(); for (const [index, file] of files.entries()) { + if (!file.type.startsWith("image/")) { + console.error(`Unsupported file type: ${file.name}`); + toast.error(t("file_error__file_type")); + return null; + } const imgData = await new Promise<string>((resolve, reject) => { const reader = new FileReader(); reader.onload = () => resolve(reader.result as string); @@ -279,7 +285,10 @@ export default function useFileUpload( })(body), }); - const handleUpload = async (associating_id: string) => { + const handleUpload = async ( + associating_id: string, + CombineToPDF?: boolean, + ) => { if (!validateFileUpload()) return; setProgress(0); From 677fd46dcc3dd0c713915207dbddb0bcf3f2b795 Mon Sep 17 00:00:00 2001 From: Don Xavier <mail.donxavier@gmail.com> Date: Wed, 22 Jan 2025 18:22:05 +0530 Subject: [PATCH 13/26] Indicate file conversion progress,replace checkbox shadcn --- public/locale/en.json | 2 ++ src/components/Files/FilesTab.tsx | 10 +++++----- src/hooks/useFileUpload.tsx | 23 +++++++++++------------ 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/public/locale/en.json b/public/locale/en.json index f4d2fb0d4ee..61c14aa37bb 100644 --- a/public/locale/en.json +++ b/public/locale/en.json @@ -1017,6 +1017,8 @@ "fetching": "Fetching", "field_required": "This field is required", "file_archived_successfully": "File archived successfully", + "file_conversion_in_progress": "File conversion in progress", + "file_conversion_success": "File conversion successful", "file_download_completed": "File download completed", "file_download_failed": "Failed to download file", "file_download_started": "Downloading file...", diff --git a/src/components/Files/FilesTab.tsx b/src/components/Files/FilesTab.tsx index 2e582b121ad..ddda3451747 100644 --- a/src/components/Files/FilesTab.tsx +++ b/src/components/Files/FilesTab.tsx @@ -7,6 +7,7 @@ import CareIcon, { IconName } from "@/CAREUI/icons/CareIcon"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; +import { Checkbox } from "@/components/ui/checkbox"; import { Dialog, DialogContent, @@ -622,15 +623,14 @@ const FileUploadDialog = ({ </div> {fileUpload.files.length > 1 && ( <div className="flex items-center gap-2 mt-4"> - <input - type="checkbox" - id="combine_as_pdf" + <Checkbox + id="file_upload_patient" checked={isPdf} - onChange={(e) => setIsPdf(e.target.checked)} + onCheckedChange={(checked) => setIsPdf(!!checked)} disabled={fileUpload.uploading} className="cursor-pointer" /> - <label htmlFor="file_upload_patient"> + <label htmlFor="file_upload_patient" className="cursor-pointer"> {t("combine_files_pdf")} </label> </div> diff --git a/src/hooks/useFileUpload.tsx b/src/hooks/useFileUpload.tsx index 7745784f591..210b7902760 100644 --- a/src/hooks/useFileUpload.tsx +++ b/src/hooks/useFileUpload.tsx @@ -103,35 +103,34 @@ export default function useFileUpload( const generatePDF = async (files: File[]): Promise<File | null> => { try { + toast.info(t("file_conversion_in_progress")); const pdf = new jsPDF(); + const totalFiles = files.length; + for (const [index, file] of files.entries()) { if (!file.type.startsWith("image/")) { - console.error(`Unsupported file type: ${file.name}`); toast.error(t("file_error__file_type")); + setProgress(0); return null; } - const imgData = await new Promise<string>((resolve, reject) => { - const reader = new FileReader(); - reader.onload = () => resolve(reader.result as string); - reader.onerror = () => reject("Error reading file"); - reader.readAsDataURL(file); - }); - + const imgData = URL.createObjectURL(file); pdf.addImage(imgData, "JPEG", 10, 10, 190, 0); + URL.revokeObjectURL(imgData); if (index < files.length - 1) pdf.addPage(); + const progress = Math.round(((index + 1) / totalFiles) * 100); + setProgress(progress); } - const pdfBlob = pdf.output("blob"); const pdfFile = new File([pdfBlob], "combined.pdf", { type: "application/pdf", }); - - console.log("Generated PDF file:", pdfFile); // Log the generated file + setProgress(0); + toast.success(t("file_conversion_success")); return pdfFile; } catch (error) { - console.error("Error generating PDF:", error); toast.error(t("file_error__generate_pdf")); setError(t("file_error__generate_pdf")); + setProgress(0); return null; } }; From 66ddf425eb97634593ba3634bb4580a9ae9303bf Mon Sep 17 00:00:00 2001 From: Don Xavier <mail.donxavier@gmail.com> Date: Fri, 24 Jan 2025 19:19:54 +0530 Subject: [PATCH 14/26] Seperate Textbox for CombineToPdf and replace textbox with input shadcn --- public/locale/en.json | 1 + src/components/Files/FilesTab.tsx | 119 ++++++++++++++++++++++-------- 2 files changed, 88 insertions(+), 32 deletions(-) diff --git a/public/locale/en.json b/public/locale/en.json index 61c14aa37bb..9b7868018dc 100644 --- a/public/locale/en.json +++ b/public/locale/en.json @@ -947,6 +947,7 @@ "enter_aadhaar_otp": "Enter OTP sent to the registered mobile with Aadhaar", "enter_abha_address": "Enter ABHA Address", "enter_any_id": "Enter any ID linked with your ABHA number", + "enter_combined_file_name": "Enter Combined File Name", "enter_file_name": "Enter File Name", "enter_message": "Start typing...", "enter_mobile_number": "Enter Mobile Number", diff --git a/src/components/Files/FilesTab.tsx b/src/components/Files/FilesTab.tsx index ddda3451747..d784e96591a 100644 --- a/src/components/Files/FilesTab.tsx +++ b/src/components/Files/FilesTab.tsx @@ -20,6 +20,7 @@ import { DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; +import { Input } from "@/components/ui/input"; import { Progress } from "@/components/ui/progress"; import { Table, @@ -39,7 +40,6 @@ import { import AudioPlayer from "@/components/Common/AudioPlayer"; import Loading from "@/components/Common/Loading"; -import TextFormField from "@/components/Form/FormFields/TextFormField"; import { FileUploadModel } from "@/components/Patient/models"; import useFileManager from "@/hooks/useFileManager"; @@ -571,12 +571,19 @@ const FileUploadDialog = ({ fileUpload: FileUploadReturn; associatingId: string; }) => { + const handleDialogClose = (open: boolean) => { + if (!open) { + setIsPdf(false); + fileUpload.clearFiles(); + } + onOpenChange(open); + }; const { t } = useTranslation(); const [isPdf, setIsPdf] = useState(false); return ( <Dialog open={open} - onOpenChange={onOpenChange} + onOpenChange={handleDialogClose} aria-labelledby="file-upload-dialog" > <DialogContent @@ -589,44 +596,92 @@ const FileUploadDialog = ({ </DialogTitle> </DialogHeader> <div className="space-y-4"> - {fileUpload.files.map((file, index) => ( - <div key={index} className="space-y-2"> - <div className="flex items-center justify-between gap-2 rounded-md bg-secondary-300 px-4 py-2"> - <span className="flex items-center truncate"> - <CareIcon icon="l-paperclip" className="mr-2 shrink-0" /> - <span className="truncate">{file.name}</span> - </span> - <Button - variant="ghost" - size="icon" - onClick={() => fileUpload.removeFile(index)} - disabled={fileUpload.uploading} + {isPdf ? ( + <> + <div className="space-y-2"> + <ul className="list-disc list-inside space-y-1"> + {fileUpload.files.map((file, index) => ( + <li key={index} className="truncate"> + {file.name} + </li> + ))} + </ul> + </div> + <div> + <label + htmlFor="upload-file-name-0" + className="block text-sm font-medium text-gray-700" > - <CareIcon icon="l-times" /> - </Button> + {t("enter_combined_file_name")} + </label> + <Input + name="combined_file_name" + type="text" + id="combined-file-name" + required + value={fileUpload.fileNames[0] || ""} + disabled={fileUpload.uploading} + onChange={(e) => fileUpload.setFileName(e.target.value)} + /> + {fileUpload.error && ( + <p className="mt-2 text-sm text-red-600"> + {fileUpload.error} + </p> + )} </div> - <TextFormField - name={`file_name_${index}`} - type="text" - label={t("enter_file_name")} - id={`upload-file-name-${index}`} - required - value={fileUpload.fileNames[index] || ""} - disabled={fileUpload.uploading} - onChange={(e) => fileUpload.setFileName(e.value, index)} - error={ - index === 0 && fileUpload.error ? fileUpload.error : undefined - } - /> - </div> - ))} + </> + ) : ( + fileUpload.files.map((file, index) => ( + <div key={index} className="space-y-2"> + <div className="flex items-center justify-between gap-2 rounded-md bg-secondary-300 px-4 py-2"> + <span className="flex items-center truncate"> + <CareIcon icon="l-paperclip" className="mr-2 shrink-0" /> + <span className="truncate">{file.name}</span> + </span> + <Button + variant="ghost" + size="icon" + onClick={() => fileUpload.removeFile(index)} + disabled={fileUpload.uploading} + > + <CareIcon icon="l-times" /> + </Button> + </div> + <div> + <label + htmlFor={`upload-file-name-${index}`} + className="block text-sm font-medium text-gray-700" + > + {t("enter_file_name")} + </label> + + <Input + name={`file_name_${index}`} + type="text" + id={`upload-file-name-${index}`} + required + value={fileUpload.fileNames[index] || ""} + disabled={fileUpload.uploading} + onChange={(e) => + fileUpload.setFileName(e.target.value, index) + } + /> + {index === 0 && fileUpload.error && ( + <p className="mt-2 text-sm text-red-600"> + {fileUpload.error} + </p> + )} + </div> + </div> + )) + )} </div> {fileUpload.files.length > 1 && ( <div className="flex items-center gap-2 mt-4"> <Checkbox id="file_upload_patient" checked={isPdf} - onCheckedChange={(checked) => setIsPdf(!!checked)} + onCheckedChange={(checked: boolean) => setIsPdf(checked)} disabled={fileUpload.uploading} className="cursor-pointer" /> From cc6e0d8f7d9e3aba4e23c3f78db6e90df7d363e0 Mon Sep 17 00:00:00 2001 From: Don Xavier <mail.donxavier@gmail.com> Date: Fri, 24 Jan 2025 19:35:49 +0530 Subject: [PATCH 15/26] Add missing jspdf dependency --- package-lock.json | 54 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/package-lock.json b/package-lock.json index 72f0c2975ff..a8b104d4a39 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7807,6 +7807,16 @@ "zxing-wasm": "1.3.4" } }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -8747,6 +8757,16 @@ "tiny-invariant": "^1.0.6" } }, + "node_modules/css-line-break": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", + "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", + "license": "MIT", + "optional": true, + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -11788,6 +11808,20 @@ "integrity": "sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA==", "license": "MIT" }, + "node_modules/html2canvas": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", + "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", + "license": "MIT", + "optional": true, + "dependencies": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/http-proxy-agent": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", @@ -19749,6 +19783,16 @@ "devOptional": true, "license": "MIT" }, + "node_modules/text-segmentation": { + "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==", + "license": "MIT", + "optional": true, + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -20754,6 +20798,16 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/utrie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", + "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", + "license": "MIT", + "optional": true, + "dependencies": { + "base64-arraybuffer": "^1.0.2" + } + }, "node_modules/uuid": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.3.tgz", From eb5b3c2fc3fc8b962206b10da6635c35949c3f8f Mon Sep 17 00:00:00 2001 From: Don Xavier <mail.donxavier@gmail.com> Date: Fri, 24 Jan 2025 19:48:18 +0530 Subject: [PATCH 16/26] Use Shadcn Label instead or normal label tag --- src/components/Files/FilesTab.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/Files/FilesTab.tsx b/src/components/Files/FilesTab.tsx index cb6bdb2e21c..b4c1ea3f7fc 100644 --- a/src/components/Files/FilesTab.tsx +++ b/src/components/Files/FilesTab.tsx @@ -355,13 +355,13 @@ export const FilesTab = (props: FilesTabProps) => { e.preventDefault(); }} > - <label + <Label htmlFor="file_upload_patient" className="flex flex-row items-center cursor-pointer text-primary-900 font-normal w-full" > <CareIcon icon="l-file-upload-alt" className="mr-1" /> <span>{t("choose_file")}</span> - </label> + </Label> {fileUpload.Input({ className: "hidden" })} </DropdownMenuItem> <DropdownMenuItem className="flex flex-row items-center text-primary-900"> @@ -609,12 +609,12 @@ const FileUploadDialog = ({ </ul> </div> <div> - <label + <Label htmlFor="upload-file-name-0" className="block text-sm font-medium text-gray-700" > {t("enter_combined_file_name")} - </label> + </Label> <Input name="combined_file_name" type="text" @@ -649,12 +649,12 @@ const FileUploadDialog = ({ </Button> </div> <div> - <label + <Label htmlFor={`upload-file-name-${index}`} className="block text-sm font-medium text-gray-700" > {t("enter_file_name")} - </label> + </Label> <Input name={`file_name_${index}`} @@ -686,9 +686,9 @@ const FileUploadDialog = ({ disabled={fileUpload.uploading} className="cursor-pointer" /> - <label htmlFor="file_upload_patient" className="cursor-pointer"> + <Label htmlFor="file_upload_patient" className="cursor-pointer"> {t("combine_files_pdf")} - </label> + </Label> </div> )} From e59ef7890b6be3f25b8fcc94d7f30c29e6be673b Mon Sep 17 00:00:00 2001 From: Don Xavier <mail.donxavier@gmail.com> Date: Sat, 25 Jan 2025 14:06:08 +0530 Subject: [PATCH 17/26] fix: rename error variable in useFileUpload hook for clarity --- src/hooks/useFileUpload.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/useFileUpload.tsx b/src/hooks/useFileUpload.tsx index fa78835daa7..6b71d2fb42b 100644 --- a/src/hooks/useFileUpload.tsx +++ b/src/hooks/useFileUpload.tsx @@ -126,7 +126,7 @@ export default function useFileUpload( setProgress(0); toast.success(t("file_conversion_success")); return pdfFile; - } catch (error) { + } catch (error_) { toast.error(t("file_error__generate_pdf")); setError(t("file_error__generate_pdf")); setProgress(0); From 84d9bd7649d6c2a088b714b4aeb8497f46be636a Mon Sep 17 00:00:00 2001 From: Don Xavier <mail.donxavier@gmail.com> Date: Sat, 25 Jan 2025 14:45:19 +0530 Subject: [PATCH 18/26] Fix Deploy preview failure --- src/hooks/useFileUpload.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hooks/useFileUpload.tsx b/src/hooks/useFileUpload.tsx index 6b71d2fb42b..8c354cfb800 100644 --- a/src/hooks/useFileUpload.tsx +++ b/src/hooks/useFileUpload.tsx @@ -126,9 +126,9 @@ export default function useFileUpload( setProgress(0); toast.success(t("file_conversion_success")); return pdfFile; - } catch (error_) { + } catch (error) { toast.error(t("file_error__generate_pdf")); - setError(t("file_error__generate_pdf")); + setError(t("file_error__generate_pdf", { error: String(error) })); setProgress(0); return null; } From 2bc75745c720f4ef88b5fe07c266f9d4007d1f1f Mon Sep 17 00:00:00 2001 From: Don Xavier <mail.donxavier@gmail.com> Date: Mon, 27 Jan 2025 13:21:47 +0530 Subject: [PATCH 19/26] Add title for tooltip . Revert Changes --- public/locale/en.json | 1 - src/components/Files/FilesTab.tsx | 8 ++++---- src/hooks/useFileUpload.tsx | 14 ++++++-------- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/public/locale/en.json b/public/locale/en.json index d03dc3477ea..ed85e87caef 100644 --- a/public/locale/en.json +++ b/public/locale/en.json @@ -971,7 +971,6 @@ "enter_aadhaar_otp": "Enter OTP sent to the registered mobile with Aadhaar", "enter_abha_address": "Enter ABHA Address", "enter_any_id": "Enter any ID linked with your ABHA number", - "enter_combined_file_name": "Enter Combined File Name", "enter_dosage_instructions": "Enter Dosage Instructions", "enter_file_name": "Enter File Name", "enter_message": "Start typing...", diff --git a/src/components/Files/FilesTab.tsx b/src/components/Files/FilesTab.tsx index b4c1ea3f7fc..4d41cedf66c 100644 --- a/src/components/Files/FilesTab.tsx +++ b/src/components/Files/FilesTab.tsx @@ -602,7 +602,7 @@ const FileUploadDialog = ({ <div className="space-y-2"> <ul className="list-disc list-inside space-y-1"> {fileUpload.files.map((file, index) => ( - <li key={index} className="truncate"> + <li key={index} className="truncate" title={file.name}> {file.name} </li> ))} @@ -613,12 +613,12 @@ const FileUploadDialog = ({ htmlFor="upload-file-name-0" className="block text-sm font-medium text-gray-700" > - {t("enter_combined_file_name")} + {t("enter_file_name")} </Label> <Input - name="combined_file_name" + name="file_name_0" type="text" - id="combined-file-name" + id="upload-file-name-0" required value={fileUpload.fileNames[0] || ""} disabled={fileUpload.uploading} diff --git a/src/hooks/useFileUpload.tsx b/src/hooks/useFileUpload.tsx index 8c354cfb800..c7780e3247e 100644 --- a/src/hooks/useFileUpload.tsx +++ b/src/hooks/useFileUpload.tsx @@ -54,7 +54,7 @@ export type FileUploadReturn = { handleAudioCapture: () => void; handleFileUpload: ( associating_id: string, - CombineToPDF?: boolean, + combineToPDF?: boolean, ) => Promise<void>; Dialogues: JSX.Element; Input: (_: FileInputProps) => JSX.Element; @@ -107,11 +107,6 @@ export default function useFileUpload( const totalFiles = files.length; for (const [index, file] of files.entries()) { - if (!file.type.startsWith("image/")) { - toast.error(t("file_error__file_type")); - setProgress(0); - return null; - } const imgData = URL.createObjectURL(file); pdf.addImage(imgData, "JPEG", 10, 10, 190, 0); URL.revokeObjectURL(imgData); @@ -285,14 +280,17 @@ export default function useFileUpload( const handleUpload = async ( associating_id: string, - CombineToPDF?: boolean, + combineToPDF?: boolean, ) => { + if (combineToPDF && "allowedExtensions" in options) { + options.allowedExtensions = ["jpg", "png", "jpeg"]; + } if (!validateFileUpload()) return; setProgress(0); const errors: File[] = []; - if (CombineToPDF && files.length > 1) { + if (combineToPDF && files.length > 1) { const pdfFile = await generatePDF(files); if (pdfFile) { files.splice(0, files.length, pdfFile); From 4534c6f8dd701e8ba8427cc5e1e3579c76dda292 Mon Sep 17 00:00:00 2001 From: Don Xavier <mail.donxavier@gmail.com> Date: Wed, 29 Jan 2025 18:24:51 +0530 Subject: [PATCH 20/26] Fix long filename width issue; add paperclip + bg. --- src/components/Files/FilesTab.tsx | 46 +++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/src/components/Files/FilesTab.tsx b/src/components/Files/FilesTab.tsx index 4d41cedf66c..27779acec8c 100644 --- a/src/components/Files/FilesTab.tsx +++ b/src/components/Files/FilesTab.tsx @@ -575,7 +575,6 @@ const FileUploadDialog = ({ const handleDialogClose = (open: boolean) => { if (!open) { setIsPdf(false); - fileUpload.clearFiles(); } onOpenChange(open); }; @@ -599,15 +598,31 @@ const FileUploadDialog = ({ <div className="space-y-4"> {isPdf ? ( <> - <div className="space-y-2"> - <ul className="list-disc list-inside space-y-1"> - {fileUpload.files.map((file, index) => ( - <li key={index} className="truncate" title={file.name}> - {file.name} - </li> - ))} - </ul> - </div> + {fileUpload.files.map((file, index) => ( + <div key={index} className="space-y-2"> + <div className="flex items-center justify-between gap-2 rounded-md bg-secondary-300 px-4 py-2"> + <span + className="flex items-center truncate" + title={file.name} + > + <CareIcon icon="l-paperclip" className="mr-2 shrink-0" /> + <span className="truncate"> + {file.name.length > 30 + ? `${file.name.substring(0, 20)}...` + : file.name} + </span> + </span> + <Button + variant="ghost" + size="icon" + onClick={() => fileUpload.removeFile(index)} + disabled={fileUpload.uploading} + > + <CareIcon icon="l-times" /> + </Button> + </div> + </div> + ))} <div> <Label htmlFor="upload-file-name-0" @@ -635,9 +650,16 @@ const FileUploadDialog = ({ fileUpload.files.map((file, index) => ( <div key={index} className="space-y-2"> <div className="flex items-center justify-between gap-2 rounded-md bg-secondary-300 px-4 py-2"> - <span className="flex items-center truncate"> + <span + className="flex items-center truncate" + title={file.name} + > <CareIcon icon="l-paperclip" className="mr-2 shrink-0" /> - <span className="truncate">{file.name}</span> + <span className="truncate"> + {file.name.length > 30 + ? `${file.name.substring(0, 20)}...` + : file.name} + </span> </span> <Button variant="ghost" From 6fff5bb2e4e62b631402c013c2291225a114b8d4 Mon Sep 17 00:00:00 2001 From: Don Xavier <mail.donxavier@gmail.com> Date: Sat, 1 Feb 2025 17:47:46 +0530 Subject: [PATCH 21/26] Enhance file upload dialog and validation logic for improved user experience --- src/components/Files/FilesTab.tsx | 12 +++++++----- src/hooks/useFileUpload.tsx | 32 ++++++++++++++++++++----------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/components/Files/FilesTab.tsx b/src/components/Files/FilesTab.tsx index a4a118d8792..a694bd7d6f6 100644 --- a/src/components/Files/FilesTab.tsx +++ b/src/components/Files/FilesTab.tsx @@ -587,7 +587,7 @@ const FileUploadDialog = ({ aria-labelledby="file-upload-dialog" > <DialogContent - className="mb-8 rounded-lg p-5 max-w-fit" + className="mb-8 rounded-lg p-5 max-w-fit md:max-w-[30rem]" aria-describedby="file-upload" > <DialogHeader> @@ -607,8 +607,8 @@ const FileUploadDialog = ({ > <CareIcon icon="l-paperclip" className="mr-2 shrink-0" /> <span className="truncate"> - {file.name.length > 30 - ? `${file.name.substring(0, 20)}...` + {file.name.length > 40 + ? `${file.name.substring(0, 30)}...` : file.name} </span> </span> @@ -638,6 +638,7 @@ const FileUploadDialog = ({ value={fileUpload.fileNames[0] || ""} disabled={fileUpload.uploading} onChange={(e) => fileUpload.setFileName(e.target.value)} + className="ml-0.5 mb-1" /> {fileUpload.error && ( <p className="mt-2 text-sm text-red-600"> @@ -656,8 +657,8 @@ const FileUploadDialog = ({ > <CareIcon icon="l-paperclip" className="mr-2 shrink-0" /> <span className="truncate"> - {file.name.length > 30 - ? `${file.name.substring(0, 20)}...` + {file.name.length > 40 + ? `${file.name.substring(0, 30)}...` : file.name} </span> </span> @@ -688,6 +689,7 @@ const FileUploadDialog = ({ onChange={(e) => fileUpload.setFileName(e.target.value, index) } + className="ml-0.5 mb-0.5" /> {index === 0 && fileUpload.error && ( <p className="mt-2 text-sm text-red-600"> diff --git a/src/hooks/useFileUpload.tsx b/src/hooks/useFileUpload.tsx index c7780e3247e..aaa012c8082 100644 --- a/src/hooks/useFileUpload.tsx +++ b/src/hooks/useFileUpload.tsx @@ -289,6 +289,23 @@ export default function useFileUpload( setProgress(0); const errors: File[] = []; + if (combineToPDF) { + if (!uploadFileNames.length || !uploadFileNames[0]) { + setError(t("file_error__single_file_name")); + return; + } + } else { + for (const [index, file] of files.entries()) { + const filename = + allowNameFallback && uploadFileNames[index] === "" && file + ? file.name + : uploadFileNames[index]; + if (!filename) { + setError(t("file_error__single_file_name")); + return; + } + } + } if (combineToPDF && files.length > 1) { const pdfFile = await generatePDF(files); @@ -300,22 +317,15 @@ export default function useFileUpload( return; } } - for (const [index, file] of files.entries()) { - const filename = - allowNameFallback && uploadFileNames[index] === "" && file - ? file.name - : uploadFileNames[index]; - if (!filename) { - setError(t("file_error__single_file_name")); - return; - } - setUploading(true); + setUploading(true); + + for (const [index, file] of files.entries()) { try { const data = await createUpload({ original_name: file.name ?? "", file_type: fileType, - name: filename, + name: uploadFileNames[index], associating_id, file_category: category, mime_type: file.type ?? "", From 781211b82c6dcf389df8fee3d838bc882dea8a87 Mon Sep 17 00:00:00 2001 From: Don Xavier <mail.donxavier@gmail.com> Date: Tue, 4 Feb 2025 00:27:34 +0530 Subject: [PATCH 22/26] expose error handling and reset logic. --- src/components/Files/FilesTab.tsx | 12 ++++++++---- src/hooks/useFileUpload.tsx | 7 ++++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/components/Files/FilesTab.tsx b/src/components/Files/FilesTab.tsx index f787efdf142..b9138e7ae86 100644 --- a/src/components/Files/FilesTab.tsx +++ b/src/components/Files/FilesTab.tsx @@ -641,7 +641,10 @@ const FileUploadDialog = ({ required value={fileUpload.fileNames[0] || ""} disabled={fileUpload.uploading} - onChange={(e) => fileUpload.setFileName(e.target.value)} + onChange={(e) => { + fileUpload.setFileName(e.target.value); + fileUpload.setError(null); + }} className="ml-0.5 mb-1" /> {fileUpload.error && ( @@ -690,9 +693,10 @@ const FileUploadDialog = ({ required value={fileUpload.fileNames[index] || ""} disabled={fileUpload.uploading} - onChange={(e) => - fileUpload.setFileName(e.target.value, index) - } + onChange={(e) => { + fileUpload.setFileName(e.target.value, index); + fileUpload.setError(null); + }} className="ml-0.5 mb-0.5" /> {index === 0 && fileUpload.error && ( diff --git a/src/hooks/useFileUpload.tsx b/src/hooks/useFileUpload.tsx index 13ab1b571ff..72a6b8a1b85 100644 --- a/src/hooks/useFileUpload.tsx +++ b/src/hooks/useFileUpload.tsx @@ -49,6 +49,7 @@ export type FileInputProps = Omit< export type FileUploadReturn = { progress: null | number; error: null | string; + setError: (error: string | null) => void; validateFiles: () => boolean; handleCameraCapture: () => void; handleAudioCapture: () => void; @@ -327,7 +328,10 @@ export default function useFileUpload( const data = await createUpload({ original_name: file.name ?? "", file_type: fileType, - name: uploadFileNames[index], + name: + allowNameFallback && uploadFileNames[index] === "" && file + ? file.name + : uploadFileNames[index], associating_id, file_category: category, mime_type: file.type ?? "", @@ -398,6 +402,7 @@ export default function useFileUpload( return { progress, error, + setError, validateFiles: validateFileUpload, handleCameraCapture: () => setCameraModalOpen(true), handleAudioCapture: () => setAudioModalOpen(true), From 87613fbb2adfb9dec85521b4362e75b97ffe1ed7 Mon Sep 17 00:00:00 2001 From: Mohammed Nihal <57055998+nihal467@users.noreply.github.com> Date: Tue, 4 Feb 2025 17:22:12 +0530 Subject: [PATCH 23/26] fixed the build failure --- package-lock.json | 159 ++-------------------------------------------- 1 file changed, 6 insertions(+), 153 deletions(-) diff --git a/package-lock.json b/package-lock.json index b0853986d6b..66d1c285fa3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7885,16 +7885,6 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, - "node_modules/barcode-detector": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/barcode-detector/-/barcode-detector-2.3.1.tgz", - "integrity": "sha512-D9KEtrquS1tmBZduxBZl8qublIKnRrFqD8TAHDYcLCyrHQBo+vitIxmjMJ61LvXjXyAMalOlO7q0Oh/9Rl2PbQ==", - "license": "MIT", - "dependencies": { - "@types/dom-webcodecs": "0.1.11", - "zxing-wasm": "1.3.4" - } - }, "node_modules/base64-arraybuffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", @@ -8237,21 +8227,17 @@ "license": "CC-BY-4.0" }, "node_modules/canvas": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz", - "integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==", - "dev": true, + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-3.1.0.tgz", + "integrity": "sha512-tTj3CqqukVJ9NgSahykNwtGda7V33VLObwrHfzT0vqJXu7J4d4C/7kQQW3fOEGDfZZoILPut5H00gOjyttPGyg==", "hasInstallScript": true, - "license": "MIT", "optional": true, - "peer": true, "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.0", - "nan": "^2.17.0", - "simple-get": "^3.0.3" + "node-addon-api": "^7.0.0", + "prebuild-install": "^7.1.1" }, "engines": { - "node": ">=6" + "node": "^18.12.0 || >= 20.9.0" } }, "node_modules/canvg": { @@ -8613,15 +8599,6 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "license": "MIT" }, - "node_modules/copy-to-clipboard": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", - "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", - "license": "MIT", - "dependencies": { - "toggle-selection": "^1.0.6" - } - }, "node_modules/core-js": { "version": "3.40.0", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.40.0.tgz", @@ -9149,19 +9126,6 @@ "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", "license": "MIT" }, - "node_modules/decompress-response": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", - "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", - "license": "MIT", - "optional": true, - "dependencies": { - "mimic-response": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/deep-equal": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", @@ -13753,19 +13717,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mimic-response": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", - "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/mini-svg-data-uri": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", @@ -14492,22 +14443,6 @@ "path2d": "^0.2.1" } }, - "node_modules/pdfjs-dist/node_modules/canvas": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/canvas/-/canvas-3.0.0.tgz", - "integrity": "sha512-NtcIBY88FjymQy+g2g5qnuP5IslrbWCQ3A6rSr1PeuYxVRapRZ3BZCrDyAakvI6CuDYidgZaf55ygulFVwROdg==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "dependencies": { - "node-addon-api": "^7.0.0", - "prebuild-install": "^7.1.1", - "simple-get": "^3.0.3" - }, - "engines": { - "node": "^18.12.0 || >= 20.9.0" - } - }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -16009,69 +15944,6 @@ "node": ">= 0.8.15" } }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/roarr": { "version": "2.15.4", "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", @@ -16595,18 +16467,6 @@ "license": "MIT", "optional": true }, - "node_modules/simple-get": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", - "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", - "license": "MIT", - "optional": true, - "dependencies": { - "decompress-response": "^4.2.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -17497,13 +17357,6 @@ "utrie": "^1.0.2" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true, - "license": "MIT" - }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", From 73efe41b165ffb70889c4f0488c687ad9a5648fa Mon Sep 17 00:00:00 2001 From: Don Xavier <mail.donxavier@gmail.com> Date: Wed, 5 Feb 2025 00:33:52 +0530 Subject: [PATCH 24/26] Add zoom to PDF . Remove Compression --- src/components/Common/FilePreviewDialog.tsx | 10 +++++++++- src/components/Common/PDFViewer.tsx | 7 ++++++- src/hooks/useFileUpload.tsx | 1 - 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/components/Common/FilePreviewDialog.tsx b/src/components/Common/FilePreviewDialog.tsx index d9eb4db62b3..fbcaf9ae69a 100644 --- a/src/components/Common/FilePreviewDialog.tsx +++ b/src/components/Common/FilePreviewDialog.tsx @@ -82,6 +82,7 @@ const FilePreviewDialog = (props: FilePreviewProps) => { const [page, setPage] = useState(1); const [numPages, setNumPages] = useState(1); const [index, setIndex] = useState<number>(currentIndex); + const [scale, setScale] = useState(1.0); useEffect(() => { if (uploadedFiles && show) { @@ -95,6 +96,7 @@ const FilePreviewDialog = (props: FilePreviewProps) => { ...file_state, zoom: !checkFull ? file_state.zoom + 1 : file_state.zoom, }); + setScale((prevScale) => Math.min(prevScale + 0.25, 2)); }; const handleZoomOut = () => { @@ -103,6 +105,7 @@ const FilePreviewDialog = (props: FilePreviewProps) => { ...file_state, zoom: !checkFull ? file_state.zoom - 1 : file_state.zoom, }); + setScale((prevScale) => Math.max(prevScale - 0.25, 0.5)); }; const fileName = file_state?.name @@ -133,6 +136,7 @@ const FilePreviewDialog = (props: FilePreviewProps) => { setPage(1); setNumPages(1); setIndex(-1); + setScale(1); onClose?.(); }; @@ -214,7 +218,7 @@ const FilePreviewDialog = (props: FilePreviewProps) => { </a> </Button> )} - <Button variant="outline" type="button" onClick={onClose}> + <Button variant="outline" type="button" onClick={handleClose}> {t("close")} </Button> </div> @@ -249,6 +253,7 @@ const FilePreviewDialog = (props: FilePreviewProps) => { setNumPages(numPages); }} pageNumber={page} + scale={scale} /> </Suspense> ) : previewExtensions.includes(file_state.extension) ? ( @@ -341,6 +346,9 @@ const FilePreviewDialog = (props: FilePreviewProps) => { {file_state.extension === "pdf" && ( <> {[ + ["Zoom In", "l-search-plus", handleZoomIn, scale >= 2], + [`${Math.round(scale * 100)}%`, false, () => {}, false], + ["Zoom Out", "l-search-minus", handleZoomOut, scale <= 0.5], [ "Previous", "l-arrow-left", diff --git a/src/components/Common/PDFViewer.tsx b/src/components/Common/PDFViewer.tsx index 1a20fa9e133..6e135f7b673 100644 --- a/src/components/Common/PDFViewer.tsx +++ b/src/components/Common/PDFViewer.tsx @@ -7,6 +7,7 @@ export default function PDFViewer( url: string; pageNumber: number; onDocumentLoadSuccess: (numPages: number) => void; + scale: number; }>, ) { pdfjs.GlobalWorkerOptions.workerSrc = "/pdf.worker.min.mjs"; @@ -21,7 +22,11 @@ export default function PDFViewer( } className="w-full" > - <Page pageNumber={props.pageNumber} height={650} /> + <Page + pageNumber={props.pageNumber} + height={650} + scale={props.scale} + /> </Document> </div> </div> diff --git a/src/hooks/useFileUpload.tsx b/src/hooks/useFileUpload.tsx index 72a6b8a1b85..14b9119add7 100644 --- a/src/hooks/useFileUpload.tsx +++ b/src/hooks/useFileUpload.tsx @@ -142,7 +142,6 @@ export default function useFileUpload( const ext: string = file.name.split(".")[1]; if (ExtImage.includes(ext)) { const options = { - initialQuality: 0.6, alwaysKeepResolution: true, }; imageCompression(file, options).then((compressedFile: File) => { From ce45fde331f61b8c10d9384f8f642df92afa2667 Mon Sep 17 00:00:00 2001 From: Don Xavier <mail.donxavier@gmail.com> Date: Wed, 5 Feb 2025 16:10:32 +0530 Subject: [PATCH 25/26] optional compression prop added --- src/components/Files/FilesTab.tsx | 1 + src/hooks/useFileUpload.tsx | 31 +++++++++++++++++-------------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/components/Files/FilesTab.tsx b/src/components/Files/FilesTab.tsx index b9138e7ae86..723eb8f7119 100644 --- a/src/components/Files/FilesTab.tsx +++ b/src/components/Files/FilesTab.tsx @@ -157,6 +157,7 @@ export const FilesTab = (props: FilesTabProps) => { onUpload: () => { refetch(); }, + compress: false, }); useEffect(() => { diff --git a/src/hooks/useFileUpload.tsx b/src/hooks/useFileUpload.tsx index 14b9119add7..d53b9497e08 100644 --- a/src/hooks/useFileUpload.tsx +++ b/src/hooks/useFileUpload.tsx @@ -32,6 +32,7 @@ export type FileUploadOptions = { onUpload?: (file: FileUploadModel) => void; // if allowed, will fallback to the name of the file if a seperate filename is not defined. allowNameFallback?: boolean; + compress?: boolean; } & ( | { allowedExtensions?: string[]; @@ -137,20 +138,22 @@ export default function useFileUpload( } const selectedFiles = Array.from(e.target.files); setFiles((prev) => [...prev, ...selectedFiles]); - - selectedFiles.forEach((file) => { - const ext: string = file.name.split(".")[1]; - if (ExtImage.includes(ext)) { - const options = { - alwaysKeepResolution: true, - }; - imageCompression(file, options).then((compressedFile: File) => { - setFiles((prev) => - prev.map((f) => (f.name === file.name ? compressedFile : f)), - ); - }); - } - }); + if (options.compress) { + selectedFiles.forEach((file) => { + const ext: string = file.name.split(".")[1]; + if (ExtImage.includes(ext)) { + const options = { + initialQuality: 0.6, + alwaysKeepResolution: true, + }; + imageCompression(file, options).then((compressedFile: File) => { + setFiles((prev) => + prev.map((f) => (f.name === file.name ? compressedFile : f)), + ); + }); + } + }); + } }; useEffect(() => { From 8ef106987d94a488b4a9ecbd6d96464b2fb34aa9 Mon Sep 17 00:00:00 2001 From: Don Xavier <mail.donxavier@gmail.com> Date: Wed, 5 Feb 2025 16:11:22 +0530 Subject: [PATCH 26/26] unimport error fix --- package-lock.json | 48 ++++++++++++++++++----------------------------- 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/package-lock.json b/package-lock.json index b7d60903a3e..81c1d138ddb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16371,6 +16371,16 @@ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "license": "MIT" }, + "node_modules/rgbcolor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", + "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", + "license": "MIT OR SEE LICENSE IN FEEL-FREE.md", + "optional": true, + "engines": { + "node": ">= 0.8.15" + } + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -16434,16 +16444,6 @@ "node": "*" } }, - "node_modules/rgbcolor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz", - "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", - "license": "MIT OR SEE LICENSE IN FEEL-FREE.md", - "optional": true, - "engines": { - "node": ">= 0.8.15" - } - }, "node_modules/roarr": { "version": "2.15.4", "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", @@ -16967,18 +16967,6 @@ "license": "MIT", "optional": true }, - "node_modules/simple-get": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", - "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", - "license": "MIT", - "optional": true, - "dependencies": { - "decompress-response": "^4.2.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, "node_modules/simple-git": { "version": "3.27.0", "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.27.0.tgz", @@ -17924,14 +17912,6 @@ "devOptional": true, "license": "MIT" }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/text-segmentation": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", @@ -17942,6 +17922,14 @@ "utrie": "^1.0.2" } }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",