mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +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:
|
environment:
|
||||||
name: staging
|
name: staging
|
||||||
url: https://stg-lti-erp.mbugroup.id
|
url: https://stg-lti-erp.mbugroup.id
|
||||||
|
|
||||||
|
|
||||||
# ====== PRODUCTION ======
|
# ====== PRODUCTION ======
|
||||||
# build:production:
|
# build:production:
|
||||||
# <<: *build_template
|
# <<: *build_template
|
||||||
|
|||||||
Generated
+227
@@ -14,6 +14,8 @@
|
|||||||
"axios": "^1.12.2",
|
"axios": "^1.12.2",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"formik": "^2.4.6",
|
"formik": "^2.4.6",
|
||||||
|
"jspdf": "^3.0.4",
|
||||||
|
"jspdf-autotable": "^5.0.2",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"next": "15.5.9",
|
"next": "15.5.9",
|
||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
@@ -1845,12 +1847,25 @@
|
|||||||
"undici-types": "~6.21.0"
|
"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": {
|
"node_modules/@types/parse-json": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
|
||||||
"integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
|
"integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/@types/react": {
|
||||||
"version": "19.2.2",
|
"version": "19.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz",
|
||||||
@@ -1879,6 +1894,13 @@
|
|||||||
"@types/react": "*"
|
"@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": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "8.46.2",
|
"version": "8.46.2",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.2.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.2.tgz",
|
||||||
@@ -2776,6 +2798,16 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/base64-js": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||||
@@ -2925,6 +2957,26 @@
|
|||||||
],
|
],
|
||||||
"license": "CC-BY-4.0"
|
"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": {
|
"node_modules/chalk": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||||
@@ -3020,6 +3072,18 @@
|
|||||||
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
|
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/cosmiconfig": {
|
||||||
"version": "7.1.0",
|
"version": "7.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
|
||||||
@@ -3057,6 +3121,16 @@
|
|||||||
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
|
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/csstype": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||||
@@ -3276,6 +3350,16 @@
|
|||||||
"csstype": "^3.0.2"
|
"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": {
|
"node_modules/dunder-proto": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||||
@@ -3995,6 +4079,23 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/fastq": {
|
||||||
"version": "1.19.1",
|
"version": "1.19.1",
|
||||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
|
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
|
||||||
@@ -4005,6 +4106,12 @@
|
|||||||
"reusify": "^1.0.4"
|
"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": {
|
"node_modules/file-entry-cache": {
|
||||||
"version": "8.0.0",
|
"version": "8.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
|
||||||
@@ -4492,6 +4599,20 @@
|
|||||||
"integrity": "sha512-LgOWAkrN0rFaQpfdWBQlv/VhkOxb5AsBjk6NQVx4yEzWS923T07X0M1Y0VNko2H52HeSpZrZNNMJ0aFqsdVzQg==",
|
"integrity": "sha512-LgOWAkrN0rFaQpfdWBQlv/VhkOxb5AsBjk6NQVx4yEzWS923T07X0M1Y0VNko2H52HeSpZrZNNMJ0aFqsdVzQg==",
|
||||||
"license": "ISC"
|
"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": {
|
"node_modules/husky": {
|
||||||
"version": "9.1.7",
|
"version": "9.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz",
|
||||||
@@ -4571,6 +4692,12 @@
|
|||||||
"node": ">= 0.4"
|
"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": {
|
"node_modules/is-array-buffer": {
|
||||||
"version": "3.0.5",
|
"version": "3.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
|
||||||
@@ -5106,6 +5233,32 @@
|
|||||||
"json5": "lib/cli.js"
|
"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": {
|
"node_modules/jsx-ast-utils": {
|
||||||
"version": "3.3.5",
|
"version": "3.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
|
||||||
@@ -6010,6 +6163,13 @@
|
|||||||
"node": ">=8"
|
"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": {
|
"node_modules/picocolors": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||||
@@ -6163,6 +6323,16 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/react": {
|
||||||
"version": "19.1.0",
|
"version": "19.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
|
||||||
@@ -6321,6 +6491,13 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"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": {
|
"node_modules/regexp.prototype.flags": {
|
||||||
"version": "1.5.4",
|
"version": "1.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
|
||||||
@@ -6413,6 +6590,16 @@
|
|||||||
"node": ">=0.10.0"
|
"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": {
|
"node_modules/run-parallel": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||||
@@ -6762,6 +6949,16 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/stop-iteration-iterator": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
|
"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==",
|
"integrity": "sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g==",
|
||||||
"license": "ISC"
|
"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": {
|
"node_modules/swr": {
|
||||||
"version": "2.3.6",
|
"version": "2.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/swr/-/swr-2.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/swr/-/swr-2.3.6.tgz",
|
||||||
@@ -7025,6 +7232,16 @@
|
|||||||
"url": "https://opencollective.com/webpack"
|
"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": {
|
"node_modules/tiny-case": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz",
|
||||||
@@ -7397,6 +7614,16 @@
|
|||||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/vite-compatible-readable-stream": {
|
||||||
"version": "3.6.1",
|
"version": "3.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/vite-compatible-readable-stream/-/vite-compatible-readable-stream-3.6.1.tgz",
|
"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",
|
"axios": "^1.12.2",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"formik": "^2.4.6",
|
"formik": "^2.4.6",
|
||||||
|
"jspdf": "^3.0.4",
|
||||||
|
"jspdf-autotable": "^5.0.2",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"next": "15.5.9",
|
"next": "15.5.9",
|
||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
|
|||||||
@@ -74,17 +74,11 @@ const ClosingFinanceTable = ({
|
|||||||
|
|
||||||
const profitLossTableData: ProfitLossTableRow[] = isResponseSuccess(finance)
|
const profitLossTableData: ProfitLossTableRow[] = isResponseSuccess(finance)
|
||||||
? [
|
? [
|
||||||
// Penjualan group
|
// Pembelian group
|
||||||
{
|
...finance.data.profit_loss.data.pembelian.map((item) => ({
|
||||||
label: 'Penjualan',
|
label: 'Pembelian',
|
||||||
group_name: 'Penjualan',
|
group_name: 'Pembelian',
|
||||||
group_index: 0,
|
group_index: 1,
|
||||||
isGroupHeader: true as const,
|
|
||||||
},
|
|
||||||
...finance.data.profit_loss.data.penjualan.map((item) => ({
|
|
||||||
label: 'Penjualan',
|
|
||||||
group_name: 'Penjualan',
|
|
||||||
group_index: 0,
|
|
||||||
type: item.type,
|
type: item.type,
|
||||||
rp_per_bird: item.rp_per_bird,
|
rp_per_bird: item.rp_per_bird,
|
||||||
rp_per_kg: item.rp_per_kg,
|
rp_per_kg: item.rp_per_kg,
|
||||||
@@ -103,17 +97,11 @@ const ClosingFinanceTable = ({
|
|||||||
finance.data.profit_loss.data.summary.gross_profit.rp_per_kg,
|
finance.data.profit_loss.data.summary.gross_profit.rp_per_kg,
|
||||||
amount: finance.data.profit_loss.data.summary.gross_profit.amount,
|
amount: finance.data.profit_loss.data.summary.gross_profit.amount,
|
||||||
},
|
},
|
||||||
// Pembelian group
|
// Penjualan group
|
||||||
{
|
...finance.data.profit_loss.data.penjualan.map((item) => ({
|
||||||
label: 'Pembelian',
|
label: 'Penjualan',
|
||||||
group_name: 'Pembelian',
|
group_name: 'Penjualan',
|
||||||
group_index: 1,
|
group_index: 0,
|
||||||
isGroupHeader: true as const,
|
|
||||||
},
|
|
||||||
...finance.data.profit_loss.data.pembelian.map((item) => ({
|
|
||||||
label: 'Pembelian',
|
|
||||||
group_name: 'Pembelian',
|
|
||||||
group_index: 1,
|
|
||||||
type: item.type,
|
type: item.type,
|
||||||
rp_per_bird: item.rp_per_bird,
|
rp_per_bird: item.rp_per_bird,
|
||||||
rp_per_kg: item.rp_per_kg,
|
rp_per_kg: item.rp_per_kg,
|
||||||
@@ -369,16 +357,16 @@ const ClosingFinanceTable = ({
|
|||||||
data={profitLossTableData}
|
data={profitLossTableData}
|
||||||
columns={[
|
columns={[
|
||||||
{
|
{
|
||||||
header: 'Type',
|
header: 'Jenis',
|
||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
accessorFn: (item) => item.type,
|
accessorFn: (item) => item.type,
|
||||||
cell: (item) => (
|
cell: (item) => (
|
||||||
<div className='ps-6'>
|
<div className=''>
|
||||||
{formatTitleCase(item.row.original.type || '-')}
|
{formatTitleCase(item.row.original.type || '-')}
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
footer: (item) => (
|
footer: (item) => (
|
||||||
<div className='font-bold'>
|
<div className='font-bold uppercase'>
|
||||||
{isResponseSuccess(finance)
|
{isResponseSuccess(finance)
|
||||||
? formatTitleCase(
|
? formatTitleCase(
|
||||||
finance.data.profit_loss.data.summary.net_profit
|
finance.data.profit_loss.data.summary.net_profit
|
||||||
@@ -446,7 +434,7 @@ const ClosingFinanceTable = ({
|
|||||||
<td
|
<td
|
||||||
className={TABLE_DEFAULT_STYLING.bodyColumnClassName}
|
className={TABLE_DEFAULT_STYLING.bodyColumnClassName}
|
||||||
>
|
>
|
||||||
<div className='font-bold'>
|
<div className='font-bold ps-6 uppercase'>
|
||||||
{formatTitleCase(rowData.label ?? '-')}
|
{formatTitleCase(rowData.label ?? '-')}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ const ProjectFlockDetail = ({
|
|||||||
latestApproval: projectFlock?.approval,
|
latestApproval: projectFlock?.approval,
|
||||||
approvalLines: PROJECT_FLOCK_APPROVAL_LINE,
|
approvalLines: PROJECT_FLOCK_APPROVAL_LINE,
|
||||||
moduleName: 'PROJECT_FLOCKS',
|
moduleName: 'PROJECT_FLOCKS',
|
||||||
moduleId: projectFlock?.id.toString() ?? '',
|
moduleId: projectFlock?.id?.toString() ?? '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const { approvals: kandangApprovals, isLoading: kandangApprovalsLoading } =
|
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 ProjectFlockKandangTable from '@/components/pages/production/project-flock/form/ProjectFlockKandangTable';
|
||||||
import { Nonstock } from '@/types/api/master-data/nonstock';
|
import { Nonstock } from '@/types/api/master-data/nonstock';
|
||||||
import { useUiStore } from '@/stores/ui/ui.store';
|
import { useUiStore } from '@/stores/ui/ui.store';
|
||||||
import Link from 'next/link';
|
|
||||||
import DrawerHeader from '@/components/helper/drawer/DrawerHeader';
|
import DrawerHeader from '@/components/helper/drawer/DrawerHeader';
|
||||||
import { formatDate } from '@/lib/helper';
|
|
||||||
|
|
||||||
interface ProjectFlockFormProps {
|
interface ProjectFlockFormProps {
|
||||||
formType?: 'add' | 'edit' | 'detail';
|
formType?: 'add' | 'edit' | 'detail';
|
||||||
@@ -260,7 +258,9 @@ const ProjectFlockForm = ({
|
|||||||
const categoryChangeHandler = (val: OptionType | OptionType[] | null) => {
|
const categoryChangeHandler = (val: OptionType | OptionType[] | null) => {
|
||||||
formik.setFieldValue('category', (val as OptionType)?.value);
|
formik.setFieldValue('category', (val as OptionType)?.value);
|
||||||
formik.setFieldValue('category_option', val);
|
formik.setFieldValue('category_option', val);
|
||||||
formik.setFieldTouched('category', true);
|
if (val == null) {
|
||||||
|
formik.setFieldTouched('category', true);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Submit Handler
|
// Submit Handler
|
||||||
@@ -788,7 +788,7 @@ const ProjectFlockForm = ({
|
|||||||
}
|
}
|
||||||
errorMessage={formik.errors.area_id as string}
|
errorMessage={formik.errors.area_id as string}
|
||||||
isClearable
|
isClearable
|
||||||
isDisabled={formType === 'detail'}
|
isDisabled={formType != 'add'}
|
||||||
/>
|
/>
|
||||||
<SelectInput
|
<SelectInput
|
||||||
required
|
required
|
||||||
@@ -807,7 +807,7 @@ const ProjectFlockForm = ({
|
|||||||
}
|
}
|
||||||
errorMessage={formik.errors.location_id as string}
|
errorMessage={formik.errors.location_id as string}
|
||||||
isClearable
|
isClearable
|
||||||
isDisabled={formType === 'detail' || disabledLocation}
|
isDisabled={formType != 'add' || disabledLocation}
|
||||||
/>
|
/>
|
||||||
<SelectInput
|
<SelectInput
|
||||||
required
|
required
|
||||||
@@ -837,7 +837,7 @@ const ProjectFlockForm = ({
|
|||||||
}
|
}
|
||||||
errorMessage={formik.errors.flock_name as string}
|
errorMessage={formik.errors.flock_name as string}
|
||||||
isClearable
|
isClearable
|
||||||
isDisabled={formType === 'detail'}
|
isDisabled={formType != 'add'}
|
||||||
/>
|
/>
|
||||||
<SelectInput
|
<SelectInput
|
||||||
required
|
required
|
||||||
@@ -851,7 +851,7 @@ const ProjectFlockForm = ({
|
|||||||
isError={formik.touched.fcr_id && Boolean(formik.errors.fcr_id)}
|
isError={formik.touched.fcr_id && Boolean(formik.errors.fcr_id)}
|
||||||
errorMessage={formik.errors.fcr_id as string}
|
errorMessage={formik.errors.fcr_id as string}
|
||||||
isClearable
|
isClearable
|
||||||
isDisabled={formType === 'detail'}
|
isDisabled={formType != 'add'}
|
||||||
/>
|
/>
|
||||||
<SelectInput
|
<SelectInput
|
||||||
required
|
required
|
||||||
@@ -864,7 +864,7 @@ const ProjectFlockForm = ({
|
|||||||
}
|
}
|
||||||
errorMessage={formik.errors.category as string}
|
errorMessage={formik.errors.category as string}
|
||||||
isClearable
|
isClearable
|
||||||
isDisabled={formType === 'detail'}
|
isDisabled={formType != 'add'}
|
||||||
/>
|
/>
|
||||||
<NumberInput
|
<NumberInput
|
||||||
name='period'
|
name='period'
|
||||||
|
|||||||
@@ -1,602 +1,218 @@
|
|||||||
import { ReportExpense } from '@/types/api/report/report-expense';
|
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 { formatCurrency, formatDate } from '@/lib/helper';
|
||||||
import pdfStyles from '@/components/pages/report/expense/pdf/styles/ReportExpenseStyles';
|
import jsPDF from 'jspdf';
|
||||||
import toast from 'react-hot-toast';
|
import autoTable, { UserOptions } from 'jspdf-autotable';
|
||||||
|
interface jsPDFWithAutoTable extends jsPDF {
|
||||||
|
lastAutoTable: {
|
||||||
|
finalY: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export interface PDFParams {
|
export interface PDFParams {
|
||||||
location_name?: string;
|
location_name?: string;
|
||||||
supplier_name?: string;
|
supplier_name?: string;
|
||||||
kandang_name?: string;
|
|
||||||
nonstock_name?: string;
|
|
||||||
category?: string;
|
|
||||||
realization_date?: string;
|
realization_date?: string;
|
||||||
search?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const getStatusStyle = (action?: string) => {
|
const getStatusColor = (action?: string): [number, number, number] => {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case 'APPROVED':
|
case 'APPROVED':
|
||||||
return { backgroundColor: '#dcfce7' };
|
case 'Selesai': // Berdasarkan data sumber
|
||||||
|
return [220, 252, 231]; // Hijau muda (#dcfce7)
|
||||||
case 'REJECTED':
|
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:
|
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 (
|
export const generateReportExpensePDF = async (
|
||||||
data: ReportExpense[],
|
data: ReportExpense[],
|
||||||
params: PDFParams
|
params: PDFParams
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
try {
|
// Inisialisasi dokumen dengan tipe yang sudah diekstensi
|
||||||
const doc = <PDFDocument data={data} params={params} />;
|
const doc = new jsPDF('l', 'mm', 'a4') as jsPDFWithAutoTable;
|
||||||
const blob = await pdf(doc).toBlob();
|
const pageWidth: number = doc.internal.pageSize.getWidth();
|
||||||
const url = URL.createObjectURL(blob);
|
const marginX: number = 14;
|
||||||
const link = document.createElement('a');
|
|
||||||
link.href = url;
|
// --- HEADER SECTION ---
|
||||||
link.download = `Laporan-BOP-${formatDate(new Date(), 'YYYY-MM-DD-HHmm')}.pdf`;
|
doc.setFont('helvetica', 'bold');
|
||||||
document.body.appendChild(link);
|
doc.setFontSize(18);
|
||||||
link.click();
|
doc.setTextColor(31, 116, 191); // #1f74bf sesuai style
|
||||||
document.body.removeChild(link);
|
doc.text('PT LUMBUNG TELUR INDONESIA', marginX, 20);
|
||||||
URL.revokeObjectURL(url);
|
|
||||||
} catch (error) {
|
doc.setFont('helvetica', 'normal');
|
||||||
throw error;
|
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(
|
async getAllFetcher(
|
||||||
endpoint: string
|
endpoint: string
|
||||||
): Promise<BaseApiResponse<ReportExpense[]>> {
|
): 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);
|
return await httpClientFetcher<BaseApiResponse<ReportExpense[]>>(endpoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user