diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index fb5503c..0218057 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -45,6 +45,7 @@
"react-grid-layout": "^1.5.1",
"react-hook-form": "^7.54.2",
"react-resizable-panels": "^2.1.7",
+ "recharts": "^2.15.2",
"sonner": "^2.0.1",
"tailwind-merge": "^3.0.2",
"tailwindcss": "^4.0.15",
@@ -327,6 +328,18 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@babel/runtime": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz",
+ "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==",
+ "license": "MIT",
+ "dependencies": {
+ "regenerator-runtime": "^0.14.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/@babel/template": {
"version": "7.26.9",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz",
@@ -3376,6 +3389,69 @@
"integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==",
"license": "MIT"
},
+ "node_modules/@types/d3-array": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
+ "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-color": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
+ "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-ease": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
+ "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-interpolate": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
+ "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-color": "*"
+ }
+ },
+ "node_modules/@types/d3-path": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
+ "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-scale": {
+ "version": "4.0.9",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
+ "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-time": "*"
+ }
+ },
+ "node_modules/@types/d3-shape": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz",
+ "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-path": "*"
+ }
+ },
+ "node_modules/@types/d3-time": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
+ "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-timer": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
+ "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
+ "license": "MIT"
+ },
"node_modules/@types/estree": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
@@ -4105,9 +4181,129 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
- "dev": true,
"license": "MIT"
},
+ "node_modules/d3-array": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
+ "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
+ "license": "ISC",
+ "dependencies": {
+ "internmap": "1 - 2"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-color": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
+ "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-ease": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
+ "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-format": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
+ "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-interpolate": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
+ "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-color": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-path": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
+ "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-scale": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
+ "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "2.10.0 - 3",
+ "d3-format": "1 - 3",
+ "d3-interpolate": "1.2.0 - 3",
+ "d3-time": "2.1.1 - 3",
+ "d3-time-format": "2 - 4"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-shape": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
+ "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-path": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
+ "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-array": "2 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-time-format": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
+ "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
+ "license": "ISC",
+ "dependencies": {
+ "d3-time": "1 - 3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/d3-timer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
+ "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/data-uri-to-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz",
@@ -4154,6 +4350,12 @@
}
}
},
+ "node_modules/decimal.js-light": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
+ "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==",
+ "license": "MIT"
+ },
"node_modules/decode-formdata": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/decode-formdata/-/decode-formdata-0.8.0.tgz",
@@ -4209,6 +4411,16 @@
"node": ">=0.3.1"
}
},
+ "node_modules/dom-helpers": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
+ "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.8.7",
+ "csstype": "^3.0.2"
+ }
+ },
"node_modules/dotenv": {
"version": "16.4.7",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
@@ -4545,6 +4757,12 @@
"node": ">=6"
}
},
+ "node_modules/eventemitter3": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
+ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
+ "license": "MIT"
+ },
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -4958,6 +5176,15 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"license": "ISC"
},
+ "node_modules/internmap": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
+ "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/is-arguments": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz",
@@ -5484,6 +5711,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "license": "MIT"
+ },
"node_modules/lodash.includes": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
@@ -6078,6 +6311,30 @@
"react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
}
},
+ "node_modules/react-smooth": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz",
+ "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-equals": "^5.0.1",
+ "prop-types": "^15.8.1",
+ "react-transition-group": "^4.4.5"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/react-smooth/node_modules/fast-equals": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.2.2.tgz",
+ "integrity": "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/react-style-singleton": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",
@@ -6100,6 +6357,22 @@
}
}
},
+ "node_modules/react-transition-group": {
+ "version": "4.4.5",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
+ "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@babel/runtime": "^7.5.5",
+ "dom-helpers": "^5.0.1",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.6.2"
+ },
+ "peerDependencies": {
+ "react": ">=16.6.0",
+ "react-dom": ">=16.6.0"
+ }
+ },
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
@@ -6113,6 +6386,50 @@
"node": ">=8.10.0"
}
},
+ "node_modules/recharts": {
+ "version": "2.15.2",
+ "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.2.tgz",
+ "integrity": "sha512-xv9lVztv3ingk7V3Jf05wfAZbM9Q2umJzu5t/cfnAK7LUslNrGT7LPBr74G+ok8kSCeFMaePmWMg0rcYOnczTw==",
+ "license": "MIT",
+ "dependencies": {
+ "clsx": "^2.0.0",
+ "eventemitter3": "^4.0.1",
+ "lodash": "^4.17.21",
+ "react-is": "^18.3.1",
+ "react-smooth": "^4.0.4",
+ "recharts-scale": "^0.4.4",
+ "tiny-invariant": "^1.3.1",
+ "victory-vendor": "^36.6.8"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/recharts-scale": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz",
+ "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==",
+ "license": "MIT",
+ "dependencies": {
+ "decimal.js-light": "^2.4.1"
+ }
+ },
+ "node_modules/recharts/node_modules/react-is": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
+ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
+ "license": "MIT"
+ },
+ "node_modules/regenerator-runtime": {
+ "version": "0.14.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+ "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
+ "license": "MIT"
+ },
"node_modules/resize-observer-polyfill": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
@@ -6712,6 +7029,28 @@
"which-typed-array": "^1.1.2"
}
},
+ "node_modules/victory-vendor": {
+ "version": "36.9.2",
+ "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz",
+ "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==",
+ "license": "MIT AND ISC",
+ "dependencies": {
+ "@types/d3-array": "^3.0.3",
+ "@types/d3-ease": "^3.0.0",
+ "@types/d3-interpolate": "^3.0.1",
+ "@types/d3-scale": "^4.0.2",
+ "@types/d3-shape": "^3.1.0",
+ "@types/d3-time": "^3.0.0",
+ "@types/d3-timer": "^3.0.0",
+ "d3-array": "^3.1.6",
+ "d3-ease": "^3.0.1",
+ "d3-interpolate": "^3.0.1",
+ "d3-scale": "^4.0.2",
+ "d3-shape": "^3.1.0",
+ "d3-time": "^3.0.0",
+ "d3-timer": "^3.0.1"
+ }
+ },
"node_modules/vite": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.2.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index a1dbba0..d71f2c6 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -49,6 +49,7 @@
"react-grid-layout": "^1.5.1",
"react-hook-form": "^7.54.2",
"react-resizable-panels": "^2.1.7",
+ "recharts": "^2.15.2",
"sonner": "^2.0.1",
"tailwind-merge": "^3.0.2",
"tailwindcss": "^4.0.15",
diff --git a/frontend/src/components/admin/modules/ModuleForm.tsx b/frontend/src/components/admin/modules/ModuleForm.tsx
new file mode 100644
index 0000000..f8212f6
--- /dev/null
+++ b/frontend/src/components/admin/modules/ModuleForm.tsx
@@ -0,0 +1,146 @@
+import { Button } from "@/components/ui/button";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/components/ui/dialog";
+
+import { Label } from "@/components/ui/label";
+import { toast } from "sonner";
+import { useState } from "react";
+//import { z } from "zod";
+//import { zodResolver } from "@hookform/resolvers/zod";
+import { useQuery } from "@tanstack/react-query";
+import { useSessionStore } from "@/lib/store/sessionStore";
+import axios from "axios";
+import { useForm } from "@tanstack/react-form";
+import { Checkbox } from "@/components/ui/checkbox";
+
+import { getModules } from "@/utils/querys/admin/modules";
+
+// const FormSchema = z.object({
+// subModule: z.boolean(),
+// });
+export function ChangeModule({ module }: { module: any }) {
+ const { token } = useSessionStore();
+ const { refetch } = useQuery(getModules(token ?? ""));
+
+ const [open, setOpen] = useState(false);
+ const [saving, setSaving] = useState(false);
+ const form = useForm({
+ defaultValues: {
+ active: module.active,
+ },
+ onSubmit: async ({ value }) => {
+ console.log(value);
+ try {
+ const result = await axios.patch(
+ `/api/server/modules/${module.module_id}`,
+ { active: value.active },
+ {
+ headers: { Authorization: `Bearer ${token}` },
+ }
+ );
+
+ if (result.data.success) {
+ setOpen(!open);
+ setSaving(false);
+ refetch();
+ toast.success(result.data.message);
+ }
+ } catch (error) {
+ console.log(error);
+ }
+ },
+ });
+
+ return (
+ <>
+
+ >
+ );
+}
diff --git a/frontend/src/components/admin/modules/ModulePage.tsx b/frontend/src/components/admin/modules/ModulePage.tsx
new file mode 100644
index 0000000..75b8d97
--- /dev/null
+++ b/frontend/src/components/admin/modules/ModulePage.tsx
@@ -0,0 +1,116 @@
+import { LstCard } from "@/components/extendedUI/LstCard";
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table";
+import { useSessionStore } from "@/lib/store/sessionStore";
+import { useModuleStore } from "@/lib/store/useModuleStore";
+import { useQuery } from "@tanstack/react-query";
+import { useRouter } from "@tanstack/react-router";
+
+import { Skeleton } from "@/components/ui/skeleton";
+import { getModules } from "@/utils/querys/admin/modules";
+import { ChangeModule } from "./ModuleForm";
+
+export type Settings = {
+ settings_id?: string;
+ name?: string;
+ value?: string;
+ description?: string;
+};
+
+export default function ModulesPage() {
+ const { user, token } = useSessionStore();
+ const { modules } = useModuleStore();
+ const router = useRouter();
+
+ const adminModule = modules.filter((n) => n.name === "admin");
+ const userLevel =
+ user?.roles.filter((r) => r.module_id === adminModule[0].module_id) ||
+ [];
+
+ if (!adminModule[0].roles.includes(userLevel[0]?.role)) {
+ router.navigate({ to: "/" });
+ }
+
+ const { data, isError, error, isLoading } = useQuery(
+ getModules(token ?? "")
+ );
+
+ // if (isLoading) {
+ // return
Loading.....
;
+ // }
+ if (isError) {
+ return {JSON.stringify(error)}
;
+ }
+
+ return (
+
+
+
+
+ Name
+ Description
+ Module In
+ Roles
+ Active
+ Edit
+
+
+ {isLoading ? (
+ <>
+
+ {Array(10)
+ .fill(0)
+ .map((_, i) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ))}
+
+ >
+ ) : (
+
+ {data?.map((i: any) => (
+
+
+ {i.name}
+
+
+ {i.description}
+
+
+ {i.moduleName}
+
+
+ {JSON.stringify(i.roles)}
+
+
+ {i.active ? "Yes" : "No"}
+
+
+
+
+
+ ))}
+
+ )}
+
+
+ );
+}
diff --git a/frontend/src/components/admin/supModules/SubModuleForm.tsx b/frontend/src/components/admin/supModules/SubModuleForm.tsx
new file mode 100644
index 0000000..03a0f11
--- /dev/null
+++ b/frontend/src/components/admin/supModules/SubModuleForm.tsx
@@ -0,0 +1,145 @@
+import { Button } from "@/components/ui/button";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/components/ui/dialog";
+
+import { Label } from "@/components/ui/label";
+import { toast } from "sonner";
+import { useState } from "react";
+//import { z } from "zod";
+//import { zodResolver } from "@hookform/resolvers/zod";
+import { useQuery } from "@tanstack/react-query";
+import { useSessionStore } from "@/lib/store/sessionStore";
+import axios from "axios";
+import { useForm } from "@tanstack/react-form";
+import { Checkbox } from "@/components/ui/checkbox";
+import { getSubModules } from "@/utils/querys/admin/subModules";
+
+// const FormSchema = z.object({
+// subModule: z.boolean(),
+// });
+export function ChangeSubModule({ subModule }: { subModule: any }) {
+ const { token } = useSessionStore();
+ const { refetch } = useQuery(getSubModules(token ?? ""));
+
+ const [open, setOpen] = useState(false);
+ const [saving, setSaving] = useState(false);
+ const form = useForm({
+ defaultValues: {
+ active: subModule.active,
+ },
+ onSubmit: async ({ value }) => {
+ console.log(value);
+ try {
+ const result = await axios.patch(
+ `/api/server/submodules/${subModule.submodule_id}`,
+ { active: value.active },
+ {
+ headers: { Authorization: `Bearer ${token}` },
+ }
+ );
+
+ if (result.data.success) {
+ setOpen(!open);
+ setSaving(false);
+ refetch();
+ toast.success(result.data.message);
+ }
+ } catch (error) {
+ console.log(error);
+ }
+ },
+ });
+
+ return (
+ <>
+
+ >
+ );
+}
diff --git a/frontend/src/components/admin/supModules/SubModulePage.tsx b/frontend/src/components/admin/supModules/SubModulePage.tsx
new file mode 100644
index 0000000..f1645f5
--- /dev/null
+++ b/frontend/src/components/admin/supModules/SubModulePage.tsx
@@ -0,0 +1,125 @@
+import { LstCard } from "@/components/extendedUI/LstCard";
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/components/ui/table";
+import { useSessionStore } from "@/lib/store/sessionStore";
+import { useModuleStore } from "@/lib/store/useModuleStore";
+import { useQuery } from "@tanstack/react-query";
+import { useRouter } from "@tanstack/react-router";
+import { ChangeSubModule } from "./SubModuleForm";
+
+import { Skeleton } from "@/components/ui/skeleton";
+import { getSubModules } from "@/utils/querys/admin/subModules";
+
+export type Settings = {
+ settings_id?: string;
+ name?: string;
+ value?: string;
+ description?: string;
+};
+
+export default function SubModulePage() {
+ const { user, token } = useSessionStore();
+ const { modules } = useModuleStore();
+ const router = useRouter();
+
+ const adminModule = modules.filter((n) => n.name === "admin");
+ const userLevel =
+ user?.roles.filter((r) => r.module_id === adminModule[0].module_id) ||
+ [];
+
+ if (!adminModule[0].roles.includes(userLevel[0]?.role)) {
+ router.navigate({ to: "/" });
+ }
+
+ const { data, isError, error, isLoading } = useQuery(
+ getSubModules(token ?? "")
+ );
+
+ // if (isLoading) {
+ // return Loading.....
;
+ // }
+ if (isError) {
+ return {JSON.stringify(error)}
;
+ }
+
+ return (
+
+
+
+
+ Name
+ Description
+ Module In
+ Roles
+ Active
+ Edit
+
+
+ {isLoading ? (
+ <>
+
+ {Array(10)
+ .fill(0)
+ .map((_, i) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ))}
+
+ >
+ ) : (
+
+ {data?.map((i: any) => (
+
+
+ {i.name}
+
+
+ {i.description}
+
+
+ {i.moduleName}
+
+
+ {JSON.stringify(i.roles)}
+
+
+ {i.active ? "Yes" : "No"}
+
+
+
+
+
+ ))}
+
+ )}
+
+
+ );
+}
diff --git a/frontend/src/components/admin/user/components/ModuleAccess.tsx b/frontend/src/components/admin/user/components/ModuleAccess.tsx
new file mode 100644
index 0000000..6b69e6c
--- /dev/null
+++ b/frontend/src/components/admin/user/components/ModuleAccess.tsx
@@ -0,0 +1,31 @@
+import ModuleForm from "./ModuleForm";
+import { getModules } from "@/utils/querys/admin/modules";
+import { useQuery } from "@tanstack/react-query";
+//import { getUserRoles } from "@/utils/querys/admin/userRoles";
+//import { Checkbox } from "@radix-ui/react-checkbox";
+
+export default function ModuleAccess(data: any) {
+ const token = localStorage.getItem("auth_token");
+ // const { data: userRoles } = useQuery(getUserRoles());
+ const {
+ data: modules,
+ isError,
+ isLoading,
+ } = useQuery(getModules(token ?? ""));
+
+ if (isError) return Error gettings Roles
;
+ if (isLoading) return Loading modules
;
+ return (
+
+ {modules?.map((m: any) => {
+ return (
+
+ );
+ })}
+
+ );
+}
diff --git a/frontend/src/components/admin/user/components/ModuleForm.tsx b/frontend/src/components/admin/user/components/ModuleForm.tsx
new file mode 100644
index 0000000..1096a6d
--- /dev/null
+++ b/frontend/src/components/admin/user/components/ModuleForm.tsx
@@ -0,0 +1,119 @@
+import { LstCard } from "@/components/extendedUI/LstCard";
+import { Button } from "@/components/ui/button";
+import { Label } from "@/components/ui/label";
+import {
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectLabel,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select";
+import { getUserRoles } from "@/utils/querys/admin/userRoles";
+import { useForm } from "@tanstack/react-form";
+import { useQuery } from "@tanstack/react-query";
+import axios from "axios";
+import { toast } from "sonner";
+
+export default function ModuleForm(props: any) {
+ const { refetch } = useQuery(getUserRoles());
+ const token = localStorage.getItem("auth_token");
+ const form = useForm({
+ defaultValues: { role: props.i.role },
+ onSubmit: async ({ value }) => {
+ const data = {
+ username: props.username,
+ module: props.i.name,
+ role: value.role,
+ };
+ console.log(data);
+ try {
+ const res = await axios.post("/api/auth/setuseraccess", data, {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ });
+
+ if (res.data.success) {
+ toast.success(res.data.message);
+ refetch();
+ form.reset();
+ } else {
+ res.data.message;
+ }
+ } catch (error) {
+ console.log(error);
+ }
+ },
+ });
+
+ return (
+
+
+ Module: {props.i.name}
+
+ Current role:
+ {}}}
+ children={(field) => {
+ return (
+
+
+
+
+ );
+ }}
+ />
+
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/components/admin/user/components/SubModuleAccess.tsx b/frontend/src/components/admin/user/components/SubModuleAccess.tsx
new file mode 100644
index 0000000..61143af
--- /dev/null
+++ b/frontend/src/components/admin/user/components/SubModuleAccess.tsx
@@ -0,0 +1,31 @@
+import { useGetUserRoles } from "@/lib/store/useGetRoles";
+import { useSubModuleStore } from "@/lib/store/useSubModuleStore";
+
+import SubModuleForm from "./SubmoduleForm";
+//import { Checkbox } from "@radix-ui/react-checkbox";
+
+export default function UserSubRoles(data: any) {
+ const { userRoles } = useGetUserRoles();
+ const { subModules } = useSubModuleStore();
+
+ return (
+
+ {subModules?.map((m: any) => {
+ const hasRole: any = userRoles.filter(
+ (r: any) =>
+ r.user_id.includes(data.user.user_id) &&
+ r.module_id === m.module_id
+ );
+ return (
+
+
+
+ );
+ })}
+
+ );
+}
diff --git a/frontend/src/components/admin/user/components/SubmoduleForm.tsx b/frontend/src/components/admin/user/components/SubmoduleForm.tsx
new file mode 100644
index 0000000..2e9e1b9
--- /dev/null
+++ b/frontend/src/components/admin/user/components/SubmoduleForm.tsx
@@ -0,0 +1,93 @@
+import { LstCard } from "@/components/extendedUI/LstCard";
+import { Label } from "@/components/ui/label";
+import {
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectLabel,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select";
+import { useForm } from "@tanstack/react-form";
+
+export default function SubModuleForm(props: any) {
+ const form = useForm({
+ defaultValues: { role: "" },
+ onSubmit: async ({ value }) => {
+ console.log(value);
+ },
+ });
+
+ return (
+
+
+
+ Module: {props.i.moduleName},
+ SubModule: {props.i.name}
+
+
+
+ Current role:{" "}
+ {props.hasRole[0]?.role
+ ? props.hasRole[0].role
+ : "not assigned"}
+
+ {}}}
+ children={(field) => {
+ return (
+
+
+
+
+ );
+ }}
+ />
+
+
+
+ );
+}
diff --git a/frontend/src/components/admin/user/components/UserCard.tsx b/frontend/src/components/admin/user/components/UserCard.tsx
index a90333b..7ccd4a5 100644
--- a/frontend/src/components/admin/user/components/UserCard.tsx
+++ b/frontend/src/components/admin/user/components/UserCard.tsx
@@ -20,7 +20,7 @@ import { useForm } from "@tanstack/react-form";
import { useQuery } from "@tanstack/react-query";
import axios from "axios";
import { toast } from "sonner";
-import UserRoles from "./UserRoles";
+
import { CardHeader } from "@/components/ui/card";
export default function UserCard(data: any) {
@@ -270,16 +270,11 @@ export default function UserCard(data: any) {
/>
-
-
-
- User Module / Role Access
-
-
-
+
);
}
diff --git a/frontend/src/components/admin/user/components/UserRoles.tsx b/frontend/src/components/admin/user/components/UserRoles.tsx
deleted file mode 100644
index 70657d0..0000000
--- a/frontend/src/components/admin/user/components/UserRoles.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import { Label } from "@/components/ui/label";
-import { useModuleStore } from "@/lib/store/useModuleStore";
-//import { Checkbox } from "@radix-ui/react-checkbox";
-
-export default function UserRoles(user: any) {
- const { modules } = useModuleStore();
- console.log(user);
- return (
-
- {modules?.map((m: any) => {
- console.log(m);
- return ;
- })}
-
- );
-}
diff --git a/frontend/src/components/layout/side-components/admin.tsx b/frontend/src/components/layout/side-components/admin.tsx
index 0d782d4..a684433 100644
--- a/frontend/src/components/layout/side-components/admin.tsx
+++ b/frontend/src/components/layout/side-components/admin.tsx
@@ -1,4 +1,5 @@
import {
+ AlignJustify,
Atom,
Logs,
Minus,
@@ -25,76 +26,120 @@ import {
CollapsibleContent,
CollapsibleTrigger,
} from "../../ui/collapsible";
+import { useSubModuleStore } from "@/lib/store/useSubModuleStore";
-const items = [
- {
- title: "Servers",
- url: "/servers",
- icon: Server,
- isActive: false,
- },
-];
-const data = {
- navMain: [
- {
- title: "Admin",
- url: "#",
- icon: ShieldCheck,
- items: [
- {
- title: "Settings",
- url: "/settings",
- icon: Settings,
- isActive: true,
- },
- {
- title: "Modules",
- url: "/modules",
- icon: Settings,
- isActive: false,
- },
- {
- title: "Swagger",
- url: "#",
- icon: Webhook,
- isActive: true,
- },
- {
- title: "Logs",
- url: "#",
- icon: Logs,
- isActive: false,
- },
- {
- title: "Users",
- url: "/users",
- icon: Users,
- isActive: true,
- },
- {
- title: "UCD",
- url: "https://ucd.alpla.net:8443/",
- icon: Atom,
- isActive: false,
- newWindow: true,
- },
- {
- title: "Lst Api",
- url: "/api/docs",
- icon: Webhook,
- isActive: false,
- },
- ],
- },
- ],
+const iconMap: any = {
+ ShieldCheck: ShieldCheck,
+ AlignJustify: AlignJustify,
+ Settings: Settings,
+ Atom: Atom,
+ Logs: Logs,
+ Users: Users,
+ Webhook: Webhook,
+ Server: Server,
};
export function AdminSideBar() {
+ const { subModules } = useSubModuleStore();
+
+ const items = subModules.filter((m) => m.moduleName === "admin");
return (
Admin section
-
+ {items.map((item: any, index) => {
+ const Icon = iconMap[item.icon] || AlignJustify;
+ // drop down menu setup
+ return (
+
+ {item.link === "#" ? (
+
+
+
+
+
+ {item.name}{" "}
+
+
+
+
+ {item.subSubModule?.length > 0 ? (
+
+
+ {item.subSubModule.map(
+ (i: any) => {
+ const SubIcon =
+ iconMap[
+ i.icon
+ ] ||
+ AlignJustify;
+ return (
+
+ {i.isActive && (
+
+
+
+
+ {
+ i.name
+ }
+
+
+
+ )}
+
+ );
+ }
+ )}
+
+
+ ) : null}
+
+
+ ) : (
+
+ {items.map((item) => {
+ if (item.link === "#") return;
+ return (
+
+
+
+
+ {item.name}
+
+
+
+ );
+ })}
+
+ )}
+
+ );
+ })}
+
+
+ );
+}
+
+{
+ /*
{data.navMain.map((item, index) => (
))}
-
-
-
- );
+ */
}
diff --git a/frontend/src/components/layout/side-components/logistics.tsx b/frontend/src/components/layout/side-components/logistics.tsx
index e8e1608..7a551d4 100644
--- a/frontend/src/components/layout/side-components/logistics.tsx
+++ b/frontend/src/components/layout/side-components/logistics.tsx
@@ -1,4 +1,4 @@
-//import { Cylinder, Package, Truck } from "lucide-react";
+import { Cylinder, Package, Truck } from "lucide-react";
import {
SidebarGroup,
SidebarGroupContent,
@@ -10,67 +10,12 @@ import {
import { hasPageAccess } from "@/utils/userAccess";
import { User } from "@/types/users";
import { useSubModuleStore } from "@/lib/store/useSubModuleStore";
-// this will need to be moved to a links section the db to make it more easy to remove and add
-// const items = [
-// {
-// title: "Silo Adjustments",
-// url: "#",
-// icon: Cylinder,
-// role: ["admin", "systemAdmin"],
-// module: "logistics",
-// active: true,
-// },
-// {
-// name: "Bulk orders",
-// moduleName: "logistics",
-// description: "",
-// link: "#",
-// icon: Truck,
-// role: ["systemAdmin"],
-// active: true,
-// subSubModule: [],
-// },
-// {
-// name: "Forecast",
-// moduleName: "logistics",
-// description: "",
-// link: "#",
-// icon: Truck,
-// role: ["systemAdmin"],
-// active: true,
-// subSubModule: [],
-// },
-// {
-// name: "Ocme cycle counts",
-// moduleName: "logistics",
-// description: "",
-// link: "#",
-// icon: Package,
-// role: ["technician", "supervisor", "manager", "admin", "systemAdmin"],
-// active: false,
-// subSubModule: [],
-// },
-// {
-// name: "Material Helper",
-// moduleName: "logistics",
-// description: "",
-// link: "/materialHelper/consumption",
-// icon: Package,
-// role: ["technician", "supervisor", "manager", "admin", "systemAdmin"],
-// active: true,
-// subSubModule: [],
-// },
-// {
-// name: "Ocme Cyclecount",
-// moduleName: "logistics",
-// description: "",
-// link: "/cyclecount",
-// icon: Package,
-// role: ["technician", "supervisor", "manager", "admin", "systemAdmin"],
-// active: true,
-// subSubModule: [],
-// },
-// ];
+
+const iconMap: any = {
+ Package: Package,
+ Truck: Truck,
+ Cylinder: Cylinder,
+};
export function LogisticsSideBar({
user,
@@ -82,13 +27,14 @@ export function LogisticsSideBar({
const { subModules } = useSubModuleStore();
const items = subModules.filter((m) => m.moduleName === "logistics");
- //console.log(items);
+
return (
Logistics
{items.map((item) => {
+ const Icon = iconMap[item.icon];
return (
<>
@@ -100,6 +46,7 @@ export function LogisticsSideBar({
item.active && (
+
{item.name}
diff --git a/frontend/src/routeTree.gen.ts b/frontend/src/routeTree.gen.ts
index ce4f3fa..85857d2 100644
--- a/frontend/src/routeTree.gen.ts
+++ b/frontend/src/routeTree.gen.ts
@@ -21,6 +21,7 @@ import { Route as OcpIndexImport } from './routes/ocp/index'
import { Route as EomEomImport } from './routes/_eom/eom'
import { Route as AuthProfileImport } from './routes/_auth/profile'
import { Route as AdminUsersImport } from './routes/_admin/users'
+import { Route as AdminSubModulesImport } from './routes/_admin/subModules'
import { Route as AdminSettingsImport } from './routes/_admin/settings'
import { Route as AdminServersImport } from './routes/_admin/servers'
import { Route as AdminModulesImport } from './routes/_admin/modules'
@@ -28,6 +29,7 @@ import { Route as ocmeCyclecountIndexImport } from './routes/(ocme)/cyclecount/i
import { Route as logisticsSiloAdjustmentsIndexImport } from './routes/(logistics)/siloAdjustments/index'
import { Route as logisticsMaterialHelperIndexImport } from './routes/(logistics)/materialHelper/index'
import { Route as EomArticleAvImport } from './routes/_eom/article/$av'
+import { Route as logisticsSiloAdjustmentsHistImport } from './routes/(logistics)/siloAdjustments/$hist'
import { Route as logisticsMaterialHelperSiloLinkIndexImport } from './routes/(logistics)/materialHelper/siloLink/index'
import { Route as logisticsMaterialHelperConsumptionIndexImport } from './routes/(logistics)/materialHelper/consumption/index'
import { Route as logisticsSiloAdjustmentsCommentCommentImport } from './routes/(logistics)/siloAdjustments/comment/$comment'
@@ -91,6 +93,12 @@ const AdminUsersRoute = AdminUsersImport.update({
getParentRoute: () => AdminRoute,
} as any)
+const AdminSubModulesRoute = AdminSubModulesImport.update({
+ id: '/subModules',
+ path: '/subModules',
+ getParentRoute: () => AdminRoute,
+} as any)
+
const AdminSettingsRoute = AdminSettingsImport.update({
id: '/settings',
path: '/settings',
@@ -135,6 +143,13 @@ const EomArticleAvRoute = EomArticleAvImport.update({
getParentRoute: () => EomRoute,
} as any)
+const logisticsSiloAdjustmentsHistRoute =
+ logisticsSiloAdjustmentsHistImport.update({
+ id: '/(logistics)/siloAdjustments/$hist',
+ path: '/siloAdjustments/$hist',
+ getParentRoute: () => rootRoute,
+ } as any)
+
const logisticsMaterialHelperSiloLinkIndexRoute =
logisticsMaterialHelperSiloLinkIndexImport.update({
id: '/(logistics)/materialHelper/siloLink/',
@@ -223,6 +238,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof AdminSettingsImport
parentRoute: typeof AdminImport
}
+ '/_admin/subModules': {
+ id: '/_admin/subModules'
+ path: '/subModules'
+ fullPath: '/subModules'
+ preLoaderRoute: typeof AdminSubModulesImport
+ parentRoute: typeof AdminImport
+ }
'/_admin/users': {
id: '/_admin/users'
path: '/users'
@@ -251,6 +273,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof OcpIndexImport
parentRoute: typeof rootRoute
}
+ '/(logistics)/siloAdjustments/$hist': {
+ id: '/(logistics)/siloAdjustments/$hist'
+ path: '/siloAdjustments/$hist'
+ fullPath: '/siloAdjustments/$hist'
+ preLoaderRoute: typeof logisticsSiloAdjustmentsHistImport
+ parentRoute: typeof rootRoute
+ }
'/_eom/article/$av': {
id: '/_eom/article/$av'
path: '/article/$av'
@@ -309,6 +338,7 @@ interface AdminRouteChildren {
AdminModulesRoute: typeof AdminModulesRoute
AdminServersRoute: typeof AdminServersRoute
AdminSettingsRoute: typeof AdminSettingsRoute
+ AdminSubModulesRoute: typeof AdminSubModulesRoute
AdminUsersRoute: typeof AdminUsersRoute
}
@@ -316,6 +346,7 @@ const AdminRouteChildren: AdminRouteChildren = {
AdminModulesRoute: AdminModulesRoute,
AdminServersRoute: AdminServersRoute,
AdminSettingsRoute: AdminSettingsRoute,
+ AdminSubModulesRoute: AdminSubModulesRoute,
AdminUsersRoute: AdminUsersRoute,
}
@@ -351,10 +382,12 @@ export interface FileRoutesByFullPath {
'/modules': typeof AdminModulesRoute
'/servers': typeof AdminServersRoute
'/settings': typeof AdminSettingsRoute
+ '/subModules': typeof AdminSubModulesRoute
'/users': typeof AdminUsersRoute
'/profile': typeof AuthProfileRoute
'/eom': typeof EomEomRoute
'/ocp': typeof OcpIndexRoute
+ '/siloAdjustments/$hist': typeof logisticsSiloAdjustmentsHistRoute
'/article/$av': typeof EomArticleAvRoute
'/materialHelper': typeof logisticsMaterialHelperIndexRoute
'/siloAdjustments': typeof logisticsSiloAdjustmentsIndexRoute
@@ -372,10 +405,12 @@ export interface FileRoutesByTo {
'/modules': typeof AdminModulesRoute
'/servers': typeof AdminServersRoute
'/settings': typeof AdminSettingsRoute
+ '/subModules': typeof AdminSubModulesRoute
'/users': typeof AdminUsersRoute
'/profile': typeof AuthProfileRoute
'/eom': typeof EomEomRoute
'/ocp': typeof OcpIndexRoute
+ '/siloAdjustments/$hist': typeof logisticsSiloAdjustmentsHistRoute
'/article/$av': typeof EomArticleAvRoute
'/materialHelper': typeof logisticsMaterialHelperIndexRoute
'/siloAdjustments': typeof logisticsSiloAdjustmentsIndexRoute
@@ -396,10 +431,12 @@ export interface FileRoutesById {
'/_admin/modules': typeof AdminModulesRoute
'/_admin/servers': typeof AdminServersRoute
'/_admin/settings': typeof AdminSettingsRoute
+ '/_admin/subModules': typeof AdminSubModulesRoute
'/_admin/users': typeof AdminUsersRoute
'/_auth/profile': typeof AuthProfileRoute
'/_eom/eom': typeof EomEomRoute
'/ocp/': typeof OcpIndexRoute
+ '/(logistics)/siloAdjustments/$hist': typeof logisticsSiloAdjustmentsHistRoute
'/_eom/article/$av': typeof EomArticleAvRoute
'/(logistics)/materialHelper/': typeof logisticsMaterialHelperIndexRoute
'/(logistics)/siloAdjustments/': typeof logisticsSiloAdjustmentsIndexRoute
@@ -419,10 +456,12 @@ export interface FileRouteTypes {
| '/modules'
| '/servers'
| '/settings'
+ | '/subModules'
| '/users'
| '/profile'
| '/eom'
| '/ocp'
+ | '/siloAdjustments/$hist'
| '/article/$av'
| '/materialHelper'
| '/siloAdjustments'
@@ -439,10 +478,12 @@ export interface FileRouteTypes {
| '/modules'
| '/servers'
| '/settings'
+ | '/subModules'
| '/users'
| '/profile'
| '/eom'
| '/ocp'
+ | '/siloAdjustments/$hist'
| '/article/$av'
| '/materialHelper'
| '/siloAdjustments'
@@ -461,10 +502,12 @@ export interface FileRouteTypes {
| '/_admin/modules'
| '/_admin/servers'
| '/_admin/settings'
+ | '/_admin/subModules'
| '/_admin/users'
| '/_auth/profile'
| '/_eom/eom'
| '/ocp/'
+ | '/(logistics)/siloAdjustments/$hist'
| '/_eom/article/$av'
| '/(logistics)/materialHelper/'
| '/(logistics)/siloAdjustments/'
@@ -483,6 +526,7 @@ export interface RootRouteChildren {
AboutRoute: typeof AboutRoute
LoginRoute: typeof LoginRoute
OcpIndexRoute: typeof OcpIndexRoute
+ logisticsSiloAdjustmentsHistRoute: typeof logisticsSiloAdjustmentsHistRoute
logisticsMaterialHelperIndexRoute: typeof logisticsMaterialHelperIndexRoute
logisticsSiloAdjustmentsIndexRoute: typeof logisticsSiloAdjustmentsIndexRoute
ocmeCyclecountIndexRoute: typeof ocmeCyclecountIndexRoute
@@ -499,6 +543,7 @@ const rootRouteChildren: RootRouteChildren = {
AboutRoute: AboutRoute,
LoginRoute: LoginRoute,
OcpIndexRoute: OcpIndexRoute,
+ logisticsSiloAdjustmentsHistRoute: logisticsSiloAdjustmentsHistRoute,
logisticsMaterialHelperIndexRoute: logisticsMaterialHelperIndexRoute,
logisticsSiloAdjustmentsIndexRoute: logisticsSiloAdjustmentsIndexRoute,
ocmeCyclecountIndexRoute: ocmeCyclecountIndexRoute,
@@ -527,6 +572,7 @@ export const routeTree = rootRoute
"/about",
"/login",
"/ocp/",
+ "/(logistics)/siloAdjustments/$hist",
"/(logistics)/materialHelper/",
"/(logistics)/siloAdjustments/",
"/(ocme)/cyclecount/",
@@ -544,6 +590,7 @@ export const routeTree = rootRoute
"/_admin/modules",
"/_admin/servers",
"/_admin/settings",
+ "/_admin/subModules",
"/_admin/users"
]
},
@@ -578,6 +625,10 @@ export const routeTree = rootRoute
"filePath": "_admin/settings.tsx",
"parent": "/_admin"
},
+ "/_admin/subModules": {
+ "filePath": "_admin/subModules.tsx",
+ "parent": "/_admin"
+ },
"/_admin/users": {
"filePath": "_admin/users.tsx",
"parent": "/_admin"
@@ -593,6 +644,9 @@ export const routeTree = rootRoute
"/ocp/": {
"filePath": "ocp/index.tsx"
},
+ "/(logistics)/siloAdjustments/$hist": {
+ "filePath": "(logistics)/siloAdjustments/$hist.tsx"
+ },
"/_eom/article/$av": {
"filePath": "_eom/article/$av.tsx",
"parent": "/_eom"