diff --git a/project/.gitignore b/project/.gitignore new file mode 100644 index 0000000..7ceb59f --- /dev/null +++ b/project/.gitignore @@ -0,0 +1,25 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +.env diff --git a/project/index.html b/project/index.html new file mode 100644 index 0000000..23ca1ce --- /dev/null +++ b/project/index.html @@ -0,0 +1,13 @@ + + + + + + + GTA + + +
+ + + diff --git a/project/package-lock.json b/project/package-lock.json new file mode 100644 index 0000000..5cb466d --- /dev/null +++ b/project/package-lock.json @@ -0,0 +1,2913 @@ +{ + "name": "gestion-conges-jsx", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "gestion-conges-jsx", + "version": "0.0.0", + "dependencies": { + "lucide-react": "^0.344.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^7.7.1" + }, + "devDependencies": { + "@vitejs/plugin-react": "^4.3.1", + "autoprefixer": "^10.4.18", + "postcss": "^8.4.35", + "tailwindcss": "^3.4.1", + "vite": "^5.4.2" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz", + "integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.25.7", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.7.tgz", + "integrity": "sha512-9ickoLz+hcXCeh7jrcin+/SLWm+GkxE2kTvoYyp38p4WkdFXfQJxDFGWp/YHjiKLPx06z2A7W8XKuqbReXDzsw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.7.tgz", + "integrity": "sha512-yJ474Zv3cwiSOO9nXJuqzvwEeM+chDuQ8GJirw+pZ91sCGCyOZ3dJkVE09fTV0VEVzXyLWhh3G/AolYTPX7Mow==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.25.7", + "@babel/generator": "^7.25.7", + "@babel/helper-compilation-targets": "^7.25.7", + "@babel/helper-module-transforms": "^7.25.7", + "@babel/helpers": "^7.25.7", + "@babel/parser": "^7.25.7", + "@babel/template": "^7.25.7", + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7", + "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/generator": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.7.tgz", + "integrity": "sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.7", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.7.tgz", + "integrity": "sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.25.7", + "@babel/helper-validator-option": "^7.25.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.7.tgz", + "integrity": "sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.7.tgz", + "integrity": "sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.25.7", + "@babel/helper-simple-access": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.7", + "@babel/traverse": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.7.tgz", + "integrity": "sha512-eaPZai0PiqCi09pPs3pAFfl/zYgGaE6IdXtYvmf0qlcDTd3WCtO7JWCcRd64e0EQrcYgiHibEZnOGsSY4QSgaw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.7.tgz", + "integrity": "sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz", + "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", + "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.7.tgz", + "integrity": "sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.7.tgz", + "integrity": "sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==", + "dev": true, + "dependencies": { + "@babel/template": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz", + "integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.7", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.7.tgz", + "integrity": "sha512-aZn7ETtQsjjGG5HruveUK06cU3Hljuhd9Iojm4M8WWv3wLE6OkE5PWbDUkItmMgegmccaITudyuW5RPYrYlgWw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.25.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.7.tgz", + "integrity": "sha512-JD9MUnLbPL0WdVK8AWC7F7tTG2OS6u/AKKnsK+NdRhUiVdnzyR1S3kKQCaRLOiaULvUiqK6Z4JQE635VgtCFeg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.7.tgz", + "integrity": "sha512-S/JXG/KrbIY06iyJPKfxr0qRxnhNOdkNXYBl/rmwgDd72cQLH9tEGkDm/yJPGvcSIUoikzfjMios9i+xT/uv9w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.7.tgz", + "integrity": "sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.25.7", + "@babel/parser": "^7.25.7", + "@babel/types": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.7.tgz", + "integrity": "sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.25.7", + "@babel/generator": "^7.25.7", + "@babel/parser": "^7.25.7", + "@babel/template": "^7.25.7", + "@babel/types": "^7.25.7", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.7.tgz", + "integrity": "sha512-vwIVdXG+j+FOpkwqHRcBgHLYNL7XMkufrlaFvL9o6Ai9sJn9+PdyIL5qa0XzTZw084c+u9LOls53eoZWP/W5WQ==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "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, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "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, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "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, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "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, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "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, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "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, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "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, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "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, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "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, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "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, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "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, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "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, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "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, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "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, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "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, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "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, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "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, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "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, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "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, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "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, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "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, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "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, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "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==", + "dev": true, + "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.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", + "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz", + "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz", + "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz", + "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz", + "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz", + "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz", + "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz", + "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz", + "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz", + "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz", + "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz", + "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz", + "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz", + "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz", + "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz", + "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "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, + "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.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "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, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.2.tgz", + "integrity": "sha512-hieu+o05v4glEBucTcKMK3dlES0OeJlD9YVOAPraVMOInBCwzumaIFiUjr4bHK7NPgnAHgiskUoceKercrN8vg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/plugin-transform-react-jsx-self": "^7.24.7", + "@babel/plugin-transform-react-jsx-source": "^7.24.7", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.14.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "dev": true + }, + "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, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "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==", + "dev": true + }, + "node_modules/autoprefixer": { + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", + "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" + } + ], + "dependencies": { + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.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 + }, + "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, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", + "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", + "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" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001663", + "electron-to-chromium": "^1.5.28", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001667", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001667.tgz", + "integrity": "sha512-7LTwJjcRkzKFmtqGsibMeuXmvFDfZq/nzIjnmgCGzKKRVzjD72selLDK1oPF/Oxzmt4fNcPvTDvGqSDG4tCALw==", + "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" + } + ] + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "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, + "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==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 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 + }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "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==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.5.33", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.33.tgz", + "integrity": "sha512-+cYTcFB1QqD4j4LegwLfpCNxifb6dDFUAwk6RsLusCwIaZI6or2f+q8rs5tTB2YC53HhOlIbEaqHMAAC8IOIwA==", + "dev": true + }, + "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==", + "dev": true + }, + "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, + "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/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "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.4" + }, + "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==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "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, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "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, + "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==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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, + "engines": { + "node": ">=6.9.0" + } + }, + "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==", + "dev": true, + "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==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "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==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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, + "engines": { + "node": ">=4" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "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==", + "dev": true, + "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.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, + "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==" + }, + "node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "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, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "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==", + "dev": true + }, + "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==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "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, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "0.344.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.344.0.tgz", + "integrity": "sha512-6YyBnn91GB45VuVT96bYCOKElbJzUHqp65vX8cDcu55MQL9T969v4dhGClpljamuI/+KMO9P6w9Acq1CVQGvIQ==", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "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==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "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==", + "dev": true + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "dev": true + }, + "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, + "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, + "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==", + "dev": true, + "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==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "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==", + "dev": true + }, + "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==", + "dev": true, + "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==", + "dev": true + }, + "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==", + "dev": true, + "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==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "dev": true, + "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" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.0", + "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==", + "dev": true, + "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.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "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==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "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==", + "dev": true, + "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==", + "dev": true + }, + "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==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "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==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.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==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.7.1.tgz", + "integrity": "sha512-jVKHXoWRIsD/qS6lvGveckwb862EekvapdHJN/cGmzw40KnJH5gg53ujOJ4qX6EKIK9LSBfFed/xiQ5yeXNrUA==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.7.1.tgz", + "integrity": "sha512-bavdk2BA5r3MYalGKZ01u8PGuDBloQmzpBZVhDLrOOv1N943Wq6dcM9GhB3x8b7AbqPMEezauv4PeGkAJfy7FQ==", + "license": "MIT", + "dependencies": { + "react-router": "7.7.1" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "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==", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", + "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.24.0", + "@rollup/rollup-android-arm64": "4.24.0", + "@rollup/rollup-darwin-arm64": "4.24.0", + "@rollup/rollup-darwin-x64": "4.24.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.24.0", + "@rollup/rollup-linux-arm-musleabihf": "4.24.0", + "@rollup/rollup-linux-arm64-gnu": "4.24.0", + "@rollup/rollup-linux-arm64-musl": "4.24.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0", + "@rollup/rollup-linux-riscv64-gnu": "4.24.0", + "@rollup/rollup-linux-s390x-gnu": "4.24.0", + "@rollup/rollup-linux-x64-gnu": "4.24.0", + "@rollup/rollup-linux-x64-musl": "4.24.0", + "@rollup/rollup-win32-arm64-msvc": "4.24.0", + "@rollup/rollup-win32-ia32-msvc": "4.24.0", + "@rollup/rollup-win32-x64-msvc": "4.24.0", + "fsevents": "~2.3.2" + } + }, + "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==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "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, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, + "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==", + "dev": true, + "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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=0.10.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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true + }, + "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==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "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": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "dev": 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.6", + "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", + "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/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "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==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "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, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "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==", + "dev": true + }, + "node_modules/update-browserslist-db": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "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" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.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==", + "dev": true + }, + "node_modules/vite": { + "version": "5.4.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz", + "integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==", + "dev": 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/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/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==", + "dev": true + }, + "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==", + "dev": true + }, + "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==", + "dev": true, + "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==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yaml": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", + "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + } + } +} diff --git a/project/package.json b/project/package.json new file mode 100644 index 0000000..f6f0f2a --- /dev/null +++ b/project/package.json @@ -0,0 +1,24 @@ +{ + "name": "gestion-conges-jsx", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "lucide-react": "^0.344.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^7.7.1" + }, + "devDependencies": { + "@vitejs/plugin-react": "^4.3.1", + "autoprefixer": "^10.4.18", + "postcss": "^8.4.35", + "tailwindcss": "^3.4.1", + "vite": "^5.4.2" + } +} diff --git a/project/postcss.config.js b/project/postcss.config.js new file mode 100644 index 0000000..2aa7205 --- /dev/null +++ b/project/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/project/public/assets/ImageEnsup.png b/project/public/assets/ImageEnsup.png new file mode 100644 index 0000000..f389a05 Binary files /dev/null and b/project/public/assets/ImageEnsup.png differ diff --git a/project/public/assets/Logo.png b/project/public/assets/Logo.png new file mode 100644 index 0000000..14bceb1 Binary files /dev/null and b/project/public/assets/Logo.png differ diff --git a/project/public/assets/Logo_Ensitech.png b/project/public/assets/Logo_Ensitech.png new file mode 100644 index 0000000..cad83ce Binary files /dev/null and b/project/public/assets/Logo_Ensitech.png differ diff --git a/project/public/assets/utilisateur.png b/project/public/assets/utilisateur.png new file mode 100644 index 0000000..ff20a20 Binary files /dev/null and b/project/public/assets/utilisateur.png differ diff --git a/project/public/getAllTeamRequests.php b/project/public/getAllTeamRequests.php new file mode 100644 index 0000000..54dac05 --- /dev/null +++ b/project/public/getAllTeamRequests.php @@ -0,0 +1,156 @@ +connect_error) { + error_log("Erreur connexion DB getPendingRequests: " . $conn->connect_error); + echo json_encode(["success" => false, "message" => "Erreur de connexion à la base de données"]); + exit(); +} + +$managerId = $_GET['manager_id'] ?? null; + +if ($managerId === null) { + echo json_encode(["success" => false, "message" => "ID manager manquant"]); + exit(); +} + +error_log("getPendingRequests - Manager ID: $managerId"); + +// Fonction pour calculer les jours ouvrés +function getWorkingDays($startDate, $endDate) { + $workingDays = 0; + $current = new DateTime($startDate); + $end = new DateTime($endDate); + + while ($current <= $end) { + $dayOfWeek = (int)$current->format('N'); + if ($dayOfWeek < 6) { + $workingDays++; + } + $current->modify('+1 day'); + } + return $workingDays; +} + +try { + // D'abord, récupérer le service du manager + $queryManagerService = "SELECT ServiceId FROM Users WHERE ID = ?"; + $stmtManager = $conn->prepare($queryManagerService); + $stmtManager->bind_param("i", $managerId); + $stmtManager->execute(); + $resultManager = $stmtManager->get_result(); + + if ($managerRow = $resultManager->fetch_assoc()) { + $serviceId = $managerRow['ServiceId']; + error_log("getPendingRequests - Service ID du manager: $serviceId"); + + // Récupérer les demandes en attente de l'équipe + $queryRequests = " + SELECT + dc.Id, + dc.DateDebut, + dc.DateFin, + dc.Statut, + dc.DateDemande, + dc.Commentaire, + dc.EmployeeId, + CONCAT(u.Prenom, ' ', u.Nom) as employee_name, + u.Email as employee_email, + tc.Nom as type + FROM DemandeConge dc + JOIN Users u ON dc.EmployeeId = u.ID + JOIN TypeConge tc ON dc.TypeCongeId = tc.Id + WHERE u.ServiceId = ? + AND dc.Statut = 'En attente' + AND u.ID != ? + ORDER BY dc.DateDemande ASC + "; + + $stmtRequests = $conn->prepare($queryRequests); + $stmtRequests->bind_param("ii", $serviceId, $managerId); + $stmtRequests->execute(); + $resultRequests = $stmtRequests->get_result(); + + $requests = []; + while ($row = $resultRequests->fetch_assoc()) { + $workingDays = getWorkingDays($row['DateDebut'], $row['DateFin']); + + $startDate = new DateTime($row['DateDebut']); + $endDate = new DateTime($row['DateFin']); + $submittedDate = new DateTime($row['DateDemande']); + + if ($row['DateDebut'] === $row['DateFin']) { + $dateDisplay = $startDate->format('d/m/Y'); + } else { + $dateDisplay = $startDate->format('d/m/Y') . ' - ' . $endDate->format('d/m/Y'); + } + + $requests[] = [ + 'id' => (int)$row['Id'], + 'employee_id' => (int)$row['EmployeeId'], + 'employee_name' => $row['employee_name'], + 'employee_email' => $row['employee_email'], + 'type' => $row['type'], + 'start_date' => $row['DateDebut'], + 'end_date' => $row['DateFin'], + 'date_display' => $dateDisplay, + 'days' => $workingDays, + 'status' => $row['Statut'], + 'reason' => $row['Commentaire'] ?: '', + 'submitted_at' => $row['DateDemande'], + 'submitted_display' => $submittedDate->format('d/m/Y') + ]; + } + + error_log("getPendingRequests - Demandes en attente trouvées: " . count($requests)); + + echo json_encode([ + "success" => true, + "message" => "Demandes en attente récupérées avec succès", + "requests" => $requests, + "service_id" => $serviceId + ]); + + $stmtRequests->close(); + } else { + error_log("getPendingRequests - Manager non trouvé: $managerId"); + echo json_encode([ + "success" => false, + "message" => "Manager non trouvé" + ]); + } + + $stmtManager->close(); + +} catch (Exception $e) { + error_log("Erreur getPendingRequests: " . $e->getMessage()); + echo json_encode([ + "success" => false, + "message" => "Erreur lors de la récupération des demandes: " . $e->getMessage() + ]); +} + +$conn->close(); +?> \ No newline at end of file diff --git a/project/public/getLeaveCounters.php b/project/public/getLeaveCounters.php new file mode 100644 index 0000000..4818ac3 --- /dev/null +++ b/project/public/getLeaveCounters.php @@ -0,0 +1,278 @@ +connect_error) { + error_log("Erreur connexion DB getLeaveCounters: " . $conn->connect_error); + echo json_encode(["success" => false, "message" => "Erreur de connexion à la base de données : " . $conn->connect_error]); + exit(); +} + +// Récupère l'ID utilisateur depuis les paramètres de requête GET +$userId = $_GET['user_id'] ?? null; + +error_log("=== DEBUT getLeaveCounters.php ==="); +error_log("getLeaveCounters - user_id reçu: " . ($userId ?? 'NULL')); +error_log("getLeaveCounters - Toutes les variables GET: " . print_r($_GET, true)); + +if ($userId === null) { + error_log("getLeaveCounters - user_id manquant"); + echo json_encode(["success" => false, "message" => "ID utilisateur manquant."]); + exit(); +} +// Fonction pour déterminer l'exercice des congés payés (01/06 au 31/05) +function getLeaveYear($date = null) { + if ($date === null) { + $date = new DateTime(); + } else { + $date = new DateTime($date); + } + + $currentYear = (int)$date->format('Y'); + $currentMonth = (int)$date->format('m'); + + // Si on est avant le 1er juin, l'exercice a commencé l'année précédente + if ($currentMonth < 6) { + return $currentYear - 1; + } + // Si on est le 1er juin ou après, l'exercice a commencé cette année + return $currentYear; +} + +// Fonction pour déterminer l'année RTT (01/01 au 31/12) +function getRTTYear($date = null) { + if ($date === null) { + $date = new DateTime(); + } else { + $date = new DateTime($date); + } + + return (int)$date->format('Y'); +} + +// Récupère l'ID utilisateur depuis les paramètres de requête GET +$userId = $_GET['user_id'] ?? null; + +if ($userId === null) { + echo json_encode(["success" => false, "message" => "ID utilisateur manquant."]); + exit(); +} + +// Calcul des exercices selon les règles de gestion +$leaveYear = getLeaveYear(); // Exercice CP (01/06 au 31/05) +$rttYear = getRTTYear(); // Exercice RTT (01/01 au 31/12) +$currentDate = date('Y-m-d'); // Date actuelle pour les filtres de demandes + +// Variables pour les soldes disponibles +$cpSolde = 0; +$rttSolde = 0; +$absSolde = 0; + +// Variables pour les demandes en cours/validées +$cpInProcess = 0; +$rttInProcess = 0; +$absenteism = 0; + +// --- FONCTION UTILITAIRE POUR CALCULER LES JOURS OUVRÉS (hors week-ends) --- +function getWorkingDays($startDate, $endDate) { + $workingDays = 0; + $current = new DateTime($startDate); + $end = new DateTime($endDate); + + while ($current <= $end) { + $dayOfWeek = (int)$current->format('N'); // 1 (pour Lundi) à 7 (pour Dimanche) + if ($dayOfWeek < 6) { // Si ce n'est ni Samedi (6) ni Dimanche (7) + $workingDays++; + } + $current->modify('+1 day'); + } + return $workingDays; +} +// ------------------------------------------------------------------------- + + +// --- Récupération du Solde de Congé Payé (CP) --- +$queryCPSolde = "SELECT cc.Solde FROM CompteurConges cc + JOIN TypeConge tc ON cc.TypeCongeId = tc.Id + WHERE cc.EmployeeId = ? AND tc.Nom = 'Congé payé' AND cc.Annee = ?"; +$stmtCPSolde = $conn->prepare($queryCPSolde); +if ($stmtCPSolde === false) { + error_log("Erreur de préparation de la requête CP Solde : " . $conn->error); +} else { + $stmtCPSolde->bind_param("ii", $userId, $leaveYear); + $stmtCPSolde->execute(); + $resultCPSolde = $stmtCPSolde->get_result(); + if ($rowCPSolde = $resultCPSolde->fetch_assoc()) { + $cpSolde = $rowCPSolde['Solde']; + } + $stmtCPSolde->close(); +} + +// --- Récupération du Solde de RTT --- +$queryRTTSolde = "SELECT cc.Solde FROM CompteurConges cc + JOIN TypeConge tc ON tc.Id = cc.TypeCongeId + WHERE cc.EmployeeId = ? AND tc.Nom = 'RTT' AND cc.Annee = ?"; +$stmtRTTSolde = $conn->prepare($queryRTTSolde); +if ($stmtRTTSolde === false) { + error_log("Erreur de préparation de la requête RTT Solde : " . $conn->error); +} else { + $stmtRTTSolde->bind_param("ii", $userId, $rttYear); + $stmtRTTSolde->execute(); + $resultRTTSolde = $stmtRTTSolde->get_result(); + if ($rowRTTSolde = $resultRTTSolde->fetch_assoc()) { + $rttSolde = $rowRTTSolde['Solde']; + } + $stmtRTTSolde->close(); +} + +// --- Récupération du Solde de Congé Maladie (ABS) --- +$queryABSSolde = "SELECT cc.Solde FROM CompteurConges cc + JOIN TypeConge tc ON tc.Id = cc.TypeCongeId + WHERE cc.EmployeeId = ? AND tc.Nom = 'Congé maladie' AND cc.Annee = ?"; +$stmtABSSolde = $conn->prepare($queryABSSolde); +if ($stmtABSSolde === false) { + error_log("Erreur de préparation de la requête ABS Solde : " . $conn->error); +} else { + $stmtABSSolde->bind_param("ii", $userId, $rttYear); + $stmtABSSolde->execute(); + $resultABSSolde = $stmtABSSolde->get_result(); + if ($rowABSSolde = $resultABSSolde->fetch_assoc()) { + $absSolde = $rowABSSolde['Solde']; + } + $stmtABSSolde->close(); +} + + +// --- Calcul des Congés Payés (CP) en cours (demandes 'En attente' ou 'Validée' dont la fin est >= date actuelle) --- +// Cette requête sélectionne les dates pour le calcul en PHP +$queryCPInProcessDates = "SELECT dc.DateDebut, dc.DateFin FROM DemandeConge dc + JOIN TypeConge tc ON dc.TypeCongeId = tc.Id + WHERE dc.EmployeeId = ? + AND tc.Nom = 'Congé payé' + AND dc.Statut IN ('En attente', 'Validée') + AND dc.DateFin >= ?"; +$stmtCPInProcessDates = $conn->prepare($queryCPInProcessDates); +if ($stmtCPInProcessDates === false) { + error_log("Erreur de préparation de la requête CP en cours dates : " . $conn->error); +} else { + $stmtCPInProcessDates->bind_param("is", $userId, $currentDate); + $stmtCPInProcessDates->execute(); + $resultCPInProcessDates = $stmtCPInProcessDates->get_result(); + while ($row = $resultCPInProcessDates->fetch_assoc()) { + $cpInProcess += getWorkingDays($row['DateDebut'], $row['DateFin']); + } + $stmtCPInProcessDates->close(); +} + +// --- Calcul des RTT en cours (mêmes critères que CP, mais pour RTT) --- +$queryRTTInProcessDates = "SELECT dc.DateDebut, dc.DateFin FROM DemandeConge dc + JOIN TypeConge tc ON dc.TypeCongeId = tc.Id + WHERE dc.EmployeeId = ? + AND tc.Nom = 'RTT' + AND dc.Statut IN ('En attente', 'Validée') + AND dc.DateFin >= ?"; +$stmtRTTInProcessDates = $conn->prepare($queryRTTInProcessDates); +if ($stmtRTTInProcessDates === false) { + error_log("Erreur de préparation de la requête RTT en cours dates : " . $conn->error); +} else { + $stmtRTTInProcessDates->bind_param("is", $userId, $currentDate); + $stmtRTTInProcessDates->execute(); + $resultRTTInProcessDates = $stmtRTTInProcessDates->get_result(); + while ($row = $resultRTTInProcessDates->fetch_assoc()) { + $rttInProcess += getWorkingDays($row['DateDebut'], $row['DateFin']); + } + $stmtRTTInProcessDates->close(); +} + + +// --- Calcul des jours d'absence (ABS) (somme des jours DATEDIFF à partir de DemandeConge) --- +// Note: Ici, on ne modifie pas le calcul, car l'absentéisme maladie est souvent compté sur tous les jours, y compris week-ends, pour le suivi global. +// Si vous devez exclure les week-ends pour les ABS, appliquez getWorkingDays ici aussi. +$queryABSInProcess = "SELECT SUM(DATEDIFF(dc.DateFin, dc.DateDebut) + 1) AS total_abs FROM DemandeConge dc + JOIN TypeConge tc ON dc.TypeCongeId = tc.Id + WHERE dc.EmployeeId = ? + AND tc.Nom = 'Congé maladie' + AND dc.Statut = 'Validée'"; +$stmtABSInProcess = $conn->prepare($queryABSInProcess); +if ($stmtABSInProcess === false) { + error_log("Erreur de préparation de la requête ABS en cours : " . $conn->error); +} else { + $stmtABSInProcess->bind_param("i", $userId); + $stmtABSInProcess->execute(); + $resultABSInProcess = $stmtABSInProcess->get_result(); + if ($rowABSInProcess = $resultABSInProcess->fetch_assoc()) { + $absenteism = $rowABSInProcess['total_abs'] ?? 0; + } + $stmtABSInProcess->close(); +} + +// --- Calcul des soldes disponibles réels (déduction "douce" pour l'affichage/validation frontend) --- +$availableCPCalculated = $cpSolde - $cpInProcess; +if ($availableCPCalculated < 0) { + $availableCPCalculated = 0; +} + +$availableRTTCalculated = $rttSolde - $rttInProcess; +if ($availableRTTCalculated < 0) { + $availableRTTCalculated = 0; +} + + +// Renvoie les compteurs sous format JSON +echo json_encode([ + "success" => true, + "message" => "Compteurs récupérés avec succès.", + "counters" => [ + "availableCP" => (int)$availableCPCalculated, // CP: Solde brut - jours ouvrés en cours/validés futurs + "availableRTT" => (int)$availableRTTCalculated, // RTT: Solde brut - jours ouvrés en cours/validés futurs + "availableABS" => (int)$absSolde, // ABS: Solde brut (sans déduction des jours en cours) + "rttInProcess" => (int)$rttInProcess, // RTT: Jours ouvrés en attente/validés futurs (pour information) + "absenteism" => (int)$absenteism // ABS: Jours d'absence maladie validés/pris (pour information) + ], + "debug_values" => [ + "initial_cp_solde" => (int)$cpSolde, + "cp_en_cours" => (int)$cpInProcess, + "calculated_available_cp" => (int)$availableCPCalculated, + "initial_rtt_solde" => (int)$rttSolde, + "rtt_en_cours" => (int)$rttInProcess, + "calculated_available_rtt" => (int)$availableRTTCalculated, + "leave_year" => $leaveYear, + "rtt_year" => $rttYear, + "current_date_php" => $currentDate, + "user_id_php" => (int)$userId + ] +]); + +$conn->close(); +?> \ No newline at end of file diff --git a/project/public/getPendingRequests.php b/project/public/getPendingRequests.php new file mode 100644 index 0000000..54dac05 --- /dev/null +++ b/project/public/getPendingRequests.php @@ -0,0 +1,156 @@ +connect_error) { + error_log("Erreur connexion DB getPendingRequests: " . $conn->connect_error); + echo json_encode(["success" => false, "message" => "Erreur de connexion à la base de données"]); + exit(); +} + +$managerId = $_GET['manager_id'] ?? null; + +if ($managerId === null) { + echo json_encode(["success" => false, "message" => "ID manager manquant"]); + exit(); +} + +error_log("getPendingRequests - Manager ID: $managerId"); + +// Fonction pour calculer les jours ouvrés +function getWorkingDays($startDate, $endDate) { + $workingDays = 0; + $current = new DateTime($startDate); + $end = new DateTime($endDate); + + while ($current <= $end) { + $dayOfWeek = (int)$current->format('N'); + if ($dayOfWeek < 6) { + $workingDays++; + } + $current->modify('+1 day'); + } + return $workingDays; +} + +try { + // D'abord, récupérer le service du manager + $queryManagerService = "SELECT ServiceId FROM Users WHERE ID = ?"; + $stmtManager = $conn->prepare($queryManagerService); + $stmtManager->bind_param("i", $managerId); + $stmtManager->execute(); + $resultManager = $stmtManager->get_result(); + + if ($managerRow = $resultManager->fetch_assoc()) { + $serviceId = $managerRow['ServiceId']; + error_log("getPendingRequests - Service ID du manager: $serviceId"); + + // Récupérer les demandes en attente de l'équipe + $queryRequests = " + SELECT + dc.Id, + dc.DateDebut, + dc.DateFin, + dc.Statut, + dc.DateDemande, + dc.Commentaire, + dc.EmployeeId, + CONCAT(u.Prenom, ' ', u.Nom) as employee_name, + u.Email as employee_email, + tc.Nom as type + FROM DemandeConge dc + JOIN Users u ON dc.EmployeeId = u.ID + JOIN TypeConge tc ON dc.TypeCongeId = tc.Id + WHERE u.ServiceId = ? + AND dc.Statut = 'En attente' + AND u.ID != ? + ORDER BY dc.DateDemande ASC + "; + + $stmtRequests = $conn->prepare($queryRequests); + $stmtRequests->bind_param("ii", $serviceId, $managerId); + $stmtRequests->execute(); + $resultRequests = $stmtRequests->get_result(); + + $requests = []; + while ($row = $resultRequests->fetch_assoc()) { + $workingDays = getWorkingDays($row['DateDebut'], $row['DateFin']); + + $startDate = new DateTime($row['DateDebut']); + $endDate = new DateTime($row['DateFin']); + $submittedDate = new DateTime($row['DateDemande']); + + if ($row['DateDebut'] === $row['DateFin']) { + $dateDisplay = $startDate->format('d/m/Y'); + } else { + $dateDisplay = $startDate->format('d/m/Y') . ' - ' . $endDate->format('d/m/Y'); + } + + $requests[] = [ + 'id' => (int)$row['Id'], + 'employee_id' => (int)$row['EmployeeId'], + 'employee_name' => $row['employee_name'], + 'employee_email' => $row['employee_email'], + 'type' => $row['type'], + 'start_date' => $row['DateDebut'], + 'end_date' => $row['DateFin'], + 'date_display' => $dateDisplay, + 'days' => $workingDays, + 'status' => $row['Statut'], + 'reason' => $row['Commentaire'] ?: '', + 'submitted_at' => $row['DateDemande'], + 'submitted_display' => $submittedDate->format('d/m/Y') + ]; + } + + error_log("getPendingRequests - Demandes en attente trouvées: " . count($requests)); + + echo json_encode([ + "success" => true, + "message" => "Demandes en attente récupérées avec succès", + "requests" => $requests, + "service_id" => $serviceId + ]); + + $stmtRequests->close(); + } else { + error_log("getPendingRequests - Manager non trouvé: $managerId"); + echo json_encode([ + "success" => false, + "message" => "Manager non trouvé" + ]); + } + + $stmtManager->close(); + +} catch (Exception $e) { + error_log("Erreur getPendingRequests: " . $e->getMessage()); + echo json_encode([ + "success" => false, + "message" => "Erreur lors de la récupération des demandes: " . $e->getMessage() + ]); +} + +$conn->close(); +?> \ No newline at end of file diff --git a/project/public/getRequests.php b/project/public/getRequests.php new file mode 100644 index 0000000..5b00459 --- /dev/null +++ b/project/public/getRequests.php @@ -0,0 +1,195 @@ +connect_error) { + error_log("Erreur connexion DB getRequests: " . $conn->connect_error); + echo json_encode(["success" => false, "message" => "Erreur de connexion à la base de données : " . $conn->connect_error]); + exit(); +} + +// Récupère l'ID utilisateur depuis les paramètres de requête GET +$userId = $_GET['user_id'] ?? null; + +error_log("=== DEBUT getRequests.php ==="); +error_log("getRequests - user_id reçu: " . ($userId ?? 'NULL')); +error_log("getRequests - Toutes les variables GET: " . print_r($_GET, true)); + +if ($userId === null) { + error_log("getRequests - user_id manquant"); + echo json_encode(["success" => false, "message" => "ID utilisateur manquant."]); + exit(); +} + +error_log("getRequests - Récupération pour user_id: $userId (type: " . gettype($userId) . ")"); + +// Vérifier si l'utilisateur existe +$checkUserQuery = "SELECT ID, Nom, Prenom FROM Users WHERE ID = ?"; +$checkUserStmt = $conn->prepare($checkUserQuery); +if ($checkUserStmt) { + $checkUserStmt->bind_param("i", $userId); + $checkUserStmt->execute(); + $userResult = $checkUserStmt->get_result(); + if ($userRow = $userResult->fetch_assoc()) { + error_log("getRequests - Utilisateur trouvé: " . $userRow['Prenom'] . " " . $userRow['Nom']); + } else { + error_log("getRequests - ATTENTION: Utilisateur ID $userId non trouvé dans la table Users"); + } + $checkUserStmt->close(); +} + +// Fonction pour calculer les jours ouvrés (hors week-ends) +function getWorkingDays($startDate, $endDate) { + $workingDays = 0; + $current = new DateTime($startDate); + $end = new DateTime($endDate); + + while ($current <= $end) { + $dayOfWeek = (int)$current->format('N'); // 1 (Lundi) à 7 (Dimanche) + if ($dayOfWeek < 6) { // Si ce n'est ni Samedi (6) ni Dimanche (7) + $workingDays++; + } + $current->modify('+1 day'); + } + return $workingDays; +} + +try { + // Requête pour récupérer les demandes de l'utilisateur avec les informations du type de congé + $query = " + SELECT + dc.Id, + dc.DateDebut, + dc.DateFin, + dc.Statut, + dc.DateDemande, + dc.Commentaire, + dc.Validateur, + tc.Nom as TypeConge + FROM DemandeConge dc + JOIN TypeConge tc ON dc.TypeCongeId = tc.Id + WHERE dc.EmployeeId = ? + ORDER BY dc.DateDemande DESC + "; + + error_log("getRequests - Requête SQL: $query"); + + $stmt = $conn->prepare($query); + if ($stmt === false) { + throw new Exception("Erreur de préparation de la requête : " . $conn->error); + } + + $stmt->bind_param("i", $userId); + $stmt->execute(); + $result = $stmt->get_result(); + + error_log("getRequests - Nombre de résultats trouvés: " . $result->num_rows); + + // Debug: Afficher toutes les demandes de la table pour cet utilisateur + $debugQuery = "SELECT COUNT(*) as total FROM DemandeConge WHERE EmployeeId = ?"; + $debugStmt = $conn->prepare($debugQuery); + if ($debugStmt) { + $debugStmt->bind_param("i", $userId); + $debugStmt->execute(); + $debugResult = $debugStmt->get_result(); + $debugRow = $debugResult->fetch_assoc(); + error_log("getRequests - Total demandes en DB pour user $userId: " . $debugRow['total']); + $debugStmt->close(); + } + + $requests = []; + + while ($row = $result->fetch_assoc()) { + error_log("getRequests - Traitement demande ID: " . $row['Id']); + + // Calcul des jours ouvrés + $workingDays = getWorkingDays($row['DateDebut'], $row['DateFin']); + + // Mapping des types de congés pour l'affichage + $displayType = $row['TypeConge']; + switch ($row['TypeConge']) { + case 'Congé payé': + $displayType = 'Congés payés'; + break; + case 'RTT': + $displayType = 'RTT'; + break; + case 'Congé maladie': + $displayType = 'Congé maladie'; + break; + } + + // Formatage des dates pour l'affichage + $startDate = new DateTime($row['DateDebut']); + $endDate = new DateTime($row['DateFin']); + $submittedDate = new DateTime($row['DateDemande']); + + // Format d'affichage des dates + if ($row['DateDebut'] === $row['DateFin']) { + $dateDisplay = $startDate->format('d/m/Y'); + } else { + $dateDisplay = $startDate->format('d/m/Y') . ' - ' . $endDate->format('d/m/Y'); + } + + $requests[] = [ + 'id' => (int)$row['Id'], + 'type' => $displayType, + 'startDate' => $row['DateDebut'], + 'endDate' => $row['DateFin'], + 'dateDisplay' => $dateDisplay, + 'days' => $workingDays, + 'status' => $row['Statut'], + 'reason' => $row['Commentaire'] ?: 'Aucun commentaire', + 'submittedAt' => $row['DateDemande'], + 'submittedDisplay' => $submittedDate->format('d/m/Y'), + 'validator' => $row['Validateur'] ?: null + ]; + } + + $stmt->close(); + + error_log("getRequests - Demandes formatées: " . count($requests)); + error_log("getRequests - Détail des demandes: " . print_r($requests, true)); + error_log("=== FIN getRequests.php ==="); + + echo json_encode([ + "success" => true, + "message" => "Demandes récupérées avec succès.", + "requests" => $requests, + "total" => count($requests) + ]); + +} catch (Exception $e) { + error_log("Erreur récupération demandes : " . $e->getMessage()); + echo json_encode([ + "success" => false, + "message" => "Erreur lors de la récupération des demandes : " . $e->getMessage() + ]); +} + +$conn->close(); +?> \ No newline at end of file diff --git a/project/public/getTeamLeaves.php b/project/public/getTeamLeaves.php new file mode 100644 index 0000000..bdda57e --- /dev/null +++ b/project/public/getTeamLeaves.php @@ -0,0 +1,115 @@ +connect_error) { + error_log("Erreur connexion DB getTeamLeaves: " . $conn->connect_error); + echo json_encode(["success" => false, "message" => "Erreur de connexion à la base de données"]); + exit(); +} + +$userId = $_GET['user_id'] ?? null; + +if ($userId === null) { + echo json_encode(["success" => false, "message" => "ID utilisateur manquant"]); + exit(); +} + +error_log("getTeamLeaves - User ID: $userId"); + +try { + // Récupérer le service de l'utilisateur + $queryUserService = "SELECT ServiceId FROM Users WHERE ID = ?"; + $stmtUser = $conn->prepare($queryUserService); + $stmtUser->bind_param("i", $userId); + $stmtUser->execute(); + $resultUser = $stmtUser->get_result(); + + if ($userRow = $resultUser->fetch_assoc()) { + $serviceId = $userRow['ServiceId']; + error_log("getTeamLeaves - Service ID: $serviceId"); + + // Récupérer les congés validés de l'équipe (même service) + $queryLeaves = " + SELECT + dc.DateDebut as start_date, + dc.DateFin as end_date, + CONCAT(u.Prenom, ' ', u.Nom) as employee_name, + tc.Nom as type, + tc.CouleurHex as color + FROM DemandeConge dc + JOIN Users u ON dc.EmployeeId = u.ID + JOIN TypeConge tc ON dc.TypeCongeId = tc.Id + WHERE u.ServiceId = ? + AND dc.Statut = 'Validée' + AND dc.DateFin >= CURDATE() - INTERVAL 30 DAY + ORDER BY dc.DateDebut ASC + "; + + $stmtLeaves = $conn->prepare($queryLeaves); + $stmtLeaves->bind_param("i", $serviceId); + $stmtLeaves->execute(); + $resultLeaves = $stmtLeaves->get_result(); + + $leaves = []; + while ($row = $resultLeaves->fetch_assoc()) { + $leaves[] = [ + 'start_date' => $row['start_date'], + 'end_date' => $row['end_date'], + 'employee_name' => $row['employee_name'], + 'type' => $row['type'], + 'color' => $row['color'] ?? '#3B82F6' + ]; + } + + error_log("getTeamLeaves - Congés trouvés: " . count($leaves)); + + echo json_encode([ + "success" => true, + "message" => "Congés de l'équipe récupérés avec succès", + "leaves" => $leaves, + "service_id" => $serviceId + ]); + + $stmtLeaves->close(); + } else { + error_log("getTeamLeaves - Utilisateur non trouvé: $userId"); + echo json_encode([ + "success" => false, + "message" => "Utilisateur non trouvé" + ]); + } + + $stmtUser->close(); + +} catch (Exception $e) { + error_log("Erreur getTeamLeaves: " . $e->getMessage()); + echo json_encode([ + "success" => false, + "message" => "Erreur lors de la récupération des congés: " . $e->getMessage() + ]); +} + +$conn->close(); +?> \ No newline at end of file diff --git a/project/public/getTeamMembers.php b/project/public/getTeamMembers.php new file mode 100644 index 0000000..3000286 --- /dev/null +++ b/project/public/getTeamMembers.php @@ -0,0 +1,116 @@ +connect_error) { + error_log("Erreur connexion DB getTeamMembers: " . $conn->connect_error); + echo json_encode(["success" => false, "message" => "Erreur de connexion à la base de données"]); + exit(); +} + +$managerId = $_GET['manager_id'] ?? null; + +if ($managerId === null) { + echo json_encode(["success" => false, "message" => "ID manager manquant"]); + exit(); +} + +error_log("getTeamMembers - Manager ID: $managerId"); + +try { + // D'abord, récupérer le service du manager + $queryManagerService = "SELECT ServiceId FROM Users WHERE ID = ?"; + $stmtManager = $conn->prepare($queryManagerService); + $stmtManager->bind_param("i", $managerId); + $stmtManager->execute(); + $resultManager = $stmtManager->get_result(); + + if ($managerRow = $resultManager->fetch_assoc()) { + $serviceId = $managerRow['ServiceId']; + error_log("getTeamMembers - Service ID du manager: $serviceId"); + + // Récupérer tous les membres du même service (sauf le manager lui-même) + $queryTeam = " + SELECT + u.ID as id, + u.Nom as nom, + u.Prenom as prenom, + u.Email as email, + u.Role as role, + u.DateEmbauche as date_embauche, + s.Nom as service_name + FROM Users u + JOIN Services s ON u.ServiceId = s.Id + WHERE u.ServiceId = ? AND u.ID != ? AND u.Actif = 1 + ORDER BY u.Prenom, u.Nom + "; + + $stmtTeam = $conn->prepare($queryTeam); + $stmtTeam->bind_param("ii", $serviceId, $managerId); + $stmtTeam->execute(); + $resultTeam = $stmtTeam->get_result(); + + $teamMembers = []; + while ($row = $resultTeam->fetch_assoc()) { + $teamMembers[] = [ + 'id' => (int)$row['id'], + 'nom' => $row['nom'], + 'prenom' => $row['prenom'], + 'email' => $row['email'], + 'role' => $row['role'], + 'date_embauche' => $row['date_embauche'], + 'service_name' => $row['service_name'] + ]; + } + + error_log("getTeamMembers - Membres trouvés: " . count($teamMembers)); + + echo json_encode([ + "success" => true, + "message" => "Équipe récupérée avec succès", + "team_members" => $teamMembers, + "service_id" => $serviceId + ]); + + $stmtTeam->close(); + } else { + error_log("getTeamMembers - Manager non trouvé: $managerId"); + echo json_encode([ + "success" => false, + "message" => "Manager non trouvé" + ]); + } + + $stmtManager->close(); + +} catch (Exception $e) { + error_log("Erreur getTeamMembers: " . $e->getMessage()); + echo json_encode([ + "success" => false, + "message" => "Erreur lors de la récupération de l'équipe: " . $e->getMessage() + ]); +} + +$conn->close(); +?> \ No newline at end of file diff --git a/project/public/login.php b/project/public/login.php new file mode 100644 index 0000000..2db287c --- /dev/null +++ b/project/public/login.php @@ -0,0 +1,74 @@ +connect_error) { + // En cas d'erreur de connexion, renvoie un JSON d'échec + die(json_encode(["success" => false, "message" => "Erreur de connexion à la base de données : " . $conn->connect_error])); +} + +// Récupère les données JSON envoyées via la requête POST +$data = json_decode(file_get_contents('php://input'), true); +$email = $data['email'] ?? ''; +$mot_de_passe = $data['mot_de_passe'] ?? ''; + + +$query = "SELECT ID, Prenom, Nom, Email, Role FROM Users WHERE Email = ? AND MDP = ?"; +$stmt = $conn->prepare($query); + +// Vérifie si la préparation de la requête a réussi +if ($stmt === false) { + die(json_encode(["success" => false, "message" => "Erreur de préparation de la requête : " . $conn->error])); +} + +// Lie les paramètres (ss = string, string pour email et mot_de_passe) +$stmt->bind_param("ss", $email, $mot_de_passe); +$stmt->execute(); + +// Récupère le résultat de la requête +$result = $stmt->get_result(); + +// Vérifie si un utilisateur correspondant a été trouvé +if ($result->num_rows === 1) { + // Récupère la ligne de l'utilisateur sous forme de tableau associatif + $user = $result->fetch_assoc(); + + // Renvoie une réponse JSON de succès avec les données de l'utilisateur + echo json_encode([ + "success" => true, + "message" => "Connexion réussie.", + "user" => [ + "id" => $user['ID'], + "prenom" => $user['Prenom'], + "nom" => $user['Nom'], + "email" => $user['Email'], + "role" => $user['Role'] + ] + ]); +} else { + // Renvoie une réponse JSON d'échec si les identifiants sont incorrects + echo json_encode(["success" => false, "message" => "Identifiants incorrects."]); +} + +// Ferme la connexion à la base de données +$stmt->close(); +$conn->close(); +?> \ No newline at end of file diff --git a/project/public/manualResetCounters.php b/project/public/manualResetCounters.php new file mode 100644 index 0000000..bd737d0 --- /dev/null +++ b/project/public/manualResetCounters.php @@ -0,0 +1,116 @@ + + + + + + + Réinitialisation des Compteurs + + + +
+

🔄 Réinitialisation des Compteurs de Congés

+ +
+

⚠️ ATTENTION

+

Cette opération va réinitialiser TOUS les compteurs de congés selon les règles suivantes :

+ +

Cette action est irréversible !

+
+ + [ + 'method' => 'POST', + 'header' => 'Content-Type: application/json', + 'content' => json_encode(['manual_reset' => true]) + ] + ]); + + $result = file_get_contents($resetUrl, false, $context); + $data = json_decode($result, true); + + if ($data && $data['success']) { + echo '
'; + echo '

✅ Réinitialisation réussie !

'; + echo '

Employés mis à jour : ' . $data['details']['employees_updated'] . '

'; + echo '

Exercice CP : ' . $data['details']['leave_year'] . '

'; + echo '

Année RTT : ' . $data['details']['rtt_year'] . '

'; + echo '

Date de réinitialisation : ' . $data['details']['reset_date'] . '

'; + + if (!empty($data['log'])) { + echo '
Voir le détail
';
+                    foreach ($data['log'] as $logLine) {
+                        echo htmlspecialchars($logLine) . "\n";
+                    }
+                    echo '
'; + } + echo '
'; + } else { + echo '
'; + echo '

❌ Erreur lors de la réinitialisation

'; + echo '

' . ($data['message'] ?? 'Erreur inconnue') . '

'; + echo '
'; + } + } + ?> + +
+

+ +

+ +
+ +
+ +

📋 Informations sur les exercices

+ format('Y'); + $currentMonth = (int)$currentDate->format('m'); + + // Calcul exercice CP + $leaveYear = ($currentMonth < 6) ? $currentYear - 1 : $currentYear; + $leaveYearEnd = $leaveYear + 1; + + echo "

Exercice Congés Payés actuel : du 01/06/$leaveYear au 31/05/$leaveYearEnd

"; + echo "

Exercice RTT actuel : du 01/01/$currentYear au 31/12/$currentYear

"; + echo "

Date actuelle : " . $currentDate->format('d/m/Y H:i:s') . "

"; + ?> + +

🔗 Actions rapides

+

+ + + +

+
+ + \ No newline at end of file diff --git a/project/public/resetLeaveCounters.php b/project/public/resetLeaveCounters.php new file mode 100644 index 0000000..60e4cd9 --- /dev/null +++ b/project/public/resetLeaveCounters.php @@ -0,0 +1,228 @@ +connect_error) { + error_log("Erreur connexion DB reset: " . $conn->connect_error); + echo json_encode([ + "success" => false, + "message" => "Erreur de connexion à la base de données : " . $conn->connect_error + ]); + exit(); +} + +// Log de debug +error_log("Reset counters - Début du script"); + +// Fonction pour déterminer l'exercice des congés payés (01/06 au 31/05) +function getLeaveYear($date = null) { + if ($date === null) { + $date = new DateTime(); + } else { + $date = new DateTime($date); + } + + $currentYear = (int)$date->format('Y'); + $currentMonth = (int)$date->format('m'); + + // Si on est avant le 1er juin, l'exercice a commencé l'année précédente + if ($currentMonth < 6) { + return $currentYear - 1; + } + // Si on est le 1er juin ou après, l'exercice a commencé cette année + return $currentYear; +} + +// Fonction pour déterminer l'année RTT (01/01 au 31/12) +function getRTTYear($date = null) { + if ($date === null) { + $date = new DateTime(); + } else { + $date = new DateTime($date); + } + + return (int)$date->format('Y'); +} + +try { + $conn->begin_transaction(); + + $currentDate = new DateTime(); + $leaveYear = getLeaveYear(); + $rttYear = getRTTYear(); + + error_log("Reset counters - Exercice CP: $leaveYear, RTT: $rttYear"); + + $resetLog = []; + + // 1. Récupérer tous les employés depuis la table Users + $queryEmployees = "SELECT ID FROM Users"; + $resultEmployees = $conn->query($queryEmployees); + + if (!$resultEmployees) { + throw new Exception("Erreur lors de la récupération des employés : " . $conn->error); + } + + error_log("Reset counters - Nombre d'employés trouvés: " . $resultEmployees->num_rows); + + // 2. Récupérer les IDs des types de congés + $queryTypes = "SELECT Id, Nom FROM TypeConge WHERE Nom IN ('Congé payé', 'RTT', 'Congé maladie')"; + $resultTypes = $conn->query($queryTypes); + + $typeIds = []; + while ($row = $resultTypes->fetch_assoc()) { + $typeIds[$row['Nom']] = $row['Id']; + } + + error_log("Reset counters - Types trouvés: " . print_r($typeIds, true)); + + if (count($typeIds) < 3) { + throw new Exception("Types de congés manquants dans la base de données"); + } + + // 3. Pour chaque employé, réinitialiser les compteurs + $employeesUpdated = 0; + while ($employee = $resultEmployees->fetch_assoc()) { + $employeeId = $employee['ID']; + + error_log("Reset counters - Traitement employé: $employeeId"); + + // CONGÉS PAYÉS - Exercice du 01/06 au 31/05 (25 jours) + $queryUpdateCP = " + INSERT INTO CompteurConges (EmployeeId, TypeCongeId, Annee, Solde, Total) + VALUES (?, ?, ?, 25, 25) + ON DUPLICATE KEY UPDATE + Solde = 25, + Total = 25 + "; + $stmtCP = $conn->prepare($queryUpdateCP); + if (!$stmtCP) { + throw new Exception("Erreur préparation CP: " . $conn->error); + } + $stmtCP->bind_param("iii", $employeeId, $typeIds['Congé payé'], $leaveYear); + + if (!$stmtCP->execute()) { + throw new Exception("Erreur lors de la mise à jour des CP pour l'employé $employeeId : " . $stmtCP->error); + } + $stmtCP->close(); + + // RTT - Année civile du 01/01 au 31/12 + // Calcul du nombre de RTT selon l'année + $rttCount = 10; // Par défaut 10 pour 2025 + if ($rttYear == 2024) { + $rttCount = 8; // Exemple pour 2024 + } elseif ($rttYear >= 2025) { + $rttCount = 10; // 10 pour 2025 et après + } + + $queryUpdateRTT = " + INSERT INTO CompteurConges (EmployeeId, TypeCongeId, Annee, Solde, Total) + VALUES (?, ?, ?, ?, ?) + ON DUPLICATE KEY UPDATE + Solde = ?, + Total = ? + "; + $stmtRTT = $conn->prepare($queryUpdateRTT); + if (!$stmtRTT) { + throw new Exception("Erreur préparation RTT: " . $conn->error); + } + $stmtRTT->bind_param("iiiiiii", $employeeId, $typeIds['RTT'], $rttYear, $rttCount, $rttCount, $rttCount, $rttCount); + + if (!$stmtRTT->execute()) { + throw new Exception("Erreur lors de la mise à jour des RTT pour l'employé $employeeId : " . $stmtRTT->error); + } + $stmtRTT->close(); + + // CONGÉ MALADIE - Réinitialiser à 0 (pas de limite) + $queryUpdateABS = " + INSERT INTO CompteurConges (EmployeeId, TypeCongeId, Annee, Solde, Total) + VALUES (?, ?, ?, 0, 0) + ON DUPLICATE KEY UPDATE + Solde = 0, + Total = 0 + "; + $stmtABS = $conn->prepare($queryUpdateABS); + if (!$stmtABS) { + throw new Exception("Erreur préparation ABS: " . $conn->error); + } + $stmtABS->bind_param("iii", $employeeId, $typeIds['Congé maladie'], $rttYear); + + if (!$stmtABS->execute()) { + throw new Exception("Erreur lors de la mise à jour des ABS pour l'employé $employeeId : " . $stmtABS->error); + } + $stmtABS->close(); + + $resetLog[] = "Employé $employeeId : CP=$leaveYear (25j), RTT=$rttYear ({$rttCount}j), ABS=$rttYear (0j)"; + $employeesUpdated++; + } + + error_log("Reset counters - Employés mis à jour: $employeesUpdated"); + + // 4. Log de la réinitialisation + $logEntry = " + === RÉINITIALISATION DES COMPTEURS === + Date: " . $currentDate->format('Y-m-d H:i:s') . " + Exercice CP: $leaveYear (01/06/$leaveYear au 31/05/" . ($leaveYear + 1) . ") + Année RTT: $rttYear (01/01/$rttYear au 31/12/$rttYear) + Employés traités: $employeesUpdated + + Détails: + " . implode("\n ", $resetLog) . " + "; + + // Sauvegarder le log (optionnel - créer une table de logs si nécessaire) + error_log($logEntry, 3, "reset_counters.log"); + + $conn->commit(); + error_log("Reset counters - Transaction commitée avec succès"); + + echo json_encode([ + "success" => true, + "message" => "Compteurs réinitialisés avec succès", + "details" => [ + "employees_updated" => $employeesUpdated, + "leave_year" => $leaveYear, + "rtt_year" => $rttYear, + "cp_days" => 25, + "rtt_days" => $rttCount, + "reset_date" => $currentDate->format('Y-m-d H:i:s') + ], + "log" => $resetLog + ]); + +} catch (Exception $e) { + $conn->rollback(); + error_log("Erreur réinitialisation compteurs : " . $e->getMessage()); + + echo json_encode([ + "success" => false, + "message" => "Erreur lors de la réinitialisation : " . $e->getMessage() + ]); +} + +$conn->close(); +?> \ No newline at end of file diff --git a/project/public/submitLeaveRequest.php b/project/public/submitLeaveRequest.php new file mode 100644 index 0000000..bcb819c --- /dev/null +++ b/project/public/submitLeaveRequest.php @@ -0,0 +1,181 @@ +connect_error) { + error_log("Erreur connexion DB submitLeaveRequest: " . $conn->connect_error); + echo json_encode([ + "success" => false, + "message" => "Erreur de connexion DB : " . $conn->connect_error + ]); + exit(); +} + +// Lecture du JSON envoyé +$input = file_get_contents('php://input'); +error_log("submitLeaveRequest - Input reçu: " . $input); + +$data = json_decode($input, true); + +if (!isset( + $data['EmployeeId'], + $data['TypeConge'], + $data['DateDebut'], + $data['DateFin'], + $data['NumDays'] +)) { + error_log("submitLeaveRequest - Données manquantes: " . print_r($data, true)); + echo json_encode([ + "success" => false, + "message" => "Données manquantes pour la demande de congé." + ]); + exit(); +} + +// Récupération des champs +$employeeId = (int) $data['EmployeeId']; +$typeCongeNom= $data['TypeConge']; +$dateDebut = $data['DateDebut']; +$dateFin = $data['DateFin']; +$commentaire = $data['Commentaire'] ?? ''; +$numDays = (int) $data['NumDays']; + +error_log("submitLeaveRequest - Données parsées: EmployeeId=$employeeId, Type=$typeCongeNom, Début=$dateDebut, Fin=$dateFin"); + +$statut = 'En attente'; +$validateur = null; +$currentDate= date('Y-m-d H:i:s'); // date complète pour DateDemande + +// Mapping frontend → DB +switch ($typeCongeNom) { + case 'CP': $dbTypeCongeName = 'Congé payé'; break; + case 'RTT': $dbTypeCongeName = 'RTT'; break; + case 'ABS': $dbTypeCongeName = 'Congé maladie'; break; + default: + error_log("submitLeaveRequest - Type de congé inconnu: $typeCongeNom"); + echo json_encode([ + "success" => false, + "message" => "Type de congé inconnu." + ]); + $conn->close(); + exit(); +} + +error_log("submitLeaveRequest - Type DB mappé: $dbTypeCongeName"); + +// Récupération de l'ID du type de congé +$stmt = $conn->prepare("SELECT Id FROM TypeConge WHERE Nom = ?"); +if (!$stmt) { + error_log("submitLeaveRequest - Erreur préparation requête TypeConge: " . $conn->error); + echo json_encode([ + "success" => false, + "message" => "Erreur préparation requête TypeConge" + ]); + $conn->close(); + exit(); +} + +$stmt->bind_param("s", $dbTypeCongeName); +$stmt->execute(); +$res = $stmt->get_result(); +if ($row = $res->fetch_assoc()) { + $typeCongeId = (int) $row['Id']; + error_log("submitLeaveRequest - TypeCongeId trouvé: $typeCongeId"); +} else { + error_log("submitLeaveRequest - Type de congé non trouvé en DB: $dbTypeCongeName"); + echo json_encode([ + "success" => false, + "message" => "Type de congé non trouvé en DB : $dbTypeCongeName" + ]); + $stmt->close(); + $conn->close(); + exit(); +} +$stmt->close(); + +// Requête d'insertion dans DemandeConge +$query = " + INSERT INTO DemandeConge + (EmployeeId, DateDebut, DateFin, TypeCongeId, Statut, DateDemande, Commentaire, Validateur, NombreJours) + VALUES + (?, ?, ?, ?, ?, ?, ?, ?, ?) +"; + +error_log("submitLeaveRequest - Requête d'insertion: $query"); + +// Préparation de la requête +$stmt = $conn->prepare($query); +if (!$stmt) { + error_log("Erreur prepare insert : " . $conn->error); + echo json_encode([ + "success" => false, + "message" => "Erreur interne lors de la préparation de la requête." + ]); + $conn->close(); + exit(); +} + +// Pour la colonne Validateur, on passe '' si null +$validParam = $validateur ?? ''; + +error_log("submitLeaveRequest - Paramètres bind: $employeeId, $dateDebut, $dateFin, $typeCongeId, $statut, $currentDate, $commentaire, $validParam, $numDays"); + +// Bind des paramètres (types : i=integer, s=string, d=decimal) +$stmt->bind_param( + "ississssi", + $employeeId, // i + $dateDebut, // s + $dateFin, // s + $typeCongeId, // i + $statut, // s + $currentDate, // s - DateDemande + $commentaire, // s + $validParam, // s + $numDays // i - NombreJours +); + +// Exécution +if ($stmt->execute()) { + $insertId = $conn->insert_id; + error_log("submitLeaveRequest - Insertion réussie, ID: $insertId"); + echo json_encode([ + "success" => true, + "message" => "Demande de congé soumise avec succès.", + "request_id" => $insertId + ]); +} else { + error_log("Erreur execute insert : " . $stmt->error); + echo json_encode([ + "success" => false, + "message" => "Erreur lors de l'enregistrement : " . $stmt->error + ]); +} + +$stmt->close(); +$conn->close(); + +error_log("submitLeaveRequest - Script terminé"); + +?> diff --git a/project/public/test_db.php b/project/public/test_db.php new file mode 100644 index 0000000..a599817 --- /dev/null +++ b/project/public/test_db.php @@ -0,0 +1,14 @@ +connect_error) { + die("❌ Connexion échouée : " . $conn->connect_error); +} +echo "✅ Connexion réussie à la base de données !"; + +?> diff --git a/project/public/validateRequest.php b/project/public/validateRequest.php new file mode 100644 index 0000000..5792465 --- /dev/null +++ b/project/public/validateRequest.php @@ -0,0 +1,197 @@ +connect_error) { + error_log("Erreur connexion DB validateRequest: " . $conn->connect_error); + echo json_encode(["success" => false, "message" => "Erreur de connexion à la base de données"]); + exit(); +} + +// Lecture du JSON envoyé +$input = file_get_contents('php://input'); +error_log("validateRequest - Input reçu: " . $input); + +$data = json_decode($input, true); + +if (!isset($data['request_id'], $data['action'], $data['validator_id'])) { + error_log("validateRequest - Données manquantes: " . print_r($data, true)); + echo json_encode([ + "success" => false, + "message" => "Données manquantes pour la validation" + ]); + exit(); +} + +$requestId = (int)$data['request_id']; +$action = $data['action']; // 'approve' ou 'reject' +$validatorId = (int)$data['validator_id']; +$comment = $data['comment'] ?? ''; + +error_log("validateRequest - Request ID: $requestId, Action: $action, Validator: $validatorId"); + +try { + $conn->begin_transaction(); + + // Vérifier que la demande existe et est en attente + $queryCheck = " + SELECT dc.Id, dc.EmployeeId, dc.TypeCongeId, dc.DateDebut, dc.DateFin, dc.NombreJours, + u.Nom, u.Prenom, tc.Nom as TypeNom + FROM DemandeConge dc + JOIN Users u ON dc.EmployeeId = u.ID + JOIN TypeConge tc ON dc.TypeCongeId = tc.Id + WHERE dc.Id = ? AND dc.Statut = 'En attente' + "; + + $stmtCheck = $conn->prepare($queryCheck); + $stmtCheck->bind_param("i", $requestId); + $stmtCheck->execute(); + $resultCheck = $stmtCheck->get_result(); + + if ($requestRow = $resultCheck->fetch_assoc()) { + $employeeId = $requestRow['EmployeeId']; + $typeCongeId = $requestRow['TypeCongeId']; + $nombreJours = $requestRow['NombreJours']; + $employeeName = $requestRow['Prenom'] . ' ' . $requestRow['Nom']; + $typeNom = $requestRow['TypeNom']; + + error_log("validateRequest - Demande trouvée: $employeeName, Type: $typeNom, Jours: $nombreJours"); + + // Déterminer le nouveau statut + $newStatus = ($action === 'approve') ? 'Validée' : 'Refusée'; + + // Mettre à jour la demande + $queryUpdate = " + UPDATE DemandeConge + SET Statut = ?, + ValidateurId = ?, + DateValidation = NOW(), + CommentaireValidation = ? + WHERE Id = ? + "; + + $stmtUpdate = $conn->prepare($queryUpdate); + $stmtUpdate->bind_param("sisi", $newStatus, $validatorId, $comment, $requestId); + + if ($stmtUpdate->execute()) { + error_log("validateRequest - Demande mise à jour avec succès"); + + // Si approuvée, déduire du solde (sauf pour congé maladie) + if ($action === 'approve' && $typeNom !== 'Congé maladie') { + // Déterminer l'année selon le type de congé + $currentDate = new DateTime(); + if ($typeNom === 'Congé payé') { + // Exercice CP: 01/06 au 31/05 + $year = ($currentDate->format('m') < 6) ? $currentDate->format('Y') - 1 : $currentDate->format('Y'); + } else { + // RTT: année civile + $year = $currentDate->format('Y'); + } + + error_log("validateRequest - Déduction solde: Type=$typeNom, Année=$year, Jours=$nombreJours"); + + // Déduire du solde + $queryDeduct = " + UPDATE CompteurConges + SET Solde = GREATEST(0, Solde - ?) + WHERE EmployeeId = ? AND TypeCongeId = ? AND Annee = ? + "; + + $stmtDeduct = $conn->prepare($queryDeduct); + $stmtDeduct->bind_param("diii", $nombreJours, $employeeId, $typeCongeId, $year); + + if ($stmtDeduct->execute()) { + error_log("validateRequest - Solde déduit avec succès"); + } else { + error_log("validateRequest - Erreur déduction solde: " . $stmtDeduct->error); + } + + $stmtDeduct->close(); + } + + // Créer une notification pour l'employé + $notificationTitle = ($action === 'approve') ? 'Demande approuvée' : 'Demande refusée'; + $notificationMessage = "Votre demande de $typeNom a été " . (($action === 'approve') ? 'approuvée' : 'refusée'); + if ($comment) { + $notificationMessage .= ". Commentaire: $comment"; + } + + $queryNotif = " + INSERT INTO Notifications (UserId, Titre, Message, Type, DemandeCongeId) + VALUES (?, ?, ?, ?, ?) + "; + + $notifType = ($action === 'approve') ? 'Success' : 'Error'; + $stmtNotif = $conn->prepare($queryNotif); + $stmtNotif->bind_param("isssi", $employeeId, $notificationTitle, $notificationMessage, $notifType, $requestId); + $stmtNotif->execute(); + $stmtNotif->close(); + + // Log dans l'historique + $actionText = ($action === 'approve') ? 'Validation congé' : 'Refus congé'; + $actionDetails = "$actionText $employeeName ($typeNom)"; + if ($comment) { + $actionDetails .= " - $comment"; + } + + $queryHistory = " + INSERT INTO HistoriqueActions (UserId, Action, Details, DemandeCongeId) + VALUES (?, ?, ?, ?) + "; + + $stmtHistory = $conn->prepare($queryHistory); + $stmtHistory->bind_param("issi", $validatorId, $actionText, $actionDetails, $requestId); + $stmtHistory->execute(); + $stmtHistory->close(); + + $conn->commit(); + + echo json_encode([ + "success" => true, + "message" => "Demande " . (($action === 'approve') ? 'approuvée' : 'refusée') . " avec succès", + "new_status" => $newStatus + ]); + + } else { + throw new Exception("Erreur lors de la mise à jour: " . $stmtUpdate->error); + } + + $stmtUpdate->close(); + } else { + throw new Exception("Demande non trouvée ou déjà traitée"); + } + + $stmtCheck->close(); + +} catch (Exception $e) { + $conn->rollback(); + error_log("Erreur validateRequest: " . $e->getMessage()); + echo json_encode([ + "success" => false, + "message" => "Erreur lors de la validation: " . $e->getMessage() + ]); +} + +$conn->close(); +?> \ No newline at end of file diff --git a/project/src/App.jsx b/project/src/App.jsx new file mode 100644 index 0000000..8dab5db --- /dev/null +++ b/project/src/App.jsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom'; +import { AuthProvider } from './context/AuthContext'; +import Dashboard from './pages/Dashboard'; +import Login from './pages/Login'; +import Requests from './pages/Requests'; +import Calendar from './pages/Calendar'; +import Manager from './pages/Manager'; +import ProtectedRoute from './components/ProtectedRoute'; + +function App() { + return ( + + + + } /> + + + + } /> + + + + } /> + + + + } /> + + + + } /> + } /> + + + + ); +} + +export default App; \ No newline at end of file diff --git a/project/src/components/NewLeaveRequestModal.jsx b/project/src/components/NewLeaveRequestModal.jsx new file mode 100644 index 0000000..22bfaf3 --- /dev/null +++ b/project/src/components/NewLeaveRequestModal.jsx @@ -0,0 +1,579 @@ +import React, { useState, useEffect } from 'react'; +import { X, Calendar, Clock, AlertCircle, RotateCcw } from 'lucide-react'; + +const NewLeaveRequestModal = ({ + onClose, + availableLeaveCounters, + userId, + onRequestSubmitted, + preselectedStartDate = null, + preselectedEndDate = null, + preselectedType = null +}) => { + const [formData, setFormData] = useState({ + types: preselectedType ? [preselectedType] : [], + startDate: '', + endDate: '', + reason: '', + medicalDocuments: [] + }); + + const [typeDistribution, setTypeDistribution] = useState({}); + + const [isSubmitting, setIsSubmitting] = useState(false); + const [error, setError] = useState(''); + const [calculatedDays, setCalculatedDays] = useState(0); + const [isPreselected, setIsPreselected] = useState(false); + + // Vérifier si des valeurs sont pré-sélectionnées + useEffect(() => { + if (preselectedStartDate || preselectedEndDate || preselectedType) { + setIsPreselected(true); + + // Pré-remplir automatiquement les dates + setFormData(prev => ({ + ...prev, + types: preselectedType ? [preselectedType] : prev.types, + startDate: preselectedStartDate ? preselectedStartDate.toISOString().split('T')[0] : prev.startDate, + endDate: preselectedEndDate ? preselectedEndDate.toISOString().split('T')[0] : + preselectedStartDate ? preselectedStartDate.toISOString().split('T')[0] : prev.endDate + })); + } + }, [preselectedStartDate, preselectedEndDate, preselectedType]); + + // Calculer le nombre de jours ouvrés + const calculateWorkingDays = (start, end) => { + if (!start || !end) return 0; + + const startDate = new Date(start); + const endDate = new Date(end); + let workingDays = 0; + + const current = new Date(startDate); + while (current <= endDate) { + const dayOfWeek = current.getDay(); + if (dayOfWeek !== 0 && dayOfWeek !== 6) { // Pas dimanche (0) ni samedi (6) + workingDays++; + } + current.setDate(current.getDate() + 1); + } + + return workingDays; + }; + + // Recalculer les jours quand les dates changent + useEffect(() => { + const days = calculateWorkingDays(formData.startDate, formData.endDate); + setCalculatedDays(days); + }, [formData.startDate, formData.endDate]); + + const handleInputChange = (e) => { + const { name, value } = e.target; + setFormData(prev => ({ + ...prev, + [name]: value + })); + setError(''); + }; + + const handleFileUpload = (e) => { + const files = Array.from(e.target.files); + const validFiles = []; + const maxSize = 5 * 1024 * 1024; // 5MB + + for (const file of files) { + // Vérifier le type de fichier + const validTypes = ['application/pdf', 'image/jpeg', 'image/jpg', 'image/png']; + if (!validTypes.includes(file.type)) { + setError(`Le fichier "${file.name}" n'est pas un format valide. Formats acceptés : PDF, JPG, PNG`); + continue; + } + + // Vérifier la taille + if (file.size > maxSize) { + setError(`Le fichier "${file.name}" est trop volumineux. Taille maximum : 5MB`); + continue; + } + + validFiles.push(file); + } + + setFormData(prev => ({ + ...prev, + medicalDocuments: [...prev.medicalDocuments, ...validFiles] + })); + + // Reset input pour permettre de re-sélectionner le même fichier + e.target.value = ''; + }; + + const removeDocument = (index) => { + setFormData(prev => ({ + ...prev, + medicalDocuments: prev.medicalDocuments.filter((_, i) => i !== index) + })); + }; + + const formatFileSize = (bytes) => { + if (bytes === 0) return '0 Bytes'; + const k = 1024; + const sizes = ['Bytes', 'KB', 'MB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; + }; + + const handleTypeToggle = (type) => { + setFormData(prev => { + const newTypes = prev.types.includes(type) + ? prev.types.filter(t => t !== type) + : [...prev.types, type]; + return { ...prev, types: newTypes }; + }); + setError(''); + }; + + const handleDistributionChange = (type, days) => { + setTypeDistribution(prev => ({ + ...prev, + [type]: Math.max(0, Math.min(days, calculatedDays)) + })); + }; + + const getTotalDistributedDays = () => { + return Object.values(typeDistribution).reduce((sum, days) => sum + (days || 0), 0); + }; + + const resetForm = () => { + setFormData({ + types: [], + startDate: '', + endDate: '', + reason: '', + medicalDocuments: [] + }); + setTypeDistribution({}); + setIsPreselected(false); + setError(''); + setCalculatedDays(0); + }; + + const validateForm = () => { + if (formData.types.length === 0) { + setError('Veuillez sélectionner au moins un type de congé'); + return false; + } + + // Vérification des documents pour congé maladie + if (formData.types.includes('ABS') && formData.medicalDocuments.length === 0) { + setError('Un justificatif médical est obligatoire pour les arrêts maladie'); + return false; + } + + if (!formData.startDate || !formData.endDate) { + setError('Veuillez sélectionner les dates de début et de fin'); + return false; + } + + if (new Date(formData.startDate) > new Date(formData.endDate)) { + setError('La date de fin doit être postérieure à la date de début'); + return false; + } + + if (new Date(formData.startDate) < new Date()) { + setError('Impossible de faire une demande pour une date passée'); + return false; + } + + // Vérification de la distribution des jours + if (formData.types.length > 1) { + const totalDistributed = getTotalDistributedDays(); + if (totalDistributed !== calculatedDays) { + setError(`Vous devez distribuer exactement ${calculatedDays} jours entre les types sélectionnés`); + return false; + } + } + + // Vérification des soldes pour chaque type + for (const type of formData.types) { + const requiredDays = formData.types.length > 1 ? (typeDistribution[type] || 0) : calculatedDays; + + if (type === 'CP' && requiredDays > availableLeaveCounters.availableCP) { + setError(`Solde CP insuffisant. Vous avez ${availableLeaveCounters.availableCP} jours disponibles`); + return false; + } + + if (type === 'RTT' && requiredDays > availableLeaveCounters.availableRTT) { + setError(`Solde RTT insuffisant. Vous avez ${availableLeaveCounters.availableRTT} jours disponibles`); + return false; + } + } + + return true; + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + + if (!validateForm()) return; + + setIsSubmitting(true); + setError(''); + + try { + // Créer une demande pour chaque type de congé + const requests = formData.types.map(type => { + const days = formData.types.length > 1 ? (typeDistribution[type] || 0) : calculatedDays; + return { + EmployeeId: userId, + TypeConge: type, + DateDebut: formData.startDate, + DateFin: formData.endDate, + Commentaire: formData.reason + (formData.types.length > 1 ? ` (${days} jours ${getTypeLabel(type)})` : ''), + NumDays: days + }; + }); + + // Soumettre toutes les demandes + const responses = await Promise.all( + requests.map(requestData => + fetch('http://localhost/GTA/project/public/submitLeaveRequest.php', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(requestData), + }) + ) + ); + + const results = await Promise.all(responses.map(r => r.json())); + + const allSuccessful = results.every(result => result.success); + + if (allSuccessful) { + onRequestSubmitted?.(); + onClose(); + } else { + const failedResults = results.filter(r => !r.success); + setError(`Erreur lors de la soumission : ${failedResults.map(r => r.message).join(', ')}`); + } + } catch (error) { + console.error('Erreur:', error); + setError('Erreur de connexion au serveur'); + } finally { + setIsSubmitting(false); + } + }; + + const getTypeLabel = (type) => { + switch (type) { + case 'CP': return 'Congés payés'; + case 'RTT': return 'RTT'; + case 'ABS': return 'Arrêt maladie'; + default: return type; + } + }; + + const getAvailableDays = (type) => { + switch (type) { + case 'CP': return availableLeaveCounters.availableCP; + case 'RTT': return availableLeaveCounters.availableRTT; + case 'ABS': return '∞'; + default: return 0; + } + }; + + const getTypeColor = (type) => { + switch (type) { + case 'CP': return 'bg-blue-100 text-blue-800 border-blue-200'; + case 'RTT': return 'bg-green-100 text-green-800 border-green-200'; + case 'ABS': return 'bg-red-100 text-red-800 border-red-200'; + default: return 'bg-gray-100 text-gray-800 border-gray-200'; + } + }; + + return ( +
+
+ {/* Header */} +
+

+ {isPreselected ? 'Confirmer la demande' : 'Nouvelle demande de congé'} +

+ +
+ + {/* Form */} +
+ {/* Pré-sélection info */} + {isPreselected && ( +
+
+ + Sélection depuis le calendrier +
+

+ Les champs ont été pré-remplis selon votre sélection. + Vous pouvez les modifier ou utiliser le bouton "Réinitialiser". +

+
+ )} + + {/* Type de congé */} +
+ +
+ {[ + { key: 'CP', label: 'Congés payés', available: availableLeaveCounters.availableCP }, + { key: 'RTT', label: 'RTT', available: availableLeaveCounters.availableRTT }, + { key: 'ABS', label: 'Arrêt maladie', available: '∞' } + ].map(type => ( +
+ +
+ ))} +
+
+ + {/* Distribution des jours si plusieurs types sélectionnés */} + {formData.types.length > 1 && calculatedDays > 0 && ( +
+

+ Répartition des {calculatedDays} jours entre les types : +

+
+ {formData.types.map(type => ( +
+ + {getTypeLabel(type)} + + handleDistributionChange(type, parseInt(e.target.value) || 0)} + className="w-20 px-2 py-1 border border-gray-300 rounded text-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent" + /> + jours +
+ ))} +
+

+ Total distribué : {getTotalDistributedDays()} / {calculatedDays} jours + {getTotalDistributedDays() !== calculatedDays && ( + + ⚠️ Répartition incomplète + + )} +

+
+
+
+ )} + + {/* Dates */} +
+
+ + +
+
+ + +
+
+ + {/* Durée calculée */} + {calculatedDays > 0 && ( +
+
+ + + Durée : {calculatedDays} jour{calculatedDays > 1 ? 's' : ''} ouvré{calculatedDays > 1 ? 's' : ''} + +
+ {formData.types.length === 1 && formData.types[0] !== 'ABS' && ( +

+ Solde {getTypeLabel(formData.types[0])} disponible : {getAvailableDays(formData.types[0])} jour{getAvailableDays(formData.types[0]) !== 1 && getAvailableDays(formData.types[0]) !== '∞' ? 's' : ''} +

+ )} + {formData.types.length > 1 && ( +

+ Types sélectionnés : {formData.types.map(type => getTypeLabel(type)).join(', ')} +

+ )} +
+ )} + + {/* Motif */} +
+ +