mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 05:22:02 +00:00
Merge branch 'dev/randy' into 'feat/FE/US-338/expense-report'
[FIX/FE][US#338] Fixing Export PDF Using jspdf Library See merge request mbugroup/lti-web-client!112
This commit is contained in:
@@ -165,8 +165,6 @@ deploy:staging:
|
||||
environment:
|
||||
name: staging
|
||||
url: https://stg-lti-erp.mbugroup.id
|
||||
|
||||
|
||||
# ====== PRODUCTION ======
|
||||
# build:production:
|
||||
# <<: *build_template
|
||||
|
||||
Generated
+227
@@ -14,6 +14,8 @@
|
||||
"axios": "^1.12.2",
|
||||
"clsx": "^2.1.1",
|
||||
"formik": "^2.4.6",
|
||||
"jspdf": "^3.0.4",
|
||||
"jspdf-autotable": "^5.0.2",
|
||||
"moment": "^2.30.1",
|
||||
"next": "15.5.9",
|
||||
"react": "19.1.0",
|
||||
@@ -1845,12 +1847,25 @@
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/pako": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.4.tgz",
|
||||
"integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/parse-json": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
|
||||
"integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
|
||||
"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": "19.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz",
|
||||
@@ -1879,6 +1894,13 @@
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"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==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "8.46.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.2.tgz",
|
||||
@@ -2776,6 +2798,16 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"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",
|
||||
@@ -2925,6 +2957,26 @@
|
||||
],
|
||||
"license": "CC-BY-4.0"
|
||||
},
|
||||
"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==",
|
||||
"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/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
@@ -3020,6 +3072,18 @@
|
||||
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/core-js": {
|
||||
"version": "3.47.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.47.0.tgz",
|
||||
"integrity": "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/core-js"
|
||||
}
|
||||
},
|
||||
"node_modules/cosmiconfig": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
|
||||
@@ -3057,6 +3121,16 @@
|
||||
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"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/csstype": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
@@ -3276,6 +3350,16 @@
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/dompurify": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz",
|
||||
"integrity": "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==",
|
||||
"license": "(MPL-2.0 OR Apache-2.0)",
|
||||
"optional": true,
|
||||
"optionalDependencies": {
|
||||
"@types/trusted-types": "^2.0.7"
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
@@ -3995,6 +4079,23 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-png": {
|
||||
"version": "6.4.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-png/-/fast-png-6.4.0.tgz",
|
||||
"integrity": "sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/pako": "^2.0.3",
|
||||
"iobuffer": "^5.3.2",
|
||||
"pako": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-png/node_modules/pako": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
|
||||
"integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==",
|
||||
"license": "(MIT AND Zlib)"
|
||||
},
|
||||
"node_modules/fastq": {
|
||||
"version": "1.19.1",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
|
||||
@@ -4005,6 +4106,12 @@
|
||||
"reusify": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"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/file-entry-cache": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
|
||||
@@ -4492,6 +4599,20 @@
|
||||
"integrity": "sha512-LgOWAkrN0rFaQpfdWBQlv/VhkOxb5AsBjk6NQVx4yEzWS923T07X0M1Y0VNko2H52HeSpZrZNNMJ0aFqsdVzQg==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"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/husky": {
|
||||
"version": "9.1.7",
|
||||
"resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz",
|
||||
@@ -4571,6 +4692,12 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/iobuffer": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.4.0.tgz",
|
||||
"integrity": "sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-array-buffer": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
|
||||
@@ -5106,6 +5233,32 @@
|
||||
"json5": "lib/cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/jspdf": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/jspdf/-/jspdf-3.0.4.tgz",
|
||||
"integrity": "sha512-dc6oQ8y37rRcHn316s4ngz/nOjayLF/FFxBF4V9zamQKRqXxyiH1zagkCdktdWhtoQId5K20xt1lB90XzkB+hQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.28.4",
|
||||
"fast-png": "^6.2.0",
|
||||
"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-autotable": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/jspdf-autotable/-/jspdf-autotable-5.0.2.tgz",
|
||||
"integrity": "sha512-YNKeB7qmx3pxOLcNeoqAv3qTS7KuvVwkFe5AduCawpop3NOkBUtqDToxNc225MlNecxT4kP2Zy3z/y/yvGdXUQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"jspdf": "^2 || ^3"
|
||||
}
|
||||
},
|
||||
"node_modules/jsx-ast-utils": {
|
||||
"version": "3.3.5",
|
||||
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
|
||||
@@ -6010,6 +6163,13 @@
|
||||
"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==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
@@ -6163,6 +6323,16 @@
|
||||
],
|
||||
"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/react": {
|
||||
"version": "19.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
|
||||
@@ -6321,6 +6491,13 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"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/regexp.prototype.flags": {
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
|
||||
@@ -6413,6 +6590,16 @@
|
||||
"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==",
|
||||
"license": "MIT OR SEE LICENSE IN FEEL-FREE.md",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">= 0.8.15"
|
||||
}
|
||||
},
|
||||
"node_modules/run-parallel": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||
@@ -6762,6 +6949,16 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"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",
|
||||
@@ -6981,6 +7178,16 @@
|
||||
"integrity": "sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"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/swr": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/swr/-/swr-2.3.6.tgz",
|
||||
@@ -7025,6 +7232,16 @@
|
||||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"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/tiny-case": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz",
|
||||
@@ -7397,6 +7614,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/vite-compatible-readable-stream": {
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmjs.org/vite-compatible-readable-stream/-/vite-compatible-readable-stream-3.6.1.tgz",
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
"axios": "^1.12.2",
|
||||
"clsx": "^2.1.1",
|
||||
"formik": "^2.4.6",
|
||||
"jspdf": "^3.0.4",
|
||||
"jspdf-autotable": "^5.0.2",
|
||||
"moment": "^2.30.1",
|
||||
"next": "15.5.9",
|
||||
"react": "19.1.0",
|
||||
|
||||
@@ -74,17 +74,11 @@ const ClosingFinanceTable = ({
|
||||
|
||||
const profitLossTableData: ProfitLossTableRow[] = isResponseSuccess(finance)
|
||||
? [
|
||||
// Penjualan group
|
||||
{
|
||||
label: 'Penjualan',
|
||||
group_name: 'Penjualan',
|
||||
group_index: 0,
|
||||
isGroupHeader: true as const,
|
||||
},
|
||||
...finance.data.profit_loss.data.penjualan.map((item) => ({
|
||||
label: 'Penjualan',
|
||||
group_name: 'Penjualan',
|
||||
group_index: 0,
|
||||
// Pembelian group
|
||||
...finance.data.profit_loss.data.pembelian.map((item) => ({
|
||||
label: 'Pembelian',
|
||||
group_name: 'Pembelian',
|
||||
group_index: 1,
|
||||
type: item.type,
|
||||
rp_per_bird: item.rp_per_bird,
|
||||
rp_per_kg: item.rp_per_kg,
|
||||
@@ -103,17 +97,11 @@ const ClosingFinanceTable = ({
|
||||
finance.data.profit_loss.data.summary.gross_profit.rp_per_kg,
|
||||
amount: finance.data.profit_loss.data.summary.gross_profit.amount,
|
||||
},
|
||||
// Pembelian group
|
||||
{
|
||||
label: 'Pembelian',
|
||||
group_name: 'Pembelian',
|
||||
group_index: 1,
|
||||
isGroupHeader: true as const,
|
||||
},
|
||||
...finance.data.profit_loss.data.pembelian.map((item) => ({
|
||||
label: 'Pembelian',
|
||||
group_name: 'Pembelian',
|
||||
group_index: 1,
|
||||
// Penjualan group
|
||||
...finance.data.profit_loss.data.penjualan.map((item) => ({
|
||||
label: 'Penjualan',
|
||||
group_name: 'Penjualan',
|
||||
group_index: 0,
|
||||
type: item.type,
|
||||
rp_per_bird: item.rp_per_bird,
|
||||
rp_per_kg: item.rp_per_kg,
|
||||
@@ -369,16 +357,16 @@ const ClosingFinanceTable = ({
|
||||
data={profitLossTableData}
|
||||
columns={[
|
||||
{
|
||||
header: 'Type',
|
||||
header: 'Jenis',
|
||||
enableSorting: false,
|
||||
accessorFn: (item) => item.type,
|
||||
cell: (item) => (
|
||||
<div className='ps-6'>
|
||||
<div className=''>
|
||||
{formatTitleCase(item.row.original.type || '-')}
|
||||
</div>
|
||||
),
|
||||
footer: (item) => (
|
||||
<div className='font-bold'>
|
||||
<div className='font-bold uppercase'>
|
||||
{isResponseSuccess(finance)
|
||||
? formatTitleCase(
|
||||
finance.data.profit_loss.data.summary.net_profit
|
||||
@@ -446,7 +434,7 @@ const ClosingFinanceTable = ({
|
||||
<td
|
||||
className={TABLE_DEFAULT_STYLING.bodyColumnClassName}
|
||||
>
|
||||
<div className='font-bold'>
|
||||
<div className='font-bold ps-6 uppercase'>
|
||||
{formatTitleCase(rowData.label ?? '-')}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
@@ -68,7 +68,7 @@ const ProjectFlockDetail = ({
|
||||
latestApproval: projectFlock?.approval,
|
||||
approvalLines: PROJECT_FLOCK_APPROVAL_LINE,
|
||||
moduleName: 'PROJECT_FLOCKS',
|
||||
moduleId: projectFlock?.id.toString() ?? '',
|
||||
moduleId: projectFlock?.id?.toString() ?? '',
|
||||
});
|
||||
|
||||
const { approvals: kandangApprovals, isLoading: kandangApprovalsLoading } =
|
||||
|
||||
@@ -47,9 +47,7 @@ import Card from '@/components/Card';
|
||||
import ProjectFlockKandangTable from '@/components/pages/production/project-flock/form/ProjectFlockKandangTable';
|
||||
import { Nonstock } from '@/types/api/master-data/nonstock';
|
||||
import { useUiStore } from '@/stores/ui/ui.store';
|
||||
import Link from 'next/link';
|
||||
import DrawerHeader from '@/components/helper/drawer/DrawerHeader';
|
||||
import { formatDate } from '@/lib/helper';
|
||||
|
||||
interface ProjectFlockFormProps {
|
||||
formType?: 'add' | 'edit' | 'detail';
|
||||
@@ -260,7 +258,9 @@ const ProjectFlockForm = ({
|
||||
const categoryChangeHandler = (val: OptionType | OptionType[] | null) => {
|
||||
formik.setFieldValue('category', (val as OptionType)?.value);
|
||||
formik.setFieldValue('category_option', val);
|
||||
formik.setFieldTouched('category', true);
|
||||
if (val == null) {
|
||||
formik.setFieldTouched('category', true);
|
||||
}
|
||||
};
|
||||
|
||||
// Submit Handler
|
||||
@@ -788,7 +788,7 @@ const ProjectFlockForm = ({
|
||||
}
|
||||
errorMessage={formik.errors.area_id as string}
|
||||
isClearable
|
||||
isDisabled={formType === 'detail'}
|
||||
isDisabled={formType != 'add'}
|
||||
/>
|
||||
<SelectInput
|
||||
required
|
||||
@@ -807,7 +807,7 @@ const ProjectFlockForm = ({
|
||||
}
|
||||
errorMessage={formik.errors.location_id as string}
|
||||
isClearable
|
||||
isDisabled={formType === 'detail' || disabledLocation}
|
||||
isDisabled={formType != 'add' || disabledLocation}
|
||||
/>
|
||||
<SelectInput
|
||||
required
|
||||
@@ -837,7 +837,7 @@ const ProjectFlockForm = ({
|
||||
}
|
||||
errorMessage={formik.errors.flock_name as string}
|
||||
isClearable
|
||||
isDisabled={formType === 'detail'}
|
||||
isDisabled={formType != 'add'}
|
||||
/>
|
||||
<SelectInput
|
||||
required
|
||||
@@ -851,7 +851,7 @@ const ProjectFlockForm = ({
|
||||
isError={formik.touched.fcr_id && Boolean(formik.errors.fcr_id)}
|
||||
errorMessage={formik.errors.fcr_id as string}
|
||||
isClearable
|
||||
isDisabled={formType === 'detail'}
|
||||
isDisabled={formType != 'add'}
|
||||
/>
|
||||
<SelectInput
|
||||
required
|
||||
@@ -864,7 +864,7 @@ const ProjectFlockForm = ({
|
||||
}
|
||||
errorMessage={formik.errors.category as string}
|
||||
isClearable
|
||||
isDisabled={formType === 'detail'}
|
||||
isDisabled={formType != 'add'}
|
||||
/>
|
||||
<NumberInput
|
||||
name='period'
|
||||
|
||||
@@ -1,602 +1,218 @@
|
||||
import { ReportExpense } from '@/types/api/report/report-expense';
|
||||
import { Document, Image, Page, pdf, Text, View } from '@react-pdf/renderer';
|
||||
import { formatCurrency, formatDate } from '@/lib/helper';
|
||||
import pdfStyles from '@/components/pages/report/expense/pdf/styles/ReportExpenseStyles';
|
||||
import toast from 'react-hot-toast';
|
||||
import jsPDF from 'jspdf';
|
||||
import autoTable, { UserOptions } from 'jspdf-autotable';
|
||||
interface jsPDFWithAutoTable extends jsPDF {
|
||||
lastAutoTable: {
|
||||
finalY: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface PDFParams {
|
||||
location_name?: string;
|
||||
supplier_name?: string;
|
||||
kandang_name?: string;
|
||||
nonstock_name?: string;
|
||||
category?: string;
|
||||
realization_date?: string;
|
||||
search?: string;
|
||||
}
|
||||
|
||||
const getStatusStyle = (action?: string) => {
|
||||
const getStatusColor = (action?: string): [number, number, number] => {
|
||||
switch (action) {
|
||||
case 'APPROVED':
|
||||
return { backgroundColor: '#dcfce7' };
|
||||
case 'Selesai': // Berdasarkan data sumber
|
||||
return [220, 252, 231]; // Hijau muda (#dcfce7)
|
||||
case 'REJECTED':
|
||||
return { backgroundColor: '#fee2e2' };
|
||||
return [254, 226, 226]; // Merah muda (#fee2e2)
|
||||
case 'Realisasi': // Berdasarkan data sumber
|
||||
return [254, 243, 199]; // Kuning/Amber muda (#fef3c7)
|
||||
default:
|
||||
return { backgroundColor: '#fef3c7' };
|
||||
return [255, 255, 255]; // Putih
|
||||
}
|
||||
};
|
||||
|
||||
const PDFDocument = ({
|
||||
data,
|
||||
params,
|
||||
}: {
|
||||
data: ReportExpense[];
|
||||
params: PDFParams;
|
||||
}) => {
|
||||
// Group data by supplier
|
||||
const groupedBySupplier = (() => {
|
||||
const groups: Record<string, ReportExpense[]> = {};
|
||||
data.forEach((item) => {
|
||||
const supplierName = item.supplier?.name || 'Unknown Supplier';
|
||||
if (!groups[supplierName]) {
|
||||
groups[supplierName] = [];
|
||||
}
|
||||
groups[supplierName].push(item);
|
||||
});
|
||||
return groups;
|
||||
})();
|
||||
|
||||
// Calculate grand totals
|
||||
const grandTotals = data.reduce(
|
||||
(acc, item) => {
|
||||
const pengajuanTotal =
|
||||
(item.pengajuan?.qty || 0) * (item.pengajuan?.price || 0);
|
||||
const realisasiTotal =
|
||||
(item.realisasi?.qty || 0) * (item.realisasi?.price || 0);
|
||||
return {
|
||||
pengajuan: acc.pengajuan + pengajuanTotal,
|
||||
realisasi: acc.realisasi + realisasiTotal,
|
||||
};
|
||||
},
|
||||
{ pengajuan: 0, realisasi: 0 }
|
||||
);
|
||||
|
||||
return (
|
||||
<Document>
|
||||
<Page size='A4' orientation='landscape' style={pdfStyles.page}>
|
||||
{/* Header Section */}
|
||||
<View style={pdfStyles.header}>
|
||||
<Image
|
||||
src={'https://placehold.co/120x30/png'}
|
||||
style={pdfStyles.logo}
|
||||
id={'mbu-logo'}
|
||||
/>
|
||||
<Text style={pdfStyles.companyInfo}>PT LUMBUNG TELUR INDONESIA</Text>
|
||||
<Text style={pdfStyles.address}>
|
||||
SOHO Building Lt.3 (Paris Van Java), Jalan Karang Tinggal, Kel.
|
||||
Cipedes, Kec. Sukajadi, Kota Bandung 40162
|
||||
</Text>
|
||||
<View style={pdfStyles.divider} />
|
||||
</View>
|
||||
|
||||
{/* Report Title */}
|
||||
<View style={pdfStyles.titleSection}>
|
||||
<Text style={pdfStyles.title}>LAPORAN BIAYA OPERASIONAL</Text>
|
||||
<View style={pdfStyles.poInfo}>
|
||||
<Text>Tanggal Cetak: {formatDate(new Date(), 'DD MMM YYYY')}</Text>
|
||||
<Text>Total Data: {data.length} transaksi</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Filters Info if any */}
|
||||
{(params.location_name ||
|
||||
params.supplier_name ||
|
||||
params.realization_date) && (
|
||||
<View style={{ marginBottom: 10, fontSize: 8 }}>
|
||||
{params.location_name && (
|
||||
<Text>Lokasi: {params.location_name}</Text>
|
||||
)}
|
||||
{params.supplier_name && (
|
||||
<Text>Supplier: {params.supplier_name}</Text>
|
||||
)}
|
||||
{params.realization_date && (
|
||||
<Text>
|
||||
Tanggal Realisasi:{' '}
|
||||
{formatDate(params.realization_date, 'DD MMM YYYY')}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* Grouped Tables by Supplier */}
|
||||
{Object.entries(groupedBySupplier).map(
|
||||
([supplierName, items], groupIndex) => {
|
||||
const supplierTotals = items.reduce(
|
||||
(acc, item) => {
|
||||
const pengajuanTotal =
|
||||
(item.pengajuan?.qty || 0) * (item.pengajuan?.price || 0);
|
||||
const realisasiTotal =
|
||||
(item.realisasi?.qty || 0) * (item.realisasi?.price || 0);
|
||||
return {
|
||||
pengajuan: acc.pengajuan + pengajuanTotal,
|
||||
realisasi: acc.realisasi + realisasiTotal,
|
||||
};
|
||||
},
|
||||
{ pengajuan: 0, realisasi: 0 }
|
||||
);
|
||||
|
||||
return (
|
||||
<View key={groupIndex} style={pdfStyles.allocationSection}>
|
||||
{/* Supplier Header */}
|
||||
<Text style={pdfStyles.sectionTitle}>{supplierName}</Text>
|
||||
|
||||
{/* Table */}
|
||||
<View style={pdfStyles.table}>
|
||||
{/* Header Row 1: Group Headers */}
|
||||
<View style={[pdfStyles.tableRow, pdfStyles.tableHeader]}>
|
||||
<View
|
||||
style={[
|
||||
pdfStyles.tableCellNarrowHeader,
|
||||
{ borderBottomWidth: 0 },
|
||||
]}
|
||||
>
|
||||
<Text>No</Text>
|
||||
</View>
|
||||
<View
|
||||
style={[
|
||||
pdfStyles.tableCellWrapHeader,
|
||||
{ borderBottomWidth: 0 },
|
||||
]}
|
||||
>
|
||||
<Text>No. PO</Text>
|
||||
</View>
|
||||
<View
|
||||
style={[
|
||||
pdfStyles.tableCellWrapHeader,
|
||||
{ borderBottomWidth: 0 },
|
||||
]}
|
||||
>
|
||||
<Text>No. Referensi</Text>
|
||||
</View>
|
||||
<View
|
||||
style={[
|
||||
pdfStyles.tableCellMediumHeader,
|
||||
{ borderBottomWidth: 0 },
|
||||
]}
|
||||
>
|
||||
<Text>Tgl Realisasi</Text>
|
||||
</View>
|
||||
<View
|
||||
style={[
|
||||
pdfStyles.tableCellMediumHeader,
|
||||
{ borderBottomWidth: 0 },
|
||||
]}
|
||||
>
|
||||
<Text>Tgl Transaksi</Text>
|
||||
</View>
|
||||
<View
|
||||
style={[
|
||||
pdfStyles.tableCellXSmallHeader,
|
||||
{ borderBottomWidth: 0 },
|
||||
]}
|
||||
>
|
||||
<Text>Kategori</Text>
|
||||
</View>
|
||||
<View
|
||||
style={[
|
||||
pdfStyles.tableCellHeader,
|
||||
{ borderBottomWidth: 0 },
|
||||
]}
|
||||
>
|
||||
<Text>Produk</Text>
|
||||
</View>
|
||||
<View
|
||||
style={[
|
||||
pdfStyles.tableCellSmallHeader,
|
||||
{ borderBottomWidth: 0 },
|
||||
]}
|
||||
>
|
||||
<Text>Lokasi</Text>
|
||||
</View>
|
||||
<View
|
||||
style={[
|
||||
pdfStyles.tableCellSmallHeader,
|
||||
{ borderBottomWidth: 0 },
|
||||
]}
|
||||
>
|
||||
<Text>Kandang</Text>
|
||||
</View>
|
||||
|
||||
{/* Pengajuan Group */}
|
||||
<View
|
||||
style={[
|
||||
pdfStyles.tableCellXSmallHeader,
|
||||
{ borderBottomWidth: 0, borderRightWidth: 0 },
|
||||
]}
|
||||
>
|
||||
<Text></Text>
|
||||
</View>
|
||||
<View
|
||||
style={[
|
||||
pdfStyles.tableCellMediumHeader,
|
||||
{
|
||||
borderBottomWidth: 0,
|
||||
borderRightWidth: 0,
|
||||
textAlign: 'center',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Text>Pengajuan</Text>
|
||||
</View>
|
||||
<View
|
||||
style={[
|
||||
pdfStyles.tableCellMediumHeader,
|
||||
{ borderBottomWidth: 0 },
|
||||
]}
|
||||
>
|
||||
<Text></Text>
|
||||
</View>
|
||||
|
||||
{/* Realisasi Group */}
|
||||
<View
|
||||
style={[
|
||||
pdfStyles.tableCellXSmallHeader,
|
||||
{ borderBottomWidth: 0, borderRightWidth: 0 },
|
||||
]}
|
||||
>
|
||||
<Text></Text>
|
||||
</View>
|
||||
<View
|
||||
style={[
|
||||
pdfStyles.tableCellMediumHeader,
|
||||
{
|
||||
borderBottomWidth: 0,
|
||||
borderRightWidth: 0,
|
||||
textAlign: 'center',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Text>Realisasi</Text>
|
||||
</View>
|
||||
<View
|
||||
style={[
|
||||
pdfStyles.tableCellMediumHeader,
|
||||
{ borderBottomWidth: 0 },
|
||||
]}
|
||||
>
|
||||
<Text></Text>
|
||||
</View>
|
||||
|
||||
<View
|
||||
style={[
|
||||
pdfStyles.tableCellMediumHeader,
|
||||
{ borderBottomWidth: 0 },
|
||||
]}
|
||||
>
|
||||
<Text>Status BOP</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Header Row 2: Sub Headers */}
|
||||
<View style={[pdfStyles.tableRow, pdfStyles.tableHeader]}>
|
||||
<View style={pdfStyles.tableCellNarrowHeader}>
|
||||
<Text></Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellWrapHeader}>
|
||||
<Text></Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellWrapHeader}>
|
||||
<Text></Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellMediumHeader}>
|
||||
<Text></Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellMediumHeader}>
|
||||
<Text></Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellXSmallHeader}>
|
||||
<Text></Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellHeader}>
|
||||
<Text></Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellSmallHeader}>
|
||||
<Text></Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellSmallHeader}>
|
||||
<Text></Text>
|
||||
</View>
|
||||
|
||||
{/* Pengajuan sub-headers */}
|
||||
<View style={pdfStyles.tableCellXSmallHeader}>
|
||||
<Text>Qty</Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellMediumHeader}>
|
||||
<Text>Harga</Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellMediumHeader}>
|
||||
<Text>Total</Text>
|
||||
</View>
|
||||
|
||||
{/* Realisasi sub-headers */}
|
||||
<View style={pdfStyles.tableCellXSmallHeader}>
|
||||
<Text>Qty</Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellMediumHeader}>
|
||||
<Text>Harga</Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellMediumHeader}>
|
||||
<Text>Total</Text>
|
||||
</View>
|
||||
|
||||
<View style={pdfStyles.tableCellMediumHeader}>
|
||||
<Text></Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Table Body */}
|
||||
{items.map((item, index) => {
|
||||
const pengajuanTotal =
|
||||
(item.pengajuan?.qty || 0) * (item.pengajuan?.price || 0);
|
||||
const realisasiTotal =
|
||||
(item.realisasi?.qty || 0) * (item.realisasi?.price || 0);
|
||||
|
||||
return (
|
||||
<View key={index} style={pdfStyles.tableRow}>
|
||||
<View style={pdfStyles.tableCellNarrow}>
|
||||
<Text>{index + 1}</Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellWrap}>
|
||||
<Text>{item.po_number || '-'}</Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellWrap}>
|
||||
<Text>{item.reference_number || '-'}</Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellMedium}>
|
||||
<Text>
|
||||
{formatDate(item.realization_date, 'DD MMM YY')}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellMedium}>
|
||||
<Text>
|
||||
{formatDate(item.transaction_date, 'DD MMM YY')}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellXSmall}>
|
||||
<Text>
|
||||
{item.category?.split('-').join(' ') || '-'}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCell}>
|
||||
<Text>{item.pengajuan?.nonstock?.name || '-'}</Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellSmall}>
|
||||
<Text>{item.kandang?.location?.name || '-'}</Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellSmall}>
|
||||
<Text>{item.kandang?.name || '-'}</Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellRightXSmall}>
|
||||
<Text>
|
||||
{(item.pengajuan?.qty || 0).toLocaleString('id-ID')}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellRightMedium}>
|
||||
<Text>
|
||||
{formatCurrency(item.pengajuan?.price || 0)}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellRightMedium}>
|
||||
<Text>{formatCurrency(pengajuanTotal)}</Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellRightXSmall}>
|
||||
<Text>
|
||||
{(item.realisasi?.qty || 0).toLocaleString('id-ID')}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellRightMedium}>
|
||||
<Text>
|
||||
{formatCurrency(item.realisasi?.price || 0)}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellRightMedium}>
|
||||
<Text>{formatCurrency(realisasiTotal)}</Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellMedium}>
|
||||
<Text
|
||||
style={[
|
||||
{ fontSize: 7, padding: 2, borderRadius: 2 },
|
||||
getStatusStyle(item.latest_approval?.action),
|
||||
]}
|
||||
>
|
||||
{item.latest_approval?.step_name || '-'}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
|
||||
{/* Supplier Subtotal Row */}
|
||||
<View style={pdfStyles.grandTotalRow}>
|
||||
<View
|
||||
style={[
|
||||
pdfStyles.tableCellNarrow,
|
||||
{ borderRightWidth: 0 },
|
||||
]}
|
||||
>
|
||||
<Text></Text>
|
||||
</View>
|
||||
<View
|
||||
style={[pdfStyles.tableCellWrap, { borderRightWidth: 0 }]}
|
||||
>
|
||||
<Text></Text>
|
||||
</View>
|
||||
<View
|
||||
style={[pdfStyles.tableCellWrap, { borderRightWidth: 0 }]}
|
||||
>
|
||||
<Text></Text>
|
||||
</View>
|
||||
<View
|
||||
style={[
|
||||
pdfStyles.tableCellMedium,
|
||||
{ borderRightWidth: 0 },
|
||||
]}
|
||||
>
|
||||
<Text></Text>
|
||||
</View>
|
||||
<View
|
||||
style={[
|
||||
pdfStyles.tableCellMedium,
|
||||
{ borderRightWidth: 0 },
|
||||
]}
|
||||
>
|
||||
<Text></Text>
|
||||
</View>
|
||||
<View
|
||||
style={[
|
||||
pdfStyles.tableCellXSmall,
|
||||
{ borderRightWidth: 0 },
|
||||
]}
|
||||
>
|
||||
<Text></Text>
|
||||
</View>
|
||||
<View
|
||||
style={[
|
||||
pdfStyles.tableCellXSmall,
|
||||
{ borderRightWidth: 0 },
|
||||
]}
|
||||
>
|
||||
<Text></Text>
|
||||
</View>
|
||||
<View
|
||||
style={[
|
||||
pdfStyles.tableCellSmall,
|
||||
{ borderRightWidth: 0 },
|
||||
]}
|
||||
>
|
||||
<Text></Text>
|
||||
</View>
|
||||
<View
|
||||
style={[
|
||||
pdfStyles.tableCellSmall,
|
||||
{ borderRightWidth: 0 },
|
||||
]}
|
||||
>
|
||||
<Text></Text>
|
||||
</View>
|
||||
|
||||
{/* Pengajuan Subtotal */}
|
||||
<View
|
||||
style={[
|
||||
pdfStyles.tableCellRightXSmall,
|
||||
{ borderRightWidth: 0 },
|
||||
]}
|
||||
>
|
||||
<Text></Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellRightMedium}>
|
||||
<Text style={{ fontWeight: 'bold' }}>Subtotal</Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellRightMedium}>
|
||||
<Text style={{ fontWeight: 'bold' }}>
|
||||
{formatCurrency(supplierTotals.pengajuan)}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{/* Realisasi Subtotal */}
|
||||
<View
|
||||
style={[
|
||||
pdfStyles.tableCellRightXSmall,
|
||||
{ borderRightWidth: 0 },
|
||||
]}
|
||||
>
|
||||
<Text></Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellRightMedium}>
|
||||
<Text style={{ fontWeight: 'bold' }}>Subtotal</Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCellRightMedium}>
|
||||
<Text style={{ fontWeight: 'bold' }}>
|
||||
{formatCurrency(supplierTotals.realisasi)}
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
<View style={pdfStyles.tableCellMedium}>
|
||||
<Text></Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
)}
|
||||
|
||||
{/* Grand Total Section */}
|
||||
<View style={pdfStyles.allocationSection}>
|
||||
<View
|
||||
style={{
|
||||
width: '30%',
|
||||
borderTopWidth: 1,
|
||||
borderTopColor: '#000000',
|
||||
borderTopStyle: 'solid',
|
||||
borderLeftWidth: 1,
|
||||
borderLeftColor: '#000000',
|
||||
borderLeftStyle: 'solid',
|
||||
}}
|
||||
>
|
||||
<View style={pdfStyles.innerRow}>
|
||||
<View style={pdfStyles.tableCell}>
|
||||
<Text style={{ fontWeight: 'bold' }}>
|
||||
GRAND TOTAL PENGAJUAN
|
||||
</Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCell}>
|
||||
<Text style={{ fontWeight: 'bold' }}>
|
||||
{formatCurrency(grandTotals.pengajuan)}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View style={pdfStyles.innerRow}>
|
||||
<View style={pdfStyles.tableCell}>
|
||||
<Text style={{ fontWeight: 'bold' }}>
|
||||
GRAND TOTAL REALISASI
|
||||
</Text>
|
||||
</View>
|
||||
<View style={pdfStyles.tableCell}>
|
||||
<Text style={{ fontWeight: 'bold' }}>
|
||||
{formatCurrency(grandTotals.realisasi)}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Footer */}
|
||||
<View style={pdfStyles.footer}>
|
||||
<View style={pdfStyles.footerCompany}>
|
||||
<Text>PT LUMBUNG TELUR INDONESIA</Text>
|
||||
</View>
|
||||
</View>
|
||||
</Page>
|
||||
</Document>
|
||||
);
|
||||
};
|
||||
|
||||
export const generateReportExpensePDF = async (
|
||||
data: ReportExpense[],
|
||||
params: PDFParams
|
||||
): Promise<void> => {
|
||||
try {
|
||||
const doc = <PDFDocument data={data} params={params} />;
|
||||
const blob = await pdf(doc).toBlob();
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = `Laporan-BOP-${formatDate(new Date(), 'YYYY-MM-DD-HHmm')}.pdf`;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(url);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
// Inisialisasi dokumen dengan tipe yang sudah diekstensi
|
||||
const doc = new jsPDF('l', 'mm', 'a4') as jsPDFWithAutoTable;
|
||||
const pageWidth: number = doc.internal.pageSize.getWidth();
|
||||
const marginX: number = 14;
|
||||
|
||||
// --- HEADER SECTION ---
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.setFontSize(18);
|
||||
doc.setTextColor(31, 116, 191); // #1f74bf sesuai style
|
||||
doc.text('PT LUMBUNG TELUR INDONESIA', marginX, 20);
|
||||
|
||||
doc.setFont('helvetica', 'normal');
|
||||
doc.setFontSize(7);
|
||||
doc.setTextColor(102, 102, 102);
|
||||
doc.text(
|
||||
'SOHO Building Lt.3 (Paris Van Java), Jalan Karang Tinggal, Kel. Cipedes, Kec. Sukajadi, Kota Bandung 40162',
|
||||
marginX,
|
||||
25
|
||||
);
|
||||
|
||||
doc.setDrawColor(0);
|
||||
doc.line(marginX, 28, pageWidth - marginX, 28);
|
||||
|
||||
// --- TITLE & INFO SECTION ---
|
||||
doc.setFontSize(18);
|
||||
doc.setTextColor(31, 116, 191);
|
||||
doc.text('LAPORAN BIAYA OPERASIONAL', marginX, 38);
|
||||
|
||||
doc.setFontSize(7);
|
||||
doc.setTextColor(0);
|
||||
const infoX: number = pageWidth - marginX;
|
||||
doc.text(
|
||||
`Tanggal Cetak: ${formatDate(new Date(), 'DD MMM YYYY')}`,
|
||||
infoX,
|
||||
35,
|
||||
{ align: 'right' }
|
||||
);
|
||||
doc.text(`Total Data: ${data.length} transaksi`, infoX, 40, {
|
||||
align: 'right',
|
||||
});
|
||||
|
||||
// --- GROUPING LOGIC ---
|
||||
const groupedBySupplier = data.reduce(
|
||||
(acc, item) => {
|
||||
const supplierName: string = item.supplier?.name || 'Unknown Supplier';
|
||||
if (!acc[supplierName]) acc[supplierName] = [];
|
||||
acc[supplierName].push(item);
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, ReportExpense[]>
|
||||
);
|
||||
|
||||
let currentY: number = 50;
|
||||
|
||||
// --- RENDER TABLES PER SUPPLIER ---
|
||||
Object.entries(groupedBySupplier).forEach(([supplierName, items]) => {
|
||||
// Cek sisa ruang halaman sebelum cetak judul supplier
|
||||
if (currentY > 180) {
|
||||
doc.addPage();
|
||||
currentY = 20;
|
||||
}
|
||||
|
||||
doc.setFontSize(14);
|
||||
doc.setTextColor(31, 116, 191);
|
||||
doc.text(supplierName, marginX, currentY);
|
||||
currentY += 5;
|
||||
|
||||
const tableOptions: UserOptions = {
|
||||
startY: currentY,
|
||||
head: [
|
||||
[
|
||||
{ content: 'No', rowSpan: 2 },
|
||||
{ content: 'No. PO', rowSpan: 2 },
|
||||
{ content: 'No. Referensi', rowSpan: 2 },
|
||||
{ content: 'Tgl Realisasi', rowSpan: 2 },
|
||||
{ content: 'Tgl Transaksi', rowSpan: 2 },
|
||||
{ content: 'Kategori', rowSpan: 2 },
|
||||
{ content: 'Produk', rowSpan: 2 },
|
||||
{ content: 'Lokasi', rowSpan: 2 },
|
||||
{ content: 'Kandang', rowSpan: 2 },
|
||||
{ content: 'Pengajuan', colSpan: 3, styles: { halign: 'center' } },
|
||||
{ content: 'Realisasi', colSpan: 3, styles: { halign: 'center' } },
|
||||
{ content: 'Status BOP', rowSpan: 2 },
|
||||
],
|
||||
['Qty', 'Harga', 'Total', 'Qty', 'Harga', 'Total'],
|
||||
],
|
||||
body: items.map((item, index) => {
|
||||
const pQty: number = item.pengajuan?.qty || 0;
|
||||
const pPrice: number = item.pengajuan?.price || 0;
|
||||
const rQty: number = item.realisasi?.qty || 0;
|
||||
const rPrice: number = item.realisasi?.price || 0;
|
||||
|
||||
return [
|
||||
index + 1,
|
||||
item.po_number || '-',
|
||||
item.reference_number || '-',
|
||||
formatDate(item.realization_date, 'DD MMM YY'),
|
||||
formatDate(item.transaction_date, 'DD MMM YY'),
|
||||
item.category?.replace('-', ' ') || '-',
|
||||
item.pengajuan?.nonstock?.name || '-',
|
||||
item.kandang?.location?.name || '-',
|
||||
item.kandang?.name || '-',
|
||||
pQty.toLocaleString('id-ID'),
|
||||
formatCurrency(pPrice),
|
||||
formatCurrency(pQty * pPrice),
|
||||
rQty.toLocaleString('id-ID'),
|
||||
formatCurrency(rPrice),
|
||||
formatCurrency(rQty * rPrice),
|
||||
item.latest_approval?.step_name || '-',
|
||||
];
|
||||
}),
|
||||
theme: 'grid',
|
||||
styles: { fontSize: 6, cellPadding: 1.5, overflow: 'linebreak' },
|
||||
headStyles: {
|
||||
fillColor: [245, 245, 245],
|
||||
textColor: 0,
|
||||
fontStyle: 'bold',
|
||||
lineWidth: 0.1,
|
||||
},
|
||||
// HOOK UNTUK BADGE:
|
||||
didParseCell: (dataCell) => {
|
||||
// Index kolom 15 adalah Status BOP (berdasarkan array di atas)
|
||||
if (dataCell.section === 'body' && dataCell.column.index === 15) {
|
||||
const statusText = dataCell.cell.raw as string;
|
||||
|
||||
// Berikan warna latar belakang sel sesuai status
|
||||
dataCell.cell.styles.fillColor = getStatusColor(statusText);
|
||||
dataCell.cell.styles.textColor = [0, 0, 0]; // Teks hitam agar terbaca
|
||||
dataCell.cell.styles.fontStyle = 'bold';
|
||||
dataCell.cell.styles.halign = 'center';
|
||||
}
|
||||
},
|
||||
margin: { left: marginX, right: marginX },
|
||||
};
|
||||
|
||||
autoTable(doc, tableOptions);
|
||||
currentY = doc.lastAutoTable.finalY + 10;
|
||||
});
|
||||
|
||||
// --- GRAND TOTAL SECTION ---
|
||||
const grandTotals = data.reduce(
|
||||
(acc, item) => {
|
||||
const pTotal = (item.pengajuan?.qty || 0) * (item.pengajuan?.price || 0);
|
||||
const rTotal = (item.realisasi?.qty || 0) * (item.realisasi?.price || 0);
|
||||
return {
|
||||
pengajuan: acc.pengajuan + pTotal,
|
||||
realisasi: acc.realisasi + rTotal,
|
||||
};
|
||||
},
|
||||
{ pengajuan: 0, realisasi: 0 }
|
||||
);
|
||||
|
||||
if (currentY > 250) {
|
||||
doc.addPage();
|
||||
currentY = 20;
|
||||
}
|
||||
|
||||
autoTable(doc, {
|
||||
startY: currentY,
|
||||
body: [
|
||||
['GRAND TOTAL PENGAJUAN', formatCurrency(grandTotals.pengajuan)],
|
||||
['GRAND TOTAL REALISASI', formatCurrency(grandTotals.realisasi)],
|
||||
],
|
||||
styles: { fontSize: 8, fontStyle: 'bold' },
|
||||
columnStyles: {
|
||||
0: { cellWidth: 50, fillColor: [245, 245, 245] },
|
||||
1: { cellWidth: 50 },
|
||||
},
|
||||
theme: 'grid',
|
||||
margin: { left: marginX },
|
||||
});
|
||||
|
||||
// --- FOOTER ---
|
||||
const finalY: number = doc.lastAutoTable.finalY + 20;
|
||||
doc.setFontSize(14);
|
||||
doc.setTextColor(31, 116, 191);
|
||||
doc.text('PT LUMBUNG TELUR INDONESIA', pageWidth - marginX, finalY, {
|
||||
align: 'right',
|
||||
});
|
||||
|
||||
// Download File
|
||||
const fileName: string = `Laporan-BOP-${formatDate(new Date(), 'YYYY-MM-DD-HHmm')}.pdf`;
|
||||
doc.save(fileName);
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,29 +0,0 @@
|
||||
/**
|
||||
* Dummy data for ReportExpense[]
|
||||
* Generated from: report-expense.json
|
||||
*
|
||||
* This file is auto-generated. Do not edit manually.
|
||||
*/
|
||||
|
||||
import { BaseApiResponse } from '@/types/api/api-general';
|
||||
import dummyData from './reports-expense.dummy.json';
|
||||
import { ReportExpense } from '@/types/api/report/report-expense';
|
||||
|
||||
/**
|
||||
* Get dummy ReportExpense[] data
|
||||
* @returns Promise with BaseApiResponse containing ReportExpense[]
|
||||
*/
|
||||
export async function getDummyExpense(): Promise<
|
||||
BaseApiResponse<ReportExpense[]>
|
||||
> {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve({
|
||||
code: 200,
|
||||
status: 'success',
|
||||
message: 'Data retrieved successfully',
|
||||
data: dummyData as unknown as ReportExpense[],
|
||||
});
|
||||
}, 500);
|
||||
});
|
||||
}
|
||||
@@ -16,11 +16,6 @@ export class ReportExpenseApiService extends BaseApiService<
|
||||
async getAllFetcher(
|
||||
endpoint: string
|
||||
): Promise<BaseApiResponse<ReportExpense[]>> {
|
||||
// TODO: Remove this block when backend is ready
|
||||
// const { getDummyExpense } = await import('@/dummy/reports-expense.dummy');
|
||||
// return await getDummyExpense();
|
||||
|
||||
// Uncomment this when backend is ready
|
||||
return await httpClientFetcher<BaseApiResponse<ReportExpense[]>>(endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user