diff --git a/package-lock.json b/package-lock.json index 480537a..0d31579 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,17 +14,20 @@ "@hello-pangea/dnd": "^18.0.1", "@mui/icons-material": "^7.3.1", "@mui/material": "^7.3.1", + "@stomp/stompjs": "^7.1.1", "@tailwindcss/vite": "^4.1.12", "@tanstack/react-query": "^5.87.4", "@tanstack/react-table": "^8.21.3", "axios": "^1.12.1", "dayjs": "^1.11.18", + "lodash": "^4.17.21", "react": "^18.3.1", "react-big-calendar": "^1.19.4", "react-dom": "^18.3.1", "react-error-boundary": "^6.0.0", "react-router-dom": "^7.8.2", "recharts": "^3.2.0", + "sockjs-client": "^1.6.1", "tailwindcss": "^4.1.12", "wx-react-gantt": "^1.3.1", "zustand": "^5.0.8" @@ -41,20 +44,6 @@ "vite": "^7.1.2" } }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/code-frame": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", @@ -70,9 +59,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", - "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", + "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", "dev": true, "license": "MIT", "engines": { @@ -80,22 +69,22 @@ } }, "node_modules/@babel/core": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz", - "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", + "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", "dev": true, "license": "MIT", "dependencies": { - "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.3", - "@babel/parser": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.3", - "@babel/types": "^7.28.2", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", + "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -110,6 +99,13 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, "node_modules/@babel/generator": { "version": "7.28.3", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", @@ -222,26 +218,26 @@ } }, "node_modules/@babel/helpers": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz", - "integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.27.2", - "@babel/types": "^7.28.2" + "@babel/types": "^7.28.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", - "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", "license": "MIT", "dependencies": { - "@babel/types": "^7.28.2" + "@babel/types": "^7.28.4" }, "bin": { "parser": "bin/babel-parser.js" @@ -283,9 +279,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz", - "integrity": "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -306,17 +302,17 @@ } }, "node_modules/@babel/traverse": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz", - "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", + "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.3", + "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", - "@babel/types": "^7.28.2", + "@babel/types": "^7.28.4", "debug": "^4.3.1" }, "engines": { @@ -324,9 +320,9 @@ } }, "node_modules/@babel/types": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", - "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", @@ -355,12 +351,6 @@ "stylis": "4.2.0" } }, - "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "license": "MIT" - }, "node_modules/@emotion/cache": { "version": "11.14.0", "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", @@ -381,9 +371,9 @@ "license": "MIT" }, "node_modules/@emotion/is-prop-valid": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz", - "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz", + "integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==", "license": "MIT", "dependencies": { "@emotion/memoize": "^0.9.0" @@ -905,9 +895,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", - "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", "dev": true, "license": "MIT", "dependencies": { @@ -1022,9 +1012,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.34.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.34.0.tgz", - "integrity": "sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==", + "version": "9.35.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.35.0.tgz", + "integrity": "sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==", "dev": true, "license": "MIT", "engines": { @@ -1112,9 +1102,9 @@ "license": "MIT" }, "node_modules/@headlessui/react": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.2.7.tgz", - "integrity": "sha512-WKdTymY8Y49H8/gUc/lIyYK1M+/6dq0Iywh4zTZVAaiTDprRfioxSgD0wnXTQTBpjpGJuTL1NO/mqEvc//5SSg==", + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.2.8.tgz", + "integrity": "sha512-vkiZulDC0lFeTrZTbA4tHvhZHvkUb2PFh5xJ1BvWAZdRK0fayMKO1QEO4inWkXxK1i0I1rcwwu1d6mo0K7Pcbw==", "license": "MIT", "dependencies": { "@floating-ui/react": "^0.26.16", @@ -1159,33 +1149,19 @@ } }, "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", "dev": true, "license": "Apache-2.0", "dependencies": { "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" + "@humanwhocodes/retry": "^0.4.0" }, "engines": { "node": ">=18.18.0" } }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -1262,9 +1238,9 @@ "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.30", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", - "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1272,9 +1248,9 @@ } }, "node_modules/@mui/core-downloads-tracker": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.3.1.tgz", - "integrity": "sha512-+mIK1Z0BhOaQ0vCgOkT1mSrIpEHLo338h4/duuL4TBLXPvUMit732mnwJY3W40Avy30HdeSfwUAAGRkKmwRaEQ==", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.3.2.tgz", + "integrity": "sha512-AOyfHjyDKVPGJJFtxOlept3EYEdLoar/RvssBTWVAvDJGIE676dLi2oT/Kx+FoVXFoA/JdV7DEMq/BVWV3KHRw==", "license": "MIT", "funding": { "type": "opencollective", @@ -1282,12 +1258,12 @@ } }, "node_modules/@mui/icons-material": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-7.3.1.tgz", - "integrity": "sha512-upzCtG6awpL6noEZlJ5Z01khZ9VnLNLaj7tb6iPbN6G97eYfUTs8e9OyPKy3rEms3VQWmVBfri7jzeaRxdFIzA==", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-7.3.2.tgz", + "integrity": "sha512-TZWazBjWXBjR6iGcNkbKklnwodcwj0SrChCNHc9BhD9rBgET22J1eFhHsEmvSvru9+opDy3umqAimQjokhfJlQ==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.2" + "@babel/runtime": "^7.28.3" }, "engines": { "node": ">=14.0.0" @@ -1297,7 +1273,7 @@ "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@mui/material": "^7.3.1", + "@mui/material": "^7.3.2", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -1308,16 +1284,16 @@ } }, "node_modules/@mui/material": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.3.1.tgz", - "integrity": "sha512-Xf6Shbo03YmcBedZMwSpEFOwpYDtU7tC+rhAHTrA9FHk0FpsDqiQ9jUa1j/9s3HLs7KWb5mDcGnlwdh9Q9KAag==", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.3.2.tgz", + "integrity": "sha512-qXvbnawQhqUVfH1LMgMaiytP+ZpGoYhnGl7yYq2x57GYzcFL/iPzSZ3L30tlbwEjSVKNYcbiKO8tANR1tadjUg==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.2", - "@mui/core-downloads-tracker": "^7.3.1", - "@mui/system": "^7.3.1", - "@mui/types": "^7.4.5", - "@mui/utils": "^7.3.1", + "@babel/runtime": "^7.28.3", + "@mui/core-downloads-tracker": "^7.3.2", + "@mui/system": "^7.3.2", + "@mui/types": "^7.4.6", + "@mui/utils": "^7.3.2", "@popperjs/core": "^2.11.8", "@types/react-transition-group": "^4.4.12", "clsx": "^2.1.1", @@ -1336,7 +1312,7 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@mui/material-pigment-css": "^7.3.1", + "@mui/material-pigment-css": "^7.3.2", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" @@ -1357,13 +1333,13 @@ } }, "node_modules/@mui/private-theming": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.3.1.tgz", - "integrity": "sha512-WU3YLkKXii/x8ZEKnrLKsPwplCVE11yZxUvlaaZSIzCcI3x2OdFC8eMlNy74hVeUsYQvzzX1Es/k4ARPlFvpPQ==", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.3.2.tgz", + "integrity": "sha512-ha7mFoOyZGJr75xeiO9lugS3joRROjc8tG1u4P50dH0KR7bwhHznVMcYg7MouochUy0OxooJm/OOSpJ7gKcMvg==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.2", - "@mui/utils": "^7.3.1", + "@babel/runtime": "^7.28.3", + "@mui/utils": "^7.3.2", "prop-types": "^15.8.1" }, "engines": { @@ -1384,12 +1360,12 @@ } }, "node_modules/@mui/styled-engine": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.3.1.tgz", - "integrity": "sha512-Nqo6OHjvJpXJ1+9TekTE//+8RybgPQUKwns2Lh0sq+8rJOUSUKS3KALv4InSOdHhIM9Mdi8/L7LTF1/Ky6D6TQ==", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.3.2.tgz", + "integrity": "sha512-PkJzW+mTaek4e0nPYZ6qLnW5RGa0KN+eRTf5FA2nc7cFZTeM+qebmGibaTLrgQBy3UpcpemaqfzToBNkzuxqew==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.2", + "@babel/runtime": "^7.28.3", "@emotion/cache": "^11.14.0", "@emotion/serialize": "^1.3.3", "@emotion/sheet": "^1.4.0", @@ -1418,16 +1394,16 @@ } }, "node_modules/@mui/system": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.3.1.tgz", - "integrity": "sha512-mIidecvcNVpNJMdPDmCeoSL5zshKBbYPcphjuh6ZMjhybhqhZ4mX6k9zmIWh6XOXcqRQMg5KrcjnO0QstrNj3w==", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.3.2.tgz", + "integrity": "sha512-9d8JEvZW+H6cVkaZ+FK56R53vkJe3HsTpcjMUtH8v1xK6Y1TjzHdZ7Jck02mGXJsE6MQGWVs3ogRHTQmS9Q/rA==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.2", - "@mui/private-theming": "^7.3.1", - "@mui/styled-engine": "^7.3.1", - "@mui/types": "^7.4.5", - "@mui/utils": "^7.3.1", + "@babel/runtime": "^7.28.3", + "@mui/private-theming": "^7.3.2", + "@mui/styled-engine": "^7.3.2", + "@mui/types": "^7.4.6", + "@mui/utils": "^7.3.2", "clsx": "^2.1.1", "csstype": "^3.1.3", "prop-types": "^15.8.1" @@ -1458,12 +1434,12 @@ } }, "node_modules/@mui/types": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.5.tgz", - "integrity": "sha512-ZPwlAOE3e8C0piCKbaabwrqZbW4QvWz0uapVPWya7fYj6PeDkl5sSJmomT7wjOcZGPB48G/a6Ubidqreptxz4g==", + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.6.tgz", + "integrity": "sha512-NVBbIw+4CDMMppNamVxyTccNv0WxtDb7motWDlMeSC8Oy95saj1TIZMGynPpFLePt3yOD8TskzumeqORCgRGWw==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.2" + "@babel/runtime": "^7.28.3" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" @@ -1475,13 +1451,13 @@ } }, "node_modules/@mui/utils": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.3.1.tgz", - "integrity": "sha512-/31y4wZqVWa0jzMnzo6JPjxwP6xXy4P3+iLbosFg/mJQowL1KIou0LC+lquWW60FKVbKz5ZUWBg2H3jausa0pw==", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.3.2.tgz", + "integrity": "sha512-4DMWQGenOdLnM3y/SdFQFwKsCLM+mqxzvoWp9+x2XdEzXapkznauHLiXtSohHs/mc0+5/9UACt1GdugCX2te5g==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.2", - "@mui/types": "^7.4.5", + "@babel/runtime": "^7.28.3", + "@mui/types": "^7.4.6", "@types/prop-types": "^15.7.15", "clsx": "^2.1.1", "prop-types": "^15.8.1", @@ -1657,9 +1633,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.49.0.tgz", - "integrity": "sha512-rlKIeL854Ed0e09QGYFlmDNbka6I3EQFw7iZuugQjMb11KMpJCLPFL4ZPbMfaEhLADEL1yx0oujGkBQ7+qW3eA==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.2.tgz", + "integrity": "sha512-uLN8NAiFVIRKX9ZQha8wy6UUs06UNSZ32xj6giK/rmMXAgKahwExvK6SsmgU5/brh4w/nSgj8e0k3c1HBQpa0A==", "cpu": [ "arm" ], @@ -1670,9 +1646,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.49.0.tgz", - "integrity": "sha512-cqPpZdKUSQYRtLLr6R4X3sD4jCBO1zUmeo3qrWBCqYIeH8Q3KRL4F3V7XJ2Rm8/RJOQBZuqzQGWPjjvFUcYa/w==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.2.tgz", + "integrity": "sha512-oEouqQk2/zxxj22PNcGSskya+3kV0ZKH+nQxuCCOGJ4oTXBdNTbv+f/E3c74cNLeMO1S5wVWacSws10TTSB77g==", "cpu": [ "arm64" ], @@ -1683,9 +1659,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.49.0.tgz", - "integrity": "sha512-99kMMSMQT7got6iYX3yyIiJfFndpojBmkHfTc1rIje8VbjhmqBXE+nb7ZZP3A5skLyujvT0eIUCUsxAe6NjWbw==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.50.2.tgz", + "integrity": "sha512-OZuTVTpj3CDSIxmPgGH8en/XtirV5nfljHZ3wrNwvgkT5DQLhIKAeuFSiwtbMto6oVexV0k1F1zqURPKf5rI1Q==", "cpu": [ "arm64" ], @@ -1696,9 +1672,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.49.0.tgz", - "integrity": "sha512-y8cXoD3wdWUDpjOLMKLx6l+NFz3NlkWKcBCBfttUn+VGSfgsQ5o/yDUGtzE9HvsodkP0+16N0P4Ty1VuhtRUGg==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.2.tgz", + "integrity": "sha512-Wa/Wn8RFkIkr1vy1k1PB//VYhLnlnn5eaJkfTQKivirOvzu5uVd2It01ukeQstMursuz7S1bU+8WW+1UPXpa8A==", "cpu": [ "x64" ], @@ -1709,9 +1685,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.49.0.tgz", - "integrity": "sha512-3mY5Pr7qv4GS4ZvWoSP8zha8YoiqrU+e0ViPvB549jvliBbdNLrg2ywPGkgLC3cmvN8ya3za+Q2xVyT6z+vZqA==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.2.tgz", + "integrity": "sha512-QkzxvH3kYN9J1w7D1A+yIMdI1pPekD+pWx7G5rXgnIlQ1TVYVC6hLl7SOV9pi5q9uIDF9AuIGkuzcbF7+fAhow==", "cpu": [ "arm64" ], @@ -1722,9 +1698,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.49.0.tgz", - "integrity": "sha512-C9KzzOAQU5gU4kG8DTk+tjdKjpWhVWd5uVkinCwwFub2m7cDYLOdtXoMrExfeBmeRy9kBQMkiyJ+HULyF1yj9w==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.2.tgz", + "integrity": "sha512-dkYXB0c2XAS3a3jmyDkX4Jk0m7gWLFzq1C3qUnJJ38AyxIF5G/dyS4N9B30nvFseCfgtCEdbYFhk0ChoCGxPog==", "cpu": [ "x64" ], @@ -1735,9 +1711,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.49.0.tgz", - "integrity": "sha512-OVSQgEZDVLnTbMq5NBs6xkmz3AADByCWI4RdKSFNlDsYXdFtlxS59J+w+LippJe8KcmeSSM3ba+GlsM9+WwC1w==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.2.tgz", + "integrity": "sha512-9VlPY/BN3AgbukfVHAB8zNFWB/lKEuvzRo1NKev0Po8sYFKx0i+AQlCYftgEjcL43F2h9Ui1ZSdVBc4En/sP2w==", "cpu": [ "arm" ], @@ -1748,9 +1724,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.49.0.tgz", - "integrity": "sha512-ZnfSFA7fDUHNa4P3VwAcfaBLakCbYaxCk0jUnS3dTou9P95kwoOLAMlT3WmEJDBCSrOEFFV0Y1HXiwfLYJuLlA==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.2.tgz", + "integrity": "sha512-+GdKWOvsifaYNlIVf07QYan1J5F141+vGm5/Y8b9uCZnG/nxoGqgCmR24mv0koIWWuqvFYnbURRqw1lv7IBINw==", "cpu": [ "arm" ], @@ -1761,9 +1737,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.49.0.tgz", - "integrity": "sha512-Z81u+gfrobVK2iV7GqZCBfEB1y6+I61AH466lNK+xy1jfqFLiQ9Qv716WUM5fxFrYxwC7ziVdZRU9qvGHkYIJg==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.2.tgz", + "integrity": "sha512-df0Eou14ojtUdLQdPFnymEQteENwSJAdLf5KCDrmZNsy1c3YaCNaJvYsEUHnrg+/DLBH612/R0xd3dD03uz2dg==", "cpu": [ "arm64" ], @@ -1774,9 +1750,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.49.0.tgz", - "integrity": "sha512-zoAwS0KCXSnTp9NH/h9aamBAIve0DXeYpll85shf9NJ0URjSTzzS+Z9evmolN+ICfD3v8skKUPyk2PO0uGdFqg==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.2.tgz", + "integrity": "sha512-iPeouV0UIDtz8j1YFR4OJ/zf7evjauqv7jQ/EFs0ClIyL+by++hiaDAfFipjOgyz6y6xbDvJuiU4HwpVMpRFDQ==", "cpu": [ "arm64" ], @@ -1786,10 +1762,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.49.0.tgz", - "integrity": "sha512-2QyUyQQ1ZtwZGiq0nvODL+vLJBtciItC3/5cYN8ncDQcv5avrt2MbKt1XU/vFAJlLta5KujqyHdYtdag4YEjYQ==", + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.50.2.tgz", + "integrity": "sha512-OL6KaNvBopLlj5fTa5D5bau4W82f+1TyTZRr2BdnfsrnQnmdxh4okMxR2DcDkJuh4KeoQZVuvHvzuD/lyLn2Kw==", "cpu": [ "loong64" ], @@ -1800,9 +1776,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.49.0.tgz", - "integrity": "sha512-k9aEmOWt+mrMuD3skjVJSSxHckJp+SiFzFG+v8JLXbc/xi9hv2icSkR3U7uQzqy+/QbbYY7iNB9eDTwrELo14g==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.2.tgz", + "integrity": "sha512-I21VJl1w6z/K5OTRl6aS9DDsqezEZ/yKpbqlvfHbW0CEF5IL8ATBMuUx6/mp683rKTK8thjs/0BaNrZLXetLag==", "cpu": [ "ppc64" ], @@ -1813,9 +1789,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.49.0.tgz", - "integrity": "sha512-rDKRFFIWJ/zJn6uk2IdYLc09Z7zkE5IFIOWqpuU0o6ZpHcdniAyWkwSUWE/Z25N/wNDmFHHMzin84qW7Wzkjsw==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.2.tgz", + "integrity": "sha512-Hq6aQJT/qFFHrYMjS20nV+9SKrXL2lvFBENZoKfoTH2kKDOJqff5OSJr4x72ZaG/uUn+XmBnGhfr4lwMRrmqCQ==", "cpu": [ "riscv64" ], @@ -1826,9 +1802,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.49.0.tgz", - "integrity": "sha512-FkkhIY/hYFVnOzz1WeV3S9Bd1h0hda/gRqvZCMpHWDHdiIHn6pqsY3b5eSbvGccWHMQ1uUzgZTKS4oGpykf8Tw==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.2.tgz", + "integrity": "sha512-82rBSEXRv5qtKyr0xZ/YMF531oj2AIpLZkeNYxmKNN6I2sVE9PGegN99tYDLK2fYHJITL1P2Lgb4ZXnv0PjQvw==", "cpu": [ "riscv64" ], @@ -1839,9 +1815,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.49.0.tgz", - "integrity": "sha512-gRf5c+A7QiOG3UwLyOOtyJMD31JJhMjBvpfhAitPAoqZFcOeK3Kc1Veg1z/trmt+2P6F/biT02fU19GGTS529A==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.2.tgz", + "integrity": "sha512-4Q3S3Hy7pC6uaRo9gtXUTJ+EKo9AKs3BXKc2jYypEcMQ49gDPFU2P1ariX9SEtBzE5egIX6fSUmbmGazwBVF9w==", "cpu": [ "s390x" ], @@ -1852,9 +1828,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.49.0.tgz", - "integrity": "sha512-BR7+blScdLW1h/2hB/2oXM+dhTmpW3rQt1DeSiCP9mc2NMMkqVgjIN3DDsNpKmezffGC9R8XKVOLmBkRUcK/sA==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.2.tgz", + "integrity": "sha512-9Jie/At6qk70dNIcopcL4p+1UirusEtznpNtcq/u/C5cC4HBX7qSGsYIcG6bdxj15EYWhHiu02YvmdPzylIZlA==", "cpu": [ "x64" ], @@ -1865,9 +1841,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.49.0.tgz", - "integrity": "sha512-hDMOAe+6nX3V5ei1I7Au3wcr9h3ktKzDvF2ne5ovX8RZiAHEtX1A5SNNk4zt1Qt77CmnbqT+upb/umzoPMWiPg==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.2.tgz", + "integrity": "sha512-HPNJwxPL3EmhzeAnsWQCM3DcoqOz3/IC6de9rWfGR8ZCuEHETi9km66bH/wG3YH0V3nyzyFEGUZeL5PKyy4xvw==", "cpu": [ "x64" ], @@ -1877,10 +1853,23 @@ "linux" ] }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.2.tgz", + "integrity": "sha512-nMKvq6FRHSzYfKLHZ+cChowlEkR2lj/V0jYj9JnGUVPL2/mIeFGmVM2mLaFeNa5Jev7W7TovXqXIG2d39y1KYA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.49.0.tgz", - "integrity": "sha512-wkNRzfiIGaElC9kXUT+HLx17z7D0jl+9tGYRKwd8r7cUqTL7GYAvgUY++U2hK6Ar7z5Z6IRRoWC8kQxpmM7TDA==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.2.tgz", + "integrity": "sha512-eFUvvnTYEKeTyHEijQKz81bLrUQOXKZqECeiWH6tb8eXXbZk+CXSG2aFrig2BQ/pjiVRj36zysjgILkqarS2YA==", "cpu": [ "arm64" ], @@ -1891,9 +1880,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.49.0.tgz", - "integrity": "sha512-gq5aW/SyNpjp71AAzroH37DtINDcX1Qw2iv9Chyz49ZgdOP3NV8QCyKZUrGsYX9Yyggj5soFiRCgsL3HwD8TdA==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.2.tgz", + "integrity": "sha512-cBaWmXqyfRhH8zmUxK3d3sAhEWLrtMjWBRwdMMHJIXSjvjLKvv49adxiEz+FJ8AP90apSDDBx2Tyd/WylV6ikA==", "cpu": [ "ia32" ], @@ -1904,9 +1893,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.49.0.tgz", - "integrity": "sha512-gEtqFbzmZLFk2xKh7g0Rlo8xzho8KrEFEkzvHbfUGkrgXOpZ4XagQ6n+wIZFNh1nTb8UD16J4nFSFKXYgnbdBg==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.2.tgz", + "integrity": "sha512-APwKy6YUhvZaEoHyM+9xqmTpviEI+9eL7LoCH+aLcvWYHJ663qG5zx7WzWZY+a9qkg5JtzcMyJ9z0WtQBMDmgA==", "cpu": [ "x64" ], @@ -1928,6 +1917,12 @@ "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", "license": "MIT" }, + "node_modules/@stomp/stompjs": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@stomp/stompjs/-/stompjs-7.1.1.tgz", + "integrity": "sha512-chcDs6YkAnKp1FqzwhGvh3i7v0+/ytzqWdKYw6XzINEKAzke/iD00dNgFPWSZEqktHOK+C1gSzXhLkLbARIaZw==", + "license": "Apache-2.0" + }, "node_modules/@swc/helpers": { "version": "0.5.17", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", @@ -1938,24 +1933,24 @@ } }, "node_modules/@tailwindcss/node": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.12.tgz", - "integrity": "sha512-3hm9brwvQkZFe++SBt+oLjo4OLDtkvlE8q2WalaD/7QWaeM7KEJbAiY/LJZUaCs7Xa8aUu4xy3uoyX4q54UVdQ==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.13.tgz", + "integrity": "sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw==", "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.5.1", "lightningcss": "1.30.1", - "magic-string": "^0.30.17", + "magic-string": "^0.30.18", "source-map-js": "^1.2.1", - "tailwindcss": "4.1.12" + "tailwindcss": "4.1.13" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.12.tgz", - "integrity": "sha512-gM5EoKHW/ukmlEtphNwaGx45fGoEmP10v51t9unv55voWh6WrOL19hfuIdo2FjxIaZzw776/BUQg7Pck++cIVw==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.13.tgz", + "integrity": "sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -1966,24 +1961,24 @@ "node": ">= 10" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.12", - "@tailwindcss/oxide-darwin-arm64": "4.1.12", - "@tailwindcss/oxide-darwin-x64": "4.1.12", - "@tailwindcss/oxide-freebsd-x64": "4.1.12", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.12", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.12", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.12", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.12", - "@tailwindcss/oxide-linux-x64-musl": "4.1.12", - "@tailwindcss/oxide-wasm32-wasi": "4.1.12", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.12", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.12" + "@tailwindcss/oxide-android-arm64": "4.1.13", + "@tailwindcss/oxide-darwin-arm64": "4.1.13", + "@tailwindcss/oxide-darwin-x64": "4.1.13", + "@tailwindcss/oxide-freebsd-x64": "4.1.13", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.13", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.13", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.13", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.13", + "@tailwindcss/oxide-linux-x64-musl": "4.1.13", + "@tailwindcss/oxide-wasm32-wasi": "4.1.13", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.13", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.13" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.12.tgz", - "integrity": "sha512-oNY5pq+1gc4T6QVTsZKwZaGpBb2N1H1fsc1GD4o7yinFySqIuRZ2E4NvGasWc6PhYJwGK2+5YT1f9Tp80zUQZQ==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.13.tgz", + "integrity": "sha512-BrpTrVYyejbgGo57yc8ieE+D6VT9GOgnNdmh5Sac6+t0m+v+sKQevpFVpwX3pBrM2qKrQwJ0c5eDbtjouY/+ew==", "cpu": [ "arm64" ], @@ -1997,9 +1992,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.12.tgz", - "integrity": "sha512-cq1qmq2HEtDV9HvZlTtrj671mCdGB93bVY6J29mwCyaMYCP/JaUBXxrQQQm7Qn33AXXASPUb2HFZlWiiHWFytw==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.13.tgz", + "integrity": "sha512-YP+Jksc4U0KHcu76UhRDHq9bx4qtBftp9ShK/7UGfq0wpaP96YVnnjFnj3ZFrUAjc5iECzODl/Ts0AN7ZPOANQ==", "cpu": [ "arm64" ], @@ -2013,9 +2008,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.12.tgz", - "integrity": "sha512-6UCsIeFUcBfpangqlXay9Ffty9XhFH1QuUFn0WV83W8lGdX8cD5/+2ONLluALJD5+yJ7k8mVtwy3zMZmzEfbLg==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.13.tgz", + "integrity": "sha512-aAJ3bbwrn/PQHDxCto9sxwQfT30PzyYJFG0u/BWZGeVXi5Hx6uuUOQEI2Fa43qvmUjTRQNZnGqe9t0Zntexeuw==", "cpu": [ "x64" ], @@ -2029,9 +2024,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.12.tgz", - "integrity": "sha512-JOH/f7j6+nYXIrHobRYCtoArJdMJh5zy5lr0FV0Qu47MID/vqJAY3r/OElPzx1C/wdT1uS7cPq+xdYYelny1ww==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.13.tgz", + "integrity": "sha512-Wt8KvASHwSXhKE/dJLCCWcTSVmBj3xhVhp/aF3RpAhGeZ3sVo7+NTfgiN8Vey/Fi8prRClDs6/f0KXPDTZE6nQ==", "cpu": [ "x64" ], @@ -2045,9 +2040,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.12.tgz", - "integrity": "sha512-v4Ghvi9AU1SYgGr3/j38PD8PEe6bRfTnNSUE3YCMIRrrNigCFtHZ2TCm8142X8fcSqHBZBceDx+JlFJEfNg5zQ==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.13.tgz", + "integrity": "sha512-mbVbcAsW3Gkm2MGwA93eLtWrwajz91aXZCNSkGTx/R5eb6KpKD5q8Ueckkh9YNboU8RH7jiv+ol/I7ZyQ9H7Bw==", "cpu": [ "arm" ], @@ -2061,9 +2056,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.12.tgz", - "integrity": "sha512-YP5s1LmetL9UsvVAKusHSyPlzSRqYyRB0f+Kl/xcYQSPLEw/BvGfxzbH+ihUciePDjiXwHh+p+qbSP3SlJw+6g==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.13.tgz", + "integrity": "sha512-wdtfkmpXiwej/yoAkrCP2DNzRXCALq9NVLgLELgLim1QpSfhQM5+ZxQQF8fkOiEpuNoKLp4nKZ6RC4kmeFH0HQ==", "cpu": [ "arm64" ], @@ -2077,9 +2072,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.12.tgz", - "integrity": "sha512-V8pAM3s8gsrXcCv6kCHSuwyb/gPsd863iT+v1PGXC4fSL/OJqsKhfK//v8P+w9ThKIoqNbEnsZqNy+WDnwQqCA==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.13.tgz", + "integrity": "sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg==", "cpu": [ "arm64" ], @@ -2093,9 +2088,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.12.tgz", - "integrity": "sha512-xYfqYLjvm2UQ3TZggTGrwxjYaLB62b1Wiysw/YE3Yqbh86sOMoTn0feF98PonP7LtjsWOWcXEbGqDL7zv0uW8Q==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.13.tgz", + "integrity": "sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ==", "cpu": [ "x64" ], @@ -2109,9 +2104,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.12.tgz", - "integrity": "sha512-ha0pHPamN+fWZY7GCzz5rKunlv9L5R8kdh+YNvP5awe3LtuXb5nRi/H27GeL2U+TdhDOptU7T6Is7mdwh5Ar3A==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.13.tgz", + "integrity": "sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ==", "cpu": [ "x64" ], @@ -2125,9 +2120,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.12.tgz", - "integrity": "sha512-4tSyu3dW+ktzdEpuk6g49KdEangu3eCYoqPhWNsZgUhyegEda3M9rG0/j1GV/JjVVsj+lG7jWAyrTlLzd/WEBg==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.13.tgz", + "integrity": "sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -2154,9 +2149,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.12.tgz", - "integrity": "sha512-iGLyD/cVP724+FGtMWslhcFyg4xyYyM+5F4hGvKA7eifPkXHRAUDFaimu53fpNg9X8dfP75pXx/zFt/jlNF+lg==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.13.tgz", + "integrity": "sha512-dziTNeQXtoQ2KBXmrjCxsuPk3F3CQ/yb7ZNZNA+UkNTeiTGgfeh+gH5Pi7mRncVgcPD2xgHvkFCh/MhZWSgyQg==", "cpu": [ "arm64" ], @@ -2170,9 +2165,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.12.tgz", - "integrity": "sha512-NKIh5rzw6CpEodv/++r0hGLlfgT/gFN+5WNdZtvh6wpU2BpGNgdjvj6H2oFc8nCM839QM1YOhjpgbAONUb4IxA==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.13.tgz", + "integrity": "sha512-3+LKesjXydTkHk5zXX01b5KMzLV1xl2mcktBJkje7rhFUpUlYJy7IMOLqjIRQncLTa1WZZiFY/foAeB5nmaiTw==", "cpu": [ "x64" ], @@ -2186,14 +2181,14 @@ } }, "node_modules/@tailwindcss/vite": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.12.tgz", - "integrity": "sha512-4pt0AMFDx7gzIrAOIYgYP0KCBuKWqyW8ayrdiLEjoJTT4pKTjrzG/e4uzWtTLDziC+66R9wbUqZBccJalSE5vQ==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.13.tgz", + "integrity": "sha512-0PmqLQ010N58SbMTJ7BVJ4I2xopiQn/5i6nlb4JmxzQf8zcS5+m2Cv6tqh+sfDwtIdjoEnOvwsGQ1hkUi8QEHQ==", "license": "MIT", "dependencies": { - "@tailwindcss/node": "4.1.12", - "@tailwindcss/oxide": "4.1.12", - "tailwindcss": "4.1.12" + "@tailwindcss/node": "4.1.13", + "@tailwindcss/oxide": "4.1.13", + "tailwindcss": "4.1.13" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" @@ -2382,9 +2377,9 @@ } }, "node_modules/@types/d3-time": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-1.1.4.tgz", - "integrity": "sha512-JIvy2HjRInE+TXOmIGN5LCmeO0hkFZx5f9FZ7kiN+D+YTcc8pptsiLiuHsvwxwC7VVKmJ2ExHUgNlAiV7vQM9g==", + "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": { @@ -2550,9 +2545,9 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.1.tgz", - "integrity": "sha512-Kn4kbSXpkFHCGE6rBFNwIv0GQs4AvDT80jlveJDKFxjbTYMUeB4QtsdPCv6H8Cm19Je7IU6VFtRl2zWZI0rudQ==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", + "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -2582,6 +2577,16 @@ "dev": true, "license": "MIT" }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.3.tgz", + "integrity": "sha512-mcE+Wr2CAhHNWxXN/DdTI+n4gsPc5QpXpWnyCQWiQYIYZX+ZMJ8juXZgjRa/0/YPJo/NSsgW15/YgmI4nbysYw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -2594,9 +2599,9 @@ } }, "node_modules/browserslist": { - "version": "4.25.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.4.tgz", - "integrity": "sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg==", + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.0.tgz", + "integrity": "sha512-P9go2WrP9FiPwLv3zqRD/Uoxo0RSHjzFCiQz7d4vbmwNqQFo9T9WCeP/Qn5EbcKQY6DBbkxEXNcpJOmncNrb7A==", "dev": true, "funding": [ { @@ -2614,9 +2619,10 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001737", - "electron-to-chromium": "^1.5.211", - "node-releases": "^2.0.19", + "baseline-browser-mapping": "^2.8.2", + "caniuse-lite": "^1.0.30001741", + "electron-to-chromium": "^1.5.218", + "node-releases": "^2.0.21", "update-browserslist-db": "^1.1.3" }, "bin": { @@ -2649,9 +2655,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001737", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001737.tgz", - "integrity": "sha512-BiloLiXtQNrY5UyF0+1nSJLXUENuhka2pzy2Fx5pGxqavdrxSCW4U6Pn/PoG3Efspi2frRbHpBV2XsrPE6EDlw==", + "version": "1.0.30001741", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001741.tgz", + "integrity": "sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw==", "dev": true, "funding": [ { @@ -2744,10 +2750,9 @@ "license": "MIT" }, "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "license": "MIT" }, "node_modules/cookie": { @@ -2845,10 +2850,13 @@ } }, "node_modules/d3-format": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz", - "integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==", - "license": "BSD-3-Clause" + "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", @@ -2887,18 +2895,6 @@ "node": ">=12" } }, - "node_modules/d3-scale/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-shape": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", @@ -2912,18 +2908,27 @@ } }, "node_modules/d3-time": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz", - "integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==", - "license": "BSD-3-Clause" + "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": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-3.0.0.tgz", - "integrity": "sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag==", - "license": "BSD-3-Clause", + "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 - 2" + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" } }, "node_modules/d3-timer": { @@ -2948,9 +2953,9 @@ "license": "MIT" }, "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -2996,9 +3001,9 @@ } }, "node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.0.tgz", + "integrity": "sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg==", "license": "Apache-2.0", "engines": { "node": ">=8" @@ -3029,9 +3034,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.211", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.211.tgz", - "integrity": "sha512-IGBvimJkotaLzFnwIVgW9/UD/AOJ2tByUmeOrtqBfACSbAw5b1G0XpvdaieKyc7ULmbwXVx+4e4Be8pOPBrYkw==", + "version": "1.5.218", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.218.tgz", + "integrity": "sha512-uwwdN0TUHs8u6iRgN8vKeWZMRll4gBkz+QMqdS7DDe49uiK68/UX92lFb61oiFPrpYZNeZIqa4bA7O6Aiasnzg==", "dev": true, "license": "ISC" }, @@ -3176,19 +3181,19 @@ } }, "node_modules/eslint": { - "version": "9.34.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.34.0.tgz", - "integrity": "sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==", + "version": "9.35.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.35.0.tgz", + "integrity": "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.1", "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.34.0", + "@eslint/js": "9.35.0", "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", @@ -3359,6 +3364,15 @@ "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", "license": "MIT" }, + "node_modules/eventsource": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", + "integrity": "sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3380,6 +3394,18 @@ "dev": true, "license": "MIT" }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "license": "Apache-2.0", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -3579,9 +3605,9 @@ "integrity": "sha512-5e01v8eLGfuQSOvx2MsDMOWS0GFtCx1wPzQSmcHw4hkxFzrQDBO3Xwg/m8Hr/7qXMrHeOIE29qWVzyv06u1TZA==" }, "node_modules/globals": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", - "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", "dev": true, "license": "MIT", "engines": { @@ -3673,6 +3699,12 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, + "node_modules/http-parser-js": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", + "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", + "license": "MIT" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -3719,6 +3751,12 @@ "node": ">=0.8.19" } }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "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", @@ -4184,18 +4222,18 @@ } }, "node_modules/luxon": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.1.tgz", - "integrity": "sha512-RkRWjA926cTvz5rAb1BqyWkKbbjzCGchDUIKMCUvNi17j6f6j8uHGDV82Aqcqtzd+icoYpELmG3ksgGiFNNcNg==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz", + "integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==", "license": "MIT", "engines": { "node": ">=12" } }, "node_modules/magic-string": { - "version": "0.30.18", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz", - "integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==", + "version": "0.30.19", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" @@ -4339,9 +4377,9 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", + "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", "dev": true, "license": "MIT" }, @@ -4558,6 +4596,12 @@ "node": ">=6" } }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "license": "MIT" + }, "node_modules/raf-schd": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", @@ -4704,9 +4748,9 @@ } }, "node_modules/react-router": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.8.2.tgz", - "integrity": "sha512-7M2fR1JbIZ/jFWqelpvSZx+7vd7UlBTfdZqf6OSdF9g6+sfdqJDAWcak6ervbHph200ePlu+7G8LdoiC3ReyAQ==", + "version": "7.9.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.1.tgz", + "integrity": "sha512-pfAByjcTpX55mqSDGwGnY9vDCpxqBLASg0BMNAuMmpSGESo/TaOUG6BllhAtAkCGx8Rnohik/XtaqiYUJtgW2g==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", @@ -4726,12 +4770,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.8.2.tgz", - "integrity": "sha512-Z4VM5mKDipal2jQ385H6UBhiiEDlnJPx6jyWsTYoZQdl5TrjxEV2a9yl3Fi60NBJxYzOTGTTHXPi0pdizvTwow==", + "version": "7.9.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.9.1.tgz", + "integrity": "sha512-U9WBQssBE9B1vmRjo9qTM7YRzfZ3lUxESIZnsf4VjR/lXYz9MHjvOxHzr/aUm4efpktbVOrF09rL/y4VHa8RMw==", "license": "MIT", "dependencies": { - "react-router": "7.8.2" + "react-router": "7.9.1" }, "engines": { "node": ">=20.0.0" @@ -4799,6 +4843,12 @@ "redux": "^5.0.0" } }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT" + }, "node_modules/reselect": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", @@ -4835,9 +4885,9 @@ } }, "node_modules/rollup": { - "version": "4.49.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.49.0.tgz", - "integrity": "sha512-3IVq0cGJ6H7fKXXEdVt+RcYvRCt8beYY9K1760wGQwSAHZcS9eot1zDG5axUbcp/kWRi5zKIIDX8MoKv/TzvZA==", + "version": "4.50.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.2.tgz", + "integrity": "sha512-BgLRGy7tNS9H66aIMASq1qSYbAAJV6Z6WR4QYTvj5FgF15rZ/ympT1uixHXwzbZUBDbkvqUI1KR0fH1FhMaQ9w==", "license": "MIT", "dependencies": { "@types/estree": "1.0.8" @@ -4850,29 +4900,50 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.49.0", - "@rollup/rollup-android-arm64": "4.49.0", - "@rollup/rollup-darwin-arm64": "4.49.0", - "@rollup/rollup-darwin-x64": "4.49.0", - "@rollup/rollup-freebsd-arm64": "4.49.0", - "@rollup/rollup-freebsd-x64": "4.49.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.49.0", - "@rollup/rollup-linux-arm-musleabihf": "4.49.0", - "@rollup/rollup-linux-arm64-gnu": "4.49.0", - "@rollup/rollup-linux-arm64-musl": "4.49.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.49.0", - "@rollup/rollup-linux-ppc64-gnu": "4.49.0", - "@rollup/rollup-linux-riscv64-gnu": "4.49.0", - "@rollup/rollup-linux-riscv64-musl": "4.49.0", - "@rollup/rollup-linux-s390x-gnu": "4.49.0", - "@rollup/rollup-linux-x64-gnu": "4.49.0", - "@rollup/rollup-linux-x64-musl": "4.49.0", - "@rollup/rollup-win32-arm64-msvc": "4.49.0", - "@rollup/rollup-win32-ia32-msvc": "4.49.0", - "@rollup/rollup-win32-x64-msvc": "4.49.0", + "@rollup/rollup-android-arm-eabi": "4.50.2", + "@rollup/rollup-android-arm64": "4.50.2", + "@rollup/rollup-darwin-arm64": "4.50.2", + "@rollup/rollup-darwin-x64": "4.50.2", + "@rollup/rollup-freebsd-arm64": "4.50.2", + "@rollup/rollup-freebsd-x64": "4.50.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.50.2", + "@rollup/rollup-linux-arm-musleabihf": "4.50.2", + "@rollup/rollup-linux-arm64-gnu": "4.50.2", + "@rollup/rollup-linux-arm64-musl": "4.50.2", + "@rollup/rollup-linux-loong64-gnu": "4.50.2", + "@rollup/rollup-linux-ppc64-gnu": "4.50.2", + "@rollup/rollup-linux-riscv64-gnu": "4.50.2", + "@rollup/rollup-linux-riscv64-musl": "4.50.2", + "@rollup/rollup-linux-s390x-gnu": "4.50.2", + "@rollup/rollup-linux-x64-gnu": "4.50.2", + "@rollup/rollup-linux-x64-musl": "4.50.2", + "@rollup/rollup-openharmony-arm64": "4.50.2", + "@rollup/rollup-win32-arm64-msvc": "4.50.2", + "@rollup/rollup-win32-ia32-msvc": "4.50.2", + "@rollup/rollup-win32-x64-msvc": "4.50.2", "fsevents": "~2.3.2" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", @@ -4921,6 +4992,34 @@ "node": ">=8" } }, + "node_modules/sockjs-client": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.6.1.tgz", + "integrity": "sha512-2g0tjOR+fRs0amxENLi/q5TiJTqY+WXFOzb5UwXndlK6TO3U/mirZznpx6w34HVMoc3g7cY24yC/ZMIYnDlfkw==", + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "eventsource": "^2.0.2", + "faye-websocket": "^0.11.4", + "inherits": "^2.0.4", + "url-parse": "^1.5.10" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://tidelift.com/funding/github/npm/sockjs-client" + } + }, + "node_modules/sockjs-client/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -4990,9 +5089,9 @@ "license": "MIT" }, "node_modules/tailwindcss": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.12.tgz", - "integrity": "sha512-DzFtxOi+7NsFf7DBtI3BJsynR+0Yp6etH+nRPTbpWnS2pZBaSksv/JGctNwSWzbFjp0vxSqknaUylseZqMDGrA==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.13.tgz", + "integrity": "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==", "license": "MIT" }, "node_modules/tapable": { @@ -5041,13 +5140,13 @@ "license": "MIT" }, "node_modules/tinyglobby": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", - "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "license": "MIT", "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { "node": ">=12.0.0" @@ -5131,6 +5230,16 @@ "punycode": "^2.1.0" } }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/use-sync-external-store": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", @@ -5162,28 +5271,10 @@ "d3-timer": "^3.0.1" } }, - "node_modules/victory-vendor/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/victory-vendor/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/vite": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.3.tgz", - "integrity": "sha512-OOUi5zjkDxYrKhTV3V7iKsoS37VUM7v40+HuwEmcrsf11Cdx9y3DIr2Px6liIcZFwt3XSRpQvFpL3WVy7ApkGw==", + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.5.tgz", + "integrity": "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==", "license": "MIT", "dependencies": { "esbuild": "^0.25.0", @@ -5191,7 +5282,7 @@ "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", - "tinyglobby": "^0.2.14" + "tinyglobby": "^0.2.15" }, "bin": { "vite": "bin/vite.js" @@ -5263,6 +5354,29 @@ "loose-envify": "^1.0.0" } }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "license": "Apache-2.0", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -5306,6 +5420,20 @@ "dev": true, "license": "ISC" }, + "node_modules/yaml": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "license": "ISC", + "optional": true, + "peer": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index dfc2789..fc78787 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@hello-pangea/dnd": "^18.0.1", "@mui/icons-material": "^7.3.1", "@mui/material": "^7.3.1", + "@stomp/stompjs": "^7.1.1", "@tailwindcss/vite": "^4.1.12", "@tanstack/react-query": "^5.87.4", "@tanstack/react-table": "^8.21.3", @@ -26,6 +27,7 @@ "react-dom": "^18.3.1", "react-error-boundary": "^6.0.0", "react-router-dom": "^7.8.2", + "sockjs-client": "^1.6.1", "recharts": "^3.2.0", "tailwindcss": "^4.1.12", "wx-react-gantt": "^1.3.1", diff --git a/src/api/chatApi.js b/src/api/chatApi.js new file mode 100644 index 0000000..1ca7459 --- /dev/null +++ b/src/api/chatApi.js @@ -0,0 +1,43 @@ +import api from './client'; + +// --- ChatRoomController API --- + +// 모든 채팅방 목록 가져오기 +export const fetchChatRooms = () => api.get('/chat-room/all'); + +// 특정 채팅방의 메시지 내역 가져오기 (페이지네이션) +export const fetchMessages = (chatRoomId, page = 0, size = 50) => + api.get(`/chat-room/${chatRoomId}/messages`, { params: { page, size } }); + +// 채팅방 생성 +export const createChatRoom = (memberIdList, roomName, type) => + api.post('/chat-room/create', { memberIdList, name: roomName, type }); + +// 채팅방에 사용자 초대 +export const inviteToChatRoom = (chatRoomId, memberIdList) => + api.post(`/chat-room/${chatRoomId}/invite`, { memberIdList }); + +// 이미 존재하는 채팅방에 멤버 추가 +export const addMembersToExistingChatRoom = (chatRoomId, memberIdList) => + api.post(`/chat-room/${chatRoomId}/add-members`, { chatRoomId, memberIdList }); + +// 메시지 삭제 +export const deleteMessage = (messageId) => api.delete(`/chat-room/messages/${messageId}`); + +// 채팅방 퇴장 +export const leaveChatRoom = (chatRoomId) => api.delete(`/chat-room/delete/${chatRoomId}`); + +// 전체 안 읽은 메시지 개수 조회 +export const fetchTotalUnreadCount = () => api.get('/chat-room/total-unread-count'); + +// 특정 채팅방 메시지 모두 읽음 처리 +export const markAllAsRead = (chatRoomId, lastReadMessageId) => + api.get(`/chat-room/${chatRoomId}/mark-all-as-read`, { params: { lastReadMessageId } }); + +// --- MemberProfileController API --- + +// 모든 사용자 프로필 조회 +export const fetchAllMembers = () => api.get('/profile/all'); + +// 이름 또는 이메일로 사용자 검색 +export const searchMembers = (keyword) => api.get('/profile/search', { params: { keyword } }); diff --git a/src/api/client.js b/src/api/client.js index b8fbe92..681ab49 100644 --- a/src/api/client.js +++ b/src/api/client.js @@ -36,4 +36,4 @@ api.interceptors.response.use( } ); -export default api; +export default api; \ No newline at end of file diff --git a/src/api/socketService.js b/src/api/socketService.js new file mode 100644 index 0000000..9c4a39b --- /dev/null +++ b/src/api/socketService.js @@ -0,0 +1,94 @@ +import { Client } from '@stomp/stompjs'; +import SockJS from 'sockjs-client'; + +let stompClient = null; +let subscriptions = new Map(); + +// WebSocket 연결 +export const connectWebSocket = (onConnected) => { + if (stompClient && stompClient.active) { + console.log('WebSocket is already connected.'); + if (onConnected) onConnected(); + return; + } + + stompClient = new Client({ + webSocketFactory: () => new SockJS('http://localhost:8081/ws/chat'), // 백엔드 포트 8081로 수정 + reconnectDelay: 5000, + heartbeatIncoming: 4000, + heartbeatOutgoing: 4000, + onConnect: () => { + console.log('WebSocket connected!'); + // 연결 성공 후, 콜백 실행 + if (onConnected) onConnected(); + }, + onStompError: (frame) => { + console.error('Broker reported error: ' + frame.headers['message']); + console.error('Additional details: ' + frame.body); + }, + // 연결 끊김 시 재연결 시도 + onDisconnect: (frame) => { + console.log('WebSocket disconnected:', frame); + // 필요한 경우 재연결 로직 추가 + }, + }); + stompClient.activate(); +}; + +// WebSocket 연결 해제 +export const disconnectWebSocket = () => { + if (stompClient) { + stompClient.deactivate(); + stompClient = null; + subscriptions.clear(); + console.log('WebSocket disconnected!'); + } +}; + +export const subscribe = (destination, callback) => { + if (!stompClient || !stompClient.active) { + console.error('STOMP client is not connected. Cannot subscribe to ', destination); + return null; + } + // 동일한 주소에 대한 중복 구독 방지 (기존 구독 해지 후 재구독) + if (subscriptions.has(destination)) { + subscriptions.get(destination).unsubscribe(); + subscriptions.delete(destination); + } + + const subscription = stompClient.subscribe(destination, (message) => { + callback(JSON.parse(message.body)); + }); + subscriptions.set(destination, subscription); + return subscription; +} + +// 특정 채팅방 토픽 구독 +export const subscribeToChatRoom = (chatRoomId, onMessageReceived) => { + return subscribe(`/topic/chatroom/${chatRoomId}`, onMessageReceived); +}; + +// 개인 알림 큐 구독 +export const subscribeToUserQueue = (queueName, onNotificationReceived) => { + return subscribe(`/user/queue/${queueName}`, onNotificationReceived); +}; + +// 구독 해지 +export const unsubscribe = (destination) => { + if (subscriptions.has(destination)) { + subscriptions.get(destination).unsubscribe(); + subscriptions.delete(destination); + } +} + +// 메시지 전송 (발행) +export const sendMessage = (chatMessage) => { + if (stompClient && stompClient.active) { + stompClient.publish({ + destination: '/app/send', + body: JSON.stringify(chatMessage), + }); + } else { + console.error('Cannot send message, STOMP client is not connected.'); + } +}; \ No newline at end of file diff --git a/src/components/common/chat/ChatHeader.jsx b/src/components/common/chat/ChatHeader.jsx index ed39683..b98906a 100644 --- a/src/components/common/chat/ChatHeader.jsx +++ b/src/components/common/chat/ChatHeader.jsx @@ -7,7 +7,7 @@ const ChatHeader = ({ isMinimized, onBackToList, onToggleMinimize, - onToggleChat + onToggleChat, }) => { const { user } = useAuth(); diff --git a/src/components/common/chat/ChatRoom.jsx b/src/components/common/chat/ChatRoom.jsx index 8bdb240..d2b72c0 100644 --- a/src/components/common/chat/ChatRoom.jsx +++ b/src/components/common/chat/ChatRoom.jsx @@ -1,33 +1,49 @@ -import { useEffect, useRef } from 'react' -import { Send } from '@mui/icons-material' -import useChatStore from '../../../store/chatStore' -import { useAuth } from '../../../hooks/useAuth' +import { useEffect, useRef } from 'react'; +import { Send } from '@mui/icons-material'; +import useChatStore from '../../../store/chatStore'; -const ChatRoom = ({ - selectedChat, - message, - onMessageChange, - onSendMessage +const ChatRoom = ({ + selectedChat, + message, + onMessageChange, + onSendMessage }) => { - const messagesEndRef = useRef(null) - const { user } = useAuth() - - // Zustand store에서 메시지 가져오기 - const { getMessages } = useChatStore() - const messages = getMessages(selectedChat?.id || 0) + const messagesEndRef = useRef(null); - // 새 메시지가 추가될 때마다 스크롤을 맨 아래로 + const { getMessages, connectWebSocket, disconnectWebSocket, markAsRead, currentUser } = useChatStore(); + const messages = [...getMessages(selectedChat?.id || 0)].sort((a, b) => { + return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(); + }); + + // WebSocket 연결 및 구독 관리 + useEffect(() => { + if (selectedChat?.id && currentUser?.id) { + connectWebSocket(currentUser.id, selectedChat.id); + + // 채팅방 진입 시 마지막 메시지를 읽음 처리 + const lastMessage = messages[messages.length - 1]; + if (lastMessage && lastMessage.senderId !== currentUser.id) { // 내가 보낸 메시지가 아닐 경우 + markAsRead(selectedChat.id, lastMessage.id); + } + } + + return () => { + disconnectWebSocket(); + }; + }, [selectedChat?.id, currentUser?.id, connectWebSocket, disconnectWebSocket, markAsRead, messages.length]); + + // 메시지 스크롤 useEffect(() => { - messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }) - }, [messages]) + messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); + }, [messages]); - // 시간 포맷팅 const formatTime = (timestamp) => { + if (!timestamp) return ''; return new Date(timestamp).toLocaleTimeString('ko-KR', { hour: '2-digit', minute: '2-digit' - }) - } + }); + }; return (
@@ -36,31 +52,42 @@ const ChatRoom = ({
{messages.length === 0 ? (
-

{selectedChat?.name}님과의 채팅을 시작해보세요!

+

{selectedChat?.name} 와(과)의 채팅을 시작해보세요!

) : ( - messages.map((msg) => ( -
-
- {msg.sender !== user?.name && ( -

{msg.sender}

- )} -

{msg.text}

- - {formatTime(msg.timestamp)} - + messages.map((msg) => { + const isMine = msg.senderId === currentUser.id; + return ( +
+
+ {!isMine && msg.type !== 'DELETED' && msg.type !== 'SYSTEM' && ( +

{msg.senderName}

+ )} + {msg.type === 'DELETED' ? ( +

삭제된 메시지입니다.

+ ) : ( +

{msg.content}

+ )} +
+ + {formatTime(msg.createdAt)} + + {isMine && msg.readCount > 0 && msg.type !== 'DELETED' && ( + {msg.readCount} + )} +
+
-
- )) + ) + }) )}
@@ -88,7 +115,8 @@ const ChatRoom = ({
- ) -} + ); +}; + -export default ChatRoom +export default ChatRoom; \ No newline at end of file diff --git a/src/components/common/chat/ChatRoomList.jsx b/src/components/common/chat/ChatRoomList.jsx index 81724f4..79bbcc0 100644 --- a/src/components/common/chat/ChatRoomList.jsx +++ b/src/components/common/chat/ChatRoomList.jsx @@ -1,12 +1,33 @@ -import { Search, Add, Group } from '@mui/icons-material' +import { Search, Add, Group } from '@mui/icons-material'; const ChatRoomList = ({ searchTerm, onSearchChange, filteredChatRooms, onSelectChatRoom, - onGoToCreateChat + onGoToCreateChat, + onContextMenu, + currentUserName // 현재 사용자 이름을 props로 전달받음 }) => { + + const formatDisplayTime = (timestamp) => { + if (!timestamp) return ''; + const date = new Date(timestamp); + return date.toLocaleTimeString('ko-KR', { + hour: '2-digit', + minute: '2-digit' + }); + }; + + const getChatRoomName = (room) => { + if (room.memberNameList && room.memberNameList.length === 2) { + // 1:1 채팅방인 경우, 현재 사용자를 제외한 이름 반환 + return room.memberNameList.find((name) => name !== currentUserName) || '알 수 없음'; + } + // 그룹 채팅방인 경우 기본 이름 사용 + return room.name || '알 수 없음'; + }; + return (
{/* 검색 및 채팅방 생성 */} @@ -33,29 +54,30 @@ const ChatRoomList = ({ {/* 채팅방 목록 */}
- {filteredChatRooms.map((room) => ( + {(filteredChatRooms || []).map((room) => (
onSelectChatRoom(room)} + onContextMenu={(e) => onContextMenu(e, room)} className="flex items-center p-3 hover:bg-gray-50 cursor-pointer transition-colors border-b border-gray-100 last:border-b-0" >
- {room.type === 'group' ? ( + {room.memberNameList && room.memberNameList.length > 2 ? ( ) : ( - room.name.substring(0, 1) + room.name?.substring(0, 1) )}
-

{room.name}

- {room.lastTime} +

{getChatRoomName(room)}

{/* 채팅방 이름 표시 */} + {formatDisplayTime(room.messageCreatedAt)}
-

{room.lastMessage}

+

{room.recentMessage || ' '}

- {room.unreadCount > 0 && ( + {room.unreadMessageCount > 0 && (
- {room.unreadCount} + {room.unreadMessageCount}
)}
@@ -65,4 +87,4 @@ const ChatRoomList = ({ ) } -export default ChatRoomList +export default ChatRoomList; \ No newline at end of file diff --git a/src/components/common/chat/ContextMenu.jsx b/src/components/common/chat/ContextMenu.jsx new file mode 100644 index 0000000..ae6392f --- /dev/null +++ b/src/components/common/chat/ContextMenu.jsx @@ -0,0 +1,49 @@ +import React, { useEffect, useRef } from 'react'; + +const ContextMenu = ({ x, y, visible, items, onClose }) => { + const menuRef = useRef(null); + + useEffect(() => { + const handleClickOutside = (event) => { + if (menuRef.current && !menuRef.current.contains(event.target)) { + onClose(); + } + }; + + if (visible) { + document.addEventListener('mousedown', handleClickOutside); + } + + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, [visible, onClose]); + + if (!visible) return null; + + return ( +
+
    + {items.map((item, index) => ( +
  • + +
  • + ))} +
+
+ ); +}; + +export default ContextMenu; diff --git a/src/components/common/chat/EmployeeSearch.jsx b/src/components/common/chat/EmployeeSearch.jsx index a422fd1..e3319b6 100644 --- a/src/components/common/chat/EmployeeSearch.jsx +++ b/src/components/common/chat/EmployeeSearch.jsx @@ -6,7 +6,9 @@ const EmployeeSearch = ({ filteredEmployees, selectedEmployees, onToggleEmployee, - onCreateGroupChat + onCreateGroupChat, + groupName, + onGroupNameChange }) => { return (
@@ -72,6 +74,17 @@ const EmployeeSearch = ({ ))}
+ {selectedEmployees.length > 1 && ( +
+ onGroupNameChange(e.target.value)} + className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-emerald-500 focus:border-emerald-500 text-sm" + /> +
+ )} - ) -} + ); +}; -export default FloatingChatButton +export default FloatingChatButton; \ No newline at end of file diff --git a/src/components/dashboard/PriorityTasks.jsx b/src/components/dashboard/PriorityTasks.jsx index 176f306..c70cbf8 100644 --- a/src/components/dashboard/PriorityTasks.jsx +++ b/src/components/dashboard/PriorityTasks.jsx @@ -150,7 +150,7 @@ function PriorityTasks({ selectedProjectId }) { {/* 작업 목록 - 우선순위별 배경색 적용 */}
{priorityGroup.priorityDataList.slice(0, currentVisibleItems).map((task) => ( -
handleIssueClick(task.id)} @@ -171,9 +171,10 @@ function PriorityTasks({ selectedProjectId }) {
))} + {/* 더 보기/더 적게 보기 버튼 */} {hasMoreItems && ( -
handleShowMore(priorityGroup.priority)} > @@ -181,8 +182,9 @@ function PriorityTasks({ selectedProjectId }) {
)} + {hasLessItems && ( -
handleShowLess(priorityGroup.priority)} > diff --git a/src/hooks/chat/useChat.js b/src/hooks/chat/useChat.js index a8efff9..7aa6206 100644 --- a/src/hooks/chat/useChat.js +++ b/src/hooks/chat/useChat.js @@ -23,8 +23,9 @@ export const useChat = () => { const selectChatRoom = (chatRoom) => { setSelectedChat(chatRoom) setCurrentView('chat') - // 채팅방 선택 시 읽음 처리 - markAsRead(chatRoom.id) + // 채팅방 선택 시 읽음 처리 (마지막 메시지 ID를 전달해야 함) + // TODO: ChatRoom.jsx에서 마지막 메시지 ID를 가져와 markAsRead 호출 시 전달하도록 수정 필요 + markAsRead(chatRoom.id, null) // 임시로 null 전달 } const backToList = () => { @@ -43,26 +44,9 @@ export const useChat = () => { const handleSendMessage = (e) => { e.preventDefault() if (message.trim() && selectedChat) { - // Zustand store를 통해 메시지 전송 + // Zustand store를 통해 메시지 전송 (WebSocket 사용) sendMessage(selectedChat.id, message.trim()) setMessage('') - - // 시뮬레이션: 2초 후 상대방 응답 - setTimeout(() => { - const responses = [ - '네, 알겠습니다!', - '확인했습니다.', - '좋은 아이디어네요.', - '언제 시간 되시나요?', - '회의실 예약해두겠습니다.' - ] - const randomResponse = responses[Math.floor(Math.random() * responses.length)] - - // 상대방 이름 (첫 번째 참가자를 상대방으로 가정) - const otherParticipant = selectedChat.participants.find(p => p !== '현재사용자') || selectedChat.name - - useChatStore.getState().receiveMessage(selectedChat.id, randomResponse, otherParticipant) - }, 2000) } } diff --git a/src/hooks/chat/useFloatingChat.js b/src/hooks/chat/useFloatingChat.js index e06393c..5e34656 100644 --- a/src/hooks/chat/useFloatingChat.js +++ b/src/hooks/chat/useFloatingChat.js @@ -1,64 +1,251 @@ -import { useChat } from './useChat' -import { useChatRooms } from './useChatRooms' -import { useEmployees } from './useEmployees' +import { useState, useEffect, useMemo, useCallback } from 'react'; +import { useAuth } from '../../hooks/useAuth'; +import useChatStore from '../../store/chatStore'; +import * as chatApi from '../../api/chatApi'; +import * as socketService from '../../api/socketService'; +import { useDebounce } from '../useDebounce'; export const useFloatingChat = () => { - const chatHook = useChat() - const chatRoomsHook = useChatRooms() - const employeesHook = useEmployees() - - // 채팅방 생성 로직 - const handleSelectEmployee = (employee) => { - const existingChat = chatRoomsHook.findExistingPrivateChat(employee.name) - - if (existingChat) { - chatHook.selectChatRoom(existingChat) - } else { - const newChat = { - id: Date.now(), - name: employee.name, - type: 'private', - lastMessage: '새로운 채팅이 시작되었습니다.', - lastTime: '방금', - unreadCount: 0, - participants: [employee.name] - } - chatRoomsHook.addChatRoom(newChat) - chatHook.selectChatRoom(newChat) + const { user } = useAuth(); + const { + chatRooms, setChatRooms, setMessages, addMessage, updateMessage, + setTotalUnreadCount, resetUnreadCount, setCurrentUser, removeChatRoom, totalUnreadCount + } = useChatStore(); + + // --- UI State --- + const [isOpen, setIsOpen] = useState(false); + const [currentView, setCurrentView] = useState('list'); + const [selectedChat, setSelectedChat] = useState(null); + const [contextMenu, setContextMenu] = useState({ visible: false, x: 0, y: 0, roomId: null }); + const [isSocketConnected, setIsSocketConnected] = useState(false); // 소켓 연결 상태 + + // --- Form State --- + const [message, setMessage] = useState(''); + const [searchTerm, setSearchTerm] = useState(''); + const [employeeSearchTerm, setEmployeeSearchTerm] = useState(''); + const [groupName, setGroupName] = useState(''); + + // --- Data State --- + const [allEmployees, setAllEmployees] = useState([]); + const [selectedEmployees, setSelectedEmployees] = useState([]); + + const debouncedSearchTerm = useDebounce(employeeSearchTerm, 500); + + // --- WebSocket Connection & Initial Data Fetching --- + useEffect(() => { + if (user) { + setCurrentUser(user); + // 로그인하면 바로 전체 안읽은 개수 가져오기 (REST API) + chatApi.fetchTotalUnreadCount().then(response => setTotalUnreadCount(response.data.data.totalUnreadCount)); + + // WebSocket 연결 시작 + socketService.connectWebSocket(() => { + setIsSocketConnected(true); // 연결 성공 시 상태만 변경 + }); + } + return () => { + socketService.disconnectWebSocket(); + setIsSocketConnected(false); + }; + }, [user, setCurrentUser, setTotalUnreadCount]); + + // --- WebSocket Subscriptions (연결 상태에 따라 구독) --- + useEffect(() => { + if (isSocketConnected) { + const subscription = socketService.subscribe('/topic/update', async (message) => { + try { + console.log('Received update message:', message); + const { totalUnreadCount, memberId } = message; + if (memberId === user?.id && totalUnreadCount !== undefined) { + setTotalUnreadCount(totalUnreadCount); + } + const response = await chatApi.fetchChatRooms(); + const chatRoomsData = Array.isArray(response.data.data) ? response.data.data : []; + setChatRooms(chatRoomsData); + } catch (error) { + console.error('Failed to process update message or fetch chat rooms:', error); + } + }); + + return () => { + if (subscription) { + socketService.unsubscribe('/topic/update'); + } + }; + } + }, [isSocketConnected, setChatRooms, setTotalUnreadCount, user]); + + // --- Chat Window Open Logic --- + useEffect(() => { + if (isOpen && user) { + chatApi.fetchChatRooms().then(response => { + const chatRoomsData = Array.isArray(response.data.data) ? response.data.data : []; + setChatRooms(chatRoomsData); + }); + } + }, [isOpen, user, setChatRooms]); + + // --- 이전 메시지 로딩 및 읽음 처리 --- + useEffect(() => { + if (selectedChat) { + chatApi.fetchMessages(selectedChat.id).then(response => { + const messages = response.data.data; + setMessages(selectedChat.id, messages); + if (messages && messages.length > 0 && selectedChat.unreadMessageCount > 0) { + const lastMessageId = messages[messages.length - 1].id; + chatApi.markAllAsRead(selectedChat.id, lastMessageId).then(() => { + chatApi.fetchTotalUnreadCount().then(res => setTotalUnreadCount(res.data.data.totalUnreadCount)); + }); + } + }); + } + }, [selectedChat, setMessages, setTotalUnreadCount]); + + // --- 실시간 메시지 구독 --- + useEffect(() => { + let subscription; + if (selectedChat && isSocketConnected) { + subscription = socketService.subscribeToChatRoom(selectedChat.id, (newMessage) => { + if (newMessage.type === 'DELETED') { + updateMessage(selectedChat.id, newMessage); + } else { + addMessage(selectedChat.id, newMessage); + } + }); } - } - - const handleCreateGroupChat = () => { - if (employeesHook.selectedEmployees.length === 1) { - handleSelectEmployee(employeesHook.selectedEmployees[0]) - } else if (employeesHook.selectedEmployees.length > 1) { - const groupName = employeesHook.selectedEmployees.map(emp => emp.name).join(', ') - const newChat = { - id: Date.now(), - name: groupName, - type: 'group', - lastMessage: '그룹 채팅이 시작되었습니다.', - lastTime: '방금', - unreadCount: 0, - participants: employeesHook.selectedEmployees.map(emp => emp.name) + return () => { + if (subscription && selectedChat) { + socketService.unsubscribe(`/topic/chatroom/${selectedChat.id}`); } - chatRoomsHook.addChatRoom(newChat) - chatHook.selectChatRoom(newChat) + }; + }, [selectedChat, isSocketConnected, addMessage, updateMessage]); + + // --- Employee Search Logic --- + useEffect(() => { + if (currentView === 'createChat') { + const promise = debouncedSearchTerm ? chatApi.searchMembers(debouncedSearchTerm) : chatApi.fetchAllMembers(); + promise.then(response => setAllEmployees(response.data.data)); } - employeesHook.clearSelectedEmployees() - } + }, [debouncedSearchTerm, currentView]); + + // --- Event Handlers (이하 동일) --- + const toggleChat = () => setIsOpen(!isOpen); + const toggleMinimize = () => { + setIsOpen(false); // Close the chat window completely + }; + const backToList = useCallback(() => { + setSelectedChat(null); + setCurrentView('list'); + }, [setSelectedChat, setCurrentView]); + + const selectChatRoom = (room) => { + setSelectedChat(room); + setCurrentView('chat'); + resetUnreadCount(room.id); + }; const handleGoToCreateChat = () => { - chatHook.goToCreateChat() - employeesHook.clearSelectedEmployees() - } + setSelectedEmployees([]); + setEmployeeSearchTerm(''); + setGroupName(''); + setCurrentView('createChat'); + }; + + const handleToggleEmployee = (employee) => { + setSelectedEmployees(prev => + prev.some(e => e.id === employee.id) + ? prev.filter(e => e.id !== employee.id) + : [...prev, employee] + ); + }; + + const handleCreateGroupChat = async () => { + if (selectedEmployees.length === 0) return; + const memberIdList = selectedEmployees.map(e => e.id); + if (user && !memberIdList.includes(user.id)) { + memberIdList.push(user.id); + } + let roomName, type; + if (memberIdList.length <= 2 && selectedEmployees.length === 1) { + type = 'One_On_One'; + roomName = selectedEmployees[0].name; + } else { + if (!groupName.trim()) { alert('그룹 채팅방 이름을 입력해주세요.'); return; } + type = 'Group'; + roomName = groupName; + } + try { + console.log('--- Debugging Chat Room Creation ---'); + console.log('Current User (creator):', user); + console.log('Selected Employees (from UI):', selectedEmployees); + console.log('memberIdList (sent to backend):', memberIdList); + console.log('Calculated roomName (for 1:1):', roomName); + console.log('Type:', type); + console.log('------------------------------------'); + await chatApi.createChatRoom(memberIdList, roomName, type); + const response = await chatApi.fetchChatRooms(); + const chatRoomsData = Array.isArray(response.data.data) ? response.data.data : []; + setChatRooms(chatRoomsData); + backToList(); + } catch (error) { + console.error('Failed to create chat room:', error); + } + }; + + const handleContextMenu = useCallback((event, room) => { + event.preventDefault(); + setContextMenu({ visible: true, x: event.clientX, y: event.clientY, roomId: room.id }); + }, []); + + const closeContextMenu = useCallback(() => { + setContextMenu(prev => ({ ...prev, visible: false })); + }, []); + + const handleLeaveChatRoom = useCallback(async () => { + if (!contextMenu.roomId) return; + try { + await chatApi.leaveChatRoom(contextMenu.roomId); + removeChatRoom(contextMenu.roomId); + if (selectedChat?.id === contextMenu.roomId) { + backToList(); + } + } catch (error) { + console.error("Failed to leave chat room:", error); + } finally { + closeContextMenu(); + } + }, [contextMenu.roomId, selectedChat, removeChatRoom, backToList, closeContextMenu]); + + const handleSendMessage = useCallback((e) => { + e.preventDefault(); + if (!message.trim() || !selectedChat || !user) return; + const messageDto = { + chatRoomId: selectedChat.id, + senderId: user.id, + content: message, + type: 'TEXT' + }; + socketService.sendMessage(messageDto); + setMessage(''); + }, [message, selectedChat, user, setMessage]); + + const filteredChatRooms = useMemo(() => { + const validChatRooms = Array.isArray(chatRooms) ? chatRooms : []; + return validChatRooms.filter(room => + room.name?.toLowerCase().includes(searchTerm.toLowerCase()) + ); + }, [chatRooms, searchTerm]); + + useEffect(() => { + console.log('Filtered Chat Rooms Updated:', filteredChatRooms); + }, [filteredChatRooms]); return { - ...chatHook, - ...chatRoomsHook, - ...employeesHook, - handleSelectEmployee, - handleCreateGroupChat, - handleGoToCreateChat - } -} + isOpen, message, setMessage, currentView, selectedChat, searchTerm, setSearchTerm, + employeeSearchTerm, setEmployeeSearchTerm, selectedEmployees, filteredEmployees: allEmployees, + groupName, setGroupName, filteredChatRooms, contextMenu, totalUnreadCount, + toggleChat, selectChatRoom, backToList, toggleMinimize, handleGoToCreateChat, + handleToggleEmployee, handleCreateGroupChat, handleContextMenu, closeContextMenu, handleLeaveChatRoom, + handleSendMessage + }; +}; \ No newline at end of file diff --git a/src/hooks/useDebounce.js b/src/hooks/useDebounce.js new file mode 100644 index 0000000..a645c77 --- /dev/null +++ b/src/hooks/useDebounce.js @@ -0,0 +1,19 @@ +import { useState, useEffect } from 'react'; + +export function useDebounce(value, delay) { + const [debouncedValue, setDebouncedValue] = useState(value); + + useEffect(() => { + // value가 변경된 후 delay 시간 뒤에 debouncedValue를 업데이트 + const handler = setTimeout(() => { + setDebouncedValue(value); + }, delay); + + // cleanup 함수: 이전 타이머를 제거하여 마지막 타이머만 실행되게 함 + return () => { + clearTimeout(handler); + }; + }, [value, delay]); + + return debouncedValue; +} diff --git a/src/store/chatStore.js b/src/store/chatStore.js index 9d20931..712e7c9 100644 --- a/src/store/chatStore.js +++ b/src/store/chatStore.js @@ -1,131 +1,218 @@ -import { create } from 'zustand' -import { chatRoomsData } from '../data/chatRooms' +import { create } from 'zustand'; +import SockJS from 'sockjs-client'; +import { Stomp } from '@stomp/stompjs'; -const useChatStore = create((set, get) => ({ - // 채팅방 목록 - chatRooms: chatRoomsData, - - // 각 채팅방의 메시지들 (chatRoomId를 key로 하는 객체) - messages: { - // 예시: { 1: [message1, message2], 2: [message3, message4] } - }, +let stompClient = null; +let subscriptions = {}; - // 현재 사용자 설정 (AuthContext에서 사용자 정보를 받아서 설정) +const useChatStore = create((set, get) => ({ + chatRooms: [], + messages: {}, + totalUnreadCount: 0, currentUser: null, - - // 현재 사용자 설정 - setCurrentUser: (user) => set({ currentUser: user }), - - // 채팅방 추가 - addChatRoom: (newChatRoom) => set((state) => ({ - chatRooms: [newChatRoom, ...state.chatRooms], - messages: { ...state.messages, [newChatRoom.id]: [] } - })), - - // 메시지 전송 - sendMessage: (chatRoomId, messageText) => { - const { currentUser } = get() - if (!currentUser) return null; - - const newMessage = { - id: Date.now(), - text: messageText, - sender: currentUser.name, - senderId: currentUser.id || currentUser.account_id, - timestamp: new Date().toISOString(), - type: 'text' - } + isConnected: false, + setCurrentUser: (user) => { + set({ currentUser: user }); + + // Removed duplicate subscription logic to avoid redundancy + }, + setChatRooms: (rooms) => set((state) => { - const updatedMessages = { - ...state.messages, - [chatRoomId]: [...(state.messages[chatRoomId] || []), newMessage] + const prevChatRooms = Array.isArray(state.chatRooms) ? state.chatRooms : []; + if (!Array.isArray(rooms)) { + console.error('setChatRooms: rooms is not an array', rooms); + return state; // 상태를 변경하지 않음 } - // 채팅방의 lastMessage도 업데이트 - const updatedChatRooms = state.chatRooms.map(room => - room.id === chatRoomId - ? { - ...room, - lastMessage: messageText, - lastTime: new Date().toLocaleTimeString('ko-KR', { - hour: '2-digit', - minute: '2-digit' - }) - } - : room - ) - + const updatedRooms = rooms.map((newRoom) => { + const existingRoom = prevChatRooms.find((room) => room.id === newRoom.id); + return existingRoom ? { ...existingRoom, ...newRoom } : newRoom; + }); + + // Add any rooms that are in the current state but not in the new rooms + const mergedRooms = prevChatRooms.filter( + (room) => !rooms.some((newRoom) => newRoom.id === room.id) + ); + + return { chatRooms: [...updatedRooms, ...mergedRooms] }; + }), + setMessages: (roomId, messages) => + set((state) => ({ + messages: { ...state.messages, [roomId]: messages }, + })), + removeChatRoom: (roomId) => + set((state) => ({ + chatRooms: state.chatRooms.filter((room) => room.id !== roomId), + })), + setTotalUnreadCount: (count) => set({ totalUnreadCount: count }), + resetUnreadCount: (roomId) => + set((state) => { + const roomToReset = state.chatRooms.find((room) => room.id === roomId); + if (!roomToReset || !roomToReset.unreadMessageCount) return {}; + const countToSubtract = roomToReset.unreadMessageCount; return { - messages: updatedMessages, - chatRooms: updatedChatRooms + chatRooms: state.chatRooms.map((room) => + room.id === roomId ? { ...room, unreadMessageCount: 0 } : room + ), + totalUnreadCount: Math.max(0, state.totalUnreadCount - countToSubtract), + }; + }), + getMessages: (chatRoomId) => get().messages[chatRoomId] || [], + + connectWebSocket: (userId) => { + if (!userId) { + console.error('Cannot connect WebSocket: userId is missing.'); + return; + } + if (stompClient && stompClient.connected) { + set({ isConnected: true }); + get().unsubscribeAll(); + return; + } + const socket = new SockJS('http://localhost:8081/ws/chat'); + stompClient = Stomp.over(socket); + stompClient.connect({}, () => { + set({ isConnected: true }); + + // Subscribe to updates globally + if (!subscriptions.update) { + subscriptions.update = stompClient.subscribe( + `/topic/update`, + (message) => { + const updateData = JSON.parse(message.body); + const currentUser = get().currentUser; + + if (currentUser && currentUser.id === updateData.memberId) { + set((state) => ({ + chatRooms: state.chatRooms.map((room) => + room.id === updateData.chatRoomId + ? { + ...room, + recentMessage: updateData.recentMessage, + unreadMessageCount: updateData.totalUnreadCount, + } + : room + ), + })); + } + } + ); } - }) - - return newMessage + }, () => { + set({ isConnected: false }); + }); }, - // 특정 채팅방의 메시지 가져오기 - getMessages: (chatRoomId) => { - const { messages } = get() - return messages[chatRoomId] || [] + disconnectWebSocket: () => { + if (stompClient) { + get().unsubscribeAll(); + stompClient.disconnect(() => { + set({ isConnected: false }); + }); + } }, - // 채팅방 찾기 - findChatRoom: (participantName) => { - const { chatRooms } = get() - return chatRooms.find(room => - room.type === 'private' && - room.participants.includes(participantName) - ) + subscribeToChatRoom: (chatRoomId) => { + get().unsubscribeAll(); + if (chatRoomId) { + subscriptions.message = stompClient.subscribe( + `/topic/chatroom/${chatRoomId}`, + (message) => { + const chatMessage = JSON.parse(message.body); + get().addMessage(chatRoomId, chatMessage); + } + ); + } }, - // 받은 메시지 추가 (다른 사용자로부터) - receiveMessage: (chatRoomId, messageText, senderName) => { - const newMessage = { - id: Date.now(), - text: messageText, - sender: senderName, - senderId: senderName, - timestamp: new Date().toISOString(), - type: 'text' + subscribeToUpdates: () => { + if (!subscriptions.update) { + subscriptions.update = stompClient.subscribe( + `/topic/update`, + (message) => { + const updateData = JSON.parse(message.body); + const currentUser = get().currentUser; + + if (currentUser && currentUser.id === updateData.memberId) { + set((state) => ({ + chatRooms: state.chatRooms.map((room) => + room.id === updateData.chatRoomId + ? { + ...room, + recentMessage: updateData.recentMessage, + unreadMessageCount: updateData.totalUnreadCount, + } + : room + ), + })); + } + } + ); } + }, - set((state) => { - const updatedMessages = { - ...state.messages, - [chatRoomId]: [...(state.messages[chatRoomId] || []), newMessage] + unsubscribeAll: () => { + for (const key in subscriptions) { + if (subscriptions[key]) { + subscriptions[key].unsubscribe(); + subscriptions[key] = null; } + } + }, - const updatedChatRooms = state.chatRooms.map(room => - room.id === chatRoomId - ? { - ...room, - lastMessage: messageText, - lastTime: new Date().toLocaleTimeString('ko-KR', { - hour: '2-digit', - minute: '2-digit' - }), - unreadCount: room.unreadCount + 1 - } - : room - ) + sendMessage: (roomId, content) => { + if (stompClient && stompClient.connected) { + const currentUser = get().currentUser; + if (!currentUser || !currentUser.id) return; + const chatMessage = { + chatRoomId: roomId, + senderId: currentUser.id, + senderName: currentUser.name, + content: content, + type: 'TEXT', + }; + stompClient.send('/app/send', {}, JSON.stringify(chatMessage)); + } + }, - return { - messages: updatedMessages, - chatRooms: updatedChatRooms - } - }) + markAsRead: (roomId, messageId) => { + if (stompClient && stompClient.connected) { + const currentUser = get().currentUser; + if (!currentUser || !currentUser.id) return; + const messageReadRequest = { + memberId: currentUser.id, + chatRoomId: roomId, + messageId: messageId, + }; + stompClient.send('/app/message/read', {}, JSON.stringify(messageReadRequest)); + get().resetUnreadCount(roomId); + } }, - // 읽음 처리 - markAsRead: (chatRoomId) => set((state) => ({ - chatRooms: state.chatRooms.map(room => - room.id === chatRoomId - ? { ...room, unreadCount: 0 } - : room - ) - })) -})) - -export default useChatStore + addMessage: (roomId, message) => + set((state) => ({ + messages: { + ...state.messages, + [roomId]: [...(state.messages[roomId] || []), message], + }, + })), + updateMessage: (roomId, updatedMessage) => + set((state) => ({ + messages: { + ...state.messages, + [roomId]: state.messages[roomId].map((msg) => { + if (msg.id !== updatedMessage.id) return msg; + if (updatedMessage.readCountUpdater) { + return { + ...msg, + readCount: updatedMessage.readCountUpdater(msg.readCount), + }; + } + return { ...msg, ...updatedMessage }; + }), + }, + })), +})); + +export default useChatStore; \ No newline at end of file diff --git a/vite.config.js b/vite.config.js index e00942d..3d89bb1 100644 --- a/vite.config.js +++ b/vite.config.js @@ -7,6 +7,9 @@ export default defineConfig({ plugins: [react(), tailwindcss(), ], + define: { + global: 'globalThis', + }, server: { proxy: { '/api': {