diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..b440869 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +node_modules +dist +.git +*.log \ No newline at end of file diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 22592c9..f68c5f3 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -30,4 +30,4 @@ module.exports = { // 여기에 필요 시 추가 규칙 설정 'react/react-in-jsx-scope': 'off', // React 17+ JSX 자동 설정 }, -} +}; diff --git "a/.github/ISSUE_TEMPLATE/\342\231\273\357\270\217refactor.md" "b/.github/ISSUE_TEMPLATE/\342\231\273\357\270\217refactor.md" index a0cfbb8..825d752 100644 --- "a/.github/ISSUE_TEMPLATE/\342\231\273\357\270\217refactor.md" +++ "b/.github/ISSUE_TEMPLATE/\342\231\273\357\270\217refactor.md" @@ -1,19 +1,22 @@ --- -name: "♻️refactor" +name: '♻️refactor' about: Refactoring 작업 사항을 입력해주세요. -title: "♻️[refactor] " +title: '♻️[refactor] ' labels: refactor assignees: '' - --- -## 📌 Description +## 📌 Description + 이슈에 대한 설명을 입력해주세요. -## ✅ Todo -수행할 작업 목록을 작성해주세요. 예: -- [ ] todo1 +## ✅ Todo + +수행할 작업 목록을 작성해주세요. 예: + +- [ ] todo1 - [ ] 필요한 항목을 추가하세요. -## 📎 ETC +## 📎 ETC + 기타 논의할 사항이나 참고할 내용이 있다면 작성해주세요. diff --git "a/.github/ISSUE_TEMPLATE/\342\234\250feat.md" "b/.github/ISSUE_TEMPLATE/\342\234\250feat.md" index 553a76a..f593eb3 100644 --- "a/.github/ISSUE_TEMPLATE/\342\234\250feat.md" +++ "b/.github/ISSUE_TEMPLATE/\342\234\250feat.md" @@ -1,17 +1,19 @@ --- -name: "✨feat" +name: '✨feat' about: Feature 작업 사항을 입력해주세요. -title: "✨[feat] " +title: '✨[feat] ' labels: feat assignees: '' - --- -## Description 📝 +## Description 📝 + 이슈에 대한 설명을 입력해주세요. -## Todo ✅ +## Todo ✅ + - [ ] todo1 -## ETC 🔍 +## ETC 🔍 + 기타 이슈사항을 입력해주세요. diff --git "a/.github/ISSUE_TEMPLATE/\360\237\220\233fix.md" "b/.github/ISSUE_TEMPLATE/\360\237\220\233fix.md" index 2f9f6b3..d365b7c 100644 --- "a/.github/ISSUE_TEMPLATE/\360\237\220\233fix.md" +++ "b/.github/ISSUE_TEMPLATE/\360\237\220\233fix.md" @@ -4,30 +4,37 @@ about: Bug 발생 시 작성해주세요. title: "\U0001F41B[fix] " labels: bug assignees: '' - --- -## Problem +## Problem + 무슨 문제가 발생했는지 설명해주세요. -## Reproduction -버그 재현 방법을 자세히 설명해주세요. +## Reproduction + +버그 재현 방법을 자세히 설명해주세요. + 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error -## Screenshot -이해를 돕기 위한 스크린샷을 첨부해주세요. +## Screenshot + +이해를 돕기 위한 스크린샷을 첨부해주세요. + +## Expected Behavior -## Expected Behavior 버그가 없었을 때 어떤 결과가 나왔어야 하는지 설명해주세요. -## Log -에러 로그가 있다면 아래에 첨부해주세요. +## Log + +에러 로그가 있다면 아래에 첨부해주세요. + +## Browser + +어떤 브라우저에서 문제가 발생했나요? -## Browser -어떤 브라우저에서 문제가 발생했나요? -- [ ] Chrome -- [ ] Firefox +- [ ] Chrome +- [ ] Firefox - [ ] 기타: (직접 입력) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index c056285..e7cadf4 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -5,11 +5,19 @@ 3. 새로운 모듈 설치시 PR message에 기재할 것. 4. PR 올리기전에 branch 반드시 확인할 것. --> - ## 개요 - - - ## 작업사항 - - - ## 변경로직 - - - ## Issue 번호 - - + +## 개요 + +- + +## 작업사항 + +- + +## 변경로직 + +- + +## Issue 번호 + +- \ No newline at end of file diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..ce964bb --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,58 @@ +name: Deploy to Cloud Run with Docker + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + deploy: + name: Build & Deploy + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + + - name: Install pnpm + uses: pnpm/action-setup@v2 + with: + version: 10 + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build Vite app + run: pnpm run build + + - name: Set up Docker + uses: docker/setup-buildx-action@v2 + + - name: Authenticate to Google Cloud + uses: google-github-actions/auth@v2 + with: + credentials_json: ${{ secrets.GCP_SA_KEY }} + + - name: Set up gcloud CLI + uses: google-github-actions/setup-gcloud@v2 + + - name: Configure Docker for GCR + run: gcloud auth configure-docker --quiet + + - name: Build and Push Docker image + run: | + docker buildx build --platform linux/amd64 -f dockerfile_frontend -t gcr.io/${{ secrets.GCP_PROJECT }}/${{ secrets.GCP_RUN_SERVICE }} --push . + + - name: Deploy to Cloud Run + run: | + gcloud run deploy ${{ secrets.GCP_RUN_SERVICE }} \ + --image gcr.io/${{ secrets.GCP_PROJECT }}/${{ secrets.GCP_RUN_SERVICE }} \ + --region ${{ secrets.GCP_REGION }} \ + --platform managed \ + --allow-unauthenticated diff --git a/.prettierrc b/.prettierrc index 2d8fb58..8dd24ea 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,6 +1,6 @@ { "singleQuote": true, - "semi": false, + "semi": true, "trailingComma": "all", "printWidth": 80, "tabWidth": 2, diff --git a/README.md b/README.md index 7959ce4..eda0665 100644 --- a/README.md +++ b/README.md @@ -1,69 +1 @@ -# React + TypeScript + Vite - -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. - -Currently, two official plugins are available: - -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh - -## Expanding the ESLint configuration - -If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: - -```js -export default tseslint.config([ - globalIgnores(['dist']), - { - files: ['**/*.{ts,tsx}'], - extends: [ - // Other configs... - - // Remove tseslint.configs.recommended and replace with this - ...tseslint.configs.recommendedTypeChecked, - // Alternatively, use this for stricter rules - ...tseslint.configs.strictTypeChecked, - // Optionally, add this for stylistic rules - ...tseslint.configs.stylisticTypeChecked, - - // Other configs... - ], - languageOptions: { - parserOptions: { - project: ['./tsconfig.node.json', './tsconfig.app.json'], - tsconfigRootDir: import.meta.dirname, - }, - // other options... - }, - }, -]) -``` - -You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: - -```js -// eslint.config.js -import reactX from 'eslint-plugin-react-x' -import reactDom from 'eslint-plugin-react-dom' - -export default tseslint.config([ - globalIgnores(['dist']), - { - files: ['**/*.{ts,tsx}'], - extends: [ - // Other configs... - // Enable lint rules for React - reactX.configs['recommended-typescript'], - // Enable lint rules for React DOM - reactDom.configs.recommended, - ], - languageOptions: { - parserOptions: { - project: ['./tsconfig.node.json', './tsconfig.app.json'], - tsconfigRootDir: import.meta.dirname, - }, - // other options... - }, - }, -]) -``` +# 매칭핏 diff --git a/eslint.config.js b/eslint.config.js index d94e7de..e821a89 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,9 +1,9 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import tseslint from 'typescript-eslint' -import { globalIgnores } from 'eslint/config' +import js from '@eslint/js'; +import globals from 'globals'; +import reactHooks from 'eslint-plugin-react-hooks'; +import reactRefresh from 'eslint-plugin-react-refresh'; +import tseslint from 'typescript-eslint'; +import { globalIgnores } from 'eslint/config'; export default tseslint.config([ globalIgnores(['dist']), @@ -20,4 +20,4 @@ export default tseslint.config([ globals: globals.browser, }, }, -]) +]); diff --git a/index.html b/index.html index c5dbba0..93e7ae6 100644 --- a/index.html +++ b/index.html @@ -2,7 +2,7 @@ - + 매칭핏 diff --git a/package.json b/package.json index 74690e8..e8c20c4 100644 --- a/package.json +++ b/package.json @@ -11,13 +11,25 @@ "preview": "vite preview" }, "dependencies": { + "axios": "^1.10.0", + "html2canvas": "^1.4.1", + "jspdf": "^3.0.1", "react": "^19.1.0", - "react-dom": "^19.1.0" + "react-dom": "^19.1.0", + "react-modal": "^3.16.3", + "react-router-dom": "^7.6.3", + "recharts": "^3.1.0", + "zustand": "^5.0.6" }, "devDependencies": { "@eslint/js": "^9.29.0", + "@types/html2canvas": "^1.0.0", + "@types/jspdf": "^2.0.0", + "@types/node": "^24.0.10", "@types/react": "^19.1.8", "@types/react-dom": "^19.1.6", + "@types/react-modal": "^3.16.3", + "@types/react-router-dom": "^5.3.3", "@typescript-eslint/eslint-plugin": "^8.35.1", "@typescript-eslint/parser": "^8.35.1", "@vitejs/plugin-react": "^4.5.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4df8fd7..54fc918 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,24 +5,61 @@ settings: excludeLinksFromLockfile: false importers: + .: dependencies: + axios: + specifier: ^1.10.0 + version: 1.10.0 + html2canvas: + specifier: ^1.4.1 + version: 1.4.1 + jspdf: + specifier: ^3.0.1 + version: 3.0.1 react: specifier: ^19.1.0 version: 19.1.0 react-dom: specifier: ^19.1.0 version: 19.1.0(react@19.1.0) + react-modal: + specifier: ^3.16.3 + version: 3.16.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react-router-dom: + specifier: ^7.6.3 + version: 7.6.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + recharts: + specifier: ^3.1.0 + version: 3.1.0(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)(redux@5.0.1) + zustand: + specifier: ^5.0.6 + version: 5.0.6(@types/react@19.1.8)(immer@10.1.1)(react@19.1.0)(use-sync-external-store@1.5.0(react@19.1.0)) devDependencies: '@eslint/js': specifier: ^9.29.0 version: 9.30.1 + '@types/html2canvas': + specifier: ^1.0.0 + version: 1.0.0 + '@types/jspdf': + specifier: ^2.0.0 + version: 2.0.0 + '@types/node': + specifier: ^24.0.10 + version: 24.0.10 '@types/react': specifier: ^19.1.8 version: 19.1.8 '@types/react-dom': specifier: ^19.1.6 version: 19.1.6(@types/react@19.1.8) + '@types/react-modal': + specifier: ^3.16.3 + version: 3.16.3 + '@types/react-router-dom': + specifier: ^5.3.3 + version: 5.3.3 '@typescript-eslint/eslint-plugin': specifier: ^8.35.1 version: 8.35.1(@typescript-eslint/parser@8.35.1(eslint@9.30.1)(typescript@5.8.3))(eslint@9.30.1)(typescript@5.8.3) @@ -31,7 +68,7 @@ importers: version: 8.35.1(eslint@9.30.1)(typescript@5.8.3) '@vitejs/plugin-react': specifier: ^4.5.2 - version: 4.6.0(vite@7.0.0) + version: 4.6.0(vite@7.0.0(@types/node@24.0.10)) eslint: specifier: ^9.29.0 version: 9.30.1 @@ -64,1319 +101,977 @@ importers: version: 8.35.1(eslint@9.30.1)(typescript@5.8.3) vite: specifier: ^7.0.0 - version: 7.0.0 + version: 7.0.0(@types/node@24.0.10) packages: + '@ampproject/remapping@2.3.0': - resolution: - { - integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==, - } - engines: { node: '>=6.0.0' } + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} '@babel/code-frame@7.27.1': - resolution: - { - integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} '@babel/compat-data@7.28.0': - resolution: - { - integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==} + engines: {node: '>=6.9.0'} '@babel/core@7.28.0': - resolution: - { - integrity: sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==} + engines: {node: '>=6.9.0'} '@babel/generator@7.28.0': - resolution: - { - integrity: sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==} + engines: {node: '>=6.9.0'} '@babel/helper-compilation-targets@7.27.2': - resolution: - { - integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} '@babel/helper-globals@7.28.0': - resolution: - { - integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} '@babel/helper-module-imports@7.27.1': - resolution: - { - integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} '@babel/helper-module-transforms@7.27.3': - resolution: - { - integrity: sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/helper-plugin-utils@7.27.1': - resolution: - { - integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} + engines: {node: '>=6.9.0'} '@babel/helper-string-parser@7.27.1': - resolution: - { - integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} '@babel/helper-validator-identifier@7.27.1': - resolution: - { - integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} '@babel/helper-validator-option@7.27.1': - resolution: - { - integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} '@babel/helpers@7.27.6': - resolution: - { - integrity: sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==} + engines: {node: '>=6.9.0'} '@babel/parser@7.28.0': - resolution: - { - integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==, - } - engines: { node: '>=6.0.0' } + resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==} + engines: {node: '>=6.0.0'} hasBin: true '@babel/plugin-transform-react-jsx-self@7.27.1': - resolution: - { - integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-transform-react-jsx-source@7.27.1': - resolution: - { - integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/runtime@7.28.2': + resolution: {integrity: sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==} + engines: {node: '>=6.9.0'} + '@babel/template@7.27.2': - resolution: - { - integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} '@babel/traverse@7.28.0': - resolution: - { - integrity: sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==} + engines: {node: '>=6.9.0'} '@babel/types@7.28.0': - resolution: - { - integrity: sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==} + engines: {node: '>=6.9.0'} '@esbuild/aix-ppc64@0.25.5': - resolution: - { - integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==} + engines: {node: '>=18'} cpu: [ppc64] os: [aix] '@esbuild/android-arm64@0.25.5': - resolution: - { - integrity: sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==} + engines: {node: '>=18'} cpu: [arm64] os: [android] '@esbuild/android-arm@0.25.5': - resolution: - { - integrity: sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==} + engines: {node: '>=18'} cpu: [arm] os: [android] '@esbuild/android-x64@0.25.5': - resolution: - { - integrity: sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==} + engines: {node: '>=18'} cpu: [x64] os: [android] '@esbuild/darwin-arm64@0.25.5': - resolution: - { - integrity: sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==} + engines: {node: '>=18'} cpu: [arm64] os: [darwin] '@esbuild/darwin-x64@0.25.5': - resolution: - { - integrity: sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==} + engines: {node: '>=18'} cpu: [x64] os: [darwin] '@esbuild/freebsd-arm64@0.25.5': - resolution: - { - integrity: sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==} + engines: {node: '>=18'} cpu: [arm64] os: [freebsd] '@esbuild/freebsd-x64@0.25.5': - resolution: - { - integrity: sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==} + engines: {node: '>=18'} cpu: [x64] os: [freebsd] '@esbuild/linux-arm64@0.25.5': - resolution: - { - integrity: sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==} + engines: {node: '>=18'} cpu: [arm64] os: [linux] '@esbuild/linux-arm@0.25.5': - resolution: - { - integrity: sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==} + engines: {node: '>=18'} cpu: [arm] os: [linux] '@esbuild/linux-ia32@0.25.5': - resolution: - { - integrity: sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==} + engines: {node: '>=18'} cpu: [ia32] os: [linux] '@esbuild/linux-loong64@0.25.5': - resolution: - { - integrity: sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==} + engines: {node: '>=18'} cpu: [loong64] os: [linux] '@esbuild/linux-mips64el@0.25.5': - resolution: - { - integrity: sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==} + engines: {node: '>=18'} cpu: [mips64el] os: [linux] '@esbuild/linux-ppc64@0.25.5': - resolution: - { - integrity: sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==} + engines: {node: '>=18'} cpu: [ppc64] os: [linux] '@esbuild/linux-riscv64@0.25.5': - resolution: - { - integrity: sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==} + engines: {node: '>=18'} cpu: [riscv64] os: [linux] '@esbuild/linux-s390x@0.25.5': - resolution: - { - integrity: sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==} + engines: {node: '>=18'} cpu: [s390x] os: [linux] '@esbuild/linux-x64@0.25.5': - resolution: - { - integrity: sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==} + engines: {node: '>=18'} cpu: [x64] os: [linux] '@esbuild/netbsd-arm64@0.25.5': - resolution: - { - integrity: sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==} + engines: {node: '>=18'} cpu: [arm64] os: [netbsd] '@esbuild/netbsd-x64@0.25.5': - resolution: - { - integrity: sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==} + engines: {node: '>=18'} cpu: [x64] os: [netbsd] '@esbuild/openbsd-arm64@0.25.5': - resolution: - { - integrity: sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==} + engines: {node: '>=18'} cpu: [arm64] os: [openbsd] '@esbuild/openbsd-x64@0.25.5': - resolution: - { - integrity: sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==} + engines: {node: '>=18'} cpu: [x64] os: [openbsd] '@esbuild/sunos-x64@0.25.5': - resolution: - { - integrity: sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==} + engines: {node: '>=18'} cpu: [x64] os: [sunos] '@esbuild/win32-arm64@0.25.5': - resolution: - { - integrity: sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==} + engines: {node: '>=18'} cpu: [arm64] os: [win32] '@esbuild/win32-ia32@0.25.5': - resolution: - { - integrity: sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==} + engines: {node: '>=18'} cpu: [ia32] os: [win32] '@esbuild/win32-x64@0.25.5': - resolution: - { - integrity: sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==} + engines: {node: '>=18'} cpu: [x64] os: [win32] '@eslint-community/eslint-utils@4.7.0': - resolution: - { - integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 '@eslint-community/regexpp@4.12.1': - resolution: - { - integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==, - } - engines: { node: ^12.0.0 || ^14.0.0 || >=16.0.0 } + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} '@eslint/config-array@0.21.0': - resolution: - { - integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/config-helpers@0.3.0': - resolution: - { - integrity: sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/core@0.14.0': - resolution: - { - integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/core@0.15.1': - resolution: - { - integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/eslintrc@3.3.1': - resolution: - { - integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/js@9.30.1': - resolution: - { - integrity: sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.6': - resolution: - { - integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/plugin-kit@0.3.3': - resolution: - { - integrity: sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@humanfs/core@0.19.1': - resolution: - { - integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==, - } - engines: { node: '>=18.18.0' } + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} '@humanfs/node@0.16.6': - resolution: - { - integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==, - } - engines: { node: '>=18.18.0' } + resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': - resolution: - { - integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==, - } - engines: { node: '>=12.22' } + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} '@humanwhocodes/retry@0.3.1': - resolution: - { - integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==, - } - engines: { node: '>=18.18' } + resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} + engines: {node: '>=18.18'} '@humanwhocodes/retry@0.4.3': - resolution: - { - integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==, - } - engines: { node: '>=18.18' } + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} '@jridgewell/gen-mapping@0.3.12': - resolution: - { - integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==, - } + resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==} '@jridgewell/resolve-uri@3.1.2': - resolution: - { - integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==, - } - engines: { node: '>=6.0.0' } + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} '@jridgewell/sourcemap-codec@1.5.4': - resolution: - { - integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==, - } + resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} '@jridgewell/trace-mapping@0.3.29': - resolution: - { - integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==, - } + resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} '@nodelib/fs.scandir@2.1.5': - resolution: - { - integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} '@nodelib/fs.stat@2.0.5': - resolution: - { - integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} '@nodelib/fs.walk@1.2.8': - resolution: - { - integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@reduxjs/toolkit@2.8.2': + resolution: {integrity: sha512-MYlOhQ0sLdw4ud48FoC5w0dH9VfWQjtCjreKwYTT3l+r427qYC5Y8PihNutepr8XrNaBUDQo9khWUwQxZaqt5A==} + peerDependencies: + react: ^16.9.0 || ^17.0.0 || ^18 || ^19 + react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 + peerDependenciesMeta: + react: + optional: true + react-redux: + optional: true '@rolldown/pluginutils@1.0.0-beta.19': - resolution: - { - integrity: sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA==, - } + resolution: {integrity: sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA==} '@rollup/rollup-android-arm-eabi@4.44.1': - resolution: - { - integrity: sha512-JAcBr1+fgqx20m7Fwe1DxPUl/hPkee6jA6Pl7n1v2EFiktAHenTaXl5aIFjUIEsfn9w3HE4gK1lEgNGMzBDs1w==, - } + resolution: {integrity: sha512-JAcBr1+fgqx20m7Fwe1DxPUl/hPkee6jA6Pl7n1v2EFiktAHenTaXl5aIFjUIEsfn9w3HE4gK1lEgNGMzBDs1w==} cpu: [arm] os: [android] '@rollup/rollup-android-arm64@4.44.1': - resolution: - { - integrity: sha512-RurZetXqTu4p+G0ChbnkwBuAtwAbIwJkycw1n6GvlGlBuS4u5qlr5opix8cBAYFJgaY05TWtM+LaoFggUmbZEQ==, - } + resolution: {integrity: sha512-RurZetXqTu4p+G0ChbnkwBuAtwAbIwJkycw1n6GvlGlBuS4u5qlr5opix8cBAYFJgaY05TWtM+LaoFggUmbZEQ==} cpu: [arm64] os: [android] '@rollup/rollup-darwin-arm64@4.44.1': - resolution: - { - integrity: sha512-fM/xPesi7g2M7chk37LOnmnSTHLG/v2ggWqKj3CCA1rMA4mm5KVBT1fNoswbo1JhPuNNZrVwpTvlCVggv8A2zg==, - } + resolution: {integrity: sha512-fM/xPesi7g2M7chk37LOnmnSTHLG/v2ggWqKj3CCA1rMA4mm5KVBT1fNoswbo1JhPuNNZrVwpTvlCVggv8A2zg==} cpu: [arm64] os: [darwin] '@rollup/rollup-darwin-x64@4.44.1': - resolution: - { - integrity: sha512-gDnWk57urJrkrHQ2WVx9TSVTH7lSlU7E3AFqiko+bgjlh78aJ88/3nycMax52VIVjIm3ObXnDL2H00e/xzoipw==, - } + resolution: {integrity: sha512-gDnWk57urJrkrHQ2WVx9TSVTH7lSlU7E3AFqiko+bgjlh78aJ88/3nycMax52VIVjIm3ObXnDL2H00e/xzoipw==} cpu: [x64] os: [darwin] '@rollup/rollup-freebsd-arm64@4.44.1': - resolution: - { - integrity: sha512-wnFQmJ/zPThM5zEGcnDcCJeYJgtSLjh1d//WuHzhf6zT3Md1BvvhJnWoy+HECKu2bMxaIcfWiu3bJgx6z4g2XA==, - } + resolution: {integrity: sha512-wnFQmJ/zPThM5zEGcnDcCJeYJgtSLjh1d//WuHzhf6zT3Md1BvvhJnWoy+HECKu2bMxaIcfWiu3bJgx6z4g2XA==} cpu: [arm64] os: [freebsd] '@rollup/rollup-freebsd-x64@4.44.1': - resolution: - { - integrity: sha512-uBmIxoJ4493YATvU2c0upGz87f99e3wop7TJgOA/bXMFd2SvKCI7xkxY/5k50bv7J6dw1SXT4MQBQSLn8Bb/Uw==, - } + resolution: {integrity: sha512-uBmIxoJ4493YATvU2c0upGz87f99e3wop7TJgOA/bXMFd2SvKCI7xkxY/5k50bv7J6dw1SXT4MQBQSLn8Bb/Uw==} cpu: [x64] os: [freebsd] '@rollup/rollup-linux-arm-gnueabihf@4.44.1': - resolution: - { - integrity: sha512-n0edDmSHlXFhrlmTK7XBuwKlG5MbS7yleS1cQ9nn4kIeW+dJH+ExqNgQ0RrFRew8Y+0V/x6C5IjsHrJmiHtkxQ==, - } + resolution: {integrity: sha512-n0edDmSHlXFhrlmTK7XBuwKlG5MbS7yleS1cQ9nn4kIeW+dJH+ExqNgQ0RrFRew8Y+0V/x6C5IjsHrJmiHtkxQ==} cpu: [arm] os: [linux] '@rollup/rollup-linux-arm-musleabihf@4.44.1': - resolution: - { - integrity: sha512-8WVUPy3FtAsKSpyk21kV52HCxB+me6YkbkFHATzC2Yd3yuqHwy2lbFL4alJOLXKljoRw08Zk8/xEj89cLQ/4Nw==, - } + resolution: {integrity: sha512-8WVUPy3FtAsKSpyk21kV52HCxB+me6YkbkFHATzC2Yd3yuqHwy2lbFL4alJOLXKljoRw08Zk8/xEj89cLQ/4Nw==} cpu: [arm] os: [linux] '@rollup/rollup-linux-arm64-gnu@4.44.1': - resolution: - { - integrity: sha512-yuktAOaeOgorWDeFJggjuCkMGeITfqvPgkIXhDqsfKX8J3jGyxdDZgBV/2kj/2DyPaLiX6bPdjJDTu9RB8lUPQ==, - } + resolution: {integrity: sha512-yuktAOaeOgorWDeFJggjuCkMGeITfqvPgkIXhDqsfKX8J3jGyxdDZgBV/2kj/2DyPaLiX6bPdjJDTu9RB8lUPQ==} cpu: [arm64] os: [linux] '@rollup/rollup-linux-arm64-musl@4.44.1': - resolution: - { - integrity: sha512-W+GBM4ifET1Plw8pdVaecwUgxmiH23CfAUj32u8knq0JPFyK4weRy6H7ooxYFD19YxBulL0Ktsflg5XS7+7u9g==, - } + resolution: {integrity: sha512-W+GBM4ifET1Plw8pdVaecwUgxmiH23CfAUj32u8knq0JPFyK4weRy6H7ooxYFD19YxBulL0Ktsflg5XS7+7u9g==} cpu: [arm64] os: [linux] '@rollup/rollup-linux-loongarch64-gnu@4.44.1': - resolution: - { - integrity: sha512-1zqnUEMWp9WrGVuVak6jWTl4fEtrVKfZY7CvcBmUUpxAJ7WcSowPSAWIKa/0o5mBL/Ij50SIf9tuirGx63Ovew==, - } + resolution: {integrity: sha512-1zqnUEMWp9WrGVuVak6jWTl4fEtrVKfZY7CvcBmUUpxAJ7WcSowPSAWIKa/0o5mBL/Ij50SIf9tuirGx63Ovew==} cpu: [loong64] os: [linux] '@rollup/rollup-linux-powerpc64le-gnu@4.44.1': - resolution: - { - integrity: sha512-Rl3JKaRu0LHIx7ExBAAnf0JcOQetQffaw34T8vLlg9b1IhzcBgaIdnvEbbsZq9uZp3uAH+JkHd20Nwn0h9zPjA==, - } + resolution: {integrity: sha512-Rl3JKaRu0LHIx7ExBAAnf0JcOQetQffaw34T8vLlg9b1IhzcBgaIdnvEbbsZq9uZp3uAH+JkHd20Nwn0h9zPjA==} cpu: [ppc64] os: [linux] '@rollup/rollup-linux-riscv64-gnu@4.44.1': - resolution: - { - integrity: sha512-j5akelU3snyL6K3N/iX7otLBIl347fGwmd95U5gS/7z6T4ftK288jKq3A5lcFKcx7wwzb5rgNvAg3ZbV4BqUSw==, - } + resolution: {integrity: sha512-j5akelU3snyL6K3N/iX7otLBIl347fGwmd95U5gS/7z6T4ftK288jKq3A5lcFKcx7wwzb5rgNvAg3ZbV4BqUSw==} cpu: [riscv64] os: [linux] '@rollup/rollup-linux-riscv64-musl@4.44.1': - resolution: - { - integrity: sha512-ppn5llVGgrZw7yxbIm8TTvtj1EoPgYUAbfw0uDjIOzzoqlZlZrLJ/KuiE7uf5EpTpCTrNt1EdtzF0naMm0wGYg==, - } + resolution: {integrity: sha512-ppn5llVGgrZw7yxbIm8TTvtj1EoPgYUAbfw0uDjIOzzoqlZlZrLJ/KuiE7uf5EpTpCTrNt1EdtzF0naMm0wGYg==} cpu: [riscv64] os: [linux] '@rollup/rollup-linux-s390x-gnu@4.44.1': - resolution: - { - integrity: sha512-Hu6hEdix0oxtUma99jSP7xbvjkUM/ycke/AQQ4EC5g7jNRLLIwjcNwaUy95ZKBJJwg1ZowsclNnjYqzN4zwkAw==, - } + resolution: {integrity: sha512-Hu6hEdix0oxtUma99jSP7xbvjkUM/ycke/AQQ4EC5g7jNRLLIwjcNwaUy95ZKBJJwg1ZowsclNnjYqzN4zwkAw==} cpu: [s390x] os: [linux] '@rollup/rollup-linux-x64-gnu@4.44.1': - resolution: - { - integrity: sha512-EtnsrmZGomz9WxK1bR5079zee3+7a+AdFlghyd6VbAjgRJDbTANJ9dcPIPAi76uG05micpEL+gPGmAKYTschQw==, - } + resolution: {integrity: sha512-EtnsrmZGomz9WxK1bR5079zee3+7a+AdFlghyd6VbAjgRJDbTANJ9dcPIPAi76uG05micpEL+gPGmAKYTschQw==} cpu: [x64] os: [linux] '@rollup/rollup-linux-x64-musl@4.44.1': - resolution: - { - integrity: sha512-iAS4p+J1az6Usn0f8xhgL4PaU878KEtutP4hqw52I4IO6AGoyOkHCxcc4bqufv1tQLdDWFx8lR9YlwxKuv3/3g==, - } + resolution: {integrity: sha512-iAS4p+J1az6Usn0f8xhgL4PaU878KEtutP4hqw52I4IO6AGoyOkHCxcc4bqufv1tQLdDWFx8lR9YlwxKuv3/3g==} cpu: [x64] os: [linux] '@rollup/rollup-win32-arm64-msvc@4.44.1': - resolution: - { - integrity: sha512-NtSJVKcXwcqozOl+FwI41OH3OApDyLk3kqTJgx8+gp6On9ZEt5mYhIsKNPGuaZr3p9T6NWPKGU/03Vw4CNU9qg==, - } + resolution: {integrity: sha512-NtSJVKcXwcqozOl+FwI41OH3OApDyLk3kqTJgx8+gp6On9ZEt5mYhIsKNPGuaZr3p9T6NWPKGU/03Vw4CNU9qg==} cpu: [arm64] os: [win32] '@rollup/rollup-win32-ia32-msvc@4.44.1': - resolution: - { - integrity: sha512-JYA3qvCOLXSsnTR3oiyGws1Dm0YTuxAAeaYGVlGpUsHqloPcFjPg+X0Fj2qODGLNwQOAcCiQmHub/V007kiH5A==, - } + resolution: {integrity: sha512-JYA3qvCOLXSsnTR3oiyGws1Dm0YTuxAAeaYGVlGpUsHqloPcFjPg+X0Fj2qODGLNwQOAcCiQmHub/V007kiH5A==} cpu: [ia32] os: [win32] '@rollup/rollup-win32-x64-msvc@4.44.1': - resolution: - { - integrity: sha512-J8o22LuF0kTe7m+8PvW9wk3/bRq5+mRo5Dqo6+vXb7otCm3TPhYOJqOaQtGU9YMWQSL3krMnoOxMr0+9E6F3Ug==, - } + resolution: {integrity: sha512-J8o22LuF0kTe7m+8PvW9wk3/bRq5+mRo5Dqo6+vXb7otCm3TPhYOJqOaQtGU9YMWQSL3krMnoOxMr0+9E6F3Ug==} cpu: [x64] os: [win32] + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + + '@standard-schema/utils@0.3.0': + resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} + '@types/babel__core@7.20.5': - resolution: - { - integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==, - } + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} '@types/babel__generator@7.27.0': - resolution: - { - integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==, - } + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} '@types/babel__template@7.4.4': - resolution: - { - integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==, - } + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} '@types/babel__traverse@7.20.7': - resolution: - { - integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==, - } + resolution: {integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==} + + '@types/d3-array@3.2.1': + resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==} + + '@types/d3-color@3.1.3': + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + + '@types/d3-ease@3.0.2': + resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} + + '@types/d3-interpolate@3.0.4': + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + + '@types/d3-path@3.1.1': + resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} + + '@types/d3-scale@4.0.9': + resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} + + '@types/d3-shape@3.1.7': + resolution: {integrity: sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==} + + '@types/d3-time@3.0.4': + resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} + + '@types/d3-timer@3.0.2': + resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} '@types/estree@1.0.8': - resolution: - { - integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==, - } + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/history@4.7.11': + resolution: {integrity: sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==} + + '@types/html2canvas@1.0.0': + resolution: {integrity: sha512-BJpVf+FIN9UERmzhbtUgpXj6XBZpG67FMgBLLoj9HZKd9XifcCpSV+UnFcwTZfEyun4U/KmCrrVOG7829L589w==} + deprecated: This is a stub types definition. html2canvas provides its own type definitions, so you do not need this installed. '@types/json-schema@7.0.15': - resolution: - { - integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==, - } + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/jspdf@2.0.0': + resolution: {integrity: sha512-oonYDXI4GegGaG7FFVtriJ+Yqlh4YR3L3NVDiwCEBVG7sbya19SoGx4MW4kg1MCMRPgkbbFTck8YKJL8PrkDfA==} + deprecated: This is a stub types definition. jspdf provides its own type definitions, so you do not need this installed. + + '@types/node@24.0.10': + resolution: {integrity: sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA==} + + '@types/raf@3.4.3': + resolution: {integrity: sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==} '@types/react-dom@19.1.6': - resolution: - { - integrity: sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==, - } + resolution: {integrity: sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==} peerDependencies: '@types/react': ^19.0.0 + '@types/react-modal@3.16.3': + resolution: {integrity: sha512-xXuGavyEGaFQDgBv4UVm8/ZsG+qxeQ7f77yNrW3n+1J6XAstUy5rYHeIHPh1KzsGc6IkCIdu6lQ2xWzu1jBTLg==} + + '@types/react-router-dom@5.3.3': + resolution: {integrity: sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==} + + '@types/react-router@5.1.20': + resolution: {integrity: sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==} + '@types/react@19.1.8': - resolution: - { - integrity: sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==, - } + resolution: {integrity: sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==} + + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + + '@types/use-sync-external-store@0.0.6': + resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} '@typescript-eslint/eslint-plugin@8.35.1': - resolution: - { - integrity: sha512-9XNTlo7P7RJxbVeICaIIIEipqxLKguyh+3UbXuT2XQuFp6d8VOeDEGuz5IiX0dgZo8CiI6aOFLg4e8cF71SFVg==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-9XNTlo7P7RJxbVeICaIIIEipqxLKguyh+3UbXuT2XQuFp6d8VOeDEGuz5IiX0dgZo8CiI6aOFLg4e8cF71SFVg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.35.1 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' '@typescript-eslint/parser@8.35.1': - resolution: - { - integrity: sha512-3MyiDfrfLeK06bi/g9DqJxP5pV74LNv4rFTyvGDmT3x2p1yp1lOd+qYZfiRPIOf/oON+WRZR5wxxuF85qOar+w==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-3MyiDfrfLeK06bi/g9DqJxP5pV74LNv4rFTyvGDmT3x2p1yp1lOd+qYZfiRPIOf/oON+WRZR5wxxuF85qOar+w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' '@typescript-eslint/project-service@8.35.1': - resolution: - { - integrity: sha512-VYxn/5LOpVxADAuP3NrnxxHYfzVtQzLKeldIhDhzC8UHaiQvYlXvKuVho1qLduFbJjjy5U5bkGwa3rUGUb1Q6Q==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-VYxn/5LOpVxADAuP3NrnxxHYfzVtQzLKeldIhDhzC8UHaiQvYlXvKuVho1qLduFbJjjy5U5bkGwa3rUGUb1Q6Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' '@typescript-eslint/scope-manager@8.35.1': - resolution: - { - integrity: sha512-s/Bpd4i7ht2934nG+UoSPlYXd08KYz3bmjLEb7Ye1UVob0d1ENiT3lY8bsCmik4RqfSbPw9xJJHbugpPpP5JUg==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-s/Bpd4i7ht2934nG+UoSPlYXd08KYz3bmjLEb7Ye1UVob0d1ENiT3lY8bsCmik4RqfSbPw9xJJHbugpPpP5JUg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/tsconfig-utils@8.35.1': - resolution: - { - integrity: sha512-K5/U9VmT9dTHoNowWZpz+/TObS3xqC5h0xAIjXPw+MNcKV9qg6eSatEnmeAwkjHijhACH0/N7bkhKvbt1+DXWQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-K5/U9VmT9dTHoNowWZpz+/TObS3xqC5h0xAIjXPw+MNcKV9qg6eSatEnmeAwkjHijhACH0/N7bkhKvbt1+DXWQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' '@typescript-eslint/type-utils@8.35.1': - resolution: - { - integrity: sha512-HOrUBlfVRz5W2LIKpXzZoy6VTZzMu2n8q9C2V/cFngIC5U1nStJgv0tMV4sZPzdf4wQm9/ToWUFPMN9Vq9VJQQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-HOrUBlfVRz5W2LIKpXzZoy6VTZzMu2n8q9C2V/cFngIC5U1nStJgv0tMV4sZPzdf4wQm9/ToWUFPMN9Vq9VJQQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' '@typescript-eslint/types@8.35.1': - resolution: - { - integrity: sha512-q/O04vVnKHfrrhNAscndAn1tuQhIkwqnaW+eu5waD5IPts2eX1dgJxgqcPx5BX109/qAz7IG6VrEPTOYKCNfRQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-q/O04vVnKHfrrhNAscndAn1tuQhIkwqnaW+eu5waD5IPts2eX1dgJxgqcPx5BX109/qAz7IG6VrEPTOYKCNfRQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/typescript-estree@8.35.1': - resolution: - { - integrity: sha512-Vvpuvj4tBxIka7cPs6Y1uvM7gJgdF5Uu9F+mBJBPY4MhvjrjWGK4H0lVgLJd/8PWZ23FTqsaJaLEkBCFUk8Y9g==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-Vvpuvj4tBxIka7cPs6Y1uvM7gJgdF5Uu9F+mBJBPY4MhvjrjWGK4H0lVgLJd/8PWZ23FTqsaJaLEkBCFUk8Y9g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' '@typescript-eslint/utils@8.35.1': - resolution: - { - integrity: sha512-lhnwatFmOFcazAsUm3ZnZFpXSxiwoa1Lj50HphnDe1Et01NF4+hrdXONSUHIcbVu2eFb1bAf+5yjXkGVkXBKAQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-lhnwatFmOFcazAsUm3ZnZFpXSxiwoa1Lj50HphnDe1Et01NF4+hrdXONSUHIcbVu2eFb1bAf+5yjXkGVkXBKAQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' '@typescript-eslint/visitor-keys@8.35.1': - resolution: - { - integrity: sha512-VRwixir4zBWCSTP/ljEo091lbpypz57PoeAQ9imjG+vbeof9LplljsL1mos4ccG6H9IjfrVGM359RozUnuFhpw==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-VRwixir4zBWCSTP/ljEo091lbpypz57PoeAQ9imjG+vbeof9LplljsL1mos4ccG6H9IjfrVGM359RozUnuFhpw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@vitejs/plugin-react@4.6.0': - resolution: - { - integrity: sha512-5Kgff+m8e2PB+9j51eGHEpn5kUzRKH2Ry0qGoe8ItJg7pqnkPrYPkDQZGgGmTa0EGarHrkjLvOdU3b1fzI8otQ==, - } - engines: { node: ^14.18.0 || >=16.0.0 } + resolution: {integrity: sha512-5Kgff+m8e2PB+9j51eGHEpn5kUzRKH2Ry0qGoe8ItJg7pqnkPrYPkDQZGgGmTa0EGarHrkjLvOdU3b1fzI8otQ==} + engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0 acorn-jsx@5.3.2: - resolution: - { - integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==, - } + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 acorn@8.15.0: - resolution: - { - integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==, - } - engines: { node: '>=0.4.0' } + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} hasBin: true ajv@6.12.6: - resolution: - { - integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==, - } + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} ansi-styles@4.3.0: - resolution: - { - integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} argparse@2.0.1: - resolution: - { - integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==, - } + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} aria-query@5.3.2: - resolution: - { - integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} array-buffer-byte-length@1.0.2: - resolution: - { - integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} array-includes@3.1.9: - resolution: - { - integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} + engines: {node: '>= 0.4'} array.prototype.findlast@1.2.5: - resolution: - { - integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} array.prototype.flat@1.3.3: - resolution: - { - integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} array.prototype.flatmap@1.3.3: - resolution: - { - integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} array.prototype.tosorted@1.1.4: - resolution: - { - integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} arraybuffer.prototype.slice@1.0.4: - resolution: - { - integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} ast-types-flow@0.0.8: - resolution: - { - integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==, - } + resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} async-function@1.0.0: - resolution: - { - integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + atob@2.1.2: + resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==} + engines: {node: '>= 4.5.0'} + hasBin: true available-typed-arrays@1.0.7: - resolution: - { - integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} axe-core@4.10.3: - resolution: - { - integrity: sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==} + engines: {node: '>=4'} + + axios@1.10.0: + resolution: {integrity: sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==} axobject-query@4.1.0: - resolution: - { - integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} balanced-match@1.0.2: - resolution: - { - integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==, - } + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + base64-arraybuffer@1.0.2: + resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==} + engines: {node: '>= 0.6.0'} brace-expansion@1.1.12: - resolution: - { - integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==, - } + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} brace-expansion@2.0.2: - resolution: - { - integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==, - } + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} braces@3.0.3: - resolution: - { - integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} browserslist@4.25.1: - resolution: - { - integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==, - } - engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 } + resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + btoa@1.2.1: + resolution: {integrity: sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==} + engines: {node: '>= 0.4.0'} hasBin: true call-bind-apply-helpers@1.0.2: - resolution: - { - integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} call-bind@1.0.8: - resolution: - { - integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} call-bound@1.0.4: - resolution: - { - integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} callsites@3.1.0: - resolution: - { - integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} caniuse-lite@1.0.30001726: - resolution: - { - integrity: sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw==, - } + resolution: {integrity: sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw==} + + canvg@3.0.11: + resolution: {integrity: sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==} + engines: {node: '>=10.0.0'} chalk@4.1.2: - resolution: - { - integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} color-convert@2.0.1: - resolution: - { - integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==, - } - engines: { node: '>=7.0.0' } + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} color-name@1.1.4: - resolution: - { - integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==, - } + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} concat-map@0.0.1: - resolution: - { - integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==, - } + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} convert-source-map@2.0.0: - resolution: - { - integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==, - } + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie@1.0.2: + resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} + engines: {node: '>=18'} + + core-js@3.44.0: + resolution: {integrity: sha512-aFCtd4l6GvAXwVEh3XbbVqJGHDJt0OZRa+5ePGx3LLwi12WfexqQxcsohb2wgsa/92xtl19Hd66G/L+TaAxDMw==} cross-spawn@7.0.6: - resolution: - { - integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-line-break@2.1.0: + resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==} csstype@3.1.3: - resolution: - { - integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==, - } + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-format@3.1.0: + resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} damerau-levenshtein@1.0.8: - resolution: - { - integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==, - } + resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} data-view-buffer@1.0.2: - resolution: - { - integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} data-view-byte-length@1.0.2: - resolution: - { - integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} data-view-byte-offset@1.0.1: - resolution: - { - integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} debug@4.4.1: - resolution: - { - integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==, - } - engines: { node: '>=6.0' } + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} peerDependencies: supports-color: '*' peerDependenciesMeta: supports-color: optional: true + decimal.js-light@2.5.1: + resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} + deep-is@0.1.4: - resolution: - { - integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==, - } + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} define-data-property@1.1.4: - resolution: - { - integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} define-properties@1.2.1: - resolution: - { - integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} doctrine@2.1.0: - resolution: - { - integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + dompurify@3.2.6: + resolution: {integrity: sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==} dunder-proto@1.0.1: - resolution: - { - integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} electron-to-chromium@1.5.179: - resolution: - { - integrity: sha512-UWKi/EbBopgfFsc5k61wFpV7WrnnSlSzW/e2XcBmS6qKYTivZlLtoll5/rdqRTxGglGHkmkW0j0pFNJG10EUIQ==, - } + resolution: {integrity: sha512-UWKi/EbBopgfFsc5k61wFpV7WrnnSlSzW/e2XcBmS6qKYTivZlLtoll5/rdqRTxGglGHkmkW0j0pFNJG10EUIQ==} emoji-regex@9.2.2: - resolution: - { - integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==, - } + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} es-abstract@1.24.0: - resolution: - { - integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} + engines: {node: '>= 0.4'} es-define-property@1.0.1: - resolution: - { - integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} es-errors@1.3.0: - resolution: - { - integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} es-iterator-helpers@1.2.1: - resolution: - { - integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} + engines: {node: '>= 0.4'} es-object-atoms@1.1.1: - resolution: - { - integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} es-set-tostringtag@2.1.0: - resolution: - { - integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} es-shim-unscopables@1.1.0: - resolution: - { - integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} es-to-primitive@1.3.0: - resolution: - { - integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} + + es-toolkit@1.39.8: + resolution: {integrity: sha512-A8QO9TfF+rltS8BXpdu8OS+rpGgEdnRhqIVxO/ZmNvnXBYgOdSsxukT55ELyP94gZIntWJ+Li9QRrT2u1Kitpg==} esbuild@0.25.5: - resolution: - { - integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==} + engines: {node: '>=18'} hasBin: true escalade@3.2.0: - resolution: - { - integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} escape-string-regexp@4.0.0: - resolution: - { - integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} eslint-config-prettier@10.1.5: - resolution: - { - integrity: sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==, - } + resolution: {integrity: sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==} hasBin: true peerDependencies: eslint: '>=7.0.0' eslint-plugin-jsx-a11y@6.10.2: - resolution: - { - integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==, - } - engines: { node: '>=4.0' } + resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==} + engines: {node: '>=4.0'} peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 eslint-plugin-react-hooks@5.2.0: - resolution: - { - integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} + engines: {node: '>=10'} peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 eslint-plugin-react-refresh@0.4.20: - resolution: - { - integrity: sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==, - } + resolution: {integrity: sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==} peerDependencies: eslint: '>=8.40' eslint-plugin-react@7.37.5: - resolution: - { - integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} + engines: {node: '>=4'} peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 eslint-scope@8.4.0: - resolution: - { - integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint-visitor-keys@3.4.3: - resolution: - { - integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} eslint-visitor-keys@4.2.1: - resolution: - { - integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint@9.30.1: - resolution: - { - integrity: sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: jiti: '*' @@ -1385,1203 +1080,832 @@ packages: optional: true espree@10.4.0: - resolution: - { - integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} esquery@1.6.0: - resolution: - { - integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==, - } - engines: { node: '>=0.10' } + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} esrecurse@4.3.0: - resolution: - { - integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==, - } - engines: { node: '>=4.0' } + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} estraverse@5.3.0: - resolution: - { - integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==, - } - engines: { node: '>=4.0' } + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} esutils@2.0.3: - resolution: - { - integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + exenv@1.2.2: + resolution: {integrity: sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==} fast-deep-equal@3.1.3: - resolution: - { - integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==, - } + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} fast-glob@3.3.3: - resolution: - { - integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==, - } - engines: { node: '>=8.6.0' } + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} fast-json-stable-stringify@2.1.0: - resolution: - { - integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==, - } + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} fast-levenshtein@2.0.6: - resolution: - { - integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==, - } + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} fastq@1.19.1: - resolution: - { - integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==, - } + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} fdir@6.4.6: - resolution: - { - integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==, - } + resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} peerDependencies: picomatch: ^3 || ^4 peerDependenciesMeta: picomatch: optional: true + fflate@0.8.2: + resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + file-entry-cache@8.0.0: - resolution: - { - integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==, - } - engines: { node: '>=16.0.0' } + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} fill-range@7.1.1: - resolution: - { - integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} find-up@5.0.0: - resolution: - { - integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} flat-cache@4.0.1: - resolution: - { - integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==, - } - engines: { node: '>=16' } + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} flatted@3.3.3: - resolution: - { - integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==, - } + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + follow-redirects@1.15.9: + resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true for-each@0.3.5: - resolution: - { - integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + form-data@4.0.3: + resolution: {integrity: sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==} + engines: {node: '>= 6'} fsevents@2.3.3: - resolution: - { - integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, - } - engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] function-bind@1.1.2: - resolution: - { - integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==, - } + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} function.prototype.name@1.1.8: - resolution: - { - integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} functions-have-names@1.2.3: - resolution: - { - integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==, - } + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} gensync@1.0.0-beta.2: - resolution: - { - integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} get-intrinsic@1.3.0: - resolution: - { - integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} get-proto@1.0.1: - resolution: - { - integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} get-symbol-description@1.1.0: - resolution: - { - integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} glob-parent@5.1.2: - resolution: - { - integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==, - } - engines: { node: '>= 6' } + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} glob-parent@6.0.2: - resolution: - { - integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==, - } - engines: { node: '>=10.13.0' } + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} globals@14.0.0: - resolution: - { - integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} globals@16.3.0: - resolution: - { - integrity: sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==} + engines: {node: '>=18'} globalthis@1.0.4: - resolution: - { - integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} gopd@1.2.0: - resolution: - { - integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} graphemer@1.4.0: - resolution: - { - integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==, - } + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} has-bigints@1.1.0: - resolution: - { - integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} has-flag@4.0.0: - resolution: - { - integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} has-property-descriptors@1.0.2: - resolution: - { - integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==, - } + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} has-proto@1.2.0: - resolution: - { - integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} has-symbols@1.1.0: - resolution: - { - integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} has-tostringtag@1.0.2: - resolution: - { - integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} hasown@2.0.2: - resolution: - { - integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + html2canvas@1.4.1: + resolution: {integrity: sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==} + engines: {node: '>=8.0.0'} ignore@5.3.2: - resolution: - { - integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==, - } - engines: { node: '>= 4' } + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} ignore@7.0.5: - resolution: - { - integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==, - } - engines: { node: '>= 4' } + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + immer@10.1.1: + resolution: {integrity: sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==} import-fresh@3.3.1: - resolution: - { - integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} imurmurhash@0.1.4: - resolution: - { - integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==, - } - engines: { node: '>=0.8.19' } + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} internal-slot@1.1.0: - resolution: - { - integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} is-array-buffer@3.0.5: - resolution: - { - integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} is-async-function@2.1.1: - resolution: - { - integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} is-bigint@1.1.0: - resolution: - { - integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} is-boolean-object@1.2.2: - resolution: - { - integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} is-callable@1.2.7: - resolution: - { - integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} is-core-module@2.16.1: - resolution: - { - integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} is-data-view@1.0.2: - resolution: - { - integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} is-date-object@1.1.0: - resolution: - { - integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} is-extglob@2.1.1: - resolution: - { - integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} is-finalizationregistry@1.1.1: - resolution: - { - integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} is-generator-function@1.1.0: - resolution: - { - integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} + engines: {node: '>= 0.4'} is-glob@4.0.3: - resolution: - { - integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} is-map@2.0.3: - resolution: - { - integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} is-negative-zero@2.0.3: - resolution: - { - integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} is-number-object@1.1.1: - resolution: - { - integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} is-number@7.0.0: - resolution: - { - integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==, - } - engines: { node: '>=0.12.0' } + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} is-regex@1.2.1: - resolution: - { - integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} is-set@2.0.3: - resolution: - { - integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} is-shared-array-buffer@1.0.4: - resolution: - { - integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} is-string@1.1.1: - resolution: - { - integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} is-symbol@1.1.1: - resolution: - { - integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} is-typed-array@1.1.15: - resolution: - { - integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} is-weakmap@2.0.2: - resolution: - { - integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} is-weakref@1.1.1: - resolution: - { - integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} is-weakset@2.0.4: - resolution: - { - integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} isarray@2.0.5: - resolution: - { - integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==, - } + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} isexe@2.0.0: - resolution: - { - integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==, - } + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} iterator.prototype@1.1.5: - resolution: - { - integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} + engines: {node: '>= 0.4'} js-tokens@4.0.0: - resolution: - { - integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==, - } + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} js-yaml@4.1.0: - resolution: - { - integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==, - } + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true jsesc@3.1.0: - resolution: - { - integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} hasBin: true json-buffer@3.0.1: - resolution: - { - integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==, - } + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} json-schema-traverse@0.4.1: - resolution: - { - integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==, - } + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} json-stable-stringify-without-jsonify@1.0.1: - resolution: - { - integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==, - } + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} json5@2.2.3: - resolution: - { - integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} hasBin: true + jspdf@3.0.1: + resolution: {integrity: sha512-qaGIxqxetdoNnFQQXxTKUD9/Z7AloLaw94fFsOiJMxbfYdBbrBuhWmbzI8TVjrw7s3jBY1PFHofBKMV/wZPapg==} + jsx-ast-utils@3.3.5: - resolution: - { - integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==, - } - engines: { node: '>=4.0' } + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} keyv@4.5.4: - resolution: - { - integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==, - } + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} language-subtag-registry@0.3.23: - resolution: - { - integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==, - } + resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} language-tags@1.0.9: - resolution: - { - integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==, - } - engines: { node: '>=0.10' } + resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} + engines: {node: '>=0.10'} levn@0.4.1: - resolution: - { - integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==, - } - engines: { node: '>= 0.8.0' } + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} locate-path@6.0.0: - resolution: - { - integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} lodash.merge@4.6.2: - resolution: - { - integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==, - } + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} loose-envify@1.4.0: - resolution: - { - integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==, - } + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true lru-cache@5.1.1: - resolution: - { - integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==, - } + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} math-intrinsics@1.1.0: - resolution: - { - integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} merge2@1.4.1: - resolution: - { - integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} micromatch@4.0.8: - resolution: - { - integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==, - } - engines: { node: '>=8.6' } + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} minimatch@3.1.2: - resolution: - { - integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==, - } + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} minimatch@9.0.5: - resolution: - { - integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==, - } - engines: { node: '>=16 || 14 >=14.17' } + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} ms@2.1.3: - resolution: - { - integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, - } + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} nanoid@3.3.11: - resolution: - { - integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==, - } - engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 } + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true natural-compare@1.4.0: - resolution: - { - integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==, - } + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} node-releases@2.0.19: - resolution: - { - integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==, - } + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} object-assign@4.1.1: - resolution: - { - integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} object-inspect@1.13.4: - resolution: - { - integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} object-keys@1.1.1: - resolution: - { - integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} object.assign@4.1.7: - resolution: - { - integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} object.entries@1.1.9: - resolution: - { - integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} + engines: {node: '>= 0.4'} object.fromentries@2.0.8: - resolution: - { - integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} object.values@1.2.1: - resolution: - { - integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} optionator@0.9.4: - resolution: - { - integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==, - } - engines: { node: '>= 0.8.0' } + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} own-keys@1.0.1: - resolution: - { - integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} p-limit@3.1.0: - resolution: - { - integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} p-locate@5.0.0: - resolution: - { - integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} parent-module@1.0.1: - resolution: - { - integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} path-exists@4.0.0: - resolution: - { - integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} path-key@3.1.1: - resolution: - { - integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} path-parse@1.0.7: - resolution: - { - integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==, - } + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + performance-now@2.1.0: + resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} picocolors@1.1.1: - resolution: - { - integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==, - } + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} picomatch@2.3.1: - resolution: - { - integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==, - } - engines: { node: '>=8.6' } + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} picomatch@4.0.2: - resolution: - { - integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} possible-typed-array-names@1.1.0: - resolution: - { - integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} postcss@8.5.6: - resolution: - { - integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==, - } - engines: { node: ^10 || ^12 || >=14 } + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: - resolution: - { - integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==, - } - engines: { node: '>= 0.8.0' } + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} prettier@3.6.2: - resolution: - { - integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + engines: {node: '>=14'} hasBin: true prop-types@15.8.1: - resolution: - { - integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==, - } + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} punycode@2.3.1: - resolution: - { - integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} queue-microtask@1.2.3: - resolution: - { - integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==, - } + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + raf@3.4.1: + resolution: {integrity: sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==} react-dom@19.1.0: - resolution: - { - integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==, - } + resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==} peerDependencies: react: ^19.1.0 react-is@16.13.1: - resolution: - { - integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==, - } + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-lifecycles-compat@3.0.4: + resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==} + + react-modal@3.16.3: + resolution: {integrity: sha512-yCYRJB5YkeQDQlTt17WGAgFJ7jr2QYcWa1SHqZ3PluDmnKJ/7+tVU+E6uKyZ0nODaeEj+xCpK4LcSnKXLMC0Nw==} + peerDependencies: + react: ^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18 || ^19 + react-dom: ^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18 || ^19 + + react-redux@9.2.0: + resolution: {integrity: sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==} + peerDependencies: + '@types/react': ^18.2.25 || ^19 + react: ^18.0 || ^19 + redux: ^5.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + redux: + optional: true react-refresh@0.17.0: - resolution: - { - integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + engines: {node: '>=0.10.0'} + + react-router-dom@7.6.3: + resolution: {integrity: sha512-DiWJm9qdUAmiJrVWaeJdu4TKu13+iB/8IEi0EW/XgaHCjW/vWGrwzup0GVvaMteuZjKnh5bEvJP/K0MDnzawHw==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + + react-router@7.6.3: + resolution: {integrity: sha512-zf45LZp5skDC6I3jDLXQUu0u26jtuP4lEGbc7BbdyxenBN1vJSTA18czM2D+h5qyMBuMrD+9uB+mU37HIoKGRA==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + peerDependenciesMeta: + react-dom: + optional: true react@19.1.0: - resolution: - { - integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} + engines: {node: '>=0.10.0'} + + recharts@3.1.0: + resolution: {integrity: sha512-NqAqQcGBmLrfDs2mHX/bz8jJCQtG2FeXfE0GqpZmIuXIjkpIwj8sd9ad0WyvKiBKPd8ZgNG0hL85c8sFDwascw==} + engines: {node: '>=18'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-is: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + redux-thunk@3.1.0: + resolution: {integrity: sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==} + peerDependencies: + redux: ^5.0.0 + + redux@5.0.1: + resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==} reflect.getprototypeof@1.0.10: - resolution: - { - integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regenerator-runtime@0.13.11: + resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} regexp.prototype.flags@1.5.4: - resolution: - { - integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + reselect@5.1.1: + resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} resolve-from@4.0.0: - resolution: - { - integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} resolve@2.0.0-next.5: - resolution: - { - integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==, - } + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} hasBin: true reusify@1.1.0: - resolution: - { - integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==, - } - engines: { iojs: '>=1.0.0', node: '>=0.10.0' } + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rgbcolor@1.0.1: + resolution: {integrity: sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==} + engines: {node: '>= 0.8.15'} rollup@4.44.1: - resolution: - { - integrity: sha512-x8H8aPvD+xbl0Do8oez5f5o8eMS3trfCghc4HhLAnCkj7Vl0d1JWGs0UF/D886zLW2rOj2QymV/JcSSsw+XDNg==, - } - engines: { node: '>=18.0.0', npm: '>=8.0.0' } + resolution: {integrity: sha512-x8H8aPvD+xbl0Do8oez5f5o8eMS3trfCghc4HhLAnCkj7Vl0d1JWGs0UF/D886zLW2rOj2QymV/JcSSsw+XDNg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true run-parallel@1.2.0: - resolution: - { - integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==, - } + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} safe-array-concat@1.1.3: - resolution: - { - integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==, - } - engines: { node: '>=0.4' } + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + engines: {node: '>=0.4'} safe-push-apply@1.0.0: - resolution: - { - integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} safe-regex-test@1.1.0: - resolution: - { - integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} scheduler@0.26.0: - resolution: - { - integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==, - } + resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} semver@6.3.1: - resolution: - { - integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==, - } + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true semver@7.7.2: - resolution: - { - integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} hasBin: true + set-cookie-parser@2.7.1: + resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} + set-function-length@1.2.2: - resolution: - { - integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} set-function-name@2.0.2: - resolution: - { - integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} set-proto@1.0.0: - resolution: - { - integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} shebang-command@2.0.0: - resolution: - { - integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} shebang-regex@3.0.0: - resolution: - { - integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} side-channel-list@1.0.0: - resolution: - { - integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} side-channel-map@1.0.1: - resolution: - { - integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} side-channel-weakmap@1.0.2: - resolution: - { - integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} side-channel@1.1.0: - resolution: - { - integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} source-map-js@1.2.1: - resolution: - { - integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + stackblur-canvas@2.7.0: + resolution: {integrity: sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==} + engines: {node: '>=0.1.14'} stop-iteration-iterator@1.1.0: - resolution: - { - integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} string.prototype.includes@2.0.1: - resolution: - { - integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} + engines: {node: '>= 0.4'} string.prototype.matchall@4.0.12: - resolution: - { - integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} + engines: {node: '>= 0.4'} string.prototype.repeat@1.0.0: - resolution: - { - integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==, - } + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} string.prototype.trim@1.2.10: - resolution: - { - integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} string.prototype.trimend@1.0.9: - resolution: - { - integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} string.prototype.trimstart@1.0.8: - resolution: - { - integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} strip-json-comments@3.1.1: - resolution: - { - integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} supports-color@7.2.0: - resolution: - { - integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} supports-preserve-symlinks-flag@1.0.0: - resolution: - { - integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + svg-pathdata@6.0.3: + resolution: {integrity: sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==} + engines: {node: '>=12.0.0'} + + text-segmentation@1.0.3: + resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==} + + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} tinyglobby@0.2.14: - resolution: - { - integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==, - } - engines: { node: '>=12.0.0' } + resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + engines: {node: '>=12.0.0'} to-regex-range@5.0.1: - resolution: - { - integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==, - } - engines: { node: '>=8.0' } + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} ts-api-utils@2.1.0: - resolution: - { - integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==, - } - engines: { node: '>=18.12' } + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} peerDependencies: typescript: '>=4.8.4' type-check@0.4.0: - resolution: - { - integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==, - } - engines: { node: '>= 0.8.0' } + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} typed-array-buffer@1.0.3: - resolution: - { - integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} typed-array-byte-length@1.0.3: - resolution: - { - integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} typed-array-byte-offset@1.0.4: - resolution: - { - integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} typed-array-length@1.0.7: - resolution: - { - integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} typescript-eslint@8.35.1: - resolution: - { - integrity: sha512-xslJjFzhOmHYQzSB/QTeASAHbjmxOGEP6Coh93TXmUBFQoJ1VU35UHIDmG06Jd6taf3wqqC1ntBnCMeymy5Ovw==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-xslJjFzhOmHYQzSB/QTeASAHbjmxOGEP6Coh93TXmUBFQoJ1VU35UHIDmG06Jd6taf3wqqC1ntBnCMeymy5Ovw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' typescript@5.8.3: - resolution: - { - integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==, - } - engines: { node: '>=14.17' } + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} hasBin: true unbox-primitive@1.1.0: - resolution: - { - integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + undici-types@7.8.0: + resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} update-browserslist-db@1.1.3: - resolution: - { - integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==, - } + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' uri-js@4.4.1: - resolution: - { - integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==, - } + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + use-sync-external-store@1.5.0: + resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + utrie@1.0.2: + resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==} + + victory-vendor@37.3.6: + resolution: {integrity: sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==} vite@7.0.0: - resolution: - { - integrity: sha512-ixXJB1YRgDIw2OszKQS9WxGHKwLdCsbQNkpJN171udl6szi/rIySHL6/Os3s2+oE4P/FLD4dxg4mD7Wust+u5g==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + resolution: {integrity: sha512-ixXJB1YRgDIw2OszKQS9WxGHKwLdCsbQNkpJN171udl6szi/rIySHL6/Os3s2+oE4P/FLD4dxg4mD7Wust+u5g==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: '@types/node': ^20.19.0 || >=22.12.0 @@ -2619,63 +1943,61 @@ packages: yaml: optional: true + warning@4.0.3: + resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} + which-boxed-primitive@1.1.1: - resolution: - { - integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} which-builtin-type@1.2.1: - resolution: - { - integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} which-collection@1.0.2: - resolution: - { - integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} which-typed-array@1.1.19: - resolution: - { - integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} + engines: {node: '>= 0.4'} which@2.0.2: - resolution: - { - integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} hasBin: true word-wrap@1.2.5: - resolution: - { - integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} yallist@3.1.1: - resolution: - { - integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==, - } + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} yocto-queue@0.1.0: - resolution: - { - integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zustand@5.0.6: + resolution: {integrity: sha512-ihAqNeUVhe0MAD+X8M5UzqyZ9k3FFZLBTtqo6JLPwV53cbRB/mJwBI0PxcIgqhBBHlEs8G45OTDTMq3gNcLq3A==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true snapshots: + '@ampproject/remapping@2.3.0': dependencies: '@jridgewell/gen-mapping': 0.3.12 @@ -2770,6 +2092,8 @@ snapshots: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 + '@babel/runtime@7.28.2': {} + '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 @@ -2955,6 +2279,18 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 + '@reduxjs/toolkit@2.8.2(react-redux@9.2.0(@types/react@19.1.8)(react@19.1.0)(redux@5.0.1))(react@19.1.0)': + dependencies: + '@standard-schema/spec': 1.0.0 + '@standard-schema/utils': 0.3.0 + immer: 10.1.1 + redux: 5.0.1 + redux-thunk: 3.1.0(redux@5.0.1) + reselect: 5.1.1 + optionalDependencies: + react: 19.1.0 + react-redux: 9.2.0(@types/react@19.1.8)(react@19.1.0)(redux@5.0.1) + '@rolldown/pluginutils@1.0.0-beta.19': {} '@rollup/rollup-android-arm-eabi@4.44.1': @@ -3017,6 +2353,10 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.44.1': optional: true + '@standard-schema/spec@1.0.0': {} + + '@standard-schema/utils@0.3.0': {} + '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.28.0 @@ -3038,18 +2378,79 @@ snapshots: dependencies: '@babel/types': 7.28.0 + '@types/d3-array@3.2.1': {} + + '@types/d3-color@3.1.3': {} + + '@types/d3-ease@3.0.2': {} + + '@types/d3-interpolate@3.0.4': + dependencies: + '@types/d3-color': 3.1.3 + + '@types/d3-path@3.1.1': {} + + '@types/d3-scale@4.0.9': + dependencies: + '@types/d3-time': 3.0.4 + + '@types/d3-shape@3.1.7': + dependencies: + '@types/d3-path': 3.1.1 + + '@types/d3-time@3.0.4': {} + + '@types/d3-timer@3.0.2': {} + '@types/estree@1.0.8': {} + '@types/history@4.7.11': {} + + '@types/html2canvas@1.0.0': + dependencies: + html2canvas: 1.4.1 + '@types/json-schema@7.0.15': {} + '@types/jspdf@2.0.0': + dependencies: + jspdf: 3.0.1 + + '@types/node@24.0.10': + dependencies: + undici-types: 7.8.0 + + '@types/raf@3.4.3': + optional: true + '@types/react-dom@19.1.6(@types/react@19.1.8)': dependencies: '@types/react': 19.1.8 + '@types/react-modal@3.16.3': + dependencies: + '@types/react': 19.1.8 + + '@types/react-router-dom@5.3.3': + dependencies: + '@types/history': 4.7.11 + '@types/react': 19.1.8 + '@types/react-router': 5.1.20 + + '@types/react-router@5.1.20': + dependencies: + '@types/history': 4.7.11 + '@types/react': 19.1.8 + '@types/react@19.1.8': dependencies: csstype: 3.1.3 + '@types/trusted-types@2.0.7': + optional: true + + '@types/use-sync-external-store@0.0.6': {} + '@typescript-eslint/eslint-plugin@8.35.1(@typescript-eslint/parser@8.35.1(eslint@9.30.1)(typescript@5.8.3))(eslint@9.30.1)(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.1 @@ -3142,7 +2543,7 @@ snapshots: '@typescript-eslint/types': 8.35.1 eslint-visitor-keys: 4.2.1 - '@vitejs/plugin-react@4.6.0(vite@7.0.0)': + '@vitejs/plugin-react@4.6.0(vite@7.0.0(@types/node@24.0.10))': dependencies: '@babel/core': 7.28.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.0) @@ -3150,7 +2551,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.19 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 7.0.0 + vite: 7.0.0(@types/node@24.0.10) transitivePeerDependencies: - supports-color @@ -3236,16 +2637,30 @@ snapshots: async-function@1.0.0: {} + asynckit@0.4.0: {} + + atob@2.1.2: {} + available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.1.0 axe-core@4.10.3: {} + axios@1.10.0: + dependencies: + follow-redirects: 1.15.9 + form-data: 4.0.3 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + axobject-query@4.1.0: {} balanced-match@1.0.2: {} + base64-arraybuffer@1.0.2: {} + brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 @@ -3266,6 +2681,8 @@ snapshots: node-releases: 2.0.19 update-browserslist-db: 1.1.3(browserslist@4.25.1) + btoa@1.2.1: {} + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -3287,29 +2704,94 @@ snapshots: caniuse-lite@1.0.30001726: {} + canvg@3.0.11: + dependencies: + '@babel/runtime': 7.28.2 + '@types/raf': 3.4.3 + core-js: 3.44.0 + raf: 3.4.1 + regenerator-runtime: 0.13.11 + rgbcolor: 1.0.1 + stackblur-canvas: 2.7.0 + svg-pathdata: 6.0.3 + optional: true + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 + clsx@2.1.1: {} + color-convert@2.0.1: dependencies: color-name: 1.1.4 color-name@1.1.4: {} + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + concat-map@0.0.1: {} convert-source-map@2.0.0: {} + cookie@1.0.2: {} + + core-js@3.44.0: + optional: true + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 + css-line-break@2.1.0: + dependencies: + utrie: 1.0.2 + csstype@3.1.3: {} + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-color@3.1.0: {} + + d3-ease@3.0.1: {} + + d3-format@3.1.0: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@3.1.0: {} + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.0 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@3.0.1: {} + damerau-levenshtein@1.0.8: {} data-view-buffer@1.0.2: @@ -3334,6 +2816,8 @@ snapshots: dependencies: ms: 2.1.3 + decimal.js-light@2.5.1: {} + deep-is@0.1.4: {} define-data-property@1.1.4: @@ -3348,10 +2832,17 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + delayed-stream@1.0.0: {} + doctrine@2.1.0: dependencies: esutils: 2.0.3 + dompurify@3.2.6: + optionalDependencies: + '@types/trusted-types': 2.0.7 + optional: true + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -3463,6 +2954,8 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 + es-toolkit@1.39.8: {} + esbuild@0.25.5: optionalDependencies: '@esbuild/aix-ppc64': 0.25.5 @@ -3615,6 +3108,10 @@ snapshots: esutils@2.0.3: {} + eventemitter3@5.0.1: {} + + exenv@1.2.2: {} + fast-deep-equal@3.1.3: {} fast-glob@3.3.3: @@ -3637,6 +3134,8 @@ snapshots: optionalDependencies: picomatch: 4.0.2 + fflate@0.8.2: {} + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -3657,10 +3156,20 @@ snapshots: flatted@3.3.3: {} + follow-redirects@1.15.9: {} + for-each@0.3.5: dependencies: is-callable: 1.2.7 + form-data@4.0.3: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + fsevents@2.3.3: optional: true @@ -3746,10 +3255,17 @@ snapshots: dependencies: function-bind: 1.1.2 + html2canvas@1.4.1: + dependencies: + css-line-break: 2.1.0 + text-segmentation: 1.0.3 + ignore@5.3.2: {} ignore@7.0.5: {} + immer@10.1.1: {} + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -3763,6 +3279,8 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 + internmap@2.0.3: {} + is-array-buffer@3.0.5: dependencies: call-bind: 1.0.8 @@ -3899,6 +3417,18 @@ snapshots: json5@2.2.3: {} + jspdf@3.0.1: + dependencies: + '@babel/runtime': 7.28.2 + atob: 2.1.2 + btoa: 1.2.1 + fflate: 0.8.2 + optionalDependencies: + canvg: 3.0.11 + core-js: 3.44.0 + dompurify: 3.2.6 + html2canvas: 1.4.1 + jsx-ast-utils@3.3.5: dependencies: array-includes: 3.1.9 @@ -3944,6 +3474,12 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + minimatch@3.1.2: dependencies: brace-expansion: 1.1.12 @@ -4029,6 +3565,9 @@ snapshots: path-parse@1.0.7: {} + performance-now@2.1.0: + optional: true + picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -4053,10 +3592,17 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 + proxy-from-env@1.1.0: {} + punycode@2.3.1: {} queue-microtask@1.2.3: {} + raf@3.4.1: + dependencies: + performance-now: 2.1.0 + optional: true + react-dom@19.1.0(react@19.1.0): dependencies: react: 19.1.0 @@ -4064,10 +3610,70 @@ snapshots: react-is@16.13.1: {} + react-lifecycles-compat@3.0.4: {} + + react-modal@3.16.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + exenv: 1.2.2 + prop-types: 15.8.1 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-lifecycles-compat: 3.0.4 + warning: 4.0.3 + + react-redux@9.2.0(@types/react@19.1.8)(react@19.1.0)(redux@5.0.1): + dependencies: + '@types/use-sync-external-store': 0.0.6 + react: 19.1.0 + use-sync-external-store: 1.5.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.8 + redux: 5.0.1 + react-refresh@0.17.0: {} + react-router-dom@7.6.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-router: 7.6.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + + react-router@7.6.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + cookie: 1.0.2 + react: 19.1.0 + set-cookie-parser: 2.7.1 + optionalDependencies: + react-dom: 19.1.0(react@19.1.0) + react@19.1.0: {} + recharts@3.1.0(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react-is@16.13.1)(react@19.1.0)(redux@5.0.1): + dependencies: + '@reduxjs/toolkit': 2.8.2(react-redux@9.2.0(@types/react@19.1.8)(react@19.1.0)(redux@5.0.1))(react@19.1.0) + clsx: 2.1.1 + decimal.js-light: 2.5.1 + es-toolkit: 1.39.8 + eventemitter3: 5.0.1 + immer: 10.1.1 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-is: 16.13.1 + react-redux: 9.2.0(@types/react@19.1.8)(react@19.1.0)(redux@5.0.1) + reselect: 5.1.1 + tiny-invariant: 1.3.3 + use-sync-external-store: 1.5.0(react@19.1.0) + victory-vendor: 37.3.6 + transitivePeerDependencies: + - '@types/react' + - redux + + redux-thunk@3.1.0(redux@5.0.1): + dependencies: + redux: 5.0.1 + + redux@5.0.1: {} + reflect.getprototypeof@1.0.10: dependencies: call-bind: 1.0.8 @@ -4079,6 +3685,9 @@ snapshots: get-proto: 1.0.1 which-builtin-type: 1.2.1 + regenerator-runtime@0.13.11: + optional: true + regexp.prototype.flags@1.5.4: dependencies: call-bind: 1.0.8 @@ -4088,6 +3697,8 @@ snapshots: gopd: 1.2.0 set-function-name: 2.0.2 + reselect@5.1.1: {} + resolve-from@4.0.0: {} resolve@2.0.0-next.5: @@ -4098,6 +3709,9 @@ snapshots: reusify@1.1.0: {} + rgbcolor@1.0.1: + optional: true + rollup@4.44.1: dependencies: '@types/estree': 1.0.8 @@ -4153,6 +3767,8 @@ snapshots: semver@7.7.2: {} + set-cookie-parser@2.7.1: {} + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -4211,6 +3827,9 @@ snapshots: source-map-js@1.2.1: {} + stackblur-canvas@2.7.0: + optional: true + stop-iteration-iterator@1.1.0: dependencies: es-errors: 1.3.0 @@ -4274,6 +3893,15 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + svg-pathdata@6.0.3: + optional: true + + text-segmentation@1.0.3: + dependencies: + utrie: 1.0.2 + + tiny-invariant@1.3.3: {} + tinyglobby@0.2.14: dependencies: fdir: 6.4.6(picomatch@4.0.2) @@ -4343,6 +3971,8 @@ snapshots: has-symbols: 1.1.0 which-boxed-primitive: 1.1.1 + undici-types@7.8.0: {} + update-browserslist-db@1.1.3(browserslist@4.25.1): dependencies: browserslist: 4.25.1 @@ -4353,7 +3983,32 @@ snapshots: dependencies: punycode: 2.3.1 - vite@7.0.0: + use-sync-external-store@1.5.0(react@19.1.0): + dependencies: + react: 19.1.0 + + utrie@1.0.2: + dependencies: + base64-arraybuffer: 1.0.2 + + victory-vendor@37.3.6: + dependencies: + '@types/d3-array': 3.2.1 + '@types/d3-ease': 3.0.2 + '@types/d3-interpolate': 3.0.4 + '@types/d3-scale': 4.0.9 + '@types/d3-shape': 3.1.7 + '@types/d3-time': 3.0.4 + '@types/d3-timer': 3.0.2 + d3-array: 3.2.4 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-scale: 4.0.2 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-timer: 3.0.1 + + vite@7.0.0(@types/node@24.0.10): dependencies: esbuild: 0.25.5 fdir: 6.4.6(picomatch@4.0.2) @@ -4362,8 +4017,13 @@ snapshots: rollup: 4.44.1 tinyglobby: 0.2.14 optionalDependencies: + '@types/node': 24.0.10 fsevents: 2.3.3 + warning@4.0.3: + dependencies: + loose-envify: 1.4.0 + which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 @@ -4414,3 +4074,10 @@ snapshots: yallist@3.1.1: {} yocto-queue@0.1.0: {} + + zustand@5.0.6(@types/react@19.1.8)(immer@10.1.1)(react@19.1.0)(use-sync-external-store@1.5.0(react@19.1.0)): + optionalDependencies: + '@types/react': 19.1.8 + immer: 10.1.1 + react: 19.1.0 + use-sync-external-store: 1.5.0(react@19.1.0) diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..86019c7 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/fonts/LINESeedKR-Bd.eot b/public/fonts/LINESeedKR-Bd.eot new file mode 100644 index 0000000..f66f522 Binary files /dev/null and b/public/fonts/LINESeedKR-Bd.eot differ diff --git a/public/fonts/LINESeedKR-Bd.otf b/public/fonts/LINESeedKR-Bd.otf new file mode 100644 index 0000000..f2e04ff Binary files /dev/null and b/public/fonts/LINESeedKR-Bd.otf differ diff --git a/public/fonts/LINESeedKR-Bd.woff b/public/fonts/LINESeedKR-Bd.woff new file mode 100644 index 0000000..e7fda8c Binary files /dev/null and b/public/fonts/LINESeedKR-Bd.woff differ diff --git a/public/fonts/LINESeedKR-Bd.woff2 b/public/fonts/LINESeedKR-Bd.woff2 new file mode 100644 index 0000000..f80e30f Binary files /dev/null and b/public/fonts/LINESeedKR-Bd.woff2 differ diff --git a/public/fonts/LINESeedKR-Rg.eot b/public/fonts/LINESeedKR-Rg.eot new file mode 100644 index 0000000..b93eccc Binary files /dev/null and b/public/fonts/LINESeedKR-Rg.eot differ diff --git a/public/fonts/LINESeedKR-Rg.otf b/public/fonts/LINESeedKR-Rg.otf new file mode 100644 index 0000000..727339e Binary files /dev/null and b/public/fonts/LINESeedKR-Rg.otf differ diff --git a/public/fonts/LINESeedKR-Rg.woff b/public/fonts/LINESeedKR-Rg.woff new file mode 100644 index 0000000..b50ee38 Binary files /dev/null and b/public/fonts/LINESeedKR-Rg.woff differ diff --git a/public/fonts/LINESeedKR-Rg.woff2 b/public/fonts/LINESeedKR-Rg.woff2 new file mode 100644 index 0000000..2f59923 Binary files /dev/null and b/public/fonts/LINESeedKR-Rg.woff2 differ diff --git a/public/icons/sprites.svg b/public/icons/sprites.svg new file mode 100644 index 0000000..21beadf --- /dev/null +++ b/public/icons/sprites.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index 1271f8d..c0b4d2e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,23 @@ +import { BrowserRouter } from 'react-router-dom'; +import Modal from 'react-modal'; +import Router from './routes/Router'; +import useAuthInit from './hooks/useAuthInit'; +import Loader from './components/loader/Loader'; +import Header from './components/header/Header'; + +Modal.setAppElement('#root'); // '#root'는 HTML에서 React가 마운트된 id + function App() { - return <> + const { isAuthChecked } = useAuthInit(); + + if (!isAuthChecked) return ; + + return ( + +
+ + + ); } -export default App +export default App; diff --git a/src/assets/icon.png b/src/assets/icon.png new file mode 100644 index 0000000..d813e82 Binary files /dev/null and b/src/assets/icon.png differ diff --git a/src/assets/logo.png b/src/assets/logo.png new file mode 100644 index 0000000..a5eb272 Binary files /dev/null and b/src/assets/logo.png differ diff --git a/src/components/authInput/AuthInput.module.css b/src/components/authInput/AuthInput.module.css new file mode 100644 index 0000000..1f42260 --- /dev/null +++ b/src/components/authInput/AuthInput.module.css @@ -0,0 +1,44 @@ +.label { + display: block; + font-size: var(--font-size-16); + font-weight: 700; + margin-bottom: 12px; +} + +.description { + margin-bottom: 12px; + max-width: 370px; + font-size: var(--font-size-14); + line-height: 22px; +} + +.inputBox { + width: 100%; + max-width: 480px; + height: 60px; + padding: 0 12px; + border: 1px solid var(--color-gray-b1); + border-radius: 5px; + font-size: var(--font-size-16); + + &::placeholder { + color: var(--color-secondary); + } + + &:focus { + outline: none; + } +} + +.errorBorder { + border-color: var(--color-error); +} + +.errorList { + color: var(--color-error); + font-size: var(--font-size-14); + margin-top: 12px; + display: flex; + flex-direction: column; + gap: 8px; +} diff --git a/src/components/authInput/AuthInput.tsx b/src/components/authInput/AuthInput.tsx new file mode 100644 index 0000000..9e597a7 --- /dev/null +++ b/src/components/authInput/AuthInput.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import styles from './AuthInput.module.css'; + +type AuthInputProps = React.InputHTMLAttributes & { + type: string; + value: string; + placeholder: string; + onChange: (e: React.ChangeEvent) => void; + label?: string; + description?: string; + errors?: string[]; +}; + +const AuthInput: React.FC = ({ + type = 'text', + value, + placeholder, + onChange, + label, + description, + errors = [], + ...props +}) => { + return ( +
+ {label && ( + + )} + {description &&

{description}

} + 0 ? styles.errorBorder : ''}`} + id={label} + type={type} + value={value} + onChange={onChange} + placeholder={placeholder} + onKeyDown={(e) => { + if (e.key === ' ') { + e.preventDefault(); + } + }} + autoComplete="off" + {...props} + /> + {errors.length > 0 && ( +
    + {errors.map((err, idx) => ( +
  • +

    {err}

    +
  • + ))} +
+ )} +
+ ); +}; + +export default AuthInput; diff --git a/src/components/button/Button.module.css b/src/components/button/Button.module.css new file mode 100644 index 0000000..f9c9b93 --- /dev/null +++ b/src/components/button/Button.module.css @@ -0,0 +1,42 @@ +.button { + border-radius: 5px; + font-weight: 700; + cursor: pointer; + + &:disabled { + background-color: var(--color-gray-b1); + cursor: not-allowed; + } +} + +.primary { + background-color: var(--color-primary); + color: var(--color-white); +} + +.white { + border: 1px solid var(--color-primary); + background-color: var(--color-white); + color: var(--color-secondary); +} + +.lg { + width: 90%; + max-width: 220px; + height: 68px; + font-size: var(--font-size-20); +} + +.md { + width: 100%; + max-width: 480px; + height: 60px; + font-size: var(--font-size-18); +} + +.sm { + width: 90%; + max-width: 166px; + height: 54px; + font-size: var(--font-size-15); +} diff --git a/src/components/button/Button.tsx b/src/components/button/Button.tsx new file mode 100644 index 0000000..e78a36a --- /dev/null +++ b/src/components/button/Button.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import styles from './Button.module.css'; + +type ButtonProps = React.ButtonHTMLAttributes & { + children: string; + variant: 'primary' | 'white'; + size: 'lg' | 'md' | 'sm'; +}; + +const Button: React.FC = ({ + children, + variant, + size, + ...props +}) => { + return ( + + ); +}; + +export default Button; diff --git a/src/components/header/Header.module.css b/src/components/header/Header.module.css new file mode 100644 index 0000000..f6e248c --- /dev/null +++ b/src/components/header/Header.module.css @@ -0,0 +1,84 @@ +.header { + display: flex; + align-items: center; + position: fixed; + top: 0; + left: 50%; + transform: translateX(-50%); + z-index: 1000; + background-color: var(--color-white); + padding: 0 16px; + width: 100%; + max-width: 1060px; + height: 60px; +} + +.logo { + width: 100px; + height: 60px; +} + +.nav { + position: relative; + display: flex; + flex: 1; + justify-content: space-between; + align-items: center; + margin-left: 24px; + font-size: var(--font-size-14); +} + +.desktopMenu { + display: flex; + gap: 32px; +} + +.menuBtn { + display: none; +} + +.overlayMenu { + position: fixed; + top: 60px; + left: 0; + z-index: 9999; + display: flex; + flex-direction: column; + justify-content: start; + align-items: left; + width: 100%; + height: calc(100vh - 60px); + background-color: var(--color-white); +} + +.mobileMenu { + display: flex; + flex-direction: column; + gap: 24px; + padding: 24px; + + .menuItem { + a { + display: block; + padding: 8px 0; + font-size: var(--font-size-18); + } + } +} + +@media (max-width: 768px) { + .nav { + justify-content: end; + gap: 16px; + } + + .desktopMenu { + display: none; + } + + .menuBtn { + order: 2; + display: block; + cursor: pointer; + } +} diff --git a/src/components/header/Header.tsx b/src/components/header/Header.tsx new file mode 100644 index 0000000..68450cd --- /dev/null +++ b/src/components/header/Header.tsx @@ -0,0 +1,90 @@ +import React, { useEffect, useState } from 'react'; +import { Link } from 'react-router-dom'; +import styles from './Header.module.css'; +import logo from '@/assets/logo.png'; +import Icon from '../icon/Icon'; +import useUserStore from '@/store/user'; +import useAuthStore from '@/store/auth'; + +const NAV_LINKS = [ + { label: '이력서 분석', to: '/analytics/start' }, + { label: '업무 성향 테스트', to: '' }, + { label: 'HR 담당자 성향 테스트', to: '/hr-test/start' }, +] as const; + +const Header: React.FC = () => { + const user = useUserStore((state) => state.user); + const userName = user?.name ?? ''; + const accessToken = useAuthStore((state) => state.accessToken); + const isLoggedIn = !!accessToken; + + const [isMenuOpen, setIsMenuOpen] = useState(false); + + useEffect(() => { + document.body.style.overflow = isMenuOpen ? 'hidden' : 'auto'; + }, [isMenuOpen]); + + const handleMenuToggle = () => { + setIsMenuOpen((prev) => !prev); + }; + + return ( +
+

+ + 매칭핏 로고 + +

+ +
+ ); +}; + +export default Header; diff --git a/src/components/icon/Icon.tsx b/src/components/icon/Icon.tsx new file mode 100644 index 0000000..739e43b --- /dev/null +++ b/src/components/icon/Icon.tsx @@ -0,0 +1,34 @@ +type IconProps = { + id: string; + width?: number; + height?: number; + color?: string; + xPos?: number; + yPos?: number; +} & React.SVGAttributes; + +const Icon = ({ + id, + width = 24, + height, + color = '#757575', + xPos = 0, + yPos = 0, + ...restProps +}: IconProps) => { + if (!height) height = width; + + return ( + + + + ); +}; + +export default Icon; diff --git a/src/components/loader/Loader.module.css b/src/components/loader/Loader.module.css new file mode 100644 index 0000000..d5e22f8 --- /dev/null +++ b/src/components/loader/Loader.module.css @@ -0,0 +1,28 @@ +@keyframes rotate { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +.loader { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 100vh; + margin-top: -60px; +} + +.spinner { + animation: rotate 3s linear infinite; + width: 64px; + height: 64px; + + @media (max-width: 480px) { + width: 50px; + height: 50px; + } +} diff --git a/src/components/loader/Loader.tsx b/src/components/loader/Loader.tsx new file mode 100644 index 0000000..1cb469c --- /dev/null +++ b/src/components/loader/Loader.tsx @@ -0,0 +1,38 @@ +import { useLayoutEffect } from 'react'; +import icon from '@/assets/icon.png'; +import styles from './Loader.module.css'; + +const Loader = () => { + // 로딩 시작, 완료를 알려줌 + // https://aoa.gitbook.io/skymimo/aoa-2018/2018-aria/loading + useLayoutEffect(() => { + const loadingStartElement = document.getElementById('loading-start'); + const loadingEndElement = document.getElementById('loading-end'); + + if (loadingStartElement) { + loadingStartElement.innerHTML = '

로딩중...

'; + loadingStartElement.setAttribute('role', 'alert'); + } + + return () => { + if (loadingStartElement) { + loadingStartElement.innerHTML = ''; + loadingStartElement.removeAttribute('role'); + } + if (loadingEndElement) { + loadingEndElement.innerHTML = '

로딩완료

'; + setTimeout(() => { + loadingEndElement.innerHTML = ''; + }, 1000); + } + }; + }, []); + + return ( +
+ +
+ ); +}; + +export default Loader; diff --git a/src/components/modal/Modal.module.css b/src/components/modal/Modal.module.css new file mode 100644 index 0000000..0c55746 --- /dev/null +++ b/src/components/modal/Modal.module.css @@ -0,0 +1,38 @@ +.modal { + position: relative; + top: 50%; + transform: translateY(-50%); + padding: 30px; + margin: auto; + width: 90%; + max-width: 460px; + border-radius: 5px; + background-color: var(--color-white); + text-align: center; + + p { + line-height: 30px; + font-size: var(--font-size-18); + margin-block: 10px 32px; + + @media (max-width: 480px) { + line-height: 24px; + font-size: var(--font-size-16); + } + + @media (max-width: 320px) { + line-height: 20px; + font-size: var(--font-size-14); + } + } +} + +.overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 9000; + background-color: rgba(0, 0, 0, 0.5); +} diff --git a/src/components/modal/Modal.tsx b/src/components/modal/Modal.tsx new file mode 100644 index 0000000..2d1b0be --- /dev/null +++ b/src/components/modal/Modal.tsx @@ -0,0 +1,48 @@ +import Modal from 'react-modal'; +import Button from '../button/Button'; +import styles from './Modal.module.css'; + +type MyModalProps = { + isOpen: boolean; + children: React.ReactNode; + contentLabel: string; + onClose?: () => void; + onConfirm?: () => void; +}; + +const MyModal = ({ + isOpen, + children, + contentLabel, + onClose, + onConfirm, +}: MyModalProps) => { + return ( + +

{children}

+ +
+ ); +}; + +export default MyModal; diff --git a/src/components/radarChart/RadarChart.tsx b/src/components/radarChart/RadarChart.tsx new file mode 100644 index 0000000..af02576 --- /dev/null +++ b/src/components/radarChart/RadarChart.tsx @@ -0,0 +1,86 @@ +// import { useEffect, useState } from 'react'; +import { + Radar, + RadarChart, + PolarGrid, + PolarAngleAxis, + PolarRadiusAxis, + Legend, + ResponsiveContainer, +} from 'recharts'; + +type ChartDataItem = { + subject: string; + score: number; + averageScore: number; +}; + +type CustomRadarChartProps = { + data: ChartDataItem[]; + averageLabel?: string; + scoreLabel?: string; +}; + +const CustomRadarChart = ({ + data, + averageLabel, + scoreLabel, +}: CustomRadarChartProps) => { + // const [animate, setAnimate] = useState(true); + + // // 컴포넌트 마운트 후 애니메이션은 한 번만 실행 + // useEffect(() => { + // const timer = setTimeout(() => setAnimate(false), 2000); // 2초 후 애니메이션 끔 + // return () => clearTimeout(timer); + // }, []); + + return ( + + + + + + + + + + + ); +}; + +export default CustomRadarChart; diff --git a/src/constants/questions.ts b/src/constants/questions.ts new file mode 100644 index 0000000..49aed1c --- /dev/null +++ b/src/constants/questions.ts @@ -0,0 +1,160 @@ +type Question = { + id: number; + text: string; + options: string[]; +}; + +export type Competency = + | 'communication' + | 'technicalExpertise' + | 'responsibility' + | 'problemSolving' + | 'creativity' + | 'leadership' + | 'growthPotential'; + +export const QUESTIONS: Question[] = [ + { + id: 1, + text: '팀 회의에서 의견이 갈릴 때 가장 이상적인 대응은 무엇인가요?', + options: [ + '모든 구성원의 의견을 경청하고 균형 있게 전달한다', // 의사소통 + '공동의 목표를 상기시키며 팀 방향성을 이끈다', // 리더십 + '문제의 핵심 원인을 파악하고 대안을 제시한다', // 문제 해결력 + ], + }, + { + id: 2, + text: '새로운 기술이나 도구를 배울 때 어떤 접근 방식을 선호하시나요?', + options: [ + '먼저 시도하고 필요시 학습하며 적응한다', // 성장 가능성 + '기능과 구조를 이론적으로 파악한 후 사용한다', // 기술 전문성 + '다르게 활용하는 법을 실험하며 응용한다', // 창의성 + ], + }, + { + id: 3, + text: '예상치 못한 문제가 발생했을 때 당신의 대응은?', + options: [ + '원인을 분석하고 체계적으로 해결한다', // 문제 해결력 + '기술적 원인을 빠르게 진단하고 최적의 해결 방법을 적용한다', // 기술 전문성 + '일단 실행해보며 실전에서 답을 찾는다', // 성장 가능성 + ], + }, + { + id: 4, + text: '업무 중 실수가 발생했을 때 가장 적절한 태도는?', + options: [ + '책임을 인정하고 스스로 해결책을 제시한다', // 책임감 + '팀과 상황을 공유하고 재발 방지를 위한 프로세스를 논의한다', // 리더십 + '실수를 분석하고 동일한 문제를 방지하는 구조를 만든다', // 문제 해결력 + ], + }, + { + id: 5, + text: '효과적인 리더가 갖추어야 할 가장 중요한 요소는?', + options: [ + '팀원들과 자주 소통하고 신뢰를 형성하는 능력', // 의사소통 + '팀 전체를 대표하고 책임지는 태도', // 리더십 + '새로운 방식으로 해결책을 제시하는 창의력', // 창의성 + ], + }, + { + id: 6, + text: '프로젝트 시작 단계에서 어떤 행동을 우선하시나요?', + options: [ + '관련 기술을 깊이 있게 조사하고 익힌다', // 기술 전문성 + '책임과 역할을 명확히 하고 계획을 세운다', // 책임감 + '새로운 방향을 탐색하며 초기 실험을 해본다', // 창의성 + ], + }, + { + id: 7, + text: '기한이 촉박한 프로젝트 상황에서 선호하는 행동은?', + options: [ + '진행 상황을 명확히 소통하며 팀과 조율한다', // 의사소통 + '자신의 역할을 다하며 결과에 책임을 진다', // 책임감 + '팀에 동기부여를 주고 중심을 잡아준다', // 리더십 + ], + }, + { + id: 8, + text: '낯선 시스템을 도입할 때 어떤 행동이 가장 중요하다고 보시나요?', + options: [ + '기능을 먼저 분석하고 구조를 파악한다', // 기술 전문성 + '팀원들과 정보를 공유하며 함께 학습한다', // 의사소통 + '시행착오를 두려워하지 않고 직접 써본다', // 성장 가능성 + ], + }, + { + id: 9, + text: '결과가 기대와 다를 경우 어떤 방식으로 대응하시나요?', + options: [ + '데이터 기반으로 원인을 분석해 해결한다', // 문제 해결력 + '새로운 아이디어를 제안하며 방식을 개선한다', // 창의성 + '실패 원인을 돌아보고 더 나은 방식을 학습하여 적용한다', // 성장 가능성 + ], + }, + { + id: 10, + text: '업무 일정이 지연될 경우, 어떤 대처를 하시나요?', + options: [ + '핵심 업무를 선별해 우선 처리한다', // 책임감 + '지연 원인을 분석하고 구체적 대안을 세운다', // 문제 해결력 + '유사 사례를 참고해 효과적인 방식을 도입한다', // 기술 전문성 + ], + }, + { + id: 11, + text: '새로운 제안을 할 때 가장 중요하다고 생각하는 방식은?', + options: [ + '아이디어를 빠르게 시각화하고 실험해본다', // 창의성 + '구성원과 충분히 소통하고 피드백을 반영한다', // 의사소통 + '조직의 목표와 연결지어 전략적으로 설득한다', // 리더십 + ], + }, + { + id: 12, + text: '직무 역량이 부족하다고 느껴질 때, 당신의 행동은?', + options: [ + '스스로 자료를 찾아 반복 학습한다', // 성장 가능성 + '전문가에게 조언을 구하고 구체적 해결을 시도한다', // 기술 전문성 + '일정 조율을 통해 품질과 기한을 모두 관리한다', // 책임감 + ], + }, + { + id: 13, + text: '팀 내 갈등 상황에서 필요한 대응은 무엇인가요?', + options: [ + '팀원 간 갈등을 중재하며 해결을 유도한다', // 리더십 + '각자의 입장을 정리하고 공통점을 찾는다', // 의사소통 + '갈등의 구조적 원인을 분석하고 장기 해결을 시도한다', // 문제 해결력 + ], + }, + { + id: 14, + text: '장기적인 커리어를 위한 이상적인 자세는?', + options: [ + '스스로 학습하며 지속적으로 도전한다', // 성장 가능성 + '업무에 대한 책임감을 가지고 완성도를 높인다', // 책임감 + '새로운 기회를 포착하고 제안할 수 있는 감각을 갖춘다', // 창의성 + ], + }, +]; + +export const QUESTION_OPTION_COMPETENCIES: Competency[][] = [ + ['communication', 'leadership', 'problemSolving'], // Q1 + ['growthPotential', 'technicalExpertise', 'creativity'], // Q2 + ['problemSolving', 'technicalExpertise', 'growthPotential'], // Q3 + ['responsibility', 'leadership', 'problemSolving'], // Q4 + ['communication', 'leadership', 'creativity'], // Q5 + ['technicalExpertise', 'responsibility', 'creativity'], // Q6 + ['communication', 'responsibility', 'leadership'], // Q7 + ['technicalExpertise', 'communication', 'growthPotential'], // Q8 + ['problemSolving', 'creativity', 'growthPotential'], // Q9 + ['responsibility', 'problemSolving', 'technicalExpertise'], // Q10 + ['creativity', 'communication', 'leadership'], // Q11 + ['growthPotential', 'technicalExpertise', 'responsibility'], // Q12 + ['leadership', 'communication', 'problemSolving'], // Q13 + ['growthPotential', 'responsibility', 'creativity'], // Q14 +]; diff --git a/src/hooks/useAuthInit.ts b/src/hooks/useAuthInit.ts new file mode 100644 index 0000000..6a817b7 --- /dev/null +++ b/src/hooks/useAuthInit.ts @@ -0,0 +1,54 @@ +import { useEffect, useState } from 'react'; +import axios from 'axios'; +import axiosInstance from '@/lib/axiosInstance'; +import useAuthStore from '@/store/auth'; +import useUserStore from '@/store/user'; + +const useAuthInit = () => { + const accessToken = useAuthStore((state) => state.accessToken); + const setAccessToken = useAuthStore((state) => state.setAccessToken); + const clearAccessToken = useAuthStore((state) => state.clearAccessToken); + const setUser = useUserStore.getState().setUser; + const clearUser = useUserStore.getState().clearUser; + const [isAuthChecked, setIsAuthChecked] = useState(false); + + useEffect(() => { + const initializeAuth = async () => { + // 이미 accessToken 있으면 재요청 안 함 + if (accessToken) { + setIsAuthChecked(true); + return; + } + + try { + // accessToken이 없을 경우 refresh 시도 + const refreshRes = await axios.post( + `${import.meta.env.VITE_API_BASE_URL}/api/v1/token/refresh`, + null, + { withCredentials: true }, + ); + + const accessToken = refreshRes.data.data; + setAccessToken(accessToken); + + // 유저 정보 요청 + const meRes = await axiosInstance.get('/api/v1/users/me'); + const user = meRes.data.data; + setUser({ id: user.id, name: user.name }); + console.log('로그인 유지 성공, 유저 정보:', user); + } catch (err) { + console.warn('로그인 유지 실패, 로그아웃 처리됨', err); + clearAccessToken(); + clearUser(); + } finally { + setIsAuthChecked(true); + } + }; + + initializeAuth(); + }, [accessToken, setAccessToken, clearAccessToken, setUser, clearUser]); + + return { isAuthChecked }; +}; + +export default useAuthInit; diff --git a/src/hooks/useModal.ts b/src/hooks/useModal.ts new file mode 100644 index 0000000..885c332 --- /dev/null +++ b/src/hooks/useModal.ts @@ -0,0 +1,21 @@ +import { useState } from 'react'; + +const useModal = () => { + const [isModalOpen, setIsModalOpen] = useState(false); + + const openModal = () => { + setIsModalOpen(true); + }; + + const closeModal = () => { + setIsModalOpen(false); + }; + + return { + isModalOpen, + openModal, + closeModal, + }; +}; + +export default useModal; diff --git a/src/lib/axiosFormInstance.ts b/src/lib/axiosFormInstance.ts new file mode 100644 index 0000000..4e756de --- /dev/null +++ b/src/lib/axiosFormInstance.ts @@ -0,0 +1,68 @@ +import axios from 'axios'; +import useAuthStore from '@/store/auth'; +import useUserStore from '@/store/user'; + +const axiosFormInstance = axios.create({ + baseURL: import.meta.env.VITE_PYTHON_BASE_URL, + withCredentials: true, +}); + +axiosFormInstance.interceptors.request.use( + (config) => { + const token = useAuthStore.getState().accessToken; + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + return config; + }, + (error) => Promise.reject(error), +); + +axiosFormInstance.interceptors.response.use( + (response) => response, + async (error) => { + const originalRequest = error.config; + + if (error.response?.status === 401 && !originalRequest._retry) { + originalRequest._retry = true; + + try { + const refreshRes = await axios.post( + `${import.meta.env.VITE_API_BASE_URL}/api/v1/token/refresh`, + null, + { withCredentials: true }, + ); + + const newAccessToken = refreshRes.data.data; + + useAuthStore.getState().setAccessToken(newAccessToken); + originalRequest.headers.Authorization = `Bearer ${newAccessToken}`; + + const meRes = await axios.get( + `${import.meta.env.VITE_API_BASE_URL}/api/v1/users/me`, + { + headers: { Authorization: `Bearer ${newAccessToken}` }, + withCredentials: true, + }, + ); + const user = meRes.data.data; + useUserStore.getState().setUser({ + id: user.id, + name: user.name, + }); + + return axiosFormInstance(originalRequest); + } catch (refreshError) { + useAuthStore.getState().clearAccessToken(); + useUserStore.getState().clearUser(); + window.location.href = '/login'; + + return Promise.reject(refreshError); + } + } + + return Promise.reject(error); + }, +); + +export default axiosFormInstance; diff --git a/src/lib/axiosInstance.ts b/src/lib/axiosInstance.ts new file mode 100644 index 0000000..cbf3ed9 --- /dev/null +++ b/src/lib/axiosInstance.ts @@ -0,0 +1,73 @@ +import axios from 'axios'; +import useAuthStore from '@/store/auth'; +import useUserStore from '@/store/user'; + +const axiosInstance = axios.create({ + baseURL: import.meta.env.VITE_API_BASE_URL, + headers: { + 'Content-Type': 'application/json', + }, + withCredentials: true, +}); + +// 요청 인터셉터: accessToken 자동 첨부 +axiosInstance.interceptors.request.use( + (config) => { + const token = useAuthStore.getState().accessToken; + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + return config; + }, + (error) => Promise.reject(error), +); + +// 응답 인터셉터: accessToken 만료 시 refreshToken으로 재발급 시도 +axiosInstance.interceptors.response.use( + (response) => response, + async (error) => { + const originalRequest = error.config; + + if (error.response?.status === 401 && !originalRequest._retry) { + originalRequest._retry = true; + + try { + const refreshRes = await axios.post( + `${import.meta.env.VITE_API_BASE_URL}/api/v1/token/refresh`, + null, + { withCredentials: true }, + ); + + const newAccessToken = refreshRes.data.data; + + useAuthStore.getState().setAccessToken(newAccessToken); + originalRequest.headers.Authorization = `Bearer ${newAccessToken}`; + + const meRes = await axios.get( + `${import.meta.env.VITE_API_BASE_URL}/api/v1/users/me`, + { + headers: { Authorization: `Bearer ${newAccessToken}` }, + withCredentials: true, + }, + ); + const user = meRes.data.data; + useUserStore.getState().setUser({ + id: user.id, + name: user.name, + }); + + return axiosInstance(originalRequest); + } catch (refreshError) { + useAuthStore.getState().clearAccessToken(); + useUserStore.getState().clearUser(); + window.location.href = '/login'; + + return Promise.reject(refreshError); + } + } + + return Promise.reject(error); + }, +); + +export default axiosInstance; diff --git a/src/main.tsx b/src/main.tsx index 4aff025..c8ed593 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,9 +1,10 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' -import App from './App.tsx' +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; +import App from './App.tsx'; +import './styles/global.css'; createRoot(document.getElementById('root')!).render( , -) +); diff --git a/src/pages/analytics/loading/Loading.module.css b/src/pages/analytics/loading/Loading.module.css new file mode 100644 index 0000000..0bd898b --- /dev/null +++ b/src/pages/analytics/loading/Loading.module.css @@ -0,0 +1,27 @@ +.loadingContainer { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + height: calc(100vh - 120px); + padding: 0 16px; +} + +.text { + margin-bottom: 20px; + font-size: var(--font-size-18); + text-align: center; +} + +.progressBar { + width: 200px; + height: 8px; + background-color: var(--color-tertiary); + border-radius: 2px; +} + +.progressFill { + height: 100%; + background-color: var(--color-primary); + transition: width 0.1s linear; +} diff --git a/src/pages/analytics/loading/Loading.tsx b/src/pages/analytics/loading/Loading.tsx new file mode 100644 index 0000000..beb33f6 --- /dev/null +++ b/src/pages/analytics/loading/Loading.tsx @@ -0,0 +1,100 @@ +import { useEffect, useRef, useState } from 'react'; +import { useLocation, useNavigate } from 'react-router-dom'; +import axiosFormInstance from '@/lib/axiosFormInstance'; +import useUserStore from '@/store/user'; +import useModal from '@/hooks/useModal'; +import MyModal from '@/components/modal/Modal'; +import styles from './Loading.module.css'; + +const Loading = () => { + const navigate = useNavigate(); + const [progress, setProgress] = useState(0); + + const user = useUserStore((state) => state.user); + const userId = user?.id; + const userName = user?.name; + + const { isModalOpen, openModal, closeModal } = useModal(); + + const location = useLocation(); + const { file, selectedJob } = location.state || {}; + + const hasRunRef = useRef(false); + + useEffect(() => { + if (!file || !selectedJob || !userId || hasRunRef.current) return; + + hasRunRef.current = true; + + const processResume = async () => { + try { + const formData = new FormData(); + formData.append('file', file); + formData.append('job_field', selectedJob); + formData.append('user_id', String(userId)); + + const response = await axiosFormInstance.post( + '/resume/process', + formData, + ); + + const result = response.data; + + navigate('/analytics/report', { + state: { + resumeId: result.resume_id, + jobField: result.job_field, + scoreResult: result.score_result.competencyScores, + AIAnalysis: result.ai_analysis, + }, + }); + } catch (err) { + console.error(err); + openModal(); + } + }; + + processResume(); + }, [file, selectedJob, userId, navigate, openModal]); + + useEffect(() => { + const interval = setInterval(() => { + setProgress((prev) => { + if (prev >= 100) { + clearInterval(interval); + return 100; + } + return prev + 0.3; + }); + }, 30); + + return () => clearInterval(interval); + }, []); + + return ( + <> +
+

{`${userName} 님의 이력서를 분석하고 있어요`}

+
+
+
+
+ + navigate('/analytics/start')} + onClose={closeModal} + > + 이력서 분석 중 오류가 발생했습니다. + + + ); +}; + +export default Loading; diff --git a/src/pages/analytics/report/Report.module.css b/src/pages/analytics/report/Report.module.css new file mode 100644 index 0000000..c28d396 --- /dev/null +++ b/src/pages/analytics/report/Report.module.css @@ -0,0 +1,135 @@ +.reportContainer { + margin-block: 50px 100px; + padding: 0 60px; + text-align: center; +} + +.report { + text-align: left; + display: flex; + flex-direction: column; + gap: 60px; + margin-bottom: 80px; + font-size: var(--font-size-16); + + h2 { + text-align: center; + font-size: var(--font-size-20); + } +} + +.jobField { + display: flex; + gap: 180px; +} + +.jobField, +.competency, +.result, +.recomCorp { + h3 { + font-size: var(--font-size-20); + font-weight: 700; + } +} + +.top3 { + margin-top: 24px; + display: flex; + gap: 40px; +} + +.top3Text { + margin-bottom: 16px; + font-size: var(--font-size-14); + color: var(--color-secondary); +} + +.top3List { + display: flex; + gap: 20px; + + li { + span { + margin-right: 4px; + color: var(--color-primary); + } + } +} + +.chart { + max-width: 400px; + aspect-ratio: 1; + margin: 0 auto; +} + +.result { + h3 { + margin-bottom: 24px; + } +} + +.resultSection { + display: flex; + flex-direction: column; + gap: 32px; + + h4 { + margin-bottom: 0.5rem; + } +} + +.resultText { + white-space: pre-wrap; + line-height: 24px; +} + +@media (max-width: 768px) { + .reportContainer { + padding: 0 32px; + } + + .top3 { + flex-direction: column; + gap: 20px; + } +} + +@media (max-width: 481px) { + .reportContainer { + padding: 0 24px; + } + + .report { + font-size: var(--font-size-14); + + h2 { + font-size: var(--font-size-18); + } + } + + .jobField, + .competency, + .result, + .recomCorp { + h3 { + font-size: var(--font-size-16); + } + } + + .jobField { + flex-direction: column; + gap: 24px; + } + + .top3Text { + font-size: var(--font-size-13); + } +} + +@media (max-width: 380px) { + .top3List { + flex-direction: column; + gap: 16px; + } +} diff --git a/src/pages/analytics/report/Report.tsx b/src/pages/analytics/report/Report.tsx new file mode 100644 index 0000000..78b790b --- /dev/null +++ b/src/pages/analytics/report/Report.tsx @@ -0,0 +1,154 @@ +import { useEffect, useMemo } from 'react'; +import { useLocation } from 'react-router-dom'; +import axiosFormInstance from '@/lib/axiosFormInstance'; +import useUserStore from '@/store/user'; +import { generatePDFBlob, downloadPDF } from '@/utils/generatePDF'; +import CustomRadarChart from '@/components/radarChart/RadarChart'; +import Button from '@/components/button/Button'; +import styles from './Report.module.css'; + +type ScoreResultItem = { + competencyName: string; + totalScore: number; +}; + +type AIAnalysisItem = string; + +const RESULT_TITLES = [ + '1. 핵심 강점', + '2. 보완할 점 또는 약점', + '3. 기술 스택 요약', + '4. 추천 직무 또는 포지션', +]; + +const subjects = [ + '의사소통', + '기술 전문성', + '책임감', + '문제 해결력', + '창의성', + '리더십', + '성장 가능성', +]; + +const Report = () => { + const location = useLocation(); + const { + resumeId, + jobField, + scoreResult = [], + AIAnalysis = [], + } = location.state || {}; + + const user = useUserStore((state) => state.user); + const userName = user?.name; + + useEffect(() => { + if (!resumeId || !user) return; + + const timer = requestAnimationFrame(() => { + uploadPDFtoServer(); + }); + + return () => cancelAnimationFrame(timer); + + async function uploadPDFtoServer() { + const blob = await generatePDFBlob(); + if (!blob) return; + + try { + const formData = new FormData(); + formData.append('file', blob, 'report.pdf'); + formData.append('resume_id', resumeId); + + await axiosFormInstance.post('/upload/pdf', formData); + } catch (err) { + console.error(err); + } + } + }, [resumeId, user]); + + // ✅ 평균 점수, radar 차트용 데이터 메모이제이션 + const averageScore = useMemo(() => { + const total = scoreResult.reduce( + (sum: number, item: ScoreResultItem) => sum + item.totalScore, + 0, + ); + return scoreResult.length > 0 ? total / scoreResult.length : 0; + }, [scoreResult]); + + const data = useMemo(() => { + return subjects.map((subject) => { + const match = scoreResult.find( + (item: ScoreResultItem) => item.competencyName === subject, + ); + return { + subject, + score: match?.totalScore ?? 0, + averageScore, + }; + }); + }, [scoreResult, averageScore]); + + const top3 = useMemo(() => { + return [...data].sort((a, b) => b.score - a.score).slice(0, 3); + }, [data]); + + return ( +
+
+

{userName} 님의 이력서 분석 결과

+
+

01 직무

+

{jobField}

+
+
+

02 이력서에 표현된 역량

+
+

역량 TOP3

+
+

+ 아래는 이력서에 드러난 당신의 역량 중 강조된 세 가지입니다. + 당신이 표현하려 했던 역량과 일치하나요? +

+
    + {top3.map((v, i) => ( +
  • + {i + 1}위 + {v.subject} +
  • + ))} +
+
+
+
+ +
+
+
+

03 분석 결과

+
+ {AIAnalysis.map((content: AIAnalysisItem, idx: number) => ( +
+

{RESULT_TITLES[idx]}

+

{content}

+
+ ))} +
+
+
+

04 추천 기업

+
+
+ +
+ ); +}; + +export default Report; diff --git a/src/pages/analytics/start/Start.module.css b/src/pages/analytics/start/Start.module.css new file mode 100644 index 0000000..75bf8f0 --- /dev/null +++ b/src/pages/analytics/start/Start.module.css @@ -0,0 +1,33 @@ +.startContainer { + margin-block: 50px 100px; + text-align: center; +} + +.startTitle { + font-size: var(--font-size-18); + margin-bottom: 12px; +} + +.attentionNote { + font-size: var(--font-size-12); + color: var(--color-secondary); +} + +.jobSelectContainer, +.fileUploadContainer { + padding: 0 16px; + margin-bottom: 80px; +} + +.jobSelectContainer { + p { + margin-bottom: 28px; + } +} + +.attentionNoteList { + display: flex; + flex-direction: column; + gap: 4px; + margin-bottom: 28px; +} diff --git a/src/pages/analytics/start/Start.tsx b/src/pages/analytics/start/Start.tsx new file mode 100644 index 0000000..a7575ab --- /dev/null +++ b/src/pages/analytics/start/Start.tsx @@ -0,0 +1,110 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import useModal from '@/hooks/useModal'; +import useAuthStore from '@/store/auth'; +import JobSelectBox from './components/jobSelectBox/JobSelectBox'; +import FileUploadBox from './components/fileUploadBox/FileUploadBox'; +import Button from '@/components/button/Button'; +import MyModal from '@/components/modal/Modal'; +import styles from './Start.module.css'; + +const MODAL_MESSAGES = { + NOT_LOGGED_IN: '로그인 후 이용 가능합니다.', + NO_JOB: '직무를 선택해주세요.', + NO_FILE: '파일을 첨부해주세요.', +} as const; + +const Start = () => { + const [selectedJob, setSelectedJob] = useState(null); + const [file, setFile] = useState(null); + const [modalMessage, setModalMessage] = useState(''); + + const accessToken = useAuthStore((state) => state.accessToken); + const isLoggedIn = !!accessToken; + + const navigate = useNavigate(); + const { isModalOpen, openModal, closeModal } = useModal(); + + const showModal = (message: string) => { + setModalMessage(message); + openModal(); + }; + + const isFormValid = () => { + if (!isLoggedIn) { + showModal(MODAL_MESSAGES.NOT_LOGGED_IN); + return false; + } + if (!selectedJob) { + showModal(MODAL_MESSAGES.NO_JOB); + return false; + } + if (!file) { + showModal(MODAL_MESSAGES.NO_FILE); + return false; + } + return true; + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + if (!isFormValid()) return; + + navigate('/analytics/loading', { + state: { + file, + selectedJob, + }, + }); + }; + + return ( + <> +
+
+
+ + 분석을 원하는 직무를 선택하세요. + +

+ * 직무명은 실제 기업의 부서명과는 다를 수 있습니다. +

+ +
+
+ + 주의사항에 유의하여 이력서를 첨부하세요. + +
    +
  • +

    + ⓘ 이력서 파일은 PDF, DOCX 형식으로 업로드해 주세요. +

    +
  • +
  • +

    + ⓘ 오류가 있을 때는 파일 용량이 50MB가 넘었는지 확인해주세요. +

    +
  • +
+ +
+ +
+
+ + + {modalMessage} + + + ); +}; + +export default Start; diff --git a/src/pages/analytics/start/components/fileUploadBox/FileUploadBox.module.css b/src/pages/analytics/start/components/fileUploadBox/FileUploadBox.module.css new file mode 100644 index 0000000..700f778 --- /dev/null +++ b/src/pages/analytics/start/components/fileUploadBox/FileUploadBox.module.css @@ -0,0 +1,35 @@ +.hiddenInput { + display: none; +} + +.fileUploadBox { + display: flex; + align-items: center; + gap: 8px; + margin: 0 auto; + width: 100%; + max-width: 480px; + height: 60px; + padding: 0 40px 0 12px; + border: 1px solid var(--color-gray-b1); + border-radius: 5px; + font-size: var(--font-size-16); + color: var(--color-secondary); + + &:focus { + outline: none; + } +} + +.fileNameContainer { + display: flex; + overflow: hidden; + color: var(--color-gray-2); +} + +.fileName { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 100%; +} diff --git a/src/pages/analytics/start/components/fileUploadBox/FileUploadBox.tsx b/src/pages/analytics/start/components/fileUploadBox/FileUploadBox.tsx new file mode 100644 index 0000000..07f33ad --- /dev/null +++ b/src/pages/analytics/start/components/fileUploadBox/FileUploadBox.tsx @@ -0,0 +1,93 @@ +import { useRef, useState } from 'react'; +import styles from './FileUploadBox.module.css'; + +type FileUploadBoxProps = { + onFileChange: (file: File | null) => void; +}; + +const FileUploadBox = ({ onFileChange }: FileUploadBoxProps) => { + const [fileName, setFileName] = useState(null); + const [fileExtension, setFileExtension] = useState(null); + const inputRef = useRef(null); + + const extractNameAndExtension = (file: File) => { + const match = file.name.match(/^(.*)\.(pdf|docx)$/i); + if (match) { + setFileName(match[1]); + setFileExtension(`.${match[2].toLowerCase()}`); + } else { + setFileName(file.name); + setFileExtension(null); + } + }; + + const handleFileChange = (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + + // 사용자가 업로드 취소한 경우 - 기존 파일 유지 + if (!file) return; + + if (file) { + extractNameAndExtension(file); + onFileChange(file); + } else { + setFileName(null); + setFileExtension(null); + onFileChange(null); + } + }; + + const handleDrop = (e: React.DragEvent) => { + e.preventDefault(); + + const file = e.dataTransfer.files?.[0]; + + if (file) { + extractNameAndExtension(file); + + if (inputRef.current) { + const dataTransfer = new DataTransfer(); + dataTransfer.items.add(file); + inputRef.current.files = dataTransfer.files; + } + + onFileChange(file); + } + }; + + const handleDragOver = (e: React.DragEvent) => { + e.preventDefault(); + }; + + return ( +
+ + +
+ ); +}; + +export default FileUploadBox; diff --git a/src/pages/analytics/start/components/jobSelectBox/JobSelectBox.module.css b/src/pages/analytics/start/components/jobSelectBox/JobSelectBox.module.css new file mode 100644 index 0000000..ac00ee7 --- /dev/null +++ b/src/pages/analytics/start/components/jobSelectBox/JobSelectBox.module.css @@ -0,0 +1,45 @@ +.jobList { + display: grid; + grid-template-columns: repeat(3, 1fr); + justify-items: center; + align-items: center; + gap: 36px; + padding: 36px 60px; + border: 1px solid var(--color-gray-ef); + border-radius: 5px; + text-align: left; + font-size: var(--font-size-15); + + li { + width: 100%; + max-width: 150px; + } + + .jobLabel { + display: flex; + align-items: center; + gap: 8px; + padding: 10px 16px; + border-radius: 8px; + cursor: pointer; + } + + .radioBtn { + accent-color: #5f812e; + } +} + +@media (max-width: 768px) { + .jobList { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (max-width: 481px) { + .jobList { + grid-template-columns: repeat(1, 1fr); + gap: 28px; + padding-left: 0; + padding-right: 0; + } +} diff --git a/src/pages/analytics/start/components/jobSelectBox/JobSelectBox.tsx b/src/pages/analytics/start/components/jobSelectBox/JobSelectBox.tsx new file mode 100644 index 0000000..5b37e87 --- /dev/null +++ b/src/pages/analytics/start/components/jobSelectBox/JobSelectBox.tsx @@ -0,0 +1,40 @@ +import styles from './JobSelectBox.module.css'; + +const JOB_LIST = [ + '프론트엔드', + '백엔드', + '모바일 개발', + '디자인', + '클라우드', + '데이터 분석', + '블록체인', + '게임 개발', + '마케팅', +] as const; + +type JobSelectBoxProps = { + onJobChange: (job: string) => void; +}; + +const JobSelectBox = ({ onJobChange }: JobSelectBoxProps) => { + return ( +
    + {JOB_LIST.map((job) => ( +
  • + +
  • + ))} +
+ ); +}; + +export default JobSelectBox; diff --git a/src/pages/hr-test/loading/Loading.module.css b/src/pages/hr-test/loading/Loading.module.css new file mode 100644 index 0000000..667fbe3 --- /dev/null +++ b/src/pages/hr-test/loading/Loading.module.css @@ -0,0 +1,26 @@ +.loadingContainer { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + height: calc(100vh - 120px); +} + +.text { + font-size: var(--font-size-18); + margin-bottom: 20px; + text-align: center; +} + +.progressBar { + width: 200px; + height: 8px; + background-color: var(--color-tertiary); + border-radius: 2px; +} + +.progressFill { + height: 100%; + background-color: var(--color-primary); + transition: width 0.1s linear; +} diff --git a/src/pages/hr-test/loading/Loading.tsx b/src/pages/hr-test/loading/Loading.tsx new file mode 100644 index 0000000..92fb5d1 --- /dev/null +++ b/src/pages/hr-test/loading/Loading.tsx @@ -0,0 +1,84 @@ +import { useEffect, useRef, useState } from 'react'; +import { useLocation, useNavigate } from 'react-router-dom'; +import axiosInstance from '@/lib/axiosInstance'; +import useModal from '@/hooks/useModal'; +import MyModal from '@/components/modal/Modal'; +import styles from './Loading.module.css'; + +const HRTestLoading = () => { + const [progress, setProgress] = useState(0); + + const location = useLocation(); + const { scoreResult } = location.state || {}; + + const hasRunRef = useRef(false); + + const navigate = useNavigate(); + const { isModalOpen, openModal, closeModal } = useModal(); + + useEffect(() => { + if (!scoreResult || hasRunRef.current) return; + + hasRunRef.current = true; + + const processHRTest = async () => { + try { + const response = await axiosInstance.post('/api/v1/managers/match', { + scores: scoreResult, + }); + const result = response.data; + + navigate('/hr-test/result', { + state: { scoreResult, matchedResumes: result.matchedResumes }, + }); + } catch (err) { + console.error(err); + openModal(); + } + }; + + processHRTest(); + }, [scoreResult, navigate, openModal]); + + useEffect(() => { + const interval = setInterval(() => { + setProgress((prev) => { + if (prev >= 100) { + clearInterval(interval); + return 100; + } + return prev + 1.5; + }); + }, 30); + + return () => clearInterval(interval); + }, []); + + return ( + <> +
+

+ 결과 분석 중... +
잠시만 기다려주세요 +

+
+
+
+
+ + navigate('/hr-test/start')} + onClose={closeModal} + > + 결과 분석 중 오류가 발생했습니다. + + + ); +}; + +export default HRTestLoading; diff --git a/src/pages/hr-test/quiz/Quiz.module.css b/src/pages/hr-test/quiz/Quiz.module.css new file mode 100644 index 0000000..2fe7b4c --- /dev/null +++ b/src/pages/hr-test/quiz/Quiz.module.css @@ -0,0 +1,73 @@ +.quizContainer { + padding: 100px 16px 50px 16px; + text-align: center; +} + +.quizHeader { + display: flex; + justify-content: center; + align-items: center; + gap: 20px; + margin-bottom: 40px; +} + +.prevButton { + width: 24px; + height: 24px; + border: 1px solid var(--color-gray-b1); + border-radius: 100%; + cursor: pointer; + + svg { + margin: 0 auto; + } +} + +.progressBar { + width: 120px; + height: 4px; + background-color: var(--color-tertiary); + border-radius: 2px; +} + +.progressFill { + height: 100%; + background-color: var(--color-primary); + transition: width 0.1s linear; +} + +.progressText { + width: 36px; + font-size: var(--font-size-14); +} + +.questionContainer { + display: flex; + flex-direction: column; + gap: 40px; + transition: + opacity 0.3s ease, + transform 0.3s ease; + opacity: 1; + transform: translateY(0); +} + +.fadeOut { + opacity: 0; + transform: translateY(20px); +} + +.quizCount { + color: var(--color-primary); + font-size: var(--font-size-30); +} + +.question { + font-size: var(--font-size-18); +} + +.answerList { + display: flex; + flex-direction: column; + gap: 16px; +} diff --git a/src/pages/hr-test/quiz/Quiz.tsx b/src/pages/hr-test/quiz/Quiz.tsx new file mode 100644 index 0000000..984b0db --- /dev/null +++ b/src/pages/hr-test/quiz/Quiz.tsx @@ -0,0 +1,130 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { + type Competency, + QUESTIONS, + QUESTION_OPTION_COMPETENCIES, +} from '@/constants/questions'; +import Icon from '@/components/icon/Icon'; +import Option from './components/option/Option'; +import styles from './Quiz.module.css'; + +const competencyNameMap: Record = { + communication: '의사소통', + technicalExpertise: '기술 전문성', + responsibility: '책임감', + problemSolving: '문제 해결력', + creativity: '창의성', + leadership: '리더십', + growthPotential: '성장 가능성', +}; + +const BASE_SCORE = 5; +const MULTIPLIER = 2; + +const HRTestQuiz = () => { + const navigate = useNavigate(); + + const total = QUESTIONS.length; + + const [currentStep, setCurrentStep] = useState(0); + const [selectedOptions, setSelectedOptions] = useState<(0 | 1 | 2)[]>([]); + const [isAnimating, setIsAnimating] = useState(false); + + const currentQuestion = QUESTIONS[currentStep]; + + const calculateScores = (): Record => { + const scores: Record = { + communication: BASE_SCORE, + technicalExpertise: BASE_SCORE, + responsibility: BASE_SCORE, + problemSolving: BASE_SCORE, + creativity: BASE_SCORE, + leadership: BASE_SCORE, + growthPotential: BASE_SCORE, + }; + + selectedOptions.forEach((selectedOption, i) => { + const competency = QUESTION_OPTION_COMPETENCIES[i][selectedOption]; + scores[competency] += 1; + }); + + // 한글 키로 매핑 + 가중치 곱하기 + const translated: Record = {}; + for (const [key, value] of Object.entries(scores)) { + const korKey = competencyNameMap[key as Competency]; + translated[korKey] = value * MULTIPLIER; + } + + return translated; + }; + + const handleSelect = (selectedIndex: 0 | 1 | 2) => { + const updatedOptions = [...selectedOptions]; + updatedOptions[currentStep] = selectedIndex; + setSelectedOptions(updatedOptions); + + if (currentStep === total - 1) { + const scoreResult = calculateScores(); + console.log(scoreResult); + navigate('/hr-test/loading', { state: { scoreResult } }); + } else { + animateStep(() => setCurrentStep((prev) => prev + 1)); + } + }; + + const goToPrevStep = () => { + if (currentStep === 0) return; + animateStep(() => setCurrentStep((prev) => prev - 1)); + }; + + const animateStep = (callback: () => void) => { + setIsAnimating(true); + setTimeout(() => { + callback(); + setIsAnimating(false); + }, 300); + }; + + return ( +
+
+ +
+
+
+
+ {currentStep + 1} + /{total} +
+
+
+
Q{currentQuestion.id}.
+
{currentQuestion.text}
+
    + {currentQuestion.options.map((option, i) => ( +
  1. + +
  2. + ))} +
+
+
+ ); +}; + +export default HRTestQuiz; diff --git a/src/pages/hr-test/quiz/components/option/Option.module.css b/src/pages/hr-test/quiz/components/option/Option.module.css new file mode 100644 index 0000000..afc8867 --- /dev/null +++ b/src/pages/hr-test/quiz/components/option/Option.module.css @@ -0,0 +1,14 @@ +.button { + padding: 12px; + width: 100%; + max-width: 440px; + height: 72px; + border: 1px solid var(--color-primary); + border-radius: 5px; + font-size: var(--font-size-16); + cursor: pointer; + + &:hover { + background-color: var(--color-tertiary); + } +} diff --git a/src/pages/hr-test/quiz/components/option/Option.tsx b/src/pages/hr-test/quiz/components/option/Option.tsx new file mode 100644 index 0000000..13b2680 --- /dev/null +++ b/src/pages/hr-test/quiz/components/option/Option.tsx @@ -0,0 +1,15 @@ +import styles from './Option.module.css'; + +type OptionProps = React.ButtonHTMLAttributes & { + children: string; +}; + +const Option = ({ children, ...props }: OptionProps) => { + return ( + + ); +}; + +export default Option; diff --git a/src/pages/hr-test/result/Result.module.css b/src/pages/hr-test/result/Result.module.css new file mode 100644 index 0000000..d99d3a9 --- /dev/null +++ b/src/pages/hr-test/result/Result.module.css @@ -0,0 +1,143 @@ +.report { + display: flex; + flex-direction: column; + gap: 80px; + margin-block: 50px 80px; + padding: 0 60px; + font-size: var(--font-size-16); + + h2 { + margin-bottom: 40px; + text-align: center; + font-size: var(--font-size-20); + font-weight: 700; + } + + p { + text-align: center; + line-height: 28px; + } +} + +.competency, +.recomRes { + h3 { + font-size: var(--font-size-20); + font-weight: 700; + margin-bottom: 24px; + } + + h4 { + font-size: var(--font-size-16); + font-weight: 700; + + margin-bottom: 24px; + } + + p { + text-align: left; + margin-bottom: 28px; + } +} + +.chart { + max-width: 400px; + aspect-ratio: 1; + margin: 0 auto 32px; +} + +.top3List { + display: flex; + flex-direction: column; + gap: 20px; +} + +.top3Competency { + display: block; + margin-bottom: 8px; + font-size: var(--font-size-16); +} + +.top3Description { + display: flex; + align-items: center; + gap: 8px; + font-size: var(--font-size-14); +} + +.resumeList { + display: flex; + flex-direction: column; + gap: 28px; + + li { + display: flex; + align-items: center; + gap: 12px; + } +} + +.jobFieldText { + color: var(--color-secondary); +} + +.downloadLink { + display: flex; + align-items: center; + gap: 4px; + padding: 4px 12px; + border: 1px solid var(--color-gray-ef); + border-radius: 5px; + font-size: var(--font-size-14); + + &:hover { + background-color: var(--color-tertiary); + } +} + +@media (max-width: 768px) { + .report { + padding: 0 32px; + } +} + +@media (max-width: 481px) { + .report { + padding: 0 24px; + font-size: var(--font-size-14); + + h2 { + text-align: center; + font-size: var(--font-size-18); + margin-bottom: 40px; + } + } + + .competency, + .recomRes { + h3 { + font-size: var(--font-size-18); + } + } + + .arrow { + font-size: var(--font-size-12); + margin-right: 4px; + } + + .downloadLink { + font-size: var(--font-size-12); + } +} + +@media (max-width: 320px) { + .report { + h2 { + font-size: var(--font-size-16); + } + } + + .downloadText { + display: none; + } +} diff --git a/src/pages/hr-test/result/Result.tsx b/src/pages/hr-test/result/Result.tsx new file mode 100644 index 0000000..a19e58b --- /dev/null +++ b/src/pages/hr-test/result/Result.tsx @@ -0,0 +1,200 @@ +import { useEffect, useState } from 'react'; +import { useLocation } from 'react-router-dom'; +import { + Radar, + RadarChart, + PolarGrid, + PolarAngleAxis, + PolarRadiusAxis, + Legend, + ResponsiveContainer, +} from 'recharts'; +import useUserStore from '@/store/user'; +import Icon from '@/components/icon/Icon'; +import styles from './Result.module.css'; + +const competencyDescriptions: { [key: string]: string } = { + 의사소통: + '팀원 간 원활한 협업과 갈등 조정을 위한 커뮤니케이션 능력을 높게 평가합니다.', + '기술 전문성': + '직무에 필요한 지식과 기술을 깊이 있게 이해하고, 실제 업무에 적용할 수 있는 인재를 선호합니다.', + '문제 해결력': + '스스로 상황을 분석하고 실행 가능한 해결책을 제시할 수 있는 인재를 선호합니다.', + 책임감: '맡은 일을 끝까지 해내며 결과에 책임지는 태도를 중요하게 생각합니다.', + 창의성: + '기존 방식에 얽매이지 않고, 새로운 아이디어로 문제를 해결하거나 개선점을 제안하는 인재를 높이 평가합니다.', + 리더십: + '팀을 이끌며 구성원에게 긍정적인 영향을 주고, 목표 달성을 위해 방향을 제시할 수 있는 역량을 중요하게 여깁니다.', + '성장 가능성': + '피드백을 수용하고 지속적으로 배우려는 태도를 가진, 잠재력 높은 인재에게 주목합니다.', +}; + +const subjects: string[] = [ + '의사소통', + '기술 전문성', + '책임감', + '문제 해결력', + '창의성', + '리더십', + '성장 가능성', +]; + +type MatchedResume = { + jobField: string; + fileUrl: string; +}; + +type HRTestResultLocationState = { + scoreResult: Record; + matchedResumes: MatchedResume[]; +}; + +const HRTestResult = () => { + const [animate, setAnimate] = useState(true); + + // 컴포넌트 마운트 후 애니메이션은 한 번만 실행 + useEffect(() => { + const timer = setTimeout(() => setAnimate(false), 2000); // 2초 후 애니메이션 끔 + return () => clearTimeout(timer); + }, []); + + const user = useUserStore((state) => state.user); + const userName = user?.name; + + const location = useLocation() as { state: HRTestResultLocationState }; + const { scoreResult, matchedResumes } = location.state || {}; + + const orderedScoreResult: Record = {}; + subjects.forEach((key) => { + if (scoreResult[key] !== undefined) { + orderedScoreResult[key] = scoreResult[key]; + } + }); + + const total = (Object.values(orderedScoreResult) as number[]).reduce( + (sum: number, score: number) => sum + score, + 0, + ); + const averageScore = total / Object.values(orderedScoreResult).length; + + const data = Object.entries(orderedScoreResult).map(([subject, score]) => ({ + subject, + score: Number(score), + averageScore, + })); + + const top3 = Object.entries(orderedScoreResult) + .sort((a, b) => b[1] - a[1]) + .slice(0, 3); + + return ( +
+
+

HR 담당자 성향 분석 결과 리포트

+

+ {userName} 님의 테스트 결과를 기반으로, 현재 선호하는 인재의 역량과 + 스타일을 분석한 보고서입니다.
+ 이는 채용 시 어떤 역량에 집중하고 있는지를 객관적으로 확인하고, 조직에 + 적합한 인재 유형을 파악하는 데 활용할 수 있습니다. +

+
+
+

📌 역량 선호도 차트

+

+ {userName} 님이 선호하는 인재 역량은 다음 7가지로 분류되며, 아래 + 그래프는 그 선호도 분포를 시각화한 것입니다.
+ 해당 차트는 인재 평가 시 중시하는 역량의 우선순위를 시각적으로 + 보여줍니다. +

+
+ + + + + + + + + + +
+

🏅 선호 역량 Top 3

+
    + {top3.map(([key], index) => ( +
  1. + + {index + 1}. {key}{' '} + + + ▶️ + {competencyDescriptions[key]} + +
  2. + ))} +
+
+
+

📄 추천 이력서

+

+ {userName} 님의 성향 분석을 바탕으로 선별된 구직자의 이력서입니다. + 선호하는 인재상에 부합하는 구직자를 확인해보세요. +

+ +
+
+ ); +}; + +export default HRTestResult; diff --git a/src/pages/hr-test/start/Start.module.css b/src/pages/hr-test/start/Start.module.css new file mode 100644 index 0000000..9fef399 --- /dev/null +++ b/src/pages/hr-test/start/Start.module.css @@ -0,0 +1,19 @@ +.startContainer { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + height: calc(100vh - 60px); + padding: 0 16px; + text-align: center; +} + +.title { + font-size: var(--font-size-24); + font-weight: 700; +} + +.text { + margin-block: 32px 60px; + line-height: 28px; +} diff --git a/src/pages/hr-test/start/Start.tsx b/src/pages/hr-test/start/Start.tsx new file mode 100644 index 0000000..5b98c48 --- /dev/null +++ b/src/pages/hr-test/start/Start.tsx @@ -0,0 +1,72 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import useModal from '@/hooks/useModal'; +import useAuthStore from '@/store/auth'; +import Button from '@/components/button/Button'; +import MyModal from '@/components/modal/Modal'; +import styles from './Start.module.css'; +// import useUserStore from '@/store/user'; + +const MODAL_MESSAGES = { + NOT_LOGGED_IN: '기업 로그인 후 이용 가능합니다.', +} as const; + +const HRTestStart = () => { + const navigate = useNavigate(); + const [modalMessage, setModalMessage] = useState(''); + + const { isModalOpen, openModal, closeModal } = useModal(); + + const accessToken = useAuthStore((state) => state.accessToken); + const isLoggedIn = !!accessToken; + // const user = useUserStore((state) => state.user); + // const isBusiness = user?.role === 'COMPANY'; + + const showModal = (message: string) => { + setModalMessage(message); + openModal(); + }; + + const isFormValid = () => { + // if (!isLoggedIn || !isBusiness) { + if (!isLoggedIn) { + showModal(MODAL_MESSAGES.NOT_LOGGED_IN); + return false; + } + return true; + }; + + const handleStart = async (e: React.FormEvent) => { + e.preventDefault(); + + if (!isFormValid()) return; + + navigate('/hr-test/quiz'); + }; + + return ( + <> +
+

HR 담당자 성향 테스트

+

+ 조직에 필요한 핵심 역량, 나의 채용 기준은 무엇일까요? +
나의 HR 업무 성향을 진단하고, 우리 조직에 꼭 맞는 인재를 + 찾아보세요! +

+ +
+ + + {modalMessage} + + + ); +}; + +export default HRTestStart; diff --git a/src/pages/login/Login.module.css b/src/pages/login/Login.module.css new file mode 100644 index 0000000..27ed629 --- /dev/null +++ b/src/pages/login/Login.module.css @@ -0,0 +1,70 @@ +.loginContainer { + padding: 0 16px; + width: 100%; + max-width: 512px; + margin: 0 auto; +} + +.loginTitle { + font-size: var(--font-size-28); + font-weight: 700; + text-align: center; + margin-block: 100px 40px; +} + +.kakaoBtn { + display: flex; + justify-content: center; + align-items: center; + gap: 8px; + width: 100%; + max-width: 480px; + height: 60px; + border-radius: 5px; + background-color: #fee500; + font-size: var(--font-size-18); + font-weight: 700; + color: var(--color-black); + cursor: pointer; + + &:disabled { + background-color: var(--color-gray-b1); + cursor: not-allowed; + } +} + +.or { + display: flex; + align-items: center; + gap: 24px; + margin: 24px 0; + color: var(--color-secondary); + font-size: var(--font-size-14); + + &::before, + &::after { + content: ''; + flex: 1; + height: 1px; + background-color: var(--color-gray-d5); + } +} + +.loginForm { + display: flex; + flex-direction: column; + gap: 16px; +} + +.signupBtn { + display: block; + margin-top: 44px; + text-align: center; + font-size: var(--font-size-16); + color: var(--color-secondary); + cursor: pointer; + + &:hover { + text-decoration: underline; + } +} diff --git a/src/pages/login/Login.tsx b/src/pages/login/Login.tsx new file mode 100644 index 0000000..766ac84 --- /dev/null +++ b/src/pages/login/Login.tsx @@ -0,0 +1,100 @@ +import { useState } from 'react'; +import { Link, useNavigate } from 'react-router-dom'; +import axiosInstance from '@/lib/axiosInstance'; +import useModal from '@/hooks/useModal'; +import useAuthStore from '@/store/auth'; +import useUserStore from '@/store/user'; +import AuthInput from '../../components/authInput/AuthInput'; +import Button from '../../components/button/Button'; +import Icon from '../../components/icon/Icon'; +import MyModal from '@/components/modal/Modal'; +import styles from './Login.module.css'; + +const Login = () => { + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + + const navigate = useNavigate(); + const { isModalOpen, openModal, closeModal } = useModal(); + + const kakaoAuthRequestUrl = `${import.meta.env.VITE_API_BASE_URL}/oauth2/authorization/kakao`; + const socialLoginRedirectUri = `${import.meta.env.VITE_FRONTEND_BASE_URL}/analytics/start`; + + const handleLogin = async (e: React.FormEvent) => { + e.preventDefault(); + + try { + const response = await axiosInstance.post('/api/v1/users/login', { + email, + password, + }); + + const { success, message, data } = response.data; + + if (!success) { + throw new Error(message || '로그인 실패'); + } + + useAuthStore.getState().setAccessToken(data); + + const meRes = await axiosInstance.get('/api/v1/users/me'); + const user = meRes.data.data; + useUserStore.getState().setUser({ id: user.id, name: user.name }); + + navigate('/analytics/start'); + } catch (error) { + openModal(); + console.log(error); + } + }; + + return ( + <> +
+

로그인

+ + + 카카오톡으로 1초만에 시작하기 + + 또는 +
+ setEmail(e.target.value)} + placeholder="이메일" + /> + setPassword(e.target.value)} + placeholder="비밀번호" + /> + + + + 회원가입 + +
+ + 이메일, 비밀번호를 다시 확인해주세요. + + + ); +}; + +export default Login; diff --git a/src/pages/signup/Signup.module.css b/src/pages/signup/Signup.module.css new file mode 100644 index 0000000..5b2c3c0 --- /dev/null +++ b/src/pages/signup/Signup.module.css @@ -0,0 +1,75 @@ +.loginBtn { + display: flex; + align-items: center; + gap: 8px; + margin-top: 24px; + padding: 0 16px; + font-size: var(--font-size-16); +} + +.signupContainer { + padding: 0 16px; + width: 100%; + max-width: 512px; + margin: 0 auto 100px; +} + +.signupTitle { + font-size: var(--font-size-28); + font-weight: 700; + text-align: center; + margin-block: 60px 40px; +} + +.signupForm { + display: flex; + flex-direction: column; + gap: 44px; +} + +.emailContainer, +.passwordContainer { + display: flex; + flex-direction: column; + gap: 12px; +} + +.email, +.authcode { + position: relative; + + button { + padding: 10px; + font-size: var(--font-size-14); + color: var(--color-primary); + cursor: pointer; + + &:disabled { + color: var(--color-gray-b1); + cursor: not-allowed; + } + } +} + +.requestBtn { + position: absolute; + top: 44px; + right: 16px; +} + +.authcodeActions { + position: absolute; + top: 10px; + right: 16px; + + time { + margin-right: 8px; + font-size: var(--font-size-14); + color: var(--color-gray-2); + } +} + +.sending { + font-size: var(--font-size-14); + color: var(--color-success); +} diff --git a/src/pages/signup/Signup.tsx b/src/pages/signup/Signup.tsx new file mode 100644 index 0000000..692bbac --- /dev/null +++ b/src/pages/signup/Signup.tsx @@ -0,0 +1,302 @@ +import { useEffect, useMemo, useState } from 'react'; +import { Link, useNavigate } from 'react-router-dom'; +import useModal from '@/hooks/useModal'; +import axiosInstance from '@/lib/axiosInstance'; +import { + validateConfirmPassword, + validateEmail, + validateName, + validatePassword, +} from '@/utils/validators'; +import formatTime from '@/utils/formatTime'; +import Icon from '@/components/icon/Icon'; +import AuthInput from '../../components/authInput/AuthInput'; +import Button from '../../components/button/Button'; +import MyModal from '@/components/modal/Modal'; +import styles from './Signup.module.css'; + +const AUTH_TIMEOUT = 180; + +const Signup = () => { + const [name, setName] = useState(''); + const [email, setEmail] = useState(''); + const [authCode, setAuthCode] = useState(''); + const [password, setPassword] = useState(''); + const [confirmPassword, setConfirmPassword] = useState(''); + + const [nameErrors, setNameErrors] = useState([]); + const [emailErrors, setEmailErrors] = useState([]); + const [authCodeErrors, setAuthCodeErrors] = useState([]); + const [passwordErrors, setPasswordErrors] = useState([]); + const [confirmPasswordErrors, setConfirmPasswordErrors] = useState( + [], + ); + + const isEmailValid = email.length > 0 && emailErrors.length === 0; + const [isRequested, setIsRequested] = useState(false); + const [isSendingEmail, setIsSendingEmail] = useState(false); + const [isEmailVerified, setIsEmailVerified] = useState(false); + const [timeLeft, setTimeLeft] = useState(0); + + const isFormValid = useMemo(() => { + return ( + name.length > 0 && + email.length > 0 && + password.length > 0 && + confirmPassword.length > 0 && + isEmailVerified && + nameErrors.length === 0 && + emailErrors.length === 0 && + passwordErrors.length === 0 && + confirmPasswordErrors.length === 0 + ); + }, [ + name, + email, + password, + confirmPassword, + nameErrors, + emailErrors, + passwordErrors, + confirmPasswordErrors, + isEmailVerified, + ]); + + const navigate = useNavigate(); + const { isModalOpen, openModal } = useModal(); + + useEffect(() => { + setNameErrors(validateName(name)); + }, [name]); + + useEffect(() => { + const errors = validateEmail(email); + setEmailErrors(errors); + + if (errors.length === 0) { + const delayDebounce = setTimeout(() => { + checkEmailDuplicate(email); + }, 200); // 200ms 디바운스 + return () => clearTimeout(delayDebounce); + } + }, [email]); + + useEffect(() => { + setPasswordErrors(validatePassword(password)); + }, [password]); + + useEffect(() => { + setConfirmPasswordErrors( + validateConfirmPassword(confirmPassword, password), + ); + }, [password, confirmPassword]); + + // 인증코드 타이머 설정 + useEffect(() => { + if (isRequested && timeLeft > 0) { + const timer = setInterval(() => { + setTimeLeft((prev) => prev - 1); + }, 1000); + return () => clearInterval(timer); + } + }, [isRequested, timeLeft]); + + const checkEmailDuplicate = async (email: string) => { + try { + const response = await axiosInstance.get('/api/v1/users/check-email', { + params: { email }, + }); + + const { success, duplicated, message } = response.data; + + if (success) { + if (duplicated) { + setEmailErrors(['이미 가입된 이메일입니다.']); + } else { + setEmailErrors([]); + } + } else { + throw new Error(message || '이메일 중복 확인 실패'); + } + } catch (error) { + if (error instanceof Error) console.log(error); + } + }; + + const requestAuthCode = async () => { + setIsSendingEmail(true); + + try { + const response = await axiosInstance.post('/api/v1/email/join/send', { + email, + }); + + const { message, success } = response.data; + + if (success) { + setIsRequested(true); + setTimeLeft(AUTH_TIMEOUT); + setAuthCode(''); + setAuthCodeErrors([]); + } else { + throw new Error(message || '인증요청 실패'); + } + } catch (error) { + if (error instanceof Error) console.log(error); + } finally { + setIsSendingEmail(false); + } + }; + + const verifyAuthCode = async () => { + try { + const response = await axiosInstance.post('/api/v1/email/verify', { + email, + code: authCode, + }); + + const { message, success } = response.data; + + if (success) { + setIsEmailVerified(true); + setAuthCodeErrors([]); + } else { + throw new Error(message || '인증코드 불일치'); + } + } catch (error) { + setAuthCodeErrors(['인증코드를 다시 확인해주세요.']); + if (error instanceof Error) console.log(error); + } + }; + + const handleSignup = async (e: React.FormEvent) => { + e.preventDefault(); + + try { + const response = await axiosInstance.post( + '/api/v1/users/join', + { name, email, password, passwordConfirm: confirmPassword }, + { withCredentials: true }, + ); + + const { message, success } = response.data; + + if (success) { + openModal(); + } else { + throw new Error(message || '회원가입 실패'); + } + } catch (error) { + if (error instanceof Error) console.log(error); + } + }; + + return ( + <> + + + 로그인하러 가기 + +
+

회원가입

+
+ setName(e.target.value)} + placeholder="이름을 입력해주세요" + label="이름" + errors={nameErrors} + /> +
+
+ setEmail(e.target.value)} + placeholder="이메일을 입력해주세요" + label="이메일" + errors={emailErrors} + /> + +
+ {isSendingEmail && ( +

이메일 전송 중...

+ )} + {isRequested && !isSendingEmail && ( +
+ setAuthCode(e.target.value)} + placeholder="인증코드 6자리를 입력해주세요" + errors={authCodeErrors} + maxLength={6} + /> +
+ {!isEmailVerified && } + +
+
+ )} + {isEmailVerified && ( +

+ 이메일 인증이 성공적으로 완료됐어요. +

+ )} +
+
+ setPassword(e.target.value)} + placeholder="비밀번호를 입력해주세요" + label="비밀번호" + description="영문/숫자/특수문자 중 2가지 이상의 종류를 조합하여 10자 이상의 비밀번호를 입력해주세요." + errors={passwordErrors} + /> + setConfirmPassword(e.target.value)} + placeholder="비밀번호를 한 번 더 입력해주세요" + errors={confirmPasswordErrors} + /> +
+ + +
+ navigate('/login')} + > + 회원가입이 완료되었습니다. +
+ 로그인 후 서비스를 시작해보세요. +
+ + ); +}; + +export default Signup; diff --git a/src/routes/Router.tsx b/src/routes/Router.tsx new file mode 100644 index 0000000..4d77e0e --- /dev/null +++ b/src/routes/Router.tsx @@ -0,0 +1,30 @@ +import { Routes, Route } from 'react-router-dom'; +import Login from '@/pages/login/Login'; +import Signup from '@/pages/signup/Signup'; +import Start from '@/pages/analytics/start/Start'; +import Loading from '@/pages/analytics/loading/Loading'; +import Report from '@/pages/analytics/report/Report'; +import HRTestStart from '@/pages/hr-test/start/Start'; +import HRTestQuiz from '@/pages/hr-test/quiz/Quiz'; +import HRTestLoading from '@/pages/hr-test/loading/Loading'; +import HRTestResult from '@/pages/hr-test/result/Result'; + +const Router = () => { + return ( + + } /> + } /> + + } /> + } /> + } /> + + } /> + } /> + } /> + } /> + + ); +}; + +export default Router; diff --git a/src/store/auth.ts b/src/store/auth.ts new file mode 100644 index 0000000..41fbc20 --- /dev/null +++ b/src/store/auth.ts @@ -0,0 +1,15 @@ +import { create } from 'zustand'; + +interface AuthState { + accessToken: string | null; + setAccessToken: (token: string) => void; + clearAccessToken: () => void; +} + +const useAuthStore = create((set) => ({ + accessToken: null, + setAccessToken: (token) => set({ accessToken: token }), + clearAccessToken: () => set({ accessToken: null }), +})); + +export default useAuthStore; diff --git a/src/store/user.ts b/src/store/user.ts new file mode 100644 index 0000000..ef97fe9 --- /dev/null +++ b/src/store/user.ts @@ -0,0 +1,20 @@ +import { create } from 'zustand'; + +interface User { + id: number; + name: string; +} + +interface UserState { + user: User | null; + setUser: (user: User) => void; + clearUser: () => void; +} + +const useUserStore = create((set) => ({ + user: null, + setUser: (user) => set({ user }), + clearUser: () => set({ user: null }), +})); + +export default useUserStore; diff --git a/src/styles/global.css b/src/styles/global.css new file mode 100644 index 0000000..893b043 --- /dev/null +++ b/src/styles/global.css @@ -0,0 +1,46 @@ +@import './reset.css'; +@import './variables.css'; + +@font-face { + font-family: 'LINESeedKR'; + font-weight: 400; + font-style: normal; + font-display: swap; + src: + url('/public/fonts/LINESeedKR-Rg.woff2') format('woff2'), + url('/public/fonts/LINESeedKR-Rg.woff') format('woff'), + url('/public/fonts/LINESeedKR-Rg.otf') format('opentype'); +} + +@font-face { + font-family: 'LINESeedKR'; + font-weight: 700; + font-style: normal; + font-display: swap; + src: + url('/public/fonts/LINESeedKR-Bd.woff2') format('woff2'), + url('/public/fonts/LINESeedKR-Bd.woff') format('woff'), + url('/public/fonts/LINESeedKR-Bd.otf') format('opentype'); +} + +body { + font-family: 'LINESeedKR', sans-serif; + max-width: 1060px; + margin: 0 auto; + padding-top: 60px; +} + +.a11yHidden { + clip: rect(1px, 1px, 1px, 1px); + clip-path: inset(50%); + width: 1px; + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; +} + +.recharts-wrapper * { + outline: none; +} diff --git a/src/styles/reset.css b/src/styles/reset.css new file mode 100644 index 0000000..8e01fac --- /dev/null +++ b/src/styles/reset.css @@ -0,0 +1,151 @@ +html, +body, +main, +div, +span, +applet, +object, +iframe, +h1, +h2, +h3, +h4, +h5, +h6, +p, +blockquote, +pre, +a, +abbr, +acronym, +address, +big, +cite, +code, +del, +dfn, +em, +img, +ins, +kbd, +q, +s, +samp, +small, +strike, +strong, +sub, +sup, +tt, +var, +b, +u, +i, +center, +dl, +dt, +dd, +ol, +ul, +li, +fieldset, +form, +label, +legend, +table, +caption, +tbody, +tfoot, +thead, +tr, +th, +td, +article, +aside, +canvas, +details, +embed, +figure, +figcaption, +footer, +header, +hgroup, +menu, +nav, +output, +ruby, +section, +summary, +time, +mark, +audio, +video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font-weight: normal; + vertical-align: baseline; +} + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +menu, +nav, +section { + display: block; +} + +ol, +ul { + list-style: none; +} + +blockquote, +q { + quotes: none; +} + +blockquote:before, +blockquote:after, +q:before, +q:after { + content: ''; + content: none; +} + +table { + border-collapse: collapse; + border-spacing: 0; +} + +html { + font-size: 16px; + box-sizing: border-box; +} +*, +*::before, +*::after { + box-sizing: inherit; +} + +button, +input, +select { + margin: 0; + padding: 0; + border: 0; + background-color: transparent; + font-family: inherit; +} + +a { + color: inherit; + text-decoration: none; +} diff --git a/src/styles/variables.css b/src/styles/variables.css new file mode 100644 index 0000000..c7b695f --- /dev/null +++ b/src/styles/variables.css @@ -0,0 +1,38 @@ +:root { + /* Primary */ + --color-primary: #5f812e; + --color-secondary: #757575; + --color-tertiary: #fafaf8; + + /* Semantic */ + --color-success: #26ae6d; + --color-warn: #f8be2b; + --color-error: #e7442e; + + /* Black & White */ + --color-black: #000; + --color-gray-2: #222; + --color-gray-46: #464646; + --color-gray-76: #767676; + --color-gray-b1: #b1b1b1; + --color-gray-d5: #d5d5d5; + --color-gray-ef: #efefef; + --color-white: #fff; + + /* font-size */ + --font-size-12: 0.75rem; + --font-size-13: 0.8125rem; + --font-size-14: 0.875rem; + --font-size-16: 1rem; + --font-size-18: 1.125rem; + --font-size-20: 1.25rem; + --font-size-24: 1.5rem; + --font-size-28: 1.75rem; + --font-size-30: 1.875rem; + --font-size-32: 2rem; + --font-size-36: 2.25rem; + + /* size */ + --size-header: 60px; + --size-max-width: 1060px; +} diff --git a/src/utils/formatTime.ts b/src/utils/formatTime.ts new file mode 100644 index 0000000..d700563 --- /dev/null +++ b/src/utils/formatTime.ts @@ -0,0 +1,7 @@ +const formatTime = (seconds: number) => { + const min = Math.floor(seconds / 60); + const sec = seconds % 60; + return `${min}:${sec < 10 ? '0' : ''}${sec}`; +}; + +export default formatTime; diff --git a/src/utils/generatePDF.ts b/src/utils/generatePDF.ts new file mode 100644 index 0000000..7d255cd --- /dev/null +++ b/src/utils/generatePDF.ts @@ -0,0 +1,113 @@ +import jsPDF from 'jspdf'; +import html2canvas from 'html2canvas'; + +// === PDF 설정 상수 === +const A4_WIDTH_MM = 210; +const A4_HEIGHT_MM = 297; +const MARGIN_MM = 20; +const TOP_MARGIN_MM = 20; +const BOTTOM_MARGIN_MM = 30; +const CONTENT_WIDTH_MM = A4_WIDTH_MM - MARGIN_MM * 2; // 170mm +const CONTENT_HEIGHT_MM = A4_HEIGHT_MM - TOP_MARGIN_MM - BOTTOM_MARGIN_MM; // 247mm +const PX_PER_MM = 96 / 25.4; // 약 3.78px/mm +const SCALE = 3; + +// === DOM 클론 생성 === +const createClonedElement = (src: HTMLElement): HTMLElement => { + const wrapper = document.createElement('div'); + wrapper.style.position = 'fixed'; + wrapper.style.left = '-100000px'; + wrapper.style.top = '0'; + wrapper.style.overflow = 'hidden'; + + const clone = src.cloneNode(true) as HTMLElement; + const contentWidthPx = Math.round(CONTENT_WIDTH_MM * PX_PER_MM); + clone.style.width = `${contentWidthPx}px`; + + wrapper.appendChild(clone); + document.body.appendChild(wrapper); + + return wrapper; +}; + +// === 페이지 단위 캔버스 자르기 === +const getCanvasSlice = ( + canvas: HTMLCanvasElement, + srcY: number, + sliceHeightPx: number, +): HTMLCanvasElement => { + const sliceCanvas = document.createElement('canvas'); + sliceCanvas.width = canvas.width; + sliceCanvas.height = sliceHeightPx; + + const ctx = sliceCanvas.getContext('2d')!; + ctx.drawImage( + canvas, + 0, + srcY, + canvas.width, + sliceHeightPx, + 0, + 0, + canvas.width, + sliceHeightPx, + ); + + return sliceCanvas; +}; + +// === PDF 생성 함수 === +const generatePDF = async () => { + const src = document.getElementById('pdf-section'); + if (!src) return; + + const wrapper = createClonedElement(src); + const clone = wrapper.firstElementChild as HTMLElement; + + const canvas = await html2canvas(clone, { + scale: SCALE, + useCORS: true, + }); + + const pxPerMm = canvas.width / CONTENT_WIDTH_MM; + const pageHeightPx = Math.floor(CONTENT_HEIGHT_MM * pxPerMm); + const totalPages = Math.ceil(canvas.height / pageHeightPx); + + const pdf = new jsPDF('p', 'mm', 'a4'); + + for (let i = 0; i < totalPages; i++) { + if (i !== 0) pdf.addPage(); + + const srcY = i * pageHeightPx; + const sliceHeightPx = Math.min(pageHeightPx, canvas.height - srcY); + const sliceHeightMm = sliceHeightPx / pxPerMm; + + const sliceCanvas = getCanvasSlice(canvas, srcY, sliceHeightPx); + const imgData = sliceCanvas.toDataURL('image/png'); + + pdf.addImage( + imgData, + 'PNG', + MARGIN_MM, + TOP_MARGIN_MM, + CONTENT_WIDTH_MM, + sliceHeightMm, + ); + } + + document.body.removeChild(wrapper); + return pdf; +}; + +export const generatePDFBlob = async () => { + const pdf = await generatePDF(); + if (!pdf) return; + return pdf.output('blob'); +}; + +export const downloadPDF = async () => { + const pdf = await generatePDF(); + if (!pdf) return; + + pdf.save(`download.pdf`); +}; diff --git a/src/utils/validators.ts b/src/utils/validators.ts new file mode 100644 index 0000000..bf3f0bb --- /dev/null +++ b/src/utils/validators.ts @@ -0,0 +1,72 @@ +const validateName = (value: string): string[] => { + const errors: string[] = []; + + if (value.length === 0) return errors; + + if (/[^a-zA-Zㄱ-ㅎ가-힣\s]/.test(value)) { + errors.push('이름은 한글 또는 영문만 입력해주세요.'); + } + + if (value.length < 2) { + errors.push('이름은 2자 이상 입력해주세요.'); + } else if (value.length > 10) { + errors.push('이름은 10자 이하로 입력해주세요.'); + } + + return errors; +}; + +const validateEmail = (value: string): string[] => { + const errors: string[] = []; + + if (value.length === 0) return errors; + + if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) { + errors.push('이메일 양식을 확인해주세요.'); + } + + return errors; +}; + +const validatePassword = (value: string): string[] => { + const errors: string[] = []; + + if (value.length === 0) return errors; + + if (value.length < 10) { + errors.push('10자 이상 입력해주세요.'); + } + + const hasLetter = /[a-zA-Z]/.test(value); + const hasNumber = /[0-9]/.test(value); + const hasSpecial = /[^a-zA-Z0-9]/.test(value); + + const typesIncluded = [hasLetter, hasNumber, hasSpecial].filter( + Boolean, + ).length; + + if (typesIncluded < 2) { + errors.push('영문/숫자/특수문자 중 2가지 이상을 조합해주세요.'); + } + + return errors; +}; + +const validateConfirmPassword = (value: string, password: string): string[] => { + const errors: string[] = []; + + if (value.length === 0) return errors; + + if (value !== password) { + errors.push('비밀번호가 일치하지 않습니다.'); + } + + return errors; +}; + +export { + validateName, + validateEmail, + validatePassword, + validateConfirmPassword, +}; diff --git a/tsconfig.app.json b/tsconfig.app.json index 227a6c6..af0f347 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -1,4 +1,5 @@ { + "extends": "./tsconfig.json", "compilerOptions": { "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", "target": "ES2022", @@ -21,7 +22,12 @@ "noUnusedParameters": true, "erasableSyntaxOnly": true, "noFallthroughCasesInSwitch": true, - "noUncheckedSideEffectImports": true + "noUncheckedSideEffectImports": true, + + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + } }, "include": ["src"] } diff --git a/tsconfig.json b/tsconfig.json index 1ffef60..a8d3cb5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,5 +3,11 @@ "references": [ { "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" } - ] + ], + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + } + } } diff --git a/vite.config.ts b/vite.config.ts index 8b0f57b..b7b70f8 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,7 +1,13 @@ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' +import path from 'path' // https://vite.dev/config/ export default defineConfig({ plugins: [react()], + resolve: { + alias: { + '@': path.resolve(__dirname, 'src'), + }, + }, })