Removed PositEasy Referenecs
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>frontend</title>
|
||||
<title>RIT Canteen Dashboard</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
236
frontend/package-lock.json
generated
236
frontend/package-lock.json
generated
@@ -12,6 +12,8 @@
|
||||
"clsx": "^2.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"framer-motion": "^12.38.0",
|
||||
"jspdf": "^4.2.1",
|
||||
"jspdf-autotable": "^5.0.7",
|
||||
"lucide-react": "^1.7.0",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
@@ -244,6 +246,15 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.29.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz",
|
||||
"integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.28.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
|
||||
@@ -1350,6 +1361,19 @@
|
||||
"undici-types": "~7.16.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/pako": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.4.tgz",
|
||||
"integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/raf": {
|
||||
"version": "3.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz",
|
||||
"integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "19.2.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
|
||||
@@ -1370,6 +1394,13 @@
|
||||
"@types/react": "^19.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/trusted-types": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@types/use-sync-external-store": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
|
||||
@@ -1804,6 +1835,16 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/base64-arraybuffer": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
|
||||
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/baseline-browser-mapping": {
|
||||
"version": "2.10.15",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.15.tgz",
|
||||
@@ -1893,6 +1934,26 @@
|
||||
],
|
||||
"license": "CC-BY-4.0"
|
||||
},
|
||||
"node_modules/canvg": {
|
||||
"version": "3.0.11",
|
||||
"resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.11.tgz",
|
||||
"integrity": "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@types/raf": "^3.4.0",
|
||||
"core-js": "^3.8.3",
|
||||
"raf": "^3.4.1",
|
||||
"regenerator-runtime": "^0.13.7",
|
||||
"rgbcolor": "^1.0.1",
|
||||
"stackblur-canvas": "^2.0.0",
|
||||
"svg-pathdata": "^6.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
@@ -1966,6 +2027,18 @@
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/core-js": {
|
||||
"version": "3.49.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.49.0.tgz",
|
||||
"integrity": "sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/core-js"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
@@ -1981,6 +2054,16 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/css-line-break": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
|
||||
"integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"utrie": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||
@@ -2160,6 +2243,16 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/dompurify": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.0.tgz",
|
||||
"integrity": "sha512-nolgK9JcaUXMSmW+j1yaSvaEaoXYHwWyGJlkoCTghc97KgGDDSnpoU/PlEnw63Ah+TGKFOyY+X5LnxaWbCSfXg==",
|
||||
"license": "(MPL-2.0 OR Apache-2.0)",
|
||||
"optional": true,
|
||||
"optionalDependencies": {
|
||||
"@types/trusted-types": "^2.0.7"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.331",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.331.tgz",
|
||||
@@ -2425,6 +2518,17 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-png": {
|
||||
"version": "6.4.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-png/-/fast-png-6.4.0.tgz",
|
||||
"integrity": "sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/pako": "^2.0.3",
|
||||
"iobuffer": "^5.3.2",
|
||||
"pako": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fdir": {
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
||||
@@ -2443,6 +2547,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/fflate": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
|
||||
"integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/file-entry-cache": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
|
||||
@@ -2620,6 +2730,20 @@
|
||||
"hermes-estree": "0.25.1"
|
||||
}
|
||||
},
|
||||
"node_modules/html2canvas": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
|
||||
"integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"css-line-break": "^2.1.0",
|
||||
"text-segmentation": "^1.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
|
||||
@@ -2676,6 +2800,12 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/iobuffer": {
|
||||
"version": "5.4.0",
|
||||
"resolved": "https://registry.npmjs.org/iobuffer/-/iobuffer-5.4.0.tgz",
|
||||
"integrity": "sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-extglob": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
@@ -2783,6 +2913,32 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/jspdf": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/jspdf/-/jspdf-4.2.1.tgz",
|
||||
"integrity": "sha512-YyAXyvnmjTbR4bHQRLzex3CuINCDlQnBqoSYyjJwTP2x9jDLuKDzy7aKUl0hgx3uhcl7xzg32agn5vlie6HIlQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.28.6",
|
||||
"fast-png": "^6.2.0",
|
||||
"fflate": "^0.8.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"canvg": "^3.0.11",
|
||||
"core-js": "^3.6.0",
|
||||
"dompurify": "^3.3.1",
|
||||
"html2canvas": "^1.0.0-rc.5"
|
||||
}
|
||||
},
|
||||
"node_modules/jspdf-autotable": {
|
||||
"version": "5.0.7",
|
||||
"resolved": "https://registry.npmjs.org/jspdf-autotable/-/jspdf-autotable-5.0.7.tgz",
|
||||
"integrity": "sha512-2wr7H6liNDBYNwt25hMQwXkEWFOEopgKIvR1Eukuw6Zmprm/ZcnmLTQEjW7Xx3FCbD3v7pflLcnMAv/h1jFDQw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"jspdf": "^2 || ^3 || ^4"
|
||||
}
|
||||
},
|
||||
"node_modules/keyv": {
|
||||
"version": "4.5.4",
|
||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
||||
@@ -3238,6 +3394,12 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/pako": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
|
||||
"integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==",
|
||||
"license": "(MIT AND Zlib)"
|
||||
},
|
||||
"node_modules/parent-module": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
@@ -3271,6 +3433,13 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/performance-now": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
@@ -3347,6 +3516,16 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/raf": {
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
|
||||
"integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"performance-now": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react": {
|
||||
"version": "19.2.4",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
|
||||
@@ -3481,6 +3660,13 @@
|
||||
"redux": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/regenerator-runtime": {
|
||||
"version": "0.13.11",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
|
||||
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/reselect": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
|
||||
@@ -3497,6 +3683,16 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/rgbcolor": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz",
|
||||
"integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==",
|
||||
"license": "MIT OR SEE LICENSE IN FEEL-FREE.md",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">= 0.8.15"
|
||||
}
|
||||
},
|
||||
"node_modules/rolldown": {
|
||||
"version": "1.0.0-rc.13",
|
||||
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.13.tgz",
|
||||
@@ -3593,6 +3789,16 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/stackblur-canvas": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz",
|
||||
"integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=0.1.14"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-json-comments": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
|
||||
@@ -3619,6 +3825,16 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/svg-pathdata": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz",
|
||||
"integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tailwind-merge": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.5.0.tgz",
|
||||
@@ -3650,6 +3866,16 @@
|
||||
"url": "https://opencollective.com/webpack"
|
||||
}
|
||||
},
|
||||
"node_modules/text-segmentation": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
|
||||
"integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"utrie": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/tiny-invariant": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
|
||||
@@ -3800,6 +4026,16 @@
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/utrie": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
|
||||
"integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"base64-arraybuffer": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/victory-vendor": {
|
||||
"version": "37.3.6",
|
||||
"resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz",
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
"clsx": "^2.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"framer-motion": "^12.38.0",
|
||||
"jspdf": "^4.2.1",
|
||||
"jspdf-autotable": "^5.0.7",
|
||||
"lucide-react": "^1.7.0",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
|
||||
@@ -20,6 +20,7 @@ import Bills from './pages/Bills.tsx';
|
||||
import PurchaseSummary from './pages/PurchaseSummary.tsx';
|
||||
import IntentDashboard from './pages/IntentDashboard.tsx';
|
||||
import IntentList from './pages/IntentList.tsx';
|
||||
import Reports from './pages/Reports.tsx';
|
||||
|
||||
const ProtectedRoute = ({ children }: { children: React.ReactNode }) => {
|
||||
const isLoggedIn = sessionStorage.getItem('isLoggedIn') === 'true';
|
||||
@@ -84,7 +85,7 @@ function App() {
|
||||
<Route path="expense/category" element={<PlaceholderPage title="Expense Category" />} />
|
||||
|
||||
{/* Others */}
|
||||
<Route path="reports" element={<PlaceholderPage title="Reports" />} />
|
||||
<Route path="reports" element={<Reports />} />
|
||||
<Route path="table" element={<PlaceholderPage title="Table Management" />} />
|
||||
<Route path="wallet" element={<PlaceholderPage title="Wallet" />} />
|
||||
<Route path="feedback" element={<PlaceholderPage title="Feedback" />} />
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import {
|
||||
Target,
|
||||
ChevronRight,
|
||||
ShoppingBag,
|
||||
ArrowRight,
|
||||
Wallet
|
||||
Wallet,
|
||||
BarChart3
|
||||
} from 'lucide-react';
|
||||
import {
|
||||
AreaChart,
|
||||
@@ -21,6 +23,7 @@ import {
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
const Dashboard = () => {
|
||||
const navigate = useNavigate();
|
||||
const [activeTab, setActiveTab] = useState('Sales');
|
||||
const [timeRange, setTimeRange] = useState('Today');
|
||||
const [customDates, setCustomDates] = useState({ from: '', to: '' });
|
||||
@@ -136,6 +139,14 @@ const Dashboard = () => {
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
onClick={() => navigate('/reports')}
|
||||
className="flex items-center gap-2 bg-indigo-50 text-indigo-600 px-4 py-2 rounded-xl text-xs font-black uppercase tracking-widest hover:bg-indigo-600 hover:text-white transition-all shadow-sm active:scale-95"
|
||||
>
|
||||
<BarChart3 size={16} />
|
||||
View Full Report
|
||||
</button>
|
||||
|
||||
<div className="relative group">
|
||||
<select title="Filter by store type or category" className="appearance-none bg-white border border-slate-200 rounded-xl px-4 py-2 pr-10 text-xs font-bold text-slate-600 outline-none focus:border-[#0f4475] transition-all cursor-pointer shadow-sm">
|
||||
<option>Store Type: All</option>
|
||||
|
||||
384
frontend/src/pages/Reports.tsx
Normal file
384
frontend/src/pages/Reports.tsx
Normal file
@@ -0,0 +1,384 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
Download,
|
||||
Calendar,
|
||||
TrendingUp,
|
||||
ShoppingBag,
|
||||
Users,
|
||||
ArrowUpRight,
|
||||
ArrowDownRight,
|
||||
FileText,
|
||||
Filter,
|
||||
RefreshCw,
|
||||
Package,
|
||||
PieChart as PieChartIcon,
|
||||
IndianRupee,
|
||||
BarChart3
|
||||
} from 'lucide-react';
|
||||
import {
|
||||
BarChart,
|
||||
Bar,
|
||||
XAxis,
|
||||
YAxis,
|
||||
CartesianGrid,
|
||||
Tooltip,
|
||||
ResponsiveContainer,
|
||||
Cell,
|
||||
PieChart,
|
||||
Pie
|
||||
} from 'recharts';
|
||||
import { format, startOfMonth, endOfMonth, startOfToday, endOfToday, subMonths } from 'date-fns';
|
||||
import jsPDF from 'jspdf';
|
||||
import autoTable from 'jspdf-autotable';
|
||||
|
||||
interface ReportData {
|
||||
totalSales: number;
|
||||
totalOrders: number;
|
||||
totalPurchases: number;
|
||||
topVendor: string;
|
||||
topSellingItems: Array<{
|
||||
name: string;
|
||||
quantity: number;
|
||||
revenue: number;
|
||||
}>;
|
||||
vendorSummary: Array<{
|
||||
name: string;
|
||||
amount: number;
|
||||
orderCount: number;
|
||||
}>;
|
||||
storeOverview: Array<{
|
||||
name: string;
|
||||
sale: number;
|
||||
orders: number;
|
||||
}>;
|
||||
}
|
||||
|
||||
const Reports: React.FC = () => {
|
||||
const [dateRange, setDateRange] = useState({
|
||||
from: startOfMonth(new Date()),
|
||||
to: endOfMonth(new Date())
|
||||
});
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [data, setData] = useState<ReportData | null>(null);
|
||||
|
||||
const fetchData = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const fromStr = dateRange.from.toISOString();
|
||||
const toStr = dateRange.to.toISOString();
|
||||
const response = await fetch(`http://${window.location.hostname}:8080/api/reports/monthly?from=${fromStr}&to=${toStr}`);
|
||||
const result = await response.json();
|
||||
setData(result);
|
||||
} catch (error) {
|
||||
console.error('Error fetching report data:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, [dateRange]);
|
||||
|
||||
const downloadPDF = () => {
|
||||
if (!data) return;
|
||||
|
||||
const doc = new jsPDF();
|
||||
const title = `Sales Report (${format(dateRange.from, 'dd MMM')} - ${format(dateRange.to, 'dd MMM yyyy')})`;
|
||||
|
||||
// Header
|
||||
doc.setFontSize(20);
|
||||
doc.setTextColor(30, 41, 59);
|
||||
doc.text('RIT CANTEEN BUSINESS REPORT', 105, 15, { align: 'center' });
|
||||
|
||||
doc.setFontSize(12);
|
||||
doc.setTextColor(100, 116, 139);
|
||||
doc.text(title, 105, 25, { align: 'center' });
|
||||
|
||||
// Summary Section
|
||||
doc.setDrawColor(226, 232, 240);
|
||||
doc.line(20, 30, 190, 30);
|
||||
|
||||
doc.setFontSize(14);
|
||||
doc.setTextColor(30, 41, 59);
|
||||
doc.text('Financial Summary', 20, 40);
|
||||
|
||||
const summaryData = [
|
||||
['Metric', 'Value'],
|
||||
['Total Sales Revenue', `INR ${data.totalSales.toLocaleString()}`],
|
||||
['Total Orders Fulfilled', data.totalOrders.toString()],
|
||||
['Total Inventory Purchases', `INR ${data.totalPurchases.toLocaleString()}`],
|
||||
['Net Performance', `INR ${(data.totalSales - data.totalPurchases).toLocaleString()}`],
|
||||
['Top Performing Vendor', data.topVendor]
|
||||
];
|
||||
|
||||
autoTable(doc, {
|
||||
startY: 45,
|
||||
head: [summaryData[0]],
|
||||
body: summaryData.slice(1),
|
||||
theme: 'striped',
|
||||
headStyles: { fillColor: [79, 70, 229] }
|
||||
});
|
||||
|
||||
// Top Selling Items
|
||||
const finalY1 = (doc as any).lastAutoTable.finalY;
|
||||
doc.text('Top Selling Items', 20, finalY1 + 15);
|
||||
autoTable(doc, {
|
||||
startY: finalY1 + 20,
|
||||
head: [['Product Name', 'Quantity Sold', 'Revenue']],
|
||||
body: data.topSellingItems.map(item => [item.name, item.quantity, `INR ${item.revenue.toLocaleString()}`]),
|
||||
theme: 'grid'
|
||||
});
|
||||
|
||||
// Vendor Summary
|
||||
const finalY2 = (doc as any).lastAutoTable.finalY;
|
||||
doc.text('Vendor Performance', 20, finalY2 + 15);
|
||||
autoTable(doc, {
|
||||
startY: finalY2 + 20,
|
||||
head: [['Vendor Name', 'Total Orders', 'Spend Amount']],
|
||||
body: data.vendorSummary.map(v => [v.name, v.orderCount, `INR ${v.amount.toLocaleString()}`]),
|
||||
theme: 'striped'
|
||||
});
|
||||
|
||||
doc.save(`Report_${format(dateRange.from, 'yyyyMMdd')}_${format(dateRange.to, 'yyyyMMdd')}.pdf`);
|
||||
};
|
||||
|
||||
const COLORS_CHART = ['#4f46e5', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6', '#06b6d4'];
|
||||
|
||||
return (
|
||||
<div className="p-8 bg-[#f8fafc] min-h-screen">
|
||||
<div className="flex justify-between items-center mb-8">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-slate-900 tracking-tight">Business Intelligence</h1>
|
||||
<p className="text-slate-500 mt-1">Robust insights and performance analytics</p>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-4">
|
||||
<div className="flex bg-white rounded-xl shadow-sm border border-slate-200 p-1">
|
||||
<button
|
||||
onClick={() => setDateRange({ from: startOfMonth(new Date()), to: endOfMonth(new Date()) })}
|
||||
className={`px-4 py-2 text-sm font-medium rounded-lg transition-all ${
|
||||
format(dateRange.from, 'MM-yyyy') === format(new Date(), 'MM-yyyy')
|
||||
? 'bg-indigo-50 text-indigo-600' : 'text-slate-600 hover:bg-slate-50'
|
||||
}`}
|
||||
>
|
||||
This Month
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setDateRange({ from: startOfMonth(subMonths(new Date(), 1)), to: endOfMonth(subMonths(new Date(), 1)) })}
|
||||
className={`px-4 py-2 text-sm font-medium rounded-lg transition-all ${
|
||||
format(dateRange.from, 'MM-yyyy') === format(subMonths(new Date(), 1), 'MM-yyyy')
|
||||
? 'bg-indigo-50 text-indigo-600' : 'text-slate-600 hover:bg-slate-50'
|
||||
}`}
|
||||
>
|
||||
Last Month
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={downloadPDF}
|
||||
className="flex items-center gap-2 bg-indigo-600 text-white px-5 py-2.5 rounded-xl font-semibold hover:bg-indigo-700 transition-all shadow-md shadow-indigo-200 active:scale-95"
|
||||
>
|
||||
<Download size={18} />
|
||||
Export PDF
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!data ? (
|
||||
<div className="flex flex-col items-center justify-center py-32 bg-white rounded-3xl border border-slate-100 shadow-sm">
|
||||
<RefreshCw size={48} className="text-indigo-200 animate-spin mb-4" />
|
||||
<p className="text-slate-400 font-medium">Aggregating business intelligence...</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-8">
|
||||
{/* Key Metrics Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
<div className="bg-white p-6 rounded-3xl border border-slate-100 shadow-sm hover:shadow-md transition-shadow group">
|
||||
<div className="flex justify-between items-start mb-4">
|
||||
<div className="p-3 bg-indigo-50 text-indigo-600 rounded-2xl group-hover:bg-indigo-600 group-hover:text-white transition-all">
|
||||
<IndianRupee size={24} />
|
||||
</div>
|
||||
<span className="flex items-center gap-1 text-emerald-500 bg-emerald-50 px-2 py-1 rounded-lg text-xs font-bold">
|
||||
<ArrowUpRight size={14} /> 12.5%
|
||||
</span>
|
||||
</div>
|
||||
<h3 className="text-slate-500 text-sm font-bold uppercase tracking-wider">Total Sales</h3>
|
||||
<p className="text-3xl font-black text-slate-900 mt-1">₹{data.totalSales.toLocaleString()}</p>
|
||||
<p className="text-xs text-slate-400 mt-2 font-medium">For the selected period</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white p-6 rounded-3xl border border-slate-100 shadow-sm hover:shadow-md transition-shadow group">
|
||||
<div className="flex justify-between items-start mb-4">
|
||||
<div className="p-3 bg-emerald-50 text-emerald-600 rounded-2xl group-hover:bg-emerald-600 group-hover:text-white transition-all">
|
||||
<ShoppingBag size={24} />
|
||||
</div>
|
||||
<span className="flex items-center gap-1 text-emerald-500 bg-emerald-50 px-2 py-1 rounded-lg text-xs font-bold">
|
||||
<ArrowUpRight size={14} /> 8.2%
|
||||
</span>
|
||||
</div>
|
||||
<h3 className="text-slate-500 text-sm font-bold uppercase tracking-wider">Total Orders</h3>
|
||||
<p className="text-3xl font-black text-slate-900 mt-1">{data.totalOrders}</p>
|
||||
<p className="text-xs text-slate-400 mt-2 font-medium">{Math.round(data.totalOrders/30)} orders avg/day</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white p-6 rounded-3xl border border-slate-100 shadow-sm hover:shadow-md transition-shadow group">
|
||||
<div className="flex justify-between items-start mb-4">
|
||||
<div className="p-3 bg-amber-50 text-amber-600 rounded-2xl group-hover:bg-amber-600 group-hover:text-white transition-all">
|
||||
<Package size={24} />
|
||||
</div>
|
||||
<span className="flex items-center gap-1 text-rose-500 bg-rose-50 px-2 py-1 rounded-lg text-xs font-bold">
|
||||
<ArrowDownRight size={14} /> 2.1%
|
||||
</span>
|
||||
</div>
|
||||
<h3 className="text-slate-500 text-sm font-bold uppercase tracking-wider">Spendings</h3>
|
||||
<p className="text-3xl font-black text-slate-900 mt-1">₹{data.totalPurchases.toLocaleString()}</p>
|
||||
<p className="text-xs text-slate-400 mt-2 font-medium">Inventory & Vendor bills</p>
|
||||
</div>
|
||||
|
||||
<div className="bg-white p-6 rounded-3xl border border-slate-100 shadow-sm hover:shadow-md transition-shadow group">
|
||||
<div className="flex justify-between items-start mb-4">
|
||||
<div className="p-3 bg-purple-50 text-purple-600 rounded-2xl group-hover:bg-purple-600 group-hover:text-white transition-all">
|
||||
<Users size={24} />
|
||||
</div>
|
||||
</div>
|
||||
<h3 className="text-slate-500 text-sm font-bold uppercase tracking-wider">Top Vendor</h3>
|
||||
<p className="text-xl font-black text-slate-900 mt-2 truncate">{data.topVendor}</p>
|
||||
<p className="text-xs text-slate-400 mt-1 font-medium truncate">Primary supplier this month</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
{/* Top Selling Products Chart */}
|
||||
<div className="bg-white p-8 rounded-[2rem] border border-slate-100 shadow-sm">
|
||||
<div className="flex items-center gap-3 mb-8">
|
||||
<div className="p-2.5 bg-indigo-50 text-indigo-600 rounded-xl">
|
||||
<TrendingUp size={20} />
|
||||
</div>
|
||||
<h3 className="text-lg font-bold text-slate-900">Highly Sold Items</h3>
|
||||
</div>
|
||||
|
||||
<div className="h-[400px]">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<BarChart data={data.topSellingItems} layout="vertical" margin={{ left: 40 }}>
|
||||
<CartesianGrid strokeDasharray="3 3" horizontal={true} vertical={false} stroke="#f1f5f9" />
|
||||
<XAxis type="number" hide />
|
||||
<YAxis
|
||||
dataKey="name"
|
||||
type="category"
|
||||
axisLine={false}
|
||||
tickLine={false}
|
||||
width={120}
|
||||
tick={{ fontSize: 12, fontWeight: 600, fill: '#64748b' }}
|
||||
/>
|
||||
<Tooltip
|
||||
cursor={{ fill: '#f8fafc' }}
|
||||
contentStyle={{ borderRadius: '12px', border: 'none', boxShadow: '0 10px 15px -3px rgb(0 0 0 / 0.1)' }}
|
||||
/>
|
||||
<Bar dataKey="quantity" radius={[0, 8, 8, 0]} barSize={32}>
|
||||
{data.topSellingItems.map((_, index) => (
|
||||
<Cell key={`cell-${index}`} fill={COLORS_CHART[index % COLORS_CHART.length]} />
|
||||
))}
|
||||
</Bar>
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Vendor Contribution Pie Chart */}
|
||||
<div className="bg-white p-8 rounded-[2rem] border border-slate-100 shadow-sm">
|
||||
<div className="flex items-center gap-3 mb-8">
|
||||
<div className="p-2.5 bg-emerald-50 text-emerald-600 rounded-xl">
|
||||
<PieChartIcon size={20} />
|
||||
</div>
|
||||
<h3 className="text-lg font-bold text-slate-900">Vendor Expenditure Share</h3>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="h-[300px] w-full">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<PieChart>
|
||||
<Pie
|
||||
data={data.vendorSummary}
|
||||
cx="50%"
|
||||
cy="50%"
|
||||
innerRadius={60}
|
||||
outerRadius={100}
|
||||
paddingAngle={5}
|
||||
dataKey="amount"
|
||||
nameKey="name"
|
||||
>
|
||||
{data.vendorSummary.map((_, index) => (
|
||||
<Cell key={`cell-${index}`} fill={COLORS_CHART[index % COLORS_CHART.length]} />
|
||||
))}
|
||||
</Pie>
|
||||
<Tooltip
|
||||
contentStyle={{ borderRadius: '12px', border: 'none', boxShadow: '0 10px 15px -3px rgb(0 0 0 / 0.1)' }}
|
||||
/>
|
||||
</PieChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-x-12 gap-y-4 mt-4">
|
||||
{data.vendorSummary.slice(0, 4).map((vendor, index) => (
|
||||
<div key={index} className="flex items-center gap-2">
|
||||
<div className="w-3 h-3 rounded-full" style={{ backgroundColor: COLORS_CHART[index % COLORS_CHART.length] }} />
|
||||
<span className="text-xs font-bold text-slate-600 truncate max-w-[120px]">{vendor.name}</span>
|
||||
<span className="text-xs text-slate-400 font-medium">₹{vendor.amount.toLocaleString()}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Detailed Performance Table */}
|
||||
<div className="bg-white rounded-[2rem] border border-slate-100 shadow-sm overflow-hidden">
|
||||
<div className="p-8 border-b border-slate-50 flex justify-between items-center">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-2.5 bg-purple-50 text-purple-600 rounded-xl">
|
||||
<BarChart3 size={20} />
|
||||
</div>
|
||||
<h3 className="text-lg font-bold text-slate-900">Store-wise Performance</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-left border-collapse">
|
||||
<thead>
|
||||
<tr className="bg-slate-50/50">
|
||||
<th className="px-8 py-4 text-xs font-bold text-slate-400 uppercase tracking-widest">Stall / Store Name</th>
|
||||
<th className="px-8 py-4 text-xs font-bold text-slate-400 uppercase tracking-widest">Orders Fulfilled</th>
|
||||
<th className="px-8 py-4 text-xs font-bold text-slate-400 uppercase tracking-widest text-right">Revenue Generated</th>
|
||||
<th className="px-8 py-4 text-xs font-bold text-slate-400 uppercase tracking-widest text-right">Avg Order Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-slate-50">
|
||||
{data.storeOverview.map((store, index) => (
|
||||
<tr key={index} className="hover:bg-slate-50/50 transition-colors">
|
||||
<td className="px-8 py-6">
|
||||
<div className="font-bold text-slate-700">{store.name}</div>
|
||||
<div className="text-[10px] text-indigo-500 font-bold uppercase tracking-wider mt-0.5">Primary Location</div>
|
||||
</td>
|
||||
<td className="px-8 py-6">
|
||||
<div className="text-slate-600 font-semibold">{store.orders} Orders</div>
|
||||
</td>
|
||||
<td className="px-8 py-6 text-right">
|
||||
<div className="text-indigo-600 font-black">₹{store.sale.toLocaleString()}</div>
|
||||
</td>
|
||||
<td className="px-8 py-6 text-right">
|
||||
<div className="text-slate-500 font-medium">₹{store.orders > 0 ? (store.sale / store.orders).toFixed(0) : 0}</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Reports;
|
||||
Reference in New Issue
Block a user