diff --git a/package-lock.json b/package-lock.json index 61da9724..d108e6bb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "clsx": "^2.1.1", "cmdk": "^1.1.1", "embla-carousel-react": "^8.6.0", + "exceljs": "^4.4.0", "formik": "^2.4.6", "html-to-image": "^1.11.13", "input-otp": "^1.4.2", @@ -52,7 +53,7 @@ "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", - "daisyui": "^5.5.8", + "daisyui": "^5.5.14", "eslint": "^9", "eslint-config-next": "^15.5.7", "husky": "^9.1.7", @@ -510,6 +511,47 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@fast-csv/format": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-4.3.5.tgz", + "integrity": "sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==", + "license": "MIT", + "dependencies": { + "@types/node": "^14.0.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isboolean": "^3.0.3", + "lodash.isequal": "^4.5.0", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0" + } + }, + "node_modules/@fast-csv/format/node_modules/@types/node": { + "version": "14.18.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", + "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", + "license": "MIT" + }, + "node_modules/@fast-csv/parse": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/@fast-csv/parse/-/parse-4.3.6.tgz", + "integrity": "sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==", + "license": "MIT", + "dependencies": { + "@types/node": "^14.0.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.groupby": "^4.6.0", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0", + "lodash.isundefined": "^3.0.1", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/@fast-csv/parse/node_modules/@types/node": { + "version": "14.18.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", + "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", + "license": "MIT" + }, "node_modules/@floating-ui/core": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", @@ -4421,6 +4463,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz", "integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==", "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -4431,6 +4474,7 @@ "integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==", "devOptional": true, "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -4503,6 +4547,7 @@ "integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.46.2", "@typescript-eslint/types": "8.46.2", @@ -5026,6 +5071,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5076,6 +5122,81 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/archiver": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^2.1.0", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^2.2.0", + "zip-stream": "^4.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "license": "MIT", + "dependencies": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/archiver-utils/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/archiver-utils/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/archiver-utils/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/archiver-utils/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -5272,6 +5393,12 @@ "dev": true, "license": "MIT" }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, "node_modules/async-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", @@ -5363,7 +5490,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, "license": "MIT" }, "node_modules/base64-arraybuffer": { @@ -5405,11 +5531,49 @@ "require-from-string": "^2.0.2" } }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "license": "Unlicense", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "license": "MIT", + "dependencies": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -5447,6 +5611,56 @@ "pako": "~1.0.5" } }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "engines": { + "node": ">=0.2.0" + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -5545,6 +5759,18 @@ "node": ">=10.0.0" } }, + "node_modules/chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "license": "MIT/X11", + "dependencies": { + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -5655,11 +5881,25 @@ "node": ">= 0.8" } }, + "node_modules/compress-commons": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", + "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", + "license": "MIT", + "dependencies": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, "license": "MIT" }, "node_modules/convert-source-map": { @@ -5680,6 +5920,12 @@ "url": "https://opencollective.com/core-js" } }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, "node_modules/cosmiconfig": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", @@ -5696,6 +5942,31 @@ "node": ">=10" } }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", + "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -5731,7 +6002,8 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/d3-array": { "version": "3.2.4", @@ -5855,9 +6127,9 @@ } }, "node_modules/daisyui": { - "version": "5.5.8", - "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-5.5.8.tgz", - "integrity": "sha512-6psL9jIEOFOw68V10j/BKCWcRgx8dh81mmNxShr+g7HDM6UHNoPharlp9zq/PQkHNuGU1ZQsajR3HgpvavbRKQ==", + "version": "5.5.14", + "resolved": "https://registry.npmjs.org/daisyui/-/daisyui-5.5.14.tgz", + "integrity": "sha512-L47rvw7I7hK68TA97VB8Ee0woHew+/ohR6Lx6Ah/krfISOqcG4My7poNpX5Mo5/ytMxiR40fEaz6njzDi7cuSg==", "dev": true, "license": "MIT", "funding": { @@ -5941,6 +6213,12 @@ "integrity": "sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==", "license": "MIT" }, + "node_modules/dayjs": { + "version": "1.11.19", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", + "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", + "license": "MIT" + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -6103,11 +6381,57 @@ "node": ">= 0.4" } }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "license": "BSD-3-Clause", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/duplexer2/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/duplexer2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/duplexer2/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/duplexer2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/embla-carousel": { "version": "8.6.0", "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz", "integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/embla-carousel-react": { "version": "8.6.0", @@ -6144,6 +6468,15 @@ "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==", "license": "MIT" }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/enhanced-resolve": { "version": "5.18.3", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", @@ -6368,6 +6701,7 @@ "integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -6541,6 +6875,7 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -6802,6 +7137,39 @@ "node": ">=0.8.x" } }, + "node_modules/exceljs": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/exceljs/-/exceljs-4.4.0.tgz", + "integrity": "sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg==", + "license": "MIT", + "dependencies": { + "archiver": "^5.0.0", + "dayjs": "^1.8.34", + "fast-csv": "^4.3.1", + "jszip": "^3.10.1", + "readable-stream": "^3.6.0", + "saxes": "^5.0.1", + "tmp": "^0.2.0", + "unzipper": "^0.10.11", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=8.3.0" + } + }, + "node_modules/fast-csv": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-4.3.6.tgz", + "integrity": "sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==", + "license": "MIT", + "dependencies": { + "@fast-csv/format": "4.3.5", + "@fast-csv/parse": "4.3.6" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -7061,6 +7429,34 @@ "react": ">=16.8.0" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -7188,6 +7584,27 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -7256,7 +7673,6 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, "license": "ISC" }, "node_modules/graphemer": { @@ -7423,6 +7839,26 @@ "integrity": "sha512-fXHXcGFTXOvZTSkPJuGOQf5Lv5T/R2itiiCVPg9LxAje5D00O0pP83yJShFq5V89Ly//Gt6acj7z8pbBr34stw==", "license": "ISC" }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -7433,6 +7869,12 @@ "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, "node_modules/immer": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/immer/-/immer-10.2.0.tgz", @@ -7469,6 +7911,17 @@ "node": ">=0.8.19" } }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -8055,6 +8508,7 @@ "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-3.0.4.tgz", "integrity": "sha512-dc6oQ8y37rRcHn316s4ngz/nOjayLF/FFxBF4V9zamQKRqXxyiH1zagkCdktdWhtoQId5K20xt1lB90XzkB+hQ==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.28.4", "fast-png": "^6.2.0", @@ -8092,6 +8546,54 @@ "node": ">=4.0" } }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -8122,6 +8624,54 @@ "node": ">=0.10" } }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -8136,6 +8686,15 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/lightningcss": { "version": "1.30.2", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", @@ -8422,6 +8981,12 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "license": "MIT" }, + "node_modules/listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", + "license": "ISC" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -8450,6 +9015,73 @@ "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", "license": "MIT" }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "license": "MIT" + }, + "node_modules/lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", + "license": "MIT" + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", + "license": "MIT" + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", + "license": "MIT" + }, + "node_modules/lodash.groupby": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", + "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", + "license": "MIT" + }, + "node_modules/lodash.isfunction": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", + "license": "MIT" + }, + "node_modules/lodash.isnil": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz", + "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isundefined": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", + "integrity": "sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -8457,6 +9089,18 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", + "license": "MIT" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "license": "MIT" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -8558,7 +9202,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -8571,12 +9214,23 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/moment": { "version": "2.30.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", @@ -8723,6 +9377,15 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/normalize-svg-path": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/normalize-svg-path/-/normalize-svg-path-1.1.0.tgz", @@ -8854,6 +9517,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -8974,6 +9646,15 @@ "node": ">=8" } }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -9096,6 +9777,12 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -9274,6 +9961,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -9304,6 +9992,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -9371,7 +10060,8 @@ "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/react-number-format": { "version": "5.4.4", @@ -9388,6 +10078,7 @@ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", "license": "MIT", + "peer": true, "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" @@ -9522,6 +10213,50 @@ "react-dom": ">=16.6.0" } }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/recharts": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.6.0.tgz", @@ -9556,7 +10291,8 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/redux-thunk": { "version": "3.1.0", @@ -9705,6 +10441,19 @@ "node": ">= 0.8.15" } }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -9804,6 +10553,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", @@ -9872,6 +10633,12 @@ "node": ">= 0.4" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, "node_modules/sharp": { "version": "0.34.4", "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.4.tgz", @@ -10347,6 +11114,22 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/text-segmentation": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", @@ -10422,6 +11205,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -10429,6 +11213,15 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/tmp": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -10448,6 +11241,15 @@ "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==", "license": "MIT" }, + "node_modules/traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "license": "MIT/X11", + "engines": { + "node": "*" + } + }, "node_modules/ts-api-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", @@ -10589,6 +11391,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -10684,6 +11487,60 @@ "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, + "node_modules/unzipper": { + "version": "0.10.14", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz", + "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", + "license": "MIT", + "dependencies": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "^1.0.12", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + } + }, + "node_modules/unzipper/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/unzipper/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/unzipper/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/unzipper/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -10788,6 +11645,15 @@ "base64-arraybuffer": "^1.0.2" } }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/vaul": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vaul/-/vaul-1.1.2.tgz", @@ -10952,6 +11818,12 @@ "node": ">=0.10.0" } }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, "node_modules/xlsx": { "version": "0.20.3", "resolved": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz", @@ -10964,6 +11836,12 @@ "node": ">=0.8" } }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "license": "MIT" + }, "node_modules/yaml": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", @@ -11004,6 +11882,41 @@ "type-fest": "^2.19.0" } }, + "node_modules/zip-stream": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", + "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^3.0.4", + "compress-commons": "^4.1.2", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zip-stream/node_modules/archiver-utils": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", + "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", + "license": "MIT", + "dependencies": { + "glob": "^7.2.3", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/zustand": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.8.tgz", diff --git a/package.json b/package.json index 981413b3..edd750f6 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "clsx": "^2.1.1", "cmdk": "^1.1.1", "embla-carousel-react": "^8.6.0", + "exceljs": "^4.4.0", "formik": "^2.4.6", "html-to-image": "^1.11.13", "input-otp": "^1.4.2", @@ -55,7 +56,7 @@ "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", - "daisyui": "^5.5.8", + "daisyui": "^5.5.14", "eslint": "^9", "eslint-config-next": "^15.5.7", "husky": "^9.1.7", diff --git a/src/app/globals.css b/src/app/globals.css index 0e3d1e43..eda1deab 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -57,6 +57,7 @@ @theme { --font-inter: var(--font-inter); + --font-roboto: var(--font-roboto); --container-sm: 40rem; --container-md: 48rem; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 91e9d624..a580cc53 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,5 +1,5 @@ import type { Metadata, Viewport } from 'next'; -import { Inter } from 'next/font/google'; +import { Inter, Roboto } from 'next/font/google'; import '@/app/globals.css'; import { Toaster } from 'react-hot-toast'; @@ -12,6 +12,12 @@ const inter = Inter({ subsets: ['latin'], }); +const roboto = Roboto({ + variable: '--font-roboto', + subsets: ['latin'], + weight: ['200', '300', '400', '500', '600', '700', '900'], +}); + export const viewport: Viewport = { themeColor: '#1f74bf', colorScheme: 'light', @@ -30,7 +36,9 @@ export default function RootLayout({ }>) { return ( - + {children} diff --git a/src/components/Drawer.tsx b/src/components/Drawer.tsx index 7b5e2374..bbc36782 100644 --- a/src/components/Drawer.tsx +++ b/src/components/Drawer.tsx @@ -162,7 +162,7 @@ const Drawer = ({
diff --git a/src/components/MainDrawer.tsx b/src/components/MainDrawer.tsx index eaf1e2c1..056d67a4 100644 --- a/src/components/MainDrawer.tsx +++ b/src/components/MainDrawer.tsx @@ -26,29 +26,34 @@ const MainDrawerContent = () => { }; return ( -
-
- MBU Logo +
+
+
+ LTI Logo -

LTI ERP

+
+

LTI ERP

+

Lumbung Telur Indonesia

+
+
@@ -121,6 +126,10 @@ const MainDrawer = ({ setOpen={setMainDrawerOpen} openOnLarge sidebarContent={} + className={{ + drawerSide: 'border-r border-base-content/10', + drawerSidebarContent: 'min-w-[244px] lg:w-[244px]', + }} >
diff --git a/src/components/Table.tsx b/src/components/Table.tsx index 9791dd59..37bc118a 100644 --- a/src/components/Table.tsx +++ b/src/components/Table.tsx @@ -86,7 +86,7 @@ export const TABLE_DEFAULT_STYLING = { tableHeaderClassName: '', headerRowClassName: '', headerColumnClassName: - 'px-4 py-3 border-base-content/10 text-base-content/50', + 'px-4 py-3 border-base-content/10 text-base-content/50 text-sm font-medium', tableBodyClassName: '', bodyRowClassName: 'border-t border-base-content/10', bodyColumnClassName: 'px-4 py-3 text-base-content', @@ -222,14 +222,37 @@ const Table = ({ }, [pageSize, setPageSize]); return ( -
-
- - +
+
+
+ {table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => { const columnRelativeDepth = @@ -262,6 +285,7 @@ const Table = ({ { 'border-b': header.colSpan > 1, }, + TABLE_DEFAULT_STYLING.headerColumnClassName, tableClassNames.headerColumnClassName )} > @@ -311,7 +335,12 @@ const Table = ({ ))} - + {table.getRowModel().rows.map((row) => { const customRowContent = renderCustomRow?.(row); @@ -320,12 +349,19 @@ const Table = ({ } return ( - + {row.getVisibleCells().map((cell) => ( - + {renderFooter && ( - + {table.getAllLeafColumns().map((column) => ( - + - + @@ -2224,51 +2254,43 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { - + - + - + @@ -2280,18 +2302,20 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { - + @@ -2468,6 +2492,14 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { wrapper: 'w-full min-w-24', }} placeholder='Masukkan jumlah pakai' + inputSuffix={ + stock.product_warehouse_id + ? getProductUomSuffix( + stock.product_warehouse_id, + 'stock' + ) + : null + } /> {(type as 'add' | 'edit' | 'detail') !== 'detail' && getStockUsageAdornment(idx)} @@ -2663,6 +2695,14 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { wrapper: 'w-full min-w-24', }} placeholder='Masukkan jumlah deplesi' + inputSuffix={ + depletion.product_warehouse_id + ? getProductUomSuffix( + depletion.product_warehouse_id, + 'depletion' + ) + : null + } /> {(type as 'add' | 'edit' | 'detail') !== 'detail' && ( @@ -2759,7 +2799,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { )} - + {(type as 'add' | 'edit' | 'detail') !== 'detail' && ( )} @@ -2856,6 +2896,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { wrapper: 'w-full min-w-24', }} placeholder='Masukkan jumlah telur' + inputSuffix={'Butir'} /> {(type as 'add' | 'edit' | 'detail') !== 'detail' && ( diff --git a/src/components/pages/production/transfer-to-laying/TransferToLayingsTable.tsx b/src/components/pages/production/transfer-to-laying/TransferToLayingsTable.tsx index 860c4616..309c42b2 100644 --- a/src/components/pages/production/transfer-to-laying/TransferToLayingsTable.tsx +++ b/src/components/pages/production/transfer-to-laying/TransferToLayingsTable.tsx @@ -1,6 +1,6 @@ 'use client'; -import { ChangeEventHandler, useState } from 'react'; +import { ChangeEventHandler, useEffect, useState } from 'react'; import useSWR from 'swr'; import { CellContext, @@ -20,33 +20,32 @@ import SelectInput, { OptionType, useSelect, } from '@/components/input/SelectInput'; -import RowDropdownOptions from '@/components/table/RowDropdownOptions'; -import RowCollapseOptions from '@/components/table/RowCollapseOptions'; -import TextInput from '@/components/input/TextInput'; import CheckboxInput from '@/components/input/CheckboxInput'; -import RowOptionsMenuWrapper from '@/components/table/RowOptionsMenuWrapper'; import ConfirmationModalWithNotes from '@/components/modal/ConfirmationModalWithNotes'; import RequirePermission from '@/components/helper/RequirePermission'; +import DateInput from '@/components/input/DateInput'; +import PopoverButton from '@/components/popover/PopoverButton'; import { TransferToLaying } from '@/types/api/production/transfer-to-laying'; import { TransferToLayingApi } from '@/services/api/production/transfer-to-laying'; import { cn, formatDate } from '@/lib/helper'; -import { isResponseSuccess } from '@/lib/api-helper'; +import { isResponseError, isResponseSuccess } from '@/lib/api-helper'; import { useTableFilter } from '@/services/hooks/useTableFilter'; -import { ROWS_OPTIONS } from '@/config/constant'; import { Flock } from '@/types/api/master-data/flock'; -import { FlockApi } from '@/services/api/master-data'; -import PillBadge from '@/components/PillBadge'; +import { ProjectFlockApi } from '@/services/api/production'; +import Badge from '@/components/Badge'; +import { Color } from '@/types/theme'; +import PopoverContent from '@/components/popover/PopoverContent'; const RowOptionsMenu = ({ - type = 'dropdown', props, + popoverPosition = 'bottom', approveClickHandler, rejectClickHandler, deleteClickHandler, }: { - type: 'dropdown' | 'collapse'; props: CellContext; + popoverPosition: 'bottom' | 'top'; approveClickHandler: () => void; rejectClickHandler: () => void; deleteClickHandler: () => void; @@ -60,80 +59,99 @@ const RowOptionsMenu = ({ const showApproveButton = showEditButton; const showRejectButton = showEditButton; + const popoverId = `transferToLaying#${props.row.original.id}`; + const popoverAnchorName = `--anchor-transferToLaying#${props.row.original.id}`; + return ( - - - - +
+ + + - {showEditButton && ( - - - - )} + +
+ + + - {/* TODO: apply RBAC */} - {showApproveButton && ( - - - - )} - {showRejectButton && ( - - - - )} - {showDeleteButton && ( - - - - )} - + {showEditButton && ( + + + + )} + + {showApproveButton && ( + + + + )} + + {showRejectButton && ( + + + + )} + + {showDeleteButton && ( + +
+ +
+ )} +
+
+
); }; @@ -150,6 +168,8 @@ const TransferToLayingsTable = () => { transferDate: '', flockSource: '', flockDestination: '', + filter_by: '', + sort_by: '', }, paramMap: { page: 'page', @@ -157,6 +177,8 @@ const TransferToLayingsTable = () => { transferDate: 'transfer_date', flockSource: 'flock_source', flockDestination: 'flock_destination', + filter_by: 'filter_by', + sort_by: 'sort_by', }, }); @@ -181,7 +203,7 @@ const TransferToLayingsTable = () => { isLoadingOptions: isLoadingFlockSourceOptions, loadMore: loadMoreFlockSource, hasMore: hasMoreFlockSource, - } = useSelect(FlockApi.basePath, 'id', 'name'); + } = useSelect(ProjectFlockApi.basePath, 'id', 'flock_name'); const { setInputValue: setFlockDestinationInputValue, @@ -189,7 +211,7 @@ const TransferToLayingsTable = () => { isLoadingOptions: isLoadingFlockDestinationOptions, loadMore: loadMoreFlockDestination, hasMore: hasMoreFlockDestination, - } = useSelect(FlockApi.basePath, 'id', 'name'); + } = useSelect(ProjectFlockApi.basePath, 'id', 'flock_name'); // Flocks value const [selectedFlockSource, setSelectedFlockSource] = @@ -244,13 +266,6 @@ const TransferToLayingsTable = () => { ); }, }, - { - header: '#', - cell: (props) => - tableFilterState.pageSize * (tableFilterState.page - 1) + - props.row.index + - 1, - }, { accessorKey: 'transfer_date', header: 'Tanggal Transfer', @@ -274,6 +289,7 @@ const TransferToLayingsTable = () => { { accessorKey: 'notes', header: 'Alasan Transfer', + enableSorting: false, }, { header: 'Status', @@ -282,34 +298,39 @@ const TransferToLayingsTable = () => { props.row.original.approval.action === 'REJECTED'; let latestApprovalStepName = props.row.original.approval.step_name; - let pillBadgeColor: 'yellow' | 'green' | 'gray' | 'red' = 'gray'; + let badgeColor: Color = 'neutral'; switch (latestApprovalStepName.toLowerCase()) { case 'pengajuan': - pillBadgeColor = 'yellow'; + badgeColor = 'neutral'; break; case 'disetujui': - pillBadgeColor = 'green'; + badgeColor = 'success'; break; } if (isLatestApprovalRejected) { - pillBadgeColor = 'red'; + badgeColor = 'error'; latestApprovalStepName = 'Ditolak'; } return ( - + + + {latestApprovalStepName} + ); }, }, { - header: 'Aksi', + id: 'actions', cell: (props) => { const currentPageSize = props.table.getPaginationRowModel().rows.length; const currentPageRows = props.table.getPaginationRowModel().flatRows; @@ -346,31 +367,13 @@ const TransferToLayingsTable = () => { }; return ( - <> - {currentPageSize > 3 && ( - - - - )} - - {currentPageSize <= 3 && ( - - - - )} - + ); }, }, @@ -397,17 +400,21 @@ const TransferToLayingsTable = () => { const confirmationModalDeleteClickHandler = async () => { setIsDeleteLoading(true); - try { - await TransferToLayingApi.delete(selectedTransferToLaying?.id as number); + const deleteResponse = await TransferToLayingApi.delete( + selectedTransferToLaying?.id as number + ); - toast.success('Berhasil menghapus data transfer ke laying!'); - refreshTransferToLayings(); - } catch (error) { - toast.success('Gagal menghapus data transfer ke laying!'); - } finally { - deleteModal.closeModal(); + if (isResponseError(deleteResponse)) { + toast.error(deleteResponse.message); setIsDeleteLoading(false); + return; } + + refreshTransferToLayings(); + + deleteModal.closeModal(); + toast.success('Berhasil menghapus data transfer ke laying!'); + setIsDeleteLoading(false); }; const confirmationModalApproveClickHandler = async (notes: string) => { @@ -499,20 +506,19 @@ const TransferToLayingsTable = () => { ); }; - // track sorting - // useEffect(() => { - // const isNameSorted = sorting.find((sortItem) => sortItem.id === 'name'); - - // if (!isNameSorted) { - // updateFilter('nameSort', ''); - // } else { - // updateFilter('nameSort', isNameSorted.desc ? 'desc' : 'asc'); - // } - // }, [sorting, updateFilter]); + useEffect(() => { + if (sorting.length === 1) { + updateFilter('filter_by', sorting[0].id); + updateFilter('sort_by', sorting[0].desc ? 'desc' : 'asc'); + } else { + updateFilter('filter_by', ''); + updateFilter('sort_by', ''); + } + }, [sorting]); return ( <> -
+
@@ -579,12 +585,10 @@ const TransferToLayingsTable = () => {
- { wrapper: 'col-span-12 sm:col-span-3', }} /> - -
@@ -653,26 +643,21 @@ const TransferToLayingsTable = () => { : 0 } onPageChange={setPage} + onPageSizeChange={setPageSize} isLoading={isLoading} sorting={sorting} setSorting={setSorting} rowSelection={rowSelection} setRowSelection={setRowSelection} enableRowSelection={tableEnableRowSelectionHandler} + withCheckbox className={{ containerClassName: cn({ - 'mb-20': + 'w-full mb-20': isResponseSuccess(transferToLayings) && transferToLayings?.data?.length === 0, }), - tableWrapperClassName: 'overflow-x-auto min-h-full!', - tableClassName: 'font-inter w-full table-auto min-h-full!', - headerRowClassName: 'border-b border-b-gray-200', - headerColumnClassName: - 'px-6 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end', - bodyRowClassName: 'border-b border-b-gray-200', - bodyColumnClassName: - 'px-6 py-3 last:flex last:flex-row last:justify-end', + headerColumnClassName: 'text-nowrap', }} />
diff --git a/src/components/pages/production/transfer-to-laying/form/TransferToLayingForm.schema.ts b/src/components/pages/production/transfer-to-laying/form/TransferToLayingForm.schema.ts index 42387992..6e47c0ca 100644 --- a/src/components/pages/production/transfer-to-laying/form/TransferToLayingForm.schema.ts +++ b/src/components/pages/production/transfer-to-laying/form/TransferToLayingForm.schema.ts @@ -80,7 +80,7 @@ export const TransferToLayingFormSchema: Yup.ObjectSchema formatNumber(props.row.original.average_weight_kg), + footer: () => { + const totalAverageWeightKg = isResponseSuccess(dailyMarketings) + ? dailyMarketings?.total?.average_weight_kg + : 0; + + return totalAverageWeightKg ? formatNumber(totalAverageWeightKg) : '-'; + }, }, { accessorKey: 'total_weight', @@ -140,6 +147,13 @@ const DailyMarketingsTable = ({ accessorKey: 'sales_price', header: 'Harga Jual (Rp)', cell: (props) => formatCurrency(props.row.original.sales_price_per_kg), + footer: () => { + const totalSalesPrice = isResponseSuccess(dailyMarketings) + ? dailyMarketings?.total?.average_sales_price + : 0; + + return totalSalesPrice ? formatNumber(totalSalesPrice) : '-'; + }, }, { accessorKey: 'hpp_price', diff --git a/src/components/pages/report/finance/export/CustomerPaymentExportXLSX.tsx b/src/components/pages/report/finance/export/CustomerPaymentExportXLSX.tsx index 830df633..fec4dc6b 100644 --- a/src/components/pages/report/finance/export/CustomerPaymentExportXLSX.tsx +++ b/src/components/pages/report/finance/export/CustomerPaymentExportXLSX.tsx @@ -1,6 +1,6 @@ 'use client'; -import * as XLSX from 'xlsx'; +import ExcelJS from 'exceljs'; import { formatDate, formatCurrency, formatNumber } from '@/lib/helper'; import { CustomerPaymentReport } from '@/types/api/report/customer-payment'; @@ -8,104 +8,130 @@ interface CustomerPaymentExportExcelParams { data: CustomerPaymentReport[]; } -export const generateCustomerPaymentExcel = ( +export const generateCustomerPaymentExcel = async ( params: CustomerPaymentExportExcelParams -): void => { +): Promise => { if (!params.data || params.data.length === 0) { return; } - const workbook = XLSX.utils.book_new(); + const workbook = new ExcelJS.Workbook(); - params.data.forEach((customerReport) => { + const columns = [ + { header: 'No', key: 'no', width: 5 }, + { header: 'Tanggal DO/Bayar', key: 'transDate', width: 15 }, + { header: 'Tanggal Realisasi', key: 'deliveryDate', width: 15 }, + { header: 'Aging', key: 'aging', width: 8 }, + { header: 'Referensi', key: 'reference', width: 12 }, + { header: 'Nomor Polisi', key: 'vehicleNumbers', width: 15 }, + { header: 'Ekor/Qty', key: 'qty', width: 10 }, + { header: 'Berat (Kg)', key: 'weight', width: 12 }, + { header: 'AVG', key: 'avgWeight', width: 10 }, + { header: 'Harga/Unit', key: 'unitPrice', width: 15 }, + { header: 'Harga Akhir', key: 'finalPrice', width: 15 }, + { header: 'Total', key: 'totalPrice', width: 15 }, + { header: 'Pembayaran', key: 'paymentAmount', width: 15 }, + { header: 'Saldo Piutang', key: 'accountsReceivable', width: 15 }, + { header: 'Keterangan', key: 'status', width: 20 }, + { header: 'Pengambilan', key: 'pickupInfo', width: 15 }, + { header: 'Sales/Marketing', key: 'salesPerson', width: 20 }, + ]; + + for (const customerReport of params.data) { const customerData = customerReport.rows; const customerName = customerReport.customer.name || 'Unknown Customer'; - const excelData: { [key: string]: string | number }[] = customerData.map( - (item, index) => ({ - No: index + 1, - 'Tanggal DO/Bayar': item.trans_date + const worksheet = workbook.addWorksheet(customerName.substring(0, 31)); + worksheet.columns = columns; + + customerData.forEach((item, index) => { + const row = worksheet.addRow({ + no: index + 1, + transDate: item.trans_date ? formatDate(item.trans_date, 'DD MMM YYYY') : '', - 'Tanggal Realisasi': item.delivery_date + deliveryDate: item.delivery_date ? formatDate(item.delivery_date, 'DD MMM YYYY') : '', - Aging: formatNumber(item.aging_day || 0), - Referensi: item.reference || '', - 'Nomor Polisi': Array.isArray(item.vehicle_numbers) + aging: formatNumber(item.aging_day || 0), + reference: item.reference || '', + vehicleNumbers: Array.isArray(item.vehicle_numbers) ? item.vehicle_numbers.join(', ') : '', - 'Ekor/Qty': formatNumber(item.qty || 0), - 'Berat (Kg)': formatNumber(item.weight || 0), - AVG: formatNumber(item.average_weight || 0), - 'Harga/Unit': formatCurrency(item.unit_price || 0), - 'Harga Akhir': formatCurrency(item.final_price || 0), - Total: formatCurrency(item.total_price || 0), - Pembayaran: formatCurrency(item.payment_amount || 0), - 'Saldo Piutang': formatCurrency(item.accounts_receivable || 0), - Keterangan: item.status || '', - Pengambilan: Array.isArray(item.pickup_info) + qty: formatNumber(item.qty || 0), + weight: formatNumber(item.weight || 0), + avgWeight: formatNumber(item.average_weight || 0), + unitPrice: formatCurrency(item.unit_price || 0), + finalPrice: formatCurrency(item.final_price || 0), + totalPrice: formatCurrency(item.total_price || 0), + paymentAmount: formatCurrency(item.payment_amount || 0), + accountsReceivable: formatCurrency(item.accounts_receivable || 0), + status: item.status || '', + pickupInfo: Array.isArray(item.pickup_info) ? item.pickup_info.join(', ') : '', - 'Sales/Marketing': item.sales_person || '', - }) - ); + salesPerson: item.sales_person || '', + }); + + const accountsReceivableCell = row.getCell('accountsReceivable'); + if ( + accountsReceivableCell.value && + accountsReceivableCell.value.toString().startsWith('-Rp') + ) { + accountsReceivableCell.font = { color: { argb: 'FFFF0000' } }; + } + }); if (customerReport.summary) { - excelData.push({ - No: 'Total', - 'Tanggal DO/Bayar': '', - 'Tanggal Realisasi': '', - Aging: '', - Referensi: '', - 'Nomor Polisi': '', - 'Ekor/Qty': formatNumber(customerReport.summary.total_qty || 0), - 'Berat (Kg)': formatNumber(customerReport.summary.total_weight || 0), - AVG: '', - 'Harga/Unit': '', - 'Harga Akhir': formatCurrency( + const summaryRow = worksheet.addRow({ + no: 'Total', + transDate: '', + deliveryDate: '', + aging: '', + reference: '', + vehicleNumbers: '', + qty: formatNumber(customerReport.summary.total_qty || 0), + weight: formatNumber(customerReport.summary.total_weight || 0), + avgWeight: '', + unitPrice: '', + finalPrice: formatCurrency( customerReport.summary.total_final_amount || 0 ), - Total: formatCurrency(customerReport.summary.total_grand_amount || 0), - Pembayaran: formatCurrency(customerReport.summary.total_payment || 0), - 'Saldo Piutang': formatCurrency( + totalPrice: formatCurrency( + customerReport.summary.total_grand_amount || 0 + ), + paymentAmount: formatCurrency( + customerReport.summary.total_payment || 0 + ), + accountsReceivable: formatCurrency( customerReport.summary.total_accounts_receivable || 0 ), - Keterangan: '', - Pengambilan: '', - 'Sales/Marketing': '', + status: '', + pickupInfo: '', + salesPerson: '', }); + + const summaryAccountsReceivableCell = + summaryRow.getCell('accountsReceivable'); + if ( + summaryAccountsReceivableCell.value && + summaryAccountsReceivableCell.value.toString().startsWith('-Rp') + ) { + summaryAccountsReceivableCell.font = { color: { argb: 'FFFF0000' } }; + } } - - const worksheet = XLSX.utils.json_to_sheet(excelData); - - const colWidths = [ - { wch: 5 }, // No - { wch: 15 }, // Tanggal DO/Bayar - { wch: 15 }, // Tanggal Realisasi - { wch: 8 }, // Aging - { wch: 12 }, // Referensi - { wch: 15 }, // Nomor Polisi - { wch: 10 }, // Ekor/Qty - { wch: 12 }, // Berat - { wch: 10 }, // AVG - { wch: 15 }, // Harga/Unit - { wch: 15 }, // Harga Akhir - { wch: 15 }, // Total - { wch: 15 }, // Pembayaran - { wch: 15 }, // Saldo Piutang - { wch: 20 }, // Keterangan - { wch: 15 }, // Pengambilan - { wch: 20 }, // Sales/Marketing - ]; - worksheet['!cols'] = colWidths; - - const sheetName = - customerName.length > 31 ? customerName.substring(0, 31) : customerName; - XLSX.utils.book_append_sheet(workbook, worksheet, sheetName); - }); + } const filename = `laporan-kontrol-pembayaran-customer-dicetak-pada-${formatDate(new Date(), 'YYYY-MM-DD-HHmm')}.xlsx`; - XLSX.writeFile(workbook, filename); + const buffer = await workbook.xlsx.writeBuffer(); + const blob = new Blob([buffer], { + type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + }); + const url = window.URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = filename; + link.click(); + window.URL.revokeObjectURL(url); }; diff --git a/src/components/pages/report/finance/tab/CustomerPaymentTab.tsx b/src/components/pages/report/finance/tab/CustomerPaymentTab.tsx index b5e8e438..c6bad2a4 100644 --- a/src/components/pages/report/finance/tab/CustomerPaymentTab.tsx +++ b/src/components/pages/report/finance/tab/CustomerPaymentTab.tsx @@ -252,7 +252,7 @@ const CustomerPaymentTab = () => { return; } - generateCustomerPaymentExcel({ data: allDataForExport }); + await generateCustomerPaymentExcel({ data: allDataForExport }); toast.success('Excel berhasil dibuat dan diunduh.'); } catch { toast.error('Gagal membuat Excel. Silakan coba lagi.'); diff --git a/src/components/popover/PopoverButton.tsx b/src/components/popover/PopoverButton.tsx new file mode 100644 index 00000000..ad72e24e --- /dev/null +++ b/src/components/popover/PopoverButton.tsx @@ -0,0 +1,29 @@ +import Button, { ButtonProps } from '@/components/Button'; + +export interface PopoverButtonProps extends ButtonProps { + popoverTarget: string; + anchorName: string; +} + +const PopoverButton = ({ + children, + popoverTarget, + anchorName, + ...props +}: PopoverButtonProps) => { + return ( + + ); +}; + +export default PopoverButton; diff --git a/src/components/popover/PopoverContent.tsx b/src/components/popover/PopoverContent.tsx new file mode 100644 index 00000000..9a634abb --- /dev/null +++ b/src/components/popover/PopoverContent.tsx @@ -0,0 +1,71 @@ +import { cn } from '@/lib/helper'; + +export interface PopoverContentProps { + children: React.ReactNode; + id: string; + anchorName: string; // Must include `--` like "--menu-anchor" + popover?: 'auto' | 'hint' | 'manual'; + position?: + | 'top' + | 'bottom' + | 'left' + | 'right' + | 'top-start' + | 'top-end' + | 'bottom-start' + | 'bottom-end' + | 'left-start' + | 'left-end' + | 'right-start' + | 'right-end'; + className?: string; +} + +const positionAreaMap: Record< + NonNullable, + string +> = { + top: 'top center', + bottom: 'bottom center', + left: 'left center', + right: 'right center', + + 'top-start': 'top left', + 'top-end': 'top right', + 'bottom-start': 'bottom left', + 'bottom-end': 'bottom right', + + 'left-start': 'left top', + 'left-end': 'left bottom', + + 'right-start': 'right top', + 'right-end': 'right bottom', +}; + +const PopoverContent = ({ + children, + id, + anchorName, + popover = 'auto', + position = 'bottom-start', + className, +}: PopoverContentProps) => { + return ( +
+ {children} +
+ ); +}; + +export default PopoverContent; diff --git a/src/components/table/RowCollapseOptions.tsx b/src/components/table/RowCollapseOptions.tsx index ce90314c..532a96b4 100644 --- a/src/components/table/RowCollapseOptions.tsx +++ b/src/components/table/RowCollapseOptions.tsx @@ -12,7 +12,7 @@ const RowCollapseOptions = ({ children }: RowCollapseOptionsProps) => { return ( + } diff --git a/src/components/table/RowDropdownOptions.tsx b/src/components/table/RowDropdownOptions.tsx index 1f1bc81b..a2535c2b 100644 --- a/src/components/table/RowDropdownOptions.tsx +++ b/src/components/table/RowDropdownOptions.tsx @@ -21,7 +21,7 @@ const RowDropdownOptions = ({ 'dropdown-end': isLast2Rows, })} > - diff --git a/src/config/constant.ts b/src/config/constant.ts index b3621c8f..eb271b9b 100644 --- a/src/config/constant.ts +++ b/src/config/constant.ts @@ -28,7 +28,7 @@ export const MAIN_DRAWER_LINKS: SidebarMenuItem[] = [ permission: ['lti.daily_checklist.dashboard.list'], }, { - text: 'Daily Checklist', + text: 'Formulir', link: '/daily-checklist/daily-checklist', icon: 'lucide:clipboard-check', permission: ['lti.daily_checklist.create'], @@ -94,7 +94,7 @@ export const MAIN_DRAWER_LINKS: SidebarMenuItem[] = [ permission: ['lti.production.recording.list'], }, { - text: 'Transfer to Laying', + text: 'Transfer ke Laying', link: '/production/transfer-to-laying', }, { @@ -389,6 +389,14 @@ export const FINANCE_INITIAL_BALANCE_TYPE_OPTIONS = [ { label: 'Saldo Awal Negatif', value: 'NEGATIVE' }, ]; +export const FINANCE_TRANSACTION_TYPE_OPTIONS = [ + { label: 'Pembelian', value: 'PEMBELIAN' }, + { label: 'Penjualan', value: 'PENJUALAN' }, + { label: 'Biaya', value: 'BIAYA' }, + { label: 'Saldo Awal', value: 'SALDO_AWAL' }, + { label: 'Injection', value: 'INJECTION' }, +]; + export const FINANCE_TRANSACTION_STATUS = ['PENJUALAN', 'PEMBELIAN', 'BIAYA']; export const FINANCE_INITIAL_BALANCE_STATUS = ['SALDO_AWAL']; diff --git a/src/figma-make/components/pages/daily-checklist/DailyChecklistContent.tsx b/src/figma-make/components/pages/daily-checklist/DailyChecklistContent.tsx index 314381fd..9b2b6bf2 100644 --- a/src/figma-make/components/pages/daily-checklist/DailyChecklistContent.tsx +++ b/src/figma-make/components/pages/daily-checklist/DailyChecklistContent.tsx @@ -41,6 +41,7 @@ import { PhaseActivity } from '@/types/api/daily-checklist/phase-activity'; import DebouncedTextArea from '@/components/input/DebouncedTextArea'; import DropFileInput from '@/components/input/DropFileInput'; import Link from 'next/link'; +import { useRouter, useSearchParams, usePathname } from 'next/navigation'; import { Icon } from '@iconify/react'; // Static categories @@ -51,7 +52,7 @@ const CATEGORIES = [ { value: 'produksi_close', label: 'Produksi Close' }, ]; -const TIME_TYPE_ORDER = ['umum', 'pagi', 'siang', 'sore', 'malam']; +const TIME_TYPE_ORDER = ['Umum', 'Pagi', 'Siang', 'Sore', 'Malam']; const TIME_TYPE_LABELS: { [key: string]: string } = { Umum: 'Umum', Pagi: 'Pagi', @@ -67,7 +68,23 @@ interface Phase { } export function DailyChecklistContent() { - const [kandangId, setKandangId] = useState(''); + const router = useRouter(); + const pathname = usePathname(); + const searchParams = useSearchParams(); + + const [kandangId, setKandangId] = useState( + searchParams.get('kandang_id') || '' + ); + const [date, setDate] = useState(() => { + const paramDate = searchParams.get('date'); + if (paramDate) return paramDate; + + const today = new Date(); + return today.toISOString().split('T')[0]; + }); + const [selectedCategory, setSelectedCategory] = useState( + searchParams.get('category') || '' + ); const { options: kandangOptions, isLoadingOptions: isLoadingKandangs } = useSelect(KandangApi.basePath, 'id', 'name', 'search', { @@ -104,12 +121,6 @@ export function DailyChecklistContent() { ? employeesRes.data || [] : []; - const [date, setDate] = useState(() => { - const today = new Date(); - return today.toISOString().split('T')[0]; - }); - - const [selectedCategory, setSelectedCategory] = useState(''); const [selectedPhaseIds, setSelectedPhaseIds] = useState([]); const [selectedEmployees, setSelectedEmployees] = useState< @@ -118,7 +129,7 @@ export function DailyChecklistContent() { const [dailyChecklistId, setDailyChecklistId] = useState(null); const [checklistStatus, setChecklistStatus] = useState('DRAFT'); - const [isEditMode, setIsEditMode] = useState(false); + // const [isEditMode, setIsEditMode] = useState(false); // Activities grouped by phase const [activitiesByPhase, setActivitiesByPhase] = useState<{ @@ -148,13 +159,57 @@ export function DailyChecklistContent() { const [searchAbk, setSearchAbk] = useState(''); const [searchPhase, setSearchPhase] = useState(''); - const [loading, setLoading] = useState(false); + const [isLoadingSubmit, setIsLoadingSubmit] = useState(false); + const [isLoadingDraft, setIsLoadingDraft] = useState(false); const [initialLoading, setInitialLoading] = useState(true); const [existingDocuments, setExistingDocuments] = useState([]); const [documents, setDocuments] = useState([]); const [deletedDocumentIds, setDeletedDocumentIds] = useState([]); + // Sync state to URL query params + useEffect(() => { + const params = new URLSearchParams(searchParams.toString()); + let pendingUpdate = false; + + // Sync date + if (date) { + if (params.get('date') !== date) { + params.set('date', date); + pendingUpdate = true; + } + } else if (params.has('date')) { + params.delete('date'); + pendingUpdate = true; + } + + // Sync kandang_id + if (kandangId) { + if (params.get('kandang_id') !== kandangId) { + params.set('kandang_id', kandangId); + pendingUpdate = true; + } + } else if (params.has('kandang_id')) { + params.delete('kandang_id'); + pendingUpdate = true; + } + + // Sync category + if (selectedCategory) { + if (params.get('category') !== selectedCategory) { + params.set('category', selectedCategory); + pendingUpdate = true; + } + } else if (params.has('category')) { + params.delete('category'); + pendingUpdate = true; + } + + if (pendingUpdate) { + router.replace(`${pathname}?${params.toString()}`); + } + }, [date, kandangId, selectedCategory, pathname, router, searchParams]); + // Format date for display const formatDateForDisplay = (dateStr: string) => { if (!dateStr) return 'Pilih tanggal'; @@ -179,7 +234,7 @@ export function DailyChecklistContent() { if (!date || !kandangId || !selectedCategory) { setDailyChecklistId(null); setChecklistStatus('DRAFT'); - setIsEditMode(false); + // setIsEditMode(false); setSelectedPhaseIds([]); setActivitiesByPhase({}); setTaskIdsByPhaseActivityId({}); @@ -216,7 +271,7 @@ export function DailyChecklistContent() { existingPhases.data.phases.length > 0 ) { // Existing checklist - EDIT MODE - setIsEditMode(true); + // setIsEditMode(true); const phaseIds = existingPhases.data.phases.map((p) => String(p.phase_id) ); @@ -234,7 +289,7 @@ export function DailyChecklistContent() { } } else { // New checklist - CREATE MODE - setIsEditMode(false); + // setIsEditMode(false); setSelectedPhaseIds([]); } } catch (error) { @@ -608,7 +663,7 @@ export function DailyChecklistContent() { // taskId, // hasTaskId: !!taskId, // checklistStatus, - // isEditable, + // isChecklistStatusDraft, // }); if (!taskId) { @@ -618,7 +673,7 @@ export function DailyChecklistContent() { return; } - if (!isEditable) { + if (!isChecklistStatusDraft) { console.warn( '[CHECKBOX] Checklist is not editable, status:', checklistStatus @@ -736,7 +791,7 @@ export function DailyChecklistContent() { return; } - setLoading(true); + setIsLoadingSubmit(true); try { const submitRes = await DailyChecklistApi.submit( @@ -757,13 +812,15 @@ export function DailyChecklistContent() { console.error('Error submitting:', error); toast.error('Terjadi kesalahan'); } finally { - setLoading(false); + setIsLoadingSubmit(false); } }; const handleSaveDraft = async () => { if (!dailyChecklistId) return; + setIsLoadingDraft(true); + const uploadImageRes = await DailyChecklistApi.uploadImage( Number(dailyChecklistId), 'DRAFT', @@ -774,10 +831,12 @@ export function DailyChecklistContent() { if (isResponseError(uploadImageRes)) { console.error('Error saving draft:', uploadImageRes.message); toast.error('Gagal menyimpan draft'); + setIsLoadingDraft(false); return; } - toast.success('Draft tersimpan otomatis'); + setIsLoadingDraft(false); + toast.success('Draft tersimpan!'); }; // Filter functions @@ -825,7 +884,7 @@ export function DailyChecklistContent() { // Group activities by time_type within this phase phaseActivities.forEach((activity) => { - const timeType = activity.time_type || 'umum'; + const timeType = activity.time_type || 'Umum'; if (!grouped[phase.id].timeGroups[timeType]) { grouped[phase.id].timeGroups[timeType] = []; @@ -838,7 +897,7 @@ export function DailyChecklistContent() { return grouped; }; - const isEditable = checklistStatus === 'DRAFT'; + const isChecklistStatusDraft = checklistStatus === 'DRAFT'; if (initialLoading) { return ( @@ -871,7 +930,7 @@ export function DailyChecklistContent() {

Daily Checklist

- {isEditMode && ( + {isChecklistStatusDraft && ( @@ -921,7 +980,7 @@ export function DailyChecklistContent() {
@@ -1320,61 +1384,68 @@ export function DailyChecklistContent() { /> - + }} + className='p-1 rounded-full text-error focus-visible:text-error-content hover:text-error-content' + > + + + )} ) )} )} - { - setDocuments(files); - }} - onDelete={(deletedFileIdx: number) => { - const newRequestDocuments = [...documents]; + {isChecklistStatusDraft && ( + { + setDocuments(files); + }} + onDelete={(deletedFileIdx: number) => { + const newRequestDocuments = [...documents]; - newRequestDocuments?.splice(deletedFileIdx, 1); + newRequestDocuments?.splice(deletedFileIdx, 1); - setDocuments(newRequestDocuments); - }} - className={{ - wrapper: 'mt-6', - inputWrapper: 'flex items-center', - label: 'font-semibold text-gray-900', - }} - /> + setDocuments(newRequestDocuments); + }} + disabled={!isChecklistStatusDraft} + className={{ + wrapper: 'mt-6', + inputWrapper: 'flex items-center', + label: 'font-semibold text-gray-900', + }} + /> + )} )} @@ -1382,24 +1453,30 @@ export function DailyChecklistContent() { {dailyChecklistId && selectedPhaseIds.length > 0 && selectedEmployees.length > 0 && - isEditable && ( + isChecklistStatusDraft && (
)} @@ -1440,7 +1517,9 @@ export function DailyChecklistContent() { if (isAllPhasesSelected) { setTempSelectedPhaseIds([]); } else { - setTempSelectedPhaseIds(availablePhases.map((p) => p.id)); + setTempSelectedPhaseIds( + availablePhases.map((p) => String(p.id)) + ); } }} className='checkbox-clean' diff --git a/src/figma-make/components/pages/dashboard/Dashboard.tsx b/src/figma-make/components/pages/dashboard/Dashboard.tsx index f6d12d79..8953ccf6 100644 --- a/src/figma-make/components/pages/dashboard/Dashboard.tsx +++ b/src/figma-make/components/pages/dashboard/Dashboard.tsx @@ -83,10 +83,7 @@ export function Dashboard() { dateFrom && dateTo ? `${DailyChecklistApi.basePath}/summary?date_from=${dateFrom}&date_to=${dateTo}&kandang_id=${kandangFilter === 'ALL' ? '' : kandangFilter}&category=${categoryFilter === 'ALL' ? '' : categoryFilter}` : '', - httpClientFetcher, - { - keepPreviousData: true, - } + httpClientFetcher ); const { options: kandangOptions, isLoadingOptions: isLoadingKandangs } = @@ -311,14 +308,14 @@ export function Dashboard() { {chartData?.map((entry, index) => ( ))} @@ -370,7 +367,7 @@ export function Dashboard() {
{employeePerformance?.map((emp, index) => ( { + const formattedDate = new Date(item.date).toISOString().split('T')[0]; + const kandangId = item.kandang.id; + const category = item.category; + + router.push( + `/daily-checklist/daily-checklist?date=${formattedDate}&kandang_id=${kandangId}&category=${category}` + ); + }; + const handleApprove = (item: DailyChecklist) => { setSelectedItem(item); setShowApproveModal(true); @@ -357,7 +367,7 @@ export function ListDailyChecklistContent() { }, { accessorKey: 'updated_at', - header: 'Update At', + header: 'Diperbarui', enableSorting: false, cell: ({ row }) => formatDateTime(row.original.updated_at), }, @@ -377,6 +387,19 @@ export function ListDailyChecklistContent() { Detail + + {row.original.status === 'DRAFT' && ( + + )} + {row.original.status === 'SUBMITTED' && ( <> + + {row.original.status === 'DRAFT' && ( + + )} ), }, diff --git a/src/figma-make/components/pages/list-daily-checklist/detail/DetailDailyChecklistContent.tsx b/src/figma-make/components/pages/list-daily-checklist/detail/DetailDailyChecklistContent.tsx index a867c29d..0b3ece27 100644 --- a/src/figma-make/components/pages/list-daily-checklist/detail/DetailDailyChecklistContent.tsx +++ b/src/figma-make/components/pages/list-daily-checklist/detail/DetailDailyChecklistContent.tsx @@ -389,7 +389,7 @@ export function DetailDailyChecklistContent() { } = {}; phaseData.activities.forEach((activityData) => { - const timeType = activityData.time_type || 'umum'; + const timeType = activityData.time_type || 'Umum'; if (!timeGroups[timeType]) { timeGroups[timeType] = { activities: [] }; diff --git a/src/figma-make/components/pages/master-data/activity/MasterAktivitasContent.tsx b/src/figma-make/components/pages/master-data/activity/MasterAktivitasContent.tsx index 36774ce2..94ca4659 100644 --- a/src/figma-make/components/pages/master-data/activity/MasterAktivitasContent.tsx +++ b/src/figma-make/components/pages/master-data/activity/MasterAktivitasContent.tsx @@ -144,13 +144,9 @@ export function MasterAktivitasContent() { id: '', name: '', description: '', - time_type: 'umum', + time_type: '', }); - useEffect(() => { - setInitialLoading(false); - }, []); - // Phase handlers const handleAddPhase = () => { if (!selectedCategory) { @@ -277,7 +273,7 @@ export function MasterAktivitasContent() { return; } setActivityModalMode('create'); - setActivityForm({ id: '', name: '', description: '', time_type: 'umum' }); + setActivityForm({ id: '', name: '', description: '', time_type: '' }); setShowActivityModal(true); }; @@ -293,16 +289,25 @@ export function MasterAktivitasContent() { }; const handleSaveActivity = async () => { - if (!activityForm.name.trim()) { + const isTimeTypeValid = Boolean(activityForm.time_type); + const isNameValid = Boolean(activityForm.name.trim()); + const isNameLengthValid = activityForm.name.trim().length >= 3; + + if (!isNameValid) { toast.error('Nama aktivitas harus diisi'); return; } - if (activityForm.name.trim().length < 3) { + if (!isNameLengthValid) { toast.error('Nama aktivitas minimal 3 karakter!'); return; } + if (!isTimeTypeValid) { + toast.error('Tipe waktu harus diisi'); + return; + } + if (!selectedPhase) { toast.error('Pilih phase terlebih dahulu'); return; @@ -356,7 +361,7 @@ export function MasterAktivitasContent() { } setShowActivityModal(false); - setActivityForm({ id: '', name: '', description: '', time_type: 'umum' }); + setActivityForm({ id: '', name: '', description: '', time_type: '' }); } catch (error) { console.error('Error saving activity:', error); toast.error('Terjadi kesalahan saat menyimpan aktivitas'); @@ -423,6 +428,14 @@ export function MasterAktivitasContent() { } }; + useEffect(() => { + setInitialLoading(false); + }, []); + + useEffect(() => { + setSelectedPhase(null); + }, [selectedCategory]); + if (initialLoading) { return (
diff --git a/src/figma-make/components/pages/master-data/configuration/MasterConfigurationContent.tsx b/src/figma-make/components/pages/master-data/configuration/MasterConfigurationContent.tsx index 1358d6ba..9fa75c33 100644 --- a/src/figma-make/components/pages/master-data/configuration/MasterConfigurationContent.tsx +++ b/src/figma-make/components/pages/master-data/configuration/MasterConfigurationContent.tsx @@ -69,6 +69,7 @@ export function MasterConfigurationContent() { } ); + const [isFormInvalid, setIsFormInvalid] = useState(false); const [showModal, setShowModal] = useState(false); const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); const [configurationToDelete, setConfigurationToDelete] = useState< @@ -173,6 +174,26 @@ export function MasterConfigurationContent() { return; } + if ( + Number(configurationForm.percentage_threshold_enough) >= 100 || + Number(configurationForm.percentage_threshold_bad) >= 100 + ) { + setIsFormInvalid(true); + toast.error('Persentase threshold tidak boleh lebih dari 100'); + return; + } + + if ( + Number(configurationForm.percentage_threshold_enough) <= + Number(configurationForm.percentage_threshold_bad) + 1 + ) { + setIsFormInvalid(true); + toast.error( + 'Persentase threshold "kurang" harus lebih kecil dari persentase threshold "cukup"' + ); + return; + } + setLoading(true); try { @@ -443,14 +464,18 @@ export function MasterConfigurationContent() { type='number' id='percentageThresholdBad' value={configurationForm.percentage_threshold_bad} - onChange={(e) => + onChange={(e) => { + setIsFormInvalid(false); + setConfigurationForm({ ...configurationForm, percentage_threshold_bad: e.target.value, - }) - } + }); + }} placeholder='Kurang' - className='w-20' + className={cn('w-20', { + 'border-red-500': isFormInvalid, + })} disabled={loading} max={100} /> @@ -476,14 +501,17 @@ export function MasterConfigurationContent() { type='number' id='percentageThresholdEnough' value={configurationForm.percentage_threshold_enough} - onChange={(e) => + onChange={(e) => { + setIsFormInvalid(false); setConfigurationForm({ ...configurationForm, percentage_threshold_enough: e.target.value, - }) - } + }); + }} placeholder='Cukup' - className='w-20' + className={cn('w-20', { + 'border-red-500': isFormInvalid, + })} disabled={loading} min={Number(configurationForm.percentage_threshold_bad) + 1} max={100} diff --git a/src/figma-make/components/pages/reports/DailyChecklistReportsContent.tsx b/src/figma-make/components/pages/reports/DailyChecklistReportsContent.tsx index 61558d4f..f1c32198 100644 --- a/src/figma-make/components/pages/reports/DailyChecklistReportsContent.tsx +++ b/src/figma-make/components/pages/reports/DailyChecklistReportsContent.tsx @@ -292,7 +292,7 @@ export function DailyChecklistReportsContent() { }; const phaseChangeHandler = (value: string) => { - updateFilter('phase_id', value); + updateFilter('phase_id', value === 'ALL' ? '' : value); }; const employeeChangeHandler = (value: string) => { diff --git a/src/types/api/closing.d.ts b/src/types/api/closing.d.ts index ec256a45..8135c013 100644 --- a/src/types/api/closing.d.ts +++ b/src/types/api/closing.d.ts @@ -23,33 +23,18 @@ export type BaseSales = { qty: number; weight: number; avg_weight: number; - price: number; - total_price: number; + sales_price: number; + total_sales_price: number; + actual_price: number; + total_actual_price: number; kandang: Kandang; - payment_status: string; -}; - -export type BaseClosingSales = { - project_type: string; - flock_id: number; - period: number; - sales: BaseSales[]; -}; - -export type BaseSales = { - id: number; - realization_date: string; - age: number; - do_number: string; - product: Product; - customer: Customer; - qty: number; - weight: number; - avg_weight: number; - price: number; - total_price: number; - kandang: Kandang; - payment_status: string; +}; + +export type ClosingSalesSummary = { + total_sales_price: number; + avg_sales_price: number; + total_actual_price: number; + avg_actual_price: number; }; export type BaseClosingSales = { @@ -57,6 +42,7 @@ export type BaseClosingSales = { flock_id: number; period: number; sales: BaseSales[]; + summary: ClosingSalesSummary; }; export type BaseClosing = { diff --git a/src/types/api/inventory/product-warehouse.d.ts b/src/types/api/inventory/product-warehouse.d.ts index eda8d1b8..8bed1aba 100644 --- a/src/types/api/inventory/product-warehouse.d.ts +++ b/src/types/api/inventory/product-warehouse.d.ts @@ -1,11 +1,13 @@ import { BaseMetadata } from '@/types/api/api-general'; import { Warehouse } from '@/types/api/master-data/warehouse'; import { Product } from '@/types/api/master-data/product'; +import { Uom } from '@/types/api/master-data/uom'; export type BaseProductWarehouse = { id: number; product_id: number; warehouse_id: number; + uom: Uom; quantity: number; product: Product; warehouse: Warehouse; diff --git a/src/types/api/report/marketing.d.ts b/src/types/api/report/marketing.d.ts index 4a0ab306..20c60725 100644 --- a/src/types/api/report/marketing.d.ts +++ b/src/types/api/report/marketing.d.ts @@ -41,7 +41,9 @@ export type DailyMarketingRow = BaseMetadata & BaseDailyMarketingRow; export interface SalesSummary { total_qty: number; + average_weight_kg: number; total_weight_kg: number; + average_sales_price: number; total_sales_amount: number; total_hpp_amount: number; total_hpp_price_per_kg: number;
@@ -342,14 +378,25 @@ const Table = ({ ); })}
@@ -372,7 +419,13 @@ const Table = ({ emptyContent} {data.length > 0 && table.getRowModel().rows.length > 0 && !isLoading && ( -
+
{
  • {item.icon && } - {item.text} + {item.text}
  • ); @@ -62,12 +61,13 @@ const SidebarMenuItem = ({ item, activeLink }: SidebarMenuItemProps) => {
    {item.icon && } - {item.text} + {item.text}
      @@ -88,7 +88,7 @@ const SidebarMenuItem = ({ item, activeLink }: SidebarMenuItemProps) => { const SidebarMenu = ({ menu, activeLink }: SidebarMenuProps) => { return ( - + {menu.map((menuItem, menuIdx) => { return ( { header: '#', cell: (props) => props.row.index + 1, }, + { + accessorKey: 'project_name', + header: 'Flock', + }, { accessorKey: 'location_name', header: 'Lokasi', diff --git a/src/components/pages/closing/sale/SalesReportTable.tsx b/src/components/pages/closing/sale/SalesReportTable.tsx index 6c12347e..6ddd9e4f 100644 --- a/src/components/pages/closing/sale/SalesReportTable.tsx +++ b/src/components/pages/closing/sale/SalesReportTable.tsx @@ -6,7 +6,11 @@ import Table from '@/components/Table'; import Card from '@/components/Card'; import Badge from '@/components/Badge'; import { formatCurrency, formatNumber, formatDate } from '@/lib/helper'; -import { BaseClosingSales, BaseSales } from '@/types/api/closing'; +import { + BaseClosingSales, + BaseSales, + ClosingSalesSummary, +} from '@/types/api/closing'; import { Product } from '@/types/api/master-data/product'; import { Customer } from '@/types/api/master-data/customer'; import { Kandang } from '@/types/api/master-data/kandang'; @@ -24,14 +28,20 @@ const SalesReportTable = ({ return initialValues?.sales || []; }, [initialValues]); + const summary: ClosingSalesSummary | undefined = useMemo(() => { + return initialValues?.summary; + }, [initialValues]); + const totals = useMemo(() => { if (salesData.length === 0) { return { totalQuantity: 0, totalWeight: 0, avgWeight: 0, - avgPricePartner: 0, - totalPartner: 0, + avgSalesPrice: 0, + totalSalesPrice: 0, + avgActualPrice: 0, + totalActualPrice: 0, }; } @@ -45,26 +55,46 @@ const SalesReportTable = ({ ); const avgWeight = totalQuantity > 0 ? totalWeight / totalQuantity : 0; - const validPriceItems = salesData.filter( - (item) => item.price != null && item.price > 0 - ); - const avgPricePartner = - validPriceItems.length > 0 - ? validPriceItems.reduce((sum, item) => sum + item.price, 0) / - validPriceItems.length - : 0; - - const totalPartner = salesData.reduce( - (sum, item) => sum + (item.total_price || 0), + const totalSalesPrice = salesData.reduce( + (sum, item) => sum + (item.total_sales_price || 0), 0 ); + const validSalesPriceItems = salesData.filter( + (item) => item.sales_price != null && item.sales_price > 0 + ); + const avgSalesPrice = + validSalesPriceItems.length > 0 + ? validSalesPriceItems.reduce( + (sum, item) => sum + item.sales_price, + 0 + ) / validSalesPriceItems.length + : 0; + + const totalActualPrice = salesData.reduce( + (sum, item) => sum + (item.total_actual_price || 0), + 0 + ); + + const validActualPriceItems = salesData.filter( + (item) => item.actual_price != null && item.actual_price > 0 + ); + const avgActualPrice = + validActualPriceItems.length > 0 + ? validActualPriceItems.reduce( + (sum, item) => sum + item.actual_price, + 0 + ) / validActualPriceItems.length + : 0; + return { totalQuantity, totalWeight, avgWeight, - avgPricePartner, - totalPartner, + avgSalesPrice, + totalSalesPrice, + avgActualPrice, + totalActualPrice, }; }, [salesData]); @@ -161,50 +191,68 @@ const SalesReportTable = ({ ), }, { - id: 'price_partner', - accessorKey: 'price', - header: 'Harga Mitra (Rp)', + id: 'sales_price', + accessorKey: 'sales_price', + header: 'Harga Sales (Rp)', cell: (props) => { const value = props.getValue() as number; return
      {formatCurrency(value)}
      ; }, footer: () => (
      - {formatCurrency(totals.avgPricePartner)} + {summary + ? formatCurrency(summary.avg_sales_price) + : formatCurrency(totals.avgSalesPrice)}
      ), }, { - id: 'total_mitra', - accessorKey: 'total_price', - header: 'Total Mitra (Rp)', + id: 'total_sales_price', + accessorKey: 'total_sales_price', + header: 'Total Sales (Rp)', cell: (props) => { const value = props.getValue() as number; return
      {formatCurrency(value)}
      ; }, footer: () => (
      - {formatCurrency(totals.totalPartner)} + {summary + ? formatCurrency(summary.total_sales_price) + : formatCurrency(totals.totalSalesPrice)}
      ), }, { - id: 'price_act', - accessorKey: 'price', + id: 'actual_price', + accessorKey: 'actual_price', header: 'Harga Act (Rp)', cell: (props) => { const value = props.getValue() as number; return
      {formatCurrency(value)}
      ; }, + footer: () => ( +
      + {summary + ? formatCurrency(summary.avg_actual_price) + : formatCurrency(totals.avgActualPrice)} +
      + ), }, { - id: 'total_act', - accessorKey: 'total_price', + id: 'total_actual_price', + accessorKey: 'total_actual_price', header: 'Total Act (Rp)', cell: (props) => { const value = props.getValue() as number; return
      {formatCurrency(value)}
      ; }, + footer: () => ( +
      + {summary + ? formatCurrency(summary.total_actual_price) + : formatCurrency(totals.totalActualPrice)} +
      + ), }, { id: 'kandang', diff --git a/src/components/pages/finance/FinanceDetail.tsx b/src/components/pages/finance/FinanceDetail.tsx index 76d2d1a0..3ee9a7df 100644 --- a/src/components/pages/finance/FinanceDetail.tsx +++ b/src/components/pages/finance/FinanceDetail.tsx @@ -64,7 +64,7 @@ const FinanceDetail = ({ finance }: { finance: Finance }) => { }, { label: 'Nominal', - value: formatCurrency(finance.nominal), + value: formatCurrency(Math.abs(finance.nominal)), }, ].filter((item) => { // Hide party account number row if transaction type is INJECTION diff --git a/src/components/pages/finance/FinanceTable.tsx b/src/components/pages/finance/FinanceTable.tsx index ff62b9db..505e411f 100644 --- a/src/components/pages/finance/FinanceTable.tsx +++ b/src/components/pages/finance/FinanceTable.tsx @@ -19,6 +19,7 @@ import { FINANCE_INITIAL_BALANCE_STATUS, FINANCE_INJECTION_STATUS, FINANCE_TRANSACTION_STATUS, + FINANCE_TRANSACTION_TYPE_OPTIONS, } from '@/config/constant'; import { FinanceApi } from '@/services/api/finance'; import { isResponseSuccess } from '@/lib/api-helper'; @@ -65,24 +66,19 @@ const RowOptionsMenu = ({ {FINANCE_TRANSACTION_STATUS.includes( props.row.original.transaction_type - ) && - props.row.original.party?.type !== 'SUPPLIER' && ( - - - - )} + ) && ( + + + + )} {FINANCE_INITIAL_BALANCE_STATUS.includes( props.row.original.transaction_type @@ -148,7 +144,8 @@ const FinanceTable = () => { search: '', transactionType: '', bankId: '', - partyType: '', + customerId: '', + supplierId: '', sortBy: '', startDate: '', endDate: '', @@ -158,7 +155,8 @@ const FinanceTable = () => { pageSize: 'limit', transactionType: 'transaction_type', bankId: 'bank_id', - partyType: 'party_type', + customerId: 'customer_id', + supplierId: 'supplier_id', sortBy: 'sort_date', startDate: 'start_date', endDate: 'end_date', @@ -172,17 +170,24 @@ const FinanceTable = () => { search: '', transactionType: '', bankId: '', - partyType: '', + customerId: '', + supplierId: '', sortBy: '', startDate: '', endDate: '', }); - const [selectedTransactionType, setSelectedTransactionType] = - useState(null); - const [selectedBank, setSelectedBank] = useState(null); - const [selectedPartyType, setSelectedPartyType] = useState( - null - ); + const [selectedTransactionType, setSelectedTransactionType] = useState< + OptionType | OptionType[] | null + >(null); + const [selectedBank, setSelectedBank] = useState< + OptionType | OptionType[] | null + >(null); + const [selectedCustomerId, setSelectedCustomerId] = useState< + OptionType | OptionType[] | null + >(null); + const [selectedSupplierId, setSelectedSupplierId] = useState< + OptionType | OptionType[] | null + >(null); const [selectedSortBy, setSelectedSortBy] = useState(null); const [selectedFinance, setSelectedFinance] = useState(null); const [isDeleteLoading, setIsDeleteLoading] = useState(false); @@ -197,27 +202,18 @@ const FinanceTable = () => { FinanceApi.getAllFetcher ); - // ===== Options ===== - const transactionTypeOptions = useMemo(() => { - return [ - { label: 'Customer', value: 'CUSTOMER' }, - { label: 'Supplier', value: 'SUPPLIER' }, - ]; - }, []); const { - options: partyTypeOptions, - isLoadingOptions: partyTypeIsLoadingOptions, - setInputValue: partyTypeInputValue, - loadMore: partyTypeLoadMore, - } = useSelect( - selectedTransactionType - ? selectedTransactionType.value === 'CUSTOMER' - ? CustomerApi.basePath - : SupplierApi.basePath - : '', - 'id', - 'name' - ); + options: customerOptions, + isLoadingOptions: customerIsLoadingOptions, + setInputValue: customerInputValue, + loadMore: customerLoadMore, + } = useSelect(CustomerApi.basePath, 'id', 'name'); + const { + options: supplierOptions, + isLoadingOptions: supplierIsLoadingOptions, + setInputValue: supplierInputValue, + loadMore: supplierLoadMore, + } = useSelect(SupplierApi.basePath, 'id', 'name'); const sortByOptions = useMemo(() => { return [ { label: 'Tanggal Pembayaran', value: 'payment_date' }, @@ -238,24 +234,47 @@ const FinanceTable = () => { const transactionTypeChangeHandler = ( val: OptionType | OptionType[] | null ) => { - setSelectedTransactionType(val as OptionType); + setSelectedTransactionType(val); setPendingFilters((prev) => ({ ...prev, - transactionType: val ? ((val as OptionType).value as string) : '', + transactionType: val + ? Array.isArray(val) + ? val.map((item) => item.value).join(',') + : (val.value as string) + : '', })); }; const bankChangeHandler = (val: OptionType | OptionType[] | null) => { - setSelectedBank(val as OptionType); + setSelectedBank(val); setPendingFilters((prev) => ({ ...prev, - bankId: val ? ((val as OptionType).value as string) : '', + bankId: val + ? Array.isArray(val) + ? val.map((item) => item.value).join(',') + : (val.value as string) + : '', })); }; - const partyTypeChangeHandler = (val: OptionType | OptionType[] | null) => { - setSelectedPartyType(val as OptionType); + const customerIdChangeHandler = (val: OptionType | OptionType[] | null) => { + setSelectedCustomerId(val); setPendingFilters((prev) => ({ ...prev, - partyType: val ? ((val as OptionType).value as string) : '', + customerId: val + ? Array.isArray(val) + ? val.map((item) => item.value).join(',') + : (val.value as string) + : '', + })); + }; + const supplierIdChangeHandler = (val: OptionType | OptionType[] | null) => { + setSelectedSupplierId(val); + setPendingFilters((prev) => ({ + ...prev, + supplierId: val + ? Array.isArray(val) + ? val.map((item) => item.value).join(',') + : (val.value as string) + : '', })); }; const sortByChangeHandler = (val: OptionType | OptionType[] | null) => { @@ -279,7 +298,8 @@ const FinanceTable = () => { updateFilter('search', pendingFilters.search); updateFilter('transactionType', pendingFilters.transactionType); updateFilter('bankId', pendingFilters.bankId); - updateFilter('partyType', pendingFilters.partyType); + updateFilter('customerId', pendingFilters.customerId); + updateFilter('supplierId', pendingFilters.supplierId); updateFilter('sortBy', pendingFilters.sortBy); updateFilter('startDate', pendingFilters.startDate); updateFilter('endDate', pendingFilters.endDate); @@ -287,14 +307,16 @@ const FinanceTable = () => { const resetFilterHandler = () => { setSelectedTransactionType(null); setSelectedBank(null); - setSelectedPartyType(null); + setSelectedCustomerId(null); + setSelectedSupplierId(null); setSelectedSortBy(null); const emptyFilters = { search: '', transactionType: '', bankId: '', - partyType: '', + customerId: '', + supplierId: '', sortBy: '', startDate: '', endDate: '', @@ -304,7 +326,8 @@ const FinanceTable = () => { updateFilter('search', ''); updateFilter('transactionType', ''); updateFilter('bankId', ''); - updateFilter('partyType', ''); + updateFilter('customerId', ''); + updateFilter('supplierId', ''); updateFilter('sortBy', ''); updateFilter('startDate', ''); updateFilter('endDate', ''); @@ -477,27 +500,34 @@ const FinanceTable = () => { >
      + { onInputChange={bankInputValue} onMenuScrollToBottom={bankLoadMore} isClearable - /> - { value={pendingFilters.endDate} onChange={endDateChangeHandler} /> +
      diff --git a/src/components/pages/finance/add/initial-balance/FormFinanceAddInitialBalance.tsx b/src/components/pages/finance/add/initial-balance/FormFinanceAddInitialBalance.tsx index e1a31415..cb46c0e1 100644 --- a/src/components/pages/finance/add/initial-balance/FormFinanceAddInitialBalance.tsx +++ b/src/components/pages/finance/add/initial-balance/FormFinanceAddInitialBalance.tsx @@ -245,7 +245,11 @@ const FormFinanceAddInitialBalance = ({ } required isClearable - isDisabled={!formik.values.party_type_option?.value} + isDisabled={ + !formik.values.party_type_option?.value || + (type === 'edit' && + formik.values.party_type_option?.value == 'SUPPLIER') + } /> { + async (values: SalesOrderProductFormValues, id?: number) => { const currentProducts = formik.values.sales_order; const newValues = { @@ -515,18 +515,12 @@ const MarketingForm = ({ id: values.id ?? Date.now(), }; - const existingIndex = currentProducts.findIndex( - (item) => - item.kandang_id === newValues.kandang_id && - item.product_warehouse_id === newValues.product_warehouse_id - ); - let updatedProducts = []; - if (existingIndex !== -1) { + if (id) { // Overwrite - updatedProducts = currentProducts.map((item, index) => - index === existingIndex ? newValues : item + updatedProducts = currentProducts.map((item) => + item.id === id ? newValues : item ); } else { // Add new item diff --git a/src/components/pages/marketing/form/repeater/sales-order/SalesOrderProductForm.tsx b/src/components/pages/marketing/form/repeater/sales-order/SalesOrderProductForm.tsx index 52d844cd..8bea75c4 100644 --- a/src/components/pages/marketing/form/repeater/sales-order/SalesOrderProductForm.tsx +++ b/src/components/pages/marketing/form/repeater/sales-order/SalesOrderProductForm.tsx @@ -39,7 +39,10 @@ const SalesOrderProductForm = ({ initialValues?: SalesOrderProductFormValues; exisitingValues?: SalesOrderProductFormValues[]; modalRef?: RefObject; - onSubmitForm?: (value: SalesOrderProductFormValues) => Promise; + onSubmitForm?: ( + value: SalesOrderProductFormValues, + id?: number + ) => Promise; }) => { const [formErrorMessage, setFormErrorMessage] = useState(''); const [currentInput, setCurrentInput] = useState(''); @@ -76,7 +79,7 @@ const SalesOrderProductForm = ({ validationSchema: SalesOrderProductSchema, onSubmit: async (values) => { setFormErrorMessage(''); - onSubmitForm?.(values); + onSubmitForm?.(values, initialValues?.id); handleResetForm(); }, validateOnBlur: true, @@ -414,7 +417,9 @@ const SalesOrderProductForm = ({ />
    - +
    + +
    FCRFCR (g) {initialValues.fcr_value != null - ? formatNumber(initialValues.fcr_value) + ? `${formatNumber(initialValues.fcr_value)} g` : '-'} {initialValues.project_flock?.fcr?.fcr_std != null - ? formatNumber( - initialValues.project_flock?.fcr?.fcr_std - ) + ? `${formatNumber(initialValues.project_flock?.fcr?.fcr_std)} g` : '-'}
    Feed Intake (KG)Feed Intake (g) {initialValues.feed_intake != null - ? formatNumber(initialValues.feed_intake) + ? `${formatNumber(initialValues.feed_intake)} g` : '-'} {initialValues.project_flock?.production_standart ?.feed_intake_std != null - ? formatNumber( - initialValues.project_flock?.production_standart - ?.feed_intake_std - ) + ? `${formatNumber(initialValues.project_flock?.production_standart?.feed_intake_std)} g` : '-'}
    Egg MassEgg Mass (kg) {initialValues.egg_mass != null - ? formatNumber(initialValues.egg_mass) + ? `${formatNumber(initialValues.egg_mass)} kg` : '-'} {initialValues.project_flock?.production_standart ?.egg_mass_std != null - ? formatNumber( - initialValues.project_flock - ?.production_standart?.egg_mass_std - ) + ? `${formatNumber(initialValues.project_flock?.production_standart?.egg_mass_std)} kg` : '-'}
    - Egg Weight (KG) - Egg Weight (g) {initialValues.egg_weight != null - ? formatNumber(initialValues.egg_weight) + ? `${formatNumber(initialValues.egg_weight)} g` : '-'} {initialValues.project_flock?.production_standart ?.egg_weight_std != null - ? formatNumber( - initialValues.project_flock - ?.production_standart?.egg_weight_std - ) + ? `${formatNumber(initialValues.project_flock?.production_standart?.egg_weight_std)} g` : '-'}
    Hen DayHen Day (%) {initialValues.hen_day != null - ? formatNumber(initialValues.hen_day) + ? `${formatNumber(initialValues.hen_day)}%` : '-'}
    Hen House + Hen House (btr) + {initialValues.hen_house != null - ? formatNumber(initialValues.hen_house) + ? `${formatNumber(initialValues.hen_house)} btr` : '-'} {initialValues.project_flock?.production_standart ?.hen_house_std != null - ? `${initialValues.project_flock?.production_standart?.hen_house_std}%` + ? `${formatNumber(initialValues.project_flock?.production_standart?.hen_house_std)} btr` : '-'}
    Kondisi Telur JumlahBerat (gram)Total Berat (Kilogram)Action @@ -2880,7 +2921,8 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { className={{ wrapper: 'w-full min-w-24', }} - placeholder='Masukkan berat telur (gram)...' + placeholder='Masukkan total berat telur (Kilogram)...' + inputSuffix='Kilogram' />