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(); } };