From 762f9cfd4abf653c0aed24241eb06df40891f233 Mon Sep 17 00:00:00 2001 From: Ouijdane IMER Date: Tue, 2 Dec 2025 17:57:33 +0100 Subject: [PATCH] GTARH_Modif_anno27 --- Backend/.env | 25 + Backend/DockerFileGTARH.backend | 8 + Backend/package-lock.json | 1841 +++++ Backend/package.json | 24 + Backend/server.js | 7103 ++++++++++++++++++ Backend/webhook-config.js | 18 + Backend/webhook-utils.js | 111 + components.json | 20 + docker-compose.yml | 45 + eslint.config.js | 26 + index.html | 22 + package-lock.json | 9772 +++++++++++++++++++++++++ package.json | 98 + postcss.config.js | 6 + src/App.css | 42 + src/App.tsx | 163 + src/DockerFileGTARH.Frontend | 15 + src/assets/GTARH.png | Bin 0 -> 16626 bytes src/components/ui/accordion.tsx | 52 + src/components/ui/alert-dialog.tsx | 104 + src/components/ui/alert.tsx | 43 + src/components/ui/aspect-ratio.tsx | 5 + src/components/ui/avatar.tsx | 38 + src/components/ui/badge.tsx | 29 + src/components/ui/breadcrumb.tsx | 90 + src/components/ui/button.tsx | 47 + src/components/ui/calendar.tsx | 54 + src/components/ui/card.tsx | 43 + src/components/ui/carousel.tsx | 224 + src/components/ui/chart.tsx | 303 + src/components/ui/checkbox.tsx | 26 + src/components/ui/collapsible.tsx | 9 + src/components/ui/command.tsx | 132 + src/components/ui/context-menu.tsx | 178 + src/components/ui/dialog.tsx | 95 + src/components/ui/drawer.tsx | 87 + src/components/ui/dropdown-menu.tsx | 179 + src/components/ui/form.tsx | 129 + src/components/ui/hover-card.tsx | 27 + src/components/ui/input-otp.tsx | 61 + src/components/ui/input.tsx | 22 + src/components/ui/label.tsx | 17 + src/components/ui/menubar.tsx | 207 + src/components/ui/navigation-menu.tsx | 120 + src/components/ui/pagination.tsx | 81 + src/components/ui/popover.tsx | 29 + src/components/ui/progress.tsx | 23 + src/components/ui/radio-group.tsx | 36 + src/components/ui/resizable.tsx | 37 + src/components/ui/scroll-area.tsx | 38 + src/components/ui/select.tsx | 143 + src/components/ui/separator.tsx | 20 + src/components/ui/sheet.tsx | 107 + src/components/ui/sidebar.tsx | 637 ++ src/components/ui/skeleton.tsx | 7 + src/components/ui/slider.tsx | 23 + src/components/ui/sonner.tsx | 27 + src/components/ui/switch.tsx | 27 + src/components/ui/table.tsx | 72 + src/components/ui/tabs.tsx | 53 + src/components/ui/textarea.tsx | 21 + src/components/ui/toast.tsx | 111 + src/components/ui/toaster.tsx | 24 + src/components/ui/toggle-group.tsx | 49 + src/components/ui/toggle.tsx | 37 + src/components/ui/tooltip.tsx | 28 + src/components/ui/use-toast.ts | 3 + src/hooks/use-mobile.tsx | 19 + src/hooks/use-toast.ts | 186 + src/hooks/useSSE.tsx | 128 + src/index.css | 111 + src/lib/utils.ts | 6 + src/main.tsx | 5 + src/pages/AuthMicrosoft.tsx | 175 + src/pages/CompteRenduRH.tsx | 748 ++ src/pages/Dashboard.tsx | 784 ++ src/pages/ExportPaie.tsx | 823 +++ src/pages/GestionCompteurs.tsx | 1111 +++ src/pages/GestionRecuperation.tsx | 865 +++ src/pages/Historique.tsx | 366 + src/pages/LoginDev.tsx | 162 + src/pages/ModificationDemande.tsx | 679 ++ src/pages/NotFound.tsx | 24 + src/pages/SaisieManuelle.tsx | 1115 +++ src/pages/TeamDetail.tsx | 298 + src/pages/Teams.tsx | 419 ++ src/pages/ValidationDemandes.tsx | 1375 ++++ src/vite-env.d.ts | 1 + tailwind | 11 + tailwind.config.js | 53 + tailwind.config.ts | 102 + tsconfig.app.json | 30 + tsconfig.json | 16 + tsconfig.node.json | 22 + vite.config.js | 40 + vite.config.ts | 18 + 96 files changed, 32985 insertions(+) create mode 100644 Backend/.env create mode 100644 Backend/DockerFileGTARH.backend create mode 100644 Backend/package-lock.json create mode 100644 Backend/package.json create mode 100644 Backend/server.js create mode 100644 Backend/webhook-config.js create mode 100644 Backend/webhook-utils.js create mode 100644 components.json create mode 100644 docker-compose.yml create mode 100644 eslint.config.js create mode 100644 index.html create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 postcss.config.js create mode 100644 src/App.css create mode 100644 src/App.tsx create mode 100644 src/DockerFileGTARH.Frontend create mode 100644 src/assets/GTARH.png create mode 100644 src/components/ui/accordion.tsx create mode 100644 src/components/ui/alert-dialog.tsx create mode 100644 src/components/ui/alert.tsx create mode 100644 src/components/ui/aspect-ratio.tsx create mode 100644 src/components/ui/avatar.tsx create mode 100644 src/components/ui/badge.tsx create mode 100644 src/components/ui/breadcrumb.tsx create mode 100644 src/components/ui/button.tsx create mode 100644 src/components/ui/calendar.tsx create mode 100644 src/components/ui/card.tsx create mode 100644 src/components/ui/carousel.tsx create mode 100644 src/components/ui/chart.tsx create mode 100644 src/components/ui/checkbox.tsx create mode 100644 src/components/ui/collapsible.tsx create mode 100644 src/components/ui/command.tsx create mode 100644 src/components/ui/context-menu.tsx create mode 100644 src/components/ui/dialog.tsx create mode 100644 src/components/ui/drawer.tsx create mode 100644 src/components/ui/dropdown-menu.tsx create mode 100644 src/components/ui/form.tsx create mode 100644 src/components/ui/hover-card.tsx create mode 100644 src/components/ui/input-otp.tsx create mode 100644 src/components/ui/input.tsx create mode 100644 src/components/ui/label.tsx create mode 100644 src/components/ui/menubar.tsx create mode 100644 src/components/ui/navigation-menu.tsx create mode 100644 src/components/ui/pagination.tsx create mode 100644 src/components/ui/popover.tsx create mode 100644 src/components/ui/progress.tsx create mode 100644 src/components/ui/radio-group.tsx create mode 100644 src/components/ui/resizable.tsx create mode 100644 src/components/ui/scroll-area.tsx create mode 100644 src/components/ui/select.tsx create mode 100644 src/components/ui/separator.tsx create mode 100644 src/components/ui/sheet.tsx create mode 100644 src/components/ui/sidebar.tsx create mode 100644 src/components/ui/skeleton.tsx create mode 100644 src/components/ui/slider.tsx create mode 100644 src/components/ui/sonner.tsx create mode 100644 src/components/ui/switch.tsx create mode 100644 src/components/ui/table.tsx create mode 100644 src/components/ui/tabs.tsx create mode 100644 src/components/ui/textarea.tsx create mode 100644 src/components/ui/toast.tsx create mode 100644 src/components/ui/toaster.tsx create mode 100644 src/components/ui/toggle-group.tsx create mode 100644 src/components/ui/toggle.tsx create mode 100644 src/components/ui/tooltip.tsx create mode 100644 src/components/ui/use-toast.ts create mode 100644 src/hooks/use-mobile.tsx create mode 100644 src/hooks/use-toast.ts create mode 100644 src/hooks/useSSE.tsx create mode 100644 src/index.css create mode 100644 src/lib/utils.ts create mode 100644 src/main.tsx create mode 100644 src/pages/AuthMicrosoft.tsx create mode 100644 src/pages/CompteRenduRH.tsx create mode 100644 src/pages/Dashboard.tsx create mode 100644 src/pages/ExportPaie.tsx create mode 100644 src/pages/GestionCompteurs.tsx create mode 100644 src/pages/GestionRecuperation.tsx create mode 100644 src/pages/Historique.tsx create mode 100644 src/pages/LoginDev.tsx create mode 100644 src/pages/ModificationDemande.tsx create mode 100644 src/pages/NotFound.tsx create mode 100644 src/pages/SaisieManuelle.tsx create mode 100644 src/pages/TeamDetail.tsx create mode 100644 src/pages/Teams.tsx create mode 100644 src/pages/ValidationDemandes.tsx create mode 100644 src/vite-env.d.ts create mode 100644 tailwind create mode 100644 tailwind.config.js create mode 100644 tailwind.config.ts create mode 100644 tsconfig.app.json create mode 100644 tsconfig.json create mode 100644 tsconfig.node.json create mode 100644 vite.config.js create mode 100644 vite.config.ts diff --git a/Backend/.env b/Backend/.env new file mode 100644 index 0000000..2966896 --- /dev/null +++ b/Backend/.env @@ -0,0 +1,25 @@ + + +COLLABORATEURS_URL=http://localhost:3000 +RH_URL=http://localhost:3001 +WEBHOOK_SECRET=secret-rh-2025 + + + +# Configuration Serveur +PORT=3000 +NODE_ENV=development + +# Configuration Azure AD (Microsoft Graph) +AZURE_TENANT_ID=9840a2a0-6ae1-4688-b03d-d2ec291be0f9 +AZURE_CLIENT_ID=4bb4cc24-bac3-427c-b02c-5d14fc67b561 +AZURE_CLIENT_SECRET=o2q8Q~nGLCkry6XOriVFBMvKUk.5cXtFutAQVdx9 +AZURE_GROUP_ID=c1ea877c-6bca-4f47-bfad-f223640813a0 +JWT_SECRET=o2q8Q~nGLCkry6XOriVFBMvKUk.5cXtFutAQVdx9 + +# Email configuration +EMAIL_FROM=gtanoreply@ensup.eu + +# Upload configuration +UPLOAD_DIR=./uploads +MAX_FILE_SIZE=5242880 \ No newline at end of file diff --git a/Backend/DockerFileGTARH.backend b/Backend/DockerFileGTARH.backend new file mode 100644 index 0000000..3622529 --- /dev/null +++ b/Backend/DockerFileGTARH.backend @@ -0,0 +1,8 @@ +FROM node:18-alpine +RUN apk add --no-cache curl mysql-client python3 make g++ +WORKDIR /app +COPY package*.json ./ +RUN npm install --production +COPY . . +EXPOSE 3000 +CMD ["node", "server.js"] \ No newline at end of file diff --git a/Backend/package-lock.json b/Backend/package-lock.json new file mode 100644 index 0000000..3674a8d --- /dev/null +++ b/Backend/package-lock.json @@ -0,0 +1,1841 @@ +{ + "name": "gtarh-backend", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "gtarh-backend", + "version": "1.0.0", + "dependencies": { + "@azure/msal-node": "^3.8.0", + "axios": "^1.12.2", + "cors": "^2.8.5", + "crypto": "^1.0.1", + "dotenv": "^17.2.3", + "express": "^5.1.0", + "jsonwebtoken": "^9.0.2", + "mysql2": "^3.15.1", + "node-fetch": "^2.7.0", + "pdfkit": "^0.17.2" + }, + "devDependencies": { + "nodemon": "^3.1.10" + } + }, + "node_modules/@azure/msal-common": { + "version": "15.13.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.13.0.tgz", + "integrity": "sha512-8oF6nj02qX7eE/6+wFT5NluXRHc05AgdCC3fJnkjiJooq8u7BcLmxaYYSwc2AfEkWRMRi6Eyvvbeqk4U4412Ag==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-node": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-3.8.0.tgz", + "integrity": "sha512-23BXm82Mp5XnRhrcd4mrHa0xuUNRp96ivu3nRatrfdAqjoeWAGyD0eEAafxAOHAEWWmdlyFK4ELFcdziXyw2sA==", + "license": "MIT", + "dependencies": { + "@azure/msal-common": "15.13.0", + "jsonwebtoken": "^9.0.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", + "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/aws-ssl-profiles": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/axios": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", + "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", + "license": "MIT", + "dependencies": { + "follow- + s": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "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/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brotli": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.1.2" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", + "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in.", + "license": "ISC" + }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "license": "MIT" + }, + "node_modules/debug": { + "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" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dfa": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", + "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==", + "license": "MIT" + }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/fontkit": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.4.tgz", + "integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==", + "license": "MIT", + "dependencies": { + "@swc/helpers": "^0.5.12", + "brotli": "^1.3.2", + "clone": "^2.1.2", + "dfa": "^1.2.0", + "fast-deep-equal": "^3.1.3", + "restructure": "^3.0.0", + "tiny-inflate": "^1.0.3", + "unicode-properties": "^1.4.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/form-data/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "license": "MIT", + "dependencies": { + "is-property": "^1.0.2" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, + "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/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", + "license": "MIT" + }, + "node_modules/jpeg-exif": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/jpeg-exif/-/jpeg-exif-1.1.4.tgz", + "integrity": "sha512-a+bKEcCjtuW5WTdgeXFzswSrdqi0jk4XlEtZlx5A94wCoBpFjfFTbo/Tra5SpNCl/YFZPvcV1dJc+TAYeg6ROQ==", + "license": "MIT" + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/linebreak": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz", + "integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==", + "license": "MIT", + "dependencies": { + "base64-js": "0.0.8", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/linebreak/node_modules/base64-js": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", + "integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/lru.min": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.2.tgz", + "integrity": "sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg==", + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=1.30.0", + "node": ">=8.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wellwelwel" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mysql2": { + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.15.1.tgz", + "integrity": "sha512-WZMIRZstT2MFfouEaDz/AGFnGi1A2GwaDe7XvKTdRJEYiAHbOrh4S3d8KFmQeh11U85G+BFjIvS1Di5alusZsw==", + "license": "MIT", + "dependencies": { + "aws-ssl-profiles": "^1.1.1", + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.7.0", + "long": "^5.2.1", + "lru.min": "^1.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/mysql2/node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", + "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", + "license": "MIT", + "dependencies": { + "lru-cache": "^7.14.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/nodemon": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz", + "integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", + "license": "MIT" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/pdfkit": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/pdfkit/-/pdfkit-0.17.2.tgz", + "integrity": "sha512-UnwF5fXy08f0dnp4jchFYAROKMNTaPqb/xgR8GtCzIcqoTnbOqtp3bwKvO4688oHI6vzEEs8Q6vqqEnC5IUELw==", + "license": "MIT", + "dependencies": { + "crypto-js": "^4.2.0", + "fontkit": "^2.0.4", + "jpeg-exif": "^1.1.4", + "linebreak": "^1.1.0", + "png-js": "^1.0.0" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/png-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/png-js/-/png-js-1.0.0.tgz", + "integrity": "sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g==" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", + "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.7.0", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/restructure": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz", + "integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==", + "license": "MIT" + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "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/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/unicode-properties": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", + "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/unicode-trie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", + "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", + "license": "MIT", + "dependencies": { + "pako": "^0.2.5", + "tiny-inflate": "^1.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + } + } +} diff --git a/Backend/package.json b/Backend/package.json new file mode 100644 index 0000000..ec372a7 --- /dev/null +++ b/Backend/package.json @@ -0,0 +1,24 @@ +{ + "name": "gtarh-backend", + "version": "1.0.0", + "main": "server.js", + "scripts": { + "start": "node server.js", + "dev": "nodemon server.js" + }, + "dependencies": { + "@azure/msal-node": "^3.8.0", + "axios": "^1.12.2", + "cors": "^2.8.5", + "crypto": "^1.0.1", + "dotenv": "^17.2.3", + "express": "^5.1.0", + "jsonwebtoken": "^9.0.2", + "mssql": "^11.0.1", + "node-fetch": "^2.7.0", + "pdfkit": "^0.17.2" + }, + "devDependencies": { + "nodemon": "^3.1.10" + } +} \ No newline at end of file diff --git a/Backend/server.js b/Backend/server.js new file mode 100644 index 0000000..fb154db --- /dev/null +++ b/Backend/server.js @@ -0,0 +1,7103 @@ + + +const express = require('express'); +const cors = require('cors'); +const sql = require('mssql'); // ⭐ CHANGEMENT: mssql au lieu de mysql2 +const jwt = require('jsonwebtoken'); +const PDFDocument = require('pdfkit'); + +const { ConfidentialClientApplication } = require('@azure/msal-node'); + +console.log('✅ 2. Modules de base chargés'); + +require('dotenv').config(); +console.log('✅ 3. Dotenv chargé'); + +// HANDLERS D'ERREURS +process.on('uncaughtException', (error) => { + console.error('\n❌❌❌ ERREUR NON CAPTURÉE ❌❌❌'); + console.error(error); + console.error(error.stack); +}); + +process.on('unhandledRejection', (reason, promise) => { + console.error('\n❌❌❌ PROMESSE REJETÉE ❌❌❌'); + console.error('Raison:', reason); + console.error('Promise:', promise); +}); + +process.on('exit', (code) => { + console.log(`\n⚠️⚠️⚠️ PROCESSUS EN COURS DE TERMINAISON - CODE: ${code} ⚠️⚠️⚠️\n`); +}); + +console.log('✅ 4. Handlers d\'erreurs installés'); + +// ⭐ IMPORTS WEBHOOKS +try { + console.log('🔄 5. Chargement WebhookManager...'); + const WebhookManager = require('./webhook-utils.js'); + console.log(' Type de WebhookManager:', typeof WebhookManager); + + console.log('🔄 6. Chargement webhook-config...'); + const { WEBHOOKS, EVENTS } = require('./webhook-config'); + console.log('✅ 7. Webhooks chargés avec succès'); + + const app = express(); + console.log('✅ 8. Express initialisé'); + + const PORT = process.env.PORT || 3000; + console.log(`✅ 9. Port configuré: ${PORT}`); + + // ⭐ INITIALISER LE WEBHOOK MANAGER + console.log('🔄 10. Initialisation WebhookManager...'); + const webhookManager = new WebhookManager(WEBHOOKS.SECRET_KEY); + console.log('✅ 11. WebhookManager créé'); + + // Middleware + app.use(cors()); + app.use(express.json()); + console.log('✅ 12. Middlewares installés'); + + // ⭐ CONFIGURATION SQL SERVER + const dbConfig = { + server: '192.168.0.3', + user: 'gta_app', + password: 'GTA2025!Secure', + database: 'GTA', + port: 1433, + options: { + encrypt: true, + trustServerCertificate: true, + enableArithAbort: true, + connectTimeout: 60000, + requestTimeout: 60000 + }, + pool: { + max: 10, + min: 0, + idleTimeoutMillis: 30000 + } + }; + + console.log('🔄 13. Test connexion SQL Server...'); + let pool; + // ⭐ FONCTION D'INITIALISATION DE LA BASE + async function initializeDatabase() { + try { + pool = await sql.connect(dbConfig); + console.log('✅ 14. Connexion SQL Server réussie'); + console.log(' Server:', dbConfig.server); + console.log(' User:', dbConfig.user); + console.log(' Database:', dbConfig.database); + } catch (err) { + console.error('❌ 14. ERREUR CONNEXION SQL SERVER:', err.message); + console.error(' Server:', dbConfig.server); + console.error(' User:', dbConfig.user); + console.error(' Database:', dbConfig.database); + throw err; + } + } + + // Initialiser la connexion + initializeDatabase().catch(err => { + console.error('❌ Impossible de démarrer le serveur:', err); + process.exit(1); + }); + + console.log('✅ 15. Pool SQL Server configuré'); + + // ================================================ + // SNAPSHOT QUOTIDIEN — Appelle la Stored Procedure + // ================================================ + async function executerSnapshotQuotidien() { + try { + console.log('[SNAPSHOT] Démarrage du snapshot quotidien...'); + + const result = await pool.request() + .query('EXEC [dbo].[SP_SnapshotQuotidienCompteurs]'); + + const resume = result.recordset?.[0]; + if (resume) { + console.log(`[SNAPSHOT] ✅ Terminé — Date: ${resume.DateSnapshot}, Insérés: ${resume.NbInseres}`); + } else { + console.log('[SNAPSHOT] ✅ Terminé'); + } + } catch (error) { + console.error('[SNAPSHOT] ❌ Erreur:', error.message); + } + } + + // Configuration Microsoft O365 + const msalConfig = { + auth: { + clientId: process.env.AZURE_CLIENT_ID, + authority: `https://login.microsoftonline.com/${process.env.AZURE_TENANT_ID}`, + clientSecret: process.env.AZURE_CLIENT_SECRET + } + }; + + const cca = new ConfidentialClientApplication(msalConfig); + + async function getGraphToken() { + try { + console.log('🔑 Demande token Graph API...'); + const params = new URLSearchParams({ + grant_type: 'client_credentials', + client_id: process.env.AZURE_CLIENT_ID, + client_secret: process.env.AZURE_CLIENT_SECRET, + scope: 'https://graph.microsoft.com/.default' + }); + + const response = await fetch( + `https://login.microsoftonline.com/${process.env.AZURE_TENANT_ID}/oauth2/v2.0/token`, + { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: params.toString() + } + ); + + console.log('📊 Réponse OAuth - Status:', response.status); + + if (!response.ok) { + const errorText = await response.text(); + console.error('❌ Erreur OAuth:', errorText); + throw new Error(`OAuth Error: ${response.status}`); + } + + const data = await response.json(); + + if (data.access_token) { + console.log('✅ Token obtenu'); + return data.access_token; + } else { + console.error('❌ Pas de token dans la réponse:', data); + return null; + } + } catch (error) { + console.error('❌ Erreur getGraphToken:', error.message); + return null; + } + } + function agregerLignesParEmploye(demandes) { + const employesMap = new Map(); + + demandes.forEach(row => { + const key = row.collaborateurId; + + if (!employesMap.has(key)) { + employesMap.set(key, { + employe: row.employe, + email: row.email, + service: row.service || 'Non assigné', + rtt: { nb: 0, dates: [], periodes: [] }, + cp: { nb: 0, dates: [], periodes: [] }, + aap: { nb: 0, dates: [], periodes: [] }, + am: { nb: 0, dates: [], periodes: [] }, + jposf: { nb: 0, dates: [], periodes: [] }, + recup: { nb: 0, dates: [], periodes: [] }, + autres: [] + }); + } + + const employe = employesMap.get(key); + const dateDebutType = row.dateDebutType ? new Date(row.dateDebutType) : new Date(row.DateDebut); + const dateFinType = row.dateFinType ? new Date(row.dateFinType) : new Date(row.DateFin); + + // Pour les demi-journées sans date propre au type, + // la date de fin = date de début (une seule journée) + const periodeJourneeRaw = row.PeriodeJournee || ''; + const isDemiJournee = periodeJourneeRaw === 'Matin' || periodeJourneeRaw === 'Après-midi'; + const dateFinEffective = (isDemiJournee && !row.dateFinType) ? dateDebutType : dateFinType; + + const formatDate = (date) => + String(date.getDate()).padStart(2, '0') + '/' + + String(date.getMonth() + 1).padStart(2, '0'); + + const dateDebutStr = formatDate(dateDebutType); + const dateFinStr = formatDate(dateFinEffective); + const periode = dateDebutStr === dateFinStr + ? dateDebutStr + : `${dateDebutStr}-${dateFinStr}`; + + const nombreJours = parseFloat(row.NombreJours); + const typeConge = row.typeConge.toLowerCase(); + + let periodeJournee = row.PeriodeJournee || 'Journée'; + if (periodeJournee === 'Journée entière') periodeJournee = 'Journée'; + + if (typeConge === 'jposf') { + employe.jposf.nb += nombreJours; + employe.jposf.dates.push(periode); + employe.jposf.periodes.push(periodeJournee); + } else if (typeConge.includes('récup') || typeConge.includes('recup')) { + employe.aap.nb += nombreJours; + employe.aap.dates.push(periode); + employe.aap.periodes.push(periodeJournee); + } else if (typeConge.includes('rtt')) { + employe.rtt.nb += nombreJours; + employe.rtt.dates.push(periode); + employe.rtt.periodes.push(periodeJournee); + } else if (typeConge.includes('cp') || typeConge.includes('congé payé') || typeConge.includes('conge paye')) { + employe.cp.nb += nombreJours; + employe.cp.dates.push(periode); + employe.cp.periodes.push(periodeJournee); + } else if (typeConge.includes('aap') || typeConge.includes('absence autorisée') || typeConge.includes('enfant malade')) { + employe.aap.nb += nombreJours; + employe.aap.dates.push(periode); + employe.aap.periodes.push(periodeJournee); + } else if (typeConge.includes('maladie') || typeConge.includes('arrêt') || typeConge.includes('arret')) { + employe.am.nb += nombreJours; + employe.am.dates.push(periode); + employe.am.periodes.push(periodeJournee); + } else { + const existingAutre = employe.autres.find(a => a.type === row.typeConge); + if (existingAutre) { + existingAutre.nb += nombreJours; + existingAutre.dates.push(periode); + existingAutre.periodes.push(periodeJournee); + } else { + employe.autres.push({ + type: row.typeConge, + nb: nombreJours, + dates: [periode], + periodes: [periodeJournee] + }); + } + } + }); + + return Array.from(employesMap.values()).map(emp => ({ + employe: emp.employe, + email: emp.email, + service: emp.service, + rtt: { nb: emp.rtt.nb, dates: emp.rtt.dates.join(' ; '), periodes: emp.rtt.periodes.join(' ; ') }, + cp: { nb: emp.cp.nb, dates: emp.cp.dates.join(' ; '), periodes: emp.cp.periodes.join(' ; ') }, + aap: { nb: emp.aap.nb, dates: emp.aap.dates.join(' ; '), periodes: emp.aap.periodes.join(' ; ') }, + am: { nb: emp.am.nb, dates: emp.am.dates.join(' ; '), periodes: emp.am.periodes.join(' ; ') }, + jposf: { nb: emp.jposf.nb, dates: emp.jposf.dates.join(' ; '), periodes: emp.jposf.periodes.join(' ; ') }, + recup: { nb: emp.recup.nb, dates: emp.recup.dates.join(' ; '), periodes: emp.recup.periodes.join(' ; ') }, + autres: emp.autres.map(a => ({ + type: a.type, + nb: a.nb, + dates: a.dates.join(' ; '), + periodes: a.periodes.join(' ; ') + })) + })); + } + + function grouperParMoisConge(lignes) { + // 1. Séparer les lignes par mois de congé + const parMois = {}; + + lignes.forEach(row => { + const dateConge = new Date(row.dateDebutType || row.DateDebut); + const moisKey = `${dateConge.getFullYear()}-${String(dateConge.getMonth() + 1).padStart(2, '0')}`; + + if (!parMois[moisKey]) { + parMois[moisKey] = []; + } + parMois[moisKey].push(row); + }); + + // 2. Dans chaque mois, agréger par employé + const result = {}; + Object.keys(parMois).sort().forEach(mois => { + result[mois] = agregerLignesParEmploye(parMois[mois]); + }); + + return result; + } + function estTeletravail(typeCongeNom) { + if (!typeCongeNom) return false; + const nom = typeCongeNom.toLowerCase(); + return nom.includes('télétravail') || nom.includes('teletravail') || nom.includes('remote'); + } + + /** + * Détermine si un type de congé est un arrêt maladie + */ + function estArretMaladie(typeCongeNom) { + if (!typeCongeNom) return false; + const nom = typeCongeNom.toLowerCase(); + return nom.includes('maladie') || nom.includes('arrêt') || nom.includes('arret') || nom.includes('am'); + } + + /** + * Génère le sujet et le corps HTML de l'email selon le contexte + * @param {string} collaborateurNom - Prénom Nom du collaborateur + * @param {string} datesPeriode - Ex: "du 02/03 au 05/03" ou "02/03 (Matin)" + * @param {number} totalJours - Nombre de jours + * @param {string[]} typesCongeNoms - Tableau des noms de types de congé + * @param {string|null} commentaire - Commentaire optionnel + * @param {string} periodeJournee - "Journée entière", "Matin", "Après-midi" + * @returns {{ subject: string, body: string }} + */ + function genererEmailSaisieManuelle( + collaborateurNom, + datesPeriode, + totalJours, + typesCongeNoms, + commentaire, + periodeJournee + ) { + const tousLesTeletravail = typesCongeNoms.length > 0 && typesCongeNoms.every(t => estTeletravail(t)); + const tousArretMaladie = typesCongeNoms.length > 0 && typesCongeNoms.every(t => estArretMaladie(t)); + const contientTeletravail = typesCongeNoms.some(t => estTeletravail(t)); + const contientMaladie = typesCongeNoms.some(t => estArretMaladie(t)); + + // ─── CAS 1 : Télétravail pur ───────────────────────────────────────────── + if (tousLesTeletravail) { + return { + subject: '🏠 Télétravail enregistré', + body: ` +
+
+

🏠 Télétravail enregistré

+
+
+

Bonjour ${collaborateurNom},

+

Votre journée de télétravail a été enregistrée par le service RH.

+ +
+ + + + + + + + + + ${periodeJournee && periodeJournee !== 'Journée entière' ? ` + + + + ` : ''} + ${commentaire ? ` + + + + ` : ''} +
📅 Période :${datesPeriode}
⏱️ Durée :${totalJours} jour${totalJours > 1 ? 's' : ''}
🕐 Horaire :${periodeJournee}
💬 Commentaire :${commentaire}
+
+ + +

+ Pour toute question, contactez le service RH. +

+
+
` + }; + } + + // ─── CAS 2 : Arrêt maladie pur ────────────────────────────────────────── + if (tousArretMaladie) { + return { + subject: '🩺 Arrêt maladie enregistré', + body: ` +
+
+

🩺 Arrêt maladie enregistré

+
+
+

Bonjour ${collaborateurNom},

+

Votre arrêt maladie a bien été pris en compte dans notre système.

+ +
+ + + + + + + + + + ${commentaire ? ` + + + + ` : ''} +
📅 Période :${datesPeriode}
⏱️ Durée :${totalJours} jour${totalJours > 1 ? 's' : ''}
💬 Commentaire :${commentaire}
+
+ + + +

+ Pour toute question, contactez le service RH. +

+
+
` + }; + } + + // ─── CAS 3 : Mix contenant du télétravail (ex: télétravail + CP) ──────── + if (contientTeletravail) { + const autresTypes = typesCongeNoms.filter(t => !estTeletravail(t)); + return { + subject: '📋 Absence mixte enregistrée', + body: ` +
+
+

📋 Absence mixte enregistrée

+
+
+

Bonjour ${collaborateurNom},

+

Votre absence a été enregistrée et validée automatiquement par le service RH.

+ +
+ + + + + + + + + + + + + + ${commentaire ? ` + + + + ` : ''} +
📅 Période :${datesPeriode}
📁 Types :${typesCongeNoms.join(', ')}
⏱️ Durée :${totalJours} jour${totalJours > 1 ? 's' : ''}
💬 Commentaire :${commentaire}
+
+

+ Pour toute question, contactez le service RH. +

+
+
` + }; + } + + // ─── CAS 4 : Congé classique (CP, RTT, AAP, Récupération, etc.) ───────── + return { + subject: '✅ Congé enregistré et validé', + body: ` +
+
+

✅ Congé validé automatiquement

+
+
+

Bonjour ${collaborateurNom},

+

Une demande de congé a été enregistrée et validée automatiquement par le service RH.

+ +
+ + + + + + + + + + + + + + ${periodeJournee && periodeJournee !== 'Journée entière' ? ` + + + + ` : ''} + ${commentaire ? ` + + + + ` : ''} +
📅 Période :${datesPeriode}
📁 Type :${typesCongeNoms.join(', ')}
⏱️ Durée :${totalJours} jour${totalJours > 1 ? 's' : ''}
🕐 Horaire :${periodeJournee}
💬 Commentaire :${commentaire}
+
+

+ Pour toute question, contactez le service RH. +

+
+
` + }; + } + async function sendMailGraph(accessToken, fromEmail, toEmail, subject, bodyHtml) { + try { + console.log('📤 Envoi email via Graph API...'); + console.log(' From:', fromEmail); + console.log(' To:', toEmail); + console.log(' Subject:', subject); + + const response = await fetch( + `https://graph.microsoft.com/v1.0/users/${fromEmail}/sendMail`, + { + method: 'POST', + headers: { + 'Authorization': `Bearer ${accessToken}`, + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + message: { + subject, + body: { contentType: 'HTML', content: bodyHtml }, + toRecipients: [{ emailAddress: { address: toEmail } }] + }, + saveToSentItems: false + }) + } + ); + + console.log('📊 Réponse Graph API - Status:', response.status); + + if (!response.ok) { + const errorText = await response.text(); + console.error('❌ Erreur Graph API:', errorText); + throw new Error(`Graph API Error: ${response.status} - ${errorText}`); + } + + console.log('✅ Email envoyé avec succès'); + return true; + } catch (error) { + console.error('❌ Erreur sendMailGraph:', error.message); + return false; + } + } + + // Middleware d'authentification + const authenticateToken = (req, res, next) => { + const token = req.headers['authorization']?.split(' ')[1]; + if (!token) return res.status(401).json({ error: 'Token requis' }); + + jwt.verify(token, process.env.JWT_SECRET, (err, user) => { + if (err) return res.status(403).json({ error: 'Token invalide' }); + req.user = user; + next(); + }); + }; + + // ================================================ + // GESTION DES SERVER-SENT EVENTS (SSE) + // ================================================ + + const sseClients = new Set(); + + // 🔌 ROUTE SSE POUR LE CALENDRIER + app.get('/events', (req, res) => { + const { token, userid } = req.query; + + let userId = userid; + + // ✅ Si token fourni, extraire l'ID utilisateur + if (token && !userId) { + try { + const decoded = jwt.verify(token, process.env.JWT_SECRET); + userId = decoded.id; + console.log('🔓 Token JWT décodé, userId:', userId); + } catch (error) { + console.error('❌ Token invalide:', error.message); + return res.status(401).json({ error: 'Token invalide' }); + } + } + + if (!userId) { + console.error('❌ Ni userid ni token fourni'); + return res.status(400).json({ error: 'userid ou token requis' }); + } + + console.log('🔗 Nouvelle connexion SSE (RH):', userId); + + // Headers SSE critiques + res.setHeader('Content-Type', 'text/event-stream'); + res.setHeader('Cache-Control', 'no-cache'); + res.setHeader('Connection', 'keep-alive'); + res.setHeader('X-Accel-Buffering', 'no'); + + // Flush pour établir la connexion + res.flushHeaders(); + + const sendEvent = (data) => { + try { + res.write(`data: ${JSON.stringify(data)}\n\n`); + } catch (error) { + console.error('Erreur envoi SSE:', error); + } + }; + + const client = { id: userId, send: sendEvent }; + sseClients.add(client); + console.log(`📊 Clients SSE connectés: ${sseClients.size}`); + + // Envoyer un heartbeat initial + sendEvent({ type: 'ping', message: 'Connexion établie', timestamp: new Date().toISOString() }); + + // Heartbeat toutes les 30 secondes + const heartbeat = setInterval(() => { + try { + sendEvent({ type: 'ping', timestamp: new Date().toISOString() }); + } catch (error) { + clearInterval(heartbeat); + } + }, 30000); + + // Gérer la déconnexion + req.on('close', () => { + console.log('🔌 Déconnexion SSE (RH):', userId); + clearInterval(heartbeat); + sseClients.delete(client); + console.log(`📊 Clients SSE connectés: ${sseClients.size}`); + }); + }); + + // 📢 FONCTION POUR NOTIFIER LES CLIENTS + const notifyClients = (event, userId = null) => { + console.log(`📢 Notification SSE: ${event.type}${userId ? ` pour ${userId}` : ''}`); + + sseClients.forEach(client => { + if (userId && client.id !== userId) { + return; + } + + try { + client.send(event); + } catch (error) { + console.error('❌ Erreur envoi event:', error); + } + }); + }; + + // ================================================ + // ROUTE WEBHOOK RECEIVER + // ================================================ + + app.post('/webhook/receive', async (req, res) => { + try { + const signature = req.headers['x-webhook-signature']; + const payload = req.body; + + console.log('📥 Webhook reçu du serveur RH:', payload.event); + + if (!webhookManager.verifySignature(payload, signature)) { + console.error('❌ Signature webhook invalide'); + return res.status(401).json({ error: 'Signature invalide' }); + } + + const { event, data } = payload; + + console.log('✅ Signature valide ! Traitement...'); + console.log(' Event:', event); + console.log(' Data:', data); + + switch (event) { + case EVENTS.DEMANDE_VALIDATED: + console.log(`\n✅ === WEBHOOK DEMANDE_VALIDATED REÇU ===`); + console.log(` Demande: ${data.demandeId}`); + console.log(` Statut: ${data.statut}`); + console.log(` Type: ${data.type || data.typeConge}`); + console.log(` Couleur: ${data.couleurHex}`); + + try { + // Notification SSE locale (serveur RH) + notifyClients({ + type: 'demande-validated', + demandeId: demandeId, + statut: 'Validé', + typeConge: 'JPOSF', + couleurHex: '#d946ef', + collaborateurId: parseInt(collaborateurid), + timestamp: new Date().toISOString() + }, parseInt(collaborateurid)); + + // 🔧 WEBHOOK vers serveur COLLABORATEURS (pour calendrier) + await webhookManager.sendWebhook( + WEBHOOKS.COLLABORATEURS_URL, + EVENTS.DEMANDEVALIDATED, + { + demandeId: demandeId, + statut: 'Validé', + typeConge: 'JPOSF', + couleurHex: '#d946ef', + date: date, // ← ESSENTIEL + dateDebut: date, // ← ESSENTIEL + dateFin: date, // ← ESSENTIEL + periode: periodejournee, // ← ESSENTIEL + periodeJournee: periodejournee, + nombreJours: nombreJours, + collaborateurId: parseInt(collaborateurid) + } + ); + + console.log('✅ Webhook JPOSF envoyé au serveur collaborateurs'); + } catch (sseError) { + console.error('❌ Erreur SSE/Webhook:', sseError.message); + } + + case EVENTS.DEMANDE_CANCELLED: + console.log(`\n🔴 === WEBHOOK DEMANDE_CANCELLED REÇU ===`); + console.log(` Demande: ${data.demandeId}`); + console.log(` Annulée par: ${data.annulateurRole}`); + console.log(` Commentaire: ${data.commentaire || 'Aucun'}`); + + notifyClients({ + type: 'demande-cancelled', + demandeId: data.demandeId, + statut: 'Annulée', + collaborateurId: data.collaborateurId, + annulateurRole: data.annulateurRole, + commentaire: data.commentaire, + timestamp: new Date().toISOString() + }); + + notifyClients({ + type: 'demande-updated', + demandeId: data.demandeId, + nouveauStatut: 'Annulée', + timestamp: new Date().toISOString() + }, data.collaborateurId); + + console.log(` ✅ SSE envoyé - Demande ${data.demandeId} marquée comme annulée`); + break; + + case EVENTS.COMPTEUR_UPDATED: + console.log(`\n🔄 === WEBHOOK COMPTEUR_UPDATED REÇU ===`); + console.log(` Collaborateur: ${data.collaborateurId}`); + + notifyClients({ + type: 'compteur-updated', + collaborateurId: data.collaborateurId, + typeConge: data.typeConge, + typeUpdate: data.typeUpdate, + jours: data.jours, + timestamp: new Date().toISOString() + }, data.collaborateurId); + + console.log(` ✅ SSE envoyé`); + break; + + case EVENTS.DEMANDE_UPDATED: + console.log(`✏️ Demande ${data.demandeId} modifiée via RH`); + + notifyClients({ + type: 'demande-updated-rh', + demandeId: data.demandeId, + timestamp: new Date().toISOString() + }, data.collaborateurId); + break; + + case EVENTS.DEMANDE_DELETED: + console.log(`🗑️ Demande ${data.demandeId} supprimée via RH`); + + notifyClients({ + type: 'demande-deleted-rh', + demandeId: data.demandeId, + timestamp: new Date().toISOString() + }, data.collaborateurId); + break; + + default: + console.warn(`⚠️ Type d'événement webhook inconnu: ${event}`); + } + + res.json({ success: true, message: 'Webhook traité' }); + + } catch (error) { + console.error('❌ Erreur traitement webhook:', error); + res.status(500).json({ error: error.message }); + } + }); + + // ================================================ + // ROUTES D'AUTHENTIFICATION + // ================================================ + + app.get('/users-dev', async (req, res) => { + try { + const result = await pool.request().query(` + SELECT id, email, nom, prenom, role, service, Actif + FROM CollaborateurAD + WHERE Actif = 1 OR Actif IS NULL + ORDER BY nom, prenom + `); + res.json(result.recordset); + } catch (error) { + console.error('❌ Erreur users-dev:', error); + res.status(500).json({ error: error.message }); + } + }); + + app.post('/login-dev', async (req, res) => { + try { + console.log('🔐 Login attempt started'); + const { accessToken } = req.body; + + if (!accessToken) { + console.error('❌ No access token provided'); + return res.status(400).json({ error: 'Token d\'accès manquant' }); + } + + console.log('👤 Validating access token...'); + + let userInfo; + try { + const graphResponse = await fetch('https://graph.microsoft.com/v1.0/me', { + headers: { + 'Authorization': `Bearer ${accessToken}` + } + }); + + if (!graphResponse.ok) { + throw new Error('Token invalide'); + } + + userInfo = await graphResponse.json(); + console.log('✅ Token validated, user:', userInfo.mail || userInfo.userPrincipalName); + } catch (graphError) { + console.error('❌ Graph API Error:', graphError); + return res.status(401).json({ + error: 'Token d\'accès invalide', + details: graphError.message + }); + } + + const userEmail = userInfo.mail || userInfo.userPrincipalName; + + // ⭐ REQUÊTE SQL SERVER + const result = await pool.request() + .input('email', sql.VarChar, userEmail) + .query(` + SELECT * FROM CollaborateurAD + WHERE email = @email + AND (Actif = 1 OR Actif IS NULL) + `); + + const users = result.recordset; + + if (users.length === 0) { + console.warn('⚠️ User not found or inactive:', userEmail); + return res.status(404).json({ + error: 'Utilisateur non trouvé ou compte désactivé', + details: `Aucun utilisateur actif avec l'email ${userEmail}` + }); + } + + const user = users[0]; + console.log('👤 User found:', user.email, 'Role:', user.role); + + if (!['RH', 'Admin', 'Validateur'].includes(user.role)) { + console.warn('⚠️ Unauthorized role:', user.role); + return res.status(403).json({ + error: 'Accès non autorisé', + details: `Le rôle '${user.role}' n'a pas accès à cette application` + }); + } + + const token = jwt.sign( + { id: user.id, email: user.email, role: user.role }, + process.env.JWT_SECRET, + { expiresIn: '8h' } + ); + + console.log('✅ Login successful for:', user.email); + res.json({ + token, + user: { + id: user.id, + nom: user.nom, + prenom: user.prenom, + role: user.role + } + }); + } catch (error) { + console.error('❌ Unexpected error in /login-dev:', error); + res.status(500).json({ + error: 'Erreur serveur inattendue', + details: error.message + }); + } + }); + + // ================================================ + // ROUTES DES DEMANDES DE CONGÉS + // ================================================ + + // ================================================ + // ROUTE POST /demandes - VERSION MODIFIÉE AVEC PRIORITÉ CP N-1 puis CP N + // Remplacer la route existante par celle-ci + // ================================================ + app.post('/demandes', authenticateToken, async (req, res) => { + const transaction = new sql.Transaction(pool); + try { + await transaction.begin(); + + const { collaborateurId, dateDebut, dateFin, typesConge, commentaire, saisieManuelle, periodeJournee } = req.body; + const currentYear = new Date().getFullYear(); + + console.log('📝 Création demande:', { collaborateurId, dateDebut, dateFin, saisieManuelle, periodeJournee }); + + // Vérifier que le collaborateur est actif + const collabResult = await transaction.request() + .input('collabId', sql.Int, collaborateurId) + .query('SELECT Actif, prenom, nom, email FROM CollaborateurAD WHERE id = @collabId'); + + const collab = collabResult.recordset; + + if (collab.length === 0 || collab[0].Actif === 0) { + await transaction.rollback(); + return res.status(403).json({ error: 'Impossible de créer une demande pour un compte désactivé' }); + } + + const totalJours = typesConge.reduce((sum, type) => sum + parseFloat(type.nombreJours), 0); + const statut = saisieManuelle ? 'Validée' : 'En attente'; + + console.log(`📝 Création demande - Saisie manuelle: ${saisieManuelle}, Statut: ${statut}`); + + // GÉNÉRATION MANUELLE DE L'ID POUR DEMANDECONGE + const maxIdResult = await transaction.request() + .query('SELECT ISNULL(MAX(Id), 0) + 1 AS NextId FROM DemandeConge'); + + const demandeId = maxIdResult.recordset[0].NextId; + console.log(`📝 Nouvel ID DemandeConge généré: ${demandeId}`); + + if (saisieManuelle) { + await transaction.request() + .input('id', sql.Int, demandeId) + .input('collaborateurId', sql.Int, collaborateurId) + .input('dateDebut', sql.Date, dateDebut) + .input('dateFin', sql.Date, dateFin) + .input('commentaire', sql.NVarChar, commentaire || 'Saisie manuelle RH') + .input('totalJours', sql.Decimal(10, 2), totalJours) + .input('validateurId', sql.Int, req.user.id) + .query(` + INSERT INTO DemandeConge + (Id, CollaborateurADId, DateDebut, DateFin, Statut, Commentaire, DateDemande, NombreJours, ValidateurADId, DateValidation) + VALUES (@id, @collaborateurId, @dateDebut, @dateFin, 'Validée', @commentaire, GETDATE(), @totalJours, @validateurId, GETDATE()) + `); + } else { + await transaction.request() + .input('id', sql.Int, demandeId) + .input('collaborateurId', sql.Int, collaborateurId) + .input('dateDebut', sql.Date, dateDebut) + .input('dateFin', sql.Date, dateFin) + .input('commentaire', sql.NVarChar, commentaire || '') + .input('totalJours', sql.Decimal(10, 2), totalJours) + .query(` + INSERT INTO DemandeConge + (Id, CollaborateurADId, DateDebut, DateFin, Statut, Commentaire, DateDemande, NombreJours) + VALUES (@id, @collaborateurId, @dateDebut, @dateFin, 'En attente', @commentaire, GETDATE(), @totalJours) + `); + } + + console.log(`✅ DemandeConge créée - ID: ${demandeId}`); + + // GÉNÉRATION MANUELLE DE L'ID POUR DEMANDECONGETUPE avec PeriodeJournee + for (const type of typesConge) { + const maxTypeIdResult = await transaction.request() + .query('SELECT ISNULL(MAX(Id), 0) + 1 AS NextId FROM DemandeCongeType'); + + const typeId = maxTypeIdResult.recordset[0].NextId; + + await transaction.request() + .input('id', sql.Int, typeId) + .input('demandeId', sql.Int, demandeId) + .input('typeCongeId', sql.Int, type.typeId) + .input('nombreJours', sql.Decimal(10, 2), type.nombreJours) + .input('periode', sql.NVarChar(50), periodeJournee || 'Journée entière') + .query('INSERT INTO DemandeCongeType (Id, DemandeCongeId, TypeCongeId, NombreJours, PeriodeJournee) VALUES (@id, @demandeId, @typeCongeId, @nombreJours, @periode)'); + + console.log(` ✅ DemandeCongeType créée - ID: ${typeId}, TypeCongeId: ${type.typeId}, Période: ${periodeJournee || 'Journée entière'}`); + } + + // ⭐ Si saisie manuelle : déduction avec DeductionDetails (même logique que serverGTA) + if (saisieManuelle) { + console.log('💼 Saisie manuelle - Déduction via DeductionDetails...'); + + // ⭐ NOUVEAU : Pré-charger les noms des types pour détecter le télétravail + const typeIds = typesConge.map(t => t.typeId); + const typesInfoResult = await transaction.request() + .query(`SELECT Id, Nom FROM TypeConge WHERE Id IN (${typeIds.join(',')})`); + const typeNomMap = {}; + typesInfoResult.recordset.forEach(t => { typeNomMap[t.Id] = t.Nom; }); + + // Déterminer l'exercice CP courant (Annee = année de FIN d'exercice) + // Ex : avant 01/06/currentYear → anneeN=currentYear ; après → anneeN=currentYear+1 + const dateBasculeCP = new Date(currentYear, 5, 1); // 01/06 + const apresBasculement = new Date() >= dateBasculeCP; + const anneeN = apresBasculement ? currentYear + 1 : currentYear; + const anneeN1 = anneeN - 1; + + // Exercice du congé (pour détecter l'anticipé) + const dateDebutObj = new Date(dateDebut); + const moisDebut = dateDebutObj.getMonth() + 1; + const anneeDebut = dateDebutObj.getFullYear(); + const anneeExConge = moisDebut >= 6 ? anneeDebut + 1 : anneeDebut; + const isAnticipe = anneeExConge > anneeN; + + // Exercice RTT = année civile du congé + const anneeRTT = anneeDebut; + const isAnticipeRTT = anneeRTT > currentYear; + + for (const type of typesConge) { + const typeId = type.typeId; + const nombreJours = parseFloat(type.nombreJours); + const typeNom = typeNomMap[typeId] || ''; + + // ⭐ NOUVEAU : Télétravail = jour travaillé, pas une absence → aucune déduction + if (estTeletravail(typeNom)) { + console.log(`🏠 Télétravail (${typeNom}) - ${nombreJours}j - Aucune déduction de compteur`); + continue; + } + + // ── CP (typeId === 1) ────────────────────────────────────────── + if (typeId === 1) { + console.log(`\n🔄 CP - ${nombreJours}j à déduire | isAnticipe=${isAnticipe} | anneeExConge=${anneeExConge} | anneeN=${anneeN}`); + let joursRestants = nombreJours; + + if (!isAnticipe) { + // ── MODE NORMAL : N-1 puis N ────────────────────────── + // 1️⃣ CP N-1 + const cptN1Res = await transaction.request() + .input('collabId', sql.Int, collaborateurId) + .input('typeId', sql.Int, typeId) + .input('annee', sql.Int, anneeN1) + .query(`SELECT TOP 1 Id, Solde, SoldeReporte FROM CompteurConges WHERE CollaborateurADId=@collabId AND TypeCongeId=@typeId AND Annee=@annee`); + + if (cptN1Res.recordset.length > 0) { + const cptN1 = cptN1Res.recordset[0]; + const soldeN1 = parseFloat(cptN1.Solde || 0); + const aDeduireN1 = Math.min(soldeN1, joursRestants); + if (aDeduireN1 > 0) { + await transaction.request() + .input('ded', sql.Decimal(10, 2), aDeduireN1) + .input('id', sql.Int, cptN1.Id) + .query(`UPDATE CompteurConges SET Solde=Solde-@ded, SoldeReporte=CASE WHEN SoldeReporte-@ded<0 THEN 0 ELSE SoldeReporte-@ded END, DerniereMiseAJour=GETDATE() WHERE Id=@id`); + await transaction.request() + .input('demandeId', sql.Int, demandeId) + .input('typeId', sql.Int, typeId) + .input('annee', sql.Int, anneeN1) + .input('jours', sql.Decimal(10, 2), aDeduireN1) + .query(`INSERT INTO DeductionDetails (DemandeCongeId, TypeCongeId, Annee, TypeDeduction, JoursUtilises) VALUES (@demandeId, @typeId, @annee, 'Année N-1', @jours)`); + console.log(` ✅ CP Année N-1 (${anneeN1}) : -${aDeduireN1}j`); + joursRestants -= aDeduireN1; + } + } + + // 2️⃣ CP N + if (joursRestants > 0) { + const cptNRes = await transaction.request() + .input('collabId', sql.Int, collaborateurId) + .input('typeId', sql.Int, typeId) + .input('annee', sql.Int, anneeN) + .query(`SELECT TOP 1 Id, Solde FROM CompteurConges WHERE CollaborateurADId=@collabId AND TypeCongeId=@typeId AND Annee=@annee`); + if (cptNRes.recordset.length > 0) { + await transaction.request() + .input('ded', sql.Decimal(10, 2), joursRestants) + .input('id', sql.Int, cptNRes.recordset[0].Id) + .query(`UPDATE CompteurConges SET Solde=Solde-@ded, DerniereMiseAJour=GETDATE() WHERE Id=@id`); + await transaction.request() + .input('demandeId', sql.Int, demandeId) + .input('typeId', sql.Int, typeId) + .input('annee', sql.Int, anneeN) + .input('jours', sql.Decimal(10, 2), joursRestants) + .query(`INSERT INTO DeductionDetails (DemandeCongeId, TypeCongeId, Annee, TypeDeduction, JoursUtilises) VALUES (@demandeId, @typeId, @annee, 'Année N', @jours)`); + console.log(` ✅ CP Année N (${anneeN}) : -${joursRestants}j`); + joursRestants = 0; + } else { + console.warn(` ⚠️ Compteur CP N (${anneeN}) introuvable`); + } + } + + } else { + // ── MODE ANTICIPÉ : tout tracé sur anneeN ───────────── + // Projeter solde CP N jusqu'au 31/05/anneeN + const TAUX_CP = 25 / 360; + const cptNRes = await transaction.request() + .input('collabId', sql.Int, collaborateurId) + .input('typeId', sql.Int, typeId) + .input('annee', sql.Int, anneeN) + .query(`SELECT TOP 1 Id, Solde FROM CompteurConges WHERE CollaborateurADId=@collabId AND TypeCongeId=@typeId AND Annee=@annee`); + const soldeNActuel = cptNRes.recordset.length > 0 ? Math.max(0, parseFloat(cptNRes.recordset[0].Solde || 0)) : 0; + + const finExN = new Date(anneeN, 4, 31); + const joursRestantsExN = Math.max(0, Math.floor((finExN - new Date()) / 86400000)); + const acquisRestantsN = parseFloat(Math.min(25 - soldeNActuel, joursRestantsExN * TAUX_CP).toFixed(2)); + const soldeNProj = parseFloat(Math.min(25, soldeNActuel + acquisRestantsN).toFixed(2)); + + // Part sur solde réel actuel → TypeDeduction='Anticip' + const aDeduireAnticip = Math.min(soldeNActuel, joursRestants); + if (aDeduireAnticip > 0 && cptNRes.recordset.length > 0) { + await transaction.request() + .input('ded', sql.Decimal(10, 2), aDeduireAnticip) + .input('id', sql.Int, cptNRes.recordset[0].Id) + .query(`UPDATE CompteurConges SET Solde=Solde-@ded, DerniereMiseAJour=GETDATE() WHERE Id=@id`); + await transaction.request() + .input('demandeId', sql.Int, demandeId) + .input('typeId', sql.Int, typeId) + .input('annee', sql.Int, anneeN) + .input('jours', sql.Decimal(10, 2), aDeduireAnticip) + .query(`INSERT INTO DeductionDetails (DemandeCongeId, TypeCongeId, Annee, TypeDeduction, JoursUtilises) VALUES (@demandeId, @typeId, @annee, 'Anticip', @jours)`); + console.log(` ✅ CP Anticip (${anneeN}) : -${aDeduireAnticip}j sur solde réel`); + joursRestants -= aDeduireAnticip; + } + + // Part sur acquisitions futures → TypeDeduction='AnticipFutur' + if (joursRestants > 0) { + const aDeduireAnticipFutur = Math.min(acquisRestantsN, joursRestants); + if (aDeduireAnticipFutur > 0) { + await transaction.request() + .input('demandeId', sql.Int, demandeId) + .input('typeId', sql.Int, typeId) + .input('annee', sql.Int, anneeN) + .input('jours', sql.Decimal(10, 2), aDeduireAnticipFutur) + .query(`INSERT INTO DeductionDetails (DemandeCongeId, TypeCongeId, Annee, TypeDeduction, JoursUtilises) VALUES (@demandeId, @typeId, @annee, 'AnticipFutur', @jours)`); + console.log(` ✅ CP AnticipFutur (${anneeN}) : ${aDeduireAnticipFutur}j tracé (acquisitions futures)`); + joursRestants -= aDeduireAnticipFutur; + } + } + + // Part sur CP N+1 (après 01/06/anneeN) → TypeDeduction='AnticipFutur' sur anneeExConge + if (joursRestants > 0) { + await transaction.request() + .input('demandeId', sql.Int, demandeId) + .input('typeId', sql.Int, typeId) + .input('annee', sql.Int, anneeExConge) + .input('jours', sql.Decimal(10, 2), joursRestants) + .query(`INSERT INTO DeductionDetails (DemandeCongeId, TypeCongeId, Annee, TypeDeduction, JoursUtilises) VALUES (@demandeId, @typeId, @annee, 'AnticipFutur', @jours)`); + console.log(` ✅ CP AnticipFutur N+1 (${anneeExConge}) : ${joursRestants}j tracé`); + joursRestants = 0; + } + } + + if (joursRestants > 0) { + console.warn(` ⚠️ CP : ${joursRestants}j non couverts (solde insuffisant, traçage forcé sur anneeN)`); + await transaction.request() + .input('demandeId', sql.Int, demandeId) + .input('typeId', sql.Int, typeId) + .input('annee', sql.Int, anneeN) + .input('jours', sql.Decimal(10, 2), joursRestants) + .query(`INSERT INTO DeductionDetails (DemandeCongeId, TypeCongeId, Annee, TypeDeduction, JoursUtilises) VALUES (@demandeId, @typeId, @annee, 'AnticipFutur', @jours)`); + } + + // ── RTT (typeId === 2) ───────────────────────────────────────── + } else if (typeId === 2) { + console.log(`\n🔄 RTT - ${nombreJours}j à déduire | isAnticipeRTT=${isAnticipeRTT} | anneeRTT=${anneeRTT}`); + let joursRestants = nombreJours; + + const cptRTTRes = await transaction.request() + .input('collabId', sql.Int, collaborateurId) + .input('typeId', sql.Int, typeId) + .input('annee', sql.Int, anneeRTT) + .query(`SELECT TOP 1 Id, Solde FROM CompteurConges WHERE CollaborateurADId=@collabId AND TypeCongeId=@typeId AND Annee=@annee`); + + if (!isAnticipeRTT) { + // ── MODE NORMAL RTT ────────────────────────────────────── + if (cptRTTRes.recordset.length > 0) { + await transaction.request() + .input('ded', sql.Decimal(10, 2), joursRestants) + .input('id', sql.Int, cptRTTRes.recordset[0].Id) + .query(`UPDATE CompteurConges SET Solde=Solde-@ded, DerniereMiseAJour=GETDATE() WHERE Id=@id`); + await transaction.request() + .input('demandeId', sql.Int, demandeId) + .input('typeId', sql.Int, typeId) + .input('annee', sql.Int, anneeRTT) + .input('jours', sql.Decimal(10, 2), joursRestants) + .query(`INSERT INTO DeductionDetails (DemandeCongeId, TypeCongeId, Annee, TypeDeduction, JoursUtilises) VALUES (@demandeId, @typeId, @annee, 'Année N', @jours)`); + console.log(` ✅ RTT Année N (${anneeRTT}) : -${joursRestants}j`); + joursRestants = 0; + } else { + console.warn(` ⚠️ Compteur RTT (${anneeRTT}) introuvable`); + } + } else { + // ── MODE ANTICIPÉ RTT ──────────────────────────────────── + const soldeRTTActuel = cptRTTRes.recordset.length > 0 ? Math.max(0, parseFloat(cptRTTRes.recordset[0].Solde || 0)) : 0; + + // Part sur solde réel → AnticipRTT + const aDeduireAnticipRTT = Math.min(soldeRTTActuel, joursRestants); + if (aDeduireAnticipRTT > 0 && cptRTTRes.recordset.length > 0) { + await transaction.request() + .input('ded', sql.Decimal(10, 2), aDeduireAnticipRTT) + .input('id', sql.Int, cptRTTRes.recordset[0].Id) + .query(`UPDATE CompteurConges SET Solde=Solde-@ded, DerniereMiseAJour=GETDATE() WHERE Id=@id`); + await transaction.request() + .input('demandeId', sql.Int, demandeId) + .input('typeId', sql.Int, typeId) + .input('annee', sql.Int, anneeRTT) + .input('jours', sql.Decimal(10, 2), aDeduireAnticipRTT) + .query(`INSERT INTO DeductionDetails (DemandeCongeId, TypeCongeId, Annee, TypeDeduction, JoursUtilises) VALUES (@demandeId, @typeId, @annee, 'AnticipRTT', @jours)`); + console.log(` ✅ RTT AnticipRTT (${anneeRTT}) : -${aDeduireAnticipRTT}j`); + joursRestants -= aDeduireAnticipRTT; + } + + // Part sur acquisitions futures → AnticipFuturRTT + if (joursRestants > 0) { + await transaction.request() + .input('demandeId', sql.Int, demandeId) + .input('typeId', sql.Int, typeId) + .input('annee', sql.Int, anneeRTT) + .input('jours', sql.Decimal(10, 2), joursRestants) + .query(`INSERT INTO DeductionDetails (DemandeCongeId, TypeCongeId, Annee, TypeDeduction, JoursUtilises) VALUES (@demandeId, @typeId, @annee, 'AnticipFuturRTT', @jours)`); + console.log(` ✅ RTT AnticipFuturRTT (${anneeRTT}) : ${joursRestants}j tracé`); + joursRestants = 0; + } + } + + // ── Autres types (Récupération, JPOSF, etc.) ────────────────── + } else { + const anneeType = anneeDebut; // année civile du congé + const cptRes = await transaction.request() + .input('collabId', sql.Int, collaborateurId) + .input('typeId', sql.Int, typeId) + .input('annee', sql.Int, anneeType) + .query(`SELECT TOP 1 Id FROM CompteurConges WHERE CollaborateurADId=@collabId AND TypeCongeId=@typeId AND Annee=@annee`); + + if (cptRes.recordset.length > 0) { + await transaction.request() + .input('ded', sql.Decimal(10, 2), nombreJours) + .input('id', sql.Int, cptRes.recordset[0].Id) + .query(`UPDATE CompteurConges SET Solde=Solde-@ded, DerniereMiseAJour=GETDATE() WHERE Id=@id`); + } else { + // Fallback : update sans filtre d'année (comportement original) + await transaction.request() + .input('ded', sql.Decimal(10, 2), nombreJours) + .input('collabId', sql.Int, collaborateurId) + .input('typeId', sql.Int, typeId) + .query(`UPDATE CompteurConges SET Solde=Solde-@ded, DerniereMiseAJour=GETDATE() WHERE CollaborateurADId=@collabId AND TypeCongeId=@typeId AND Annee=YEAR(GETDATE())`); + } + await transaction.request() + .input('demandeId', sql.Int, demandeId) + .input('typeId', sql.Int, typeId) + .input('annee', sql.Int, anneeType) + .input('jours', sql.Decimal(10, 2), nombreJours) + .query(`INSERT INTO DeductionDetails (DemandeCongeId, TypeCongeId, Annee, TypeDeduction, JoursUtilises) VALUES (@demandeId, @typeId, @annee, 'Année N', @jours)`); + console.log(` ✅ Type ${typeId} : -${nombreJours}j (Année N ${anneeType})`); + } + } + + // GÉNÉRATION MANUELLE DE L'ID POUR HISTORIQUEACTIONS + try { + const maxHistIdResult = await transaction.request() + .query('SELECT ISNULL(MAX(Id), 0) + 1 AS NextId FROM HistoriqueActions'); + + const histId = maxHistIdResult.recordset[0].NextId; + + await transaction.request() + .input('id', sql.Int, histId) + .input('userId', sql.Int, req.user.id) + .input('details', sql.NVarChar, `Demande créée et validée automatiquement par ${req.user.role}`) + .input('demandeId', sql.Int, demandeId) + .query(` + INSERT INTO HistoriqueActions (Id, CollaborateurADId, Action, Details, DemandeCongeId, DateAction) + VALUES (@id, @userId, 'Saisie manuelle validée', @details, @demandeId, GETDATE()) + `); + } catch (histError) { + console.log('⚠️ Tentative sans ID pour HistoriqueActions...'); + await transaction.request() + .input('userId', sql.Int, req.user.id) + .input('details', sql.NVarChar, `Demande créée et validée automatiquement par ${req.user.role}`) + .input('demandeId', sql.Int, demandeId) + .query(` + INSERT INTO HistoriqueActions (CollaborateurADId, Action, Details, DemandeCongeId, DateAction) + VALUES (@userId, 'Saisie manuelle validée', @details, @demandeId, GETDATE()) + `); + } + } + + await transaction.commit(); + + // Notifications SSE + if (saisieManuelle) { + notifyClients({ + type: 'demande-validated', + demandeId: demandeId, + statut: 'Validée', + collaborateurId: collaborateurId, + timestamp: new Date().toISOString() + }, collaborateurId); + + notifyClients({ + type: 'compteur-updated', + collaborateurId: collaborateurId, + action: 'saisie_manuelle_rh', + timestamp: new Date().toISOString() + }, collaborateurId); + + // Webhooks + try { + await webhookManager.sendWebhook( + WEBHOOKS.COLLABORATEURS_URL, + EVENTS.DEMANDE_VALIDATED, + { demandeId, statut: 'Validée', collaborateurId, validateurId: req.user.id } + ); + await webhookManager.sendWebhook( + WEBHOOKS.COLLABORATEURS_URL, + EVENTS.COMPTEUR_UPDATED, + { collaborateurId } + ); + } catch (webhookError) { + console.error('⚠️ Erreur webhook:', webhookError.message); + } + + // Email + try { + const accessToken = await getGraphToken(); + if (accessToken && collab[0].email) { + const collaborateurNom = `${collab[0].prenom} ${collab[0].nom}`; + const dateDebutFormatted = new Date(dateDebut).toLocaleDateString('fr-FR'); + const dateFinFormatted = new Date(dateFin).toLocaleDateString('fr-FR'); + const datesPeriode = dateDebutFormatted === dateFinFormatted + ? `${dateDebutFormatted}${periodeJournee && periodeJournee !== 'Journée entière' ? ` (${periodeJournee})` : ''}` + : `du ${dateDebutFormatted} au ${dateFinFormatted}`; + + // Récupérer les noms des types de congé + const typesCongeNoms = []; + for (const type of typesConge) { + const nomRes = await pool.request() + .input('typeId', sql.Int, type.typeId) + .query('SELECT Nom FROM TypeConge WHERE Id = @typeId'); + if (nomRes.recordset.length > 0) typesCongeNoms.push(nomRes.recordset[0].Nom); + } + + const { subject, body } = genererEmailSaisieManuelle( + collaborateurNom, + datesPeriode, + totalJours, + typesCongeNoms, + commentaire, + periodeJournee + ); + + await sendMailGraph(accessToken, 'gtanoreply@ensup.eu', collab[0].email, subject, body); + } + } catch (emailError) { + console.error('⚠️ Erreur email:', emailError.message); + } + } else { + notifyClients({ + type: 'demande-created', + demandeId: demandeId, + collaborateurId: collaborateurId, + timestamp: new Date().toISOString() + }); + } + + console.log(`✅ Demande ${demandeId} créée avec statut: ${statut}`); + + res.json({ + id: demandeId, + message: saisieManuelle ? 'Demande créée et validée automatiquement' : 'Demande créée avec succès', + statut: statut + }); + + } catch (error) { + await transaction.rollback(); + console.error('❌ Erreur création demande:', error); + res.status(500).json({ error: error.message }); + } + }); + // ================================================ + // ROUTES COMPTE-RENDU D'ACTIVITÉS + // ================================================ + + // GET /api/compte-rendu/services - Liste des services avec forfaits jour + app.get('/compte-rendu/services', authenticateToken, async (req, res) => { + try { + if (!['Admin', 'RH'].includes(req.user.role)) { + return res.status(403).json({ error: 'Accès non autorisé' }); + } + + const result = await pool.request().query(` + SELECT + s.Id, + s.Nom, + COUNT(DISTINCT ca.id) as nombreForfaitJour + FROM Services s + LEFT JOIN CollaborateurAD ca ON s.Id = ca.ServiceId + AND ca.TypeContrat = 'forfait_jour' + AND (ca.Actif = 1 OR ca.Actif IS NULL) + GROUP BY s.Id, s.Nom + HAVING COUNT(DISTINCT ca.id) > 0 + ORDER BY s.Nom + `); + + res.json(result.recordset); + } catch (error) { + console.error('❌ Erreur /compte-rendu/services:', error); + res.status(500).json({ error: error.message }); + } + }); + + // GET /api/compte-rendu/stats - Stats globales des comptes-rendus + app.get('/compte-rendu/stats', authenticateToken, async (req, res) => { + try { + if (!['Admin', 'RH'].includes(req.user.role)) { + return res.status(403).json({ error: 'Accès non autorisé' }); + } + + const { annee, mois, service_id } = req.query; + + const request = pool.request() + .input('annee', sql.Int, parseInt(annee)) + .input('mois', sql.Int, parseInt(mois)); + + let serviceFilter = ''; + if (service_id && service_id !== 'all') { + serviceFilter = 'AND ca.ServiceId = @serviceId'; + request.input('serviceId', sql.Int, parseInt(service_id)); + } + + // Compter les forfaits jour actifs + const totalResult = await request.query(` + SELECT COUNT(DISTINCT ca.id) as total + FROM CollaborateurAD ca + WHERE ca.TypeContrat = 'forfait_jour' + AND (ca.Actif = 1 OR ca.Actif IS NULL) + ${serviceFilter} + `); + + const totalCollaborateurs = totalResult.recordset[0].total; + + // Compter les mensuels validés (verrouillés) + const validesRequest = pool.request() + .input('annee', sql.Int, parseInt(annee)) + .input('mois', sql.Int, parseInt(mois)); + + if (service_id && service_id !== 'all') { + validesRequest.input('serviceId', sql.Int, parseInt(service_id)); + } + + const validesResult = await validesRequest.query(` + SELECT COUNT(DISTINCT m.CollaborateurADId) as valides + FROM CompteRenduMensuel m + JOIN CollaborateurAD ca ON m.CollaborateurADId = ca.id + WHERE m.Annee = @annee + AND m.Mois = @mois + AND m.Verrouille = 1 + AND ca.TypeContrat = 'forfait_jour' + AND (ca.Actif = 1 OR ca.Actif IS NULL) + ${serviceFilter} + `); + + const mensuelsValides = validesResult.recordset[0].valides || 0; + const mensuelsEnAttente = totalCollaborateurs - mensuelsValides; + + res.json({ + totalCollaborateurs, + mensuelsValides, + mensuelsEnAttente + }); + } catch (error) { + console.error('❌ Erreur /compte-rendu/stats:', error); + res.status(500).json({ error: error.message }); + } + }); + + // Route GET /api/compte-rendu-activites + app.get('/compte-rendu-activites', authenticateToken, async (req, res) => { + try { + const { userid, annee, mois } = req.query; + + if (!userid || !annee || !mois) { + return res.status(400).json({ + success: false, + message: 'Paramètres manquants : userid, annee et mois requis' + }); + } + + // Vérifier si l'utilisateur est RH/Admin + if (!['RH', 'Admin'].includes(req.user.role)) { + return res.status(403).json({ error: 'Accès réservé aux RH et Administrateurs' }); + } + + const collaborateurId = parseInt(userid); + const anneeInt = parseInt(annee); + const moisInt = parseInt(mois); + + // Récupérer les jours du mois + const joursResult = await pool.request() + .input('collaborateurId', sql.Int, collaborateurId) + .input('annee', sql.Int, anneeInt) + .input('mois', sql.Int, moisInt) + .query(` + SELECT + JourDate, JourTravaille, ReposQuotidienRespect, + ReposHebdomadaireRespect, CommentaireRepos, + DateSaisie, DateModification + FROM CompteRenduActivites + WHERE CollaborateurADId = @collaborateurId + AND Annee = @annee + AND Mois = @mois + ORDER BY JourDate + `); + // Récupérer les statistiques mensuelles + const mensuelResult = await pool.request() + .input('collaborateurId', sql.Int, collaborateurId) + .input('annee', sql.Int, anneeInt) + .input('mois', sql.Int, moisInt) + .query(` + SELECT + CAST(NbJoursTravailles AS DECIMAL(10,2)) as NbJoursTravailles, + NbJoursNonRespectsReposQuotidien, + NbJoursNonRespectsReposHebdo, + Verrouille + FROM CompteRenduMensuel + WHERE CollaborateurADId = @collaborateurId + AND Annee = @annee + AND Mois = @mois + `); + + const jours = joursResult.recordset; + const mensuel = mensuelResult.recordset.length > 0 ? mensuelResult.recordset[0] : null; + + res.json({ + success: true, + jours: jours, + mensuel: mensuel + }); + + } catch (error) { + console.error('Erreur /compte-rendu-activites:', error); + res.status(500).json({ + success: false, + message: 'Erreur serveur', + error: error.message + }); + } + }); + + + + + // GET /api/export-compte-rendu-pdf - Export PDF + // ================================================ + // ROUTE CORRIGÉE : GET /api/export-compte-rendu-pdf + // ================================================ + + // ================================================ + // ROUTE EXPORT PDF COMPTE-RENDU (DESIGN MODERNE) + // ================================================ + app.get('/export-compte-rendu-pdf', authenticateToken, async (req, res) => { + try { + if (!['Admin', 'RH'].includes(req.user.role)) { + return res.status(403).json({ error: 'Accès non autorisé' }); + } + + const { user_id, annee, mois } = req.query; + + if (!user_id || !annee || !mois) { + return res.status(400).json({ + success: false, + message: 'Paramètres manquants (user_id, annee, mois requis)' + }); + } + + console.log(`📄 Génération PDF moderne: User=${user_id}, ${mois}/${annee}`); + + // ✅ DÉFINIR LES HEADERS + res.setHeader('Content-Type', 'application/pdf'); + res.setHeader('Content-Disposition', + `attachment; filename="compte-rendu-${annee}-${String(mois).padStart(2, '0')}.pdf"`); + res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate'); + + // ✅ REQUÊTES SQL SERVER + const collabResult = await pool.request() + .input('userId', sql.Int, parseInt(user_id)) + .query(` + SELECT + ca.prenom, + ca.nom, + ca.email, + COALESCE(s.Nom, 'Non défini') as service + FROM CollaborateurAD ca + LEFT JOIN Services s ON ca.ServiceId = s.Id + WHERE ca.id = @userId + `); + + if (collabResult.recordset.length === 0) { + return res.status(404).json({ + success: false, + message: 'Collaborateur non trouvé' + }); + } + + const collab = collabResult.recordset[0]; + + // Récupérer le mensuel + const mensuelResult = await pool.request() + .input('userId', sql.Int, parseInt(user_id)) + .input('annee', sql.Int, parseInt(annee)) + .input('mois', sql.Int, parseInt(mois)) + .query(` + SELECT + CAST(NbJoursTravailles AS DECIMAL(10,2)) as NbJoursTravailles, + NbJoursNonRespectsReposQuotidien, + NbJoursNonRespectsReposHebdo, + Verrouille, + DateValidation + FROM CompteRenduMensuel +WHERE CollaborateurADId = @userId + AND Annee = @annee + AND Mois = @mois + `); + + const mensuel = mensuelResult.recordset.length > 0 + ? mensuelResult.recordset[0] + : null; + + // Récupérer les jours + const joursResult = await pool.request() + .input('userId', sql.Int, parseInt(user_id)) + .input('annee', sql.Int, parseInt(annee)) + .input('mois', sql.Int, parseInt(mois)) + .query(` + SELECT + JourDate, + JourTravaille, + ReposQuotidienRespect, + ReposHebdomadaireRespect, + CommentaireRepos + FROM CompteRenduActivites +WHERE CollaborateurADId = @userId + AND Annee = @annee + AND Mois = @mois + ORDER BY JourDate + `); + const jours = joursResult.recordset || []; + + console.log(`✅ Données: ${jours.length} jours`); + + // ✅ CRÉER LE PDF + const doc = new PDFDocument({ + bufferPages: true, + margin: 60, + size: 'A4' + }); + + doc.pipe(res); + + const monthNames = ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', + 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre']; + + // ===== TITRE PRINCIPAL (CYAN) ===== + doc.fontSize(24) + .fillColor('#06b6d4') // Cyan + .text('Compte-rendu d\'activités', { align: 'center' }); + + doc.moveDown(0.5); + + // ===== SOUS-TITRE (GRIS) ===== + doc.fontSize(16) + .fillColor('#6b7280') + .text(`${monthNames[parseInt(mois) - 1]} ${annee}`, { align: 'center' }); + + doc.moveDown(2); + + // ===== SECTION COLLABORATEUR ===== + doc.fontSize(12) + .fillColor('#000000') + .text('Collaborateur', { underline: true }); + + doc.moveDown(0.3); + + doc.fontSize(10) + .fillColor('#374151') + .text(`Nom: ${collab.prenom} ${collab.nom}`) + .text(`Email: ${collab.email}`) + .text(`Service: ${collab.service}`); + + doc.moveDown(1.5); + + // ===== ENCADRÉ RÉSUMÉ (FOND CYAN CLAIR) ===== + doc.fontSize(12) + .fillColor('#000000') + .text('Résumé du mois', { underline: true }); + + doc.moveDown(0.5); + + const resumeY = doc.y; + const resumeHeight = 80; + + // Rectangle avec fond cyan très clair et bordure cyan + doc.rect(60, resumeY, 475, resumeHeight) + .fillAndStroke('#e0f2fe', '#06b6d4'); + + // Contenu de l'encadré (2 colonnes) + const col1X = 75; + const col2X = 320; + const textY = resumeY + 15; + + doc.fontSize(10) + .fillColor('#374151'); + + // Colonne 1 + doc.text(`Jours travaillés: ${mensuel?.NbJoursTravailles || 0}`, col1X, textY); + doc.text(`Non-respect repos quotidien: ${mensuel?.NbJoursNonRespectsReposQuotidien || 0}`, col1X, textY + 20); + doc.text(`Non-respect repos hebdomadaire: ${mensuel?.NbJoursNonRespectsReposHebdo || 0}`, col1X, textY + 40); + + // Colonne 2 + const statutText = mensuel?.Verrouille ? 'Verrouillé' : 'Ouvert'; + doc.text(`Statut: ${statutText}`, col2X, textY); + + if (mensuel?.DateValidation) { + const dateVal = new Date(mensuel.DateValidation); + const dateStr = dateVal.toLocaleDateString('fr-FR'); + doc.text(`Date validation: ${dateStr}`, col2X, textY + 20); + } + + doc.y = resumeY + resumeHeight + 20; + doc.moveDown(1); + + // ===== TABLEAU DÉTAIL DES JOURS ===== + doc.fontSize(12) + .fillColor('#000000') + .text('Détail des jours', { align: 'center' }); + + doc.moveDown(0.5); + + const tableTop = doc.y; + const colWidth = { + date: 120, + travaille: 80, + repos: 120, + commentaire: 155 + }; + + let currentX = 60; + const tableHeaderY = tableTop; + + // En-têtes du tableau + doc.fontSize(9) + .fillColor('#6b7280'); + + doc.text('Date', currentX, tableHeaderY, { width: colWidth.date, align: 'left' }); + currentX += colWidth.date; + + doc.text('Travaillé', currentX, tableHeaderY, { width: colWidth.travaille, align: 'center' }); + currentX += colWidth.travaille; + + doc.text('Respect des repos', currentX, tableHeaderY, { width: colWidth.repos, align: 'center' }); + currentX += colWidth.repos; + + doc.text('Commentaire', currentX, tableHeaderY, { width: colWidth.commentaire, align: 'left' }); + + // Ligne de séparation sous les en-têtes + doc.moveTo(60, tableHeaderY + 15) + .lineTo(535, tableHeaderY + 15) + .stroke('#d1d5db'); + + let currentY = tableHeaderY + 25; + const rowHeight = 20; + + // Lignes du tableau + jours.forEach((jour, index) => { + // Vérifier nouvelle page + if (currentY > 720) { + doc.addPage(); + currentY = 60; + } + + // Alterner le fond des lignes + if (index % 2 === 0) { + doc.rect(60, currentY - 3, 475, rowHeight) + .fill('#f9fafb'); + } + + currentX = 60; + + doc.fontSize(8) + .fillColor('#374151'); + + // Date + const dateStr = new Date(jour.JourDate).toLocaleDateString('fr-FR'); + doc.text(dateStr, currentX, currentY, { width: colWidth.date, align: 'left' }); + currentX += colWidth.date; + + // Travaillé (coche simple) + const travailleText = jour.JourTravaille ? '✓' : '-'; + doc.text(travailleText, currentX, currentY, { width: colWidth.travaille, align: 'center' }); + currentX += colWidth.travaille; + + // Respect des repos (coche verte ou rien) + const respectOk = jour.ReposQuotidienRespect && jour.ReposHebdomadaireRespect; + if (respectOk) { + // Dessiner une coche verte circulaire + const centerX = currentX + colWidth.repos / 2; + const centerY = currentY + 5; + + doc.circle(centerX, centerY, 6) + .fillAndStroke('#22c55e', '#16a34a'); + + doc.fontSize(8) + .fillColor('#ffffff') + .text('✓', centerX - 3, centerY - 4, { width: 6, align: 'center' }); + } else { + doc.fillColor('#374151') + .text('-', currentX, currentY, { width: colWidth.repos, align: 'center' }); + } + currentX += colWidth.repos; + + // Commentaire + const comment = jour.CommentaireRepos || '-'; + doc.fillColor('#374151') + .text(comment.substring(0, 25), currentX, currentY, { width: colWidth.commentaire, align: 'left' }); + + currentY += rowHeight; + }); + + // Ligne de séparation finale + doc.moveTo(60, currentY) + .lineTo(535, currentY) + .stroke('#d1d5db'); + + // ===== PIED DE PAGE ===== + doc.moveDown(2); + doc.fontSize(8) + .fillColor('#9ca3af') + .text( + `Document généré automatiquement le ${new Date().toLocaleDateString('fr-FR')}`, + 60, + 750, + { align: 'center', width: 475 } + ); + + doc.end(); + + console.log(`✅ PDF moderne généré pour ${collab.nom}`); + + } catch (error) { + console.error('❌ Erreur export PDF:', error); + + if (!res.headersSent) { + res.status(500).json({ + success: false, + message: error.message || 'Erreur lors de l\'export PDF' + }); + } + } + }); + + + // GET /api/compte-rendu/cumul-annuel - Cumul annuel d'un collaborateur + app.get('/compte-rendu/cumul-annuel', authenticateToken, async (req, res) => { + try { + if (!['Admin', 'RH'].includes(req.user.role)) { + return res.status(403).json({ error: 'Accès non autorisé' }); + } + + const { user_id, annee } = req.query; + + if (!user_id || !annee) { + return res.status(400).json({ + success: false, + message: 'Paramètres manquants (user_id, annee)' + }); + } + + console.log(`📊 Calcul cumul annuel: User=${user_id}, Année=${annee}`); + + // Récupérer les données annuelles + const cumulResult = await pool.request() + .input('userId', sql.Int, parseInt(user_id)) + .input('annee', sql.Int, parseInt(annee)) + .query(` + SELECT + SUM(CAST(NbJoursTravailles AS DECIMAL(10,2))) as totalJoursTravailles, + SUM(NbJoursNonRespectsReposQuotidien) as totalReposQuotidien, + SUM(NbJoursNonRespectsReposHebdo) as totalReposHebdo, + COUNT(CASE WHEN Verrouille = 1 THEN 1 END) as moisValides, + COUNT(*) as moisSaisis +FROM CompteRenduMensuel +WHERE CollaborateurADId = @userId + AND Annee = @annee + `); + + const cumul = cumulResult.recordset[0]; + + res.json({ + success: true, + cumul: { + totalJoursTravailles: cumul.totalJoursTravailles || 0, + totalReposQuotidien: cumul.totalReposQuotidien || 0, + totalReposHebdo: cumul.totalReposHebdo || 0, + moisValides: cumul.moisValides || 0, + moisSaisis: cumul.moisSaisis || 0 + } + }); + + } catch (error) { + console.error('❌ Erreur cumul annuel:', error); + res.status(500).json({ + success: false, + message: error.message + }); + } + }); + + + + // ================================================ + // ROUTE POST /compteurs/saisir-jposf - VERSION AVEC GÉNÉRATION MANUELLE D'ID + // ================================================ + app.post('/compteurs/saisir-jposf', authenticateToken, async (req, res) => { + const transaction = new sql.Transaction(pool); + + try { + console.log('\n📥 ========== DÉBUT SAISIE JPOSF =========='); + + await transaction.begin(); + + if (!['Admin', 'RH'].includes(req.user.role)) { + await transaction.rollback(); + return res.status(403).json({ success: false, error: 'Accès non autorisé' }); + } + + const { collaborateur_id, date, periode_journee, commentaire } = req.body; + + if (!collaborateur_id || !date || !periode_journee) { + await transaction.rollback(); + return res.status(400).json({ success: false, message: 'Données manquantes' }); + } + + console.log('📋 Données:', { collaborateur_id, date, periode_journee }); + + // Récupérer le collaborateur + const collabResult = await transaction.request() + .input('id', sql.Int, parseInt(collaborateur_id)) + .query('SELECT id, prenom, nom, email, Actif FROM CollaborateurAD WHERE id = @id'); + + if (collabResult.recordset.length === 0) { + await transaction.rollback(); + return res.status(404).json({ success: false, message: 'Collaborateur non trouvé' }); + } + + const collab = collabResult.recordset[0]; + + if (collab.Actif === false || collab.Actif === 0) { + await transaction.rollback(); + return res.status(403).json({ success: false, message: 'Compte désactivé' }); + } + + const currentYear = new Date().getFullYear(); + const nombreJours = periode_journee === 'Journée entière' ? 1 : 0.5; + + // Récupérer les types de congé + const jposfTypeResult = await transaction.request() + .query("SELECT Id FROM TypeConge WHERE Nom = 'JPOSF'"); + + if (jposfTypeResult.recordset.length === 0) { + await transaction.rollback(); + return res.status(500).json({ success: false, message: 'Type JPOSF non trouvé' }); + } + + const recupTypeResult = await transaction.request() + .query("SELECT Id FROM TypeConge WHERE Nom = 'Récupération'"); + + if (recupTypeResult.recordset.length === 0) { + await transaction.rollback(); + return res.status(500).json({ success: false, message: 'Type Récupération non trouvé' }); + } + + const jposfTypeId = jposfTypeResult.recordset[0].Id; + const recupTypeId = recupTypeResult.recordset[0].Id; + + console.log(`📋 Types: JPOSF=${jposfTypeId}, Récupération=${recupTypeId}`); + + // ⭐ ÉTAPE 1: Mettre à jour le compteur Récupération + const compteurResult = await transaction.request() + .input('collaborateurId', sql.Int, parseInt(collaborateur_id)) + .input('recupTypeId', sql.Int, recupTypeId) + .input('currentYear', sql.Int, currentYear) + .query(` + SELECT Id, Total, Solde FROM CompteurConges + WHERE CollaborateurADId = @collaborateurId + AND TypeCongeId = @recupTypeId + AND Annee = @currentYear + `); + + let nouveauTotalRecup, nouveauSoldeRecup; + + if (compteurResult.recordset.length > 0) { + // ✅ Compteur existe : UPDATE + const compteur = compteurResult.recordset[0]; + nouveauTotalRecup = parseFloat(compteur.Total || 0) + nombreJours; + nouveauSoldeRecup = parseFloat(compteur.Solde || 0) + nombreJours; + + await transaction.request() + .input('total', sql.Decimal(10, 2), nouveauTotalRecup) + .input('solde', sql.Decimal(10, 2), nouveauSoldeRecup) + .input('id', sql.Int, compteur.Id) + .query('UPDATE CompteurConges SET Total = @total, Solde = @solde, DerniereMiseAJour = GETDATE() WHERE Id = @id'); + + console.log(`✅ Compteur Récup mis à jour: ${nouveauSoldeRecup}j`); + } else { + // ⭐ CORRECTION : Générer l'ID manuellement pour CompteurConges + const maxCompteurIdResult = await transaction.request() + .query('SELECT ISNULL(MAX(Id), 0) + 1 AS NextId FROM CompteurConges'); + + const compteurId = maxCompteurIdResult.recordset[0].NextId; + console.log(`📝 Nouvel ID CompteurConges: ${compteurId}`); + + nouveauTotalRecup = nombreJours; + nouveauSoldeRecup = nombreJours; + + // ✅ INSERT avec ID explicite + await transaction.request() + .input('id', sql.Int, compteurId) + .input('collaborateurId', sql.Int, parseInt(collaborateur_id)) + .input('recupTypeId', sql.Int, recupTypeId) + .input('currentYear', sql.Int, currentYear) + .input('total', sql.Decimal(10, 2), nouveauTotalRecup) + .input('solde', sql.Decimal(10, 2), nouveauSoldeRecup) + .query(` + INSERT INTO CompteurConges + (Id, CollaborateurADId, TypeCongeId, Annee, Total, Solde, SoldeReporte, DerniereMiseAJour) + VALUES (@id, @collaborateurId, @recupTypeId, @currentYear, @total, @solde, 0, GETDATE()) + `); + + console.log(`✅ Compteur Récup créé: ${nouveauSoldeRecup}j`); + } + + // ⭐ ÉTAPE 2: Générer l'ID pour DemandeConge + const maxDemandeIdResult = await transaction.request() + .query('SELECT ISNULL(MAX(Id), 0) + 1 AS NextId FROM DemandeConge'); + + const demandeId = maxDemandeIdResult.recordset[0].NextId; + console.log(`📝 Nouvel ID DemandeConge: ${demandeId}`); + + const commentaireFinal = commentaire?.trim() || `JPOSF - ${periode_journee}`; + + // Créer la demande JPOSF + await transaction.request() + .input('id', sql.Int, demandeId) + .input('collaborateurId', sql.Int, parseInt(collaborateur_id)) + .input('dateDebut', sql.Date, date) + .input('dateFin', sql.Date, date) + .input('commentaire', sql.NVarChar(500), commentaireFinal) + .input('totalJours', sql.Decimal(10, 2), nombreJours) + .input('validateurId', sql.Int, req.user.id) + .query(` + INSERT INTO DemandeConge + (Id, CollaborateurADId, DateDebut, DateFin, Statut, Commentaire, DateDemande, NombreJours, ValidateurADId, DateValidation) + VALUES (@id, @collaborateurId, @dateDebut, @dateFin, 'Validée', @commentaire, GETDATE(), @totalJours, @validateurId, GETDATE()) + `); + + console.log(`✅ DemandeConge créée`); + + // ⭐ ÉTAPE 3: Générer l'ID pour DemandeCongeType + const maxTypeIdResult = await transaction.request() + .query('SELECT ISNULL(MAX(Id), 0) + 1 AS NextId FROM DemandeCongeType'); + + const demandeCongeTypeId = maxTypeIdResult.recordset[0].NextId; + console.log(`📝 Nouvel ID DemandeCongeType: ${demandeCongeTypeId}`); + + // Lier avec le type JPOSF + await transaction.request() + .input('id', sql.Int, demandeCongeTypeId) + .input('demandeId', sql.Int, demandeId) + .input('typeId', sql.Int, jposfTypeId) + .input('nombreJours', sql.Decimal(10, 2), nombreJours) + .query('INSERT INTO DemandeCongeType (Id, DemandeCongeId, TypeCongeId, NombreJours) VALUES (@id, @demandeId, @typeId, @nombreJours)'); + + console.log(`✅ DemandeCongeType créée`); + + // Historique (avec gestion d'erreur si la table a IDENTITY ou non) + try { + const maxHistIdResult = await transaction.request() + .query('SELECT ISNULL(MAX(Id), 0) + 1 AS NextId FROM HistoriqueActions'); + + const histId = maxHistIdResult.recordset[0].NextId; + + await transaction.request() + .input('id', sql.Int, histId) + .input('userId', sql.Int, req.user.id) + .input('details', sql.NVarChar(500), `Saisie JPOSF - ${date} (${periode_journee}) : +${nombreJours}j pour ${collab.prenom} ${collab.nom}`) + .input('demandeId', sql.Int, demandeId) + .query(` + INSERT INTO HistoriqueActions (Id, CollaborateurADId, Action, Details, DemandeCongeId, DateAction) + VALUES (@id, @userId, 'Saisie JPOSF', @details, @demandeId, GETDATE()) + `); + } catch (histError) { + // Fallback sans ID si la table a IDENTITY + console.log('⚠️ Fallback historique sans ID...'); + await transaction.request() + .input('userId', sql.Int, req.user.id) + .input('details', sql.NVarChar(500), `Saisie JPOSF - ${date} (${periode_journee}) : +${nombreJours}j pour ${collab.prenom} ${collab.nom}`) + .input('demandeId', sql.Int, demandeId) + .query(` + INSERT INTO HistoriqueActions (CollaborateurADId, Action, Details, DemandeCongeId, DateAction) + VALUES (@userId, 'Saisie JPOSF', @details, @demandeId, GETDATE()) + `); + } + + console.log(`✅ Historique créé`); + + await transaction.commit(); + console.log('✅ Transaction COMMIT'); + + // Notifications SSE + // ========== Notifications SSE ========== + try { + // ⭐ CORRECTION: Envoyer les détails complets de la demande pour affichage calendrier + notifyClients({ + type: 'demande-validated', + demandeId: demandeId, + statut: 'Validée', + typeConge: 'JPOSF', + couleurHex: '#d946ef', + date: date, + dateDebut: date, + dateFin: date, + periode: periode_journee, + periodeJournee: periode_journee, + nombreJours: nombreJours, + collaborateurId: parseInt(collaborateur_id), + timestamp: new Date().toISOString() + }, parseInt(collaborateur_id)); + + notifyClients({ + type: 'compteur-updated', + collaborateurId: parseInt(collaborateur_id), + typeConge: 'Récupération', + action: 'saisie_jposf_rh', + nouveauSolde: nouveauSoldeRecup, + timestamp: new Date().toISOString() + }, parseInt(collaborateur_id)); + + console.log('✅ Notifications SSE envoyées avec détails complets pour calendrier'); + } catch (sseError) { + console.error('⚠️ Erreur SSE (non bloquant):', sseError.message); + } + // Webhooks + try { + if (typeof webhookManager !== 'undefined' && WEBHOOKS && EVENTS) { + await webhookManager.sendWebhook(WEBHOOKS.COLLABORATEURS_URL, EVENTS.DEMANDE_VALIDATED, { + demandeId, statut: 'Validée', typeConge: 'JPOSF', collaborateurId: parseInt(collaborateur_id) + }); + await webhookManager.sendWebhook(WEBHOOKS.COLLABORATEURS_URL, EVENTS.COMPTEUR_UPDATED, { + collaborateurId: parseInt(collaborateur_id), typeConge: 'Récupération' + }); + } + } catch (webhookError) { + console.error('⚠️ Erreur webhook:', webhookError.message); + } + + // Email + try { + const accessToken = await getGraphToken(); + if (accessToken && collab.email) { + const subject = '📅 JPOSF enregistré'; + const body = ` +
+
+

📅 JPOSF enregistré

+
+
+

Bonjour ${collab.prenom} ${collab.nom},

+
+

Date : ${new Date(date).toLocaleDateString('fr-FR')}

+

Période : ${periode_journee}

+

Jours ajoutés : ${nombreJours}j

+
+

+${nombreJours}j ajoutés au compteur Récupération

+
+
`; + await sendMailGraph(accessToken, 'gtanoreply@ensup.eu', collab.email, subject, body); + } + } catch (emailError) { + console.error('⚠️ Erreur email:', emailError.message); + } + + console.log('✅ ========== JPOSF ENREGISTRÉ ==========\n'); + + return res.json({ + success: true, + message: 'JPOSF enregistré avec succès', + collaborateur: `${collab.prenom} ${collab.nom}`, + date: date, + periode: periode_journee, + jours_ajoutes: nombreJours, + nouveau_solde_recup: nouveauSoldeRecup, + demandeId: demandeId + }); + + } catch (error) { + console.error('❌ ERREUR JPOSF:', error.message); + console.error('Stack:', error.stack); + try { await transaction.rollback(); } catch (e) { } + + return res.status(500).json({ + success: false, + message: 'Erreur serveur', + error: error.message + }); + } + }); + + // ⭐ ROUTE GET /api/jposf - Ajoutez cette route dans server.js + app.get('/jposf', authenticateToken, async (req, res) => { + try { + const currentYear = new Date().getFullYear(); + + console.log('📊 GET /api/jposf - Année:', currentYear); + + const result = await pool.request() + .input('currentYear', sql.Int, currentYear) + .query(` + SELECT + MIN(dc.Id) AS Id, + dc.CollaborateurADId, + CONCAT(ca.prenom, ' ', ca.nom) AS employe, + ca.email, + COALESCE(s.Nom, 'Sans service') AS service, + COUNT(DISTINCT dc.Id) AS totalEvenements, + SUM(dct.NombreJours) AS solde + FROM DemandeConge dc + JOIN CollaborateurAD ca ON dc.CollaborateurADId = ca.id + LEFT JOIN Services s ON ca.ServiceId = s.Id + JOIN DemandeCongeType dct ON dc.Id = dct.DemandeCongeId + JOIN TypeConge tc ON dct.TypeCongeId = tc.Id + WHERE tc.Nom = 'JPOSF' + AND dc.Statut = 'Validée' + AND YEAR(dc.DateDebut) = @currentYear + AND (ca.Actif = 1 OR ca.Actif IS NULL) + GROUP BY + dc.CollaborateurADId, + ca.prenom, + ca.nom, + ca.email, + s.Nom + ORDER BY ca.nom, ca.prenom + `); + + console.log('📊 JPOSF trouvés:', result.recordset.length); + res.json(result.recordset); + + } catch (error) { + console.error('❌ Erreur GET /api/jposf:', error); + res.status(500).json({ error: error.message }); + } + }); + + + + // ROUTE GET /demandes avec pagination et filtres + + // Dans server.js, remplacer la route GET /demandes + app.get('/demandes', authenticateToken, async (req, res) => { + try { + const { limit, statut, serviceid, campusid } = req.query; + const request = pool.request(); + + // Filtres WHERE + let whereClause = "WHERE (ca.Actif = 1 OR ca.Actif IS NULL)"; + + if (statut && statut !== 'all') { + whereClause += " AND dc.Statut = @statut"; + request.input('statut', sql.VarChar, statut); + } + + if (serviceid && serviceid !== 'all') { + whereClause += " AND ca.ServiceId = @serviceid"; + request.input('serviceid', sql.Int, parseInt(serviceid)); + } + + if (campusid && campusid !== 'all') { + whereClause += " AND ca.CampusId = @campusid"; + request.input('campusid', sql.Int, parseInt(campusid)); + } + + let topClause = ''; + if (limit) { + topClause = `TOP ${parseInt(limit)}`; + } + + // Requête principale + const query = ` + SELECT ${topClause} + dc.Id, + dc.CollaborateurADId, + dc.DateDebut, + dc.DateFin, + dc.Statut, + dc.Commentaire, + dc.DateDemande, + dc.NombreJours, + dc.ValidateurADId, + dc.DateValidation, + dc.CommentaireValidation, + CONCAT(ca.prenom, ' ', ca.nom) AS nomEmploye, + ca.email AS emailEmploye, + s.Nom AS service, + c.Nom AS campus, + -- Agréger les types de congé (pour affichage simple) + STUFF(( + SELECT DISTINCT ', ' + tc2.Nom + FROM DemandeCongeType dct2 + JOIN TypeConge tc2 ON dct2.TypeCongeId = tc2.Id + WHERE dct2.DemandeCongeId = dc.Id + ORDER BY ', ' + tc2.Nom + FOR XML PATH('') + ), 1, 2, '') AS typesConge + FROM DemandeConge dc + JOIN CollaborateurAD ca ON dc.CollaborateurADId = ca.id + LEFT JOIN Services s ON ca.ServiceId = s.Id + LEFT JOIN Campus c ON ca.CampusId = c.Id + ${whereClause} + ORDER BY dc.DateDemande DESC + `; + + const result = await request.query(query); + const demandes = result.recordset; + + // Récupérer les détails de tous les types de congé + const detailsQuery = ` + SELECT + dct.DemandeCongeId, + tc.Nom AS typeConge, + dct.NombreJours AS nombreJours, + dct.PeriodeJournee, + dc.DateDebut AS dateDebut, + dc.DateFin AS dateFin + FROM DemandeCongeType dct + JOIN TypeConge tc ON dct.TypeCongeId = tc.Id + JOIN DemandeConge dc ON dct.DemandeCongeId = dc.Id + WHERE dct.DemandeCongeId IN (${demandes.map(d => d.Id).join(',') || '0'}) + ORDER BY dct.DemandeCongeId, tc.Nom + `; + + const detailsResult = await pool.request().query(detailsQuery); + + // Grouper les détails par demande + const detailsParDemande = {}; + detailsResult.recordset.forEach(detail => { + if (!detailsParDemande[detail.DemandeCongeId]) { + detailsParDemande[detail.DemandeCongeId] = []; + } + detailsParDemande[detail.DemandeCongeId].push({ + typeConge: detail.typeConge, + nombreJours: parseFloat(detail.nombreJours), + dateDebut: detail.dateDebut, + dateFin: detail.dateFin + }); + }); + + // Enrichir chaque demande avec ses détails + const demandesEnrichies = demandes.map(demande => ({ + Id: demande.Id, + nomEmploye: demande.nomEmploye, + emailEmploye: demande.emailEmploye, + service: demande.service, + Campus: demande.campus, + typesConge: demande.typesConge || 'Non spécifié', + detailsConges: detailsParDemande[demande.Id] || [], // ✅ Toujours présent + DateDebut: demande.DateDebut, + DateFin: demande.DateFin, + NombreJours: demande.NombreJours, + Statut: demande.Statut, + Commentaire: demande.Commentaire, + DateDemande: demande.DateDemande + })); + + console.log(`${demandesEnrichies.length} demandes avec détails`); + res.json(demandesEnrichies); + + } catch (error) { + console.error('Erreur GET /demandes:', error); + res.status(500).json({ error: error.message }); + } + }); + + + app.get('/demandes/:id', authenticateToken, async (req, res) => { + try { + const result = await pool.request() + .input('id', sql.Int, req.params.id) + .query(` + SELECT + dc.*, + CONCAT(ca.prenom, ' ', ca.nom) AS nomEmploye, + ca.email AS emailEmploye, + ca.Actif AS employeActif, + ca.DateSortie AS employeDateSortie, + s.Nom AS service, + s.Id AS serviceId, + CONCAT(v.prenom, ' ', v.nom) AS nomValidateur, + v.email AS emailValidateur + FROM DemandeConge dc + JOIN CollaborateurAD ca ON dc.CollaborateurADId = ca.id + LEFT JOIN Services s ON ca.ServiceId = s.Id + LEFT JOIN CollaborateurAD v ON dc.ValidateurADId = v.id + WHERE dc.Id = @id + `); + + const demandes = result.recordset; + + if (demandes.length === 0) { + return res.status(404).json({ error: 'Demande non trouvée' }); + } + + const typesResult = await pool.request() + .input('id', sql.Int, req.params.id) + .query(` + SELECT + dct.TypeCongeId as typeId, + dct.NombreJours as nombreJours, + tc.Nom AS nom, + tc.CouleurHex AS couleur + FROM DemandeCongeType dct + JOIN TypeConge tc ON dct.TypeCongeId = tc.Id + WHERE dct.DemandeCongeId = @id + `); + + const types = typesResult.recordset; + + res.json({ + ...demandes[0], + typesConge: types + }); + } catch (error) { + console.error('❌ Erreur détails demande:', error); + res.status(500).json({ error: error.message }); + } + }); + + + // ================================================ + // ROUTE PUT /demandes/:id - AVEC VALIDATION AUTOMATIQUE + // Remplacer la route existante dans server.js (ligne ~1438) + // ================================================ + app.put('/demandes/:id', authenticateToken, async (req, res) => { + const transaction = new sql.Transaction(pool); + try { + await transaction.begin(); + + const { id } = req.params; + const { dateDebut, dateFin, typesConge, commentaire } = req.body; + + console.log('📝 Modification demande:', { id, dateDebut, dateFin, typesConge }); + + const demandeResult = await transaction.request() + .input('id', sql.Int, id) + .query('SELECT Statut, CollaborateurADId FROM DemandeConge WHERE Id = @id'); + + const demande = demandeResult.recordset; + + if (demande.length === 0) { + await transaction.rollback(); + return res.status(404).json({ error: 'Demande non trouvée' }); + } + + const ancienStatut = demande[0].Statut; + const collaborateurId = demande[0].CollaborateurADId; + + const rolesAutorises = ['Admin', 'RH', 'Validateur']; + const estAuteur = collaborateurId === req.user.id; + const estRoleAutorise = rolesAutorises.includes(req.user.role); + + if (!estAuteur && !estRoleAutorise) { + await transaction.rollback(); + return res.status(403).json({ + error: 'Accès non autorisé', + details: 'Seuls les Admin, RH, Validateurs ou l\'auteur peuvent modifier cette demande' + }); + } + + // ⭐ RESTAURER LES COMPTEURS SI LA DEMANDE ÉTAIT VALIDÉE + if (ancienStatut === 'Validée') { + console.log('⚠️ Modification d\'une demande validée - restauration des compteurs'); + + const ancienTypesResult = await transaction.request() + .input('id', sql.Int, id) + .query('SELECT TypeCongeId, NombreJours FROM DemandeCongeType WHERE DemandeCongeId = @id'); + + const ancienTypes = ancienTypesResult.recordset; + const currentYear = new Date().getFullYear(); + + for (const type of ancienTypes) { + // ⭐ Parser correctement les décimales + const nombreJoursARestaurer = parseFloat(type.NombreJours); + + // Restaurer avec priorité CP N-1 puis CP N + if (type.TypeCongeId === 1) { + console.log(`\n🔄 Restauration CP - ${nombreJoursARestaurer}j à restaurer`); + let joursRestantsARestaurer = nombreJoursARestaurer; + + // D'abord : Restaurer dans CP N + const cpNResult = await transaction.request() + .input('collaborateurId', sql.Int, collaborateurId) + .input('typeId', sql.Int, type.TypeCongeId) + .input('currentYear', sql.Int, currentYear) + .query(` + SELECT Id, Solde, Total + FROM CompteurConges + WHERE CollaborateurADId = @collaborateurId + AND TypeCongeId = @typeId + AND Annee = @currentYear + `); + + if (cpNResult.recordset.length > 0) { + const compteurN = cpNResult.recordset[0]; + const soldeN = parseFloat(compteurN.Solde || 0); + const totalN = parseFloat(compteurN.Total || 0); + const aRestaurerN = Math.min(joursRestantsARestaurer, totalN - soldeN); + + if (aRestaurerN > 0) { + await transaction.request() + .input('restauration', sql.Decimal(10, 2), aRestaurerN) + .input('compteurId', sql.Int, compteurN.Id) + .query(` + UPDATE CompteurConges + SET Solde = Solde + @restauration, + DerniereMiseAJour = GETDATE() + WHERE Id = @compteurId + `); + + console.log(` ✅ CP N (${currentYear}) : +${aRestaurerN}j restaurés`); + joursRestantsARestaurer -= aRestaurerN; + } + } + + // Ensuite : Si besoin, restaurer dans CP N-1 + if (joursRestantsARestaurer > 0) { + console.log(` 🔄 Il reste ${joursRestantsARestaurer}j, restauration dans CP N-1`); + + const cpN1Result = await transaction.request() + .input('collaborateurId', sql.Int, collaborateurId) + .input('typeId', sql.Int, type.TypeCongeId) + .input('previousYear', sql.Int, currentYear - 1) + .query(` + SELECT Id, Solde, Total + FROM CompteurConges + WHERE CollaborateurADId = @collaborateurId + AND TypeCongeId = @typeId + AND Annee = @previousYear + `); + + if (cpN1Result.recordset.length > 0) { + const compteurN1 = cpN1Result.recordset[0]; + const soldeN1 = parseFloat(compteurN1.Solde || 0); + const totalN1 = parseFloat(compteurN1.Total || 0); + const aRestaurerN1 = Math.min(joursRestantsARestaurer, totalN1 - soldeN1); + + if (aRestaurerN1 > 0) { + await transaction.request() + .input('restauration', sql.Decimal(10, 2), aRestaurerN1) + .input('compteurId', sql.Int, compteurN1.Id) + .query(` + UPDATE CompteurConges + SET Solde = Solde + @restauration, + DerniereMiseAJour = GETDATE() + WHERE Id = @compteurId + `); + + console.log(` ✅ CP N-1 (${currentYear - 1}) : +${aRestaurerN1}j restaurés`); + } + } + } + } else { + // Autres types : restauration simple + await transaction.request() + .input('nombreJours', sql.Decimal(10, 2), nombreJoursARestaurer) + .input('collaborateurId', sql.Int, collaborateurId) + .input('typeCongeId', sql.Int, type.TypeCongeId) + .query(` + UPDATE CompteurConges + SET Solde = Solde + @nombreJours, + DerniereMiseAJour = GETDATE() + WHERE CollaborateurADId = @collaborateurId + AND TypeCongeId = @typeCongeId + AND Annee = YEAR(GETDATE()) + `); + + console.log(` ✅ Type ${type.TypeCongeId} : +${nombreJoursARestaurer}j restaurés`); + } + } + } + + // ⭐ Calculer le total avec support des décimales + const totalJours = typesConge.reduce((sum, type) => { + return sum + parseFloat(type.nombreJours || 0); + }, 0); + + const nouveauStatut = 'Validée'; + + console.log(`📝 Mise à jour demande ${id} - Nouveau statut: ${nouveauStatut}`); + + // Mise à jour de la demande + await transaction.request() + .input('dateDebut', sql.Date, dateDebut) + .input('dateFin', sql.Date, dateFin) + .input('commentaire', sql.NVarChar, commentaire || '') + .input('totalJours', sql.Decimal(10, 2), totalJours) + .input('nouveauStatut', sql.VarChar, nouveauStatut) + .input('validateurId', sql.Int, req.user.id) + .input('id', sql.Int, id) + .query(` + UPDATE DemandeConge + SET DateDebut = @dateDebut, + DateFin = @dateFin, + Commentaire = @commentaire, + NombreJours = @totalJours, + Statut = @nouveauStatut, + ValidateurADId = @validateurId, + DateValidation = GETDATE(), + CommentaireValidation = 'Validation automatique après modification' + WHERE Id = @id + `); + + // Supprimer les anciens types + await transaction.request() + .input('id', sql.Int, id) + .query('DELETE FROM DemandeCongeType WHERE DemandeCongeId = @id'); + + console.log('✅ Anciens types supprimés'); + + // Recréer les types avec génération manuelle d'ID + for (const type of typesConge) { + const maxTypeIdResult = await transaction.request() + .query('SELECT ISNULL(MAX(Id), 0) + 1 AS NextId FROM DemandeCongeType'); + + const typeId = maxTypeIdResult.recordset[0].NextId; + + // ⭐ Parser correctement le nombre de jours + const nombreJours = parseFloat(type.nombreJours || 0); + + await transaction.request() + .input('id', sql.Int, typeId) + .input('demandeId', sql.Int, id) + .input('typeId', sql.Int, type.typeId) + .input('nombreJours', sql.Decimal(10, 2), nombreJours) + .query('INSERT INTO DemandeCongeType (Id, DemandeCongeId, TypeCongeId, NombreJours) VALUES (@id, @demandeId, @typeId, @nombreJours)'); + + console.log(`✅ Type ${type.typeId} recréé avec ID ${typeId} - ${nombreJours}j`); + } + + // ⭐ DÉDUIRE LES NOUVEAUX COMPTEURS (AVEC PRIORITÉ CP N-1 puis CP N) + console.log('💼 Déduction des nouveaux compteurs avec priorité CP N-1...'); + + const currentYear = new Date().getFullYear(); + + for (const type of typesConge) { + const nombreJours = parseFloat(type.nombreJours || 0); + + // SI C'EST UN TYPE CP (ID = 1), GÉRER LA PRIORITÉ CP N-1 PUIS CP N + if (type.typeId === 1) { + console.log(`\n🔄 Traitement CP - ${nombreJours}j à déduire`); + let joursRestantsADeduire = nombreJours; + + // 1️⃣ D'ABORD : Pioche dans CP N-1 + const cpN1Result = await transaction.request() + .input('collaborateurId', sql.Int, collaborateurId) + .input('typeId', sql.Int, type.typeId) + .input('previousYear', sql.Int, currentYear - 1) + .query(` + SELECT Id, Solde + FROM CompteurConges + WHERE CollaborateurADId = @collaborateurId + AND TypeCongeId = @typeId + AND Annee = @previousYear + `); + + if (cpN1Result.recordset.length > 0) { + const compteurN1 = cpN1Result.recordset[0]; + const soldeN1 = parseFloat(compteurN1.Solde || 0); + + if (soldeN1 > 0) { + const aDeduireN1 = Math.min(soldeN1, joursRestantsADeduire); + + await transaction.request() + .input('deduction', sql.Decimal(10, 2), aDeduireN1) + .input('compteurId', sql.Int, compteurN1.Id) + .query(` + UPDATE CompteurConges + SET Solde = Solde - @deduction, + DerniereMiseAJour = GETDATE() + WHERE Id = @compteurId + `); + + console.log(` ✅ CP N-1 (${currentYear - 1}) : -${aDeduireN1}j`); + joursRestantsADeduire -= aDeduireN1; + } + } + + // 2️⃣ ENSUITE : S'il reste des jours, pioche dans CP N + if (joursRestantsADeduire > 0) { + console.log(` 🔄 Il reste ${joursRestantsADeduire}j, pioche dans CP N`); + + const cpNResult = await transaction.request() + .input('collaborateurId', sql.Int, collaborateurId) + .input('typeId', sql.Int, type.typeId) + .input('currentYear', sql.Int, currentYear) + .query(` + SELECT Id, Solde + FROM CompteurConges + WHERE CollaborateurADId = @collaborateurId + AND TypeCongeId = @typeId + AND Annee = @currentYear + `); + + if (cpNResult.recordset.length > 0) { + const compteurN = cpNResult.recordset[0]; + + await transaction.request() + .input('deduction', sql.Decimal(10, 2), joursRestantsADeduire) + .input('compteurId', sql.Int, compteurN.Id) + .query(` + UPDATE CompteurConges + SET Solde = Solde - @deduction, + DerniereMiseAJour = GETDATE() + WHERE Id = @compteurId + `); + + console.log(` ✅ CP N (${currentYear}) : -${joursRestantsADeduire}j`); + } + } + } else { + // Pour les autres types (RTT, Récupération, etc.) + await transaction.request() + .input('nombreJours', sql.Decimal(10, 2), nombreJours) + .input('collaborateurId', sql.Int, collaborateurId) + .input('typeId', sql.Int, type.typeId) + .query(` + UPDATE CompteurConges + SET Solde = Solde - @nombreJours, + DerniereMiseAJour = GETDATE() + WHERE CollaborateurADId = @collaborateurId + AND TypeCongeId = @typeId + AND Annee = YEAR(GETDATE()) + `); + + console.log(` ✅ Type ${type.typeId} : -${nombreJours}j`); + } + } + + const actionDetails = `Modification et validation automatique par ${req.user.role}`; + + // Historique + try { + const maxHistIdResult = await transaction.request() + .query('SELECT ISNULL(MAX(Id), 0) + 1 AS NextId FROM HistoriqueActions'); + + const histId = maxHistIdResult.recordset[0].NextId; + + await transaction.request() + .input('id', sql.Int, histId) + .input('userId', sql.Int, req.user.id) + .input('details', sql.NVarChar, actionDetails) + .input('demandeId', sql.Int, id) + .query(` + INSERT INTO HistoriqueActions (Id, CollaborateurADId, Action, Details, DemandeCongeId, DateAction) + VALUES (@id, @userId, 'Modification et validation', @details, @demandeId, GETDATE()) + `); + } catch (histError) { + console.log('⚠️ Fallback historique sans ID...'); + await transaction.request() + .input('userId', sql.Int, req.user.id) + .input('details', sql.NVarChar, actionDetails) + .input('demandeId', sql.Int, id) + .query(` + INSERT INTO HistoriqueActions (CollaborateurADId, Action, Details, DemandeCongeId, DateAction) + VALUES (@userId, 'Modification et validation', @details, @demandeId, GETDATE()) + `); + } + + await transaction.commit(); + console.log('✅ Modification et validation terminées'); + + // Notifications SSE + notifyClients({ + type: 'demande-validated', + demandeId: parseInt(id), + statut: 'Validée', + collaborateurId: collaborateurId, + timestamp: new Date().toISOString() + }, collaborateurId); + + notifyClients({ + type: 'demande-updated', + demandeId: parseInt(id), + action: 'modification', + nouveauStatut: nouveauStatut, + ancienStatut: ancienStatut, + timestamp: new Date().toISOString() + }, collaborateurId); + + notifyClients({ + type: 'demande-list-updated', + action: 'modification', + demandeId: parseInt(id), + timestamp: new Date().toISOString() + }); + + // Webhooks + try { + await webhookManager.sendWebhook( + WEBHOOKS.COLLABORATEURS_URL, + EVENTS.DEMANDE_VALIDATED, + { + demandeId: parseInt(id), + statut: 'Validée', + collaborateurId: collaborateurId, + validateurId: req.user.id + } + ); + + await webhookManager.sendWebhook( + WEBHOOKS.COLLABORATEURS_URL, + EVENTS.COMPTEUR_UPDATED, + { collaborateurId: collaborateurId } + ); + } catch (webhookError) { + console.error('❌ Erreur envoi webhook (non bloquant):', webhookError.message); + } + + res.json({ + message: 'Demande modifiée et validée automatiquement', + nouveauStatut: nouveauStatut, + ancienStatut: ancienStatut + }); + + console.log(`✅ Demande ${id} modifiée et validée par ${req.user.role}`); + + } catch (error) { + await transaction.rollback(); + console.error('❌ Erreur modification demande:', error); + res.status(500).json({ error: error.message }); + } + }); + + app.delete('/demandes/:id', authenticateToken, async (req, res) => { + const transaction = new sql.Transaction(pool); + try { + await transaction.begin(); + + const { id } = req.params; + + const demandeResult = await transaction.request() + .input('id', sql.Int, id) + .query('SELECT Statut, CollaborateurADId FROM DemandeConge WHERE Id = @id'); + + const demande = demandeResult.recordset; + + if (demande.length === 0) { + await transaction.rollback(); + return res.status(404).json({ error: 'Demande non trouvée' }); + } + + const collaborateurId = demande[0].CollaborateurADId; + + const rolesAutorises = ['Admin', 'RH', 'Validateur']; + const estAuteur = collaborateurId === req.user.id; + const estRoleAutorise = rolesAutorises.includes(req.user.role); + + if (!estAuteur && !estRoleAutorise) { + await transaction.rollback(); + return res.status(403).json({ + error: 'Accès non autorisé', + details: 'Seuls les Admin, RH, Validateurs ou l\'auteur peuvent supprimer cette demande' + }); + } + + // Supprimer d'abord les entrées d'historique + await transaction.request() + .input('id', sql.Int, id) + .query('DELETE FROM HistoriqueActions WHERE DemandeCongeId = @id'); + + // Supprimer les types de congé + await transaction.request() + .input('id', sql.Int, id) + .query('DELETE FROM DemandeCongeType WHERE DemandeCongeId = @id'); + + // Supprimer la demande + await transaction.request() + .input('id', sql.Int, id) + .query('DELETE FROM DemandeConge WHERE Id = @id'); + + await transaction.commit(); + + notifyClients({ + type: 'demande-deleted', + demandeId: parseInt(id), + collaborateurId: collaborateurId, + timestamp: new Date().toISOString() + }); + + try { + await webhookManager.sendWebhook( + WEBHOOKS.COLLABORATEURS_URL, + EVENTS.DEMANDE_DELETED, + { + demandeId: parseInt(id), + collaborateurId: collaborateurId + } + ); + } catch (webhookError) { + console.error('❌ Erreur envoi webhook (non bloquant):', webhookError.message); + } + + console.log(`✅ Demande ${id} supprimée par ${req.user.role}`); + res.json({ message: 'Demande supprimée avec succès' }); + } catch (error) { + await transaction.rollback(); + console.error('❌ Erreur suppression demande:', error); + res.status(500).json({ error: error.message }); + } + }); + + // ⭐ ROUTE VALIDATION AVEC WEBHOOKS + // ⭐ ROUTE VALIDATION AVEC WEBHOOKS - CORRIGÉE + app.put('/demandes/:id/valider', authenticateToken, async (req, res) => { + const transaction = new sql.Transaction(pool); + try { + await transaction.begin(); + + const { id } = req.params; + const { statut, commentaire } = req.body; + + console.log('\n🔍 === DÉBUT VALIDATION DEMANDE ==='); + + const demandeInfoResult = await transaction.request() + .input('id', sql.Int, id) + .query(` + SELECT + dc.Id, + dc.CollaborateurADId, + dc.DateDebut, + dc.DateFin, + dc.NombreJours, + ca.prenom, + ca.nom, + ca.email as collaborateur_email, + ca.Actif, + STRING_AGG(tc.Nom, ', ') as TypeConge + FROM DemandeConge dc + JOIN CollaborateurAD ca ON dc.CollaborateurADId = ca.id + LEFT JOIN DemandeCongeType dct ON dc.Id = dct.DemandeCongeId + LEFT JOIN TypeConge tc ON dct.TypeCongeId = tc.Id + WHERE dc.Id = @id + GROUP BY dc.Id, dc.CollaborateurADId, dc.DateDebut, dc.DateFin, dc.NombreJours, + ca.prenom, ca.nom, ca.email, ca.Actif + `); + + const demandeInfo = demandeInfoResult.recordset; + + if (demandeInfo.length === 0) { + await transaction.rollback(); + return res.status(404).json({ error: 'Demande non trouvée' }); + } + + const demande = demandeInfo[0]; + const collaborateurId = demande.CollaborateurADId; + + await transaction.request() + .input('statut', sql.VarChar, statut) + .input('commentaire', sql.NVarChar, commentaire || '') + .input('validateurId', sql.Int, req.user.id) + .input('id', sql.Int, id) + .query(` + UPDATE DemandeConge + SET Statut = @statut, + CommentaireValidation = @commentaire, + ValidateurADId = @validateurId, + DateValidation = GETDATE() + WHERE Id = @id + `); + + if (statut === 'Validée') { + const typesResult = await transaction.request() + .input('id', sql.Int, id) + .query('SELECT TypeCongeId, NombreJours FROM DemandeCongeType WHERE DemandeCongeId = @id'); + + const types = typesResult.recordset; + const currentYear = new Date().getFullYear(); + + for (const type of types) { + await transaction.request() + .input('nombreJours', sql.Decimal(10, 2), type.NombreJours) + .input('collaborateurId', sql.Int, collaborateurId) + .input('typeCongeId', sql.Int, type.TypeCongeId) + .query(` + UPDATE CompteurConges + SET Solde = Solde - @nombreJours + WHERE CollaborateurADId = @collaborateurId + AND TypeCongeId = @typeCongeId + AND Annee = YEAR(GETDATE()) + `); + + // ✅ AJOUT : Insérer dans DeductionDetails pour cohérence avec /annuler + await transaction.request() + .input('demandeId', sql.Int, id) + .input('typeId', sql.Int, type.TypeCongeId) + .input('annee', sql.Int, currentYear) + .input('jours', sql.Decimal(10, 2), type.NombreJours) + .query(` + INSERT INTO DeductionDetails + (DemandeCongeId, TypeCongeId, Annee, TypeDeduction, JoursUtilises) + VALUES (@demandeId, @typeId, @annee, 'Année N', @jours) + `); + } + } + + // ⭐ CORRECTION: Génération manuelle de l'ID pour HistoriqueActions + try { + const maxHistIdResult = await transaction.request() + .query('SELECT ISNULL(MAX(Id), 0) + 1 AS NextId FROM HistoriqueActions'); + + const histId = maxHistIdResult.recordset[0].NextId; + + await transaction.request() + .input('id', sql.Int, histId) + .input('userId', sql.Int, req.user.id) + .input('action', sql.VarChar, statut === 'Validée' ? 'Validation congé' : 'Refus congé') + .input('commentaire', sql.NVarChar, commentaire || '') + .input('demandeId', sql.Int, id) + .query(` + INSERT INTO HistoriqueActions (Id, CollaborateurADId, Action, Details, DemandeCongeId, DateAction) + VALUES (@id, @userId, @action, @commentaire, @demandeId, GETDATE()) + `); + + console.log(`✅ Historique validation créé avec ID: ${histId}`); + } catch (histError) { + console.error('⚠️ Erreur historique (non bloquant):', histError.message); + } + + await transaction.commit(); + + // ⭐ ENVOI EMAIL + const accessToken = await getGraphToken(); + if (accessToken && demande.collaborateur_email) { + const fromEmail = 'noreply@ensup.eu'; + const collaborateurNom = `${demande.prenom} ${demande.nom}`; + const validateurNom = req.user.prenom && req.user.nom + ? `${req.user.prenom} ${req.user.nom}` + : 'l\'équipe RH'; + + const dateDebut = new Date(demande.DateDebut).toLocaleDateString('fr-FR'); + const dateFin = new Date(demande.DateFin).toLocaleDateString('fr-FR'); + const datesPeriode = dateDebut === dateFin ? dateDebut : `du ${dateDebut} au ${dateFin}`; + + const subject = statut === 'Validée' + ? '✅ Votre demande de congé a été approuvée' + : '❌ Votre demande de congé a été refusée'; + + const body = statut === 'Validée' + ? `
+
+

✅ Demande approuvée

+
+
+

Bonjour ${collaborateurNom},

+

Votre demande de congé a été approuvée par ${validateurNom}.

+
+

Type : ${demande.TypeConge}

+

Période : ${datesPeriode}

+

Durée : ${demande.NombreJours} jour(s)

+ ${commentaire ? `

Commentaire : ${commentaire}

` : ''} +
+
+
` + : `
+
+

❌ Demande refusée

+
+
+

Bonjour ${collaborateurNom},

+

Votre demande de congé a été refusée par ${validateurNom}.

+
+

Type : ${demande.TypeConge}

+

Période : ${datesPeriode}

+

Durée : ${demande.NombreJours} jour(s)

+ ${commentaire ? `

Motif du refus : ${commentaire}

` : ''} +
+
+
`; + + await sendMailGraph(accessToken, fromEmail, demande.collaborateur_email, subject, body); + } + + notifyClients({ + type: 'demande-validated', + demandeId: parseInt(id), + statut: statut, + collaborateurId: collaborateurId, + timestamp: new Date().toISOString() + }, collaborateurId); + + notifyClients({ + type: 'demande-list-updated', + action: 'validation', + demandeId: parseInt(id), + timestamp: new Date().toISOString() + }); + + try { + await webhookManager.sendWebhook( + WEBHOOKS.COLLABORATEURS_URL, + EVENTS.DEMANDE_VALIDATED, + { + demandeId: parseInt(id), + statut: statut, + collaborateurId: collaborateurId, + validateurId: req.user.id, + commentaire: commentaire + } + ); + + await webhookManager.sendWebhook( + WEBHOOKS.COLLABORATEURS_URL, + EVENTS.COMPTEUR_UPDATED, + { collaborateurId: collaborateurId } + ); + } catch (webhookError) { + console.error('❌ Erreur envoi webhook (non bloquant):', webhookError.message); + } + + res.json({ message: 'Demande mise à jour' }); + } catch (error) { + await transaction.rollback(); + console.error('\n❌ ERREUR VALIDATION:', error); + res.status(500).json({ error: error.message }); + } + }); + + // Route pour export paie par plage de dates + app.get('/export/paie-plage', authenticateToken, async (req, res) => { + const { dateDebut, dateFin } = req.query; + + if (!dateDebut || !dateFin) { + return res.status(400).json({ error: 'Date de début et date de fin requises' }); + } + + console.log(`📊 Export paie - Plage: ${dateDebut} au ${dateFin}`); + + try { + const debut = new Date(dateDebut); + const fin = new Date(dateFin); + + if (isNaN(debut.getTime()) || isNaN(fin.getTime())) { + return res.status(400).json({ error: 'Dates invalides' }); + } + if (debut > fin) { + return res.status(400).json({ error: 'La date de début doit être antérieure à la date de fin' }); + } + + const result = await pool.request() + .input('dateDebut', sql.Date, dateDebut) + .input('dateFin', sql.Date, dateFin) + .query(` + SELECT + ca.id AS collaborateurId, + CONCAT(ca.prenom, ' ', ca.nom) AS employe, + ca.email, + COALESCE(s.Nom, 'Non défini') AS service, + tc.Nom AS typeConge, + dct.NombreJours, + dct.PeriodeJournee, + -- ⭐ Dates précises du TYPE de congé + COALESCE(dct.DateDebut, dc.DateDebut) AS dateDebutType, + COALESCE(dct.DateFin, dc.DateFin) AS dateFinType, + dc.DateDebut, + dc.DateFin, + dc.Statut + FROM DemandeConge dc + JOIN CollaborateurAD ca ON dc.CollaborateurADId = ca.id + LEFT JOIN Services s ON ca.ServiceId = s.Id + JOIN DemandeCongeType dct ON dc.Id = dct.DemandeCongeId + JOIN TypeConge tc ON dct.TypeCongeId = tc.Id + WHERE dc.Statut = 'Validée' + AND (ca.Actif = 1 OR ca.Actif IS NULL) + AND ( + (COALESCE(dct.DateDebut, dc.DateDebut) BETWEEN @dateDebut AND @dateFin) + OR (COALESCE(dct.DateFin, dc.DateFin) BETWEEN @dateDebut AND @dateFin) + OR (COALESCE(dct.DateDebut, dc.DateDebut) <= @dateDebut AND COALESCE(dct.DateFin, dc.DateFin) >= @dateFin) + ) + ORDER BY ca.nom, ca.prenom, COALESCE(dct.DateDebut, dc.DateDebut) + `); + + console.log(`📊 ${result.recordset.length} entrées trouvées`); + const dataFormatted = agregerLignesParEmploye(result.recordset); + console.log(`✅ Export paie-plage formaté: ${dataFormatted.length} employés`); + res.json(dataFormatted); + + } catch (error) { + console.error('❌ Erreur export paie plage:', error); + res.status(500).json({ error: 'Erreur lors de la génération du rapport', details: error.message }); + } + }); + + // ================================================ + // ROUTE GET /export/paie-saisie + // Filtre par date de SAISIE de la demande (DateDemande) + // À ajouter dans server.js après la route /export/paie-plage + // ================================================ + + app.get('/export/paie-saisie', authenticateToken, async (req, res) => { + const { dateSaisieDebut, dateSaisieFin } = req.query; + + console.log(`📊 Export paie-saisie: saisie entre ${dateSaisieDebut} et ${dateSaisieFin}`); + + if (!dateSaisieDebut || !dateSaisieFin) { + return res.status(400).json({ error: 'Date de saisie début et fin requises' }); + } + + try { + // ✅ DEBUG : voir ce qu'il y a en base pour juin + const debug = await pool.request() + .input('dateSaisieDebut', sql.Date, dateSaisieDebut) + .input('dateSaisieFin', sql.Date, dateSaisieFin) + .query(` + SELECT + CONCAT(ca.prenom, ' ', ca.nom) AS employe, + dc.DateDemande, + dc.DateDebut, + dc.DateFin, + COALESCE(dct.DateDebut, dc.DateDebut) AS dateDebutType, + COALESCE(dct.DateFin, dc.DateFin) AS dateFinType, + tc.Nom AS typeConge, + dc.Statut + FROM DemandeConge dc + JOIN CollaborateurAD ca ON dc.CollaborateurADId = ca.id + JOIN DemandeCongeType dct ON dc.Id = dct.DemandeCongeId + JOIN TypeConge tc ON dct.TypeCongeId = tc.Id + WHERE dc.Statut = 'Validée' + AND (ca.Actif = 1 OR ca.Actif IS NULL) + AND COALESCE(dct.DateDebut, dc.DateDebut) <= @dateSaisieFin + AND COALESCE(dct.DateFin, dc.DateFin) >= @dateSaisieDebut + `); + + console.log('🔍 DEBUG - Résultats sans filtre saisie:'); + debug.recordset.forEach(r => { + console.log(` 👤 ${r.employe} | Saisie: ${r.DateDemande} | Congé: ${r.DateDebut} → ${r.DateFin} | Type dates: ${r.dateDebutType} → ${r.dateFinType} | Type: ${r.typeConge}`); + }); + + const result = await pool.request() + .input('dateSaisieDebut', sql.Date, dateSaisieDebut) + .input('dateSaisieFin', sql.Date, dateSaisieFin) + .query(` + SELECT + ca.id AS collaborateurId, + CONCAT(ca.prenom, ' ', ca.nom) AS employe, + ca.email, + COALESCE(s.Nom, 'Non défini') AS service, + tc.Nom AS typeConge, + dct.NombreJours, + dct.PeriodeJournee, + COALESCE(dct.DateDebut, dc.DateDebut) AS dateDebutType, + COALESCE(dct.DateFin, dc.DateFin) AS dateFinType, + dc.DateDebut, + dc.DateFin, + dc.Statut, + dc.DateDemande + FROM DemandeConge dc + JOIN CollaborateurAD ca ON dc.CollaborateurADId = ca.id + LEFT JOIN Services s ON ca.ServiceId = s.Id + JOIN DemandeCongeType dct ON dc.Id = dct.DemandeCongeId + JOIN TypeConge tc ON dct.TypeCongeId = tc.Id + WHERE dc.Statut = 'Validée' + AND (ca.Actif = 1 OR ca.Actif IS NULL) + AND COALESCE(dct.DateDebut, dc.DateDebut) <= @dateSaisieFin + AND COALESCE(dct.DateFin, dc.DateFin) >= @dateSaisieDebut + AND CAST(dc.DateDemande AS DATE) <= @dateSaisieFin + ORDER BY ca.nom, ca.prenom, + COALESCE(dct.DateDebut, dc.DateDebut) + `); + + console.log(`📊 ${result.recordset.length} entrées trouvées`); + const dataFormatted = agregerLignesParEmploye(result.recordset); + console.log(`✅ Export paie-saisie formaté: ${dataFormatted.length} employés`); + res.json(dataFormatted); + + } catch (error) { + console.error('❌ Erreur export paie-saisie:', error); + res.status(500).json({ error: 'Erreur lors de la génération du rapport', details: error.message }); + } + }); + + // ================================================ + // ROUTES EMPLOYÉS + // ================================================ + app.get('/employes', authenticateToken, async (req, res) => { + try { + const currentYear = new Date().getFullYear(); + const previousYear = currentYear - 1; + + const result = await pool.request() + .input('currentYear', sql.Int, currentYear) + .input('previousYear', sql.Int, previousYear) + .query(` + SELECT + ca.id, + ca.nom, + ca.prenom, + ca.email, + ca.role, + COALESCE(s.Nom, 'Sans service') as service, + ca.Actif, + ca.TypeContrat, + + -- CP Année N + (SELECT TOP 1 COALESCE(Solde, 0) + FROM CompteurConges cc1 + WHERE cc1.CollaborateurADId = ca.id + AND cc1.TypeCongeId = (SELECT TOP 1 Id FROM TypeConge WHERE Nom LIKE '%Congé%payé%' OR Nom LIKE '%CP%' ORDER BY Id) + AND cc1.Annee = @currentYear) as soldeCPN, + + -- CP Année N-1 + (SELECT TOP 1 COALESCE(Solde, 0) + FROM CompteurConges cc2 + WHERE cc2.CollaborateurADId = ca.id + AND cc2.TypeCongeId = (SELECT TOP 1 Id FROM TypeConge WHERE Nom LIKE '%Congé%payé%' OR Nom LIKE '%CP%' ORDER BY Id) + AND cc2.Annee = @previousYear) as soldeCPNMoins1, + + -- RTT + (SELECT TOP 1 COALESCE(Solde, 0) + FROM CompteurConges cc3 + WHERE cc3.CollaborateurADId = ca.id + AND cc3.TypeCongeId = (SELECT TOP 1 Id FROM TypeConge WHERE Nom = 'RTT' ORDER BY Id) + AND cc3.Annee = @currentYear) as soldeRTT, + + -- Récupération + (SELECT TOP 1 COALESCE(Solde, 0) + FROM CompteurConges cc4 + WHERE cc4.CollaborateurADId = ca.id + AND cc4.TypeCongeId = (SELECT TOP 1 Id FROM TypeConge WHERE Nom LIKE '%Récup%' OR Nom LIKE '%Recup%' ORDER BY Id) + AND cc4.Annee = @currentYear) as soldeRecup + + FROM CollaborateurAD ca + LEFT JOIN Services s ON ca.ServiceId = s.Id + WHERE ca.Actif = 1 OR ca.Actif IS NULL + ORDER BY ca.nom, ca.prenom + `); + + console.log(`✅ ${result.recordset.length} employés récupérés avec compteurs`); + res.json(result.recordset); + } catch (error) { + console.error('❌ Erreur /api/employes:', error); + res.status(500).json({ error: error.message }); + } + }); + + + + // ✅ NOUVELLE ROUTE: Désactiver un collaborateur + app.post('/employes/desactiver', authenticateToken, async (req, res) => { + try { + if (!['Admin', 'RH'].includes(req.user.role)) { + return res.status(403).json({ error: 'Accès non autorisé' }); + } + + const { collaborateur_id, date_sortie } = req.body; + + if (!collaborateur_id) { + return res.json({ success: false, message: 'ID collaborateur manquant' }); + } + + const dateSortie = date_sortie || new Date().toISOString().split('T')[0]; + + const collabResult = await pool.request() + .input('id', sql.Int, collaborateur_id) + .query('SELECT prenom, nom, email FROM CollaborateurAD WHERE id = @id'); + + const collab = collabResult.recordset; + + if (collab.length === 0) { + return res.json({ success: false, message: 'Collaborateur non trouvé' }); + } + + await pool.request() + .input('dateSortie', sql.Date, dateSortie) + .input('id', sql.Int, collaborateur_id) + .query('UPDATE CollaborateurAD SET Actif = 0, DateSortie = @dateSortie WHERE id = @id'); + + res.json({ + success: true, + message: 'Collaborateur désactivé', + nom: `${collab[0].prenom} ${collab[0].nom}`, + date_sortie: dateSortie + }); + } catch (error) { + console.error('Erreur desactiverCollaborateur:', error); + res.status(500).json({ success: false, message: 'Erreur serveur', error: error.message }); + } + }); + + // ✅ NOUVELLE ROUTE: Réactiver un collaborateur + app.post('/employes/reactiver', authenticateToken, async (req, res) => { + try { + if (!['Admin', 'RH'].includes(req.user.role)) { + return res.status(403).json({ error: 'Accès non autorisé' }); + } + + const { collaborateur_id } = req.body; + + if (!collaborateur_id) { + return res.json({ success: false, message: 'ID collaborateur manquant' }); + } + + const collabResult = await pool.request() + .input('id', sql.Int, collaborateur_id) + .query('SELECT prenom, nom, email FROM CollaborateurAD WHERE id = @id'); + + const collab = collabResult.recordset; + + if (collab.length === 0) { + return res.json({ success: false, message: 'Collaborateur non trouvé' }); + } + + await pool.request() + .input('id', sql.Int, collaborateur_id) + .query('UPDATE CollaborateurAD SET Actif = 1, DateSortie = NULL WHERE id = @id'); + + res.json({ + success: true, + message: 'Collaborateur réactivé', + nom: `${collab[0].prenom} ${collab[0].nom}` + }); + } catch (error) { + console.error('Erreur reactiverCollaborateur:', error); + res.status(500).json({ success: false, message: 'Erreur serveur', error: error.message }); + } + }); + + app.get('/types-conge', authenticateToken, async (req, res) => { + try { + const result = await pool.request().query( + 'SELECT Id as id, Nom as nom, CouleurHex as couleur FROM TypeConge WHERE Actif = 1 ORDER BY Nom' + ); + res.json(result.recordset); + } catch (error) { + res.status(500).json({ error: error.message }); + } + }); + + // Suite - Routes export, compteurs, équipes, stats, etc. + + app.get('/export/paie', authenticateToken, async (req, res) => { + try { + const { mois, annee, dateSaisieDebut, dateSaisieFin } = req.query; + + const premierJour = `${annee}-${String(mois).padStart(2, '0')}-01`; + const dernierJour = new Date(annee, mois, 0).getDate(); + const dernierJourMois = `${annee}-${String(mois).padStart(2, '0')}-${dernierJour}`; + + // Le filtre création est actif uniquement si les deux dates sont fournies + const filtreCreationActif = dateSaisieDebut && dateSaisieFin; + + console.log(`📊 Export paie: ${mois}/${annee} (${premierJour} -> ${dernierJourMois})${filtreCreationActif ? ` | Filtre création: ${dateSaisieDebut} -> ${dateSaisieFin}` : ''}`); + + const request = pool.request() + .input('premierJour', sql.Date, premierJour) + .input('dernierJourMois', sql.Date, dernierJourMois); + + // Construire la clause WHERE du filtre de création + let whereCreation = ''; + if (filtreCreationActif) { + request + .input('dateSaisieDebut', sql.Date, dateSaisieDebut) + .input('dateSaisieFin', sql.Date, dateSaisieFin); + whereCreation = `AND CAST(dc.DateDemande AS DATE) >= @dateSaisieDebut + AND CAST(dc.DateDemande AS DATE) <= @dateSaisieFin`; + } + + const result = await request.query(` + SELECT + ca.id AS collaborateurId, + CONCAT(ca.prenom, ' ', ca.nom) AS employe, + ca.email, + COALESCE(s.Nom, 'Non assigné') AS service, + tc.Nom AS typeConge, + dct.NombreJours, + dct.PeriodeJournee, + -- Dates précises du TYPE de congé + COALESCE(dct.DateDebut, dc.DateDebut) AS dateDebutType, + COALESCE(dct.DateFin, dc.DateFin) AS dateFinType, + -- Dates de la demande (pour le filtre période) + dc.DateDebut, + dc.DateFin, + dc.Statut + FROM DemandeConge dc + JOIN CollaborateurAD ca ON dc.CollaborateurADId = ca.id + LEFT JOIN Services s ON ca.ServiceId = s.Id + JOIN DemandeCongeType dct ON dc.Id = dct.DemandeCongeId + JOIN TypeConge tc ON dct.TypeCongeId = tc.Id + WHERE dc.Statut = 'Validée' + AND (ca.Actif = 1 OR ca.Actif IS NULL) + AND ( + -- La période du TYPE chevauche le mois + (COALESCE(dct.DateDebut, dc.DateDebut) BETWEEN @premierJour AND @dernierJourMois) + OR (COALESCE(dct.DateFin, dc.DateFin) BETWEEN @premierJour AND @dernierJourMois) + OR (COALESCE(dct.DateDebut, dc.DateDebut) <= @premierJour AND COALESCE(dct.DateFin, dc.DateFin) >= @dernierJourMois) + ) + ${whereCreation} + ORDER BY ca.nom, ca.prenom, COALESCE(dct.DateDebut, dc.DateDebut) + `); + + console.log(`📊 ${result.recordset.length} entrées trouvées`); + const dataFormatted = agregerLignesParEmploye(result.recordset); + console.log(`✅ Export formaté: ${dataFormatted.length} employés`); + res.json(dataFormatted); + + } catch (error) { + console.error('❌ Erreur export paie:', error); + res.status(500).json({ error: error.message }); + } + }); + // Route pour obtenir les informations d'un collaborateur + app.get('/employes/:id/info', authenticateToken, async (req, res) => { + try { + const result = await pool.request() + .input('id', sql.Int, req.params.id) + .query(` + SELECT + id, + nom, + prenom, + email, + role, + TypeContrat, + Actif, + DateSortie, + ServiceId + FROM CollaborateurAD + WHERE id = @id + `); + + const employe = result.recordset; + + if (employe.length === 0) { + return res.status(404).json({ error: 'Employé non trouvé' }); + } + + const collab = employe[0]; + + // Déterminer si c'est un apprenti + const estApprenti = collab.TypeContrat === 'Apprentissage' || + collab.role === 'Apprenti' || + collab.role?.toLowerCase().includes('apprenti'); + + res.json({ + ...collab, + estApprenti, + droitRTT: !estApprenti + }); + } catch (error) { + console.error('Erreur récupération info employé:', error); + res.status(500).json({ error: error.message }); + } + }); + + // ✅ Modifier la route GET /compteurs pour inclure les infos sur les apprentis + app.get('/compteurs', authenticateToken, async (req, res) => { + try { + const includeInactifs = req.query.include_inactifs === 'true'; + const { user_id } = req.query; + + let query = ` + SELECT + cc.Id AS id, + cc.CollaborateurADId AS collaborateurId, + cc.TypeCongeId AS typeCongeId, + CONCAT(ca.prenom, ' ', ca.nom) AS employe, + ca.email AS email, + ca.Actif AS Actif, + ca.role AS role, + ca.TypeContrat AS TypeContrat, + COALESCE(s.Nom, 'Sans service') AS service, + tc.Nom AS typeConge, + cc.Annee AS annee, + cc.Total AS total, + cc.Solde AS solde, + cc.SoldeReporte AS soldeReporte, + (cc.Total - cc.Solde) AS consomme + FROM CompteurConges cc + JOIN CollaborateurAD ca ON cc.CollaborateurADId = ca.id + LEFT JOIN Services s ON ca.ServiceId = s.Id + JOIN TypeConge tc ON cc.TypeCongeId = tc.Id + WHERE 1=1 + AND (ca.description IS NULL OR ca.description NOT LIKE '%stagiaire%') + `; + + const request = pool.request(); + + // Filtre par utilisateur si fourni + if (user_id) { + query += ' AND cc.CollaborateurADId = @userId'; + request.input('userId', sql.Int, parseInt(user_id)); + } + + // Filtre actif/inactif + if (!includeInactifs) { + query += ' AND (ca.Actif = 1 OR ca.Actif IS NULL)'; + } + + query += ' ORDER BY ca.Actif DESC, ca.nom, ca.prenom, tc.Nom'; + + const result = await request.query(query); + const compteurs = result.recordset; + + console.log(`✅ ${compteurs.length} compteurs trouvés`); + + // ⭐ DEBUG: Afficher les types de congés trouvés + const typesConges = [...new Set(compteurs.map(c => c.typeConge))]; + console.log('📊 Types de congés:', typesConges); + + // Ajouter l'indicateur apprenti et formater les données + const compteursFormates = compteurs.map(c => ({ + id: c.id, + collaborateurId: c.collaborateurId, + typeCongeId: c.typeCongeId, + employe: c.employe, + email: c.email, + service: c.service || 'Sans service', + typeConge: c.typeConge, + annee: c.annee, + total: parseFloat(c.total) || 0, + solde: parseFloat(c.solde) || 0, + soldeReporte: parseFloat(c.soldeReporte) || 0, + consomme: parseFloat(c.consomme) || 0, + estApprenti: c.TypeContrat === 'Apprentissage' || + c.role === 'Apprenti' || + (c.role || '').toLowerCase().includes('apprenti') + })); + + res.json(compteursFormates); + + } catch (error) { + console.error('❌ Erreur récupération compteurs:', error); + res.status(500).json({ error: error.message }); + } + }); + app.put('/compteurs/:id', authenticateToken, async (req, res) => { + const transaction = new sql.Transaction(pool); + try { + await transaction.begin(); + + if (!['Admin', 'RH', 'Validateur'].includes(req.user.role)) { + await transaction.rollback(); + return res.status(403).json({ error: 'Accès non autorisé' }); + } + + const { id } = req.params; + const { total, solde } = req.body; + + const totalNum = parseFloat(total); + const soldeNum = parseFloat(solde); + + const existingResult = await transaction.request() + .input('id', sql.Int, id) + .query('SELECT * FROM CompteurConges WHERE Id = @id'); + + const existing = existingResult.recordset; + + if (existing.length === 0) { + await transaction.rollback(); + return res.status(404).json({ error: 'Compteur non trouvé' }); + } + + const collaborateurId = existing[0].CollaborateurADId; + const typeCongeId = existing[0].TypeCongeId; + const annee = existing[0].Annee; + + // Récupérer le nom du type de congé + const typeInfoResult = await transaction.request() + .input('typeCongeId', sql.Int, typeCongeId) + .query('SELECT Nom FROM TypeConge WHERE Id = @typeCongeId'); + + const typeConge = typeInfoResult.recordset.length > 0 ? typeInfoResult.recordset[0].Nom : 'Inconnu'; + + // Mise à jour + await transaction.request() + .input('total', sql.Decimal(10, 2), totalNum) + .input('solde', sql.Decimal(10, 2), soldeNum) + .input('id', sql.Int, id) + .query('UPDATE CompteurConges SET Total = @total, Solde = @solde, DerniereMiseAJour = GETDATE() WHERE Id = @id'); + + await transaction.commit(); + + // Notifier les clients SSE + notifyClients({ + type: 'compteur-updated', + collaborateurId: collaborateurId, + typeCongeId: typeCongeId, + typeConge: typeConge, + annee: annee, + action: 'modification_rh', + nouveauTotal: totalNum, + nouveauSolde: soldeNum, + timestamp: new Date().toISOString() + }, collaborateurId); + + // Webhook + try { + await webhookManager.sendWebhook( + WEBHOOKS.COLLABORATEURS_URL, + EVENTS.COMPTEUR_UPDATED, + { + collaborateurId: collaborateurId, + typeCongeId: typeCongeId, + typeConge: typeConge, + typeUpdate: 'modification_manuelle_rh', + annee: annee, + nouveauTotal: totalNum, + nouveauSolde: soldeNum, + source: 'rh', + timestamp: new Date().toISOString() + } + ); + console.log(`✅ Webhook envoyé pour ${collaborateurId} (${typeConge} ${annee}: Total=${totalNum}j, Solde=${soldeNum}j)`); + } catch (webhookError) { + console.error('❌ Erreur envoi webhook (non bloquant):', webhookError.message); + } + + res.json({ + message: 'Compteur modifié avec succès', + total: totalNum, + solde: soldeNum + }); + } catch (error) { + await transaction.rollback(); + console.error('Erreur modification compteur:', error); + res.status(500).json({ error: error.message }); + } + }); + + // ================================================ + // ROUTE POUR AJOUTER DES JOURS DE RÉCUPÉRATION + // ================================================ + app.post('/compteurs/ajouter-recup', authenticateToken, async (req, res) => { + const transaction = new sql.Transaction(pool); + try { + await transaction.begin(); + + if (!['Admin', 'RH'].includes(req.user.role)) { + await transaction.rollback(); + return res.status(403).json({ error: 'Accès non autorisé' }); + } + + const { collaborateurId, nombreJours, commentaire } = req.body; + + if (!collaborateurId || !nombreJours || nombreJours <= 0) { + await transaction.rollback(); + return res.json({ + success: false, + message: 'Données invalides' + }); + } + + console.log('📥 Ajout de récupération:', { collaborateurId, nombreJours, commentaire }); + + // Récupérer les infos du collaborateur + const collaborateurResult = await transaction.request() + .input('id', sql.Int, collaborateurId) + .query('SELECT prenom, nom, email, Actif FROM CollaborateurAD WHERE id = @id'); + + const collaborateur = collaborateurResult.recordset; + + if (collaborateur.length === 0) { + await transaction.rollback(); + return res.status(404).json({ + success: false, + message: 'Collaborateur non trouvé' + }); + } + + if (collaborateur[0].Actif === 0) { + await transaction.rollback(); + return res.status(403).json({ + success: false, + message: 'Impossible d\'ajouter des jours à un compte désactivé' + }); + } + + const collab = collaborateur[0]; + const currentYear = new Date().getFullYear(); + + // Récupérer l'ID du type "Récupération" + const recupTypeResult = await transaction.request() + .query("SELECT Id FROM TypeConge WHERE Nom = 'Récupération'"); + + const recupType = recupTypeResult.recordset; + + if (recupType.length === 0) { + await transaction.rollback(); + return res.status(500).json({ + success: false, + message: 'Type de congé "Récupération" non trouvé' + }); + } + + const recupTypeId = recupType[0].Id; + + // Vérifier si le compteur existe déjà + const compteurExistingResult = await transaction.request() + .input('collaborateurId', sql.Int, collaborateurId) + .input('recupTypeId', sql.Int, recupTypeId) + .input('currentYear', sql.Int, currentYear) + .query(` + SELECT Id, Total, Solde FROM CompteurConges + WHERE CollaborateurADId = @collaborateurId + AND TypeCongeId = @recupTypeId + AND Annee = @currentYear + `); + + const compteurExisting = compteurExistingResult.recordset; + + let nouveauTotal, nouveauSolde; + + if (compteurExisting.length > 0) { + nouveauTotal = parseFloat(compteurExisting[0].Total) + nombreJours; + nouveauSolde = parseFloat(compteurExisting[0].Solde) + nombreJours; + + // Mettre à jour le compteur existant + await transaction.request() + .input('total', sql.Decimal(10, 2), nouveauTotal) + .input('solde', sql.Decimal(10, 2), nouveauSolde) + .input('id', sql.Int, compteurExisting[0].Id) + .query('UPDATE CompteurConges SET Total = @total, Solde = @solde, DerniereMiseAJour = GETDATE() WHERE Id = @id'); + + console.log(`✅ Compteur Récupération mis à jour: +${nombreJours}j (nouveau solde: ${nouveauSolde.toFixed(2)}j)`); + } else { + nouveauTotal = nombreJours; + nouveauSolde = nombreJours; + + // Créer un nouveau compteur + await transaction.request() + .input('collaborateurId', sql.Int, collaborateurId) + .input('recupTypeId', sql.Int, recupTypeId) + .input('currentYear', sql.Int, currentYear) + .input('total', sql.Decimal(10, 2), nouveauTotal) + .input('solde', sql.Decimal(10, 2), nouveauSolde) + .query(` + INSERT INTO CompteurConges + (CollaborateurADId, TypeCongeId, Annee, Total, Solde, SoldeReporte, DerniereMiseAJour) + VALUES (@collaborateurId, @recupTypeId, @currentYear, @total, @solde, 0, GETDATE()) + `); + + console.log(`✅ Compteur Récupération créé: ${nombreJours}j`); + } + + // Enregistrer dans l'historique + await transaction.request() + .input('userId', sql.Int, req.user.id) + .input('details', sql.NVarChar, `Ajout de ${nombreJours}j de récupération pour ${collab.prenom} ${collab.nom}${commentaire ? ` - ${commentaire}` : ''}`) + .query(` + INSERT INTO HistoriqueActions (CollaborateurADId, Action, Details, DateAction) + VALUES (@userId, 'Ajout récupération', @details, GETDATE()) + `); + + await transaction.commit(); + + // Webhook + try { + await webhookManager.sendWebhook( + WEBHOOKS.COLLABORATEURS_URL, + EVENTS.COMPTEUR_UPDATED, + { + collaborateurId: collaborateurId, + typeCongeId: recupTypeId, + typeConge: 'Récupération', + typeUpdate: 'ajout_manuel_rh', + annee: currentYear, + nouveauTotal: nouveauTotal, + nouveauSolde: nouveauSolde, + source: 'bouton_plus_rh', + timestamp: new Date().toISOString() + } + ); + console.log('✅ Webhook GTA-RH envoyé (compteur récup mis à jour)'); + } catch (webhookError) { + console.error('❌ Erreur webhook (non bloquant):', webhookError.message); + } + + // Notifier les clients SSE + notifyClients({ + type: 'compteur-updated', + collaborateurId: collaborateurId, + typeConge: 'Récupération', + action: 'ajout', + nombreJours: nombreJours, + timestamp: new Date().toISOString() + }, collaborateurId); + + // Envoyer un email + const accessToken = await getGraphToken(); + if (accessToken && collab.email) { + const fromEmail = 'noreply@ensup.eu'; + const subject = '✅ Jours de récupération ajoutés'; + const body = ` +
+
+

✅ Jours de récupération ajoutés

+
+
+

Bonjour ${collab.prenom} ${collab.nom},

+

Des jours de récupération ont été ajoutés à votre compteur.

+
+

Jours ajoutés : ${nombreJours} jour${nombreJours > 1 ? 's' : ''}

+ ${commentaire ? `

Motif : ${commentaire}

` : ''} +

Année : ${currentYear}

+
+

Ces jours sont désormais disponibles et peuvent être utilisés via une demande de congé.

+
+
+ `; + + try { + await sendMailGraph(accessToken, fromEmail, collab.email, subject, body); + console.log('✅ Email de notification envoyé'); + } catch (emailError) { + console.error('❌ Erreur envoi email:', emailError); + } + } + + res.json({ + success: true, + message: 'Jours de récupération ajoutés avec succès', + collaborateur: `${collab.prenom} ${collab.nom}`, + nombreJours: nombreJours, + annee: currentYear + }); + + } catch (error) { + await transaction.rollback(); + console.error('❌ Erreur ajout récupération:', error); + res.status(500).json({ + success: false, + message: 'Erreur serveur', + error: error.message + }); + } + }); + + // Suite - Routes équipes, stats, historique + + // ================================================ + // ROUTE POUR RETIRER DES JOURS DE RÉCUPÉRATION + // ================================================ + app.post('/compteurs/retirer-recup', authenticateToken, async (req, res) => { + const transaction = new sql.Transaction(pool); + try { + await transaction.begin(); + + if (!['Admin', 'RH'].includes(req.user.role)) { + await transaction.rollback(); + return res.status(403).json({ error: 'Accès non autorisé' }); + } + + const { collaborateurId, nombreJours, commentaire } = req.body; + + if (!collaborateurId || !nombreJours || nombreJours <= 0) { + await transaction.rollback(); + return res.json({ + success: false, + message: 'Données invalides' + }); + } + + console.log('📤 Retrait de récupération:', { collaborateurId, nombreJours, commentaire }); + + // Récupérer les infos du collaborateur + const collaborateurResult = await transaction.request() + .input('id', sql.Int, collaborateurId) + .query('SELECT prenom, nom, email FROM CollaborateurAD WHERE id = @id'); + + const collaborateur = collaborateurResult.recordset; + + if (collaborateur.length === 0) { + await transaction.rollback(); + return res.status(404).json({ + success: false, + message: 'Collaborateur non trouvé' + }); + } + + const collab = collaborateur[0]; + const currentYear = new Date().getFullYear(); + + // Récupérer l'ID du type "Récupération" + const recupTypeResult = await transaction.request() + .query("SELECT Id FROM TypeConge WHERE Nom = 'Récupération'"); + + const recupType = recupTypeResult.recordset; + + if (recupType.length === 0) { + await transaction.rollback(); + return res.status(500).json({ + success: false, + message: 'Type de congé "Récupération" non trouvé' + }); + } + + const recupTypeId = recupType[0].Id; + + // Vérifier le solde actuel + const compteurResult = await transaction.request() + .input('collaborateurId', sql.Int, collaborateurId) + .input('recupTypeId', sql.Int, recupTypeId) + .input('currentYear', sql.Int, currentYear) + .query(` + SELECT Id, Total, Solde FROM CompteurConges + WHERE CollaborateurADId = @collaborateurId + AND TypeCongeId = @recupTypeId + AND Annee = @currentYear + `); + + const compteur = compteurResult.recordset; + + if (compteur.length === 0 || compteur[0].Solde < nombreJours) { + await transaction.rollback(); + return res.status(400).json({ + success: false, + message: 'Solde de récupération insuffisant', + soldeActuel: compteur.length > 0 ? compteur[0].Solde : 0 + }); + } + + const nouveauTotal = Math.max(0, parseFloat(compteur[0].Total) - nombreJours); + const nouveauSolde = Math.max(0, parseFloat(compteur[0].Solde) - nombreJours); + + // Retirer les jours + await transaction.request() + .input('total', sql.Decimal(10, 2), nouveauTotal) + .input('solde', sql.Decimal(10, 2), nouveauSolde) + .input('id', sql.Int, compteur[0].Id) + .query('UPDATE CompteurConges SET Total = @total, Solde = @solde, DerniereMiseAJour = GETDATE() WHERE Id = @id'); + + console.log(`✅ ${nombreJours}j retirés du compteur Récupération`); + + // Enregistrer dans l'historique + await transaction.request() + .input('userId', sql.Int, req.user.id) + .input('details', sql.NVarChar, `Retrait de ${nombreJours}j de récupération pour ${collab.prenom} ${collab.nom}${commentaire ? ` - ${commentaire}` : ''}`) + .query(` + INSERT INTO HistoriqueActions (CollaborateurADId, Action, Details, DateAction) + VALUES (@userId, 'Retrait récupération', @details, GETDATE()) + `); + + await transaction.commit(); + + // Webhook + try { + await webhookManager.sendWebhook( + WEBHOOKS.COLLABORATEURS_URL, + EVENTS.COMPTEUR_UPDATED, + { + collaborateurId: collaborateurId, + typeCongeId: recupTypeId, + typeConge: 'Récupération', + typeUpdate: 'retrait_manuel_rh', + annee: currentYear, + nouveauTotal: nouveauTotal, + nouveauSolde: nouveauSolde, + source: 'bouton_moins_rh', + timestamp: new Date().toISOString() + } + ); + console.log('✅ Webhook GTA-RH envoyé (compteur récup mis à jour)'); + } catch (webhookError) { + console.error('❌ Erreur webhook (non bloquant):', webhookError.message); + } + + // Notifier les clients SSE + notifyClients({ + type: 'compteur-updated', + collaborateurId: collaborateurId, + typeConge: 'Récupération', + action: 'retrait', + nombreJours: nombreJours, + timestamp: new Date().toISOString() + }, collaborateurId); + + res.json({ + success: true, + message: 'Jours de récupération retirés avec succès', + collaborateur: `${collab.prenom} ${collab.nom}`, + nombreJours: nombreJours, + annee: currentYear + }); + + } catch (error) { + await transaction.rollback(); + console.error('❌ Erreur retrait récupération:', error); + res.status(500).json({ + success: false, + message: 'Erreur serveur', + error: error.message + }); + } + }); + + app.get('/equipes', authenticateToken, async (req, res) => { + try { + const result = await pool.request().query(` + SELECT + s.Id, + s.Nom as nomService, + s.Nom as service, + COUNT(DISTINCT CASE WHEN (ca.Actif = 1 OR ca.Actif IS NULL) THEN ca.id END) as nombreMembres, + COUNT(DISTINCT CASE WHEN dc.Statut = 'En attente' AND (ca.Actif = 1 OR ca.Actif IS NULL) THEN dc.Id END) as demandesEnAttente, + COALESCE( + STUFF(( + SELECT DISTINCT ',' + c1.Nom + FROM ServiceAffectation sa1 + JOIN Campus c1 ON sa1.CampusId = c1.Id + WHERE sa1.ServiceId = s.Id + ORDER BY ',' + c1.Nom + FOR XML PATH('') + ), 1, 1, ''), + STUFF(( + SELECT DISTINCT ',' + c2.Nom + FROM CollaborateurAD ca2 + JOIN Campus c2 ON ca2.CampusId = c2.Id + WHERE ca2.ServiceId = s.Id AND (ca2.Actif = 1 OR ca2.Actif IS NULL) + ORDER BY ',' + c2.Nom + FOR XML PATH('') + ), 1, 1, ''), + 'Non assigné' + ) as campus + FROM Services s + LEFT JOIN CollaborateurAD ca ON s.Id = ca.ServiceId AND (ca.Actif = 1 OR ca.Actif IS NULL) + LEFT JOIN DemandeConge dc ON ca.id = dc.CollaborateurADId AND dc.Statut = 'En attente' + GROUP BY s.Id, s.Nom + HAVING COUNT(DISTINCT CASE WHEN (ca.Actif = 1 OR ca.Actif IS NULL) THEN ca.id END) > 0 + OR EXISTS (SELECT 1 FROM ServiceAffectation sa2 WHERE sa2.ServiceId = s.Id) + ORDER BY s.Nom + `); + + const equipes = result.recordset; + res.json(equipes); + } catch (error) { + console.error('❌ Erreur /equipes:', error); + res.status(500).json({ error: error.message }); + } + }); + + app.get('/equipes/:id', authenticateToken, async (req, res) => { + try { + const result = await pool.request() + .input('id', sql.Int, req.params.id) + .query(` + SELECT + s.Id, + s.Nom, + s.Nom as nomService, + COUNT(DISTINCT CASE + WHEN (ca.Actif = 1 OR ca.Actif IS NULL) + THEN ca.id + END) as nombreMembres, + COUNT(DISTINCT CASE + WHEN dc.Statut = 'En attente' + AND (ca.Actif = 1 OR ca.Actif IS NULL) + THEN dc.Id + END) as demandesEnAttente, + COALESCE( + STUFF(( + SELECT DISTINCT ', ' + c.Nom + FROM ServiceAffectation sa + JOIN Campus c ON sa.CampusId = c.Id + WHERE sa.ServiceId = s.Id + ORDER BY ', ' + c.Nom + FOR XML PATH('') + ), 1, 2, ''), + 'Non assigné' + ) as campus + FROM Services s + LEFT JOIN CollaborateurAD ca ON s.Id = ca.ServiceId + LEFT JOIN DemandeConge dc ON ca.id = dc.CollaborateurADId + WHERE s.Id = @id + GROUP BY s.Id, s.Nom + `); + + const equipes = result.recordset; + + if (equipes.length === 0) { + return res.status(404).json({ error: 'Service non trouvé' }); + } + + res.json(equipes[0]); + } catch (error) { + console.error('Erreur /equipes/:id:', error); + res.status(500).json({ error: error.message }); + } + }); + + app.get('/equipes/:id/membres', authenticateToken, async (req, res) => { + try { + const result = await pool.request() + .input('id', sql.Int, req.params.id) + .query(` + SELECT + ca.id, + CONCAT(ca.prenom, ' ', ca.nom) as nom, + ca.email, + ca.role, + ca.Actif, + ca.DateSortie, + ca.TypeContrat, + (SELECT MAX(Solde) FROM CompteurConges cc + WHERE cc.CollaborateurADId = ca.id + AND cc.TypeCongeId = 1 + AND cc.Annee = YEAR(GETDATE())) as soldeCP, + (SELECT MAX(Solde) FROM CompteurConges cc2 + WHERE cc2.CollaborateurADId = ca.id + AND cc2.TypeCongeId = 2 + AND cc2.Annee = YEAR(GETDATE())) as soldeRTT + FROM CollaborateurAD ca + WHERE ca.ServiceId = @id + AND (ca.Actif = 1 OR ca.Actif IS NULL) + ORDER BY ca.nom, ca.prenom + `); + + const membres = result.recordset; + console.log(`${membres.length} membres trouvés pour le service ${req.params.id}`); + res.json(membres); + } catch (error) { + console.error('Erreur /equipes/:id/membres:', error); + res.status(500).json({ error: error.message }); + } + }); + + app.get('/stats', authenticateToken, async (req, res) => { + try { + const result = await pool.request().query(` + SELECT + COUNT(CASE WHEN Statut = 'En attente' AND (ca.Actif = 1 OR ca.Actif IS NULL) THEN 1 END) as enAttente, + COUNT(CASE WHEN Statut = 'Validée' AND MONTH(DateValidation) = MONTH(GETDATE()) AND (ca.Actif = 1 OR ca.Actif IS NULL) THEN 1 END) as valideeCeMois, + COUNT(DISTINCT ca.ServiceId) as nombreEquipes + FROM DemandeConge dc + JOIN CollaborateurAD ca ON dc.CollaborateurADId = ca.id + WHERE (ca.Actif = 1 OR ca.Actif IS NULL) + `); + + const stats = result.recordset; + res.json(stats[0]); + } catch (error) { + res.status(500).json({ error: error.message }); + } + }); + + // Route /historique modifiée pour inclure le collaborateur concerné ET la personne qui a réalisé l'action + + app.get('/historique', authenticateToken, async (req, res) => { + try { + const { dateDebut, dateFin, action } = req.query; + + const request = pool.request(); + + let whereClause = 'WHERE 1=1'; + + if (dateDebut) { + whereClause += ' AND ha.DateAction >= @dateDebut'; + request.input('dateDebut', sql.DateTime, dateDebut); + } + + if (dateFin) { + whereClause += ' AND ha.DateAction <= @dateFin'; + request.input('dateFin', sql.DateTime, dateFin); + } + + if (action && action !== 'all') { + whereClause += ' AND ha.Action = @action'; + request.input('action', sql.VarChar, action); + } + + // ✅ REQUÊTE MODIFIÉE : Inclut le collaborateur concerné ET la personne qui a réalisé l'action + const query = ` + SELECT TOP 500 + ha.Id, + -- Personne qui a réalisé l'action (celui qui est dans HistoriqueActions.CollaborateurADId) + CONCAT(ca.prenom, ' ', ca.nom) AS realisePar, + ca.email AS realiseParEmail, + -- Collaborateur concerné par l'action (récupéré via la demande de congé) + CASE + WHEN dc.CollaborateurADId IS NOT NULL + THEN CONCAT(collab_concerne.prenom, ' ', collab_concerne.nom) + ELSE CONCAT(ca.prenom, ' ', ca.nom) + END AS collaborateur, + CASE + WHEN dc.CollaborateurADId IS NOT NULL + THEN collab_concerne.email + ELSE ca.email + END AS collaborateurEmail, + ha.Action, + ha.Details, + ha.DateAction, + ha.AdresseIP, + ha.DemandeCongeId + FROM HistoriqueActions ha + -- Jointure pour récupérer la personne qui a réalisé l'action + JOIN CollaborateurAD ca ON ha.CollaborateurADId = ca.id + -- Jointure optionnelle pour récupérer la demande de congé associée + LEFT JOIN DemandeConge dc ON ha.DemandeCongeId = dc.Id + -- Jointure optionnelle pour récupérer le collaborateur concerné par la demande + LEFT JOIN CollaborateurAD collab_concerne ON dc.CollaborateurADId = collab_concerne.id + ${whereClause} + ORDER BY ha.DateAction DESC + `; + + console.log('📊 Requête historique SQL:', query); + + const result = await request.query(query); + const historique = result.recordset || []; + + console.log(`✅ ${historique.length} entrées d'historique trouvées`); + + // ⭐ TOUJOURS retourner un tableau + res.json(historique); + } catch (error) { + console.error('❌ Erreur /historique:', error); + res.status(500).json({ error: error.message }); + } + }); + // ================================================ + // FONCTIONS DE CALCUL CP ET RTT + // ================================================ + + /** + * Calcule l'acquisition CP avec la formule Excel exacte + */ + function calculerAcquisitionCP(dateReference = new Date(), dateEntree = null) { + const d = new Date(dateReference); + d.setHours(0, 0, 0, 0); + const annee = d.getFullYear(); + const mois = d.getMonth() + 1; // 1-12 + + // 1. Déterminer le début de l'exercice CP (01/06) + let exerciceDebut; + if (mois >= 6) { + exerciceDebut = new Date(annee, 5, 1); // 01/06/N + } else { + exerciceDebut = new Date(annee - 1, 5, 1); // 01/06/N-1 + } + exerciceDebut.setHours(0, 0, 0, 0); + + // 2. Ajuster si date d'entrée postérieure + let dateDebutAcquis = new Date(exerciceDebut); + if (dateEntree && dateEntree !== null) { + const entree = new Date(dateEntree); + entree.setHours(0, 0, 0, 0); + + if (!isNaN(entree.getTime()) && entree > exerciceDebut) { + dateDebutAcquis = entree; + } + } + + // 3. Calculer avec la formule Excel + const coeffCP = 25 / 12; // 2.0833 + const acquisition = calculerAcquisitionFormuleExcel(dateDebutAcquis, d, coeffCP); + + // 4. Plafonner à 25 jours + return Math.min(acquisition, 25); + } + + /** + * Calcule l'acquisition RTT avec la configuration variable + */ + async function calculerAcquisitionRTT(collaborateurId, dateReference = new Date()) { + const d = new Date(dateReference); + d.setHours(0, 0, 0, 0); + const annee = d.getFullYear(); + + // 1. Récupérer les infos du collaborateur + const collabInfoResult = await pool.request() + .input('collaborateurId', sql.Int, collaborateurId) + .query('SELECT TypeContrat, DateEntree, role FROM CollaborateurAD WHERE id = @collaborateurId'); + + const collabInfo = collabInfoResult.recordset; + + if (collabInfo.length === 0) { + throw new Error(`Collaborateur ${collaborateurId} non trouvé`); + } + + const typeContrat = collabInfo[0].TypeContrat || '37h'; + const dateEntree = collabInfo[0].DateEntree; + const isApprenti = collabInfo[0].role === 'Apprenti'; + + // 2. Apprentis : pas de RTT + if (isApprenti) { + return { + acquisition: 0, + moisTravailles: 0, + config: { joursAnnuels: 0, acquisitionMensuelle: 0 }, + typeContrat: typeContrat + }; + } + + // 3. Récupérer la configuration RTT + const config = await getConfigurationRTT(annee, typeContrat); + + // 4. Début d'acquisition : 01/01/N ou date d'entrée + let dateDebutAcquis = new Date(annee, 0, 1); // 01/01/N + dateDebutAcquis.setHours(0, 0, 0, 0); + + if (dateEntree && dateEntree !== null) { + const entree = new Date(dateEntree); + entree.setHours(0, 0, 0, 0); + + if (!isNaN(entree.getTime())) { + if (entree.getFullYear() === annee && entree > dateDebutAcquis) { + dateDebutAcquis = entree; + } + if (entree.getFullYear() > annee) { + return { + acquisition: 0, + moisTravailles: 0, + config: config, + typeContrat: typeContrat + }; + } + } + } + + // 5. Calculer avec la formule Excel + const acquisition = calculerAcquisitionFormuleExcel( + dateDebutAcquis, + d, + config.acquisitionMensuelle + ); + + // 6. Calculer les mois travaillés + const moisTravailles = config.acquisitionMensuelle > 0 + ? acquisition / config.acquisitionMensuelle + : 0; + + // 7. Plafonner au maximum annuel + const acquisitionFinale = Math.min(acquisition, config.joursAnnuels); + + return { + acquisition: Math.round(acquisitionFinale * 100) / 100, + moisTravailles: Math.round(moisTravailles * 100) / 100, + config: config, + typeContrat: typeContrat + }; + } + + function dateDifMonths(date1, date2) { + const d1 = new Date(date1); + const d2 = new Date(date2); + + // Vérifier que les dates sont valides + if (isNaN(d1.getTime()) || isNaN(d2.getTime())) { + console.error('❌ Date invalide dans dateDifMonths'); + return 0; + } + + let months = (d2.getFullYear() - d1.getFullYear()) * 12; + months += d2.getMonth() - d1.getMonth(); + + // Si le jour de d2 < jour de d1, on n'a pas encore complété le mois + if (d2.getDate() < d1.getDate()) { + months--; + } + + return Math.max(0, months); + } + + /** + * Formule Excel exacte pour calculer l'acquisition + */ + function calculerAcquisitionFormuleExcel(dateDebut, dateReference, coeffMensuel) { + const b1 = new Date(dateDebut); + const b2 = new Date(dateReference); + b1.setHours(0, 0, 0, 0); + b2.setHours(0, 0, 0, 0); + + // Vérifier que les dates sont valides + if (isNaN(b1.getTime()) || isNaN(b2.getTime())) { + console.error('❌ Date invalide dans calculerAcquisitionFormuleExcel'); + return 0; + } + + // Vérifier que coeffMensuel est un nombre + if (isNaN(coeffMensuel) || coeffMensuel <= 0) { + console.error('❌ Coefficient mensuel invalide:', coeffMensuel); + return 0; + } + + // Si date référence avant date début + if (b2 < b1) return 0; + + // Si même mois et même année + if (b1.getFullYear() === b2.getFullYear() && b1.getMonth() === b2.getMonth()) { + const joursTotal = new Date(b2.getFullYear(), b2.getMonth() + 1, 0).getDate(); + const joursAcquis = b2.getDate() - b1.getDate() + 1; + return Math.round((joursAcquis / joursTotal) * coeffMensuel * 100) / 100; + } + + // 1. Fraction du PREMIER mois + const joursFinMoisB1 = new Date(b1.getFullYear(), b1.getMonth() + 1, 0).getDate(); + const jourB1 = b1.getDate(); + const fractionPremierMois = (joursFinMoisB1 - jourB1 + 1) / joursFinMoisB1; + + // 2. Mois COMPLETS entre + const moisComplets = dateDifMonths(b1, b2) - 1; + + // 3. Fraction du DERNIER mois + const joursFinMoisB2 = new Date(b2.getFullYear(), b2.getMonth() + 1, 0).getDate(); + const jourB2 = b2.getDate(); + const fractionDernierMois = jourB2 / joursFinMoisB2; + + // 4. Total + const totalMois = fractionPremierMois + Math.max(0, moisComplets) + fractionDernierMois; + const acquisition = totalMois * coeffMensuel; + + return Math.round(acquisition * 100) / 100; + } + + /** + * Récupère la configuration RTT pour une année et un type de contrat + */ + async function getConfigurationRTT(annee, typeContrat = '37h') { + try { + const result = await pool.request() + .input('annee', sql.Int, annee) + .input('typeContrat', sql.VarChar, typeContrat) + .query(` + SELECT JoursAnnuels, AcquisitionMensuelle + FROM ConfigurationRTT + WHERE Annee = @annee AND TypeContrat = @typeContrat + `); + + const config = result.recordset; + + if (config.length > 0) { + return { + joursAnnuels: parseFloat(config[0].JoursAnnuels), + acquisitionMensuelle: parseFloat(config[0].AcquisitionMensuelle) + }; + } + + // Sinon, utiliser les règles par défaut + return getConfigurationRTTDefaut(annee, typeContrat); + } catch (error) { + console.error('Erreur getConfigurationRTT:', error); + return getConfigurationRTTDefaut(annee, typeContrat); + } + } + + /** + * Configuration RTT par défaut selon les règles métier + */ + function getConfigurationRTTDefaut(annee, typeContrat = '37h') { + // 37h : toujours 10 RTT/an + if (typeContrat === '37h' || typeContrat === 'tempspartiel') { + return { + joursAnnuels: 10, + acquisitionMensuelle: 10 / 12 // 0.8333 + }; + } + + // Forfait jour : dépend de l'année + if (typeContrat === 'forfaitjour') { + if (annee <= 2025) { + // 2025 et avant : 10 RTT/an + return { + joursAnnuels: 10, + acquisitionMensuelle: 10 / 12 // 0.8333 + }; + } else { + // 2026 et après : 12 RTT/an + return { + joursAnnuels: 12, + acquisitionMensuelle: 12 / 12 // 1.0 + }; + } + } + + // Par défaut : 10 RTT/an + return { + joursAnnuels: 10, + acquisitionMensuelle: 10 / 12 + }; + } + + // Suite et fin - Réinitialisation compteurs, routes API V2, etc. + + // ======================================== + // ROUTE COMPLÈTE : /reinitializeAllCounters + // ======================================== + app.post('/reinitializeAllCounters', async (req, res) => { + const transaction = new sql.Transaction(pool); + try { + await transaction.begin(); + + console.log('🔄 Réinitialisation de tous les compteurs...'); + + const collaborateursResult = await transaction.request().query(` + SELECT id, prenom, nom, DateEntree, TypeContrat, CampusId, SocieteId, role + FROM CollaborateurAD + WHERE Actif = 1 OR Actif IS NULL + `); + + const collaborateurs = collaborateursResult.recordset; + + console.log(`📋 ${collaborateurs.length} collaborateurs trouvés`); + + const dateRefParam = req.body.dateReference; + const today = dateRefParam ? new Date(dateRefParam) : new Date(); + const currentYear = today.getFullYear(); + const previousYear = currentYear - 1; + const results = []; + + // Récupérer les types de congés + const cpTypeResult = await transaction.request() + .query("SELECT Id FROM TypeConge WHERE Nom = 'Congé payé'"); + const cpType = cpTypeResult.recordset; + + const rttTypeResult = await transaction.request() + .query("SELECT Id FROM TypeConge WHERE Nom = 'RTT'"); + const rttType = rttTypeResult.recordset; + + for (const collab of collaborateurs) { + console.log(`\n${'='.repeat(60)}`); + console.log(`👤 ${collab.prenom} ${collab.nom} (ID: ${collab.id})`); + console.log(` Type contrat: ${collab.TypeContrat || '37h'}`); + console.log(` Date entrée: ${collab.DateEntree ? new Date(collab.DateEntree).toISOString().split('T')[0] : 'NULL'}`); + console.log(` Rôle: ${collab.role || 'Non défini'}`); + + const dateEntree = collab.DateEntree; + const typeContrat = collab.TypeContrat || '37h'; + const isApprenti = collab.role === 'Apprenti'; + + // ========================================== + // CALCUL CP + // ========================================== + let acquisCP = calculerAcquisitionCP(today, dateEntree); + + if (isNaN(acquisCP) || !isFinite(acquisCP)) { + console.warn(`⚠️ Acquisition CP invalide - Forcé à 0`); + acquisCP = 0; + } + + // ========================================== + // CALCUL RTT + // ========================================== + let acquisRTT = 0; + if (!isApprenti) { + try { + const rttData = await calculerAcquisitionRTT(collab.id, today); + acquisRTT = rttData.acquisition; + + if (isNaN(acquisRTT) || !isFinite(acquisRTT)) { + console.warn(`⚠️ Acquisition RTT invalide - Forcé à 0`); + acquisRTT = 0; + } + } catch (error) { + console.error(`❌ Erreur calcul RTT:`, error.message); + acquisRTT = 0; + } + } + + // ========================================== + // MISE À JOUR CP N + // ========================================== + if (cpType.length > 0) { + const deductionsCPResult = await transaction.request() + .input('collabId', sql.Int, collab.id) + .input('typeId', sql.Int, cpType[0].Id) + .input('currentYear', sql.Int, currentYear) + .query(` + SELECT COALESCE(SUM(dd.JoursUtilises), 0) as totalConsomme + FROM DeductionDetails dd + JOIN DemandeConge dc ON dd.DemandeCongeId = dc.Id + WHERE dc.CollaborateurADId = @collabId + AND dd.TypeCongeId = @typeId + AND dd.Annee = @currentYear + AND dc.Statut != 'Refusé' + `); + + let totalConsomme = parseFloat(deductionsCPResult.recordset[0].totalConsomme || 0); + + if (isNaN(totalConsomme) || !isFinite(totalConsomme)) { + console.warn(`⚠️ Total consommé CP invalide - Forcé à 0`); + totalConsomme = 0; + } + + const compteurExistingResult = await transaction.request() + .input('collabId', sql.Int, collab.id) + .input('typeId', sql.Int, cpType[0].Id) + .input('currentYear', sql.Int, currentYear) + .query(` + SELECT SoldeReporte FROM CompteurConges + WHERE CollaborateurADId = @collabId + AND TypeCongeId = @typeId + AND Annee = @currentYear + `); + + const compteurExisting = compteurExistingResult.recordset; + + let soldeReporte = compteurExisting.length > 0 + ? parseFloat(compteurExisting[0].SoldeReporte || 0) + : 0; + + if (isNaN(soldeReporte) || !isFinite(soldeReporte)) { + console.warn(`⚠️ Solde reporté CP invalide - Forcé à 0`); + soldeReporte = 0; + } + + let nouveauSolde = Math.max(0, acquisCP + soldeReporte - totalConsomme); + + if (isNaN(nouveauSolde) || !isFinite(nouveauSolde)) { + console.warn(`⚠️ Nouveau solde CP invalide - Forcé à 0`); + nouveauSolde = 0; + } + + console.log(` 📊 CP - Acquis: ${acquisCP.toFixed(2)}j, Consommé: ${totalConsomme.toFixed(2)}j, Reporté: ${soldeReporte.toFixed(2)}j, Solde: ${nouveauSolde.toFixed(2)}j`); + + if (compteurExisting.length > 0) { + await transaction.request() + .input('total', sql.Decimal(10, 2), acquisCP) + .input('solde', sql.Decimal(10, 2), nouveauSolde) + .input('collabId', sql.Int, collab.id) + .input('typeId', sql.Int, cpType[0].Id) + .input('currentYear', sql.Int, currentYear) + .query(` + UPDATE CompteurConges + SET Total = @total, Solde = @solde, DerniereMiseAJour = GETDATE() + WHERE CollaborateurADId = @collabId AND TypeCongeId = @typeId AND Annee = @currentYear + `); + } else { + await transaction.request() + .input('collabId', sql.Int, collab.id) + .input('typeId', sql.Int, cpType[0].Id) + .input('currentYear', sql.Int, currentYear) + .input('total', sql.Decimal(10, 2), acquisCP) + .input('solde', sql.Decimal(10, 2), nouveauSolde) + .query(` + INSERT INTO CompteurConges + (CollaborateurADId, TypeCongeId, Annee, Total, Solde, SoldeReporte, DerniereMiseAJour) + VALUES (@collabId, @typeId, @currentYear, @total, @solde, 0, GETDATE()) + `); + } + + // Créer CP N-1 si nécessaire + const cpN1Result = await transaction.request() + .input('collabId', sql.Int, collab.id) + .input('typeId', sql.Int, cpType[0].Id) + .input('previousYear', sql.Int, previousYear) + .query(` + SELECT Id FROM CompteurConges + WHERE CollaborateurADId = @collabId AND TypeCongeId = @typeId AND Annee = @previousYear + `); + + const cpN1 = cpN1Result.recordset; + + if (cpN1.length === 0) { + await transaction.request() + .input('collabId', sql.Int, collab.id) + .input('typeId', sql.Int, cpType[0].Id) + .input('previousYear', sql.Int, previousYear) + .query(` + INSERT INTO CompteurConges + (CollaborateurADId, TypeCongeId, Annee, Total, Solde, SoldeReporte, DerniereMiseAJour) + VALUES (@collabId, @typeId, @previousYear, 0, 0, 0, GETDATE()) + `); + } + } + + // ========================================== + // MISE À JOUR RTT N + // ========================================== + if (rttType.length > 0 && !isApprenti) { + const deductionsRTTResult = await transaction.request() + .input('collabId', sql.Int, collab.id) + .input('typeId', sql.Int, rttType[0].Id) + .input('currentYear', sql.Int, currentYear) + .query(` + SELECT COALESCE(SUM(dd.JoursUtilises), 0) as totalConsomme + FROM DeductionDetails dd + JOIN DemandeConge dc ON dd.DemandeCongeId = dc.Id + WHERE dc.CollaborateurADId = @collabId + AND dd.TypeCongeId = @typeId + AND dd.Annee = @currentYear + AND dc.Statut != 'Refusé' + `); + + let totalConsomme = parseFloat(deductionsRTTResult.recordset[0].totalConsomme || 0); + + if (isNaN(totalConsomme) || !isFinite(totalConsomme)) { + console.warn(`⚠️ Total consommé RTT invalide - Forcé à 0`); + totalConsomme = 0; + } + + let nouveauSolde = Math.max(0, acquisRTT - totalConsomme); + + if (isNaN(nouveauSolde) || !isFinite(nouveauSolde)) { + console.warn(`⚠️ Nouveau solde RTT invalide - Forcé à 0`); + nouveauSolde = 0; + } + + console.log(` 📊 RTT - Acquis: ${acquisRTT.toFixed(2)}j, Consommé: ${totalConsomme.toFixed(2)}j, Solde: ${nouveauSolde.toFixed(2)}j`); + + const compteurExistingResult = await transaction.request() + .input('collabId', sql.Int, collab.id) + .input('typeId', sql.Int, rttType[0].Id) + .input('currentYear', sql.Int, currentYear) + .query(` + SELECT Id FROM CompteurConges + WHERE CollaborateurADId = @collabId AND TypeCongeId = @typeId AND Annee = @currentYear + `); + + const compteurExisting = compteurExistingResult.recordset; + + if (compteurExisting.length > 0) { + await transaction.request() + .input('total', sql.Decimal(10, 2), acquisRTT) + .input('solde', sql.Decimal(10, 2), nouveauSolde) + .input('id', sql.Int, compteurExisting[0].Id) + .query(` + UPDATE CompteurConges + SET Total = @total, Solde = @solde, DerniereMiseAJour = GETDATE() + WHERE Id = @id + `); + } else { + await transaction.request() + .input('collabId', sql.Int, collab.id) + .input('typeId', sql.Int, rttType[0].Id) + .input('currentYear', sql.Int, currentYear) + .input('total', sql.Decimal(10, 2), acquisRTT) + .input('solde', sql.Decimal(10, 2), nouveauSolde) + .query(` + INSERT INTO CompteurConges + (CollaborateurADId, TypeCongeId, Annee, Total, Solde, SoldeReporte, DerniereMiseAJour) + VALUES (@collabId, @typeId, @currentYear, @total, @solde, 0, GETDATE()) + `); + } + + // Créer RTT N-1 si nécessaire + const rttN1Result = await transaction.request() + .input('collabId', sql.Int, collab.id) + .input('typeId', sql.Int, rttType[0].Id) + .input('previousYear', sql.Int, previousYear) + .query(` + SELECT Id FROM CompteurConges + WHERE CollaborateurADId = @collabId AND TypeCongeId = @typeId AND Annee = @previousYear + `); + + const rttN1 = rttN1Result.recordset; + + if (rttN1.length === 0) { + await transaction.request() + .input('collabId', sql.Int, collab.id) + .input('typeId', sql.Int, rttType[0].Id) + .input('previousYear', sql.Int, previousYear) + .query(` + INSERT INTO CompteurConges + (CollaborateurADId, TypeCongeId, Annee, Total, Solde, SoldeReporte, DerniereMiseAJour) + VALUES (@collabId, @typeId, @previousYear, 0, 0, 0, GETDATE()) + `); + } + } + + // Ajouter au résultat + results.push({ + collaborateur: `${collab.prenom} ${collab.nom}`, + typecontrat: typeContrat, + cpacquis: acquisCP.toFixed(2), + rttacquis: acquisRTT.toFixed(2) + }); + } + + await transaction.commit(); + console.log('\n✅ Réinitialisation terminée'); + + res.json({ + success: true, + message: `Compteurs réinitialisés pour ${collaborateurs.length} collaborateurs`, + datereference: today.toISOString().split('T')[0], + totalcollaborateurs: collaborateurs.length, + results: results + }); + + } catch (error) { + await transaction.rollback(); + console.error('❌ Erreur réinitialisation:', error); + res.status(500).json({ + success: false, + message: 'Erreur lors de la réinitialisation', + error: error.message + }); + } + }); + + // ============================================================ + // ENDPOINTS API V2 + // ============================================================ + + app.get('/v2/compteurs', async (req, res) => { + try { + const result = await pool.request().query(` + SELECT + cc.Id AS id, + cc.CollaborateurADId AS collaborateurId, + cc.TypeCongeId AS typeCongeId, + CONCAT(c.nom, ' ', c.prenom) AS employe, + c.email AS email, + c.service AS service, + tc.Nom AS typeConge, + cc.Annee AS annee, + cc.Total AS total, + cc.Solde AS solde, + cc.SoldeReporte AS soldeReporte, + (cc.Total - cc.Solde) AS consomme, + c.TypeContrat AS typeContrat, + c.role AS role + FROM CompteurConges cc + INNER JOIN CollaborateurAD c ON cc.CollaborateurADId = c.id + INNER JOIN TypeConge tc ON cc.TypeCongeId = tc.Id + WHERE (c.description IS NULL OR c.description NOT LIKE '%stagiaire%') + ORDER BY c.nom ASC, cc.Annee DESC + `); + + return res.json(result.recordset); + + } catch (error) { + console.error("❌ Erreur GET compteurs V2 :", error); + return res.status(500).json({ message: "Erreur serveur V2" }); + } + }); + + app.put('/v2/compteurs/update', async (req, res) => { + try { + const { + collaborateurId, + typeConge, + annee, + nouveauTotal, + nouveauSolde, + source + } = req.body; + + if (!collaborateurId || !typeConge || !annee) { + return res.status(400).json({ message: "Champs manquants" }); + } + + console.log('📝 Modification compteur V2:', { collaborateurId, typeConge, annee, nouveauTotal, nouveauSolde }); + + // Mapper le nom exact du type de congé + let nomTypeConge; + switch (typeConge) { + case 'CP': + nomTypeConge = 'Congé payé'; + break; + case 'RTT': + nomTypeConge = 'RTT'; + break; + case 'Récupération': + case 'Recuperation': + case 'recup': + nomTypeConge = 'Récupération'; + break; + default: + nomTypeConge = typeConge; + } + + console.log(` Recherche du type: "${nomTypeConge}"`); + + // Trouver l'ID du type de congé + const typeRowResult = await pool.request() + .input('nomTypeConge', sql.NVarChar, nomTypeConge) + .query("SELECT Id FROM TypeConge WHERE Nom = @nomTypeConge"); + + const typeRow = typeRowResult.recordset; + + if (typeRow.length === 0) { + console.error(`❌ Type de congé "${nomTypeConge}" introuvable`); + return res.status(404).json({ + message: `Type de congé "${nomTypeConge}" introuvable`, + nomRecherche: nomTypeConge + }); + } + + const typeCongeId = typeRow[0].Id; + console.log(` ✅ Type trouvé - ID: ${typeCongeId}`); + + // Trouver compteur + const compResult = await pool.request() + .input('collaborateurId', sql.Int, collaborateurId) + .input('typeCongeId', sql.Int, typeCongeId) + .input('annee', sql.Int, annee) + .query(` + SELECT Id FROM CompteurConges + WHERE CollaborateurADId = @collaborateurId + AND TypeCongeId = @typeCongeId + AND Annee = @annee + `); + + const comp = compResult.recordset; + + if (comp.length === 0) { + console.error(`❌ Compteur introuvable pour collaborateur ${collaborateurId}, type ${typeCongeId}, année ${annee}`); + return res.status(404).json({ + message: "Compteur introuvable", + details: { collaborateurId, typeCongeId, annee } + }); + } + + // Mise à jour compteur + await pool.request() + .input('total', sql.Decimal(10, 2), nouveauTotal) + .input('solde', sql.Decimal(10, 2), nouveauSolde) + .input('id', sql.Int, comp[0].Id) + .query('UPDATE CompteurConges SET Total = @total, Solde = @solde, DerniereMiseAJour = GETDATE() WHERE Id = @id'); + + console.log(` ✅ Compteur mis à jour - Total: ${nouveauTotal}, Solde: ${nouveauSolde}`); + + // Notifier les clients SSE + notifyClients({ + type: 'compteur-updated', + collaborateurId: collaborateurId, + typeConge: nomTypeConge, + annee: annee, + nouveauTotal: nouveauTotal, + nouveauSolde: nouveauSolde, + source: source || 'RH_UI', + timestamp: new Date().toISOString() + }, collaborateurId); + + // Envoyer webhook + try { + if (typeof webhookManager !== 'undefined' && WEBHOOKS && EVENTS) { + await webhookManager.sendWebhook( + WEBHOOKS.COLLABORATEURS_URL, + EVENTS.COMPTEUR_UPDATED, + { + collaborateurId: collaborateurId, + typeConge: nomTypeConge, + typeUpdate: 'modification_manuelle_rh', + annee: annee, + nouveauTotal: nouveauTotal, + nouveauSolde: nouveauSolde, + source: source || 'RH_UI', + timestamp: new Date().toISOString() + } + ); + console.log(' ✅ Webhook envoyé au serveur collaborateurs'); + } + } catch (webhookError) { + console.error('⚠️ Erreur webhook (non bloquant):', webhookError.message); + } + + res.json({ + success: true, + message: "Compteur mis à jour (V2)", + details: { + typeConge: nomTypeConge, + typeCongeId: typeCongeId, + nouveauTotal: nouveauTotal, + nouveauSolde: nouveauSolde + } + }); + + } catch (err) { + console.error("❌ Erreur update compteur V2:", err); + res.status(500).json({ + message: "Erreur serveur", + error: err.message + }); + } + }); + + // ================================================ + // ROUTE POUR SAISIR UN JPOSF + // ================================================ + app.post('/compteurs/saisir-jposf', authenticateToken, async (req, res) => { + const transaction = new sql.Transaction(pool); + + try { + console.log('\n📥 ========== DÉBUT SAISIE JPOSF =========='); + console.log('📥 Body reçu:', JSON.stringify(req.body, null, 2)); + console.log('📥 User:', req.user); + + await transaction.begin(); + console.log('✅ Transaction démarrée'); + + // Vérifier les permissions + if (!['Admin', 'RH'].includes(req.user.role)) { + console.log('❌ Accès refusé - Role:', req.user.role); + await transaction.rollback(); + return res.status(403).json({ + success: false, + error: 'Accès non autorisé', + message: 'Seuls les Admin et RH peuvent saisir des JPOSF' + }); + } + + const { collaborateur_id, date, periode_journee, commentaire } = req.body; + + // Validation des données + if (!collaborateur_id) { + console.log('❌ collaborateur_id manquant'); + await transaction.rollback(); + return res.status(400).json({ + success: false, + message: 'ID du collaborateur manquant' + }); + } + + if (!date) { + console.log('❌ date manquante'); + await transaction.rollback(); + return res.status(400).json({ + success: false, + message: 'Date manquante' + }); + } + + if (!periode_journee) { + console.log('❌ periode_journee manquante'); + await transaction.rollback(); + return res.status(400).json({ + success: false, + message: 'Période de journée manquante' + }); + } + + console.log('📋 Données validées:', { collaborateur_id, date, periode_journee }); + + // ========== ÉTAPE 1: Récupérer le collaborateur ========== + console.log('\n🔍 ÉTAPE 1: Récupération du collaborateur...'); + + const collabResult = await transaction.request() + .input('id', sql.Int, parseInt(collaborateur_id)) + .query('SELECT id, prenom, nom, email, Actif FROM CollaborateurAD WHERE id = @id'); + + console.log(' Résultat:', collabResult.recordset.length, 'enregistrement(s)'); + + if (collabResult.recordset.length === 0) { + console.log('❌ Collaborateur non trouvé'); + await transaction.rollback(); + return res.status(404).json({ + success: false, + message: `Collaborateur ID ${collaborateur_id} non trouvé` + }); + } + + const collab = collabResult.recordset[0]; + console.log(' ✅ Collaborateur trouvé:', collab.prenom, collab.nom); + + if (collab.Actif === false || collab.Actif === 0) { + console.log('❌ Collaborateur inactif'); + await transaction.rollback(); + return res.status(403).json({ + success: false, + message: 'Impossible d\'ajouter des jours à un compte désactivé' + }); + } + + const currentYear = new Date().getFullYear(); + const nombreJours = periode_journee === 'Journée entière' ? 1 : 0.5; + + console.log(' Année:', currentYear); + console.log(' Nombre de jours:', nombreJours); + + // ========== ÉTAPE 2: Récupérer les types de congé ========== + console.log('\n🔍 ÉTAPE 2: Récupération des types de congé...'); + + const jposfTypeResult = await transaction.request() + .query("SELECT Id, Nom FROM TypeConge WHERE Nom = 'JPOSF'"); + + console.log(' JPOSF trouvé:', jposfTypeResult.recordset.length > 0); + + if (jposfTypeResult.recordset.length === 0) { + console.log('❌ Type JPOSF non trouvé dans TypeConge'); + + // Lister les types disponibles pour debug + const allTypesResult = await transaction.request() + .query("SELECT Id, Nom FROM TypeConge WHERE Actif = 1 OR Actif IS NULL"); + console.log(' Types disponibles:', allTypesResult.recordset.map(t => t.Nom).join(', ')); + + await transaction.rollback(); + return res.status(500).json({ + success: false, + message: 'Type de congé "JPOSF" non trouvé dans la base de données', + details: 'Contactez l\'administrateur pour créer ce type de congé' + }); + } + + const recupTypeResult = await transaction.request() + .query("SELECT Id, Nom FROM TypeConge WHERE Nom = 'Récupération'"); + + console.log(' Récupération trouvé:', recupTypeResult.recordset.length > 0); + + if (recupTypeResult.recordset.length === 0) { + console.log('❌ Type Récupération non trouvé dans TypeConge'); + await transaction.rollback(); + return res.status(500).json({ + success: false, + message: 'Type de congé "Récupération" non trouvé dans la base de données', + details: 'Contactez l\'administrateur pour créer ce type de congé' + }); + } + + const jposfTypeId = jposfTypeResult.recordset[0].Id; + const recupTypeId = recupTypeResult.recordset[0].Id; + + console.log(' ✅ JPOSF TypeId:', jposfTypeId); + console.log(' ✅ Récupération TypeId:', recupTypeId); + + // ========== ÉTAPE 3: Mettre à jour le compteur Récupération ========== + console.log('\n🔍 ÉTAPE 3: Mise à jour du compteur Récupération...'); + + const compteurRecupResult = await transaction.request() + .input('collaborateurId', sql.Int, parseInt(collaborateur_id)) + .input('recupTypeId', sql.Int, recupTypeId) + .input('currentYear', sql.Int, currentYear) + .query(` + SELECT Id, Total, Solde FROM CompteurConges + WHERE CollaborateurADId = @collaborateurId + AND TypeCongeId = @recupTypeId + AND Annee = @currentYear + `); + + let nouveauTotalRecup, nouveauSoldeRecup; + + if (compteurRecupResult.recordset.length > 0) { + const compteur = compteurRecupResult.recordset[0]; + nouveauTotalRecup = parseFloat(compteur.Total || 0) + nombreJours; + nouveauSoldeRecup = parseFloat(compteur.Solde || 0) + nombreJours; + + console.log(' Compteur existant - Ancien Total:', compteur.Total, '-> Nouveau:', nouveauTotalRecup); + console.log(' Compteur existant - Ancien Solde:', compteur.Solde, '-> Nouveau:', nouveauSoldeRecup); + + await transaction.request() + .input('total', sql.Decimal(10, 2), nouveauTotalRecup) + .input('solde', sql.Decimal(10, 2), nouveauSoldeRecup) + .input('id', sql.Int, compteur.Id) + .query('UPDATE CompteurConges SET Total = @total, Solde = @solde, DerniereMiseAJour = GETDATE() WHERE Id = @id'); + + console.log(' ✅ Compteur Récupération mis à jour'); + } else { + nouveauTotalRecup = nombreJours; + nouveauSoldeRecup = nombreJours; + + console.log(' Création nouveau compteur - Total:', nouveauTotalRecup, 'Solde:', nouveauSoldeRecup); + + await transaction.request() + .input('collaborateurId', sql.Int, parseInt(collaborateur_id)) + .input('recupTypeId', sql.Int, recupTypeId) + .input('currentYear', sql.Int, currentYear) + .input('total', sql.Decimal(10, 2), nouveauTotalRecup) + .input('solde', sql.Decimal(10, 2), nouveauSoldeRecup) + .query(` + INSERT INTO CompteurConges + (CollaborateurADId, TypeCongeId, Annee, Total, Solde, SoldeReporte, DerniereMiseAJour) + VALUES (@collaborateurId, @recupTypeId, @currentYear, @total, @solde, 0, GETDATE()) + `); + + console.log(' ✅ Compteur Récupération créé'); + } + + // ========== ÉTAPE 4: Créer la demande JPOSF ========== + console.log('\n🔍 ÉTAPE 4: Création de la demande JPOSF...'); + + // Préparer le commentaire + const commentaireFinal = commentaire && commentaire.trim() + ? commentaire.trim() + : `JPOSF - ${periode_journee}`; + + console.log(' Commentaire:', commentaireFinal); + console.log(' Date:', date); + console.log(' ValidateurId:', req.user.id); + + // ⭐ MÉTHODE ROBUSTE: INSERT puis SELECT séparé + try { + // D'abord, faire l'INSERT sans OUTPUT + await transaction.request() + .input('collaborateurId', sql.Int, parseInt(collaborateur_id)) + .input('dateDebut', sql.Date, date) + .input('dateFin', sql.Date, date) + .input('commentaire', sql.NVarChar(500), commentaireFinal) + .input('totalJours', sql.Decimal(10, 2), nombreJours) + .input('validateurId', sql.Int, req.user.id) + .query(` + INSERT INTO DemandeConge + (CollaborateurADId, DateDebut, DateFin, Statut, Commentaire, DateDemande, NombreJours, ValidateurADId, DateValidation) + VALUES (@collaborateurId, @dateDebut, @dateFin, 'Validée', @commentaire, GETDATE(), @totalJours, @validateurId, GETDATE()) + `); + + console.log(' ✅ INSERT DemandeConge réussi'); + + // Ensuite, récupérer l'ID avec SCOPE_IDENTITY ou en cherchant le dernier enregistrement + const lastIdResult = await transaction.request() + .query('SELECT SCOPE_IDENTITY() AS Id'); + + let demandeId = lastIdResult.recordset[0]?.Id; + + // Si SCOPE_IDENTITY ne fonctionne pas, fallback + if (!demandeId) { + console.log(' ⚠️ SCOPE_IDENTITY() a retourné NULL, utilisation du fallback...'); + + const fallbackResult = await transaction.request() + .input('collaborateurId', sql.Int, parseInt(collaborateur_id)) + .input('dateDebut', sql.Date, date) + .query(` + SELECT TOP 1 Id FROM DemandeConge + WHERE CollaborateurADId = @collaborateurId + AND DateDebut = @dateDebut + AND Statut = 'Validée' + ORDER BY DateDemande DESC, Id DESC + `); + + demandeId = fallbackResult.recordset[0]?.Id; + } + + console.log(' DemandeId récupéré:', demandeId); + + if (!demandeId) { + throw new Error('Impossible de récupérer l\'ID de la demande créée'); + } + + // ========== ÉTAPE 5: Lier avec le type JPOSF ========== + console.log('\n🔍 ÉTAPE 5: Liaison avec le type JPOSF...'); + + await transaction.request() + .input('demandeId', sql.Int, demandeId) + .input('typeId', sql.Int, jposfTypeId) + .input('nombreJours', sql.Decimal(10, 2), nombreJours) + .input('periode', sql.NVarChar(50), periode_journee) // ⭐ AJOUTÉ + .query('INSERT INTO DemandeCongeType (Id, DemandeCongeId, TypeCongeId, NombreJours, PeriodeJournee) VALUES (@id, @demandeId, @typeId, @nombreJours, @periode)'); + + + console.log(' ✅ DemandeCongeType créé'); + + // ========== ÉTAPE 6: Historique ========== + console.log('\n🔍 ÉTAPE 6: Enregistrement dans l\'historique...'); + + await transaction.request() + .input('userId', sql.Int, req.user.id) + .input('details', sql.NVarChar(500), `Saisie JPOSF - ${date} (${periode_journee}) : +${nombreJours}j pour ${collab.prenom} ${collab.nom}`) + .input('demandeId', sql.Int, demandeId) + .query(` + INSERT INTO HistoriqueActions (CollaborateurADId, Action, Details, DemandeCongeId, DateAction) + VALUES (@userId, 'Saisie JPOSF', @details, @demandeId, GETDATE()) + `); + + console.log(' ✅ Historique enregistré'); + + // ========== COMMIT ========== + await transaction.commit(); + console.log('\n✅ ========== TRANSACTION COMMIT =========='); + + // ========== Notifications SSE ========== + try { + notifyClients({ + type: 'demande-validated', + demandeId: demandeId, + statut: 'Validée', + typeConge: 'JPOSF', + couleurHex: '#d946ef', + collaborateurId: parseInt(collaborateur_id), + timestamp: new Date().toISOString() + }, parseInt(collaborateur_id)); + + notifyClients({ + type: 'compteur-updated', + collaborateurId: parseInt(collaborateur_id), + typeConge: 'Récupération', + action: 'saisie_jposf_rh', + nouveauSolde: nouveauSoldeRecup, + timestamp: new Date().toISOString() + }, parseInt(collaborateur_id)); + + console.log('✅ Notifications SSE envoyées'); + } catch (sseError) { + console.error('⚠️ Erreur SSE (non bloquant):', sseError.message); + } + + // ========== Webhooks ========== + try { + if (typeof webhookManager !== 'undefined' && WEBHOOKS && EVENTS) { + await webhookManager.sendWebhook( + WEBHOOKS.COLLABORATEURS_URL, + EVENTS.DEMANDE_VALIDATED, + { + demandeId: demandeId, + statut: 'Validée', + typeConge: 'JPOSF', + collaborateurId: parseInt(collaborateur_id), + validateurId: req.user.id + } + ); + + await webhookManager.sendWebhook( + WEBHOOKS.COLLABORATEURS_URL, + EVENTS.COMPTEUR_UPDATED, + { + collaborateurId: parseInt(collaborateur_id), + typeConge: 'Récupération', + typeUpdate: 'saisie_jposf' + } + ); + console.log('✅ Webhooks envoyés'); + } + } catch (webhookError) { + console.error('⚠️ Erreur webhook (non bloquant):', webhookError.message); + } + + // ========== Email ========== + try { + const accessToken = await getGraphToken(); + if (accessToken && collab.email) { + const fromEmail = 'gtanoreply@ensup.eu'; + const subject = '📅 JPOSF enregistré'; + const body = ` +
+
+

📅 JPOSF enregistré

+
+
+

Bonjour ${collab.prenom} ${collab.nom},

+

Une Journée Portes Ouvertes a été enregistrée pour vous :

+
+

Date : ${new Date(date).toLocaleDateString('fr-FR')}

+

Période : ${periode_journee}

+

Jours ajoutés : ${nombreJours}j

+ ${commentaire ? `

Commentaire : ${commentaire}

` : ''} +
+

+${nombreJours}j ont été ajoutés à votre compteur de récupération

+
+
+ `; + + await sendMailGraph(accessToken, fromEmail, collab.email, subject, body); + console.log('✅ Email envoyé à', collab.email); + } + } catch (emailError) { + console.error('⚠️ Erreur email (non bloquant):', emailError.message); + } + + // ========== Réponse succès ========== + console.log('\n✅ ========== JPOSF ENREGISTRÉ AVEC SUCCÈS ==========\n'); + + return res.json({ + success: true, + message: 'JPOSF enregistré avec succès', + collaborateur: `${collab.prenom} ${collab.nom}`, + date: date, + periode: periode_journee, + jours_ajoutes: nombreJours, + nouveau_solde_recup: nouveauSoldeRecup, + demandeId: demandeId + }); + + } catch (insertError) { + console.error('\n❌ ERREUR lors de l\'INSERT DemandeConge:', insertError.message); + console.error(' Stack:', insertError.stack); + throw insertError; + } + + } catch (error) { + console.error('\n❌ ========== ERREUR SAISIE JPOSF =========='); + console.error('Message:', error.message); + console.error('Stack:', error.stack); + + try { + await transaction.rollback(); + console.log('✅ Transaction rollback effectué'); + } catch (rollbackError) { + console.error('⚠️ Erreur rollback:', rollbackError.message); + } + + return res.status(500).json({ + success: false, + message: 'Erreur serveur lors de la saisie du JPOSF', + error: error.message, + details: process.env.NODE_ENV === 'development' ? error.stack : undefined + }); + } + }); + + // ✅ CORRECTION COMPLÈTE - Route /getAllDetailedCounters + app.get('/getAllDetailedCounters', async (req, res) => { + try { + console.log('📊 Récupération de TOUS les compteurs détaillés'); + + const today = new Date(); + const currentYearSys = today.getFullYear(); // 2026 + + // ── Détection bascule CP ────────────────────────────────────────── + const cpTypeForCheck = await pool.request() + .query("SELECT TOP 1 Id FROM TypeConge WHERE Nom = 'Congé payé'"); + + let currentYear = currentYearSys; // CP N (2026 ou 2027 après bascule) + let previousYear = currentYearSys - 1; // CP N-1 + + if (cpTypeForCheck.recordset.length > 0) { + const cpTypeId = cpTypeForCheck.recordset[0].Id; + const checkBascule = await pool.request() + .input('cpTypeId', sql.Int, cpTypeId) + .input('anneeN1', sql.Int, currentYearSys + 1) + .query(` + SELECT TOP 1 Id FROM CompteurConges + WHERE TypeCongeId = @cpTypeId + AND Annee = @anneeN1 + AND SoldeReporte > 0 + `); + + if (checkBascule.recordset.length > 0) { + currentYear = currentYearSys + 1; // 2027 + previousYear = currentYearSys; // 2026 + console.log('✅ Bascule CP détectée — currentYear=2027, previousYear=2026'); + } + } + + // RTT et Récup : toujours l'année civile système (jamais basculent) + const anneeRTT = currentYearSys; // 2026 + const anneeRecup = currentYearSys; // 2026 + + console.log(`📅 CP N=${currentYear}, CP N-1=${previousYear}, RTT=${anneeRTT}, Récup=${anneeRecup}`); + + const typesCongeResult = await pool.request().query(` + SELECT Id, Nom FROM TypeConge WHERE Nom IN ('Congé payé', 'RTT', 'Récupération') + `); + + const typeCongeMap = {}; + typesCongeResult.recordset.forEach(tc => { + typeCongeMap[tc.Nom] = tc.Id; + }); + + const collaborateursResult = await pool.request().query(` + SELECT + ca.id, + ca.prenom, + ca.nom, + ca.email, + ca.role, + ca.TypeContrat, + ca.DateEntree, + COALESCE(s.Nom, 'Non assigné') as service, + s.Id as serviceId, + ca.CampusId as campusId, + COALESCE(camp.Nom, 'Sans campus') as campusNom, + ca.SocieteId as societeId, + COALESCE(soc.Nom, 'Sans société') as societe + FROM CollaborateurAD ca + LEFT JOIN Services s ON ca.ServiceId = s.Id + LEFT JOIN Campus camp ON ca.CampusId = camp.Id + LEFT JOIN Societe soc ON ca.SocieteId = soc.Id + WHERE (ca.Actif = 1 OR ca.Actif IS NULL) + AND (ca.description IS NULL OR ca.description NOT LIKE '%stagiaire%') + ORDER BY soc.Nom, camp.Nom, s.Nom, ca.nom, ca.prenom + `); + + const collaborateurs = collaborateursResult.recordset; + const resultats = []; + + console.log(`📋 ${collaborateurs.length} collaborateurs trouvés`); + + for (const collab of collaborateurs) { + const campusNom = collab.campusNom; + + // ==================== CP N ==================== + if (typeCongeMap['Congé payé']) { + const cpNResult = await pool.request() + .input('collabId', sql.Int, collab.id) + .input('typeId', sql.Int, typeCongeMap['Congé payé']) + .input('annee', sql.Int, currentYear) + .query(` + SELECT Total, Solde + FROM CompteurConges + WHERE CollaborateurADId = @collabId + AND TypeCongeId = @typeId + AND Annee = @annee + `); + + const cpN = cpNResult.recordset; + + let cpTotalN, cpSoldeN; + if (cpN.length > 0) { + cpTotalN = parseFloat(cpN[0].Total); + cpSoldeN = parseFloat(cpN[0].Solde); + } else { + // Après bascule, si pas de ligne en base → 0 + // Avant bascule, on calcule l'acquisition + if (currentYear > currentYearSys) { + cpTotalN = 0; + cpSoldeN = 0; + } else { + cpTotalN = calculerAcquisitionCP(new Date(), collab.DateEntree); + const consommeResult = await pool.request() + .input('collabId', sql.Int, collab.id) + .input('typeId', sql.Int, typeCongeMap['Congé payé']) + .input('annee', sql.Int, currentYear) + .query(` + SELECT COALESCE(SUM(JoursUtilises), 0) AS total + FROM DeductionDetails dd + JOIN DemandeConge dc ON dc.Id = dd.DemandeCongeId + WHERE dc.CollaborateurADId = @collabId + AND dd.TypeCongeId = @typeId + AND dd.Annee = @annee + AND dd.TypeDeduction NOT IN ('Accum. Récup', 'N Anticipé') + AND dc.Statut != 'Refusé' + `); + cpSoldeN = Math.max(0, cpTotalN - consommeResult.recordset[0].total); + } + } + + // ⭐ PUSH CP N (était manquant — cause du bug d'affichage) + resultats.push({ + collaborateurId: collab.id, + employe: `${collab.prenom} ${collab.nom}`, + email: collab.email, + service: collab.service, + campus: campusNom, + societe: collab.societe, + typeCongeId: typeCongeMap['Congé payé'], + typeConge: 'Congé payé', + annee: currentYear, + total: parseFloat(cpTotalN.toFixed(2)), + solde: parseFloat(cpSoldeN.toFixed(2)), + consomme: parseFloat((cpTotalN - cpSoldeN).toFixed(2)), + role: collab.role, + typeContrat: collab.TypeContrat, + estApprenti: collab.role === 'Apprenti' || collab.TypeContrat === 'Apprentissage' + }); + + // ==================== CP N-1 ==================== + const cpN1Result = await pool.request() + .input('collabId', sql.Int, collab.id) + .input('typeId', sql.Int, typeCongeMap['Congé payé']) + .input('annee', sql.Int, previousYear) + .query(` + SELECT Total, Solde + FROM CompteurConges + WHERE CollaborateurADId = @collabId + AND TypeCongeId = @typeId + AND Annee = @annee + `); + + const cpN1 = cpN1Result.recordset; + + if (cpN1.length > 0 && cpN1[0].Solde > 0) { + const total = parseFloat(cpN1[0].Total); + const solde = parseFloat(cpN1[0].Solde); + resultats.push({ + collaborateurId: collab.id, + employe: `${collab.prenom} ${collab.nom}`, + email: collab.email, + service: collab.service, + campus: campusNom, + societe: collab.societe, + typeCongeId: typeCongeMap['Congé payé'], + typeConge: 'Congé payé', + annee: previousYear, + total, + solde, + consomme: parseFloat((total - solde).toFixed(2)), + role: collab.role, + typeContrat: collab.TypeContrat, + estApprenti: collab.role === 'Apprenti' || collab.TypeContrat === 'Apprentissage' + }); + } + } + + // ==================== RTT — toujours anneeRTT (2026) ==================== + if (typeCongeMap['RTT'] && collab.role !== 'Apprenti' && collab.TypeContrat !== 'Apprentissage') { + const rttNResult = await pool.request() + .input('collabId', sql.Int, collab.id) + .input('typeId', sql.Int, typeCongeMap['RTT']) + .input('annee', sql.Int, anneeRTT) + .query(` + SELECT Total, Solde + FROM CompteurConges + WHERE CollaborateurADId = @collabId + AND TypeCongeId = @typeId + AND Annee = @annee + `); + + const rttN = rttNResult.recordset; + + let rttTotalN, rttSoldeN; + if (rttN.length > 0) { + rttTotalN = parseFloat(rttN[0].Total); + rttSoldeN = parseFloat(rttN[0].Solde); + } else { + const rtt = await calculerAcquisitionRTT(collab.id, new Date()); + const consommeRTTResult = await pool.request() + .input('collabId', sql.Int, collab.id) + .input('typeId', sql.Int, typeCongeMap['RTT']) + .input('annee', sql.Int, anneeRTT) + .query(` + SELECT COALESCE(SUM(JoursUtilises), 0) AS total + FROM DeductionDetails dd + JOIN DemandeConge dc ON dc.Id = dd.DemandeCongeId + WHERE dc.CollaborateurADId = @collabId + AND dd.TypeCongeId = @typeId + AND dd.Annee = @annee + AND dd.TypeDeduction NOT IN ('Accum. Récup', 'Récup Dosée') + AND dc.Statut != 'Refusé' + `); + rttTotalN = rtt.acquisition; + rttSoldeN = Math.max(0, rttTotalN - consommeRTTResult.recordset[0].total); + } + + resultats.push({ + collaborateurId: collab.id, + employe: `${collab.prenom} ${collab.nom}`, + email: collab.email, + service: collab.service, + campus: campusNom, + societe: collab.societe, + typeCongeId: typeCongeMap['RTT'], + typeConge: 'RTT', + annee: anneeRTT, + total: parseFloat(rttTotalN.toFixed(2)), + solde: parseFloat(rttSoldeN.toFixed(2)), + consomme: parseFloat((rttTotalN - rttSoldeN).toFixed(2)), + role: collab.role, + typeContrat: collab.TypeContrat, + estApprenti: collab.role === 'Apprenti' || collab.TypeContrat === 'Apprentissage' + }); + } + + // ==================== Récupération — toujours anneeRecup (2026) ==================== + if (typeCongeMap['Récupération']) { + const recupNResult = await pool.request() + .input('collabId', sql.Int, collab.id) + .input('typeId', sql.Int, typeCongeMap['Récupération']) + .input('annee', sql.Int, anneeRecup) + .query(` + SELECT Total, Solde + FROM CompteurConges + WHERE CollaborateurADId = @collabId + AND TypeCongeId = @typeId + AND Annee = @annee + `); + + const recupN = recupNResult.recordset; + + if (recupN.length > 0) { + const recupTotal = parseFloat(recupN[0].Total); + const recupSolde = parseFloat(recupN[0].Solde); + + resultats.push({ + collaborateurId: collab.id, + employe: `${collab.prenom} ${collab.nom}`, + email: collab.email, + service: collab.service, + campus: campusNom, + societe: collab.societe, + typeCongeId: typeCongeMap['Récupération'], + typeConge: 'Récupération', + annee: anneeRecup, + total: recupTotal, + solde: recupSolde, + consomme: parseFloat((recupTotal - recupSolde).toFixed(2)), + role: collab.role, + typeContrat: collab.TypeContrat, + estApprenti: collab.role === 'Apprenti' || collab.TypeContrat === 'Apprentissage' + }); + } + } + } + + console.log(`✅ ${resultats.length} compteurs détaillés générés`); + res.json(resultats); + } catch (err) { + console.error('❌ Erreur GET ALL:', err); + res.status(500).json({ error: err.message }); + } + }); + + + + // ================================================ + // ROUTES POUR LA GESTION DES COLLABORATEURS - Adaptées à votre structure BDD + // ================================================ + + // GET /api/collaborateurs/details - Liste complète des collaborateurs avec toutes leurs infos + app.get('/collaborateurs/details', authenticateToken, async (req, res) => { + try { + if (!['Admin', 'RH', 'Validateur'].includes(req.user.role)) { + return res.status(403).json({ error: 'Accès non autorisé' }); + } + + const currentYear = new Date().getFullYear(); + const previousYear = currentYear - 1; + + const result = await pool.request() + .input('currentYear', sql.Int, currentYear) + .input('previousYear', sql.Int, previousYear) + .query(` + SELECT + ca.id, + ca.nom, + ca.prenom, + ca.email, + ca.fonction as poste, + ca.TypeContrat as typeContrat, + ca.DateEntree as dateEntree, + ca.actif as Actif, + ca.description, + + -- Société + COALESCE(soc.Nom, 'Sans société') as societe, + ca.SocieteId as societeId, + + -- Campus + COALESCE(camp.Nom, 'Sans campus') as campus, + ca.CampusId as campusId, + + -- Service + COALESCE(serv.Nom, 'Sans service') as service, + ca.ServiceId as serviceId, + + -- Hiérarchie - ✅ CORRECTION : Utiliser [SuperieurIdn+2] avec crochets + hv.SuperieurId as nPlus1Id, + CONCAT(n1.prenom, ' ', n1.nom) as nPlus1Nom, + hv.[SuperieurIdn+2] as nPlus2Id, -- ✅ CROCHETS pour le + + CONCAT(n2.prenom, ' ', n2.nom) as nPlus2Nom, + NULL as nPlus3Id, + NULL as nPlus3Nom, + + -- Compteurs + compteurs.soldeCPN, + compteurs.soldeCPNMoins1, + compteurs.soldeRTT, + compteurs.soldeRecup + + FROM CollaborateurAD ca + LEFT JOIN Societe soc ON ca.SocieteId = soc.Id + LEFT JOIN Campus camp ON ca.CampusId = camp.Id + LEFT JOIN Services serv ON ca.ServiceId = serv.Id + LEFT JOIN HierarchieValidationAD hv ON ca.id = hv.CollaborateurId + LEFT JOIN CollaborateurAD n1 ON hv.SuperieurId = n1.id + LEFT JOIN CollaborateurAD n2 ON hv.[SuperieurIdn+2] = n2.id -- ✅ CROCHETS + OUTER APPLY ( + SELECT + MAX(CASE WHEN tc.Nom = 'Congé payé' AND cc.Annee = @currentYear THEN cc.Solde END) as soldeCPN, + MAX(CASE WHEN tc.Nom = 'Congé payé' AND cc.Annee = @previousYear THEN cc.Solde END) as soldeCPNMoins1, + MAX(CASE WHEN tc.Nom = 'RTT' AND cc.Annee = @currentYear THEN cc.Solde END) as soldeRTT, + MAX(CASE WHEN tc.Nom = 'Récupération' AND cc.Annee = @currentYear THEN cc.Solde END) as soldeRecup + FROM CompteurConges cc + JOIN TypeConge tc ON cc.TypeCongeId = tc.Id + WHERE cc.CollaborateurADId = ca.id + AND tc.Nom IN ('Congé payé', 'RTT', 'Récupération') + AND cc.Annee IN (@currentYear, @previousYear) + ) compteurs + WHERE ca.actif = 1 OR ca.actif IS NULL + ORDER BY soc.Nom, camp.Nom, serv.Nom, ca.nom, ca.prenom + `); + + const collaborateurs = result.recordset.map(row => ({ + ...row, + soldeCPN: row.soldeCPN || 0, + soldeCPNMoins1: row.soldeCPNMoins1 || 0, + soldeRTT: row.soldeRTT || 0, + soldeRecup: row.soldeRecup || 0 + })); + + console.log(`✅ ${collaborateurs.length} collaborateurs récupérés avec détails`); + res.json(collaborateurs); + } catch (error) { + console.error('❌ Erreur /collaborateurs/details:', error); + res.status(500).json({ error: error.message }); + } + }); + + + + // GET /api/societes - Liste des sociétés + app.get('/societes', authenticateToken, async (req, res) => { + try { + const result = await pool.request().query(` + SELECT Id as id, Nom as nom + FROM Societe + ORDER BY Nom + `); + res.json(result.recordset); + } catch (error) { + console.error('❌ Erreur /societes:', error); + res.status(500).json({ error: error.message }); + } + }); + + const CAMPUS_MAPPING = { + 1: 'Cergy', + 2: 'Saint-Quentin-en-Yvelines', + 3: 'Marseille', + 4: 'Nantes' + // Ajouter les autres campus selon tes besoins + }; + + // GET /api/campus - Liste des campus + app.get('/campus', authenticateToken, async (req, res) => { + try { + const result = await pool.request().query(` + SELECT Id as id, Nom as nom + FROM Campus + ORDER BY Nom + `); + + console.log('✅ Campus récupérés:', result.recordset); + res.json(result.recordset); + } catch (error) { + console.error('❌ Erreur /campus:', error); + res.status(500).json({ error: error.message }); + } + }); + + + + + // GET /api/services - Liste des services + app.get('/services', authenticateToken, async (req, res) => { + try { + const result = await pool.request().query(` + SELECT Id as id, Nom as nom + FROM Services + ORDER BY Nom + `); + res.json(result.recordset); + } catch (error) { + console.error('❌ Erreur /services:', error); + res.status(500).json({ error: error.message }); + } + }); + + + // PUT /api/collaborateurs/:id - Modifier un collaborateur + app.put('/collaborateurs/:id', authenticateToken, async (req, res) => { + const transaction = new sql.Transaction(pool); + try { + await transaction.begin(); + + if (!['Admin', 'RH'].includes(req.user.role)) { + await transaction.rollback(); + return res.status(403).json({ error: 'Accès non autorisé' }); + } + + const { id } = req.params; + const { + nom, + prenom, + email, + societeId, + campusId, + serviceId, + poste, + typeContrat, + dateEntree, + nPlus1Id, + nPlus2Id, + description + } = req.body; + + console.log('Modification collaborateur', id, req.body); + + // Vérifier que le collaborateur existe + const existingResult = await transaction.request() + .input('id', sql.Int, id) + .query(`SELECT id, prenom, nom FROM CollaborateurAD WHERE id = @id`); + + if (existingResult.recordset.length === 0) { + await transaction.rollback(); + return res.status(404).json({ error: 'Collaborateur non trouvé' }); + } + + // Mise à jour de CollaborateurAD + await transaction.request() + .input('id', sql.Int, id) + .input('nom', sql.NVarChar(100), nom) + .input('prenom', sql.NVarChar(100), prenom) + .input('email', sql.NVarChar(255), email) + .input('societeId', sql.Int, societeId) + .input('campusId', sql.Int, campusId) + .input('serviceId', sql.Int, serviceId) + .input('fonction', sql.NVarChar(255), poste) + .input('typeContrat', sql.NVarChar(50), typeContrat) + .input('dateEntree', sql.Date, dateEntree || null) + .input('description', sql.NVarChar(500), description || null) + .query(` + UPDATE CollaborateurAD + SET nom = @nom, + prenom = @prenom, + email = @email, + SocieteId = @societeId, + CampusId = @campusId, + ServiceId = @serviceId, + fonction = @fonction, + TypeContrat = @typeContrat, + DateEntree = @dateEntree, + description = @description + WHERE id = @id + `); + + // Mise à jour de la hiérarchie - ✅ CORRECTION avec crochets + const hierarchieExistResult = await transaction.request() + .input('collaborateurId', sql.Int, id) + .query(`SELECT CollaborateurId FROM HierarchieValidationAD WHERE CollaborateurId = @collaborateurId`); + + if (hierarchieExistResult.recordset.length > 0) { + // UPDATE + await transaction.request() + .input('collaborateurId', sql.Int, id) + .input('superieurId', sql.Int, nPlus1Id) + .input('superieurIdN2', sql.Int, nPlus2Id) + .query(` + UPDATE HierarchieValidationAD + SET SuperieurId = @superieurId, + [SuperieurIdn+2] = @superieurIdN2 + WHERE CollaborateurId = @collaborateurId + `); + } else { + // INSERT + if (nPlus1Id || nPlus2Id) { + await transaction.request() + .input('collaborateurId', sql.Int, id) + .input('superieurId', sql.Int, nPlus1Id) + .input('superieurIdN2', sql.Int, nPlus2Id) + .query(` + INSERT INTO HierarchieValidationAD (CollaborateurId, SuperieurId, [SuperieurIdn+2]) + VALUES (@collaborateurId, @superieurId, @superieurIdN2) + `); + } + } + + // Enregistrer dans l'historique + try { + await transaction.request() + .input('userId', sql.Int, req.user.id) + .input('details', sql.NVarChar(500), `Modification des informations de ${prenom} ${nom}`) + .query(` + INSERT INTO HistoriqueActions (CollaborateurADId, Action, Details, DateAction) + VALUES (@userId, 'Modification collaborateur', @details, GETDATE()) + `); + } catch (histError) { + console.warn('Erreur historique non bloquant:', histError.message); + } + + await transaction.commit(); + console.log(`✅ Collaborateur ${id} mis à jour`); + res.json({ success: true, message: 'Collaborateur mis à jour avec succès' }); + + } catch (error) { + await transaction.rollback(); + console.error('❌ Erreur PUT /collaborateurs/:id:', error); + res.status(500).json({ error: error.message }); + } + }); + + // GET /api/snapshot/historique/:employeeId + app.get('/snapshot/historique/:employeeId', authenticateToken, async (req, res) => { + try { + const { employeeId } = req.params; + const { typeCongeId, typeConge, annee } = req.query; + + const request = pool.request() + .input('employeeId', sql.Int, parseInt(employeeId)); + + let where = 'WHERE s.CollaborateurADId = @employeeId'; + + // ⭐ Filtrer par ID ou par nom selon ce qui est fourni + if (typeCongeId) { + request.input('typeCongeId', sql.Int, parseInt(typeCongeId)); + where += ' AND s.TypeCongeId = @typeCongeId'; + } else if (typeConge) { + request.input('typeCongeNom', sql.NVarChar, typeConge); + where += ' AND tc.Nom = @typeCongeNom'; + } + + if (annee) { + request.input('annee', sql.Int, parseInt(annee)); + where += ' AND s.Annee = @annee'; + } + + const result = await request.query(` + SELECT + s.DateSnapshot, + s.Solde, + s.SoldeAnticipe, + s.Total, + s.SoldeReporte, + ISNULL(s.IsAnticipe, 0) AS IsAnticipe, + tc.Nom AS typeConge, + s.Annee, + -- ⭐ Récupérer SoldeAnticipe depuis CompteurConges (valeur actuelle) + ISNULL(cc.SoldeAnticipe, 0) AS SoldeAnticipeActuel + FROM SnapshotQuotidienCompteurs s + JOIN TypeConge tc ON s.TypeCongeId = tc.Id + LEFT JOIN CompteurConges cc + ON cc.CollaborateurADId = s.CollaborateurADId + AND cc.TypeCongeId = s.TypeCongeId + AND cc.Annee = s.Annee + ${where} + ORDER BY s.DateSnapshot ASC +`); + res.json(result.recordset || []); + + } catch (error) { + console.error('❌ Erreur snapshot historique:', error); + res.status(500).json({ error: error.message }); + } + }); + + // ================================================ + // ⭐ ROUTE GET /soldes-previsionnel + // Calcule les soldes CP et RTT projetés à la date de début du congé, + // en lisant DeductionDetails exactement comme serverGTA. + // Gère NORMAL et ANTICIPÉ (CP + RTT). + // Usage: GET /api/soldes-previsionnel?userid=X&dateDebut=YYYY-MM-DD&joursCP=N&joursRTT=N + // ================================================ + app.get('/soldes-previsionnel', authenticateToken, async (req, res) => { + const { userid, dateDebut, joursCP, joursRTT } = req.query; + if (!userid || !dateDebut) { + return res.json({ success: false, message: 'Paramètres requis: userid, dateDebut' }); + } + const userIdInt = parseInt(userid, 10); + if (isNaN(userIdInt)) return res.json({ success: false, message: 'userid invalide' }); + + try { + const today = new Date(); today.setHours(0, 0, 0, 0); + const currentYear = today.getFullYear(); + const dateDebutObj = new Date(dateDebut); dateDebutObj.setHours(0, 0, 0, 0); + const moisDebut = dateDebutObj.getMonth() + 1; + const anneeDebut = dateDebutObj.getFullYear(); + + // ── Infos collaborateur ──────────────────────────────────────── + const collabRes = await pool.request() + .input('userId', sql.Int, userIdInt) + .query(`SELECT TOP 1 role, DateEntree, TypeContrat FROM CollaborateurAD WHERE id=@userId`); + if (!collabRes.recordset.length) throw new Error('Collaborateur introuvable'); + const isApprenti = collabRes.recordset[0].role === 'Apprenti'; + const dateEntree = collabRes.recordset[0].DateEntree ?? null; + const typeContrat = collabRes.recordset[0].TypeContrat ?? '37h'; + + // ── Exercice CP (Annee = année de FIN d'exercice) ───────────── + const dateBasculeCP = new Date(currentYear, 5, 1); dateBasculeCP.setHours(0, 0, 0, 0); + const apresBasculement = today >= dateBasculeCP; + const anneeN = apresBasculement ? currentYear + 1 : currentYear; + const anneeN1 = anneeN - 1; + const anneeExConge = moisDebut >= 6 ? anneeDebut + 1 : anneeDebut; + const isAnticipe = anneeExConge > anneeN; + + // ── Exercice RTT (année civile) ──────────────────────────────── + const anneeRTT = anneeDebut; + const isAnticipeRTT = anneeRTT > currentYear; + + // ── TypeConge IDs ────────────────────────────────────────────── + const cpTypeRes = await pool.request() + .input('nom', sql.NVarChar, 'Congé payé') + .query(`SELECT TOP 1 Id FROM TypeConge WHERE Nom=@nom`); + if (!cpTypeRes.recordset.length) throw new Error('Type CP introuvable'); + const cpTypeId = cpTypeRes.recordset[0].Id; + + const rttTypeRes = await pool.request() + .input('nom', sql.NVarChar, 'RTT') + .query(`SELECT TOP 1 Id FROM TypeConge WHERE Nom=@nom`); + const rttTypeId = rttTypeRes.recordset[0]?.Id ?? null; + + // ── Helper : lire compteur CP ────────────────────────────────── + const lireCompteurCP = async (annee) => { + const r = await pool.request() + .input('userId', sql.Int, userIdInt) + .input('typeId', sql.Int, cpTypeId) + .input('annee', sql.Int, annee) + .query(`SELECT TOP 1 CAST(COALESCE(Solde,0) AS FLOAT) AS Solde, CAST(COALESCE(SoldeReporte,0) AS FLOAT) AS SoldeReporte, CAST(COALESCE(Total,0) AS FLOAT) AS Total FROM CompteurConges WHERE CollaborateurADId=@userId AND TypeCongeId=@typeId AND Annee=@annee ORDER BY Id DESC`); + if (!r.recordset.length) return null; + const row = r.recordset[0]; + const solde = parseFloat(row.Solde); + const report = parseFloat(row.SoldeReporte); + return { solde, reporte: report, total: parseFloat(row.Total), dispoN1: Math.max(0, solde), dispoN: Math.max(0, solde - report) }; + }; + + // ── Helper : lire compteur RTT ───────────────────────────────── + const lireCompteurRTT = async (annee) => { + if (!rttTypeId) return null; + const r = await pool.request() + .input('userId', sql.Int, userIdInt) + .input('typeId', sql.Int, rttTypeId) + .input('annee', sql.Int, annee) + .query(`SELECT TOP 1 CAST(COALESCE(Solde,0) AS FLOAT) AS Solde, CAST(COALESCE(Total,0) AS FLOAT) AS Total FROM CompteurConges WHERE CollaborateurADId=@userId AND TypeCongeId=@typeId AND Annee=@annee ORDER BY Id DESC`); + if (!r.recordset.length) return null; + return { solde: parseFloat(r.recordset[0].Solde), total: parseFloat(r.recordset[0].Total) }; + }; + + // ── Compteurs CP ─────────────────────────────────────────────── + const cptN1 = await lireCompteurCP(anneeN1); + const cptN = await lireCompteurCP(anneeN); + + const soldeNActuel = cptN ? Math.max(0, parseFloat(cptN.solde || 0)) : 0; + const soldeN1Actuel = cptN1 ? Math.max(0, parseFloat(cptN1.solde || 0)) : 0; + + // ── Déductions futures sur CP N (après dateDebutObj) ────────── + const dejaConsoFuturNRes = await pool.request() + .input('userId', sql.Int, userIdInt) + .input('typeId', sql.Int, cpTypeId) + .input('annee', sql.Int, anneeN) + .input('dateDebut', sql.Date, dateDebutObj) // ← dateDebutObj au lieu de today + .query(` + SELECT CAST(COALESCE(SUM(dd.JoursUtilises),0) AS FLOAT) AS total + FROM DeductionDetails dd + JOIN DemandeConge dc ON dd.DemandeCongeId = dc.Id + WHERE dc.CollaborateurADId = @userId + AND dd.TypeCongeId = @typeId + AND dd.Annee = @annee + AND dc.DateDebut > @dateDebut + AND dc.Statut NOT IN ('Refusé','Refusée','Annulé','Annulée') + `); + const dejaConsoFuturN = parseFloat(dejaConsoFuturNRes.recordset[0]?.total ?? 0); + + // ── Déductions futures sur CP N-1 (après dateDebutObj) ──────── + const dejaConsoFuturN1Res = await pool.request() + .input('userId', sql.Int, userIdInt) + .input('typeId', sql.Int, cpTypeId) + .input('annee', sql.Int, anneeN1) + .input('dateDebut', sql.Date, dateDebutObj) // ← dateDebutObj au lieu de today + .query(` + SELECT CAST(COALESCE(SUM(dd.JoursUtilises),0) AS FLOAT) AS total + FROM DeductionDetails dd + JOIN DemandeConge dc ON dd.DemandeCongeId = dc.Id + WHERE dc.CollaborateurADId = @userId + AND dd.TypeCongeId = @typeId + AND dd.Annee = @annee + AND dc.DateDebut > @dateDebut + AND dc.Statut NOT IN ('Refusé','Refusée','Annulé','Annulée') + `); + const dejaConsoFuturN1 = parseFloat(dejaConsoFuturN1Res.recordset[0]?.total ?? 0); + + // ── Soldes disponibles ───────────────────────────────────────── + const cpN1Dispo = Math.max(0, soldeN1Actuel - dejaConsoFuturN1); + const cpNAcquisAujourdHui = Math.max(0, soldeNActuel - dejaConsoFuturN); + + let totalCPDisponible; + let cpNAnticipeDispo = 0; + let acquisCPADateDebut = 0; + let cpSoldeADateDebut = 0; + + if (!isAnticipe) { + // ── MODE NORMAL : calculerAcquisitionFormuleExcel au jour près ── + const acquisADateDebut = calculerAcquisitionFormuleExcel( + today, + dateDebutObj, + 25 / 12 + ); + + const soldeNALaDate = parseFloat( + Math.min(25, soldeNActuel + acquisADateDebut).toFixed(2) + ); + + acquisCPADateDebut = soldeNALaDate; + cpSoldeADateDebut = Math.max(0, soldeNALaDate - dejaConsoFuturN); + cpNAnticipeDispo = Math.max(0, cpSoldeADateDebut - cpNAcquisAujourdHui); + + totalCPDisponible = parseFloat( + Math.max(0, cpN1Dispo + cpSoldeADateDebut).toFixed(2) + ); + + console.log(`🔍 CP NORMAL: solde actuel=${soldeNActuel}j + acq futures=${acquisADateDebut.toFixed(2)}j = à date ${soldeNALaDate}j - conso future=${dejaConsoFuturN}j = ${cpSoldeADateDebut}j | N-1=${cpN1Dispo}j | Total=${totalCPDisponible}j`); + + } else { + // ── MODE ANTICIPÉ ──────────────────────────────────────────── + // Fin exercice N = 31/05/currentYear (toujours l'année civile courante) + const finExN = new Date(currentYear, 4, 31); + finExN.setHours(23, 59, 59, 0); + const debutNp1 = new Date(currentYear, 5, 1); // 01/06/currentYear + debutNp1.setHours(0, 0, 0, 0); + + // Acquisitions depuis aujourd'hui jusqu'au 31/05 (peut être 0 si passé) + const acquisJusquAuFinExN = today <= finExN + ? calculerAcquisitionFormuleExcel(today, finExN, 25 / 12) + : 0; + + const soldeNAFinEx = parseFloat( + Math.min(25, soldeNActuel + acquisJusquAuFinExN).toFixed(2) + ); + + // Acquisitions N+1 depuis 01/06/currentYear jusqu'à dateDebutObj + const acquisNp1 = dateDebutObj > debutNp1 + ? parseFloat(Math.min(25, calculerAcquisitionFormuleExcel(debutNp1, dateDebutObj, 25 / 12)).toFixed(2)) + : 0; + + // Déductions anticipées APRÈS dateDebutObj uniquement + const dejaConsoAnticipRes = await pool.request() + .input('userId', sql.Int, userIdInt) + .input('typeId', sql.Int, cpTypeId) + .input('annee', sql.Int, anneeN) + .input('dateDebut', sql.Date, dateDebutObj) + .query(` + SELECT CAST(COALESCE(SUM(dd.JoursUtilises),0) AS FLOAT) AS total + FROM DeductionDetails dd + JOIN DemandeConge dc ON dd.DemandeCongeId = dc.Id + WHERE dc.CollaborateurADId = @userId + AND dd.TypeCongeId = @typeId + AND dd.Annee = @annee + AND dd.TypeDeduction IN ('Anticip','AnticipFutur') + AND dc.DateDebut > @dateDebut + AND dc.Statut NOT IN ('Refusé','Refusée','Annulé','Annulée') + `); + const dejaConsoAnticip = parseFloat(dejaConsoAnticipRes.recordset[0]?.total ?? 0); + + // Déductions sur N+1 après dateDebutObj + const dejaConsoNp1Res = await pool.request() + .input('userId', sql.Int, userIdInt) + .input('typeId', sql.Int, cpTypeId) + .input('annee', sql.Int, anneeExConge) + .input('dateDebut', sql.Date, dateDebutObj) + .query(` + SELECT CAST(COALESCE(SUM(dd.JoursUtilises),0) AS FLOAT) AS total + FROM DeductionDetails dd + JOIN DemandeConge dc ON dd.DemandeCongeId = dc.Id + WHERE dc.CollaborateurADId = @userId + AND dd.TypeCongeId = @typeId + AND dd.Annee = @annee + AND dc.DateDebut > @dateDebut + AND dc.Statut NOT IN ('Refusé','Refusée','Annulé','Annulée') + `); + const dejaConsoNp1 = parseFloat(dejaConsoNp1Res.recordset[0]?.total ?? 0); + const dispoNp1 = parseFloat(Math.max(0, acquisNp1 - dejaConsoNp1).toFixed(2)); + + acquisCPADateDebut = soldeNAFinEx; + cpSoldeADateDebut = Math.max(0, soldeNAFinEx - dejaConsoAnticip); + cpNAnticipeDispo = dispoNp1; + + totalCPDisponible = parseFloat( + Math.max(0, cpN1Dispo + cpSoldeADateDebut + dispoNp1).toFixed(2) + ); + + console.log(`🔍 CP ANTICIPÉ: solde=${soldeNActuel}j + acq→31/05=${acquisJusquAuFinExN.toFixed(2)}j = ${soldeNAFinEx}j - conso après dateDebut=${dejaConsoAnticip}j | N+1 (01/06→${dateDebut})=${dispoNp1}j | Total=${totalCPDisponible}j`); + } + + console.log(`🔍 CP — N-1: ${cpN1Dispo.toFixed(2)}j | N acquis: ${cpNAcquisAujourdHui.toFixed(2)}j | Anticipé: ${cpNAnticipeDispo.toFixed(2)}j | Total: ${totalCPDisponible.toFixed(2)}j`); + + // ── RTT ──────────────────────────────────────────────────────── + let rttDispo = 0, acquisRTTADateDebut = 0, rttNAcquisAujourdHui = 0, rttAnticipeDispo = 0; + + if (rttTypeId && !isApprenti) { + const rttData = await calculerAcquisitionRTT(userIdInt, dateDebutObj); + acquisRTTADateDebut = rttData.acquisition; + + const cptRTT = await lireCompteurRTT(anneeRTT); + rttNAcquisAujourdHui = cptRTT ? Math.max(0, cptRTT.solde) : 0; + + const rttConsoFuturRes = await pool.request() + .input('userId', sql.Int, userIdInt) + .input('typeId', sql.Int, rttTypeId) + .input('annee', sql.Int, anneeRTT) + .input('dateDebut', sql.Date, dateDebutObj) // ← dateDebutObj + .query(` + SELECT CAST(COALESCE(SUM(dd.JoursUtilises),0) AS FLOAT) AS total + FROM DeductionDetails dd + JOIN DemandeConge dc ON dd.DemandeCongeId = dc.Id + WHERE dc.CollaborateurADId = @userId + AND dd.TypeCongeId = @typeId + AND dd.Annee = @annee + AND dc.DateDebut > @dateDebut + AND dc.Statut NOT IN ('Refusé','Refusée','Annulé','Annulée') + `); + const rttConsoFutur = parseFloat(rttConsoFuturRes.recordset[0]?.total ?? 0); + + if (!isAnticipeRTT) { + const cfgRTT = await getConfigurationRTT(anneeRTT, typeContrat); + const acquisFutRTT = parseFloat( + Math.min(cfgRTT.joursAnnuels - rttNAcquisAujourdHui, + calculerAcquisitionFormuleExcel(today, dateDebutObj, cfgRTT.joursAnnuels / 12) + ).toFixed(2) + ); + const rttALaDate = parseFloat( + Math.min(cfgRTT.joursAnnuels, rttNAcquisAujourdHui + acquisFutRTT).toFixed(2) + ); + rttAnticipeDispo = Math.max(0, rttALaDate - rttNAcquisAujourdHui); + rttDispo = Math.max(0, rttALaDate - rttConsoFutur); + + } else { + const cfgRTT = await getConfigurationRTT(anneeRTT, typeContrat); + const finExRTT = new Date(anneeRTT, 11, 31); finExRTT.setHours(23, 59, 59, 0); + const acquisRest = Math.min( + cfgRTT.joursAnnuels - rttNAcquisAujourdHui, + calculerAcquisitionFormuleExcel(today, finExRTT, cfgRTT.joursAnnuels / 12) + ); + const soldeProj = Math.min(cfgRTT.joursAnnuels, rttNAcquisAujourdHui + acquisRest); + const dispoRTTN = Math.max(0, soldeProj - rttConsoFutur); + + const anneeRTTNp1 = anneeRTT + 1; + const cfgRTTNp1 = await getConfigurationRTT(anneeRTTNp1, typeContrat); + const debutNp1RTT = new Date(anneeRTTNp1, 0, 1); debutNp1RTT.setHours(0, 0, 0, 0); + const acquisNp1RTT = parseFloat( + Math.min(cfgRTTNp1.joursAnnuels, + calculerAcquisitionFormuleExcel(debutNp1RTT, dateDebutObj, cfgRTTNp1.joursAnnuels / 12) + ).toFixed(2) + ); + + const rttConsoNp1Res = await pool.request() + .input('userId', sql.Int, userIdInt) + .input('typeId', sql.Int, rttTypeId) + .input('annee', sql.Int, anneeRTTNp1) + .input('dateDebut', sql.Date, dateDebutObj) // ← dateDebutObj + .query(` + SELECT CAST(COALESCE(SUM(dd.JoursUtilises),0) AS FLOAT) AS total + FROM DeductionDetails dd + JOIN DemandeConge dc ON dd.DemandeCongeId = dc.Id + WHERE dc.CollaborateurADId = @userId + AND dd.TypeCongeId = @typeId + AND dd.Annee = @annee + AND dc.DateDebut > @dateDebut + AND dc.Statut NOT IN ('Refusé','Refusée','Annulé','Annulée') + `); + const rttConsoNp1 = parseFloat(rttConsoNp1Res.recordset[0]?.total ?? 0); + const dispoNp1RTT = parseFloat(Math.max(0, acquisNp1RTT - rttConsoNp1).toFixed(2)); + + rttDispo = parseFloat((dispoRTTN + dispoNp1RTT).toFixed(2)); + rttAnticipeDispo = dispoNp1RTT; + console.log(`🔍 RTT ANTICIPÉ: N=${dispoRTTN.toFixed(2)}j + N+1=${dispoNp1RTT.toFixed(2)}j = ${rttDispo.toFixed(2)}j`); + } + console.log(`🔍 RTT (${anneeRTT}) — Acquis à dateDebut: ${acquisRTTADateDebut.toFixed(2)}j | En base: ${rttNAcquisAujourdHui.toFixed(2)}j | Anticipé: ${rttAnticipeDispo.toFixed(2)}j | Dispo: ${rttDispo.toFixed(2)}j`); + } + + // ── Vérification suffisance ──────────────────────────────────── + const joursNecessairesCP = parseFloat(joursCP) || 0; + const joursNecessairesRTT = parseFloat(joursRTT) || 0; + const suffisantCP = joursNecessairesCP <= 0 || totalCPDisponible >= joursNecessairesCP; + const suffisantRTT = joursNecessairesRTT <= 0 || rttDispo >= joursNecessairesRTT; + const suffisant = suffisantCP && suffisantRTT; + const deficitCP = suffisantCP ? 0 : parseFloat((joursNecessairesCP - totalCPDisponible).toFixed(2)); + const deficitRTT = suffisantRTT ? 0 : parseFloat((joursNecessairesRTT - rttDispo).toFixed(2)); + + let modeLabel = ''; + if (!isAnticipe) modeLabel = apresBasculement ? 'NORMAL POST-BASCULEMENT' : 'NORMAL'; + else if (!apresBasculement) modeLabel = 'ANTICIPÉ CLASSIQUE'; + else modeLabel = 'ANTICIPÉ POST-BASCULEMENT'; + + const exerciceDemande = anneeN; + const exerciceConges = anneeExConge - 1; + + res.json({ + success: true, + previsionnel: { + mode: modeLabel, + isAnticipe, + isAnticipeRTT, + apresBasculement, + exerciceDemande: `${exerciceDemande}-${exerciceDemande + 1}`, + exerciceConges: `${exerciceConges}-${exerciceConges + 1}`, + anneeN1, anneeN, anneeRTT, + + cpN1Reporte: parseFloat(cpN1Dispo.toFixed(2)), + cpNSolde: parseFloat(cpNAcquisAujourdHui.toFixed(2)), + cpNSoldeALaDate: parseFloat(cpSoldeADateDebut.toFixed(2)), + cpNAnticipeAutorise: parseFloat(cpNAnticipeDispo.toFixed(2)), + acquisCPADateDebut: parseFloat(acquisCPADateDebut.toFixed(2)), + cpNAcquisAujourdHui: parseFloat(cpNAcquisAujourdHui.toFixed(2)), + cpNAnticipeDispo: parseFloat(cpNAnticipeDispo.toFixed(2)), + totalCPDisponible, + + acquisRTTADateDebut: parseFloat(acquisRTTADateDebut.toFixed(2)), + rttNAcquisAujourdHui: parseFloat(rttNAcquisAujourdHui.toFixed(2)), + rttAnticipeDispo: parseFloat(rttAnticipeDispo.toFixed(2)), + rttDispo: parseFloat(rttDispo.toFixed(2)), + + joursNecessairesCP, joursNecessairesRTT, + totalDisponible: totalCPDisponible, + suffisant, suffisantCP, suffisantRTT, + deficitCP, deficitRTT, + + message: (() => { + const parts = []; + if (cpN1Dispo > 0) parts.push(`N-1(${anneeN1}): ${cpN1Dispo.toFixed(2)}j`); + if (cpNAcquisAujourdHui > 0) parts.push(`N acquis(${anneeN}): ${cpNAcquisAujourdHui.toFixed(2)}j`); + if (cpNAnticipeDispo > 0) parts.push(`N anticipé: ${cpNAnticipeDispo.toFixed(2)}j`); + if (rttDispo > 0) parts.push(`RTT(${anneeRTT}): ${rttDispo.toFixed(2)}j`); + return `${modeLabel} — ${parts.join(' + ')} → CP total: ${totalCPDisponible.toFixed(2)}j`; + })() + } + }); + + } catch (error) { + console.error('❌ Erreur soldes-previsionnel:', error); + res.status(500).json({ success: false, message: error.message }); + } + }); + // ================================================ + // ROUTE PUT /demandes/:id/annuler - ANNULATION SANS SUPPRESSION + // ================================================ + + + app.put('/demandes/:id/annuler', authenticateToken, async (req, res) => { + const demandeId = parseInt(req.params.id); + const { commentaire } = req.body; + + console.log(`🚫 Annulation demande #${demandeId}`); + + const transaction = new sql.Transaction(); + + try { + await transaction.begin(); + + // 1. Récupérer la demande + const demandeResult = await transaction.request() + .input('demandeId', sql.Int, demandeId) + .query(` + SELECT + dc.*, + c.Email as EmailEmploye, + c.Nom, + c.Prenom + FROM DemandeConge dc + INNER JOIN CollaborateurAD c ON dc.CollaborateurADId = c.Id + WHERE dc.Id = @demandeId + `); + + if (demandeResult.recordset.length === 0) { + await transaction.rollback(); + return res.status(404).json({ error: 'Demande non trouvée' }); + } + + const demande = demandeResult.recordset[0]; + const collaborateurId = demande.CollaborateurADId; + const ancienStatut = demande.Statut; + + console.log(`📋 Demande trouvée - Statut actuel: ${ancienStatut}`); + + // 2. Récupérer les types de congés + const typesResult = await transaction.request() + .input('demandeId', sql.Int, demandeId) + .query(` + SELECT + dct.Id, + dct.TypeCongeId, + dct.NombreJours, + tc.Nom as TypeConge + FROM DemandeCongeType dct + INNER JOIN TypeConge tc ON dct.TypeCongeId = tc.Id + WHERE dct.DemandeCongeId = @demandeId + `); + + console.log(`📊 ${typesResult.recordset.length} type(s) de congé trouvé(s)`); + + // 3. ⭐ RESTAURATION VIA DeductionDetails (même logique que serverGTA) + if (ancienStatut === 'En attente' || ancienStatut === 'Validée') { + console.log('💰 Restauration des compteurs via DeductionDetails...'); + + // Lire toutes les lignes DeductionDetails de cette demande + const deductionsResult = await transaction.request() + .input('demandeId', sql.Int, demandeId) + .query(` + SELECT dd.Id AS DeductionId, dd.TypeCongeId, dd.Annee, + dd.TypeDeduction, dd.JoursUtilises, tc.Nom AS TypeNom + FROM DeductionDetails dd + JOIN TypeConge tc ON dd.TypeCongeId = tc.Id + WHERE dd.DemandeCongeId = @demandeId + ORDER BY dd.Id DESC + `); + + const deductions = deductionsResult.recordset; + console.log(`📊 ${deductions.length} ligne(s) DeductionDetails trouvée(s)`); + + const restorations = []; + + for (const ded of deductions) { + const { DeductionId, TypeCongeId, Annee, TypeDeduction, JoursUtilises, TypeNom } = ded; + const jours = parseFloat(JoursUtilises); + console.log(`\n🔍 ${TypeNom} - ${TypeDeduction} - ${jours}j (Année: ${Annee})`); + + // Helper : lire compteur + const lireCpt = async (annee) => { + const r = await transaction.request() + .input('collabId', sql.Int, collaborateurId) + .input('typeId', sql.Int, TypeCongeId) + .input('annee', sql.Int, annee) + .query(`SELECT TOP 1 Id, Solde, SoldeReporte FROM CompteurConges WHERE CollaborateurADId=@collabId AND TypeCongeId=@typeId AND Annee=@annee`); + return r.recordset[0] ?? null; + }; + + // ── Année N-1 → Solde + SoldeReporte ───────────────── + if (TypeDeduction === 'Année N-1' || TypeDeduction === 'Anne N-1') { + const cpt = await lireCpt(Annee); + if (cpt) { + await transaction.request() + .input('jours', sql.Decimal(10, 2), jours) + .input('id', sql.Int, cpt.Id) + .query(`UPDATE CompteurConges SET Solde=Solde+@jours, SoldeReporte=SoldeReporte+@jours, DerniereMiseAJour=GETDATE() WHERE Id=@id`); + console.log(`✅ Année N-1 restaurée: +${jours}j (Solde + SoldeReporte)`); + } + await transaction.request().input('id', sql.Int, DeductionId).query(`DELETE FROM DeductionDetails WHERE Id=@id`); + restorations.push({ type: TypeNom, annee: Annee, typeDeduction: TypeDeduction, joursRestores: jours }); + continue; + } + + // ── Année N → Solde seulement ───────────────────────── + if (TypeDeduction === 'Année N' || TypeDeduction === 'Anne N') { + const cpt = await lireCpt(Annee); + if (cpt) { + await transaction.request() + .input('jours', sql.Decimal(10, 2), jours) + .input('id', sql.Int, cpt.Id) + .query(`UPDATE CompteurConges SET Solde=Solde+@jours, DerniereMiseAJour=GETDATE() WHERE Id=@id`); + console.log(`✅ Année N restaurée: +${jours}j`); + } + await transaction.request().input('id', sql.Int, DeductionId).query(`DELETE FROM DeductionDetails WHERE Id=@id`); + restorations.push({ type: TypeNom, annee: Annee, typeDeduction: TypeDeduction, joursRestores: jours }); + continue; + } + + // ── Anticip (jours réels déduits du solde CP N) → remettre ── + if (TypeDeduction === 'Anticip') { + const cpt = await lireCpt(Annee); + if (cpt) { + await transaction.request() + .input('jours', sql.Decimal(10, 2), jours) + .input('id', sql.Int, cpt.Id) + .query(`UPDATE CompteurConges SET Solde=Solde+@jours, DerniereMiseAJour=GETDATE() WHERE Id=@id`); + console.log(`✅ Anticip restauré: +${jours}j sur CompteurConges`); + } + await transaction.request().input('id', sql.Int, DeductionId).query(`DELETE FROM DeductionDetails WHERE Id=@id`); + restorations.push({ type: TypeNom, annee: Annee, typeDeduction: TypeDeduction, joursRestores: jours }); + continue; + } + + // ── AnticipFutur (tracé uniquement, solde jamais touché) → juste supprimer ── + if (TypeDeduction === 'AnticipFutur') { + await transaction.request().input('id', sql.Int, DeductionId).query(`DELETE FROM DeductionDetails WHERE Id=@id`); + restorations.push({ type: TypeNom, annee: Annee, typeDeduction: TypeDeduction, joursRestores: jours }); + console.log(`✅ AnticipFutur supprimé: ${jours}j tracé retiré (aucun solde à remettre)`); + continue; + } + + // ── AnticipRTT (solde RTT N réel déduit) → remettre ── + if (TypeDeduction === 'AnticipRTT') { + const cpt = await lireCpt(Annee); + if (cpt) { + await transaction.request() + .input('jours', sql.Decimal(10, 2), jours) + .input('id', sql.Int, cpt.Id) + .query(`UPDATE CompteurConges SET Solde=Solde+@jours, DerniereMiseAJour=GETDATE() WHERE Id=@id`); + console.log(`✅ AnticipRTT restauré: +${jours}j sur RTT ${Annee}`); + } + await transaction.request().input('id', sql.Int, DeductionId).query(`DELETE FROM DeductionDetails WHERE Id=@id`); + restorations.push({ type: TypeNom, annee: Annee, typeDeduction: TypeDeduction, joursRestores: jours }); + continue; + } + + // ── AnticipFuturRTT (tracé uniquement) → juste supprimer ── + if (TypeDeduction === 'AnticipFuturRTT') { + await transaction.request().input('id', sql.Int, DeductionId).query(`DELETE FROM DeductionDetails WHERE Id=@id`); + restorations.push({ type: TypeNom, annee: Annee, typeDeduction: TypeDeduction, joursRestores: jours }); + console.log(`✅ AnticipFuturRTT supprimé: ${jours}j tracé retiré (aucun solde à remettre)`); + continue; + } + + // ── Cas non reconnu → log warning ─────────────────── + console.warn(`⚠️ TypeDeduction non géré: "${TypeDeduction}" — ignoré`); + } + + // ── Fallback : si aucune ligne DeductionDetails (anciennes demandes) ── + // On applique l'ancienne logique simple pour rester compatible + if (deductions.length === 0) { + console.log('⚠️ Aucune DeductionDetails — fallback restauration simple'); + for (const type of typesResult.recordset) { + const nombreJours = parseFloat(type.NombreJours); + const typeCongeId = type.TypeCongeId; + const anneeDemande = new Date(demande.DateDebut).getFullYear(); + const cptFallback = await transaction.request() + .input('collabId', sql.Int, collaborateurId) + .input('typeId', sql.Int, typeCongeId) + .input('annee', sql.Int, anneeDemande) + .query(`SELECT TOP 1 Id, Solde FROM CompteurConges WHERE CollaborateurADId=@collabId AND TypeCongeId=@typeId AND Annee=@annee`); + if (cptFallback.recordset.length > 0) { + await transaction.request() + .input('jours', sql.Decimal(10, 2), nombreJours) + .input('id', sql.Int, cptFallback.recordset[0].Id) + .query(`UPDATE CompteurConges SET Solde=Solde+@jours, DerniereMiseAJour=GETDATE() WHERE Id=@id`); + console.log(`✅ Fallback: ${type.TypeConge} +${nombreJours}j`); + } + } + } + + // Historique global + try { + await transaction.request() + .input('collabId', sql.Int, collaborateurId) + .input('details', sql.NVarChar(500), `Annulation demande #${demandeId} - Restauration compteurs via DeductionDetails`) + .input('demandeId', sql.Int, demandeId) + .query(`INSERT INTO HistoriqueActions (CollaborateurADId, Action, Details, DemandeCongeId, DateAction) VALUES (@collabId, 'Restauration compteur', @details, @demandeId, GETDATE())`); + } catch (histErr) { + console.warn('⚠️ Historique non créé (non bloquant):', histErr.message); + } + + console.log(`✅ Restauration terminée: ${restorations.length} opération(s)`); + } else { + console.log(`ℹ️ Pas de restauration de compteurs (statut était "${ancienStatut}")`); + } + + // 4. Mettre à jour le statut de la demande + await transaction.request() + .input('demandeId', sql.Int, demandeId) + .input('commentaire', sql.NVarChar, commentaire || 'Annulée par le service RH') + .query(` + UPDATE DemandeConge + SET Statut = 'Annulée', + Commentaire = @commentaire, + DateValidation = GETDATE() + WHERE Id = @demandeId + `); + + await transaction.commit(); + + console.log(`✅ Demande #${demandeId} annulée avec succès`); + + // 5. ✅ CORRECTION : Envoyer un email avec Microsoft Graph + const emailEmploye = demande.EmailEmploye; + const nomComplet = `${demande.Prenom} ${demande.Nom}`; + + try { + const accessToken = await getGraphToken(); + + if (accessToken && emailEmploye) { + const subject = '🚫 Votre demande de congé a été annulée'; + const body = ` +
+
+

🚫 Annulation de demande de congé

+
+
+

Bonjour ${nomComplet},

+
+

Période : ${new Date(demande.DateDebut).toLocaleDateString('fr-FR')} au ${new Date(demande.DateFin).toLocaleDateString('fr-FR')}

+

Statut : Annulée

+ ${commentaire ? `

Motif : ${commentaire}

` : ''} +
+ ${(ancienStatut === 'En attente' || ancienStatut === 'Validée') ? '

ℹ️ Les jours de congés ont été automatiquement restitués à votre compteur.

' : ''} +

Pour toute question, veuillez contacter le service RH.

+
+
+ `; + + await sendMailGraph(accessToken, 'gtanoreply@ensup.eu', emailEmploye, subject, body); + console.log(`📧 Email d'annulation envoyé à ${emailEmploye}`); + } + } catch (emailError) { + console.error('❌ Erreur envoi email (non bloquant):', emailError.message); + } + + // 6. Notification SSE + notifyClients({ + type: 'demande-cancelled', + demandeId: demandeId, + collaborateurId: collaborateurId, + message: 'Demande annulée' + }); + + // 7. Webhook (optionnel) + try { + if (typeof webhookManager !== 'undefined' && (ancienStatut === 'En attente' || ancienStatut === 'Validée')) { + await webhookManager.sendWebhook( + WEBHOOKS.COLLABORATEURS.URL, + EVENTS.DEMANDE_CANCELLED, + { + demandeId: demandeId, + collaborateur: nomComplet, + dateDebut: demande.DateDebut, + dateFin: demande.DateFin, + ancienStatut: ancienStatut, + compteursRestaurés: true + } + ); + } + } catch (webhookError) { + console.error('⚠️ Erreur webhook (non bloquant):', webhookError.message); + } + + res.json({ + success: true, + message: 'Demande annulée avec succès', + compteursRestaurés: ancienStatut === 'En attente' || ancienStatut === 'Validée' + }); + + } catch (error) { + // ✅ CORRECTION : Vérifier si la transaction est toujours active avant rollback + try { + if (transaction._acquiredConnection) { + await transaction.rollback(); + console.log('🔄 Transaction annulée (rollback)'); + } + } catch (rollbackError) { + console.error('⚠️ Erreur rollback:', rollbackError.message); + } + + console.error('❌ Erreur annulation demande:', error); + res.status(500).json({ + error: 'Erreur lors de l\'annulation de la demande', + details: error.message + }); + } + }); + + // PUT /api/compte-rendu/jour - RH modifie un jour du compte-rendu d'activité + app.put('/compte-rendu/jour', authenticateToken, async (req, res) => { + const transaction = new sql.Transaction(pool); + try { + await transaction.begin(); + + if (!['Admin', 'RH'].includes(req.user.role)) { + await transaction.rollback(); + return res.status(403).json({ success: false, error: 'Accès non autorisé' }); + } + + const { collaborateurid, jourdate, jourtravaille, reposquotidien, reposhebdo, commentaire } = req.body; + + if (!collaborateurid || !jourdate) { + await transaction.rollback(); + return res.status(400).json({ success: false, error: 'collaborateurid et jourdate sont requis' }); + } + + const collaborateurId = parseInt(collaborateurid); + const dateObj = new Date(jourdate); + const annee = dateObj.getFullYear(); + const mois = dateObj.getMonth() + 1; + + // UPSERT du jour dans CompteRenduActivites + const existing = await transaction.request() + .input('collaborateurId', sql.Int, collaborateurId) + .input('jourDate', sql.Date, jourdate) + .query(`SELECT Id FROM CompteRenduActivites + WHERE CollaborateurADId = @collaborateurId AND JourDate = @jourDate`); + + if (existing.recordset.length > 0) { + await transaction.request() + .input('collaborateurId', sql.Int, collaborateurId) + .input('jourDate', sql.Date, jourdate) + .input('jourTravaille', sql.Bit, jourtravaille ? 1 : 0) + .input('reposQ', sql.Bit, reposquotidien ? 1 : 0) + .input('reposH', sql.Bit, reposhebdo ? 1 : 0) + .input('commentaire', sql.NVarChar(500), commentaire || '') + .query(`UPDATE CompteRenduActivites SET + JourTravaille = @jourTravaille, + ReposQuotidienRespect = @reposQ, + ReposHebdomadaireRespect = @reposH, + CommentaireRepos = @commentaire, + DateModification = GETDATE() + WHERE CollaborateurADId = @collaborateurId AND JourDate = @jourDate`); + } else { + // ✅ Générer l'ID manuellement + const maxIdResult = await transaction.request() + .query('SELECT ISNULL(MAX(Id), 0) + 1 AS NextId FROM CompteRenduActivites'); + const newId = maxIdResult.recordset[0].NextId; + + await transaction.request() + .input('id', sql.Int, newId) + .input('collaborateurId', sql.Int, collaborateurId) + .input('jourDate', sql.Date, jourdate) + .input('annee', sql.Int, annee) + .input('mois', sql.Int, mois) + .input('jourTravaille', sql.Bit, jourtravaille ? 1 : 0) + .input('reposQ', sql.Bit, reposquotidien ? 1 : 0) + .input('reposH', sql.Bit, reposhebdo ? 1 : 0) + .input('commentaire', sql.NVarChar(500), commentaire || '') + .query(`INSERT INTO CompteRenduActivites + (Id, CollaborateurADId, JourDate, Annee, Mois, JourTravaille, + ReposQuotidienRespect, ReposHebdomadaireRespect, CommentaireRepos, DateSaisie, DateModification) + VALUES + (@id, @collaborateurId, @jourDate, @annee, @mois, @jourTravaille, + @reposQ, @reposH, @commentaire, GETDATE(), GETDATE())`); + } + + // Recalculer et mettre à jour CompteRenduMensuel + const statsResult = await transaction.request() + .input('collaborateurId', sql.Int, collaborateurId) + .input('annee', sql.Int, annee) + .input('mois', sql.Int, mois) + .query(`SELECT + COUNT(CASE WHEN JourTravaille = 1 THEN 1 END) AS NbJoursTravailles, + COUNT(CASE WHEN JourTravaille = 1 AND ReposQuotidienRespect = 0 THEN 1 END) AS NbReposQ, + COUNT(CASE WHEN JourTravaille = 1 AND ReposHebdomadaireRespect = 0 THEN 1 END) AS NbReposH + FROM CompteRenduActivites + WHERE CollaborateurADId = @collaborateurId AND Annee = @annee AND Mois = @mois`); + + const stats = statsResult.recordset[0]; + + const mensuelExist = await transaction.request() + .input('collaborateurId', sql.Int, collaborateurId) + .input('annee', sql.Int, annee) + .input('mois', sql.Int, mois) + .query(`SELECT Id FROM CompteRenduMensuel + WHERE CollaborateurADId = @collaborateurId AND Annee = @annee AND Mois = @mois`); + + if (mensuelExist.recordset.length > 0) { + await transaction.request() + .input('collaborateurId', sql.Int, collaborateurId) + .input('annee', sql.Int, annee) + .input('mois', sql.Int, mois) + .input('nbJours', sql.Decimal(10, 2), stats.NbJoursTravailles) + .input('nbReposQ', sql.Int, stats.NbReposQ) + .input('nbReposH', sql.Int, stats.NbReposH) + .query(`UPDATE CompteRenduMensuel SET + NbJoursTravailles = @nbJours, + NbJoursNonRespectsReposQuotidien = @nbReposQ, + NbJoursNonRespectsReposHebdo = @nbReposH + WHERE CollaborateurADId = @collaborateurId AND Annee = @annee AND Mois = @mois`); + } else { + // ✅ Générer l'ID manuellement pour CompteRenduMensuel aussi + const maxMensuelIdResult = await transaction.request() + .query('SELECT ISNULL(MAX(Id), 0) + 1 AS NextId FROM CompteRenduMensuel'); + const newMensuelId = maxMensuelIdResult.recordset[0].NextId; + + await transaction.request() + .input('id', sql.Int, newMensuelId) + .input('collaborateurId', sql.Int, collaborateurId) + .input('annee', sql.Int, annee) + .input('mois', sql.Int, mois) + .input('nbJours', sql.Decimal(10, 2), stats.NbJoursTravailles) + .input('nbReposQ', sql.Int, stats.NbReposQ) + .input('nbReposH', sql.Int, stats.NbReposH) + .query(`INSERT INTO CompteRenduMensuel + (Id, CollaborateurADId, Annee, Mois, NbJoursTravailles, + NbJoursNonRespectsReposQuotidien, NbJoursNonRespectsReposHebdo, Verrouille) + VALUES (@id, @collaborateurId, @annee, @mois, @nbJours, @nbReposQ, @nbReposH, 0)`); + } + + await transaction.commit(); + res.json({ success: true, message: 'Jour mis à jour avec succès' }); + + } catch (error) { + try { await transaction.rollback(); } catch (e) { } + console.error('Erreur PUT compte-rendu/jour', error); + res.status(500).json({ success: false, error: error.message }); + } + }); + + // Verrouiller un mois + app.post('/compte-rendu/verrouiller', authenticateToken, async (req, res) => { + try { + const { collaborateurid, annee, mois } = req.body; + + // Upsert : créer la ligne si elle n'existe pas, puis verrouiller + const existing = await pool.request() + .input('collaborateurId', sql.Int, collaborateurid) + .input('annee', sql.Int, annee) + .input('mois', sql.Int, mois) + .query(`SELECT Id FROM CompteRenduMensuel + WHERE CollaborateurADId = @collaborateurId + AND Annee = @annee AND Mois = @mois`); + + if (existing.recordset.length > 0) { + await pool.request() + .input('collaborateurId', sql.Int, collaborateurid) + .input('annee', sql.Int, annee) + .input('mois', sql.Int, mois) + .query(`UPDATE CompteRenduMensuel + SET Verrouille = 1 + WHERE CollaborateurADId = @collaborateurId + AND Annee = @annee AND Mois = @mois`); + } else { + // Créer l'entrée mensuelle si elle n'existe pas encore + await pool.request() + .input('collaborateurId', sql.Int, collaborateurid) + .input('annee', sql.Int, annee) + .input('mois', sql.Int, mois) + .query(`INSERT INTO CompteRenduMensuel + (CollaborateurADId, Annee, Mois, NbJoursTravailles, + NbJoursNonRespectsReposQuotidien, NbJoursNonRespectsReposHebdo, Verrouille) + VALUES (@collaborateurId, @annee, @mois, 0, 0, 0, 1)`); + } + + res.json({ success: true, message: 'Mois verrouillé' }); + } catch (error) { + console.error('Erreur verrouillage:', error); + res.status(500).json({ success: false, error: error.message }); + } + }); + + // Déverrouiller un mois + app.post('/compte-rendu/deverrouiller', authenticateToken, async (req, res) => { + try { + const { collaborateurid, annee, mois } = req.body; + + await pool.request() + .input('collaborateurId', sql.Int, collaborateurid) + .input('annee', sql.Int, annee) + .input('mois', sql.Int, mois) + .query(`UPDATE CompteRenduMensuel + SET Verrouille = 0 + WHERE CollaborateurADId = @collaborateurId + AND Annee = @annee AND Mois = @mois`); + + res.json({ success: true, message: 'Mois déverrouillé' }); + } catch (error) { + console.error('Erreur déverrouillage:', error); + res.status(500).json({ success: false, error: error.message }); + } + }); + // ================================================ + // CRON — Snapshot quotidien à 23h55 heure de Paris + // ================================================ + function planifierSnapshotQuotidien() { + const maintenant = new Date(); + const prochainDeclenchement = new Date(); + + prochainDeclenchement.setHours(23, 55, 0, 0); + + // Si 23h55 est déjà passé aujourd'hui, programmer pour demain + if (prochainDeclenchement <= maintenant) { + prochainDeclenchement.setDate(prochainDeclenchement.getDate() + 1); + } + + const delai = prochainDeclenchement - maintenant; + + console.log(`[CRON] ✅ Snapshot planifié dans ${Math.round(delai / 1000 / 60)} minutes`); + + setTimeout(async () => { + await executerSnapshotQuotidien(); + // Re-planifier pour le lendemain + planifierSnapshotQuotidien(); + }, delai); + } + + planifierSnapshotQuotidien(); + + // ================================================ + // DÉMARRAGE DU SERVEUR + // ================================================ + + console.log('✅ 99. Toutes les routes définies, démarrage du serveur...'); + + const server = app.listen(PORT, '0.0.0.0', () => { // ← AJOUT DE '0.0.0.0' + console.log('\n================================================'); + console.log(`✅ SERVEUR RH DÉMARRÉ sur http://0.0.0.0:${PORT}`); // ← Changé localhost en 0.0.0.0 + console.log('🔔 Server-Sent Events activés sur /events'); + console.log('🔗 WEBHOOKS configurés:'); + console.log(` - Serveur Collaborateurs: ${WEBHOOKS.COLLABORATEURS_URL}`); + console.log(` - Route webhook receiver: POST /webhook/receive`); + console.log(''); + console.log('📋 Routes disponibles:'); + console.log(' - GET /events (SSE)'); + console.log(' - POST /webhook/receive'); + console.log(' - POST /login-dev ✅ SQL SERVER'); + console.log(' - GET /demandes'); + console.log(' - GET /demandes/:id'); + console.log(' - POST /demandes'); + console.log(' - PUT /demandes/:id'); + console.log(' - DELETE /demandes/:id'); + console.log(' - PUT /demandes/:id/valider'); + console.log(' - GET /employes'); + console.log(' - POST /employes/desactiver'); + console.log(' - POST /employes/reactiver'); + console.log(' - GET /types-conge'); + console.log(' - GET /export/paie'); + console.log(' - GET /compteurs'); + console.log(' - POST /reinitializeAllCounters ✅ SQL SERVER'); + console.log(' - PUT /compteurs/:id'); + console.log(' - POST /compteurs/ajouter-recup'); + console.log(' - POST /compteurs/retirer-recup'); + console.log(' - GET /equipes'); + console.log(' - GET /equipes/:id'); + console.log(' - GET /equipes/:id/membres'); + console.log(' - GET /stats'); + console.log(' - GET /historique'); + console.log(' - GET /v2/compteurs ✅ API V2'); + console.log(' - PUT /v2/compteurs/update ✅ API V2'); + console.log(' - GET /getAllDetailedCounters ✅ SQL SERVER'); + console.log('================================================'); + }); + + server.on('error', (error) => { + console.error('\n❌ ERREUR SERVEUR:', error); + if (error.code === 'EADDRINUSE') { + console.error(`⚠️ Le port ${PORT} est déjà utilisé`); + } + }); + + setInterval(() => { }, 60000); + +} catch (error) { + console.error('\n❌❌❌ ERREUR FATALE AU CHARGEMENT ❌❌❌'); + console.error(error); + console.error(error.stack); + process.exit(1); +} \ No newline at end of file diff --git a/Backend/webhook-config.js b/Backend/webhook-config.js new file mode 100644 index 0000000..cfe85e8 --- /dev/null +++ b/Backend/webhook-config.js @@ -0,0 +1,18 @@ +// Configuration des webhooks +const WEBHOOKS = { + COLLABORATEURS_URL: process.env.COLLABORATEURS_URL || 'http://localhost:3000', + RH_URL: process.env.RH_URL || 'http://localhost:3001', + SECRET_KEY: process.env.WEBHOOK_SECRET || 'secret-key-securise' +}; + +// Types d'événements +const EVENTS = { + DEMANDE_VALIDATED: 'demande.validated', + DEMANDE_CREATED: 'demande.created', + DEMANDE_UPDATED: 'demande.updated', + DEMANDE_CANCELLED: 'demande.cancelled', + DEMANDE_DELETED: 'demande.deleted', + COMPTEUR_UPDATED: 'compteur.updated' +}; + +module.exports = { WEBHOOKS, EVENTS }; \ No newline at end of file diff --git a/Backend/webhook-utils.js b/Backend/webhook-utils.js new file mode 100644 index 0000000..9fb4d78 --- /dev/null +++ b/Backend/webhook-utils.js @@ -0,0 +1,111 @@ +// webhook-utils.js (VERSION COMMONJS) +const axios = require('axios'); +const crypto = require('crypto'); + +class WebhookManager { + constructor(secretKey) { + this.secretKey = secretKey; + } + + /** + * Génère une signature HMAC SHA-256 pour sécuriser le webhook + * @param {Object} payload - Les données à signer + * @returns {string} La signature hexadécimale + */ + generateSignature(payload) { + return crypto + .createHmac('sha256', this.secretKey) + .update(JSON.stringify(payload)) + .digest('hex'); + } + + /** + * Vérifie la signature d'un webhook reçu + * @param {Object} payload - Les données reçues + * @param {string} receivedSignature - La signature reçue dans le header + * @returns {boolean} True si la signature est valide + */ + verifySignature(payload, receivedSignature) { + if (!receivedSignature) { + console.error('❌ Aucune signature fournie'); + return false; + } + + try { + const expectedSignature = this.generateSignature(payload); + return crypto.timingSafeEqual( + Buffer.from(expectedSignature), + Buffer.from(receivedSignature) + ); + } catch (error) { + console.error('❌ Erreur vérification signature:', error); + return false; + } + } + + /** + * Envoie un webhook à une URL cible avec retry automatique + * @param {string} targetUrl - URL du serveur cible + * @param {string} eventType - Type d'événement (ex: 'demande.validated') + * @param {Object} data - Données de l'événement + * @param {number} retries - Nombre de tentatives (défaut: 3) + * @returns {Promise} La réponse du serveur + */ + async sendWebhook(targetUrl, eventType, data, retries = 3) { + const payload = { + event: eventType, + data: data, + timestamp: new Date().toISOString() + }; + + const signature = this.generateSignature(payload); + + for (let attempt = 1; attempt <= retries; attempt++) { + try { + console.log(`📤 Envoi webhook: ${eventType} vers ${targetUrl} (tentative ${attempt}/${retries})`); + + const response = await axios.post( + `${targetUrl}/api/webhook/receive`, + payload, + { + headers: { + 'Content-Type': 'application/json', + 'X-Webhook-Signature': signature + }, + timeout: 5000 + } + ); + + console.log(`✅ Webhook envoyé avec succès: ${eventType}`); + return response.data; + + } catch (error) { + console.error(`❌ Erreur envoi webhook (tentative ${attempt}/${retries}):`, error.message); + + if (attempt === retries) { + console.error(`❌ Échec définitif du webhook après ${retries} tentatives`); + throw error; + } + + const waitTime = 1000 * attempt; + console.log(`⏳ Nouvelle tentative dans ${waitTime}ms...`); + await new Promise(resolve => setTimeout(resolve, waitTime)); + } + } + } + + /** + * Envoie un webhook sans attendre la réponse (fire and forget) + * @param {string} targetUrl - URL du serveur cible + * @param {string} eventType - Type d'événement + * @param {Object} data - Données de l'événement + */ + sendWebhookAsync(targetUrl, eventType, data) { + this.sendWebhook(targetUrl, eventType, data) + .catch(error => { + console.error('❌ Webhook async échoué (non bloquant):', error.message); + }); + } +} + +module.exports = WebhookManager; \ No newline at end of file diff --git a/components.json b/components.json new file mode 100644 index 0000000..62e1011 --- /dev/null +++ b/components.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "src/index.css", + "baseColor": "slate", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + } +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..57b17a2 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,45 @@ +networks: + gtarh: + external: false + +services: + backend: + build: + context: ./Backend + dockerfile: DockerFileGTARH.backend + image: ouijdaneim/gtarh-backend:latest + container_name: gtarh-backend + environment: + - USER_UID=1000 + - USER_GID=1000 + - DB_SERVER=192.168.0.3 + - DB_DATABASE=gta + - DB_USER=gta_app + - DB_PASSWORD=GTA2025!Secure + - DB_ENCRYPT=true + - DB_TRUST_SERVER_CERTIFICATE=true + - PORT=3000 + - NODE_ENV=production + restart: always + networks: + - gtarh + ports: + - "8013:3000" + + frontend: + build: + context: . + dockerfile: ./src/DockerFileGTARH.Frontend + args: + - VITE_API_URL=https://mygta-rh.ensup-adm.net + image: ouijdaneim/gtarh-frontend:latest + container_name: gtarh-frontend + environment: + - NODE_ENV=development + restart: always + networks: + - gtarh + ports: + - "3014:3014" + depends_on: + - backend \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..40f72cc --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,26 @@ +import js from "@eslint/js"; +import globals from "globals"; +import reactHooks from "eslint-plugin-react-hooks"; +import reactRefresh from "eslint-plugin-react-refresh"; +import tseslint from "typescript-eslint"; + +export default tseslint.config( + { ignores: ["dist"] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ["**/*.{ts,tsx}"], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + "react-hooks": reactHooks, + "react-refresh": reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + "react-refresh/only-export-components": ["warn", { allowConstantExport: true }], + "@typescript-eslint/no-unused-vars": "off", + }, + }, +); diff --git a/index.html b/index.html new file mode 100644 index 0000000..7c2cf94 --- /dev/null +++ b/index.html @@ -0,0 +1,22 @@ + + + + + + RH - Gestion des absences + + + + + + + + + + + + +
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..849ccc0 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,9772 @@ +{ + "name": "vite_react_shadcn_ts", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "vite_react_shadcn_ts", + "version": "0.0.0", + "dependencies": { + "@azure/msal-browser": "^4.24.1", + "@azure/msal-react": "^3.0.20", + "@hookform/resolvers": "^3.10.0", + "@radix-ui/react-accordion": "^1.2.11", + "@radix-ui/react-alert-dialog": "^1.1.14", + "@radix-ui/react-aspect-ratio": "^1.1.7", + "@radix-ui/react-avatar": "^1.1.10", + "@radix-ui/react-checkbox": "^1.3.2", + "@radix-ui/react-collapsible": "^1.1.11", + "@radix-ui/react-context-menu": "^2.2.15", + "@radix-ui/react-dialog": "^1.1.14", + "@radix-ui/react-dropdown-menu": "^2.1.15", + "@radix-ui/react-hover-card": "^1.1.14", + "@radix-ui/react-label": "^2.1.7", + "@radix-ui/react-menubar": "^1.1.15", + "@radix-ui/react-navigation-menu": "^1.2.13", + "@radix-ui/react-popover": "^1.1.14", + "@radix-ui/react-progress": "^1.1.7", + "@radix-ui/react-radio-group": "^1.3.7", + "@radix-ui/react-scroll-area": "^1.2.9", + "@radix-ui/react-select": "^2.2.5", + "@radix-ui/react-separator": "^1.1.7", + "@radix-ui/react-slider": "^1.3.5", + "@radix-ui/react-slot": "^1.2.3", + "@radix-ui/react-switch": "^1.2.5", + "@radix-ui/react-tabs": "^1.1.12", + "@radix-ui/react-toast": "^1.2.14", + "@radix-ui/react-toggle": "^1.1.9", + "@radix-ui/react-toggle-group": "^1.1.10", + "@radix-ui/react-tooltip": "^1.2.7", + "@supabase/supabase-js": "^2.58.0", + "@tanstack/react-query": "^5.90.2", + "bcryptjs": "^3.0.2", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "cmdk": "^1.1.1", + "cors": "^2.8.5", + "date-fns": "^3.6.0", + "dotenv": "^17.2.3", + "embla-carousel-react": "^8.6.0", + "exceljs": "^4.4.0", + "express": "^5.1.0", + "input-otp": "^1.4.2", + "jsonwebtoken": "^9.0.2", + "lucide-react": "^0.462.0", + "mysql2": "^3.15.1", + "next-themes": "^0.3.0", + "pdfkit": "^0.17.2", + "pdfkit-table": "^0.1.99", + "react": "^18.3.1", + "react-day-picker": "^8.10.1", + "react-dom": "^18.3.1", + "react-hook-form": "^7.61.1", + "react-resizable-panels": "^2.1.9", + "react-router-dom": "^6.30.2", + "recharts": "^2.15.4", + "sonner": "^1.7.4", + "tailwind-merge": "^2.6.0", + "tailwindcss-animate": "^1.0.7", + "vaul": "^0.9.9", + "xlsx": "^0.18.5", + "zod": "^3.25.76" + }, + "devDependencies": { + "@eslint/js": "^9.32.0", + "@tailwindcss/typography": "^0.5.16", + "@types/node": "^22.16.5", + "@types/react": "^18.3.27", + "@types/react-dom": "^18.3.7", + "@vitejs/plugin-react": "^5.1.1", + "@vitejs/plugin-react-swc": "^3.11.0", + "autoprefixer": "^10.4.21", + "eslint": "^9.32.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.20", + "globals": "^15.15.0", + "lovable-tagger": "^1.1.10", + "nodemon": "^3.1.10", + "postcss": "^8.5.6", + "tailwindcss": "^3.4.17", + "typescript": "^5.8.3", + "typescript-eslint": "^8.38.0", + "vite": "^5.4.19" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@azure/msal-browser": { + "version": "4.24.1", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.24.1.tgz", + "integrity": "sha512-e4sp8ihJIyZQvN0ZM1MMuKlEiiLWUS9V9+kxsVAc6K8MtpXHui8VINmKUxXH0OOksLhFDpdq4sGW1w6uYp431A==", + "license": "MIT", + "peer": true, + "dependencies": { + "@azure/msal-common": "15.13.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-common": { + "version": "15.13.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.13.0.tgz", + "integrity": "sha512-8oF6nj02qX7eE/6+wFT5NluXRHc05AgdCC3fJnkjiJooq8u7BcLmxaYYSwc2AfEkWRMRi6Eyvvbeqk4U4412Ag==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-react": { + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/@azure/msal-react/-/msal-react-3.0.20.tgz", + "integrity": "sha512-+mlGe5rzJDe1Feb0BcPwCkcRTIXAUX0mxBnP8hDuzIXrwBAT/iHHl6wcsZ5iKBnMuqOicJhGX5l2/Iwqguom0Q==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@azure/msal-browser": "^4.24.0", + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "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.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "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" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz", + "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.10.tgz", + "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz", + "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.10.tgz", + "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz", + "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz", + "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz", + "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz", + "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz", + "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz", + "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz", + "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz", + "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz", + "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz", + "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz", + "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz", + "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz", + "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz", + "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz", + "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz", + "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz", + "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz", + "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz", + "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz", + "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz", + "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz", + "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "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": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.0.tgz", + "integrity": "sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.16.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz", + "integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.37.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.37.0.tgz", + "integrity": "sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz", + "integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.16.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@fast-csv/format": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-4.3.5.tgz", + "integrity": "sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==", + "license": "MIT", + "dependencies": { + "@types/node": "^14.0.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isboolean": "^3.0.3", + "lodash.isequal": "^4.5.0", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0" + } + }, + "node_modules/@fast-csv/format/node_modules/@types/node": { + "version": "14.18.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", + "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", + "license": "MIT" + }, + "node_modules/@fast-csv/parse": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/@fast-csv/parse/-/parse-4.3.6.tgz", + "integrity": "sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==", + "license": "MIT", + "dependencies": { + "@types/node": "^14.0.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.groupby": "^4.6.0", + "lodash.isfunction": "^3.0.9", + "lodash.isnil": "^4.0.0", + "lodash.isundefined": "^3.0.1", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/@fast-csv/parse/node_modules/@types/node": { + "version": "14.18.63", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", + "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==", + "license": "MIT" + }, + "node_modules/@floating-ui/core": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", + "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", + "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.3", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz", + "integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.4" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" + }, + "node_modules/@hookform/resolvers": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.10.0.tgz", + "integrity": "sha512-79Dv+3mDF7i+2ajj7SkypSKHhl1cbln1OGavqrsF7p6mbUv11xpqpacPsGDCTRvCSjEEIez2ef1NveSVL3b0Ag==", + "license": "MIT", + "peerDependencies": { + "react-hook-form": "^7.0.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "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.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "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", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@radix-ui/number": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", + "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==", + "license": "MIT" + }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-accordion": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.12.tgz", + "integrity": "sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collapsible": "1.1.12", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-alert-dialog": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.15.tgz", + "integrity": "sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dialog": "1.1.15", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", + "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-aspect-ratio": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-aspect-ratio/-/react-aspect-ratio-1.1.7.tgz", + "integrity": "sha512-Yq6lvO9HQyPwev1onK1daHCHqXVLzPhSVjmsNjCa2Zcxy2f7uJD2itDtxknv6FzAKCwD1qQkeVDmX/cev13n/g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-avatar": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.10.tgz", + "integrity": "sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-is-hydrated": "0.1.0", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-checkbox": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.3.tgz", + "integrity": "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collapsible": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.12.tgz", + "integrity": "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", + "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context-menu": { + "version": "2.2.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context-menu/-/react-context-menu-2.2.16.tgz", + "integrity": "sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-menu": "2.1.16", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", + "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", + "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz", + "integrity": "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-menu": "2.1.16", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", + "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-hover-card": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.15.tgz", + "integrity": "sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.7.tgz", + "integrity": "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.16.tgz", + "integrity": "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menubar": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menubar/-/react-menubar-1.1.16.tgz", + "integrity": "sha512-EB1FktTz5xRRi2Er974AUQZWg2yVBb1yjip38/lgwtCVRd3a+maUoGHN/xs9Yv8SY8QwbSEb+YrxGadVWbEutA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-menu": "2.1.16", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-navigation-menu": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.14.tgz", + "integrity": "sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz", + "integrity": "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", + "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-rect": "1.1.1", + "@radix-ui/react-use-size": "1.1.1", + "@radix-ui/rect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", + "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-progress": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.7.tgz", + "integrity": "sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-radio-group": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.3.8.tgz", + "integrity": "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", + "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-scroll-area": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.10.tgz", + "integrity": "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.1", + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz", + "integrity": "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.1", + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.3", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-separator": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.7.tgz", + "integrity": "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slider": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.3.6.tgz", + "integrity": "sha512-JPYb1GuM1bxfjMRlNLE+BcmBC8onfCi60Blk7OBqi2MLTFdS+8401U4uFjnwkOr49BLmXxLC6JHkvAsx5OJvHw==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.1", + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-switch": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.6.tgz", + "integrity": "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz", + "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toast": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.15.tgz", + "integrity": "sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toggle": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.10.tgz", + "integrity": "sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toggle-group": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.11.tgz", + "integrity": "sha512-5umnS0T8JQzQT6HbPyO7Hh9dgd82NmS36DQr+X/YJ9ctFNCiiQd6IJAYYZ33LUwm8M+taCz5t2ui29fHZc4Y6Q==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-toggle": "1.1.10", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz", + "integrity": "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-visually-hidden": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-is-hydrated": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.0.tgz", + "integrity": "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.5.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", + "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", + "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", + "license": "MIT", + "dependencies": { + "@radix-ui/rect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", + "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", + "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", + "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", + "license": "MIT" + }, + "node_modules/@remix-run/router": { + "version": "1.23.1", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.1.tgz", + "integrity": "sha512-vDbaOzF7yT2Qs4vO6XV1MHcJv+3dgR1sT+l3B8xxOVhUC336prMvqrvsLL/9Dnw2xr6Qhz4J0dmS0llNAbnUmQ==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.4.tgz", + "integrity": "sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.4.tgz", + "integrity": "sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.4.tgz", + "integrity": "sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.4.tgz", + "integrity": "sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.4.tgz", + "integrity": "sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.4.tgz", + "integrity": "sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.4.tgz", + "integrity": "sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.4.tgz", + "integrity": "sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.4.tgz", + "integrity": "sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.4.tgz", + "integrity": "sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.4.tgz", + "integrity": "sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.4.tgz", + "integrity": "sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.4.tgz", + "integrity": "sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.4.tgz", + "integrity": "sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.4.tgz", + "integrity": "sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.4.tgz", + "integrity": "sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.4.tgz", + "integrity": "sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.4.tgz", + "integrity": "sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.4.tgz", + "integrity": "sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.4.tgz", + "integrity": "sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.4.tgz", + "integrity": "sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.4.tgz", + "integrity": "sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@supabase/auth-js": { + "version": "2.72.0", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.72.0.tgz", + "integrity": "sha512-4+bnUrtTDK1YD0/FCx2YtMiQH5FGu9Jlf4IQi5kcqRwRwqp2ey39V61nHNdH86jm3DIzz0aZKiWfTW8qXk1swQ==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/functions-js": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.5.0.tgz", + "integrity": "sha512-SXBx6Jvp+MOBekeKFu+G11YLYPeVeGQl23eYyAG9+Ro0pQ1aIP0UZNIBxHKNHqxzR0L0n6gysNr2KT3841NATw==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/node-fetch": { + "version": "2.6.15", + "resolved": "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz", + "integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/@supabase/postgrest-js": { + "version": "1.21.4", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.21.4.tgz", + "integrity": "sha512-TxZCIjxk6/dP9abAi89VQbWWMBbybpGWyvmIzTd79OeravM13OjR/YEYeyUOPcM1C3QyvXkvPZhUfItvmhY1IQ==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/realtime-js": { + "version": "2.15.5", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.15.5.tgz", + "integrity": "sha512-/Rs5Vqu9jejRD8ZeuaWXebdkH+J7V6VySbCZ/zQM93Ta5y3mAmocjioa/nzlB6qvFmyylUgKVS1KpE212t30OA==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.13", + "@types/phoenix": "^1.6.6", + "@types/ws": "^8.18.1", + "ws": "^8.18.2" + } + }, + "node_modules/@supabase/storage-js": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.12.2.tgz", + "integrity": "sha512-SiySHxi3q7gia7NBYpsYRu8gyI0NhFwSORMxbZIxJ/zAVkN6QpwDRan158CJ+UdzD4WB/rQMAGRqIJQP+7ccAQ==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/supabase-js": { + "version": "2.58.0", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.58.0.tgz", + "integrity": "sha512-Tm1RmQpoAKdQr4/8wiayGti/no+If7RtveVZjHR8zbO7hhQjmPW2Ok5ZBPf1MGkt5c+9R85AVMsTfSaqAP1sUg==", + "license": "MIT", + "dependencies": { + "@supabase/auth-js": "2.72.0", + "@supabase/functions-js": "2.5.0", + "@supabase/node-fetch": "2.6.15", + "@supabase/postgrest-js": "1.21.4", + "@supabase/realtime-js": "2.15.5", + "@supabase/storage-js": "2.12.2" + } + }, + "node_modules/@swc/core": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.5.tgz", + "integrity": "sha512-WezcBo8a0Dg2rnR82zhwoR6aRNxeTGfK5QCD6TQ+kg3xx/zNT02s/0o+81h/3zhvFSB24NtqEr8FTw88O5W/JQ==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.24" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.13.5", + "@swc/core-darwin-x64": "1.13.5", + "@swc/core-linux-arm-gnueabihf": "1.13.5", + "@swc/core-linux-arm64-gnu": "1.13.5", + "@swc/core-linux-arm64-musl": "1.13.5", + "@swc/core-linux-x64-gnu": "1.13.5", + "@swc/core-linux-x64-musl": "1.13.5", + "@swc/core-win32-arm64-msvc": "1.13.5", + "@swc/core-win32-ia32-msvc": "1.13.5", + "@swc/core-win32-x64-msvc": "1.13.5" + }, + "peerDependencies": { + "@swc/helpers": ">=0.5.17" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.13.5.tgz", + "integrity": "sha512-lKNv7SujeXvKn16gvQqUQI5DdyY8v7xcoO3k06/FJbHJS90zEwZdQiMNRiqpYw/orU543tPaWgz7cIYWhbopiQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.13.5.tgz", + "integrity": "sha512-ILd38Fg/w23vHb0yVjlWvQBoE37ZJTdlLHa8LRCFDdX4WKfnVBiblsCU9ar4QTMNdeTBEX9iUF4IrbNWhaF1Ng==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.13.5.tgz", + "integrity": "sha512-Q6eS3Pt8GLkXxqz9TAw+AUk9HpVJt8Uzm54MvPsqp2yuGmY0/sNaPPNVqctCX9fu/Nu8eaWUen0si6iEiCsazQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.13.5.tgz", + "integrity": "sha512-aNDfeN+9af+y+M2MYfxCzCy/VDq7Z5YIbMqRI739o8Ganz6ST+27kjQFd8Y/57JN/hcnUEa9xqdS3XY7WaVtSw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.13.5.tgz", + "integrity": "sha512-9+ZxFN5GJag4CnYnq6apKTnnezpfJhCumyz0504/JbHLo+Ue+ZtJnf3RhyA9W9TINtLE0bC4hKpWi8ZKoETyOQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.13.5.tgz", + "integrity": "sha512-WD530qvHrki8Ywt/PloKUjaRKgstQqNGvmZl54g06kA+hqtSE2FTG9gngXr3UJxYu/cNAjJYiBifm7+w4nbHbA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.13.5.tgz", + "integrity": "sha512-Luj8y4OFYx4DHNQTWjdIuKTq2f5k6uSXICqx+FSabnXptaOBAbJHNbHT/06JZh6NRUouaf0mYXN0mcsqvkhd7Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.13.5.tgz", + "integrity": "sha512-cZ6UpumhF9SDJvv4DA2fo9WIzlNFuKSkZpZmPG1c+4PFSEMy5DFOjBSllCvnqihCabzXzpn6ykCwBmHpy31vQw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.13.5.tgz", + "integrity": "sha512-C5Yi/xIikrFUzZcyGj9L3RpKljFvKiDMtyDzPKzlsDrKIw2EYY+bF88gB6oGY5RGmv4DAX8dbnpRAqgFD0FMEw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.13.5.tgz", + "integrity": "sha512-YrKdMVxbYmlfybCSbRtrilc6UA8GF5aPmGKBdPvjrarvsmf4i7ZHGCEnLtfOMd3Lwbs2WUZq3WdMbozYeLU93Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@swc/helpers": { + "version": "0.5.18", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.18.tgz", + "integrity": "sha512-TXTnIcNJQEKwThMMqBXsZ4VGAza6bvN4pa41Rkqoio6QBKMvo+5lexeTMScGCIxtzgQJzElcvIltani+adC5PQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@swc/types": { + "version": "0.1.25", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.25.tgz", + "integrity": "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, + "node_modules/@tailwindcss/typography": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.19.tgz", + "integrity": "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "6.0.10" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" + } + }, + "node_modules/@tanstack/query-core": { + "version": "5.90.2", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.2.tgz", + "integrity": "sha512-k/TcR3YalnzibscALLwxeiLUub6jN5EDLwKDiO7q5f4ICEoptJ+n9+7vcEFy5/x/i6Q+Lb/tXrsKCggf5uQJXQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.90.2", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.2.tgz", + "integrity": "sha512-CLABiR+h5PYfOWr/z+vWFt5VsOA2ekQeRQBFSKlcoW6Ndx/f8rfyVmq4LbgOM4GG2qtxAxjLYLOpCNTYm4uKzw==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.90.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", + "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.18.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.18.8.tgz", + "integrity": "sha512-pAZSHMiagDR7cARo/cch1f3rXy0AEXwsVsVH09FcyeJVAzCnGgmYis7P3JidtTUjyadhTeSo8TgRPswstghDaw==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/phoenix": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.6.tgz", + "integrity": "sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==", + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.27", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz", + "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", + "devOptional": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "devOptional": true, + "license": "MIT", + "peer": true, + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.45.0.tgz", + "integrity": "sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/type-utils": "8.45.0", + "@typescript-eslint/utils": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.45.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.45.0.tgz", + "integrity": "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.45.0.tgz", + "integrity": "sha512-3pcVHwMG/iA8afdGLMuTibGR7pDsn9RjDev6CCB+naRsSYs2pns5QbinF4Xqw6YC/Sj3lMrm/Im0eMfaa61WUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.45.0", + "@typescript-eslint/types": "^8.45.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.45.0.tgz", + "integrity": "sha512-clmm8XSNj/1dGvJeO6VGH7EUSeA0FMs+5au/u3lrA3KfG8iJ4u8ym9/j2tTEoacAffdW1TVUzXO30W1JTJS7dA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.45.0.tgz", + "integrity": "sha512-aFdr+c37sc+jqNMGhH+ajxPXwjv9UtFZk79k8pLoJ6p4y0snmYpPA52GuWHgt2ZF4gRRW6odsEj41uZLojDt5w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.45.0.tgz", + "integrity": "sha512-bpjepLlHceKgyMEPglAeULX1vixJDgaKocp0RVJ5u4wLJIMNuKtUXIczpJCPcn2waII0yuvks/5m5/h3ZQKs0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/utils": "8.45.0", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz", + "integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.45.0.tgz", + "integrity": "sha512-GfE1NfVbLam6XQ0LcERKwdTTPlLvHvXXhOeUGC1OXi4eQBoyy1iVsW+uzJ/J9jtCz6/7GCQ9MtrQ0fml/jWCnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.45.0", + "@typescript-eslint/tsconfig-utils": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.45.0.tgz", + "integrity": "sha512-bxi1ht+tLYg4+XV2knz/F7RVhU0k6VrSMc9sb8DQ6fyCTrGQLHfo7lDtN0QJjZjKkLA2ThrKuCdHEvLReqtIGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.45.0.tgz", + "integrity": "sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.45.0", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.1.tgz", + "integrity": "sha512-WQfkSw0QbQ5aJ2CHYw23ZGkqnRwqKHD/KYsMeTkZzPT4Jcf0DcBxBtwMJxnu6E7oxw5+JC6ZAiePgh28uJ1HBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.5", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.47", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/@vitejs/plugin-react-swc": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.11.0.tgz", + "integrity": "sha512-YTJCGFdNMHCMfjODYtxRNVAYmTWQ1Lb8PulP/2/f/oEEtglw8oKxKIZmmRkyXrVrHfsKOaVkAc3NT9/dMutO5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-beta.27", + "@swc/core": "^1.12.11" + }, + "peerDependencies": { + "vite": "^4 || ^5 || ^6 || ^7" + } + }, + "node_modules/@vitejs/plugin-react/node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.47", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.47.tgz", + "integrity": "sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw==", + "dev": true, + "license": "MIT" + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/adler-32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz", + "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/archiver": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^2.1.0", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^2.2.0", + "zip-stream": "^4.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "license": "MIT", + "dependencies": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/archiver-utils/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/archiver-utils/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/archiver-utils/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/archiver-utils/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/autoprefixer": { + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/aws-ssl-profiles": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", + "integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "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/baseline-browser-mapping": { + "version": "2.8.12", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.12.tgz", + "integrity": "sha512-vAPMQdnyKCBtkmQA6FMCBvU9qFIppS3nzyXnEM+Lo2IAhG4Mpjv9cCxMudhgV3YdNNJv6TNqXy97dfRVL2LmaQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/bcryptjs": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.2.tgz", + "integrity": "sha512-k38b3XOZKv60C4E2hVsXTolJWfkGRMbILBIe2IBITXciy5bOsTKot5kDrf3ZfufQtQOUN5mXceUEpU1rTl9Uog==", + "license": "BSD-3-Clause", + "bin": { + "bcrypt": "bin/bcrypt" + } + }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "license": "Unlicense", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "license": "MIT", + "dependencies": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brotli": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.1.2" + } + }, + "node_modules/browserslist": { + "version": "4.26.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.3.tgz", + "integrity": "sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "baseline-browser-mapping": "^2.8.9", + "caniuse-lite": "^1.0.30001746", + "electron-to-chromium": "^1.5.227", + "node-releases": "^2.0.21", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "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", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "engines": { + "node": ">=0.2.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001748", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001748.tgz", + "integrity": "sha512-5P5UgAr0+aBmNiplks08JLw+AW/XG/SurlgZLgB1dDLfAw7EfRGxIwzPHxdSCGY/BTKDqIVyJL87cCN6s0ZR0w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/cfb": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz", + "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "license": "MIT/X11", + "dependencies": { + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cmdk": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz", + "integrity": "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "^1.1.1", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-id": "^1.1.0", + "@radix-ui/react-primitive": "^2.0.2" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "react-dom": "^18 || ^19 || ^19.0.0-rc" + } + }, + "node_modules/codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/compress-commons": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", + "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", + "license": "MIT", + "dependencies": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "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/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", + "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "license": "MIT" + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "license": "MIT", + "peer": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/dayjs": { + "version": "1.11.19", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", + "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", + "license": "MIT" + }, + "node_modules/debug": { + "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" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", + "license": "MIT" + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, + "node_modules/dfa": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", + "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==", + "license": "MIT" + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "license": "MIT" + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "license": "BSD-3-Clause", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/duplexer2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/duplexer2/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/duplexer2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.230", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.230.tgz", + "integrity": "sha512-A6A6Fd3+gMdaed9wX83CvHYJb4UuapPD5X5SLq72VZJzxHSY0/LUweGXRWmQlh2ln7KV7iw7jnwXK7dlPoOnHQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/embla-carousel": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz", + "integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==", + "license": "MIT", + "peer": true + }, + "node_modules/embla-carousel-react": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel-react/-/embla-carousel-react-8.6.0.tgz", + "integrity": "sha512-0/PjqU7geVmo6F734pmPqpyHqiM99olvyecY7zdweCw+6tKEXnrE90pBiBbMMU8s5tICemzpQ3hi5EpxzGW+JA==", + "license": "MIT", + "dependencies": { + "embla-carousel": "8.6.0", + "embla-carousel-reactive-utils": "8.6.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/embla-carousel-reactive-utils": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel-reactive-utils/-/embla-carousel-reactive-utils-8.6.0.tgz", + "integrity": "sha512-fMVUDUEx0/uIEDM0Mz3dHznDhfX+znCCDCeIophYb1QGVM7YThSWX+wz11zlYwWFOr74b4QLGg0hrGPJeG2s4A==", + "license": "MIT", + "peerDependencies": { + "embla-carousel": "8.6.0" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", + "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.10", + "@esbuild/android-arm": "0.25.10", + "@esbuild/android-arm64": "0.25.10", + "@esbuild/android-x64": "0.25.10", + "@esbuild/darwin-arm64": "0.25.10", + "@esbuild/darwin-x64": "0.25.10", + "@esbuild/freebsd-arm64": "0.25.10", + "@esbuild/freebsd-x64": "0.25.10", + "@esbuild/linux-arm": "0.25.10", + "@esbuild/linux-arm64": "0.25.10", + "@esbuild/linux-ia32": "0.25.10", + "@esbuild/linux-loong64": "0.25.10", + "@esbuild/linux-mips64el": "0.25.10", + "@esbuild/linux-ppc64": "0.25.10", + "@esbuild/linux-riscv64": "0.25.10", + "@esbuild/linux-s390x": "0.25.10", + "@esbuild/linux-x64": "0.25.10", + "@esbuild/netbsd-arm64": "0.25.10", + "@esbuild/netbsd-x64": "0.25.10", + "@esbuild/openbsd-arm64": "0.25.10", + "@esbuild/openbsd-x64": "0.25.10", + "@esbuild/openharmony-arm64": "0.25.10", + "@esbuild/sunos-x64": "0.25.10", + "@esbuild/win32-arm64": "0.25.10", + "@esbuild/win32-ia32": "0.25.10", + "@esbuild/win32-x64": "0.25.10" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.37.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.37.0.tgz", + "integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.4.0", + "@eslint/core": "^0.16.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.37.0", + "@eslint/plugin-kit": "^0.4.0", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.23.tgz", + "integrity": "sha512-G4j+rv0NmbIR45kni5xJOrYvCtyD3/7LjpVH8MPPcudXDcNu8gv+4ATTDXTtbRR8rTCM5HxECvCSsRmxKnWDsA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/exceljs": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/exceljs/-/exceljs-4.4.0.tgz", + "integrity": "sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg==", + "license": "MIT", + "dependencies": { + "archiver": "^5.0.0", + "dayjs": "^1.8.34", + "fast-csv": "^4.3.1", + "jszip": "^3.10.1", + "readable-stream": "^3.6.0", + "saxes": "^5.0.1", + "tmp": "^0.2.0", + "unzipper": "^0.10.11", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=8.3.0" + } + }, + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/fast-csv": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-4.3.6.tgz", + "integrity": "sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==", + "license": "MIT", + "dependencies": { + "@fast-csv/format": "4.3.5", + "@fast-csv/parse": "4.3.6" + }, + "engines": { + "node": ">=10.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", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-equals": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.3.2.tgz", + "integrity": "sha512-6rxyATwPCkaFIL3JLqw8qXqMpIZ942pTX/tbQFkRsDGblS8tNGtlUauA/+mt6RUfqn/4MoEr+WDkYoIQbibWuQ==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/fontkit": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.4.tgz", + "integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==", + "license": "MIT", + "dependencies": { + "@swc/helpers": "^0.5.12", + "brotli": "^1.3.2", + "clone": "^2.1.2", + "dfa": "^1.2.0", + "fast-deep-equal": "^3.1.3", + "restructure": "^3.0.0", + "tiny-inflate": "^1.0.3", + "unicode-properties": "^1.4.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/frac": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "dependencies": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generate-function": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "license": "MIT", + "dependencies": { + "is-property": "^1.0.2" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "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": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "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/input-otp": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/input-otp/-/input-otp-1.4.2.tgz", + "integrity": "sha512-l3jWwYNvrEa6NTCt7BECfCm48GvwuZzkoeG3gBL2w4CHeOXW3eKFmf9UNYkNfYc3mxMrthMnxjIE07MT0zLBQA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", + "license": "MIT" + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "license": "MIT", + "peer": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/jpeg-exif": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/jpeg-exif/-/jpeg-exif-1.1.4.tgz", + "integrity": "sha512-a+bKEcCjtuW5WTdgeXFzswSrdqi0jk4XlEtZlx5A94wCoBpFjfFTbo/Tra5SpNCl/YFZPvcV1dJc+TAYeg6ROQ==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "license": "MIT" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/linebreak": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz", + "integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==", + "license": "MIT", + "dependencies": { + "base64-js": "0.0.8", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/linebreak/node_modules/base64-js": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz", + "integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", + "license": "ISC" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "license": "MIT" + }, + "node_modules/lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", + "license": "MIT" + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", + "license": "MIT" + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", + "license": "MIT" + }, + "node_modules/lodash.groupby": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", + "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==", + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", + "license": "MIT" + }, + "node_modules/lodash.isfunction": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnil": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz", + "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.isundefined": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", + "integrity": "sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", + "license": "MIT" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lovable-tagger": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/lovable-tagger/-/lovable-tagger-1.1.10.tgz", + "integrity": "sha512-LbYaxi6vgrqg7Sq93/cRbIM78EP+X+GUU7spx804yqB2bxfiOej8UvcZHeE4WqMjAFI2dHGhXpy8r6SnvmrzGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.8", + "esbuild": "^0.25.0", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.12", + "tailwindcss": "^3.4.17" + }, + "peerDependencies": { + "vite": ">=5.0.0 <8.0.0" + } + }, + "node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/lru.min": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.2.tgz", + "integrity": "sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg==", + "license": "MIT", + "engines": { + "bun": ">=1.0.0", + "deno": ">=1.30.0", + "node": ">=8.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wellwelwel" + } + }, + "node_modules/lucide-react": { + "version": "0.462.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.462.0.tgz", + "integrity": "sha512-NTL7EbAao9IFtuSivSZgrAh4fZd09Lr+6MTkqIxuHaH2nnYiYIzXPo06cOxHg9wKLdj6LL8TByG4qpePqwgx/g==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" + } + }, + "node_modules/magic-string": { + "version": "0.30.19", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/mysql2": { + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.15.1.tgz", + "integrity": "sha512-WZMIRZstT2MFfouEaDz/AGFnGi1A2GwaDe7XvKTdRJEYiAHbOrh4S3d8KFmQeh11U85G+BFjIvS1Di5alusZsw==", + "license": "MIT", + "dependencies": { + "aws-ssl-profiles": "^1.1.1", + "denque": "^2.1.0", + "generate-function": "^2.3.1", + "iconv-lite": "^0.7.0", + "long": "^5.2.1", + "lru.min": "^1.0.0", + "named-placeholders": "^1.1.3", + "seq-queue": "^0.0.5", + "sqlstring": "^2.3.2" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/mysql2/node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/named-placeholders": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", + "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", + "license": "MIT", + "dependencies": { + "lru-cache": "^7.14.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/next-themes": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.3.0.tgz", + "integrity": "sha512-/QHIrsYpd6Kfk7xakK4svpDI5mmXP0gfvCoJdGpZQ2TOrQZmsW0QxjaiLn8wbIKjtm4BTSqLoix4lxYYOnLJ/w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8 || ^17 || ^18", + "react-dom": "^16.8 || ^17 || ^18" + } + }, + "node_modules/node-releases": { + "version": "2.0.23", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.23.tgz", + "integrity": "sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/nodemon": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz", + "integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/pdfkit": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/pdfkit/-/pdfkit-0.17.2.tgz", + "integrity": "sha512-UnwF5fXy08f0dnp4jchFYAROKMNTaPqb/xgR8GtCzIcqoTnbOqtp3bwKvO4688oHI6vzEEs8Q6vqqEnC5IUELw==", + "license": "MIT", + "dependencies": { + "crypto-js": "^4.2.0", + "fontkit": "^2.0.4", + "jpeg-exif": "^1.1.4", + "linebreak": "^1.1.0", + "png-js": "^1.0.0" + } + }, + "node_modules/pdfkit-table": { + "version": "0.1.99", + "resolved": "https://registry.npmjs.org/pdfkit-table/-/pdfkit-table-0.1.99.tgz", + "integrity": "sha512-BaunFJ7cvKqIPONzX9FXUpeC/3iJeLo0v2nVNjCccAxdMXKvDMBjxyF4wZhKOYlmP+nYZcikf9ByVJeCzjNK6g==", + "license": "MIT", + "dependencies": { + "pdfkit": "latest" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/png-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/png-js/-/png-js-1.0.0.tgz", + "integrity": "sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g==" + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-nested/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "license": "MIT" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "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/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", + "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.7.0", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-day-picker": { + "version": "8.10.1", + "resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-8.10.1.tgz", + "integrity": "sha512-TMx7fNbhLk15eqcMt+7Z7S2KF7mfTId/XJDjKE8f+IUcFn0l08/kI4FiYTL/0yuOLmEcbR4Fwe3GJf/NiiMnPA==", + "license": "MIT", + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/gpbl" + }, + "peerDependencies": { + "date-fns": "^2.28.0 || ^3.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-hook-form": { + "version": "7.64.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.64.0.tgz", + "integrity": "sha512-fnN+vvTiMLnRqKNTVhDysdrUay0kUUAymQnFIznmgDvapjveUWOOPqMNzPg+A+0yf9DuE2h6xzBjN1s+Qx8wcg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-remove-scroll": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz", + "integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-resizable-panels": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-2.1.9.tgz", + "integrity": "sha512-z77+X08YDIrgAes4jl8xhnUu1LNIRp4+E7cv4xHmLOxxUPO/ML7PSrE813b90vj7xvQ1lcf7g2uA9GeMZonjhQ==", + "license": "MIT", + "peerDependencies": { + "react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/react-router": { + "version": "6.30.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.2.tgz", + "integrity": "sha512-H2Bm38Zu1bm8KUE5NVWRMzuIyAV8p/JrOaBJAwVmp37AXG72+CZJlEBw6pdn9i5TBgLMhNDgijS4ZlblpHyWTA==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.30.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.2.tgz", + "integrity": "sha512-l2OwHn3UUnEVUqc6/1VMmR1cvZryZ3j3NzapC2eUXO1dB0sYp5mvwdjiXhpUbRb21eFow3qSxpP8Yv6oAU824Q==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.1", + "react-router": "6.30.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/react-smooth": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz", + "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==", + "license": "MIT", + "dependencies": { + "fast-equals": "^5.0.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/recharts": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz", + "integrity": "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==", + "license": "MIT", + "dependencies": { + "clsx": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.21", + "react-is": "^18.3.1", + "react-smooth": "^4.0.4", + "recharts-scale": "^0.4.4", + "tiny-invariant": "^1.3.1", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "license": "MIT", + "dependencies": { + "decimal.js-light": "^2.4.1" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/restructure": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz", + "integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==", + "license": "MIT" + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.52.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.4.tgz", + "integrity": "sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.52.4", + "@rollup/rollup-android-arm64": "4.52.4", + "@rollup/rollup-darwin-arm64": "4.52.4", + "@rollup/rollup-darwin-x64": "4.52.4", + "@rollup/rollup-freebsd-arm64": "4.52.4", + "@rollup/rollup-freebsd-x64": "4.52.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.4", + "@rollup/rollup-linux-arm-musleabihf": "4.52.4", + "@rollup/rollup-linux-arm64-gnu": "4.52.4", + "@rollup/rollup-linux-arm64-musl": "4.52.4", + "@rollup/rollup-linux-loong64-gnu": "4.52.4", + "@rollup/rollup-linux-ppc64-gnu": "4.52.4", + "@rollup/rollup-linux-riscv64-gnu": "4.52.4", + "@rollup/rollup-linux-riscv64-musl": "4.52.4", + "@rollup/rollup-linux-s390x-gnu": "4.52.4", + "@rollup/rollup-linux-x64-gnu": "4.52.4", + "@rollup/rollup-linux-x64-musl": "4.52.4", + "@rollup/rollup-openharmony-arm64": "4.52.4", + "@rollup/rollup-win32-arm64-msvc": "4.52.4", + "@rollup/rollup-win32-ia32-msvc": "4.52.4", + "@rollup/rollup-win32-x64-gnu": "4.52.4", + "@rollup/rollup-win32-x64-msvc": "4.52.4", + "fsevents": "~2.3.2" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "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", + "dependencies": { + "queue-microtask": "^1.2.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/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/seq-queue": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", + "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sonner": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/sonner/-/sonner-1.7.4.tgz", + "integrity": "sha512-DIS8z4PfJRbIyfVFDVnK9rO3eYDtse4Omcm6bt0oEr5/jtLgysmjuBl1frJ9E/EQZrFmKx2A8m/s5s9CRXIzhw==", + "license": "MIT", + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sqlstring": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "license": "Apache-2.0", + "dependencies": { + "frac": "~1.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwind-merge": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz", + "integrity": "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.18", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.18.tgz", + "integrity": "sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss-animate": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz", + "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", + "license": "MIT", + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders" + } + }, + "node_modules/tailwindcss/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", + "license": "MIT" + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "node_modules/tmp": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "license": "MIT/X11", + "engines": { + "node": "*" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "license": "Apache-2.0" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.45.0.tgz", + "integrity": "sha512-qzDmZw/Z5beNLUrXfd0HIW6MzIaAV5WNDxmMs9/3ojGOpYavofgNAAD/nC6tGV2PczIi0iw8vot2eAe/sBn7zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.45.0", + "@typescript-eslint/parser": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/utils": "8.45.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" + }, + "node_modules/unicode-properties": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", + "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/unicode-trie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", + "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", + "license": "MIT", + "dependencies": { + "pako": "^0.2.5", + "tiny-inflate": "^1.0.0" + } + }, + "node_modules/unicode-trie/node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unzipper": { + "version": "0.10.14", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz", + "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", + "license": "MIT", + "dependencies": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "^1.0.12", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + } + }, + "node_modules/unzipper/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/unzipper/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/unzipper/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vaul": { + "version": "0.9.9", + "resolved": "https://registry.npmjs.org/vaul/-/vaul-0.9.9.tgz", + "integrity": "sha512-7afKg48srluhZwIkaU+lgGtFCUsYBSGOl8vcc8N/M3YQlZFlynHD15AE+pwrYdc826o7nrIND4lL9Y6b9WWZZQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-dialog": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, + "node_modules/victory-vendor": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", + "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, + "node_modules/vite": { + "version": "5.4.20", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.20.tgz", + "integrity": "sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xlsx": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", + "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + }, + "bin": { + "xlsx": "bin/xlsx.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "license": "MIT" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zip-stream": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", + "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^3.0.4", + "compress-commons": "^4.1.2", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zip-stream/node_modules/archiver-utils": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", + "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", + "license": "MIT", + "dependencies": { + "glob": "^7.2.3", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zip-stream/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..d06ced8 --- /dev/null +++ b/package.json @@ -0,0 +1,98 @@ +{ + "name": "vite_react_shadcn_ts", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "build:dev": "vite build --mode development", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@azure/msal-browser": "^4.24.1", + "@azure/msal-react": "^3.0.20", + "@hookform/resolvers": "^3.10.0", + "@radix-ui/react-accordion": "^1.2.11", + "@radix-ui/react-alert-dialog": "^1.1.14", + "@radix-ui/react-aspect-ratio": "^1.1.7", + "@radix-ui/react-avatar": "^1.1.10", + "@radix-ui/react-checkbox": "^1.3.2", + "@radix-ui/react-collapsible": "^1.1.11", + "@radix-ui/react-context-menu": "^2.2.15", + "@radix-ui/react-dialog": "^1.1.14", + "@radix-ui/react-dropdown-menu": "^2.1.15", + "@radix-ui/react-hover-card": "^1.1.14", + "@radix-ui/react-label": "^2.1.7", + "@radix-ui/react-menubar": "^1.1.15", + "@radix-ui/react-navigation-menu": "^1.2.13", + "@radix-ui/react-popover": "^1.1.14", + "@radix-ui/react-progress": "^1.1.7", + "@radix-ui/react-radio-group": "^1.3.7", + "@radix-ui/react-scroll-area": "^1.2.9", + "@radix-ui/react-select": "^2.2.5", + "@radix-ui/react-separator": "^1.1.7", + "@radix-ui/react-slider": "^1.3.5", + "@radix-ui/react-slot": "^1.2.3", + "@radix-ui/react-switch": "^1.2.5", + "@radix-ui/react-tabs": "^1.1.12", + "@radix-ui/react-toast": "^1.2.14", + "@radix-ui/react-toggle": "^1.1.9", + "@radix-ui/react-toggle-group": "^1.1.10", + "@radix-ui/react-tooltip": "^1.2.7", + "@supabase/supabase-js": "^2.58.0", + "@tanstack/react-query": "^5.90.2", + "bcryptjs": "^3.0.2", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "cmdk": "^1.1.1", + "cors": "^2.8.5", + "date-fns": "^3.6.0", + "dotenv": "^17.2.3", + "embla-carousel-react": "^8.6.0", + "exceljs": "^4.4.0", + "express": "^5.1.0", + "input-otp": "^1.4.2", + "jsonwebtoken": "^9.0.2", + "lucide-react": "^0.462.0", + "mysql2": "^3.15.1", + "next-themes": "^0.3.0", + "pdfkit": "^0.17.2", + "pdfkit-table": "^0.1.99", + "react": "^18.3.1", + "react-day-picker": "^8.10.1", + "react-dom": "^18.3.1", + "react-hook-form": "^7.61.1", + "react-resizable-panels": "^2.1.9", + "react-router-dom": "^6.30.2", + "recharts": "^2.15.4", + "sonner": "^1.7.4", + "tailwind-merge": "^2.6.0", + "tailwindcss-animate": "^1.0.7", + "vaul": "^0.9.9", + "xlsx": "^0.18.5", + "zod": "^3.25.76" + }, + "devDependencies": { + "@eslint/js": "^9.32.0", + "@tailwindcss/typography": "^0.5.16", + "@types/node": "^22.16.5", + "@types/react": "^18.3.27", + "@types/react-dom": "^18.3.7", + "@vitejs/plugin-react": "^5.1.1", + "@vitejs/plugin-react-swc": "^3.11.0", + "autoprefixer": "^10.4.21", + "eslint": "^9.32.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.20", + "globals": "^15.15.0", + "lovable-tagger": "^1.1.10", + "nodemon": "^3.1.10", + "postcss": "^8.5.6", + "tailwindcss": "^3.4.17", + "typescript": "^5.8.3", + "typescript-eslint": "^8.38.0", + "vite": "^5.4.19" + } +} diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..2aa7205 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/src/App.css b/src/App.css new file mode 100644 index 0000000..b9d355d --- /dev/null +++ b/src/App.css @@ -0,0 +1,42 @@ +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..e21b331 --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,163 @@ +import { Toaster } from "@/components/ui/toaster"; +import { Toaster as Sonner } from "@/components/ui/sonner"; +import { TooltipProvider } from "@/components/ui/tooltip"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom"; +import NotFound from "./pages/NotFound"; +import AuthMicrosoft from "./pages/AuthMicrosoft"; +import Dashboard from "./pages/Dashboard"; +import Teams from "./pages/Teams"; +import TeamDetail from "./pages/TeamDetail"; +import ValidationDemandes from "./pages/ValidationDemandes"; +import SaisieManuelle from "./pages/SaisieManuelle"; +import ExportPaie from "./pages/ExportPaie"; +import GestionCompteurs from "./pages/GestionCompteurs"; +import ModificationDemande from "./pages/ModificationDemande"; +import Historique from "./pages/Historique"; +import GestionRecuperation from "./pages/GestionRecuperation"; +import CompteRenduRH from "./pages/CompteRenduRH"; + +const queryClient = new QueryClient(); + +// Composant de protection des routes +const ProtectedRoute = ({ children }: { children: React.ReactNode }) => { + const token = localStorage.getItem('token'); + + if (!token) { + return ; + } + + return <>{children}; +}; + +const App = () => ( + + + + + + + {/* Route publique - Authentification Microsoft */} + } /> + {/* } />*/} + + {/* Routes protégées */} + + + + } + /> + + + + + } + /> + + + + + } + /> + + + + + } + /> + + + + + } + /> + + + + + } + /> + + + + + } + /> + + + + + } + /> + + + + + } + /> + + + + + } + /> + + {/* ✅ ROUTE COMPTE-RENDU RH - BIEN PROTÉGÉE */} + + + + } + /> + + {/* ✅ ROUTE GESTION RÉCUPÉRATION - BIEN PROTÉGÉE */} + + + + } + /> + + {/* Route 404 */} + } /> + + + + +); + +export default App; \ No newline at end of file diff --git a/src/DockerFileGTARH.Frontend b/src/DockerFileGTARH.Frontend new file mode 100644 index 0000000..4c24902 --- /dev/null +++ b/src/DockerFileGTARH.Frontend @@ -0,0 +1,15 @@ +FROM node:18-alpine + +WORKDIR /app + +COPY package.json package-lock.json ./ + +# ✅ FORCE l'installation du plugin React manquant +RUN npm install @vitejs/plugin-react --save-dev +RUN npm ci --legacy-peer-deps + +COPY . . + +EXPOSE 3014 + +CMD ["npx", "vite", "--host", "0.0.0.0", "--port", "3014"] diff --git a/src/assets/GTARH.png b/src/assets/GTARH.png new file mode 100644 index 0000000000000000000000000000000000000000..bef2c27af3878424a745eb26f2ee14a8b6975952 GIT binary patch literal 16626 zcmeIZcT`hb*Ebpn9RWc>I!KikI!G4S_l$Jt zXxM2$AP}9Nu9gW1M2f!nQc(bR9JCCYfj`tfx>o)m&{e~WFUiwSX_6oi*{rJ>EC6O` zpp5YLgutD=9i1T|o<4vy2&AeJ;sZyxI|uMOIwM`Z)P#51yM%dNoz#Rab2be#GDC#9Bb1zkHPmTTLzDppp3VVq-Vjd@FMs6_HR0cSm4W{+Zc7RSLVivz z$|hQO|0M!ksR=&}2=Gyslnf3Ih6Kw(y#0`p(n?B7l2S5~GBOf?goJ;nR{%Uj!pol@ z&_KicTZfjjKf=$|C&1O)i}yk&+|fHQKuuVfhWB5pH3I_tTpj<>?&S}G|GpD=F$Dq` zEeUt>mz0J`T`Y^2SM_gsWlbmli_5LiW@O$vT%A0uyA|76tqAv4~*niynEvqVd;av~czn${?;$L3*XU5+y`&;loh2{Tz zsGJc0gvlq+&*L`)oDh=E9?qW5UI7~J9&j(D znsA7Old}sv&?7)t9m%W7E5R%MzcA)s^HnAPlj{E)ruh6PKwm)acV1n94Hy9Iko}h( zz?HIQV8BCfKXp@QzaUqHGcO>ddLi|H+y`*}+uQ$_JPIV@KQ8?*%Lsw{{3pxd< z>g{Ld?d_p{2ksRF=e=R#?B(R_=j*S44)6&$D4KlUXm+?XJ2KqbushMkP`gDPQ_i*(O z;JpX;@Z`Pi3`acVz2gz+$ZOz@^j4MppO^B#H2-gp0Q3P6iR3?Y16=$=Sis8Fg#7?I zI+(pn4Faij>S<}3g%seYr0GCXmoVrYjdjNvdF^_}7WgS62=Gw+k))hxBeS@Lh$b>&%3{I4&>_lCOJzh`Sf7WL{>&5VfQwh zbo;o0%{@5JG=_8=ZBh1u&D$2cYLhatt}>#SKybJ|$HD zzWM*KLtsBDn!|lj(}GH>1KoP`x_&U!9ietrC2Wyyz0$yw&bQ~%f4%`}VMyK0CW9yi zj&xD7Ssie;*N2{{{H%xu?~QO5r8&8ZW);Z5x?Z|Ly*KI&*b2Sxo6i*%E*A(Om!m5`K!2H` z==5rK@{frPMRX3_Wa4NcGtXcx6f`U1A-SY~FzVo^;d|#P;hNcPoB&Uehh*WHE03<9 z_Trc6k+ler|8<90^IEs|QQ>z^>A5Eci(hg_H=NApBE<(L8DP6j0i9R;C&$>zKHMjQG?Eq-YkSq}pZ*3Zp= zK~;|`wia@~rC1<7xT6YC%Oe$AQxdDViY+7Pe*2K5UDmg$M|9?M50v`cNezZ+#L*jO zo(I3G@b7CT*0Kt|D*n0yReRyqejt1_m2B83m!DNIq4F&<+fLni=`ht=%|P zjSOP}gomy(e|fns0TxGF*&P1k&uhY?Z{B-73N80FpA5Q|rbc#)&ms0tbI)KyR2W{V z+-Eq*j%b2*g!ZcY{^QrQeMJcU+BdV?an;d)G3oS4NySj zEm2D@5og(i5EOz~9u)?-il@rpxllAr4^7cSb^x~HTqtIbQ7H9Et zn%+XT)1qtoV)_IMU+grM@up3zXZ5|tK(tWpR?#v>fasJ*#0b>t@1qJ^M@Em!qZUp( zKtFa%>L@|5CxZo_++N$3@ASjVV# zb{p0CR&s9L7$hYqhQ4(l$cpPdZX355oM6KpZ@o9{jeI+0vkHorzX&7@Ira*MN%gp` z`C0n$JoBIw3_ZHqH=hY-4uhi7B2B%mTklK;2@8foQ7uTS*^gEELGm09NNU)N8|HJT zqUY!RRr#n!K(62~P5{vlQ49h~j?GtCRu=^94gDT0e#wxGXmD#kMO7K^j|g}}>!;%$ zcx~n%r!p5aIdG7&+_*a#v?_G8uspgW@UF^xgIInuYE27IqGbGaeX^k6RB zfB(FlS1)V1-*Z8^#e0L}yA;nYCIAhkZtdYRUqwHQFF|3ef=7<19t+22^JT1nr4wxO z?0|O5tZ67gbk$!-d}|^kjiyZ91rqaGirI zmYxUQ0>y)uJ1mEtWgLdDpfMHZP4jI6ib6^*cqdF*_onvUZ)m6oS!_E8VHRvJ@*F^30P^>?X13A^UqhK5k)J+P*e*f$X*=N zfma}aKn-$9(>8o|$gMAz#Ogq=8c1oY*ne||qSdUVUY)AOMt&Q2IF*AZ5jg%|)JF5+ zhn}YI`}SJ?^u^v7P>-M0uX(*Dl&QO>ZVNSr%)Ku#(ldFb!z99$mTdI4z>H~J`;n&N zyQelv%P6h77U+|QEsqP6Z$=Ltxl6j&Doq!-56e$K_m8w2{Sh7&UM;Z2IWsgYP zJ9Q(s(6YX+rW?SmZ>O_m*qdOSp+d#@%uC-vyHvtRdy-ZvskPl_*!V z_wANLu5`j45;u)Ve`$nd<(c9LzRV|6_!N_@`-+XHh$5oodmltVWGwLNZkR*%8eTR= z%suRk4@ZHM!#U!T}KS53O$<+n|Jz1k@?aOEv-gUF}`8v2w47@aBT*tn0zXEq*I7j{{@uX;oL>VS>{bRzZV0$c&Vc8ohe^V6z8}^gLr7 ze0*vmvCNs}VXIW%BQDQ~D7Tvi5Ne{Xaqw7Brr(g-*&f3_1KWvxw{0<*x)nf}z|iWp zr83uneX{nxm93xhkQW)Bu{_ZnR!|?}>i$Pt#W7+tKlYk7a#X~3^o+blNm)7kZr|pg zV(9sjh94yjwImBR0ylBbFlD_7!{bEnjFC>3L)~uT;7t!Q|K!A-4?Ms`sV$5`9V{WoCu?}R zF)Ur|1g&kSst*7}C9Z>Ws+miz?Q62MGw=ROLHOm}G}2rzs)*Lj1u752ajZLRla;5)vRjpp@7+Zb zOn6v#bN1auW^nybfs`U-$~M2BY}fQVXZBg1Wo5N@nUob ztxfKqn7C{LfZDKO;E#jzo60Kqq$A7KRIbZ~!<8+{C^8ma<#v7_y;x|*j_(dBN&>_{ ze+9f-e|_f~V_cit2cplZT0`3_o@#mwIS0 z8)Mj`o*{m|t>!PS;LDi8K|eoHl9|+)8`Cd>A#YGy2!{oKJ*Gmt1_Z5vp}h%%v6eqsXL*&wp(BxlWxmT+N=v(z&kliucG3GoxFBc$|6BT+#ZR;|(%z`0bt3BUAa>%H#mGoIF~LBdl@ zv)A`AGI!MXjd15NJKD%DtM7N0dai)ge|U~zy%|EhgD*u>v}HBc0y(_qzbG4{@$1={ z)fiUe(iQvTs=}BCe~8uP!UqzoXa1}mvprR7_>vMJvs_4CgW3Z+PNmRZ0fYq-#N|wu z2^P{!5rA>X^Ps5KPSr>v9LI;M*H>1Pg*&qW6WtPAt#jKyT8iLAm-u5on*I&Ls#2```=0H5J4(!H^<}#}Y zEOoZ$&>ZvuI$Xc~r@oYi^oRHHwx+rP1jeNv63nP9BozGFpT{HYJg_(6fTS-wB4B&x z`aJSf$>Ci62P^81D>5gdGqJJY)59bKlBJ$z0lmK%1(dhS_3hEu!juYgYwzap#CUAi9!jZvtsgv!>Z7b0V4(4M+shd=f4Zu!X~2dE9LWE}XjnCBi6q zgg%V5;^`WmL66YqB6#11ohSLM<;_s{MCfuAg#Kl?{DGL@cC{yiM=k@36SL_YpL(FO zWxgLepL9%txsAF@T})DB?pWjbWge>42KdtT?azcWb4;hez@Xh2_ViT)!xt`SK+yew zAPp3I)qK*I2@yM$pWm1=)IzRS4vt|*L`kE;otA$Q_y*Q*Z8`nWLhewiXG@>(kZ%_= zv@&HxjCU=q;fKet&f~RZdKPt-f+CaOW5WHuhtXI+X3m5FL}%T9xEqF&G3UrKW1LY{ z4&MlQ71zjwA&5Ww;AP{JToP^-%~_~c>*KI8EYOQ2thY^8Z{M*kR0sb&F3#n<@S}#d z$zV(3S1(}2I=~+Hs-a7Fb^daz&}z>6R0idS>wM=WWEQ0WT)jy~uD_M?QAC>u5T5Ma zZGz3d-7pj|zvQyqz{IG4*JD3(b0knETbRR zM)C-B70h}#^vU=q>$YDw3_wm1hE0HHYyqh1&b&JAO90GFy~~2z`&lhPQ-fy4-u4Nz zQ>;oB*X=rR=a};>5JQ9Pod%+xJZ=#$t(>)FZj}orSLlfnpuE)35gDVTU0Me4OXH(G zI1=S`7YdH13`!pv;qW%Yj`ut$*0UcWRgmy8&SYuSW~aHdsfb}-{{&~#W}4xfz9cYz z9^p7W=X~&$+Y0ymv9pkzx`)wtKkih3YcUKvawzqBsDp3Z?!xJqLo~Gyd&;>H&(*^B ze5*Nnd)INf>$NRnA)|&$h(*Z#aW~d!a4wyXedZn8cschQOVRCixfNlT3s0qwh~`Nn zXPr(4=hR<~$L}psvQ4Wp_j^u?@lD*JuF~xRYA@|^ZTIqkF~KoxRxhgw+%=(WURPZi z(#D`5sd>8mS(Dix%*EBO&uYn59j%u&SRB~VbYpXtvu`;yd$uz(NFiKyU@D^zCeyv$ z*s}N1Kt!I=RRqWYx|}m9jlxm{qd*c)rSIDPP}hT+teD|chIvEMumSx)An7CYP$?E(9Bo!uJ8nob4AyV1w+Gkp;?vTXkXRX+p@(*&6=c zO;z-sGuK28evCJLyUyqyNjd#aq=#tpPc^PlTEoIedWf6`de9UOk? zfvw~7;6KcUZoYTl4%cmF`dKfI4o*TQ>e5~S_eG=AR1TN-yv~kOiF8SWu@;g?BIKy- zL2dqicyTFNQ27&2JM)C7w)Xa6Xx3pIl$1 z;_5oH`fd(Y1|?z`!s*Hr-gV~qcPaZP8At>0820y>-W+s$Sg3rzq+uEq{8}tcr={HKYq*yT7 z4dok`0n+RFN>^CNbSZPcXe2|cl5)Gl*VwD@2w2mHmB4BYy+pN2grsqeME=0xt{Lh( zxup)$8{#vV>)%Td+c#iz<+(sQAkG+aXL{~H3)w*5)@U6-kg11C7?#LLIC5lR$|0o= zq_iFkLoNd}6jme1y`W4l8#bOP$jywz#>5M-%)|;Bzd3j0j5YWJ(aUvAkhLVBVx7ttqpSU{oQaxBiJg z#gVL};j!BQJCB9%(baE2`CANC{Y!v%6FqIa6d#q7WWaV>cFh&E)MJ8~QhNfaRl3S% zo8NNzY7+zc60|ven7QzMJiaGvl+;ee7REO&ci|y8KdNMB-m6;WG8^n~S-s8K&#P^A z`)*v7z-lf^jPXl0K*~_R80tgI*qOd3b&r_2G;{fo5c6PZgk*b^b*}sH^Jes})^Em? z>-ny>5H6)m9G&IQ)XcVu^?X1&_*ahwcv8YBYoRLMelYXtccx5{m%v`YHnp`^WMQB0 z>}KgOk#5ya3Y@HJxg9Gyo}p%m2n zD>~|u_P&w#3`z101KOPaWaF_Qwb+#Y`Eb6j|6cjg<>^j=@a7Z+$$o#ok-Q6VQUejq z+Ih#eldG5LbW-j|t2ry;E{029!*|e>p9t;x^v-O%R$4QId#JcHW8NS z9*s)3y5q@{$5z^57oVED#;}9^UVrV~UAj}Nw!}Bi2LwcDv7T+` zoCEDr7~6mEQx&FJkR*TokHA@~?<2}XP2%Wjsv=>&E5I?ef7#}#>{Mk(jzM{|IZOXV zAv!|>qbg90(}^g=>vQ_+?5Z+8%rp~p!(9H)L-fWQ(zgTK0$lYY$s=d4HU?fb z9FS`Cvqoj^!IvtEMfkE1zfTU4YJlq2Kew23l)_Y%iCWtJlP~c!czwBctB*r{hl{M= zWIK)OKg|txVy^Q8Wl?IT#l_0w3hE5qQ%aPh$+$W8H+NzxRd%muSI^DO#9!2|vBNR# zF~u3X_g6`xKe5gk%EPrU=%#;qIwg`bZ5$6$vL;)nuPg89NOU1Ih}k zKo5S4Am}Z`6&MT8!tuxrOJLm@nGcHG6?UK8SF_RTt-P;ss8xRd_|9=n>Nn=tG`CBy znZ~fJg+RGZ`_bkvI~oth!uLK`!$pPf@n4x~Y@FL(AJl(-?Pun+wZ$ZrmbE@WH?qr3 zxClef`)0sN{tj-C9CAvD)(L2SSg&$_{K}EDyiBa1D@Qwv4>W*a`6>z)K(NO3rheO^ zJV8qGUAf>Z*%M`?_~vD(DW^T|InY{&W(^g1C96waT`q(smM&J}J0^{%H0xGkS|(W& z62AZ;)Rnc(k!=pmfhB%nNk`UKC|G5Rxj*siXXMGObqQ;st5xhg8;txAsHlPi)#R$F!+m>q#Znn6a>ai$ zAv^CW$tke_`aB9&W=|UaK;nMth*=e;KBVc#khCiCOX@*x$x*b1R%yxwY2DO7;?04R zm0L<_7_EOVcgl!5%tqkV*fBF~sYf<;Ydja3di_yD|F(6DPGM|f|Jua{lW5j0|RS4BJt`#?M&zfUF=(x+8rL-k`k1m^o%5W|jFQam3Cw zqV-B%mhXUFZvsEyw`$p%$gph zw3i2fvQBPrHTI6Kdd1etX>Y=n@eJUn{l-Z0pyHoI!!hi&4B5*|J<^zjuuE#-nIflJ zpc_#@044zk6tDegP%QNbTkYF^eiydY(3r{eiICgRt5kFdVrmpqBGZj;1dSvPNS3T zxSaZy>U;kPigX`Oy=NtFaV;kikzriBB1zl9N>8x;*YJh};9zrLI* z|86}Y@dZHNb>k*h{;_?2kZ|C1A20t{0BfC#Vdt^CG!)R%CZ(KjIT*~jFUp5^jexyu z*3~NY1IUP7TsfS1cM(`s9-$|F{+10@9*&G9#Imn;l*i^O7DCSAlv*99@0KL@`~rz8 zG&d!Y5o|b>4RQ^OYU@~e^(xYi>ZNL)G_f4<_2gY`rQD8&8kkmT*oAJ0GV$gB{JFb7 z#D?+a=pkBD?Y>@ab2Wy$|F6=MoHY+lq?8h5h%Nl(-Z%3mZ9cy2i>;P)J!)oaKs}UB zFSiCd{yxQ|cB%?f9iJys&te$}>F z#s`3Dv2~oN%iDswJN~^wML=6%nOR62F!4mi2R1#|lE$CI6g7=Jxw+J1>Yk*fCHm^( z_*BwxKvFBvMgP4XGtir0I^m^{Kt#Z3L%e%;fm0W9l5LdgVkxdgWAmJd@#ysBz$$Qv z$nKdkDP!h_!EtnmmJ?_U+XYhjLr$f?a`c_BPxaonl_s*OkB7UeI3#9P?TH3sp<2a4 z;$6qQbIrUw;Kc6(+WpRbr(rX_tW=)H*8k}qd?Hf^aNmVZCSzD(pfv`zgGTk$1jYiK z^O}{%Ky3^#FA`;4=->8)g~b0=L&7xkV4{2!InX|k-+i3=&2oxWUu1pb><5i)8|QB1 z(w&21ZN_K!fGAo+l*1X(j6UL6p4aN+GqkNA4}D7a;FI@1A|gVP_rARsNQj=ovv+dO z2q=fozA4h6NV5Z+h6mZ+s^2QjExdcQ>a1HV6dfHX47T-(2)|qe>%|Pc65pnbKO<&BMciKpey))4G^hE#s-5b1I_@3*~ zEt)%v1J44AKP{&ZK#Q7J zgva8`JO=20AyaP%FlOF01sbR(@oV@wJ)^_oY=5jO?w$iU+lz^Dp#0)7^0Mkxy5n5c z{u!Ge$MdsZ!V#&PTHY;d$ljiTQs_PXiowoUP!w_7)2m>C%vGp+A~#Q1@U7YD6$=-zC@1W6NZ5`YPR+b+{&N5@HVBI=j5*siZGg|wfLm50-Lrwp^Ddv>^6xRSinBXcGih$qz$Xo04KB82^rZkG$UpLcqa zylzXTN;{N_?fU#p)*Ugd$RN~4}Na%5q{D433YS=CvY^Y#Vx0@nSzvMw% zMJJB=bR>j}i&EsG%(bJo1&LD*rDGT6y91iZIfIJqX;nrS;|rssXW)fV++7-J*yxJk zefe1bm{pQ%4bgRd2QzesR2GEQMyrKiZA`YaTzOVoV$zZ>J1Wadd=6Od%|)(;^N1D( zvfKrl2&(q->hwazD00O`oNz6ga12pY6Ps};V0$InE!y7&dyBVGq#q?Dca3aVu=#Y4KCiqaV zmh98x)2q09yy8;t!C=7Ch$Rlxak2UrZO0zYl5;nA_P-kKLOmLwT% zJR%aqOXZ(MzK65lE2B^VQahwEr>AX(Zar;={Gh^bVrJmQV2Sx=)Xd370DK!6Y9v>D zN6GG!;V}7(x4!^de<A#z&5YbAbWRW;^?0Be}b5nj{$By;%+W z&huYt#edirY_*l&{+|+*Jc3AFpNCrZy0P631zo}b%? zKL-^s+8#sD5kqH5qU#o(3f`}LuZ8&xMZL7{MYUuEVZTF%&lw={`@8!B`x67c7y>3E zmJ2~JR#-uh_^6=0eb%#PKCYMG#><(Be8ZGR)R0pFm&H-si+pm4uCXgCf#E*um6&J{ zkv!Kc1v^^($HJqiAGq;)%pyX5hf;CcA^T?yq?TH2rQwBh`|upAci8DnOF3ok!j*=u z#`!jo!`J@0h`ow5e0{>!@6$HotF&gZ!gP&|(_4 zc&vlhG!%`QXSMiofHU%-5rT zfpL;C@nT+%EJ3eNw#L6Gm`M}urKW5T2gc7m@7X17WzfR>ykd5G-7<_M=1pdwrJQn4 zKRq{gi_l!3m|n}Dd%Lrag0W;NJX#_zCqx_>uSC)}GpLbqUbc0c<6IPPZM?Fp2)p(s zqtQZH?gQT5<64JN8Pg5LwxP(5d1M@P6CwYx3k1n8siGV2w~u~}BAsEhP(JIr62iHe zAxFawHtoD-J6kC?nelu+hB;=Z4Mywqn*;{NZ2}N248OIQf-x^Bz6je`^a;*F`_w; z+MBGQbuZax#+q$Ica&rlTLa)e>jSv)e9=;(Xw_8ty!(omSPtXs9Z~{mXIkLMB9{vM zOu03{)yK7(CX}_bS5DAeM@}PQSp2hlSeAxZ)0Lf%cVpUc<5j*ZK2q$aug#9Q#Xg7f zthGfAooUqJPNl@}SzpN?7bM9b&oyFy`TU4_XLDFT>nwA|d*W;E_CpU|hc~n59l5x1 zcYTdvuDY{oF63p_+g4TjtBd|-pE@euD%*9^bue;7_+L}FeWHsaV!Y#_p091$YsVcA zNN^+`PEiP=F`U>liy=`%xi$(J1}P-ImAlqX&t*n2EYBG?eB$HfTFok0NGL0cM%M5nXv6h9ey&22y5CvgB zj>OP!JA%56K81s7X(8_e6Acy8tZ|}U95WwRm|^yHpBxDp6uE@%?I>d^$b8DLCia}W z!~WUUyA6(n;-a-Dj)YMM!z^U>{e2sRqI`lw)2%`H=^qK6Cd9CYywmLNGAR1~{6yW6 zlm_kB!IB0Svn{ZSZ0i@*;Ejz`L-~W7Xv^Bz$ELO{>Mqgd#pCSFZ&mH0QVdt*R9YKF zEvRvIKUPA!%a8~{7MEJoRRyK9b>Fe|Zlj*>nrh0eKPMFiIko#Foc4Yy#xc%&Jro%jL=7 zpgAejUDm>&BD)0;3Q&|?Y0%)#=ign0-I~jN0gtQs=w^1XEwfgRP2E@DW+QhbC?X6k zp@>(JeG_yqy+47{)@KSb6pdG!5d@(2t4+c3#M0diBe{9Awee<=efpo}Rrx8ygB_$k zArr&aqm&GdFq*mG9LRaprTjBb7PRZoS_~kC_-3cULFKOQT$;8-TKZGjIE!wVB@YVhE# zrzziZ(d-q#9(AKBlJCAYvJbC#qm~Xq$p45BPRAB=HWW|0uL!;q7jk==ey#U-!6Fg^ z6;5!N2%M*iXzHny-q{jvEB#@J@0M$)$hB>uqX?H-)=k7NK6((Y@PYK_t4}4O#!HoH z+TgbhrAzZ9B+_bQA8Y}PQeSdAAb+;-#&zF6Z6jEVo?G%aWb3ZX4vkB0EJ+jlIA{lx95nD0aCRR9k7v_C1@s?}n?2=|;baGYoX=QcWHb3fw5o%vo8oVi*sUe&YpN+w<4t?}8F#2fy=eD?nQ%aWPQ)!_F#Nbi_ z^qriW-8GT9kD0)ZcCDe92d^}|YFZS-WZTA&|JqwU=tU=&d)$e46?J{CAy702SVbAP zpV>K^tqNf@9E4Xm>eP!Rb0ZA;C`ck+8dRvt0bBW}f+yGDu^Z~n&5sgdveED8r+eJ& zfFK$dEEDX+BiVLJXR~0vn=?3*A0LCXtCBqC_s=(D!C}90J?g!ETvH%}qZ!zi{5DTn zk1vFSn1pz!A{LTN3Ie1e}v;uP>A`jy#T-l^GGU*;cUBSiF0kZ%jF=_XfYTA_AA-(W9Z zk`}@hGyj!`a)<_UN)>Lf$DXQI@XZ&0q9h9WV6DSG-)gkEkA=6Zrm9iBoP+{9Oh$Di zt-`}F;A~@o*K;3rFa~{P6D!_i&A$^?R}N~XC^7ud1@&>&sCxU*9XIUk`>I)%Wc6nr zUm2XaO+&K7vuh046XT)zh1Jzq+tPNdCt&MC` zJHd)C-aXq>^3Z>c<(DfQd_?$V*GA^L1TZE1j%bP)tP({%uj83K$|X_d~FGr;>A#H(a8leBO?Aou9&dzU;c=#4x!P z0shZK%a~p=257_$qhu?BHtMV947-b9p`4GJ_&nws@z-FVqx|qYjrsh5A8f~?%Z|di z@D$5+$(ozI`_gpbam<(, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AccordionItem.displayName = "AccordionItem"; + +const AccordionTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + svg]:rotate-180", + className, + )} + {...props} + > + {children} + + + +)); +AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName; + +const AccordionContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + +
{children}
+
+)); + +AccordionContent.displayName = AccordionPrimitive.Content.displayName; + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }; diff --git a/src/components/ui/alert-dialog.tsx b/src/components/ui/alert-dialog.tsx new file mode 100644 index 0000000..6dfbfb4 --- /dev/null +++ b/src/components/ui/alert-dialog.tsx @@ -0,0 +1,104 @@ +import * as React from "react"; +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"; + +import { cn } from "@/lib/utils"; +import { buttonVariants } from "@/components/ui/button"; + +const AlertDialog = AlertDialogPrimitive.Root; + +const AlertDialogTrigger = AlertDialogPrimitive.Trigger; + +const AlertDialogPortal = AlertDialogPrimitive.Portal; + +const AlertDialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName; + +const AlertDialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + +)); +AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName; + +const AlertDialogHeader = ({ className, ...props }: React.HTMLAttributes) => ( +
+); +AlertDialogHeader.displayName = "AlertDialogHeader"; + +const AlertDialogFooter = ({ className, ...props }: React.HTMLAttributes) => ( +
+); +AlertDialogFooter.displayName = "AlertDialogFooter"; + +const AlertDialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName; + +const AlertDialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName; + +const AlertDialogAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName; + +const AlertDialogCancel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName; + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +}; diff --git a/src/components/ui/alert.tsx b/src/components/ui/alert.tsx new file mode 100644 index 0000000..2efc3c8 --- /dev/null +++ b/src/components/ui/alert.tsx @@ -0,0 +1,43 @@ +import * as React from "react"; +import { cva, type VariantProps } from "class-variance-authority"; + +import { cn } from "@/lib/utils"; + +const alertVariants = cva( + "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground", + { + variants: { + variant: { + default: "bg-background text-foreground", + destructive: "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", + }, + }, + defaultVariants: { + variant: "default", + }, + }, +); + +const Alert = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & VariantProps +>(({ className, variant, ...props }, ref) => ( +
+)); +Alert.displayName = "Alert"; + +const AlertTitle = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ), +); +AlertTitle.displayName = "AlertTitle"; + +const AlertDescription = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ), +); +AlertDescription.displayName = "AlertDescription"; + +export { Alert, AlertTitle, AlertDescription }; diff --git a/src/components/ui/aspect-ratio.tsx b/src/components/ui/aspect-ratio.tsx new file mode 100644 index 0000000..c9e6f4b --- /dev/null +++ b/src/components/ui/aspect-ratio.tsx @@ -0,0 +1,5 @@ +import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio"; + +const AspectRatio = AspectRatioPrimitive.Root; + +export { AspectRatio }; diff --git a/src/components/ui/avatar.tsx b/src/components/ui/avatar.tsx new file mode 100644 index 0000000..68d21bb --- /dev/null +++ b/src/components/ui/avatar.tsx @@ -0,0 +1,38 @@ +import * as React from "react"; +import * as AvatarPrimitive from "@radix-ui/react-avatar"; + +import { cn } from "@/lib/utils"; + +const Avatar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +Avatar.displayName = AvatarPrimitive.Root.displayName; + +const AvatarImage = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AvatarImage.displayName = AvatarPrimitive.Image.displayName; + +const AvatarFallback = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName; + +export { Avatar, AvatarImage, AvatarFallback }; diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx new file mode 100644 index 0000000..0853c44 --- /dev/null +++ b/src/components/ui/badge.tsx @@ -0,0 +1,29 @@ +import * as React from "react"; +import { cva, type VariantProps } from "class-variance-authority"; + +import { cn } from "@/lib/utils"; + +const badgeVariants = cva( + "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + { + variants: { + variant: { + default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", + secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", + destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", + outline: "text-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + }, +); + +export interface BadgeProps extends React.HTMLAttributes, VariantProps {} + +function Badge({ className, variant, ...props }: BadgeProps) { + return
; +} + +export { Badge, badgeVariants }; diff --git a/src/components/ui/breadcrumb.tsx b/src/components/ui/breadcrumb.tsx new file mode 100644 index 0000000..ca91ff5 --- /dev/null +++ b/src/components/ui/breadcrumb.tsx @@ -0,0 +1,90 @@ +import * as React from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { ChevronRight, MoreHorizontal } from "lucide-react"; + +import { cn } from "@/lib/utils"; + +const Breadcrumb = React.forwardRef< + HTMLElement, + React.ComponentPropsWithoutRef<"nav"> & { + separator?: React.ReactNode; + } +>(({ ...props }, ref) =>