diff --git a/client/eslint.config.js b/client/eslint.config.js
index 612379ea..4ebf9795 100644
--- a/client/eslint.config.js
+++ b/client/eslint.config.js
@@ -1,27 +1,33 @@
-import js from '@eslint/js';
+import { globalIgnores } from 'eslint/config';
import globals from 'globals';
+import js from '@eslint/js';
import reactHooks from 'eslint-plugin-react-hooks';
import reactRefresh from 'eslint-plugin-react-refresh';
import tseslint from 'typescript-eslint';
-import { globalIgnores } from 'eslint/config';
export default tseslint.config([
globalIgnores(['dist', 'node_modules']),
+ js.configs.recommended,
+ ...tseslint.configs.recommended,
{
files: ['**/*.{ts,tsx}'],
- extends: [
- js.configs.recommended,
- tseslint.configs.recommended,
- reactHooks.configs['recommended-latest'],
- reactRefresh.configs.vite,
- ],
+ plugins: {
+ 'react-hooks': reactHooks,
+ 'react-refresh': reactRefresh,
+ },
languageOptions: {
ecmaVersion: 2020,
globals: {
...globals.browser,
},
+ parserOptions: {
+ ecmaFeatures: {
+ jsx: true,
+ },
+ },
},
rules: {
+ ...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
},
},
diff --git a/client/package-lock.json b/client/package-lock.json
index e1b495c1..459e39e2 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -13,9 +13,9 @@
"@mui/icons-material": "^7.3.7",
"@mui/lab": "^7.0.1-beta.21",
"@mui/material": "^7.3.7",
- "@mui/x-charts": "^7.6.1",
- "@mui/x-date-pickers": "^7.6.1",
- "@react-oauth/google": "^0.12.1",
+ "@mui/x-charts": "^8.27.0",
+ "@mui/x-date-pickers": "^8.27.0",
+ "@react-oauth/google": "^0.13.4",
"@tanstack/react-query": "^5.90.20",
"@tanstack/react-query-devtools": "^5.91.3",
"axios": "^1.6.7",
@@ -32,15 +32,15 @@
"devDependencies": {
"@eslint/js": "^9.30.1",
"@tanstack/eslint-plugin-query": "^5.91.4",
- "@types/node": "^20.19.9",
- "@types/react": "^19.2.10",
+ "@types/node": "^25.2.1",
+ "@types/react": "^19.2.13",
"@types/react-dom": "^19.2.3",
- "@vitejs/plugin-react": "^4.6.0",
+ "@vitejs/plugin-react": "^5.1.3",
"eslint": "^9.30.1",
- "eslint-plugin-react-hooks": "^5.2.0",
- "eslint-plugin-react-refresh": "^0.4.20",
- "globals": "^16.3.0",
- "typescript": "~5.8.3",
+ "eslint-plugin-react-hooks": "^7.0.1",
+ "eslint-plugin-react-refresh": "^0.5.0",
+ "globals": "^17.3.0",
+ "typescript": "^5.9.3",
"typescript-eslint": "^8.35.1",
"vite": "^7.0.4"
}
@@ -1514,19 +1514,21 @@
}
},
"node_modules/@mui/x-charts": {
- "version": "7.29.1",
- "resolved": "https://registry.npmjs.org/@mui/x-charts/-/x-charts-7.29.1.tgz",
- "integrity": "sha512-5s9PX51HWhpMa+DCDa4RgjtODSaMe+PlTZUqoGIil2vaW/+4ouDLREXvyuVvIF93KfZwrPKAL2SJKSQS4YYB2w==",
+ "version": "8.27.0",
+ "resolved": "https://registry.npmjs.org/@mui/x-charts/-/x-charts-8.27.0.tgz",
+ "integrity": "sha512-MzP1jeiEkMOPWQfzRNo11iwbTwXcSDP7hKd3s/mSN0U4aINKpyHEQX6RAM8OkWzqK8ijTXYWRPVKaRYTLBx7+A==",
"license": "MIT",
"dependencies": {
- "@babel/runtime": "^7.25.7",
- "@mui/utils": "^5.16.6 || ^6.0.0 || ^7.0.0",
- "@mui/x-charts-vendor": "7.20.0",
- "@mui/x-internals": "7.29.0",
- "@react-spring/rafz": "^9.7.5",
- "@react-spring/web": "^9.7.5",
+ "@babel/runtime": "^7.28.4",
+ "@mui/utils": "^7.3.5",
+ "@mui/x-charts-vendor": "8.26.0",
+ "@mui/x-internal-gestures": "0.4.0",
+ "@mui/x-internals": "8.26.0",
+ "bezier-easing": "^2.1.0",
"clsx": "^2.1.1",
- "prop-types": "^15.8.1"
+ "prop-types": "^15.8.1",
+ "reselect": "^5.1.1",
+ "use-sync-external-store": "^1.6.0"
},
"engines": {
"node": ">=14.0.0"
@@ -1549,98 +1551,46 @@
}
},
"node_modules/@mui/x-charts-vendor": {
- "version": "7.20.0",
- "resolved": "https://registry.npmjs.org/@mui/x-charts-vendor/-/x-charts-vendor-7.20.0.tgz",
- "integrity": "sha512-pzlh7z/7KKs5o0Kk0oPcB+sY0+Dg7Q7RzqQowDQjpy5Slz6qqGsgOB5YUzn0L+2yRmvASc4Pe0914Ao3tMBogg==",
+ "version": "8.26.0",
+ "resolved": "https://registry.npmjs.org/@mui/x-charts-vendor/-/x-charts-vendor-8.26.0.tgz",
+ "integrity": "sha512-R//+WSWvsLJRTjTRN90EKX9sgRzAb4HQBvtUA3cTQpkGrmEjmatD4BJAm3IdRdkSagf6yKWF+ypESctyRhbwnA==",
"license": "MIT AND ISC",
"dependencies": {
- "@babel/runtime": "^7.25.7",
+ "@babel/runtime": "^7.28.4",
+ "@types/d3-array": "^3.2.2",
"@types/d3-color": "^3.1.3",
- "@types/d3-delaunay": "^6.0.4",
+ "@types/d3-format": "^3.0.4",
"@types/d3-interpolate": "^3.0.4",
- "@types/d3-scale": "^4.0.8",
- "@types/d3-shape": "^3.1.6",
- "@types/d3-time": "^3.0.3",
+ "@types/d3-path": "^3.1.1",
+ "@types/d3-scale": "^4.0.9",
+ "@types/d3-shape": "^3.1.7",
+ "@types/d3-time": "^3.0.4",
+ "@types/d3-time-format": "^4.0.3",
+ "@types/d3-timer": "^3.0.2",
+ "d3-array": "^3.2.4",
"d3-color": "^3.1.0",
- "d3-delaunay": "^6.0.4",
+ "d3-format": "^3.1.0",
"d3-interpolate": "^3.0.1",
+ "d3-path": "^3.1.0",
"d3-scale": "^4.0.2",
"d3-shape": "^3.2.0",
"d3-time": "^3.1.0",
- "delaunator": "^5.0.1",
- "robust-predicates": "^3.0.2"
- }
- },
- "node_modules/@mui/x-charts/node_modules/@react-spring/web": {
- "version": "9.7.5",
- "resolved": "https://registry.npmjs.org/@react-spring/web/-/web-9.7.5.tgz",
- "integrity": "sha512-lmvqGwpe+CSttsWNZVr+Dg62adtKhauGwLyGE/RRyZ8AAMLgb9x3NDMA5RMElXo+IMyTkPp7nxTB8ZQlmhb6JQ==",
- "license": "MIT",
- "dependencies": {
- "@react-spring/animated": "~9.7.5",
- "@react-spring/core": "~9.7.5",
- "@react-spring/shared": "~9.7.5",
- "@react-spring/types": "~9.7.5"
- },
- "peerDependencies": {
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
- "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
- }
- },
- "node_modules/@mui/x-charts/node_modules/@react-spring/web/node_modules/@react-spring/animated": {
- "version": "9.7.5",
- "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.7.5.tgz",
- "integrity": "sha512-Tqrwz7pIlsSDITzxoLS3n/v/YCUHQdOIKtOJf4yL6kYVSDTSmVK1LI1Q3M/uu2Sx4X3pIWF3xLUhlsA6SPNTNg==",
- "license": "MIT",
- "dependencies": {
- "@react-spring/shared": "~9.7.5",
- "@react-spring/types": "~9.7.5"
- },
- "peerDependencies": {
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
- }
- },
- "node_modules/@mui/x-charts/node_modules/@react-spring/web/node_modules/@react-spring/core": {
- "version": "9.7.5",
- "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.7.5.tgz",
- "integrity": "sha512-rmEqcxRcu7dWh7MnCcMXLvrf6/SDlSokLaLTxiPlAYi11nN3B5oiCUAblO72o+9z/87j2uzxa2Inm8UbLjXA+w==",
- "license": "MIT",
- "dependencies": {
- "@react-spring/animated": "~9.7.5",
- "@react-spring/shared": "~9.7.5",
- "@react-spring/types": "~9.7.5"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/react-spring/donate"
- },
- "peerDependencies": {
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
- }
- },
- "node_modules/@mui/x-charts/node_modules/@react-spring/web/node_modules/@react-spring/shared": {
- "version": "9.7.5",
- "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.7.5.tgz",
- "integrity": "sha512-wdtoJrhUeeyD/PP/zo+np2s1Z820Ohr/BbuVYv+3dVLW7WctoiN7std8rISoYoHpUXtbkpesSKuPIw/6U1w1Pw==",
- "license": "MIT",
- "dependencies": {
- "@react-spring/rafz": "~9.7.5",
- "@react-spring/types": "~9.7.5"
- },
- "peerDependencies": {
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ "d3-time-format": "^4.1.0",
+ "d3-timer": "^3.0.1",
+ "flatqueue": "^3.0.0",
+ "internmap": "^2.0.3"
}
},
"node_modules/@mui/x-date-pickers": {
- "version": "7.29.4",
- "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.29.4.tgz",
- "integrity": "sha512-wJ3tsqk/y6dp+mXGtT9czciAMEO5Zr3IIAHg9x6IL0Eqanqy0N3chbmQQZv3iq0m2qUpQDLvZ4utZBUTJdjNzw==",
+ "version": "8.27.0",
+ "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-8.27.0.tgz",
+ "integrity": "sha512-mw47IgelP5qFSBANqxUhqDEly2XO9RT/BcNKwgumy8BmmdosrGAmTev8dgFMoWg20iPHxEczlpBdDGyV6ht0jg==",
"license": "MIT",
"dependencies": {
- "@babel/runtime": "^7.25.7",
- "@mui/utils": "^5.16.6 || ^6.0.0 || ^7.0.0",
- "@mui/x-internals": "7.29.0",
- "@types/react-transition-group": "^4.4.11",
+ "@babel/runtime": "^7.28.4",
+ "@mui/utils": "^7.3.5",
+ "@mui/x-internals": "8.26.0",
+ "@types/react-transition-group": "^4.4.12",
"clsx": "^2.1.1",
"prop-types": "^15.8.1",
"react-transition-group": "^4.4.5"
@@ -1697,14 +1647,25 @@
}
}
},
+ "node_modules/@mui/x-internal-gestures": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@mui/x-internal-gestures/-/x-internal-gestures-0.4.0.tgz",
+ "integrity": "sha512-i0W6v9LoiNY8Yf1goOmaygtz/ncPJGBedhpDfvNg/i8BvzPwJcBaeW4rqPucJfVag9KQ8MSssBBrvYeEnrQmhw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.28.4"
+ }
+ },
"node_modules/@mui/x-internals": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.29.0.tgz",
- "integrity": "sha512-+Gk6VTZIFD70XreWvdXBwKd8GZ2FlSCuecQFzm6znwqXg1ZsndavrhG9tkxpxo2fM1Zf7Tk8+HcOO0hCbhTQFA==",
+ "version": "8.26.0",
+ "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-8.26.0.tgz",
+ "integrity": "sha512-B9OZau5IQUvIxwpJZhoFJKqRpmWf5r0yMmSXjQuqb5WuqM755EuzWJOenY48denGoENzMLT8hQpA0hRTeU2IPA==",
"license": "MIT",
"dependencies": {
- "@babel/runtime": "^7.25.7",
- "@mui/utils": "^5.16.6 || ^6.0.0 || ^7.0.0"
+ "@babel/runtime": "^7.28.4",
+ "@mui/utils": "^7.3.5",
+ "reselect": "^5.1.1",
+ "use-sync-external-store": "^1.6.0"
},
"engines": {
"node": ">=14.0.0"
@@ -1728,27 +1689,15 @@
}
},
"node_modules/@react-oauth/google": {
- "version": "0.12.2",
- "resolved": "https://registry.npmjs.org/@react-oauth/google/-/google-0.12.2.tgz",
- "integrity": "sha512-d1GVm2uD4E44EJft2RbKtp8Z1fp/gK8Lb6KHgs3pHlM0PxCXGLaq8LLYQYENnN4xPWO1gkL4apBtlPKzpLvZwg==",
+ "version": "0.13.4",
+ "resolved": "https://registry.npmjs.org/@react-oauth/google/-/google-0.13.4.tgz",
+ "integrity": "sha512-hGKyNEH+/PK8M0sFEuo3MAEk0txtHpgs94tDQit+s2LXg7b6z53NtzHfqDvoB2X8O6lGB+FRg80hY//X6hfD+w==",
"license": "MIT",
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
- "node_modules/@react-spring/rafz": {
- "version": "9.7.5",
- "resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.7.5.tgz",
- "integrity": "sha512-5ZenDQMC48wjUzPAm1EtwQ5Ot3bLIAwwqP2w2owG5KoNdNHpEJV263nGhCeKKmuA3vG2zLLOdu3or6kuDjA6Aw==",
- "license": "MIT"
- },
- "node_modules/@react-spring/types": {
- "version": "9.7.5",
- "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.7.5.tgz",
- "integrity": "sha512-HVj7LrZ4ReHWBimBvu2SKND3cDVUPWKLqRTmWe/fNY6o1owGOX0cAHbdPDTMelgBlVbrTKrre6lFkhqGZErK/g==",
- "license": "MIT"
- },
"node_modules/@remix-run/router": {
"version": "1.23.2",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.2.tgz",
@@ -1759,9 +1708,9 @@
}
},
"node_modules/@rolldown/pluginutils": {
- "version": "1.0.0-beta.27",
- "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
- "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==",
+ "version": "1.0.0-rc.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.2.tgz",
+ "integrity": "sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw==",
"dev": true,
"license": "MIT"
},
@@ -2237,16 +2186,22 @@
"@babel/types": "^7.28.2"
}
},
+ "node_modules/@types/d3-array": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz",
+ "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==",
+ "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-delaunay": {
- "version": "6.0.4",
- "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
- "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==",
+ "node_modules/@types/d3-format": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz",
+ "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==",
"license": "MIT"
},
"node_modules/@types/d3-interpolate": {
@@ -2288,6 +2243,18 @@
"integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==",
"license": "MIT"
},
+ "node_modules/@types/d3-time-format": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz",
+ "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==",
+ "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/debug": {
"version": "4.1.12",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
@@ -2344,14 +2311,14 @@
"license": "MIT"
},
"node_modules/@types/node": {
- "version": "20.19.30",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.30.tgz",
- "integrity": "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==",
+ "version": "25.2.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.1.tgz",
+ "integrity": "sha512-CPrnr8voK8vC6eEtyRzvMpgp3VyVRhgclonE7qYi6P9sXwYb59ucfrnmFBTaP0yUi8Gk4yZg/LlTJULGxvTNsg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
- "undici-types": "~6.21.0"
+ "undici-types": "~7.16.0"
}
},
"node_modules/@types/parse-json": {
@@ -2367,9 +2334,9 @@
"license": "MIT"
},
"node_modules/@types/react": {
- "version": "19.2.10",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.10.tgz",
- "integrity": "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==",
+ "version": "19.2.13",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.13.tgz",
+ "integrity": "sha512-KkiJeU6VbYbUOp5ITMIc7kBfqlYkKA5KhEHVrGMmUUMt7NeaZg65ojdPk+FtNrBAOXNVM5QM72jnADjM+XVRAQ==",
"license": "MIT",
"peer": true,
"dependencies": {
@@ -2652,21 +2619,21 @@
"license": "ISC"
},
"node_modules/@vitejs/plugin-react": {
- "version": "4.7.0",
- "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
- "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.3.tgz",
+ "integrity": "sha512-NVUnA6gQCl8jfoYqKqQU5Clv0aPw14KkZYCsX6T9Lfu9slI0LOU10OTwFHS/WmptsMMpshNd/1tuWsHQ2Uk+cg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/core": "^7.28.0",
+ "@babel/core": "^7.29.0",
"@babel/plugin-transform-react-jsx-self": "^7.27.1",
"@babel/plugin-transform-react-jsx-source": "^7.27.1",
- "@rolldown/pluginutils": "1.0.0-beta.27",
+ "@rolldown/pluginutils": "1.0.0-rc.2",
"@types/babel__core": "^7.20.5",
- "react-refresh": "^0.17.0"
+ "react-refresh": "^0.18.0"
},
"engines": {
- "node": "^14.18.0 || >=16.0.0"
+ "node": "^20.19.0 || >=22.12.0"
},
"peerDependencies": {
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
@@ -2795,6 +2762,12 @@
"baseline-browser-mapping": "dist/cli.js"
}
},
+ "node_modules/bezier-easing": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/bezier-easing/-/bezier-easing-2.1.0.tgz",
+ "integrity": "sha512-gbIqZ/eslnUFC1tjEvtz0sgx+xTK20wDnYMIA27VA04R7w6xxXQPZDbibjA9DTWZRA2CXtwHykkVzlCaAJAZig==",
+ "license": "MIT"
+ },
"node_modules/brace-expansion": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
@@ -3081,18 +3054,6 @@
"node": ">=12"
}
},
- "node_modules/d3-delaunay": {
- "version": "6.0.4",
- "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz",
- "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==",
- "license": "ISC",
- "dependencies": {
- "delaunator": "5"
- },
- "engines": {
- "node": ">=12"
- }
- },
"node_modules/d3-format": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz",
@@ -3175,6 +3136,15 @@
"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/dayjs": {
"version": "1.11.19",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz",
@@ -3219,15 +3189,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/delaunator": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz",
- "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==",
- "license": "ISC",
- "dependencies": {
- "robust-predicates": "^3.0.2"
- }
- },
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -3470,26 +3431,33 @@
}
},
"node_modules/eslint-plugin-react-hooks": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz",
- "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==",
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz",
+ "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==",
"dev": true,
"license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.24.4",
+ "@babel/parser": "^7.24.4",
+ "hermes-parser": "^0.25.1",
+ "zod": "^3.25.0 || ^4.0.0",
+ "zod-validation-error": "^3.5.0 || ^4.0.0"
+ },
"engines": {
- "node": ">=10"
+ "node": ">=18"
},
"peerDependencies": {
"eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0"
}
},
"node_modules/eslint-plugin-react-refresh": {
- "version": "0.4.26",
- "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.26.tgz",
- "integrity": "sha512-1RETEylht2O6FM/MvgnyvT+8K21wLqDNg4qD51Zj3guhjt433XbnnkVttHMyaVyAFD03QSV4LPS5iE3VQmO7XQ==",
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.0.tgz",
+ "integrity": "sha512-ZYvmh7VfVgqR/7wR71I3Zl6hK/C5CcxdWYKZSpHawS5JCNgE4efhQWg/+/WPpgGAp9Ngp/rRZYyaIwmPQBq/lA==",
"dev": true,
"license": "MIT",
"peerDependencies": {
- "eslint": ">=8.40"
+ "eslint": ">=9"
}
},
"node_modules/eslint-scope": {
@@ -3741,6 +3709,12 @@
"node": ">=16"
}
},
+ "node_modules/flatqueue": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/flatqueue/-/flatqueue-3.0.0.tgz",
+ "integrity": "sha512-y1deYaVt+lIc/d2uIcWDNd0CrdQTO5xoCjeFdhX0kSXvm2Acm0o+3bAOiYklTEoRyzwio3sv3/IiBZdusbAe2Q==",
+ "license": "ISC"
+ },
"node_modules/flatted": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
@@ -3869,9 +3843,9 @@
}
},
"node_modules/globals": {
- "version": "16.5.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz",
- "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==",
+ "version": "17.3.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-17.3.0.tgz",
+ "integrity": "sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3982,6 +3956,23 @@
"url": "https://opencollective.com/unified"
}
},
+ "node_modules/hermes-estree": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz",
+ "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/hermes-parser": {
+ "version": "0.25.1",
+ "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz",
+ "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hermes-estree": "0.25.1"
+ }
+ },
"node_modules/hoist-non-react-statics": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
@@ -5607,9 +5598,9 @@
}
},
"node_modules/react-refresh": {
- "version": "0.17.0",
- "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
- "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
+ "version": "0.18.0",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz",
+ "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==",
"dev": true,
"license": "MIT",
"engines": {
@@ -5745,6 +5736,12 @@
"url": "https://opencollective.com/unified"
}
},
+ "node_modules/reselect": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
+ "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==",
+ "license": "MIT"
+ },
"node_modules/resolve": {
"version": "1.22.11",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
@@ -5774,12 +5771,6 @@
"node": ">=4"
}
},
- "node_modules/robust-predicates": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz",
- "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==",
- "license": "Unlicense"
- },
"node_modules/rollup": {
"version": "4.57.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz",
@@ -6036,9 +6027,9 @@
}
},
"node_modules/typescript": {
- "version": "5.8.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
- "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
@@ -6075,9 +6066,9 @@
}
},
"node_modules/undici-types": {
- "version": "6.21.0",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
- "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "version": "7.16.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
+ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
"dev": true,
"license": "MIT"
},
@@ -6209,6 +6200,15 @@
"punycode": "^2.1.0"
}
},
+ "node_modules/use-sync-external-store": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
+ "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/vfile": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
@@ -6359,6 +6359,30 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/zod": {
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
+ "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/zod-validation-error": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz",
+ "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "zod": "^3.25.0 || ^4.0.0"
+ }
+ },
"node_modules/zwitch": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
diff --git a/client/package.json b/client/package.json
index adcf4516..1678f009 100644
--- a/client/package.json
+++ b/client/package.json
@@ -19,9 +19,9 @@
"@mui/icons-material": "^7.3.7",
"@mui/lab": "^7.0.1-beta.21",
"@mui/material": "^7.3.7",
- "@mui/x-charts": "^7.6.1",
- "@mui/x-date-pickers": "^7.6.1",
- "@react-oauth/google": "^0.12.1",
+ "@mui/x-charts": "^8.27.0",
+ "@mui/x-date-pickers": "^8.27.0",
+ "@react-oauth/google": "^0.13.4",
"@tanstack/react-query": "^5.90.20",
"@tanstack/react-query-devtools": "^5.91.3",
"axios": "^1.6.7",
@@ -38,15 +38,15 @@
"devDependencies": {
"@eslint/js": "^9.30.1",
"@tanstack/eslint-plugin-query": "^5.91.4",
- "@types/node": "^20.19.9",
- "@types/react": "^19.2.10",
+ "@types/node": "^25.2.1",
+ "@types/react": "^19.2.13",
"@types/react-dom": "^19.2.3",
- "@vitejs/plugin-react": "^4.6.0",
+ "@vitejs/plugin-react": "^5.1.3",
"eslint": "^9.30.1",
- "eslint-plugin-react-hooks": "^5.2.0",
- "eslint-plugin-react-refresh": "^0.4.20",
- "globals": "^16.3.0",
- "typescript": "~5.8.3",
+ "eslint-plugin-react-hooks": "^7.0.1",
+ "eslint-plugin-react-refresh": "^0.5.0",
+ "globals": "^17.3.0",
+ "typescript": "^5.9.3",
"typescript-eslint": "^8.35.1",
"vite": "^7.0.4"
},
diff --git a/client/src/features/dashboard/components/DashboardPieChart.tsx b/client/src/features/dashboard/components/DashboardPieChart.tsx
index 312be16b..d5d56419 100644
--- a/client/src/features/dashboard/components/DashboardPieChart.tsx
+++ b/client/src/features/dashboard/components/DashboardPieChart.tsx
@@ -1,7 +1,6 @@
import { Box, Stack, Typography } from '@mui/material';
import { ChartData, DashboardData } from '../Dashboard';
-import { MakeOptional } from '@mui/x-date-pickers/internals';
import { PieChart } from '@mui/x-charts/PieChart';
import { PieValueType } from '@mui/x-charts';
@@ -28,23 +27,34 @@ export const DashboardPieChart = ({ chartData }: DashboardPieChartProps) => {
return (
- {piesData.map((pie: ChartData[], index: number) => (
-
-
- {chartTitle[index]}
-
- `${(item as unknown as ChartData).percent}%`,
- arcLabelMinAngle: 45,
- data: pie as MakeOptional[],
- },
- ]}
- {...size}
- />
-
- ))}
+ {piesData.map((pie: ChartData[], index: number) => {
+ // Map ChartData to PieValueType with all required fields
+ const formattedData: PieValueType[] = pie.map((item) => ({
+ id: item.label,
+ label: item.label,
+ value: item.value,
+ }));
+ return (
+
+
+ {chartTitle[index]}
+
+ {
+ const chartItem = pie.find((data) => data.label === item.label);
+ return chartItem ? `${chartItem.percent}%` : '';
+ },
+ arcLabelMinAngle: 45,
+ data: formattedData,
+ },
+ ]}
+ {...size}
+ />
+
+ );
+ })}
);
};
diff --git a/client/src/features/trainee-profile/education/EducationInfo.tsx b/client/src/features/trainee-profile/education/EducationInfo.tsx
index 62830e35..9e0e5955 100644
--- a/client/src/features/trainee-profile/education/EducationInfo.tsx
+++ b/client/src/features/trainee-profile/education/EducationInfo.tsx
@@ -54,15 +54,17 @@ const EducationInfo = () => {
name="currentCohort"
label="Cohort"
value={editedFields?.currentCohort ?? 'No cohort assigned'}
- InputProps={{
- readOnly: isEditing ? false : true,
- inputMode: 'numeric',
+ slotProps={{
+ input: {
+ readOnly: isEditing ? false : true,
+ inputMode: 'numeric',
+ },
+ inputLabel: { shrink: true },
}}
inputProps={{
pattern: '[0-9]*',
maxLength: 3,
}}
- InputLabelProps={{ shrink: true }}
variant={isEditing ? 'outlined' : 'standard'}
onChange={handleNumericChange}
/>
@@ -97,8 +99,10 @@ const EducationInfo = () => {
label="Quit date"
type="date"
value={formatDate(editedFields?.quitDate)}
- InputProps={{ readOnly: isEditing ? false : true }}
- InputLabelProps={{ shrink: true }}
+ slotProps={{
+ input: { readOnly: isEditing ? false : true },
+ inputLabel: { shrink: true },
+ }}
variant={isEditing ? 'outlined' : 'standard'}
onChange={handleTextChange}
/>
@@ -139,8 +143,7 @@ const EducationInfo = () => {
label="Graduation date"
type="date"
value={formatDate(editedFields?.graduationDate)}
- InputProps={{ readOnly: isEditing ? false : true }}
- InputLabelProps={{ shrink: true }}
+ slotProps={{ input: { readOnly: isEditing ? false : true }, inputLabel: { shrink: true } }}
variant={isEditing ? 'outlined' : 'standard'}
onChange={handleTextChange}
/>
@@ -155,15 +158,17 @@ const EducationInfo = () => {
name="startCohort"
label="Start cohort"
value={editedFields?.startCohort || ''}
- InputProps={{
- readOnly: isEditing ? false : true,
- inputMode: 'numeric',
+ slotProps={{
+ input: {
+ readOnly: isEditing ? false : true,
+ inputMode: 'numeric',
+ },
+ inputLabel: { shrink: true },
}}
inputProps={{
pattern: '[0-9]*',
maxLength: 3,
}}
- InputLabelProps={{ shrink: true }}
variant={isEditing ? 'outlined' : 'standard'}
onChange={handleNumericChange}
/>
@@ -177,8 +182,7 @@ const EducationInfo = () => {
label="Start date"
type="date"
value={formatDate(editedFields?.startDate)}
- InputProps={{ readOnly: isEditing ? false : true }}
- InputLabelProps={{ shrink: true }}
+ slotProps={{ input: { readOnly: isEditing ? false : true }, inputLabel: { shrink: true } }}
variant={isEditing ? 'outlined' : 'standard'}
onChange={handleTextChange}
/>
@@ -198,8 +202,7 @@ const EducationInfo = () => {
label="Technical Mentor"
type="text"
value={editedFields?.techMentor ?? ''}
- InputProps={{ readOnly: isEditing ? false : true }}
- InputLabelProps={{ shrink: true }}
+ slotProps={{ input: { readOnly: isEditing ? false : true }, inputLabel: { shrink: true } }}
variant={isEditing ? 'outlined' : 'standard'}
onChange={handleTextChange}
/>
@@ -213,8 +216,7 @@ const EducationInfo = () => {
label="HR Mentor"
type="text"
value={editedFields?.hrMentor ?? ''}
- InputProps={{ readOnly: isEditing ? false : true }}
- InputLabelProps={{ shrink: true }}
+ slotProps={{ input: { readOnly: isEditing ? false : true }, inputLabel: { shrink: true } }}
variant={isEditing ? 'outlined' : 'standard'}
onChange={handleTextChange}
/>
@@ -228,8 +230,7 @@ const EducationInfo = () => {
label="English Mentor"
type="text"
value={editedFields?.englishMentor ?? ''}
- InputProps={{ readOnly: isEditing ? false : true }}
- InputLabelProps={{ shrink: true }}
+ slotProps={{ input: { readOnly: isEditing ? false : true }, inputLabel: { shrink: true } }}
variant={isEditing ? 'outlined' : 'standard'}
onChange={handleTextChange}
/>
@@ -250,8 +251,7 @@ const EducationInfo = () => {
type="text"
multiline
value={editedFields?.comments || ''}
- InputProps={{ readOnly: isEditing ? false : true }}
- InputLabelProps={{ shrink: true }}
+ slotProps={{ input: { readOnly: isEditing ? false : true }, inputLabel: { shrink: true } }}
variant={isEditing ? 'outlined' : 'standard'}
onChange={handleTextChange}
/>
diff --git a/client/src/features/trainee-profile/education/strikes/StrikeDetailsModal.tsx b/client/src/features/trainee-profile/education/strikes/StrikeDetailsModal.tsx
index 6effeb53..255765b9 100644
--- a/client/src/features/trainee-profile/education/strikes/StrikeDetailsModal.tsx
+++ b/client/src/features/trainee-profile/education/strikes/StrikeDetailsModal.tsx
@@ -15,10 +15,10 @@ import {
Typography,
} from '@mui/material';
import { Strike, StrikeReason } from '../../../../data/types/Trainee';
-import { useEffect, useState } from 'react';
import { LoadingButton } from '@mui/lab';
import { formatDate } from '../../utils/dateHelper';
+import { useState } from 'react';
interface StrikeDetailsModalProps {
isOpen: boolean;
@@ -27,7 +27,7 @@ interface StrikeDetailsModalProps {
onClose: () => void;
onConfirmAdd: (strike: Strike) => void;
onConfirmEdit: (strike: Strike) => void;
- strikeToEdit: Strike | null;
+ initialStrike: Strike | null;
}
export const StrikeDetailsModal = ({
@@ -37,47 +37,22 @@ export const StrikeDetailsModal = ({
onClose,
onConfirmAdd,
onConfirmEdit,
- strikeToEdit,
+ initialStrike,
}: StrikeDetailsModalProps) => {
const [strikeFields, setStrikeFields] = useState({
- id: '',
- date: new Date(),
- reporterID: '',
- comments: '',
- reason: null,
+ id: initialStrike?.id || '',
+ date: initialStrike?.date || new Date(),
+ reporterID: initialStrike?.reporterID || '',
+ comments: initialStrike?.comments || '',
+ reason: initialStrike?.reason || null,
} as Strike);
const [commentsRequiredError, setCommentsRequiredError] = useState(false);
const [reasonRequiredError, setReasonRequiredError] = useState(false);
- const [isEditMode, setIsEditMode] = useState(false);
-
- useEffect(() => {
- if (strikeToEdit) {
- setStrikeFields(strikeToEdit);
- setIsEditMode(true);
- return;
- } else {
- setIsEditMode(false);
- }
- resetForm();
- }, [strikeToEdit]);
-
- const resetForm = () => {
- setStrikeFields({
- id: '',
- date: new Date(),
- reporterID: '',
- reason: null,
- comments: '',
- } as Strike);
-
- setCommentsRequiredError(false);
- setReasonRequiredError(false);
- };
+ const isEditMode = Boolean(initialStrike);
const handleClose = () => {
onClose();
- resetForm();
};
const handleStrikeChange = (e: React.ChangeEvent) => {
const { name, value } = e.target;
@@ -109,11 +84,10 @@ export const StrikeDetailsModal = ({
return;
}
- if (strikeToEdit) {
+ if (initialStrike) {
onConfirmEdit(strikeFields);
} else {
onConfirmAdd(strikeFields);
- resetForm();
}
};
diff --git a/client/src/features/trainee-profile/education/strikes/StrikesComponent.tsx b/client/src/features/trainee-profile/education/strikes/StrikesComponent.tsx
index 7759bbbd..96506551 100644
--- a/client/src/features/trainee-profile/education/strikes/StrikesComponent.tsx
+++ b/client/src/features/trainee-profile/education/strikes/StrikesComponent.tsx
@@ -41,6 +41,7 @@ export const StrikesComponent = () => {
};
const onConfirmAdd = async (strike: Strike) => {
+ if (modalError) setModalError('');
addStrike(strike, {
onSuccess: handleSuccess,
onError: (e) => {
@@ -50,6 +51,7 @@ export const StrikesComponent = () => {
};
const onConfirmEdit = (strike: Strike) => {
+ if (modalError) setModalError('');
editStrike(strike, {
onSuccess: handleSuccess,
onError: (e) => {
@@ -128,13 +130,14 @@ export const StrikesComponent = () => {
)}
>
diff --git a/client/src/features/trainee-profile/education/tests/TestDetailsModal.tsx b/client/src/features/trainee-profile/education/tests/TestDetailsModal.tsx
index 3fad4356..8b7595d1 100644
--- a/client/src/features/trainee-profile/education/tests/TestDetailsModal.tsx
+++ b/client/src/features/trainee-profile/education/tests/TestDetailsModal.tsx
@@ -15,10 +15,10 @@ import {
Typography,
} from '@mui/material';
import { Test, TestResult, TestType } from '../../../../data/types/Trainee';
-import { useEffect, useState } from 'react';
import { LoadingButton } from '@mui/lab';
import { formatDate } from '../../utils/dateHelper';
+import { useState } from 'react';
type TestDetailsModalProps = {
isOpen: boolean;
@@ -27,7 +27,7 @@ type TestDetailsModalProps = {
onClose: () => void;
onConfirmAdd: (t: Test) => void;
onConfirmEdit: (t: Test) => void;
- testToEdit: Test | null;
+ initialTest: Test | null;
};
export const TestDetailsModal = ({
@@ -37,48 +37,25 @@ export const TestDetailsModal = ({
onClose,
onConfirmAdd,
onConfirmEdit,
- testToEdit,
+ initialTest,
}: TestDetailsModalProps) => {
const [testFields, setTestFields] = useState>({
- id: '',
- date: new Date(),
- type: undefined,
- score: undefined,
- result: undefined,
- comments: '',
+ id: initialTest?.id || '',
+ date: initialTest?.date || new Date(),
+ type: initialTest?.type || undefined,
+ score: initialTest?.score ?? undefined,
+ result: initialTest?.result || undefined,
+ comments: initialTest?.comments || '',
});
const [typeError, setTypeError] = useState(false);
const [resultError, setResultError] = useState(false);
const [scoreError, setScoreError] = useState(false);
- const isEditMode = Boolean(testToEdit);
-
- useEffect(() => {
- if (testToEdit) {
- setTestFields(testToEdit);
- } else {
- resetForm();
- }
- }, [testToEdit]);
-
- const resetForm = () => {
- setTestFields({
- id: '',
- date: new Date(),
- type: undefined,
- score: undefined,
- result: undefined,
- comments: '',
- });
- setTypeError(false);
- setResultError(false);
- setScoreError(false);
- };
+ const isEditMode = Boolean(initialTest);
const handleClose = () => {
onClose();
- resetForm();
};
const handleChange = (e: React.ChangeEvent) => {
@@ -121,7 +98,6 @@ export const TestDetailsModal = ({
if (isEditMode) onConfirmEdit(testFields as Test);
else onConfirmAdd(testFields as Test);
- resetForm();
};
return (
diff --git a/client/src/features/trainee-profile/education/tests/TestsComponent.tsx b/client/src/features/trainee-profile/education/tests/TestsComponent.tsx
index 367efbb3..c05e8375 100644
--- a/client/src/features/trainee-profile/education/tests/TestsComponent.tsx
+++ b/client/src/features/trainee-profile/education/tests/TestsComponent.tsx
@@ -14,7 +14,7 @@ export const TestsComponent = () => {
const [isModalOpen, setIsModalOpen] = useState(false);
const [modalError, setModalError] = useState('');
- const [testToEdit, setTestToEdit] = useState(null);
+ const [initialTest, setInitialTest] = useState(null);
const { traineeId } = useTraineeProfileContext();
const { mutate: addTest, isPending: addTestLoading } = useAddTest(traineeId);
const { mutate: deleteTest, isPending: deleteTestLoading, error: deleteTestError } = useDeleteTest(traineeId);
@@ -27,7 +27,7 @@ export const TestsComponent = () => {
const handleSuccess = () => {
queryClient.invalidateQueries({ queryKey: ['tests', traineeId] });
setIsModalOpen(false);
- setTestToEdit(null);
+ setInitialTest(null);
};
const getErrorMessage = (error: Error | unknown) => {
@@ -37,11 +37,13 @@ export const TestsComponent = () => {
const onClickEdit = (id: string) => {
const test = tests?.find((test) => test.id === id) || null;
- setTestToEdit(test);
+ setInitialTest(test);
setIsModalOpen(true);
};
const onConfirmAdd = async (test: Test) => {
+ if (modalError) setModalError('');
+
addTest(test, {
onSuccess: handleSuccess,
onError: (e) => {
@@ -51,6 +53,7 @@ export const TestsComponent = () => {
};
const onConfirmEdit = (test: Test) => {
+ if (modalError) setModalError('');
editTest(test, {
onSuccess: handleSuccess,
onError: (e) => {
@@ -76,7 +79,7 @@ export const TestsComponent = () => {
*/
const closeModal = () => {
setIsModalOpen(false);
- setTestToEdit(null);
+ setInitialTest(null);
setModalError('');
};
@@ -127,13 +130,14 @@ export const TestsComponent = () => {
)}
>
diff --git a/client/src/features/trainee-profile/interactions/InteractionsInfo.tsx b/client/src/features/trainee-profile/interactions/InteractionsInfo.tsx
index a5c85e6b..cb623354 100644
--- a/client/src/features/trainee-profile/interactions/InteractionsInfo.tsx
+++ b/client/src/features/trainee-profile/interactions/InteractionsInfo.tsx
@@ -38,6 +38,7 @@ const InteractionsInfo = () => {
};
const onConfirmAdd = async (interaction: Interaction) => {
+ if (modalError) setModalError('');
addInteraction(interaction, {
onSuccess: handleSuccess,
onError: (e) => {
@@ -47,6 +48,7 @@ const InteractionsInfo = () => {
};
const onConfirmEdit = (interaction: Interaction) => {
+ if (modalError) setModalError('');
editInteraction(interaction, {
onSuccess: handleSuccess,
onError: (e) => {
@@ -90,13 +92,14 @@ const InteractionsInfo = () => {
)}
>
diff --git a/client/src/features/trainee-profile/interactions/components/InteractionDetailsModal.tsx b/client/src/features/trainee-profile/interactions/components/InteractionDetailsModal.tsx
index 6b064799..9e58d778 100644
--- a/client/src/features/trainee-profile/interactions/components/InteractionDetailsModal.tsx
+++ b/client/src/features/trainee-profile/interactions/components/InteractionDetailsModal.tsx
@@ -12,12 +12,12 @@ import {
Typography,
} from '@mui/material';
import { Interaction, InteractionType } from '../Interactions';
-import { useEffect, useState } from 'react';
import FormSelect from './FormSelect';
import FormTextField from './FormTextField';
import { LoadingButton } from '@mui/lab';
import { formatDate } from '../../utils/dateHelper';
+import { useState } from 'react';
const types = Object.values(InteractionType);
@@ -28,7 +28,7 @@ type InteractionDetailsModalProps = {
onClose: () => void;
onConfirmAdd: (t: Interaction) => void;
onConfirmEdit: (t: Interaction) => void;
- interactionToEdit: Interaction | null;
+ initialInteraction: Interaction | null;
};
export const InteractionDetailsModal = ({
@@ -38,51 +38,25 @@ export const InteractionDetailsModal = ({
onClose,
onConfirmAdd,
onConfirmEdit,
- interactionToEdit,
+ initialInteraction,
}: InteractionDetailsModalProps) => {
const [interactionFields, setInteractionFields] = useState>({
- id: '',
- date: new Date(),
- type: undefined,
- title: '',
- details: '',
- reporter: undefined,
+ id: initialInteraction?.id || '',
+ date: initialInteraction?.date || new Date(),
+ type: initialInteraction?.type || undefined,
+ title: initialInteraction?.title || '',
+ details: initialInteraction?.details || '',
+ reporter: initialInteraction?.reporter || undefined,
});
const [typeError, setTypeError] = useState(false);
const [detailsError, setDetailsError] = useState(false);
const [titleError, setTitleError] = useState(false);
- const [isEditMode, setIsEditMode] = useState(false);
-
- useEffect(() => {
- if (interactionToEdit) {
- setInteractionFields(interactionToEdit);
- setIsEditMode(true);
- return;
- } else {
- setIsEditMode(false);
- }
- resetForm();
- }, [interactionToEdit]);
-
- const resetForm = () => {
- setInteractionFields({
- id: '',
- date: new Date(),
- type: undefined,
- title: '',
- details: '',
- reporter: undefined,
- });
- setTypeError(false);
- setDetailsError(false);
- setTitleError(false);
- };
+ const isEditMode = Boolean(initialInteraction);
const handleClose = () => {
onClose();
- resetForm();
};
const handleChange = (field: keyof Interaction) => (e: React.ChangeEvent) => {
@@ -132,7 +106,6 @@ export const InteractionDetailsModal = ({
onConfirmEdit(interactionFields as Interaction);
} else {
onConfirmAdd(interactionFields as Interaction);
- resetForm();
}
};