diff --git a/components/page-title.tsx b/components/page-title.tsx index 9d75d136..e0d1a6c6 100644 --- a/components/page-title.tsx +++ b/components/page-title.tsx @@ -1,5 +1,5 @@ "use client"; -import React, { useEffect } from "react"; +import React, { useEffect, useState } from "react"; import DateRangePicker from "@/components/date-range-picker"; import { usePathname } from "@/components/navigation"; import { cn, getCookiesDecrypt } from "@/lib/utils"; @@ -14,6 +14,8 @@ import { import { Button } from "./ui/button"; import { Label } from "./ui/label"; import { Input } from "./ui/input"; +import pdfGenerator from "@/utils/pdf-generator"; +import { tableauSignin, tableauViewImage } from "@/service/tableau/tableau-service"; const PageTitle = ({ title, @@ -22,6 +24,8 @@ const PageTitle = ({ title?: string; className?: string; }) => { + + const [reportDate, setReportDate] = useState(); const pathname = usePathname(); const name = pathname?.split("/").slice(1).join(" "); const roleId = getCookiesDecrypt("urie"); @@ -29,6 +33,169 @@ const PageTitle = ({ useEffect(() => { console.log("role", roleId); }, [roleId]); + + const downloadReport = async () => { + console.log(reportDate); + const formattedDate = `${reportDate.year}-${String( + reportDate.month + ).padStart(2, "0")}-${String(reportDate.day).padStart(2, "0")}`; + + const resLogin = await tableauSignin(); + const token = resLogin?.data?.data?.credentials?.token; + + const resFrontCover = await tableauViewImage( + token, + "5b35ec9c-add5-45e7-b71a-31a28db9954d", + reportDate + ); + const resAssignmentDetail = await tableauViewImage( + token, + "b2e7e184-8311-49e1-8314-a6b2d8a8c6f8", + reportDate + ); + // const resMonitoringContent = await tableauViewImage( + // token, + // "34683f52-1029-49e4-950d-2c94159ebd8f", + // reportDate + // ); + // const resMonitoringArticle = await tableauViewImage( + // token, + // "34683f52-1029-49e4-950d-2c94159ebd8f", + // reportDate + // ); + const resInteractionContent = await tableauViewImage( + token, + "8f038669-4135-4693-96e5-e6a957a1cc4f", + reportDate + ); + const resTotalInteractionContent = await tableauViewImage( + token, + "e019e7f7-8d3b-4303-aa20-3fdbb318df1e", + reportDate + ); + const resInteractionContentCategory1 = await tableauViewImage( + token, + "c1dcc6c8-17dc-4e30-85db-7938e30b4418", + reportDate + ); + const resInteractionContentCategory2 = await tableauViewImage( + token, + "68b95c1d-aee1-4073-afb7-05188eccee67", + reportDate + ); + const resInteractionContentCategory3 = await tableauViewImage( + token, + "7de1f07f-9a09-45a8-8ff6-96484106492d", + reportDate + ); + const resInteractionDistribution = await tableauViewImage( + token, + "0781d7e6-e133-416c-bf41-e5b2fa5996dd", + reportDate + ); + const resAssignments = await tableauViewImage( + token, + "b2e7e184-8311-49e1-8314-a6b2d8a8c6f8", + reportDate + ); + const resUserCount = await tableauViewImage( + token, + "36ff675d-790c-4420-b0c7-0a6441f59cb1", + reportDate + ); + const resUserCountDistribution = await tableauViewImage( + token, + "86370e20-b13d-4cb0-a54a-c25dc13bb427", + reportDate + ); + const resBackCover = await tableauViewImage( + token, + "516c8790-ccd5-44dc-bb66-b0e146d7168b", + reportDate + ); + const blobFrontCover = new Blob([resFrontCover.data], { type: "image/png" }); + const blobAssignmentDetail = new Blob([resAssignmentDetail.data], { type: "image/png" }); + // const blobMonitoringContent = new Blob([resMonitoringContent.data], { type: "image/png" }); + // const blobMonitoringArticle = new Blob([resMonitoringArticle.data], { type: "image/png" }); + const blobInteractionContent = new Blob([resInteractionContent.data], { type: "image/png" }); + const blobTotalInteractionContent = new Blob([resTotalInteractionContent.data], { type: "image/png" }); + const blobInteractionContentCategory1 = new Blob([resInteractionContentCategory1.data], { type: "image/png" }); + const blobInteractionContentCategory2 = new Blob([resInteractionContentCategory2.data], { type: "image/png" }); + const blobInteractionContentCategory3 = new Blob([resInteractionContentCategory3.data], { type: "image/png" }); + const blobInteractionDistribution = new Blob([resInteractionDistribution.data], { type: "image/png" }); + const blobAssignments = new Blob([resAssignments.data], { type: "image/png" }); + const blobUserCount = new Blob([resUserCount.data], { type: "image/png" }); + const blobUserCountDistribution = new Blob([resUserCountDistribution.data], { type: "image/png" }); + const blobBackCover = new Blob([resBackCover.data], { type: "image/png" }); + + console.log(blobFrontCover); + + await pdfGenerator([ + blobFrontCover, + blobAssignmentDetail, + // blobMonitoringContent, + // blobMonitoringArticle, + blobInteractionContent, + blobTotalInteractionContent, + blobInteractionContentCategory1, + blobInteractionContentCategory2, + blobInteractionContentCategory3, + blobInteractionDistribution, + blobAssignments, + blobUserCount, + blobUserCountDistribution, + blobBackCover + ]); + }; + + async function mergeImagesToCanvas(blobs: Blob[]) { + try { + const images = await Promise.all( + blobs.map(blob => { + return new Promise((resolve) => { + const url = URL.createObjectURL(blob); + const img = new Image(); + img.onload = () => { + resolve(img); + URL.revokeObjectURL(url); + }; + img.src = url; + }); + }) + ); + + // Hitung total tinggi dari semua gambar (stacked vertically) + const width = Math.max(...images.map(img => img.width)); + const height = images.reduce((sum, img) => sum + img.height, 0); + + const canvas = document.getElementById('pdf-canvas') as HTMLCanvasElement; + const ctx = canvas.getContext('2d')!; + canvas.width = width; + canvas.height = height; + + let y = 0; + for (const img of images) { + ctx.drawImage(img, 0, y, img.width, img.height); + y += img.height; + } + + // Simpan hasil sebagai gambar (PNG) atau bisa print + const dataURL = canvas.toDataURL('image/png'); + + // Simpan atau cetak (print sebagai PDF) + const link = document.createElement('a'); + link.href = dataURL; + link.download = 'merged-image.png'; + link.click(); + + // Atau tampilkan dan user bisa "Print to PDF" + window.open(dataURL, '_blank'); + } catch (error) { + console.log("Error :::", error) + } + } + + return Number(roleId) == 2 || Number(roleId) == 11 || Number(roleId) == 12 ? ( "" ) : ( @@ -51,11 +218,12 @@ const PageTitle = ({
+ setDateFilter(e.target.value)} + value={reportDate} + onChange={(e) => setReportDate(e.target.value)} className="w-full" />
@@ -63,7 +231,7 @@ const PageTitle = ({ diff --git a/package-lock.json b/package-lock.json index 6b2d47b0..d5216532 100644 --- a/package-lock.json +++ b/package-lock.json @@ -94,6 +94,7 @@ "jotai": "^2.9.3", "jquery": "^3.7.1", "js-cookie": "^3.0.5", + "jspdf": "^3.0.1", "layout-grid": "^2.2.0", "leaflet": "^1.9.4", "lucide-react": "^0.390.0", @@ -542,9 +543,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", + "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -4318,6 +4319,12 @@ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.17.tgz", "integrity": "sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==" }, + "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==", + "optional": true + }, "node_modules/@types/react": { "version": "18.3.14", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.14.tgz", @@ -4417,6 +4424,12 @@ "integrity": "sha512-xzLEyKB50yqCUPUJkIsrVvoWNfFUbIZI+RspLWt8u+tIW/BetMBZtgV2LY/2o+tYH8dRvQ+eoPf3NdhQCcLE2w==", "dev": true }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "optional": true + }, "node_modules/@types/unist": { "version": "2.0.11", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", @@ -5245,6 +5258,17 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, + "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==", + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, "node_modules/attr-accept": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.5.tgz", @@ -5332,6 +5356,15 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "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==", + "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", @@ -5440,6 +5473,17 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "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==", + "bin": { + "btoa": "bin/btoa.js" + }, + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -5666,6 +5710,31 @@ } ] }, + "node_modules/canvg": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.11.tgz", + "integrity": "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==", + "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==", + "optional": true + }, "node_modules/ccount": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", @@ -6081,6 +6150,17 @@ "node": ">=18" } }, + "node_modules/core-js": { + "version": "3.41.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.41.0.tgz", + "integrity": "sha512-SJ4/EHwS36QMJd6h/Rg+GyR4A5xE0FSI3eZ+iBVpfqf1x0eTSg1smWLHrA+2jQThZSh97fmSgFSU8B61nxosxA==", + "hasInstallScript": true, + "optional": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/cose-base": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", @@ -6134,6 +6214,15 @@ "postcss": "^8.0.9" } }, + "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==", + "optional": true, + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/css-loader": { "version": "5.2.7", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-5.2.7.tgz", @@ -8293,6 +8382,11 @@ "node": "^12.20 || >= 14.13" } }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -9543,6 +9637,19 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/html2canvas": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", + "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", + "optional": true, + "dependencies": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/htmlparser2": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", @@ -10568,6 +10675,32 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jspdf": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-3.0.1.tgz", + "integrity": "sha512-qaGIxqxetdoNnFQQXxTKUD9/Z7AloLaw94fFsOiJMxbfYdBbrBuhWmbzI8TVjrw7s3jBY1PFHofBKMV/wZPapg==", + "dependencies": { + "@babel/runtime": "^7.26.7", + "atob": "^2.1.2", + "btoa": "^1.2.1", + "fflate": "^0.8.1" + }, + "optionalDependencies": { + "canvg": "^3.0.11", + "core-js": "^3.6.0", + "dompurify": "^3.2.4", + "html2canvas": "^1.0.0-rc.5" + } + }, + "node_modules/jspdf/node_modules/dompurify": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.5.tgz", + "integrity": "sha512-mLPd29uoRe9HpvwP2TxClGQBzGXeEC/we/q+bFlmPPmj2p2Ugl3r6ATu/UU1v77DXNcehiBg9zsr1dREyA/dJQ==", + "optional": true, + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -13643,6 +13776,12 @@ "node": ">=8" } }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "optional": true + }, "node_modules/periscopic": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", @@ -14708,6 +14847,15 @@ "node": ">=0.10" } }, + "node_modules/raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "optional": true, + "dependencies": { + "performance-now": "^2.1.0" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -15839,6 +15987,15 @@ "node": ">=0.10.0" } }, + "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==", + "optional": true, + "engines": { + "node": ">= 0.8.15" + } + }, "node_modules/rich-text": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/rich-text/-/rich-text-1.0.3.tgz", @@ -16765,6 +16922,15 @@ "integrity": "sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==", "dev": true }, + "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==", + "optional": true, + "engines": { + "node": ">=0.1.14" + } + }, "node_modules/stdin-discarder": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", @@ -17247,6 +17413,15 @@ "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==", + "optional": true, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/svg.draggable.js": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz", @@ -17567,6 +17742,15 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, + "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==", + "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", @@ -18414,6 +18598,15 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "node_modules/utrie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", + "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", + "optional": true, + "dependencies": { + "base64-arraybuffer": "^1.0.2" + } + }, "node_modules/uuid": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", diff --git a/package.json b/package.json index 7930dd3c..b7f46ae6 100644 --- a/package.json +++ b/package.json @@ -95,6 +95,7 @@ "jotai": "^2.9.3", "jquery": "^3.7.1", "js-cookie": "^3.0.5", + "jspdf": "^3.0.1", "layout-grid": "^2.2.0", "leaflet": "^1.9.4", "lucide-react": "^0.390.0", diff --git a/service/http-config/http-interceptor-service.ts b/service/http-config/http-interceptor-service.ts index c695d280..adc11dcc 100644 --- a/service/http-config/http-interceptor-service.ts +++ b/service/http-config/http-interceptor-service.ts @@ -175,6 +175,26 @@ export async function httpGetInterceptorWithToken(pathUrl: any, headers?: any) { } } +export async function httpGetArrayBuffer(pathUrl: any, headers: any) { + const response = await axiosInterceptorInstance + .get(pathUrl, { headers, responseType: 'arraybuffer' }) + .catch((error) => error.response); + console.log("Response base svc : ", response); + if (response?.status == "200") { + return { + error: false, + message: "success", + data: response?.data, + }; + } else { + return { + error: true, + message: response?.data?.message || null, + data: null, + }; + } +} + export async function postAPIWithJson(url: any, data: any, token: any) { const headers = { Authorization: `Bearer ${token}`, diff --git a/service/tableau/tableau-service.ts b/service/tableau/tableau-service.ts index 0c1851a5..bc7bb8f6 100644 --- a/service/tableau/tableau-service.ts +++ b/service/tableau/tableau-service.ts @@ -1,6 +1,18 @@ -import { httpPostInterceptor } from "../http-config/http-interceptor-prod-service"; +import { httpGetArrayBuffer, httpPostInterceptor } from "../http-config/http-interceptor-service"; export async function generateTicket() { const url = "/admin/tableau-ticket"; return httpPostInterceptor(url, null); } + +export async function tableauSignin() { + const url = "/admin/tableau-signin"; + return httpPostInterceptor(url, null); +} + +export async function tableauViewImage(token: string, viewId: string, date?: string) { + const headers = { + "x-token": token + } + return await httpGetArrayBuffer(`/admin/tableau-view-image?viewId=${viewId}&date=${date}`, headers); +} \ No newline at end of file diff --git a/utils/pdf-generator.tsx b/utils/pdf-generator.tsx new file mode 100644 index 00000000..4bdea08d --- /dev/null +++ b/utils/pdf-generator.tsx @@ -0,0 +1,43 @@ +import { jsPDF } from 'jspdf'; + +export default async function pdfGenerator(blobs: Blob[]) { + const pdf = new jsPDF({ + unit: 'px', + compress: true, + }); + + for (let i = 0; i < blobs.length; i++) { + const blob = blobs[i]; + + const imageUrl = URL.createObjectURL(blob); + const img = new Image(); + img.src = imageUrl; + + await new Promise((resolve) => { + img.onload = () => { + const imgWidth = img.width; + const imgHeight = img.height; + + const orientation = imgWidth > imgHeight ? 'landscape' : 'portrait'; + + if (i === 0) { + // Set ukuran dan orientasi halaman pertama + (pdf as any).internal.pageSize.setWidth(imgWidth); + (pdf as any).internal.pageSize.setHeight(imgHeight); + pdf.setPage(1); + } else { + // Tambahkan halaman baru dengan ukuran dan orientasi yang sesuai + pdf.addPage([imgWidth, imgHeight], orientation); + pdf.setPage(pdf.getNumberOfPages()); + } + + pdf.addImage(img, 'PNG', 0, 0, imgWidth, imgHeight); + resolve(''); + }; + }); + + URL.revokeObjectURL(imageUrl); + } + + pdf.save('downloaded-images.pdf'); +}