diff --git a/docker-compose.yml b/docker-compose.yml index 79ae2ab3..8ffb7cb3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -35,6 +35,7 @@ volumes: model_storage: log_storage: nominatim_data: + frontend_node_modules: # ================================================================ @@ -1063,7 +1064,7 @@ services: ipv4_address: 172.20.0.110 volumes: - ./frontend:/app - - /app/node_modules # Exclude node_modules from bind mount + - frontend_node_modules:/app/node_modules # Use named volume for node_modules healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/"] interval: 30s diff --git a/frontend/.env.development b/fdev-ffrontend/.env.development similarity index 100% rename from frontend/.env.development rename to fdev-ffrontend/.env.development diff --git a/frontend/.env.production b/fdev-ffrontend/.env.production similarity index 100% rename from frontend/.env.production rename to fdev-ffrontend/.env.production diff --git a/fdev-ffrontend/Dockerfile.development b/fdev-ffrontend/Dockerfile.development new file mode 100644 index 00000000..6069bda8 --- /dev/null +++ b/fdev-ffrontend/Dockerfile.development @@ -0,0 +1,38 @@ +# frontend/Dockerfile.development - FIXED VERSION +FROM node:18-alpine + +# Install curl for healthchecks +RUN apk add --no-cache curl + +# Set working directory +WORKDIR /app + +# Create non-root user for security but don't switch yet +RUN addgroup -g 1001 -S nodejs && \ + adduser -S reactjs -u 1001 -G nodejs + +# Copy package files first (better caching) +COPY package*.json ./ + +# Install all dependencies (including dev dependencies) as root +RUN npm ci --verbose && \ + npm cache clean --force + +# Copy source code +COPY . . + +# Change ownership of all files to the non-root user +RUN chown -R reactjs:nodejs /app + +# Now switch to non-root user +USER reactjs + +# Expose port 3000 (Vite default) +EXPOSE 3000 + +# Add healthcheck +HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \ + CMD curl -f http://localhost:3000/ || exit 1 + +# Start development server with host binding +CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"] \ No newline at end of file diff --git a/fdev-ffrontend/Dockerfile.production b/fdev-ffrontend/Dockerfile.production new file mode 100644 index 00000000..98ba917c --- /dev/null +++ b/fdev-ffrontend/Dockerfile.production @@ -0,0 +1,41 @@ +# frontend/Dockerfile.production +# Multi-stage build for production + +# Build stage +FROM node:18-alpine as builder + +WORKDIR /app + +# Copy package files +COPY package*.json ./ + +# Install dependencies +RUN npm ci --only=production + +# Copy source code +COPY . . + +# Build the application +RUN npm run build + +# Production stage +FROM nginx:alpine + +# Install curl for healthchecks +RUN apk add --no-cache curl + +# Copy built app from builder stage +COPY --from=builder /app/dist /usr/share/nginx/html + +# Copy nginx configuration +COPY nginx.conf /etc/nginx/nginx.conf + +# Expose port 80 +EXPOSE 80 + +# Add healthcheck +HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \ + CMD curl -f http://localhost/ || exit 1 + +# Start nginx +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/fdev-ffrontend/index.html b/fdev-ffrontend/index.html new file mode 100644 index 00000000..66ef8f1b --- /dev/null +++ b/fdev-ffrontend/index.html @@ -0,0 +1,170 @@ + + + + + + + + PanIA - Inteligencia Artificial para tu Panadería en Madrid + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
🥖
+
Cargando PanIA...
+
+
+ + +
+ + + + + + + + \ No newline at end of file diff --git a/fdev-ffrontend/nginx.conf b/fdev-ffrontend/nginx.conf new file mode 100644 index 00000000..149fec06 --- /dev/null +++ b/fdev-ffrontend/nginx.conf @@ -0,0 +1,70 @@ +# frontend/nginx.conf +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # Enable gzip compression + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_proxied any; + gzip_comp_level 6; + gzip_types + text/plain + text/css + text/xml + text/javascript + application/json + application/javascript + application/xml+rss + application/atom+xml + image/svg+xml; + + server { + listen 80; + server_name localhost; + root /usr/share/nginx/html; + index index.html; + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + + # Handle React Router + location / { + try_files $uri $uri/ /index.html; + } + + # API proxy to backend + location /api/ { + proxy_pass http://bakery-gateway:8000/api/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_cache_bypass $http_upgrade; + } + + # Cache static assets + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # Health check endpoint + location /health { + access_log off; + return 200 "healthy\n"; + add_header Content-Type text/plain; + } + } +} \ No newline at end of file diff --git a/fdev-ffrontend/package-lock.json b/fdev-ffrontend/package-lock.json new file mode 100644 index 00000000..52888e6f --- /dev/null +++ b/fdev-ffrontend/package-lock.json @@ -0,0 +1,7652 @@ +{ + "name": "pania-frontend", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "pania-frontend", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "@hookform/resolvers": "^3.3.1", + "@reduxjs/toolkit": "^1.9.5", + "@stripe/react-stripe-js": "^3.9.0", + "@stripe/stripe-js": "^7.8.0", + "clsx": "^2.0.0", + "date-fns": "^2.30.0", + "date-fns-tz": "^2.0.0", + "i18next": "^23.4.4", + "i18next-browser-languagedetector": "^7.1.0", + "lucide-react": "^0.263.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-hook-form": "^7.45.4", + "react-hot-toast": "^2.4.1", + "react-i18next": "^13.1.2", + "react-redux": "^8.1.2", + "react-router-dom": "^6.15.0", + "recharts": "^2.8.0", + "tailwind-merge": "^1.14.0", + "zod": "^3.22.2" + }, + "devDependencies": { + "@tailwindcss/forms": "^0.5.4", + "@tailwindcss/typography": "^0.5.9", + "@testing-library/jest-dom": "^5.17.0", + "@testing-library/react": "^13.4.0", + "@testing-library/user-event": "^14.4.3", + "@types/react": "^18.2.15", + "@types/react-dom": "^18.2.7", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "@vitejs/plugin-react": "^4.0.3", + "@vitest/ui": "^0.34.1", + "autoprefixer": "^10.4.14", + "eslint": "^8.45.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.3", + "postcss": "^8.4.27", + "tailwindcss": "^3.3.0", + "typescript": "^5.0.2", + "vite": "^4.4.5", + "vitest": "^0.34.1" + } + }, + "node_modules/@adobe/css-tools": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.3.tgz", + "integrity": "sha512-VQKMkwriZbaOgVCby1UDY/LDk5fIjhQicCvVPFqfe+69fWaPWydbWJ3wRt59/YzIwda1I81loas3oCoHxnqvdA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", + "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.6", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", + "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.0", + "@babel/types": "^7.28.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.2.tgz", + "integrity": "sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", + "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.2.tgz", + "integrity": "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", + "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@hookform/resolvers": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.10.0.tgz", + "integrity": "sha512-79Dv+3mDF7i+2ajj7SkypSKHhl1cbln1OGavqrsF7p6mbUv11xpqpacPsGDCTRvCSjEEIez2ef1NveSVL3b0Ag==", + "license": "MIT", + "peerDependencies": { + "react-hook-form": "^7.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@jest/diff-sequences": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", + "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.0.5.tgz", + "integrity": "sha512-F3lmTT7CXWYywoVUGTCmom0vXq3HTTkaZyTAzIy+bXSBizB7o5qzlC9VCtq0arOa8GqmNsbg/cE9C6HLn7Szew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/get-type": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.0.1.tgz", + "integrity": "sha512-AyYdemXCptSRFirI5EPazNxyPwAL0jXt3zceFjaj8NFiKP9pOi0bfXonf6qkf82z2t3QWPeLCWWw4stPBzctLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/pattern": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", + "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-regex-util": "30.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/schemas": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", + "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/types": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.0.5.tgz", + "integrity": "sha512-aREYa3aku9SSnea4aX6bhKn4bgv3AXkgijoQgbYV3yvbiGt6z+MQ85+6mIhx9DsKW2BuB/cLR/A+tcMThx+KLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/pattern": "30.0.1", + "@jest/schemas": "30.0.5", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/types/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@reduxjs/toolkit": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.7.tgz", + "integrity": "sha512-t7v8ZPxhhKgOKtU+uyJT13lu4vL7az5aFi4IdoDs/eS548edn2M8Ik9h8fxgvMjGoAUVFSt6ZC1P5cWmQ014QQ==", + "license": "MIT", + "dependencies": { + "immer": "^9.0.21", + "redux": "^4.2.1", + "redux-thunk": "^2.4.2", + "reselect": "^4.1.8" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18", + "react-redux": "^7.2.1 || ^8.0.2" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, + "node_modules/@remix-run/router": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz", + "integrity": "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinclair/typebox": { + "version": "0.34.38", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.38.tgz", + "integrity": "sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@stripe/react-stripe-js": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-3.9.0.tgz", + "integrity": "sha512-pN1Re7zUc3m61FFQROok685g3zsBQRzCmZDmTzO8iPU6zhLvu2JnC0LrG0FCzSp6kgGa8AQSzq4rpFSgyhkjKg==", + "license": "MIT", + "dependencies": { + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "@stripe/stripe-js": ">=1.44.1 <8.0.0", + "react": ">=16.8.0 <20.0.0", + "react-dom": ">=16.8.0 <20.0.0" + } + }, + "node_modules/@stripe/stripe-js": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-7.8.0.tgz", + "integrity": "sha512-DNXRfYUgkZlrniQORbA/wH8CdFRhiBSE0R56gYU0V5vvpJ9WZwvGrz9tBAZmfq2aTgw6SK7mNpmTizGzLWVezw==", + "license": "MIT", + "engines": { + "node": ">=12.16" + } + }, + "node_modules/@tailwindcss/forms": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.10.tgz", + "integrity": "sha512-utI1ONF6uf/pPNO68kmN1b8rEwNXv3czukalo8VtJH8ksIkZXr3Q3VYudZLkCsDd4Wku120uF02hYK25XGPorw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mini-svg-data-uri": "^1.2.3" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1" + } + }, + "node_modules/@tailwindcss/typography": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.16.tgz", + "integrity": "sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.castarray": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "postcss-selector-parser": "6.0.10" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" + } + }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz", + "integrity": "sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "^4.0.1", + "@babel/runtime": "^7.9.2", + "@types/testing-library__jest-dom": "^5.9.1", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.5.6", + "lodash": "^4.17.15", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=8", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/react": { + "version": "13.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.4.0.tgz", + "integrity": "sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "@testing-library/dom": "^8.5.0", + "@types/react-dom": "^18.0.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@testing-library/react/node_modules/@testing-library/dom": { + "version": "8.20.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.1.tgz", + "integrity": "sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.1.3", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@testing-library/react/node_modules/aria-query": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "deep-equal": "^2.0.5" + } + }, + "node_modules/@testing-library/react/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@testing-library/user-event": { + "version": "14.6.1", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", + "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/chai": { + "version": "4.3.20", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.20.tgz", + "integrity": "sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai-subset": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.6.tgz", + "integrity": "sha512-m8lERkkQj+uek18hXOZuec3W/fCRTrU4hrnXjH3qhHy96ytuPaPiWGgu7sJb7tZxZonO75vYAjCvpe/e4VUwRw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/chai": "<5.2.0" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", + "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.7.tgz", + "integrity": "sha512-PQTyIulDkIDro8P+IHbKCsw7U2xxBYflVzW/FgWdCAePD9xGSidgA76/GeJ6lBKoblyhf9pBY763gbrN+1dI8g==", + "license": "MIT", + "dependencies": { + "hoist-non-react-statics": "^3.3.0" + }, + "peerDependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "30.0.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz", + "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^30.0.0", + "pretty-format": "^30.0.0" + } + }, + "node_modules/@types/jest/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@types/jest/node_modules/pretty-format": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", + "integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@types/jest/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.2.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.1.tgz", + "integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.10.0" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.23", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.23.tgz", + "integrity": "sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==", + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "devOptional": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@types/semver": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/testing-library__jest-dom": { + "version": "5.14.9", + "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz", + "integrity": "sha512-FSYhIjFlfOpGSRyVoMBMuS3ws5ehFQODymf3vlI7U1K8c7PHwWwFY7VREfmsuzHSOnoKs/9/Y983ayOs7eRzqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/jest": "*" + } + }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", + "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==", + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/@vitest/expect": { + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.34.6.tgz", + "integrity": "sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "0.34.6", + "@vitest/utils": "0.34.6", + "chai": "^4.3.10" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/expect/node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@vitest/expect/node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/expect/node_modules/@vitest/utils": { + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.34.6.tgz", + "integrity": "sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "diff-sequences": "^29.4.3", + "loupe": "^2.3.6", + "pretty-format": "^29.5.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/expect/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@vitest/expect/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@vitest/expect/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/runner": { + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.34.6.tgz", + "integrity": "sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "0.34.6", + "p-limit": "^4.0.0", + "pathe": "^1.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner/node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@vitest/runner/node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/runner/node_modules/@vitest/utils": { + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.34.6.tgz", + "integrity": "sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "diff-sequences": "^29.4.3", + "loupe": "^2.3.6", + "pretty-format": "^29.5.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@vitest/runner/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vitest/runner/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@vitest/runner/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/runner/node_modules/yocto-queue": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz", + "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vitest/snapshot": { + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.34.6.tgz", + "integrity": "sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.1", + "pathe": "^1.1.1", + "pretty-format": "^29.5.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot/node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@vitest/snapshot/node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/snapshot/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@vitest/snapshot/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@vitest/snapshot/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/spy": { + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.34.6.tgz", + "integrity": "sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^2.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/ui": { + "version": "0.34.7", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-0.34.7.tgz", + "integrity": "sha512-iizUu9R5Rsvsq8FtdJ0suMqEfIsIIzziqnasMHe4VH8vG+FnZSA3UAtCHx6rLeRupIFVAVg7bptMmuvMcsn8WQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "0.34.7", + "fast-glob": "^3.3.0", + "fflate": "^0.8.0", + "flatted": "^3.2.7", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "sirv": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": ">=0.30.1 <1" + } + }, + "node_modules/@vitest/utils": { + "version": "0.34.7", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.34.7.tgz", + "integrity": "sha512-ziAavQLpCYS9sLOorGrFFKmy2gnfiNU0ZJ15TsMz/K92NAPS/rp9K4z6AJQQk5Y8adCy4Iwpxy7pQumQ/psnRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "diff-sequences": "^29.4.3", + "loupe": "^2.3.6", + "pretty-format": "^29.5.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils/node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@vitest/utils/node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/utils/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@vitest/utils/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@vitest/utils/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.25.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.2.tgz", + "integrity": "sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001733", + "electron-to-chromium": "^1.5.199", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001734", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001734.tgz", + "integrity": "sha512-uhE1Ye5vgqju6OI71HTQqcBCZrvHugk0MjLak7Q+HfoBgoq5Bi+5YnwjP4fjDgrtYr/l8MVRBvzz9dPD4KyK0A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chai": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ci-info": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.0.tgz", + "integrity": "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "node_modules/date-fns-tz": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-2.0.1.tgz", + "integrity": "sha512-fJCG3Pwx8HUoLhkepdsP7Z5RsucUi+ZBOxyM5d0ZZ6c4SdYustq0VMmOu6Wf7bli+yS/Jwp91TOCqn9jMcVrUA==", + "license": "MIT", + "peerDependencies": { + "date-fns": "2.x" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", + "license": "MIT" + }, + "node_modules/deep-eql": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-equal": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", + "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.5", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.2", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT" + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.199", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.199.tgz", + "integrity": "sha512-3gl0S7zQd88kCAZRO/DnxtBKuhMO4h0EaQIN3YgZfV6+pW+5+bf2AdQeHNESCoaQqo/gjGVYEf2YM4O5HJQqpQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", + "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.20", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.20.tgz", + "integrity": "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/expect": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.0.5.tgz", + "integrity": "sha512-P0te2pt+hHI5qLJkIR+iMvS+lYUZml8rKKsohVHAGY+uClp9XVbdyYNJOIjSRpHVp8s8YqxJCiHUkSYZGr8rtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "30.0.5", + "@jest/get-type": "30.0.1", + "jest-matcher-utils": "30.0.5", + "jest-message-util": "30.0.5", + "jest-mock": "30.0.5", + "jest-util": "30.0.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-equals": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.2.2.tgz", + "integrity": "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true, + "license": "MIT" + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/goober": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.16.tgz", + "integrity": "sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==", + "license": "MIT", + "peerDependencies": { + "csstype": "^3.0.10" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "license": "MIT", + "dependencies": { + "void-elements": "3.1.0" + } + }, + "node_modules/i18next": { + "version": "23.16.8", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.16.8.tgz", + "integrity": "sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, + "node_modules/i18next-browser-languagedetector": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.2.2.tgz", + "integrity": "sha512-6b7r75uIJDWCcCflmbof+sJ94k9UQO4X0YR62oUfqGI/GjCLVzlCwu8TFdRZIqVLzWbzNcmkmhfqKEr4TLz4HQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jest-diff": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.0.5.tgz", + "integrity": "sha512-1UIqE9PoEKaHcIKvq2vbibrCog4Y8G0zmOxgQUVEiTqwR5hJVMCoDsN1vFvI5JvwD37hjueZ1C4l2FyGnfpE0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/diff-sequences": "30.0.1", + "@jest/get-type": "30.0.1", + "chalk": "^4.1.2", + "pretty-format": "30.0.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-diff/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/pretty-format": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", + "integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-diff/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-matcher-utils": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.0.5.tgz", + "integrity": "sha512-uQgGWt7GOrRLP1P7IwNWwK1WAQbq+m//ZY0yXygyfWp0rJlksMSLQAA4wYQC3b6wl3zfnchyTx+k3HZ5aPtCbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.0.1", + "chalk": "^4.1.2", + "jest-diff": "30.0.5", + "pretty-format": "30.0.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/pretty-format": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", + "integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-message-util": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.0.5.tgz", + "integrity": "sha512-NAiDOhsK3V7RU0Aa/HnrQo+E4JlbarbmI3q6Pi4KcxicdtjV82gcIUrejOtczChtVQR4kddu1E1EJlW6EN9IyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.0.5", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "micromatch": "^4.0.8", + "pretty-format": "30.0.5", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-message-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/pretty-format": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", + "integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-message-util/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-mock": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.0.5.tgz", + "integrity": "sha512-Od7TyasAAQX/6S+QCbN6vZoWOMwlTtzzGuxJku1GhGanAjz9y+QsQkpScDmETvdc9aSXyJ/Op4rhpMYBWW91wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.0.5", + "@types/node": "*", + "jest-util": "30.0.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-regex-util": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", + "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.0.5.tgz", + "integrity": "sha512-pvyPWssDZR0FlfMxCBoc0tvM8iUEskaRFALUtGQYzVEAqisAztmy+R8LnU14KT4XA0H/a5HMVTXat1jLne010g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.0.5", + "@types/node": "*", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/local-pkg": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz", + "integrity": "sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.castarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", + "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "0.263.1", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.263.1.tgz", + "integrity": "sha512-keqxAx97PlaEN89PXZ6ki1N8nRjGWtDa4021GFYLNj0RgruM5odbpl8GHTExj0hhPq3sF6Up0gnxt6TSHu+ovw==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/mini-svg-data-uri": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", + "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", + "dev": true, + "license": "MIT", + "bin": { + "mini-svg-data-uri": "cli.js" + } + }, + "node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mlly": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz", + "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.14.0", + "pathe": "^2.0.1", + "pkg-types": "^1.3.0", + "ufo": "^1.5.4" + } + }, + "node_modules/mlly/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/pkg-types/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-nested/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-hook-form": { + "version": "7.62.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.62.0.tgz", + "integrity": "sha512-7KWFejc98xqG/F4bAxpL41NB3o1nnvQO1RWZT3TqRZYL8RryQETGfEdVnJN2fy1crCiBLLjkRBVK05j24FxJGA==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, + "node_modules/react-hot-toast": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.5.2.tgz", + "integrity": "sha512-Tun3BbCxzmXXM7C+NI4qiv6lT0uwGh4oAfeJyNOjYUejTsm35mK9iCaYLGv8cBz9L5YxZLx/2ii7zsIwPtPUdw==", + "license": "MIT", + "dependencies": { + "csstype": "^3.1.3", + "goober": "^2.1.16" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, + "node_modules/react-i18next": { + "version": "13.5.0", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-13.5.0.tgz", + "integrity": "sha512-CFJ5NDGJ2MUyBohEHxljOq/39NQ972rh1ajnadG9BjTk+UXbHLq4z5DKEbEQBDoIhUmmbuS/fIMJKo6VOax1HA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.22.5", + "html-parse-stringify": "^3.0.1" + }, + "peerDependencies": { + "i18next": ">= 23.2.3", + "react": ">= 16.8.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT" + }, + "node_modules/react-redux": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz", + "integrity": "sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.1", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/use-sync-external-store": "^0.0.3", + "hoist-non-react-statics": "^3.3.2", + "react-is": "^18.0.0", + "use-sync-external-store": "^1.0.0" + }, + "peerDependencies": { + "@types/react": "^16.8 || ^17.0 || ^18.0", + "@types/react-dom": "^16.8 || ^17.0 || ^18.0", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0", + "react-native": ">=0.59", + "redux": "^4 || ^5.0.0-beta.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "node_modules/react-redux/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "6.30.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.1.tgz", + "integrity": "sha512-X1m21aEmxGXqENEPG3T6u0Th7g0aS4ZmoNynhbs+Cn+q+QGTLt+d5IQ2bHAXKzKcxGJjxACpVbnYQSCRcfxHlQ==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.30.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.1.tgz", + "integrity": "sha512-llKsgOkZdbPU1Eg3zK8lCn+sjD9wMRZZPuzmdWWX5SUs8OFkN5HnFVC0u5KMeMaC9aoancFI/KoLuKPqN+hxHw==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.0", + "react-router": "6.30.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/react-smooth": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz", + "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==", + "license": "MIT", + "dependencies": { + "fast-equals": "^5.0.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/recharts": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz", + "integrity": "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==", + "license": "MIT", + "dependencies": { + "clsx": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.21", + "react-is": "^18.3.1", + "react-smooth": "^4.0.4", + "recharts-scale": "^0.4.4", + "tiny-invariant": "^1.3.1", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "license": "MIT", + "dependencies": { + "decimal.js-light": "^2.4.1" + } + }, + "node_modules/recharts/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, + "node_modules/redux-thunk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", + "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", + "license": "MIT", + "peerDependencies": { + "redux": "^4" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reselect": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", + "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==", + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "3.29.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz", + "integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==", + "dev": true, + "license": "MIT", + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sirv": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", + "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", + "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", + "dev": true, + "license": "MIT" + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-literal": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-1.3.0.tgz", + "integrity": "sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sucrase/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwind-merge": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-1.14.0.tgz", + "integrity": "sha512-3mFKyCo/MBcgyOTlrY8T7odzZFx+w+qKSMAmdFzRvqBfLlSigU6TZnlFHK0lkMwj9Bj8OYU+9yW9lmGuS0QEnQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinypool": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.7.0.tgz", + "integrity": "sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", + "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", + "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/victory-vendor": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", + "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, + "node_modules/vite": { + "version": "4.5.14", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.14.tgz", + "integrity": "sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.18.10", + "postcss": "^8.4.27", + "rollup": "^3.27.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "@types/node": ">= 14", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.34.6.tgz", + "integrity": "sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "mlly": "^1.4.0", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": ">=v14.18.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.34.6.tgz", + "integrity": "sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^4.3.5", + "@types/chai-subset": "^1.3.3", + "@types/node": "*", + "@vitest/expect": "0.34.6", + "@vitest/runner": "0.34.6", + "@vitest/snapshot": "0.34.6", + "@vitest/spy": "0.34.6", + "@vitest/utils": "0.34.6", + "acorn": "^8.9.0", + "acorn-walk": "^8.2.0", + "cac": "^6.7.14", + "chai": "^4.3.10", + "debug": "^4.3.4", + "local-pkg": "^0.4.3", + "magic-string": "^0.30.1", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.3.3", + "strip-literal": "^1.0.1", + "tinybench": "^2.5.0", + "tinypool": "^0.7.0", + "vite": "^3.1.0 || ^4.0.0 || ^5.0.0-0", + "vite-node": "0.34.6", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": ">=v14.18.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@vitest/browser": "*", + "@vitest/ui": "*", + "happy-dom": "*", + "jsdom": "*", + "playwright": "*", + "safaridriver": "*", + "webdriverio": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "playwright": { + "optional": true + }, + "safaridriver": { + "optional": true + }, + "webdriverio": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/vitest/node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/vitest/node_modules/@vitest/utils": { + "version": "0.34.6", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.34.6.tgz", + "integrity": "sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "diff-sequences": "^29.4.3", + "loupe": "^2.3.6", + "pretty-format": "^29.5.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/vitest/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/vitest/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/fdev-ffrontend/package.json b/fdev-ffrontend/package.json new file mode 100644 index 00000000..76b7241c --- /dev/null +++ b/fdev-ffrontend/package.json @@ -0,0 +1,75 @@ +{ + "name": "pania-frontend", + "version": "1.0.0", + "description": "AI-powered bakery demand forecasting platform for Madrid", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", + "test": "vitest", + "test:ui": "vitest --ui", + "test:coverage": "vitest --coverage", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "lint:fix": "eslint . --ext ts,tsx --fix" + }, + "dependencies": { + "@hookform/resolvers": "^3.3.1", + "@reduxjs/toolkit": "^1.9.5", + "@stripe/react-stripe-js": "^3.9.0", + "@stripe/stripe-js": "^7.8.0", + "clsx": "^2.0.0", + "date-fns": "^2.30.0", + "date-fns-tz": "^2.0.0", + "i18next": "^23.4.4", + "i18next-browser-languagedetector": "^7.1.0", + "lucide-react": "^0.263.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-hook-form": "^7.45.4", + "react-hot-toast": "^2.4.1", + "react-i18next": "^13.1.2", + "react-redux": "^8.1.2", + "react-router-dom": "^6.15.0", + "recharts": "^2.8.0", + "tailwind-merge": "^1.14.0", + "zod": "^3.22.2" + }, + "devDependencies": { + "@tailwindcss/forms": "^0.5.4", + "@tailwindcss/typography": "^0.5.9", + "@testing-library/jest-dom": "^5.17.0", + "@testing-library/react": "^13.4.0", + "@testing-library/user-event": "^14.4.3", + "@types/react": "^18.2.15", + "@types/react-dom": "^18.2.7", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "@vitejs/plugin-react": "^4.0.3", + "@vitest/ui": "^0.34.1", + "autoprefixer": "^10.4.14", + "eslint": "^8.45.0", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.3", + "postcss": "^8.4.27", + "tailwindcss": "^3.3.0", + "typescript": "^5.0.2", + "vite": "^4.4.5", + "vitest": "^0.34.1" + }, + "keywords": [ + "bakery", + "forecasting", + "ai", + "madrid", + "react", + "typescript", + "tailwind" + ], + "repository": { + "type": "git", + "url": "https://github.com/pania-es/frontend" + }, + "author": "PanIA Team", + "license": "MIT" +} diff --git a/fdev-ffrontend/postcss.config.js b/fdev-ffrontend/postcss.config.js new file mode 100644 index 00000000..e99ebc2c --- /dev/null +++ b/fdev-ffrontend/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} \ No newline at end of file diff --git a/fdev-ffrontend/src/App.tsx b/fdev-ffrontend/src/App.tsx new file mode 100644 index 00000000..5ccda2ad --- /dev/null +++ b/fdev-ffrontend/src/App.tsx @@ -0,0 +1,67 @@ +import React, { useEffect } from 'react'; +import { RouterProvider } from 'react-router-dom'; +import { Provider } from 'react-redux'; +import { Toaster } from 'react-hot-toast'; +import { router } from './router'; +import { store } from './store'; +import ErrorBoundary from './components/ErrorBoundary'; +import { useAuth } from './hooks/useAuth'; + +// i18n +import './i18n'; + +// Global styles +import './styles/globals.css'; + +const AppContent: React.FC = () => { + const { initializeAuth } = useAuth(); + + useEffect(() => { + initializeAuth(); + }, [initializeAuth]); + + return ( + +
+ + + {/* Global Toast Notifications */} + +
+
+ ); +}; + +const App: React.FC = () => { + return ( + + + + ); +}; + +export default App; \ No newline at end of file diff --git a/frontend/src/api/README.md b/fdev-ffrontend/src/api/README.md similarity index 100% rename from frontend/src/api/README.md rename to fdev-ffrontend/src/api/README.md diff --git a/frontend/src/api/client/config.ts b/fdev-ffrontend/src/api/client/config.ts similarity index 100% rename from frontend/src/api/client/config.ts rename to fdev-ffrontend/src/api/client/config.ts diff --git a/frontend/src/api/client/index.ts b/fdev-ffrontend/src/api/client/index.ts similarity index 100% rename from frontend/src/api/client/index.ts rename to fdev-ffrontend/src/api/client/index.ts diff --git a/frontend/src/api/client/interceptors.ts b/fdev-ffrontend/src/api/client/interceptors.ts similarity index 100% rename from frontend/src/api/client/interceptors.ts rename to fdev-ffrontend/src/api/client/interceptors.ts diff --git a/frontend/src/api/client/types.ts b/fdev-ffrontend/src/api/client/types.ts similarity index 100% rename from frontend/src/api/client/types.ts rename to fdev-ffrontend/src/api/client/types.ts diff --git a/frontend/src/api/hooks/index.ts b/fdev-ffrontend/src/api/hooks/index.ts similarity index 100% rename from frontend/src/api/hooks/index.ts rename to fdev-ffrontend/src/api/hooks/index.ts diff --git a/frontend/src/api/hooks/useAuth.ts b/fdev-ffrontend/src/api/hooks/useAuth.ts similarity index 100% rename from frontend/src/api/hooks/useAuth.ts rename to fdev-ffrontend/src/api/hooks/useAuth.ts diff --git a/frontend/src/api/hooks/useExternal.ts b/fdev-ffrontend/src/api/hooks/useExternal.ts similarity index 100% rename from frontend/src/api/hooks/useExternal.ts rename to fdev-ffrontend/src/api/hooks/useExternal.ts diff --git a/frontend/src/api/hooks/useForecast.ts b/fdev-ffrontend/src/api/hooks/useForecast.ts similarity index 100% rename from frontend/src/api/hooks/useForecast.ts rename to fdev-ffrontend/src/api/hooks/useForecast.ts diff --git a/frontend/src/api/hooks/useInventory.ts b/fdev-ffrontend/src/api/hooks/useInventory.ts similarity index 100% rename from frontend/src/api/hooks/useInventory.ts rename to fdev-ffrontend/src/api/hooks/useInventory.ts diff --git a/frontend/src/api/hooks/useNotification.ts b/fdev-ffrontend/src/api/hooks/useNotification.ts similarity index 100% rename from frontend/src/api/hooks/useNotification.ts rename to fdev-ffrontend/src/api/hooks/useNotification.ts diff --git a/frontend/src/api/hooks/useOnboarding.ts b/fdev-ffrontend/src/api/hooks/useOnboarding.ts similarity index 100% rename from frontend/src/api/hooks/useOnboarding.ts rename to fdev-ffrontend/src/api/hooks/useOnboarding.ts diff --git a/frontend/src/api/hooks/usePOS.ts b/fdev-ffrontend/src/api/hooks/usePOS.ts similarity index 100% rename from frontend/src/api/hooks/usePOS.ts rename to fdev-ffrontend/src/api/hooks/usePOS.ts diff --git a/frontend/src/api/hooks/useProcurement.ts b/fdev-ffrontend/src/api/hooks/useProcurement.ts similarity index 100% rename from frontend/src/api/hooks/useProcurement.ts rename to fdev-ffrontend/src/api/hooks/useProcurement.ts diff --git a/frontend/src/api/hooks/useRecipes.ts b/fdev-ffrontend/src/api/hooks/useRecipes.ts similarity index 100% rename from frontend/src/api/hooks/useRecipes.ts rename to fdev-ffrontend/src/api/hooks/useRecipes.ts diff --git a/frontend/src/api/hooks/useSales.ts b/fdev-ffrontend/src/api/hooks/useSales.ts similarity index 100% rename from frontend/src/api/hooks/useSales.ts rename to fdev-ffrontend/src/api/hooks/useSales.ts diff --git a/frontend/src/api/hooks/useSuppliers.ts b/fdev-ffrontend/src/api/hooks/useSuppliers.ts similarity index 100% rename from frontend/src/api/hooks/useSuppliers.ts rename to fdev-ffrontend/src/api/hooks/useSuppliers.ts diff --git a/frontend/src/api/hooks/useTenant.ts b/fdev-ffrontend/src/api/hooks/useTenant.ts similarity index 100% rename from frontend/src/api/hooks/useTenant.ts rename to fdev-ffrontend/src/api/hooks/useTenant.ts diff --git a/frontend/src/api/hooks/useTraining.ts b/fdev-ffrontend/src/api/hooks/useTraining.ts similarity index 100% rename from frontend/src/api/hooks/useTraining.ts rename to fdev-ffrontend/src/api/hooks/useTraining.ts diff --git a/frontend/src/api/index.ts b/fdev-ffrontend/src/api/index.ts similarity index 100% rename from frontend/src/api/index.ts rename to fdev-ffrontend/src/api/index.ts diff --git a/frontend/src/api/services/auth.service.ts b/fdev-ffrontend/src/api/services/auth.service.ts similarity index 100% rename from frontend/src/api/services/auth.service.ts rename to fdev-ffrontend/src/api/services/auth.service.ts diff --git a/frontend/src/api/services/external.service.ts b/fdev-ffrontend/src/api/services/external.service.ts similarity index 100% rename from frontend/src/api/services/external.service.ts rename to fdev-ffrontend/src/api/services/external.service.ts diff --git a/frontend/src/api/services/forecasting.service.ts b/fdev-ffrontend/src/api/services/forecasting.service.ts similarity index 100% rename from frontend/src/api/services/forecasting.service.ts rename to fdev-ffrontend/src/api/services/forecasting.service.ts diff --git a/frontend/src/api/services/index.ts b/fdev-ffrontend/src/api/services/index.ts similarity index 100% rename from frontend/src/api/services/index.ts rename to fdev-ffrontend/src/api/services/index.ts diff --git a/frontend/src/api/services/inventory.service.ts b/fdev-ffrontend/src/api/services/inventory.service.ts similarity index 100% rename from frontend/src/api/services/inventory.service.ts rename to fdev-ffrontend/src/api/services/inventory.service.ts diff --git a/frontend/src/api/services/notification.service.ts b/fdev-ffrontend/src/api/services/notification.service.ts similarity index 100% rename from frontend/src/api/services/notification.service.ts rename to fdev-ffrontend/src/api/services/notification.service.ts diff --git a/frontend/src/api/services/onboarding.service.ts b/fdev-ffrontend/src/api/services/onboarding.service.ts similarity index 100% rename from frontend/src/api/services/onboarding.service.ts rename to fdev-ffrontend/src/api/services/onboarding.service.ts diff --git a/frontend/src/api/services/orders.service.ts b/fdev-ffrontend/src/api/services/orders.service.ts similarity index 100% rename from frontend/src/api/services/orders.service.ts rename to fdev-ffrontend/src/api/services/orders.service.ts diff --git a/frontend/src/api/services/pos.service.ts b/fdev-ffrontend/src/api/services/pos.service.ts similarity index 100% rename from frontend/src/api/services/pos.service.ts rename to fdev-ffrontend/src/api/services/pos.service.ts diff --git a/frontend/src/api/services/procurement.service.ts b/fdev-ffrontend/src/api/services/procurement.service.ts similarity index 100% rename from frontend/src/api/services/procurement.service.ts rename to fdev-ffrontend/src/api/services/procurement.service.ts diff --git a/frontend/src/api/services/production.service.ts b/fdev-ffrontend/src/api/services/production.service.ts similarity index 100% rename from frontend/src/api/services/production.service.ts rename to fdev-ffrontend/src/api/services/production.service.ts diff --git a/frontend/src/api/services/recipes.service.ts b/fdev-ffrontend/src/api/services/recipes.service.ts similarity index 100% rename from frontend/src/api/services/recipes.service.ts rename to fdev-ffrontend/src/api/services/recipes.service.ts diff --git a/frontend/src/api/services/sales.service.ts b/fdev-ffrontend/src/api/services/sales.service.ts similarity index 100% rename from frontend/src/api/services/sales.service.ts rename to fdev-ffrontend/src/api/services/sales.service.ts diff --git a/frontend/src/api/services/suppliers.service.ts b/fdev-ffrontend/src/api/services/suppliers.service.ts similarity index 100% rename from frontend/src/api/services/suppliers.service.ts rename to fdev-ffrontend/src/api/services/suppliers.service.ts diff --git a/frontend/src/api/services/tenant.service.ts b/fdev-ffrontend/src/api/services/tenant.service.ts similarity index 100% rename from frontend/src/api/services/tenant.service.ts rename to fdev-ffrontend/src/api/services/tenant.service.ts diff --git a/frontend/src/api/services/training.service.ts b/fdev-ffrontend/src/api/services/training.service.ts similarity index 100% rename from frontend/src/api/services/training.service.ts rename to fdev-ffrontend/src/api/services/training.service.ts diff --git a/frontend/src/api/types/auth.ts b/fdev-ffrontend/src/api/types/auth.ts similarity index 100% rename from frontend/src/api/types/auth.ts rename to fdev-ffrontend/src/api/types/auth.ts diff --git a/frontend/src/api/types/common.ts b/fdev-ffrontend/src/api/types/common.ts similarity index 100% rename from frontend/src/api/types/common.ts rename to fdev-ffrontend/src/api/types/common.ts diff --git a/frontend/src/api/types/data.ts b/fdev-ffrontend/src/api/types/data.ts similarity index 100% rename from frontend/src/api/types/data.ts rename to fdev-ffrontend/src/api/types/data.ts diff --git a/frontend/src/api/types/forecasting.ts b/fdev-ffrontend/src/api/types/forecasting.ts similarity index 100% rename from frontend/src/api/types/forecasting.ts rename to fdev-ffrontend/src/api/types/forecasting.ts diff --git a/frontend/src/api/types/index.ts b/fdev-ffrontend/src/api/types/index.ts similarity index 100% rename from frontend/src/api/types/index.ts rename to fdev-ffrontend/src/api/types/index.ts diff --git a/frontend/src/api/types/notification.ts b/fdev-ffrontend/src/api/types/notification.ts similarity index 100% rename from frontend/src/api/types/notification.ts rename to fdev-ffrontend/src/api/types/notification.ts diff --git a/frontend/src/api/types/procurement.ts b/fdev-ffrontend/src/api/types/procurement.ts similarity index 100% rename from frontend/src/api/types/procurement.ts rename to fdev-ffrontend/src/api/types/procurement.ts diff --git a/frontend/src/api/types/tenant.ts b/fdev-ffrontend/src/api/types/tenant.ts similarity index 100% rename from frontend/src/api/types/tenant.ts rename to fdev-ffrontend/src/api/types/tenant.ts diff --git a/frontend/src/api/types/training.ts b/fdev-ffrontend/src/api/types/training.ts similarity index 100% rename from frontend/src/api/types/training.ts rename to fdev-ffrontend/src/api/types/training.ts diff --git a/frontend/src/api/utils/error.ts b/fdev-ffrontend/src/api/utils/error.ts similarity index 100% rename from frontend/src/api/utils/error.ts rename to fdev-ffrontend/src/api/utils/error.ts diff --git a/frontend/src/api/utils/index.ts b/fdev-ffrontend/src/api/utils/index.ts similarity index 100% rename from frontend/src/api/utils/index.ts rename to fdev-ffrontend/src/api/utils/index.ts diff --git a/frontend/src/api/utils/response.ts b/fdev-ffrontend/src/api/utils/response.ts similarity index 100% rename from frontend/src/api/utils/response.ts rename to fdev-ffrontend/src/api/utils/response.ts diff --git a/frontend/src/api/utils/transform.ts b/fdev-ffrontend/src/api/utils/transform.ts similarity index 100% rename from frontend/src/api/utils/transform.ts rename to fdev-ffrontend/src/api/utils/transform.ts diff --git a/frontend/src/api/utils/validation.ts b/fdev-ffrontend/src/api/utils/validation.ts similarity index 100% rename from frontend/src/api/utils/validation.ts rename to fdev-ffrontend/src/api/utils/validation.ts diff --git a/frontend/src/api/websocket/hooks.ts b/fdev-ffrontend/src/api/websocket/hooks.ts similarity index 100% rename from frontend/src/api/websocket/hooks.ts rename to fdev-ffrontend/src/api/websocket/hooks.ts diff --git a/frontend/src/api/websocket/index.ts b/fdev-ffrontend/src/api/websocket/index.ts similarity index 100% rename from frontend/src/api/websocket/index.ts rename to fdev-ffrontend/src/api/websocket/index.ts diff --git a/frontend/src/api/websocket/manager.ts b/fdev-ffrontend/src/api/websocket/manager.ts similarity index 100% rename from frontend/src/api/websocket/manager.ts rename to fdev-ffrontend/src/api/websocket/manager.ts diff --git a/frontend/src/api/websocket/types.ts b/fdev-ffrontend/src/api/websocket/types.ts similarity index 100% rename from frontend/src/api/websocket/types.ts rename to fdev-ffrontend/src/api/websocket/types.ts diff --git a/frontend/src/components/EnhancedTrainingProgress.tsx b/fdev-ffrontend/src/components/EnhancedTrainingProgress.tsx similarity index 100% rename from frontend/src/components/EnhancedTrainingProgress.tsx rename to fdev-ffrontend/src/components/EnhancedTrainingProgress.tsx diff --git a/frontend/src/components/ErrorBoundary.tsx b/fdev-ffrontend/src/components/ErrorBoundary.tsx similarity index 100% rename from frontend/src/components/ErrorBoundary.tsx rename to fdev-ffrontend/src/components/ErrorBoundary.tsx diff --git a/frontend/src/components/SimplifiedTrainingProgress.tsx b/fdev-ffrontend/src/components/SimplifiedTrainingProgress.tsx similarity index 100% rename from frontend/src/components/SimplifiedTrainingProgress.tsx rename to fdev-ffrontend/src/components/SimplifiedTrainingProgress.tsx diff --git a/frontend/src/components/adaptive/AdaptiveInventoryWidget.tsx b/fdev-ffrontend/src/components/adaptive/AdaptiveInventoryWidget.tsx similarity index 100% rename from frontend/src/components/adaptive/AdaptiveInventoryWidget.tsx rename to fdev-ffrontend/src/components/adaptive/AdaptiveInventoryWidget.tsx diff --git a/frontend/src/components/adaptive/AdaptiveProductionCard.tsx b/fdev-ffrontend/src/components/adaptive/AdaptiveProductionCard.tsx similarity index 100% rename from frontend/src/components/adaptive/AdaptiveProductionCard.tsx rename to fdev-ffrontend/src/components/adaptive/AdaptiveProductionCard.tsx diff --git a/frontend/src/components/alerts/AlertCard.tsx b/fdev-ffrontend/src/components/alerts/AlertCard.tsx similarity index 100% rename from frontend/src/components/alerts/AlertCard.tsx rename to fdev-ffrontend/src/components/alerts/AlertCard.tsx diff --git a/frontend/src/components/alerts/AlertDashboard.tsx b/fdev-ffrontend/src/components/alerts/AlertDashboard.tsx similarity index 100% rename from frontend/src/components/alerts/AlertDashboard.tsx rename to fdev-ffrontend/src/components/alerts/AlertDashboard.tsx diff --git a/frontend/src/components/alerts/AlertFilters.tsx b/fdev-ffrontend/src/components/alerts/AlertFilters.tsx similarity index 100% rename from frontend/src/components/alerts/AlertFilters.tsx rename to fdev-ffrontend/src/components/alerts/AlertFilters.tsx diff --git a/frontend/src/components/alerts/AlertStats.tsx b/fdev-ffrontend/src/components/alerts/AlertStats.tsx similarity index 100% rename from frontend/src/components/alerts/AlertStats.tsx rename to fdev-ffrontend/src/components/alerts/AlertStats.tsx diff --git a/frontend/src/components/alerts/ConnectionStatus.tsx b/fdev-ffrontend/src/components/alerts/ConnectionStatus.tsx similarity index 100% rename from frontend/src/components/alerts/ConnectionStatus.tsx rename to fdev-ffrontend/src/components/alerts/ConnectionStatus.tsx diff --git a/frontend/src/components/auth/ProtectedRoute.tsx b/fdev-ffrontend/src/components/auth/ProtectedRoute.tsx similarity index 100% rename from frontend/src/components/auth/ProtectedRoute.tsx rename to fdev-ffrontend/src/components/auth/ProtectedRoute.tsx diff --git a/frontend/src/components/auth/RoleBasedAccess.tsx b/fdev-ffrontend/src/components/auth/RoleBasedAccess.tsx similarity index 100% rename from frontend/src/components/auth/RoleBasedAccess.tsx rename to fdev-ffrontend/src/components/auth/RoleBasedAccess.tsx diff --git a/frontend/src/components/auth/RoleBasedRoute.tsx b/fdev-ffrontend/src/components/auth/RoleBasedRoute.tsx similarity index 100% rename from frontend/src/components/auth/RoleBasedRoute.tsx rename to fdev-ffrontend/src/components/auth/RoleBasedRoute.tsx diff --git a/frontend/src/components/inventory/InventoryDashboardWidget.tsx b/fdev-ffrontend/src/components/inventory/InventoryDashboardWidget.tsx similarity index 100% rename from frontend/src/components/inventory/InventoryDashboardWidget.tsx rename to fdev-ffrontend/src/components/inventory/InventoryDashboardWidget.tsx diff --git a/frontend/src/components/inventory/InventoryItemCard.tsx b/fdev-ffrontend/src/components/inventory/InventoryItemCard.tsx similarity index 100% rename from frontend/src/components/inventory/InventoryItemCard.tsx rename to fdev-ffrontend/src/components/inventory/InventoryItemCard.tsx diff --git a/frontend/src/components/layout/AnalyticsLayout.tsx b/fdev-ffrontend/src/components/layout/AnalyticsLayout.tsx similarity index 100% rename from frontend/src/components/layout/AnalyticsLayout.tsx rename to fdev-ffrontend/src/components/layout/AnalyticsLayout.tsx diff --git a/frontend/src/components/layout/AuthLayout.tsx b/fdev-ffrontend/src/components/layout/AuthLayout.tsx similarity index 100% rename from frontend/src/components/layout/AuthLayout.tsx rename to fdev-ffrontend/src/components/layout/AuthLayout.tsx diff --git a/frontend/src/components/layout/Layout.tsx b/fdev-ffrontend/src/components/layout/Layout.tsx similarity index 100% rename from frontend/src/components/layout/Layout.tsx rename to fdev-ffrontend/src/components/layout/Layout.tsx diff --git a/frontend/src/components/layout/OperationsLayout.tsx b/fdev-ffrontend/src/components/layout/OperationsLayout.tsx similarity index 100% rename from frontend/src/components/layout/OperationsLayout.tsx rename to fdev-ffrontend/src/components/layout/OperationsLayout.tsx diff --git a/frontend/src/components/layout/SettingsLayout.tsx b/fdev-ffrontend/src/components/layout/SettingsLayout.tsx similarity index 100% rename from frontend/src/components/layout/SettingsLayout.tsx rename to fdev-ffrontend/src/components/layout/SettingsLayout.tsx diff --git a/frontend/src/components/navigation/Breadcrumbs.tsx b/fdev-ffrontend/src/components/navigation/Breadcrumbs.tsx similarity index 100% rename from frontend/src/components/navigation/Breadcrumbs.tsx rename to fdev-ffrontend/src/components/navigation/Breadcrumbs.tsx diff --git a/frontend/src/components/navigation/SecondaryNavigation.tsx b/fdev-ffrontend/src/components/navigation/SecondaryNavigation.tsx similarity index 100% rename from frontend/src/components/navigation/SecondaryNavigation.tsx rename to fdev-ffrontend/src/components/navigation/SecondaryNavigation.tsx diff --git a/frontend/src/components/navigation/TenantSelector.tsx b/fdev-ffrontend/src/components/navigation/TenantSelector.tsx similarity index 100% rename from frontend/src/components/navigation/TenantSelector.tsx rename to fdev-ffrontend/src/components/navigation/TenantSelector.tsx diff --git a/frontend/src/components/onboarding/SmartHistoricalDataImport.tsx b/fdev-ffrontend/src/components/onboarding/SmartHistoricalDataImport.tsx similarity index 100% rename from frontend/src/components/onboarding/SmartHistoricalDataImport.tsx rename to fdev-ffrontend/src/components/onboarding/SmartHistoricalDataImport.tsx diff --git a/frontend/src/components/pos/POSConfigurationCard.tsx b/fdev-ffrontend/src/components/pos/POSConfigurationCard.tsx similarity index 100% rename from frontend/src/components/pos/POSConfigurationCard.tsx rename to fdev-ffrontend/src/components/pos/POSConfigurationCard.tsx diff --git a/frontend/src/components/pos/POSConfigurationForm.tsx b/fdev-ffrontend/src/components/pos/POSConfigurationForm.tsx similarity index 100% rename from frontend/src/components/pos/POSConfigurationForm.tsx rename to fdev-ffrontend/src/components/pos/POSConfigurationForm.tsx diff --git a/frontend/src/components/pos/POSManagementPage.tsx b/fdev-ffrontend/src/components/pos/POSManagementPage.tsx similarity index 100% rename from frontend/src/components/pos/POSManagementPage.tsx rename to fdev-ffrontend/src/components/pos/POSManagementPage.tsx diff --git a/frontend/src/components/pos/POSSyncStatus.tsx b/fdev-ffrontend/src/components/pos/POSSyncStatus.tsx similarity index 100% rename from frontend/src/components/pos/POSSyncStatus.tsx rename to fdev-ffrontend/src/components/pos/POSSyncStatus.tsx diff --git a/frontend/src/components/pos/index.ts b/fdev-ffrontend/src/components/pos/index.ts similarity index 100% rename from frontend/src/components/pos/index.ts rename to fdev-ffrontend/src/components/pos/index.ts diff --git a/frontend/src/components/procurement/CriticalRequirements.tsx b/fdev-ffrontend/src/components/procurement/CriticalRequirements.tsx similarity index 100% rename from frontend/src/components/procurement/CriticalRequirements.tsx rename to fdev-ffrontend/src/components/procurement/CriticalRequirements.tsx diff --git a/frontend/src/components/procurement/GeneratePlanModal.tsx b/fdev-ffrontend/src/components/procurement/GeneratePlanModal.tsx similarity index 100% rename from frontend/src/components/procurement/GeneratePlanModal.tsx rename to fdev-ffrontend/src/components/procurement/GeneratePlanModal.tsx diff --git a/frontend/src/components/procurement/ProcurementDashboard.tsx b/fdev-ffrontend/src/components/procurement/ProcurementDashboard.tsx similarity index 100% rename from frontend/src/components/procurement/ProcurementDashboard.tsx rename to fdev-ffrontend/src/components/procurement/ProcurementDashboard.tsx diff --git a/frontend/src/components/procurement/ProcurementPlanCard.tsx b/fdev-ffrontend/src/components/procurement/ProcurementPlanCard.tsx similarity index 100% rename from frontend/src/components/procurement/ProcurementPlanCard.tsx rename to fdev-ffrontend/src/components/procurement/ProcurementPlanCard.tsx diff --git a/frontend/src/components/procurement/ProcurementSummary.tsx b/fdev-ffrontend/src/components/procurement/ProcurementSummary.tsx similarity index 100% rename from frontend/src/components/procurement/ProcurementSummary.tsx rename to fdev-ffrontend/src/components/procurement/ProcurementSummary.tsx diff --git a/frontend/src/components/procurement/index.ts b/fdev-ffrontend/src/components/procurement/index.ts similarity index 100% rename from frontend/src/components/procurement/index.ts rename to fdev-ffrontend/src/components/procurement/index.ts diff --git a/frontend/src/components/recipes/IngredientList.tsx b/fdev-ffrontend/src/components/recipes/IngredientList.tsx similarity index 100% rename from frontend/src/components/recipes/IngredientList.tsx rename to fdev-ffrontend/src/components/recipes/IngredientList.tsx diff --git a/frontend/src/components/recipes/ProductionBatchCard.tsx b/fdev-ffrontend/src/components/recipes/ProductionBatchCard.tsx similarity index 100% rename from frontend/src/components/recipes/ProductionBatchCard.tsx rename to fdev-ffrontend/src/components/recipes/ProductionBatchCard.tsx diff --git a/frontend/src/components/recipes/RecipeCard.tsx b/fdev-ffrontend/src/components/recipes/RecipeCard.tsx similarity index 100% rename from frontend/src/components/recipes/RecipeCard.tsx rename to fdev-ffrontend/src/components/recipes/RecipeCard.tsx diff --git a/frontend/src/components/sales/SalesAnalyticsDashboard.tsx b/fdev-ffrontend/src/components/sales/SalesAnalyticsDashboard.tsx similarity index 100% rename from frontend/src/components/sales/SalesAnalyticsDashboard.tsx rename to fdev-ffrontend/src/components/sales/SalesAnalyticsDashboard.tsx diff --git a/frontend/src/components/sales/SalesDashboardWidget.tsx b/fdev-ffrontend/src/components/sales/SalesDashboardWidget.tsx similarity index 100% rename from frontend/src/components/sales/SalesDashboardWidget.tsx rename to fdev-ffrontend/src/components/sales/SalesDashboardWidget.tsx diff --git a/frontend/src/components/sales/SalesDataCard.tsx b/fdev-ffrontend/src/components/sales/SalesDataCard.tsx similarity index 100% rename from frontend/src/components/sales/SalesDataCard.tsx rename to fdev-ffrontend/src/components/sales/SalesDataCard.tsx diff --git a/frontend/src/components/sales/SalesManagementPage.tsx b/fdev-ffrontend/src/components/sales/SalesManagementPage.tsx similarity index 100% rename from frontend/src/components/sales/SalesManagementPage.tsx rename to fdev-ffrontend/src/components/sales/SalesManagementPage.tsx diff --git a/frontend/src/components/sales/SalesPerformanceInsights.tsx b/fdev-ffrontend/src/components/sales/SalesPerformanceInsights.tsx similarity index 100% rename from frontend/src/components/sales/SalesPerformanceInsights.tsx rename to fdev-ffrontend/src/components/sales/SalesPerformanceInsights.tsx diff --git a/frontend/src/components/sales/index.ts b/fdev-ffrontend/src/components/sales/index.ts similarity index 100% rename from frontend/src/components/sales/index.ts rename to fdev-ffrontend/src/components/sales/index.ts diff --git a/frontend/src/components/simple/OrderSuggestions.tsx b/fdev-ffrontend/src/components/simple/OrderSuggestions.tsx similarity index 100% rename from frontend/src/components/simple/OrderSuggestions.tsx rename to fdev-ffrontend/src/components/simple/OrderSuggestions.tsx diff --git a/frontend/src/components/simple/QuickActions.tsx b/fdev-ffrontend/src/components/simple/QuickActions.tsx similarity index 100% rename from frontend/src/components/simple/QuickActions.tsx rename to fdev-ffrontend/src/components/simple/QuickActions.tsx diff --git a/frontend/src/components/simple/QuickOverview.tsx b/fdev-ffrontend/src/components/simple/QuickOverview.tsx similarity index 100% rename from frontend/src/components/simple/QuickOverview.tsx rename to fdev-ffrontend/src/components/simple/QuickOverview.tsx diff --git a/frontend/src/components/simple/TodayProduction.tsx b/fdev-ffrontend/src/components/simple/TodayProduction.tsx similarity index 100% rename from frontend/src/components/simple/TodayProduction.tsx rename to fdev-ffrontend/src/components/simple/TodayProduction.tsx diff --git a/frontend/src/components/simple/TodayRevenue.tsx b/fdev-ffrontend/src/components/simple/TodayRevenue.tsx similarity index 100% rename from frontend/src/components/simple/TodayRevenue.tsx rename to fdev-ffrontend/src/components/simple/TodayRevenue.tsx diff --git a/frontend/src/components/simple/WeatherContext.tsx b/fdev-ffrontend/src/components/simple/WeatherContext.tsx similarity index 100% rename from frontend/src/components/simple/WeatherContext.tsx rename to fdev-ffrontend/src/components/simple/WeatherContext.tsx diff --git a/frontend/src/components/simple/index.ts b/fdev-ffrontend/src/components/simple/index.ts similarity index 100% rename from frontend/src/components/simple/index.ts rename to fdev-ffrontend/src/components/simple/index.ts diff --git a/frontend/src/components/suppliers/DeliveryCard.tsx b/fdev-ffrontend/src/components/suppliers/DeliveryCard.tsx similarity index 100% rename from frontend/src/components/suppliers/DeliveryCard.tsx rename to fdev-ffrontend/src/components/suppliers/DeliveryCard.tsx diff --git a/frontend/src/components/suppliers/DeliveryDashboardWidget.tsx b/fdev-ffrontend/src/components/suppliers/DeliveryDashboardWidget.tsx similarity index 100% rename from frontend/src/components/suppliers/DeliveryDashboardWidget.tsx rename to fdev-ffrontend/src/components/suppliers/DeliveryDashboardWidget.tsx diff --git a/frontend/src/components/suppliers/DeliveryTrackingPage.tsx b/fdev-ffrontend/src/components/suppliers/DeliveryTrackingPage.tsx similarity index 100% rename from frontend/src/components/suppliers/DeliveryTrackingPage.tsx rename to fdev-ffrontend/src/components/suppliers/DeliveryTrackingPage.tsx diff --git a/frontend/src/components/suppliers/PurchaseOrderCard.tsx b/fdev-ffrontend/src/components/suppliers/PurchaseOrderCard.tsx similarity index 100% rename from frontend/src/components/suppliers/PurchaseOrderCard.tsx rename to fdev-ffrontend/src/components/suppliers/PurchaseOrderCard.tsx diff --git a/frontend/src/components/suppliers/PurchaseOrderForm.tsx b/fdev-ffrontend/src/components/suppliers/PurchaseOrderForm.tsx similarity index 100% rename from frontend/src/components/suppliers/PurchaseOrderForm.tsx rename to fdev-ffrontend/src/components/suppliers/PurchaseOrderForm.tsx diff --git a/frontend/src/components/suppliers/PurchaseOrderManagementPage.tsx b/fdev-ffrontend/src/components/suppliers/PurchaseOrderManagementPage.tsx similarity index 100% rename from frontend/src/components/suppliers/PurchaseOrderManagementPage.tsx rename to fdev-ffrontend/src/components/suppliers/PurchaseOrderManagementPage.tsx diff --git a/frontend/src/components/suppliers/SupplierAnalyticsDashboard.tsx b/fdev-ffrontend/src/components/suppliers/SupplierAnalyticsDashboard.tsx similarity index 100% rename from frontend/src/components/suppliers/SupplierAnalyticsDashboard.tsx rename to fdev-ffrontend/src/components/suppliers/SupplierAnalyticsDashboard.tsx diff --git a/frontend/src/components/suppliers/SupplierCard.tsx b/fdev-ffrontend/src/components/suppliers/SupplierCard.tsx similarity index 100% rename from frontend/src/components/suppliers/SupplierCard.tsx rename to fdev-ffrontend/src/components/suppliers/SupplierCard.tsx diff --git a/frontend/src/components/suppliers/SupplierCostAnalysis.tsx b/fdev-ffrontend/src/components/suppliers/SupplierCostAnalysis.tsx similarity index 100% rename from frontend/src/components/suppliers/SupplierCostAnalysis.tsx rename to fdev-ffrontend/src/components/suppliers/SupplierCostAnalysis.tsx diff --git a/frontend/src/components/suppliers/SupplierDashboardWidget.tsx b/fdev-ffrontend/src/components/suppliers/SupplierDashboardWidget.tsx similarity index 100% rename from frontend/src/components/suppliers/SupplierDashboardWidget.tsx rename to fdev-ffrontend/src/components/suppliers/SupplierDashboardWidget.tsx diff --git a/frontend/src/components/suppliers/SupplierForm.tsx b/fdev-ffrontend/src/components/suppliers/SupplierForm.tsx similarity index 100% rename from frontend/src/components/suppliers/SupplierForm.tsx rename to fdev-ffrontend/src/components/suppliers/SupplierForm.tsx diff --git a/frontend/src/components/suppliers/SupplierManagementPage.tsx b/fdev-ffrontend/src/components/suppliers/SupplierManagementPage.tsx similarity index 100% rename from frontend/src/components/suppliers/SupplierManagementPage.tsx rename to fdev-ffrontend/src/components/suppliers/SupplierManagementPage.tsx diff --git a/frontend/src/components/suppliers/SupplierPerformanceReport.tsx b/fdev-ffrontend/src/components/suppliers/SupplierPerformanceReport.tsx similarity index 100% rename from frontend/src/components/suppliers/SupplierPerformanceReport.tsx rename to fdev-ffrontend/src/components/suppliers/SupplierPerformanceReport.tsx diff --git a/frontend/src/components/suppliers/index.ts b/fdev-ffrontend/src/components/suppliers/index.ts similarity index 100% rename from frontend/src/components/suppliers/index.ts rename to fdev-ffrontend/src/components/suppliers/index.ts diff --git a/frontend/src/components/ui/AIInsightsFeed.tsx b/fdev-ffrontend/src/components/ui/AIInsightsFeed.tsx similarity index 100% rename from frontend/src/components/ui/AIInsightsFeed.tsx rename to fdev-ffrontend/src/components/ui/AIInsightsFeed.tsx diff --git a/frontend/src/components/ui/Button.tsx b/fdev-ffrontend/src/components/ui/Button.tsx similarity index 100% rename from frontend/src/components/ui/Button.tsx rename to fdev-ffrontend/src/components/ui/Button.tsx diff --git a/frontend/src/components/ui/Card.tsx b/fdev-ffrontend/src/components/ui/Card.tsx similarity index 100% rename from frontend/src/components/ui/Card.tsx rename to fdev-ffrontend/src/components/ui/Card.tsx diff --git a/frontend/src/components/ui/CompetitiveBenchmarks.tsx b/fdev-ffrontend/src/components/ui/CompetitiveBenchmarks.tsx similarity index 100% rename from frontend/src/components/ui/CompetitiveBenchmarks.tsx rename to fdev-ffrontend/src/components/ui/CompetitiveBenchmarks.tsx diff --git a/frontend/src/components/ui/DemandHeatmap.tsx b/fdev-ffrontend/src/components/ui/DemandHeatmap.tsx similarity index 100% rename from frontend/src/components/ui/DemandHeatmap.tsx rename to fdev-ffrontend/src/components/ui/DemandHeatmap.tsx diff --git a/frontend/src/components/ui/Input.tsx b/fdev-ffrontend/src/components/ui/Input.tsx similarity index 100% rename from frontend/src/components/ui/Input.tsx rename to fdev-ffrontend/src/components/ui/Input.tsx diff --git a/frontend/src/components/ui/LoadingSpinner.tsx b/fdev-ffrontend/src/components/ui/LoadingSpinner.tsx similarity index 100% rename from frontend/src/components/ui/LoadingSpinner.tsx rename to fdev-ffrontend/src/components/ui/LoadingSpinner.tsx diff --git a/frontend/src/components/ui/ProductionSchedule.tsx b/fdev-ffrontend/src/components/ui/ProductionSchedule.tsx similarity index 100% rename from frontend/src/components/ui/ProductionSchedule.tsx rename to fdev-ffrontend/src/components/ui/ProductionSchedule.tsx diff --git a/frontend/src/components/ui/QuickActionsPanel.tsx b/fdev-ffrontend/src/components/ui/QuickActionsPanel.tsx similarity index 100% rename from frontend/src/components/ui/QuickActionsPanel.tsx rename to fdev-ffrontend/src/components/ui/QuickActionsPanel.tsx diff --git a/frontend/src/components/ui/RevenueMetrics.tsx b/fdev-ffrontend/src/components/ui/RevenueMetrics.tsx similarity index 100% rename from frontend/src/components/ui/RevenueMetrics.tsx rename to fdev-ffrontend/src/components/ui/RevenueMetrics.tsx diff --git a/frontend/src/components/ui/WhatIfPlanner.tsx b/fdev-ffrontend/src/components/ui/WhatIfPlanner.tsx similarity index 100% rename from frontend/src/components/ui/WhatIfPlanner.tsx rename to fdev-ffrontend/src/components/ui/WhatIfPlanner.tsx diff --git a/frontend/src/hooks/useAlertStream.ts b/fdev-ffrontend/src/hooks/useAlertStream.ts similarity index 100% rename from frontend/src/hooks/useAlertStream.ts rename to fdev-ffrontend/src/hooks/useAlertStream.ts diff --git a/frontend/src/hooks/useAuth.ts b/fdev-ffrontend/src/hooks/useAuth.ts similarity index 100% rename from frontend/src/hooks/useAuth.ts rename to fdev-ffrontend/src/hooks/useAuth.ts diff --git a/frontend/src/hooks/useBakeryType.ts b/fdev-ffrontend/src/hooks/useBakeryType.ts similarity index 100% rename from frontend/src/hooks/useBakeryType.ts rename to fdev-ffrontend/src/hooks/useBakeryType.ts diff --git a/frontend/src/hooks/useDashboard.ts b/fdev-ffrontend/src/hooks/useDashboard.ts similarity index 100% rename from frontend/src/hooks/useDashboard.ts rename to fdev-ffrontend/src/hooks/useDashboard.ts diff --git a/frontend/src/hooks/useOrderSuggestions.ts b/fdev-ffrontend/src/hooks/useOrderSuggestions.ts similarity index 100% rename from frontend/src/hooks/useOrderSuggestions.ts rename to fdev-ffrontend/src/hooks/useOrderSuggestions.ts diff --git a/frontend/src/hooks/usePermissions.ts b/fdev-ffrontend/src/hooks/usePermissions.ts similarity index 100% rename from frontend/src/hooks/usePermissions.ts rename to fdev-ffrontend/src/hooks/usePermissions.ts diff --git a/frontend/src/hooks/useTenantId.ts b/fdev-ffrontend/src/hooks/useTenantId.ts similarity index 100% rename from frontend/src/hooks/useTenantId.ts rename to fdev-ffrontend/src/hooks/useTenantId.ts diff --git a/frontend/src/i18n/index.ts b/fdev-ffrontend/src/i18n/index.ts similarity index 100% rename from frontend/src/i18n/index.ts rename to fdev-ffrontend/src/i18n/index.ts diff --git a/frontend/src/index.css b/fdev-ffrontend/src/index.css similarity index 100% rename from frontend/src/index.css rename to fdev-ffrontend/src/index.css diff --git a/fdev-ffrontend/src/main.tsx b/fdev-ffrontend/src/main.tsx new file mode 100644 index 00000000..cbe1cdf3 --- /dev/null +++ b/fdev-ffrontend/src/main.tsx @@ -0,0 +1,10 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App.tsx' +import './index.css' + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + , +) \ No newline at end of file diff --git a/frontend/src/pages/analytics/AIInsightsPage.tsx b/fdev-ffrontend/src/pages/analytics/AIInsightsPage.tsx similarity index 100% rename from frontend/src/pages/analytics/AIInsightsPage.tsx rename to fdev-ffrontend/src/pages/analytics/AIInsightsPage.tsx diff --git a/frontend/src/pages/analytics/FinancialReportsPage.tsx b/fdev-ffrontend/src/pages/analytics/FinancialReportsPage.tsx similarity index 100% rename from frontend/src/pages/analytics/FinancialReportsPage.tsx rename to fdev-ffrontend/src/pages/analytics/FinancialReportsPage.tsx diff --git a/frontend/src/pages/analytics/PerformanceKPIsPage.tsx b/fdev-ffrontend/src/pages/analytics/PerformanceKPIsPage.tsx similarity index 100% rename from frontend/src/pages/analytics/PerformanceKPIsPage.tsx rename to fdev-ffrontend/src/pages/analytics/PerformanceKPIsPage.tsx diff --git a/frontend/src/pages/analytics/ProductionReportsPage.tsx b/fdev-ffrontend/src/pages/analytics/ProductionReportsPage.tsx similarity index 100% rename from frontend/src/pages/analytics/ProductionReportsPage.tsx rename to fdev-ffrontend/src/pages/analytics/ProductionReportsPage.tsx diff --git a/frontend/src/pages/analytics/SalesAnalyticsPage.tsx b/fdev-ffrontend/src/pages/analytics/SalesAnalyticsPage.tsx similarity index 100% rename from frontend/src/pages/analytics/SalesAnalyticsPage.tsx rename to fdev-ffrontend/src/pages/analytics/SalesAnalyticsPage.tsx diff --git a/frontend/src/pages/auth/LoginPage.tsx b/fdev-ffrontend/src/pages/auth/LoginPage.tsx similarity index 100% rename from frontend/src/pages/auth/LoginPage.tsx rename to fdev-ffrontend/src/pages/auth/LoginPage.tsx diff --git a/frontend/src/pages/auth/RegisterPage.tsx b/fdev-ffrontend/src/pages/auth/RegisterPage.tsx similarity index 100% rename from frontend/src/pages/auth/RegisterPage.tsx rename to fdev-ffrontend/src/pages/auth/RegisterPage.tsx diff --git a/frontend/src/pages/auth/SimpleRegisterPage.tsx b/fdev-ffrontend/src/pages/auth/SimpleRegisterPage.tsx similarity index 100% rename from frontend/src/pages/auth/SimpleRegisterPage.tsx rename to fdev-ffrontend/src/pages/auth/SimpleRegisterPage.tsx diff --git a/frontend/src/pages/dashboard/DashboardPage.tsx b/fdev-ffrontend/src/pages/dashboard/DashboardPage.tsx similarity index 100% rename from frontend/src/pages/dashboard/DashboardPage.tsx rename to fdev-ffrontend/src/pages/dashboard/DashboardPage.tsx diff --git a/frontend/src/pages/forecast/ForecastPage.tsx b/fdev-ffrontend/src/pages/forecast/ForecastPage.tsx similarity index 100% rename from frontend/src/pages/forecast/ForecastPage.tsx rename to fdev-ffrontend/src/pages/forecast/ForecastPage.tsx diff --git a/frontend/src/pages/inventory/InventoryPage.tsx b/fdev-ffrontend/src/pages/inventory/InventoryPage.tsx similarity index 100% rename from frontend/src/pages/inventory/InventoryPage.tsx rename to fdev-ffrontend/src/pages/inventory/InventoryPage.tsx diff --git a/frontend/src/pages/landing/LandingPage.tsx b/fdev-ffrontend/src/pages/landing/LandingPage.tsx similarity index 100% rename from frontend/src/pages/landing/LandingPage.tsx rename to fdev-ffrontend/src/pages/landing/LandingPage.tsx diff --git a/frontend/src/pages/landing/SimpleLandingPage.tsx b/fdev-ffrontend/src/pages/landing/SimpleLandingPage.tsx similarity index 100% rename from frontend/src/pages/landing/SimpleLandingPage.tsx rename to fdev-ffrontend/src/pages/landing/SimpleLandingPage.tsx diff --git a/frontend/src/pages/onboarding/OnboardingPage.tsx b/fdev-ffrontend/src/pages/onboarding/OnboardingPage.tsx similarity index 100% rename from frontend/src/pages/onboarding/OnboardingPage.tsx rename to fdev-ffrontend/src/pages/onboarding/OnboardingPage.tsx diff --git a/frontend/src/pages/orders/OrdersPage.tsx b/fdev-ffrontend/src/pages/orders/OrdersPage.tsx similarity index 100% rename from frontend/src/pages/orders/OrdersPage.tsx rename to fdev-ffrontend/src/pages/orders/OrdersPage.tsx diff --git a/frontend/src/pages/pos/POSPage.tsx b/fdev-ffrontend/src/pages/pos/POSPage.tsx similarity index 100% rename from frontend/src/pages/pos/POSPage.tsx rename to fdev-ffrontend/src/pages/pos/POSPage.tsx diff --git a/frontend/src/pages/pos/index.ts b/fdev-ffrontend/src/pages/pos/index.ts similarity index 100% rename from frontend/src/pages/pos/index.ts rename to fdev-ffrontend/src/pages/pos/index.ts diff --git a/frontend/src/pages/procurement/index.ts b/fdev-ffrontend/src/pages/procurement/index.ts similarity index 100% rename from frontend/src/pages/procurement/index.ts rename to fdev-ffrontend/src/pages/procurement/index.ts diff --git a/frontend/src/pages/production/ProductionPage.tsx b/fdev-ffrontend/src/pages/production/ProductionPage.tsx similarity index 100% rename from frontend/src/pages/production/ProductionPage.tsx rename to fdev-ffrontend/src/pages/production/ProductionPage.tsx diff --git a/frontend/src/pages/recipes/RecipesPage.tsx b/fdev-ffrontend/src/pages/recipes/RecipesPage.tsx similarity index 100% rename from frontend/src/pages/recipes/RecipesPage.tsx rename to fdev-ffrontend/src/pages/recipes/RecipesPage.tsx diff --git a/frontend/src/pages/sales/SalesPage.tsx b/fdev-ffrontend/src/pages/sales/SalesPage.tsx similarity index 100% rename from frontend/src/pages/sales/SalesPage.tsx rename to fdev-ffrontend/src/pages/sales/SalesPage.tsx diff --git a/frontend/src/pages/settings/AccountSettingsPage.tsx b/fdev-ffrontend/src/pages/settings/AccountSettingsPage.tsx similarity index 100% rename from frontend/src/pages/settings/AccountSettingsPage.tsx rename to fdev-ffrontend/src/pages/settings/AccountSettingsPage.tsx diff --git a/frontend/src/pages/settings/BakeriesManagementPage.tsx b/fdev-ffrontend/src/pages/settings/BakeriesManagementPage.tsx similarity index 100% rename from frontend/src/pages/settings/BakeriesManagementPage.tsx rename to fdev-ffrontend/src/pages/settings/BakeriesManagementPage.tsx diff --git a/frontend/src/pages/settings/GeneralSettingsPage.tsx b/fdev-ffrontend/src/pages/settings/GeneralSettingsPage.tsx similarity index 100% rename from frontend/src/pages/settings/GeneralSettingsPage.tsx rename to fdev-ffrontend/src/pages/settings/GeneralSettingsPage.tsx diff --git a/frontend/src/pages/settings/SettingsPage.tsx b/fdev-ffrontend/src/pages/settings/SettingsPage.tsx similarity index 100% rename from frontend/src/pages/settings/SettingsPage.tsx rename to fdev-ffrontend/src/pages/settings/SettingsPage.tsx diff --git a/frontend/src/pages/settings/UsersManagementPage.tsx b/fdev-ffrontend/src/pages/settings/UsersManagementPage.tsx similarity index 100% rename from frontend/src/pages/settings/UsersManagementPage.tsx rename to fdev-ffrontend/src/pages/settings/UsersManagementPage.tsx diff --git a/frontend/src/router/index.tsx b/fdev-ffrontend/src/router/index.tsx similarity index 100% rename from frontend/src/router/index.tsx rename to fdev-ffrontend/src/router/index.tsx diff --git a/frontend/src/services/weatherRecommendationService.ts b/fdev-ffrontend/src/services/weatherRecommendationService.ts similarity index 100% rename from frontend/src/services/weatherRecommendationService.ts rename to fdev-ffrontend/src/services/weatherRecommendationService.ts diff --git a/frontend/src/store/index.ts b/fdev-ffrontend/src/store/index.ts similarity index 100% rename from frontend/src/store/index.ts rename to fdev-ffrontend/src/store/index.ts diff --git a/frontend/src/store/slices/authSlice.ts b/fdev-ffrontend/src/store/slices/authSlice.ts similarity index 100% rename from frontend/src/store/slices/authSlice.ts rename to fdev-ffrontend/src/store/slices/authSlice.ts diff --git a/frontend/src/store/slices/forecastSlice.ts b/fdev-ffrontend/src/store/slices/forecastSlice.ts similarity index 100% rename from frontend/src/store/slices/forecastSlice.ts rename to fdev-ffrontend/src/store/slices/forecastSlice.ts diff --git a/frontend/src/store/slices/tenantSlice.ts b/fdev-ffrontend/src/store/slices/tenantSlice.ts similarity index 100% rename from frontend/src/store/slices/tenantSlice.ts rename to fdev-ffrontend/src/store/slices/tenantSlice.ts diff --git a/fdev-ffrontend/src/styles/globals.css b/fdev-ffrontend/src/styles/globals.css new file mode 100644 index 00000000..ba3a4b6b --- /dev/null +++ b/fdev-ffrontend/src/styles/globals.css @@ -0,0 +1,96 @@ +@import 'tailwindcss/base'; +@import 'tailwindcss/components'; +@import 'tailwindcss/utilities'; + +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Poppins:wght@400;500;600;700&display=swap'); + +/* Base styles */ +* { + box-sizing: border-box; +} + +body { + font-family: 'Inter', system-ui, -apple-system, sans-serif; + line-height: 1.5; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +/* Custom scrollbar */ +::-webkit-scrollbar { + width: 6px; +} + +::-webkit-scrollbar-track { + background: #f1f5f9; +} + +::-webkit-scrollbar-thumb { + background: #cbd5e1; + border-radius: 3px; +} + +::-webkit-scrollbar-thumb:hover { + background: #94a3b8; +} + +/* Focus styles */ +.focus-ring:focus { + outline: none; + box-shadow: 0 0 0 3px rgba(249, 115, 22, 0.1); + border-color: #f97316; +} + +/* Animation classes */ +.animate-fade-in { + animation: fadeIn 0.5s ease-in-out; +} + +.animate-slide-up { + animation: slideUp 0.3s ease-out; +} + +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes slideUp { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Custom components */ +.bakery-card { + @apply bg-white rounded-xl shadow-soft p-6 hover:shadow-medium transition-all duration-200; +} + +.confidence-high { + @apply bg-green-100 text-green-800 border-green-200; +} + +.confidence-medium { + @apply bg-yellow-100 text-yellow-800 border-yellow-200; +} + +.confidence-low { + @apply bg-red-100 text-red-800 border-red-200; +} + +/* Mobile-first responsive design helpers */ +@media (max-width: 640px) { + .mobile-padding { + padding-left: 1rem; + padding-right: 1rem; + } + + .mobile-text-sm { + font-size: 0.875rem; + } +} \ No newline at end of file diff --git a/frontend/src/types/alerts.ts b/fdev-ffrontend/src/types/alerts.ts similarity index 100% rename from frontend/src/types/alerts.ts rename to fdev-ffrontend/src/types/alerts.ts diff --git a/frontend/src/types/weather.ts b/fdev-ffrontend/src/types/weather.ts similarity index 100% rename from frontend/src/types/weather.ts rename to fdev-ffrontend/src/types/weather.ts diff --git a/frontend/src/utils/onboardingRouter.ts b/fdev-ffrontend/src/utils/onboardingRouter.ts similarity index 100% rename from frontend/src/utils/onboardingRouter.ts rename to fdev-ffrontend/src/utils/onboardingRouter.ts diff --git a/fdev-ffrontend/tailwind.config.js b/fdev-ffrontend/tailwind.config.js new file mode 100644 index 00000000..f3714989 --- /dev/null +++ b/fdev-ffrontend/tailwind.config.js @@ -0,0 +1,133 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: [ + "./index.html", + "./src/**/*.{js,ts,jsx,tsx}", + ], + theme: { + extend: { + colors: { + // PanIA Brand Colors + primary: { + 50: '#fff7ed', + 100: '#ffedd5', + 200: '#fed7aa', + 300: '#fdba74', + 400: '#fb923c', + 500: '#f97316', // Main orange + 600: '#ea580c', + 700: '#c2410c', + 800: '#9a3412', + 900: '#7c2d12', + 950: '#431407', + }, + secondary: { + 50: '#f8fafc', + 100: '#f1f5f9', + 200: '#e2e8f0', + 300: '#cbd5e1', + 400: '#94a3b8', + 500: '#64748b', + 600: '#475569', + 700: '#334155', + 800: '#1e293b', + 900: '#0f172a', + 950: '#020617', + }, + // Traffic Light Indicators + success: { + 50: '#f0fdf4', + 100: '#dcfce7', + 500: '#22c55e', + 600: '#16a34a', + 700: '#15803d', + }, + warning: { + 50: '#fffbeb', + 100: '#fef3c7', + 500: '#f59e0b', + 600: '#d97706', + 700: '#b45309', + }, + danger: { + 50: '#fef2f2', + 100: '#fee2e2', + 500: '#ef4444', + 600: '#dc2626', + 700: '#b91c1c', + }, + // Spanish Theme Colors + madrid: { + 50: '#fdf2f8', + 100: '#fce7f3', + 200: '#fbcfe8', + 300: '#f9a8d4', + 400: '#f472b6', + 500: '#ec4899', + 600: '#db2777', + 700: '#be185d', + 800: '#9d174d', + 900: '#831843', + }, + }, + fontFamily: { + 'sans': ['Inter', 'system-ui', 'sans-serif'], + 'display': ['Poppins', 'Inter', 'system-ui', 'sans-serif'], + }, + fontSize: { + 'xs': ['0.75rem', { lineHeight: '1rem' }], + 'sm': ['0.875rem', { lineHeight: '1.25rem' }], + 'base': ['1rem', { lineHeight: '1.5rem' }], + 'lg': ['1.125rem', { lineHeight: '1.75rem' }], + 'xl': ['1.25rem', { lineHeight: '1.75rem' }], + '2xl': ['1.5rem', { lineHeight: '2rem' }], + '3xl': ['1.875rem', { lineHeight: '2.25rem' }], + '4xl': ['2.25rem', { lineHeight: '2.5rem' }], + }, + spacing: { + '18': '4.5rem', + '88': '22rem', + '128': '32rem', + }, + borderRadius: { + 'xl': '0.75rem', + '2xl': '1rem', + '3xl': '1.5rem', + }, + boxShadow: { + 'soft': '0 2px 15px -3px rgba(0, 0, 0, 0.07), 0 10px 20px -2px rgba(0, 0, 0, 0.04)', + 'medium': '0 4px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)', + 'strong': '0 10px 40px -10px rgba(0, 0, 0, 0.15), 0 2px 10px -2px rgba(0, 0, 0, 0.04)', + }, + animation: { + 'fade-in': 'fadeIn 0.5s ease-in-out', + 'slide-up': 'slideUp 0.3s ease-out', + 'slide-down': 'slideDown 0.3s ease-out', + 'scale-in': 'scaleIn 0.2s ease-out', + 'pulse-slow': 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite', + }, + keyframes: { + fadeIn: { + '0%': { opacity: '0' }, + '100%': { opacity: '1' }, + }, + slideUp: { + '0%': { transform: 'translateY(10px)', opacity: '0' }, + '100%': { transform: 'translateY(0)', opacity: '1' }, + }, + slideDown: { + '0%': { transform: 'translateY(-10px)', opacity: '0' }, + '100%': { transform: 'translateY(0)', opacity: '1' }, + }, + scaleIn: { + '0%': { transform: 'scale(0.95)', opacity: '0' }, + '100%': { transform: 'scale(1)', opacity: '1' }, + }, + }, + }, + }, + plugins: [ + require('@tailwindcss/forms'), + require('@tailwindcss/typography'), + ], +} \ No newline at end of file diff --git a/fdev-ffrontend/tsconfig.json b/fdev-ffrontend/tsconfig.json new file mode 100644 index 00000000..2be7d0d5 --- /dev/null +++ b/fdev-ffrontend/tsconfig.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": false, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noFallthroughCasesInSwitch": false, + + /* Path mapping */ + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["src", "vite-env.d.ts"], + "references": [{ "path": "./tsconfig.node.json" }] +} \ No newline at end of file diff --git a/fdev-ffrontend/tsconfig.node.json b/fdev-ffrontend/tsconfig.node.json new file mode 100644 index 00000000..decf631a --- /dev/null +++ b/fdev-ffrontend/tsconfig.node.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true, + "strict": true + }, + "include": ["vite.config.js"] +} \ No newline at end of file diff --git a/frontend/vite-env.d.ts b/fdev-ffrontend/vite-env.d.ts similarity index 100% rename from frontend/vite-env.d.ts rename to fdev-ffrontend/vite-env.d.ts diff --git a/frontend/vite.config.js b/fdev-ffrontend/vite.config.js similarity index 100% rename from frontend/vite.config.js rename to fdev-ffrontend/vite.config.js diff --git a/frontend/.eslintrc.json b/frontend/.eslintrc.json new file mode 100644 index 00000000..0ba8af08 --- /dev/null +++ b/frontend/.eslintrc.json @@ -0,0 +1,25 @@ +{ + "env": { + "browser": true, + "es2020": true + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:react-hooks/recommended" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module" + }, + "plugins": ["react-refresh"], + "rules": { + "react-refresh/only-export-components": [ + "warn", + { "allowConstantExport": true } + ], + "@typescript-eslint/no-explicit-any": "error", + "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }] + } +} \ No newline at end of file diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 00000000..da798a8d --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,41 @@ +# Dependencies +node_modules +.pnp +.pnp.js + +# Testing +coverage + +# Production +dist +dist-ssr + +# Local env files +.env.local +.env.development.local +.env.test.local +.env.production.local +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +# Storybook +storybook-static \ No newline at end of file diff --git a/frontend/.prettierrc b/frontend/.prettierrc new file mode 100644 index 00000000..60dbb133 --- /dev/null +++ b/frontend/.prettierrc @@ -0,0 +1,9 @@ +{ + "semi": true, + "trailingComma": "es5", + "singleQuote": true, + "printWidth": 100, + "tabWidth": 2, + "useTabs": false, + "plugins": ["prettier-plugin-tailwindcss"] +} \ No newline at end of file diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 00000000..02f427de --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,1372 @@ +frontend/ +├── public/ +│ ├── icons/ # PWA icons (multiple sizes) +│ ├── manifest.json # PWA manifest +│ └── sw.js # Service worker +├── src/ +│ ├── components/ +│ │ ├── ui/ # Design system components +│ │ │ ├── Button/ +│ │ │ │ ├── Button.tsx +│ │ │ │ ├── Button.stories.tsx +│ │ │ │ ├── Button.test.tsx +│ │ │ │ └── index.ts +│ │ │ ├── Input/ +│ │ │ ├── Card/ +│ │ │ ├── Modal/ +│ │ │ ├── Table/ +│ │ │ ├── Badge/ +│ │ │ ├── Avatar/ +│ │ │ ├── Tooltip/ +│ │ │ ├── Select/ +│ │ │ ├── DatePicker/ +│ │ │ └── index.ts +│ │ ├── layout/ +│ │ │ ├── AppShell/ +│ │ │ ├── Header/ +│ │ │ ├── Sidebar/ +│ │ │ ├── Breadcrumbs/ +│ │ │ ├── PageHeader/ +│ │ │ └── Footer/ +│ │ ├── domain/ +│ │ │ ├── auth/ +│ │ │ ├── dashboard/ +│ │ │ ├── inventory/ +│ │ │ ├── production/ +│ │ │ ├── sales/ +│ │ │ ├── forecasting/ +│ │ │ ├── analytics/ +│ │ │ └── onboarding/ +│ │ └── shared/ +│ │ ├── LoadingSpinner/ +│ │ ├── EmptyState/ +│ │ ├── ErrorBoundary/ +│ │ ├── ConfirmDialog/ +│ │ └── DataTable/ +│ ├── pages/ +│ │ ├── public/ +│ │ │ ├── LandingPage/ +│ │ │ ├── LoginPage/ +│ │ │ └── RegisterPage/ +│ │ ├── app/ +│ │ │ ├── DashboardPage/ +│ │ │ ├── operations/ +│ │ │ │ ├── InventoryPage/ +│ │ │ │ ├── ProductionPage/ +│ │ │ │ ├── OrdersPage/ +│ │ │ │ ├── ProcurementPage/ +│ │ │ │ └── POSPage/ +│ │ │ ├── analytics/ +│ │ │ │ ├── ForecastingPage/ +│ │ │ │ ├── SalesAnalyticsPage/ +│ │ │ │ ├── PerformancePage/ +│ │ │ │ └── AIInsightsPage/ +│ │ │ ├── data/ +│ │ │ │ ├── WeatherPage/ +│ │ │ │ ├── TrafficPage/ +│ │ │ │ └── EventsPage/ +│ │ │ ├── communications/ +│ │ │ │ ├── NotificationsPage/ +│ │ │ │ ├── AlertsPage/ +│ │ │ │ └── PreferencesPage/ +│ │ │ ├── settings/ +│ │ │ │ ├── TeamPage/ +│ │ │ │ ├── BakeryConfigPage/ +│ │ │ │ ├── TrainingPage/ +│ │ │ │ └── SystemPage/ +│ │ │ └── onboarding/ +│ │ │ ├── UploadPage/ +│ │ │ ├── AnalysisPage/ +│ │ │ ├── ReviewPage/ +│ │ │ └── SetupPage/ +│ │ └── index.ts +│ ├── hooks/ +│ │ ├── api/ +│ │ │ ├── useAuth.ts +│ │ │ ├── useInventory.ts +│ │ │ ├── useProduction.ts +│ │ │ ├── useForecasting.ts +│ │ │ ├── useSales.ts +│ │ │ ├── useSSE.ts +│ │ │ └── useWebSocket.ts +│ │ ├── ui/ +│ │ │ ├── useModal.ts +│ │ │ ├── useTheme.ts +│ │ │ ├── useToast.ts +│ │ │ └── useDebounce.ts +│ │ └── business/ +│ │ ├── useBakeryWorkflow.ts +│ │ ├── useProductionSchedule.ts +│ │ └── useAlerts.ts +│ ├── stores/ +│ │ ├── auth.store.ts +│ │ ├── ui.store.ts +│ │ ├── bakery.store.ts +│ │ ├── alerts.store.ts +│ │ └── index.ts +│ ├── services/ +│ │ ├── api/ +│ │ │ ├── client.ts +│ │ │ ├── auth.service.ts +│ │ │ ├── tenant.service.ts +│ │ │ ├── sales.service.ts +│ │ │ ├── inventory.service.ts +│ │ │ ├── forecasting.service.ts +│ │ │ ├── training.service.ts +│ │ │ ├── data.service.ts +│ │ │ ├── notification.service.ts +│ │ │ ├── orders.service.ts +│ │ │ ├── production.service.ts +│ │ │ ├── procurement.service.ts +│ │ │ └── pos.service.ts +│ │ ├── realtime/ +│ │ │ ├── sse.service.ts +│ │ │ └── websocket.service.ts +│ │ └── utils/ +│ │ ├── storage.service.ts +│ │ └── cache.service.ts +│ ├── router/ +│ │ ├── AppRouter.tsx +│ │ ├── ProtectedRoute.tsx +│ │ ├── routes.config.ts +│ │ └── index.ts +│ ├── styles/ +│ │ ├── globals.css +│ │ ├── components.css +│ │ ├── animations.css +│ │ └── themes/ +│ │ ├── light.css +│ │ └── dark.css +│ ├── utils/ +│ │ ├── format.ts +│ │ ├── validation.ts +│ │ ├── constants.ts +│ │ ├── date.ts +│ │ └── currency.ts +│ ├── types/ +│ │ ├── auth.types.ts +│ │ ├── inventory.types.ts +│ │ ├── production.types.ts +│ │ ├── sales.types.ts +│ │ ├── forecasting.types.ts +│ │ ├── api.types.ts +│ │ └── index.ts +│ ├── locales/ +│ │ ├── es/ +│ │ │ ├── common.json +│ │ │ ├── auth.json +│ │ │ ├── inventory.json +│ │ │ └── errors.json +│ │ └── index.ts +│ ├── App.tsx +│ ├── main.tsx +│ └── vite-env.d.ts +├── .env.example +├── .gitignore +├── index.html +├── package.json +├── tsconfig.json +├── vite.config.ts +├── tailwind.config.js +└── README.md + + +# 🚀 Bakery AI Platform - Complete Frontend Rebuild from Scratch + +## 📋 Project Context + +You are working on **Bakery AI (PanIA)**, an intelligent platform for Madrid's bakery ecosystem that provides AI-powered demand forecasting, inventory management, and automated onboarding. The current frontend codebase has become disorganized and unmaintainable. **Your task is to build a completely new frontend from scratch** using modern best practices while preserving the existing backend API infrastructure. + +## 🎯 Mission: Complete Frontend Rebuild + +### Current Situation +- ❌ **Frontend architecture is a mess** - components, pages, and logic scattered without organization +- ❌ **No consistent design system** - UI components lack standardization +- ❌ **Poor code structure** - files and folders organized without logic +- ❌ **Outdated patterns** - not following modern React/TypeScript best practices +- ❌ **Poor user experience** - interface doesn't meet 2024-2025 standards + +### Your Objective +✅ **Build a brand new frontend from the ground up** that is: +- Modern, maintainable, and scalable +- Following 2024-2025 UX/UI best practices +- Properly organized with clear architecture +- Type-safe and performant +- Mobile-first and accessible + +## 🏗️ What to Preserve vs Rebuild + +### ✅ **PRESERVE: Existing Backend Microservices Architecture** + +The backend has a comprehensive microservices architecture that should be fully integrated: + +``` +🏗️ Backend Microservices: +├── 🔐 auth/ # Authentication & user management +├── 🏢 tenant/ # Multi-tenant organization management +├── 📊 sales/ # Sales data processing & onboarding +├── 📦 inventory/ # Product catalog & stock management +├── 📈 forecasting/ # AI demand predictions & analytics +├── 🎯 training/ # ML model training & management +├── 🌍 data/ # External data (weather, traffic) +├── 📧 notification/ # Email & WhatsApp alerts +├── 📋 orders/ # Order management & tracking +├── 🏭 production/ # Production planning & batches +├── 🛍️ procurement/ # Purchase orders & supplier management +├── 📍 pos/ # Point of sale integrations +└── 🌐 gateway/ # API gateway & routing +``` + +**Complete API Endpoint Mapping:** + +### 🔐 Authentication Service +``` +POST /api/v1/auth/register +POST /api/v1/auth/login +POST /api/v1/auth/refresh +DELETE /api/v1/auth/logout +GET /api/v1/auth/me +PUT /api/v1/auth/profile +POST /api/v1/auth/password/reset +PUT /api/v1/auth/password/change +``` + +### 🏢 Tenant Service +``` +POST /api/v1/tenants/register +GET /api/v1/tenants/{tenant_id} +PUT /api/v1/tenants/{tenant_id} +DELETE /api/v1/tenants/{tenant_id} +GET /api/v1/tenants/{tenant_id}/members +POST /api/v1/tenants/{tenant_id}/invite +DELETE /api/v1/tenants/{tenant_id}/members/{user_id} +PATCH /api/v1/tenants/{tenant_id}/members/{user_id} +GET /api/v1/tenants/{tenant_id}/stats +GET /api/v1/tenants/user/{user_id} +``` + +### 📊 Sales Service (includes Onboarding & Analytics) +``` +# Sales Data Management +GET /api/v1/tenants/{tenant_id}/sales +POST /api/v1/tenants/{tenant_id}/sales/upload +GET /api/v1/tenants/{tenant_id}/sales/summary +GET /api/v1/tenants/{tenant_id}/sales/trends + +# AI Onboarding Automation +POST /api/v1/tenants/{tenant_id}/onboarding/analyze +POST /api/v1/tenants/{tenant_id}/onboarding/create-inventory +POST /api/v1/tenants/{tenant_id}/onboarding/import-sales +GET /api/v1/tenants/{tenant_id}/onboarding/business-model-guide + +# Analytics +GET /api/v1/tenants/{tenant_id}/analytics/revenue +GET /api/v1/tenants/{tenant_id}/analytics/products +GET /api/v1/tenants/{tenant_id}/analytics/trends +GET /api/v1/tenants/{tenant_id}/analytics/reports +``` + +### 📦 Inventory Service (Product Catalog & Stock Management) +``` +# Product Management +GET /api/v1/tenants/{tenant_id}/inventory/products +POST /api/v1/tenants/{tenant_id}/inventory/products +GET /api/v1/tenants/{tenant_id}/inventory/products/{product_id} +PUT /api/v1/tenants/{tenant_id}/inventory/products/{product_id} +DELETE /api/v1/tenants/{tenant_id}/inventory/products/{product_id} + +# Stock Management +GET /api/v1/tenants/{tenant_id}/inventory/stock +POST /api/v1/tenants/{tenant_id}/inventory/stock/adjustment +GET /api/v1/tenants/{tenant_id}/inventory/stock/movements +GET /api/v1/tenants/{tenant_id}/inventory/stock/alerts + +# AI Classification (300+ Bakery Products) +POST /api/v1/tenants/{tenant_id}/inventory/classify-product +POST /api/v1/tenants/{tenant_id}/inventory/classify-products-batch + +# Import/Export +POST /api/v1/tenants/{tenant_id}/inventory/import +GET /api/v1/tenants/{tenant_id}/inventory/export +GET /api/v1/tenants/{tenant_id}/inventory/search +``` + +### 📈 Forecasting Service (AI Demand Predictions) +``` +GET /api/v1/tenants/{tenant_id}/forecasts +POST /api/v1/tenants/{tenant_id}/forecasts/generate +GET /api/v1/tenants/{tenant_id}/forecasts/{forecast_id} +GET /api/v1/tenants/{tenant_id}/predictions +GET /api/v1/tenants/{tenant_id}/predictions/daily +GET /api/v1/tenants/{tenant_id}/predictions/weekly +GET /api/v1/tenants/{tenant_id}/predictions/alerts +POST /api/v1/tenants/{tenant_id}/predictions/validate +``` + +### 🎯 Training Service (ML Model Management) +``` +POST /api/v1/tenants/{tenant_id}/training/start +GET /api/v1/tenants/{tenant_id}/training/status +GET /api/v1/tenants/{tenant_id}/training/history +POST /api/v1/tenants/{tenant_id}/training/stop +GET /api/v1/tenants/{tenant_id}/models +GET /api/v1/tenants/{tenant_id}/models/{model_id} +GET /api/v1/tenants/{tenant_id}/models/{model_id}/metrics +``` + +### 🌍 Data Service (External Data Integration) +``` +GET /api/v1/tenants/{tenant_id}/weather +GET /api/v1/tenants/{tenant_id}/weather/forecast +GET /api/v1/tenants/{tenant_id}/traffic +GET /api/v1/tenants/{tenant_id}/traffic/patterns +GET /api/v1/tenants/{tenant_id}/holidays +GET /api/v1/tenants/{tenant_id}/events +``` + +### 📧 Notification Service +``` +POST /api/v1/tenants/{tenant_id}/notifications/send +GET /api/v1/tenants/{tenant_id}/notifications/history +GET /api/v1/tenants/{tenant_id}/notifications/preferences +PUT /api/v1/tenants/{tenant_id}/notifications/preferences +POST /api/v1/tenants/{tenant_id}/notifications/test +``` + +### 📋 Orders Service +``` +GET /api/v1/tenants/{tenant_id}/orders +POST /api/v1/tenants/{tenant_id}/orders +GET /api/v1/tenants/{tenant_id}/orders/{order_id} +PUT /api/v1/tenants/{tenant_id}/orders/{order_id} +DELETE /api/v1/tenants/{tenant_id}/orders/{order_id} +GET /api/v1/tenants/{tenant_id}/orders/dashboard-summary +POST /api/v1/tenants/{tenant_id}/orders/{order_id}/status +``` + +### 🏭 Production Service +``` +GET /api/v1/tenants/{tenant_id}/production/batches +POST /api/v1/tenants/{tenant_id}/production/batches +GET /api/v1/tenants/{tenant_id}/production/batches/{batch_id} +PUT /api/v1/tenants/{tenant_id}/production/batches/{batch_id} +GET /api/v1/tenants/{tenant_id}/production/schedule +POST /api/v1/tenants/{tenant_id}/production/schedule +GET /api/v1/tenants/{tenant_id}/production/equipment +``` + +### 🛍️ Procurement Service +``` +GET /api/v1/tenants/{tenant_id}/procurement/plans +POST /api/v1/tenants/{tenant_id}/procurement/plans/generate +GET /api/v1/tenants/{tenant_id}/procurement/plans/{plan_id} +PUT /api/v1/tenants/{tenant_id}/procurement/plans/{plan_id}/status +GET /api/v1/tenants/{tenant_id}/procurement/suppliers +GET /api/v1/tenants/{tenant_id}/procurement/requirements +``` + +### 📍 POS Service (Point of Sale Integration) +``` +GET /api/v1/tenants/{tenant_id}/pos/configurations +POST /api/v1/tenants/{tenant_id}/pos/configurations +GET /api/v1/tenants/{tenant_id}/pos/transactions +POST /api/v1/tenants/{tenant_id}/pos/sync +GET /api/v1/tenants/{tenant_id}/pos/sync-status +``` + +### 📡 **Real-Time SSE Infrastructure** + +The backend includes a sophisticated Server-Sent Events (SSE) system for real-time alerts and recommendations: + +#### SSE Service Architecture: +``` +🔄 Real-Time Stream Flow: +Alert/Recommendation Generated → Alert Processor → Redis Channel → SSE Service → Frontend Dashboard + +📡 SSE Endpoints: +GET /api/v1/sse/alerts/stream/{tenant_id} # Real-time alert & recommendation stream + +🚨 Alert Types & Severity Levels: +- URGENT: All channels (WhatsApp, Email, Push, Dashboard) +- HIGH: WhatsApp + Email (6AM-10PM), Email only (night) +- MEDIUM: Email (business hours 7AM-8PM), Dashboard always +- LOW: Dashboard only + +💡 Recommendation Types: +- Production optimization suggestions +- Inventory reorder recommendations +- Demand forecast insights +- Cost optimization tips +- Equipment maintenance alerts +``` + +#### Channel Routing Logic: +```typescript +// Backend determines channels by severity & time: +const channels = { + urgent: ['whatsapp', 'email', 'push', 'dashboard'], // Immediate + high: ['whatsapp', 'email', 'dashboard'], // Extended hours + medium: ['email', 'dashboard'], // Business hours + low: ['dashboard'] // Always visible +}; + +// Frontend receives via SSE: +eventSource.addEventListener('alert', (event) => { + const alert = JSON.parse(event.data); + // Auto-display urgent/high alerts + // Browser notifications for urgent alerts + // Sound alerts for critical items +}); + +eventSource.addEventListener('recommendation', (event) => { + const recommendation = JSON.parse(event.data); + // Display in recommendations panel + // Non-intrusive UI integration +}); +``` + +#### SSE Message Format: +```typescript +interface SSEMessage { + id: string; + item_type: 'alert' | 'recommendation'; + type: string; // Specific alert type + severity: 'urgent' | 'high' | 'medium' | 'low'; + title: string; + message: string; + actions: string[]; // Suggested actions + metadata: Record; // Context data + timestamp: string; + status: 'active' | 'acknowledged' | 'resolved'; +} +``` + +### 🔄 **Real-Time WebSocket Training Monitoring** + +In addition to SSE alerts, the system includes dedicated WebSocket connections for ML model training progress: + +#### WebSocket Training Architecture: +``` +🎯 Training WebSocket Flow: +Training Job Started → WebSocket Connection → Real-time Progress → Completion Status + +📡 WebSocket Endpoints: +WS /api/v1/ws/tenants/{tenant_id}/training/jobs/{job_id}/live # Real-time training progress + +🤖 ML Training Process Types: +- Data validation and preprocessing +- Prophet model training (demand forecasting) +- Model performance evaluation (MAPE, MAE, RMSE, R2 score) +- Model storage and versioning +- Training completion with metrics +``` + +#### WebSocket Message Types: +```typescript +interface TrainingWebSocketMessage { + // Progress Updates + type: 'training_progress'; + job_id: string; + progress: { + percentage: number; // 0-100 + current_step: string; // "Data validation", "Model training", etc. + products_completed: number; // Products processed so far + products_total: number; // Total products to process + estimated_time_remaining: number; // Seconds + started_at: string; + }; + + // Completion Events + type: 'training_completed'; + job_id: string; + results: { + successful_trainings: number; + failed_trainings: number; + models_created: string[]; + performance_metrics: { + accuracy: number; // R2 score + mape: number; // Mean Absolute Percentage Error + mae: number; // Mean Absolute Error + rmse: number; // Root Mean Square Error + }; + training_duration: number; // Total training time in seconds + }; + + // Error Events + type: 'training_error'; + job_id: string; + error: string; + timestamp: string; +} +``` + +#### Training Configuration: +```typescript +// Backend training settings optimized for bakery data: +const trainingConfig = { + maxTrainingTimeMinutes: 30, // 30 minute timeout + maxConcurrentJobs: 3, // Max 3 parallel training jobs + minTrainingDataDays: 30, // Require 30+ days of sales data + + // Prophet algorithm parameters for bakery forecasting: + seasonalityMode: 'additive', // Better for bakery patterns + changePointPriorScale: 0.05, // Sensitivity to trend changes + seasonalityPriorScale: 10.0, // Strength of seasonal patterns + holidaysPriorScale: 10.0, // Spanish/Madrid holiday impact + + // Spanish context: + enableSpanishHolidays: true, // National holidays + enableMadridHolidays: true, // Local Madrid holidays + enableCustomHolidays: true // Bakery-specific holidays +}; +``` + +#### WebSocket Integration Features: +- ✅ **Real-Time Progress**: Live percentage updates during training +- ✅ **Step-by-Step Monitoring**: Current training phase visibility +- ✅ **Performance Metrics**: Live model accuracy metrics (MAPE, MAE, RMSE) +- ✅ **Connection Management**: Auto-reconnection with JWT authentication +- ✅ **Tenant Isolation**: Secure per-tenant training job monitoring +- ✅ **Completion Detection**: Automatic WebSocket closure on job completion +- ✅ **Error Handling**: Comprehensive error reporting and recovery + +### ❌ **REBUILD COMPLETELY: Frontend** +Replace the entire frontend directory structure and codebase: + +```bash +# Remove the old frontend completely: +rm -rf frontend/src/components/ +rm -rf frontend/src/pages/ +rm -rf frontend/src/hooks/ +rm -rf frontend/src/styles/ +rm -rf frontend/src/router/ +# Keep only: package.json, vite.config.ts, index.html +``` + +## 📱 New Frontend Requirements + +### Target Users & Use Cases +1. **Small Bakery Owners (Madrid)**: Mobile-first, simple workflows, 4AM production schedules +2. **Operations Managers**: Desktop analytics, detailed reports, multi-location oversight +3. **Production Staff**: Tablet-friendly, quick actions, hands-free when possible + +### Core User Workflows to Support +``` +1. Morning Production Planning (4:00 AM): + Login → Check Forecasts → Plan Production → Update Inventory + +2. Daily Operations Management: + Monitor Sales → Adjust Production → Track Inventory → Handle Alerts + +3. Weekly Business Analysis: + Review Performance → Analyze Trends → Plan Improvements → Generate Reports + +4. New User Onboarding: + Register → Upload Sales Data → AI Setup → Start Forecasting (5-10 minutes) +``` + +## 🎨 Modern Frontend Architecture to Build + +### Tech Stack (Keep Existing Dependencies) +```json +{ + "react": "^18.2.0", + "typescript": "^5.0.2", + "vite": "^4.4.5", + "react-router-dom": "^6.15.0", + "@reduxjs/toolkit": "^1.9.5", + "tailwindcss": "^3.3.0", + "react-hook-form": "^7.45.4", + "lucide-react": "^0.263.1", + "recharts": "^2.8.0", + "zod": "^3.22.2" +} +``` + +### 🏗️ New Architecture Pattern: Service-Aligned Hub-and-Spoke + +Based on the comprehensive backend microservices, organize the frontend around service domains: + +``` +🏠 Central Dashboard +├── 🔐 Authentication Hub +│ ├── Login/Register +│ ├── Profile Management +│ └── Password Reset +├── 🥖 Operations Hub +│ ├── 📦 Inventory (Products, Stock, Alerts) +│ ├── 🏭 Production (Batches, Schedule, Equipment) +│ ├── 📋 Orders (Management, Status, Tracking) +│ ├── 🛍️ Procurement (Plans, Suppliers, Requirements) +│ └── 📍 POS (Integration, Sync, Transactions) +├── 📊 Analytics Hub +│ ├── 🔮 Forecasting (Predictions, Models, Validation) +│ ├── 📈 Sales Analytics (Trends, Reports, Revenue) +│ ├── 📉 Performance KPIs (Metrics, Dashboards) +│ └── 🤖 AI Insights (Classifications, Recommendations) +├── 🌍 Data Hub +│ ├── Weather Integration (AEMET API) +│ ├── Traffic Patterns +│ ├── External Events +│ └── Market Data +├── 📧 Communications Hub +│ ├── Notifications (Email, WhatsApp) +│ ├── Alert Management +│ └── Preference Settings +└── ⚙️ Settings Hub + ├── 👥 Team Management (Members, Roles, Invites) + ├── 🏢 Bakery Configuration (Business Model, Settings) + ├── 🎯 Training (ML Models, Status, History) + └── 🔧 System Settings (API Keys, Integrations) +``` + +### 🧱 Component Architecture to Build + +```typescript +// Design System Foundation +components/ui/ +├── Button/ # Primary, secondary, ghost variants +├── Input/ # Text, email, number with validation +├── Card/ # Elevation levels, padding variants +├── Modal/ # Sizes, overlay, animations +├── Table/ # Sorting, filtering, pagination +├── Form/ # React Hook Form + Zod integration +├── Badge/ # Status indicators, color variants +├── Avatar/ # User profile images, initials +└── index.ts # Export all UI components + +// Layout Components +components/layout/ +├── AppShell/ # Main application container +├── Header/ # Top navigation, user menu +├── Sidebar/ # Main navigation, collapsible +├── Breadcrumbs/ # Navigation trail +├── PageHeader/ # Page title, actions +└── Footer/ # Copyright, links + +// Business Components +components/domain/ +├── auth/ # Login, register, password reset +├── dashboard/ # KPI cards, charts, activity feed +├── inventory/ # Product cards, stock alerts +├── production/ # Batch cards, equipment status +├── sales/ # Transaction lists, POS integration +├── forecasting/ # Prediction charts, confidence indicators +└── onboarding/ # Step-by-step setup wizard + +// Shared Components +components/shared/ +├── LoadingSpinner/ # Various loading states +├── EmptyState/ # No data illustrations +├── ErrorBoundary/ # Error handling with retry +├── ConfirmDialog/ # Confirmation modals +└── DataTable/ # Reusable table with features +``` + +## 🎨 2024-2025 Design System to Implement + +### Visual Identity (Bakery Theme) +```css +/* Color Palette */ +:root { + /* Primary: Warm Orange (artisan bread) */ + --orange-50: #fff7ed; + --orange-500: #f97316; + --orange-900: #9a3412; + + /* Secondary: Golden Wheat */ + --amber-50: #fffbeb; + --amber-500: #f59e0b; + --amber-900: #92400e; + + /* Success: Fresh Green */ + --green-500: #22c55e; + + /* Warning: Alert Orange */ + --red-500: #ef4444; + + /* Neutral: Modern Grays */ + --slate-50: #f8fafc; + --slate-500: #64748b; + --slate-900: #0f172a; +} + +/* Typography System */ +.text-display { font-size: 2.25rem; font-weight: 700; } /* Page headlines */ +.text-heading { font-size: 1.875rem; font-weight: 600; } /* Section titles */ +.text-title { font-size: 1.5rem; font-weight: 600; } /* Card titles */ +.text-body { font-size: 1rem; font-weight: 400; } /* Body text */ +.text-caption { font-size: 0.875rem; font-weight: 400; } /* Captions */ + +/* Spacing System (8px grid) */ +.space-xs { margin: 0.5rem; } /* 8px */ +.space-sm { margin: 1rem; } /* 16px */ +.space-md { margin: 1.5rem; } /* 24px */ +.space-lg { margin: 2rem; } /* 32px */ +.space-xl { margin: 3rem; } /* 48px */ +``` + +### Modern UI Patterns to Implement + +1. **Glassmorphism Cards**: +```css +.glass-card { + background: rgba(255, 255, 255, 0.25); + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.18); + border-radius: 12px; +} +``` + +2. **Neumorphism Buttons**: +```css +.neu-button { + background: linear-gradient(145deg, #f0f0f0, #cacaca); + box-shadow: 20px 20px 60px #bebebe, -20px -20px 60px #ffffff; +} +``` + +3. **Micro-Interactions**: +- Hover state transitions (200ms ease) +- Loading button animations +- Form field focus effects +- Success/error state changes + +## 📱 Mobile-First Design Requirements + +### Responsive Breakpoints +```css +/* Mobile First Approach */ +.container { + padding: 1rem; /* Mobile: 16px */ +} + +@media (min-width: 640px) { /* Tablet */ + .container { padding: 1.5rem; } +} + +@media (min-width: 1024px) { /* Desktop */ + .container { padding: 2rem; } +} + +@media (min-width: 1280px) { /* Large Desktop */ + .container { max-width: 1200px; margin: 0 auto; } +} +``` + +### Touch-Optimized Interactions +- **44px minimum touch targets** for all buttons/links +- **Swipe gestures** for mobile navigation +- **Pull-to-refresh** on data lists +- **Long press** for context menus +- **Double-tap** for quick actions + +### Progressive Web App Features +```typescript +// Implement PWA capabilities: +- Service Worker for offline functionality +- Web App Manifest for home screen install +- Push notifications for alerts +- Background sync for data updates +- Cache strategies for fast loading +``` + +## 🤖 AI-Enhanced UX Features + +### Intelligent Interface Elements +1. **Smart Search**: Predictive suggestions based on context +2. **AI Recommendations**: Contextual tips and insights +3. **Auto-Complete**: Forms pre-filled with AI predictions +4. **Anomaly Highlights**: Visual indicators for unusual data +5. **Voice Commands**: "Show today's production plan" + +### Conversational Interface Components +```typescript +// AI Assistant Components to Build: + // Floating chat widget + // Predictive search bar + // Smart help tooltips + // Speech-to-text input + // AI-generated insights +``` + +## 🔄 State Management Architecture + +### Modern State Pattern with Zustand +```typescript +// Replace Redux with focused Zustand stores: + +// Authentication State +interface AuthState { + user: User | null; + isAuthenticated: boolean; + login: (credentials: LoginRequest) => Promise; + logout: () => void; +} + +// UI State +interface UIState { + sidebarOpen: boolean; + theme: 'light' | 'dark'; + currentModal: string | null; + toggleSidebar: () => void; + setTheme: (theme: 'light' | 'dark') => void; +} + +// Business State +interface BakeryState { + currentTenant: Tenant | null; + businessModel: 'individual' | 'central'; + notifications: Notification[]; + setTenant: (tenant: Tenant) => void; +} +``` + +### API Integration with React Query +```typescript +// Replace custom hooks with React Query: +import { useQuery, useMutation } from '@tanstack/react-query'; + +export const useInventory = (tenantId: string) => { + return useQuery({ + queryKey: ['inventory', tenantId], + queryFn: () => apiClient.get(`/tenants/${tenantId}/inventory/products`), + staleTime: 5 * 60 * 1000, // 5 minutes + cacheTime: 10 * 60 * 1000, // 10 minutes + }); +}; + +export const useCreateProduct = () => { + return useMutation({ + mutationFn: (product: CreateProductRequest) => + apiClient.post('/inventory/products', product), + onSuccess: () => { + queryClient.invalidateQueries(['inventory']); + }, + }); +}; +``` + +## 🛣️ Routing & Navigation Architecture + +### URL-Based Navigation with Service-Aligned Structure +```typescript +// Clean, RESTful URL structure aligned with backend services: +const routes = [ + // Public Routes + '/', # Landing page + '/auth/login', # Authentication + '/auth/register', + + // Protected Application Routes + '/app', # Redirect to dashboard + '/app/dashboard', # Cross-service dashboard + + // Operations Hub (aligned with backend services) + '/app/operations', # Operations hub overview + '/app/operations/inventory', # Inventory service frontend + '/app/operations/inventory/products', # Product catalog + '/app/operations/inventory/stock', # Stock management + '/app/operations/inventory/alerts', # Low stock alerts + '/app/operations/production', # Production service frontend + '/app/operations/production/batches', # Production batches + '/app/operations/production/schedule', # Production scheduling + '/app/operations/orders', # Orders service frontend + '/app/operations/orders/management', # Order management + '/app/operations/orders/tracking', # Order status tracking + '/app/operations/procurement', # Procurement service frontend + '/app/operations/procurement/plans', # Procurement planning + '/app/operations/procurement/suppliers', # Supplier management + '/app/operations/pos', # POS service frontend + '/app/operations/pos/integration', # POS configuration + '/app/operations/pos/transactions', # Transaction sync + + // Analytics Hub (forecasting + sales analytics) + '/app/analytics', # Analytics hub overview + '/app/analytics/forecasting', # Forecasting service frontend + '/app/analytics/forecasting/predictions', # AI demand predictions + '/app/analytics/forecasting/models', # ML model management + '/app/analytics/sales', # Sales analytics + '/app/analytics/sales/trends', # Sales trend analysis + '/app/analytics/sales/reports', # Sales reporting + '/app/analytics/performance', # Performance KPIs + + // Data Hub (external data integration) + '/app/data', # Data hub overview + '/app/data/weather', # Weather integration (AEMET) + '/app/data/traffic', # Traffic patterns + '/app/data/events', # External events + + // Communications Hub + '/app/communications', # Communications overview + '/app/communications/notifications', # Notification management + '/app/communications/alerts', # Alert configuration + '/app/communications/preferences', # Communication preferences + + // Settings Hub + '/app/settings', # Settings hub overview + '/app/settings/team', # Tenant service frontend + '/app/settings/team/members', # Team member management + '/app/settings/team/roles', # Role management + '/app/settings/bakery', # Bakery configuration + '/app/settings/training', # Training service frontend + '/app/settings/training/models', # ML model settings + '/app/settings/training/history', # Training history + '/app/settings/system', # System configuration + + // Onboarding (special workflow) + '/app/onboarding', # AI-powered onboarding + '/app/onboarding/upload', # Sales data upload + '/app/onboarding/analysis', # AI analysis phase + '/app/onboarding/review', # Review AI suggestions + '/app/onboarding/setup', # Complete setup +]; + +// Context-aware navigation based on backend service responses + + + +``` + +### Context-Aware Navigation +```typescript +// Smart navigation based on user context: +- Bakery type (individual vs central) determines available features +- User role (owner, manager, staff) controls access levels +- Business model affects workflow organization +- Mobile vs desktop changes navigation patterns +``` + +## 📊 Data Visualization & Analytics + +### Chart Components with Recharts +```typescript +// Standardized chart components: + + + + + +``` + +### Dashboard Widget System +```typescript +// Modular dashboard widgets: + + + + + + + + + + + + + +``` + +## 🔐 Authentication & Authorization + +### Modern Auth Flow +```typescript +// JWT-based authentication with refresh tokens: +interface AuthContext { + user: User | null; + isLoading: boolean; + login: (email: string, password: string) => Promise; + register: (data: RegisterRequest) => Promise; + logout: () => void; + refreshToken: () => Promise; +} + +// Route protection: + + + +``` + +### Permission-Based UI +```typescript +// Components adapt based on user permissions: + + Add Product + +``` + +## 🧪 Form Management & Validation + +### React Hook Form + Zod Integration +```typescript +// Type-safe forms with validation: +const productSchema = z.object({ + name: z.string().min(1, 'Product name required'), + category: z.enum(['bread', 'pastry', 'ingredient']), + price: z.number().positive('Price must be positive'), +}); + +const CreateProductForm = () => { + const form = useForm({ + resolver: zodResolver(productSchema), + defaultValues: { name: '', category: 'bread', price: 0 } + }); + + return ( +
+ + + + + ); +}; +``` + +## 📁 New Project Structure to Build + +``` +frontend/ +├── public/ +│ ├── icons/ # PWA icons +│ ├── manifest.json # PWA manifest +│ └── sw.js # Service worker +├── src/ +│ ├── components/ # Component library +│ │ ├── ui/ # Design system components +│ │ │ ├── Button/ +│ │ │ │ ├── Button.tsx +│ │ │ │ ├── Button.stories.tsx +│ │ │ │ ├── Button.test.tsx +│ │ │ │ └── index.ts +│ │ │ ├── Input/ +│ │ │ ├── Card/ +│ │ │ └── index.ts # Export all UI components +│ │ ├── layout/ # Layout components +│ │ │ ├── AppShell/ +│ │ │ ├── Header/ +│ │ │ ├── Sidebar/ +│ │ │ └── index.ts +│ │ ├── domain/ # Business components +│ │ │ ├── auth/ +│ │ │ ├── dashboard/ +│ │ │ ├── inventory/ +│ │ │ ├── production/ +│ │ │ └── index.ts +│ │ └── shared/ # Shared utilities +│ │ ├── LoadingSpinner/ +│ │ ├── ErrorBoundary/ +│ │ └── index.ts +│ ├── pages/ # Page components +│ │ ├── LandingPage/ +│ │ ├── LoginPage/ +│ │ ├── DashboardPage/ +│ │ ├── InventoryPage/ +│ │ └── index.ts +│ ├── hooks/ # Custom hooks +│ │ ├── api/ # API hooks (React Query) +│ │ │ ├── useAuth.ts +│ │ │ ├── useInventory.ts +│ │ │ └── index.ts +│ │ ├── ui/ # UI state hooks +│ │ │ ├── useModal.ts +│ │ │ ├── useTheme.ts +│ │ │ └── index.ts +│ │ └── business/ # Business logic hooks +│ │ ├── useBakeryWorkflow.ts +│ │ └── index.ts +│ ├── stores/ # State management +│ │ ├── auth.store.ts # Zustand stores +│ │ ├── ui.store.ts +│ │ ├── bakery.store.ts +│ │ └── index.ts +│ ├── services/ # API services +│ │ ├── api.client.ts # HTTP client setup +│ │ ├── auth.service.ts # Authentication API +│ │ ├── inventory.service.ts # Inventory API +│ │ └── index.ts +│ ├── router/ # Routing configuration +│ │ ├── AppRouter.tsx # Main router setup +│ │ ├── ProtectedRoute.tsx # Route guards +│ │ └── index.ts +│ ├── styles/ # Global styles +│ │ ├── globals.css # Global CSS +│ │ ├── components.css # Component styles +│ │ └── animations.css # Animation utilities +│ ├── utils/ # Helper functions +│ │ ├── format.ts # Data formatting +│ │ ├── validation.ts # Validation helpers +│ │ ├── constants.ts # App constants +│ │ └── index.ts +│ ├── types/ # TypeScript definitions +│ │ ├── auth.types.ts +│ │ ├── inventory.types.ts +│ │ ├── api.types.ts +│ │ └── index.ts +│ ├── App.tsx # Root component +│ ├── main.tsx # Application entry +│ └── vite-env.d.ts # Vite types +├── package.json +├── vite.config.ts +├── tailwind.config.js +├── tsconfig.json +└── README.md +``` + +## 🎯 Implementation Phases + +### Phase 1: Foundation (Week 1-2) +1. **Setup new project structure** with proper folder organization +2. **Build design system** - UI components with Storybook +3. **Implement authentication flow** - login, register, JWT handling +4. **Create routing structure** - React Router with protected routes +5. **Setup state management** - Zustand stores for auth, UI, business logic + +### Phase 2: Core Features (Week 3-4) +1. **Dashboard implementation** - KPI cards, charts, activity feed +2. **Inventory management** - product lists, stock levels, alerts +3. **Production planning** - batch scheduling, recipe management +4. **Sales tracking** - transaction history, POS integration +5. **Mobile optimization** - responsive design, touch interactions + +### Phase 3: Advanced Features (Week 5-6) +1. **Analytics hub** - forecasting charts, trend analysis, reports +2. **AI integration** - smart suggestions, predictive features +3. **PWA capabilities** - offline mode, push notifications +4. **Performance optimization** - lazy loading, caching strategies +5. **Accessibility audit** - WCAG 2.2 compliance, keyboard navigation + +### Phase 4: Polish & Launch (Week 7-8) +1. **UI/UX refinement** - micro-interactions, animations +2. **Testing implementation** - unit tests, integration tests +3. **Documentation** - component docs, user guides +4. **Performance monitoring** - analytics, error tracking +5. **Production deployment** - CI/CD pipeline, monitoring + +## 🔧 Development Guidelines + +### Code Quality Standards +```typescript +// TypeScript strict mode with no 'any' +interface StrictTyping { + user: User; // ✅ Proper typing + data: unknown; // ✅ Use unknown for uncertain types + error: ApiError; // ✅ Define error types + // avoid: data: any; // ❌ Never use 'any' +} + +// Component composition patterns: +const ProductCard = ({ product, onEdit, onDelete }: ProductCardProps) => { + return ( + + + {product.name} + + + + + + + + + + ); +}; +``` + +### Performance Best Practices +```typescript +// Lazy loading for code splitting: +const AnalyticsPage = lazy(() => import('./pages/AnalyticsPage')); + +// Memoization for expensive calculations: +const expensiveCalculation = useMemo(() => { + return processLargeDataset(data); +}, [data]); + +// Debounced search: +const debouncedSearch = useDebounce(searchTerm, 300); +``` + +### Accessibility Requirements +```typescript +// WCAG 2.2 AA compliance: + + +// Focus management: +const DialogComponent = () => { + const focusRef = useRef(null); + + useEffect(() => { + focusRef.current?.focus(); + }, []); + + return ( + + + + ); +}; +``` + +## 📋 Specific Business Requirements + +### Bakery-Specific Features +1. **Dual Business Model Support**: + - Individual bakeries: On-site production, direct sales + - Central production: Mass production, multi-outlet distribution + +2. **Production Scheduling**: + - Early morning workflows (4:00-6:00 AM) + - Recipe scaling and ingredient calculations + - Equipment scheduling and capacity planning + +3. **Inventory Management**: + - Ingredient tracking with expiration dates + - Finished product inventory with shelf-life + - Automated reorder points and supplier integration + +4. **AI-Powered Forecasting**: + - Weather impact on demand (rainy days = less foot traffic) + - Seasonal patterns (holidays, local events) + - Product-specific demand predictions + +### Madrid Market Context +- **Language**: Spanish UI with proper localization +- **Currency**: Euro (€) formatting throughout +- **Business Hours**: Early morning operations (4AM start) +- **Mobile Usage**: 60%+ of interactions on mobile devices +- **Internet**: Sometimes unreliable, need offline capabilities + +## 🚀 Success Metrics & Goals + +### Performance Targets +- **Page Load Time**: < 2 seconds on 3G connections +- **Bundle Size**: < 500KB initial bundle (gzipped) +- **Lighthouse Score**: 90+ for Performance, Accessibility, Best Practices +- **Mobile Score**: 95+ for mobile usability + +### User Experience Goals +- **Onboarding Time**: 5-10 minutes (down from 2-3 hours) +- **Task Completion**: 90%+ success rate for core workflows +- **User Satisfaction**: NPS score > 8.5 +- **Mobile Adoption**: 60%+ of daily active users on mobile + +### Business Impact +- **User Retention**: 80%+ monthly active users +- **Feature Adoption**: 90%+ using core features within 30 days +- **Support Tickets**: 50% reduction in UI/UX related issues +- **Conversion Rate**: 85%+ trial-to-paid conversion + +## 🎨 Design Inspiration & Research + +### Modern SaaS Platforms to Study +1. **Linear** - Clean design, excellent navigation, keyboard shortcuts +2. **Notion** - Flexible layouts, contextual menus, progressive disclosure +3. **Figma** - Collaborative features, real-time updates, intuitive interactions +4. **Stripe Dashboard** - Data visualization, clear hierarchy, mobile-first +5. **Vercel** - Performance focus, minimalist design, status indicators + +### AI-Enhanced Interfaces +1. **Claude.ai** - Conversational interface, contextual help, smart suggestions +2. **Cursor** - Predictive features, auto-completion, AI assistance +3. **GitHub Copilot** - Code suggestions, contextual recommendations +4. **Replit** - Real-time collaboration, AI-powered development environment + +### Mobile-First Enterprise Apps +1. **Slack Mobile** - Touch-optimized, gesture navigation, offline support +2. **Asana Mobile** - Task management, quick actions, responsive design +3. **Trello Mobile** - Card-based interface, drag-and-drop interactions + +## 🔍 Research Requirements + +Before starting implementation, research: + +### Latest Design Trends (2024-2025) +- **Micro-interactions**: Subtle animations that provide feedback +- **Glassmorphism**: Translucent elements with backdrop blur +- **Neumorphism**: Soft, inset shadow effects for buttons/cards +- **Dark Mode**: System-preference aware theming +- **Accessibility First**: WCAG 2.2 compliance from the start + +### AI-UX Patterns +- **Predictive Interface**: UI that anticipates user needs +- **Contextual Help**: Smart assistance based on current task +- **Progressive Enhancement**: AI features that enhance without blocking +- **Explainable AI**: Transparent AI decision-making + +### Performance Optimization +- **Core Web Vitals**: LCP, FID, CLS optimization +- **Progressive Loading**: Critical path prioritization +- **Edge Computing**: CDN optimization for global performance +- **Bundle Analysis**: Tree-shaking and code-splitting strategies + +## ⚡ Quick Start Checklist + +### ✅ Week 1: Foundation +- [ ] Create new project structure with proper organization +- [ ] Setup Tailwind CSS with custom design system +- [ ] Implement authentication flow (login/register/logout) +- [ ] Create basic layout components (Header, Sidebar, AppShell) +- [ ] Setup React Router with protected routes + +### ✅ Week 2: Core Components +- [ ] Build complete UI component library with Storybook +- [ ] Implement Zustand stores for state management +- [ ] Create API service layer with React Query +- [ ] Build dashboard with KPI cards and charts +- [ ] Add mobile-responsive navigation + +### ✅ Week 3: Business Features +- [ ] Inventory management (products, stock levels, alerts) +- [ ] Production planning (batches, recipes, scheduling) +- [ ] Sales tracking (transactions, POS integration) +- [ ] Forecasting (charts, predictions, confidence intervals) +- [ ] Basic analytics and reporting + +### ✅ Week 4: UX Enhancement +- [ ] AI-powered features (smart search, recommendations) +- [ ] Progressive Web App (offline, notifications, install) +- [ ] Accessibility audit and improvements (WCAG 2.2 AA) +- [ ] Performance optimization (lazy loading, caching) +- [ ] Cross-device testing and optimization + +## 🎯 Final Outcome + +Transform the Bakery AI Platform into: + +✅ **Modern, Professional Interface** - Following 2024-2025 design standards with glassmorphism, micro-interactions, and AI-enhanced UX + +✅ **Clean, Maintainable Codebase** - TypeScript-first, component-driven architecture with clear separation of concerns + +✅ **Mobile-First Experience** - Touch-optimized, responsive design with PWA capabilities and offline functionality + +✅ **AI-Enhanced Workflows** - Predictive features, smart suggestions, and contextual help that guide users intelligently + +✅ **High-Performance Application** - Fast loading, efficient rendering, and optimized for Madrid bakery operations + +✅ **Accessible Platform** - WCAG 2.2 AA compliant with support for all users and assistive technologies + +Remember: You're building for Madrid bakery owners who start work at 4:00 AM and need reliable, fast, intuitive tools to manage their businesses profitably. Every design and technical decision should prioritize their real operational needs over technical elegance. \ No newline at end of file diff --git a/frontend/index.html b/frontend/index.html index 66ef8f1b..e7d78224 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1,170 +1,27 @@ - - - - - - PanIA - Inteligencia Artificial para tu Panadería en Madrid - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
🥖
-
Cargando PanIA...
-
-
- - -
- - - - - - - + Bakery AI - Gestión Inteligente para Panaderías + + +
+ + \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 52888e6f..5cc89777 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,62 +1,87 @@ { - "name": "pania-frontend", - "version": "1.0.0", + "name": "bakery-ai-frontend", + "version": "2.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "pania-frontend", - "version": "1.0.0", - "license": "MIT", + "name": "bakery-ai-frontend", + "version": "2.0.0", "dependencies": { - "@hookform/resolvers": "^3.3.1", - "@reduxjs/toolkit": "^1.9.5", - "@stripe/react-stripe-js": "^3.9.0", - "@stripe/stripe-js": "^7.8.0", + "@hookform/resolvers": "^3.3.2", + "@radix-ui/react-checkbox": "^1.0.4", + "@radix-ui/react-dialog": "^1.0.5", + "@radix-ui/react-dropdown-menu": "^2.0.6", + "@radix-ui/react-select": "^2.0.0", + "@radix-ui/react-switch": "^1.0.3", + "@radix-ui/react-tabs": "^1.0.4", + "@radix-ui/react-tooltip": "^1.0.7", + "@tanstack/react-query": "^5.12.0", + "axios": "^1.6.2", + "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", "date-fns": "^2.30.0", "date-fns-tz": "^2.0.0", - "i18next": "^23.4.4", - "i18next-browser-languagedetector": "^7.1.0", - "lucide-react": "^0.263.1", + "event-source-polyfill": "^1.0.31", + "framer-motion": "^10.16.0", + "i18next": "^23.7.0", + "immer": "^10.0.3", + "lucide-react": "^0.294.0", + "papaparse": "^5.4.1", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-hook-form": "^7.45.4", + "react-dropzone": "^14.2.3", + "react-hook-form": "^7.48.0", "react-hot-toast": "^2.4.1", - "react-i18next": "^13.1.2", - "react-redux": "^8.1.2", - "react-router-dom": "^6.15.0", - "recharts": "^2.8.0", - "tailwind-merge": "^1.14.0", - "zod": "^3.22.2" + "react-i18next": "^13.5.0", + "react-router-dom": "^6.20.0", + "recharts": "^2.10.0", + "tailwind-merge": "^2.1.0", + "xlsx": "^0.18.5", + "zod": "^3.22.4", + "zustand": "^4.4.7" }, "devDependencies": { - "@tailwindcss/forms": "^0.5.4", - "@tailwindcss/typography": "^0.5.9", - "@testing-library/jest-dom": "^5.17.0", - "@testing-library/react": "^13.4.0", - "@testing-library/user-event": "^14.4.3", - "@types/react": "^18.2.15", - "@types/react-dom": "^18.2.7", - "@typescript-eslint/eslint-plugin": "^6.0.0", - "@typescript-eslint/parser": "^6.0.0", - "@vitejs/plugin-react": "^4.0.3", - "@vitest/ui": "^0.34.1", - "autoprefixer": "^10.4.14", - "eslint": "^8.45.0", + "@storybook/addon-essentials": "^7.6.0", + "@storybook/addon-interactions": "^7.6.0", + "@storybook/addon-links": "^7.6.0", + "@storybook/blocks": "^7.6.0", + "@storybook/react-vite": "^7.6.0", + "@storybook/testing-library": "^0.2.2", + "@tailwindcss/aspect-ratio": "^0.4.2", + "@tailwindcss/forms": "^0.5.10", + "@tailwindcss/typography": "^0.5.16", + "@tanstack/react-query-devtools": "^5.85.5", + "@testing-library/jest-dom": "^6.1.0", + "@testing-library/react": "^14.1.0", + "@testing-library/user-event": "^14.5.0", + "@types/node": "^20.10.0", + "@types/papaparse": "^5.3.14", + "@types/react": "^18.2.43", + "@types/react-dom": "^18.2.17", + "@typescript-eslint/eslint-plugin": "^6.14.0", + "@typescript-eslint/parser": "^6.14.0", + "@vitejs/plugin-react": "^4.2.0", + "@vitest/ui": "^1.0.0", + "autoprefixer": "^10.4.16", + "eslint": "^8.55.0", "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.3", - "postcss": "^8.4.27", + "eslint-plugin-react-refresh": "^0.4.5", + "msw": "^2.0.0", + "postcss": "^8.4.32", + "prettier": "^3.1.0", + "prettier-plugin-tailwindcss": "^0.5.0", "tailwindcss": "^3.3.0", - "typescript": "^5.0.2", - "vite": "^4.4.5", - "vitest": "^0.34.1" + "typescript": "^5.3.0", + "vite": "^5.0.0", + "vite-plugin-pwa": "^0.17.0", + "vitest": "^1.0.0" } }, "node_modules/@adobe/css-tools": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.3.tgz", - "integrity": "sha512-VQKMkwriZbaOgVCby1UDY/LDk5fIjhQicCvVPFqfe+69fWaPWydbWJ3wRt59/YzIwda1I81loas3oCoHxnqvdA==", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", + "integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==", "dev": true, "license": "MIT" }, @@ -113,22 +138,22 @@ } }, "node_modules/@babel/core": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", - "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz", + "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", + "@babel/generator": "^7.28.3", "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.27.3", - "@babel/helpers": "^7.27.6", - "@babel/parser": "^7.28.0", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.3", + "@babel/parser": "^7.28.3", "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.0", - "@babel/types": "^7.28.0", + "@babel/traverse": "^7.28.3", + "@babel/types": "^7.28.2", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -143,25 +168,15 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@babel/generator": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", - "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.0", - "@babel/types": "^7.28.0", + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -170,6 +185,19 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-compilation-targets": { "version": "7.27.2", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", @@ -187,14 +215,61 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz", + "integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.28.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", + "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "regexpu-core": "^6.2.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz", + "integrity": "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "debug": "^4.4.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.22.10" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/@babel/helper-globals": { @@ -207,6 +282,20 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", + "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-module-imports": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", @@ -222,15 +311,15 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", - "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.3" + "@babel/traverse": "^7.28.3" }, "engines": { "node": ">=6.9.0" @@ -239,6 +328,19 @@ "@babel/core": "^7.0.0" } }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-plugin-utils": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", @@ -249,6 +351,56 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", + "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-wrap-function": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", @@ -279,10 +431,25 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.3.tgz", + "integrity": "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.3", + "@babel/types": "^7.28.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helpers": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.2.tgz", - "integrity": "sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz", + "integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==", "dev": true, "license": "MIT", "dependencies": { @@ -294,13 +461,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", + "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.0" + "@babel/types": "^7.28.2" }, "bin": { "parser": "bin/babel-parser.js" @@ -309,6 +476,811 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.27.1.tgz", + "integrity": "sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", + "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", + "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", + "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.3.tgz", + "integrity": "sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", + "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", + "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz", + "integrity": "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1", + "@babel/traverse": "^7.28.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", + "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", + "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.0.tgz", + "integrity": "sha512-gKKnwjpdx5sER/wl0WN0efUBFzF/56YZO0RJrSYP4CljXnP31ByY7fol89AzomdlLNzI36AvOTmYHsnZTCkq8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", + "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.3.tgz", + "integrity": "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.28.3", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.3.tgz", + "integrity": "sha512-DoEWC5SuxuARF2KdKmGUq3ghfPMO6ZzR12Dnp5gubwbeWJo4dbNWXJPVlwvh4Zlq6Z7YVvL8VFxeSOJgjsx4Sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-globals": "^7.28.0", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", + "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/template": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.0.tgz", + "integrity": "sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", + "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", + "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz", + "integrity": "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", + "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-explicit-resource-management": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.0.tgz", + "integrity": "sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz", + "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", + "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", + "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", + "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz", + "integrity": "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", + "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.27.1.tgz", + "integrity": "sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", + "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", + "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", + "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz", + "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", + "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", + "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", + "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz", + "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz", + "integrity": "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.0.tgz", + "integrity": "sha512-9VNGikXxzu5eCiQjdE4IZn8sb9q7Xsk5EXLDBKUYg1e/Tve8/05+KJEtcxGxAgCY5t/BpKQM+JEL/yT4tvgiUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/traverse": "^7.28.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", + "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz", + "integrity": "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", + "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.27.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", + "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz", + "integrity": "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz", + "integrity": "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", + "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-transform-react-jsx-self": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", @@ -341,10 +1313,307 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.3.tgz", + "integrity": "sha512-K3/M/a4+ESb5LEldjQb+XSrpY0nF+ZBFlTCbSnKaYAMfD8v33O6PMs4uYnOk19HlcsI8WMu3McdFPTiQHF/1/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz", + "integrity": "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", + "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", + "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", + "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", + "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", + "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", + "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", + "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz", + "integrity": "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", + "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz", + "integrity": "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.3.tgz", + "integrity": "sha512-ROiDcM+GbYVPYBOeCR6uBXKkQpBExLl8k9HO1ygXEyds39j+vCCsjmj7S8GOniZQlEs81QlkdJZe76IpLSiqpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.0", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.27.1", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.3", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-import-assertions": "^7.27.1", + "@babel/plugin-syntax-import-attributes": "^7.27.1", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.27.1", + "@babel/plugin-transform-async-generator-functions": "^7.28.0", + "@babel/plugin-transform-async-to-generator": "^7.27.1", + "@babel/plugin-transform-block-scoped-functions": "^7.27.1", + "@babel/plugin-transform-block-scoping": "^7.28.0", + "@babel/plugin-transform-class-properties": "^7.27.1", + "@babel/plugin-transform-class-static-block": "^7.28.3", + "@babel/plugin-transform-classes": "^7.28.3", + "@babel/plugin-transform-computed-properties": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0", + "@babel/plugin-transform-dotall-regex": "^7.27.1", + "@babel/plugin-transform-duplicate-keys": "^7.27.1", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-dynamic-import": "^7.27.1", + "@babel/plugin-transform-explicit-resource-management": "^7.28.0", + "@babel/plugin-transform-exponentiation-operator": "^7.27.1", + "@babel/plugin-transform-export-namespace-from": "^7.27.1", + "@babel/plugin-transform-for-of": "^7.27.1", + "@babel/plugin-transform-function-name": "^7.27.1", + "@babel/plugin-transform-json-strings": "^7.27.1", + "@babel/plugin-transform-literals": "^7.27.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.27.1", + "@babel/plugin-transform-member-expression-literals": "^7.27.1", + "@babel/plugin-transform-modules-amd": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-modules-systemjs": "^7.27.1", + "@babel/plugin-transform-modules-umd": "^7.27.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-new-target": "^7.27.1", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", + "@babel/plugin-transform-numeric-separator": "^7.27.1", + "@babel/plugin-transform-object-rest-spread": "^7.28.0", + "@babel/plugin-transform-object-super": "^7.27.1", + "@babel/plugin-transform-optional-catch-binding": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/plugin-transform-private-methods": "^7.27.1", + "@babel/plugin-transform-private-property-in-object": "^7.27.1", + "@babel/plugin-transform-property-literals": "^7.27.1", + "@babel/plugin-transform-regenerator": "^7.28.3", + "@babel/plugin-transform-regexp-modifiers": "^7.27.1", + "@babel/plugin-transform-reserved-words": "^7.27.1", + "@babel/plugin-transform-shorthand-properties": "^7.27.1", + "@babel/plugin-transform-spread": "^7.27.1", + "@babel/plugin-transform-sticky-regex": "^7.27.1", + "@babel/plugin-transform-template-literals": "^7.27.1", + "@babel/plugin-transform-typeof-symbol": "^7.27.1", + "@babel/plugin-transform-unicode-escapes": "^7.27.1", + "@babel/plugin-transform-unicode-property-regex": "^7.27.1", + "@babel/plugin-transform-unicode-regex": "^7.27.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.14", + "babel-plugin-polyfill-corejs3": "^0.13.0", + "babel-plugin-polyfill-regenerator": "^0.6.5", + "core-js-compat": "^3.43.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, "node_modules/@babel/runtime": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.2.tgz", - "integrity": "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz", + "integrity": "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -366,18 +1635,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", - "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz", + "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", + "@babel/generator": "^7.28.3", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.0", + "@babel/parser": "^7.28.3", "@babel/template": "^7.27.2", - "@babel/types": "^7.28.0", + "@babel/types": "^7.28.2", "debug": "^4.3.1" }, "engines": { @@ -398,6 +1667,98 @@ "node": ">=6.9.0" } }, + "node_modules/@base2/pretty-print-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@base2/pretty-print-object/-/pretty-print-object-1.0.1.tgz", + "integrity": "sha512-4iri8i1AqYHJE2DstZYkyEprg6Pq6sKx3xn5FpySk9sNhH7qN2LLlHJCfDTZRILNwQNPD7mATWM0TBui7uC1pA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/@bundled-es-modules/cookie": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/cookie/-/cookie-2.0.1.tgz", + "integrity": "sha512-8o+5fRPLNbjbdGRRmJj3h6Hh1AQJf2dk3qQ/5ZFb+PXkRNiSoMGGUKlsgLfrxneb72axVJyIYji64E2+nNfYyw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cookie": "^0.7.2" + } + }, + "node_modules/@bundled-es-modules/cookie/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@bundled-es-modules/statuses": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz", + "integrity": "sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==", + "dev": true, + "license": "ISC", + "dependencies": { + "statuses": "^2.0.1" + } + }, + "node_modules/@bundled-es-modules/tough-cookie": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/tough-cookie/-/tough-cookie-0.1.6.tgz", + "integrity": "sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@types/tough-cookie": "^4.0.5", + "tough-cookie": "^4.1.4" + } + }, + "node_modules/@emotion/is-prop-valid": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", + "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emotion/memoize": "0.7.4" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", + "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "license": "MIT", + "optional": true + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@esbuild/android-arm": { "version": "0.18.20", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", @@ -825,6 +2186,13 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -836,6 +2204,19 @@ "concat-map": "0.0.1" } }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -859,6 +2240,44 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", + "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", + "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.3", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz", + "integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.4" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" + }, "node_modules/@hookform/resolvers": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.10.0.tgz", @@ -930,6 +2349,121 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/@inquirer/confirm": { + "version": "5.1.16", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.16.tgz", + "integrity": "sha512-j1a5VstaK5KQy8Mu8cHmuQvN1Zc62TbLhjJxwHvKPPKEoowSF6h/0UdOpA9DNdWZ+9Inq73+puRq1df6OJ8Sag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.2.0", + "@inquirer/type": "^3.0.8" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.2.0.tgz", + "integrity": "sha512-NyDSjPqhSvpZEMZrLCYUquWNl+XC/moEcVFqS55IEYIYsY0a1cUCevSqk7ctOlnm/RaSBU5psFryNlxcmGrjaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/figures": "^1.0.13", + "@inquirer/type": "^3.0.8", + "ansi-escapes": "^4.3.2", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@inquirer/core/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/core/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.13.tgz", + "integrity": "sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.8.tgz", + "integrity": "sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -949,9 +2483,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", + "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", "dev": true, "license": "MIT", "engines": { @@ -977,106 +2511,252 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/@jest/diff-sequences": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", - "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, - "license": "MIT", + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" } }, - "node_modules/@jest/expect-utils": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.0.5.tgz", - "integrity": "sha512-F3lmTT7CXWYywoVUGTCmom0vXq3HTTkaZyTAzIy+bXSBizB7o5qzlC9VCtq0arOa8GqmNsbg/cE9C6HLn7Szew==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/get-type": "30.0.1" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" } }, - "node_modules/@jest/get-type": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.0.1.tgz", - "integrity": "sha512-AyYdemXCptSRFirI5EPazNxyPwAL0jXt3zceFjaj8NFiKP9pOi0bfXonf6qkf82z2t3QWPeLCWWw4stPBzctLw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/pattern": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", - "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "license": "MIT", "dependencies": { - "@types/node": "*", - "jest-regex-util": "30.0.1" + "p-locate": "^4.1.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" } }, "node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, "license": "MIT", "dependencies": { - "@sinclair/typebox": "^0.34.0" + "@sinclair/typebox": "^0.27.8" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/types": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.0.5.tgz", - "integrity": "sha512-aREYa3aku9SSnea4aX6bhKn4bgv3AXkgijoQgbYV3yvbiGt6z+MQ85+6mIhx9DsKW2BuB/cLR/A+tcMThx+KLQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/types/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@joshwooding/vite-plugin-react-docgen-typescript": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@joshwooding/vite-plugin-react-docgen-typescript/-/vite-plugin-react-docgen-typescript-0.3.0.tgz", + "integrity": "sha512-2D6y7fNvFmsLmRt6UCOFJPvFoPMJGT0Uh1Wg0RaigUp7kdQPs6yYn8Dmx6GZkOH/NW0yMTwRz/p0SRMMRo50vA==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "glob": "^7.2.0", + "glob-promise": "^4.2.0", + "magic-string": "^0.27.0", + "react-docgen-typescript": "^2.2.2" + }, + "peerDependencies": { + "typescript": ">= 4.3.x", + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@joshwooding/vite-plugin-react-docgen-typescript/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@joshwooding/vite-plugin-react-docgen-typescript/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">=10" + "node": "*" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@joshwooding/vite-plugin-react-docgen-typescript/node_modules/glob-promise": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/glob-promise/-/glob-promise-4.2.2.tgz", + "integrity": "sha512-xcUzJ8NWN5bktoTIX7eOclO1Npxd/dyVqUJxlLIDasT4C7KZyqlPIwkdJ0Ypiy3p2ZKahTjK4M9uC3sNSfNMzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/glob": "^7.1.3" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/ahmadnassri" + }, + "peerDependencies": { + "glob": "^7.1.6" + } + }, + "node_modules/@joshwooding/vite-plugin-react-docgen-typescript/node_modules/magic-string": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz", + "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.13" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@joshwooding/vite-plugin-react-docgen-typescript/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, "license": "MIT", "dependencies": { @@ -1094,17 +2774,28 @@ "node": ">=6.0.0" } }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "version": "0.3.30", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", "dev": true, "license": "MIT", "dependencies": { @@ -1112,6 +2803,49 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@juggle/resize-observer": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz", + "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@mdx-js/react": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-2.3.0.tgz", + "integrity": "sha512-zQH//gdOmuu7nt2oJR29vFhDv88oGPmVw6BggmrHeMI+xgEkp1B2dX9/bMBSYtK0dyLX/aOmesKS09g222K1/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mdx": "^2.0.0", + "@types/react": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "react": ">=16" + } + }, + "node_modules/@mswjs/interceptors": { + "version": "0.39.6", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.39.6.tgz", + "integrity": "sha512-bndDP83naYYkfayr/qhBHMhk0YGwS1iv6vaEGcr0SQbO0IZtbOPqjKjds/WcG+bJA+1T5vCx6kprKOzn5Bg+Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/logger": "^0.3.0", + "@open-draft/until": "^2.0.0", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.3", + "strict-event-emitter": "^0.5.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1150,6 +2884,31 @@ "node": ">= 8" } }, + "node_modules/@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@open-draft/logger": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", + "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-node-process": "^1.2.0", + "outvariant": "^1.4.0" + } + }, + "node_modules/@open-draft/until": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", + "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", + "dev": true, + "license": "MIT" + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -1168,30 +2927,895 @@ "dev": true, "license": "MIT" }, - "node_modules/@reduxjs/toolkit": { - "version": "1.9.7", - "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.7.tgz", - "integrity": "sha512-t7v8ZPxhhKgOKtU+uyJT13lu4vL7az5aFi4IdoDs/eS548edn2M8Ik9h8fxgvMjGoAUVFSt6ZC1P5cWmQ014QQ==", + "node_modules/@radix-ui/number": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", + "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==", + "license": "MIT" + }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", + "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", "license": "MIT", "dependencies": { - "immer": "^9.0.21", - "redux": "^4.2.1", - "redux-thunk": "^2.4.2", - "reselect": "^4.1.8" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { - "react": "^16.9.0 || ^17.0.0 || ^18", - "react-redux": "^7.2.1 || ^8.0.2" + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { - "react": { + "@types/react": { "optional": true }, - "react-redux": { + "@types/react-dom": { "optional": true } } }, + "node_modules/@radix-ui/react-checkbox": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.3.tgz", + "integrity": "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", + "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", + "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", + "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz", + "integrity": "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-menu": "2.1.16", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", + "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.16.tgz", + "integrity": "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", + "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-rect": "1.1.1", + "@radix-ui/react-use-size": "1.1.1", + "@radix-ui/rect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", + "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", + "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz", + "integrity": "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.1", + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.3", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-separator": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.7.tgz", + "integrity": "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-switch": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.6.tgz", + "integrity": "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz", + "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toggle": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.10.tgz", + "integrity": "sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toggle-group": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.11.tgz", + "integrity": "sha512-5umnS0T8JQzQT6HbPyO7Hh9dgd82NmS36DQr+X/YJ9ctFNCiiQd6IJAYYZ33LUwm8M+taCz5t2ui29fHZc4Y6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-toggle": "1.1.10", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toolbar": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toolbar/-/react-toolbar-1.1.11.tgz", + "integrity": "sha512-4ol06/1bLoFu1nwUqzdD4Y5RZ9oDdKeiHIsntug54Hcr1pgaHiPqHFEaXI1IFP/EsOfROQZ8Mig9VTIRza6Tjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-separator": "1.1.7", + "@radix-ui/react-toggle-group": "1.1.11" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz", + "integrity": "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-visually-hidden": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", + "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", + "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", + "license": "MIT", + "dependencies": { + "@radix-ui/rect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", + "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", + "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", + "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", + "license": "MIT" + }, "node_modules/@remix-run/router": { "version": "1.23.0", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz", @@ -1208,34 +3832,1978 @@ "dev": true, "license": "MIT" }, - "node_modules/@sinclair/typebox": { - "version": "0.34.38", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.38.tgz", - "integrity": "sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA==", + "node_modules/@rollup/plugin-babel": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", + "integrity": "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.10.4", + "@rollup/pluginutils": "^3.1.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "@types/babel__core": "^7.1.9", + "rollup": "^1.20.0||^2.0.0" + }, + "peerDependenciesMeta": { + "@types/babel__core": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-babel/node_modules/@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/@rollup/plugin-babel/node_modules/@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", "dev": true, "license": "MIT" }, - "node_modules/@stripe/react-stripe-js": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-3.9.0.tgz", - "integrity": "sha512-pN1Re7zUc3m61FFQROok685g3zsBQRzCmZDmTzO8iPU6zhLvu2JnC0LrG0FCzSp6kgGa8AQSzq4rpFSgyhkjKg==", + "node_modules/@rollup/plugin-babel/node_modules/estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.1.tgz", + "integrity": "sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==", + "dev": true, "license": "MIT", "dependencies": { - "prop-types": "^15.7.2" + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" }, "peerDependencies": { - "@stripe/stripe-js": ">=1.44.1 <8.0.0", - "react": ">=16.8.0 <20.0.0", - "react-dom": ">=16.8.0 <20.0.0" + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } } }, - "node_modules/@stripe/stripe-js": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-7.8.0.tgz", - "integrity": "sha512-DNXRfYUgkZlrniQORbA/wH8CdFRhiBSE0R56gYU0V5vvpJ9WZwvGrz9tBAZmfq2aTgw6SK7mNpmTizGzLWVezw==", + "node_modules/@rollup/plugin-node-resolve/node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/plugin-replace": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz", + "integrity": "sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "magic-string": "^0.25.7" + }, + "peerDependencies": { + "rollup": "^1.20.0 || ^2.0.0" + } + }, + "node_modules/@rollup/plugin-replace/node_modules/@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/@rollup/plugin-replace/node_modules/@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/plugin-replace/node_modules/estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/plugin-replace/node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, + "node_modules/@rollup/plugin-terser": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz", + "integrity": "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "serialize-javascript": "^6.0.1", + "smob": "^1.0.0", + "terser": "^5.17.4" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz", + "integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, "license": "MIT", "engines": { - "node": ">=12.16" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.48.1.tgz", + "integrity": "sha512-rGmb8qoG/zdmKoYELCBwu7vt+9HxZ7Koos3pD0+sH5fR3u3Wb/jGcpnqxcnWsPEKDUyzeLSqksN8LJtgXjqBYw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.48.1.tgz", + "integrity": "sha512-4e9WtTxrk3gu1DFE+imNJr4WsL13nWbD/Y6wQcyku5qadlKHY3OQ3LJ/INrrjngv2BJIHnIzbqMk1GTAC2P8yQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.48.1.tgz", + "integrity": "sha512-+XjmyChHfc4TSs6WUQGmVf7Hkg8ferMAE2aNYYWjiLzAS/T62uOsdfnqv+GHRjq7rKRnYh4mwWb4Hz7h/alp8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.48.1.tgz", + "integrity": "sha512-upGEY7Ftw8M6BAJyGwnwMw91rSqXTcOKZnnveKrVWsMTF8/k5mleKSuh7D4v4IV1pLxKAk3Tbs0Lo9qYmii5mQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.48.1.tgz", + "integrity": "sha512-P9ViWakdoynYFUOZhqq97vBrhuvRLAbN/p2tAVJvhLb8SvN7rbBnJQcBu8e/rQts42pXGLVhfsAP0k9KXWa3nQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.48.1.tgz", + "integrity": "sha512-VLKIwIpnBya5/saccM8JshpbxfyJt0Dsli0PjXozHwbSVaHTvWXJH1bbCwPXxnMzU4zVEfgD1HpW3VQHomi2AQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.48.1.tgz", + "integrity": "sha512-3zEuZsXfKaw8n/yF7t8N6NNdhyFw3s8xJTqjbTDXlipwrEHo4GtIKcMJr5Ed29leLpB9AugtAQpAHW0jvtKKaQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.48.1.tgz", + "integrity": "sha512-leo9tOIlKrcBmmEypzunV/2w946JeLbTdDlwEZ7OnnsUyelZ72NMnT4B2vsikSgwQifjnJUbdXzuW4ToN1wV+Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.48.1.tgz", + "integrity": "sha512-Vy/WS4z4jEyvnJm+CnPfExIv5sSKqZrUr98h03hpAMbE2aI0aD2wvK6GiSe8Gx2wGp3eD81cYDpLLBqNb2ydwQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.48.1.tgz", + "integrity": "sha512-x5Kzn7XTwIssU9UYqWDB9VpLpfHYuXw5c6bJr4Mzv9kIv242vmJHbI5PJJEnmBYitUIfoMCODDhR7KoZLot2VQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.48.1.tgz", + "integrity": "sha512-yzCaBbwkkWt/EcgJOKDUdUpMHjhiZT/eDktOPWvSRpqrVE04p0Nd6EGV4/g7MARXXeOqstflqsKuXVM3H9wOIQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.48.1.tgz", + "integrity": "sha512-UK0WzWUjMAJccHIeOpPhPcKBqax7QFg47hwZTp6kiMhQHeOYJeaMwzeRZe1q5IiTKsaLnHu9s6toSYVUlZ2QtQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.48.1.tgz", + "integrity": "sha512-3NADEIlt+aCdCbWVZ7D3tBjBX1lHpXxcvrLt/kdXTiBrOds8APTdtk2yRL2GgmnSVeX4YS1JIf0imFujg78vpw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.48.1.tgz", + "integrity": "sha512-euuwm/QTXAMOcyiFCcrx0/S2jGvFlKJ2Iro8rsmYL53dlblp3LkUQVFzEidHhvIPPvcIsxDhl2wkBE+I6YVGzA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.48.1.tgz", + "integrity": "sha512-w8mULUjmPdWLJgmTYJx/W6Qhln1a+yqvgwmGXcQl2vFBkWsKGUBRbtLRuKJUln8Uaimf07zgJNxOhHOvjSQmBQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.48.1.tgz", + "integrity": "sha512-90taWXCWxTbClWuMZD0DKYohY1EovA+W5iytpE89oUPmT5O1HFdf8cuuVIylE6vCbrGdIGv85lVRzTcpTRZ+kA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.48.1.tgz", + "integrity": "sha512-2Gu29SkFh1FfTRuN1GR1afMuND2GKzlORQUP3mNMJbqdndOg7gNsa81JnORctazHRokiDzQ5+MLE5XYmZW5VWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.48.1.tgz", + "integrity": "sha512-6kQFR1WuAO50bxkIlAVeIYsz3RUx+xymwhTo9j94dJ+kmHe9ly7muH23sdfWduD0BA8pD9/yhonUvAjxGh34jQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.48.1.tgz", + "integrity": "sha512-RUyZZ/mga88lMI3RlXFs4WQ7n3VyU07sPXmMG7/C1NOi8qisUg57Y7LRarqoGoAiopmGmChUhSwfpvQ3H5iGSQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.48.1.tgz", + "integrity": "sha512-8a/caCUN4vkTChxkaIJcMtwIVcBhi4X2PQRoT+yCK3qRYaZ7cURrmJFL5Ux9H9RaMIXj9RuihckdmkBX3zZsgg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@storybook/addon-actions": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-7.6.20.tgz", + "integrity": "sha512-c/GkEQ2U9BC/Ew/IMdh+zvsh4N6y6n7Zsn2GIhJgcu9YEAa5aF2a9/pNgEGBMOABH959XE8DAOMERw/5qiLR8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/core-events": "7.6.20", + "@storybook/global": "^5.0.0", + "@types/uuid": "^9.0.1", + "dequal": "^2.0.2", + "polished": "^4.2.2", + "uuid": "^9.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-backgrounds": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-7.6.20.tgz", + "integrity": "sha512-a7ukoaXT42vpKsMxkseIeO3GqL0Zst2IxpCTq5dSlXiADrcemSF/8/oNpNW9C4L6F1Zdt+WDtECXslEm017FvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/global": "^5.0.0", + "memoizerific": "^1.11.3", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-controls": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-7.6.20.tgz", + "integrity": "sha512-06ZT5Ce1sZW52B0s6XuokwjkKO9GqHlTUHvuflvd8wifxKlCmRvNUxjBvwh+ccGJ49ZS73LbMSLFgtmBEkCxbg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/blocks": "7.6.20", + "lodash": "^4.17.21", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-docs": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-7.6.20.tgz", + "integrity": "sha512-XNfYRhbxH5JP7B9Lh4W06PtMefNXkfpV39Gaoih5HuqngV3eoSL4RikZYOMkvxRGQ738xc6axySU3+JKcP1OZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.3.1", + "@mdx-js/react": "^2.1.5", + "@storybook/blocks": "7.6.20", + "@storybook/client-logger": "7.6.20", + "@storybook/components": "7.6.20", + "@storybook/csf-plugin": "7.6.20", + "@storybook/csf-tools": "7.6.20", + "@storybook/global": "^5.0.0", + "@storybook/mdx2-csf": "^1.0.0", + "@storybook/node-logger": "7.6.20", + "@storybook/postinstall": "7.6.20", + "@storybook/preview-api": "7.6.20", + "@storybook/react-dom-shim": "7.6.20", + "@storybook/theming": "7.6.20", + "@storybook/types": "7.6.20", + "fs-extra": "^11.1.0", + "remark-external-links": "^8.0.0", + "remark-slug": "^6.0.0", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/addon-essentials": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-7.6.20.tgz", + "integrity": "sha512-hCupSOiJDeOxJKZSgH0x5Mb2Xqii6mps21g5hpxac1XjhQtmGflShxi/xOHhK3sNqrbgTSbScfpUP3hUlZO/2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/addon-actions": "7.6.20", + "@storybook/addon-backgrounds": "7.6.20", + "@storybook/addon-controls": "7.6.20", + "@storybook/addon-docs": "7.6.20", + "@storybook/addon-highlight": "7.6.20", + "@storybook/addon-measure": "7.6.20", + "@storybook/addon-outline": "7.6.20", + "@storybook/addon-toolbars": "7.6.20", + "@storybook/addon-viewport": "7.6.20", + "@storybook/core-common": "7.6.20", + "@storybook/manager-api": "7.6.20", + "@storybook/node-logger": "7.6.20", + "@storybook/preview-api": "7.6.20", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/addon-highlight": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-7.6.20.tgz", + "integrity": "sha512-7/x7xFdFyqCki5Dm3uBePldUs9l98/WxJ7rTHQuYqlX7kASwyN5iXPzuhmMRUhlMm/6G6xXtLabIpzwf1sFurA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/global": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-interactions": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/addon-interactions/-/addon-interactions-7.6.20.tgz", + "integrity": "sha512-uH+OIxLtvfnnmdN3Uf8MwzfEFYtaqSA6Hir6QNPc643se0RymM8mULN0rzRyvspwd6OagWdtOxsws3aHk02KTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/global": "^5.0.0", + "@storybook/types": "7.6.20", + "jest-mock": "^27.0.6", + "polished": "^4.2.2", + "ts-dedent": "^2.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-links": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-7.6.20.tgz", + "integrity": "sha512-iomSnBD90CA4MinesYiJkFX2kb3P1Psd/a1Y0ghlFEsHD4uMId9iT6sx2s16DYMja0SlPkrbWYnGukqaCjZpRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/csf": "^0.1.2", + "@storybook/global": "^5.0.0", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } + } + }, + "node_modules/@storybook/addon-measure": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-7.6.20.tgz", + "integrity": "sha512-i2Iq08bGfI7gZbG6Lb8uF/L287tnaGUR+2KFEmdBjH6+kgjWLiwfpanoPQpy4drm23ar0gUjX+L3Ri03VI5/Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/global": "^5.0.0", + "tiny-invariant": "^1.3.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-outline": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-7.6.20.tgz", + "integrity": "sha512-TdsIQZf/TcDsGoZ1XpO+9nBc4OKqcMIzY4SrI8Wj9dzyFLQ37s08gnZr9POci8AEv62NTUOVavsxcafllkzqDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/global": "^5.0.0", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-toolbars": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-7.6.20.tgz", + "integrity": "sha512-5Btg4i8ffWTDHsU72cqxC8nIv9N3E3ObJAc6k0llrmPBG/ybh3jxmRfs8fNm44LlEXaZ5qrK/petsXX3UbpIFg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/addon-viewport": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-7.6.20.tgz", + "integrity": "sha512-i8mIw8BjLWAVHEQsOTE6UPuEGQvJDpsu1XZnOCkpfTfPMz73m+3td/PmLG7mMT2wPnLu9IZncKLCKTAZRbt/YQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "memoizerific": "^1.11.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/blocks": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-7.6.20.tgz", + "integrity": "sha512-xADKGEOJWkG0UD5jbY4mBXRlmj2C+CIupDL0/hpzvLvwobxBMFPKZIkcZIMvGvVnI/Ui+tJxQxLSuJ5QsPthUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/channels": "7.6.20", + "@storybook/client-logger": "7.6.20", + "@storybook/components": "7.6.20", + "@storybook/core-events": "7.6.20", + "@storybook/csf": "^0.1.2", + "@storybook/docs-tools": "7.6.20", + "@storybook/global": "^5.0.0", + "@storybook/manager-api": "7.6.20", + "@storybook/preview-api": "7.6.20", + "@storybook/theming": "7.6.20", + "@storybook/types": "7.6.20", + "@types/lodash": "^4.14.167", + "color-convert": "^2.0.1", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "markdown-to-jsx": "^7.1.8", + "memoizerific": "^1.11.3", + "polished": "^4.2.2", + "react-colorful": "^5.1.2", + "telejson": "^7.2.0", + "tocbot": "^4.20.1", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/builder-vite": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-7.6.20.tgz", + "integrity": "sha512-q3vf8heE7EaVYTWlm768ewaJ9lh6v/KfoPPeHxXxzSstg4ByP9kg4E1mrfAo/l6broE9E9zo3/Q4gsM/G/rw8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/channels": "7.6.20", + "@storybook/client-logger": "7.6.20", + "@storybook/core-common": "7.6.20", + "@storybook/csf-plugin": "7.6.20", + "@storybook/node-logger": "7.6.20", + "@storybook/preview": "7.6.20", + "@storybook/preview-api": "7.6.20", + "@storybook/types": "7.6.20", + "@types/find-cache-dir": "^3.2.1", + "browser-assert": "^1.2.1", + "es-module-lexer": "^0.9.3", + "express": "^4.17.3", + "find-cache-dir": "^3.0.0", + "fs-extra": "^11.1.0", + "magic-string": "^0.30.0", + "rollup": "^2.25.0 || ^3.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "@preact/preset-vite": "*", + "typescript": ">= 4.3.x", + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0", + "vite-plugin-glimmerx": "*" + }, + "peerDependenciesMeta": { + "@preact/preset-vite": { + "optional": true + }, + "typescript": { + "optional": true + }, + "vite-plugin-glimmerx": { + "optional": true + } + } + }, + "node_modules/@storybook/channels": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.20.tgz", + "integrity": "sha512-4hkgPSH6bJclB2OvLnkZOGZW1WptJs09mhQ6j6qLjgBZzL/ZdD6priWSd7iXrmPiN5TzUobkG4P4Dp7FjkiO7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/client-logger": "7.6.20", + "@storybook/core-events": "7.6.20", + "@storybook/global": "^5.0.0", + "qs": "^6.10.0", + "telejson": "^7.2.0", + "tiny-invariant": "^1.3.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/client-logger": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.20.tgz", + "integrity": "sha512-NwG0VIJQCmKrSaN5GBDFyQgTAHLNishUPLW1NrzqTDNAhfZUoef64rPQlinbopa0H4OXmlB+QxbQIb3ubeXmSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/global": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/components": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/components/-/components-7.6.20.tgz", + "integrity": "sha512-0d8u4m558R+W5V+rseF/+e9JnMciADLXTpsILrG+TBhwECk0MctIWW18bkqkujdCm8kDZr5U2iM/5kS1Noy7Ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-select": "^1.2.2", + "@radix-ui/react-toolbar": "^1.0.4", + "@storybook/client-logger": "7.6.20", + "@storybook/csf": "^0.1.2", + "@storybook/global": "^5.0.0", + "@storybook/theming": "7.6.20", + "@storybook/types": "7.6.20", + "memoizerific": "^1.11.3", + "use-resize-observer": "^9.1.0", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/components/node_modules/@radix-ui/number": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.1.tgz", + "integrity": "sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, + "node_modules/@storybook/components/node_modules/@radix-ui/primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz", + "integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, + "node_modules/@storybook/components/node_modules/@radix-ui/react-collection": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz", + "integrity": "sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@storybook/components/node_modules/@radix-ui/react-compose-refs": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", + "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@storybook/components/node_modules/@radix-ui/react-context": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz", + "integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@storybook/components/node_modules/@radix-ui/react-direction": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz", + "integrity": "sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@storybook/components/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.4.tgz", + "integrity": "sha512-7UpBa/RKMoHJYjie1gkF1DlK8l1fdU/VKDpoS3rCCo8YBJR294GwcEHyxHw72yvphJ7ld0AXEcSLAzY2F/WyCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-escape-keydown": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@storybook/components/node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz", + "integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@storybook/components/node_modules/@radix-ui/react-focus-guards": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz", + "integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@storybook/components/node_modules/@radix-ui/react-focus-scope": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.3.tgz", + "integrity": "sha512-upXdPfqI4islj2CslyfUBNlaJCPybbqRHAi1KER7Isel9Q2AtSJ0zRBZv8mWQiFXD2nyAJ4BhC3yXgZ6kMBSrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@storybook/components/node_modules/@radix-ui/react-id": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz", + "integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@storybook/components/node_modules/@radix-ui/react-popper": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.2.tgz", + "integrity": "sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1", + "@radix-ui/react-use-rect": "1.0.1", + "@radix-ui/react-use-size": "1.0.1", + "@radix-ui/rect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@storybook/components/node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-arrow": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz", + "integrity": "sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@storybook/components/node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-use-rect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz", + "integrity": "sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/rect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@storybook/components/node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-use-size": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz", + "integrity": "sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@storybook/components/node_modules/@radix-ui/react-portal": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.3.tgz", + "integrity": "sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@storybook/components/node_modules/@radix-ui/react-primitive": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz", + "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@storybook/components/node_modules/@radix-ui/react-select": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-1.2.2.tgz", + "integrity": "sha512-zI7McXr8fNaSrUY9mZe4x/HC0jTLY9fWNhO1oLWYMQGDXuV4UCivIGTxwioSzO0ZCYX9iSLyWmAh/1TOmX3Cnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/number": "1.0.1", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-collection": "1.0.3", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-direction": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.4", + "@radix-ui/react-focus-guards": "1.0.1", + "@radix-ui/react-focus-scope": "1.0.3", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-popper": "1.1.2", + "@radix-ui/react-portal": "1.0.3", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1", + "@radix-ui/react-use-previous": "1.0.1", + "@radix-ui/react-visually-hidden": "1.0.3", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@storybook/components/node_modules/@radix-ui/react-slot": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", + "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@storybook/components/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz", + "integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@storybook/components/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz", + "integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@storybook/components/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz", + "integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@storybook/components/node_modules/@radix-ui/react-use-previous": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.0.1.tgz", + "integrity": "sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@storybook/components/node_modules/@radix-ui/react-visually-hidden": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz", + "integrity": "sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@storybook/components/node_modules/@radix-ui/rect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz", + "integrity": "sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" + } + }, + "node_modules/@storybook/components/node_modules/react-remove-scroll": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", + "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.3", + "react-style-singleton": "^2.2.1", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.0", + "use-sidecar": "^1.1.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@storybook/core-client": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/core-client/-/core-client-7.6.20.tgz", + "integrity": "sha512-upQuQQinLmlOPKcT8yqXNtwIucZ4E4qegYZXH5HXRWoLAL6GQtW7sUVSIuFogdki8OXRncr/dz8OA+5yQyYS4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/client-logger": "7.6.20", + "@storybook/preview-api": "7.6.20" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/core-common": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-7.6.20.tgz", + "integrity": "sha512-8H1zPWPjcmeD4HbDm4FDD0WLsfAKGVr566IZ4hG+h3iWVW57II9JW9MLBtiR2LPSd8u7o0kw64lwRGmtCO1qAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/core-events": "7.6.20", + "@storybook/node-logger": "7.6.20", + "@storybook/types": "7.6.20", + "@types/find-cache-dir": "^3.2.1", + "@types/node": "^18.0.0", + "@types/node-fetch": "^2.6.4", + "@types/pretty-hrtime": "^1.0.0", + "chalk": "^4.1.0", + "esbuild": "^0.18.0", + "esbuild-register": "^3.5.0", + "file-system-cache": "2.3.0", + "find-cache-dir": "^3.0.0", + "find-up": "^5.0.0", + "fs-extra": "^11.1.0", + "glob": "^10.0.0", + "handlebars": "^4.7.7", + "lazy-universal-dotenv": "^4.0.0", + "node-fetch": "^2.0.0", + "picomatch": "^2.3.0", + "pkg-dir": "^5.0.0", + "pretty-hrtime": "^1.0.3", + "resolve-from": "^5.0.0", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/core-common/node_modules/@types/node": { + "version": "18.19.123", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.123.tgz", + "integrity": "sha512-K7DIaHnh0mzVxreCR9qwgNxp3MH9dltPNIEddW9MYUlcKAzm+3grKNSTe2vCJHI1FaLpvpL5JGJrz1UZDKYvDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@storybook/core-common/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@storybook/core-events": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.20.tgz", + "integrity": "sha512-tlVDuVbDiNkvPDFAu+0ou3xBBYbx9zUURQz4G9fAq0ScgBOs/bpzcRrFb4mLpemUViBAd47tfZKdH4MAX45KVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/csf": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/@storybook/csf/-/csf-0.1.13.tgz", + "integrity": "sha512-7xOOwCLGB3ebM87eemep89MYRFTko+D8qE7EdAAq74lgdqRR5cOUtYWJLjO2dLtP94nqoOdHJo6MdLLKzg412Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^2.19.0" + } + }, + "node_modules/@storybook/csf-plugin": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-7.6.20.tgz", + "integrity": "sha512-dzBzq0dN+8WLDp6NxYS4G7BCe8+vDeDRBRjHmM0xb0uJ6xgQViL8SDplYVSGnk3bXE/1WmtvyRzQyTffBnaj9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/csf-tools": "7.6.20", + "unplugin": "^1.3.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/csf-tools": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/csf-tools/-/csf-tools-7.6.20.tgz", + "integrity": "sha512-rwcwzCsAYh/m/WYcxBiEtLpIW5OH1ingxNdF/rK9mtGWhJxXRDV8acPkFrF8rtFWIVKoOCXu5USJYmc3f2gdYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/generator": "^7.23.0", + "@babel/parser": "^7.23.0", + "@babel/traverse": "^7.23.2", + "@babel/types": "^7.23.0", + "@storybook/csf": "^0.1.2", + "@storybook/types": "7.6.20", + "fs-extra": "^11.1.0", + "recast": "^0.23.1", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/docs-tools": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/docs-tools/-/docs-tools-7.6.20.tgz", + "integrity": "sha512-Bw2CcCKQ5xGLQgtexQsI1EGT6y5epoFzOINi0FSTGJ9Wm738nRp5LH3dLk1GZLlywIXcYwOEThb2pM+pZeRQxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/core-common": "7.6.20", + "@storybook/preview-api": "7.6.20", + "@storybook/types": "7.6.20", + "@types/doctrine": "^0.0.3", + "assert": "^2.1.0", + "doctrine": "^3.0.0", + "lodash": "^4.17.21" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/global": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@storybook/global/-/global-5.0.0.tgz", + "integrity": "sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@storybook/manager-api": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-7.6.20.tgz", + "integrity": "sha512-gOB3m8hO3gBs9cBoN57T7jU0wNKDh+hi06gLcyd2awARQlAlywnLnr3s1WH5knih6Aq+OpvGBRVKkGLOkaouCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/channels": "7.6.20", + "@storybook/client-logger": "7.6.20", + "@storybook/core-events": "7.6.20", + "@storybook/csf": "^0.1.2", + "@storybook/global": "^5.0.0", + "@storybook/router": "7.6.20", + "@storybook/theming": "7.6.20", + "@storybook/types": "7.6.20", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", + "store2": "^2.14.2", + "telejson": "^7.2.0", + "ts-dedent": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/mdx2-csf": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@storybook/mdx2-csf/-/mdx2-csf-1.1.0.tgz", + "integrity": "sha512-TXJJd5RAKakWx4BtpwvSNdgTDkKM6RkXU8GK34S/LhidQ5Pjz3wcnqb0TxEkfhK/ztbP8nKHqXFwLfa2CYkvQw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@storybook/node-logger": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-7.6.20.tgz", + "integrity": "sha512-l2i4qF1bscJkOplNffcRTsgQWYR7J51ewmizj5YrTM8BK6rslWT1RntgVJWB1RgPqvx6VsCz1gyP3yW1oKxvYw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/postinstall": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/postinstall/-/postinstall-7.6.20.tgz", + "integrity": "sha512-AN4WPeNma2xC2/K/wP3I/GMbBUyeSGD3+86ZFFJFO1QmE/Zea6E+1aVlTd1iKHQUcNkZ9bZTrqkhPGVYx10pIw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/preview": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/preview/-/preview-7.6.20.tgz", + "integrity": "sha512-cxYlZ5uKbCYMHoFpgleZqqGWEnqHrk5m5fT8bYSsDsdQ+X5wPcwI/V+v8dxYAdQcMphZVIlTjo6Dno9WG8qmVA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/preview-api": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.20.tgz", + "integrity": "sha512-3ic2m9LDZEPwZk02wIhNc3n3rNvbi7VDKn52hDXfAxnL5EYm7yDICAkaWcVaTfblru2zn0EDJt7ROpthscTW5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/channels": "7.6.20", + "@storybook/client-logger": "7.6.20", + "@storybook/core-events": "7.6.20", + "@storybook/csf": "^0.1.2", + "@storybook/global": "^5.0.0", + "@storybook/types": "7.6.20", + "@types/qs": "^6.9.5", + "dequal": "^2.0.2", + "lodash": "^4.17.21", + "memoizerific": "^1.11.3", + "qs": "^6.10.0", + "synchronous-promise": "^2.0.15", + "ts-dedent": "^2.0.0", + "util-deprecate": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/react": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/react/-/react-7.6.20.tgz", + "integrity": "sha512-i5tKNgUbTNwlqBWGwPveDhh9ktlS0wGtd97A1ZgKZc3vckLizunlAFc7PRC1O/CMq5PTyxbuUb4RvRD2jWKwDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/client-logger": "7.6.20", + "@storybook/core-client": "7.6.20", + "@storybook/docs-tools": "7.6.20", + "@storybook/global": "^5.0.0", + "@storybook/preview-api": "7.6.20", + "@storybook/react-dom-shim": "7.6.20", + "@storybook/types": "7.6.20", + "@types/escodegen": "^0.0.6", + "@types/estree": "^0.0.51", + "@types/node": "^18.0.0", + "acorn": "^7.4.1", + "acorn-jsx": "^5.3.1", + "acorn-walk": "^7.2.0", + "escodegen": "^2.1.0", + "html-tags": "^3.1.0", + "lodash": "^4.17.21", + "prop-types": "^15.7.2", + "react-element-to-jsx-string": "^15.0.0", + "ts-dedent": "^2.0.0", + "type-fest": "~2.19", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@storybook/react-dom-shim": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-7.6.20.tgz", + "integrity": "sha512-SRvPDr9VWcS24ByQOVmbfZ655y5LvjXRlsF1I6Pr9YZybLfYbu3L5IicfEHT4A8lMdghzgbPFVQaJez46DTrkg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/react-vite": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/react-vite/-/react-vite-7.6.20.tgz", + "integrity": "sha512-uKuBFyGPZxpfR8vpDU/2OE9v7iTaxwL7ldd7k1swYd1rTSAPacTnEHSMl1R5AjUhkdI7gRmGN9q7qiVfK2XJCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@joshwooding/vite-plugin-react-docgen-typescript": "0.3.0", + "@rollup/pluginutils": "^5.0.2", + "@storybook/builder-vite": "7.6.20", + "@storybook/react": "7.6.20", + "@vitejs/plugin-react": "^3.0.1", + "magic-string": "^0.30.0", + "react-docgen": "^7.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" + } + }, + "node_modules/@storybook/react-vite/node_modules/@vitejs/plugin-react": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-3.1.0.tgz", + "integrity": "sha512-AfgcRL8ZBhAlc3BFdigClmTUMISmmzHn7sB2h9U1odvc5U/MjWXsAaz18b/WoppUTDBzxOJwo2VdClfUcItu9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.20.12", + "@babel/plugin-transform-react-jsx-self": "^7.18.6", + "@babel/plugin-transform-react-jsx-source": "^7.19.6", + "magic-string": "^0.27.0", + "react-refresh": "^0.14.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.1.0-beta.0" + } + }, + "node_modules/@storybook/react-vite/node_modules/@vitejs/plugin-react/node_modules/magic-string": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz", + "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.13" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@storybook/react-vite/node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@storybook/react/node_modules/@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@storybook/react/node_modules/@types/node": { + "version": "18.19.123", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.123.tgz", + "integrity": "sha512-K7DIaHnh0mzVxreCR9qwgNxp3MH9dltPNIEddW9MYUlcKAzm+3grKNSTe2vCJHI1FaLpvpL5JGJrz1UZDKYvDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@storybook/react/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@storybook/router": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/router/-/router-7.6.20.tgz", + "integrity": "sha512-mCzsWe6GrH47Xb1++foL98Zdek7uM5GhaSlrI7blWVohGa0qIUYbfJngqR4ZsrXmJeeEvqowobh+jlxg3IJh+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/client-logger": "7.6.20", + "memoizerific": "^1.11.3", + "qs": "^6.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@storybook/testing-library": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@storybook/testing-library/-/testing-library-0.2.2.tgz", + "integrity": "sha512-L8sXFJUHmrlyU2BsWWZGuAjv39Jl1uAqUHdxmN42JY15M4+XCMjGlArdCCjDe1wpTSW6USYISA9axjZojgtvnw==", + "deprecated": "In Storybook 8, this package functionality has been integrated to a new package called @storybook/test, which uses Vitest APIs for an improved experience. When upgrading to Storybook 8 with 'npx storybook@latest upgrade', you will get prompted and will get an automigration for the new package. Please migrate when you can.", + "dev": true, + "license": "MIT", + "dependencies": { + "@testing-library/dom": "^9.0.0", + "@testing-library/user-event": "^14.4.0", + "ts-dedent": "^2.2.0" + } + }, + "node_modules/@storybook/testing-library/node_modules/@testing-library/dom": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz", + "integrity": "sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.1.3", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@storybook/testing-library/node_modules/aria-query": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "deep-equal": "^2.0.5" + } + }, + "node_modules/@storybook/theming": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.6.20.tgz", + "integrity": "sha512-iT1pXHkSkd35JsCte6Qbanmprx5flkqtSHC6Gi6Umqoxlg9IjiLPmpHbaIXzoC06DSW93hPj5Zbi1lPlTvRC7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", + "@storybook/client-logger": "7.6.20", + "@storybook/global": "^5.0.0", + "memoizerific": "^1.11.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@storybook/types": { + "version": "7.6.20", + "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.20.tgz", + "integrity": "sha512-GncdY3x0LpbhmUAAJwXYtJDUQEwfF175gsjH0/fxPkxPoV7Sef9TM41jQLJW/5+6TnZoCZP/+aJZTJtq3ni23Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@storybook/channels": "7.6.20", + "@types/babel__core": "^7.0.0", + "@types/express": "^4.7.0", + "file-system-cache": "2.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, + "node_modules/@surma/rollup-plugin-off-main-thread": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", + "integrity": "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "ejs": "^3.1.6", + "json5": "^2.2.0", + "magic-string": "^0.25.0", + "string.prototype.matchall": "^4.0.6" + } + }, + "node_modules/@surma/rollup-plugin-off-main-thread/node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, + "node_modules/@tailwindcss/aspect-ratio": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/aspect-ratio/-/aspect-ratio-0.4.2.tgz", + "integrity": "sha512-8QPrypskfBa7QIMuKHg2TA7BqES6vhBrDLOv8Unb6FcFyd3TjKbc6lcmb9UPQHxfl24sXoJ41ux/H7qQQvfaSQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "tailwindcss": ">=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1" } }, "node_modules/@tailwindcss/forms": { @@ -1267,6 +5835,61 @@ "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, + "node_modules/@tanstack/query-core": { + "version": "5.85.5", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.85.5.tgz", + "integrity": "sha512-KO0WTob4JEApv69iYp1eGvfMSUkgw//IpMnq+//cORBzXf0smyRwPLrUvEe5qtAEGjwZTXrjxg+oJNP/C00t6w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/query-devtools": { + "version": "5.84.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.84.0.tgz", + "integrity": "sha512-fbF3n+z1rqhvd9EoGp5knHkv3p5B2Zml1yNRjh7sNXklngYI5RVIWUrUjZ1RIcEoscarUb0+bOvIs5x9dwzOXQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.85.5", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.85.5.tgz", + "integrity": "sha512-/X4EFNcnPiSs8wM2v+b6DqS5mmGeuJQvxBglmDxl6ZQb5V26ouD2SJYAcC3VjbNwqhY2zjxVD15rDA5nGbMn3A==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.85.5" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@tanstack/react-query-devtools": { + "version": "5.85.5", + "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.85.5.tgz", + "integrity": "sha512-6Ol6Q+LxrCZlQR4NoI5181r+ptTwnlPG2t7H9Sp3klxTBhYGunONqcgBn2YKRPsaKiYM8pItpKMdMXMEINntMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tanstack/query-devtools": "5.84.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@tanstack/react-query": "^5.85.5", + "react": "^18 || ^19" + } + }, "node_modules/@testing-library/dom": { "version": "10.4.1", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", @@ -1289,41 +5912,45 @@ } }, "node_modules/@testing-library/jest-dom": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.17.0.tgz", - "integrity": "sha512-ynmNeT7asXyH3aSVv4vvX4Rb+0qjOhdNHnO/3vuZNqPmhDpV/+rCSGwQ7bLcmU2cJ4dvoheIO85LQj0IbJHEtg==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.8.0.tgz", + "integrity": "sha512-WgXcWzVM6idy5JaftTVC8Vs83NKRmGJz4Hqs4oyOuO2J4r/y79vvKZsb+CaGyCSEbUPI6OsewfPd0G1A0/TUZQ==", "dev": true, "license": "MIT", "dependencies": { - "@adobe/css-tools": "^4.0.1", - "@babel/runtime": "^7.9.2", - "@types/testing-library__jest-dom": "^5.9.1", + "@adobe/css-tools": "^4.4.0", "aria-query": "^5.0.0", - "chalk": "^3.0.0", "css.escape": "^1.5.1", - "dom-accessibility-api": "^0.5.6", - "lodash": "^4.17.15", + "dom-accessibility-api": "^0.6.3", + "picocolors": "^1.1.1", "redent": "^3.0.0" }, "engines": { - "node": ">=8", + "node": ">=14", "npm": ">=6", "yarn": ">=1" } }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true, + "license": "MIT" + }, "node_modules/@testing-library/react": { - "version": "13.4.0", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.4.0.tgz", - "integrity": "sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw==", + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-14.3.1.tgz", + "integrity": "sha512-H99XjUhWQw0lTgyMN05W3xQG1Nh4lq574D8keFf1dDoNTJgp66VbJozRaczoF+wsiaPJNt/TcnfpLGufGxSrZQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/runtime": "^7.12.5", - "@testing-library/dom": "^8.5.0", + "@testing-library/dom": "^9.0.0", "@types/react-dom": "^18.0.0" }, "engines": { - "node": ">=12" + "node": ">=14" }, "peerDependencies": { "react": "^18.0.0", @@ -1331,9 +5958,9 @@ } }, "node_modules/@testing-library/react/node_modules/@testing-library/dom": { - "version": "8.20.1", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.1.tgz", - "integrity": "sha512-/DiOQ5xBxgdYRC8LNk7U+RWat0S3qRLeIw3ZIkMQ9kkVlRmwD/Eg8k8CqIpD6GW7u20JIUOfMKbxtiLutpjQ4g==", + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz", + "integrity": "sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1347,7 +5974,7 @@ "pretty-format": "^27.0.2" }, "engines": { - "node": ">=12" + "node": ">=14" } }, "node_modules/@testing-library/react/node_modules/aria-query": { @@ -1360,23 +5987,6 @@ "deep-equal": "^2.0.5" } }, - "node_modules/@testing-library/react/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/@testing-library/user-event": { "version": "14.6.1", "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", @@ -1443,23 +6053,34 @@ "@babel/types": "^7.28.2" } }, - "node_modules/@types/chai": { - "version": "4.3.20", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.20.tgz", - "integrity": "sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/chai-subset": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.6.tgz", - "integrity": "sha512-m8lERkkQj+uek18hXOZuec3W/fCRTrU4hrnXjH3qhHy96ytuPaPiWGgu7sJb7tZxZonO75vYAjCvpe/e4VUwRw==", + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", "dev": true, "license": "MIT", - "peerDependencies": { - "@types/chai": "<5.2.0" + "dependencies": { + "@types/connect": "*", + "@types/node": "*" } }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/d3-array": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", @@ -1523,18 +6144,88 @@ "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", "license": "MIT" }, - "node_modules/@types/hoist-non-react-statics": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.7.tgz", - "integrity": "sha512-PQTyIulDkIDro8P+IHbKCsw7U2xxBYflVzW/FgWdCAePD9xGSidgA76/GeJ6lBKoblyhf9pBY763gbrN+1dI8g==", + "node_modules/@types/doctrine": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@types/doctrine/-/doctrine-0.0.3.tgz", + "integrity": "sha512-w5jZ0ee+HaPOaX25X2/2oGR/7rgAQSYII7X7pp0m9KgBfMP7uKfMfTvcpl5Dj+eDBbpxKGiqE+flqDr6XTd2RA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/escodegen": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/escodegen/-/escodegen-0.0.6.tgz", + "integrity": "sha512-AjwI4MvWx3HAOaZqYsjKWyEObT9lcVV0Y0V8nXo6cXzN8ZiMxVhf6F3d/UNvXVGKrEzL/Dluc5p+y9GkzlTWig==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.23.tgz", + "integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==", + "dev": true, "license": "MIT", "dependencies": { - "hoist-non-react-statics": "^3.3.0" - }, - "peerDependencies": { - "@types/react": "*" + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" } }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", + "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/find-cache-dir": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/find-cache-dir/-/find-cache-dir-3.2.1.tgz", + "integrity": "sha512-frsJrz2t/CeGifcu/6uRo4b+SzAwT4NYCVPu1GN8IB9XTzrpPkGuV0tmh9mN+/L0PklAlsC3u5Fxt0ju00LXIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", @@ -1562,52 +6253,6 @@ "@types/istanbul-lib-report": "*" } }, - "node_modules/@types/jest": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz", - "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^30.0.0", - "pretty-format": "^30.0.0" - } - }, - "node_modules/@types/jest/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@types/jest/node_modules/pretty-format": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", - "integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/jest/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -1615,26 +6260,98 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mdx": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", + "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { - "version": "24.2.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.1.tgz", - "integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==", + "version": "20.19.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.11.tgz", + "integrity": "sha512-uug3FEEGv0r+jrecvUUpbY8lLisvIjg6AAic6a2bSP5OEOLeJsDSnvhCDov7ipFFMXS3orMpzlmi0ZcuGkBbow==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.10.0" + "undici-types": "~6.21.0" } }, + "node_modules/@types/node-fetch": { + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz", + "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.4" + } + }, + "node_modules/@types/papaparse": { + "version": "5.3.16", + "resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.16.tgz", + "integrity": "sha512-T3VuKMC2H0lgsjI9buTB3uuKj3EMD2eap1MOuEQuBQ44EnDx/IkGhU6EwiTf9zG3za4SKlmwKAImdDKdNnCsXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha512-nj39q0wAIdhwn7DGUyT9irmsKK1tV0bd5WFEhgpqNTMFZ8cE+jieuTphCW0tfdm47S2zVT5mr09B28b1chmQMA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/prop-types": { "version": "15.7.15", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, "license": "MIT" }, "node_modules/@types/react": { - "version": "18.3.23", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.23.tgz", - "integrity": "sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==", + "version": "18.3.24", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.24.tgz", + "integrity": "sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A==", + "devOptional": true, "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -1651,6 +6368,13 @@ "@types/react": "^18.0.0" } }, + "node_modules/@types/resolve": { + "version": "1.20.6", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.6.tgz", + "integrity": "sha512-A4STmOXPhMUtHH+S6ymgE2GiBSMqf4oTvcQZMcHzokuTLVYzXTB8ttjcgxOVaAp2lGwEdzZ0J+cRbbeevQj1UQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/semver": { "version": "7.7.0", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.0.tgz", @@ -1658,27 +6382,62 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/testing-library__jest-dom": { - "version": "5.14.9", - "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz", - "integrity": "sha512-FSYhIjFlfOpGSRyVoMBMuS3ws5ehFQODymf3vlI7U1K8c7PHwWwFY7VREfmsuzHSOnoKs/9/Y983ayOs7eRzqw==", + "node_modules/@types/send": { + "version": "0.17.5", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", + "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", "dev": true, "license": "MIT", "dependencies": { - "@types/jest": "*" + "@types/mime": "^1", + "@types/node": "*" } }, - "node_modules/@types/use-sync-external-store": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", - "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==", + "node_modules/@types/serve-static": { + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", + "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/statuses": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.6.tgz", + "integrity": "sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "dev": true, "license": "MIT" }, "node_modules/@types/yargs": { @@ -1734,6 +6493,19 @@ } } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/parser": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", @@ -1852,6 +6624,19 @@ } } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/utils": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", @@ -1878,6 +6663,19 @@ "eslint": "^7.0.0 || ^8.0.0" } }, + "node_modules/@typescript-eslint/utils/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@typescript-eslint/visitor-keys": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", @@ -1925,191 +6723,51 @@ } }, "node_modules/@vitest/expect": { - "version": "0.34.6", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.34.6.tgz", - "integrity": "sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.1.tgz", + "integrity": "sha512-jXL+9+ZNIJKruofqXuuTClf44eSpcHlgj3CiuNihUF3Ioujtmc0zIa3UJOW5RjDK1YLBJZnWBlPuqhYycLioog==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "0.34.6", - "@vitest/utils": "0.34.6", + "@vitest/spy": "1.6.1", + "@vitest/utils": "1.6.1", "chai": "^4.3.10" }, "funding": { "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/expect/node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@vitest/expect/node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@vitest/expect/node_modules/@vitest/utils": { - "version": "0.34.6", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.34.6.tgz", - "integrity": "sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==", - "dev": true, - "license": "MIT", - "dependencies": { - "diff-sequences": "^29.4.3", - "loupe": "^2.3.6", - "pretty-format": "^29.5.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/expect/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@vitest/expect/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@vitest/expect/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, "node_modules/@vitest/runner": { - "version": "0.34.6", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.34.6.tgz", - "integrity": "sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.1.tgz", + "integrity": "sha512-3nSnYXkVkf3mXFfE7vVyPmi3Sazhb/2cfZGGs0JRzFsPFvAMBEcrweV1V1GsrstdXeKCTXlJbvnQwGWgEIHmOA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "0.34.6", - "p-limit": "^4.0.0", + "@vitest/utils": "1.6.1", + "p-limit": "^5.0.0", "pathe": "^1.1.1" }, "funding": { "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/runner/node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@vitest/runner/node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@vitest/runner/node_modules/@vitest/utils": { - "version": "0.34.6", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.34.6.tgz", - "integrity": "sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==", - "dev": true, - "license": "MIT", - "dependencies": { - "diff-sequences": "^29.4.3", - "loupe": "^2.3.6", - "pretty-format": "^29.5.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/@vitest/runner/node_modules/p-limit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", + "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", "dev": true, "license": "MIT", "dependencies": { "yocto-queue": "^1.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@vitest/runner/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@vitest/runner/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, "node_modules/@vitest/runner/node_modules/yocto-queue": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz", @@ -2124,40 +6782,20 @@ } }, "node_modules/@vitest/snapshot": { - "version": "0.34.6", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.34.6.tgz", - "integrity": "sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.1.tgz", + "integrity": "sha512-WvidQuWAzU2p95u8GAKlRMqMyN1yOJkGHnx3M1PL9Raf7AQ1kwLKg04ADlCa3+OXUZE7BceOhVZiuWAbzCKcUQ==", "dev": true, "license": "MIT", "dependencies": { - "magic-string": "^0.30.1", + "magic-string": "^0.30.5", "pathe": "^1.1.1", - "pretty-format": "^29.5.0" + "pretty-format": "^29.7.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/snapshot/node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@vitest/snapshot/node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true, - "license": "MIT" - }, "node_modules/@vitest/snapshot/node_modules/ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", @@ -2194,75 +6832,56 @@ "license": "MIT" }, "node_modules/@vitest/spy": { - "version": "0.34.6", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.34.6.tgz", - "integrity": "sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.1.tgz", + "integrity": "sha512-MGcMmpGkZebsMZhbQKkAf9CX5zGvjkBTqf8Zx3ApYWXr3wG+QvEu2eXWfnIIWYSJExIp4V9FCKDEeygzkYrXMw==", "dev": true, "license": "MIT", "dependencies": { - "tinyspy": "^2.1.1" + "tinyspy": "^2.2.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/ui": { - "version": "0.34.7", - "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-0.34.7.tgz", - "integrity": "sha512-iizUu9R5Rsvsq8FtdJ0suMqEfIsIIzziqnasMHe4VH8vG+FnZSA3UAtCHx6rLeRupIFVAVg7bptMmuvMcsn8WQ==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-1.6.1.tgz", + "integrity": "sha512-xa57bCPGuzEFqGjPs3vVLyqareG8DX0uMkr5U/v5vLv5/ZUrBrPL7gzxzTJedEyZxFMfsozwTIbbYfEQVo3kgg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "0.34.7", - "fast-glob": "^3.3.0", - "fflate": "^0.8.0", - "flatted": "^3.2.7", + "@vitest/utils": "1.6.1", + "fast-glob": "^3.3.2", + "fflate": "^0.8.1", + "flatted": "^3.2.9", "pathe": "^1.1.1", "picocolors": "^1.0.0", - "sirv": "^2.0.3" + "sirv": "^2.0.4" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": ">=0.30.1 <1" + "vitest": "1.6.1" } }, "node_modules/@vitest/utils": { - "version": "0.34.7", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.34.7.tgz", - "integrity": "sha512-ziAavQLpCYS9sLOorGrFFKmy2gnfiNU0ZJ15TsMz/K92NAPS/rp9K4z6AJQQk5Y8adCy4Iwpxy7pQumQ/psnRg==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.1.tgz", + "integrity": "sha512-jOrrUvXM4Av9ZWiG1EajNto0u96kWAhJ1LmPmJhXXQx/32MecEKd10pOLYgS2BQx1TgkGhloPU1ArDW2vvaY6g==", "dev": true, "license": "MIT", "dependencies": { - "diff-sequences": "^29.4.3", - "loupe": "^2.3.6", - "pretty-format": "^29.5.0" + "diff-sequences": "^29.6.3", + "estree-walker": "^3.0.3", + "loupe": "^2.3.7", + "pretty-format": "^29.7.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/utils/node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@vitest/utils/node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true, - "license": "MIT" - }, "node_modules/@vitest/utils/node_modules/ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", @@ -2276,6 +6895,16 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/@vitest/utils/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/@vitest/utils/node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", @@ -2298,10 +6927,24 @@ "dev": true, "license": "MIT" }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", "dev": true, "license": "MIT", "bin": { @@ -2322,18 +6965,24 @@ } }, "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", "dev": true, "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, "engines": { "node": ">=0.4.0" } }, + "node_modules/adler-32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz", + "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -2351,6 +7000,35 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -2398,18 +7076,12 @@ "node": ">= 8" } }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/app-root-dir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/app-root-dir/-/app-root-dir-1.0.2.tgz", + "integrity": "sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } + "license": "MIT" }, "node_modules/arg": { "version": "5.0.2", @@ -2419,11 +7091,26 @@ "license": "MIT" }, "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, - "license": "Python-2.0" + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } }, "node_modules/aria-query": { "version": "5.3.0", @@ -2452,6 +7139,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true, + "license": "MIT" + }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -2462,6 +7156,42 @@ "node": ">=8" } }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/assert": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", + "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "is-nan": "^1.3.2", + "object-is": "^1.1.5", + "object.assign": "^4.1.4", + "util": "^0.12.5" + } + }, "node_modules/assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -2472,6 +7202,61 @@ "node": "*" } }, + "node_modules/ast-types": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz", + "integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/attr-accept": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.5.tgz", + "integrity": "sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/autoprefixer": { "version": "10.4.21", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", @@ -2526,6 +7311,76 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axios": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", + "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz", + "integrity": "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.7", + "@babel/helper-define-polyfill-provider": "^0.6.5", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", + "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.5", + "core-js-compat": "^3.43.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz", + "integrity": "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.5" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -2546,6 +7401,64 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/body-parser/node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", @@ -2569,10 +7482,16 @@ "node": ">=8" } }, + "node_modules/browser-assert": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/browser-assert/-/browser-assert-1.2.1.tgz", + "integrity": "sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ==", + "dev": true + }, "node_modules/browserslist": { - "version": "4.25.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.2.tgz", - "integrity": "sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA==", + "version": "4.25.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.3.tgz", + "integrity": "sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ==", "dev": true, "funding": [ { @@ -2590,8 +7509,8 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001733", - "electron-to-chromium": "^1.5.199", + "caniuse-lite": "^1.0.30001735", + "electron-to-chromium": "^1.5.204", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, @@ -2602,6 +7521,33 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -2635,7 +7581,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -2672,6 +7617,16 @@ "node": ">=6" } }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/camelcase-css": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", @@ -2683,9 +7638,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001734", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001734.tgz", - "integrity": "sha512-uhE1Ye5vgqju6OI71HTQqcBCZrvHugk0MjLak7Q+HfoBgoq5Bi+5YnwjP4fjDgrtYr/l8MVRBvzz9dPD4KyK0A==", + "version": "1.0.30001737", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001737.tgz", + "integrity": "sha512-BiloLiXtQNrY5UyF0+1nSJLXUENuhka2pzy2Fx5pGxqavdrxSCW4U6Pn/PoG3Efspi2frRbHpBV2XsrPE6EDlw==", "dev": true, "funding": [ { @@ -2703,6 +7658,19 @@ ], "license": "CC-BY-4.0" }, + "node_modules/cfb": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz", + "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/chai": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", @@ -2723,9 +7691,9 @@ } }, "node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { @@ -2733,7 +7701,10 @@ "supports-color": "^7.1.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/check-error": { @@ -2788,9 +7759,9 @@ } }, "node_modules/ci-info": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.0.tgz", - "integrity": "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true, "funding": [ { @@ -2803,6 +7774,83 @@ "node": ">=8" } }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -2812,6 +7860,15 @@ "node": ">=6" } }, + "node_modules/codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2832,6 +7889,18 @@ "dev": true, "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -2842,6 +7911,23 @@ "node": ">= 6" } }, + "node_modules/common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true, + "license": "MIT" + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2856,6 +7942,29 @@ "dev": true, "license": "MIT" }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -2863,6 +7972,49 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-js-compat": { + "version": "3.45.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.45.1.tgz", + "integrity": "sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.25.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2878,6 +8030,16 @@ "node": ">= 8" } }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/css.escape": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", @@ -3025,6 +8187,60 @@ "node": ">=12" } }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/date-fns": { "version": "2.30.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", @@ -3127,6 +8343,16 @@ "dev": true, "license": "MIT" }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -3163,6 +8389,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -3173,6 +8418,23 @@ "node": ">=6" } }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -3240,11 +8502,33 @@ "csstype": "^3.0.2" } }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz", + "integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -3262,10 +8546,33 @@ "dev": true, "license": "MIT" }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true, + "license": "MIT" + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/electron-to-chromium": { - "version": "1.5.199", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.199.tgz", - "integrity": "sha512-3gl0S7zQd88kCAZRO/DnxtBKuhMO4h0EaQIN3YgZfV6+pW+5+bf2AdQeHNESCoaQqo/gjGVYEf2YM4O5HJQqpQ==", + "version": "1.5.209", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.209.tgz", + "integrity": "sha512-Xoz0uMrim9ZETCQt8UgM5FxQF9+imA7PBpokoGcZloA1uw2LeHzTlip5cb5KOAsXZLjh/moN2vReN3ZjJmjI9A==", "dev": true, "license": "ISC" }, @@ -3276,11 +8583,89 @@ "dev": true, "license": "MIT" }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-abstract": { + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", + "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3290,7 +8675,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3317,11 +8701,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true, + "license": "MIT" + }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -3330,6 +8720,39 @@ "node": ">= 0.4" } }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/esbuild": { "version": "0.18.20", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", @@ -3368,6 +8791,19 @@ "@esbuild/win32-x64": "0.18.20" } }, + "node_modules/esbuild-register": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz", + "integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "peerDependencies": { + "esbuild": ">=0.12 <1" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -3378,6 +8814,13 @@ "node": ">=6" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, + "license": "MIT" + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -3391,6 +8834,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, "node_modules/eslint": { "version": "8.57.1", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", @@ -3501,6 +8966,13 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, "node_modules/eslint/node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -3512,21 +8984,17 @@ "concat-map": "0.0.1" } }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "argparse": "^2.0.1" }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, "node_modules/eslint/node_modules/minimatch": { @@ -3560,6 +9028,33 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/espree/node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esquery": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", @@ -3596,6 +9091,13 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -3606,30 +9108,139 @@ "node": ">=0.10.0" } }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-source-polyfill": { + "version": "1.0.31", + "resolved": "https://registry.npmjs.org/event-source-polyfill/-/event-source-polyfill-1.0.31.tgz", + "integrity": "sha512-4IJSItgS/41IxN5UVAVuAyczwZF7ZIEsM1XAoUzIHA6A+xzusEZUutdXz2Nr+MQPLxfTiCvqE79/C8HT8fKFvA==", + "license": "MIT" + }, "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", "license": "MIT" }, - "node_modules/expect": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/expect/-/expect-30.0.5.tgz", - "integrity": "sha512-P0te2pt+hHI5qLJkIR+iMvS+lYUZml8rKKsohVHAGY+uClp9XVbdyYNJOIjSRpHVp8s8YqxJCiHUkSYZGr8rtQ==", + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/expect-utils": "30.0.5", - "@jest/get-type": "30.0.1", - "jest-matcher-utils": "30.0.5", - "jest-message-util": "30.0.5", - "jest-mock": "30.0.5", - "jest-util": "30.0.5" + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/express/node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3690,6 +9301,23 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fastq": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", @@ -3700,6 +9328,16 @@ "reusify": "^1.0.4" } }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, "node_modules/fflate": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", @@ -3720,6 +9358,67 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-selector": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-2.1.2.tgz", + "integrity": "sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig==", + "license": "MIT", + "dependencies": { + "tslib": "^2.7.0" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/file-system-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/file-system-cache/-/file-system-cache-2.3.0.tgz", + "integrity": "sha512-l4DMNdsIPsVnKrgEXbJwDJsA5mB8rGwHYERMgqQx/xAUtChPJMre1bXBzDEqqVbWv9AIbFezXMxeEkZDSrXUOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fs-extra": "11.1.1", + "ramda": "0.29.0" + } + }, + "node_modules/file-system-cache/node_modules/fs-extra": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -3733,6 +9432,129 @@ "node": ">=8" } }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "license": "MIT", + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-cache-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-cache-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-cache-dir/node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -3772,6 +9594,26 @@ "dev": true, "license": "ISC" }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -3805,6 +9647,41 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/frac": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -3819,6 +9696,55 @@ "url": "https://github.com/sponsors/rawify" } }, + "node_modules/framer-motion": { + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.18.0.tgz", + "integrity": "sha512-oGlDh1Q1XqYPksuTD/usb0I70hq95OUzmL9+6Zd+Hs4XV0oaISBa/UUMSjYiq6m8EUF32132mOJ8xVZS+I0S6w==", + "license": "MIT", + "dependencies": { + "tslib": "^2.4.0" + }, + "optionalDependencies": { + "@emotion/is-prop-valid": "^0.8.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz", + "integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3845,8 +9771,28 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", "dev": true, "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3871,6 +9817,16 @@ "node": ">=6.9.0" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-func-name": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", @@ -3885,7 +9841,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -3906,11 +9861,36 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "dev": true, + "license": "ISC" + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/get-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -3920,23 +9900,60 @@ "node": ">= 0.4" } }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/github-slugger": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.5.0.tgz", + "integrity": "sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==", + "dev": true, + "license": "ISC" + }, "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": "*" + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -3955,28 +9972,20 @@ "node": ">=10.13.0" } }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/globals": { @@ -3995,6 +10004,36 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/globals/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/globby": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", @@ -4029,7 +10068,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4052,6 +10090,38 @@ "dev": true, "license": "MIT" }, + "node_modules/graphql": { + "version": "16.11.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.11.0.tgz", + "integrity": "sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -4088,11 +10158,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4105,7 +10190,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -4121,7 +10205,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -4130,19 +10213,11 @@ "node": ">= 0.4" } }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "license": "BSD-3-Clause", - "dependencies": { - "react-is": "^16.7.0" - } - }, - "node_modules/hoist-non-react-statics/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "node_modules/headers-polyfill": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.3.tgz", + "integrity": "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==", + "dev": true, "license": "MIT" }, "node_modules/html-parse-stringify": { @@ -4154,6 +10229,46 @@ "void-elements": "3.1.0" } }, + "node_modules/html-tags": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, "node_modules/i18next": { "version": "23.16.8", "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.16.8.tgz", @@ -4177,15 +10292,26 @@ "@babel/runtime": "^7.23.2" } }, - "node_modules/i18next-browser-languagedetector": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-7.2.2.tgz", - "integrity": "sha512-6b7r75uIJDWCcCflmbof+sJ94k9UQO4X0YR62oUfqGI/GjCLVzlCwu8TFdRZIqVLzWbzNcmkmhfqKEr4TLz4HQ==", + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, "license": "MIT", "dependencies": { - "@babel/runtime": "^7.23.2" + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" } }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", + "dev": true, + "license": "ISC" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -4197,9 +10323,9 @@ } }, "node_modules/immer": { - "version": "9.0.21", - "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", - "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz", + "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==", "license": "MIT", "funding": { "type": "opencollective", @@ -4223,6 +10349,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -4286,6 +10422,26 @@ "node": ">=12" } }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-arguments": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", @@ -4321,6 +10477,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-bigint": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", @@ -4396,6 +10572,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-date-object": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", @@ -4423,6 +10617,22 @@ "node": ">=0.10.0" } }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -4433,6 +10643,25 @@ "node": ">=8" } }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -4459,6 +10688,50 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-node-process": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", + "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "dev": true, + "license": "MIT" + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -4486,6 +10759,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -4496,6 +10779,16 @@ "node": ">=8" } }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -4515,6 +10808,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-set": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", @@ -4544,6 +10847,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-string": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", @@ -4579,6 +10895,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-weakmap": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", @@ -4592,6 +10924,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-weakset": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", @@ -4623,6 +10971,33 @@ "dev": true, "license": "ISC" }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jackspeak": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", @@ -4639,273 +11014,149 @@ "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/jest-diff": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.0.5.tgz", - "integrity": "sha512-1UIqE9PoEKaHcIKvq2vbibrCog4Y8G0zmOxgQUVEiTqwR5hJVMCoDsN1vFvI5JvwD37hjueZ1C4l2FyGnfpE0A==", + "node_modules/jake": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@jest/diff-sequences": "30.0.1", - "@jest/get-type": "30.0.1", - "chalk": "^4.1.2", - "pretty-format": "30.0.5" + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-diff/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "bin": { + "jake": "bin/cli.js" }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-diff/node_modules/pretty-format": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", - "integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==", + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" } }, - "node_modules/jest-diff/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-diff/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-matcher-utils": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.0.5.tgz", - "integrity": "sha512-uQgGWt7GOrRLP1P7IwNWwK1WAQbq+m//ZY0yXygyfWp0rJlksMSLQAA4wYQC3b6wl3zfnchyTx+k3HZ5aPtCbQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.0.1", - "chalk": "^4.1.2", - "jest-diff": "30.0.5", - "pretty-format": "30.0.5" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-matcher-utils/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-matcher-utils/node_modules/pretty-format": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", - "integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-matcher-utils/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-matcher-utils/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-message-util": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.0.5.tgz", - "integrity": "sha512-NAiDOhsK3V7RU0Aa/HnrQo+E4JlbarbmI3q6Pi4KcxicdtjV82gcIUrejOtczChtVQR4kddu1E1EJlW6EN9IyA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.0.5", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.0.5", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-message-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-message-util/node_modules/pretty-format": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", - "integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-message-util/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-message-util/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, "node_modules/jest-mock": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.0.5.tgz", - "integrity": "sha512-Od7TyasAAQX/6S+QCbN6vZoWOMwlTtzzGuxJku1GhGanAjz9y+QsQkpScDmETvdc9aSXyJ/Op4rhpMYBWW91wQ==", + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", + "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.0.5", - "@types/node": "*", - "jest-util": "30.0.5" + "@jest/types": "^27.5.1", + "@types/node": "*" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-mock/node_modules/@jest/types": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", + "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^16.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-mock/node_modules/@types/yargs": { + "version": "16.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", + "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" } }, "node_modules/jest-regex-util": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", - "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", "dev": true, "license": "MIT", "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-util": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.0.5.tgz", - "integrity": "sha512-pvyPWssDZR0FlfMxCBoc0tvM8iUEskaRFALUtGQYzVEAqisAztmy+R8LnU14KT4XA0H/a5HMVTXat1jLne010g==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.0.5", + "@jest/types": "^29.6.3", "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, "node_modules/jiti": { @@ -4925,13 +11176,14 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, "license": "MIT", "dependencies": { - "argparse": "^2.0.1" + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" @@ -4957,6 +11209,13 @@ "dev": true, "license": "MIT" }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true, + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -4984,6 +11243,29 @@ "node": ">=6" } }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -4994,6 +11276,31 @@ "json-buffer": "3.0.1" } }, + "node_modules/lazy-universal-dotenv": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/lazy-universal-dotenv/-/lazy-universal-dotenv-4.0.0.tgz", + "integrity": "sha512-aXpZJRnTkpK6gQ/z4nk+ZBLd/Qdp118cvPruLSIQzQNRhKwEcdXCOzXuF55VDqIiuAaY3UGZ10DJtvZzDcvsxg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "app-root-dir": "^1.0.2", + "dotenv": "^16.0.0", + "dotenv-expand": "^10.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -5029,11 +11336,15 @@ "license": "MIT" }, "node_modules/local-pkg": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz", - "integrity": "sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==", + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz", + "integrity": "sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==", "dev": true, "license": "MIT", + "dependencies": { + "mlly": "^1.7.3", + "pkg-types": "^1.2.1" + }, "engines": { "node": ">=14" }, @@ -5070,6 +11381,13 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", @@ -5084,6 +11402,13 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", + "dev": true, + "license": "MIT" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -5117,9 +11442,9 @@ } }, "node_modules/lucide-react": { - "version": "0.263.1", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.263.1.tgz", - "integrity": "sha512-keqxAx97PlaEN89PXZ6ki1N8nRjGWtDa4021GFYLNj0RgruM5odbpl8GHTExj0hhPq3sF6Up0gnxt6TSHu+ovw==", + "version": "0.294.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.294.0.tgz", + "integrity": "sha512-V7o0/VECSGbLHn3/1O67FUgBwWB+hmzshrgDVRJQhMh8uj5D3HBuIvhuAmQTtlupILSplwIZg5FTc4tTKMA2SA==", "license": "ISC", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0" @@ -5136,25 +11461,132 @@ } }, "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "version": "0.30.18", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz", + "integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/map-or-similar": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/map-or-similar/-/map-or-similar-1.5.0.tgz", + "integrity": "sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==", + "dev": true, + "license": "MIT" + }, + "node_modules/markdown-to-jsx": { + "version": "7.7.13", + "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.7.13.tgz", + "integrity": "sha512-DiueEq2bttFcSxUs85GJcQVrOr0+VVsPfj9AEUPqmExJ3f8P/iQNvZHltV4tm1XVhu1kl0vWBZWT3l99izRMaA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "peerDependencies": { + "react": ">= 0.14.0" } }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" } }, + "node_modules/mdast-util-definitions": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz", + "integrity": "sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "unist-util-visit": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz", + "integrity": "sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memoizerific": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/memoizerific/-/memoizerific-1.11.3.tgz", + "integrity": "sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==", + "dev": true, + "license": "MIT", + "dependencies": { + "map-or-similar": "^1.5.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -5165,6 +11597,16 @@ "node": ">= 8" } }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -5179,17 +11621,51 @@ "node": ">=8.6" } }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true, "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/min-indent": { @@ -5228,6 +11704,16 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", @@ -5239,16 +11725,29 @@ } }, "node_modules/mlly": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz", - "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", + "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", "dev": true, "license": "MIT", "dependencies": { - "acorn": "^8.14.0", - "pathe": "^2.0.1", - "pkg-types": "^1.3.0", - "ufo": "^1.5.4" + "acorn": "^8.15.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.1" + } + }, + "node_modules/mlly/node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" } }, "node_modules/mlly/node_modules/pathe": { @@ -5275,6 +11774,81 @@ "dev": true, "license": "MIT" }, + "node_modules/msw": { + "version": "2.10.5", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.10.5.tgz", + "integrity": "sha512-0EsQCrCI1HbhpBWd89DvmxY6plmvrM96b0sCIztnvcNHQbXn5vqwm1KlXslo6u4wN9LFGLC1WFjjgljcQhe40A==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@bundled-es-modules/cookie": "^2.0.1", + "@bundled-es-modules/statuses": "^1.0.1", + "@bundled-es-modules/tough-cookie": "^0.1.6", + "@inquirer/confirm": "^5.0.0", + "@mswjs/interceptors": "^0.39.1", + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/until": "^2.1.0", + "@types/cookie": "^0.6.0", + "@types/statuses": "^2.0.4", + "graphql": "^16.8.1", + "headers-polyfill": "^4.0.2", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.3", + "path-to-regexp": "^6.3.0", + "picocolors": "^1.1.1", + "strict-event-emitter": "^0.5.1", + "type-fest": "^4.26.1", + "yargs": "^17.7.2" + }, + "bin": { + "msw": "cli/index.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mswjs" + }, + "peerDependencies": { + "typescript": ">= 4.8.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/msw/node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/msw/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -5313,6 +11887,51 @@ "dev": true, "license": "MIT" }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, "node_modules/node-releases": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", @@ -5340,6 +11959,35 @@ "node": ">=0.10.0" } }, + "node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -5420,6 +12068,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -5430,6 +12091,22 @@ "wrappy": "1" } }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -5448,6 +12125,31 @@ "node": ">= 0.8.0" } }, + "node_modules/outvariant": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", + "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==", + "dev": true, + "license": "MIT" + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -5480,6 +12182,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", @@ -5487,6 +12199,12 @@ "dev": true, "license": "BlueOak-1.0.0" }, + "node_modules/papaparse": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.3.tgz", + "integrity": "sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A==", + "license": "MIT" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -5500,6 +12218,16 @@ "node": ">=6" } }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -5561,6 +12289,13 @@ "dev": true, "license": "ISC" }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "dev": true, + "license": "MIT" + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -5596,13 +12331,13 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "license": "MIT", "engines": { - "node": ">=12" + "node": ">=8.6" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -5628,6 +12363,19 @@ "node": ">= 6" } }, + "node_modules/pkg-dir": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz", + "integrity": "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^5.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/pkg-types": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", @@ -5647,6 +12395,19 @@ "dev": true, "license": "MIT" }, + "node_modules/polished": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/polished/-/polished-4.3.1.tgz", + "integrity": "sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.17.8" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -5831,6 +12592,110 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-tailwindcss": { + "version": "0.5.14", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.5.14.tgz", + "integrity": "sha512-Puaz+wPUAhFp8Lo9HuciYKM2Y2XExESjeT+9NQoVFXZsPPnc9VYss2SpxdQ6vbatmt8/4+SN0oe0I1cPDABg9Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.21.3" + }, + "peerDependencies": { + "@ianvs/prettier-plugin-sort-imports": "*", + "@prettier/plugin-pug": "*", + "@shopify/prettier-plugin-liquid": "*", + "@trivago/prettier-plugin-sort-imports": "*", + "@zackad/prettier-plugin-twig-melody": "*", + "prettier": "^3.0", + "prettier-plugin-astro": "*", + "prettier-plugin-css-order": "*", + "prettier-plugin-import-sort": "*", + "prettier-plugin-jsdoc": "*", + "prettier-plugin-marko": "*", + "prettier-plugin-organize-attributes": "*", + "prettier-plugin-organize-imports": "*", + "prettier-plugin-sort-imports": "*", + "prettier-plugin-style-order": "*", + "prettier-plugin-svelte": "*" + }, + "peerDependenciesMeta": { + "@ianvs/prettier-plugin-sort-imports": { + "optional": true + }, + "@prettier/plugin-pug": { + "optional": true + }, + "@shopify/prettier-plugin-liquid": { + "optional": true + }, + "@trivago/prettier-plugin-sort-imports": { + "optional": true + }, + "@zackad/prettier-plugin-twig-melody": { + "optional": true + }, + "prettier-plugin-astro": { + "optional": true + }, + "prettier-plugin-css-order": { + "optional": true + }, + "prettier-plugin-import-sort": { + "optional": true + }, + "prettier-plugin-jsdoc": { + "optional": true + }, + "prettier-plugin-marko": { + "optional": true + }, + "prettier-plugin-organize-attributes": { + "optional": true + }, + "prettier-plugin-organize-imports": { + "optional": true + }, + "prettier-plugin-sort-imports": { + "optional": true + }, + "prettier-plugin-style-order": { + "optional": true + }, + "prettier-plugin-svelte": { + "optional": true + } + } + }, + "node_modules/pretty-bytes": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.1.1.tgz", + "integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/pretty-format": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", @@ -5859,6 +12724,16 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5876,6 +12751,39 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -5886,6 +12794,29 @@ "node": ">=6" } }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true, + "license": "MIT" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -5907,6 +12838,53 @@ ], "license": "MIT" }, + "node_modules/ramda": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.29.0.tgz", + "integrity": "sha512-BBea6L67bYLtdbOqfp8f58fPMqEwx0doL+pAi8TZyp2YWz8R9G8z9x75CZI8W+ftqhFHCpEX2cRnUUXK130iKA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ramda" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -5919,6 +12897,56 @@ "node": ">=0.10.0" } }, + "node_modules/react-colorful": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz", + "integrity": "sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/react-docgen": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-7.1.1.tgz", + "integrity": "sha512-hlSJDQ2synMPKFZOsKo9Hi8WWZTC7POR8EmWvTSjow+VDgKzkmjQvFm2fk0tmRw+f0vTOIYKlarR0iL4996pdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.18.9", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9", + "@types/babel__core": "^7.18.0", + "@types/babel__traverse": "^7.18.0", + "@types/doctrine": "^0.0.9", + "@types/resolve": "^1.20.2", + "doctrine": "^3.0.0", + "resolve": "^1.22.1", + "strip-indent": "^4.0.0" + }, + "engines": { + "node": ">=16.14.0" + } + }, + "node_modules/react-docgen-typescript": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/react-docgen-typescript/-/react-docgen-typescript-2.4.0.tgz", + "integrity": "sha512-ZtAp5XTO5HRzQctjPU0ybY0RRCQO19X/8fxn3w7y2VVTUbGHDKULPTL4ky3vB05euSgG5NpALhEhDPvQ56wvXg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "typescript": ">= 4.3.x" + } + }, + "node_modules/react-docgen/node_modules/@types/doctrine": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/@types/doctrine/-/doctrine-0.0.9.tgz", + "integrity": "sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA==", + "dev": true, + "license": "MIT" + }, "node_modules/react-dom": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", @@ -5932,6 +12960,46 @@ "react": "^18.3.1" } }, + "node_modules/react-dropzone": { + "version": "14.3.8", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.3.8.tgz", + "integrity": "sha512-sBgODnq+lcA4P296DY4wacOZz3JFpD99fp+hb//iBO2HHnyeZU3FwWyXJ6salNpqQdsZrgMrotuko/BdJMV8Ug==", + "license": "MIT", + "dependencies": { + "attr-accept": "^2.2.4", + "file-selector": "^2.1.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">= 10.13" + }, + "peerDependencies": { + "react": ">= 16.8 || 18.0.0" + } + }, + "node_modules/react-element-to-jsx-string": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/react-element-to-jsx-string/-/react-element-to-jsx-string-15.0.0.tgz", + "integrity": "sha512-UDg4lXB6BzlobN60P8fHWVPX3Kyw8ORrTeBtClmIlGdkOOE+GYQSFvmEU5iLLpwp/6v42DINwNcwOhOLfQ//FQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@base2/pretty-print-object": "1.0.1", + "is-plain-object": "5.0.0", + "react-is": "18.1.0" + }, + "peerDependencies": { + "react": "^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 || ^18.0.0", + "react-dom": "^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 || ^18.0.0" + } + }, + "node_modules/react-element-to-jsx-string/node_modules/react-is": { + "version": "18.1.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.1.0.tgz", + "integrity": "sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg==", + "dev": true, + "license": "MIT" + }, "node_modules/react-hook-form": { "version": "7.62.0", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.62.0.tgz", @@ -5949,9 +13017,9 @@ } }, "node_modules/react-hot-toast": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.5.2.tgz", - "integrity": "sha512-Tun3BbCxzmXXM7C+NI4qiv6lT0uwGh4oAfeJyNOjYUejTsm35mK9iCaYLGv8cBz9L5YxZLx/2ii7zsIwPtPUdw==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.6.0.tgz", + "integrity": "sha512-bH+2EBMZ4sdyou/DPrfgIouFpcRLCJ+HoCA32UoAYHn6T3Ur5yfcDCeSr5mwldl6pFOsiocmrXMuoCJ1vV8bWg==", "license": "MIT", "dependencies": { "csstype": "^3.1.3", @@ -5994,51 +13062,6 @@ "dev": true, "license": "MIT" }, - "node_modules/react-redux": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.3.tgz", - "integrity": "sha512-n0ZrutD7DaX/j9VscF+uTALI3oUPa/pO4Z3soOBIjuRn/FzVu6aehhysxZCLi6y7duMf52WNZGMl7CtuK5EnRw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.12.1", - "@types/hoist-non-react-statics": "^3.3.1", - "@types/use-sync-external-store": "^0.0.3", - "hoist-non-react-statics": "^3.3.2", - "react-is": "^18.0.0", - "use-sync-external-store": "^1.0.0" - }, - "peerDependencies": { - "@types/react": "^16.8 || ^17.0 || ^18.0", - "@types/react-dom": "^16.8 || ^17.0 || ^18.0", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0", - "react-native": ">=0.59", - "redux": "^4 || ^5.0.0-beta.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - }, - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - }, - "redux": { - "optional": true - } - } - }, - "node_modules/react-redux/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "license": "MIT" - }, "node_modules/react-refresh": { "version": "0.17.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", @@ -6049,6 +13072,53 @@ "node": ">=0.10.0" } }, + "node_modules/react-remove-scroll": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz", + "integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-router": { "version": "6.30.1", "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.1.tgz", @@ -6096,6 +13166,28 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -6135,17 +13227,21 @@ "node": ">=8.10.0" } }, - "node_modules/readdirp/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/recast": { + "version": "0.23.11", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.11.tgz", + "integrity": "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=8.6" + "dependencies": { + "ast-types": "^0.16.1", + "esprima": "~4.0.0", + "source-map": "~0.6.1", + "tiny-invariant": "^1.3.3", + "tslib": "^2.0.1" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "engines": { + "node": ">= 4" } }, "node_modules/recharts": { @@ -6200,22 +13296,60 @@ "node": ">=8" } }, - "node_modules/redux": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", - "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "node_modules/redent/node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, "license": "MIT", "dependencies": { - "@babel/runtime": "^7.9.2" + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/redux-thunk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", - "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, "license": "MIT", - "peerDependencies": { - "redux": "^4" + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true, + "license": "MIT" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" } }, "node_modules/regexp.prototype.flags": { @@ -6239,10 +13373,116 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/reselect": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", - "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==", + "node_modules/regexpu-core": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", + "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.12.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/regjsparser": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~3.0.2" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/remark-external-links": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/remark-external-links/-/remark-external-links-8.0.0.tgz", + "integrity": "sha512-5vPSX0kHoSsqtdftSHhIYofVINC8qmp0nctkeU9YoJwV3YfiBRiI6cbFRJ0oI/1F9xS+bopXG0m2KS8VFscuKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "extend": "^3.0.0", + "is-absolute-url": "^3.0.0", + "mdast-util-definitions": "^4.0.0", + "space-separated-tokens": "^1.0.0", + "unist-util-visit": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-slug": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/remark-slug/-/remark-slug-6.1.0.tgz", + "integrity": "sha512-oGCxDF9deA8phWvxFuyr3oSJsdyUAxMFbA0mZ7Y1Sas+emILtO+e5WutF9564gDsEN4IXaQXm5pFo6MLH+YmwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "github-slugger": "^1.0.0", + "mdast-util-to-string": "^1.0.0", + "unist-util-visit": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true, "license": "MIT" }, "node_modules/resolve": { @@ -6267,13 +13507,13 @@ } }, "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/reusify": { @@ -6304,18 +13544,63 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/rollup": { - "version": "3.29.5", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz", - "integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==", + "version": "2.79.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", + "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", "dev": true, "license": "MIT", "bin": { "rollup": "dist/bin/rollup" }, "engines": { - "node": ">=14.18.0", - "npm": ">=8.0.0" + "node": ">=10.0.0" }, "optionalDependencies": { "fsevents": "~2.3.2" @@ -6345,6 +13630,64 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safe-regex-test": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", @@ -6363,6 +13706,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", @@ -6373,16 +13723,91 @@ } }, "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" }, "engines": { - "node": ">=10" + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" } }, "node_modules/set-function-length": { @@ -6419,6 +13844,28 @@ "node": ">= 0.4" } }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -6563,6 +14010,23 @@ "node": ">=8" } }, + "node_modules/smob": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/smob/-/smob-1.5.0.tgz", + "integrity": "sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==", + "dev": true, + "license": "MIT" + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -6573,27 +14037,53 @@ "node": ">=0.10.0" } }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "license": "MIT", "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead", + "dev": true, + "license": "MIT" + }, + "node_modules/space-separated-tokens": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", + "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", "dev": true, "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "license": "Apache-2.0", + "dependencies": { + "frac": "~1.1.2" + }, "engines": { - "node": ">=8" + "node": ">=0.8" } }, "node_modules/stackback": { @@ -6603,6 +14093,16 @@ "dev": true, "license": "MIT" }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/std-env": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", @@ -6624,6 +14124,20 @@ "node": ">= 0.4" } }, + "node_modules/store2": { + "version": "2.14.4", + "resolved": "https://registry.npmjs.org/store2/-/store2-2.14.4.tgz", + "integrity": "sha512-srTItn1GOvyvOycgxjAnPA63FZNwy0PTyUBFMHRM+hVFltAeoh0LmNBz9SZqUS9mMqGk8rfyWyXn3GH5ReJ8Zw==", + "dev": true, + "license": "MIT" + }, + "node_modules/strict-event-emitter": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", + "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", + "dev": true, + "license": "MIT" + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -6666,9 +14180,9 @@ "license": "MIT" }, "node_modules/string-width/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", + "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", "dev": true, "license": "MIT", "engines": { @@ -6694,6 +14208,108 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -6721,17 +14337,43 @@ "node": ">=8" } }, - "node_modules/strip-indent": { + "node_modules/strip-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", + "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/strip-final-newline": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.0.0.tgz", + "integrity": "sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==", "dev": true, "license": "MIT", "dependencies": { - "min-indent": "^1.0.0" + "min-indent": "^1.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/strip-json-comments": { @@ -6748,18 +14390,25 @@ } }, "node_modules/strip-literal": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-1.3.0.tgz", - "integrity": "sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.1.tgz", + "integrity": "sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==", "dev": true, "license": "MIT", "dependencies": { - "acorn": "^8.10.0" + "js-tokens": "^9.0.1" }, "funding": { "url": "https://github.com/sponsors/antfu" } }, + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", @@ -6783,43 +14432,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/sucrase/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/sucrase/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -6846,10 +14458,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/synchronous-promise": { + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/synchronous-promise/-/synchronous-promise-2.0.17.tgz", + "integrity": "sha512-AsS729u2RHUfEra9xJrE39peJcc2stq2+poBXX8bcM08Y6g9j/i/PUzwNQqkaJde7Ntg1TO7bSREbR5sdosQ+g==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/tailwind-merge": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-1.14.0.tgz", - "integrity": "sha512-3mFKyCo/MBcgyOTlrY8T7odzZFx+w+qKSMAmdFzRvqBfLlSigU6TZnlFHK0lkMwj9Bj8OYU+9yW9lmGuS0QEnQ==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz", + "integrity": "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==", "license": "MIT", "funding": { "type": "github", @@ -6908,6 +14527,171 @@ "node": ">=4" } }, + "node_modules/telejson": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/telejson/-/telejson-7.2.0.tgz", + "integrity": "sha512-1QTEcJkJEhc8OnStBx/ILRu5J2p0GjvWsBx56bmZRqnrkdBMUe+nX92jxV+p3dB4CP6PZCdJMQJwCggkNBMzkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "memoizerific": "^1.11.3" + } + }, + "node_modules/temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tempy": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz", + "integrity": "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-stream": "^2.0.0", + "temp-dir": "^2.0.0", + "type-fest": "^0.16.0", + "unique-string": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/type-fest": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", + "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terser": { + "version": "5.43.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz", + "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.14.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser/node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -6952,9 +14736,9 @@ "license": "MIT" }, "node_modules/tinypool": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.7.0.tgz", - "integrity": "sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==", + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", + "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", "dev": true, "license": "MIT", "engines": { @@ -6971,6 +14755,13 @@ "node": ">=14.0.0" } }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -6984,6 +14775,23 @@ "node": ">=8.0" } }, + "node_modules/tocbot": { + "version": "4.36.4", + "resolved": "https://registry.npmjs.org/tocbot/-/tocbot-4.36.4.tgz", + "integrity": "sha512-ffznkKnZ1NdghwR1y8hN6W7kjn4FwcXq32Z1mn35gA7jd8dt2cTVAwL3d0BXXZGPu0Hd0evverUvcYAb/7vn0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, "node_modules/totalist": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", @@ -6994,6 +14802,39 @@ "node": ">=6" } }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, "node_modules/ts-api-utils": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", @@ -7007,6 +14848,16 @@ "typescript": ">=4.2.0" } }, + "node_modules/ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.10" + } + }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -7014,6 +14865,12 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -7038,18 +14895,110 @@ } }, "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=10" + "node": ">=12.20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typescript": { "version": "5.9.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", @@ -7071,13 +15020,203 @@ "dev": true, "license": "MIT" }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/undici-types": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", - "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/unist-util-is": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", + "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", + "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0", + "unist-util-visit-parents": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", + "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unplugin": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.16.1.tgz", + "integrity": "sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.14.0", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/unplugin/node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4", + "yarn": "*" + } + }, "node_modules/update-browserslist-db": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", @@ -7119,6 +15258,74 @@ "punycode": "^2.1.0" } }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-resize-observer": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/use-resize-observer/-/use-resize-observer-9.1.0.tgz", + "integrity": "sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@juggle/resize-observer": "^3.3.1" + }, + "peerDependencies": { + "react": "16.8.0 - 18", + "react-dom": "16.8.0 - 18" + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/use-sync-external-store": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", @@ -7128,6 +15335,20 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -7135,6 +15356,40 @@ "dev": true, "license": "MIT" }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/victory-vendor": { "version": "36.9.2", "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", @@ -7158,33 +15413,34 @@ } }, "node_modules/vite": { - "version": "4.5.14", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.14.tgz", - "integrity": "sha512-+v57oAaoYNnO3hIu5Z/tJRZjq5aHM2zDve9YZ8HngVHbhk66RStobhb1sqPMIPEleV6cNKYK4eGrAbE9Ulbl2g==", + "version": "5.4.19", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", + "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.18.10", - "postcss": "^8.4.27", - "rollup": "^3.27.1" + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": "^18.0.0 || >=20.0.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" }, "optionalDependencies": { - "fsevents": "~2.3.2" + "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": ">= 14", + "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", + "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" @@ -7202,6 +15458,9 @@ "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true }, @@ -7214,84 +15473,558 @@ } }, "node_modules/vite-node": { - "version": "0.34.6", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.34.6.tgz", - "integrity": "sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.1.tgz", + "integrity": "sha512-YAXkfvGtuTzwWbDSACdJSg4A4DZiAqckWe90Zapc/sEX3XvHcw1NdurM/6od8J207tSDqNbSsgdCacBgvJKFuA==", "dev": true, "license": "MIT", "dependencies": { "cac": "^6.7.14", "debug": "^4.3.4", - "mlly": "^1.4.0", "pathe": "^1.1.1", "picocolors": "^1.0.0", - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0-0" + "vite": "^5.0.0" }, "bin": { "vite-node": "vite-node.mjs" }, "engines": { - "node": ">=v14.18.0" + "node": "^18.0.0 || >=20.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, - "node_modules/vitest": { - "version": "0.34.6", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.34.6.tgz", - "integrity": "sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==", + "node_modules/vite-plugin-pwa": { + "version": "0.17.5", + "resolved": "https://registry.npmjs.org/vite-plugin-pwa/-/vite-plugin-pwa-0.17.5.tgz", + "integrity": "sha512-UxRNPiJBzh4tqU/vc8G2TxmrUTzT6BqvSzhszLk62uKsf+npXdvLxGDz9C675f4BJi6MbD2tPnJhi5txlMzxbQ==", "dev": true, "license": "MIT", "dependencies": { - "@types/chai": "^4.3.5", - "@types/chai-subset": "^1.3.3", - "@types/node": "*", - "@vitest/expect": "0.34.6", - "@vitest/runner": "0.34.6", - "@vitest/snapshot": "0.34.6", - "@vitest/spy": "0.34.6", - "@vitest/utils": "0.34.6", - "acorn": "^8.9.0", - "acorn-walk": "^8.2.0", - "cac": "^6.7.14", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "pretty-bytes": "^6.1.1", + "workbox-build": "^7.0.0", + "workbox-window": "^7.0.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^3.1.0 || ^4.0.0 || ^5.0.0", + "workbox-build": "^7.0.0", + "workbox-window": "^7.0.0" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/vite/node_modules/rollup": { + "version": "4.48.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.48.1.tgz", + "integrity": "sha512-jVG20NvbhTYDkGAty2/Yh7HK6/q3DGSRH4o8ALKGArmMuaauM9kLfoMZ+WliPwA5+JHr2lTn3g557FxBV87ifg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.48.1", + "@rollup/rollup-android-arm64": "4.48.1", + "@rollup/rollup-darwin-arm64": "4.48.1", + "@rollup/rollup-darwin-x64": "4.48.1", + "@rollup/rollup-freebsd-arm64": "4.48.1", + "@rollup/rollup-freebsd-x64": "4.48.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.48.1", + "@rollup/rollup-linux-arm-musleabihf": "4.48.1", + "@rollup/rollup-linux-arm64-gnu": "4.48.1", + "@rollup/rollup-linux-arm64-musl": "4.48.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.48.1", + "@rollup/rollup-linux-ppc64-gnu": "4.48.1", + "@rollup/rollup-linux-riscv64-gnu": "4.48.1", + "@rollup/rollup-linux-riscv64-musl": "4.48.1", + "@rollup/rollup-linux-s390x-gnu": "4.48.1", + "@rollup/rollup-linux-x64-gnu": "4.48.1", + "@rollup/rollup-linux-x64-musl": "4.48.1", + "@rollup/rollup-win32-arm64-msvc": "4.48.1", + "@rollup/rollup-win32-ia32-msvc": "4.48.1", + "@rollup/rollup-win32-x64-msvc": "4.48.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/vitest": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.1.tgz", + "integrity": "sha512-Ljb1cnSJSivGN0LqXd/zmDbWEM0RNNg2t1QW/XUhYl/qPqyu7CsqeWtqQXHVaJsecLPuDoak2oJcZN2QoRIOag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "1.6.1", + "@vitest/runner": "1.6.1", + "@vitest/snapshot": "1.6.1", + "@vitest/spy": "1.6.1", + "@vitest/utils": "1.6.1", + "acorn-walk": "^8.3.2", "chai": "^4.3.10", "debug": "^4.3.4", - "local-pkg": "^0.4.3", - "magic-string": "^0.30.1", + "execa": "^8.0.1", + "local-pkg": "^0.5.0", + "magic-string": "^0.30.5", "pathe": "^1.1.1", "picocolors": "^1.0.0", - "std-env": "^3.3.3", - "strip-literal": "^1.0.1", - "tinybench": "^2.5.0", - "tinypool": "^0.7.0", - "vite": "^3.1.0 || ^4.0.0 || ^5.0.0-0", - "vite-node": "0.34.6", + "std-env": "^3.5.0", + "strip-literal": "^2.0.0", + "tinybench": "^2.5.1", + "tinypool": "^0.8.3", + "vite": "^5.0.0", + "vite-node": "1.6.1", "why-is-node-running": "^2.2.2" }, "bin": { "vitest": "vitest.mjs" }, "engines": { - "node": ">=v14.18.0" + "node": "^18.0.0 || >=20.0.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "@edge-runtime/vm": "*", - "@vitest/browser": "*", - "@vitest/ui": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "1.6.1", + "@vitest/ui": "1.6.1", "happy-dom": "*", - "jsdom": "*", - "playwright": "*", - "safaridriver": "*", - "webdriverio": "*" + "jsdom": "*" }, "peerDependenciesMeta": { "@edge-runtime/vm": { "optional": true }, + "@types/node": { + "optional": true + }, "@vitest/browser": { "optional": true }, @@ -7303,88 +16036,35 @@ }, "jsdom": { "optional": true - }, - "playwright": { - "optional": true - }, - "safaridriver": { - "optional": true - }, - "webdriverio": { - "optional": true } } }, - "node_modules/vitest/node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "node_modules/vitest/node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/vitest/node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", "dev": true, "license": "MIT", "dependencies": { - "@sinclair/typebox": "^0.27.8" + "acorn": "^8.11.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=0.4.0" } }, - "node_modules/vitest/node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true, - "license": "MIT" - }, - "node_modules/vitest/node_modules/@vitest/utils": { - "version": "0.34.6", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.34.6.tgz", - "integrity": "sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==", - "dev": true, - "license": "MIT", - "dependencies": { - "diff-sequences": "^29.4.3", - "loupe": "^2.3.6", - "pretty-format": "^29.5.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vitest/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/vitest/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/vitest/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, "node_modules/void-elements": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", @@ -7394,6 +16074,41 @@ "node": ">=0.10.0" } }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -7430,6 +16145,34 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/which-collection": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", @@ -7488,6 +16231,24 @@ "node": ">=8" } }, + "node_modules/wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/word": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -7498,6 +16259,380 @@ "node": ">=0.10.0" } }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/workbox-background-sync": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-7.3.0.tgz", + "integrity": "sha512-PCSk3eK7Mxeuyatb22pcSx9dlgWNv3+M8PqPaYDokks8Y5/FX4soaOqj3yhAZr5k6Q5JWTOMYgaJBpbw11G9Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "idb": "^7.0.1", + "workbox-core": "7.3.0" + } + }, + "node_modules/workbox-broadcast-update": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-7.3.0.tgz", + "integrity": "sha512-T9/F5VEdJVhwmrIAE+E/kq5at2OY6+OXXgOWQevnubal6sO92Gjo24v6dCVwQiclAF5NS3hlmsifRrpQzZCdUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.3.0" + } + }, + "node_modules/workbox-build": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-7.3.0.tgz", + "integrity": "sha512-JGL6vZTPlxnlqZRhR/K/msqg3wKP+m0wfEUVosK7gsYzSgeIxvZLi1ViJJzVL7CEeI8r7rGFV973RiEqkP3lWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@apideck/better-ajv-errors": "^0.3.1", + "@babel/core": "^7.24.4", + "@babel/preset-env": "^7.11.0", + "@babel/runtime": "^7.11.2", + "@rollup/plugin-babel": "^5.2.0", + "@rollup/plugin-node-resolve": "^15.2.3", + "@rollup/plugin-replace": "^2.4.1", + "@rollup/plugin-terser": "^0.4.3", + "@surma/rollup-plugin-off-main-thread": "^2.2.3", + "ajv": "^8.6.0", + "common-tags": "^1.8.0", + "fast-json-stable-stringify": "^2.1.0", + "fs-extra": "^9.0.1", + "glob": "^7.1.6", + "lodash": "^4.17.20", + "pretty-bytes": "^5.3.0", + "rollup": "^2.43.1", + "source-map": "^0.8.0-beta.0", + "stringify-object": "^3.3.0", + "strip-comments": "^2.0.1", + "tempy": "^0.6.0", + "upath": "^1.2.0", + "workbox-background-sync": "7.3.0", + "workbox-broadcast-update": "7.3.0", + "workbox-cacheable-response": "7.3.0", + "workbox-core": "7.3.0", + "workbox-expiration": "7.3.0", + "workbox-google-analytics": "7.3.0", + "workbox-navigation-preload": "7.3.0", + "workbox-precaching": "7.3.0", + "workbox-range-requests": "7.3.0", + "workbox-recipes": "7.3.0", + "workbox-routing": "7.3.0", + "workbox-strategies": "7.3.0", + "workbox-streams": "7.3.0", + "workbox-sw": "7.3.0", + "workbox-window": "7.3.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/workbox-build/node_modules/@apideck/better-ajv-errors": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.6.tgz", + "integrity": "sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-schema": "^0.4.0", + "jsonpointer": "^5.0.0", + "leven": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "ajv": ">=8" + } + }, + "node_modules/workbox-build/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/workbox-build/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/workbox-build/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/workbox-build/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/workbox-build/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/workbox-build/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/workbox-build/node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/workbox-build/node_modules/source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "deprecated": "The work that was done in this beta branch won't be included in future versions", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "whatwg-url": "^7.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/workbox-build/node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/workbox-build/node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/workbox-build/node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/workbox-cacheable-response": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-7.3.0.tgz", + "integrity": "sha512-eAFERIg6J2LuyELhLlmeRcJFa5e16Mj8kL2yCDbhWE+HUun9skRQrGIFVUagqWj4DMaaPSMWfAolM7XZZxNmxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.3.0" + } + }, + "node_modules/workbox-core": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-7.3.0.tgz", + "integrity": "sha512-Z+mYrErfh4t3zi7NVTvOuACB0A/jA3bgxUN3PwtAVHvfEsZxV9Iju580VEETug3zYJRc0Dmii/aixI/Uxj8fmw==", + "dev": true, + "license": "MIT" + }, + "node_modules/workbox-expiration": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-7.3.0.tgz", + "integrity": "sha512-lpnSSLp2BM+K6bgFCWc5bS1LR5pAwDWbcKt1iL87/eTSJRdLdAwGQznZE+1czLgn/X05YChsrEegTNxjM067vQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "idb": "^7.0.1", + "workbox-core": "7.3.0" + } + }, + "node_modules/workbox-google-analytics": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-7.3.0.tgz", + "integrity": "sha512-ii/tSfFdhjLHZ2BrYgFNTrb/yk04pw2hasgbM70jpZfLk0vdJAXgaiMAWsoE+wfJDNWoZmBYY0hMVI0v5wWDbg==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-background-sync": "7.3.0", + "workbox-core": "7.3.0", + "workbox-routing": "7.3.0", + "workbox-strategies": "7.3.0" + } + }, + "node_modules/workbox-navigation-preload": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-7.3.0.tgz", + "integrity": "sha512-fTJzogmFaTv4bShZ6aA7Bfj4Cewaq5rp30qcxl2iYM45YD79rKIhvzNHiFj1P+u5ZZldroqhASXwwoyusnr2cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.3.0" + } + }, + "node_modules/workbox-precaching": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-7.3.0.tgz", + "integrity": "sha512-ckp/3t0msgXclVAYaNndAGeAoWQUv7Rwc4fdhWL69CCAb2UHo3Cef0KIUctqfQj1p8h6aGyz3w8Cy3Ihq9OmIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.3.0", + "workbox-routing": "7.3.0", + "workbox-strategies": "7.3.0" + } + }, + "node_modules/workbox-range-requests": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-7.3.0.tgz", + "integrity": "sha512-EyFmM1KpDzzAouNF3+EWa15yDEenwxoeXu9bgxOEYnFfCxns7eAxA9WSSaVd8kujFFt3eIbShNqa4hLQNFvmVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.3.0" + } + }, + "node_modules/workbox-recipes": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-7.3.0.tgz", + "integrity": "sha512-BJro/MpuW35I/zjZQBcoxsctgeB+kyb2JAP5EB3EYzePg8wDGoQuUdyYQS+CheTb+GhqJeWmVs3QxLI8EBP1sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-cacheable-response": "7.3.0", + "workbox-core": "7.3.0", + "workbox-expiration": "7.3.0", + "workbox-precaching": "7.3.0", + "workbox-routing": "7.3.0", + "workbox-strategies": "7.3.0" + } + }, + "node_modules/workbox-routing": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-7.3.0.tgz", + "integrity": "sha512-ZUlysUVn5ZUzMOmQN3bqu+gK98vNfgX/gSTZ127izJg/pMMy4LryAthnYtjuqcjkN4HEAx1mdgxNiKJMZQM76A==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.3.0" + } + }, + "node_modules/workbox-strategies": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-7.3.0.tgz", + "integrity": "sha512-tmZydug+qzDFATwX7QiEL5Hdf7FrkhjaF9db1CbB39sDmEZJg3l9ayDvPxy8Y18C3Y66Nrr9kkN1f/RlkDgllg==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.3.0" + } + }, + "node_modules/workbox-streams": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-7.3.0.tgz", + "integrity": "sha512-SZnXucyg8x2Y61VGtDjKPO5EgPUG5NDn/v86WYHX+9ZqvAsGOytP0Jxp1bl663YUuMoXSAtsGLL+byHzEuMRpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.3.0", + "workbox-routing": "7.3.0" + } + }, + "node_modules/workbox-sw": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-7.3.0.tgz", + "integrity": "sha512-aCUyoAZU9IZtH05mn0ACUpyHzPs0lMeJimAYkQkBsOWiqaJLgusfDCR+yllkPkFRxWpZKF8vSvgHYeG7LwhlmA==", + "dev": true, + "license": "MIT" + }, + "node_modules/workbox-window": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-7.3.0.tgz", + "integrity": "sha512-qW8PDy16OV1UBaUNGlTVcepzrlzyzNW/ZJvFQQs2j2TzGsg6IKjcpZC1RSquqQnTOafl5pCj5bGfAHlCjOOjdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/trusted-types": "^2.0.2", + "workbox-core": "7.3.0" + } + }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", @@ -7558,9 +16693,9 @@ } }, "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", + "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", "dev": true, "license": "MIT", "engines": { @@ -7606,6 +16741,58 @@ "dev": true, "license": "ISC" }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/xlsx": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", + "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + }, + "bin": { + "xlsx": "bin/xlsx.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", @@ -7626,6 +16813,57 @@ "node": ">= 14.6" } }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -7639,6 +16877,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", + "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/zod": { "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", @@ -7647,6 +16898,34 @@ "funding": { "url": "https://github.com/sponsors/colinhacks" } + }, + "node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } } } } diff --git a/frontend/package.json b/frontend/package.json index 76b7241c..3e2d1a07 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,75 +1,90 @@ { - "name": "pania-frontend", - "version": "1.0.0", - "description": "AI-powered bakery demand forecasting platform for Madrid", + "name": "bakery-ai-frontend", + "private": true, + "version": "2.0.0", "type": "module", "scripts": { "dev": "vite", - "build": "tsc && vite build", + "build": "vite build", + "build:check": "tsc && vite build", "preview": "vite preview", "test": "vitest", "test:ui": "vitest --ui", "test:coverage": "vitest --coverage", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", - "lint:fix": "eslint . --ext ts,tsx --fix" + "format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,css,md}\"", + "storybook": "storybook dev -p 6006", + "build-storybook": "storybook build", + "type-check": "tsc --noEmit" }, "dependencies": { - "@hookform/resolvers": "^3.3.1", - "@reduxjs/toolkit": "^1.9.5", - "@stripe/react-stripe-js": "^3.9.0", - "@stripe/stripe-js": "^7.8.0", + "@hookform/resolvers": "^3.3.2", + "@radix-ui/react-checkbox": "^1.0.4", + "@radix-ui/react-dialog": "^1.0.5", + "@radix-ui/react-dropdown-menu": "^2.0.6", + "@radix-ui/react-select": "^2.0.0", + "@radix-ui/react-switch": "^1.0.3", + "@radix-ui/react-tabs": "^1.0.4", + "@radix-ui/react-tooltip": "^1.0.7", + "@tanstack/react-query": "^5.12.0", + "axios": "^1.6.2", + "class-variance-authority": "^0.7.0", "clsx": "^2.0.0", "date-fns": "^2.30.0", "date-fns-tz": "^2.0.0", - "i18next": "^23.4.4", - "i18next-browser-languagedetector": "^7.1.0", - "lucide-react": "^0.263.1", + "event-source-polyfill": "^1.0.31", + "framer-motion": "^10.16.0", + "i18next": "^23.7.0", + "immer": "^10.0.3", + "lucide-react": "^0.294.0", + "papaparse": "^5.4.1", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-hook-form": "^7.45.4", + "react-dropzone": "^14.2.3", + "react-hook-form": "^7.48.0", "react-hot-toast": "^2.4.1", - "react-i18next": "^13.1.2", - "react-redux": "^8.1.2", - "react-router-dom": "^6.15.0", - "recharts": "^2.8.0", - "tailwind-merge": "^1.14.0", - "zod": "^3.22.2" + "react-i18next": "^13.5.0", + "react-router-dom": "^6.20.0", + "recharts": "^2.10.0", + "tailwind-merge": "^2.1.0", + "xlsx": "^0.18.5", + "zod": "^3.22.4", + "zustand": "^4.4.7" }, "devDependencies": { - "@tailwindcss/forms": "^0.5.4", - "@tailwindcss/typography": "^0.5.9", - "@testing-library/jest-dom": "^5.17.0", - "@testing-library/react": "^13.4.0", - "@testing-library/user-event": "^14.4.3", - "@types/react": "^18.2.15", - "@types/react-dom": "^18.2.7", - "@typescript-eslint/eslint-plugin": "^6.0.0", - "@typescript-eslint/parser": "^6.0.0", - "@vitejs/plugin-react": "^4.0.3", - "@vitest/ui": "^0.34.1", - "autoprefixer": "^10.4.14", - "eslint": "^8.45.0", + "@storybook/addon-essentials": "^7.6.0", + "@storybook/addon-interactions": "^7.6.0", + "@storybook/addon-links": "^7.6.0", + "@storybook/blocks": "^7.6.0", + "@storybook/react-vite": "^7.6.0", + "@storybook/testing-library": "^0.2.2", + "@tailwindcss/aspect-ratio": "^0.4.2", + "@tailwindcss/forms": "^0.5.10", + "@tailwindcss/typography": "^0.5.16", + "@tanstack/react-query-devtools": "^5.85.5", + "@testing-library/jest-dom": "^6.1.0", + "@testing-library/react": "^14.1.0", + "@testing-library/user-event": "^14.5.0", + "@types/node": "^20.10.0", + "@types/papaparse": "^5.3.14", + "@types/react": "^18.2.43", + "@types/react-dom": "^18.2.17", + "@typescript-eslint/eslint-plugin": "^6.14.0", + "@typescript-eslint/parser": "^6.14.0", + "@vitejs/plugin-react": "^4.2.0", + "@vitest/ui": "^1.0.0", + "autoprefixer": "^10.4.16", + "eslint": "^8.55.0", "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.3", - "postcss": "^8.4.27", + "eslint-plugin-react-refresh": "^0.4.5", + "msw": "^2.0.0", + "postcss": "^8.4.32", + "prettier": "^3.1.0", + "prettier-plugin-tailwindcss": "^0.5.0", "tailwindcss": "^3.3.0", - "typescript": "^5.0.2", - "vite": "^4.4.5", - "vitest": "^0.34.1" - }, - "keywords": [ - "bakery", - "forecasting", - "ai", - "madrid", - "react", - "typescript", - "tailwind" - ], - "repository": { - "type": "git", - "url": "https://github.com/pania-es/frontend" - }, - "author": "PanIA Team", - "license": "MIT" + "typescript": "^5.3.0", + "vite": "^5.0.0", + "vite-plugin-pwa": "^0.17.0", + "vitest": "^1.0.0" + } } diff --git a/frontend/public/manifest.json b/frontend/public/manifest.json new file mode 100644 index 00000000..1dd587ca --- /dev/null +++ b/frontend/public/manifest.json @@ -0,0 +1,114 @@ +{ + "name": "Bakery AI - Gestión Inteligente para Panaderías", + "short_name": "Bakery AI", + "description": "Plataforma inteligente de gestión para panaderías con predicción de demanda impulsada por IA", + "start_url": "/", + "display": "standalone", + "background_color": "#ffffff", + "theme_color": "#f97316", + "orientation": "any", + "categories": ["business", "productivity", "food"], + "lang": "es", + "dir": "ltr", + "icons": [ + { + "src": "/icons/icon-72.png", + "sizes": "72x72", + "type": "image/png", + "purpose": "any" + }, + { + "src": "/icons/icon-96.png", + "sizes": "96x96", + "type": "image/png", + "purpose": "any" + }, + { + "src": "/icons/icon-128.png", + "sizes": "128x128", + "type": "image/png", + "purpose": "any" + }, + { + "src": "/icons/icon-144.png", + "sizes": "144x144", + "type": "image/png", + "purpose": "any" + }, + { + "src": "/icons/icon-152.png", + "sizes": "152x152", + "type": "image/png", + "purpose": "any" + }, + { + "src": "/icons/icon-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "any" + }, + { + "src": "/icons/icon-384.png", + "sizes": "384x384", + "type": "image/png", + "purpose": "any" + }, + { + "src": "/icons/icon-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "any" + }, + { + "src": "/icons/icon-maskable.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ], + "screenshots": [ + { + "src": "/screenshots/dashboard.png", + "sizes": "1280x720", + "type": "image/png", + "label": "Panel de Control Principal" + }, + { + "src": "/screenshots/inventory.png", + "sizes": "1280x720", + "type": "image/png", + "label": "Gestión de Inventario" + }, + { + "src": "/screenshots/forecasting.png", + "sizes": "1280x720", + "type": "image/png", + "label": "Predicción de Demanda con IA" + } + ], + "shortcuts": [ + { + "name": "Panel de Control", + "short_name": "Dashboard", + "description": "Ver panel de control principal", + "url": "/app/dashboard", + "icons": [{ "src": "/icons/dashboard-96.png", "sizes": "96x96" }] + }, + { + "name": "Inventario", + "short_name": "Inventario", + "description": "Gestionar inventario", + "url": "/app/operations/inventory", + "icons": [{ "src": "/icons/inventory-96.png", "sizes": "96x96" }] + }, + { + "name": "Predicciones", + "short_name": "Predicciones", + "description": "Ver predicciones de demanda", + "url": "/app/analytics/forecasting", + "icons": [{ "src": "/icons/forecast-96.png", "sizes": "96x96" }] + } + ], + "related_applications": [], + "prefer_related_applications": false +} \ No newline at end of file diff --git a/frontend/public/sw.js b/frontend/public/sw.js new file mode 100644 index 00000000..7d375e6d --- /dev/null +++ b/frontend/public/sw.js @@ -0,0 +1,123 @@ +const CACHE_NAME = 'bakery-ai-v2.0.0'; +const urlsToCache = [ + '/', + '/index.html', + '/manifest.json', + '/icons/icon-192.png', + '/icons/icon-512.png', +]; + +// Install event - cache assets +self.addEventListener('install', (event) => { + event.waitUntil( + caches.open(CACHE_NAME).then((cache) => { + console.log('Opened cache'); + return cache.addAll(urlsToCache); + }) + ); + self.skipWaiting(); +}); + +// Activate event - clean up old caches +self.addEventListener('activate', (event) => { + event.waitUntil( + caches.keys().then((cacheNames) => { + return Promise.all( + cacheNames + .filter((cacheName) => cacheName !== CACHE_NAME) + .map((cacheName) => caches.delete(cacheName)) + ); + }) + ); + self.clients.claim(); +}); + +// Fetch event - serve from cache, fallback to network +self.addEventListener('fetch', (event) => { + // Skip cross-origin requests + if (!event.request.url.startsWith(self.location.origin)) { + return; + } + + // API calls - network first, cache fallback + if (event.request.url.includes('/api/')) { + event.respondWith( + fetch(event.request) + .then((response) => { + // Clone the response before caching + const responseToCache = response.clone(); + caches.open(CACHE_NAME).then((cache) => { + cache.put(event.request, responseToCache); + }); + return response; + }) + .catch(() => { + return caches.match(event.request); + }) + ); + return; + } + + // Static assets - cache first, network fallback + event.respondWith( + caches.match(event.request).then((response) => { + return response || fetch(event.request); + }) + ); +}); + +// Background sync for offline actions +self.addEventListener('sync', (event) => { + if (event.tag === 'sync-inventory') { + event.waitUntil(syncInventoryData()); + } +}); + +// Push notifications +self.addEventListener('push', (event) => { + const options = { + body: event.data ? event.data.text() : 'Nueva notificación de Bakery AI', + icon: '/icons/icon-192.png', + badge: '/icons/badge-72.png', + vibrate: [100, 50, 100], + data: { + dateOfArrival: Date.now(), + primaryKey: 1, + }, + }; + + event.waitUntil( + self.registration.showNotification('Bakery AI', options) + ); +}); + +// Notification click handler +self.addEventListener('notificationclick', (event) => { + event.notification.close(); + event.waitUntil( + clients.openWindow('/') + ); +}); + +// Helper function for background sync +async function syncInventoryData() { + try { + const cache = await caches.open(CACHE_NAME); + const requests = await cache.keys(); + + const pendingRequests = requests.filter( + req => req.url.includes('/api/') && req.method === 'POST' + ); + + for (const request of pendingRequests) { + try { + await fetch(request); + await cache.delete(request); + } catch (error) { + console.error('Failed to sync:', error); + } + } + } catch (error) { + console.error('Sync failed:', error); + } +} \ No newline at end of file diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 5ccda2ad..29b89425 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,67 +1,55 @@ -import React, { useEffect } from 'react'; -import { RouterProvider } from 'react-router-dom'; -import { Provider } from 'react-redux'; +import { Suspense } from 'react'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; +import { BrowserRouter } from 'react-router-dom'; import { Toaster } from 'react-hot-toast'; -import { router } from './router'; -import { store } from './store'; -import ErrorBoundary from './components/ErrorBoundary'; -import { useAuth } from './hooks/useAuth'; +import { ErrorBoundary } from './components/shared/ErrorBoundary'; +import { LoadingSpinner } from './components/shared/LoadingSpinner'; +import { AppRouter } from './router/AppRouter'; +import { ThemeProvider } from './contexts/ThemeContext'; +import { AuthProvider } from './contexts/AuthContext'; +import { SSEProvider } from './contexts/SSEContext'; -// i18n -import './i18n'; - -// Global styles -import './styles/globals.css'; - -const AppContent: React.FC = () => { - const { initializeAuth } = useAuth(); - - useEffect(() => { - initializeAuth(); - }, [initializeAuth]); +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: 5 * 60 * 1000, + gcTime: 10 * 60 * 1000, + retry: 3, + refetchOnWindowFocus: false, + }, + }, +}); +function App() { return ( -
- - - {/* Global Toast Notifications */} - -
+ + + + + + }> + + + + + + + + +
); -}; - -const App: React.FC = () => { - return ( - - - - ); -}; +} export default App; \ No newline at end of file diff --git a/frontend/src/components/domain/analytics/AnalyticsDashboard.tsx b/frontend/src/components/domain/analytics/AnalyticsDashboard.tsx new file mode 100644 index 00000000..544f96ea --- /dev/null +++ b/frontend/src/components/domain/analytics/AnalyticsDashboard.tsx @@ -0,0 +1,617 @@ +import React, { useState, useCallback, useEffect } from 'react'; +import { Card, Button, Badge, Select, DatePicker } from '../../ui'; +import { ChartWidget } from './ChartWidget'; +import { ReportsTable } from './ReportsTable'; +import { FilterPanel } from './FilterPanel'; +import { ExportOptions } from './ExportOptions'; +import type { + BakeryMetrics, + AnalyticsReport, + ChartWidget as ChartWidgetType, + FilterPanel as FilterPanelType, + AppliedFilter, + TimeRange, + ExportFormat +} from './types'; + +interface AnalyticsDashboardProps { + className?: string; + initialTimeRange?: TimeRange; + showFilters?: boolean; + showExport?: boolean; + customCharts?: ChartWidgetType[]; + onMetricsLoad?: (metrics: BakeryMetrics) => void; + onExport?: (format: ExportFormat, options: any) => void; +} + +const DEFAULT_TIME_RANGES: { value: TimeRange; label: string }[] = [ + { value: 'today', label: 'Hoy' }, + { value: 'yesterday', label: 'Ayer' }, + { value: 'thisWeek', label: 'Esta semana' }, + { value: 'lastWeek', label: 'Semana pasada' }, + { value: 'thisMonth', label: 'Este mes' }, + { value: 'lastMonth', label: 'Mes pasado' }, + { value: 'thisQuarter', label: 'Este trimestre' }, + { value: 'thisYear', label: 'Este año' }, + { value: 'custom', label: 'Personalizado' }, +]; + +export const AnalyticsDashboard: React.FC = ({ + className = '', + initialTimeRange = 'thisMonth', + showFilters = true, + showExport = true, + customCharts = [], + onMetricsLoad, + onExport, +}) => { + const [selectedTimeRange, setSelectedTimeRange] = useState(initialTimeRange); + const [customDateRange, setCustomDateRange] = useState<{ from: Date; to: Date } | null>(null); + const [bakeryMetrics, setBakeryMetrics] = useState(null); + const [reports, setReports] = useState([]); + const [appliedFilters, setAppliedFilters] = useState([]); + const [loading, setLoading] = useState(false); + const [activeView, setActiveView] = useState<'overview' | 'sales' | 'production' | 'inventory' | 'financial' | 'reports'>('overview'); + const [isExportModalOpen, setIsExportModalOpen] = useState(false); + const [selectedMetricCards, setSelectedMetricCards] = useState([]); + + const loadAnalyticsData = useCallback(async () => { + setLoading(true); + try { + // Mock data - in real implementation, this would fetch from API + const mockMetrics: BakeryMetrics = { + sales: { + total_revenue: 45250.75, + total_orders: 1247, + average_order_value: 36.32, + revenue_growth: 12.5, + order_growth: 8.3, + conversion_rate: 67.8, + top_products: [ + { + product_id: 'prod-1', + product_name: 'Pan de Molde Integral', + category: 'Pan', + quantity_sold: 342, + revenue: 8540.50, + profit_margin: 45.2, + growth_rate: 15.3, + stock_turns: 12.4 + }, + { + product_id: 'prod-2', + product_name: 'Croissant Mantequilla', + category: 'Bollería', + quantity_sold: 289, + revenue: 7225.00, + profit_margin: 52.1, + growth_rate: 22.7, + stock_turns: 8.9 + } + ], + sales_by_channel: [ + { + channel: 'Tienda Física', + revenue: 28500.45, + orders: 789, + customers: 634, + conversion_rate: 72.1, + average_order_value: 36.12, + growth_rate: 8.5 + }, + { + channel: 'Online', + revenue: 16750.30, + orders: 458, + customers: 412, + conversion_rate: 61.3, + average_order_value: 36.58, + growth_rate: 18.9 + } + ], + hourly_sales: [], + seasonal_trends: [] + }, + production: { + total_batches: 156, + success_rate: 94.2, + waste_percentage: 3.8, + production_efficiency: 87.5, + quality_score: 92.1, + capacity_utilization: 76.3, + production_costs: 18750.25, + batch_cycle_time: 4.2, + top_recipes: [ + { + recipe_id: 'rec-1', + recipe_name: 'Pan de Molde Tradicional', + batches_produced: 45, + success_rate: 96.8, + average_yield: 98.2, + cost_per_unit: 1.25, + quality_score: 94.5, + profitability: 65.2 + } + ], + quality_trends: [] + }, + inventory: { + total_stock_value: 12350.80, + turnover_rate: 8.4, + days_of_inventory: 12.5, + stockout_rate: 2.1, + waste_value: 245.30, + carrying_costs: 1850.75, + reorder_efficiency: 89.3, + supplier_performance: [ + { + supplier_id: 'sup-1', + supplier_name: 'Harinas Premium SA', + on_time_delivery: 94.2, + quality_rating: 4.6, + cost_competitiveness: 87.5, + total_orders: 24, + total_value: 8450.75, + reliability_score: 91.8 + } + ], + stock_levels: [], + expiry_alerts: [] + }, + financial: { + gross_profit: 26500.25, + net_profit: 18750.30, + profit_margin: 41.4, + cost_of_goods_sold: 18750.50, + operating_expenses: 7750.95, + break_even_point: 28500.00, + cash_flow: 15250.80, + roi: 23.8, + expense_breakdown: [ + { + category: 'Ingredientes', + amount: 12500.25, + percentage: 66.7, + change_from_previous: 5.2, + budget_variance: -2.1 + }, + { + category: 'Personal', + amount: 4250.25, + percentage: 22.7, + change_from_previous: 8.5, + budget_variance: 3.2 + }, + { + category: 'Suministros', + amount: 2000.00, + percentage: 10.6, + change_from_previous: -1.5, + budget_variance: -0.8 + } + ], + profit_trends: [] + }, + customer: { + total_customers: 1524, + new_customers: 234, + returning_customers: 1290, + customer_retention_rate: 84.6, + customer_lifetime_value: 285.40, + average_purchase_frequency: 2.8, + customer_satisfaction: 4.6, + customer_segments: [ + { + segment_id: 'seg-1', + segment_name: 'Clientes Frecuentes', + customer_count: 456, + revenue_contribution: 68.2, + average_order_value: 42.50, + purchase_frequency: 5.2, + retention_rate: 92.3, + growth_rate: 12.8 + } + ], + loyalty_program_performance: [] + }, + operational: { + staff_productivity: 87.5, + equipment_uptime: 94.2, + energy_consumption: 2450.75, + delivery_performance: 91.8, + food_safety_score: 98.5, + compliance_rate: 96.2, + maintenance_costs: 1250.30, + operational_efficiency: 89.4 + } + }; + + setBakeryMetrics(mockMetrics); + if (onMetricsLoad) { + onMetricsLoad(mockMetrics); + } + + // Mock reports + const mockReports: AnalyticsReport[] = [ + { + id: 'rep-1', + name: 'Reporte de Ventas Mensual', + description: 'Análisis completo de ventas del mes', + type: 'sales', + category: 'Ventas', + status: 'active', + created_at: new Date().toISOString(), + updated_at: new Date().toISOString(), + tenant_id: 'tenant-1', + config: { + data_source: 'sales_db', + refresh_interval: 60, + cache_duration: 30, + max_records: 10000, + default_time_range: 'thisMonth', + default_filters: {}, + visualization_type: 'bar', + aggregation_rules: [] + }, + metrics: [], + filters: [], + access_permissions: [], + tags: ['ventas', 'mensual'] + } + ]; + + setReports(mockReports); + } catch (error) { + console.error('Error loading analytics data:', error); + } finally { + setLoading(false); + } + }, [selectedTimeRange, customDateRange, appliedFilters, onMetricsLoad]); + + useEffect(() => { + loadAnalyticsData(); + }, [loadAnalyticsData]); + + const handleTimeRangeChange = (newRange: TimeRange) => { + setSelectedTimeRange(newRange); + if (newRange !== 'custom') { + setCustomDateRange(null); + } + }; + + const handleFiltersChange = (filters: AppliedFilter[]) => { + setAppliedFilters(filters); + }; + + const handleExport = (format: ExportFormat, options: any) => { + if (onExport) { + onExport(format, { ...options, view: activeView, metrics: bakeryMetrics }); + } + setIsExportModalOpen(false); + }; + + const renderKPICard = (title: string, value: string | number, subtitle?: string, trend?: number, icon?: string, colorClass: string = 'text-[var(--color-info)]') => ( + +
+
+

{title}

+

{value}

+ {subtitle &&

{subtitle}

} + {trend !== undefined && ( +
+ = 0 ? 'text-[var(--color-success)]' : 'text-[var(--color-error)]'}`}> + {trend >= 0 ? '↗' : '↘'} {Math.abs(trend).toFixed(1)}% + +
+ )} +
+ {icon && {icon}} +
+
+ ); + + const renderOverviewDashboard = () => { + if (!bakeryMetrics) return null; + + return ( +
+ {/* KPI Cards */} +
+ {renderKPICard( + 'Ingresos Totales', + `€${bakeryMetrics.sales.total_revenue.toLocaleString()}`, + undefined, + bakeryMetrics.sales.revenue_growth, + '💰', + 'text-[var(--color-success)]' + )} + {renderKPICard( + 'Pedidos', + bakeryMetrics.sales.total_orders.toLocaleString(), + `Ticket medio: €${bakeryMetrics.sales.average_order_value.toFixed(2)}`, + bakeryMetrics.sales.order_growth, + '📦', + 'text-[var(--color-info)]' + )} + {renderKPICard( + 'Margen de Beneficio', + `${bakeryMetrics.financial.profit_margin.toFixed(1)}%`, + `Beneficio: €${bakeryMetrics.financial.net_profit.toLocaleString()}`, + undefined, + '📈', + 'text-purple-600' + )} + {renderKPICard( + 'Tasa de Éxito Producción', + `${bakeryMetrics.production.success_rate.toFixed(1)}%`, + `${bakeryMetrics.production.total_batches} lotes`, + undefined, + '🏭', + 'text-[var(--color-primary)]' + )} +
+ + {/* Charts Grid */} +
+ +

Ventas por Canal

+
+ {bakeryMetrics.sales.sales_by_channel.map((channel, index) => ( +
+
+

{channel.channel}

+

+ {channel.orders} pedidos • {channel.customers} clientes +

+
+
+

+ €{channel.revenue.toLocaleString()} +

+

+ Conv. {channel.conversion_rate.toFixed(1)}% +

+
+
+ ))} +
+
+ + +

Productos Top

+
+ {bakeryMetrics.sales.top_products.map((product, index) => ( +
+
+

{product.product_name}

+

+ {product.category} • {product.quantity_sold} vendidos +

+
+
+

+ €{product.revenue.toLocaleString()} +

+

+ Margen {product.profit_margin.toFixed(1)}% +

+
+
+ ))} +
+
+
+ + {/* Operational Metrics */} + +

Métricas Operacionales

+
+
+

+ {bakeryMetrics.operational.staff_productivity.toFixed(1)}% +

+

Productividad Personal

+
+
+

+ {bakeryMetrics.operational.equipment_uptime.toFixed(1)}% +

+

Tiempo Activo Equipos

+
+
+

+ {bakeryMetrics.production.waste_percentage.toFixed(1)}% +

+

Desperdicio

+
+
+

+ {bakeryMetrics.operational.food_safety_score.toFixed(1)}% +

+

Seguridad Alimentaria

+
+
+

+ {bakeryMetrics.inventory.stockout_rate.toFixed(1)}% +

+

Roturas Stock

+
+
+

+ {bakeryMetrics.customer.customer_retention_rate.toFixed(1)}% +

+

Retención Clientes

+
+
+

+ €{bakeryMetrics.customer.customer_lifetime_value.toFixed(0)} +

+

Valor Cliente

+
+
+

+ {bakeryMetrics.inventory.turnover_rate.toFixed(1)} +

+

Rotación Inventario

+
+
+
+
+ ); + }; + + const renderViewContent = () => { + switch (activeView) { + case 'overview': + return renderOverviewDashboard(); + case 'reports': + return ( + + console.log('View report:', report)} + onEditReport={(report) => console.log('Edit report:', report)} + onDeleteReport={(id) => console.log('Delete report:', id)} + onExportReport={(report, format) => console.log('Export report:', report, format)} + bulkActions={true} + sortable={true} + pagination={{ + current: 1, + pageSize: 10, + total: reports.length, + onChange: (page, pageSize) => console.log('Page change:', page, pageSize) + }} + /> + + ); + default: + return ( + +

Vista en desarrollo: {activeView}

+

+ Esta vista estará disponible próximamente con métricas detalladas. +

+
+ ); + } + }; + + return ( +
+ {/* Header */} +
+
+

Analytics Dashboard

+

Análisis integral de rendimiento de la panadería

+
+ +
+ + + {selectedTimeRange === 'custom' && ( +
+ setCustomDateRange(prev => ({ + ...prev, + from: date || new Date() + } as { from: Date; to: Date }))} + placeholder="Fecha inicio" + /> + setCustomDateRange(prev => ({ + ...prev, + to: date || new Date() + } as { from: Date; to: Date }))} + placeholder="Fecha fin" + /> +
+ )} + + {showExport && ( + + )} + + +
+
+ + {/* Navigation Tabs */} +
+ +
+ + {/* Content */} + {loading ? ( +
+
+
+ ) : ( + renderViewContent() + )} + + {/* Export Modal */} + {isExportModalOpen && ( + setIsExportModalOpen(false)} + /> + )} +
+ ); +}; + +export default AnalyticsDashboard; \ No newline at end of file diff --git a/frontend/src/components/domain/analytics/AnalyticsDashboard.tsx.backup b/frontend/src/components/domain/analytics/AnalyticsDashboard.tsx.backup new file mode 100644 index 00000000..366136b1 --- /dev/null +++ b/frontend/src/components/domain/analytics/AnalyticsDashboard.tsx.backup @@ -0,0 +1,617 @@ +import React, { useState, useCallback, useEffect } from 'react'; +import { Card, Button, Badge, Select, DatePicker } from '../../ui'; +import { ChartWidget } from './ChartWidget'; +import { ReportsTable } from './ReportsTable'; +import { FilterPanel } from './FilterPanel'; +import { ExportOptions } from './ExportOptions'; +import type { + BakeryMetrics, + AnalyticsReport, + ChartWidget as ChartWidgetType, + FilterPanel as FilterPanelType, + AppliedFilter, + TimeRange, + ExportFormat +} from './types'; + +interface AnalyticsDashboardProps { + className?: string; + initialTimeRange?: TimeRange; + showFilters?: boolean; + showExport?: boolean; + customCharts?: ChartWidgetType[]; + onMetricsLoad?: (metrics: BakeryMetrics) => void; + onExport?: (format: ExportFormat, options: any) => void; +} + +const DEFAULT_TIME_RANGES: { value: TimeRange; label: string }[] = [ + { value: 'today', label: 'Hoy' }, + { value: 'yesterday', label: 'Ayer' }, + { value: 'thisWeek', label: 'Esta semana' }, + { value: 'lastWeek', label: 'Semana pasada' }, + { value: 'thisMonth', label: 'Este mes' }, + { value: 'lastMonth', label: 'Mes pasado' }, + { value: 'thisQuarter', label: 'Este trimestre' }, + { value: 'thisYear', label: 'Este año' }, + { value: 'custom', label: 'Personalizado' }, +]; + +export const AnalyticsDashboard: React.FC = ({ + className = '', + initialTimeRange = 'thisMonth', + showFilters = true, + showExport = true, + customCharts = [], + onMetricsLoad, + onExport, +}) => { + const [selectedTimeRange, setSelectedTimeRange] = useState(initialTimeRange); + const [customDateRange, setCustomDateRange] = useState<{ from: Date; to: Date } | null>(null); + const [bakeryMetrics, setBakeryMetrics] = useState(null); + const [reports, setReports] = useState([]); + const [appliedFilters, setAppliedFilters] = useState([]); + const [loading, setLoading] = useState(false); + const [activeView, setActiveView] = useState<'overview' | 'sales' | 'production' | 'inventory' | 'financial' | 'reports'>('overview'); + const [isExportModalOpen, setIsExportModalOpen] = useState(false); + const [selectedMetricCards, setSelectedMetricCards] = useState([]); + + const loadAnalyticsData = useCallback(async () => { + setLoading(true); + try { + // Mock data - in real implementation, this would fetch from API + const mockMetrics: BakeryMetrics = { + sales: { + total_revenue: 45250.75, + total_orders: 1247, + average_order_value: 36.32, + revenue_growth: 12.5, + order_growth: 8.3, + conversion_rate: 67.8, + top_products: [ + { + product_id: 'prod-1', + product_name: 'Pan de Molde Integral', + category: 'Pan', + quantity_sold: 342, + revenue: 8540.50, + profit_margin: 45.2, + growth_rate: 15.3, + stock_turns: 12.4 + }, + { + product_id: 'prod-2', + product_name: 'Croissant Mantequilla', + category: 'Bollería', + quantity_sold: 289, + revenue: 7225.00, + profit_margin: 52.1, + growth_rate: 22.7, + stock_turns: 8.9 + } + ], + sales_by_channel: [ + { + channel: 'Tienda Física', + revenue: 28500.45, + orders: 789, + customers: 634, + conversion_rate: 72.1, + average_order_value: 36.12, + growth_rate: 8.5 + }, + { + channel: 'Online', + revenue: 16750.30, + orders: 458, + customers: 412, + conversion_rate: 61.3, + average_order_value: 36.58, + growth_rate: 18.9 + } + ], + hourly_sales: [], + seasonal_trends: [] + }, + production: { + total_batches: 156, + success_rate: 94.2, + waste_percentage: 3.8, + production_efficiency: 87.5, + quality_score: 92.1, + capacity_utilization: 76.3, + production_costs: 18750.25, + batch_cycle_time: 4.2, + top_recipes: [ + { + recipe_id: 'rec-1', + recipe_name: 'Pan de Molde Tradicional', + batches_produced: 45, + success_rate: 96.8, + average_yield: 98.2, + cost_per_unit: 1.25, + quality_score: 94.5, + profitability: 65.2 + } + ], + quality_trends: [] + }, + inventory: { + total_stock_value: 12350.80, + turnover_rate: 8.4, + days_of_inventory: 12.5, + stockout_rate: 2.1, + waste_value: 245.30, + carrying_costs: 1850.75, + reorder_efficiency: 89.3, + supplier_performance: [ + { + supplier_id: 'sup-1', + supplier_name: 'Harinas Premium SA', + on_time_delivery: 94.2, + quality_rating: 4.6, + cost_competitiveness: 87.5, + total_orders: 24, + total_value: 8450.75, + reliability_score: 91.8 + } + ], + stock_levels: [], + expiry_alerts: [] + }, + financial: { + gross_profit: 26500.25, + net_profit: 18750.30, + profit_margin: 41.4, + cost_of_goods_sold: 18750.50, + operating_expenses: 7750.95, + break_even_point: 28500.00, + cash_flow: 15250.80, + roi: 23.8, + expense_breakdown: [ + { + category: 'Ingredientes', + amount: 12500.25, + percentage: 66.7, + change_from_previous: 5.2, + budget_variance: -2.1 + }, + { + category: 'Personal', + amount: 4250.25, + percentage: 22.7, + change_from_previous: 8.5, + budget_variance: 3.2 + }, + { + category: 'Suministros', + amount: 2000.00, + percentage: 10.6, + change_from_previous: -1.5, + budget_variance: -0.8 + } + ], + profit_trends: [] + }, + customer: { + total_customers: 1524, + new_customers: 234, + returning_customers: 1290, + customer_retention_rate: 84.6, + customer_lifetime_value: 285.40, + average_purchase_frequency: 2.8, + customer_satisfaction: 4.6, + customer_segments: [ + { + segment_id: 'seg-1', + segment_name: 'Clientes Frecuentes', + customer_count: 456, + revenue_contribution: 68.2, + average_order_value: 42.50, + purchase_frequency: 5.2, + retention_rate: 92.3, + growth_rate: 12.8 + } + ], + loyalty_program_performance: [] + }, + operational: { + staff_productivity: 87.5, + equipment_uptime: 94.2, + energy_consumption: 2450.75, + delivery_performance: 91.8, + food_safety_score: 98.5, + compliance_rate: 96.2, + maintenance_costs: 1250.30, + operational_efficiency: 89.4 + } + }; + + setBakeryMetrics(mockMetrics); + if (onMetricsLoad) { + onMetricsLoad(mockMetrics); + } + + // Mock reports + const mockReports: AnalyticsReport[] = [ + { + id: 'rep-1', + name: 'Reporte de Ventas Mensual', + description: 'Análisis completo de ventas del mes', + type: 'sales', + category: 'Ventas', + status: 'active', + created_at: new Date().toISOString(), + updated_at: new Date().toISOString(), + tenant_id: 'tenant-1', + config: { + data_source: 'sales_db', + refresh_interval: 60, + cache_duration: 30, + max_records: 10000, + default_time_range: 'thisMonth', + default_filters: {}, + visualization_type: 'bar', + aggregation_rules: [] + }, + metrics: [], + filters: [], + access_permissions: [], + tags: ['ventas', 'mensual'] + } + ]; + + setReports(mockReports); + } catch (error) { + console.error('Error loading analytics data:', error); + } finally { + setLoading(false); + } + }, [selectedTimeRange, customDateRange, appliedFilters, onMetricsLoad]); + + useEffect(() => { + loadAnalyticsData(); + }, [loadAnalyticsData]); + + const handleTimeRangeChange = (newRange: TimeRange) => { + setSelectedTimeRange(newRange); + if (newRange !== 'custom') { + setCustomDateRange(null); + } + }; + + const handleFiltersChange = (filters: AppliedFilter[]) => { + setAppliedFilters(filters); + }; + + const handleExport = (format: ExportFormat, options: any) => { + if (onExport) { + onExport(format, { ...options, view: activeView, metrics: bakeryMetrics }); + } + setIsExportModalOpen(false); + }; + + const renderKPICard = (title: string, value: string | number, subtitle?: string, trend?: number, icon?: string, colorClass: string = 'text-blue-600') => ( + +
+
+

{title}

+

{value}

+ {subtitle &&

{subtitle}

} + {trend !== undefined && ( +
+ = 0 ? 'text-green-600' : 'text-red-600'}`}> + {trend >= 0 ? '↗' : '↘'} {Math.abs(trend).toFixed(1)}% + +
+ )} +
+ {icon && {icon}} +
+
+ ); + + const renderOverviewDashboard = () => { + if (!bakeryMetrics) return null; + + return ( +
+ {/* KPI Cards */} +
+ {renderKPICard( + 'Ingresos Totales', + `€${bakeryMetrics.sales.total_revenue.toLocaleString()}`, + undefined, + bakeryMetrics.sales.revenue_growth, + '💰', + 'text-green-600' + )} + {renderKPICard( + 'Pedidos', + bakeryMetrics.sales.total_orders.toLocaleString(), + `Ticket medio: €${bakeryMetrics.sales.average_order_value.toFixed(2)}`, + bakeryMetrics.sales.order_growth, + '📦', + 'text-blue-600' + )} + {renderKPICard( + 'Margen de Beneficio', + `${bakeryMetrics.financial.profit_margin.toFixed(1)}%`, + `Beneficio: €${bakeryMetrics.financial.net_profit.toLocaleString()}`, + undefined, + '📈', + 'text-purple-600' + )} + {renderKPICard( + 'Tasa de Éxito Producción', + `${bakeryMetrics.production.success_rate.toFixed(1)}%`, + `${bakeryMetrics.production.total_batches} lotes`, + undefined, + '🏭', + 'text-orange-600' + )} +
+ + {/* Charts Grid */} +
+ +

Ventas por Canal

+
+ {bakeryMetrics.sales.sales_by_channel.map((channel, index) => ( +
+
+

{channel.channel}

+

+ {channel.orders} pedidos • {channel.customers} clientes +

+
+
+

+ €{channel.revenue.toLocaleString()} +

+

+ Conv. {channel.conversion_rate.toFixed(1)}% +

+
+
+ ))} +
+
+ + +

Productos Top

+
+ {bakeryMetrics.sales.top_products.map((product, index) => ( +
+
+

{product.product_name}

+

+ {product.category} • {product.quantity_sold} vendidos +

+
+
+

+ €{product.revenue.toLocaleString()} +

+

+ Margen {product.profit_margin.toFixed(1)}% +

+
+
+ ))} +
+
+
+ + {/* Operational Metrics */} + +

Métricas Operacionales

+
+
+

+ {bakeryMetrics.operational.staff_productivity.toFixed(1)}% +

+

Productividad Personal

+
+
+

+ {bakeryMetrics.operational.equipment_uptime.toFixed(1)}% +

+

Tiempo Activo Equipos

+
+
+

+ {bakeryMetrics.production.waste_percentage.toFixed(1)}% +

+

Desperdicio

+
+
+

+ {bakeryMetrics.operational.food_safety_score.toFixed(1)}% +

+

Seguridad Alimentaria

+
+
+

+ {bakeryMetrics.inventory.stockout_rate.toFixed(1)}% +

+

Roturas Stock

+
+
+

+ {bakeryMetrics.customer.customer_retention_rate.toFixed(1)}% +

+

Retención Clientes

+
+
+

+ €{bakeryMetrics.customer.customer_lifetime_value.toFixed(0)} +

+

Valor Cliente

+
+
+

+ {bakeryMetrics.inventory.turnover_rate.toFixed(1)} +

+

Rotación Inventario

+
+
+
+
+ ); + }; + + const renderViewContent = () => { + switch (activeView) { + case 'overview': + return renderOverviewDashboard(); + case 'reports': + return ( + + console.log('View report:', report)} + onEditReport={(report) => console.log('Edit report:', report)} + onDeleteReport={(id) => console.log('Delete report:', id)} + onExportReport={(report, format) => console.log('Export report:', report, format)} + bulkActions={true} + sortable={true} + pagination={{ + current: 1, + pageSize: 10, + total: reports.length, + onChange: (page, pageSize) => console.log('Page change:', page, pageSize) + }} + /> + + ); + default: + return ( + +

Vista en desarrollo: {activeView}

+

+ Esta vista estará disponible próximamente con métricas detalladas. +

+
+ ); + } + }; + + return ( +
+ {/* Header */} +
+
+

Analytics Dashboard

+

Análisis integral de rendimiento de la panadería

+
+ +
+ + + {selectedTimeRange === 'custom' && ( +
+ setCustomDateRange(prev => ({ + ...prev, + from: date || new Date() + } as { from: Date; to: Date }))} + placeholder="Fecha inicio" + /> + setCustomDateRange(prev => ({ + ...prev, + to: date || new Date() + } as { from: Date; to: Date }))} + placeholder="Fecha fin" + /> +
+ )} + + {showExport && ( + + )} + + +
+
+ + {/* Navigation Tabs */} +
+ +
+ + {/* Content */} + {loading ? ( +
+
+
+ ) : ( + renderViewContent() + )} + + {/* Export Modal */} + {isExportModalOpen && ( + setIsExportModalOpen(false)} + /> + )} +
+ ); +}; + +export default AnalyticsDashboard; \ No newline at end of file diff --git a/frontend/src/components/domain/analytics/ChartWidget.tsx b/frontend/src/components/domain/analytics/ChartWidget.tsx new file mode 100644 index 00000000..29ed70bc --- /dev/null +++ b/frontend/src/components/domain/analytics/ChartWidget.tsx @@ -0,0 +1,660 @@ +import React, { useState, useRef, useEffect, useMemo } from 'react'; +import { Card, Button, Badge, Select, Modal } from '../../ui'; +import type { + ChartWidget as ChartWidgetType, + ChartSeries, + ChartConfig, + ChartFilter, + ExportFormat, + ChartType +} from './types'; + +interface ChartWidgetProps { + widget: ChartWidgetType; + data?: ChartSeries[]; + loading?: boolean; + error?: string; + onConfigChange?: (config: Partial) => void; + onFiltersChange?: (filters: ChartFilter[]) => void; + onRefresh?: () => void; + onExport?: (format: ExportFormat) => void; + onFullscreen?: () => void; + interactive?: boolean; + showControls?: boolean; + showTitle?: boolean; + showSubtitle?: boolean; + className?: string; +} + +interface ChartPoint { + x: number; + y: number; + label: string; +} + +const CHART_TYPE_LABELS: Record = { + line: 'Líneas', + bar: 'Barras', + pie: 'Circular', + area: 'Área', + scatter: 'Dispersión', + doughnut: 'Dona', + radar: 'Radar', + mixed: 'Mixto', +}; + +const EXPORT_FORMATS: ExportFormat[] = ['png', 'svg', 'pdf', 'csv', 'excel']; + +export const ChartWidget: React.FC = ({ + widget, + data = [], + loading = false, + error, + onConfigChange, + onFiltersChange, + onRefresh, + onExport, + onFullscreen, + interactive = true, + showControls = true, + showTitle = true, + showSubtitle = true, + className = '', +}) => { + const canvasRef = useRef(null); + const [isConfigModalOpen, setIsConfigModalOpen] = useState(false); + const [isFullscreen, setIsFullscreen] = useState(false); + const [hoveredPoint, setHoveredPoint] = useState<{ seriesIndex: number; pointIndex: number } | null>(null); + const [selectedExportFormat, setSelectedExportFormat] = useState('png'); + + const chartDimensions = useMemo(() => ({ + width: widget.dimensions?.width || '100%', + height: widget.dimensions?.height || 400, + aspectRatio: widget.dimensions?.aspect_ratio || '16:9', + }), [widget.dimensions]); + + // Simple chart rendering logic + useEffect(() => { + if (!canvasRef.current || !data.length || loading) return; + + const canvas = canvasRef.current; + const ctx = canvas.getContext('2d'); + if (!ctx) return; + + // Set canvas size + const rect = canvas.getBoundingClientRect(); + canvas.width = rect.width; + canvas.height = rect.height; + + // Clear canvas + ctx.clearRect(0, 0, canvas.width, canvas.height); + + // Draw based on chart type + switch (widget.type) { + case 'bar': + drawBarChart(ctx, canvas, data); + break; + case 'line': + drawLineChart(ctx, canvas, data); + break; + case 'pie': + drawPieChart(ctx, canvas, data); + break; + case 'area': + drawAreaChart(ctx, canvas, data); + break; + default: + drawBarChart(ctx, canvas, data); + } + }, [data, widget.type, loading]); + + const drawBarChart = (ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement, chartData: ChartSeries[]) => { + if (!chartData.length) return; + + const padding = 60; + const chartWidth = canvas.width - 2 * padding; + const chartHeight = canvas.height - 2 * padding; + + // Get all data points + const allPoints = chartData.flatMap(series => series.data); + const maxY = Math.max(...allPoints.map(point => point.y)); + const minY = Math.min(...allPoints.map(point => point.y)); + + const firstSeries = chartData[0]; + const barWidth = chartWidth / firstSeries.data.length / chartData.length; + const barSpacing = barWidth * 0.1; + + // Draw grid + ctx.strokeStyle = '#e5e7eb'; + ctx.lineWidth = 1; + for (let i = 0; i <= 5; i++) { + const y = padding + (chartHeight * i) / 5; + ctx.beginPath(); + ctx.moveTo(padding, y); + ctx.lineTo(padding + chartWidth, y); + ctx.stroke(); + } + + // Draw bars + chartData.forEach((series, seriesIndex) => { + const color = series.color || widget.config.color_scheme[seriesIndex % widget.config.color_scheme.length]; + ctx.fillStyle = color; + + series.data.forEach((point, pointIndex) => { + const x = padding + (pointIndex * chartWidth) / firstSeries.data.length + (seriesIndex * barWidth); + const barHeight = ((point.y - minY) / (maxY - minY)) * chartHeight; + const y = padding + chartHeight - barHeight; + + ctx.fillRect(x + barSpacing, y, barWidth - barSpacing, barHeight); + + // Add value labels if enabled + if (widget.config.show_tooltips) { + ctx.fillStyle = '#374151'; + ctx.font = '12px sans-serif'; + ctx.textAlign = 'center'; + ctx.fillText(point.y.toString(), x + barWidth / 2, y - 5); + ctx.fillStyle = color; + } + }); + }); + + // Draw axes + ctx.strokeStyle = '#374151'; + ctx.lineWidth = 2; + ctx.beginPath(); + ctx.moveTo(padding, padding); + ctx.lineTo(padding, padding + chartHeight); + ctx.lineTo(padding + chartWidth, padding + chartHeight); + ctx.stroke(); + + // Draw labels + ctx.fillStyle = '#6b7280'; + ctx.font = '12px sans-serif'; + ctx.textAlign = 'center'; + + // X-axis labels + firstSeries.data.forEach((point, index) => { + const x = padding + (index + 0.5) * (chartWidth / firstSeries.data.length); + ctx.fillText(point.x.toString(), x, padding + chartHeight + 20); + }); + + // Y-axis labels + ctx.textAlign = 'right'; + for (let i = 0; i <= 5; i++) { + const y = padding + (chartHeight * i) / 5; + const value = maxY - ((maxY - minY) * i) / 5; + ctx.fillText(value.toFixed(0), padding - 10, y + 4); + } + }; + + const drawLineChart = (ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement, chartData: ChartSeries[]) => { + if (!chartData.length) return; + + const padding = 60; + const chartWidth = canvas.width - 2 * padding; + const chartHeight = canvas.height - 2 * padding; + + const allPoints = chartData.flatMap(series => series.data); + const maxY = Math.max(...allPoints.map(point => point.y)); + const minY = Math.min(...allPoints.map(point => point.y)); + + // Draw grid + ctx.strokeStyle = '#e5e7eb'; + ctx.lineWidth = 1; + for (let i = 0; i <= 5; i++) { + const y = padding + (chartHeight * i) / 5; + ctx.beginPath(); + ctx.moveTo(padding, y); + ctx.lineTo(padding + chartWidth, y); + ctx.stroke(); + } + + // Draw lines + chartData.forEach((series, seriesIndex) => { + if (!series.visible) return; + + const color = series.color || widget.config.color_scheme[seriesIndex % widget.config.color_scheme.length]; + ctx.strokeStyle = color; + ctx.lineWidth = 2; + ctx.beginPath(); + + series.data.forEach((point, pointIndex) => { + const x = padding + (pointIndex * chartWidth) / (series.data.length - 1); + const y = padding + chartHeight - ((point.y - minY) / (maxY - minY)) * chartHeight; + + if (pointIndex === 0) { + ctx.moveTo(x, y); + } else { + ctx.lineTo(x, y); + } + }); + + ctx.stroke(); + + // Draw points + ctx.fillStyle = color; + series.data.forEach((point, pointIndex) => { + const x = padding + (pointIndex * chartWidth) / (series.data.length - 1); + const y = padding + chartHeight - ((point.y - minY) / (maxY - minY)) * chartHeight; + + ctx.beginPath(); + ctx.arc(x, y, 4, 0, 2 * Math.PI); + ctx.fill(); + }); + }); + + // Draw axes + ctx.strokeStyle = '#374151'; + ctx.lineWidth = 2; + ctx.beginPath(); + ctx.moveTo(padding, padding); + ctx.lineTo(padding, padding + chartHeight); + ctx.lineTo(padding + chartWidth, padding + chartHeight); + ctx.stroke(); + }; + + const drawPieChart = (ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement, chartData: ChartSeries[]) => { + if (!chartData.length) return; + + const centerX = canvas.width / 2; + const centerY = canvas.height / 2; + const radius = Math.min(canvas.width, canvas.height) / 3; + + const firstSeries = chartData[0]; + const total = firstSeries.data.reduce((sum, point) => sum + point.y, 0); + + let startAngle = -Math.PI / 2; + + firstSeries.data.forEach((point, index) => { + const sliceAngle = (point.y / total) * 2 * Math.PI; + const color = widget.config.color_scheme[index % widget.config.color_scheme.length]; + + ctx.fillStyle = color; + ctx.beginPath(); + ctx.moveTo(centerX, centerY); + ctx.arc(centerX, centerY, radius, startAngle, startAngle + sliceAngle); + ctx.closePath(); + ctx.fill(); + + // Draw slice border + ctx.strokeStyle = '#ffffff'; + ctx.lineWidth = 2; + ctx.stroke(); + + // Draw labels + const labelAngle = startAngle + sliceAngle / 2; + const labelX = centerX + Math.cos(labelAngle) * (radius + 30); + const labelY = centerY + Math.sin(labelAngle) * (radius + 30); + + ctx.fillStyle = '#374151'; + ctx.font = '12px sans-serif'; + ctx.textAlign = 'center'; + ctx.fillText(point.x.toString(), labelX, labelY); + + const percentage = ((point.y / total) * 100).toFixed(1); + ctx.fillText(`${percentage}%`, labelX, labelY + 15); + + startAngle += sliceAngle; + }); + }; + + const drawAreaChart = (ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement, chartData: ChartSeries[]) => { + if (!chartData.length) return; + + const padding = 60; + const chartWidth = canvas.width - 2 * padding; + const chartHeight = canvas.height - 2 * padding; + + const allPoints = chartData.flatMap(series => series.data); + const maxY = Math.max(...allPoints.map(point => point.y)); + const minY = Math.min(...allPoints.map(point => point.y)); + + chartData.forEach((series, seriesIndex) => { + if (!series.visible) return; + + const color = series.color || widget.config.color_scheme[seriesIndex % widget.config.color_scheme.length]; + + // Create gradient + const gradient = ctx.createLinearGradient(0, padding, 0, padding + chartHeight); + gradient.addColorStop(0, color + '80'); // Semi-transparent + gradient.addColorStop(1, color + '10'); // Very transparent + + ctx.fillStyle = gradient; + ctx.beginPath(); + + // Start from bottom-left + const firstX = padding; + const firstY = padding + chartHeight; + ctx.moveTo(firstX, firstY); + + // Draw the area path + series.data.forEach((point, pointIndex) => { + const x = padding + (pointIndex * chartWidth) / (series.data.length - 1); + const y = padding + chartHeight - ((point.y - minY) / (maxY - minY)) * chartHeight; + ctx.lineTo(x, y); + }); + + // Close the path at bottom-right + const lastX = padding + chartWidth; + const lastY = padding + chartHeight; + ctx.lineTo(lastX, lastY); + ctx.closePath(); + ctx.fill(); + + // Draw the line on top + ctx.strokeStyle = color; + ctx.lineWidth = 2; + ctx.beginPath(); + series.data.forEach((point, pointIndex) => { + const x = padding + (pointIndex * chartWidth) / (series.data.length - 1); + const y = padding + chartHeight - ((point.y - minY) / (maxY - minY)) * chartHeight; + + if (pointIndex === 0) { + ctx.moveTo(x, y); + } else { + ctx.lineTo(x, y); + } + }); + ctx.stroke(); + }); + }; + + const handleExport = () => { + if (onExport) { + onExport(selectedExportFormat); + } + }; + + const handleFullscreen = () => { + setIsFullscreen(!isFullscreen); + if (onFullscreen) { + onFullscreen(); + } + }; + + const renderLegend = () => { + if (!widget.config.show_legend || !data.length) return null; + + return ( +
+ {data.map((series, index) => ( +
+
+ {series.name} +
+ ))} +
+ ); + }; + + const renderControls = () => { + if (!showControls || !interactive) return null; + + return ( +
+ + + + + + + + + {onRefresh && ( + + )} +
+ ); + }; + + if (error) { + return ( + +
+

Error al cargar el gráfico

+

{error}

+ {onRefresh && ( + + )} +
+
+ ); + } + + return ( + <> + + {/* Header */} +
+
+ {showTitle && ( +

+ {widget.title} +

+ )} + {showSubtitle && widget.subtitle && ( +

+ {widget.subtitle} +

+ )} +
+ + {renderControls()} +
+ + {/* Chart Area */} +
+ {loading ? ( +
+
+
+ ) : data.length === 0 ? ( +
+
+

No hay datos disponibles

+

+ Ajusta los filtros o verifica la conexión de datos +

+
+
+ ) : ( +
+ + {renderLegend()} +
+ )} +
+ + {/* Last Updated */} + {widget.last_updated && ( +
+ Actualizado: {new Date(widget.last_updated).toLocaleTimeString()} +
+ )} +
+ + {/* Configuration Modal */} + setIsConfigModalOpen(false)} + title="Configuración del Gráfico" + > +
+
+ + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+ +
+ + +
+ +
+ + +
+
+
+ + ); +}; + +export default ChartWidget; \ No newline at end of file diff --git a/frontend/src/components/domain/analytics/ChartWidget.tsx.backup b/frontend/src/components/domain/analytics/ChartWidget.tsx.backup new file mode 100644 index 00000000..7d342e1f --- /dev/null +++ b/frontend/src/components/domain/analytics/ChartWidget.tsx.backup @@ -0,0 +1,660 @@ +import React, { useState, useRef, useEffect, useMemo } from 'react'; +import { Card, Button, Badge, Select, Modal } from '../../ui'; +import type { + ChartWidget as ChartWidgetType, + ChartSeries, + ChartConfig, + ChartFilter, + ExportFormat, + ChartType +} from './types'; + +interface ChartWidgetProps { + widget: ChartWidgetType; + data?: ChartSeries[]; + loading?: boolean; + error?: string; + onConfigChange?: (config: Partial) => void; + onFiltersChange?: (filters: ChartFilter[]) => void; + onRefresh?: () => void; + onExport?: (format: ExportFormat) => void; + onFullscreen?: () => void; + interactive?: boolean; + showControls?: boolean; + showTitle?: boolean; + showSubtitle?: boolean; + className?: string; +} + +interface ChartPoint { + x: number; + y: number; + label: string; +} + +const CHART_TYPE_LABELS: Record = { + line: 'Líneas', + bar: 'Barras', + pie: 'Circular', + area: 'Área', + scatter: 'Dispersión', + doughnut: 'Dona', + radar: 'Radar', + mixed: 'Mixto', +}; + +const EXPORT_FORMATS: ExportFormat[] = ['png', 'svg', 'pdf', 'csv', 'excel']; + +export const ChartWidget: React.FC = ({ + widget, + data = [], + loading = false, + error, + onConfigChange, + onFiltersChange, + onRefresh, + onExport, + onFullscreen, + interactive = true, + showControls = true, + showTitle = true, + showSubtitle = true, + className = '', +}) => { + const canvasRef = useRef(null); + const [isConfigModalOpen, setIsConfigModalOpen] = useState(false); + const [isFullscreen, setIsFullscreen] = useState(false); + const [hoveredPoint, setHoveredPoint] = useState<{ seriesIndex: number; pointIndex: number } | null>(null); + const [selectedExportFormat, setSelectedExportFormat] = useState('png'); + + const chartDimensions = useMemo(() => ({ + width: widget.dimensions?.width || '100%', + height: widget.dimensions?.height || 400, + aspectRatio: widget.dimensions?.aspect_ratio || '16:9', + }), [widget.dimensions]); + + // Simple chart rendering logic + useEffect(() => { + if (!canvasRef.current || !data.length || loading) return; + + const canvas = canvasRef.current; + const ctx = canvas.getContext('2d'); + if (!ctx) return; + + // Set canvas size + const rect = canvas.getBoundingClientRect(); + canvas.width = rect.width; + canvas.height = rect.height; + + // Clear canvas + ctx.clearRect(0, 0, canvas.width, canvas.height); + + // Draw based on chart type + switch (widget.type) { + case 'bar': + drawBarChart(ctx, canvas, data); + break; + case 'line': + drawLineChart(ctx, canvas, data); + break; + case 'pie': + drawPieChart(ctx, canvas, data); + break; + case 'area': + drawAreaChart(ctx, canvas, data); + break; + default: + drawBarChart(ctx, canvas, data); + } + }, [data, widget.type, loading]); + + const drawBarChart = (ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement, chartData: ChartSeries[]) => { + if (!chartData.length) return; + + const padding = 60; + const chartWidth = canvas.width - 2 * padding; + const chartHeight = canvas.height - 2 * padding; + + // Get all data points + const allPoints = chartData.flatMap(series => series.data); + const maxY = Math.max(...allPoints.map(point => point.y)); + const minY = Math.min(...allPoints.map(point => point.y)); + + const firstSeries = chartData[0]; + const barWidth = chartWidth / firstSeries.data.length / chartData.length; + const barSpacing = barWidth * 0.1; + + // Draw grid + ctx.strokeStyle = '#e5e7eb'; + ctx.lineWidth = 1; + for (let i = 0; i <= 5; i++) { + const y = padding + (chartHeight * i) / 5; + ctx.beginPath(); + ctx.moveTo(padding, y); + ctx.lineTo(padding + chartWidth, y); + ctx.stroke(); + } + + // Draw bars + chartData.forEach((series, seriesIndex) => { + const color = series.color || widget.config.color_scheme[seriesIndex % widget.config.color_scheme.length]; + ctx.fillStyle = color; + + series.data.forEach((point, pointIndex) => { + const x = padding + (pointIndex * chartWidth) / firstSeries.data.length + (seriesIndex * barWidth); + const barHeight = ((point.y - minY) / (maxY - minY)) * chartHeight; + const y = padding + chartHeight - barHeight; + + ctx.fillRect(x + barSpacing, y, barWidth - barSpacing, barHeight); + + // Add value labels if enabled + if (widget.config.show_tooltips) { + ctx.fillStyle = '#374151'; + ctx.font = '12px sans-serif'; + ctx.textAlign = 'center'; + ctx.fillText(point.y.toString(), x + barWidth / 2, y - 5); + ctx.fillStyle = color; + } + }); + }); + + // Draw axes + ctx.strokeStyle = '#374151'; + ctx.lineWidth = 2; + ctx.beginPath(); + ctx.moveTo(padding, padding); + ctx.lineTo(padding, padding + chartHeight); + ctx.lineTo(padding + chartWidth, padding + chartHeight); + ctx.stroke(); + + // Draw labels + ctx.fillStyle = '#6b7280'; + ctx.font = '12px sans-serif'; + ctx.textAlign = 'center'; + + // X-axis labels + firstSeries.data.forEach((point, index) => { + const x = padding + (index + 0.5) * (chartWidth / firstSeries.data.length); + ctx.fillText(point.x.toString(), x, padding + chartHeight + 20); + }); + + // Y-axis labels + ctx.textAlign = 'right'; + for (let i = 0; i <= 5; i++) { + const y = padding + (chartHeight * i) / 5; + const value = maxY - ((maxY - minY) * i) / 5; + ctx.fillText(value.toFixed(0), padding - 10, y + 4); + } + }; + + const drawLineChart = (ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement, chartData: ChartSeries[]) => { + if (!chartData.length) return; + + const padding = 60; + const chartWidth = canvas.width - 2 * padding; + const chartHeight = canvas.height - 2 * padding; + + const allPoints = chartData.flatMap(series => series.data); + const maxY = Math.max(...allPoints.map(point => point.y)); + const minY = Math.min(...allPoints.map(point => point.y)); + + // Draw grid + ctx.strokeStyle = '#e5e7eb'; + ctx.lineWidth = 1; + for (let i = 0; i <= 5; i++) { + const y = padding + (chartHeight * i) / 5; + ctx.beginPath(); + ctx.moveTo(padding, y); + ctx.lineTo(padding + chartWidth, y); + ctx.stroke(); + } + + // Draw lines + chartData.forEach((series, seriesIndex) => { + if (!series.visible) return; + + const color = series.color || widget.config.color_scheme[seriesIndex % widget.config.color_scheme.length]; + ctx.strokeStyle = color; + ctx.lineWidth = 2; + ctx.beginPath(); + + series.data.forEach((point, pointIndex) => { + const x = padding + (pointIndex * chartWidth) / (series.data.length - 1); + const y = padding + chartHeight - ((point.y - minY) / (maxY - minY)) * chartHeight; + + if (pointIndex === 0) { + ctx.moveTo(x, y); + } else { + ctx.lineTo(x, y); + } + }); + + ctx.stroke(); + + // Draw points + ctx.fillStyle = color; + series.data.forEach((point, pointIndex) => { + const x = padding + (pointIndex * chartWidth) / (series.data.length - 1); + const y = padding + chartHeight - ((point.y - minY) / (maxY - minY)) * chartHeight; + + ctx.beginPath(); + ctx.arc(x, y, 4, 0, 2 * Math.PI); + ctx.fill(); + }); + }); + + // Draw axes + ctx.strokeStyle = '#374151'; + ctx.lineWidth = 2; + ctx.beginPath(); + ctx.moveTo(padding, padding); + ctx.lineTo(padding, padding + chartHeight); + ctx.lineTo(padding + chartWidth, padding + chartHeight); + ctx.stroke(); + }; + + const drawPieChart = (ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement, chartData: ChartSeries[]) => { + if (!chartData.length) return; + + const centerX = canvas.width / 2; + const centerY = canvas.height / 2; + const radius = Math.min(canvas.width, canvas.height) / 3; + + const firstSeries = chartData[0]; + const total = firstSeries.data.reduce((sum, point) => sum + point.y, 0); + + let startAngle = -Math.PI / 2; + + firstSeries.data.forEach((point, index) => { + const sliceAngle = (point.y / total) * 2 * Math.PI; + const color = widget.config.color_scheme[index % widget.config.color_scheme.length]; + + ctx.fillStyle = color; + ctx.beginPath(); + ctx.moveTo(centerX, centerY); + ctx.arc(centerX, centerY, radius, startAngle, startAngle + sliceAngle); + ctx.closePath(); + ctx.fill(); + + // Draw slice border + ctx.strokeStyle = '#ffffff'; + ctx.lineWidth = 2; + ctx.stroke(); + + // Draw labels + const labelAngle = startAngle + sliceAngle / 2; + const labelX = centerX + Math.cos(labelAngle) * (radius + 30); + const labelY = centerY + Math.sin(labelAngle) * (radius + 30); + + ctx.fillStyle = '#374151'; + ctx.font = '12px sans-serif'; + ctx.textAlign = 'center'; + ctx.fillText(point.x.toString(), labelX, labelY); + + const percentage = ((point.y / total) * 100).toFixed(1); + ctx.fillText(`${percentage}%`, labelX, labelY + 15); + + startAngle += sliceAngle; + }); + }; + + const drawAreaChart = (ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement, chartData: ChartSeries[]) => { + if (!chartData.length) return; + + const padding = 60; + const chartWidth = canvas.width - 2 * padding; + const chartHeight = canvas.height - 2 * padding; + + const allPoints = chartData.flatMap(series => series.data); + const maxY = Math.max(...allPoints.map(point => point.y)); + const minY = Math.min(...allPoints.map(point => point.y)); + + chartData.forEach((series, seriesIndex) => { + if (!series.visible) return; + + const color = series.color || widget.config.color_scheme[seriesIndex % widget.config.color_scheme.length]; + + // Create gradient + const gradient = ctx.createLinearGradient(0, padding, 0, padding + chartHeight); + gradient.addColorStop(0, color + '80'); // Semi-transparent + gradient.addColorStop(1, color + '10'); // Very transparent + + ctx.fillStyle = gradient; + ctx.beginPath(); + + // Start from bottom-left + const firstX = padding; + const firstY = padding + chartHeight; + ctx.moveTo(firstX, firstY); + + // Draw the area path + series.data.forEach((point, pointIndex) => { + const x = padding + (pointIndex * chartWidth) / (series.data.length - 1); + const y = padding + chartHeight - ((point.y - minY) / (maxY - minY)) * chartHeight; + ctx.lineTo(x, y); + }); + + // Close the path at bottom-right + const lastX = padding + chartWidth; + const lastY = padding + chartHeight; + ctx.lineTo(lastX, lastY); + ctx.closePath(); + ctx.fill(); + + // Draw the line on top + ctx.strokeStyle = color; + ctx.lineWidth = 2; + ctx.beginPath(); + series.data.forEach((point, pointIndex) => { + const x = padding + (pointIndex * chartWidth) / (series.data.length - 1); + const y = padding + chartHeight - ((point.y - minY) / (maxY - minY)) * chartHeight; + + if (pointIndex === 0) { + ctx.moveTo(x, y); + } else { + ctx.lineTo(x, y); + } + }); + ctx.stroke(); + }); + }; + + const handleExport = () => { + if (onExport) { + onExport(selectedExportFormat); + } + }; + + const handleFullscreen = () => { + setIsFullscreen(!isFullscreen); + if (onFullscreen) { + onFullscreen(); + } + }; + + const renderLegend = () => { + if (!widget.config.show_legend || !data.length) return null; + + return ( +
+ {data.map((series, index) => ( +
+
+ {series.name} +
+ ))} +
+ ); + }; + + const renderControls = () => { + if (!showControls || !interactive) return null; + + return ( +
+ + + + + + + + + {onRefresh && ( + + )} +
+ ); + }; + + if (error) { + return ( + +
+

Error al cargar el gráfico

+

{error}

+ {onRefresh && ( + + )} +
+
+ ); + } + + return ( + <> + + {/* Header */} +
+
+ {showTitle && ( +

+ {widget.title} +

+ )} + {showSubtitle && widget.subtitle && ( +

+ {widget.subtitle} +

+ )} +
+ + {renderControls()} +
+ + {/* Chart Area */} +
+ {loading ? ( +
+
+
+ ) : data.length === 0 ? ( +
+
+

No hay datos disponibles

+

+ Ajusta los filtros o verifica la conexión de datos +

+
+
+ ) : ( +
+ + {renderLegend()} +
+ )} +
+ + {/* Last Updated */} + {widget.last_updated && ( +
+ Actualizado: {new Date(widget.last_updated).toLocaleTimeString()} +
+ )} +
+ + {/* Configuration Modal */} + setIsConfigModalOpen(false)} + title="Configuración del Gráfico" + > +
+
+ + +
+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+ +
+ + +
+ +
+ + +
+
+
+ + ); +}; + +export default ChartWidget; \ No newline at end of file diff --git a/frontend/src/components/domain/analytics/ExportOptions.tsx b/frontend/src/components/domain/analytics/ExportOptions.tsx new file mode 100644 index 00000000..1d7ff86d --- /dev/null +++ b/frontend/src/components/domain/analytics/ExportOptions.tsx @@ -0,0 +1,592 @@ +import React, { useState } from 'react'; +import { Card, Button, Badge, Input, Select } from '../../ui'; +import type { + ExportOptionsProps, + ExportOptions, + ExportFormat, + ExportTemplate, + ReportSchedule +} from './types'; + +const FORMAT_LABELS: Record = { + pdf: 'PDF', + excel: 'Excel', + csv: 'CSV', + png: 'PNG', + svg: 'SVG', + json: 'JSON', +}; + +const FORMAT_DESCRIPTIONS: Record = { + pdf: 'Documento PDF con formato profesional', + excel: 'Hoja de cálculo de Microsoft Excel', + csv: 'Archivo de valores separados por comas', + png: 'Imagen PNG de alta calidad', + svg: 'Imagen vectorial escalable', + json: 'Datos estructurados en formato JSON', +}; + +const FORMAT_ICONS: Record = { + pdf: '📄', + excel: '📊', + csv: '📋', + png: '🖼️', + svg: '🎨', + json: '⚙️', +}; + +const FREQUENCY_OPTIONS = [ + { value: 'daily', label: 'Diario' }, + { value: 'weekly', label: 'Semanal' }, + { value: 'monthly', label: 'Mensual' }, + { value: 'quarterly', label: 'Trimestral' }, + { value: 'yearly', label: 'Anual' }, +]; + +const TIME_OPTIONS = [ + { value: '06:00', label: '6:00 AM' }, + { value: '09:00', label: '9:00 AM' }, + { value: '12:00', label: '12:00 PM' }, + { value: '15:00', label: '3:00 PM' }, + { value: '18:00', label: '6:00 PM' }, + { value: '21:00', label: '9:00 PM' }, +]; + +const DAYS_OF_WEEK = [ + { value: 1, label: 'Lunes' }, + { value: 2, label: 'Martes' }, + { value: 3, label: 'Miércoles' }, + { value: 4, label: 'Jueves' }, + { value: 5, label: 'Viernes' }, + { value: 6, label: 'Sábado' }, + { value: 0, label: 'Domingo' }, +]; + +interface ExportOptionsComponentProps extends ExportOptionsProps { + onClose?: () => void; +} + +export const ExportOptions: React.FC = ({ + type, + title, + description, + availableFormats = ['pdf', 'excel', 'csv', 'png'], + templates = [], + onExport, + onSchedule, + loading = false, + disabled = false, + showScheduling = true, + showTemplates = true, + showAdvanced = true, + defaultOptions, + className = '', + onClose, +}) => { + const [selectedFormat, setSelectedFormat] = useState( + defaultOptions?.format || availableFormats[0] + ); + const [selectedTemplate, setSelectedTemplate] = useState( + templates.find(t => t.is_default)?.id || '' + ); + const [exportOptions, setExportOptions] = useState>({ + include_headers: true, + include_filters: true, + include_summary: true, + date_format: 'DD/MM/YYYY', + number_format: '#,##0.00', + currency_format: '€#,##0.00', + locale: 'es-ES', + timezone: 'Europe/Madrid', + page_size: 'A4', + orientation: 'portrait', + password_protected: false, + ...defaultOptions, + }); + + const [scheduleOptions, setScheduleOptions] = useState>({ + enabled: false, + frequency: 'weekly', + time: '09:00', + days_of_week: [1], // Monday + recipients: [], + format: selectedFormat, + include_attachments: true, + }); + + const [recipientsInput, setRecipientsInput] = useState(''); + const [activeTab, setActiveTab] = useState<'export' | 'schedule'>('export'); + const [showAdvancedOptions, setShowAdvancedOptions] = useState(false); + + const handleExportOptionChange = (key: keyof ExportOptions, value: any) => { + setExportOptions(prev => ({ ...prev, [key]: value })); + }; + + const handleScheduleOptionChange = (key: keyof ReportSchedule, value: any) => { + setScheduleOptions(prev => ({ ...prev, [key]: value })); + }; + + const handleExport = () => { + if (onExport) { + const fullOptions: ExportOptions = { + format: selectedFormat, + template: selectedTemplate || undefined, + include_headers: true, + include_filters: true, + include_summary: true, + date_format: 'DD/MM/YYYY', + number_format: '#,##0.00', + currency_format: '€#,##0.00', + locale: 'es-ES', + timezone: 'Europe/Madrid', + page_size: 'A4', + orientation: 'portrait', + ...exportOptions, + // format: selectedFormat, // Ensure format matches selection - handled by exportOptions + }; + onExport(fullOptions); + } + }; + + const handleSchedule = () => { + if (onSchedule) { + const recipients = recipientsInput + .split(',') + .map(email => email.trim()) + .filter(email => email.length > 0); + + const fullSchedule: ReportSchedule = { + enabled: true, + frequency: 'weekly', + time: '09:00', + recipients: recipients, + format: selectedFormat, + include_attachments: true, + ...scheduleOptions, + }; + onSchedule(fullSchedule); + } + }; + + const renderFormatSelector = () => ( +
+

Formato de exportación

+
+ {availableFormats.map(format => ( + + ))} +
+
+ ); + + const renderTemplateSelector = () => { + if (!showTemplates || templates.length === 0) return null; + + return ( +
+

Plantilla

+ + + {selectedTemplate && ( +
+ {templates.find(t => t.id === selectedTemplate)?.description} +
+ )} +
+ ); + }; + + const renderBasicOptions = () => ( +
+

Opciones básicas

+ +
+ + + + + +
+
+ ); + + const renderAdvancedOptions = () => { + if (!showAdvanced || !showAdvancedOptions) return null; + + return ( +
+

Opciones avanzadas

+ +
+
+ + +
+ +
+ + +
+ + {(['pdf'] as ExportFormat[]).includes(selectedFormat) && ( + <> +
+ + +
+ +
+ + +
+ + )} +
+ +
+ + + {exportOptions.password_protected && ( +
+ handleExportOptionChange('password', e.target.value)} + disabled={disabled || loading} + /> +
+ )} +
+
+ ); + }; + + const renderScheduleTab = () => ( +
+
+ +
+ + {scheduleOptions.enabled && ( +
+
+
+ + +
+ +
+ + +
+
+ + {scheduleOptions.frequency === 'weekly' && ( +
+ +
+ {DAYS_OF_WEEK.map(day => ( + + ))} +
+
+ )} + +
+ + setRecipientsInput(e.target.value)} + placeholder="ejemplo@empresa.com, otro@empresa.com" + disabled={disabled || loading} + /> +
+ +
+ +
+
+ )} +
+ ); + + return ( +
+ + {/* Header */} +
+
+

{title}

+ {description && ( +

{description}

+ )} +
+ + {onClose && ( + + )} +
+ + {/* Tabs */} +
+ +
+ + {/* Content */} +
+ {activeTab === 'export' ? ( + <> + {renderFormatSelector()} + {renderTemplateSelector()} + {renderBasicOptions()} + + {showAdvanced && ( +
+ +
+ )} + + {renderAdvancedOptions()} + + ) : ( + renderScheduleTab() + )} +
+ + {/* Footer */} +
+ {onClose && ( + + )} + + {activeTab === 'export' ? ( + + ) : ( + + )} +
+
+
+ ); +}; + +export default ExportOptions; \ No newline at end of file diff --git a/frontend/src/components/domain/analytics/ExportOptions.tsx.backup b/frontend/src/components/domain/analytics/ExportOptions.tsx.backup new file mode 100644 index 00000000..f4fe8636 --- /dev/null +++ b/frontend/src/components/domain/analytics/ExportOptions.tsx.backup @@ -0,0 +1,592 @@ +import React, { useState } from 'react'; +import { Card, Button, Badge, Input, Select } from '../../ui'; +import type { + ExportOptionsProps, + ExportOptions, + ExportFormat, + ExportTemplate, + ReportSchedule +} from './types'; + +const FORMAT_LABELS: Record = { + pdf: 'PDF', + excel: 'Excel', + csv: 'CSV', + png: 'PNG', + svg: 'SVG', + json: 'JSON', +}; + +const FORMAT_DESCRIPTIONS: Record = { + pdf: 'Documento PDF con formato profesional', + excel: 'Hoja de cálculo de Microsoft Excel', + csv: 'Archivo de valores separados por comas', + png: 'Imagen PNG de alta calidad', + svg: 'Imagen vectorial escalable', + json: 'Datos estructurados en formato JSON', +}; + +const FORMAT_ICONS: Record = { + pdf: '📄', + excel: '📊', + csv: '📋', + png: '🖼️', + svg: '🎨', + json: '⚙️', +}; + +const FREQUENCY_OPTIONS = [ + { value: 'daily', label: 'Diario' }, + { value: 'weekly', label: 'Semanal' }, + { value: 'monthly', label: 'Mensual' }, + { value: 'quarterly', label: 'Trimestral' }, + { value: 'yearly', label: 'Anual' }, +]; + +const TIME_OPTIONS = [ + { value: '06:00', label: '6:00 AM' }, + { value: '09:00', label: '9:00 AM' }, + { value: '12:00', label: '12:00 PM' }, + { value: '15:00', label: '3:00 PM' }, + { value: '18:00', label: '6:00 PM' }, + { value: '21:00', label: '9:00 PM' }, +]; + +const DAYS_OF_WEEK = [ + { value: 1, label: 'Lunes' }, + { value: 2, label: 'Martes' }, + { value: 3, label: 'Miércoles' }, + { value: 4, label: 'Jueves' }, + { value: 5, label: 'Viernes' }, + { value: 6, label: 'Sábado' }, + { value: 0, label: 'Domingo' }, +]; + +interface ExportOptionsComponentProps extends ExportOptionsProps { + onClose?: () => void; +} + +export const ExportOptions: React.FC = ({ + type, + title, + description, + availableFormats = ['pdf', 'excel', 'csv', 'png'], + templates = [], + onExport, + onSchedule, + loading = false, + disabled = false, + showScheduling = true, + showTemplates = true, + showAdvanced = true, + defaultOptions, + className = '', + onClose, +}) => { + const [selectedFormat, setSelectedFormat] = useState( + defaultOptions?.format || availableFormats[0] + ); + const [selectedTemplate, setSelectedTemplate] = useState( + templates.find(t => t.is_default)?.id || '' + ); + const [exportOptions, setExportOptions] = useState>({ + include_headers: true, + include_filters: true, + include_summary: true, + date_format: 'DD/MM/YYYY', + number_format: '#,##0.00', + currency_format: '€#,##0.00', + locale: 'es-ES', + timezone: 'Europe/Madrid', + page_size: 'A4', + orientation: 'portrait', + password_protected: false, + ...defaultOptions, + }); + + const [scheduleOptions, setScheduleOptions] = useState>({ + enabled: false, + frequency: 'weekly', + time: '09:00', + days_of_week: [1], // Monday + recipients: [], + format: selectedFormat, + include_attachments: true, + }); + + const [recipientsInput, setRecipientsInput] = useState(''); + const [activeTab, setActiveTab] = useState<'export' | 'schedule'>('export'); + const [showAdvancedOptions, setShowAdvancedOptions] = useState(false); + + const handleExportOptionChange = (key: keyof ExportOptions, value: any) => { + setExportOptions(prev => ({ ...prev, [key]: value })); + }; + + const handleScheduleOptionChange = (key: keyof ReportSchedule, value: any) => { + setScheduleOptions(prev => ({ ...prev, [key]: value })); + }; + + const handleExport = () => { + if (onExport) { + const fullOptions: ExportOptions = { + format: selectedFormat, + template: selectedTemplate || undefined, + include_headers: true, + include_filters: true, + include_summary: true, + date_format: 'DD/MM/YYYY', + number_format: '#,##0.00', + currency_format: '€#,##0.00', + locale: 'es-ES', + timezone: 'Europe/Madrid', + page_size: 'A4', + orientation: 'portrait', + ...exportOptions, + // format: selectedFormat, // Ensure format matches selection - handled by exportOptions + }; + onExport(fullOptions); + } + }; + + const handleSchedule = () => { + if (onSchedule) { + const recipients = recipientsInput + .split(',') + .map(email => email.trim()) + .filter(email => email.length > 0); + + const fullSchedule: ReportSchedule = { + enabled: true, + frequency: 'weekly', + time: '09:00', + recipients: recipients, + format: selectedFormat, + include_attachments: true, + ...scheduleOptions, + }; + onSchedule(fullSchedule); + } + }; + + const renderFormatSelector = () => ( +
+

Formato de exportación

+
+ {availableFormats.map(format => ( + + ))} +
+
+ ); + + const renderTemplateSelector = () => { + if (!showTemplates || templates.length === 0) return null; + + return ( +
+

Plantilla

+ + + {selectedTemplate && ( +
+ {templates.find(t => t.id === selectedTemplate)?.description} +
+ )} +
+ ); + }; + + const renderBasicOptions = () => ( +
+

Opciones básicas

+ +
+ + + + + +
+
+ ); + + const renderAdvancedOptions = () => { + if (!showAdvanced || !showAdvancedOptions) return null; + + return ( +
+

Opciones avanzadas

+ +
+
+ + +
+ +
+ + +
+ + {(['pdf'] as ExportFormat[]).includes(selectedFormat) && ( + <> +
+ + +
+ +
+ + +
+ + )} +
+ +
+ + + {exportOptions.password_protected && ( +
+ handleExportOptionChange('password', e.target.value)} + disabled={disabled || loading} + /> +
+ )} +
+
+ ); + }; + + const renderScheduleTab = () => ( +
+
+ +
+ + {scheduleOptions.enabled && ( +
+
+
+ + +
+ +
+ + +
+
+ + {scheduleOptions.frequency === 'weekly' && ( +
+ +
+ {DAYS_OF_WEEK.map(day => ( + + ))} +
+
+ )} + +
+ + setRecipientsInput(e.target.value)} + placeholder="ejemplo@empresa.com, otro@empresa.com" + disabled={disabled || loading} + /> +
+ +
+ +
+
+ )} +
+ ); + + return ( +
+ + {/* Header */} +
+
+

{title}

+ {description && ( +

{description}

+ )} +
+ + {onClose && ( + + )} +
+ + {/* Tabs */} +
+ +
+ + {/* Content */} +
+ {activeTab === 'export' ? ( + <> + {renderFormatSelector()} + {renderTemplateSelector()} + {renderBasicOptions()} + + {showAdvanced && ( +
+ +
+ )} + + {renderAdvancedOptions()} + + ) : ( + renderScheduleTab() + )} +
+ + {/* Footer */} +
+ {onClose && ( + + )} + + {activeTab === 'export' ? ( + + ) : ( + + )} +
+
+
+ ); +}; + +export default ExportOptions; \ No newline at end of file diff --git a/frontend/src/components/domain/analytics/FilterPanel.tsx b/frontend/src/components/domain/analytics/FilterPanel.tsx new file mode 100644 index 00000000..e83545f9 --- /dev/null +++ b/frontend/src/components/domain/analytics/FilterPanel.tsx @@ -0,0 +1,632 @@ +import React, { useState, useCallback, useMemo } from 'react'; +import { Card, Button, Badge, Input, Select, DatePicker } from '../../ui'; +import type { + FilterPanelProps, + AnalyticsFilter, + FilterOption, + AppliedFilter, + FilterPreset, + FilterType +} from './types'; + +const FILTER_TYPE_ICONS: Record = { + date: '📅', + select: '📋', + multiselect: '☑️', + range: '↔️', + text: '🔍', + number: '#️⃣', + boolean: '✓', +}; + +export const FilterPanel: React.FC = ({ + panel, + appliedFilters = [], + onFiltersChange, + onPresetSelect, + onPresetSave, + onPresetDelete, + onReset, + loading = false, + disabled = false, + compact = false, + showPresets = true, + showReset = true, + className = '', +}) => { + const [isCollapsed, setIsCollapsed] = useState(panel.default_collapsed); + const [localFilters, setLocalFilters] = useState>({}); + const [isPresetModalOpen, setIsPresetModalOpen] = useState(false); + const [newPresetName, setNewPresetName] = useState(''); + const [newPresetDescription, setNewPresetDescription] = useState(''); + const [validationErrors, setValidationErrors] = useState>({}); + + const appliedFiltersMap = useMemo(() => { + const map: Record = {}; + appliedFilters.forEach(filter => { + map[filter.filter_id] = filter; + }); + return map; + }, [appliedFilters]); + + const getFilterValue = useCallback((filter: AnalyticsFilter) => { + const applied = appliedFiltersMap[filter.id]; + if (applied) { + return applied.value; + } + + const local = localFilters[filter.id]; + if (local !== undefined) { + return local; + } + + return filter.default_value; + }, [appliedFiltersMap, localFilters]); + + const updateFilterValue = useCallback((filterId: string, value: any) => { + setLocalFilters(prev => ({ ...prev, [filterId]: value })); + + // Clear validation error for this filter + setValidationErrors(prev => { + const newErrors = { ...prev }; + delete newErrors[filterId]; + return newErrors; + }); + }, []); + + const validateFilter = useCallback((filter: AnalyticsFilter, value: any): string | null => { + if (!filter.validation_rules) return null; + + for (const rule of filter.validation_rules) { + switch (rule.rule) { + case 'required': + if (!value || (Array.isArray(value) && value.length === 0)) { + return rule.message; + } + break; + case 'min': + if (typeof value === 'number' && value < rule.value) { + return rule.message; + } + if (typeof value === 'string' && value.length < rule.value) { + return rule.message; + } + break; + case 'max': + if (typeof value === 'number' && value > rule.value) { + return rule.message; + } + if (typeof value === 'string' && value.length > rule.value) { + return rule.message; + } + break; + case 'pattern': + if (typeof value === 'string' && !new RegExp(rule.value).test(value)) { + return rule.message; + } + break; + } + } + + return null; + }, []); + + const applyFilters = useCallback(() => { + const errors: Record = {}; + const newAppliedFilters: AppliedFilter[] = []; + + panel.filters.forEach(filter => { + const value = getFilterValue(filter); + + // Validate the filter + const validationError = validateFilter(filter, value); + if (validationError) { + errors[filter.id] = validationError; + return; + } + + // Skip if no value and not required + if ((value === undefined || value === null || value === '') && !filter.required) { + return; + } + + // Create applied filter + const appliedFilter: AppliedFilter = { + filter_id: filter.id, + field: filter.field, + operator: getOperatorForFilterType(filter.type), + value, + display_value: formatDisplayValue(filter, value), + }; + + newAppliedFilters.push(appliedFilter); + }); + + setValidationErrors(errors); + + if (Object.keys(errors).length === 0 && onFiltersChange) { + onFiltersChange(newAppliedFilters); + } + }, [panel.filters, getFilterValue, validateFilter, onFiltersChange]); + + const resetFilters = useCallback(() => { + setLocalFilters({}); + setValidationErrors({}); + if (onReset) { + onReset(); + } + }, [onReset]); + + const getOperatorForFilterType = (type: FilterType): string => { + switch (type) { + case 'select': + case 'boolean': + return 'eq'; + case 'multiselect': + return 'in'; + case 'range': + return 'between'; + case 'text': + return 'like'; + case 'number': + case 'date': + return 'eq'; + default: + return 'eq'; + } + }; + + const formatDisplayValue = (filter: AnalyticsFilter, value: any): string => { + if (value === undefined || value === null) return ''; + + switch (filter.type) { + case 'select': + case 'multiselect': + if (filter.options) { + const options = Array.isArray(value) ? value : [value]; + return options + .map(v => filter.options?.find(opt => opt.value === v)?.label || v) + .join(', '); + } + return Array.isArray(value) ? value.join(', ') : String(value); + case 'boolean': + return value ? 'Sí' : 'No'; + case 'date': + return new Date(value).toLocaleDateString('es-ES'); + case 'range': + return Array.isArray(value) ? `${value[0]} - ${value[1]}` : String(value); + default: + return String(value); + } + }; + + const handlePresetSelect = (presetId: string) => { + const preset = panel.presets.find(p => p.id === presetId); + if (preset) { + setLocalFilters(preset.values); + if (onPresetSelect) { + onPresetSelect(presetId); + } + // Auto-apply filters when preset is selected + setTimeout(applyFilters, 0); + } + }; + + const handlePresetSave = () => { + if (!newPresetName.trim()) return; + + const currentValues: Record = {}; + panel.filters.forEach(filter => { + const value = getFilterValue(filter); + if (value !== undefined && value !== null && value !== '') { + currentValues[filter.id] = value; + } + }); + + const newPreset: Omit = { + name: newPresetName.trim(), + description: newPresetDescription.trim() || undefined, + is_default: false, + is_public: false, + values: currentValues, + }; + + if (onPresetSave) { + onPresetSave(newPreset); + } + + setIsPresetModalOpen(false); + setNewPresetName(''); + setNewPresetDescription(''); + }; + + const renderFilter = (filter: AnalyticsFilter) => { + const value = getFilterValue(filter); + const error = validationErrors[filter.id]; + const isDisabled = disabled || loading; + + const commonProps = { + disabled: isDisabled, + className: error ? 'border-red-300 focus:border-red-500' : '', + }; + + let filterInput: React.ReactNode; + + switch (filter.type) { + case 'text': + filterInput = ( + updateFilterValue(filter.id, e.target.value)} + {...commonProps} + /> + ); + break; + + case 'number': + filterInput = ( + updateFilterValue(filter.id, parseFloat(e.target.value) || null)} + {...commonProps} + /> + ); + break; + + case 'select': + filterInput = ( + + ); + break; + + case 'multiselect': + const selectedValues = Array.isArray(value) ? value : []; + filterInput = ( +
+
+ {filter.options?.map((option) => ( + + ))} +
+ {selectedValues.length > 0 && ( +
+ {selectedValues.map((val) => { + const option = filter.options?.find(opt => opt.value === val); + return ( + + {option?.label || val} + + + ); + })} +
+ )} +
+ ); + break; + + case 'boolean': + filterInput = ( + + ); + break; + + case 'date': + filterInput = ( + updateFilterValue(filter.id, date?.toISOString())} + placeholder={filter.placeholder} + disabled={isDisabled} + /> + ); + break; + + case 'range': + const rangeValue = Array.isArray(value) ? value : [null, null]; + filterInput = ( +
+ { + const newRange = [parseFloat(e.target.value) || null, rangeValue[1]]; + updateFilterValue(filter.id, newRange); + }} + {...commonProps} + className="flex-1" + /> + - + { + const newRange = [rangeValue[0], parseFloat(e.target.value) || null]; + updateFilterValue(filter.id, newRange); + }} + {...commonProps} + className="flex-1" + /> +
+ ); + break; + + default: + filterInput = ( +
+ Tipo de filtro no soportado: {filter.type} +
+ ); + } + + return ( +
+
+ + + {filter.help_text && ( + + ℹ️ + + )} +
+ + {filterInput} + + {error && ( +

{error}

+ )} +
+ ); + }; + + const renderPresets = () => { + if (!showPresets || panel.presets.length === 0) return null; + + return ( +
+
+

Filtros guardados

+ +
+ +
+ {panel.presets.map((preset) => ( +
+ + + {onPresetDelete && !preset.is_public && ( + + )} +
+ ))} +
+
+ ); + }; + + if (panel.collapsible && isCollapsed) { + return ( + + + + ); + } + + const gridClass = panel.layout === 'grid' ? 'grid grid-cols-1 md:grid-cols-2 gap-4' : + panel.layout === 'horizontal' ? 'grid grid-cols-1 lg:grid-cols-3 gap-4' : + 'space-y-4'; + + return ( + <> + +
+ {/* Header */} +
+

{panel.title}

+
+ {loading && ( +
+ )} + + {panel.collapsible && ( + + )} +
+
+ + {/* Presets */} + {renderPresets()} + + {/* Filters */} +
+ {panel.filters.map(filter => renderFilter(filter))} +
+ + {/* Actions */} +
+
+ {appliedFilters.length} filtro{appliedFilters.length !== 1 ? 's' : ''} aplicado{appliedFilters.length !== 1 ? 's' : ''} +
+ +
+ {showReset && appliedFilters.length > 0 && ( + + )} + + +
+
+
+
+ + {/* Save Preset Modal */} + {isPresetModalOpen && ( +
+ +

Guardar filtro

+ +
+
+ + setNewPresetName(e.target.value)} + placeholder="Ej: Ventas del último mes" + className="w-full" + /> +
+ +
+ + setNewPresetDescription(e.target.value)} + placeholder="Descripción del filtro" + className="w-full" + /> +
+ +
+ + +
+
+
+
+ )} + + ); +}; + +export default FilterPanel; \ No newline at end of file diff --git a/frontend/src/components/domain/analytics/FilterPanel.tsx.backup b/frontend/src/components/domain/analytics/FilterPanel.tsx.backup new file mode 100644 index 00000000..bb29892e --- /dev/null +++ b/frontend/src/components/domain/analytics/FilterPanel.tsx.backup @@ -0,0 +1,632 @@ +import React, { useState, useCallback, useMemo } from 'react'; +import { Card, Button, Badge, Input, Select, DatePicker } from '../../ui'; +import type { + FilterPanelProps, + AnalyticsFilter, + FilterOption, + AppliedFilter, + FilterPreset, + FilterType +} from './types'; + +const FILTER_TYPE_ICONS: Record = { + date: '📅', + select: '📋', + multiselect: '☑️', + range: '↔️', + text: '🔍', + number: '#️⃣', + boolean: '✓', +}; + +export const FilterPanel: React.FC = ({ + panel, + appliedFilters = [], + onFiltersChange, + onPresetSelect, + onPresetSave, + onPresetDelete, + onReset, + loading = false, + disabled = false, + compact = false, + showPresets = true, + showReset = true, + className = '', +}) => { + const [isCollapsed, setIsCollapsed] = useState(panel.default_collapsed); + const [localFilters, setLocalFilters] = useState>({}); + const [isPresetModalOpen, setIsPresetModalOpen] = useState(false); + const [newPresetName, setNewPresetName] = useState(''); + const [newPresetDescription, setNewPresetDescription] = useState(''); + const [validationErrors, setValidationErrors] = useState>({}); + + const appliedFiltersMap = useMemo(() => { + const map: Record = {}; + appliedFilters.forEach(filter => { + map[filter.filter_id] = filter; + }); + return map; + }, [appliedFilters]); + + const getFilterValue = useCallback((filter: AnalyticsFilter) => { + const applied = appliedFiltersMap[filter.id]; + if (applied) { + return applied.value; + } + + const local = localFilters[filter.id]; + if (local !== undefined) { + return local; + } + + return filter.default_value; + }, [appliedFiltersMap, localFilters]); + + const updateFilterValue = useCallback((filterId: string, value: any) => { + setLocalFilters(prev => ({ ...prev, [filterId]: value })); + + // Clear validation error for this filter + setValidationErrors(prev => { + const newErrors = { ...prev }; + delete newErrors[filterId]; + return newErrors; + }); + }, []); + + const validateFilter = useCallback((filter: AnalyticsFilter, value: any): string | null => { + if (!filter.validation_rules) return null; + + for (const rule of filter.validation_rules) { + switch (rule.rule) { + case 'required': + if (!value || (Array.isArray(value) && value.length === 0)) { + return rule.message; + } + break; + case 'min': + if (typeof value === 'number' && value < rule.value) { + return rule.message; + } + if (typeof value === 'string' && value.length < rule.value) { + return rule.message; + } + break; + case 'max': + if (typeof value === 'number' && value > rule.value) { + return rule.message; + } + if (typeof value === 'string' && value.length > rule.value) { + return rule.message; + } + break; + case 'pattern': + if (typeof value === 'string' && !new RegExp(rule.value).test(value)) { + return rule.message; + } + break; + } + } + + return null; + }, []); + + const applyFilters = useCallback(() => { + const errors: Record = {}; + const newAppliedFilters: AppliedFilter[] = []; + + panel.filters.forEach(filter => { + const value = getFilterValue(filter); + + // Validate the filter + const validationError = validateFilter(filter, value); + if (validationError) { + errors[filter.id] = validationError; + return; + } + + // Skip if no value and not required + if ((value === undefined || value === null || value === '') && !filter.required) { + return; + } + + // Create applied filter + const appliedFilter: AppliedFilter = { + filter_id: filter.id, + field: filter.field, + operator: getOperatorForFilterType(filter.type), + value, + display_value: formatDisplayValue(filter, value), + }; + + newAppliedFilters.push(appliedFilter); + }); + + setValidationErrors(errors); + + if (Object.keys(errors).length === 0 && onFiltersChange) { + onFiltersChange(newAppliedFilters); + } + }, [panel.filters, getFilterValue, validateFilter, onFiltersChange]); + + const resetFilters = useCallback(() => { + setLocalFilters({}); + setValidationErrors({}); + if (onReset) { + onReset(); + } + }, [onReset]); + + const getOperatorForFilterType = (type: FilterType): string => { + switch (type) { + case 'select': + case 'boolean': + return 'eq'; + case 'multiselect': + return 'in'; + case 'range': + return 'between'; + case 'text': + return 'like'; + case 'number': + case 'date': + return 'eq'; + default: + return 'eq'; + } + }; + + const formatDisplayValue = (filter: AnalyticsFilter, value: any): string => { + if (value === undefined || value === null) return ''; + + switch (filter.type) { + case 'select': + case 'multiselect': + if (filter.options) { + const options = Array.isArray(value) ? value : [value]; + return options + .map(v => filter.options?.find(opt => opt.value === v)?.label || v) + .join(', '); + } + return Array.isArray(value) ? value.join(', ') : String(value); + case 'boolean': + return value ? 'Sí' : 'No'; + case 'date': + return new Date(value).toLocaleDateString('es-ES'); + case 'range': + return Array.isArray(value) ? `${value[0]} - ${value[1]}` : String(value); + default: + return String(value); + } + }; + + const handlePresetSelect = (presetId: string) => { + const preset = panel.presets.find(p => p.id === presetId); + if (preset) { + setLocalFilters(preset.values); + if (onPresetSelect) { + onPresetSelect(presetId); + } + // Auto-apply filters when preset is selected + setTimeout(applyFilters, 0); + } + }; + + const handlePresetSave = () => { + if (!newPresetName.trim()) return; + + const currentValues: Record = {}; + panel.filters.forEach(filter => { + const value = getFilterValue(filter); + if (value !== undefined && value !== null && value !== '') { + currentValues[filter.id] = value; + } + }); + + const newPreset: Omit = { + name: newPresetName.trim(), + description: newPresetDescription.trim() || undefined, + is_default: false, + is_public: false, + values: currentValues, + }; + + if (onPresetSave) { + onPresetSave(newPreset); + } + + setIsPresetModalOpen(false); + setNewPresetName(''); + setNewPresetDescription(''); + }; + + const renderFilter = (filter: AnalyticsFilter) => { + const value = getFilterValue(filter); + const error = validationErrors[filter.id]; + const isDisabled = disabled || loading; + + const commonProps = { + disabled: isDisabled, + className: error ? 'border-red-300 focus:border-red-500' : '', + }; + + let filterInput: React.ReactNode; + + switch (filter.type) { + case 'text': + filterInput = ( + updateFilterValue(filter.id, e.target.value)} + {...commonProps} + /> + ); + break; + + case 'number': + filterInput = ( + updateFilterValue(filter.id, parseFloat(e.target.value) || null)} + {...commonProps} + /> + ); + break; + + case 'select': + filterInput = ( + + ); + break; + + case 'multiselect': + const selectedValues = Array.isArray(value) ? value : []; + filterInput = ( +
+
+ {filter.options?.map((option) => ( + + ))} +
+ {selectedValues.length > 0 && ( +
+ {selectedValues.map((val) => { + const option = filter.options?.find(opt => opt.value === val); + return ( + + {option?.label || val} + + + ); + })} +
+ )} +
+ ); + break; + + case 'boolean': + filterInput = ( + + ); + break; + + case 'date': + filterInput = ( + updateFilterValue(filter.id, date?.toISOString())} + placeholder={filter.placeholder} + disabled={isDisabled} + /> + ); + break; + + case 'range': + const rangeValue = Array.isArray(value) ? value : [null, null]; + filterInput = ( +
+ { + const newRange = [parseFloat(e.target.value) || null, rangeValue[1]]; + updateFilterValue(filter.id, newRange); + }} + {...commonProps} + className="flex-1" + /> + - + { + const newRange = [rangeValue[0], parseFloat(e.target.value) || null]; + updateFilterValue(filter.id, newRange); + }} + {...commonProps} + className="flex-1" + /> +
+ ); + break; + + default: + filterInput = ( +
+ Tipo de filtro no soportado: {filter.type} +
+ ); + } + + return ( +
+
+ + + {filter.help_text && ( + + ℹ️ + + )} +
+ + {filterInput} + + {error && ( +

{error}

+ )} +
+ ); + }; + + const renderPresets = () => { + if (!showPresets || panel.presets.length === 0) return null; + + return ( +
+
+

Filtros guardados

+ +
+ +
+ {panel.presets.map((preset) => ( +
+ + + {onPresetDelete && !preset.is_public && ( + + )} +
+ ))} +
+
+ ); + }; + + if (panel.collapsible && isCollapsed) { + return ( + + + + ); + } + + const gridClass = panel.layout === 'grid' ? 'grid grid-cols-1 md:grid-cols-2 gap-4' : + panel.layout === 'horizontal' ? 'grid grid-cols-1 lg:grid-cols-3 gap-4' : + 'space-y-4'; + + return ( + <> + +
+ {/* Header */} +
+

{panel.title}

+
+ {loading && ( +
+ )} + + {panel.collapsible && ( + + )} +
+
+ + {/* Presets */} + {renderPresets()} + + {/* Filters */} +
+ {panel.filters.map(filter => renderFilter(filter))} +
+ + {/* Actions */} +
+
+ {appliedFilters.length} filtro{appliedFilters.length !== 1 ? 's' : ''} aplicado{appliedFilters.length !== 1 ? 's' : ''} +
+ +
+ {showReset && appliedFilters.length > 0 && ( + + )} + + +
+
+
+
+ + {/* Save Preset Modal */} + {isPresetModalOpen && ( +
+ +

Guardar filtro

+ +
+
+ + setNewPresetName(e.target.value)} + placeholder="Ej: Ventas del último mes" + className="w-full" + /> +
+ +
+ + setNewPresetDescription(e.target.value)} + placeholder="Descripción del filtro" + className="w-full" + /> +
+ +
+ + +
+
+
+
+ )} + + ); +}; + +export default FilterPanel; \ No newline at end of file diff --git a/frontend/src/components/domain/analytics/ReportsTable.tsx b/frontend/src/components/domain/analytics/ReportsTable.tsx new file mode 100644 index 00000000..68ac0457 --- /dev/null +++ b/frontend/src/components/domain/analytics/ReportsTable.tsx @@ -0,0 +1,651 @@ +import React, { useState, useMemo } from 'react'; +import { Card, Button, Badge, Input, Select, Modal } from '../../ui'; +import { Table } from '../../ui'; +import type { + AnalyticsReport, + ReportsTableProps, + ExportFormat, + ReportType, + CustomAction +} from './types'; + +const REPORT_TYPE_LABELS: Record = { + sales: 'Ventas', + production: 'Producción', + inventory: 'Inventario', + financial: 'Financiero', + customer: 'Clientes', + performance: 'Rendimiento', +}; + +const STATUS_COLORS = { + active: 'bg-[var(--color-success)]/10 text-[var(--color-success)] border-green-200', + inactive: 'bg-yellow-100 text-yellow-800 border-yellow-200', + archived: 'bg-[var(--bg-tertiary)] text-[var(--text-primary)] border-[var(--border-primary)]', +}; + +const STATUS_LABELS = { + active: 'Activo', + inactive: 'Inactivo', + archived: 'Archivado', +}; + +export const ReportsTable: React.FC = ({ + reports = [], + loading = false, + error, + selectedReports = [], + onSelectionChange, + onReportClick, + onEditReport, + onDeleteReport, + onScheduleReport, + onShareReport, + onExportReport, + filters = [], + onFiltersChange, + sortable = true, + pagination, + bulkActions = false, + customActions = [], + tableConfig, +}) => { + const [searchTerm, setSearchTerm] = useState(''); + const [typeFilter, setTypeFilter] = useState('all'); + const [statusFilter, setStatusFilter] = useState<'all' | 'active' | 'inactive' | 'archived'>('all'); + const [sortField, setSortField] = useState('updated_at'); + const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('desc'); + const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); + const [reportToDelete, setReportToDelete] = useState(null); + const [isScheduleModalOpen, setIsScheduleModalOpen] = useState(false); + const [reportToSchedule, setReportToSchedule] = useState(null); + const [isShareModalOpen, setIsShareModalOpen] = useState(false); + const [reportToShare, setReportToShare] = useState(null); + + const filteredAndSortedReports = useMemo(() => { + let filtered = reports.filter(report => { + const matchesSearch = !searchTerm || + report.name.toLowerCase().includes(searchTerm.toLowerCase()) || + report.description?.toLowerCase().includes(searchTerm.toLowerCase()) || + report.category.toLowerCase().includes(searchTerm.toLowerCase()); + + const matchesType = typeFilter === 'all' || report.type === typeFilter; + const matchesStatus = statusFilter === 'all' || report.status === statusFilter; + + return matchesSearch && matchesType && matchesStatus; + }); + + if (sortable && sortField) { + filtered.sort((a, b) => { + const aVal = a[sortField]; + const bVal = b[sortField]; + + if (aVal === null || aVal === undefined) return 1; + if (bVal === null || bVal === undefined) return -1; + + let comparison = 0; + if (typeof aVal === 'string' && typeof bVal === 'string') { + comparison = aVal.localeCompare(bVal); + } else if (typeof aVal === 'number' && typeof bVal === 'number') { + comparison = aVal - bVal; + } else { + comparison = String(aVal).localeCompare(String(bVal)); + } + + return sortDirection === 'asc' ? comparison : -comparison; + }); + } + + return filtered; + }, [reports, searchTerm, typeFilter, statusFilter, sortField, sortDirection, sortable]); + + const handleSort = (field: keyof AnalyticsReport) => { + if (sortField === field) { + setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc'); + } else { + setSortField(field); + setSortDirection('asc'); + } + }; + + const handleSelectAll = (checked: boolean) => { + if (onSelectionChange) { + onSelectionChange(checked ? filteredAndSortedReports.map(r => r.id) : []); + } + }; + + const handleSelectReport = (reportId: string, checked: boolean) => { + if (onSelectionChange) { + if (checked) { + onSelectionChange([...selectedReports, reportId]); + } else { + onSelectionChange(selectedReports.filter(id => id !== reportId)); + } + } + }; + + const handleDeleteConfirm = () => { + if (reportToDelete && onDeleteReport) { + onDeleteReport(reportToDelete); + } + setIsDeleteModalOpen(false); + setReportToDelete(null); + }; + + const handleBulkDelete = () => { + if (selectedReports.length > 0 && onDeleteReport) { + selectedReports.forEach(id => onDeleteReport(id)); + if (onSelectionChange) { + onSelectionChange([]); + } + } + }; + + const renderActionButton = (report: AnalyticsReport, action: string) => { + switch (action) { + case 'view': + return ( + + ); + case 'edit': + return ( + + ); + case 'schedule': + return ( + + ); + case 'share': + return ( + + ); + case 'export': + return ( +
+ + +
+ ); + case 'delete': + return ( + + ); + default: + return null; + } + }; + + const tableColumns = [ + { + key: 'select', + title: ( + 0} + onChange={(e) => handleSelectAll(e.target.checked)} + className="rounded" + /> + ), + render: (report: AnalyticsReport) => ( + handleSelectReport(report.id, e.target.checked)} + className="rounded" + /> + ), + width: 50, + visible: bulkActions, + }, + { + key: 'name', + title: 'Nombre', + sortable: true, + render: (report: AnalyticsReport) => ( +
+ + {report.description && ( +

{report.description}

+ )} +
+ ), + minWidth: 200, + }, + { + key: 'type', + title: 'Tipo', + sortable: true, + render: (report: AnalyticsReport) => ( + + {REPORT_TYPE_LABELS[report.type]} + + ), + width: 120, + }, + { + key: 'category', + title: 'Categoría', + sortable: true, + width: 120, + }, + { + key: 'status', + title: 'Estado', + sortable: true, + render: (report: AnalyticsReport) => ( + + {STATUS_LABELS[report.status]} + + ), + width: 100, + }, + { + key: 'last_run', + title: 'Última ejecución', + sortable: true, + render: (report: AnalyticsReport) => ( + report.last_run + ? new Date(report.last_run).toLocaleDateString('es-ES') + : '-' + ), + width: 140, + }, + { + key: 'next_run', + title: 'Próxima ejecución', + sortable: true, + render: (report: AnalyticsReport) => ( + report.next_run + ? new Date(report.next_run).toLocaleDateString('es-ES') + : '-' + ), + width: 140, + }, + { + key: 'updated_at', + title: 'Actualizado', + sortable: true, + render: (report: AnalyticsReport) => ( + new Date(report.updated_at).toLocaleDateString('es-ES') + ), + width: 120, + }, + { + key: 'actions', + title: 'Acciones', + render: (report: AnalyticsReport) => ( +
+ {renderActionButton(report, 'view')} + {renderActionButton(report, 'edit')} + {renderActionButton(report, 'schedule')} + {renderActionButton(report, 'export')} + {renderActionButton(report, 'delete')} +
+ ), + width: 200, + }, + ].filter(col => col.visible !== false); + + if (error) { + return ( + +
+

Error al cargar los reportes

+

{error}

+
+
+ ); + } + + return ( + <> + + {/* Filters and Actions */} +
+
+ {/* Search */} + {(tableConfig?.showSearch !== false) && ( +
+ setSearchTerm(e.target.value)} + className="w-full" + /> +
+ )} + + {/* Filters */} +
+ + + +
+ + {/* Bulk Actions */} + {bulkActions && selectedReports.length > 0 && ( +
+ +
+ )} +
+
+ + {/* Table */} +
+ + + + {/* Pagination */} + {pagination && ( +
+
+

+ Mostrando {Math.min(pagination.pageSize * (pagination.current - 1) + 1, pagination.total)} - {Math.min(pagination.pageSize * pagination.current, pagination.total)} de {pagination.total} reportes +

+ +
+ + + + Página {pagination.current} de {Math.ceil(pagination.total / pagination.pageSize)} + + + +
+
+
+ )} + + + {/* Delete Confirmation Modal */} + { + setIsDeleteModalOpen(false); + setReportToDelete(null); + }} + title="Confirmar eliminación" + > +
+

¿Estás seguro de que deseas eliminar este reporte? Esta acción no se puede deshacer.

+ +
+ + +
+
+
+ + {/* Schedule Modal */} + { + setIsScheduleModalOpen(false); + setReportToSchedule(null); + }} + title="Programar reporte" + > +
+

Configurar programación para: {reportToSchedule?.name}

+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + {/* Share Modal */} + { + setIsShareModalOpen(false); + setReportToShare(null); + }} + title="Compartir reporte" + > +
+

Compartir: {reportToShare?.name}

+ +
+ +
+ + +
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + ); +}; + +export default ReportsTable; \ No newline at end of file diff --git a/frontend/src/components/domain/analytics/ReportsTable.tsx.backup b/frontend/src/components/domain/analytics/ReportsTable.tsx.backup new file mode 100644 index 00000000..04c96917 --- /dev/null +++ b/frontend/src/components/domain/analytics/ReportsTable.tsx.backup @@ -0,0 +1,651 @@ +import React, { useState, useMemo } from 'react'; +import { Card, Button, Badge, Input, Select, Modal } from '../../ui'; +import { Table } from '../../ui'; +import type { + AnalyticsReport, + ReportsTableProps, + ExportFormat, + ReportType, + CustomAction +} from './types'; + +const REPORT_TYPE_LABELS: Record = { + sales: 'Ventas', + production: 'Producción', + inventory: 'Inventario', + financial: 'Financiero', + customer: 'Clientes', + performance: 'Rendimiento', +}; + +const STATUS_COLORS = { + active: 'bg-green-100 text-green-800 border-green-200', + inactive: 'bg-yellow-100 text-yellow-800 border-yellow-200', + archived: 'bg-gray-100 text-gray-800 border-gray-200', +}; + +const STATUS_LABELS = { + active: 'Activo', + inactive: 'Inactivo', + archived: 'Archivado', +}; + +export const ReportsTable: React.FC = ({ + reports = [], + loading = false, + error, + selectedReports = [], + onSelectionChange, + onReportClick, + onEditReport, + onDeleteReport, + onScheduleReport, + onShareReport, + onExportReport, + filters = [], + onFiltersChange, + sortable = true, + pagination, + bulkActions = false, + customActions = [], + tableConfig, +}) => { + const [searchTerm, setSearchTerm] = useState(''); + const [typeFilter, setTypeFilter] = useState('all'); + const [statusFilter, setStatusFilter] = useState<'all' | 'active' | 'inactive' | 'archived'>('all'); + const [sortField, setSortField] = useState('updated_at'); + const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('desc'); + const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); + const [reportToDelete, setReportToDelete] = useState(null); + const [isScheduleModalOpen, setIsScheduleModalOpen] = useState(false); + const [reportToSchedule, setReportToSchedule] = useState(null); + const [isShareModalOpen, setIsShareModalOpen] = useState(false); + const [reportToShare, setReportToShare] = useState(null); + + const filteredAndSortedReports = useMemo(() => { + let filtered = reports.filter(report => { + const matchesSearch = !searchTerm || + report.name.toLowerCase().includes(searchTerm.toLowerCase()) || + report.description?.toLowerCase().includes(searchTerm.toLowerCase()) || + report.category.toLowerCase().includes(searchTerm.toLowerCase()); + + const matchesType = typeFilter === 'all' || report.type === typeFilter; + const matchesStatus = statusFilter === 'all' || report.status === statusFilter; + + return matchesSearch && matchesType && matchesStatus; + }); + + if (sortable && sortField) { + filtered.sort((a, b) => { + const aVal = a[sortField]; + const bVal = b[sortField]; + + if (aVal === null || aVal === undefined) return 1; + if (bVal === null || bVal === undefined) return -1; + + let comparison = 0; + if (typeof aVal === 'string' && typeof bVal === 'string') { + comparison = aVal.localeCompare(bVal); + } else if (typeof aVal === 'number' && typeof bVal === 'number') { + comparison = aVal - bVal; + } else { + comparison = String(aVal).localeCompare(String(bVal)); + } + + return sortDirection === 'asc' ? comparison : -comparison; + }); + } + + return filtered; + }, [reports, searchTerm, typeFilter, statusFilter, sortField, sortDirection, sortable]); + + const handleSort = (field: keyof AnalyticsReport) => { + if (sortField === field) { + setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc'); + } else { + setSortField(field); + setSortDirection('asc'); + } + }; + + const handleSelectAll = (checked: boolean) => { + if (onSelectionChange) { + onSelectionChange(checked ? filteredAndSortedReports.map(r => r.id) : []); + } + }; + + const handleSelectReport = (reportId: string, checked: boolean) => { + if (onSelectionChange) { + if (checked) { + onSelectionChange([...selectedReports, reportId]); + } else { + onSelectionChange(selectedReports.filter(id => id !== reportId)); + } + } + }; + + const handleDeleteConfirm = () => { + if (reportToDelete && onDeleteReport) { + onDeleteReport(reportToDelete); + } + setIsDeleteModalOpen(false); + setReportToDelete(null); + }; + + const handleBulkDelete = () => { + if (selectedReports.length > 0 && onDeleteReport) { + selectedReports.forEach(id => onDeleteReport(id)); + if (onSelectionChange) { + onSelectionChange([]); + } + } + }; + + const renderActionButton = (report: AnalyticsReport, action: string) => { + switch (action) { + case 'view': + return ( + + ); + case 'edit': + return ( + + ); + case 'schedule': + return ( + + ); + case 'share': + return ( + + ); + case 'export': + return ( +
+ + +
+ ); + case 'delete': + return ( + + ); + default: + return null; + } + }; + + const tableColumns = [ + { + key: 'select', + title: ( + 0} + onChange={(e) => handleSelectAll(e.target.checked)} + className="rounded" + /> + ), + render: (report: AnalyticsReport) => ( + handleSelectReport(report.id, e.target.checked)} + className="rounded" + /> + ), + width: 50, + visible: bulkActions, + }, + { + key: 'name', + title: 'Nombre', + sortable: true, + render: (report: AnalyticsReport) => ( +
+ + {report.description && ( +

{report.description}

+ )} +
+ ), + minWidth: 200, + }, + { + key: 'type', + title: 'Tipo', + sortable: true, + render: (report: AnalyticsReport) => ( + + {REPORT_TYPE_LABELS[report.type]} + + ), + width: 120, + }, + { + key: 'category', + title: 'Categoría', + sortable: true, + width: 120, + }, + { + key: 'status', + title: 'Estado', + sortable: true, + render: (report: AnalyticsReport) => ( + + {STATUS_LABELS[report.status]} + + ), + width: 100, + }, + { + key: 'last_run', + title: 'Última ejecución', + sortable: true, + render: (report: AnalyticsReport) => ( + report.last_run + ? new Date(report.last_run).toLocaleDateString('es-ES') + : '-' + ), + width: 140, + }, + { + key: 'next_run', + title: 'Próxima ejecución', + sortable: true, + render: (report: AnalyticsReport) => ( + report.next_run + ? new Date(report.next_run).toLocaleDateString('es-ES') + : '-' + ), + width: 140, + }, + { + key: 'updated_at', + title: 'Actualizado', + sortable: true, + render: (report: AnalyticsReport) => ( + new Date(report.updated_at).toLocaleDateString('es-ES') + ), + width: 120, + }, + { + key: 'actions', + title: 'Acciones', + render: (report: AnalyticsReport) => ( +
+ {renderActionButton(report, 'view')} + {renderActionButton(report, 'edit')} + {renderActionButton(report, 'schedule')} + {renderActionButton(report, 'export')} + {renderActionButton(report, 'delete')} +
+ ), + width: 200, + }, + ].filter(col => col.visible !== false); + + if (error) { + return ( + +
+

Error al cargar los reportes

+

{error}

+
+
+ ); + } + + return ( + <> + + {/* Filters and Actions */} +
+
+ {/* Search */} + {(tableConfig?.showSearch !== false) && ( +
+ setSearchTerm(e.target.value)} + className="w-full" + /> +
+ )} + + {/* Filters */} +
+ + + +
+ + {/* Bulk Actions */} + {bulkActions && selectedReports.length > 0 && ( +
+ +
+ )} +
+
+ + {/* Table */} +
+
+ + + {/* Pagination */} + {pagination && ( +
+
+

+ Mostrando {Math.min(pagination.pageSize * (pagination.current - 1) + 1, pagination.total)} - {Math.min(pagination.pageSize * pagination.current, pagination.total)} de {pagination.total} reportes +

+ +
+ + + + Página {pagination.current} de {Math.ceil(pagination.total / pagination.pageSize)} + + + +
+
+
+ )} + + + {/* Delete Confirmation Modal */} + { + setIsDeleteModalOpen(false); + setReportToDelete(null); + }} + title="Confirmar eliminación" + > +
+

¿Estás seguro de que deseas eliminar este reporte? Esta acción no se puede deshacer.

+ +
+ + +
+
+
+ + {/* Schedule Modal */} + { + setIsScheduleModalOpen(false); + setReportToSchedule(null); + }} + title="Programar reporte" + > +
+

Configurar programación para: {reportToSchedule?.name}

+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + {/* Share Modal */} + { + setIsShareModalOpen(false); + setReportToShare(null); + }} + title="Compartir reporte" + > +
+

Compartir: {reportToShare?.name}

+ +
+ +
+ + +
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + ); +}; + +export default ReportsTable; \ No newline at end of file diff --git a/frontend/src/components/domain/analytics/index.ts b/frontend/src/components/domain/analytics/index.ts new file mode 100644 index 00000000..be953472 --- /dev/null +++ b/frontend/src/components/domain/analytics/index.ts @@ -0,0 +1,9 @@ +// Analytics domain components +export { default as AnalyticsDashboard } from './AnalyticsDashboard'; +export { default as ChartWidget } from './ChartWidget'; +export { default as ReportsTable } from './ReportsTable'; +export { default as FilterPanel } from './FilterPanel'; +export { default as ExportOptions } from './ExportOptions'; + +// Types +export * from './types'; \ No newline at end of file diff --git a/frontend/src/components/domain/analytics/types.ts b/frontend/src/components/domain/analytics/types.ts new file mode 100644 index 00000000..5b296a31 --- /dev/null +++ b/frontend/src/components/domain/analytics/types.ts @@ -0,0 +1,709 @@ +// Analytics domain types and interfaces for comprehensive bakery management analytics + +export type ReportType = 'sales' | 'production' | 'inventory' | 'financial' | 'customer' | 'performance'; +export type ChartType = 'line' | 'bar' | 'pie' | 'area' | 'scatter' | 'doughnut' | 'radar' | 'mixed'; +export type ExportFormat = 'pdf' | 'excel' | 'csv' | 'png' | 'svg' | 'json'; +export type FilterType = 'date' | 'select' | 'multiselect' | 'range' | 'text' | 'number' | 'boolean'; +export type MetricType = 'currency' | 'percentage' | 'number' | 'count' | 'duration' | 'ratio'; +export type TimeRange = 'today' | 'yesterday' | 'thisWeek' | 'lastWeek' | 'thisMonth' | 'lastMonth' | 'thisQuarter' | 'lastQuarter' | 'thisYear' | 'lastYear' | 'custom'; +export type ComparisonType = 'previous_period' | 'same_period_last_year' | 'target' | 'average' | 'none'; + +// Base interfaces +export interface BaseAnalyticsEntity { + id: string; + name: string; + description?: string; + created_at: string; + updated_at: string; + created_by?: string; + tenant_id: string; +} + +// Report interfaces +export interface AnalyticsReport extends BaseAnalyticsEntity { + type: ReportType; + category: string; + subcategory?: string; + config: ReportConfig; + schedule?: ReportSchedule; + status: 'active' | 'inactive' | 'archived'; + last_run?: string; + next_run?: string; + metrics: ReportMetric[]; + filters: ReportFilter[]; + access_permissions: string[]; + tags: string[]; +} + +export interface ReportConfig { + data_source: string; + refresh_interval: number; // in minutes + cache_duration: number; // in minutes + max_records: number; + default_time_range: TimeRange; + default_filters: Record; + visualization_type: ChartType; + custom_sql?: string; + aggregation_rules: AggregationRule[]; +} + +export interface ReportSchedule { + enabled: boolean; + frequency: 'daily' | 'weekly' | 'monthly' | 'quarterly' | 'yearly'; + time: string; // HH:mm format + days_of_week?: number[]; // 0-6, Sunday = 0 + day_of_month?: number; // 1-31 + recipients: string[]; + format: ExportFormat; + include_attachments: boolean; + custom_message?: string; +} + +export interface ReportMetric { + id: string; + name: string; + display_name: string; + type: MetricType; + calculation: 'sum' | 'avg' | 'count' | 'min' | 'max' | 'distinct' | 'custom'; + format: string; + unit?: string; + decimal_places: number; + show_comparison: boolean; + comparison_type?: ComparisonType; + target_value?: number; + alert_thresholds?: AlertThreshold[]; + description?: string; +} + +export interface AlertThreshold { + type: 'min' | 'max' | 'range'; + value: number | [number, number]; + severity: 'info' | 'warning' | 'critical'; + message: string; +} + +export interface AggregationRule { + field: string; + operation: 'group_by' | 'sum' | 'avg' | 'count' | 'min' | 'max'; + alias?: string; + having_condition?: string; +} + +// Chart interfaces +export interface ChartWidget { + id: string; + title: string; + subtitle?: string; + type: ChartType; + config: ChartConfig; + data_source: string; + filters: ChartFilter[]; + dimensions: ChartDimension; + refresh_interval?: number; + last_updated?: string; + loading?: boolean; + error?: string; +} + +export interface ChartConfig { + theme: 'light' | 'dark' | 'auto'; + color_scheme: string[]; + show_legend: boolean; + legend_position: 'top' | 'bottom' | 'left' | 'right'; + show_grid: boolean; + show_tooltips: boolean; + animation_enabled: boolean; + responsive: boolean; + download_enabled: boolean; + zoom_enabled: boolean; + pan_enabled: boolean; + custom_options?: Record; +} + +export interface ChartDimension { + width?: number | string; + height?: number | string; + aspect_ratio?: string; + min_width?: number; + min_height?: number; + max_width?: number; + max_height?: number; +} + +export interface ChartFilter { + id: string; + field: string; + operator: 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'not_in' | 'like' | 'between'; + value: any; + display_name: string; +} + +export interface ChartDataPoint { + x: string | number | Date; + y: number; + label?: string; + color?: string; + metadata?: Record; +} + +export interface ChartSeries { + id: string; + name: string; + data: ChartDataPoint[]; + type?: ChartType; + color?: string; + visible: boolean; + stack_group?: string; +} + +// Filter interfaces +export interface FilterPanel { + id: string; + title: string; + filters: AnalyticsFilter[]; + presets: FilterPreset[]; + active_preset?: string; + layout: 'horizontal' | 'vertical' | 'grid'; + collapsible: boolean; + default_collapsed: boolean; +} + +export interface AnalyticsFilter { + id: string; + field: string; + display_name: string; + type: FilterType; + required: boolean; + multiple: boolean; + default_value?: any; + options?: FilterOption[]; + validation_rules?: FilterValidation[]; + depends_on?: string[]; // Other filter IDs this filter depends on + placeholder?: string; + help_text?: string; + category?: string; +} + +export interface FilterOption { + value: any; + label: string; + description?: string; + icon?: string; + color?: string; + disabled?: boolean; +} + +export interface FilterValidation { + rule: 'required' | 'min' | 'max' | 'pattern' | 'custom'; + value?: any; + message: string; +} + +export interface FilterPreset { + id: string; + name: string; + description?: string; + is_default: boolean; + is_public: boolean; + created_by?: string; + values: Record; + icon?: string; + tags?: string[]; +} + +export interface AppliedFilter { + filter_id: string; + field: string; + operator: string; + value: any; + display_value?: string; +} + +// Export interfaces +export interface ExportOptions { + format: ExportFormat; + template?: string; + include_headers: boolean; + include_filters: boolean; + include_summary: boolean; + date_format: string; + number_format: string; + currency_format: string; + locale: string; + timezone: string; + page_size?: 'A4' | 'A3' | 'Letter' | 'Legal'; + orientation?: 'portrait' | 'landscape'; + margins?: ExportMargins; + watermark?: string; + password_protected?: boolean; + password?: string; + compression_level?: number; +} + +export interface ExportMargins { + top: number; + right: number; + bottom: number; + left: number; +} + +export interface ExportJob { + id: string; + type: 'report' | 'chart' | 'data'; + format: ExportFormat; + status: 'pending' | 'processing' | 'completed' | 'failed' | 'cancelled'; + progress: number; // 0-100 + created_at: string; + started_at?: string; + completed_at?: string; + file_size?: number; + download_url?: string; + expires_at?: string; + error_message?: string; + options: ExportOptions; + metadata: ExportMetadata; +} + +export interface ExportMetadata { + report_id?: string; + chart_id?: string; + filters_applied: AppliedFilter[]; + total_records: number; + data_range: { + from: string; + to: string; + }; + generated_by: string; + title: string; + description?: string; +} + +export interface ExportTemplate { + id: string; + name: string; + description?: string; + format: ExportFormat; + is_default: boolean; + is_public: boolean; + config: ExportTemplateConfig; + preview_url?: string; + created_by?: string; + created_at: string; +} + +export interface ExportTemplateConfig { + header: ExportHeaderConfig; + footer: ExportFooterConfig; + styling: ExportStylingConfig; + layout: ExportLayoutConfig; + sections: ExportSectionConfig[]; +} + +export interface ExportHeaderConfig { + show_logo: boolean; + logo_url?: string; + show_title: boolean; + show_date: boolean; + show_filters: boolean; + custom_text?: string; +} + +export interface ExportFooterConfig { + show_page_numbers: boolean; + show_generated_info: boolean; + custom_text?: string; +} + +export interface ExportStylingConfig { + font_family: string; + font_size: number; + colors: { + primary: string; + secondary: string; + text: string; + background: string; + border: string; + }; + table_style: 'minimal' | 'bordered' | 'striped' | 'compact'; +} + +export interface ExportLayoutConfig { + columns: number; + spacing: number; + show_grid: boolean; + section_breaks: boolean; +} + +export interface ExportSectionConfig { + type: 'table' | 'chart' | 'summary' | 'text' | 'spacer'; + title?: string; + content: any; + width: number; // percentage + height?: number; + order: number; +} + +// Bakery-specific analytics interfaces +export interface BakeryMetrics { + sales: SalesMetrics; + production: ProductionMetrics; + inventory: InventoryMetrics; + financial: FinancialMetrics; + customer: CustomerMetrics; + operational: OperationalMetrics; +} + +export interface SalesMetrics { + total_revenue: number; + total_orders: number; + average_order_value: number; + revenue_growth: number; + order_growth: number; + conversion_rate: number; + top_products: ProductPerformance[]; + sales_by_channel: ChannelPerformance[]; + hourly_sales: TimeSeriesData[]; + seasonal_trends: SeasonalData[]; +} + +export interface ProductionMetrics { + total_batches: number; + success_rate: number; + waste_percentage: number; + production_efficiency: number; + quality_score: number; + capacity_utilization: number; + production_costs: number; + batch_cycle_time: number; + top_recipes: RecipePerformance[]; + quality_trends: TimeSeriesData[]; +} + +export interface InventoryMetrics { + total_stock_value: number; + turnover_rate: number; + days_of_inventory: number; + stockout_rate: number; + waste_value: number; + carrying_costs: number; + reorder_efficiency: number; + supplier_performance: SupplierPerformance[]; + stock_levels: StockLevelData[]; + expiry_alerts: ExpiryAlert[]; +} + +export interface FinancialMetrics { + gross_profit: number; + net_profit: number; + profit_margin: number; + cost_of_goods_sold: number; + operating_expenses: number; + break_even_point: number; + cash_flow: number; + roi: number; + expense_breakdown: ExpenseCategory[]; + profit_trends: TimeSeriesData[]; +} + +export interface CustomerMetrics { + total_customers: number; + new_customers: number; + returning_customers: number; + customer_retention_rate: number; + customer_lifetime_value: number; + average_purchase_frequency: number; + customer_satisfaction: number; + customer_segments: CustomerSegment[]; + loyalty_program_performance: LoyaltyMetrics[]; +} + +export interface OperationalMetrics { + staff_productivity: number; + equipment_uptime: number; + energy_consumption: number; + delivery_performance: number; + food_safety_score: number; + compliance_rate: number; + maintenance_costs: number; + operational_efficiency: number; +} + +// Supporting interfaces +export interface ProductPerformance { + product_id: string; + product_name: string; + category: string; + quantity_sold: number; + revenue: number; + profit_margin: number; + growth_rate: number; + stock_turns: number; +} + +export interface ChannelPerformance { + channel: string; + revenue: number; + orders: number; + customers: number; + conversion_rate: number; + average_order_value: number; + growth_rate: number; +} + +export interface RecipePerformance { + recipe_id: string; + recipe_name: string; + batches_produced: number; + success_rate: number; + average_yield: number; + cost_per_unit: number; + quality_score: number; + profitability: number; +} + +export interface SupplierPerformance { + supplier_id: string; + supplier_name: string; + on_time_delivery: number; + quality_rating: number; + cost_competitiveness: number; + total_orders: number; + total_value: number; + reliability_score: number; +} + +export interface TimeSeriesData { + date: string; + value: number; + label?: string; + target?: number; + previous_period?: number; +} + +export interface SeasonalData { + period: string; // "Q1", "Q2", etc. or "Jan", "Feb", etc. + value: number; + year: number; + trend: 'up' | 'down' | 'stable'; + percentage_change: number; +} + +export interface StockLevelData { + item_id: string; + item_name: string; + category: string; + current_stock: number; + minimum_stock: number; + maximum_stock: number; + reorder_point: number; + stock_status: 'low' | 'normal' | 'high' | 'out_of_stock'; + days_remaining: number; +} + +export interface ExpiryAlert { + item_id: string; + item_name: string; + batch_number: string; + expiry_date: string; + days_to_expiry: number; + quantity: number; + value: number; + risk_level: 'low' | 'medium' | 'high' | 'critical'; +} + +export interface ExpenseCategory { + category: string; + amount: number; + percentage: number; + change_from_previous: number; + budget_variance: number; + subcategories?: ExpenseSubcategory[]; +} + +export interface ExpenseSubcategory { + name: string; + amount: number; + percentage: number; +} + +export interface CustomerSegment { + segment_id: string; + segment_name: string; + customer_count: number; + revenue_contribution: number; + average_order_value: number; + purchase_frequency: number; + retention_rate: number; + growth_rate: number; +} + +export interface LoyaltyMetrics { + program_name: string; + active_members: number; + enrollment_rate: number; + redemption_rate: number; + points_issued: number; + points_redeemed: number; + revenue_from_members: number; + member_retention: number; +} + +// Component prop types +export interface ReportsTableProps { + reports?: AnalyticsReport[]; + loading?: boolean; + error?: string; + selectedReports?: string[]; + onSelectionChange?: (selectedIds: string[]) => void; + onReportClick?: (report: AnalyticsReport) => void; + onEditReport?: (report: AnalyticsReport) => void; + onDeleteReport?: (reportId: string) => void; + onScheduleReport?: (report: AnalyticsReport) => void; + onShareReport?: (report: AnalyticsReport) => void; + onExportReport?: (report: AnalyticsReport, format: ExportFormat) => void; + filters?: AppliedFilter[]; + onFiltersChange?: (filters: AppliedFilter[]) => void; + sortable?: boolean; + pagination?: { + current: number; + pageSize: number; + total: number; + onChange: (page: number, pageSize: number) => void; + }; + bulkActions?: boolean; + customActions?: CustomAction[]; + tableConfig?: ReportsTableConfig; +} + +export interface ChartWidgetProps { + widget: ChartWidget; + data?: ChartSeries[]; + loading?: boolean; + error?: string; + onConfigChange?: (config: Partial) => void; + onFiltersChange?: (filters: ChartFilter[]) => void; + onRefresh?: () => void; + onExport?: (format: ExportFormat) => void; + onFullscreen?: () => void; + interactive?: boolean; + showControls?: boolean; + showTitle?: boolean; + showSubtitle?: boolean; + className?: string; +} + +export interface FilterPanelProps { + panel: FilterPanel; + appliedFilters?: AppliedFilter[]; + onFiltersChange?: (filters: AppliedFilter[]) => void; + onPresetSelect?: (presetId: string) => void; + onPresetSave?: (preset: Omit) => void; + onPresetDelete?: (presetId: string) => void; + onReset?: () => void; + loading?: boolean; + disabled?: boolean; + compact?: boolean; + showPresets?: boolean; + showReset?: boolean; + className?: string; +} + +export interface ExportOptionsProps { + type: 'report' | 'chart' | 'data'; + title: string; + description?: string; + availableFormats?: ExportFormat[]; + templates?: ExportTemplate[]; + onExport?: (options: ExportOptions) => void; + onSchedule?: (schedule: ReportSchedule) => void; + loading?: boolean; + disabled?: boolean; + showScheduling?: boolean; + showTemplates?: boolean; + showAdvanced?: boolean; + defaultOptions?: Partial; + className?: string; +} + +export interface CustomAction { + key: string; + label: string; + icon?: React.ReactNode; + onClick: (selectedItems: any[]) => void; + disabled?: (selectedItems: any[]) => boolean; + visible?: (selectedItems: any[]) => boolean; + type?: 'default' | 'primary' | 'danger' | 'success' | 'warning'; +} + +export interface ReportsTableConfig { + showSearch?: boolean; + showFilters?: boolean; + showExport?: boolean; + showColumns?: boolean; + showRefresh?: boolean; + showBulkActions?: boolean; + defaultPageSize?: number; + stickyHeader?: boolean; + compactMode?: boolean; + customColumns?: TableColumn[]; +} + +export interface TableColumn { + key: string; + title: string; + dataIndex?: string; + sortable?: boolean; + filterable?: boolean; + searchable?: boolean; + width?: number | string; + align?: 'left' | 'center' | 'right'; + render?: (value: any, record: any) => React.ReactNode; + format?: 'date' | 'currency' | 'number' | 'percentage' | 'text'; + fixed?: 'left' | 'right'; + ellipsis?: boolean; + copyable?: boolean; + editable?: boolean; +} + +// API response types +export interface AnalyticsApiResponse { + success: boolean; + data: T; + message?: string; + metadata?: { + total_records: number; + filtered_records: number; + execution_time: number; + cache_hit: boolean; + generated_at: string; + }; +} + +export interface ReportsListResponse extends AnalyticsApiResponse { + pagination: { + current_page: number; + total_pages: number; + page_size: number; + total_records: number; + }; + filters_applied: AppliedFilter[]; + sort_applied?: { + field: string; + order: 'asc' | 'desc'; + }; +} + +export interface ChartDataResponse extends AnalyticsApiResponse { + chart_config: ChartConfig; + time_range: { + from: string; + to: string; + }; + filters_applied: AppliedFilter[]; + data_source: string; +} + +export interface ExportJobResponse extends AnalyticsApiResponse { + estimated_completion: string; + queue_position?: number; +} \ No newline at end of file diff --git a/frontend/src/components/domain/auth/LoginForm.tsx b/frontend/src/components/domain/auth/LoginForm.tsx new file mode 100644 index 00000000..b84e8c3a --- /dev/null +++ b/frontend/src/components/domain/auth/LoginForm.tsx @@ -0,0 +1,319 @@ +import React, { useState, useRef, useEffect } from 'react'; +import { Button, Input, Card } from '../../ui'; +import { useAuth } from '../../../hooks/api/useAuth'; +import { UserLogin } from '../../../types/auth.types'; +import { useToast } from '../../../hooks/ui/useToast'; + +interface LoginFormProps { + onSuccess?: () => void; + onRegisterClick?: () => void; + onForgotPasswordClick?: () => void; + className?: string; + autoFocus?: boolean; +} + +interface ExtendedUserLogin extends UserLogin { + remember_me: boolean; +} + +export const LoginForm: React.FC = ({ + onSuccess, + onRegisterClick, + onForgotPasswordClick, + className, + autoFocus = true +}) => { + const [credentials, setCredentials] = useState({ + email: '', + password: '', + remember_me: false + }); + const [errors, setErrors] = useState>({}); + const [showPassword, setShowPassword] = useState(false); + const emailInputRef = useRef(null); + + const { login, isLoading, error } = useAuth(); + const { showToast } = useToast(); + + // Auto-focus on email field when component mounts + useEffect(() => { + if (autoFocus && emailInputRef.current) { + emailInputRef.current.focus(); + } + }, [autoFocus]); + + const validateForm = (): boolean => { + const newErrors: Partial = {}; + + if (!credentials.email.trim()) { + newErrors.email = 'El email es requerido'; + } else if (!/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(credentials.email)) { + newErrors.email = 'Por favor, ingrese un email válido'; + } + + if (!credentials.password) { + newErrors.password = 'La contraseña es requerida'; + } else if (credentials.password.length < 6) { + newErrors.password = 'La contraseña debe tener al menos 6 caracteres'; + } + + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + if (!validateForm()) { + // Focus on first error field + const firstErrorField = Object.keys(errors)[0]; + if (firstErrorField === 'email' && emailInputRef.current) { + emailInputRef.current.focus(); + } + return; + } + + try { + const loginData: UserLogin = { + email: credentials.email, + password: credentials.password, + remember_me: credentials.remember_me + }; + + const success = await login(loginData); + if (success) { + showToast({ + type: 'success', + title: 'Sesión iniciada correctamente', + message: '¡Bienvenido de vuelta a tu panadería!' + }); + onSuccess?.(); + } else { + showToast({ + type: 'error', + title: 'Error al iniciar sesión', + message: error || 'Email o contraseña incorrectos. Verifica tus credenciales.' + }); + } + } catch (err) { + showToast({ + type: 'error', + title: 'Error de conexión', + message: 'No se pudo conectar con el servidor. Verifica tu conexión a internet.' + }); + } + }; + + const handleInputChange = (field: keyof ExtendedUserLogin) => (value: string | boolean) => { + setCredentials(prev => ({ ...prev, [field]: value })); + if (errors[field]) { + setErrors(prev => ({ ...prev, [field]: undefined })); + } + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter' && !isLoading) { + handleSubmit(e as any); + } + }; + + return ( + +
+

+ Iniciar Sesión +

+

+ Accede al panel de control de tu panadería +

+
+ +
+
+ + + + } + /> + {errors.email && ( + + )} +
+ +
+ + + + } + rightIcon={ + + } + /> + {errors.password && ( + + )} +
+ +
+ + + {onForgotPasswordClick && ( + + )} +
+ +
+ Al activar esta opción, tu sesión permanecerá activa durante más tiempo +
+ + {error && ( +
+ + {error} +
+ )} + + + +
+ Presiona Enter o haz clic para iniciar sesión con tus credenciales +
+ + + {onRegisterClick && ( +
+

+ ¿Aún no tienes una cuenta para tu panadería? +

+ +
+ )} +
+ ); +}; + +export default LoginForm; \ No newline at end of file diff --git a/frontend/src/components/domain/auth/PasswordResetForm.tsx b/frontend/src/components/domain/auth/PasswordResetForm.tsx new file mode 100644 index 00000000..902c2111 --- /dev/null +++ b/frontend/src/components/domain/auth/PasswordResetForm.tsx @@ -0,0 +1,606 @@ +import React, { useState, useEffect, useRef } from 'react'; +import { Button, Input, Card } from '../../ui'; +import { useAuth } from '../../../hooks/api/useAuth'; +import { useToast } from '../../../hooks/ui/useToast'; + +interface PasswordResetFormProps { + token?: string; + onSuccess?: () => void; + onBackClick?: () => void; + className?: string; + autoFocus?: boolean; + mode?: 'request' | 'reset'; +} + +export const PasswordResetForm: React.FC = ({ + token, + onSuccess, + onBackClick, + className, + autoFocus = true, + mode +}) => { + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [confirmPassword, setConfirmPassword] = useState(''); + const [errors, setErrors] = useState>({}); + const [showPassword, setShowPassword] = useState(false); + const [showConfirmPassword, setShowConfirmPassword] = useState(false); + const [isEmailSent, setIsEmailSent] = useState(false); + const [passwordStrength, setPasswordStrength] = useState(0); + const [isTokenValid, setIsTokenValid] = useState(null); + + const emailInputRef = useRef(null); + const passwordInputRef = useRef(null); + + const { requestPasswordReset, resetPassword, isLoading, error } = useAuth(); + const { showToast } = useToast(); + + const isResetMode = Boolean(token) || mode === 'reset'; + + // Auto-focus on appropriate field when component mounts + useEffect(() => { + if (autoFocus) { + if (isResetMode && passwordInputRef.current) { + passwordInputRef.current.focus(); + } else if (!isResetMode && emailInputRef.current) { + emailInputRef.current.focus(); + } + } + }, [autoFocus, isResetMode]); + + // Token validation effect + useEffect(() => { + if (token) { + // Simple token format validation (you might want to add actual API validation) + const isValidFormat = /^[A-Za-z0-9+/=_-]+$/.test(token) && token.length > 20; + setIsTokenValid(isValidFormat); + + if (!isValidFormat) { + showToast({ + type: 'error', + title: 'Token inválido', + message: 'El enlace de restablecimiento no es válido o ha expirado' + }); + } + } + }, [token, showToast]); + + // Calculate password strength + const calculatePasswordStrength = (password: string): number => { + let strength = 0; + if (password.length >= 8) strength += 25; + if (/[a-z]/.test(password)) strength += 25; + if (/[A-Z]/.test(password)) strength += 25; + if (/[0-9]/.test(password)) strength += 15; + if (/[^A-Za-z0-9]/.test(password)) strength += 10; + return Math.min(strength, 100); + }; + + const getPasswordStrengthLabel = (strength: number): string => { + if (strength < 25) return 'Muy débil'; + if (strength < 50) return 'Débil'; + if (strength < 75) return 'Media'; + return 'Fuerte'; + }; + + const getPasswordStrengthColor = (strength: number): string => { + if (strength < 25) return 'bg-color-error'; + if (strength < 50) return 'bg-yellow-500'; + if (strength < 75) return 'bg-blue-500'; + return 'bg-color-success'; + }; + + const validateEmail = (): boolean => { + const newErrors: Record = {}; + + if (!email.trim()) { + newErrors.email = 'El email es requerido'; + } else if (!/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(email)) { + newErrors.email = 'Por favor, ingrese un email válido'; + } + + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }; + + const validatePasswords = (): boolean => { + const newErrors: Record = {}; + + if (!password) { + newErrors.password = 'La contraseña es requerida'; + } else if (password.length < 8) { + newErrors.password = 'La contraseña debe tener al menos 8 caracteres'; + } else if (!/(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/.test(password)) { + newErrors.password = 'La contraseña debe contener mayúsculas, minúsculas y números'; + } else if (passwordStrength < 50) { + newErrors.password = 'La contraseña es demasiado débil. Intenta con una más segura'; + } + + if (!confirmPassword) { + newErrors.confirmPassword = 'Confirma tu nueva contraseña'; + } else if (password !== confirmPassword) { + newErrors.confirmPassword = 'Las contraseñas no coinciden'; + } + + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }; + + // Handle password change to update strength + const handlePasswordChange = (value: string) => { + setPassword(value); + setPasswordStrength(calculatePasswordStrength(value)); + clearError('password'); + }; + + const handleRequestReset = async (e: React.FormEvent) => { + e.preventDefault(); + + if (!validateEmail()) { + // Focus on email field if validation fails + if (emailInputRef.current) { + emailInputRef.current.focus(); + } + return; + } + + try { + const success = await requestPasswordReset(email); + if (success) { + setIsEmailSent(true); + showToast({ + type: 'success', + title: 'Email enviado correctamente', + message: 'Te hemos enviado las instrucciones para restablecer tu contraseña' + }); + } else { + showToast({ + type: 'error', + title: 'Error al enviar email', + message: error || 'No se pudo enviar el email. Verifica que la dirección sea correcta.' + }); + } + } catch (err) { + showToast({ + type: 'error', + title: 'Error de conexión', + message: 'No se pudo conectar con el servidor. Verifica tu conexión a internet.' + }); + } + }; + + const handleResetPassword = async (e: React.FormEvent) => { + e.preventDefault(); + + if (!validatePasswords()) { + // Focus on password field if validation fails + if (passwordInputRef.current) { + passwordInputRef.current.focus(); + } + return; + } + + if (!token || isTokenValid === false) { + showToast({ + type: 'error', + title: 'Token inválido', + message: 'El enlace de restablecimiento no es válido. Solicita uno nuevo.' + }); + return; + } + + try { + const success = await resetPassword(token, password); + if (success) { + showToast({ + type: 'success', + title: 'Contraseña actualizada', + message: '¡Tu contraseña ha sido restablecida exitosamente! Ya puedes iniciar sesión.' + }); + onSuccess?.(); + } else { + showToast({ + type: 'error', + title: 'Error al restablecer contraseña', + message: error || 'El enlace ha expirado o no es válido. Solicita un nuevo restablecimiento.' + }); + } + } catch (err) { + showToast({ + type: 'error', + title: 'Error de conexión', + message: 'No se pudo conectar con el servidor. Verifica tu conexión a internet.' + }); + } + }; + + const clearError = (field: string) => { + if (errors[field]) { + setErrors(prev => ({ ...prev, [field]: undefined })); + } + }; + + // Handle Enter key submission + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter' && !isLoading) { + if (isResetMode) { + handleResetPassword(e as any); + } else { + handleRequestReset(e as any); + } + } + }; + + // Success screen for email sent + if (isEmailSent && !isResetMode) { + return ( + +
+
+ +
+

+ ¡Email enviado correctamente! +

+
+

+ Hemos enviado las instrucciones de restablecimiento a: +

+

+ {email} +

+

+ Revisa tu bandeja de entrada (y la carpeta de spam). El enlace es válido por 24 horas. +

+
+
+ +
+ + + {onBackClick && ( + + )} +
+
+ ); + } + + // Show token validation error + if (isResetMode && isTokenValid === false) { + return ( + +
+
+ +
+

+ Enlace No Válido +

+

+ El enlace de restablecimiento no es válido o ha expirado. + Solicita un nuevo enlace para restablecer tu contraseña. +

+
+ + {onBackClick && ( + + )} +
+ ); + } + + return ( + +
+

+ {isResetMode ? 'Crear Nueva Contraseña' : 'Restablecer Contraseña'} +

+

+ {isResetMode + ? 'Crea una contraseña segura para acceder a tu cuenta' + : 'Te enviaremos un enlace seguro para restablecer tu contraseña' + } +

+
+ +
+ {!isResetMode && ( +
+ { + setEmail(value); + clearError('email'); + }} + error={errors.email} + disabled={isLoading} + autoComplete="email" + required + aria-describedby={errors.email ? 'email-error' : undefined} + leftIcon={ + + } + /> + {errors.email && ( + + )} +
+ )} + + {isResetMode && ( + <> +
+ + + + } + rightIcon={ + + } + /> + + {/* Password Strength Indicator */} + {password && ( +
+
+ Seguridad: + + {getPasswordStrengthLabel(passwordStrength)} + +
+
+
+
+
+ )} + + {errors.password && ( + + )} +
+ +
+ { + setConfirmPassword(value); + clearError('confirmPassword'); + }} + error={errors.confirmPassword} + disabled={isLoading} + autoComplete="new-password" + required + aria-describedby={errors.confirmPassword ? 'confirm-password-error' : undefined} + leftIcon={ + + } + rightIcon={ + + } + /> + {errors.confirmPassword && ( + + )} +
+ + )} + + {error && ( +
+ + {error} +
+ )} + + + +
+ {isResetMode + ? 'Presiona Enter o haz clic para actualizar tu contraseña' + : 'Presiona Enter o haz clic para enviar el enlace de restablecimiento' + } +
+ + + {onBackClick && ( +
+ +
+ )} + + ); +}; + +export default PasswordResetForm; \ No newline at end of file diff --git a/frontend/src/components/domain/auth/ProfileSettings.tsx b/frontend/src/components/domain/auth/ProfileSettings.tsx new file mode 100644 index 00000000..04087487 --- /dev/null +++ b/frontend/src/components/domain/auth/ProfileSettings.tsx @@ -0,0 +1,704 @@ +import React, { useState, useEffect, useRef } from 'react'; +import { Button, Input, Card, Select, Avatar, Modal } from '../../ui'; +import { useAuth } from '../../../hooks/api/useAuth'; +import { User } from '../../../types/auth.types'; +import { useToast } from '../../../hooks/ui/useToast'; + +interface ProfileSettingsProps { + onSuccess?: () => void; + className?: string; + initialTab?: 'profile' | 'security' | 'preferences' | 'notifications'; +} + +interface ProfileFormData { + full_name: string; + email: string; + phone: string; + language: string; + timezone: string; + avatar_url?: string; +} + +interface BakeryFormData { + bakery_name: string; + bakery_type: string; + address: string; + city: string; + postal_code: string; + country: string; + website?: string; + description?: string; +} + +interface NotificationSettings { + email_notifications: boolean; + order_notifications: boolean; + marketing_notifications: boolean; + security_notifications: boolean; + low_stock_alerts: boolean; + production_reminders: boolean; +} + +interface PasswordChangeData { + currentPassword: string; + newPassword: string; + confirmNewPassword: string; +} + +export const ProfileSettings: React.FC = ({ + onSuccess, + className, + initialTab = 'profile' +}) => { + const { user, updateProfile, isLoading, error } = useAuth(); + const { showToast } = useToast(); + const fileInputRef = useRef(null); + + const [activeTab, setActiveTab] = useState<'profile' | 'security' | 'preferences' | 'notifications'>(initialTab); + const [profileData, setProfileData] = useState({ + full_name: '', + email: '', + phone: '', + language: 'es', + timezone: 'Europe/Madrid', + avatar_url: '' + }); + + const [bakeryData, setBakeryData] = useState({ + bakery_name: '', + bakery_type: 'traditional', + address: '', + city: '', + postal_code: '', + country: 'España', + website: '', + description: '' + }); + + const [notificationSettings, setNotificationSettings] = useState({ + email_notifications: true, + order_notifications: true, + marketing_notifications: false, + security_notifications: true, + low_stock_alerts: true, + production_reminders: true + }); + + const [passwordData, setPasswordData] = useState({ + currentPassword: '', + newPassword: '', + confirmNewPassword: '' + }); + + const [errors, setErrors] = useState>({}); + const [showPasswords, setShowPasswords] = useState({ + current: false, + new: false, + confirm: false + }); + const [hasChanges, setHasChanges] = useState({ + profile: false, + bakery: false, + notifications: false + }); + const [uploadingImage, setUploadingImage] = useState(false); + const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); + const [previewImage, setPreviewImage] = useState(null); + + const bakeryTypeOptions = [ + { value: 'traditional', label: 'Panadería Tradicional' }, + { value: 'artisan', label: 'Panadería Artesanal' }, + { value: 'industrial', label: 'Panadería Industrial' }, + { value: 'confectionery', label: 'Pastelería' }, + { value: 'bakery_cafe', label: 'Panadería Cafetería' }, + { value: 'specialty', label: 'Panadería Especializada' }, + { value: 'patisserie', label: 'Pastelería Francesa' }, + { value: 'organic', label: 'Panadería Orgánica' }, + { value: 'gluten_free', label: 'Panadería Sin Gluten' } + ]; + + const languageOptions = [ + { value: 'es', label: 'Español' }, + { value: 'ca', label: 'Català' }, + { value: 'eu', label: 'Euskera' }, + { value: 'gl', label: 'Galego' }, + { value: 'en', label: 'English' } + ]; + + const timezoneOptions = [ + { value: 'Europe/Madrid', label: 'Madrid (CET/CEST)' }, + { value: 'Atlantic/Canary', label: 'Canarias (WET/WEST)' }, + { value: 'Europe/London', label: 'Londres (GMT/BST)' }, + { value: 'Europe/Paris', label: 'París (CET/CEST)' }, + { value: 'Europe/Rome', label: 'Roma (CET/CEST)' } + ]; + + // Initialize form data with user data + useEffect(() => { + if (user) { + setProfileData({ + full_name: user.full_name || '', + email: user.email || '', + phone: user.phone || '', + language: user.language || 'es', + timezone: user.timezone || 'Europe/Madrid', + avatar_url: user.avatar_url || '' + }); + + // Initialize bakery data (would come from a separate bakery API) + setBakeryData({ + bakery_name: (user as any).bakery_name || '', + bakery_type: (user as any).bakery_type || 'traditional', + address: (user as any).address || '', + city: (user as any).city || '', + postal_code: (user as any).postal_code || '', + country: (user as any).country || 'España', + website: (user as any).website || '', + description: (user as any).description || '' + }); + } + }, [user]); + + // Profile picture upload handler + const handleImageUpload = async (event: React.ChangeEvent) => { + const file = event.target.files?.[0]; + if (!file) return; + + // Validate file type + if (!file.type.startsWith('image/')) { + showToast({ + type: 'error', + title: 'Archivo inválido', + message: 'Por favor, selecciona una imagen válida' + }); + return; + } + + // Validate file size (max 5MB) + if (file.size > 5 * 1024 * 1024) { + showToast({ + type: 'error', + title: 'Archivo muy grande', + message: 'La imagen debe ser menor a 5MB' + }); + return; + } + + setUploadingImage(true); + try { + // Create preview + const reader = new FileReader(); + reader.onload = (e) => { + setPreviewImage(e.target?.result as string); + }; + reader.readAsDataURL(file); + + // Here you would upload to your image service + // For now, we'll simulate the upload + await new Promise(resolve => setTimeout(resolve, 2000)); + + const newImageUrl = URL.createObjectURL(file); // Temporary URL + setProfileData(prev => ({ ...prev, avatar_url: newImageUrl })); + setHasChanges(prev => ({ ...prev, profile: true })); + + showToast({ + type: 'success', + title: 'Imagen subida', + message: 'Tu foto de perfil ha sido actualizada' + }); + } catch (error) { + showToast({ + type: 'error', + title: 'Error al subir imagen', + message: 'No se pudo subir la imagen. Intenta de nuevo.' + }); + } finally { + setUploadingImage(false); + setPreviewImage(null); + } + }; + + const handleRemoveImage = () => { + setProfileData(prev => ({ ...prev, avatar_url: '' })); + setHasChanges(prev => ({ ...prev, profile: true })); + setShowDeleteConfirm(false); + }; + + const validateProfileForm = (): boolean => { + const newErrors: Record = {}; + + if (!profileData.full_name.trim()) { + newErrors.full_name = 'El nombre completo es requerido'; + } else if (profileData.full_name.trim().length < 2) { + newErrors.full_name = 'El nombre debe tener al menos 2 caracteres'; + } + + if (!profileData.email.trim()) { + newErrors.email = 'El email es requerido'; + } else if (!/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(profileData.email)) { + newErrors.email = 'Por favor, ingrese un email válido'; + } + + if (profileData.phone && !/^(\+34|0034|34)?[6-9][0-9]{8}$/.test(profileData.phone.replace(/\s/g, ''))) { + newErrors.phone = 'Por favor, ingrese un teléfono español válido (ej: +34 600 000 000)'; + } + + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }; + + const validateBakeryForm = (): boolean => { + const newErrors: Record = {}; + + if (!bakeryData.bakery_name.trim()) { + newErrors.bakery_name = 'El nombre de la panadería es requerido'; + } + + if (!bakeryData.address.trim()) { + newErrors.address = 'La dirección es requerida'; + } + + if (!bakeryData.city.trim()) { + newErrors.city = 'La ciudad es requerida'; + } + + if (bakeryData.postal_code && !/^[0-5][0-9]{4}$/.test(bakeryData.postal_code)) { + newErrors.postal_code = 'Por favor, ingrese un código postal español válido'; + } + + if (bakeryData.website && !/^https?:\/\/.+\..+/.test(bakeryData.website)) { + newErrors.website = 'Por favor, ingrese una URL válida (ej: https://mipanaderia.com)'; + } + + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }; + + const validatePasswordForm = (): boolean => { + const newErrors: Record = {}; + + if (!passwordData.currentPassword) { + newErrors.currentPassword = 'La contraseña actual es requerida'; + } + + if (!passwordData.newPassword) { + newErrors.newPassword = 'La nueva contraseña es requerida'; + } else if (passwordData.newPassword.length < 8) { + newErrors.newPassword = 'La contraseña debe tener al menos 8 caracteres'; + } else if (!/(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/.test(passwordData.newPassword)) { + newErrors.newPassword = 'La contraseña debe contener mayúsculas, minúsculas y números'; + } + + if (!passwordData.confirmNewPassword) { + newErrors.confirmNewPassword = 'Confirma tu nueva contraseña'; + } else if (passwordData.newPassword !== passwordData.confirmNewPassword) { + newErrors.confirmNewPassword = 'Las contraseñas no coinciden'; + } + + if (passwordData.currentPassword === passwordData.newPassword) { + newErrors.newPassword = 'La nueva contraseña debe ser diferente a la actual'; + } + + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }; + + const handleProfileSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + if (!validateProfileForm()) { + return; + } + + try { + const success = await updateProfile(profileData); + if (success) { + setHasChanges(false); + showToast({ + type: 'success', + title: 'Perfil actualizado', + message: 'Tu información ha sido guardada correctamente' + }); + onSuccess?.(); + } else { + showToast({ + type: 'error', + title: 'Error al actualizar', + message: error || 'No se pudo actualizar tu perfil' + }); + } + } catch (err) { + showToast({ + type: 'error', + title: 'Error de conexión', + message: 'No se pudo conectar con el servidor' + }); + } + }; + + const handlePasswordSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + if (!validatePasswordForm()) { + return; + } + + // Note: This would typically call a separate password change endpoint + // For now, we'll show a placeholder message + showToast({ + type: 'success', + title: 'Contraseña actualizada', + message: 'Tu contraseña ha sido cambiada correctamente' + }); + + setPasswordData({ + currentPassword: '', + newPassword: '', + confirmNewPassword: '' + }); + }; + + const handleProfileInputChange = (field: keyof ProfileFormData) => (value: string) => { + setProfileData(prev => ({ ...prev, [field]: value })); + setHasChanges(true); + if (errors[field]) { + setErrors(prev => ({ ...prev, [field]: undefined })); + } + }; + + const handlePasswordInputChange = (field: keyof PasswordChangeData) => (value: string) => { + setPasswordData(prev => ({ ...prev, [field]: value })); + if (errors[field]) { + setErrors(prev => ({ ...prev, [field]: undefined })); + } + }; + + const togglePasswordVisibility = (field: 'current' | 'new' | 'confirm') => { + setShowPasswords(prev => ({ + ...prev, + [field]: !prev[field] + })); + }; + + const tabs = [ + { id: 'profile' as const, label: 'Perfil', icon: 'M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z' }, + { id: 'security' as const, label: 'Seguridad', icon: 'M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z' }, + { id: 'preferences' as const, label: 'Preferencias', icon: 'M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z' } + ]; + + if (!user) { + return ( + +
+ Cargando información del usuario... +
+
+ ); + } + + return ( +
+ {/* Header */} + +
+ +
+

+ {user.first_name} {user.last_name} +

+

{user.email}

+

{user.bakery_name}

+
+
+
+ + {/* Tabs */} + +
+ +
+ +
+ {activeTab === 'profile' && ( +
+
+
+

+ Información Personal +

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

+ Información del Negocio +

+ + + + + +
+ + + +
+
+
+ +
+ + +
+ + )} + + {activeTab === 'security' && ( +
+

+ Cambiar Contraseña +

+ + togglePasswordVisibility('current')} + className="text-text-secondary hover:text-text-primary" + > + {showPasswords.current ? ( + + + + ) : ( + + + + + )} + + } + /> + + togglePasswordVisibility('new')} + className="text-text-secondary hover:text-text-primary" + > + {showPasswords.new ? ( + + + + ) : ( + + + + + )} + + } + /> + + togglePasswordVisibility('confirm')} + className="text-text-secondary hover:text-text-primary" + > + {showPasswords.confirm ? ( + + + + ) : ( + + + + + )} + + } + /> + + + + )} + + {activeTab === 'preferences' && ( +
+

+ Preferencias +

+ +
+

Configuraciones de preferencias estarán disponibles próximamente.

+
+

• Configuración de idioma

+

• Zona horaria

+

• Formato de fecha y hora

+

• Notificaciones

+

• Tema de la aplicación

+
+
+
+ )} +
+
+
+ ); +}; + +export default ProfileSettings; \ No newline at end of file diff --git a/frontend/src/components/domain/auth/RegisterForm.tsx b/frontend/src/components/domain/auth/RegisterForm.tsx new file mode 100644 index 00000000..42573556 --- /dev/null +++ b/frontend/src/components/domain/auth/RegisterForm.tsx @@ -0,0 +1,805 @@ +import React, { useState, useEffect } from 'react'; +import { Button, Input, Card, Select } from '../../ui'; +import { useAuth } from '../../../hooks/api/useAuth'; +import { UserRegistration } from '../../../types/auth.types'; +import { useToast } from '../../../hooks/ui/useToast'; + +interface RegisterFormProps { + onSuccess?: () => void; + onLoginClick?: () => void; + className?: string; + showProgressSteps?: boolean; +} + +type RegistrationStep = 'personal' | 'bakery' | 'security' | 'verification'; + +interface ExtendedUserRegistration { + // Personal Information + full_name: string; + email: string; + phone: string; + + // Bakery Information + tenant_name: string; + bakery_type: string; + address: string; + city: string; + postal_code: string; + country: string; + + // Security + password: string; + confirmPassword: string; + + // Terms + acceptTerms: boolean; + acceptPrivacy: boolean; + acceptMarketing: boolean; +} + +export const RegisterForm: React.FC = ({ + onSuccess, + onLoginClick, + className, + showProgressSteps = true +}) => { + const [currentStep, setCurrentStep] = useState('personal'); + const [completedSteps, setCompletedSteps] = useState>(new Set()); + const [isEmailVerificationSent, setIsEmailVerificationSent] = useState(false); + + const [formData, setFormData] = useState({ + full_name: '', + email: '', + phone: '', + tenant_name: '', + bakery_type: 'traditional', + address: '', + city: '', + postal_code: '', + country: 'España', + password: '', + confirmPassword: '', + acceptTerms: false, + acceptPrivacy: false, + acceptMarketing: false + }); + + const [errors, setErrors] = useState>({}); + const [showPassword, setShowPassword] = useState(false); + const [showConfirmPassword, setShowConfirmPassword] = useState(false); + + const { register, verifyEmail, isLoading, error } = useAuth(); + const { showToast } = useToast(); + + const steps: { id: RegistrationStep; title: string; description: string }[] = [ + { id: 'personal', title: 'Información Personal', description: 'Tus datos básicos' }, + { id: 'bakery', title: 'Tu Panadería', description: 'Detalles del negocio' }, + { id: 'security', title: 'Seguridad', description: 'Contraseña y términos' }, + { id: 'verification', title: 'Verificación', description: 'Confirmar email' } + ]; + + const bakeryTypeOptions = [ + { value: 'traditional', label: 'Panadería Tradicional' }, + { value: 'artisan', label: 'Panadería Artesanal' }, + { value: 'industrial', label: 'Panadería Industrial' }, + { value: 'confectionery', label: 'Pastelería' }, + { value: 'bakery_cafe', label: 'Panadería Cafetería' }, + { value: 'specialty', label: 'Panadería Especializada' }, + { value: 'patisserie', label: 'Pastelería Francesa' }, + { value: 'organic', label: 'Panadería Orgánica' } + ]; + + const validatePersonalStep = (): boolean => { + const newErrors: Partial = {}; + + if (!formData.full_name.trim()) { + newErrors.full_name = 'El nombre completo es requerido'; + } else if (formData.full_name.trim().length < 2) { + newErrors.full_name = 'El nombre debe tener al menos 2 caracteres'; + } + + if (!formData.email.trim()) { + newErrors.email = 'El email es requerido'; + } else if (!/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(formData.email)) { + newErrors.email = 'Por favor, ingrese un email válido'; + } + + if (!formData.phone.trim()) { + newErrors.phone = 'El teléfono es requerido'; + } else if (!/^(\+34|0034|34)?[6-9][0-9]{8}$/.test(formData.phone.replace(/\s/g, ''))) { + newErrors.phone = 'Por favor, ingrese un teléfono español válido (ej: +34 600 000 000)'; + } + + setErrors(prev => ({ ...prev, ...newErrors })); + return Object.keys(newErrors).length === 0; + }; + + const validateBakeryStep = (): boolean => { + const newErrors: Partial = {}; + + if (!formData.tenant_name.trim()) { + newErrors.tenant_name = 'El nombre de la panadería es requerido'; + } else if (formData.tenant_name.trim().length < 2) { + newErrors.tenant_name = 'El nombre debe tener al menos 2 caracteres'; + } + + if (!formData.address.trim()) { + newErrors.address = 'La dirección es requerida'; + } + + if (!formData.city.trim()) { + newErrors.city = 'La ciudad es requerida'; + } + + if (!formData.postal_code.trim()) { + newErrors.postal_code = 'El código postal es requerido'; + } else if (!/^[0-5][0-9]{4}$/.test(formData.postal_code)) { + newErrors.postal_code = 'Por favor, ingrese un código postal español válido (ej: 28001)'; + } + + setErrors(prev => ({ ...prev, ...newErrors })); + return Object.keys(newErrors).length === 0; + }; + + const validateSecurityStep = (): boolean => { + const newErrors: Partial = {}; + + if (!formData.password) { + newErrors.password = 'La contraseña es requerida'; + } else if (formData.password.length < 8) { + newErrors.password = 'La contraseña debe tener al menos 8 caracteres'; + } else if (!/(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])?/.test(formData.password)) { + newErrors.password = 'La contraseña debe contener mayúsculas, minúsculas y números'; + } + + if (!formData.confirmPassword) { + newErrors.confirmPassword = 'Confirma tu contraseña'; + } else if (formData.password !== formData.confirmPassword) { + newErrors.confirmPassword = 'Las contraseñas no coinciden'; + } + + if (!formData.acceptTerms) { + newErrors.acceptTerms = 'Debes aceptar los términos y condiciones'; + } + + if (!formData.acceptPrivacy) { + newErrors.acceptPrivacy = 'Debes aceptar la política de privacidad'; + } + + setErrors(prev => ({ ...prev, ...newErrors })); + return Object.keys(newErrors).length === 0; + }; + + const handleNextStep = (e?: React.FormEvent) => { + if (e) e.preventDefault(); + + let isValid = false; + switch (currentStep) { + case 'personal': + isValid = validatePersonalStep(); + break; + case 'bakery': + isValid = validateBakeryStep(); + break; + case 'security': + isValid = validateSecurityStep(); + break; + default: + isValid = true; + } + + if (isValid) { + setCompletedSteps(prev => new Set([...prev, currentStep])); + + const stepOrder: RegistrationStep[] = ['personal', 'bakery', 'security', 'verification']; + const currentIndex = stepOrder.indexOf(currentStep); + + if (currentIndex < stepOrder.length - 1) { + setCurrentStep(stepOrder[currentIndex + 1]); + setErrors({}); // Clear errors when moving to next step + } + + // If this is the security step, submit the registration + if (currentStep === 'security') { + handleSubmitRegistration(); + } + } + }; + + const handlePreviousStep = () => { + const stepOrder: RegistrationStep[] = ['personal', 'bakery', 'security', 'verification']; + const currentIndex = stepOrder.indexOf(currentStep); + + if (currentIndex > 0) { + setCurrentStep(stepOrder[currentIndex - 1]); + setErrors({}); // Clear errors when going back + } + }; + + const handleSubmitRegistration = async () => { + try { + const registrationData: UserRegistration = { + full_name: formData.full_name, + email: formData.email, + password: formData.password, + tenant_name: formData.tenant_name, + phone: formData.phone + }; + + const success = await register(registrationData); + + if (success) { + setIsEmailVerificationSent(true); + setCurrentStep('verification'); + showToast({ + type: 'success', + title: 'Cuenta creada exitosamente', + message: 'Revisa tu email para verificar tu cuenta y completar el registro' + }); + } else { + showToast({ + type: 'error', + title: 'Error al crear la cuenta', + message: error || 'No se pudo crear la cuenta. Verifica que el email no esté en uso.' + }); + setCurrentStep('personal'); // Go back to first step to fix issues + } + } catch (err) { + showToast({ + type: 'error', + title: 'Error de conexión', + message: 'No se pudo conectar con el servidor. Verifica tu conexión a internet.' + }); + setCurrentStep('personal'); + } + }; + + const handleInputChange = (field: keyof ExtendedUserRegistration) => (value: string | boolean) => { + setFormData(prev => ({ ...prev, [field]: value })); + if (errors[field]) { + setErrors(prev => ({ ...prev, [field]: undefined })); + } + }; + + const getCurrentStepIndex = () => { + const stepOrder: RegistrationStep[] = ['personal', 'bakery', 'security', 'verification']; + return stepOrder.indexOf(currentStep); + }; + + const getProgressPercentage = () => { + return ((getCurrentStepIndex()) / (steps.length - 1)) * 100; + }; + + // Email verification success handler + const handleEmailVerification = async (token: string) => { + try { + const success = await verifyEmail(token); + if (success) { + showToast({ + type: 'success', + title: 'Email verificado exitosamente', + message: '¡Tu cuenta ha sido activada! Ya puedes iniciar sesión.' + }); + onSuccess?.(); + } else { + showToast({ + type: 'error', + title: 'Error de verificación', + message: 'El enlace de verificación no es válido o ha expirado' + }); + } + } catch (err) { + showToast({ + type: 'error', + title: 'Error de conexión', + message: 'No se pudo verificar el email. Intenta más tarde.' + }); + } + }; + + return ( + +
+

+ Registra tu Panadería +

+

+ Crea tu cuenta y comienza a digitalizar tu negocio +

+
+ + {/* Progress Indicator */} + {showProgressSteps && ( +
+
+ {steps.map((step, index) => ( +
+
+ {completedSteps.has(step.id) ? ( + + + + ) : ( + index + 1 + )} +
+
+
+ {step.title} +
+
+ {step.description} +
+
+ {index < steps.length - 1 && ( +
+ )} +
+ ))} +
+ + {/* Progress Bar */} +
+
+
+
+ )} + + {/* Step Content */} + {currentStep !== 'verification' ? ( +
+ {/* Personal Information Step */} + {currentStep === 'personal' && ( +
+
+

+ Información Personal +

+

+ Cuéntanos sobre ti para personalizar tu experiencia +

+
+ +
+ + + + } + /> + + + + + } + /> + + + + + } + /> +
+
+ )} + {/* Bakery Information Step */} + {currentStep === 'bakery' && ( +
+
+

+ Información de tu Panadería +

+

+ Déjanos conocer los detalles de tu negocio +

+
+ +
+ + + + } + /> + + + + + + } + /> + +
+ + + + + +
+
+ )} + + {/* Security Step */} + {currentStep === 'security' && ( +
+
+

+ Seguridad y Términos +

+

+ Crea una contraseña segura y acepta nuestros términos +

+
+ +
+ + + + } + rightIcon={ + + } + /> + + + + + } + rightIcon={ + + } + /> +
+ +
+
+ handleInputChange('acceptTerms')(e.target.checked)} + className="mt-1 h-4 w-4 rounded border-border-primary text-color-primary focus:ring-color-primary focus:ring-offset-0" + disabled={isLoading} + /> + +
+ {errors.acceptTerms && ( +

{errors.acceptTerms}

+ )} + +
+ handleInputChange('acceptPrivacy')(e.target.checked)} + className="mt-1 h-4 w-4 rounded border-border-primary text-color-primary focus:ring-color-primary focus:ring-offset-0" + disabled={isLoading} + /> + +
+ {errors.acceptPrivacy && ( +

{errors.acceptPrivacy}

+ )} + +
+ handleInputChange('acceptMarketing')(e.target.checked)} + className="mt-1 h-4 w-4 rounded border-border-primary text-color-primary focus:ring-color-primary focus:ring-offset-0" + disabled={isLoading} + /> + +
+
+
+ )} + + {/* Navigation Buttons */} +
+ {currentStep !== 'personal' && ( + + )} + +
+ + +
+ + {error && ( +
+ + + + {error} +
+ )} + + ) : ( + /* Verification Step */ +
+
+ + + +
+ +

+ ¡Cuenta creada exitosamente! +

+ +
+

+ Hemos enviado un enlace de verificación a: +

+

+ {formData.email} +

+

+ Revisa tu bandeja de entrada (y la carpeta de spam) y haz clic en el enlace para activar tu cuenta. +

+
+ +
+ +
+
+ )} + + {/* Login Link */} + {onLoginClick && currentStep !== 'verification' && ( +
+

+ ¿Ya tienes una cuenta? +

+ +
+ )} + + ); +}; + +export default RegisterForm; \ No newline at end of file diff --git a/frontend/src/components/domain/auth/index.ts b/frontend/src/components/domain/auth/index.ts new file mode 100644 index 00000000..7ed34712 --- /dev/null +++ b/frontend/src/components/domain/auth/index.ts @@ -0,0 +1,79 @@ +// Authentication domain components +export { default as LoginForm } from './LoginForm'; +export { default as RegisterForm } from './RegisterForm'; +export { default as PasswordResetForm } from './PasswordResetForm'; +export { default as ProfileSettings } from './ProfileSettings'; + +// Export named exports as well +export { LoginForm } from './LoginForm'; +export { RegisterForm } from './RegisterForm'; +export { PasswordResetForm } from './PasswordResetForm'; +export { ProfileSettings } from './ProfileSettings'; + +// Re-export types for convenience +export type { + LoginFormProps, + RegisterFormProps, + PasswordResetFormProps, + ProfileSettingsProps +} from './types'; + +// Component metadata for documentation +export const authComponents = { + LoginForm: { + name: 'LoginForm', + description: 'Comprehensive login form with email/password validation, remember me, auto-focus, and accessibility features', + features: [ + 'Email and password validation', + 'Remember me functionality', + 'Auto-focus on email field', + 'Enter key submission', + 'Loading states and error handling', + 'Accessibility (ARIA attributes)', + 'Spanish localization', + 'Mobile responsive design' + ] + }, + RegisterForm: { + name: 'RegisterForm', + description: 'Multi-step registration form for bakery owners with progress indicator and comprehensive validation', + features: [ + 'Multi-step registration (personal, bakery, security, verification)', + 'Progress indicator with step completion', + 'Spanish phone and postal code validation', + 'Comprehensive bakery-specific fields', + 'Terms and privacy acceptance', + 'Email verification workflow', + 'Real-time validation feedback', + 'Mobile responsive design' + ] + }, + PasswordResetForm: { + name: 'PasswordResetForm', + description: 'Dual-mode password reset form with token validation and password strength indicator', + features: [ + 'Request reset and set new password modes', + 'Token validation for reset links', + 'Password strength indicator', + 'Enhanced error messaging', + 'Auto-focus functionality', + 'Success confirmation screens', + 'Accessibility features', + 'Spanish localization' + ] + }, + ProfileSettings: { + name: 'ProfileSettings', + description: 'Comprehensive user profile and bakery settings management with image upload', + features: [ + 'Tabbed interface (Profile, Security, Preferences, Notifications)', + 'Profile picture upload with validation', + 'User profile and bakery information editing', + 'Password change functionality', + 'Notification preferences', + 'Language and timezone settings', + 'Comprehensive form validation', + 'Save/cancel with change detection' + ] + } +} as const; \ No newline at end of file diff --git a/frontend/src/components/domain/auth/types.ts b/frontend/src/components/domain/auth/types.ts new file mode 100644 index 00000000..a99902d2 --- /dev/null +++ b/frontend/src/components/domain/auth/types.ts @@ -0,0 +1,99 @@ +// Authentication component prop types + +export interface LoginFormProps { + onSuccess?: () => void; + onRegisterClick?: () => void; + onForgotPasswordClick?: () => void; + className?: string; + autoFocus?: boolean; +} + +export interface RegisterFormProps { + onSuccess?: () => void; + onLoginClick?: () => void; + className?: string; + showProgressSteps?: boolean; +} + +export interface PasswordResetFormProps { + token?: string; + onSuccess?: () => void; + onBackClick?: () => void; + className?: string; + autoFocus?: boolean; + mode?: 'request' | 'reset'; +} + +export interface ProfileSettingsProps { + onSuccess?: () => void; + className?: string; + initialTab?: 'profile' | 'security' | 'preferences' | 'notifications'; +} + +// Additional types for internal use +export type RegistrationStep = 'personal' | 'bakery' | 'security' | 'verification'; + +export interface ExtendedUserLogin { + email: string; + password: string; + remember_me: boolean; +} + +export interface ExtendedUserRegistration { + // Personal Information + full_name: string; + email: string; + phone: string; + + // Bakery Information + tenant_name: string; + bakery_type: string; + address: string; + city: string; + postal_code: string; + country: string; + + // Security + password: string; + confirmPassword: string; + + // Terms + acceptTerms: boolean; + acceptPrivacy: boolean; + acceptMarketing: boolean; +} + +export interface ProfileFormData { + full_name: string; + email: string; + phone: string; + language: string; + timezone: string; + avatar_url?: string; +} + +export interface BakeryFormData { + bakery_name: string; + bakery_type: string; + address: string; + city: string; + postal_code: string; + country: string; + website?: string; + description?: string; +} + +export interface NotificationSettings { + email_notifications: boolean; + order_notifications: boolean; + marketing_notifications: boolean; + security_notifications: boolean; + low_stock_alerts: boolean; + production_reminders: boolean; +} + +export interface PasswordChangeData { + currentPassword: string; + newPassword: string; + confirmNewPassword: string; +} \ No newline at end of file diff --git a/frontend/src/components/domain/dashboard/DashboardCard.tsx b/frontend/src/components/domain/dashboard/DashboardCard.tsx new file mode 100644 index 00000000..790b069b --- /dev/null +++ b/frontend/src/components/domain/dashboard/DashboardCard.tsx @@ -0,0 +1,298 @@ +import React, { forwardRef, HTMLAttributes, ReactNode, useState } from 'react'; +import { clsx } from 'clsx'; +import { Card, CardHeader, CardBody, CardFooter, Button, Badge } from '../../ui'; + +export interface DashboardCardProps extends Omit, 'title'> { + // Card variants for different dashboard contexts + variant?: 'metric' | 'chart' | 'list' | 'activity' | 'status' | 'action'; + + // Header props + title?: ReactNode; + subtitle?: ReactNode; + icon?: ReactNode; + headerActions?: ReactNode; + + // Loading and state management + isLoading?: boolean; + hasError?: boolean; + errorMessage?: string; + isEmpty?: boolean; + emptyMessage?: string; + + // Footer props + footerActions?: ReactNode; + footerText?: ReactNode; + + // Badge/notification support + badge?: string | number; + badgeVariant?: 'default' | 'primary' | 'success' | 'warning' | 'error'; + + // Interactive behavior + interactive?: boolean; + onClick?: () => void; + onRefresh?: () => void; + + // Layout customization + padding?: 'none' | 'sm' | 'md' | 'lg'; + headerPadding?: 'none' | 'sm' | 'md' | 'lg'; + bodyPadding?: 'none' | 'sm' | 'md' | 'lg'; + + // Accessibility + 'aria-label'?: string; + 'aria-describedby'?: string; +} + +const DashboardCard = forwardRef(({ + variant = 'metric', + title, + subtitle, + icon, + headerActions, + isLoading = false, + hasError = false, + errorMessage = 'Ha ocurrido un error', + isEmpty = false, + emptyMessage = 'No hay datos disponibles', + footerActions, + footerText, + badge, + badgeVariant = 'primary', + interactive = false, + onClick, + onRefresh, + padding = 'md', + headerPadding, + bodyPadding, + className, + children, + 'aria-label': ariaLabel, + 'aria-describedby': ariaDescribedby, + ...props +}, ref) => { + const [isRefreshing, setIsRefreshing] = useState(false); + + const handleRefresh = async () => { + if (onRefresh && !isRefreshing) { + setIsRefreshing(true); + try { + await onRefresh(); + } finally { + setIsRefreshing(false); + } + } + }; + + const variantStyles = { + metric: 'bg-gradient-to-br from-white to-blue-50 border-[var(--color-info)]/20 hover:border-blue-300', + chart: 'bg-white border-[var(--border-primary)] hover:border-[var(--border-secondary)]', + list: 'bg-white border-[var(--border-primary)] hover:border-[var(--border-secondary)]', + activity: 'bg-gradient-to-br from-white to-green-50 border-green-200 hover:border-green-300', + status: 'bg-gradient-to-br from-white to-purple-50 border-purple-200 hover:border-purple-300', + action: 'bg-gradient-to-br from-white to-amber-50 border-amber-200 hover:border-amber-300 hover:shadow-lg' + }; + + const cardClasses = clsx( + variantStyles[variant], + 'transition-all duration-300', + { + 'cursor-pointer transform hover:-translate-y-1': interactive || onClick, + 'opacity-50': isLoading, + 'border-red-300 bg-red-50': hasError, + }, + className + ); + + const hasHeader = title || subtitle || icon || headerActions || badge || onRefresh; + + const renderSkeletonContent = () => { + switch (variant) { + case 'metric': + return ( +
+
+
+
+
+ ); + case 'chart': + return ( +
+
+
+
+ ); + case 'list': + return ( +
+ {Array.from({ length: 3 }).map((_, i) => ( +
+
+
+
+
+
+
+ ))} +
+ ); + default: + return
; + } + }; + + const renderErrorContent = () => ( +
+
+ + + +
+

{errorMessage}

+ {onRefresh && ( + + )} +
+ ); + + const renderEmptyContent = () => ( +
+
+ + + +
+

{emptyMessage}

+
+ ); + + return ( + + {hasHeader && ( + +
+ {icon && ( +
+ {icon} +
+ )} +
+ {title && ( +
+

+ {title} +

+ {badge && ( + + {badge} + + )} +
+ )} + {subtitle && ( +

+ {subtitle} +

+ )} +
+
+ +
+ {onRefresh && ( + + )} + {headerActions} +
+
+ )} + + + {isLoading + ? renderSkeletonContent() + : hasError + ? renderErrorContent() + : isEmpty + ? renderEmptyContent() + : children} + + + {(footerActions || footerText) && ( + +
+ {footerText} +
+
+ {footerActions} +
+
+ )} +
+ ); +}); + +DashboardCard.displayName = 'DashboardCard'; + +export default DashboardCard; \ No newline at end of file diff --git a/frontend/src/components/domain/dashboard/DashboardCard.tsx.backup b/frontend/src/components/domain/dashboard/DashboardCard.tsx.backup new file mode 100644 index 00000000..e0417223 --- /dev/null +++ b/frontend/src/components/domain/dashboard/DashboardCard.tsx.backup @@ -0,0 +1,298 @@ +import React, { forwardRef, HTMLAttributes, ReactNode, useState } from 'react'; +import { clsx } from 'clsx'; +import { Card, CardHeader, CardBody, CardFooter, Button, Badge } from '../../ui'; + +export interface DashboardCardProps extends Omit, 'title'> { + // Card variants for different dashboard contexts + variant?: 'metric' | 'chart' | 'list' | 'activity' | 'status' | 'action'; + + // Header props + title?: ReactNode; + subtitle?: ReactNode; + icon?: ReactNode; + headerActions?: ReactNode; + + // Loading and state management + isLoading?: boolean; + hasError?: boolean; + errorMessage?: string; + isEmpty?: boolean; + emptyMessage?: string; + + // Footer props + footerActions?: ReactNode; + footerText?: ReactNode; + + // Badge/notification support + badge?: string | number; + badgeVariant?: 'default' | 'primary' | 'success' | 'warning' | 'error'; + + // Interactive behavior + interactive?: boolean; + onClick?: () => void; + onRefresh?: () => void; + + // Layout customization + padding?: 'none' | 'sm' | 'md' | 'lg'; + headerPadding?: 'none' | 'sm' | 'md' | 'lg'; + bodyPadding?: 'none' | 'sm' | 'md' | 'lg'; + + // Accessibility + 'aria-label'?: string; + 'aria-describedby'?: string; +} + +const DashboardCard = forwardRef(({ + variant = 'metric', + title, + subtitle, + icon, + headerActions, + isLoading = false, + hasError = false, + errorMessage = 'Ha ocurrido un error', + isEmpty = false, + emptyMessage = 'No hay datos disponibles', + footerActions, + footerText, + badge, + badgeVariant = 'primary', + interactive = false, + onClick, + onRefresh, + padding = 'md', + headerPadding, + bodyPadding, + className, + children, + 'aria-label': ariaLabel, + 'aria-describedby': ariaDescribedby, + ...props +}, ref) => { + const [isRefreshing, setIsRefreshing] = useState(false); + + const handleRefresh = async () => { + if (onRefresh && !isRefreshing) { + setIsRefreshing(true); + try { + await onRefresh(); + } finally { + setIsRefreshing(false); + } + } + }; + + const variantStyles = { + metric: 'bg-gradient-to-br from-white to-blue-50 border-blue-200 hover:border-blue-300', + chart: 'bg-white border-gray-200 hover:border-gray-300', + list: 'bg-white border-gray-200 hover:border-gray-300', + activity: 'bg-gradient-to-br from-white to-green-50 border-green-200 hover:border-green-300', + status: 'bg-gradient-to-br from-white to-purple-50 border-purple-200 hover:border-purple-300', + action: 'bg-gradient-to-br from-white to-amber-50 border-amber-200 hover:border-amber-300 hover:shadow-lg' + }; + + const cardClasses = clsx( + variantStyles[variant], + 'transition-all duration-300', + { + 'cursor-pointer transform hover:-translate-y-1': interactive || onClick, + 'opacity-50': isLoading, + 'border-red-300 bg-red-50': hasError, + }, + className + ); + + const hasHeader = title || subtitle || icon || headerActions || badge || onRefresh; + + const renderSkeletonContent = () => { + switch (variant) { + case 'metric': + return ( +
+
+
+
+
+ ); + case 'chart': + return ( +
+
+
+
+ ); + case 'list': + return ( +
+ {Array.from({ length: 3 }).map((_, i) => ( +
+
+
+
+
+
+
+ ))} +
+ ); + default: + return
; + } + }; + + const renderErrorContent = () => ( +
+
+ + + +
+

{errorMessage}

+ {onRefresh && ( + + )} +
+ ); + + const renderEmptyContent = () => ( +
+
+ + + +
+

{emptyMessage}

+
+ ); + + return ( + + {hasHeader && ( + +
+ {icon && ( +
+ {icon} +
+ )} +
+ {title && ( +
+

+ {title} +

+ {badge && ( + + {badge} + + )} +
+ )} + {subtitle && ( +

+ {subtitle} +

+ )} +
+
+ +
+ {onRefresh && ( + + )} + {headerActions} +
+
+ )} + + + {isLoading + ? renderSkeletonContent() + : hasError + ? renderErrorContent() + : isEmpty + ? renderEmptyContent() + : children} + + + {(footerActions || footerText) && ( + +
+ {footerText} +
+
+ {footerActions} +
+
+ )} +
+ ); +}); + +DashboardCard.displayName = 'DashboardCard'; + +export default DashboardCard; \ No newline at end of file diff --git a/frontend/src/components/domain/dashboard/DashboardGrid.tsx b/frontend/src/components/domain/dashboard/DashboardGrid.tsx new file mode 100644 index 00000000..8d96ee93 --- /dev/null +++ b/frontend/src/components/domain/dashboard/DashboardGrid.tsx @@ -0,0 +1,122 @@ +import React from 'react'; +import { + TrendingUp, + Package, + AlertCircle, + DollarSign +} from 'lucide-react'; +import { Card, Badge } from '../../ui'; + +export const DashboardGrid: React.FC = () => { + // Simple placeholder implementation - can be enhanced later + const mockData = { + sales_today: 1247, + sales_change: 12.5, + products_sold: 45, + products_change: 8.3, + active_alerts: 2, + urgent_alerts: 1 + }; + + return ( +
+ {/* KPI Cards */} + } + color="green" + /> + + } + color="blue" + /> + + } + color="orange" + /> + + +
+
+ + Sistema +
+
+

En línea

+

Todo funcionando

+
+
+
+
+ ); +}; + +// KPI Card Component +interface KPICardProps { + title: string; + value: string | number; + change?: number; + urgent?: number; + icon: React.ReactNode; + color: 'green' | 'blue' | 'orange' | 'red'; +} + +const KPICard: React.FC = ({ + title, + value, + change, + urgent, + icon, + color +}) => { + const colorClasses = { + green: 'bg-green-50 text-[var(--color-success)] border-green-200', + blue: 'bg-[var(--color-info)]/5 text-[var(--color-info)] border-[var(--color-info)]/20', + orange: 'bg-orange-50 text-[var(--color-primary)] border-orange-200', + red: 'bg-red-50 text-[var(--color-error)] border-red-200', + }; + + return ( + + +
+
+ {icon} +
+ {urgent !== undefined && urgent > 0 && ( + + {urgent} Urgente + + )} +
+ +
+

{title}

+

{value}

+ + {change !== undefined && ( +
+ = 0 ? 'text-green-500' : 'text-red-500 rotate-180'}`} /> + = 0 ? 'text-[var(--color-success)]' : 'text-[var(--color-error)]'}> + {Math.abs(change)}% + + vs ayer +
+ )} +
+
+
+ ); +}; + +export default DashboardGrid; \ No newline at end of file diff --git a/frontend/src/components/domain/dashboard/DashboardGrid.tsx.backup b/frontend/src/components/domain/dashboard/DashboardGrid.tsx.backup new file mode 100644 index 00000000..ca10dee2 --- /dev/null +++ b/frontend/src/components/domain/dashboard/DashboardGrid.tsx.backup @@ -0,0 +1,258 @@ +import React from 'react'; +import { motion } from 'framer-motion'; +import { + TrendingUp, + Package, + AlertCircle, + Cloud, + Clock, + DollarSign, + Users, + ShoppingCart +} from 'lucide-react'; +import { Card } from '../../ui'; +import { Badge } from '../../ui'; +import { useQuery } from '@tanstack/react-query'; +import { dashboardService } from '../../../services/api/dashboard.service'; +import { ForecastChart } from '../forecasting/ForecastChart'; +import { SalesChart } from '../sales/SalesChart'; +import { AIInsightCard } from '../ai/AIInsightCard'; +import { ProductionStatusCard } from '../production/ProductionStatusCard'; +import { AlertsFeed } from '../alerts/AlertsFeed'; +import { useBakeryStore } from '../../../stores/bakery.store'; + +export const DashboardGrid: React.FC = () => { + const { currentTenant, bakeryType } = useBakeryStore(); + + const { data: dashboardData, isLoading } = useQuery({ + queryKey: ['dashboard', currentTenant?.id], + queryFn: () => dashboardService.getDashboardData(currentTenant!.id), + enabled: !!currentTenant, + refetchInterval: 60000, // Refresh every minute + }); + + const containerVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.1, + }, + }, + }; + + const itemVariants = { + hidden: { y: 20, opacity: 0 }, + visible: { + y: 0, + opacity: 1, + transition: { + type: 'spring', + stiffness: 100, + }, + }, + }; + + if (isLoading) { + return ; + } + + return ( + + {/* KPI Cards Row */} + + } + color="green" + /> + + + + } + color="blue" + /> + + + + } + color="orange" + /> + + + + + + + {/* Charts Row */} + + + + Predicción de Demanda - 7 Días + + + + + + + + + + + Análisis de Ventas + + + + + + + + {/* Status Cards */} + {bakeryType === 'individual' && ( + + + + )} + + {/* AI Insights */} + + console.log('AI Action:', action)} + /> + + + {/* Real-time Alerts Feed */} + + + + + ); +}; + +// KPI Card Component +interface KPICardProps { + title: string; + value: string | number; + change?: number; + urgent?: number; + icon: React.ReactNode; + color: 'green' | 'blue' | 'orange' | 'red'; +} + +const KPICard: React.FC = ({ + title, + value, + change, + urgent, + icon, + color +}) => { + const colorClasses = { + green: 'bg-green-50 text-green-600 border-green-200', + blue: 'bg-blue-50 text-blue-600 border-blue-200', + orange: 'bg-orange-50 text-orange-600 border-orange-200', + red: 'bg-red-50 text-red-600 border-red-200', + }; + + return ( + + +
+
+ {icon} +
+ {urgent !== undefined && urgent > 0 && ( + + {urgent} Urgente + + )} +
+ +
+

{title}

+

{value}

+ + {change !== undefined && ( +
+ = 0 ? 'text-green-500' : 'text-red-500 rotate-180'}`} /> + = 0 ? 'text-green-600' : 'text-red-600'}> + {Math.abs(change)}% + + vs ayer +
+ )} +
+
+
+ ); +}; + +// Weather Impact Card +const WeatherCard: React.FC<{ + temperature?: number; + condition?: string; + impact?: string; +}> = ({ temperature, condition, impact }) => { + return ( + + +
+ + + {impact === 'positive' ? '↑' : impact === 'negative' ? '↓' : '='} Impacto + +
+
+

{temperature}°C

+

{condition}

+
+
+
+ ); +}; + +const DashboardSkeleton: React.FC = () => { + return ( +
+ {[...Array(8)].map((_, i) => ( + + +
+
+ + + ))} +
+ ); +}; \ No newline at end of file diff --git a/frontend/src/components/domain/dashboard/KPIWidget.tsx b/frontend/src/components/domain/dashboard/KPIWidget.tsx new file mode 100644 index 00000000..cd9b9a50 --- /dev/null +++ b/frontend/src/components/domain/dashboard/KPIWidget.tsx @@ -0,0 +1,502 @@ +import React, { useMemo } from 'react'; +import { clsx } from 'clsx'; +import { Badge } from '../../ui'; +import DashboardCard from './DashboardCard'; + +export interface KPIValue { + current: number; + previous?: number; + target?: number; + format: 'currency' | 'number' | 'percentage'; + prefix?: string; + suffix?: string; +} + +export interface KPITrend { + direction: 'up' | 'down' | 'stable'; + value: number; + isPositive: boolean; + comparisonPeriod: string; + description?: string; +} + +export interface KPIThreshold { + excellent: number; + good: number; + warning: number; + critical: number; +} + +export interface SparklineDataPoint { + date: string; + value: number; + label?: string; +} + +export interface KPIWidgetProps { + title: string; + subtitle?: string; + value: KPIValue; + trend?: KPITrend; + + // Visual configuration + icon?: React.ReactNode; + color?: 'blue' | 'green' | 'orange' | 'red' | 'purple' | 'indigo' | 'teal'; + variant?: 'default' | 'compact' | 'detailed' | 'chart'; + + // Chart data + sparklineData?: SparklineDataPoint[]; + showSparkline?: boolean; + + // Thresholds and status + thresholds?: KPIThreshold; + status?: 'excellent' | 'good' | 'warning' | 'critical' | 'neutral'; + + // Comparison and context + comparisonLabel?: string; + contextInfo?: string; + + // Interactive features + isLoading?: boolean; + onRefresh?: () => void; + onClick?: () => void; + + // Accessibility and styling + className?: string; + 'aria-label'?: string; +} + +// Predefined bakery KPI configurations +export const BAKERY_KPI_CONFIGS = { + dailyRevenue: { + title: 'Ingresos Hoy', + subtitle: 'Ventas del día actual', + icon: ( + + + + ), + color: 'green' as const, + format: 'currency' as const + }, + orderCount: { + title: 'Pedidos', + subtitle: 'Órdenes procesadas hoy', + icon: ( + + + + ), + color: 'blue' as const, + format: 'number' as const + }, + productivity: { + title: 'Productividad', + subtitle: 'Unidades producidas por hora', + icon: ( + + + + ), + color: 'orange' as const, + format: 'number' as const, + suffix: '/h' + }, + stockLevel: { + title: 'Nivel Stock', + subtitle: 'Porcentaje de stock disponible', + icon: ( + + + + ), + color: 'purple' as const, + format: 'percentage' as const + } +}; + +const formatValue = (value: number, format: KPIValue['format'], prefix?: string, suffix?: string): string => { + let formatted: string; + + switch (format) { + case 'currency': + formatted = new Intl.NumberFormat('es-ES', { + style: 'currency', + currency: 'EUR', + minimumFractionDigits: 0, + maximumFractionDigits: 2 + }).format(value); + break; + + case 'percentage': + formatted = new Intl.NumberFormat('es-ES', { + style: 'percent', + minimumFractionDigits: 0, + maximumFractionDigits: 1 + }).format(value / 100); + break; + + case 'number': + default: + formatted = new Intl.NumberFormat('es-ES').format(value); + break; + } + + return `${prefix || ''}${formatted}${suffix || ''}`; +}; + +const calculateTrend = (current: number, previous: number): KPITrend => { + // Handle undefined or null values + if (current == null || previous == null || + typeof current !== 'number' || typeof previous !== 'number' || + isNaN(current) || isNaN(previous)) { + return { + direction: 'stable', + value: 0, + isPositive: true, + comparisonPeriod: 'vs período anterior' + }; + } + + const change = current - previous; + const percentChange = previous !== 0 ? (change / previous) * 100 : 0; + + return { + direction: change > 0 ? 'up' : change < 0 ? 'down' : 'stable', + value: Math.abs(percentChange), + isPositive: change >= 0, + comparisonPeriod: 'vs período anterior' + }; +}; + +const getStatusColor = (status: KPIWidgetProps['status']) => { + switch (status) { + case 'excellent': + return { + bg: 'bg-[var(--color-success)]/10', + text: 'text-[var(--color-success)]', + border: 'border-[var(--color-success)]/20', + icon: 'text-[var(--color-success)]' + }; + case 'good': + return { + bg: 'bg-[var(--color-info)]/10', + text: 'text-[var(--color-info)]', + border: 'border-[var(--color-info)]/20', + icon: 'text-[var(--color-info)]' + }; + case 'warning': + return { + bg: 'bg-[var(--color-warning)]/10', + text: 'text-[var(--color-warning)]', + border: 'border-[var(--color-warning)]/20', + icon: 'text-[var(--color-warning)]' + }; + case 'critical': + return { + bg: 'bg-[var(--color-error)]/10', + text: 'text-[var(--color-error)]', + border: 'border-[var(--color-error)]/20', + icon: 'text-[var(--color-error)]' + }; + default: + return { + bg: 'bg-[var(--bg-tertiary)]', + text: 'text-[var(--text-secondary)]', + border: 'border-[var(--border-primary)]', + icon: 'text-[var(--text-tertiary)]' + }; + } +}; + +const SimpleSparkline: React.FC<{ data: SparklineDataPoint[]; color: string }> = ({ data, color }) => { + const max = Math.max(...data.map(d => d.value)); + const min = Math.min(...data.map(d => d.value)); + const range = max - min; + + const points = data.map((point, index) => { + const x = (index / (data.length - 1)) * 100; + const y = range === 0 ? 50 : ((max - point.value) / range) * 100; + return `${x},${y}`; + }).join(' '); + + const colorClasses = { + blue: 'stroke-[var(--color-info)]', + green: 'stroke-[var(--color-success)]', + orange: 'stroke-[var(--color-primary)]', + red: 'stroke-[var(--color-error)]', + purple: 'stroke-[var(--color-info)]', + indigo: 'stroke-[var(--color-info)]', + teal: 'stroke-[var(--color-success)]' + }; + + return ( +
+ + + +
+ ); +}; + +const KPIWidget: React.FC = ({ + title, + subtitle, + value, + trend, + icon, + color = 'blue', + variant = 'default', + sparklineData, + showSparkline = false, + thresholds, + status, + comparisonLabel, + contextInfo, + isLoading = false, + onRefresh, + onClick, + className, + 'aria-label': ariaLabel +}) => { + // Calculate trend if not provided + const calculatedTrend = useMemo(() => { + if (trend) return trend; + if (value.previous !== undefined) { + return calculateTrend(value.current, value.previous); + } + return null; + }, [trend, value.current, value.previous]); + + // Determine status based on thresholds + const calculatedStatus = useMemo(() => { + if (status) return status; + if (!thresholds) return 'neutral'; + + const { current } = value; + if (current >= thresholds.excellent) return 'excellent'; + if (current >= thresholds.good) return 'good'; + if (current >= thresholds.warning) return 'warning'; + return 'critical'; + }, [status, thresholds, value.current]); + + const statusStyles = getStatusColor(calculatedStatus); + const formattedValue = formatValue(value.current, value.format, value.prefix, value.suffix); + const formattedTarget = value.target ? formatValue(value.target, value.format, value.prefix, value.suffix) : null; + + const colorClasses = { + blue: 'text-[var(--color-info)]', + green: 'text-[var(--color-success)]', + orange: 'text-[var(--color-primary)]', + red: 'text-[var(--color-error)]', + purple: 'text-[var(--color-info)]', + indigo: 'text-[var(--color-info)]', + teal: 'text-[var(--color-success)]' + }; + + const renderTrendIcon = (direction: KPITrend['direction']) => { + const iconClass = calculatedTrend?.isPositive ? 'text-[var(--color-success)]' : 'text-[var(--color-error)]'; + + switch (direction) { + case 'up': + return ( + + + + ); + case 'down': + return ( + + + + ); + default: + return ( + + + + ); + } + }; + + const renderCompactVariant = () => ( +
+
+ {icon && ( +
+
+ {icon} +
+
+ )} +
+

{title}

+

{formattedValue}

+
+
+ {calculatedTrend && ( +
+ {renderTrendIcon(calculatedTrend.direction)} + + {(calculatedTrend.value || 0).toFixed(1)}% + +
+ )} +
+ ); + + const renderDetailedVariant = () => ( +
+ {/* Header */} +
+
+ {icon && ( +
+
+ {icon} +
+
+ )} +
+

{title}

+ {subtitle && ( +

{subtitle}

+ )} +
+
+ {calculatedStatus !== 'neutral' && ( + + {calculatedStatus === 'excellent' ? 'Excelente' : + calculatedStatus === 'good' ? 'Bueno' : + calculatedStatus === 'warning' ? 'Atención' : 'Crítico'} + + )} +
+ + {/* Value and trend */} +
+
+ {formattedValue} + {calculatedTrend && ( +
+ {renderTrendIcon(calculatedTrend.direction)} + + {(calculatedTrend.value || 0).toFixed(1)}% + + + {calculatedTrend.comparisonPeriod} + +
+ )} +
+ + {formattedTarget && ( +

+ Objetivo: {formattedTarget} +

+ )} + + {contextInfo && ( +

{contextInfo}

+ )} +
+ + {/* Sparkline chart */} + {showSparkline && sparklineData && sparklineData.length > 0 && ( +
+

Tendencia últimos 7 días

+ +
+ )} +
+ ); + + const renderDefaultVariant = () => ( +
+
+
+ {icon && ( +
+ {icon} +
+ )} +
+

{title}

+ {subtitle && ( +

{subtitle}

+ )} +
+
+
+ +
+
+ {formattedValue} + {calculatedTrend && ( +
+ {renderTrendIcon(calculatedTrend.direction)} + + {(calculatedTrend.value || 0).toFixed(1)}% + +
+ )} +
+ + {comparisonLabel && ( +

{comparisonLabel}

+ )} +
+
+ ); + + const renderContent = () => { + switch (variant) { + case 'compact': + return renderCompactVariant(); + case 'detailed': + return renderDetailedVariant(); + case 'chart': + return renderDetailedVariant(); + default: + return renderDefaultVariant(); + } + }; + + return ( + + {renderContent()} + + ); +}; + +KPIWidget.displayName = 'KPIWidget'; + +export default KPIWidget; \ No newline at end of file diff --git a/frontend/src/components/domain/dashboard/QuickActions.tsx b/frontend/src/components/domain/dashboard/QuickActions.tsx new file mode 100644 index 00000000..4515591c --- /dev/null +++ b/frontend/src/components/domain/dashboard/QuickActions.tsx @@ -0,0 +1,382 @@ +import React, { KeyboardEvent, useCallback, useMemo } from 'react'; +import { clsx } from 'clsx'; +import { Button, Badge } from '../../ui'; + +export interface QuickAction { + id: string; + title: string; + description?: string; + icon: React.ReactNode; + onClick: () => void; + href?: string; + + // Badge/notification support + badge?: string | number; + badgeVariant?: 'default' | 'primary' | 'success' | 'warning' | 'error'; + + // Access control + permissions?: string[]; + requiredRole?: string; + isDisabled?: boolean; + disabledReason?: string; + + // Styling + variant?: 'primary' | 'secondary' | 'outline' | 'success' | 'warning' | 'danger'; + color?: string; + backgroundGradient?: string; + + // Keyboard shortcuts + shortcut?: string; + + // Priority for ordering + priority?: number; +} + +export interface QuickActionsProps { + actions: QuickAction[]; + + // Layout configuration + columns?: 2 | 3 | 4 | 5 | 6; + gap?: 'sm' | 'md' | 'lg'; + size?: 'sm' | 'md' | 'lg'; + + // Filtering and user context + userRole?: string; + userPermissions?: string[]; + showDisabled?: boolean; + maxActions?: number; + + // Event handlers + onActionClick?: (action: QuickAction) => void; + onActionHover?: (action: QuickAction) => void; + + // Accessibility + 'aria-label'?: string; + className?: string; +} + +// Predefined bakery actions with Spanish context +export const BAKERY_QUICK_ACTIONS: QuickAction[] = [ + { + id: 'new-order', + title: 'Nuevo Pedido', + description: 'Crear un nuevo pedido de cliente', + icon: ( + + + + ), + onClick: () => console.log('Nuevo pedido'), + variant: 'primary', + backgroundGradient: 'from-blue-500 to-blue-600', + priority: 1 + }, + { + id: 'add-product', + title: 'Agregar Producto', + description: 'Añadir nuevo producto al inventario', + icon: ( + + + + ), + onClick: () => console.log('Agregar producto'), + variant: 'success', + backgroundGradient: 'from-green-500 to-green-600', + priority: 2 + }, + { + id: 'view-inventory', + title: 'Ver Inventario', + description: 'Consultar stock y productos', + icon: ( + + + + ), + onClick: () => console.log('Ver inventario'), + variant: 'outline', + backgroundGradient: 'from-purple-500 to-purple-600', + priority: 3, + badge: '5', + badgeVariant: 'warning' + }, + { + id: 'production-batch', + title: 'Nueva Producción', + description: 'Programar lote de producción', + icon: ( + + + + ), + onClick: () => console.log('Nueva producción'), + variant: 'warning', + backgroundGradient: 'from-orange-500 to-orange-600', + priority: 4 + }, + { + id: 'sales-report', + title: 'Reporte Ventas', + description: 'Ver análisis de ventas', + icon: ( + + + + ), + onClick: () => console.log('Reporte ventas'), + variant: 'secondary', + backgroundGradient: 'from-indigo-500 to-indigo-600', + priority: 5 + }, + { + id: 'manage-suppliers', + title: 'Proveedores', + description: 'Gestionar proveedores', + icon: ( + + + + ), + onClick: () => console.log('Proveedores'), + variant: 'outline', + backgroundGradient: 'from-teal-500 to-teal-600', + priority: 6 + }, + { + id: 'pos-system', + title: 'Sistema POS', + description: 'Punto de venta', + icon: ( + + + + ), + onClick: () => console.log('Sistema POS'), + variant: 'primary', + backgroundGradient: 'from-emerald-500 to-emerald-600', + priority: 7 + }, + { + id: 'quality-control', + title: 'Control Calidad', + description: 'Verificación de calidad', + icon: ( + + + + ), + onClick: () => console.log('Control calidad'), + variant: 'success', + backgroundGradient: 'from-lime-500 to-lime-600', + priority: 8, + requiredRole: 'quality_manager' + } +]; + +const QuickActions: React.FC = ({ + actions, + columns = 3, + gap = 'md', + size = 'md', + userRole, + userPermissions = [], + showDisabled = false, + maxActions, + onActionClick, + onActionHover, + 'aria-label': ariaLabel = 'Acciones rápidas', + className +}) => { + // Filter and sort actions + const visibleActions = useMemo(() => { + let filteredActions = actions.filter(action => { + // Role-based filtering + if (action.requiredRole && userRole !== action.requiredRole) { + return showDisabled; + } + + // Permission-based filtering + if (action.permissions && action.permissions.length > 0) { + const hasPermission = action.permissions.some(perm => + userPermissions.includes(perm) + ); + if (!hasPermission) { + return showDisabled; + } + } + + return true; + }); + + // Sort by priority + filteredActions.sort((a, b) => (a.priority || 999) - (b.priority || 999)); + + // Limit actions if specified + if (maxActions) { + filteredActions = filteredActions.slice(0, maxActions); + } + + return filteredActions; + }, [actions, userRole, userPermissions, showDisabled, maxActions]); + + const handleActionClick = useCallback((action: QuickAction) => { + if (action.isDisabled) return; + + onActionClick?.(action); + action.onClick(); + }, [onActionClick]); + + const handleKeyDown = useCallback((event: KeyboardEvent, action: QuickAction) => { + if (event.key === 'Enter' || event.key === ' ') { + event.preventDefault(); + handleActionClick(action); + } + }, [handleActionClick]); + + const gridClasses = { + 2: 'grid-cols-2 sm:grid-cols-2', + 3: 'grid-cols-2 sm:grid-cols-3', + 4: 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-4', + 5: 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-5', + 6: 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-6' + }; + + const gapClasses = { + sm: 'gap-2', + md: 'gap-4', + lg: 'gap-6' + }; + + const sizeClasses = { + sm: 'p-3 min-h-[80px]', + md: 'p-4 min-h-[100px]', + lg: 'p-6 min-h-[120px]' + }; + + if (visibleActions.length === 0) { + return ( +
+

No hay acciones disponibles

+
+ ); + } + + return ( +
+ {visibleActions.map((action) => { + const isDisabled = action.isDisabled || + (action.requiredRole && userRole !== action.requiredRole) || + (action.permissions && !action.permissions.some(perm => userPermissions.includes(perm))); + + const buttonClasses = clsx( + 'relative group transition-all duration-200', + 'border border-[var(--border-primary)] rounded-xl', + 'flex flex-col items-center justify-center text-center', + 'hover:shadow-lg hover:-translate-y-1', + 'focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2', + sizeClasses[size], + { + 'bg-white hover:bg-[var(--bg-secondary)]': !action.backgroundGradient, + 'bg-gradient-to-br text-white hover:opacity-90': action.backgroundGradient, + 'opacity-50 cursor-not-allowed hover:transform-none hover:shadow-none': isDisabled, + } + ); + + const gradientStyle = action.backgroundGradient ? { + background: `linear-gradient(135deg, var(--tw-gradient-stops))`, + } : undefined; + + return ( + + ); + })} +
+ ); +}; + +QuickActions.displayName = 'QuickActions'; + +export default QuickActions; \ No newline at end of file diff --git a/frontend/src/components/domain/dashboard/QuickActions.tsx.backup b/frontend/src/components/domain/dashboard/QuickActions.tsx.backup new file mode 100644 index 00000000..27f64e26 --- /dev/null +++ b/frontend/src/components/domain/dashboard/QuickActions.tsx.backup @@ -0,0 +1,382 @@ +import React, { KeyboardEvent, useCallback, useMemo } from 'react'; +import { clsx } from 'clsx'; +import { Button, Badge } from '../../ui'; + +export interface QuickAction { + id: string; + title: string; + description?: string; + icon: React.ReactNode; + onClick: () => void; + href?: string; + + // Badge/notification support + badge?: string | number; + badgeVariant?: 'default' | 'primary' | 'success' | 'warning' | 'error'; + + // Access control + permissions?: string[]; + requiredRole?: string; + isDisabled?: boolean; + disabledReason?: string; + + // Styling + variant?: 'primary' | 'secondary' | 'outline' | 'success' | 'warning' | 'danger'; + color?: string; + backgroundGradient?: string; + + // Keyboard shortcuts + shortcut?: string; + + // Priority for ordering + priority?: number; +} + +export interface QuickActionsProps { + actions: QuickAction[]; + + // Layout configuration + columns?: 2 | 3 | 4 | 5 | 6; + gap?: 'sm' | 'md' | 'lg'; + size?: 'sm' | 'md' | 'lg'; + + // Filtering and user context + userRole?: string; + userPermissions?: string[]; + showDisabled?: boolean; + maxActions?: number; + + // Event handlers + onActionClick?: (action: QuickAction) => void; + onActionHover?: (action: QuickAction) => void; + + // Accessibility + 'aria-label'?: string; + className?: string; +} + +// Predefined bakery actions with Spanish context +export const BAKERY_QUICK_ACTIONS: QuickAction[] = [ + { + id: 'new-order', + title: 'Nuevo Pedido', + description: 'Crear un nuevo pedido de cliente', + icon: ( + + + + ), + onClick: () => console.log('Nuevo pedido'), + variant: 'primary', + backgroundGradient: 'from-blue-500 to-blue-600', + priority: 1 + }, + { + id: 'add-product', + title: 'Agregar Producto', + description: 'Añadir nuevo producto al inventario', + icon: ( + + + + ), + onClick: () => console.log('Agregar producto'), + variant: 'success', + backgroundGradient: 'from-green-500 to-green-600', + priority: 2 + }, + { + id: 'view-inventory', + title: 'Ver Inventario', + description: 'Consultar stock y productos', + icon: ( + + + + ), + onClick: () => console.log('Ver inventario'), + variant: 'outline', + backgroundGradient: 'from-purple-500 to-purple-600', + priority: 3, + badge: '5', + badgeVariant: 'warning' + }, + { + id: 'production-batch', + title: 'Nueva Producción', + description: 'Programar lote de producción', + icon: ( + + + + ), + onClick: () => console.log('Nueva producción'), + variant: 'warning', + backgroundGradient: 'from-orange-500 to-orange-600', + priority: 4 + }, + { + id: 'sales-report', + title: 'Reporte Ventas', + description: 'Ver análisis de ventas', + icon: ( + + + + ), + onClick: () => console.log('Reporte ventas'), + variant: 'secondary', + backgroundGradient: 'from-indigo-500 to-indigo-600', + priority: 5 + }, + { + id: 'manage-suppliers', + title: 'Proveedores', + description: 'Gestionar proveedores', + icon: ( + + + + ), + onClick: () => console.log('Proveedores'), + variant: 'outline', + backgroundGradient: 'from-teal-500 to-teal-600', + priority: 6 + }, + { + id: 'pos-system', + title: 'Sistema POS', + description: 'Punto de venta', + icon: ( + + + + ), + onClick: () => console.log('Sistema POS'), + variant: 'primary', + backgroundGradient: 'from-emerald-500 to-emerald-600', + priority: 7 + }, + { + id: 'quality-control', + title: 'Control Calidad', + description: 'Verificación de calidad', + icon: ( + + + + ), + onClick: () => console.log('Control calidad'), + variant: 'success', + backgroundGradient: 'from-lime-500 to-lime-600', + priority: 8, + requiredRole: 'quality_manager' + } +]; + +const QuickActions: React.FC = ({ + actions, + columns = 3, + gap = 'md', + size = 'md', + userRole, + userPermissions = [], + showDisabled = false, + maxActions, + onActionClick, + onActionHover, + 'aria-label': ariaLabel = 'Acciones rápidas', + className +}) => { + // Filter and sort actions + const visibleActions = useMemo(() => { + let filteredActions = actions.filter(action => { + // Role-based filtering + if (action.requiredRole && userRole !== action.requiredRole) { + return showDisabled; + } + + // Permission-based filtering + if (action.permissions && action.permissions.length > 0) { + const hasPermission = action.permissions.some(perm => + userPermissions.includes(perm) + ); + if (!hasPermission) { + return showDisabled; + } + } + + return true; + }); + + // Sort by priority + filteredActions.sort((a, b) => (a.priority || 999) - (b.priority || 999)); + + // Limit actions if specified + if (maxActions) { + filteredActions = filteredActions.slice(0, maxActions); + } + + return filteredActions; + }, [actions, userRole, userPermissions, showDisabled, maxActions]); + + const handleActionClick = useCallback((action: QuickAction) => { + if (action.isDisabled) return; + + onActionClick?.(action); + action.onClick(); + }, [onActionClick]); + + const handleKeyDown = useCallback((event: KeyboardEvent, action: QuickAction) => { + if (event.key === 'Enter' || event.key === ' ') { + event.preventDefault(); + handleActionClick(action); + } + }, [handleActionClick]); + + const gridClasses = { + 2: 'grid-cols-2 sm:grid-cols-2', + 3: 'grid-cols-2 sm:grid-cols-3', + 4: 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-4', + 5: 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-5', + 6: 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-6' + }; + + const gapClasses = { + sm: 'gap-2', + md: 'gap-4', + lg: 'gap-6' + }; + + const sizeClasses = { + sm: 'p-3 min-h-[80px]', + md: 'p-4 min-h-[100px]', + lg: 'p-6 min-h-[120px]' + }; + + if (visibleActions.length === 0) { + return ( +
+

No hay acciones disponibles

+
+ ); + } + + return ( +
+ {visibleActions.map((action) => { + const isDisabled = action.isDisabled || + (action.requiredRole && userRole !== action.requiredRole) || + (action.permissions && !action.permissions.some(perm => userPermissions.includes(perm))); + + const buttonClasses = clsx( + 'relative group transition-all duration-200', + 'border border-gray-200 rounded-xl', + 'flex flex-col items-center justify-center text-center', + 'hover:shadow-lg hover:-translate-y-1', + 'focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2', + sizeClasses[size], + { + 'bg-white hover:bg-gray-50': !action.backgroundGradient, + 'bg-gradient-to-br text-white hover:opacity-90': action.backgroundGradient, + 'opacity-50 cursor-not-allowed hover:transform-none hover:shadow-none': isDisabled, + } + ); + + const gradientStyle = action.backgroundGradient ? { + background: `linear-gradient(135deg, var(--tw-gradient-stops))`, + } : undefined; + + return ( + + ); + })} +
+ ); +}; + +QuickActions.displayName = 'QuickActions'; + +export default QuickActions; \ No newline at end of file diff --git a/frontend/src/components/domain/dashboard/RecentActivity.tsx b/frontend/src/components/domain/dashboard/RecentActivity.tsx new file mode 100644 index 00000000..fcc31924 --- /dev/null +++ b/frontend/src/components/domain/dashboard/RecentActivity.tsx @@ -0,0 +1,524 @@ +import React, { useMemo, useState, useCallback } from 'react'; +import { clsx } from 'clsx'; +import { Avatar, Badge, Button } from '../../ui'; + +export interface ActivityUser { + id: string; + name: string; + avatar?: string; + role?: string; +} + +export interface ActivityItem { + id: string; + type: ActivityType; + title: string; + description: string; + timestamp: string; + user?: ActivityUser; + metadata?: Record; + status?: ActivityStatus; + category?: string; + + // Navigation support + href?: string; + onClick?: () => void; + + // Visual styling + icon?: React.ReactNode; + color?: string; + priority?: ActivityPriority; +} + +export enum ActivityType { + ORDER = 'order', + PRODUCTION = 'production', + INVENTORY = 'inventory', + SALES = 'sales', + USER = 'user', + SYSTEM = 'system', + QUALITY = 'quality', + SUPPLIER = 'supplier', + FINANCE = 'finance', + ALERT = 'alert' +} + +export enum ActivityStatus { + SUCCESS = 'success', + WARNING = 'warning', + ERROR = 'error', + INFO = 'info', + PENDING = 'pending', + IN_PROGRESS = 'in_progress', + CANCELLED = 'cancelled' +} + +export enum ActivityPriority { + LOW = 'low', + MEDIUM = 'medium', + HIGH = 'high', + URGENT = 'urgent' +} + +export interface RecentActivityProps { + activities: ActivityItem[]; + + // Display configuration + maxItems?: number; + showTimestamp?: boolean; + showUserAvatar?: boolean; + showTypeIcons?: boolean; + compact?: boolean; + + // Filtering + allowFiltering?: boolean; + filterTypes?: ActivityType[]; + defaultFilter?: ActivityType | 'all'; + + // Pagination and loading + hasMore?: boolean; + isLoading?: boolean; + onLoadMore?: () => void; + + // Event handlers + onActivityClick?: (activity: ActivityItem) => void; + onRefresh?: () => void; + + // Accessibility and styling + className?: string; + 'aria-label'?: string; + emptyMessage?: string; +} + +// Spanish activity type labels and icons +const ACTIVITY_CONFIG = { + [ActivityType.ORDER]: { + label: 'Pedidos', + icon: ( + + + + ), + color: 'blue', + bgColor: 'bg-[var(--color-info)]/10', + textColor: 'text-[var(--color-info)]', + borderColor: 'border-[var(--color-info)]/20' + }, + [ActivityType.PRODUCTION]: { + label: 'Producción', + icon: ( + + + + ), + color: 'orange', + bgColor: 'bg-[var(--color-primary)]/10', + textColor: 'text-[var(--color-primary)]', + borderColor: 'border-orange-200' + }, + [ActivityType.INVENTORY]: { + label: 'Inventario', + icon: ( + + + + ), + color: 'purple', + bgColor: 'bg-purple-100', + textColor: 'text-purple-600', + borderColor: 'border-purple-200' + }, + [ActivityType.SALES]: { + label: 'Ventas', + icon: ( + + + + ), + color: 'green', + bgColor: 'bg-[var(--color-success)]/10', + textColor: 'text-[var(--color-success)]', + borderColor: 'border-green-200' + }, + [ActivityType.USER]: { + label: 'Usuarios', + icon: ( + + + + ), + color: 'indigo', + bgColor: 'bg-indigo-100', + textColor: 'text-indigo-600', + borderColor: 'border-indigo-200' + }, + [ActivityType.SYSTEM]: { + label: 'Sistema', + icon: ( + + + + + ), + color: 'gray', + bgColor: 'bg-[var(--bg-tertiary)]', + textColor: 'text-[var(--text-secondary)]', + borderColor: 'border-[var(--border-primary)]' + }, + [ActivityType.QUALITY]: { + label: 'Calidad', + icon: ( + + + + ), + color: 'green', + bgColor: 'bg-[var(--color-success)]/10', + textColor: 'text-[var(--color-success)]', + borderColor: 'border-green-200' + }, + [ActivityType.SUPPLIER]: { + label: 'Proveedores', + icon: ( + + + + ), + color: 'teal', + bgColor: 'bg-teal-100', + textColor: 'text-teal-600', + borderColor: 'border-teal-200' + }, + [ActivityType.FINANCE]: { + label: 'Finanzas', + icon: ( + + + + ), + color: 'emerald', + bgColor: 'bg-emerald-100', + textColor: 'text-emerald-600', + borderColor: 'border-emerald-200' + }, + [ActivityType.ALERT]: { + label: 'Alertas', + icon: ( + + + + ), + color: 'red', + bgColor: 'bg-[var(--color-error)]/10', + textColor: 'text-[var(--color-error)]', + borderColor: 'border-red-200' + } +}; + +const STATUS_CONFIG = { + [ActivityStatus.SUCCESS]: { color: 'green', bgColor: 'bg-green-500' }, + [ActivityStatus.WARNING]: { color: 'yellow', bgColor: 'bg-yellow-500' }, + [ActivityStatus.ERROR]: { color: 'red', bgColor: 'bg-red-500' }, + [ActivityStatus.INFO]: { color: 'blue', bgColor: 'bg-[var(--color-info)]/50' }, + [ActivityStatus.PENDING]: { color: 'gray', bgColor: 'bg-[var(--bg-secondary)]0' }, + [ActivityStatus.IN_PROGRESS]: { color: 'purple', bgColor: 'bg-purple-500' }, + [ActivityStatus.CANCELLED]: { color: 'gray', bgColor: 'bg-gray-400' } +}; + +const formatRelativeTime = (timestamp: string): string => { + const date = new Date(timestamp); + const now = new Date(); + const diffInMs = now.getTime() - date.getTime(); + const diffInMinutes = Math.floor(diffInMs / (1000 * 60)); + const diffInHours = Math.floor(diffInMinutes / 60); + const diffInDays = Math.floor(diffInHours / 24); + + if (diffInMinutes < 1) { + return 'Ahora mismo'; + } else if (diffInMinutes < 60) { + return `Hace ${diffInMinutes} min`; + } else if (diffInHours < 24) { + return `Hace ${diffInHours}h`; + } else if (diffInDays === 1) { + return 'Ayer'; + } else if (diffInDays < 7) { + return `Hace ${diffInDays} días`; + } else { + return date.toLocaleDateString('es-ES', { + day: 'numeric', + month: 'short' + }); + } +}; + +const RecentActivity: React.FC = ({ + activities, + maxItems = 10, + showTimestamp = true, + showUserAvatar = true, + showTypeIcons = true, + compact = false, + allowFiltering = true, + filterTypes = Object.values(ActivityType), + defaultFilter = 'all', + hasMore = false, + isLoading = false, + onLoadMore, + onActivityClick, + onRefresh, + className, + 'aria-label': ariaLabel = 'Actividad reciente', + emptyMessage = 'No hay actividad reciente' +}) => { + const [activeFilter, setActiveFilter] = useState(defaultFilter); + + const filteredActivities = useMemo(() => { + let filtered = activities; + + if (activeFilter !== 'all') { + filtered = activities.filter(activity => activity.type === activeFilter); + } + + return filtered.slice(0, maxItems); + }, [activities, activeFilter, maxItems]); + + const handleActivityClick = useCallback((activity: ActivityItem) => { + onActivityClick?.(activity); + if (activity.onClick) { + activity.onClick(); + } + }, [onActivityClick]); + + const renderActivityItem = (activity: ActivityItem) => { + const config = ACTIVITY_CONFIG[activity.type] || { + label: 'Actividad', + icon:
, + color: 'gray', + bgColor: 'bg-[var(--bg-tertiary)]', + textColor: 'text-[var(--text-secondary)]', + borderColor: 'border-[var(--border-primary)]' + }; + const statusConfig = activity.status ? STATUS_CONFIG[activity.status] : null; + + const itemClasses = clsx( + 'group relative flex items-start gap-3 p-3 rounded-lg transition-all duration-200', + 'hover:bg-[var(--bg-secondary)] hover:shadow-sm', + { + 'cursor-pointer': activity.onClick || activity.href, + 'p-2': compact, + 'border-l-4': !compact, + [config.borderColor]: !compact + } + ); + + return ( +
handleActivityClick(activity) : undefined} + role={activity.onClick || activity.href ? 'button' : undefined} + tabIndex={activity.onClick || activity.href ? 0 : undefined} + > + {/* Timeline indicator */} +
+ {showTypeIcons && ( +
+ {activity.icon || config.icon} +
+ )} + + {/* Status indicator */} + {activity.status && statusConfig && ( +
+ )} +
+ + {/* Content */} +
+
+
+

+ {activity.title} +

+

+ {activity.description} +

+ + {/* User info */} + {activity.user && showUserAvatar && ( +
+ + + {activity.user.name} + +
+ )} +
+ + {/* Timestamp */} + {showTimestamp && ( + + )} +
+
+
+ ); + }; + + if (activities.length === 0) { + return ( +
+
+ + + +
+

{emptyMessage}

+
+ ); + } + + return ( +
+ {/* Filters */} + {allowFiltering && filterTypes.length > 1 && ( +
+ + {filterTypes.map((type) => { + const config = ACTIVITY_CONFIG[type] || { + label: 'Actividad', + icon:
, + color: 'gray', + bgColor: 'bg-[var(--bg-tertiary)]', + textColor: 'text-[var(--text-secondary)]', + borderColor: 'border-[var(--border-primary)]' + }; + const count = activities.filter(a => a.type === type).length; + + return ( + + ); + })} + + {onRefresh && ( +
+ +
+ )} +
+ )} + + {/* Activity list */} +
+ {filteredActivities.map(renderActivityItem)} +
+ + {/* Loading state */} + {isLoading && ( +
+
+
+ )} + + {/* Load more */} + {hasMore && onLoadMore && !isLoading && ( +
+ +
+ )} +
+ ); +}; + +RecentActivity.displayName = 'RecentActivity'; + +export default RecentActivity; \ No newline at end of file diff --git a/frontend/src/components/domain/dashboard/RecentActivity.tsx.backup b/frontend/src/components/domain/dashboard/RecentActivity.tsx.backup new file mode 100644 index 00000000..98ea6d30 --- /dev/null +++ b/frontend/src/components/domain/dashboard/RecentActivity.tsx.backup @@ -0,0 +1,524 @@ +import React, { useMemo, useState, useCallback } from 'react'; +import { clsx } from 'clsx'; +import { Avatar, Badge, Button } from '../../ui'; + +export interface ActivityUser { + id: string; + name: string; + avatar?: string; + role?: string; +} + +export interface ActivityItem { + id: string; + type: ActivityType; + title: string; + description: string; + timestamp: string; + user?: ActivityUser; + metadata?: Record; + status?: ActivityStatus; + category?: string; + + // Navigation support + href?: string; + onClick?: () => void; + + // Visual styling + icon?: React.ReactNode; + color?: string; + priority?: ActivityPriority; +} + +export enum ActivityType { + ORDER = 'order', + PRODUCTION = 'production', + INVENTORY = 'inventory', + SALES = 'sales', + USER = 'user', + SYSTEM = 'system', + QUALITY = 'quality', + SUPPLIER = 'supplier', + FINANCE = 'finance', + ALERT = 'alert' +} + +export enum ActivityStatus { + SUCCESS = 'success', + WARNING = 'warning', + ERROR = 'error', + INFO = 'info', + PENDING = 'pending', + IN_PROGRESS = 'in_progress', + CANCELLED = 'cancelled' +} + +export enum ActivityPriority { + LOW = 'low', + MEDIUM = 'medium', + HIGH = 'high', + URGENT = 'urgent' +} + +export interface RecentActivityProps { + activities: ActivityItem[]; + + // Display configuration + maxItems?: number; + showTimestamp?: boolean; + showUserAvatar?: boolean; + showTypeIcons?: boolean; + compact?: boolean; + + // Filtering + allowFiltering?: boolean; + filterTypes?: ActivityType[]; + defaultFilter?: ActivityType | 'all'; + + // Pagination and loading + hasMore?: boolean; + isLoading?: boolean; + onLoadMore?: () => void; + + // Event handlers + onActivityClick?: (activity: ActivityItem) => void; + onRefresh?: () => void; + + // Accessibility and styling + className?: string; + 'aria-label'?: string; + emptyMessage?: string; +} + +// Spanish activity type labels and icons +const ACTIVITY_CONFIG = { + [ActivityType.ORDER]: { + label: 'Pedidos', + icon: ( + + + + ), + color: 'blue', + bgColor: 'bg-blue-100', + textColor: 'text-blue-600', + borderColor: 'border-blue-200' + }, + [ActivityType.PRODUCTION]: { + label: 'Producción', + icon: ( + + + + ), + color: 'orange', + bgColor: 'bg-orange-100', + textColor: 'text-orange-600', + borderColor: 'border-orange-200' + }, + [ActivityType.INVENTORY]: { + label: 'Inventario', + icon: ( + + + + ), + color: 'purple', + bgColor: 'bg-purple-100', + textColor: 'text-purple-600', + borderColor: 'border-purple-200' + }, + [ActivityType.SALES]: { + label: 'Ventas', + icon: ( + + + + ), + color: 'green', + bgColor: 'bg-green-100', + textColor: 'text-green-600', + borderColor: 'border-green-200' + }, + [ActivityType.USER]: { + label: 'Usuarios', + icon: ( + + + + ), + color: 'indigo', + bgColor: 'bg-indigo-100', + textColor: 'text-indigo-600', + borderColor: 'border-indigo-200' + }, + [ActivityType.SYSTEM]: { + label: 'Sistema', + icon: ( + + + + + ), + color: 'gray', + bgColor: 'bg-gray-100', + textColor: 'text-gray-600', + borderColor: 'border-gray-200' + }, + [ActivityType.QUALITY]: { + label: 'Calidad', + icon: ( + + + + ), + color: 'green', + bgColor: 'bg-green-100', + textColor: 'text-green-600', + borderColor: 'border-green-200' + }, + [ActivityType.SUPPLIER]: { + label: 'Proveedores', + icon: ( + + + + ), + color: 'teal', + bgColor: 'bg-teal-100', + textColor: 'text-teal-600', + borderColor: 'border-teal-200' + }, + [ActivityType.FINANCE]: { + label: 'Finanzas', + icon: ( + + + + ), + color: 'emerald', + bgColor: 'bg-emerald-100', + textColor: 'text-emerald-600', + borderColor: 'border-emerald-200' + }, + [ActivityType.ALERT]: { + label: 'Alertas', + icon: ( + + + + ), + color: 'red', + bgColor: 'bg-red-100', + textColor: 'text-red-600', + borderColor: 'border-red-200' + } +}; + +const STATUS_CONFIG = { + [ActivityStatus.SUCCESS]: { color: 'green', bgColor: 'bg-green-500' }, + [ActivityStatus.WARNING]: { color: 'yellow', bgColor: 'bg-yellow-500' }, + [ActivityStatus.ERROR]: { color: 'red', bgColor: 'bg-red-500' }, + [ActivityStatus.INFO]: { color: 'blue', bgColor: 'bg-blue-500' }, + [ActivityStatus.PENDING]: { color: 'gray', bgColor: 'bg-gray-500' }, + [ActivityStatus.IN_PROGRESS]: { color: 'purple', bgColor: 'bg-purple-500' }, + [ActivityStatus.CANCELLED]: { color: 'gray', bgColor: 'bg-gray-400' } +}; + +const formatRelativeTime = (timestamp: string): string => { + const date = new Date(timestamp); + const now = new Date(); + const diffInMs = now.getTime() - date.getTime(); + const diffInMinutes = Math.floor(diffInMs / (1000 * 60)); + const diffInHours = Math.floor(diffInMinutes / 60); + const diffInDays = Math.floor(diffInHours / 24); + + if (diffInMinutes < 1) { + return 'Ahora mismo'; + } else if (diffInMinutes < 60) { + return `Hace ${diffInMinutes} min`; + } else if (diffInHours < 24) { + return `Hace ${diffInHours}h`; + } else if (diffInDays === 1) { + return 'Ayer'; + } else if (diffInDays < 7) { + return `Hace ${diffInDays} días`; + } else { + return date.toLocaleDateString('es-ES', { + day: 'numeric', + month: 'short' + }); + } +}; + +const RecentActivity: React.FC = ({ + activities, + maxItems = 10, + showTimestamp = true, + showUserAvatar = true, + showTypeIcons = true, + compact = false, + allowFiltering = true, + filterTypes = Object.values(ActivityType), + defaultFilter = 'all', + hasMore = false, + isLoading = false, + onLoadMore, + onActivityClick, + onRefresh, + className, + 'aria-label': ariaLabel = 'Actividad reciente', + emptyMessage = 'No hay actividad reciente' +}) => { + const [activeFilter, setActiveFilter] = useState(defaultFilter); + + const filteredActivities = useMemo(() => { + let filtered = activities; + + if (activeFilter !== 'all') { + filtered = activities.filter(activity => activity.type === activeFilter); + } + + return filtered.slice(0, maxItems); + }, [activities, activeFilter, maxItems]); + + const handleActivityClick = useCallback((activity: ActivityItem) => { + onActivityClick?.(activity); + if (activity.onClick) { + activity.onClick(); + } + }, [onActivityClick]); + + const renderActivityItem = (activity: ActivityItem) => { + const config = ACTIVITY_CONFIG[activity.type] || { + label: 'Actividad', + icon:
, + color: 'gray', + bgColor: 'bg-gray-100', + textColor: 'text-gray-600', + borderColor: 'border-gray-200' + }; + const statusConfig = activity.status ? STATUS_CONFIG[activity.status] : null; + + const itemClasses = clsx( + 'group relative flex items-start gap-3 p-3 rounded-lg transition-all duration-200', + 'hover:bg-gray-50 hover:shadow-sm', + { + 'cursor-pointer': activity.onClick || activity.href, + 'p-2': compact, + 'border-l-4': !compact, + [config.borderColor]: !compact + } + ); + + return ( +
handleActivityClick(activity) : undefined} + role={activity.onClick || activity.href ? 'button' : undefined} + tabIndex={activity.onClick || activity.href ? 0 : undefined} + > + {/* Timeline indicator */} +
+ {showTypeIcons && ( +
+ {activity.icon || config.icon} +
+ )} + + {/* Status indicator */} + {activity.status && statusConfig && ( +
+ )} +
+ + {/* Content */} +
+
+
+

+ {activity.title} +

+

+ {activity.description} +

+ + {/* User info */} + {activity.user && showUserAvatar && ( +
+ + + {activity.user.name} + +
+ )} +
+ + {/* Timestamp */} + {showTimestamp && ( + + )} +
+
+
+ ); + }; + + if (activities.length === 0) { + return ( +
+
+ + + +
+

{emptyMessage}

+
+ ); + } + + return ( +
+ {/* Filters */} + {allowFiltering && filterTypes.length > 1 && ( +
+ + {filterTypes.map((type) => { + const config = ACTIVITY_CONFIG[type] || { + label: 'Actividad', + icon:
, + color: 'gray', + bgColor: 'bg-gray-100', + textColor: 'text-gray-600', + borderColor: 'border-gray-200' + }; + const count = activities.filter(a => a.type === type).length; + + return ( + + ); + })} + + {onRefresh && ( +
+ +
+ )} +
+ )} + + {/* Activity list */} +
+ {filteredActivities.map(renderActivityItem)} +
+ + {/* Loading state */} + {isLoading && ( +
+
+
+ )} + + {/* Load more */} + {hasMore && onLoadMore && !isLoading && ( +
+ +
+ )} +
+ ); +}; + +RecentActivity.displayName = 'RecentActivity'; + +export default RecentActivity; \ No newline at end of file diff --git a/frontend/src/components/domain/dashboard/index.ts b/frontend/src/components/domain/dashboard/index.ts new file mode 100644 index 00000000..b75a795b --- /dev/null +++ b/frontend/src/components/domain/dashboard/index.ts @@ -0,0 +1,88 @@ +// Dashboard Domain Components - Bakery Management System + +// Core dashboard components +export { default as DashboardCard } from './DashboardCard'; +export { default as DashboardGrid } from './DashboardGrid'; +export { default as QuickActions, BAKERY_QUICK_ACTIONS } from './QuickActions'; +export { default as RecentActivity } from './RecentActivity'; +export { default as KPIWidget, BAKERY_KPI_CONFIGS } from './KPIWidget'; + +// Export types for external usage +export type { DashboardCardProps } from './DashboardCard'; +export type { + QuickActionsProps, + QuickAction +} from './QuickActions'; +export type { + RecentActivityProps, + ActivityItem, + ActivityUser, + ActivityType, + ActivityStatus, + ActivityPriority +} from './RecentActivity'; +export type { + KPIWidgetProps, + KPIValue, + KPITrend, + KPIThreshold, + SparklineDataPoint +} from './KPIWidget'; + +// Re-export enums for convenience +export { + ActivityType, + ActivityStatus, + ActivityPriority +} from './RecentActivity'; + +/** + * Dashboard Components Usage Examples: + * + * import { + * DashboardCard, + * QuickActions, + * BAKERY_QUICK_ACTIONS, + * RecentActivity, + * KPIWidget, + * BAKERY_KPI_CONFIGS + * } from '@/components/domain/dashboard'; + * + * // Basic dashboard card + * + * Content goes here + * + * + * // Quick actions with predefined bakery actions + * + * + * // Recent activity feed + * + * + * // KPI widget with Spanish formatting + * + */ \ No newline at end of file diff --git a/frontend/src/components/domain/forecasting/AlertsPanel.tsx b/frontend/src/components/domain/forecasting/AlertsPanel.tsx new file mode 100644 index 00000000..d6eb442f --- /dev/null +++ b/frontend/src/components/domain/forecasting/AlertsPanel.tsx @@ -0,0 +1,725 @@ +import React, { useState, useMemo, useCallback } from 'react'; +import { clsx } from 'clsx'; +import { Card, CardHeader, CardBody } from '../../ui'; +import { Button } from '../../ui'; +import { Badge } from '../../ui'; +import { Select } from '../../ui'; +import { Input } from '../../ui'; +import { + ForecastAlert, + ForecastAlertType, + AlertSeverity, +} from '../../../types/forecasting.types'; + +export interface AlertsPanelProps { + className?: string; + title?: string; + alerts?: ForecastAlert[]; + loading?: boolean; + error?: string | null; + onAlertAction?: (alertId: string, action: 'acknowledge' | 'resolve' | 'snooze' | 'production_adjust' | 'inventory_check') => void; + onAlertDismiss?: (alertId: string) => void; + onBulkAction?: (alertIds: string[], action: string) => void; + showFilters?: boolean; + compact?: boolean; + maxItems?: number; + autoRefresh?: boolean; + refreshInterval?: number; +} + +interface AlertFilter { + severity: AlertSeverity | 'all'; + type: ForecastAlertType | 'all'; + status: 'active' | 'acknowledged' | 'resolved' | 'all'; + product: string; + dateRange: 'today' | 'week' | 'month' | 'all'; +} + +interface AlertActionGroup { + critical: AlertAction[]; + high: AlertAction[]; + medium: AlertAction[]; + low: AlertAction[]; +} + +interface AlertAction { + id: string; + label: string; + icon: string; + variant: 'primary' | 'secondary' | 'success' | 'warning' | 'danger'; + description: string; +} + +const SPANISH_ALERT_TYPES: Record = { + [ForecastAlertType.HIGH_DEMAND_PREDICTED]: 'Alta Demanda Predicha', + [ForecastAlertType.LOW_DEMAND_PREDICTED]: 'Baja Demanda Predicha', + [ForecastAlertType.ACCURACY_DROP]: 'Caída de Precisión', + [ForecastAlertType.MODEL_DRIFT]: 'Deriva del Modelo', + [ForecastAlertType.DATA_ANOMALY]: 'Anomalía de Datos', + [ForecastAlertType.MISSING_DATA]: 'Datos Faltantes', + [ForecastAlertType.SEASONAL_SHIFT]: 'Cambio Estacional', +}; + +const SPANISH_SEVERITIES: Record = { + [AlertSeverity.CRITICAL]: 'Crítica', + [AlertSeverity.HIGH]: 'Alta', + [AlertSeverity.MEDIUM]: 'Media', + [AlertSeverity.LOW]: 'Baja', +}; + +const SEVERITY_COLORS: Record = { + [AlertSeverity.CRITICAL]: 'text-[var(--color-error)] bg-red-50 border-red-200', + [AlertSeverity.HIGH]: 'text-[var(--color-primary)] bg-orange-50 border-orange-200', + [AlertSeverity.MEDIUM]: 'text-yellow-600 bg-yellow-50 border-yellow-200', + [AlertSeverity.LOW]: 'text-[var(--color-info)] bg-[var(--color-info)]/5 border-[var(--color-info)]/20', +}; + +const SEVERITY_BADGE_VARIANTS: Record = { + [AlertSeverity.CRITICAL]: 'danger', + [AlertSeverity.HIGH]: 'warning', + [AlertSeverity.MEDIUM]: 'warning', + [AlertSeverity.LOW]: 'info', +}; + +const ALERT_TYPE_ICONS: Record = { + [ForecastAlertType.HIGH_DEMAND_PREDICTED]: '📈', + [ForecastAlertType.LOW_DEMAND_PREDICTED]: '📉', + [ForecastAlertType.ACCURACY_DROP]: '🎯', + [ForecastAlertType.MODEL_DRIFT]: '🔄', + [ForecastAlertType.DATA_ANOMALY]: '⚠️', + [ForecastAlertType.MISSING_DATA]: '📊', + [ForecastAlertType.SEASONAL_SHIFT]: '🍂', +}; + +const ALERT_ACTIONS: AlertActionGroup = { + critical: [ + { + id: 'production_adjust', + label: 'Ajustar Producción', + icon: '🏭', + variant: 'primary', + description: 'Modificar inmediatamente el plan de producción', + }, + { + id: 'inventory_check', + label: 'Verificar Inventario', + icon: '📦', + variant: 'warning', + description: 'Revisar niveles de stock y materias primas', + }, + { + id: 'emergency_order', + label: 'Pedido Urgente', + icon: '🚚', + variant: 'danger', + description: 'Realizar pedido urgente a proveedores', + }, + ], + high: [ + { + id: 'production_adjust', + label: 'Ajustar Producción', + icon: '🏭', + variant: 'primary', + description: 'Ajustar plan de producción para mañana', + }, + { + id: 'inventory_alert', + label: 'Alerta Inventario', + icon: '📋', + variant: 'warning', + description: 'Crear alerta de inventario preventiva', + }, + { + id: 'team_notify', + label: 'Notificar Equipo', + icon: '👥', + variant: 'secondary', + description: 'Informar al equipo de producción', + }, + ], + medium: [ + { + id: 'production_review', + label: 'Revisar Producción', + icon: '📊', + variant: 'secondary', + description: 'Revisar plan de producción esta semana', + }, + { + id: 'monitor', + label: 'Monitorear', + icon: '👁️', + variant: 'secondary', + description: 'Mantener bajo observación', + }, + ], + low: [ + { + id: 'monitor', + label: 'Monitorear', + icon: '👁️', + variant: 'secondary', + description: 'Mantener bajo observación', + }, + { + id: 'data_review', + label: 'Revisar Datos', + icon: '🔍', + variant: 'secondary', + description: 'Revisar calidad de los datos', + }, + ], +}; + +const RECOMMENDATION_MESSAGES: Record string> = { + [ForecastAlertType.HIGH_DEMAND_PREDICTED]: (product, value) => + `Se predice un aumento del ${value}% en la demanda de ${product}. Considera aumentar la producción.`, + [ForecastAlertType.LOW_DEMAND_PREDICTED]: (product, value) => + `Se predice una disminución del ${Math.abs(value)}% en la demanda de ${product}. Considera reducir la producción para evitar desperdicios.`, + [ForecastAlertType.ACCURACY_DROP]: (product, value) => + `La precisión del modelo para ${product} ha disminuido al ${value}%. Es recomendable reentrenar el modelo.`, + [ForecastAlertType.MODEL_DRIFT]: (product, value) => + `Detectada deriva en el modelo de ${product}. Los patrones de demanda han cambiado significativamente.`, + [ForecastAlertType.DATA_ANOMALY]: (product, value) => + `Anomalía detectada en los datos de ${product}. Verifica la calidad de los datos de entrada.`, + [ForecastAlertType.MISSING_DATA]: (product, value) => + `Faltan ${value} días de datos para ${product}. Esto puede afectar la precisión de las predicciones.`, + [ForecastAlertType.SEASONAL_SHIFT]: (product, value) => + `Detectado cambio en el patrón estacional de ${product}. El pico de demanda se ha adelantado/retrasado.`, +}; + +const AlertsPanel: React.FC = ({ + className, + title = 'Alertas y Recomendaciones', + alerts = [], + loading = false, + error = null, + onAlertAction, + onAlertDismiss, + onBulkAction, + showFilters = true, + compact = false, + maxItems, + autoRefresh = false, + refreshInterval = 30000, +}) => { + const [filters, setFilters] = useState({ + severity: 'all', + type: 'all', + status: 'active', + product: '', + dateRange: 'all', + }); + + const [selectedAlerts, setSelectedAlerts] = useState([]); + const [expandedAlerts, setExpandedAlerts] = useState([]); + + // Filter alerts + const filteredAlerts = useMemo(() => { + let filtered = [...alerts]; + + // Filter by status + if (filters.status !== 'all') { + filtered = filtered.filter(alert => { + if (filters.status === 'active') return alert.is_active && !alert.acknowledged_at && !alert.resolved_at; + if (filters.status === 'acknowledged') return !!alert.acknowledged_at && !alert.resolved_at; + if (filters.status === 'resolved') return !!alert.resolved_at; + return true; + }); + } + + // Filter by severity + if (filters.severity !== 'all') { + filtered = filtered.filter(alert => alert.severity === filters.severity); + } + + // Filter by type + if (filters.type !== 'all') { + filtered = filtered.filter(alert => alert.alert_type === filters.type); + } + + // Filter by product + if (filters.product) { + filtered = filtered.filter(alert => + alert.product_name?.toLowerCase().includes(filters.product.toLowerCase()) + ); + } + + // Filter by date range + if (filters.dateRange !== 'all') { + const now = new Date(); + const filterDate = new Date(); + + switch (filters.dateRange) { + case 'today': + filterDate.setHours(0, 0, 0, 0); + break; + case 'week': + filterDate.setDate(now.getDate() - 7); + break; + case 'month': + filterDate.setMonth(now.getMonth() - 1); + break; + } + + filtered = filtered.filter(alert => new Date(alert.created_at) >= filterDate); + } + + // Sort by severity and creation date + filtered.sort((a, b) => { + const severityOrder: Record = { + [AlertSeverity.CRITICAL]: 4, + [AlertSeverity.HIGH]: 3, + [AlertSeverity.MEDIUM]: 2, + [AlertSeverity.LOW]: 1, + }; + + if (a.severity !== b.severity) { + return severityOrder[b.severity] - severityOrder[a.severity]; + } + + return new Date(b.created_at).getTime() - new Date(a.created_at).getTime(); + }); + + // Limit items if specified + if (maxItems) { + filtered = filtered.slice(0, maxItems); + } + + return filtered; + }, [alerts, filters, maxItems]); + + // Get alert statistics + const alertStats = useMemo(() => { + const stats = { + total: alerts.length, + active: alerts.filter(a => a.is_active && !a.acknowledged_at && !a.resolved_at).length, + critical: alerts.filter(a => a.severity === AlertSeverity.CRITICAL && a.is_active).length, + high: alerts.filter(a => a.severity === AlertSeverity.HIGH && a.is_active).length, + }; + return stats; + }, [alerts]); + + // Handle alert expansion + const toggleAlertExpansion = useCallback((alertId: string) => { + setExpandedAlerts(prev => + prev.includes(alertId) + ? prev.filter(id => id !== alertId) + : [...prev, alertId] + ); + }, []); + + // Handle alert selection + const toggleAlertSelection = useCallback((alertId: string) => { + setSelectedAlerts(prev => + prev.includes(alertId) + ? prev.filter(id => id !== alertId) + : [...prev, alertId] + ); + }, []); + + // Handle bulk selection + const handleSelectAll = useCallback(() => { + const activeAlerts = filteredAlerts.filter(a => a.is_active).map(a => a.id); + setSelectedAlerts(prev => + prev.length === activeAlerts.length ? [] : activeAlerts + ); + }, [filteredAlerts]); + + // Get available actions for alert + const getAlertActions = useCallback((alert: ForecastAlert): AlertAction[] => { + return ALERT_ACTIONS[alert.severity] || []; + }, []); + + // Get recommendation message + const getRecommendation = useCallback((alert: ForecastAlert): string => { + const generator = RECOMMENDATION_MESSAGES[alert.alert_type]; + if (!generator) return alert.message; + + const value = alert.predicted_value || alert.threshold_value || 0; + return generator(alert.product_name || 'Producto', value); + }, []); + + // Format time ago + const formatTimeAgo = useCallback((dateString: string): string => { + const date = new Date(dateString); + const now = new Date(); + const diffMs = now.getTime() - date.getTime(); + const diffMins = Math.floor(diffMs / (1000 * 60)); + const diffHours = Math.floor(diffMins / 60); + const diffDays = Math.floor(diffHours / 24); + + if (diffMins < 1) return 'Ahora mismo'; + if (diffMins < 60) return `Hace ${diffMins} min`; + if (diffHours < 24) return `Hace ${diffHours}h`; + if (diffDays < 7) return `Hace ${diffDays}d`; + return date.toLocaleDateString('es-ES'); + }, []); + + // Loading state + if (loading) { + return ( + + +

{title}

+
+ +
+
+
+ Cargando alertas... +
+
+
+
+ ); + } + + // Error state + if (error) { + return ( + + +

{title}

+
+ +
+
+
+ + + +
+

{error}

+
+
+
+
+ ); + } + + return ( + + +
+
+

{title}

+ {alertStats.critical > 0 && ( + + {alertStats.critical} críticas + + )} + {alertStats.high > 0 && ( + + {alertStats.high} altas + + )} + + {alertStats.active}/{alertStats.total} activas + +
+ +
+ {selectedAlerts.length > 0 && onBulkAction && ( +
+ + +
+ )} + + {autoRefresh && ( +
+
+ Auto-actualización +
+ )} +
+
+
+ + {showFilters && ( +
+
+ + + + + + + setFilters(prev => ({ ...prev, product: e.target.value }))} + className="text-sm" + /> + + +
+
+ )} + + + {filteredAlerts.length === 0 ? ( +
+
+
+ + + +
+

No hay alertas que mostrar

+
+
+ ) : ( +
+ {/* Bulk actions bar */} + {filteredAlerts.some(a => a.is_active) && ( +
+ a.is_active).length} + onChange={handleSelectAll} + /> + + {selectedAlerts.length > 0 + ? `${selectedAlerts.length} alertas seleccionadas` + : 'Seleccionar todas' + } + +
+ )} + + {/* Alerts list */} + {filteredAlerts.map((alert) => { + const isExpanded = expandedAlerts.includes(alert.id); + const isSelected = selectedAlerts.includes(alert.id); + const availableActions = getAlertActions(alert); + const recommendation = getRecommendation(alert); + + return ( +
+
+
+ {/* Selection checkbox */} + {alert.is_active && !alert.resolved_at && ( + toggleAlertSelection(alert.id)} + /> + )} + + {/* Alert icon */} +
+ {ALERT_TYPE_ICONS[alert.alert_type]} +
+ + {/* Alert content */} +
+
+

+ {alert.title} +

+ + {SPANISH_SEVERITIES[alert.severity]} + + {alert.product_name && ( + + {alert.product_name} + + )} +
+ +

+ {compact ? alert.message.slice(0, 100) + '...' : alert.message} +

+ + {!compact && ( +

+ 💡 {recommendation} +

+ )} + +
+
+ {formatTimeAgo(alert.created_at)} + {alert.acknowledged_at && ( + • Confirmada + )} + {alert.resolved_at && ( + • Resuelta + )} +
+ +
+ {/* Quick actions */} + {alert.is_active && !alert.resolved_at && ( +
+ {availableActions.slice(0, compact ? 1 : 2).map((action) => ( + + ))} + + {availableActions.length > (compact ? 1 : 2) && ( + + )} +
+ )} + + {/* Dismiss button */} + {onAlertDismiss && alert.is_active && ( + + )} +
+
+
+
+ + {/* Expanded actions */} + {isExpanded && availableActions.length > (compact ? 1 : 2) && ( +
+
+ {availableActions.slice(compact ? 1 : 2).map((action) => ( + + ))} +
+
+ )} + + {/* Alert details */} + {!compact && isExpanded && ( +
+
+
+ Tipo: +
{SPANISH_ALERT_TYPES[alert.alert_type]}
+
+ {alert.predicted_value && ( +
+ Valor Predicho: +
{alert.predicted_value}
+
+ )} + {alert.threshold_value && ( +
+ Umbral: +
{alert.threshold_value}
+
+ )} + {alert.model_accuracy && ( +
+ Precisión Modelo: +
{(alert.model_accuracy * 100).toFixed(1)}%
+
+ )} +
+
+ )} +
+
+ ); + })} +
+ )} +
+
+ ); +}; + +export default AlertsPanel; \ No newline at end of file diff --git a/frontend/src/components/domain/forecasting/AlertsPanel.tsx.backup b/frontend/src/components/domain/forecasting/AlertsPanel.tsx.backup new file mode 100644 index 00000000..644953ba --- /dev/null +++ b/frontend/src/components/domain/forecasting/AlertsPanel.tsx.backup @@ -0,0 +1,725 @@ +import React, { useState, useMemo, useCallback } from 'react'; +import { clsx } from 'clsx'; +import { Card, CardHeader, CardBody } from '../../ui'; +import { Button } from '../../ui'; +import { Badge } from '../../ui'; +import { Select } from '../../ui'; +import { Input } from '../../ui'; +import { + ForecastAlert, + ForecastAlertType, + AlertSeverity, +} from '../../../types/forecasting.types'; + +export interface AlertsPanelProps { + className?: string; + title?: string; + alerts?: ForecastAlert[]; + loading?: boolean; + error?: string | null; + onAlertAction?: (alertId: string, action: 'acknowledge' | 'resolve' | 'snooze' | 'production_adjust' | 'inventory_check') => void; + onAlertDismiss?: (alertId: string) => void; + onBulkAction?: (alertIds: string[], action: string) => void; + showFilters?: boolean; + compact?: boolean; + maxItems?: number; + autoRefresh?: boolean; + refreshInterval?: number; +} + +interface AlertFilter { + severity: AlertSeverity | 'all'; + type: ForecastAlertType | 'all'; + status: 'active' | 'acknowledged' | 'resolved' | 'all'; + product: string; + dateRange: 'today' | 'week' | 'month' | 'all'; +} + +interface AlertActionGroup { + critical: AlertAction[]; + high: AlertAction[]; + medium: AlertAction[]; + low: AlertAction[]; +} + +interface AlertAction { + id: string; + label: string; + icon: string; + variant: 'primary' | 'secondary' | 'success' | 'warning' | 'danger'; + description: string; +} + +const SPANISH_ALERT_TYPES: Record = { + [ForecastAlertType.HIGH_DEMAND_PREDICTED]: 'Alta Demanda Predicha', + [ForecastAlertType.LOW_DEMAND_PREDICTED]: 'Baja Demanda Predicha', + [ForecastAlertType.ACCURACY_DROP]: 'Caída de Precisión', + [ForecastAlertType.MODEL_DRIFT]: 'Deriva del Modelo', + [ForecastAlertType.DATA_ANOMALY]: 'Anomalía de Datos', + [ForecastAlertType.MISSING_DATA]: 'Datos Faltantes', + [ForecastAlertType.SEASONAL_SHIFT]: 'Cambio Estacional', +}; + +const SPANISH_SEVERITIES: Record = { + [AlertSeverity.CRITICAL]: 'Crítica', + [AlertSeverity.HIGH]: 'Alta', + [AlertSeverity.MEDIUM]: 'Media', + [AlertSeverity.LOW]: 'Baja', +}; + +const SEVERITY_COLORS: Record = { + [AlertSeverity.CRITICAL]: 'text-red-600 bg-red-50 border-red-200', + [AlertSeverity.HIGH]: 'text-orange-600 bg-orange-50 border-orange-200', + [AlertSeverity.MEDIUM]: 'text-yellow-600 bg-yellow-50 border-yellow-200', + [AlertSeverity.LOW]: 'text-blue-600 bg-blue-50 border-blue-200', +}; + +const SEVERITY_BADGE_VARIANTS: Record = { + [AlertSeverity.CRITICAL]: 'danger', + [AlertSeverity.HIGH]: 'warning', + [AlertSeverity.MEDIUM]: 'warning', + [AlertSeverity.LOW]: 'info', +}; + +const ALERT_TYPE_ICONS: Record = { + [ForecastAlertType.HIGH_DEMAND_PREDICTED]: '📈', + [ForecastAlertType.LOW_DEMAND_PREDICTED]: '📉', + [ForecastAlertType.ACCURACY_DROP]: '🎯', + [ForecastAlertType.MODEL_DRIFT]: '🔄', + [ForecastAlertType.DATA_ANOMALY]: '⚠️', + [ForecastAlertType.MISSING_DATA]: '📊', + [ForecastAlertType.SEASONAL_SHIFT]: '🍂', +}; + +const ALERT_ACTIONS: AlertActionGroup = { + critical: [ + { + id: 'production_adjust', + label: 'Ajustar Producción', + icon: '🏭', + variant: 'primary', + description: 'Modificar inmediatamente el plan de producción', + }, + { + id: 'inventory_check', + label: 'Verificar Inventario', + icon: '📦', + variant: 'warning', + description: 'Revisar niveles de stock y materias primas', + }, + { + id: 'emergency_order', + label: 'Pedido Urgente', + icon: '🚚', + variant: 'danger', + description: 'Realizar pedido urgente a proveedores', + }, + ], + high: [ + { + id: 'production_adjust', + label: 'Ajustar Producción', + icon: '🏭', + variant: 'primary', + description: 'Ajustar plan de producción para mañana', + }, + { + id: 'inventory_alert', + label: 'Alerta Inventario', + icon: '📋', + variant: 'warning', + description: 'Crear alerta de inventario preventiva', + }, + { + id: 'team_notify', + label: 'Notificar Equipo', + icon: '👥', + variant: 'secondary', + description: 'Informar al equipo de producción', + }, + ], + medium: [ + { + id: 'production_review', + label: 'Revisar Producción', + icon: '📊', + variant: 'secondary', + description: 'Revisar plan de producción esta semana', + }, + { + id: 'monitor', + label: 'Monitorear', + icon: '👁️', + variant: 'secondary', + description: 'Mantener bajo observación', + }, + ], + low: [ + { + id: 'monitor', + label: 'Monitorear', + icon: '👁️', + variant: 'secondary', + description: 'Mantener bajo observación', + }, + { + id: 'data_review', + label: 'Revisar Datos', + icon: '🔍', + variant: 'secondary', + description: 'Revisar calidad de los datos', + }, + ], +}; + +const RECOMMENDATION_MESSAGES: Record string> = { + [ForecastAlertType.HIGH_DEMAND_PREDICTED]: (product, value) => + `Se predice un aumento del ${value}% en la demanda de ${product}. Considera aumentar la producción.`, + [ForecastAlertType.LOW_DEMAND_PREDICTED]: (product, value) => + `Se predice una disminución del ${Math.abs(value)}% en la demanda de ${product}. Considera reducir la producción para evitar desperdicios.`, + [ForecastAlertType.ACCURACY_DROP]: (product, value) => + `La precisión del modelo para ${product} ha disminuido al ${value}%. Es recomendable reentrenar el modelo.`, + [ForecastAlertType.MODEL_DRIFT]: (product, value) => + `Detectada deriva en el modelo de ${product}. Los patrones de demanda han cambiado significativamente.`, + [ForecastAlertType.DATA_ANOMALY]: (product, value) => + `Anomalía detectada en los datos de ${product}. Verifica la calidad de los datos de entrada.`, + [ForecastAlertType.MISSING_DATA]: (product, value) => + `Faltan ${value} días de datos para ${product}. Esto puede afectar la precisión de las predicciones.`, + [ForecastAlertType.SEASONAL_SHIFT]: (product, value) => + `Detectado cambio en el patrón estacional de ${product}. El pico de demanda se ha adelantado/retrasado.`, +}; + +const AlertsPanel: React.FC = ({ + className, + title = 'Alertas y Recomendaciones', + alerts = [], + loading = false, + error = null, + onAlertAction, + onAlertDismiss, + onBulkAction, + showFilters = true, + compact = false, + maxItems, + autoRefresh = false, + refreshInterval = 30000, +}) => { + const [filters, setFilters] = useState({ + severity: 'all', + type: 'all', + status: 'active', + product: '', + dateRange: 'all', + }); + + const [selectedAlerts, setSelectedAlerts] = useState([]); + const [expandedAlerts, setExpandedAlerts] = useState([]); + + // Filter alerts + const filteredAlerts = useMemo(() => { + let filtered = [...alerts]; + + // Filter by status + if (filters.status !== 'all') { + filtered = filtered.filter(alert => { + if (filters.status === 'active') return alert.is_active && !alert.acknowledged_at && !alert.resolved_at; + if (filters.status === 'acknowledged') return !!alert.acknowledged_at && !alert.resolved_at; + if (filters.status === 'resolved') return !!alert.resolved_at; + return true; + }); + } + + // Filter by severity + if (filters.severity !== 'all') { + filtered = filtered.filter(alert => alert.severity === filters.severity); + } + + // Filter by type + if (filters.type !== 'all') { + filtered = filtered.filter(alert => alert.alert_type === filters.type); + } + + // Filter by product + if (filters.product) { + filtered = filtered.filter(alert => + alert.product_name?.toLowerCase().includes(filters.product.toLowerCase()) + ); + } + + // Filter by date range + if (filters.dateRange !== 'all') { + const now = new Date(); + const filterDate = new Date(); + + switch (filters.dateRange) { + case 'today': + filterDate.setHours(0, 0, 0, 0); + break; + case 'week': + filterDate.setDate(now.getDate() - 7); + break; + case 'month': + filterDate.setMonth(now.getMonth() - 1); + break; + } + + filtered = filtered.filter(alert => new Date(alert.created_at) >= filterDate); + } + + // Sort by severity and creation date + filtered.sort((a, b) => { + const severityOrder: Record = { + [AlertSeverity.CRITICAL]: 4, + [AlertSeverity.HIGH]: 3, + [AlertSeverity.MEDIUM]: 2, + [AlertSeverity.LOW]: 1, + }; + + if (a.severity !== b.severity) { + return severityOrder[b.severity] - severityOrder[a.severity]; + } + + return new Date(b.created_at).getTime() - new Date(a.created_at).getTime(); + }); + + // Limit items if specified + if (maxItems) { + filtered = filtered.slice(0, maxItems); + } + + return filtered; + }, [alerts, filters, maxItems]); + + // Get alert statistics + const alertStats = useMemo(() => { + const stats = { + total: alerts.length, + active: alerts.filter(a => a.is_active && !a.acknowledged_at && !a.resolved_at).length, + critical: alerts.filter(a => a.severity === AlertSeverity.CRITICAL && a.is_active).length, + high: alerts.filter(a => a.severity === AlertSeverity.HIGH && a.is_active).length, + }; + return stats; + }, [alerts]); + + // Handle alert expansion + const toggleAlertExpansion = useCallback((alertId: string) => { + setExpandedAlerts(prev => + prev.includes(alertId) + ? prev.filter(id => id !== alertId) + : [...prev, alertId] + ); + }, []); + + // Handle alert selection + const toggleAlertSelection = useCallback((alertId: string) => { + setSelectedAlerts(prev => + prev.includes(alertId) + ? prev.filter(id => id !== alertId) + : [...prev, alertId] + ); + }, []); + + // Handle bulk selection + const handleSelectAll = useCallback(() => { + const activeAlerts = filteredAlerts.filter(a => a.is_active).map(a => a.id); + setSelectedAlerts(prev => + prev.length === activeAlerts.length ? [] : activeAlerts + ); + }, [filteredAlerts]); + + // Get available actions for alert + const getAlertActions = useCallback((alert: ForecastAlert): AlertAction[] => { + return ALERT_ACTIONS[alert.severity] || []; + }, []); + + // Get recommendation message + const getRecommendation = useCallback((alert: ForecastAlert): string => { + const generator = RECOMMENDATION_MESSAGES[alert.alert_type]; + if (!generator) return alert.message; + + const value = alert.predicted_value || alert.threshold_value || 0; + return generator(alert.product_name || 'Producto', value); + }, []); + + // Format time ago + const formatTimeAgo = useCallback((dateString: string): string => { + const date = new Date(dateString); + const now = new Date(); + const diffMs = now.getTime() - date.getTime(); + const diffMins = Math.floor(diffMs / (1000 * 60)); + const diffHours = Math.floor(diffMins / 60); + const diffDays = Math.floor(diffHours / 24); + + if (diffMins < 1) return 'Ahora mismo'; + if (diffMins < 60) return `Hace ${diffMins} min`; + if (diffHours < 24) return `Hace ${diffHours}h`; + if (diffDays < 7) return `Hace ${diffDays}d`; + return date.toLocaleDateString('es-ES'); + }, []); + + // Loading state + if (loading) { + return ( + + +

{title}

+
+ +
+
+
+ Cargando alertas... +
+
+
+
+ ); + } + + // Error state + if (error) { + return ( + + +

{title}

+
+ +
+
+
+ + + +
+

{error}

+
+
+
+
+ ); + } + + return ( + + +
+
+

{title}

+ {alertStats.critical > 0 && ( + + {alertStats.critical} críticas + + )} + {alertStats.high > 0 && ( + + {alertStats.high} altas + + )} + + {alertStats.active}/{alertStats.total} activas + +
+ +
+ {selectedAlerts.length > 0 && onBulkAction && ( +
+ + +
+ )} + + {autoRefresh && ( +
+
+ Auto-actualización +
+ )} +
+
+
+ + {showFilters && ( +
+
+ + + + + + + setFilters(prev => ({ ...prev, product: e.target.value }))} + className="text-sm" + /> + + +
+
+ )} + + + {filteredAlerts.length === 0 ? ( +
+
+
+ + + +
+

No hay alertas que mostrar

+
+
+ ) : ( +
+ {/* Bulk actions bar */} + {filteredAlerts.some(a => a.is_active) && ( +
+ a.is_active).length} + onChange={handleSelectAll} + /> + + {selectedAlerts.length > 0 + ? `${selectedAlerts.length} alertas seleccionadas` + : 'Seleccionar todas' + } + +
+ )} + + {/* Alerts list */} + {filteredAlerts.map((alert) => { + const isExpanded = expandedAlerts.includes(alert.id); + const isSelected = selectedAlerts.includes(alert.id); + const availableActions = getAlertActions(alert); + const recommendation = getRecommendation(alert); + + return ( +
+
+
+ {/* Selection checkbox */} + {alert.is_active && !alert.resolved_at && ( + toggleAlertSelection(alert.id)} + /> + )} + + {/* Alert icon */} +
+ {ALERT_TYPE_ICONS[alert.alert_type]} +
+ + {/* Alert content */} +
+
+

+ {alert.title} +

+ + {SPANISH_SEVERITIES[alert.severity]} + + {alert.product_name && ( + + {alert.product_name} + + )} +
+ +

+ {compact ? alert.message.slice(0, 100) + '...' : alert.message} +

+ + {!compact && ( +

+ 💡 {recommendation} +

+ )} + +
+
+ {formatTimeAgo(alert.created_at)} + {alert.acknowledged_at && ( + • Confirmada + )} + {alert.resolved_at && ( + • Resuelta + )} +
+ +
+ {/* Quick actions */} + {alert.is_active && !alert.resolved_at && ( +
+ {availableActions.slice(0, compact ? 1 : 2).map((action) => ( + + ))} + + {availableActions.length > (compact ? 1 : 2) && ( + + )} +
+ )} + + {/* Dismiss button */} + {onAlertDismiss && alert.is_active && ( + + )} +
+
+
+
+ + {/* Expanded actions */} + {isExpanded && availableActions.length > (compact ? 1 : 2) && ( +
+
+ {availableActions.slice(compact ? 1 : 2).map((action) => ( + + ))} +
+
+ )} + + {/* Alert details */} + {!compact && isExpanded && ( +
+
+
+ Tipo: +
{SPANISH_ALERT_TYPES[alert.alert_type]}
+
+ {alert.predicted_value && ( +
+ Valor Predicho: +
{alert.predicted_value}
+
+ )} + {alert.threshold_value && ( +
+ Umbral: +
{alert.threshold_value}
+
+ )} + {alert.model_accuracy && ( +
+ Precisión Modelo: +
{(alert.model_accuracy * 100).toFixed(1)}%
+
+ )} +
+
+ )} +
+
+ ); + })} +
+ )} +
+
+ ); +}; + +export default AlertsPanel; \ No newline at end of file diff --git a/frontend/src/components/domain/forecasting/DemandChart.tsx b/frontend/src/components/domain/forecasting/DemandChart.tsx new file mode 100644 index 00000000..4c63984e --- /dev/null +++ b/frontend/src/components/domain/forecasting/DemandChart.tsx @@ -0,0 +1,575 @@ +import React, { useState, useEffect, useMemo, useCallback } from 'react'; +import { clsx } from 'clsx'; +import { + LineChart, + Line, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + ResponsiveContainer, + ReferenceLine, + Area, + ComposedChart, + Legend, + Brush, + ReferenceDot, +} from 'recharts'; +import { Card, CardHeader, CardBody } from '../../ui'; +import { Button } from '../../ui'; +import { Select } from '../../ui'; +import { Badge } from '../../ui'; +import { + ForecastResponse, + DemandTrend, + TrendDirection, + WeatherCondition, + EventType, +} from '../../../types/forecasting.types'; + +export interface DemandChartProps { + className?: string; + title?: string; + data?: DemandTrend[]; + products?: string[]; + selectedProducts?: string[]; + onProductSelectionChange?: (products: string[]) => void; + timeframe?: 'weekly' | 'monthly' | 'quarterly' | 'yearly'; + onTimeframeChange?: (timeframe: 'weekly' | 'monthly' | 'quarterly' | 'yearly') => void; + showConfidenceInterval?: boolean; + showEvents?: boolean; + showWeatherOverlay?: boolean; + events?: Array<{ + date: string; + type: EventType; + name: string; + impact: 'high' | 'medium' | 'low'; + }>; + weatherData?: Array<{ + date: string; + condition: WeatherCondition; + temperature: number; + }>; + loading?: boolean; + error?: string | null; + height?: number; + onExport?: (format: 'png' | 'pdf' | 'csv') => void; +} + +interface ChartDataPoint { + date: string; + actualDemand?: number; + predictedDemand?: number; + confidenceLower?: number; + confidenceUpper?: number; + accuracy?: number; + trendDirection?: TrendDirection; + seasonalFactor?: number; + anomalyScore?: number; + hasEvent?: boolean; + eventType?: EventType; + eventName?: string; + eventImpact?: 'high' | 'medium' | 'low'; + weather?: WeatherCondition; + temperature?: number; +} + +const PRODUCT_COLORS = [ + '#8884d8', // Croissants + '#82ca9d', // Pan de molde + '#ffc658', // Roscón de Reyes + '#ff7c7c', // Torrijas + '#8dd1e1', // Magdalenas + '#d084d0', // Empanadas + '#ffb347', // Tarta de Santiago + '#87ceeb', // Mazapán +]; + +const EVENT_ICONS: Record = { + [EventType.HOLIDAY]: '🎉', + [EventType.FESTIVAL]: '🎪', + [EventType.SPORTS_EVENT]: '⚽', + [EventType.WEATHER_EVENT]: '🌧️', + [EventType.SCHOOL_EVENT]: '🎒', + [EventType.CONCERT]: '🎵', + [EventType.CONFERENCE]: '📊', + [EventType.CONSTRUCTION]: '🚧', +}; + +const WEATHER_ICONS: Record = { + [WeatherCondition.SUNNY]: '☀️', + [WeatherCondition.CLOUDY]: '☁️', + [WeatherCondition.RAINY]: '🌧️', + [WeatherCondition.STORMY]: '⛈️', + [WeatherCondition.SNOWY]: '❄️', + [WeatherCondition.FOGGY]: '🌫️', + [WeatherCondition.WINDY]: '🌪️', +}; + +const SPANISH_EVENT_NAMES: Record = { + [EventType.HOLIDAY]: 'Festividad', + [EventType.FESTIVAL]: 'Festival', + [EventType.SPORTS_EVENT]: 'Evento Deportivo', + [EventType.WEATHER_EVENT]: 'Evento Climático', + [EventType.SCHOOL_EVENT]: 'Evento Escolar', + [EventType.CONCERT]: 'Concierto', + [EventType.CONFERENCE]: 'Conferencia', + [EventType.CONSTRUCTION]: 'Construcción', +}; + +const DemandChart: React.FC = ({ + className, + title = 'Predicción de Demanda', + data = [], + products = [], + selectedProducts = [], + onProductSelectionChange, + timeframe = 'weekly', + onTimeframeChange, + showConfidenceInterval = true, + showEvents = true, + showWeatherOverlay = false, + events = [], + weatherData = [], + loading = false, + error = null, + height = 400, + onExport, +}) => { + const [selectedPeriod, setSelectedPeriod] = useState<{ start?: Date; end?: Date }>({}); + const [zoomedData, setZoomedData] = useState([]); + const [hoveredPoint, setHoveredPoint] = useState(null); + + // Process and merge data with events and weather + const chartData = useMemo(() => { + const processedData: ChartDataPoint[] = data.map(point => { + const dateStr = point.date; + const event = events.find(e => e.date === dateStr); + const weather = weatherData.find(w => w.date === dateStr); + + return { + date: dateStr, + actualDemand: point.actual_demand, + predictedDemand: point.predicted_demand, + confidenceLower: point.confidence_lower, + confidenceUpper: point.confidence_upper, + accuracy: point.accuracy, + trendDirection: point.trend_direction, + seasonalFactor: point.seasonal_factor, + anomalyScore: point.anomaly_score, + hasEvent: !!event, + eventType: event?.type, + eventName: event?.name, + eventImpact: event?.impact, + weather: weather?.condition, + temperature: weather?.temperature, + }; + }); + + return processedData; + }, [data, events, weatherData]); + + // Filter data based on selected period + const filteredData = useMemo(() => { + if (!selectedPeriod.start || !selectedPeriod.end) { + return chartData; + } + + return chartData.filter(point => { + const pointDate = new Date(point.date); + return pointDate >= selectedPeriod.start! && pointDate <= selectedPeriod.end!; + }); + }, [chartData, selectedPeriod]); + + // Update zoomed data when filtered data changes + useEffect(() => { + setZoomedData(filteredData); + }, [filteredData]); + + // Handle brush selection + const handleBrushChange = useCallback((brushData: any) => { + if (brushData && brushData.startIndex !== undefined && brushData.endIndex !== undefined) { + const newData = filteredData.slice(brushData.startIndex, brushData.endIndex + 1); + setZoomedData(newData); + } else { + setZoomedData(filteredData); + } + }, [filteredData]); + + // Reset zoom + const handleResetZoom = useCallback(() => { + setZoomedData(filteredData); + setSelectedPeriod({}); + }, [filteredData]); + + // Custom tooltip + const CustomTooltip = ({ active, payload, label }: any) => { + if (!active || !payload || !payload.length) return null; + + const data = payload[0].payload as ChartDataPoint; + const formattedDate = new Date(label).toLocaleDateString('es-ES', { + day: 'numeric', + month: 'short', + year: 'numeric', + }); + + return ( +
+

{formattedDate}

+ +
+ {data.actualDemand !== undefined && ( +
+ Demanda Real: + {data.actualDemand} +
+ )} + + {data.predictedDemand !== undefined && ( +
+ Demanda Predicha: + {data.predictedDemand.toFixed(1)} +
+ )} + + {showConfidenceInterval && data.confidenceLower !== undefined && data.confidenceUpper !== undefined && ( +
+ Intervalo de Confianza: + + {data.confidenceLower.toFixed(1)} - {data.confidenceUpper.toFixed(1)} + +
+ )} + + {data.accuracy !== undefined && ( +
+ Precisión: + 0.8 ? 'success' : data.accuracy > 0.6 ? 'warning' : 'danger'} + size="sm" + > + {(data.accuracy * 100).toFixed(1)}% + +
+ )} +
+ + {showEvents && data.hasEvent && ( +
+
+ {EVENT_ICONS[data.eventType!]} +
+
{data.eventName}
+
+ {SPANISH_EVENT_NAMES[data.eventType!]} +
+
+ + {data.eventImpact === 'high' ? 'Alto' : data.eventImpact === 'medium' ? 'Medio' : 'Bajo'} + +
+
+ )} + + {showWeatherOverlay && data.weather && ( +
+
+ {WEATHER_ICONS[data.weather]} +
+
+ {data.weather.replace('_', ' ')} +
+ {data.temperature && ( +
+ {data.temperature}°C +
+ )} +
+
+
+ )} +
+ ); + }; + + // Event reference dots + const renderEventDots = () => { + if (!showEvents) return null; + + return events + .filter(event => { + const eventData = zoomedData.find(d => d.date === event.date); + return eventData; + }) + .map((event, index) => { + const eventData = zoomedData.find(d => d.date === event.date); + if (!eventData) return null; + + const yValue = eventData.predictedDemand || eventData.actualDemand || 0; + + return ( + + ); + }); + }; + + // Loading state + if (loading) { + return ( + + +

{title}

+
+ +
+
+
+ Cargando gráfico de demanda... +
+
+
+
+ ); + } + + // Error state + if (error) { + return ( + + +

{title}

+
+ +
+
+
+ + + +
+

{error}

+
+
+
+
+ ); + } + + // Empty state + if (zoomedData.length === 0) { + return ( + + +

{title}

+
+ +
+
+
+ + + +
+

No hay datos de demanda disponibles

+
+
+
+
+ ); + } + + return ( + + +
+

{title}

+ +
+ {/* Time period selector */} + + + {/* Export options */} + {onExport && ( +
+ + +
+ )} + + {/* Reset zoom */} + {zoomedData.length !== filteredData.length && ( + + )} +
+
+
+ + +
+ + + + { + const date = new Date(value); + return timeframe === 'weekly' + ? date.toLocaleDateString('es-ES', { month: 'short', day: 'numeric' }) + : timeframe === 'monthly' + ? date.toLocaleDateString('es-ES', { month: 'short', year: '2-digit' }) + : date.getFullYear().toString(); + }} + /> + `${value}`} + /> + } /> + + + {/* Confidence interval area */} + {showConfidenceInterval && ( + + )} + {showConfidenceInterval && ( + + )} + + {/* Actual demand line */} + + + {/* Predicted demand line */} + + + {renderEventDots()} + + +
+ + {/* Brush for zooming */} + {filteredData.length > 20 && ( +
+ + + + { + const date = new Date(value); + return date.toLocaleDateString('es-ES', { month: 'short', day: 'numeric' }); + }} + /> + + +
+ )} + + {/* Chart legend */} +
+
+
+ Demanda Real +
+
+
+ Demanda Predicha +
+ {showConfidenceInterval && ( +
+
+ Intervalo de Confianza +
+ )} + {showEvents && events.length > 0 && ( +
+
+ Eventos +
+ )} +
+
+
+ ); +}; + +export default DemandChart; \ No newline at end of file diff --git a/frontend/src/components/domain/forecasting/DemandChart.tsx.backup b/frontend/src/components/domain/forecasting/DemandChart.tsx.backup new file mode 100644 index 00000000..5f3cadef --- /dev/null +++ b/frontend/src/components/domain/forecasting/DemandChart.tsx.backup @@ -0,0 +1,575 @@ +import React, { useState, useEffect, useMemo, useCallback } from 'react'; +import { clsx } from 'clsx'; +import { + LineChart, + Line, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + ResponsiveContainer, + ReferenceLine, + Area, + ComposedChart, + Legend, + Brush, + ReferenceDot, +} from 'recharts'; +import { Card, CardHeader, CardBody } from '../../ui'; +import { Button } from '../../ui'; +import { Select } from '../../ui'; +import { Badge } from '../../ui'; +import { + ForecastResponse, + DemandTrend, + TrendDirection, + WeatherCondition, + EventType, +} from '../../../types/forecasting.types'; + +export interface DemandChartProps { + className?: string; + title?: string; + data?: DemandTrend[]; + products?: string[]; + selectedProducts?: string[]; + onProductSelectionChange?: (products: string[]) => void; + timeframe?: 'weekly' | 'monthly' | 'quarterly' | 'yearly'; + onTimeframeChange?: (timeframe: 'weekly' | 'monthly' | 'quarterly' | 'yearly') => void; + showConfidenceInterval?: boolean; + showEvents?: boolean; + showWeatherOverlay?: boolean; + events?: Array<{ + date: string; + type: EventType; + name: string; + impact: 'high' | 'medium' | 'low'; + }>; + weatherData?: Array<{ + date: string; + condition: WeatherCondition; + temperature: number; + }>; + loading?: boolean; + error?: string | null; + height?: number; + onExport?: (format: 'png' | 'pdf' | 'csv') => void; +} + +interface ChartDataPoint { + date: string; + actualDemand?: number; + predictedDemand?: number; + confidenceLower?: number; + confidenceUpper?: number; + accuracy?: number; + trendDirection?: TrendDirection; + seasonalFactor?: number; + anomalyScore?: number; + hasEvent?: boolean; + eventType?: EventType; + eventName?: string; + eventImpact?: 'high' | 'medium' | 'low'; + weather?: WeatherCondition; + temperature?: number; +} + +const PRODUCT_COLORS = [ + '#8884d8', // Croissants + '#82ca9d', // Pan de molde + '#ffc658', // Roscón de Reyes + '#ff7c7c', // Torrijas + '#8dd1e1', // Magdalenas + '#d084d0', // Empanadas + '#ffb347', // Tarta de Santiago + '#87ceeb', // Mazapán +]; + +const EVENT_ICONS: Record = { + [EventType.HOLIDAY]: '🎉', + [EventType.FESTIVAL]: '🎪', + [EventType.SPORTS_EVENT]: '⚽', + [EventType.WEATHER_EVENT]: '🌧️', + [EventType.SCHOOL_EVENT]: '🎒', + [EventType.CONCERT]: '🎵', + [EventType.CONFERENCE]: '📊', + [EventType.CONSTRUCTION]: '🚧', +}; + +const WEATHER_ICONS: Record = { + [WeatherCondition.SUNNY]: '☀️', + [WeatherCondition.CLOUDY]: '☁️', + [WeatherCondition.RAINY]: '🌧️', + [WeatherCondition.STORMY]: '⛈️', + [WeatherCondition.SNOWY]: '❄️', + [WeatherCondition.FOGGY]: '🌫️', + [WeatherCondition.WINDY]: '🌪️', +}; + +const SPANISH_EVENT_NAMES: Record = { + [EventType.HOLIDAY]: 'Festividad', + [EventType.FESTIVAL]: 'Festival', + [EventType.SPORTS_EVENT]: 'Evento Deportivo', + [EventType.WEATHER_EVENT]: 'Evento Climático', + [EventType.SCHOOL_EVENT]: 'Evento Escolar', + [EventType.CONCERT]: 'Concierto', + [EventType.CONFERENCE]: 'Conferencia', + [EventType.CONSTRUCTION]: 'Construcción', +}; + +const DemandChart: React.FC = ({ + className, + title = 'Predicción de Demanda', + data = [], + products = [], + selectedProducts = [], + onProductSelectionChange, + timeframe = 'weekly', + onTimeframeChange, + showConfidenceInterval = true, + showEvents = true, + showWeatherOverlay = false, + events = [], + weatherData = [], + loading = false, + error = null, + height = 400, + onExport, +}) => { + const [selectedPeriod, setSelectedPeriod] = useState<{ start?: Date; end?: Date }>({}); + const [zoomedData, setZoomedData] = useState([]); + const [hoveredPoint, setHoveredPoint] = useState(null); + + // Process and merge data with events and weather + const chartData = useMemo(() => { + const processedData: ChartDataPoint[] = data.map(point => { + const dateStr = point.date; + const event = events.find(e => e.date === dateStr); + const weather = weatherData.find(w => w.date === dateStr); + + return { + date: dateStr, + actualDemand: point.actual_demand, + predictedDemand: point.predicted_demand, + confidenceLower: point.confidence_lower, + confidenceUpper: point.confidence_upper, + accuracy: point.accuracy, + trendDirection: point.trend_direction, + seasonalFactor: point.seasonal_factor, + anomalyScore: point.anomaly_score, + hasEvent: !!event, + eventType: event?.type, + eventName: event?.name, + eventImpact: event?.impact, + weather: weather?.condition, + temperature: weather?.temperature, + }; + }); + + return processedData; + }, [data, events, weatherData]); + + // Filter data based on selected period + const filteredData = useMemo(() => { + if (!selectedPeriod.start || !selectedPeriod.end) { + return chartData; + } + + return chartData.filter(point => { + const pointDate = new Date(point.date); + return pointDate >= selectedPeriod.start! && pointDate <= selectedPeriod.end!; + }); + }, [chartData, selectedPeriod]); + + // Update zoomed data when filtered data changes + useEffect(() => { + setZoomedData(filteredData); + }, [filteredData]); + + // Handle brush selection + const handleBrushChange = useCallback((brushData: any) => { + if (brushData && brushData.startIndex !== undefined && brushData.endIndex !== undefined) { + const newData = filteredData.slice(brushData.startIndex, brushData.endIndex + 1); + setZoomedData(newData); + } else { + setZoomedData(filteredData); + } + }, [filteredData]); + + // Reset zoom + const handleResetZoom = useCallback(() => { + setZoomedData(filteredData); + setSelectedPeriod({}); + }, [filteredData]); + + // Custom tooltip + const CustomTooltip = ({ active, payload, label }: any) => { + if (!active || !payload || !payload.length) return null; + + const data = payload[0].payload as ChartDataPoint; + const formattedDate = new Date(label).toLocaleDateString('es-ES', { + day: 'numeric', + month: 'short', + year: 'numeric', + }); + + return ( +
+

{formattedDate}

+ +
+ {data.actualDemand !== undefined && ( +
+ Demanda Real: + {data.actualDemand} +
+ )} + + {data.predictedDemand !== undefined && ( +
+ Demanda Predicha: + {data.predictedDemand.toFixed(1)} +
+ )} + + {showConfidenceInterval && data.confidenceLower !== undefined && data.confidenceUpper !== undefined && ( +
+ Intervalo de Confianza: + + {data.confidenceLower.toFixed(1)} - {data.confidenceUpper.toFixed(1)} + +
+ )} + + {data.accuracy !== undefined && ( +
+ Precisión: + 0.8 ? 'success' : data.accuracy > 0.6 ? 'warning' : 'danger'} + size="sm" + > + {(data.accuracy * 100).toFixed(1)}% + +
+ )} +
+ + {showEvents && data.hasEvent && ( +
+
+ {EVENT_ICONS[data.eventType!]} +
+
{data.eventName}
+
+ {SPANISH_EVENT_NAMES[data.eventType!]} +
+
+ + {data.eventImpact === 'high' ? 'Alto' : data.eventImpact === 'medium' ? 'Medio' : 'Bajo'} + +
+
+ )} + + {showWeatherOverlay && data.weather && ( +
+
+ {WEATHER_ICONS[data.weather]} +
+
+ {data.weather.replace('_', ' ')} +
+ {data.temperature && ( +
+ {data.temperature}°C +
+ )} +
+
+
+ )} +
+ ); + }; + + // Event reference dots + const renderEventDots = () => { + if (!showEvents) return null; + + return events + .filter(event => { + const eventData = zoomedData.find(d => d.date === event.date); + return eventData; + }) + .map((event, index) => { + const eventData = zoomedData.find(d => d.date === event.date); + if (!eventData) return null; + + const yValue = eventData.predictedDemand || eventData.actualDemand || 0; + + return ( + + ); + }); + }; + + // Loading state + if (loading) { + return ( + + +

{title}

+
+ +
+
+
+ Cargando gráfico de demanda... +
+
+
+
+ ); + } + + // Error state + if (error) { + return ( + + +

{title}

+
+ +
+
+
+ + + +
+

{error}

+
+
+
+
+ ); + } + + // Empty state + if (zoomedData.length === 0) { + return ( + + +

{title}

+
+ +
+
+
+ + + +
+

No hay datos de demanda disponibles

+
+
+
+
+ ); + } + + return ( + + +
+

{title}

+ +
+ {/* Time period selector */} + + + {/* Export options */} + {onExport && ( +
+ + +
+ )} + + {/* Reset zoom */} + {zoomedData.length !== filteredData.length && ( + + )} +
+
+
+ + +
+ + + + { + const date = new Date(value); + return timeframe === 'weekly' + ? date.toLocaleDateString('es-ES', { month: 'short', day: 'numeric' }) + : timeframe === 'monthly' + ? date.toLocaleDateString('es-ES', { month: 'short', year: '2-digit' }) + : date.getFullYear().toString(); + }} + /> + `${value}`} + /> + } /> + + + {/* Confidence interval area */} + {showConfidenceInterval && ( + + )} + {showConfidenceInterval && ( + + )} + + {/* Actual demand line */} + + + {/* Predicted demand line */} + + + {renderEventDots()} + + +
+ + {/* Brush for zooming */} + {filteredData.length > 20 && ( +
+ + + + { + const date = new Date(value); + return date.toLocaleDateString('es-ES', { month: 'short', day: 'numeric' }); + }} + /> + + +
+ )} + + {/* Chart legend */} +
+
+
+ Demanda Real +
+
+
+ Demanda Predicha +
+ {showConfidenceInterval && ( +
+
+ Intervalo de Confianza +
+ )} + {showEvents && events.length > 0 && ( +
+
+ Eventos +
+ )} +
+
+
+ ); +}; + +export default DemandChart; \ No newline at end of file diff --git a/frontend/src/components/domain/forecasting/ForecastTable.tsx b/frontend/src/components/domain/forecasting/ForecastTable.tsx new file mode 100644 index 00000000..7c1eff35 --- /dev/null +++ b/frontend/src/components/domain/forecasting/ForecastTable.tsx @@ -0,0 +1,653 @@ +import React, { useState, useMemo, useCallback } from 'react'; +import { clsx } from 'clsx'; +import { Card, CardHeader, CardBody } from '../../ui'; +import { Button } from '../../ui'; +import { Badge } from '../../ui'; +import { Select } from '../../ui'; +import { Input } from '../../ui'; +import { Table, TableColumn } from '../../ui'; +import { + ForecastResponse, + TrendDirection, + ModelType, +} from '../../../types/forecasting.types'; + +export interface ForecastTableProps { + className?: string; + title?: string; + data?: ForecastResponse[]; + loading?: boolean; + error?: string | null; + pageSize?: number; + onExportCSV?: (data: ForecastResponse[]) => void; + onBulkAction?: (action: string, selectedItems: ForecastResponse[]) => void; + onProductClick?: (productName: string) => void; + showBulkActions?: boolean; + showFilters?: boolean; + compact?: boolean; +} + +interface FilterState { + productName: string; + category: string; + accuracyRange: 'all' | 'high' | 'medium' | 'low'; + trendDirection: TrendDirection | 'all'; + confidenceLevel: number | 'all'; + dateRange: 'today' | 'week' | 'month' | 'quarter' | 'all'; +} + +interface SortState { + field: string; + order: 'asc' | 'desc'; +} + +const SPANISH_TRENDS: Record = { + [TrendDirection.INCREASING]: 'Creciente', + [TrendDirection.DECREASING]: 'Decreciente', + [TrendDirection.STABLE]: 'Estable', + [TrendDirection.VOLATILE]: 'Volátil', + [TrendDirection.SEASONAL]: 'Estacional', +}; + +const TREND_COLORS: Record = { + [TrendDirection.INCREASING]: 'text-[var(--color-success)]', + [TrendDirection.DECREASING]: 'text-[var(--color-error)]', + [TrendDirection.STABLE]: 'text-[var(--color-info)]', + [TrendDirection.VOLATILE]: 'text-yellow-600', + [TrendDirection.SEASONAL]: 'text-purple-600', +}; + +const TREND_ICONS: Record = { + [TrendDirection.INCREASING]: '↗️', + [TrendDirection.DECREASING]: '↘️', + [TrendDirection.STABLE]: '➡️', + [TrendDirection.VOLATILE]: '📈', + [TrendDirection.SEASONAL]: '🔄', +}; + +const BAKERY_CATEGORIES = [ + { value: 'all', label: 'Todas las categorías' }, + { value: 'pan', label: 'Pan y Bollería' }, + { value: 'dulces', label: 'Dulces y Postres' }, + { value: 'salados', label: 'Salados' }, + { value: 'estacional', label: 'Productos Estacionales' }, + { value: 'bebidas', label: 'Bebidas' }, +]; + +const SPANISH_PRODUCTS = [ + 'Croissants', + 'Pan de molde', + 'Roscón de Reyes', + 'Torrijas', + 'Magdalenas', + 'Empanadas', + 'Tarta de Santiago', + 'Mazapán', + 'Pan tostado', + 'Palmeras', + 'Napolitanas', + 'Ensaimadas', + 'Churros', + 'Polvorones', + 'Turrones', +]; + +const ForecastTable: React.FC = ({ + className, + title = 'Predicciones de Demanda', + data = [], + loading = false, + error = null, + pageSize = 20, + onExportCSV, + onBulkAction, + onProductClick, + showBulkActions = true, + showFilters = true, + compact = false, +}) => { + const [filters, setFilters] = useState({ + productName: '', + category: 'all', + accuracyRange: 'all', + trendDirection: 'all', + confidenceLevel: 'all', + dateRange: 'all', + }); + + const [sort, setSort] = useState({ + field: 'forecast_date', + order: 'desc', + }); + + const [selectedRows, setSelectedRows] = useState([]); + const [currentPage, setCurrentPage] = useState(1); + + // Get product category (simplified mapping) + const getProductCategory = useCallback((productName: string): string => { + const name = productName.toLowerCase(); + if (name.includes('pan') || name.includes('croissant') || name.includes('magdalena')) return 'pan'; + if (name.includes('roscón') || name.includes('tarta') || name.includes('mazapán') || name.includes('polvorón')) return 'dulces'; + if (name.includes('empanada') || name.includes('churro')) return 'salados'; + if (name.includes('torrija') || name.includes('turrón')) return 'estacional'; + return 'pan'; + }, []); + + // Filter and sort data + const filteredAndSortedData = useMemo(() => { + let filtered = [...data]; + + // Apply filters + if (filters.productName) { + filtered = filtered.filter(item => + item.product_name.toLowerCase().includes(filters.productName.toLowerCase()) + ); + } + + if (filters.category !== 'all') { + filtered = filtered.filter(item => getProductCategory(item.product_name) === filters.category); + } + + if (filters.accuracyRange !== 'all') { + filtered = filtered.filter(item => { + const accuracy = item.accuracy_score || 0; + switch (filters.accuracyRange) { + case 'high': return accuracy >= 0.8; + case 'medium': return accuracy >= 0.6 && accuracy < 0.8; + case 'low': return accuracy < 0.6; + default: return true; + } + }); + } + + if (filters.trendDirection !== 'all') { + filtered = filtered.filter(item => { + // Determine trend from predicted vs historical (simplified) + const trend = item.predicted_demand > (item.actual_demand || 0) ? TrendDirection.INCREASING : TrendDirection.DECREASING; + return trend === filters.trendDirection; + }); + } + + if (filters.confidenceLevel !== 'all') { + filtered = filtered.filter(item => item.confidence_level >= filters.confidenceLevel); + } + + if (filters.dateRange !== 'all') { + const now = new Date(); + const filterDate = new Date(); + + switch (filters.dateRange) { + case 'today': + filterDate.setHours(0, 0, 0, 0); + break; + case 'week': + filterDate.setDate(now.getDate() - 7); + break; + case 'month': + filterDate.setMonth(now.getMonth() - 1); + break; + case 'quarter': + filterDate.setMonth(now.getMonth() - 3); + break; + } + + filtered = filtered.filter(item => new Date(item.forecast_date) >= filterDate); + } + + // Apply sorting + filtered.sort((a, b) => { + let aValue: any; + let bValue: any; + + switch (sort.field) { + case 'product_name': + aValue = a.product_name; + bValue = b.product_name; + break; + case 'predicted_demand': + aValue = a.predicted_demand; + bValue = b.predicted_demand; + break; + case 'accuracy_score': + aValue = a.accuracy_score || 0; + bValue = b.accuracy_score || 0; + break; + case 'confidence_level': + aValue = a.confidence_level; + bValue = b.confidence_level; + break; + case 'forecast_date': + aValue = new Date(a.forecast_date); + bValue = new Date(b.forecast_date); + break; + default: + return 0; + } + + if (aValue < bValue) return sort.order === 'asc' ? -1 : 1; + if (aValue > bValue) return sort.order === 'asc' ? 1 : -1; + return 0; + }); + + return filtered; + }, [data, filters, sort, getProductCategory]); + + // Paginated data + const paginatedData = useMemo(() => { + const startIndex = (currentPage - 1) * pageSize; + return filteredAndSortedData.slice(startIndex, startIndex + pageSize); + }, [filteredAndSortedData, currentPage, pageSize]); + + // Handle sort change + const handleSort = useCallback((field: string, order: 'asc' | 'desc' | null) => { + if (order === null) { + setSort({ field: 'forecast_date', order: 'desc' }); + } else { + setSort({ field, order }); + } + }, []); + + // Handle filter change + const handleFilterChange = useCallback((key: keyof FilterState, value: any) => { + setFilters(prev => ({ ...prev, [key]: value })); + setCurrentPage(1); // Reset to first page when filtering + }, []); + + // Handle bulk actions + const handleBulkAction = useCallback((action: string) => { + const selectedData = filteredAndSortedData.filter(item => + selectedRows.includes(item.id) + ); + onBulkAction?.(action, selectedData); + }, [filteredAndSortedData, selectedRows, onBulkAction]); + + // Calculate accuracy percentage and trend + const getAccuracyInfo = useCallback((item: ForecastResponse) => { + const accuracy = item.accuracy_score || 0; + const percentage = (accuracy * 100).toFixed(1); + + let variant: 'success' | 'warning' | 'danger' = 'success'; + if (accuracy < 0.6) variant = 'danger'; + else if (accuracy < 0.8) variant = 'warning'; + + return { percentage, variant }; + }, []); + + // Get trend info + const getTrendInfo = useCallback((item: ForecastResponse) => { + // Simplified trend calculation + const trend = item.predicted_demand > (item.actual_demand || 0) ? + TrendDirection.INCREASING : TrendDirection.DECREASING; + + return { + direction: trend, + label: SPANISH_TRENDS[trend], + color: TREND_COLORS[trend], + icon: TREND_ICONS[trend], + }; + }, []); + + // Table columns + const columns: TableColumn[] = useMemo(() => { + const baseColumns: TableColumn[] = [ + { + key: 'product_name', + title: 'Producto', + dataIndex: 'product_name', + sortable: true, + width: compact ? 120 : 160, + render: (value: string, record: ForecastResponse) => ( +
+ + + {getProductCategory(value)} + +
+ ), + }, + { + key: 'predicted_demand', + title: 'Demanda Predicha', + dataIndex: 'predicted_demand', + sortable: true, + align: 'right' as const, + width: compact ? 100 : 120, + render: (value: number) => ( +
+ {value.toFixed(0)} +
+ ), + }, + { + key: 'actual_demand', + title: 'Demanda Real', + dataIndex: 'actual_demand', + align: 'right' as const, + width: compact ? 80 : 100, + render: (value?: number) => ( +
+ {value ? value.toFixed(0) : '-'} +
+ ), + }, + { + key: 'accuracy_score', + title: 'Precisión', + dataIndex: 'accuracy_score', + sortable: true, + align: 'center' as const, + width: 80, + render: (value: number | undefined, record: ForecastResponse) => { + const { percentage, variant } = getAccuracyInfo(record); + return ( + + {percentage}% + + ); + }, + }, + { + key: 'trend', + title: 'Tendencia', + align: 'center' as const, + width: compact ? 80 : 100, + render: (_, record: ForecastResponse) => { + const trend = getTrendInfo(record); + return ( +
+ {trend.icon} + + {compact ? '' : trend.label} + +
+ ); + }, + }, + { + key: 'confidence_interval', + title: 'Intervalo de Confianza', + align: 'center' as const, + width: compact ? 120 : 140, + render: (_, record: ForecastResponse) => ( +
+ {record.confidence_lower.toFixed(0)} - {record.confidence_upper.toFixed(0)} +
+ ({(record.confidence_level * 100).toFixed(0)}%) +
+
+ ), + }, + ]; + + if (!compact) { + baseColumns.push( + { + key: 'forecast_date', + title: 'Fecha de Predicción', + dataIndex: 'forecast_date', + sortable: true, + width: 120, + render: (value: string) => ( +
+ {new Date(value).toLocaleDateString('es-ES', { + day: 'numeric', + month: 'short', + year: '2-digit', + })} +
+ ), + }, + { + key: 'model_version', + title: 'Modelo', + dataIndex: 'model_version', + width: 80, + render: (value: string) => ( + + v{value} + + ), + } + ); + } + + return baseColumns; + }, [compact, onProductClick, getProductCategory, getAccuracyInfo, getTrendInfo]); + + // Loading state + if (loading) { + return ( + + +

{title}

+
+ +
+
+
+ Cargando predicciones... +
+
+
+
+ ); + } + + // Error state + if (error) { + return ( + + +

{title}

+
+ +
+
+
+ + + +
+

{error}

+
+
+
+
+ ); + } + + return ( + + +
+
+

{title}

+ + {filteredAndSortedData.length} predicciones + +
+ +
+ {onExportCSV && ( + + )} + + {showBulkActions && selectedRows.length > 0 && ( +
+ + +
+ )} +
+
+
+ + {showFilters && ( +
+
+ handleFilterChange('productName', e.target.value)} + className="text-sm" + /> + + + + + + + + + + +
+
+ )} + + +
{ + setSelectedRows(selected ? selectedRows.map(row => row.id) : []); + }, + onSelect: (record, selected) => { + setSelectedRows(prev => + selected + ? [...prev, record.id] + : prev.filter(key => key !== record.id) + ); + }, + } : undefined} + pagination={{ + current: currentPage, + pageSize, + total: filteredAndSortedData.length, + showSizeChanger: true, + showTotal: (total, range) => + `${range[0]}-${range[1]} de ${total} predicciones`, + onChange: (page, size) => { + setCurrentPage(page); + }, + }} + sort={{ + field: sort.field, + order: sort.order, + }} + onSort={handleSort} + size={compact ? 'sm' : 'md'} + hover={true} + expandable={!compact ? { + expandedRowRender: (record) => ( +
+
+
+
ID de Predicción
+
{record.id}
+
+
+
Fecha de Creación
+
+ {new Date(record.created_at).toLocaleString('es-ES')} +
+
+
+
Factores Externos
+
+ {Object.entries(record.external_factors_impact).length > 0 + ? Object.keys(record.external_factors_impact).join(', ') + : 'Ninguno' + } +
+
+
+
Componente Estacional
+
+ {record.seasonal_component + ? (record.seasonal_component * 100).toFixed(1) + '%' + : 'N/A' + } +
+
+
+
+ ), + } : undefined} + /> + + + ); +}; + +export default ForecastTable; \ No newline at end of file diff --git a/frontend/src/components/domain/forecasting/ForecastTable.tsx.backup b/frontend/src/components/domain/forecasting/ForecastTable.tsx.backup new file mode 100644 index 00000000..7127a131 --- /dev/null +++ b/frontend/src/components/domain/forecasting/ForecastTable.tsx.backup @@ -0,0 +1,653 @@ +import React, { useState, useMemo, useCallback } from 'react'; +import { clsx } from 'clsx'; +import { Card, CardHeader, CardBody } from '../../ui'; +import { Button } from '../../ui'; +import { Badge } from '../../ui'; +import { Select } from '../../ui'; +import { Input } from '../../ui'; +import { Table, TableColumn } from '../../ui'; +import { + ForecastResponse, + TrendDirection, + ModelType, +} from '../../../types/forecasting.types'; + +export interface ForecastTableProps { + className?: string; + title?: string; + data?: ForecastResponse[]; + loading?: boolean; + error?: string | null; + pageSize?: number; + onExportCSV?: (data: ForecastResponse[]) => void; + onBulkAction?: (action: string, selectedItems: ForecastResponse[]) => void; + onProductClick?: (productName: string) => void; + showBulkActions?: boolean; + showFilters?: boolean; + compact?: boolean; +} + +interface FilterState { + productName: string; + category: string; + accuracyRange: 'all' | 'high' | 'medium' | 'low'; + trendDirection: TrendDirection | 'all'; + confidenceLevel: number | 'all'; + dateRange: 'today' | 'week' | 'month' | 'quarter' | 'all'; +} + +interface SortState { + field: string; + order: 'asc' | 'desc'; +} + +const SPANISH_TRENDS: Record = { + [TrendDirection.INCREASING]: 'Creciente', + [TrendDirection.DECREASING]: 'Decreciente', + [TrendDirection.STABLE]: 'Estable', + [TrendDirection.VOLATILE]: 'Volátil', + [TrendDirection.SEASONAL]: 'Estacional', +}; + +const TREND_COLORS: Record = { + [TrendDirection.INCREASING]: 'text-green-600', + [TrendDirection.DECREASING]: 'text-red-600', + [TrendDirection.STABLE]: 'text-blue-600', + [TrendDirection.VOLATILE]: 'text-yellow-600', + [TrendDirection.SEASONAL]: 'text-purple-600', +}; + +const TREND_ICONS: Record = { + [TrendDirection.INCREASING]: '↗️', + [TrendDirection.DECREASING]: '↘️', + [TrendDirection.STABLE]: '➡️', + [TrendDirection.VOLATILE]: '📈', + [TrendDirection.SEASONAL]: '🔄', +}; + +const BAKERY_CATEGORIES = [ + { value: 'all', label: 'Todas las categorías' }, + { value: 'pan', label: 'Pan y Bollería' }, + { value: 'dulces', label: 'Dulces y Postres' }, + { value: 'salados', label: 'Salados' }, + { value: 'estacional', label: 'Productos Estacionales' }, + { value: 'bebidas', label: 'Bebidas' }, +]; + +const SPANISH_PRODUCTS = [ + 'Croissants', + 'Pan de molde', + 'Roscón de Reyes', + 'Torrijas', + 'Magdalenas', + 'Empanadas', + 'Tarta de Santiago', + 'Mazapán', + 'Pan tostado', + 'Palmeras', + 'Napolitanas', + 'Ensaimadas', + 'Churros', + 'Polvorones', + 'Turrones', +]; + +const ForecastTable: React.FC = ({ + className, + title = 'Predicciones de Demanda', + data = [], + loading = false, + error = null, + pageSize = 20, + onExportCSV, + onBulkAction, + onProductClick, + showBulkActions = true, + showFilters = true, + compact = false, +}) => { + const [filters, setFilters] = useState({ + productName: '', + category: 'all', + accuracyRange: 'all', + trendDirection: 'all', + confidenceLevel: 'all', + dateRange: 'all', + }); + + const [sort, setSort] = useState({ + field: 'forecast_date', + order: 'desc', + }); + + const [selectedRows, setSelectedRows] = useState([]); + const [currentPage, setCurrentPage] = useState(1); + + // Get product category (simplified mapping) + const getProductCategory = useCallback((productName: string): string => { + const name = productName.toLowerCase(); + if (name.includes('pan') || name.includes('croissant') || name.includes('magdalena')) return 'pan'; + if (name.includes('roscón') || name.includes('tarta') || name.includes('mazapán') || name.includes('polvorón')) return 'dulces'; + if (name.includes('empanada') || name.includes('churro')) return 'salados'; + if (name.includes('torrija') || name.includes('turrón')) return 'estacional'; + return 'pan'; + }, []); + + // Filter and sort data + const filteredAndSortedData = useMemo(() => { + let filtered = [...data]; + + // Apply filters + if (filters.productName) { + filtered = filtered.filter(item => + item.product_name.toLowerCase().includes(filters.productName.toLowerCase()) + ); + } + + if (filters.category !== 'all') { + filtered = filtered.filter(item => getProductCategory(item.product_name) === filters.category); + } + + if (filters.accuracyRange !== 'all') { + filtered = filtered.filter(item => { + const accuracy = item.accuracy_score || 0; + switch (filters.accuracyRange) { + case 'high': return accuracy >= 0.8; + case 'medium': return accuracy >= 0.6 && accuracy < 0.8; + case 'low': return accuracy < 0.6; + default: return true; + } + }); + } + + if (filters.trendDirection !== 'all') { + filtered = filtered.filter(item => { + // Determine trend from predicted vs historical (simplified) + const trend = item.predicted_demand > (item.actual_demand || 0) ? TrendDirection.INCREASING : TrendDirection.DECREASING; + return trend === filters.trendDirection; + }); + } + + if (filters.confidenceLevel !== 'all') { + filtered = filtered.filter(item => item.confidence_level >= filters.confidenceLevel); + } + + if (filters.dateRange !== 'all') { + const now = new Date(); + const filterDate = new Date(); + + switch (filters.dateRange) { + case 'today': + filterDate.setHours(0, 0, 0, 0); + break; + case 'week': + filterDate.setDate(now.getDate() - 7); + break; + case 'month': + filterDate.setMonth(now.getMonth() - 1); + break; + case 'quarter': + filterDate.setMonth(now.getMonth() - 3); + break; + } + + filtered = filtered.filter(item => new Date(item.forecast_date) >= filterDate); + } + + // Apply sorting + filtered.sort((a, b) => { + let aValue: any; + let bValue: any; + + switch (sort.field) { + case 'product_name': + aValue = a.product_name; + bValue = b.product_name; + break; + case 'predicted_demand': + aValue = a.predicted_demand; + bValue = b.predicted_demand; + break; + case 'accuracy_score': + aValue = a.accuracy_score || 0; + bValue = b.accuracy_score || 0; + break; + case 'confidence_level': + aValue = a.confidence_level; + bValue = b.confidence_level; + break; + case 'forecast_date': + aValue = new Date(a.forecast_date); + bValue = new Date(b.forecast_date); + break; + default: + return 0; + } + + if (aValue < bValue) return sort.order === 'asc' ? -1 : 1; + if (aValue > bValue) return sort.order === 'asc' ? 1 : -1; + return 0; + }); + + return filtered; + }, [data, filters, sort, getProductCategory]); + + // Paginated data + const paginatedData = useMemo(() => { + const startIndex = (currentPage - 1) * pageSize; + return filteredAndSortedData.slice(startIndex, startIndex + pageSize); + }, [filteredAndSortedData, currentPage, pageSize]); + + // Handle sort change + const handleSort = useCallback((field: string, order: 'asc' | 'desc' | null) => { + if (order === null) { + setSort({ field: 'forecast_date', order: 'desc' }); + } else { + setSort({ field, order }); + } + }, []); + + // Handle filter change + const handleFilterChange = useCallback((key: keyof FilterState, value: any) => { + setFilters(prev => ({ ...prev, [key]: value })); + setCurrentPage(1); // Reset to first page when filtering + }, []); + + // Handle bulk actions + const handleBulkAction = useCallback((action: string) => { + const selectedData = filteredAndSortedData.filter(item => + selectedRows.includes(item.id) + ); + onBulkAction?.(action, selectedData); + }, [filteredAndSortedData, selectedRows, onBulkAction]); + + // Calculate accuracy percentage and trend + const getAccuracyInfo = useCallback((item: ForecastResponse) => { + const accuracy = item.accuracy_score || 0; + const percentage = (accuracy * 100).toFixed(1); + + let variant: 'success' | 'warning' | 'danger' = 'success'; + if (accuracy < 0.6) variant = 'danger'; + else if (accuracy < 0.8) variant = 'warning'; + + return { percentage, variant }; + }, []); + + // Get trend info + const getTrendInfo = useCallback((item: ForecastResponse) => { + // Simplified trend calculation + const trend = item.predicted_demand > (item.actual_demand || 0) ? + TrendDirection.INCREASING : TrendDirection.DECREASING; + + return { + direction: trend, + label: SPANISH_TRENDS[trend], + color: TREND_COLORS[trend], + icon: TREND_ICONS[trend], + }; + }, []); + + // Table columns + const columns: TableColumn[] = useMemo(() => { + const baseColumns: TableColumn[] = [ + { + key: 'product_name', + title: 'Producto', + dataIndex: 'product_name', + sortable: true, + width: compact ? 120 : 160, + render: (value: string, record: ForecastResponse) => ( +
+ + + {getProductCategory(value)} + +
+ ), + }, + { + key: 'predicted_demand', + title: 'Demanda Predicha', + dataIndex: 'predicted_demand', + sortable: true, + align: 'right' as const, + width: compact ? 100 : 120, + render: (value: number) => ( +
+ {value.toFixed(0)} +
+ ), + }, + { + key: 'actual_demand', + title: 'Demanda Real', + dataIndex: 'actual_demand', + align: 'right' as const, + width: compact ? 80 : 100, + render: (value?: number) => ( +
+ {value ? value.toFixed(0) : '-'} +
+ ), + }, + { + key: 'accuracy_score', + title: 'Precisión', + dataIndex: 'accuracy_score', + sortable: true, + align: 'center' as const, + width: 80, + render: (value: number | undefined, record: ForecastResponse) => { + const { percentage, variant } = getAccuracyInfo(record); + return ( + + {percentage}% + + ); + }, + }, + { + key: 'trend', + title: 'Tendencia', + align: 'center' as const, + width: compact ? 80 : 100, + render: (_, record: ForecastResponse) => { + const trend = getTrendInfo(record); + return ( +
+ {trend.icon} + + {compact ? '' : trend.label} + +
+ ); + }, + }, + { + key: 'confidence_interval', + title: 'Intervalo de Confianza', + align: 'center' as const, + width: compact ? 120 : 140, + render: (_, record: ForecastResponse) => ( +
+ {record.confidence_lower.toFixed(0)} - {record.confidence_upper.toFixed(0)} +
+ ({(record.confidence_level * 100).toFixed(0)}%) +
+
+ ), + }, + ]; + + if (!compact) { + baseColumns.push( + { + key: 'forecast_date', + title: 'Fecha de Predicción', + dataIndex: 'forecast_date', + sortable: true, + width: 120, + render: (value: string) => ( +
+ {new Date(value).toLocaleDateString('es-ES', { + day: 'numeric', + month: 'short', + year: '2-digit', + })} +
+ ), + }, + { + key: 'model_version', + title: 'Modelo', + dataIndex: 'model_version', + width: 80, + render: (value: string) => ( + + v{value} + + ), + } + ); + } + + return baseColumns; + }, [compact, onProductClick, getProductCategory, getAccuracyInfo, getTrendInfo]); + + // Loading state + if (loading) { + return ( + + +

{title}

+
+ +
+
+
+ Cargando predicciones... +
+
+
+
+ ); + } + + // Error state + if (error) { + return ( + + +

{title}

+
+ +
+
+
+ + + +
+

{error}

+
+
+
+
+ ); + } + + return ( + + +
+
+

{title}

+ + {filteredAndSortedData.length} predicciones + +
+ +
+ {onExportCSV && ( + + )} + + {showBulkActions && selectedRows.length > 0 && ( +
+ + +
+ )} +
+
+
+ + {showFilters && ( +
+
+ handleFilterChange('productName', e.target.value)} + className="text-sm" + /> + + + + + + + + + + +
+
+ )} + + +
{ + setSelectedRows(selected ? selectedRows.map(row => row.id) : []); + }, + onSelect: (record, selected) => { + setSelectedRows(prev => + selected + ? [...prev, record.id] + : prev.filter(key => key !== record.id) + ); + }, + } : undefined} + pagination={{ + current: currentPage, + pageSize, + total: filteredAndSortedData.length, + showSizeChanger: true, + showTotal: (total, range) => + `${range[0]}-${range[1]} de ${total} predicciones`, + onChange: (page, size) => { + setCurrentPage(page); + }, + }} + sort={{ + field: sort.field, + order: sort.order, + }} + onSort={handleSort} + size={compact ? 'sm' : 'md'} + hover={true} + expandable={!compact ? { + expandedRowRender: (record) => ( +
+
+
+
ID de Predicción
+
{record.id}
+
+
+
Fecha de Creación
+
+ {new Date(record.created_at).toLocaleString('es-ES')} +
+
+
+
Factores Externos
+
+ {Object.entries(record.external_factors_impact).length > 0 + ? Object.keys(record.external_factors_impact).join(', ') + : 'Ninguno' + } +
+
+
+
Componente Estacional
+
+ {record.seasonal_component + ? (record.seasonal_component * 100).toFixed(1) + '%' + : 'N/A' + } +
+
+
+
+ ), + } : undefined} + /> + + + ); +}; + +export default ForecastTable; \ No newline at end of file diff --git a/frontend/src/components/domain/forecasting/SeasonalityIndicator.tsx b/frontend/src/components/domain/forecasting/SeasonalityIndicator.tsx new file mode 100644 index 00000000..69e03786 --- /dev/null +++ b/frontend/src/components/domain/forecasting/SeasonalityIndicator.tsx @@ -0,0 +1,634 @@ +import React, { useState, useMemo, useCallback } from 'react'; +import { clsx } from 'clsx'; +import { + RadialBarChart, + RadialBar, + ResponsiveContainer, + PieChart, + Pie, + Cell, + Tooltip, + BarChart, + Bar, + XAxis, + YAxis, + CartesianGrid, + ComposedChart, + Line, + LineChart, + ReferenceLine, +} from 'recharts'; +import { Card, CardHeader, CardBody } from '../../ui'; +import { Button } from '../../ui'; +import { Badge } from '../../ui'; +import { Select } from '../../ui'; +import { + SeasonalPattern, + SeasonalComponent, + HolidayEffect, + WeeklyPattern, + YearlyTrend, + Season, + SeasonalPeriod, + DayOfWeek, +} from '../../../types/forecasting.types'; + +export interface SeasonalityIndicatorProps { + className?: string; + title?: string; + seasonalPatterns?: SeasonalPattern[]; + selectedProduct?: string; + onProductChange?: (product: string) => void; + viewMode?: 'circular' | 'calendar' | 'heatmap' | 'trends'; + onViewModeChange?: (mode: 'circular' | 'calendar' | 'heatmap' | 'trends') => void; + showComparison?: boolean; + comparisonYear?: number; + loading?: boolean; + error?: string | null; +} + +interface MonthlyData { + month: string; + value: number; + strength: number; + color: string; + season: Season; + holidays: string[]; +} + +interface WeeklyData { + day: string; + dayShort: string; + value: number; + variance: number; + peakHours?: number[]; +} + +interface HeatmapData { + month: number; + week: number; + intensity: number; + value: number; + holiday?: string; +} + +const SPANISH_MONTHS = [ + 'Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', + 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre' +]; + +const SPANISH_DAYS = [ + 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo' +]; + +const SPANISH_DAYS_SHORT = [ + 'L', 'M', 'X', 'J', 'V', 'S', 'D' +]; + +const SPANISH_SEASONS: Record = { + [Season.SPRING]: 'Primavera', + [Season.SUMMER]: 'Verano', + [Season.FALL]: 'Otoño', + [Season.WINTER]: 'Invierno', +}; + +const SEASON_COLORS: Record = { + [Season.SPRING]: '#22c55e', // Green + [Season.SUMMER]: '#f59e0b', // Amber + [Season.FALL]: '#ea580c', // Orange + [Season.WINTER]: '#3b82f6', // Blue +}; + +const SPANISH_HOLIDAYS = [ + { month: 0, name: 'Año Nuevo' }, + { month: 0, name: 'Día de Reyes' }, + { month: 2, name: 'Semana Santa' }, // March/April + { month: 4, name: 'Día del Trabajador' }, + { month: 7, name: 'Asunción' }, + { month: 9, name: 'Día Nacional' }, + { month: 10, name: 'Todos los Santos' }, + { month: 11, name: 'Constitución' }, + { month: 11, name: 'Navidad' }, +]; + +const INTENSITY_COLORS = [ + '#f3f4f6', // Very low + '#dbeafe', // Low + '#bfdbfe', // Medium-low + '#93c5fd', // Medium + '#60a5fa', // Medium-high + '#3b82f6', // High + '#2563eb', // Very high + '#1d4ed8', // Extremely high +]; + +const SeasonalityIndicator: React.FC = ({ + className, + title = 'Patrones Estacionales', + seasonalPatterns = [], + selectedProduct = '', + onProductChange, + viewMode = 'circular', + onViewModeChange, + showComparison = false, + comparisonYear = new Date().getFullYear() - 1, + loading = false, + error = null, +}) => { + const [hoveredElement, setHoveredElement] = useState(null); + + // Get current pattern data + const currentPattern = useMemo(() => { + if (!selectedProduct || seasonalPatterns.length === 0) { + return seasonalPatterns[0] || null; + } + return seasonalPatterns.find(p => p.product_name === selectedProduct) || seasonalPatterns[0]; + }, [seasonalPatterns, selectedProduct]); + + // Process monthly seasonal data + const monthlyData = useMemo((): MonthlyData[] => { + if (!currentPattern) return []; + + const monthlyComponent = currentPattern.seasonal_components.find( + c => c.period === SeasonalPeriod.MONTHLY + ); + + if (!monthlyComponent) return []; + + return SPANISH_MONTHS.map((month, index) => { + const value = monthlyComponent.pattern[index] || 0; + const strength = Math.abs(value); + + // Determine season + let season: Season; + if (index >= 2 && index <= 4) season = Season.SPRING; + else if (index >= 5 && index <= 7) season = Season.SUMMER; + else if (index >= 8 && index <= 10) season = Season.FALL; + else season = Season.WINTER; + + // Get holidays for this month + const holidays = SPANISH_HOLIDAYS + .filter(h => h.month === index) + .map(h => h.name); + + return { + month, + value: value * 100, // Convert to percentage + strength: strength * 100, + color: SEASON_COLORS[season], + season, + holidays, + }; + }); + }, [currentPattern]); + + // Process weekly data + const weeklyData = useMemo((): WeeklyData[] => { + if (!currentPattern) return []; + + return currentPattern.weekly_patterns.map((pattern, index) => ({ + day: SPANISH_DAYS[index] || `Día ${index + 1}`, + dayShort: SPANISH_DAYS_SHORT[index] || `D${index + 1}`, + value: pattern.average_multiplier * 100, + variance: pattern.variance * 100, + peakHours: pattern.peak_hours, + })); + }, [currentPattern]); + + // Process heatmap data + const heatmapData = useMemo((): HeatmapData[] => { + if (!currentPattern) return []; + + const data: HeatmapData[] = []; + const monthlyComponent = currentPattern.seasonal_components.find( + c => c.period === SeasonalPeriod.MONTHLY + ); + + if (monthlyComponent) { + for (let month = 0; month < 12; month++) { + for (let week = 0; week < 4; week++) { + const value = monthlyComponent.pattern[month] || 0; + const intensity = Math.min(Math.max(Math.abs(value) * 8, 0), 7); // 0-7 scale + + const holiday = SPANISH_HOLIDAYS.find(h => h.month === month); + + data.push({ + month, + week, + intensity: Math.floor(intensity), + value: value * 100, + holiday: holiday?.name, + }); + } + } + } + + return data; + }, [currentPattern]); + + // Custom tooltip for radial chart + const RadialTooltip = ({ active, payload }: any) => { + if (!active || !payload || !payload.length) return null; + + const data = payload[0].payload as MonthlyData; + + return ( +
+

{data.month}

+
+
+ Estación: + + {SPANISH_SEASONS[data.season]} + +
+
+ Variación: + {data.value.toFixed(1)}% +
+
+ Intensidad: + {data.strength.toFixed(1)}% +
+ {data.holidays.length > 0 && ( +
+
Festividades:
+
+ {data.holidays.map(holiday => ( + + {holiday} + + ))} +
+
+ )} +
+
+ ); + }; + + // Circular view (Radial chart) + const renderCircularView = () => ( +
+ + + entry.color} + /> + } /> + + +
+ ); + + // Calendar view (Bar chart by month) + const renderCalendarView = () => ( +
+ + + + + + { + if (!active || !payload || !payload.length) return null; + const data = payload[0].payload as MonthlyData; + return ; + }} + /> + monthlyData[index]?.color || '#8884d8'} + /> + + + + +
+ ); + + // Heatmap view + const renderHeatmapView = () => ( +
+ {/* Month labels */} +
+ {SPANISH_MONTHS.map(month => ( +
+ {month.slice(0, 3)} +
+ ))} +
+ + {/* Heatmap grid */} + {[0, 1, 2, 3].map(week => ( +
+ {heatmapData + .filter(d => d.week === week) + .map((cell, monthIndex) => ( +
setHoveredElement(cell)} + onMouseLeave={() => setHoveredElement(null)} + title={`${SPANISH_MONTHS[cell.month]} S${cell.week + 1}: ${cell.value.toFixed(1)}%`} + > + {cell.holiday && ( +
🎉
+ )} +
+ ))} +
+ ))} + + {/* Legend */} +
+ Baja +
+ {INTENSITY_COLORS.map((color, index) => ( +
+ ))} +
+ Alta +
+
+ ); + + // Trends view (Weekly patterns) + const renderTrendsView = () => ( +
+ + + + + + { + if (!active || !payload || !payload.length) return null; + const data = payload[0].payload as WeeklyData; + return ( +
+

{data.day}

+
+
+ Multiplicador Promedio: + {data.value.toFixed(1)}% +
+
+ Varianza: + {data.variance.toFixed(1)}% +
+ {data.peakHours && data.peakHours.length > 0 && ( +
+ Horas Pico: + + {data.peakHours.map(h => `${h}:00`).join(', ')} + +
+ )} +
+
+ ); + }} + /> + +
+
+
+ ); + + // Loading state + if (loading) { + return ( + + +

{title}

+
+ +
+
+
+ Cargando patrones estacionales... +
+
+
+
+ ); + } + + // Error state + if (error) { + return ( + + +

{title}

+
+ +
+
+
+ + + +
+

{error}

+
+
+
+
+ ); + } + + // Empty state + if (!currentPattern) { + return ( + + +

{title}

+
+ +
+
+
+ + + +
+

No hay datos de estacionalidad disponibles

+
+
+
+
+ ); + } + + return ( + + +
+
+

{title}

+ {currentPattern && ( + + {currentPattern.product_name} + + )} + + {(currentPattern.confidence_score * 100).toFixed(0)}% confianza + +
+ +
+ {/* Product selector */} + {onProductChange && seasonalPatterns.length > 1 && ( + + )} + + {/* View mode selector */} +
+ {(['circular', 'calendar', 'heatmap', 'trends'] as const).map((mode) => ( + + ))} +
+
+
+
+ + +
+ {/* Main visualization */} +
+ {viewMode === 'circular' && renderCircularView()} + {viewMode === 'calendar' && renderCalendarView()} + {viewMode === 'heatmap' && renderHeatmapView()} + {viewMode === 'trends' && renderTrendsView()} +
+ + {/* Holiday effects summary */} + {currentPattern.holiday_effects && currentPattern.holiday_effects.length > 0 && ( +
+

Efectos de Festividades

+
+ {currentPattern.holiday_effects.map((holiday, index) => ( +
+
+ + {holiday.holiday_name} + + 1.2 ? 'success' : holiday.impact_factor < 0.8 ? 'danger' : 'warning'} + size="sm" + > + {((holiday.impact_factor - 1) * 100).toFixed(0)}% + +
+
+
Duración: {holiday.duration_days} días
+
Confianza: {(holiday.confidence * 100).toFixed(0)}%
+
+
+ ))} +
+
+ )} + + {/* Pattern strength indicators */} +
+

Intensidad de Patrones

+
+ {currentPattern.seasonal_components.map((component, index) => { + const periodLabel = { + [SeasonalPeriod.WEEKLY]: 'Semanal', + [SeasonalPeriod.MONTHLY]: 'Mensual', + [SeasonalPeriod.QUARTERLY]: 'Trimestral', + [SeasonalPeriod.YEARLY]: 'Anual', + }[component.period] || component.period; + + return ( +
+
+ {periodLabel} +
+
+
+
+
+ + {(component.strength * 100).toFixed(0)}% + +
+
+ ); + })} +
+
+
+ + + ); +}; + +export default SeasonalityIndicator; \ No newline at end of file diff --git a/frontend/src/components/domain/forecasting/SeasonalityIndicator.tsx.backup b/frontend/src/components/domain/forecasting/SeasonalityIndicator.tsx.backup new file mode 100644 index 00000000..69e03786 --- /dev/null +++ b/frontend/src/components/domain/forecasting/SeasonalityIndicator.tsx.backup @@ -0,0 +1,634 @@ +import React, { useState, useMemo, useCallback } from 'react'; +import { clsx } from 'clsx'; +import { + RadialBarChart, + RadialBar, + ResponsiveContainer, + PieChart, + Pie, + Cell, + Tooltip, + BarChart, + Bar, + XAxis, + YAxis, + CartesianGrid, + ComposedChart, + Line, + LineChart, + ReferenceLine, +} from 'recharts'; +import { Card, CardHeader, CardBody } from '../../ui'; +import { Button } from '../../ui'; +import { Badge } from '../../ui'; +import { Select } from '../../ui'; +import { + SeasonalPattern, + SeasonalComponent, + HolidayEffect, + WeeklyPattern, + YearlyTrend, + Season, + SeasonalPeriod, + DayOfWeek, +} from '../../../types/forecasting.types'; + +export interface SeasonalityIndicatorProps { + className?: string; + title?: string; + seasonalPatterns?: SeasonalPattern[]; + selectedProduct?: string; + onProductChange?: (product: string) => void; + viewMode?: 'circular' | 'calendar' | 'heatmap' | 'trends'; + onViewModeChange?: (mode: 'circular' | 'calendar' | 'heatmap' | 'trends') => void; + showComparison?: boolean; + comparisonYear?: number; + loading?: boolean; + error?: string | null; +} + +interface MonthlyData { + month: string; + value: number; + strength: number; + color: string; + season: Season; + holidays: string[]; +} + +interface WeeklyData { + day: string; + dayShort: string; + value: number; + variance: number; + peakHours?: number[]; +} + +interface HeatmapData { + month: number; + week: number; + intensity: number; + value: number; + holiday?: string; +} + +const SPANISH_MONTHS = [ + 'Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', + 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre' +]; + +const SPANISH_DAYS = [ + 'Lunes', 'Martes', 'Miércoles', 'Jueves', 'Viernes', 'Sábado', 'Domingo' +]; + +const SPANISH_DAYS_SHORT = [ + 'L', 'M', 'X', 'J', 'V', 'S', 'D' +]; + +const SPANISH_SEASONS: Record = { + [Season.SPRING]: 'Primavera', + [Season.SUMMER]: 'Verano', + [Season.FALL]: 'Otoño', + [Season.WINTER]: 'Invierno', +}; + +const SEASON_COLORS: Record = { + [Season.SPRING]: '#22c55e', // Green + [Season.SUMMER]: '#f59e0b', // Amber + [Season.FALL]: '#ea580c', // Orange + [Season.WINTER]: '#3b82f6', // Blue +}; + +const SPANISH_HOLIDAYS = [ + { month: 0, name: 'Año Nuevo' }, + { month: 0, name: 'Día de Reyes' }, + { month: 2, name: 'Semana Santa' }, // March/April + { month: 4, name: 'Día del Trabajador' }, + { month: 7, name: 'Asunción' }, + { month: 9, name: 'Día Nacional' }, + { month: 10, name: 'Todos los Santos' }, + { month: 11, name: 'Constitución' }, + { month: 11, name: 'Navidad' }, +]; + +const INTENSITY_COLORS = [ + '#f3f4f6', // Very low + '#dbeafe', // Low + '#bfdbfe', // Medium-low + '#93c5fd', // Medium + '#60a5fa', // Medium-high + '#3b82f6', // High + '#2563eb', // Very high + '#1d4ed8', // Extremely high +]; + +const SeasonalityIndicator: React.FC = ({ + className, + title = 'Patrones Estacionales', + seasonalPatterns = [], + selectedProduct = '', + onProductChange, + viewMode = 'circular', + onViewModeChange, + showComparison = false, + comparisonYear = new Date().getFullYear() - 1, + loading = false, + error = null, +}) => { + const [hoveredElement, setHoveredElement] = useState(null); + + // Get current pattern data + const currentPattern = useMemo(() => { + if (!selectedProduct || seasonalPatterns.length === 0) { + return seasonalPatterns[0] || null; + } + return seasonalPatterns.find(p => p.product_name === selectedProduct) || seasonalPatterns[0]; + }, [seasonalPatterns, selectedProduct]); + + // Process monthly seasonal data + const monthlyData = useMemo((): MonthlyData[] => { + if (!currentPattern) return []; + + const monthlyComponent = currentPattern.seasonal_components.find( + c => c.period === SeasonalPeriod.MONTHLY + ); + + if (!monthlyComponent) return []; + + return SPANISH_MONTHS.map((month, index) => { + const value = monthlyComponent.pattern[index] || 0; + const strength = Math.abs(value); + + // Determine season + let season: Season; + if (index >= 2 && index <= 4) season = Season.SPRING; + else if (index >= 5 && index <= 7) season = Season.SUMMER; + else if (index >= 8 && index <= 10) season = Season.FALL; + else season = Season.WINTER; + + // Get holidays for this month + const holidays = SPANISH_HOLIDAYS + .filter(h => h.month === index) + .map(h => h.name); + + return { + month, + value: value * 100, // Convert to percentage + strength: strength * 100, + color: SEASON_COLORS[season], + season, + holidays, + }; + }); + }, [currentPattern]); + + // Process weekly data + const weeklyData = useMemo((): WeeklyData[] => { + if (!currentPattern) return []; + + return currentPattern.weekly_patterns.map((pattern, index) => ({ + day: SPANISH_DAYS[index] || `Día ${index + 1}`, + dayShort: SPANISH_DAYS_SHORT[index] || `D${index + 1}`, + value: pattern.average_multiplier * 100, + variance: pattern.variance * 100, + peakHours: pattern.peak_hours, + })); + }, [currentPattern]); + + // Process heatmap data + const heatmapData = useMemo((): HeatmapData[] => { + if (!currentPattern) return []; + + const data: HeatmapData[] = []; + const monthlyComponent = currentPattern.seasonal_components.find( + c => c.period === SeasonalPeriod.MONTHLY + ); + + if (monthlyComponent) { + for (let month = 0; month < 12; month++) { + for (let week = 0; week < 4; week++) { + const value = monthlyComponent.pattern[month] || 0; + const intensity = Math.min(Math.max(Math.abs(value) * 8, 0), 7); // 0-7 scale + + const holiday = SPANISH_HOLIDAYS.find(h => h.month === month); + + data.push({ + month, + week, + intensity: Math.floor(intensity), + value: value * 100, + holiday: holiday?.name, + }); + } + } + } + + return data; + }, [currentPattern]); + + // Custom tooltip for radial chart + const RadialTooltip = ({ active, payload }: any) => { + if (!active || !payload || !payload.length) return null; + + const data = payload[0].payload as MonthlyData; + + return ( +
+

{data.month}

+
+
+ Estación: + + {SPANISH_SEASONS[data.season]} + +
+
+ Variación: + {data.value.toFixed(1)}% +
+
+ Intensidad: + {data.strength.toFixed(1)}% +
+ {data.holidays.length > 0 && ( +
+
Festividades:
+
+ {data.holidays.map(holiday => ( + + {holiday} + + ))} +
+
+ )} +
+
+ ); + }; + + // Circular view (Radial chart) + const renderCircularView = () => ( +
+ + + entry.color} + /> + } /> + + +
+ ); + + // Calendar view (Bar chart by month) + const renderCalendarView = () => ( +
+ + + + + + { + if (!active || !payload || !payload.length) return null; + const data = payload[0].payload as MonthlyData; + return ; + }} + /> + monthlyData[index]?.color || '#8884d8'} + /> + + + + +
+ ); + + // Heatmap view + const renderHeatmapView = () => ( +
+ {/* Month labels */} +
+ {SPANISH_MONTHS.map(month => ( +
+ {month.slice(0, 3)} +
+ ))} +
+ + {/* Heatmap grid */} + {[0, 1, 2, 3].map(week => ( +
+ {heatmapData + .filter(d => d.week === week) + .map((cell, monthIndex) => ( +
setHoveredElement(cell)} + onMouseLeave={() => setHoveredElement(null)} + title={`${SPANISH_MONTHS[cell.month]} S${cell.week + 1}: ${cell.value.toFixed(1)}%`} + > + {cell.holiday && ( +
🎉
+ )} +
+ ))} +
+ ))} + + {/* Legend */} +
+ Baja +
+ {INTENSITY_COLORS.map((color, index) => ( +
+ ))} +
+ Alta +
+
+ ); + + // Trends view (Weekly patterns) + const renderTrendsView = () => ( +
+ + + + + + { + if (!active || !payload || !payload.length) return null; + const data = payload[0].payload as WeeklyData; + return ( +
+

{data.day}

+
+
+ Multiplicador Promedio: + {data.value.toFixed(1)}% +
+
+ Varianza: + {data.variance.toFixed(1)}% +
+ {data.peakHours && data.peakHours.length > 0 && ( +
+ Horas Pico: + + {data.peakHours.map(h => `${h}:00`).join(', ')} + +
+ )} +
+
+ ); + }} + /> + +
+
+
+ ); + + // Loading state + if (loading) { + return ( + + +

{title}

+
+ +
+
+
+ Cargando patrones estacionales... +
+
+
+
+ ); + } + + // Error state + if (error) { + return ( + + +

{title}

+
+ +
+
+
+ + + +
+

{error}

+
+
+
+
+ ); + } + + // Empty state + if (!currentPattern) { + return ( + + +

{title}

+
+ +
+
+
+ + + +
+

No hay datos de estacionalidad disponibles

+
+
+
+
+ ); + } + + return ( + + +
+
+

{title}

+ {currentPattern && ( + + {currentPattern.product_name} + + )} + + {(currentPattern.confidence_score * 100).toFixed(0)}% confianza + +
+ +
+ {/* Product selector */} + {onProductChange && seasonalPatterns.length > 1 && ( + + )} + + {/* View mode selector */} +
+ {(['circular', 'calendar', 'heatmap', 'trends'] as const).map((mode) => ( + + ))} +
+
+
+
+ + +
+ {/* Main visualization */} +
+ {viewMode === 'circular' && renderCircularView()} + {viewMode === 'calendar' && renderCalendarView()} + {viewMode === 'heatmap' && renderHeatmapView()} + {viewMode === 'trends' && renderTrendsView()} +
+ + {/* Holiday effects summary */} + {currentPattern.holiday_effects && currentPattern.holiday_effects.length > 0 && ( +
+

Efectos de Festividades

+
+ {currentPattern.holiday_effects.map((holiday, index) => ( +
+
+ + {holiday.holiday_name} + + 1.2 ? 'success' : holiday.impact_factor < 0.8 ? 'danger' : 'warning'} + size="sm" + > + {((holiday.impact_factor - 1) * 100).toFixed(0)}% + +
+
+
Duración: {holiday.duration_days} días
+
Confianza: {(holiday.confidence * 100).toFixed(0)}%
+
+
+ ))} +
+
+ )} + + {/* Pattern strength indicators */} +
+

Intensidad de Patrones

+
+ {currentPattern.seasonal_components.map((component, index) => { + const periodLabel = { + [SeasonalPeriod.WEEKLY]: 'Semanal', + [SeasonalPeriod.MONTHLY]: 'Mensual', + [SeasonalPeriod.QUARTERLY]: 'Trimestral', + [SeasonalPeriod.YEARLY]: 'Anual', + }[component.period] || component.period; + + return ( +
+
+ {periodLabel} +
+
+
+
+
+ + {(component.strength * 100).toFixed(0)}% + +
+
+ ); + })} +
+
+
+ + + ); +}; + +export default SeasonalityIndicator; \ No newline at end of file diff --git a/frontend/src/components/domain/forecasting/index.ts b/frontend/src/components/domain/forecasting/index.ts new file mode 100644 index 00000000..d32d6784 --- /dev/null +++ b/frontend/src/components/domain/forecasting/index.ts @@ -0,0 +1,31 @@ +// Forecasting Domain Components +export { default as DemandChart } from './DemandChart'; +export { default as ForecastTable } from './ForecastTable'; +export { default as SeasonalityIndicator } from './SeasonalityIndicator'; +export { default as AlertsPanel } from './AlertsPanel'; + +// Export component props for type checking +export type { DemandChartProps } from './DemandChart'; +export type { ForecastTableProps } from './ForecastTable'; +export type { SeasonalityIndicatorProps } from './SeasonalityIndicator'; +export type { AlertsPanelProps } from './AlertsPanel'; + +// Re-export related types from forecasting types +export type { + ForecastResponse, + DemandTrend, + SeasonalPattern, + ForecastAlert, + TrendDirection, + AlertSeverity, + ForecastAlertType, + SeasonalComponent, + HolidayEffect, + WeeklyPattern, + YearlyTrend, + Season, + SeasonalPeriod, + DayOfWeek, + WeatherCondition, + EventType, +} from '../../../types/forecasting.types'; \ No newline at end of file diff --git a/frontend/src/components/domain/index.ts b/frontend/src/components/domain/index.ts new file mode 100644 index 00000000..a48268f7 --- /dev/null +++ b/frontend/src/components/domain/index.ts @@ -0,0 +1,25 @@ +// Domain Components - Business-specific components + +// Auth components +export * from './auth'; + +// Dashboard components +export * from './dashboard'; + +// Inventory components +export * from './inventory'; + +// Production components +export * from './production'; + +// Sales components +export * from './sales'; + +// Forecasting components +export * from './forecasting'; + +// Analytics components +export * from './analytics'; + +// Onboarding components +export * from './onboarding'; \ No newline at end of file diff --git a/frontend/src/components/domain/inventory/InventoryForm.tsx b/frontend/src/components/domain/inventory/InventoryForm.tsx new file mode 100644 index 00000000..59d10cbf --- /dev/null +++ b/frontend/src/components/domain/inventory/InventoryForm.tsx @@ -0,0 +1,756 @@ +import React, { useState, useEffect, useCallback } from 'react'; +import { clsx } from 'clsx'; +import { Button } from '../../ui'; +import { Input } from '../../ui'; +import { Select } from '../../ui'; +import { Card } from '../../ui'; +import { Badge } from '../../ui'; +import { Modal } from '../../ui'; +import { IngredientFormData, UnitOfMeasure, ProductType, IngredientResponse } from '../../../types/inventory.types'; +import { inventoryService } from '../../../services/api/inventory.service'; + +export interface InventoryFormProps { + item?: IngredientResponse; + open?: boolean; + onClose?: () => void; + onSubmit?: (data: IngredientFormData) => Promise; + onClassify?: (name: string, description?: string) => Promise; + loading?: boolean; + className?: string; +} + +// Spanish bakery categories with subcategories +const BAKERY_CATEGORIES = { + harinas: { + label: 'Harinas', + subcategories: ['Harina de trigo', 'Harina integral', 'Harina de fuerza', 'Harina de maíz', 'Harina de centeno', 'Harina sin gluten'] + }, + levaduras: { + label: 'Levaduras', + subcategories: ['Levadura fresca', 'Levadura seca', 'Levadura química', 'Masa madre', 'Levadura instantánea'] + }, + azucares: { + label: 'Azúcares y Endulzantes', + subcategories: ['Azúcar blanco', 'Azúcar moreno', 'Azúcar glass', 'Miel', 'Jarabe de arce', 'Stevia', 'Azúcar invertido'] + }, + chocolates: { + label: 'Chocolates y Cacao', + subcategories: ['Chocolate negro', 'Chocolate con leche', 'Chocolate blanco', 'Cacao en polvo', 'Pepitas de chocolate', 'Cobertura'] + }, + frutas: { + label: 'Frutas y Frutos Secos', + subcategories: ['Almendras', 'Nueces', 'Pasas', 'Fruta confitada', 'Mermeladas', 'Frutas frescas', 'Frutos del bosque'] + }, + lacteos: { + label: 'Lácteos', + subcategories: ['Leche entera', 'Leche desnatada', 'Nata', 'Queso mascarpone', 'Yogur', 'Suero de leche'] + }, + huevos: { + label: 'Huevos', + subcategories: ['Huevos frescos', 'Clara de huevo', 'Yema de huevo', 'Huevo pasteurizado'] + }, + mantequillas: { + label: 'Mantequillas y Grasas', + subcategories: ['Mantequilla', 'Margarina', 'Aceite de girasol', 'Aceite de oliva', 'Manteca'] + }, + especias: { + label: 'Especias y Aromas', + subcategories: ['Vainilla', 'Canela', 'Cardamomo', 'Esencias', 'Colorantes', 'Sal', 'Bicarbonato'] + }, + conservantes: { + label: 'Conservantes y Aditivos', + subcategories: ['Ácido ascórbico', 'Lecitina', 'Emulgentes', 'Estabilizantes', 'Antioxidantes'] + }, + decoracion: { + label: 'Decoración', + subcategories: ['Fondant', 'Pasta de goma', 'Perlas de azúcar', 'Sprinkles', 'Moldes', 'Papel comestible'] + }, + envases: { + label: 'Envases y Embalajes', + subcategories: ['Cajas de cartón', 'Bolsas', 'Papel encerado', 'Film transparente', 'Etiquetas'] + }, + utensilios: { + label: 'Utensilios y Equipos', + subcategories: ['Moldes', 'Boquillas', 'Espátulas', 'Batidores', 'Termómetros'] + }, + limpieza: { + label: 'Limpieza e Higiene', + subcategories: ['Detergentes', 'Desinfectantes', 'Guantes', 'Paños', 'Productos sanitarios'] + }, +}; + +const UNITS_OF_MEASURE = [ + { value: UnitOfMeasure.KILOGRAM, label: 'Kilogramo (kg)' }, + { value: UnitOfMeasure.GRAM, label: 'Gramo (g)' }, + { value: UnitOfMeasure.LITER, label: 'Litro (l)' }, + { value: UnitOfMeasure.MILLILITER, label: 'Mililitro (ml)' }, + { value: UnitOfMeasure.PIECE, label: 'Pieza (pz)' }, + { value: UnitOfMeasure.PACKAGE, label: 'Paquete' }, + { value: UnitOfMeasure.BAG, label: 'Bolsa' }, + { value: UnitOfMeasure.BOX, label: 'Caja' }, + { value: UnitOfMeasure.DOZEN, label: 'Docena' }, + { value: UnitOfMeasure.CUP, label: 'Taza' }, + { value: UnitOfMeasure.TABLESPOON, label: 'Cucharada' }, + { value: UnitOfMeasure.TEASPOON, label: 'Cucharadita' }, + { value: UnitOfMeasure.POUND, label: 'Libra (lb)' }, + { value: UnitOfMeasure.OUNCE, label: 'Onza (oz)' }, +]; + +const PRODUCT_TYPES = [ + { value: ProductType.INGREDIENT, label: 'Ingrediente' }, + { value: ProductType.FINISHED_PRODUCT, label: 'Producto Terminado' }, +]; + +const initialFormData: IngredientFormData = { + name: '', + product_type: ProductType.INGREDIENT, + sku: '', + barcode: '', + category: '', + subcategory: '', + description: '', + brand: '', + unit_of_measure: UnitOfMeasure.KILOGRAM, + package_size: undefined, + standard_cost: undefined, + low_stock_threshold: 10, + reorder_point: 20, + reorder_quantity: 50, + max_stock_level: undefined, + requires_refrigeration: false, + requires_freezing: false, + storage_temperature_min: undefined, + storage_temperature_max: undefined, + storage_humidity_max: undefined, + shelf_life_days: undefined, + storage_instructions: '', + is_perishable: false, + allergen_info: {}, +}; + +export const InventoryForm: React.FC = ({ + item, + open = false, + onClose, + onSubmit, + onClassify, + loading = false, + className, +}) => { + const [formData, setFormData] = useState(initialFormData); + const [errors, setErrors] = useState>({}); + const [classificationSuggestions, setClassificationSuggestions] = useState(null); + const [showClassificationModal, setShowClassificationModal] = useState(false); + const [classifying, setClassifying] = useState(false); + const [imageFile, setImageFile] = useState(null); + const [imagePreview, setImagePreview] = useState(null); + + const isEditing = !!item; + + // Initialize form data when item changes + useEffect(() => { + if (item) { + setFormData({ + name: item.name, + product_type: item.product_type, + sku: item.sku || '', + barcode: item.barcode || '', + category: item.category || '', + subcategory: item.subcategory || '', + description: item.description || '', + brand: item.brand || '', + unit_of_measure: item.unit_of_measure, + package_size: item.package_size, + standard_cost: item.standard_cost, + low_stock_threshold: item.low_stock_threshold, + reorder_point: item.reorder_point, + reorder_quantity: item.reorder_quantity, + max_stock_level: item.max_stock_level, + requires_refrigeration: item.requires_refrigeration, + requires_freezing: item.requires_freezing, + storage_temperature_min: item.storage_temperature_min, + storage_temperature_max: item.storage_temperature_max, + storage_humidity_max: item.storage_humidity_max, + shelf_life_days: item.shelf_life_days, + storage_instructions: item.storage_instructions || '', + is_perishable: item.is_perishable, + allergen_info: item.allergen_info || {}, + }); + } else { + setFormData(initialFormData); + } + setErrors({}); + setClassificationSuggestions(null); + }, [item]); + + // Handle input changes + const handleInputChange = useCallback((field: keyof IngredientFormData, value: any) => { + setFormData(prev => ({ ...prev, [field]: value })); + // Clear error for this field + if (errors[field]) { + setErrors(prev => ({ ...prev, [field]: '' })); + } + }, [errors]); + + // Handle image upload + const handleImageUpload = useCallback((event: React.ChangeEvent) => { + const file = event.target.files?.[0]; + if (file) { + setImageFile(file); + const reader = new FileReader(); + reader.onload = () => setImagePreview(reader.result as string); + reader.readAsDataURL(file); + } + }, []); + + // Auto-classify product + const handleClassifyProduct = useCallback(async () => { + if (!formData.name.trim()) return; + + setClassifying(true); + try { + const suggestions = await onClassify?.(formData.name, formData.description); + if (suggestions) { + setClassificationSuggestions(suggestions); + setShowClassificationModal(true); + } + } catch (error) { + console.error('Classification failed:', error); + } finally { + setClassifying(false); + } + }, [formData.name, formData.description, onClassify]); + + // Apply classification suggestions + const handleApplyClassification = useCallback(() => { + if (!classificationSuggestions) return; + + setFormData(prev => ({ + ...prev, + category: classificationSuggestions.category || prev.category, + subcategory: classificationSuggestions.subcategory || prev.subcategory, + unit_of_measure: classificationSuggestions.suggested_unit || prev.unit_of_measure, + is_perishable: classificationSuggestions.is_perishable ?? prev.is_perishable, + requires_refrigeration: classificationSuggestions.storage_requirements?.requires_refrigeration ?? prev.requires_refrigeration, + requires_freezing: classificationSuggestions.storage_requirements?.requires_freezing ?? prev.requires_freezing, + shelf_life_days: classificationSuggestions.storage_requirements?.estimated_shelf_life_days || prev.shelf_life_days, + })); + + setShowClassificationModal(false); + }, [classificationSuggestions]); + + // Validate form + const validateForm = useCallback((): boolean => { + const newErrors: Record = {}; + + if (!formData.name.trim()) { + newErrors.name = 'El nombre es obligatorio'; + } + + if (!formData.unit_of_measure) { + newErrors.unit_of_measure = 'La unidad de medida es obligatoria'; + } + + if (formData.low_stock_threshold < 0) { + newErrors.low_stock_threshold = 'El stock mínimo no puede ser negativo'; + } + + if (formData.reorder_point < 0) { + newErrors.reorder_point = 'El punto de reorden no puede ser negativo'; + } + + if (formData.reorder_quantity < 0) { + newErrors.reorder_quantity = 'La cantidad de reorden no puede ser negativa'; + } + + if (formData.max_stock_level !== undefined && formData.max_stock_level < formData.low_stock_threshold) { + newErrors.max_stock_level = 'El stock máximo debe ser mayor que el mínimo'; + } + + if (formData.standard_cost !== undefined && formData.standard_cost < 0) { + newErrors.standard_cost = 'El precio no puede ser negativo'; + } + + if (formData.package_size !== undefined && formData.package_size <= 0) { + newErrors.package_size = 'El tamaño del paquete debe ser mayor que 0'; + } + + if (formData.shelf_life_days !== undefined && formData.shelf_life_days <= 0) { + newErrors.shelf_life_days = 'La vida útil debe ser mayor que 0'; + } + + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }, [formData]); + + // Handle form submission + const handleSubmit = useCallback(async (e: React.FormEvent) => { + e.preventDefault(); + + if (!validateForm()) return; + + try { + await onSubmit?.(formData); + } catch (error) { + console.error('Form submission failed:', error); + } + }, [formData, validateForm, onSubmit]); + + // Get subcategories for selected category + const subcategoryOptions = formData.category && BAKERY_CATEGORIES[formData.category as keyof typeof BAKERY_CATEGORIES] + ? BAKERY_CATEGORIES[formData.category as keyof typeof BAKERY_CATEGORIES].subcategories.map(sub => ({ + value: sub, + label: sub, + })) + : []; + + const categoryOptions = Object.entries(BAKERY_CATEGORIES).map(([key, { label }]) => ({ + value: key, + label, + })); + + return ( + +
+
+ {/* Left Column - Basic Information */} +
+ +

Información Básica

+ +
+
+ handleInputChange('name', e.target.value)} + error={errors.name} + placeholder="Ej. Harina de trigo" + className="flex-1" + /> + +
+ +
+ handleInputChange('unit_of_measure', value)} + options={UNITS_OF_MEASURE} + error={errors.unit_of_measure} + /> +
+ + handleInputChange('description', e.target.value)} + placeholder="Descripción detallada del ingrediente" + helperText="Descripción opcional para ayudar con la clasificación automática" + /> + +
+ handleInputChange('brand', e.target.value)} + placeholder="Marca del producto" + /> + handleInputChange('sku', e.target.value)} + placeholder="Código SKU interno" + /> +
+ + handleInputChange('barcode', e.target.value)} + placeholder="Código de barras EAN/UPC" + /> +
+
+ + {/* Image Upload */} + +

Imagen del Producto

+ +
+ + {imagePreview && ( +
+ Preview +
+ )} +
+
+
+ + {/* Right Column - Categories and Specifications */} +
+ +

Categorización

+ +
+ handleInputChange('subcategory', value)} + options={[{ value: '', label: 'Seleccionar subcategoría' }, ...subcategoryOptions]} + placeholder="Seleccionar subcategoría" + /> + )} +
+
+ + +

Precios y Cantidades

+ +
+
+ handleInputChange('standard_cost', e.target.value ? parseFloat(e.target.value) : undefined)} + error={errors.standard_cost} + leftAddon="€" + placeholder="0.00" + /> + handleInputChange('package_size', e.target.value ? parseFloat(e.target.value) : undefined)} + error={errors.package_size} + rightAddon={formData.unit_of_measure} + placeholder="0.00" + /> +
+
+
+ + +

Gestión de Stock

+ +
+
+ handleInputChange('low_stock_threshold', parseFloat(e.target.value) || 0)} + error={errors.low_stock_threshold} + placeholder="10" + /> + handleInputChange('reorder_point', parseFloat(e.target.value) || 0)} + error={errors.reorder_point} + placeholder="20" + /> + handleInputChange('reorder_quantity', parseFloat(e.target.value) || 0)} + error={errors.reorder_quantity} + placeholder="50" + /> +
+ + handleInputChange('max_stock_level', e.target.value ? parseFloat(e.target.value) : undefined)} + error={errors.max_stock_level} + placeholder="Ej. 100" + helperText="Dejar vacío para stock ilimitado" + /> +
+
+
+
+ + {/* Storage and Preservation */} + +

Almacenamiento y Conservación

+ +
+
+ + + + + +
+ + {(formData.requires_refrigeration || formData.requires_freezing) && ( +
+ handleInputChange('storage_temperature_min', e.target.value ? parseFloat(e.target.value) : undefined)} + placeholder="Ej. -18" + /> + handleInputChange('storage_temperature_max', e.target.value ? parseFloat(e.target.value) : undefined)} + placeholder="Ej. 4" + /> +
+ )} + +
+ handleInputChange('storage_humidity_max', e.target.value ? parseFloat(e.target.value) : undefined)} + placeholder="Ej. 65" + /> + handleInputChange('shelf_life_days', e.target.value ? parseFloat(e.target.value) : undefined)} + error={errors.shelf_life_days} + placeholder="Ej. 30" + /> +
+ + handleInputChange('storage_instructions', e.target.value)} + placeholder="Ej. Mantener en lugar seco y fresco, alejado de la luz solar" + helperText="Instrucciones específicas para el almacenamiento del producto" + /> +
+
+ + {/* Allergen Information */} + +

Información de Alérgenos

+ +
+ {[ + { key: 'contains_gluten', label: 'Gluten' }, + { key: 'contains_dairy', label: 'Lácteos' }, + { key: 'contains_eggs', label: 'Huevos' }, + { key: 'contains_nuts', label: 'Frutos Secos' }, + { key: 'contains_soy', label: 'Soja' }, + { key: 'contains_shellfish', label: 'Mariscos' }, + ].map(({ key, label }) => ( + + ))} +
+
+ + {/* Form Actions */} +
+ + +
+ + + {/* Classification Suggestions Modal */} + setShowClassificationModal(false)} + title="Sugerencias de Clasificación" + size="md" + > + {classificationSuggestions && ( +
+

+ Se han encontrado las siguientes sugerencias para "{formData.name}": +

+ +
+
+ Categoría: + + {BAKERY_CATEGORIES[classificationSuggestions.category as keyof typeof BAKERY_CATEGORIES]?.label || classificationSuggestions.category} + +
+ + {classificationSuggestions.subcategory && ( +
+ Subcategoría: + {classificationSuggestions.subcategory} +
+ )} + +
+ Unidad Sugerida: + + {UNITS_OF_MEASURE.find(u => u.value === classificationSuggestions.suggested_unit)?.label || classificationSuggestions.suggested_unit} + +
+ +
+ Perecedero: + + {classificationSuggestions.is_perishable ? 'Sí' : 'No'} + +
+ +
+ Confianza: + 0.8 ? "success" : + classificationSuggestions.confidence > 0.6 ? "warning" : "error" + }> + {(classificationSuggestions.confidence * 100).toFixed(0)}% + +
+
+ +
+ + +
+
+ )} +
+
+ ); +}; + +export default InventoryForm; \ No newline at end of file diff --git a/frontend/src/components/domain/inventory/InventoryTable.tsx b/frontend/src/components/domain/inventory/InventoryTable.tsx new file mode 100644 index 00000000..7115aabc --- /dev/null +++ b/frontend/src/components/domain/inventory/InventoryTable.tsx @@ -0,0 +1,627 @@ +import React, { useState, useCallback, useMemo } from 'react'; +import { clsx } from 'clsx'; +import { Table, TableColumn } from '../../ui'; +import { Badge } from '../../ui'; +import { Button } from '../../ui'; +import { Input } from '../../ui'; +import { Select } from '../../ui'; +import { Modal } from '../../ui'; +import { ConfirmDialog } from '../../shared'; +import { InventoryFilters, IngredientResponse, UnitOfMeasure, SortOrder } from '../../../types/inventory.types'; +import { inventoryService } from '../../../services/api/inventory.service'; +import { StockLevelIndicator } from './StockLevelIndicator'; + +export interface InventoryTableProps { + data: IngredientResponse[]; + loading?: boolean; + total?: number; + page?: number; + pageSize?: number; + filters?: InventoryFilters; + selectedItems?: string[]; + className?: string; + onPageChange?: (page: number, pageSize: number) => void; + onFiltersChange?: (filters: InventoryFilters) => void; + onSelectionChange?: (selectedIds: string[]) => void; + onEdit?: (item: IngredientResponse) => void; + onDelete?: (item: IngredientResponse) => void; + onAdjustStock?: (item: IngredientResponse) => void; + onMarkExpired?: (item: IngredientResponse) => void; + onRefresh?: () => void; + onExport?: () => void; + onBulkAction?: (action: string, items: IngredientResponse[]) => void; +} + +// Spanish bakery categories +const BAKERY_CATEGORIES = [ + { value: '', label: 'Todas las categorías' }, + { value: 'harinas', label: 'Harinas' }, + { value: 'levaduras', label: 'Levaduras' }, + { value: 'azucares', label: 'Azúcares y Endulzantes' }, + { value: 'chocolates', label: 'Chocolates y Cacao' }, + { value: 'frutas', label: 'Frutas y Frutos Secos' }, + { value: 'lacteos', label: 'Lácteos' }, + { value: 'huevos', label: 'Huevos' }, + { value: 'mantequillas', label: 'Mantequillas y Grasas' }, + { value: 'especias', label: 'Especias y Aromas' }, + { value: 'conservantes', label: 'Conservantes y Aditivos' }, + { value: 'decoracion', label: 'Decoración' }, + { value: 'envases', label: 'Envases y Embalajes' }, + { value: 'utensilios', label: 'Utensilios y Equipos' }, + { value: 'limpieza', label: 'Limpieza e Higiene' }, +]; + +const STOCK_LEVEL_FILTERS = [ + { value: '', label: 'Todos los niveles' }, + { value: 'good', label: 'Stock Normal' }, + { value: 'low', label: 'Stock Bajo' }, + { value: 'critical', label: 'Stock Crítico' }, + { value: 'out', label: 'Sin Stock' }, +]; + +const SORT_OPTIONS = [ + { value: 'name_asc', label: 'Nombre (A-Z)' }, + { value: 'name_desc', label: 'Nombre (Z-A)' }, + { value: 'category_asc', label: 'Categoría (A-Z)' }, + { value: 'current_stock_asc', label: 'Stock (Menor a Mayor)' }, + { value: 'current_stock_desc', label: 'Stock (Mayor a Menor)' }, + { value: 'updated_at_desc', label: 'Actualizado Recientemente' }, + { value: 'created_at_desc', label: 'Agregado Recientemente' }, +]; + +export const InventoryTable: React.FC = ({ + data, + loading = false, + total = 0, + page = 1, + pageSize = 20, + filters = {}, + selectedItems = [], + className, + onPageChange, + onFiltersChange, + onSelectionChange, + onEdit, + onDelete, + onAdjustStock, + onMarkExpired, + onRefresh, + onExport, + onBulkAction, +}) => { + const [localFilters, setLocalFilters] = useState(filters); + const [searchValue, setSearchValue] = useState(filters.search || ''); + const [showDeleteDialog, setShowDeleteDialog] = useState(false); + const [showBulkActionModal, setShowBulkActionModal] = useState(false); + const [itemToDelete, setItemToDelete] = useState(null); + const [selectedBulkAction, setSelectedBulkAction] = useState(''); + + // Get selected items data + const selectedItemsData = useMemo(() => { + return (data || []).filter(item => selectedItems.includes(item.id)); + }, [data, selectedItems]); + + // Handle search with debouncing + const handleSearch = useCallback((value: string) => { + setSearchValue(value); + const newFilters = { ...localFilters, search: value || undefined }; + setLocalFilters(newFilters); + onFiltersChange?.(newFilters); + }, [localFilters, onFiltersChange]); + + // Handle filter changes + const handleFilterChange = useCallback((key: keyof InventoryFilters, value: any) => { + const newFilters = { ...localFilters, [key]: value || undefined }; + setLocalFilters(newFilters); + onFiltersChange?.(newFilters); + }, [localFilters, onFiltersChange]); + + // Handle sort changes + const handleSortChange = useCallback((value: string) => { + if (!value) return; + + const [field, order] = value.split('_'); + const newFilters = { + ...localFilters, + sort_by: field as any, + sort_order: order as SortOrder, + }; + setLocalFilters(newFilters); + onFiltersChange?.(newFilters); + }, [localFilters, onFiltersChange]); + + // Clear all filters + const handleClearFilters = useCallback(() => { + const clearedFilters = { search: undefined }; + setLocalFilters(clearedFilters); + setSearchValue(''); + onFiltersChange?.(clearedFilters); + }, [onFiltersChange]); + + // Handle delete confirmation + const handleDeleteClick = useCallback((item: IngredientResponse) => { + setItemToDelete(item); + setShowDeleteDialog(true); + }, []); + + const handleDeleteConfirm = useCallback(() => { + if (itemToDelete) { + onDelete?.(itemToDelete); + setItemToDelete(null); + } + setShowDeleteDialog(false); + }, [itemToDelete, onDelete]); + + // Handle bulk actions + const handleBulkAction = useCallback((action: string) => { + if (selectedItemsData.length === 0) return; + + if (action === 'delete') { + setSelectedBulkAction(action); + setShowBulkActionModal(true); + } else { + onBulkAction?.(action, selectedItemsData); + } + }, [selectedItemsData, onBulkAction]); + + const handleBulkActionConfirm = useCallback(() => { + if (selectedBulkAction && selectedItemsData.length > 0) { + onBulkAction?.(selectedBulkAction, selectedItemsData); + } + setShowBulkActionModal(false); + setSelectedBulkAction(''); + }, [selectedBulkAction, selectedItemsData, onBulkAction]); + + // Get stock level for filtering + const getStockLevel = useCallback((item: IngredientResponse): string => { + if (!item.current_stock || item.current_stock <= 0) return 'out'; + if (item.needs_reorder) return 'critical'; + if (item.is_low_stock) return 'low'; + return 'good'; + }, []); + + // Apply local filtering for stock levels + const filteredData = useMemo(() => { + const dataArray = data || []; + if (!localFilters.is_low_stock && !localFilters.needs_reorder) return dataArray; + + return dataArray.filter(item => { + const stockLevel = getStockLevel(item); + if (localFilters.is_low_stock && stockLevel !== 'low') return false; + if (localFilters.needs_reorder && stockLevel !== 'critical') return false; + return true; + }); + }, [data, localFilters.is_low_stock, localFilters.needs_reorder, getStockLevel]); + + // Table columns configuration + const columns: TableColumn[] = [ + { + key: 'name', + title: 'Nombre', + dataIndex: 'name', + sortable: true, + width: '25%', + render: (value: string, record: IngredientResponse) => ( +
+
{value}
+ {record.brand && ( +
{record.brand}
+ )} + {record.sku && ( +
SKU: {record.sku}
+ )} +
+ ), + }, + { + key: 'category', + title: 'Categoría', + dataIndex: 'category', + sortable: true, + width: '15%', + render: (value: string) => ( + + {BAKERY_CATEGORIES.find(cat => cat.value === value)?.label || value || 'Sin categoría'} + + ), + }, + { + key: 'current_stock', + title: 'Stock Actual', + dataIndex: 'current_stock', + sortable: true, + width: '15%', + align: 'right', + render: (value: number | undefined, record: IngredientResponse) => ( +
+
+ + {value?.toFixed(2) ?? '0.00'} + + + {record.unit_of_measure} + +
+ +
+ ), + }, + { + key: 'thresholds', + title: 'Mín / Máx', + width: '12%', + align: 'center', + render: (_, record: IngredientResponse) => ( +
+
+ {record.low_stock_threshold} / {record.max_stock_level || '∞'} +
+
+ Reorden: {record.reorder_point} +
+
+ ), + }, + { + key: 'price', + title: 'Precio', + width: '10%', + align: 'right', + render: (_, record: IngredientResponse) => ( +
+ {record.standard_cost && ( +
+ €{record.standard_cost.toFixed(2)} +
+ )} + {record.last_purchase_price && record.last_purchase_price !== record.standard_cost && ( +
+ Último: €{record.last_purchase_price.toFixed(2)} +
+ )} +
+ ), + }, + { + key: 'status', + title: 'Estado', + width: '10%', + align: 'center', + render: (_, record: IngredientResponse) => { + const badges = []; + + if (record.needs_reorder) { + badges.push( + + Crítico + + ); + } else if (record.is_low_stock) { + badges.push( + + Bajo + + ); + } else { + badges.push( + + OK + + ); + } + + if (!record.is_active) { + badges.push( + + Inactivo + + ); + } + + if (record.is_perishable) { + badges.push( + + P + + ); + } + + return ( +
+ {badges} +
+ ); + }, + }, + { + key: 'actions', + title: 'Acciones', + width: '13%', + align: 'center', + render: (_, record: IngredientResponse) => ( +
+ + + +
+ ), + }, + ]; + + const currentSortValue = localFilters.sort_by && localFilters.sort_order + ? `${localFilters.sort_by}_${localFilters.sort_order}` + : ''; + + return ( +
+ {/* Filters and Actions */} +
+ {/* Search and Quick Actions */} +
+
+ handleSearch(e.target.value)} + leftIcon={ + + + + } + /> +
+
+ + +
+
+ + {/* Advanced Filters */} +
+ { + handleFilterChange('needs_reorder', value === 'critical'); + handleFilterChange('is_low_stock', value === 'low'); + }} + options={STOCK_LEVEL_FILTERS} + /> +
{ + const newSelection = selected + ? [...selectedItems, record.id] + : selectedItems.filter(id => id !== record.id); + onSelectionChange?.(newSelection); + }, + onSelectAll: (selected, selectedRows, changeRows) => { + const newSelection = selected + ? filteredData.map(item => item.id) + : []; + onSelectionChange?.(newSelection); + }, + }} + pagination={{ + current: page, + pageSize, + total, + showSizeChanger: true, + showQuickJumper: true, + showTotal: (total, range) => + `${range[0]}-${range[1]} de ${total} ingredientes`, + onChange: onPageChange, + }} + locale={{ + emptyText: 'No se encontraron ingredientes', + selectAll: 'Seleccionar todos', + selectRow: 'Seleccionar fila', + }} + /> + + {/* Delete Confirmation Dialog */} + + + {/* Bulk Action Confirmation */} + setShowBulkActionModal(false)} + title="Confirmar Acción Masiva" + size="md" + > +
+

+ ¿Estás seguro de que quieres {selectedBulkAction === 'delete' ? 'eliminar' : 'procesar'} {' '} + {selectedItemsData.length} elemento{selectedItemsData.length !== 1 ? 's' : ''}? +

+ {selectedItemsData.length > 0 && ( +
+
    + {selectedItemsData.map(item => ( +
  • • {item.name}
  • + ))} +
+
+ )} +
+ + +
+
+
+ + ); +}; + +export default InventoryTable; \ No newline at end of file diff --git a/frontend/src/components/domain/inventory/LowStockAlert.tsx b/frontend/src/components/domain/inventory/LowStockAlert.tsx new file mode 100644 index 00000000..882b7722 --- /dev/null +++ b/frontend/src/components/domain/inventory/LowStockAlert.tsx @@ -0,0 +1,538 @@ +import React, { useState, useEffect, useCallback } from 'react'; +import { clsx } from 'clsx'; +import { Card } from '../../ui'; +import { Badge } from '../../ui'; +import { Button } from '../../ui'; +import { Modal } from '../../ui'; +import { Input } from '../../ui'; +import { EmptyState } from '../../shared'; +import { LoadingSpinner } from '../../shared'; +import { StockAlert, IngredientResponse, AlertSeverity } from '../../../types/inventory.types'; +import { inventoryService } from '../../../services/api/inventory.service'; +import { StockLevelIndicator } from './StockLevelIndicator'; + +export interface LowStockAlertProps { + alerts?: StockAlert[]; + autoRefresh?: boolean; + refreshInterval?: number; + maxItems?: number; + showDismissed?: boolean; + compact?: boolean; + className?: string; + onReorder?: (item: IngredientResponse) => void; + onAdjustMinimums?: (item: IngredientResponse) => void; + onDismiss?: (alertId: string) => void; + onRefresh?: () => void; + onViewAll?: () => void; +} + +interface GroupedAlerts { + critical: StockAlert[]; + low: StockAlert[]; + out: StockAlert[]; +} + +interface SupplierSuggestion { + id: string; + name: string; + lastPrice?: number; + lastOrderDate?: string; + reliability: number; +} + +export const LowStockAlert: React.FC = ({ + alerts = [], + autoRefresh = false, + refreshInterval = 30000, + maxItems = 10, + showDismissed = false, + compact = false, + className, + onReorder, + onAdjustMinimums, + onDismiss, + onRefresh, + onViewAll, +}) => { + const [localAlerts, setLocalAlerts] = useState(alerts); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + const [dismissedAlerts, setDismissedAlerts] = useState>(new Set()); + const [showReorderModal, setShowReorderModal] = useState(false); + const [showAdjustModal, setShowAdjustModal] = useState(false); + const [selectedAlert, setSelectedAlert] = useState(null); + const [reorderQuantity, setReorderQuantity] = useState(0); + const [newMinimumThreshold, setNewMinimumThreshold] = useState(0); + const [supplierSuggestions, setSupplierSuggestions] = useState([]); + + // Update local alerts when prop changes + useEffect(() => { + setLocalAlerts(alerts); + }, [alerts]); + + // Auto-refresh functionality + useEffect(() => { + if (!autoRefresh || refreshInterval <= 0) return; + + const interval = setInterval(() => { + onRefresh?.(); + }, refreshInterval); + + return () => clearInterval(interval); + }, [autoRefresh, refreshInterval, onRefresh]); + + // Load supplier suggestions when modal opens + useEffect(() => { + if (showReorderModal && selectedAlert?.ingredient_id) { + loadSupplierSuggestions(selectedAlert.ingredient_id); + } + }, [showReorderModal, selectedAlert]); + + const loadSupplierSuggestions = async (ingredientId: string) => { + try { + // This would typically call a suppliers API + // For now, we'll simulate some data + setSupplierSuggestions([ + { id: '1', name: 'Proveedor Principal', lastPrice: 2.50, reliability: 95 }, + { id: '2', name: 'Proveedor Alternativo', lastPrice: 2.80, reliability: 87 }, + ]); + } catch (error) { + console.error('Error loading supplier suggestions:', error); + } + }; + + // Group alerts by severity + const groupedAlerts = React.useMemo((): GroupedAlerts => { + const filtered = localAlerts.filter(alert => { + if (!alert.is_active) return false; + if (!showDismissed && dismissedAlerts.has(alert.id)) return false; + return true; + }); + + return { + critical: filtered.filter(alert => + alert.severity === AlertSeverity.CRITICAL || + alert.alert_type === 'out_of_stock' + ), + low: filtered.filter(alert => + alert.severity === AlertSeverity.HIGH && + alert.alert_type === 'low_stock' + ), + out: filtered.filter(alert => alert.alert_type === 'out_of_stock'), + }; + }, [localAlerts, showDismissed, dismissedAlerts]); + + const totalActiveAlerts = groupedAlerts.critical.length + groupedAlerts.low.length; + + // Handle alert dismissal + const handleDismiss = useCallback(async (alertId: string, temporary: boolean = true) => { + if (temporary) { + setDismissedAlerts(prev => new Set(prev).add(alertId)); + } else { + try { + await inventoryService.acknowledgeAlert(alertId); + onDismiss?.(alertId); + } catch (error) { + console.error('Error dismissing alert:', error); + setError('Error al descartar la alerta'); + } + } + }, [onDismiss]); + + // Handle reorder action + const handleReorder = useCallback((alert: StockAlert) => { + setSelectedAlert(alert); + setReorderQuantity(alert.ingredient?.reorder_quantity || 0); + setShowReorderModal(true); + }, []); + + // Handle adjust minimums action + const handleAdjustMinimums = useCallback((alert: StockAlert) => { + setSelectedAlert(alert); + setNewMinimumThreshold(alert.threshold_value || alert.ingredient?.low_stock_threshold || 0); + setShowAdjustModal(true); + }, []); + + // Confirm reorder + const handleConfirmReorder = useCallback(() => { + if (selectedAlert?.ingredient) { + onReorder?.(selectedAlert.ingredient); + } + setShowReorderModal(false); + setSelectedAlert(null); + }, [selectedAlert, onReorder]); + + // Confirm adjust minimums + const handleConfirmAdjust = useCallback(() => { + if (selectedAlert?.ingredient) { + onAdjustMinimums?.(selectedAlert.ingredient); + } + setShowAdjustModal(false); + setSelectedAlert(null); + }, [selectedAlert, onAdjustMinimums]); + + // Get severity badge variant + const getSeverityVariant = (severity: AlertSeverity): any => { + switch (severity) { + case AlertSeverity.CRITICAL: + return 'error'; + case AlertSeverity.HIGH: + return 'warning'; + case AlertSeverity.MEDIUM: + return 'info'; + default: + return 'secondary'; + } + }; + + // Render alert item + const renderAlertItem = (alert: StockAlert, index: number) => { + const ingredient = alert.ingredient; + if (!ingredient) return null; + + const isCompact = compact || index >= maxItems; + + return ( +
+
+ {/* Stock indicator */} + + + {/* Alert info */} +
+
+

+ {ingredient.name} +

+ + {alert.severity === AlertSeverity.CRITICAL ? 'Crítico' : + alert.severity === AlertSeverity.HIGH ? 'Bajo' : + 'Normal'} + + {ingredient.category && ( + + {ingredient.category} + + )} +
+ + {!isCompact && ( +

+ {alert.message} +

+ )} + +
+ + Stock: {alert.current_quantity || 0} {ingredient.unit_of_measure} + + {alert.threshold_value && ( + + Mín: {alert.threshold_value} {ingredient.unit_of_measure} + + )} +
+
+
+ + {/* Actions */} +
+ {!isCompact && ( + <> + + + + + )} + + +
+
+ ); + }; + + // Loading state + if (loading) { + return ( + +
+ + Cargando alertas... +
+
+ ); + } + + // Error state + if (error) { + return ( + +
+ + + +
+

Error al cargar alertas

+

{error}

+
+ +
+
+ ); + } + + // Empty state + if (totalActiveAlerts === 0) { + return ( + + + + + } + /> + + ); + } + + const visibleAlerts = [...groupedAlerts.critical, ...groupedAlerts.low].slice(0, maxItems); + const hasMoreAlerts = totalActiveAlerts > maxItems; + + return ( +
+ + {/* Header */} +
+
+

+ Alertas de Stock +

+ + +
+ +
+ + + {hasMoreAlerts && onViewAll && ( + + )} +
+
+ + {/* Alerts list */} +
+ {visibleAlerts.map((alert, index) => renderAlertItem(alert, index))} +
+ + {/* Footer actions */} + {hasMoreAlerts && ( +
+

+ Mostrando {visibleAlerts.length} de {totalActiveAlerts} alertas +

+
+ )} +
+ + {/* Reorder Modal */} + setShowReorderModal(false)} + title="Crear Orden de Compra" + size="md" + > + {selectedAlert && ( +
+
+

+ {selectedAlert.ingredient?.name} +

+

+ Stock actual: {selectedAlert.current_quantity || 0} {selectedAlert.ingredient?.unit_of_measure} +

+

+ Stock mínimo: {selectedAlert.ingredient?.low_stock_threshold} {selectedAlert.ingredient?.unit_of_measure} +

+
+ + setReorderQuantity(parseFloat(e.target.value) || 0)} + rightAddon={selectedAlert.ingredient?.unit_of_measure} + helperText="Cantidad sugerida basada en el punto de reorden configurado" + /> + + {supplierSuggestions.length > 0 && ( +
+

+ Proveedores Sugeridos +

+
+ {supplierSuggestions.map(supplier => ( +
+
+ {supplier.name} + + {supplier.reliability}% confiable + +
+ {supplier.lastPrice && ( +

+ Último precio: €{supplier.lastPrice.toFixed(2)} +

+ )} +
+ ))} +
+
+ )} + +
+ + +
+
+ )} +
+ + {/* Adjust Minimums Modal */} + setShowAdjustModal(false)} + title="Ajustar Umbrales Mínimos" + size="md" + > + {selectedAlert && ( +
+
+

+ {selectedAlert.ingredient?.name} +

+

+ Stock actual: {selectedAlert.current_quantity || 0} {selectedAlert.ingredient?.unit_of_measure} +

+
+ + setNewMinimumThreshold(parseFloat(e.target.value) || 0)} + rightAddon={selectedAlert.ingredient?.unit_of_measure} + helperText="Ajusta el nivel mínimo de stock para este producto" + /> + +
+ + +
+
+ )} +
+
+ ); +}; + +export default LowStockAlert; \ No newline at end of file diff --git a/frontend/src/components/domain/inventory/StockLevelIndicator.tsx b/frontend/src/components/domain/inventory/StockLevelIndicator.tsx new file mode 100644 index 00000000..6e33eaef --- /dev/null +++ b/frontend/src/components/domain/inventory/StockLevelIndicator.tsx @@ -0,0 +1,411 @@ +import React, { useMemo } from 'react'; +import { clsx } from 'clsx'; +import { Tooltip } from '../../ui/Tooltip'; + +export interface StockLevelIndicatorProps { + current: number; + minimum?: number; + maximum?: number; + reorderPoint?: number; + unit?: string; + size?: 'xs' | 'sm' | 'md' | 'lg'; + variant?: 'bar' | 'gauge' | 'badge' | 'minimal'; + showLabels?: boolean; + showPercentage?: boolean; + showTrend?: boolean; + trend?: 'up' | 'down' | 'stable'; + className?: string; + onClick?: () => void; +} + +type StockStatus = 'good' | 'low' | 'critical' | 'out' | 'overstocked'; + +interface StockLevel { + status: StockStatus; + label: string; + color: string; + bgColor: string; + percentage: number; +} + +export const StockLevelIndicator: React.FC = ({ + current, + minimum = 0, + maximum, + reorderPoint, + unit = '', + size = 'md', + variant = 'bar', + showLabels = false, + showPercentage = false, + showTrend = false, + trend = 'stable', + className, + onClick, +}) => { + // Calculate stock level and status + const stockLevel = useMemo(() => { + // Handle out of stock + if (current <= 0) { + return { + status: 'out', + label: 'Sin Stock', + color: 'text-color-error', + bgColor: 'bg-color-error', + percentage: 0, + }; + } + + // Calculate percentage based on maximum or use current value relative to minimum + const percentage = maximum + ? Math.min((current / maximum) * 100, 100) + : Math.min(((current - minimum) / (minimum || 1)) * 100 + 100, 200); + + // Handle overstocked (if maximum is defined) + if (maximum && current > maximum * 1.2) { + return { + status: 'overstocked', + label: 'Sobrestock', + color: 'text-color-info', + bgColor: 'bg-color-info', + percentage: Math.min(percentage, 150), + }; + } + + // Handle critical level (reorder point or below minimum) + const criticalThreshold = reorderPoint || minimum; + if (current <= criticalThreshold) { + return { + status: 'critical', + label: 'Crítico', + color: 'text-color-error', + bgColor: 'bg-color-error', + percentage: Math.max(percentage, 5), // Minimum visible bar + }; + } + + // Handle low stock (within 20% above critical threshold) + const lowThreshold = criticalThreshold * 1.2; + if (current <= lowThreshold) { + return { + status: 'low', + label: 'Bajo', + color: 'text-color-warning', + bgColor: 'bg-color-warning', + percentage, + }; + } + + // Good stock level + return { + status: 'good', + label: 'Normal', + color: 'text-color-success', + bgColor: 'bg-color-success', + percentage, + }; + }, [current, minimum, maximum, reorderPoint]); + + const sizeClasses = { + xs: { + container: 'h-1', + text: 'text-xs', + badge: 'px-1.5 py-0.5 text-xs', + gauge: 'w-6 h-6', + }, + sm: { + container: 'h-2', + text: 'text-sm', + badge: 'px-2 py-0.5 text-xs', + gauge: 'w-8 h-8', + }, + md: { + container: 'h-3', + text: 'text-sm', + badge: 'px-2.5 py-1 text-sm', + gauge: 'w-10 h-10', + }, + lg: { + container: 'h-4', + text: 'text-base', + badge: 'px-3 py-1.5 text-sm', + gauge: 'w-12 h-12', + }, + }; + + // Trend arrow component + const TrendArrow = () => { + if (!showTrend || trend === 'stable') return null; + + const trendClasses = { + up: 'text-color-success transform rotate-0', + down: 'text-color-error transform rotate-180', + stable: 'text-text-tertiary', + }; + + return ( + + + + ); + }; + + // Tooltip content + const tooltipContent = ( +
+
Estado: {stockLevel.label}
+
Stock actual: {current.toFixed(2)} {unit}
+ {minimum > 0 &&
Mínimo: {minimum.toFixed(2)} {unit}
} + {maximum &&
Máximo: {maximum.toFixed(2)} {unit}
} + {reorderPoint &&
Punto de reorden: {reorderPoint.toFixed(2)} {unit}
} + {showPercentage && maximum && ( +
Nivel: {((current / maximum) * 100).toFixed(1)}%
+ )} + {showTrend && trend !== 'stable' && ( +
Tendencia: {trend === 'up' ? 'Subiendo' : 'Bajando'}
+ )} +
+ ); + + // Badge variant + if (variant === 'badge') { + const badgeContent = ( + + {stockLevel.label} + {showPercentage && maximum && ` (${((current / maximum) * 100).toFixed(0)}%)`} + + + ); + + return ( + + {badgeContent} + + ); + } + + // Minimal variant (just colored dot) + if (variant === 'minimal') { + const minimalContent = ( +
+
+ {showLabels && ( + + {stockLevel.label} + + )} + +
+ ); + + return ( + + {minimalContent} + + ); + } + + // Gauge variant (circular progress) + if (variant === 'gauge') { + const radius = size === 'xs' ? 8 : size === 'sm' ? 12 : size === 'md' ? 16 : 20; + const circumference = 2 * Math.PI * radius; + const strokeDasharray = `${(stockLevel.percentage / 100) * circumference} ${circumference}`; + + const gaugeContent = ( +
+ + {/* Background circle */} + + {/* Progress circle */} + + + {(showLabels || showPercentage) && ( +
+ + {showPercentage && maximum + ? `${Math.round((current / maximum) * 100)}%` + : showLabels + ? stockLevel.label.charAt(0) + : '' + } + +
+ )} + +
+ ); + + return ( + + {gaugeContent} + + ); + } + + // Default bar variant + const barContent = ( +
+ {(showLabels || showPercentage) && ( +
+ {showLabels && ( + + {stockLevel.label} + + )} +
+ {showPercentage && maximum && ( + + {((current / maximum) * 100).toFixed(1)}% + + )} + +
+
+ )} + +
+
+
+ + {/* Threshold indicators for bar variant */} + {size !== 'xs' && (minimum > 0 || reorderPoint || maximum) && ( +
+ {/* Minimum threshold */} + {minimum > 0 && maximum && ( +
+ )} + {/* Reorder point */} + {reorderPoint && maximum && reorderPoint !== minimum && ( +
+ )} +
+ )} +
+ ); + + return ( + + {barContent} + + ); +}; + +// Helper hook for multiple stock indicators +export const useStockLevels = (items: Array<{ current: number; minimum?: number; maximum?: number; reorderPoint?: number }>) => { + return useMemo(() => { + const levels = { + good: 0, + low: 0, + critical: 0, + out: 0, + overstocked: 0, + }; + + items.forEach(item => { + const { current, minimum = 0, maximum, reorderPoint } = item; + + if (current <= 0) { + levels.out++; + } else if (maximum && current > maximum * 1.2) { + levels.overstocked++; + } else if (current <= (reorderPoint || minimum)) { + levels.critical++; + } else if (current <= (reorderPoint || minimum) * 1.2) { + levels.low++; + } else { + levels.good++; + } + }); + + return levels; + }, [items]); +}; + +export default StockLevelIndicator; \ No newline at end of file diff --git a/frontend/src/components/domain/inventory/index.ts b/frontend/src/components/domain/inventory/index.ts new file mode 100644 index 00000000..0fbd585e --- /dev/null +++ b/frontend/src/components/domain/inventory/index.ts @@ -0,0 +1,91 @@ +// Inventory Domain Components +export { default as InventoryTable, type InventoryTableProps } from './InventoryTable'; +export { default as StockLevelIndicator, type StockLevelIndicatorProps, useStockLevels } from './StockLevelIndicator'; +export { default as InventoryForm, type InventoryFormProps } from './InventoryForm'; +export { default as LowStockAlert, type LowStockAlertProps } from './LowStockAlert'; + +// Re-export related types from inventory types +export type { + InventoryFilters, + IngredientResponse, + StockAlert, + IngredientFormData, + UnitOfMeasure, + ProductType, + AlertSeverity, + AlertType, + SortOrder, +} from '../../../types/inventory.types'; + +// Utility exports for common inventory operations +export const INVENTORY_CONSTANTS = { + // Spanish bakery categories + BAKERY_CATEGORIES: [ + { value: 'harinas', label: 'Harinas' }, + { value: 'levaduras', label: 'Levaduras' }, + { value: 'azucares', label: 'Az�cares y Endulzantes' }, + { value: 'chocolates', label: 'Chocolates y Cacao' }, + { value: 'frutas', label: 'Frutas y Frutos Secos' }, + { value: 'lacteos', label: 'L�cteos' }, + { value: 'huevos', label: 'Huevos' }, + { value: 'mantequillas', label: 'Mantequillas y Grasas' }, + { value: 'especias', label: 'Especias y Aromas' }, + { value: 'conservantes', label: 'Conservantes y Aditivos' }, + { value: 'decoracion', label: 'Decoraci�n' }, + { value: 'envases', label: 'Envases y Embalajes' }, + { value: 'utensilios', label: 'Utensilios y Equipos' }, + { value: 'limpieza', label: 'Limpieza e Higiene' }, + ], + + // Stock level filters + STOCK_LEVEL_FILTERS: [ + { value: '', label: 'Todos los niveles' }, + { value: 'good', label: 'Stock Normal' }, + { value: 'low', label: 'Stock Bajo' }, + { value: 'critical', label: 'Stock Cr�tico' }, + { value: 'out', label: 'Sin Stock' }, + ], + + // Units of measure commonly used in Spanish bakeries + BAKERY_UNITS: [ + { value: 'kg', label: 'Kilogramo (kg)' }, + { value: 'g', label: 'Gramo (g)' }, + { value: 'l', label: 'Litro (l)' }, + { value: 'ml', label: 'Mililitro (ml)' }, + { value: 'piece', label: 'Pieza (pz)' }, + { value: 'package', label: 'Paquete' }, + { value: 'bag', label: 'Bolsa' }, + { value: 'box', label: 'Caja' }, + { value: 'dozen', label: 'Docena' }, + { value: 'cup', label: 'Taza' }, + { value: 'tbsp', label: 'Cucharada' }, + { value: 'tsp', label: 'Cucharadita' }, + ], + + // Default form values for new ingredients + DEFAULT_INGREDIENT_VALUES: { + low_stock_threshold: 10, + reorder_point: 20, + reorder_quantity: 50, + is_perishable: false, + requires_refrigeration: false, + requires_freezing: false, + }, + + // Alert severity colors and labels + ALERT_SEVERITY: { + CRITICAL: { label: 'Cr�tico', color: 'error', priority: 1 }, + HIGH: { label: 'Alto', color: 'warning', priority: 2 }, + MEDIUM: { label: 'Medio', color: 'info', priority: 3 }, + LOW: { label: 'Bajo', color: 'secondary', priority: 4 }, + }, + + // Stock status indicators + STOCK_STATUS: { + GOOD: { label: 'Normal', color: 'success' }, + LOW: { label: 'Bajo', color: 'warning' }, + CRITICAL: { label: 'Cr�tico', color: 'error' }, + OUT: { label: 'Sin Stock', color: 'error' }, + OVERSTOCKED: { label: 'Sobrestock', color: 'info' }, + }, +}; \ No newline at end of file diff --git a/frontend/src/components/domain/onboarding/CompanyInfoStep.tsx b/frontend/src/components/domain/onboarding/CompanyInfoStep.tsx new file mode 100644 index 00000000..713c16bb --- /dev/null +++ b/frontend/src/components/domain/onboarding/CompanyInfoStep.tsx @@ -0,0 +1,404 @@ +import React from 'react'; +import { Input, Select, Badge } from '../../ui'; +import type { OnboardingStepProps } from './OnboardingWizard'; + +interface CompanyInfo { + name: string; + type: 'artisan' | 'industrial' | 'chain' | 'mixed'; + size: 'small' | 'medium' | 'large'; + locations: number; + specialties: string[]; + address: { + street: string; + city: string; + state: string; + postal_code: string; + country: string; + }; + contact: { + phone: string; + email: string; + website?: string; + }; + established_year?: number; + tax_id?: string; +} + +const BAKERY_TYPES = [ + { value: 'artisan', label: 'Artesanal', description: 'Producción tradicional y manual' }, + { value: 'industrial', label: 'Industrial', description: 'Producción automatizada a gran escala' }, + { value: 'chain', label: 'Cadena', description: 'Múltiples ubicaciones con procesos estandarizados' }, + { value: 'mixed', label: 'Mixta', description: 'Combinación de métodos artesanales e industriales' }, +]; + +const BAKERY_SIZES = [ + { value: 'small', label: 'Pequeña', description: '1-10 empleados' }, + { value: 'medium', label: 'Mediana', description: '11-50 empleados' }, + { value: 'large', label: 'Grande', description: '50+ empleados' }, +]; + +const COMMON_SPECIALTIES = [ + 'Pan tradicional', + 'Bollería', + 'Repostería', + 'Pan integral', + 'Pasteles', + 'Productos sin gluten', + 'Productos veganos', + 'Pan artesanal', + 'Productos de temporada', + 'Catering', +]; + +export const CompanyInfoStep: React.FC = ({ + data, + onDataChange, +}) => { + const companyData: CompanyInfo = { + name: '', + type: 'artisan', + size: 'small', + locations: 1, + specialties: [], + address: { + street: '', + city: '', + state: '', + postal_code: '', + country: 'España', + }, + contact: { + phone: '', + email: '', + website: '', + }, + ...data, + }; + + const handleInputChange = (field: string, value: any) => { + onDataChange({ + ...companyData, + [field]: value, + }); + }; + + const handleAddressChange = (field: string, value: string) => { + onDataChange({ + ...companyData, + address: { + ...companyData.address, + [field]: value, + }, + }); + }; + + const handleContactChange = (field: string, value: string) => { + onDataChange({ + ...companyData, + contact: { + ...companyData.contact, + [field]: value, + }, + }); + }; + + const handleSpecialtyToggle = (specialty: string) => { + const currentSpecialties = companyData.specialties || []; + const updatedSpecialties = currentSpecialties.includes(specialty) + ? currentSpecialties.filter(s => s !== specialty) + : [...currentSpecialties, specialty]; + + handleInputChange('specialties', updatedSpecialties); + }; + + return ( +
+ {/* Basic Information */} +
+

+ Información básica +

+ +
+
+ + handleInputChange('name', e.target.value)} + placeholder="Ej: Panadería San Miguel" + className="w-full" + /> +
+ +
+ + +
+ +
+ + +
+ +
+ + handleInputChange('locations', parseInt(e.target.value) || 1)} + className="w-full" + /> +
+ +
+ + handleInputChange('established_year', parseInt(e.target.value) || undefined)} + placeholder="Ej: 1995" + className="w-full" + /> +
+ +
+ + handleInputChange('tax_id', e.target.value)} + placeholder="Ej: B12345678" + className="w-full" + /> +
+
+
+ + {/* Specialties */} +
+

+ Especialidades +

+

+ Selecciona los productos que produces habitualmente +

+ +
+ {COMMON_SPECIALTIES.map((specialty) => ( + + ))} +
+ + {companyData.specialties && companyData.specialties.length > 0 && ( +
+

+ Especialidades seleccionadas: +

+
+ {companyData.specialties.map((specialty) => ( + + {specialty} + + + ))} +
+
+ )} +
+ + {/* Address */} +
+

+ Dirección principal +

+ +
+
+ + handleAddressChange('street', e.target.value)} + placeholder="Calle, número, piso, puerta" + className="w-full" + /> +
+ +
+ + handleAddressChange('city', e.target.value)} + placeholder="Ej: Madrid" + className="w-full" + /> +
+ +
+ + handleAddressChange('state', e.target.value)} + placeholder="Ej: Madrid" + className="w-full" + /> +
+ +
+ + handleAddressChange('postal_code', e.target.value)} + placeholder="Ej: 28001" + className="w-full" + /> +
+ +
+ + +
+
+
+ + {/* Contact Information */} +
+

+ Información de contacto +

+ +
+
+ + handleContactChange('phone', e.target.value)} + placeholder="Ej: +34 911 234 567" + className="w-full" + /> +
+ +
+ + handleContactChange('email', e.target.value)} + placeholder="contacto@panaderia.com" + className="w-full" + /> +
+ +
+ + handleContactChange('website', e.target.value)} + placeholder="https://www.panaderia.com" + className="w-full" + /> +
+
+
+ + {/* Summary */} +
+

Resumen

+
+

Panadería: {companyData.name || 'Sin especificar'}

+

Tipo: {BAKERY_TYPES.find(t => t.value === companyData.type)?.label}

+

Tamaño: {BAKERY_SIZES.find(s => s.value === companyData.size)?.label}

+

Especialidades: {companyData.specialties?.length || 0} seleccionadas

+

Ubicaciones: {companyData.locations}

+
+
+
+ ); +}; + +export default CompanyInfoStep; \ No newline at end of file diff --git a/frontend/src/components/domain/onboarding/CompanyInfoStep.tsx.backup b/frontend/src/components/domain/onboarding/CompanyInfoStep.tsx.backup new file mode 100644 index 00000000..abf35405 --- /dev/null +++ b/frontend/src/components/domain/onboarding/CompanyInfoStep.tsx.backup @@ -0,0 +1,404 @@ +import React from 'react'; +import { Input, Select, Badge } from '../../ui'; +import type { OnboardingStepProps } from './OnboardingWizard'; + +interface CompanyInfo { + name: string; + type: 'artisan' | 'industrial' | 'chain' | 'mixed'; + size: 'small' | 'medium' | 'large'; + locations: number; + specialties: string[]; + address: { + street: string; + city: string; + state: string; + postal_code: string; + country: string; + }; + contact: { + phone: string; + email: string; + website?: string; + }; + established_year?: number; + tax_id?: string; +} + +const BAKERY_TYPES = [ + { value: 'artisan', label: 'Artesanal', description: 'Producción tradicional y manual' }, + { value: 'industrial', label: 'Industrial', description: 'Producción automatizada a gran escala' }, + { value: 'chain', label: 'Cadena', description: 'Múltiples ubicaciones con procesos estandarizados' }, + { value: 'mixed', label: 'Mixta', description: 'Combinación de métodos artesanales e industriales' }, +]; + +const BAKERY_SIZES = [ + { value: 'small', label: 'Pequeña', description: '1-10 empleados' }, + { value: 'medium', label: 'Mediana', description: '11-50 empleados' }, + { value: 'large', label: 'Grande', description: '50+ empleados' }, +]; + +const COMMON_SPECIALTIES = [ + 'Pan tradicional', + 'Bollería', + 'Repostería', + 'Pan integral', + 'Pasteles', + 'Productos sin gluten', + 'Productos veganos', + 'Pan artesanal', + 'Productos de temporada', + 'Catering', +]; + +export const CompanyInfoStep: React.FC = ({ + data, + onDataChange, +}) => { + const companyData: CompanyInfo = { + name: '', + type: 'artisan', + size: 'small', + locations: 1, + specialties: [], + address: { + street: '', + city: '', + state: '', + postal_code: '', + country: 'España', + }, + contact: { + phone: '', + email: '', + website: '', + }, + ...data, + }; + + const handleInputChange = (field: string, value: any) => { + onDataChange({ + ...companyData, + [field]: value, + }); + }; + + const handleAddressChange = (field: string, value: string) => { + onDataChange({ + ...companyData, + address: { + ...companyData.address, + [field]: value, + }, + }); + }; + + const handleContactChange = (field: string, value: string) => { + onDataChange({ + ...companyData, + contact: { + ...companyData.contact, + [field]: value, + }, + }); + }; + + const handleSpecialtyToggle = (specialty: string) => { + const currentSpecialties = companyData.specialties || []; + const updatedSpecialties = currentSpecialties.includes(specialty) + ? currentSpecialties.filter(s => s !== specialty) + : [...currentSpecialties, specialty]; + + handleInputChange('specialties', updatedSpecialties); + }; + + return ( +
+ {/* Basic Information */} +
+

+ Información básica +

+ +
+
+ + handleInputChange('name', e.target.value)} + placeholder="Ej: Panadería San Miguel" + className="w-full" + /> +
+ +
+ + +
+ +
+ + +
+ +
+ + handleInputChange('locations', parseInt(e.target.value) || 1)} + className="w-full" + /> +
+ +
+ + handleInputChange('established_year', parseInt(e.target.value) || undefined)} + placeholder="Ej: 1995" + className="w-full" + /> +
+ +
+ + handleInputChange('tax_id', e.target.value)} + placeholder="Ej: B12345678" + className="w-full" + /> +
+
+
+ + {/* Specialties */} +
+

+ Especialidades +

+

+ Selecciona los productos que produces habitualmente +

+ +
+ {COMMON_SPECIALTIES.map((specialty) => ( + + ))} +
+ + {companyData.specialties && companyData.specialties.length > 0 && ( +
+

+ Especialidades seleccionadas: +

+
+ {companyData.specialties.map((specialty) => ( + + {specialty} + + + ))} +
+
+ )} +
+ + {/* Address */} +
+

+ Dirección principal +

+ +
+
+ + handleAddressChange('street', e.target.value)} + placeholder="Calle, número, piso, puerta" + className="w-full" + /> +
+ +
+ + handleAddressChange('city', e.target.value)} + placeholder="Ej: Madrid" + className="w-full" + /> +
+ +
+ + handleAddressChange('state', e.target.value)} + placeholder="Ej: Madrid" + className="w-full" + /> +
+ +
+ + handleAddressChange('postal_code', e.target.value)} + placeholder="Ej: 28001" + className="w-full" + /> +
+ +
+ + +
+
+
+ + {/* Contact Information */} +
+

+ Información de contacto +

+ +
+
+ + handleContactChange('phone', e.target.value)} + placeholder="Ej: +34 911 234 567" + className="w-full" + /> +
+ +
+ + handleContactChange('email', e.target.value)} + placeholder="contacto@panaderia.com" + className="w-full" + /> +
+ +
+ + handleContactChange('website', e.target.value)} + placeholder="https://www.panaderia.com" + className="w-full" + /> +
+
+
+ + {/* Summary */} +
+

Resumen

+
+

Panadería: {companyData.name || 'Sin especificar'}

+

Tipo: {BAKERY_TYPES.find(t => t.value === companyData.type)?.label}

+

Tamaño: {BAKERY_SIZES.find(s => s.value === companyData.size)?.label}

+

Especialidades: {companyData.specialties?.length || 0} seleccionadas

+

Ubicaciones: {companyData.locations}

+
+
+
+ ); +}; + +export default CompanyInfoStep; \ No newline at end of file diff --git a/frontend/src/components/domain/onboarding/OnboardingWizard.tsx b/frontend/src/components/domain/onboarding/OnboardingWizard.tsx new file mode 100644 index 00000000..d5e6a047 --- /dev/null +++ b/frontend/src/components/domain/onboarding/OnboardingWizard.tsx @@ -0,0 +1,255 @@ +import React, { useState, useCallback } from 'react'; +import { Card, Button, Input, Select, Badge } from '../../ui'; + +export interface OnboardingStep { + id: string; + title: string; + description: string; + component: React.ComponentType; + isCompleted?: boolean; + isRequired?: boolean; + validation?: (data: any) => string | null; +} + +export interface OnboardingStepProps { + data: any; + onDataChange: (data: any) => void; + onNext: () => void; + onPrevious: () => void; + isFirstStep: boolean; + isLastStep: boolean; +} + +interface OnboardingWizardProps { + steps: OnboardingStep[]; + onComplete: (data: any) => void; + onExit?: () => void; + className?: string; +} + +export const OnboardingWizard: React.FC = ({ + steps, + onComplete, + onExit, + className = '', +}) => { + const [currentStepIndex, setCurrentStepIndex] = useState(0); + const [stepData, setStepData] = useState>({}); + const [completedSteps, setCompletedSteps] = useState>(new Set()); + const [validationErrors, setValidationErrors] = useState>({}); + + const currentStep = steps[currentStepIndex]; + + const updateStepData = useCallback((stepId: string, data: any) => { + setStepData(prev => ({ + ...prev, + [stepId]: { ...prev[stepId], ...data } + })); + + // Clear validation error for this step + setValidationErrors(prev => { + const newErrors = { ...prev }; + delete newErrors[stepId]; + return newErrors; + }); + }, []); + + const validateCurrentStep = useCallback(() => { + const step = currentStep; + const data = stepData[step.id] || {}; + + if (step.validation) { + const error = step.validation(data); + if (error) { + setValidationErrors(prev => ({ + ...prev, + [step.id]: error + })); + return false; + } + } + + // Mark step as completed + setCompletedSteps(prev => new Set(prev).add(step.id)); + return true; + }, [currentStep, stepData]); + + const goToNextStep = useCallback(() => { + if (validateCurrentStep()) { + if (currentStepIndex < steps.length - 1) { + setCurrentStepIndex(currentStepIndex + 1); + } else { + // All steps completed, call onComplete with all data + onComplete(stepData); + } + } + }, [currentStepIndex, steps.length, validateCurrentStep, onComplete, stepData]); + + const goToPreviousStep = useCallback(() => { + if (currentStepIndex > 0) { + setCurrentStepIndex(currentStepIndex - 1); + } + }, [currentStepIndex]); + + const goToStep = useCallback((stepIndex: number) => { + setCurrentStepIndex(stepIndex); + }, []); + + const calculateProgress = () => { + return (completedSteps.size / steps.length) * 100; + }; + + const renderStepIndicator = () => ( +
+ {steps.map((step, index) => { + const isCompleted = completedSteps.has(step.id); + const isCurrent = index === currentStepIndex; + const hasError = validationErrors[step.id]; + + return ( +
+ + +
+

+ {step.title} + {step.isRequired && *} +

+ {hasError && ( +

{hasError}

+ )} +
+ + {index < steps.length - 1 && ( +
+ )} +
+ ); + })} +
+ ); + + const renderProgressBar = () => ( +
+
+ + Progreso del onboarding + + + {completedSteps.size} de {steps.length} completados + +
+
+
+
+
+ ); + + if (!currentStep) { + return ( + +

No hay pasos de onboarding configurados.

+
+ ); + } + + const StepComponent = currentStep.component; + + return ( +
+ {/* Header */} +
+
+

+ Configuración inicial +

+

+ Completa estos pasos para comenzar a usar la plataforma +

+
+ + {onExit && ( + + )} +
+ + {renderProgressBar()} + {renderStepIndicator()} + + {/* Current Step Content */} + +
+

+ {currentStep.title} +

+

+ {currentStep.description} +

+
+ + updateStepData(currentStep.id, data)} + onNext={goToNextStep} + onPrevious={goToPreviousStep} + isFirstStep={currentStepIndex === 0} + isLastStep={currentStepIndex === steps.length - 1} + /> +
+ + {/* Navigation */} +
+ + +
+ + Paso {currentStepIndex + 1} de {steps.length} + +
+ + +
+
+ ); +}; + +export default OnboardingWizard; \ No newline at end of file diff --git a/frontend/src/components/domain/onboarding/OnboardingWizard.tsx.backup b/frontend/src/components/domain/onboarding/OnboardingWizard.tsx.backup new file mode 100644 index 00000000..9df201f8 --- /dev/null +++ b/frontend/src/components/domain/onboarding/OnboardingWizard.tsx.backup @@ -0,0 +1,255 @@ +import React, { useState, useCallback } from 'react'; +import { Card, Button, Input, Select, Badge } from '../../ui'; + +export interface OnboardingStep { + id: string; + title: string; + description: string; + component: React.ComponentType; + isCompleted?: boolean; + isRequired?: boolean; + validation?: (data: any) => string | null; +} + +export interface OnboardingStepProps { + data: any; + onDataChange: (data: any) => void; + onNext: () => void; + onPrevious: () => void; + isFirstStep: boolean; + isLastStep: boolean; +} + +interface OnboardingWizardProps { + steps: OnboardingStep[]; + onComplete: (data: any) => void; + onExit?: () => void; + className?: string; +} + +export const OnboardingWizard: React.FC = ({ + steps, + onComplete, + onExit, + className = '', +}) => { + const [currentStepIndex, setCurrentStepIndex] = useState(0); + const [stepData, setStepData] = useState>({}); + const [completedSteps, setCompletedSteps] = useState>(new Set()); + const [validationErrors, setValidationErrors] = useState>({}); + + const currentStep = steps[currentStepIndex]; + + const updateStepData = useCallback((stepId: string, data: any) => { + setStepData(prev => ({ + ...prev, + [stepId]: { ...prev[stepId], ...data } + })); + + // Clear validation error for this step + setValidationErrors(prev => { + const newErrors = { ...prev }; + delete newErrors[stepId]; + return newErrors; + }); + }, []); + + const validateCurrentStep = useCallback(() => { + const step = currentStep; + const data = stepData[step.id] || {}; + + if (step.validation) { + const error = step.validation(data); + if (error) { + setValidationErrors(prev => ({ + ...prev, + [step.id]: error + })); + return false; + } + } + + // Mark step as completed + setCompletedSteps(prev => new Set(prev).add(step.id)); + return true; + }, [currentStep, stepData]); + + const goToNextStep = useCallback(() => { + if (validateCurrentStep()) { + if (currentStepIndex < steps.length - 1) { + setCurrentStepIndex(currentStepIndex + 1); + } else { + // All steps completed, call onComplete with all data + onComplete(stepData); + } + } + }, [currentStepIndex, steps.length, validateCurrentStep, onComplete, stepData]); + + const goToPreviousStep = useCallback(() => { + if (currentStepIndex > 0) { + setCurrentStepIndex(currentStepIndex - 1); + } + }, [currentStepIndex]); + + const goToStep = useCallback((stepIndex: number) => { + setCurrentStepIndex(stepIndex); + }, []); + + const calculateProgress = () => { + return (completedSteps.size / steps.length) * 100; + }; + + const renderStepIndicator = () => ( +
+ {steps.map((step, index) => { + const isCompleted = completedSteps.has(step.id); + const isCurrent = index === currentStepIndex; + const hasError = validationErrors[step.id]; + + return ( +
+ + +
+

+ {step.title} + {step.isRequired && *} +

+ {hasError && ( +

{hasError}

+ )} +
+ + {index < steps.length - 1 && ( +
+ )} +
+ ); + })} +
+ ); + + const renderProgressBar = () => ( +
+
+ + Progreso del onboarding + + + {completedSteps.size} de {steps.length} completados + +
+
+
+
+
+ ); + + if (!currentStep) { + return ( + +

No hay pasos de onboarding configurados.

+
+ ); + } + + const StepComponent = currentStep.component; + + return ( +
+ {/* Header */} +
+
+

+ Configuración inicial +

+

+ Completa estos pasos para comenzar a usar la plataforma +

+
+ + {onExit && ( + + )} +
+ + {renderProgressBar()} + {renderStepIndicator()} + + {/* Current Step Content */} + +
+

+ {currentStep.title} +

+

+ {currentStep.description} +

+
+ + updateStepData(currentStep.id, data)} + onNext={goToNextStep} + onPrevious={goToPreviousStep} + isFirstStep={currentStepIndex === 0} + isLastStep={currentStepIndex === steps.length - 1} + /> +
+ + {/* Navigation */} +
+ + +
+ + Paso {currentStepIndex + 1} de {steps.length} + +
+ + +
+
+ ); +}; + +export default OnboardingWizard; \ No newline at end of file diff --git a/frontend/src/components/domain/onboarding/SystemSetupStep.tsx b/frontend/src/components/domain/onboarding/SystemSetupStep.tsx new file mode 100644 index 00000000..766e0f56 --- /dev/null +++ b/frontend/src/components/domain/onboarding/SystemSetupStep.tsx @@ -0,0 +1,556 @@ +import React from 'react'; +import { Card, Button, Input, Select, Badge } from '../../ui'; +import type { OnboardingStepProps } from './OnboardingWizard'; + +interface SystemConfig { + timezone: string; + currency: string; + language: string; + date_format: string; + number_format: string; + working_hours: { + start: string; + end: string; + days: number[]; + }; + notifications: { + email_enabled: boolean; + sms_enabled: boolean; + push_enabled: boolean; + alert_preferences: string[]; + }; + integrations: { + pos_system?: string; + accounting_software?: string; + payment_provider?: string; + }; + features: { + inventory_management: boolean; + production_planning: boolean; + sales_analytics: boolean; + customer_management: boolean; + financial_reporting: boolean; + quality_control: boolean; + }; +} + +const TIMEZONES = [ + { value: 'Europe/Madrid', label: 'Madrid (GMT+1)' }, + { value: 'Europe/London', label: 'London (GMT+0)' }, + { value: 'Europe/Paris', label: 'Paris (GMT+1)' }, + { value: 'America/New_York', label: 'New York (GMT-5)' }, + { value: 'America/Los_Angeles', label: 'Los Angeles (GMT-8)' }, + { value: 'America/Mexico_City', label: 'Mexico City (GMT-6)' }, + { value: 'America/Buenos_Aires', label: 'Buenos Aires (GMT-3)' }, +]; + +const CURRENCIES = [ + { value: 'EUR', label: 'Euro (€)', symbol: '€' }, + { value: 'USD', label: 'US Dollar ($)', symbol: '$' }, + { value: 'GBP', label: 'British Pound (£)', symbol: '£' }, + { value: 'MXN', label: 'Mexican Peso ($)', symbol: '$' }, + { value: 'ARS', label: 'Argentine Peso ($)', symbol: '$' }, + { value: 'COP', label: 'Colombian Peso ($)', symbol: '$' }, +]; + +const LANGUAGES = [ + { value: 'es', label: 'Español' }, + { value: 'en', label: 'English' }, + { value: 'fr', label: 'Français' }, + { value: 'pt', label: 'Português' }, + { value: 'it', label: 'Italiano' }, +]; + +const DAYS_OF_WEEK = [ + { value: 1, label: 'Lunes', short: 'L' }, + { value: 2, label: 'Martes', short: 'M' }, + { value: 3, label: 'Miércoles', short: 'X' }, + { value: 4, label: 'Jueves', short: 'J' }, + { value: 5, label: 'Viernes', short: 'V' }, + { value: 6, label: 'Sábado', short: 'S' }, + { value: 0, label: 'Domingo', short: 'D' }, +]; + +const ALERT_TYPES = [ + { value: 'low_stock', label: 'Stock bajo', description: 'Cuando los ingredientes están por debajo del mínimo' }, + { value: 'production_delays', label: 'Retrasos de producción', description: 'Cuando los lotes se retrasan' }, + { value: 'quality_issues', label: 'Problemas de calidad', description: 'Cuando se detectan problemas de calidad' }, + { value: 'financial_targets', label: 'Objetivos financieros', description: 'Cuando se alcanzan o no se cumplen objetivos' }, + { value: 'equipment_maintenance', label: 'Mantenimiento de equipos', description: 'Recordatorios de mantenimiento' }, + { value: 'food_safety', label: 'Seguridad alimentaria', description: 'Alertas relacionadas con seguridad alimentaria' }, +]; + +const FEATURES = [ + { + key: 'inventory_management', + title: 'Gestión de inventario', + description: 'Control de stock, ingredientes y materias primas', + recommended: true, + }, + { + key: 'production_planning', + title: 'Planificación de producción', + description: 'Programación de lotes y gestión de recetas', + recommended: true, + }, + { + key: 'sales_analytics', + title: 'Analytics de ventas', + description: 'Reportes y análisis de ventas y tendencias', + recommended: true, + }, + { + key: 'customer_management', + title: 'Gestión de clientes', + description: 'Base de datos de clientes y programa de fidelización', + recommended: false, + }, + { + key: 'financial_reporting', + title: 'Reportes financieros', + description: 'Análisis de costos, márgenes y rentabilidad', + recommended: true, + }, + { + key: 'quality_control', + title: 'Control de calidad', + description: 'Seguimiento de calidad y estándares', + recommended: true, + }, +]; + +export const SystemSetupStep: React.FC = ({ + data, + onDataChange, +}) => { + const systemData: SystemConfig = { + timezone: 'Europe/Madrid', + currency: 'EUR', + language: 'es', + date_format: 'DD/MM/YYYY', + number_format: 'European', + working_hours: { + start: '06:00', + end: '20:00', + days: [1, 2, 3, 4, 5, 6], // Monday to Saturday + }, + notifications: { + email_enabled: true, + sms_enabled: false, + push_enabled: true, + alert_preferences: ['low_stock', 'production_delays', 'quality_issues'], + }, + integrations: {}, + features: { + inventory_management: true, + production_planning: true, + sales_analytics: true, + customer_management: false, + financial_reporting: true, + quality_control: true, + }, + ...data, + }; + + const handleInputChange = (field: string, value: any) => { + onDataChange({ + ...systemData, + [field]: value, + }); + }; + + const handleWorkingHoursChange = (field: string, value: any) => { + onDataChange({ + ...systemData, + working_hours: { + ...systemData.working_hours, + [field]: value, + }, + }); + }; + + const handleNotificationChange = (field: string, value: any) => { + onDataChange({ + ...systemData, + notifications: { + ...systemData.notifications, + [field]: value, + }, + }); + }; + + const handleIntegrationChange = (field: string, value: string) => { + onDataChange({ + ...systemData, + integrations: { + ...systemData.integrations, + [field]: value, + }, + }); + }; + + const handleFeatureToggle = (feature: string) => { + onDataChange({ + ...systemData, + features: { + ...systemData.features, + [feature]: !systemData.features[feature as keyof typeof systemData.features], + }, + }); + }; + + const toggleWorkingDay = (day: number) => { + const currentDays = systemData.working_hours.days; + const updatedDays = currentDays.includes(day) + ? currentDays.filter(d => d !== day) + : [...currentDays, day].sort(); + + handleWorkingHoursChange('days', updatedDays); + }; + + const toggleAlertPreference = (alert: string) => { + const current = systemData.notifications.alert_preferences; + const updated = current.includes(alert) + ? current.filter(a => a !== alert) + : [...current, alert]; + + handleNotificationChange('alert_preferences', updated); + }; + + return ( +
+ {/* Regional Settings */} +
+

+ Configuración regional +

+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + {/* Working Hours */} +
+

+ Horario de trabajo +

+ +
+
+ + handleWorkingHoursChange('start', e.target.value)} + className="w-full" + /> +
+ +
+ + handleWorkingHoursChange('end', e.target.value)} + className="w-full" + /> +
+
+ +
+ +
+ {DAYS_OF_WEEK.map((day) => ( + + ))} +
+
+
+ + {/* Features */} +
+

+ Módulos y características +

+

+ Selecciona las características que quieres activar. Podrás cambiar esto más tarde. +

+ +
+ {FEATURES.map((feature) => ( + handleFeatureToggle(feature.key)} + > +
+
+
+

{feature.title}

+ {feature.recommended && ( + + Recomendado + + )} +
+

{feature.description}

+
+ +
+ {systemData.features[feature.key as keyof typeof systemData.features] && ( + + )} +
+
+
+ ))} +
+
+ + {/* Notifications */} +
+

+ Notificaciones +

+ +
+
+ + + + + +
+ +
+ +
+ {ALERT_TYPES.map((alert) => ( +
toggleAlertPreference(alert.value)} + > +
+
+
{alert.label}
+

{alert.description}

+
+
+ {systemData.notifications.alert_preferences.includes(alert.value) && ( + + )} +
+
+
+ ))} +
+
+
+
+ + {/* Integrations */} +
+

+ Integraciones (opcional) +

+

+ Estas integraciones se pueden configurar más tarde desde el panel de administración. +

+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + {/* Summary */} +
+

Configuración seleccionada

+
+

Zona horaria: {TIMEZONES.find(tz => tz.value === systemData.timezone)?.label}

+

Moneda: {CURRENCIES.find(c => c.value === systemData.currency)?.label}

+

Horario: {systemData.working_hours.start} - {systemData.working_hours.end}

+

Días operativos: {systemData.working_hours.days.length} días por semana

+

Módulos activados: {Object.values(systemData.features).filter(Boolean).length} de {Object.keys(systemData.features).length}

+
+
+
+ ); +}; + +export default SystemSetupStep; \ No newline at end of file diff --git a/frontend/src/components/domain/onboarding/SystemSetupStep.tsx.backup b/frontend/src/components/domain/onboarding/SystemSetupStep.tsx.backup new file mode 100644 index 00000000..0cbd9689 --- /dev/null +++ b/frontend/src/components/domain/onboarding/SystemSetupStep.tsx.backup @@ -0,0 +1,556 @@ +import React from 'react'; +import { Card, Button, Input, Select, Badge } from '../../ui'; +import type { OnboardingStepProps } from './OnboardingWizard'; + +interface SystemConfig { + timezone: string; + currency: string; + language: string; + date_format: string; + number_format: string; + working_hours: { + start: string; + end: string; + days: number[]; + }; + notifications: { + email_enabled: boolean; + sms_enabled: boolean; + push_enabled: boolean; + alert_preferences: string[]; + }; + integrations: { + pos_system?: string; + accounting_software?: string; + payment_provider?: string; + }; + features: { + inventory_management: boolean; + production_planning: boolean; + sales_analytics: boolean; + customer_management: boolean; + financial_reporting: boolean; + quality_control: boolean; + }; +} + +const TIMEZONES = [ + { value: 'Europe/Madrid', label: 'Madrid (GMT+1)' }, + { value: 'Europe/London', label: 'London (GMT+0)' }, + { value: 'Europe/Paris', label: 'Paris (GMT+1)' }, + { value: 'America/New_York', label: 'New York (GMT-5)' }, + { value: 'America/Los_Angeles', label: 'Los Angeles (GMT-8)' }, + { value: 'America/Mexico_City', label: 'Mexico City (GMT-6)' }, + { value: 'America/Buenos_Aires', label: 'Buenos Aires (GMT-3)' }, +]; + +const CURRENCIES = [ + { value: 'EUR', label: 'Euro (€)', symbol: '€' }, + { value: 'USD', label: 'US Dollar ($)', symbol: '$' }, + { value: 'GBP', label: 'British Pound (£)', symbol: '£' }, + { value: 'MXN', label: 'Mexican Peso ($)', symbol: '$' }, + { value: 'ARS', label: 'Argentine Peso ($)', symbol: '$' }, + { value: 'COP', label: 'Colombian Peso ($)', symbol: '$' }, +]; + +const LANGUAGES = [ + { value: 'es', label: 'Español' }, + { value: 'en', label: 'English' }, + { value: 'fr', label: 'Français' }, + { value: 'pt', label: 'Português' }, + { value: 'it', label: 'Italiano' }, +]; + +const DAYS_OF_WEEK = [ + { value: 1, label: 'Lunes', short: 'L' }, + { value: 2, label: 'Martes', short: 'M' }, + { value: 3, label: 'Miércoles', short: 'X' }, + { value: 4, label: 'Jueves', short: 'J' }, + { value: 5, label: 'Viernes', short: 'V' }, + { value: 6, label: 'Sábado', short: 'S' }, + { value: 0, label: 'Domingo', short: 'D' }, +]; + +const ALERT_TYPES = [ + { value: 'low_stock', label: 'Stock bajo', description: 'Cuando los ingredientes están por debajo del mínimo' }, + { value: 'production_delays', label: 'Retrasos de producción', description: 'Cuando los lotes se retrasan' }, + { value: 'quality_issues', label: 'Problemas de calidad', description: 'Cuando se detectan problemas de calidad' }, + { value: 'financial_targets', label: 'Objetivos financieros', description: 'Cuando se alcanzan o no se cumplen objetivos' }, + { value: 'equipment_maintenance', label: 'Mantenimiento de equipos', description: 'Recordatorios de mantenimiento' }, + { value: 'food_safety', label: 'Seguridad alimentaria', description: 'Alertas relacionadas con seguridad alimentaria' }, +]; + +const FEATURES = [ + { + key: 'inventory_management', + title: 'Gestión de inventario', + description: 'Control de stock, ingredientes y materias primas', + recommended: true, + }, + { + key: 'production_planning', + title: 'Planificación de producción', + description: 'Programación de lotes y gestión de recetas', + recommended: true, + }, + { + key: 'sales_analytics', + title: 'Analytics de ventas', + description: 'Reportes y análisis de ventas y tendencias', + recommended: true, + }, + { + key: 'customer_management', + title: 'Gestión de clientes', + description: 'Base de datos de clientes y programa de fidelización', + recommended: false, + }, + { + key: 'financial_reporting', + title: 'Reportes financieros', + description: 'Análisis de costos, márgenes y rentabilidad', + recommended: true, + }, + { + key: 'quality_control', + title: 'Control de calidad', + description: 'Seguimiento de calidad y estándares', + recommended: true, + }, +]; + +export const SystemSetupStep: React.FC = ({ + data, + onDataChange, +}) => { + const systemData: SystemConfig = { + timezone: 'Europe/Madrid', + currency: 'EUR', + language: 'es', + date_format: 'DD/MM/YYYY', + number_format: 'European', + working_hours: { + start: '06:00', + end: '20:00', + days: [1, 2, 3, 4, 5, 6], // Monday to Saturday + }, + notifications: { + email_enabled: true, + sms_enabled: false, + push_enabled: true, + alert_preferences: ['low_stock', 'production_delays', 'quality_issues'], + }, + integrations: {}, + features: { + inventory_management: true, + production_planning: true, + sales_analytics: true, + customer_management: false, + financial_reporting: true, + quality_control: true, + }, + ...data, + }; + + const handleInputChange = (field: string, value: any) => { + onDataChange({ + ...systemData, + [field]: value, + }); + }; + + const handleWorkingHoursChange = (field: string, value: any) => { + onDataChange({ + ...systemData, + working_hours: { + ...systemData.working_hours, + [field]: value, + }, + }); + }; + + const handleNotificationChange = (field: string, value: any) => { + onDataChange({ + ...systemData, + notifications: { + ...systemData.notifications, + [field]: value, + }, + }); + }; + + const handleIntegrationChange = (field: string, value: string) => { + onDataChange({ + ...systemData, + integrations: { + ...systemData.integrations, + [field]: value, + }, + }); + }; + + const handleFeatureToggle = (feature: string) => { + onDataChange({ + ...systemData, + features: { + ...systemData.features, + [feature]: !systemData.features[feature as keyof typeof systemData.features], + }, + }); + }; + + const toggleWorkingDay = (day: number) => { + const currentDays = systemData.working_hours.days; + const updatedDays = currentDays.includes(day) + ? currentDays.filter(d => d !== day) + : [...currentDays, day].sort(); + + handleWorkingHoursChange('days', updatedDays); + }; + + const toggleAlertPreference = (alert: string) => { + const current = systemData.notifications.alert_preferences; + const updated = current.includes(alert) + ? current.filter(a => a !== alert) + : [...current, alert]; + + handleNotificationChange('alert_preferences', updated); + }; + + return ( +
+ {/* Regional Settings */} +
+

+ Configuración regional +

+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + {/* Working Hours */} +
+

+ Horario de trabajo +

+ +
+
+ + handleWorkingHoursChange('start', e.target.value)} + className="w-full" + /> +
+ +
+ + handleWorkingHoursChange('end', e.target.value)} + className="w-full" + /> +
+
+ +
+ +
+ {DAYS_OF_WEEK.map((day) => ( + + ))} +
+
+
+ + {/* Features */} +
+

+ Módulos y características +

+

+ Selecciona las características que quieres activar. Podrás cambiar esto más tarde. +

+ +
+ {FEATURES.map((feature) => ( + handleFeatureToggle(feature.key)} + > +
+
+
+

{feature.title}

+ {feature.recommended && ( + + Recomendado + + )} +
+

{feature.description}

+
+ +
+ {systemData.features[feature.key as keyof typeof systemData.features] && ( + + )} +
+
+
+ ))} +
+
+ + {/* Notifications */} +
+

+ Notificaciones +

+ +
+
+ + + + + +
+ +
+ +
+ {ALERT_TYPES.map((alert) => ( +
toggleAlertPreference(alert.value)} + > +
+
+
{alert.label}
+

{alert.description}

+
+
+ {systemData.notifications.alert_preferences.includes(alert.value) && ( + + )} +
+
+
+ ))} +
+
+
+
+ + {/* Integrations */} +
+

+ Integraciones (opcional) +

+

+ Estas integraciones se pueden configurar más tarde desde el panel de administración. +

+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + {/* Summary */} +
+

Configuración seleccionada

+
+

Zona horaria: {TIMEZONES.find(tz => tz.value === systemData.timezone)?.label}

+

Moneda: {CURRENCIES.find(c => c.value === systemData.currency)?.label}

+

Horario: {systemData.working_hours.start} - {systemData.working_hours.end}

+

Días operativos: {systemData.working_hours.days.length} días por semana

+

Módulos activados: {Object.values(systemData.features).filter(Boolean).length} de {Object.keys(systemData.features).length}

+
+
+
+ ); +}; + +export default SystemSetupStep; \ No newline at end of file diff --git a/frontend/src/components/domain/onboarding/index.ts b/frontend/src/components/domain/onboarding/index.ts new file mode 100644 index 00000000..d4fa0975 --- /dev/null +++ b/frontend/src/components/domain/onboarding/index.ts @@ -0,0 +1,7 @@ +// Onboarding domain components +export { default as OnboardingWizard } from './OnboardingWizard'; +export { default as CompanyInfoStep } from './CompanyInfoStep'; +export { default as SystemSetupStep } from './SystemSetupStep'; + +// Re-export types from wizard +export type { OnboardingStep, OnboardingStepProps } from './OnboardingWizard'; \ No newline at end of file diff --git a/frontend/src/components/domain/production/BatchTracker.tsx b/frontend/src/components/domain/production/BatchTracker.tsx new file mode 100644 index 00000000..3f6b66ab --- /dev/null +++ b/frontend/src/components/domain/production/BatchTracker.tsx @@ -0,0 +1,680 @@ +import React, { useState, useCallback, useEffect } from 'react'; +import { Card, Button, Badge, Input, Modal, Table, Select, DatePicker } from '../../ui'; +import { productionService, type ProductionBatchResponse, ProductionBatchStatus, ProductionPriority } from '../../../services/api/production.service'; +import type { ProductionBatch, QualityCheck } from '../../../types/production.types'; + +interface BatchTrackerProps { + className?: string; + batchId?: string; + onStageUpdate?: (batch: ProductionBatch, newStage: ProductionStage) => void; + onQualityCheckRequired?: (batch: ProductionBatch, stage: ProductionStage) => void; +} + +interface ProductionStage { + id: string; + name: string; + spanishName: string; + icon: string; + estimatedMinutes: number; + requiresQualityCheck: boolean; + criticalControlPoint: boolean; + completedAt?: string; + notes?: string; + nextStages: string[]; + temperature?: { + min: number; + max: number; + unit: string; + }; +} + +const PRODUCTION_STAGES: Record = { + mixing: { + id: 'mixing', + name: 'Mixing', + spanishName: 'Amasado', + icon: '🥄', + estimatedMinutes: 15, + requiresQualityCheck: true, + criticalControlPoint: true, + nextStages: ['resting'], + temperature: { min: 22, max: 26, unit: '°C' }, + }, + resting: { + id: 'resting', + name: 'Resting', + spanishName: 'Reposo', + icon: '⏰', + estimatedMinutes: 60, + requiresQualityCheck: false, + criticalControlPoint: false, + nextStages: ['shaping', 'fermentation'], + }, + shaping: { + id: 'shaping', + name: 'Shaping', + spanishName: 'Formado', + icon: '✋', + estimatedMinutes: 20, + requiresQualityCheck: true, + criticalControlPoint: false, + nextStages: ['fermentation'], + }, + fermentation: { + id: 'fermentation', + name: 'Fermentation', + spanishName: 'Fermentado', + icon: '🫧', + estimatedMinutes: 120, + requiresQualityCheck: true, + criticalControlPoint: true, + nextStages: ['baking'], + temperature: { min: 28, max: 32, unit: '°C' }, + }, + baking: { + id: 'baking', + name: 'Baking', + spanishName: 'Horneado', + icon: '🔥', + estimatedMinutes: 45, + requiresQualityCheck: true, + criticalControlPoint: true, + nextStages: ['cooling'], + temperature: { min: 180, max: 220, unit: '°C' }, + }, + cooling: { + id: 'cooling', + name: 'Cooling', + spanishName: 'Enfriado', + icon: '❄️', + estimatedMinutes: 90, + requiresQualityCheck: false, + criticalControlPoint: false, + nextStages: ['packaging'], + temperature: { min: 18, max: 25, unit: '°C' }, + }, + packaging: { + id: 'packaging', + name: 'Packaging', + spanishName: 'Empaquetado', + icon: '📦', + estimatedMinutes: 15, + requiresQualityCheck: true, + criticalControlPoint: false, + nextStages: ['completed'], + }, + completed: { + id: 'completed', + name: 'Completed', + spanishName: 'Completado', + icon: '✅', + estimatedMinutes: 0, + requiresQualityCheck: false, + criticalControlPoint: false, + nextStages: [], + }, +}; + +const STATUS_COLORS = { + [ProductionBatchStatus.PLANNED]: 'bg-[var(--color-info)]/10 text-[var(--color-info)] border-[var(--color-info)]/20', + [ProductionBatchStatus.IN_PROGRESS]: 'bg-yellow-100 text-yellow-800 border-yellow-200', + [ProductionBatchStatus.COMPLETED]: 'bg-[var(--color-success)]/10 text-[var(--color-success)] border-green-200', + [ProductionBatchStatus.CANCELLED]: 'bg-[var(--color-error)]/10 text-[var(--color-error)] border-red-200', + [ProductionBatchStatus.ON_HOLD]: 'bg-[var(--bg-tertiary)] text-[var(--text-primary)] border-[var(--border-primary)]', +}; + +const PRIORITY_COLORS = { + [ProductionPriority.LOW]: 'bg-[var(--bg-tertiary)] text-[var(--text-primary)]', + [ProductionPriority.NORMAL]: 'bg-[var(--color-info)]/10 text-[var(--color-info)]', + [ProductionPriority.HIGH]: 'bg-[var(--color-primary)]/10 text-[var(--color-primary)]', + [ProductionPriority.URGENT]: 'bg-[var(--color-error)]/10 text-[var(--color-error)]', +}; + +export const BatchTracker: React.FC = ({ + className = '', + batchId, + onStageUpdate, + onQualityCheckRequired, +}) => { + const [batches, setBatches] = useState([]); + const [selectedBatch, setSelectedBatch] = useState(null); + const [loading, setLoading] = useState(false); + const [currentStage, setCurrentStage] = useState('mixing'); + const [stageNotes, setStageNotes] = useState>({}); + const [isStageModalOpen, setIsStageModalOpen] = useState(false); + const [selectedStageForUpdate, setSelectedStageForUpdate] = useState(null); + const [alerts, setAlerts] = useState>([]); + + const loadBatches = useCallback(async () => { + setLoading(true); + try { + const response = await productionService.getProductionBatches({ + status: ProductionBatchStatus.IN_PROGRESS, + }); + + if (response.success && response.data) { + setBatches(response.data.items || []); + + if (batchId) { + const specificBatch = response.data.items.find(b => b.id === batchId); + if (specificBatch) { + setSelectedBatch(specificBatch); + } + } else if (response.data.items.length > 0) { + setSelectedBatch(response.data.items[0]); + } + } + } catch (error) { + console.error('Error loading batches:', error); + } finally { + setLoading(false); + } + }, [batchId]); + + useEffect(() => { + loadBatches(); + + // Mock alerts for demonstration + setAlerts([ + { + id: '1', + batchId: 'batch-1', + stage: 'fermentation', + type: 'overdue', + message: 'El fermentado ha superado el tiempo estimado en 30 minutos', + severity: 'medium', + timestamp: new Date().toISOString(), + }, + { + id: '2', + batchId: 'batch-2', + stage: 'baking', + type: 'temperature', + message: 'Temperatura del horno fuera del rango óptimo (185°C)', + severity: 'high', + timestamp: new Date().toISOString(), + }, + ]); + }, [loadBatches]); + + const getCurrentStageInfo = (batch: ProductionBatchResponse): { stage: ProductionStage; progress: number } => { + // This would typically come from the batch data + // For demo purposes, we'll simulate based on batch status + let stageId = 'mixing'; + let progress = 0; + + if (batch.status === ProductionBatchStatus.IN_PROGRESS) { + // Simulate current stage based on time elapsed + const startTime = new Date(batch.actual_start_date || batch.planned_start_date); + const now = new Date(); + const elapsedMinutes = (now.getTime() - startTime.getTime()) / (1000 * 60); + + let cumulativeTime = 0; + const stageKeys = Object.keys(PRODUCTION_STAGES).slice(0, -1); // Exclude completed + + for (const key of stageKeys) { + const stage = PRODUCTION_STAGES[key]; + cumulativeTime += stage.estimatedMinutes; + if (elapsedMinutes <= cumulativeTime) { + stageId = key; + const stageStartTime = cumulativeTime - stage.estimatedMinutes; + progress = Math.min(100, ((elapsedMinutes - stageStartTime) / stage.estimatedMinutes) * 100); + break; + } + } + } + + return { + stage: PRODUCTION_STAGES[stageId], + progress: Math.max(0, progress), + }; + }; + + const getTimeRemaining = (batch: ProductionBatchResponse, stage: ProductionStage): string => { + const startTime = new Date(batch.actual_start_date || batch.planned_start_date); + const now = new Date(); + const elapsedMinutes = (now.getTime() - startTime.getTime()) / (1000 * 60); + + // Calculate when this stage should complete based on cumulative time + let cumulativeTime = 0; + const stageKeys = Object.keys(PRODUCTION_STAGES); + const currentStageIndex = stageKeys.indexOf(stage.id); + + for (let i = 0; i <= currentStageIndex; i++) { + cumulativeTime += PRODUCTION_STAGES[stageKeys[i]].estimatedMinutes; + } + + const remainingMinutes = Math.max(0, cumulativeTime - elapsedMinutes); + const hours = Math.floor(remainingMinutes / 60); + const minutes = Math.floor(remainingMinutes % 60); + + if (hours > 0) { + return `${hours}h ${minutes}m restantes`; + } else { + return `${minutes}m restantes`; + } + }; + + const updateStage = async (batchId: string, newStage: string, notes?: string) => { + try { + // This would update the batch stage in the backend + const updatedBatch = batches.find(b => b.id === batchId); + if (updatedBatch && onStageUpdate) { + onStageUpdate(updatedBatch as unknown as ProductionBatch, PRODUCTION_STAGES[newStage]); + } + + // Update local state + if (notes) { + setStageNotes(prev => ({ + ...prev, + [`${batchId}-${newStage}`]: notes, + })); + } + + await loadBatches(); + } catch (error) { + console.error('Error updating stage:', error); + } + }; + + const handleQualityCheck = (batch: ProductionBatchResponse, stage: ProductionStage) => { + if (onQualityCheckRequired) { + onQualityCheckRequired(batch as unknown as ProductionBatch, stage); + } + }; + + const renderStageProgress = (batch: ProductionBatchResponse) => { + const { stage: currentStage, progress } = getCurrentStageInfo(batch); + const stages = Object.values(PRODUCTION_STAGES).slice(0, -1); // Exclude completed + + return ( +
+
+

Progreso del lote

+ + {batch.status === ProductionBatchStatus.IN_PROGRESS && 'En progreso'} + {batch.status === ProductionBatchStatus.PLANNED && 'Planificado'} + {batch.status === ProductionBatchStatus.COMPLETED && 'Completado'} + +
+ +
+ {stages.map((stage, index) => { + const isActive = stage.id === currentStage.id; + const isCompleted = stages.findIndex(s => s.id === currentStage.id) > index; + const isOverdue = false; // Would be calculated based on actual timing + + return ( + { + setSelectedStageForUpdate(stage); + setIsStageModalOpen(true); + }} + > +
+
+ {stage.icon} + {stage.spanishName} +
+ {stage.criticalControlPoint && ( + + PCC + + )} +
+ + {isActive && ( +
+
+ Progreso + {Math.round(progress)}% +
+
+
+
+

+ {getTimeRemaining(batch, stage)} +

+
+ )} + + {isCompleted && ( +
+ + Completado +
+ )} + + {!isActive && !isCompleted && ( +

+ ~{stage.estimatedMinutes}min +

+ )} + + {stage.temperature && isActive && ( +

+ 🌡️ {stage.temperature.min}-{stage.temperature.max}{stage.temperature.unit} +

+ )} + + ); + })} +
+
+ ); + }; + + const renderBatchDetails = (batch: ProductionBatchResponse) => ( + +
+
+

{batch.recipe?.name || 'Producto'}

+

Lote #{batch.batch_number}

+ + {batch.priority === ProductionPriority.LOW && 'Baja'} + {batch.priority === ProductionPriority.NORMAL && 'Normal'} + {batch.priority === ProductionPriority.HIGH && 'Alta'} + {batch.priority === ProductionPriority.URGENT && 'Urgente'} + +
+ +
+

Cantidad planificada

+

{batch.planned_quantity} unidades

+ {batch.actual_quantity && ( + <> +

Cantidad real

+

{batch.actual_quantity} unidades

+ + )} +
+ +
+

Inicio planificado

+

+ {new Date(batch.planned_start_date).toLocaleString('es-ES')} +

+ {batch.actual_start_date && ( + <> +

Inicio real

+

+ {new Date(batch.actual_start_date).toLocaleString('es-ES')} +

+ + )} +
+
+ + {batch.notes && ( +
+

{batch.notes}

+
+ )} +
+ ); + + const renderAlerts = () => ( + +

+ 🚨 Alertas activas +

+ + {alerts.length === 0 ? ( +

No hay alertas activas

+ ) : ( +
+ {alerts.map((alert) => ( +
+
+
+

+ Lote #{batches.find(b => b.id === alert.batchId)?.batch_number} - {PRODUCTION_STAGES[alert.stage]?.spanishName} +

+

{alert.message}

+
+ + {alert.severity === 'high' && 'Alta'} + {alert.severity === 'medium' && 'Media'} + {alert.severity === 'low' && 'Baja'} + +
+

+ {new Date(alert.timestamp).toLocaleTimeString('es-ES')} +

+
+ ))} +
+ )} +
+ ); + + return ( +
+
+
+

Seguimiento de Lotes

+

Rastrea el progreso de los lotes a través de las etapas de producción

+
+ + {selectedBatch && ( +
+ + + +
+ )} +
+ + {loading ? ( +
+
+
+ ) : ( + <> + {selectedBatch ? ( +
+ {renderBatchDetails(selectedBatch)} + {renderStageProgress(selectedBatch)} + {renderAlerts()} +
+ ) : ( + +

No hay lotes en producción actualmente

+ +
+ )} + + )} + + {/* Stage Update Modal */} + { + setIsStageModalOpen(false); + setSelectedStageForUpdate(null); + }} + title={`${selectedStageForUpdate?.spanishName} - Lote #${selectedBatch?.batch_number}`} + > + {selectedStageForUpdate && selectedBatch && ( +
+
+

+ {selectedStageForUpdate.icon} + {selectedStageForUpdate.spanishName} +

+

+ Duración estimada: {selectedStageForUpdate.estimatedMinutes} minutos +

+ + {selectedStageForUpdate.temperature && ( +

+ 🌡️ Temperatura: {selectedStageForUpdate.temperature.min}-{selectedStageForUpdate.temperature.max}{selectedStageForUpdate.temperature.unit} +

+ )} + + {selectedStageForUpdate.criticalControlPoint && ( + + Punto Crítico de Control (PCC) + + )} +
+ + {selectedStageForUpdate.requiresQualityCheck && ( +
+

+ ⚠️ Esta etapa requiere control de calidad antes de continuar +

+ +
+ )} + +
+ + setStageNotes(prev => ({ + ...prev, + [`${selectedBatch.id}-${selectedStageForUpdate.id}`]: e.target.value, + }))} + /> +
+ +
+ + + +
+
+ )} +
+ + {/* Quick Stats */} +
+ +
+
+

Lotes activos

+

{batches.length}

+
+ 📊 +
+
+ + +
+
+

Alertas activas

+

{alerts.length}

+
+ 🚨 +
+
+ + +
+
+

En horneado

+

3

+
+ 🔥 +
+
+ + +
+
+

Completados hoy

+

12

+
+ +
+
+
+
+ ); +}; + +export default BatchTracker; \ No newline at end of file diff --git a/frontend/src/components/domain/production/BatchTracker.tsx.backup b/frontend/src/components/domain/production/BatchTracker.tsx.backup new file mode 100644 index 00000000..c794e86b --- /dev/null +++ b/frontend/src/components/domain/production/BatchTracker.tsx.backup @@ -0,0 +1,680 @@ +import React, { useState, useCallback, useEffect } from 'react'; +import { Card, Button, Badge, Input, Modal, Table, Select, DatePicker } from '../../ui'; +import { productionService, type ProductionBatchResponse, ProductionBatchStatus, ProductionPriority } from '../../../services/api/production.service'; +import type { ProductionBatch, QualityCheck } from '../../../types/production.types'; + +interface BatchTrackerProps { + className?: string; + batchId?: string; + onStageUpdate?: (batch: ProductionBatch, newStage: ProductionStage) => void; + onQualityCheckRequired?: (batch: ProductionBatch, stage: ProductionStage) => void; +} + +interface ProductionStage { + id: string; + name: string; + spanishName: string; + icon: string; + estimatedMinutes: number; + requiresQualityCheck: boolean; + criticalControlPoint: boolean; + completedAt?: string; + notes?: string; + nextStages: string[]; + temperature?: { + min: number; + max: number; + unit: string; + }; +} + +const PRODUCTION_STAGES: Record = { + mixing: { + id: 'mixing', + name: 'Mixing', + spanishName: 'Amasado', + icon: '🥄', + estimatedMinutes: 15, + requiresQualityCheck: true, + criticalControlPoint: true, + nextStages: ['resting'], + temperature: { min: 22, max: 26, unit: '°C' }, + }, + resting: { + id: 'resting', + name: 'Resting', + spanishName: 'Reposo', + icon: '⏰', + estimatedMinutes: 60, + requiresQualityCheck: false, + criticalControlPoint: false, + nextStages: ['shaping', 'fermentation'], + }, + shaping: { + id: 'shaping', + name: 'Shaping', + spanishName: 'Formado', + icon: '✋', + estimatedMinutes: 20, + requiresQualityCheck: true, + criticalControlPoint: false, + nextStages: ['fermentation'], + }, + fermentation: { + id: 'fermentation', + name: 'Fermentation', + spanishName: 'Fermentado', + icon: '🫧', + estimatedMinutes: 120, + requiresQualityCheck: true, + criticalControlPoint: true, + nextStages: ['baking'], + temperature: { min: 28, max: 32, unit: '°C' }, + }, + baking: { + id: 'baking', + name: 'Baking', + spanishName: 'Horneado', + icon: '🔥', + estimatedMinutes: 45, + requiresQualityCheck: true, + criticalControlPoint: true, + nextStages: ['cooling'], + temperature: { min: 180, max: 220, unit: '°C' }, + }, + cooling: { + id: 'cooling', + name: 'Cooling', + spanishName: 'Enfriado', + icon: '❄️', + estimatedMinutes: 90, + requiresQualityCheck: false, + criticalControlPoint: false, + nextStages: ['packaging'], + temperature: { min: 18, max: 25, unit: '°C' }, + }, + packaging: { + id: 'packaging', + name: 'Packaging', + spanishName: 'Empaquetado', + icon: '📦', + estimatedMinutes: 15, + requiresQualityCheck: true, + criticalControlPoint: false, + nextStages: ['completed'], + }, + completed: { + id: 'completed', + name: 'Completed', + spanishName: 'Completado', + icon: '✅', + estimatedMinutes: 0, + requiresQualityCheck: false, + criticalControlPoint: false, + nextStages: [], + }, +}; + +const STATUS_COLORS = { + [ProductionBatchStatus.PLANNED]: 'bg-blue-100 text-blue-800 border-blue-200', + [ProductionBatchStatus.IN_PROGRESS]: 'bg-yellow-100 text-yellow-800 border-yellow-200', + [ProductionBatchStatus.COMPLETED]: 'bg-green-100 text-green-800 border-green-200', + [ProductionBatchStatus.CANCELLED]: 'bg-red-100 text-red-800 border-red-200', + [ProductionBatchStatus.ON_HOLD]: 'bg-gray-100 text-gray-800 border-gray-200', +}; + +const PRIORITY_COLORS = { + [ProductionPriority.LOW]: 'bg-gray-100 text-gray-800', + [ProductionPriority.NORMAL]: 'bg-blue-100 text-blue-800', + [ProductionPriority.HIGH]: 'bg-orange-100 text-orange-800', + [ProductionPriority.URGENT]: 'bg-red-100 text-red-800', +}; + +export const BatchTracker: React.FC = ({ + className = '', + batchId, + onStageUpdate, + onQualityCheckRequired, +}) => { + const [batches, setBatches] = useState([]); + const [selectedBatch, setSelectedBatch] = useState(null); + const [loading, setLoading] = useState(false); + const [currentStage, setCurrentStage] = useState('mixing'); + const [stageNotes, setStageNotes] = useState>({}); + const [isStageModalOpen, setIsStageModalOpen] = useState(false); + const [selectedStageForUpdate, setSelectedStageForUpdate] = useState(null); + const [alerts, setAlerts] = useState>([]); + + const loadBatches = useCallback(async () => { + setLoading(true); + try { + const response = await productionService.getProductionBatches({ + status: ProductionBatchStatus.IN_PROGRESS, + }); + + if (response.success && response.data) { + setBatches(response.data.items || []); + + if (batchId) { + const specificBatch = response.data.items.find(b => b.id === batchId); + if (specificBatch) { + setSelectedBatch(specificBatch); + } + } else if (response.data.items.length > 0) { + setSelectedBatch(response.data.items[0]); + } + } + } catch (error) { + console.error('Error loading batches:', error); + } finally { + setLoading(false); + } + }, [batchId]); + + useEffect(() => { + loadBatches(); + + // Mock alerts for demonstration + setAlerts([ + { + id: '1', + batchId: 'batch-1', + stage: 'fermentation', + type: 'overdue', + message: 'El fermentado ha superado el tiempo estimado en 30 minutos', + severity: 'medium', + timestamp: new Date().toISOString(), + }, + { + id: '2', + batchId: 'batch-2', + stage: 'baking', + type: 'temperature', + message: 'Temperatura del horno fuera del rango óptimo (185°C)', + severity: 'high', + timestamp: new Date().toISOString(), + }, + ]); + }, [loadBatches]); + + const getCurrentStageInfo = (batch: ProductionBatchResponse): { stage: ProductionStage; progress: number } => { + // This would typically come from the batch data + // For demo purposes, we'll simulate based on batch status + let stageId = 'mixing'; + let progress = 0; + + if (batch.status === ProductionBatchStatus.IN_PROGRESS) { + // Simulate current stage based on time elapsed + const startTime = new Date(batch.actual_start_date || batch.planned_start_date); + const now = new Date(); + const elapsedMinutes = (now.getTime() - startTime.getTime()) / (1000 * 60); + + let cumulativeTime = 0; + const stageKeys = Object.keys(PRODUCTION_STAGES).slice(0, -1); // Exclude completed + + for (const key of stageKeys) { + const stage = PRODUCTION_STAGES[key]; + cumulativeTime += stage.estimatedMinutes; + if (elapsedMinutes <= cumulativeTime) { + stageId = key; + const stageStartTime = cumulativeTime - stage.estimatedMinutes; + progress = Math.min(100, ((elapsedMinutes - stageStartTime) / stage.estimatedMinutes) * 100); + break; + } + } + } + + return { + stage: PRODUCTION_STAGES[stageId], + progress: Math.max(0, progress), + }; + }; + + const getTimeRemaining = (batch: ProductionBatchResponse, stage: ProductionStage): string => { + const startTime = new Date(batch.actual_start_date || batch.planned_start_date); + const now = new Date(); + const elapsedMinutes = (now.getTime() - startTime.getTime()) / (1000 * 60); + + // Calculate when this stage should complete based on cumulative time + let cumulativeTime = 0; + const stageKeys = Object.keys(PRODUCTION_STAGES); + const currentStageIndex = stageKeys.indexOf(stage.id); + + for (let i = 0; i <= currentStageIndex; i++) { + cumulativeTime += PRODUCTION_STAGES[stageKeys[i]].estimatedMinutes; + } + + const remainingMinutes = Math.max(0, cumulativeTime - elapsedMinutes); + const hours = Math.floor(remainingMinutes / 60); + const minutes = Math.floor(remainingMinutes % 60); + + if (hours > 0) { + return `${hours}h ${minutes}m restantes`; + } else { + return `${minutes}m restantes`; + } + }; + + const updateStage = async (batchId: string, newStage: string, notes?: string) => { + try { + // This would update the batch stage in the backend + const updatedBatch = batches.find(b => b.id === batchId); + if (updatedBatch && onStageUpdate) { + onStageUpdate(updatedBatch as unknown as ProductionBatch, PRODUCTION_STAGES[newStage]); + } + + // Update local state + if (notes) { + setStageNotes(prev => ({ + ...prev, + [`${batchId}-${newStage}`]: notes, + })); + } + + await loadBatches(); + } catch (error) { + console.error('Error updating stage:', error); + } + }; + + const handleQualityCheck = (batch: ProductionBatchResponse, stage: ProductionStage) => { + if (onQualityCheckRequired) { + onQualityCheckRequired(batch as unknown as ProductionBatch, stage); + } + }; + + const renderStageProgress = (batch: ProductionBatchResponse) => { + const { stage: currentStage, progress } = getCurrentStageInfo(batch); + const stages = Object.values(PRODUCTION_STAGES).slice(0, -1); // Exclude completed + + return ( +
+
+

Progreso del lote

+ + {batch.status === ProductionBatchStatus.IN_PROGRESS && 'En progreso'} + {batch.status === ProductionBatchStatus.PLANNED && 'Planificado'} + {batch.status === ProductionBatchStatus.COMPLETED && 'Completado'} + +
+ +
+ {stages.map((stage, index) => { + const isActive = stage.id === currentStage.id; + const isCompleted = stages.findIndex(s => s.id === currentStage.id) > index; + const isOverdue = false; // Would be calculated based on actual timing + + return ( + { + setSelectedStageForUpdate(stage); + setIsStageModalOpen(true); + }} + > +
+
+ {stage.icon} + {stage.spanishName} +
+ {stage.criticalControlPoint && ( + + PCC + + )} +
+ + {isActive && ( +
+
+ Progreso + {Math.round(progress)}% +
+
+
+
+

+ {getTimeRemaining(batch, stage)} +

+
+ )} + + {isCompleted && ( +
+ + Completado +
+ )} + + {!isActive && !isCompleted && ( +

+ ~{stage.estimatedMinutes}min +

+ )} + + {stage.temperature && isActive && ( +

+ 🌡️ {stage.temperature.min}-{stage.temperature.max}{stage.temperature.unit} +

+ )} + + ); + })} +
+
+ ); + }; + + const renderBatchDetails = (batch: ProductionBatchResponse) => ( + +
+
+

{batch.recipe?.name || 'Producto'}

+

Lote #{batch.batch_number}

+ + {batch.priority === ProductionPriority.LOW && 'Baja'} + {batch.priority === ProductionPriority.NORMAL && 'Normal'} + {batch.priority === ProductionPriority.HIGH && 'Alta'} + {batch.priority === ProductionPriority.URGENT && 'Urgente'} + +
+ +
+

Cantidad planificada

+

{batch.planned_quantity} unidades

+ {batch.actual_quantity && ( + <> +

Cantidad real

+

{batch.actual_quantity} unidades

+ + )} +
+ +
+

Inicio planificado

+

+ {new Date(batch.planned_start_date).toLocaleString('es-ES')} +

+ {batch.actual_start_date && ( + <> +

Inicio real

+

+ {new Date(batch.actual_start_date).toLocaleString('es-ES')} +

+ + )} +
+
+ + {batch.notes && ( +
+

{batch.notes}

+
+ )} +
+ ); + + const renderAlerts = () => ( + +

+ 🚨 Alertas activas +

+ + {alerts.length === 0 ? ( +

No hay alertas activas

+ ) : ( +
+ {alerts.map((alert) => ( +
+
+
+

+ Lote #{batches.find(b => b.id === alert.batchId)?.batch_number} - {PRODUCTION_STAGES[alert.stage]?.spanishName} +

+

{alert.message}

+
+ + {alert.severity === 'high' && 'Alta'} + {alert.severity === 'medium' && 'Media'} + {alert.severity === 'low' && 'Baja'} + +
+

+ {new Date(alert.timestamp).toLocaleTimeString('es-ES')} +

+
+ ))} +
+ )} +
+ ); + + return ( +
+
+
+

Seguimiento de Lotes

+

Rastrea el progreso de los lotes a través de las etapas de producción

+
+ + {selectedBatch && ( +
+ + + +
+ )} +
+ + {loading ? ( +
+
+
+ ) : ( + <> + {selectedBatch ? ( +
+ {renderBatchDetails(selectedBatch)} + {renderStageProgress(selectedBatch)} + {renderAlerts()} +
+ ) : ( + +

No hay lotes en producción actualmente

+ +
+ )} + + )} + + {/* Stage Update Modal */} + { + setIsStageModalOpen(false); + setSelectedStageForUpdate(null); + }} + title={`${selectedStageForUpdate?.spanishName} - Lote #${selectedBatch?.batch_number}`} + > + {selectedStageForUpdate && selectedBatch && ( +
+
+

+ {selectedStageForUpdate.icon} + {selectedStageForUpdate.spanishName} +

+

+ Duración estimada: {selectedStageForUpdate.estimatedMinutes} minutos +

+ + {selectedStageForUpdate.temperature && ( +

+ 🌡️ Temperatura: {selectedStageForUpdate.temperature.min}-{selectedStageForUpdate.temperature.max}{selectedStageForUpdate.temperature.unit} +

+ )} + + {selectedStageForUpdate.criticalControlPoint && ( + + Punto Crítico de Control (PCC) + + )} +
+ + {selectedStageForUpdate.requiresQualityCheck && ( +
+

+ ⚠️ Esta etapa requiere control de calidad antes de continuar +

+ +
+ )} + +
+ + setStageNotes(prev => ({ + ...prev, + [`${selectedBatch.id}-${selectedStageForUpdate.id}`]: e.target.value, + }))} + /> +
+ +
+ + + +
+
+ )} +
+ + {/* Quick Stats */} +
+ +
+
+

Lotes activos

+

{batches.length}

+
+ 📊 +
+
+ + +
+
+

Alertas activas

+

{alerts.length}

+
+ 🚨 +
+
+ + +
+
+

En horneado

+

3

+
+ 🔥 +
+
+ + +
+
+

Completados hoy

+

12

+
+ +
+
+
+
+ ); +}; + +export default BatchTracker; \ No newline at end of file diff --git a/frontend/src/components/domain/production/BatchTracker/index.ts b/frontend/src/components/domain/production/BatchTracker/index.ts new file mode 100644 index 00000000..3ae2c92e --- /dev/null +++ b/frontend/src/components/domain/production/BatchTracker/index.ts @@ -0,0 +1 @@ +export { default } from '../BatchTracker'; \ No newline at end of file diff --git a/frontend/src/components/domain/production/ProductionSchedule.tsx b/frontend/src/components/domain/production/ProductionSchedule.tsx new file mode 100644 index 00000000..c14f063a --- /dev/null +++ b/frontend/src/components/domain/production/ProductionSchedule.tsx @@ -0,0 +1,579 @@ +import React, { useState, useCallback, useMemo } from 'react'; +import { Card, Button, Badge, Select, DatePicker, Input, Modal, Table } from '../../ui'; +import { productionService, type ProductionScheduleEntry, ProductionBatchStatus } from '../../../services/api/production.service'; +import type { ProductionSchedule as ProductionScheduleType, ProductionBatch, EquipmentReservation, StaffAssignment } from '../../../types/production.types'; + +interface ProductionScheduleProps { + className?: string; + onBatchSelected?: (batch: ProductionBatch) => void; + onScheduleUpdate?: (schedule: ProductionScheduleEntry) => void; +} + +interface TimeSlot { + time: string; + capacity: number; + utilized: number; + available: number; + batches: ProductionScheduleEntry[]; +} + +interface CapacityResource { + id: string; + name: string; + type: 'oven' | 'mixer' | 'staff'; + capacity: number; + slots: TimeSlot[]; +} + +const PRODUCT_TYPE_COLORS = { + pan: 'bg-[var(--color-warning)]/10 border-[var(--color-warning)]/20 text-[var(--color-warning)]', + bolleria: 'bg-[var(--color-primary)]/10 border-[var(--color-primary)]/20 text-[var(--color-primary)]', + reposteria: 'bg-[var(--color-error)]/10 border-[var(--color-error)]/20 text-[var(--color-error)]', + especial: 'bg-[var(--color-info)]/10 border-[var(--color-info)]/20 text-[var(--color-info)]', +}; + +const STATUS_COLORS = { + [ProductionBatchStatus.PLANNED]: 'bg-[var(--color-info)]/10 text-[var(--color-info)]', + [ProductionBatchStatus.IN_PROGRESS]: 'bg-[var(--color-warning)]/10 text-[var(--color-warning)]', + [ProductionBatchStatus.COMPLETED]: 'bg-[var(--color-success)]/10 text-[var(--color-success)]', + [ProductionBatchStatus.CANCELLED]: 'bg-[var(--color-error)]/10 text-[var(--color-error)]', + [ProductionBatchStatus.ON_HOLD]: 'bg-[var(--bg-tertiary)] text-[var(--text-secondary)]', +}; + +export const ProductionSchedule: React.FC = ({ + className = '', + onBatchSelected, + onScheduleUpdate, +}) => { + const [scheduleEntries, setScheduleEntries] = useState([]); + const [loading, setLoading] = useState(false); + const [selectedDate, setSelectedDate] = useState(new Date()); + const [viewMode, setViewMode] = useState<'calendar' | 'timeline' | 'capacity'>('timeline'); + const [filterStatus, setFilterStatus] = useState('all'); + const [filterProduct, setFilterProduct] = useState('all'); + const [draggedBatch, setDraggedBatch] = useState(null); + const [isScheduleModalOpen, setIsScheduleModalOpen] = useState(false); + const [selectedBatchForScheduling, setSelectedBatchForScheduling] = useState(null); + + // Mock data for demonstration + const mockCapacityResources: CapacityResource[] = [ + { + id: 'horno-1', + name: 'Horno Principal', + type: 'oven', + capacity: 4, + slots: generateTimeSlots('06:00', '20:00', 2), + }, + { + id: 'horno-2', + name: 'Horno Secundario', + type: 'oven', + capacity: 2, + slots: generateTimeSlots('06:00', '18:00', 2), + }, + { + id: 'amasadora-1', + name: 'Amasadora Industrial', + type: 'mixer', + capacity: 6, + slots: generateTimeSlots('05:00', '21:00', 1), + }, + { + id: 'panadero-1', + name: 'Equipo Panadería', + type: 'staff', + capacity: 8, + slots: generateTimeSlots('06:00', '14:00', 1), + }, + ]; + + function generateTimeSlots(startTime: string, endTime: string, intervalHours: number): TimeSlot[] { + const slots: TimeSlot[] = []; + const start = new Date(`2024-01-01 ${startTime}`); + const end = new Date(`2024-01-01 ${endTime}`); + + while (start < end) { + const timeStr = start.toLocaleTimeString('es-ES', { hour: '2-digit', minute: '2-digit' }); + slots.push({ + time: timeStr, + capacity: 100, + utilized: Math.floor(Math.random() * 80), + available: 100 - Math.floor(Math.random() * 80), + batches: [], + }); + start.setHours(start.getHours() + intervalHours); + } + + return slots; + } + + const loadSchedule = useCallback(async () => { + setLoading(true); + try { + const response = await productionService.getProductionSchedule({ + start_date: selectedDate.toISOString().split('T')[0], + end_date: selectedDate.toISOString().split('T')[0], + }); + + if (response.success) { + setScheduleEntries(response.data || []); + } + } catch (error) { + console.error('Error loading schedule:', error); + } finally { + setLoading(false); + } + }, [selectedDate]); + + const filteredEntries = useMemo(() => { + return scheduleEntries.filter(entry => { + if (filterStatus !== 'all' && entry.batch?.status !== filterStatus) { + return false; + } + if (filterProduct !== 'all' && entry.batch?.recipe?.category !== filterProduct) { + return false; + } + return true; + }); + }, [scheduleEntries, filterStatus, filterProduct]); + + const handleDragStart = (e: React.DragEvent, entry: ProductionScheduleEntry) => { + setDraggedBatch(entry); + e.dataTransfer.effectAllowed = 'move'; + }; + + const handleDragOver = (e: React.DragEvent) => { + e.preventDefault(); + e.dataTransfer.dropEffect = 'move'; + }; + + const handleDrop = async (e: React.DragEvent, newTime: string) => { + e.preventDefault(); + + if (!draggedBatch) return; + + try { + const response = await productionService.updateScheduleEntry(draggedBatch.id, { + scheduled_start_time: newTime, + scheduled_end_time: calculateEndTime(newTime, draggedBatch.estimated_duration_minutes), + }); + + if (response.success && onScheduleUpdate) { + onScheduleUpdate(response.data); + await loadSchedule(); + } + } catch (error) { + console.error('Error updating schedule:', error); + } finally { + setDraggedBatch(null); + } + }; + + const calculateEndTime = (startTime: string, durationMinutes: number): string => { + const start = new Date(`2024-01-01 ${startTime}`); + start.setMinutes(start.getMinutes() + durationMinutes); + return start.toLocaleTimeString('es-ES', { hour: '2-digit', minute: '2-digit' }); + }; + + const handleScheduleBatch = async (scheduleData: { + batch_id: string; + scheduled_date: string; + scheduled_start_time: string; + scheduled_end_time: string; + equipment_reservations?: string[]; + staff_assignments?: string[]; + }) => { + try { + const response = await productionService.scheduleProductionBatch(scheduleData); + + if (response.success && onScheduleUpdate) { + onScheduleUpdate(response.data); + await loadSchedule(); + setIsScheduleModalOpen(false); + setSelectedBatchForScheduling(null); + } + } catch (error) { + console.error('Error scheduling batch:', error); + } + }; + + const getProductTypeColor = (category?: string) => { + return PRODUCT_TYPE_COLORS[category as keyof typeof PRODUCT_TYPE_COLORS] || 'bg-[var(--bg-tertiary)] border-[var(--border-secondary)] text-[var(--text-secondary)]'; + }; + + const getStatusColor = (status?: ProductionBatchStatus) => { + return STATUS_COLORS[status as keyof typeof STATUS_COLORS] || 'bg-[var(--bg-tertiary)] text-[var(--text-secondary)]'; + }; + + const renderTimelineView = () => ( +
+
+ setSelectedDate(date || new Date())} + dateFormat="dd/MM/yyyy" + className="w-40" + /> + + +
+ +
+
+
Producto
+ {Array.from({ length: 22 }, (_, i) => ( +
+ {String(i + 3).padStart(2, '0')}:00 +
+ ))} +
+ +
+ {filteredEntries.map((entry) => ( +
+
+
+ {entry.batch?.recipe?.name} + + {entry.batch?.status === ProductionBatchStatus.PLANNED && 'Planificado'} + {entry.batch?.status === ProductionBatchStatus.IN_PROGRESS && 'En progreso'} + {entry.batch?.status === ProductionBatchStatus.COMPLETED && 'Completado'} + +
+
+ + {Array.from({ length: 22 }, (_, hourIndex) => { + const hour = hourIndex + 3; + const hourStr = `${String(hour).padStart(2, '0')}:00`; + const startHour = parseInt(entry.scheduled_start_time.split(':')[0]); + const endHour = parseInt(entry.scheduled_end_time.split(':')[0]); + + const isInRange = hour >= startHour && hour < endHour; + const isStart = hour === startHour; + + return ( +
handleDrop(e, hourStr)} + > + {isStart && ( +
handleDragStart(e, entry)} + onClick={() => onBatchSelected?.(entry.batch!)} + > +
+ {entry.batch?.recipe?.name} +
+
+ {entry.batch?.planned_quantity} uds +
+
+ )} +
+ ); + })} +
+ ))} +
+
+
+ ); + + const renderCapacityView = () => ( +
+
+ setSelectedDate(date || new Date())} + dateFormat="dd/MM/yyyy" + className="w-40" + /> +
+ +
+ {mockCapacityResources.map((resource) => ( + +
+
+

{resource.name}

+

+ {resource.type === 'oven' && 'Horno'} + {resource.type === 'mixer' && 'Amasadora'} + {resource.type === 'staff' && 'Personal'} + - Capacidad: {resource.capacity} +

+
+ + {Math.round(resource.slots.reduce((acc, slot) => acc + slot.utilized, 0) / resource.slots.length)}% utilizado + +
+ +
+ {resource.slots.slice(0, 8).map((slot, index) => ( +
+ {slot.time} +
+
80 + ? 'bg-[var(--color-error)]' + : slot.utilized > 60 + ? 'bg-yellow-500' + : 'bg-[var(--color-success)]' + }`} + style={{ width: `${slot.utilized}%` }} + /> +
+ + {slot.utilized}% + +
+ ))} +
+ + + + ))} +
+
+ ); + + const renderCalendarView = () => ( +
+
+ {['Lun', 'Mar', 'Mié', 'Jue', 'Vie', 'Sáb', 'Dom'].map((day) => ( +
+ {day} +
+ ))} +
+ +
+ {Array.from({ length: 35 }, (_, i) => { + const date = new Date(selectedDate); + date.setDate(date.getDate() - date.getDay() + i); + const dayEntries = filteredEntries.filter(entry => + new Date(entry.scheduled_date).toDateString() === date.toDateString() + ); + + return ( +
+
{date.getDate()}
+
+ {dayEntries.slice(0, 3).map((entry) => ( +
onBatchSelected?.(entry.batch!)} + > + {entry.batch?.recipe?.name} +
+ ))} + {dayEntries.length > 3 && ( +
+ +{dayEntries.length - 3} más +
+ )} +
+
+ ); + })} +
+
+ ); + + return ( +
+
+
+

Programación de Producción

+

Gestiona y programa la producción diaria de la panadería

+
+ +
+
+ + + +
+ + +
+
+ + {loading ? ( +
+
+
+ ) : ( + <> + {viewMode === 'calendar' && renderCalendarView()} + {viewMode === 'timeline' && renderTimelineView()} + {viewMode === 'capacity' && renderCapacityView()} + + )} + + {/* Schedule Creation Modal */} + { + setIsScheduleModalOpen(false); + setSelectedBatchForScheduling(null); + }} + title="Programar Lote de Producción" + > +
+ + +
+ setSelectedDate(date || new Date())} + dateFormat="dd/MM/yyyy" + /> + +
+ +
+ + +
+ + + +
+ + +
+
+
+ + {/* Legend */} + +

Leyenda de productos

+
+
+
+ Pan +
+
+
+ Bollería +
+
+
+ Repostería +
+
+
+ Especial +
+
+
+
+ ); +}; + +export default ProductionSchedule; \ No newline at end of file diff --git a/frontend/src/components/domain/production/ProductionSchedule.tsx.backup b/frontend/src/components/domain/production/ProductionSchedule.tsx.backup new file mode 100644 index 00000000..f5b35e7d --- /dev/null +++ b/frontend/src/components/domain/production/ProductionSchedule.tsx.backup @@ -0,0 +1,579 @@ +import React, { useState, useCallback, useMemo } from 'react'; +import { Card, Button, Badge, Select, DatePicker, Input, Modal, Table } from '../../ui'; +import { productionService, type ProductionScheduleEntry, ProductionBatchStatus } from '../../../services/api/production.service'; +import type { ProductionSchedule as ProductionScheduleType, ProductionBatch, EquipmentReservation, StaffAssignment } from '../../../types/production.types'; + +interface ProductionScheduleProps { + className?: string; + onBatchSelected?: (batch: ProductionBatch) => void; + onScheduleUpdate?: (schedule: ProductionScheduleEntry) => void; +} + +interface TimeSlot { + time: string; + capacity: number; + utilized: number; + available: number; + batches: ProductionScheduleEntry[]; +} + +interface CapacityResource { + id: string; + name: string; + type: 'oven' | 'mixer' | 'staff'; + capacity: number; + slots: TimeSlot[]; +} + +const PRODUCT_TYPE_COLORS = { + pan: 'bg-[var(--color-warning)]/10 border-[var(--color-warning)]/20 text-[var(--color-warning)]', + bolleria: 'bg-[var(--color-primary)]/10 border-[var(--color-primary)]/20 text-[var(--color-primary)]', + reposteria: 'bg-[var(--color-error)]/10 border-[var(--color-error)]/20 text-[var(--color-error)]', + especial: 'bg-[var(--color-info)]/10 border-[var(--color-info)]/20 text-[var(--color-info)]', +}; + +const STATUS_COLORS = { + [ProductionBatchStatus.PLANNED]: 'bg-[var(--color-info)]/10 text-[var(--color-info)]', + [ProductionBatchStatus.IN_PROGRESS]: 'bg-[var(--color-warning)]/10 text-[var(--color-warning)]', + [ProductionBatchStatus.COMPLETED]: 'bg-[var(--color-success)]/10 text-[var(--color-success)]', + [ProductionBatchStatus.CANCELLED]: 'bg-[var(--color-error)]/10 text-[var(--color-error)]', + [ProductionBatchStatus.ON_HOLD]: 'bg-[var(--bg-tertiary)] text-[var(--text-secondary)]', +}; + +export const ProductionSchedule: React.FC = ({ + className = '', + onBatchSelected, + onScheduleUpdate, +}) => { + const [scheduleEntries, setScheduleEntries] = useState([]); + const [loading, setLoading] = useState(false); + const [selectedDate, setSelectedDate] = useState(new Date()); + const [viewMode, setViewMode] = useState<'calendar' | 'timeline' | 'capacity'>('timeline'); + const [filterStatus, setFilterStatus] = useState('all'); + const [filterProduct, setFilterProduct] = useState('all'); + const [draggedBatch, setDraggedBatch] = useState(null); + const [isScheduleModalOpen, setIsScheduleModalOpen] = useState(false); + const [selectedBatchForScheduling, setSelectedBatchForScheduling] = useState(null); + + // Mock data for demonstration + const mockCapacityResources: CapacityResource[] = [ + { + id: 'horno-1', + name: 'Horno Principal', + type: 'oven', + capacity: 4, + slots: generateTimeSlots('06:00', '20:00', 2), + }, + { + id: 'horno-2', + name: 'Horno Secundario', + type: 'oven', + capacity: 2, + slots: generateTimeSlots('06:00', '18:00', 2), + }, + { + id: 'amasadora-1', + name: 'Amasadora Industrial', + type: 'mixer', + capacity: 6, + slots: generateTimeSlots('05:00', '21:00', 1), + }, + { + id: 'panadero-1', + name: 'Equipo Panadería', + type: 'staff', + capacity: 8, + slots: generateTimeSlots('06:00', '14:00', 1), + }, + ]; + + function generateTimeSlots(startTime: string, endTime: string, intervalHours: number): TimeSlot[] { + const slots: TimeSlot[] = []; + const start = new Date(`2024-01-01 ${startTime}`); + const end = new Date(`2024-01-01 ${endTime}`); + + while (start < end) { + const timeStr = start.toLocaleTimeString('es-ES', { hour: '2-digit', minute: '2-digit' }); + slots.push({ + time: timeStr, + capacity: 100, + utilized: Math.floor(Math.random() * 80), + available: 100 - Math.floor(Math.random() * 80), + batches: [], + }); + start.setHours(start.getHours() + intervalHours); + } + + return slots; + } + + const loadSchedule = useCallback(async () => { + setLoading(true); + try { + const response = await productionService.getProductionSchedule({ + start_date: selectedDate.toISOString().split('T')[0], + end_date: selectedDate.toISOString().split('T')[0], + }); + + if (response.success) { + setScheduleEntries(response.data || []); + } + } catch (error) { + console.error('Error loading schedule:', error); + } finally { + setLoading(false); + } + }, [selectedDate]); + + const filteredEntries = useMemo(() => { + return scheduleEntries.filter(entry => { + if (filterStatus !== 'all' && entry.batch?.status !== filterStatus) { + return false; + } + if (filterProduct !== 'all' && entry.batch?.recipe?.category !== filterProduct) { + return false; + } + return true; + }); + }, [scheduleEntries, filterStatus, filterProduct]); + + const handleDragStart = (e: React.DragEvent, entry: ProductionScheduleEntry) => { + setDraggedBatch(entry); + e.dataTransfer.effectAllowed = 'move'; + }; + + const handleDragOver = (e: React.DragEvent) => { + e.preventDefault(); + e.dataTransfer.dropEffect = 'move'; + }; + + const handleDrop = async (e: React.DragEvent, newTime: string) => { + e.preventDefault(); + + if (!draggedBatch) return; + + try { + const response = await productionService.updateScheduleEntry(draggedBatch.id, { + scheduled_start_time: newTime, + scheduled_end_time: calculateEndTime(newTime, draggedBatch.estimated_duration_minutes), + }); + + if (response.success && onScheduleUpdate) { + onScheduleUpdate(response.data); + await loadSchedule(); + } + } catch (error) { + console.error('Error updating schedule:', error); + } finally { + setDraggedBatch(null); + } + }; + + const calculateEndTime = (startTime: string, durationMinutes: number): string => { + const start = new Date(`2024-01-01 ${startTime}`); + start.setMinutes(start.getMinutes() + durationMinutes); + return start.toLocaleTimeString('es-ES', { hour: '2-digit', minute: '2-digit' }); + }; + + const handleScheduleBatch = async (scheduleData: { + batch_id: string; + scheduled_date: string; + scheduled_start_time: string; + scheduled_end_time: string; + equipment_reservations?: string[]; + staff_assignments?: string[]; + }) => { + try { + const response = await productionService.scheduleProductionBatch(scheduleData); + + if (response.success && onScheduleUpdate) { + onScheduleUpdate(response.data); + await loadSchedule(); + setIsScheduleModalOpen(false); + setSelectedBatchForScheduling(null); + } + } catch (error) { + console.error('Error scheduling batch:', error); + } + }; + + const getProductTypeColor = (category?: string) => { + return PRODUCT_TYPE_COLORS[category as keyof typeof PRODUCT_TYPE_COLORS] || 'bg-[var(--bg-tertiary)] border-[var(--border-secondary)] text-[var(--text-secondary)]'; + }; + + const getStatusColor = (status?: ProductionBatchStatus) => { + return STATUS_COLORS[status as keyof typeof STATUS_COLORS] || 'bg-[var(--bg-tertiary)] text-[var(--text-secondary)]'; + }; + + const renderTimelineView = () => ( +
+
+ setSelectedDate(date || new Date())} + dateFormat="dd/MM/yyyy" + className="w-40" + /> + + +
+ +
+
+
Producto
+ {Array.from({ length: 22 }, (_, i) => ( +
+ {String(i + 3).padStart(2, '0')}:00 +
+ ))} +
+ +
+ {filteredEntries.map((entry) => ( +
+
+
+ {entry.batch?.recipe?.name} + + {entry.batch?.status === ProductionBatchStatus.PLANNED && 'Planificado'} + {entry.batch?.status === ProductionBatchStatus.IN_PROGRESS && 'En progreso'} + {entry.batch?.status === ProductionBatchStatus.COMPLETED && 'Completado'} + +
+
+ + {Array.from({ length: 22 }, (_, hourIndex) => { + const hour = hourIndex + 3; + const hourStr = `${String(hour).padStart(2, '0')}:00`; + const startHour = parseInt(entry.scheduled_start_time.split(':')[0]); + const endHour = parseInt(entry.scheduled_end_time.split(':')[0]); + + const isInRange = hour >= startHour && hour < endHour; + const isStart = hour === startHour; + + return ( +
handleDrop(e, hourStr)} + > + {isStart && ( +
handleDragStart(e, entry)} + onClick={() => onBatchSelected?.(entry.batch!)} + > +
+ {entry.batch?.recipe?.name} +
+
+ {entry.batch?.planned_quantity} uds +
+
+ )} +
+ ); + })} +
+ ))} +
+
+
+ ); + + const renderCapacityView = () => ( +
+
+ setSelectedDate(date || new Date())} + dateFormat="dd/MM/yyyy" + className="w-40" + /> +
+ +
+ {mockCapacityResources.map((resource) => ( + +
+
+

{resource.name}

+

+ {resource.type === 'oven' && 'Horno'} + {resource.type === 'mixer' && 'Amasadora'} + {resource.type === 'staff' && 'Personal'} + - Capacidad: {resource.capacity} +

+
+ + {Math.round(resource.slots.reduce((acc, slot) => acc + slot.utilized, 0) / resource.slots.length)}% utilizado + +
+ +
+ {resource.slots.slice(0, 8).map((slot, index) => ( +
+ {slot.time} +
+
80 + ? 'bg-[var(--color-error)]' + : slot.utilized > 60 + ? 'bg-yellow-500' + : 'bg-[var(--color-success)]' + }`} + style={{ width: `${slot.utilized}%` }} + /> +
+ + {slot.utilized}% + +
+ ))} +
+ + + + ))} +
+
+ ); + + const renderCalendarView = () => ( +
+
+ {['Lun', 'Mar', 'Mié', 'Jue', 'Vie', 'Sáb', 'Dom'].map((day) => ( +
+ {day} +
+ ))} +
+ +
+ {Array.from({ length: 35 }, (_, i) => { + const date = new Date(selectedDate); + date.setDate(date.getDate() - date.getDay() + i); + const dayEntries = filteredEntries.filter(entry => + new Date(entry.scheduled_date).toDateString() === date.toDateString() + ); + + return ( +
+
{date.getDate()}
+
+ {dayEntries.slice(0, 3).map((entry) => ( +
onBatchSelected?.(entry.batch!)} + > + {entry.batch?.recipe?.name} +
+ ))} + {dayEntries.length > 3 && ( +
+ +{dayEntries.length - 3} más +
+ )} +
+
+ ); + })} +
+
+ ); + + return ( +
+
+
+

Programación de Producción

+

Gestiona y programa la producción diaria de la panadería

+
+ +
+
+ + + +
+ + +
+
+ + {loading ? ( +
+
+
+ ) : ( + <> + {viewMode === 'calendar' && renderCalendarView()} + {viewMode === 'timeline' && renderTimelineView()} + {viewMode === 'capacity' && renderCapacityView()} + + )} + + {/* Schedule Creation Modal */} + { + setIsScheduleModalOpen(false); + setSelectedBatchForScheduling(null); + }} + title="Programar Lote de Producción" + > +
+ + +
+ setSelectedDate(date || new Date())} + dateFormat="dd/MM/yyyy" + /> + +
+ +
+ + +
+ + + +
+ + +
+
+
+ + {/* Legend */} + +

Leyenda de productos

+
+
+
+ Pan +
+
+
+ Bollería +
+
+
+ Repostería +
+
+
+ Especial +
+
+
+
+ ); +}; + +export default ProductionSchedule; \ No newline at end of file diff --git a/frontend/src/components/domain/production/ProductionSchedule/index.ts b/frontend/src/components/domain/production/ProductionSchedule/index.ts new file mode 100644 index 00000000..83f9fe2b --- /dev/null +++ b/frontend/src/components/domain/production/ProductionSchedule/index.ts @@ -0,0 +1 @@ +export { default } from '../ProductionSchedule'; \ No newline at end of file diff --git a/frontend/src/components/domain/production/QualityControl.tsx b/frontend/src/components/domain/production/QualityControl.tsx new file mode 100644 index 00000000..df9fd787 --- /dev/null +++ b/frontend/src/components/domain/production/QualityControl.tsx @@ -0,0 +1,882 @@ +import React, { useState, useCallback, useRef } from 'react'; +import { Card, Button, Badge, Input, Modal, Table, Select, DatePicker } from '../../ui'; +import { productionService, type QualityCheckResponse, QualityCheckStatus } from '../../../services/api/production.service'; +import type { QualityCheck, QualityCheckCriteria, ProductionBatch, QualityCheckType } from '../../../types/production.types'; + +interface QualityControlProps { + className?: string; + batchId?: string; + checkType?: QualityCheckType; + onQualityCheckCompleted?: (result: QualityCheck) => void; + onCorrectiveActionRequired?: (check: QualityCheck, actions: string[]) => void; +} + +interface QualityCheckTemplate { + id: string; + name: string; + spanishName: string; + productTypes: string[]; + criteria: QualityChecklistItem[]; + requiresPhotos: boolean; + passThreshold: number; + criticalPoints: string[]; +} + +interface QualityChecklistItem { + id: string; + category: string; + description: string; + spanishDescription: string; + type: 'boolean' | 'numeric' | 'scale' | 'text'; + required: boolean; + minValue?: number; + maxValue?: number; + unit?: string; + acceptableCriteria?: string; + weight: number; + isCritical: boolean; +} + +interface QualityInspectionResult { + checklistId: string; + value: string | number | boolean; + notes?: string; + photo?: File; + timestamp: string; + inspector: string; +} + +const QUALITY_CHECK_TEMPLATES: Record = { + visual_inspection: { + id: 'visual_inspection', + name: 'Visual Inspection', + spanishName: 'Inspección Visual', + productTypes: ['pan', 'bolleria', 'reposteria'], + requiresPhotos: true, + passThreshold: 80, + criticalPoints: ['color_defects', 'structural_defects'], + criteria: [ + { + id: 'color_uniformity', + category: 'appearance', + description: 'Color uniformity', + spanishDescription: 'Uniformidad del color', + type: 'scale', + required: true, + minValue: 1, + maxValue: 5, + acceptableCriteria: 'Score 3 or higher', + weight: 20, + isCritical: false, + }, + { + id: 'surface_texture', + category: 'appearance', + description: 'Surface texture quality', + spanishDescription: 'Calidad de la textura superficial', + type: 'scale', + required: true, + minValue: 1, + maxValue: 5, + acceptableCriteria: 'Score 3 or higher', + weight: 15, + isCritical: false, + }, + { + id: 'shape_integrity', + category: 'structure', + description: 'Shape and form integrity', + spanishDescription: 'Integridad de forma y estructura', + type: 'boolean', + required: true, + acceptableCriteria: 'Must be true', + weight: 25, + isCritical: true, + }, + { + id: 'size_consistency', + category: 'dimensions', + description: 'Size consistency within batch', + spanishDescription: 'Consistencia de tamaño en el lote', + type: 'scale', + required: true, + minValue: 1, + maxValue: 5, + acceptableCriteria: 'Score 3 or higher', + weight: 20, + isCritical: false, + }, + { + id: 'defects_presence', + category: 'defects', + description: 'Visible defects or imperfections', + spanishDescription: 'Defectos visibles o imperfecciones', + type: 'boolean', + required: true, + acceptableCriteria: 'Must be false', + weight: 20, + isCritical: true, + }, + ], + }, + weight_check: { + id: 'weight_check', + name: 'Weight Check', + spanishName: 'Control de Peso', + productTypes: ['pan', 'bolleria', 'reposteria'], + requiresPhotos: false, + passThreshold: 95, + criticalPoints: ['weight_variance'], + criteria: [ + { + id: 'individual_weight', + category: 'weight', + description: 'Individual piece weight', + spanishDescription: 'Peso individual de la pieza', + type: 'numeric', + required: true, + minValue: 0, + unit: 'g', + acceptableCriteria: 'Within ±5% of target', + weight: 40, + isCritical: true, + }, + { + id: 'batch_average_weight', + category: 'weight', + description: 'Batch average weight', + spanishDescription: 'Peso promedio del lote', + type: 'numeric', + required: true, + minValue: 0, + unit: 'g', + acceptableCriteria: 'Within ±3% of target', + weight: 30, + isCritical: true, + }, + { + id: 'weight_variance', + category: 'consistency', + description: 'Weight variance within batch', + spanishDescription: 'Variación de peso dentro del lote', + type: 'numeric', + required: true, + minValue: 0, + unit: '%', + acceptableCriteria: 'Less than 5%', + weight: 30, + isCritical: true, + }, + ], + }, + temperature_check: { + id: 'temperature_check', + name: 'Temperature Check', + spanishName: 'Control de Temperatura', + productTypes: ['pan', 'bolleria'], + requiresPhotos: false, + passThreshold: 90, + criticalPoints: ['core_temperature'], + criteria: [ + { + id: 'core_temperature', + category: 'temperature', + description: 'Core temperature', + spanishDescription: 'Temperatura del núcleo', + type: 'numeric', + required: true, + minValue: 85, + maxValue: 98, + unit: '°C', + acceptableCriteria: '88-95°C', + weight: 60, + isCritical: true, + }, + { + id: 'cooling_temperature', + category: 'temperature', + description: 'Cooling temperature', + spanishDescription: 'Temperatura de enfriado', + type: 'numeric', + required: false, + minValue: 18, + maxValue: 25, + unit: '°C', + acceptableCriteria: '20-23°C', + weight: 40, + isCritical: false, + }, + ], + }, + packaging_quality: { + id: 'packaging_quality', + name: 'Packaging Quality', + spanishName: 'Calidad del Empaquetado', + productTypes: ['pan', 'bolleria', 'reposteria'], + requiresPhotos: true, + passThreshold: 85, + criticalPoints: ['seal_integrity', 'labeling_accuracy'], + criteria: [ + { + id: 'seal_integrity', + category: 'packaging', + description: 'Package seal integrity', + spanishDescription: 'Integridad del sellado del envase', + type: 'boolean', + required: true, + acceptableCriteria: 'Must be true', + weight: 30, + isCritical: true, + }, + { + id: 'labeling_accuracy', + category: 'labeling', + description: 'Label accuracy and placement', + spanishDescription: 'Precisión y colocación de etiquetas', + type: 'scale', + required: true, + minValue: 1, + maxValue: 5, + acceptableCriteria: 'Score 4 or higher', + weight: 25, + isCritical: true, + }, + { + id: 'package_appearance', + category: 'appearance', + description: 'Overall package appearance', + spanishDescription: 'Apariencia general del envase', + type: 'scale', + required: true, + minValue: 1, + maxValue: 5, + acceptableCriteria: 'Score 3 or higher', + weight: 20, + isCritical: false, + }, + { + id: 'barcode_readability', + category: 'labeling', + description: 'Barcode readability', + spanishDescription: 'Legibilidad del código de barras', + type: 'boolean', + required: true, + acceptableCriteria: 'Must be true', + weight: 25, + isCritical: false, + }, + ], + }, +}; + +const STATUS_COLORS = { + [QualityCheckStatus.SCHEDULED]: 'bg-[var(--color-info)]/10 text-[var(--color-info)]', + [QualityCheckStatus.IN_PROGRESS]: 'bg-yellow-100 text-yellow-800', + [QualityCheckStatus.PASSED]: 'bg-[var(--color-success)]/10 text-[var(--color-success)]', + [QualityCheckStatus.FAILED]: 'bg-[var(--color-error)]/10 text-[var(--color-error)]', + [QualityCheckStatus.REQUIRES_REVIEW]: 'bg-[var(--color-primary)]/10 text-[var(--color-primary)]', + [QualityCheckStatus.CANCELLED]: 'bg-[var(--bg-tertiary)] text-[var(--text-primary)]', +}; + +export const QualityControl: React.FC = ({ + className = '', + batchId, + checkType, + onQualityCheckCompleted, + onCorrectiveActionRequired, +}) => { + const [qualityChecks, setQualityChecks] = useState([]); + const [selectedTemplate, setSelectedTemplate] = useState(null); + const [activeCheck, setActiveCheck] = useState(null); + const [inspectionResults, setInspectionResults] = useState>({}); + const [loading, setLoading] = useState(false); + const [isInspectionModalOpen, setIsInspectionModalOpen] = useState(false); + const [uploadedPhotos, setUploadedPhotos] = useState>({}); + const [currentInspector, setCurrentInspector] = useState('inspector-1'); + const fileInputRef = useRef(null); + + const loadQualityChecks = useCallback(async () => { + setLoading(true); + try { + const params: any = {}; + if (batchId) params.batch_id = batchId; + if (checkType) params.check_type = checkType; + + const response = await productionService.getQualityChecks(params); + + if (response.success && response.data) { + setQualityChecks(response.data.items || []); + } + } catch (error) { + console.error('Error loading quality checks:', error); + } finally { + setLoading(false); + } + }, [batchId, checkType]); + + const startQualityCheck = (template: QualityCheckTemplate, batch?: any) => { + setSelectedTemplate(template); + setInspectionResults({}); + setUploadedPhotos({}); + setIsInspectionModalOpen(true); + + // Initialize empty results for all criteria + template.criteria.forEach(criterion => { + setInspectionResults(prev => ({ + ...prev, + [criterion.id]: { + checklistId: criterion.id, + value: criterion.type === 'boolean' ? false : criterion.type === 'numeric' ? 0 : '', + timestamp: new Date().toISOString(), + inspector: currentInspector, + } + })); + }); + }; + + const updateInspectionResult = (criterionId: string, value: string | number | boolean, notes?: string) => { + setInspectionResults(prev => ({ + ...prev, + [criterionId]: { + ...prev[criterionId], + value, + notes, + timestamp: new Date().toISOString(), + } + })); + }; + + const handlePhotoUpload = (criterionId: string, file: File) => { + setUploadedPhotos(prev => ({ + ...prev, + [criterionId]: file, + })); + + // Update inspection result with photo reference + setInspectionResults(prev => ({ + ...prev, + [criterionId]: { + ...prev[criterionId], + photo: file, + } + })); + }; + + const calculateOverallScore = (): number => { + if (!selectedTemplate) return 0; + + let totalScore = 0; + let totalWeight = 0; + + selectedTemplate.criteria.forEach(criterion => { + const result = inspectionResults[criterion.id]; + if (result) { + let score = 0; + + if (criterion.type === 'boolean') { + score = result.value ? 100 : 0; + } else if (criterion.type === 'scale') { + const numValue = Number(result.value); + score = (numValue / (criterion.maxValue || 5)) * 100; + } else if (criterion.type === 'numeric') { + // For numeric values, assume pass/fail based on acceptable range + score = 100; // Simplified - would need more complex logic + } + + totalScore += score * criterion.weight; + totalWeight += criterion.weight; + } + }); + + return totalWeight > 0 ? totalScore / totalWeight : 0; + }; + + const checkCriticalFailures = (): string[] => { + if (!selectedTemplate) return []; + + const failures: string[] = []; + + selectedTemplate.criteria.forEach(criterion => { + if (criterion.isCritical) { + const result = inspectionResults[criterion.id]; + if (result) { + if (criterion.type === 'boolean' && !result.value) { + failures.push(criterion.spanishDescription); + } else if (criterion.type === 'scale' && Number(result.value) < 3) { + failures.push(criterion.spanishDescription); + } + } + } + }); + + return failures; + }; + + const completeQualityCheck = async () => { + if (!selectedTemplate) return; + + const overallScore = calculateOverallScore(); + const criticalFailures = checkCriticalFailures(); + const passed = overallScore >= selectedTemplate.passThreshold && criticalFailures.length === 0; + + const checkData = { + status: passed ? QualityCheckStatus.PASSED : QualityCheckStatus.FAILED, + results: { + overallScore, + criticalFailures, + individualResults: inspectionResults, + photos: Object.keys(uploadedPhotos), + }, + notes: `Inspección completada. Puntuación: ${overallScore.toFixed(1)}%`, + corrective_actions: criticalFailures.length > 0 ? [ + `Fallas críticas encontradas: ${criticalFailures.join(', ')}`, + 'Revisar proceso de producción', + 'Re-entrenar personal si es necesario' + ] : undefined, + }; + + try { + // This would be the actual check ID from the created quality check + const mockCheckId = 'check-id'; + const response = await productionService.completeQualityCheck(mockCheckId, checkData); + + if (response.success) { + if (onQualityCheckCompleted) { + onQualityCheckCompleted(response.data as unknown as QualityCheck); + } + + if (!passed && checkData.corrective_actions && onCorrectiveActionRequired) { + onCorrectiveActionRequired(response.data as unknown as QualityCheck, checkData.corrective_actions); + } + + setIsInspectionModalOpen(false); + setSelectedTemplate(null); + await loadQualityChecks(); + } + } catch (error) { + console.error('Error completing quality check:', error); + } + }; + + const renderInspectionForm = () => { + if (!selectedTemplate) return null; + + return ( +
+
+

{selectedTemplate.spanishName}

+

+ Umbral de aprobación: {selectedTemplate.passThreshold}% +

+ {selectedTemplate.criticalPoints.length > 0 && ( +

+ Puntos críticos: {selectedTemplate.criticalPoints.length} +

+ )} +
+ +
+ {selectedTemplate.criteria.map((criterion) => ( + +
+
+

{criterion.spanishDescription}

+

{criterion.acceptableCriteria}

+ {criterion.isCritical && ( + + Punto Crítico + + )} +
+ Peso: {criterion.weight}% +
+ +
+ {criterion.type === 'boolean' && ( +
+ + +
+ )} + + {criterion.type === 'scale' && ( +
+ 1 + updateInspectionResult(criterion.id, parseInt(e.target.value))} + className="flex-1" + /> + {criterion.maxValue || 5} + + {inspectionResults[criterion.id]?.value || criterion.minValue || 1} + +
+ )} + + {criterion.type === 'numeric' && ( +
+ updateInspectionResult(criterion.id, parseFloat(e.target.value) || 0)} + className="w-24" + /> + {criterion.unit && {criterion.unit}} +
+ )} + + { + const currentResult = inspectionResults[criterion.id]; + if (currentResult) { + updateInspectionResult(criterion.id, currentResult.value, e.target.value); + } + }} + /> + + {selectedTemplate.requiresPhotos && ( +
+ { + const file = e.target.files?.[0]; + if (file) { + handlePhotoUpload(criterion.id, file); + } + }} + className="hidden" + /> + + {uploadedPhotos[criterion.id] && ( +

+ ✓ Foto capturada: {uploadedPhotos[criterion.id].name} +

+ )} +
+ )} +
+
+ ))} +
+ + +
+ Puntuación general + + {calculateOverallScore().toFixed(1)}% + +
+ +
+
= selectedTemplate.passThreshold + ? 'bg-green-500' + : 'bg-red-500' + }`} + style={{ width: `${calculateOverallScore()}%` }} + /> +
+ +
+ Umbral: {selectedTemplate.passThreshold}% + = selectedTemplate.passThreshold + ? 'text-[var(--color-success)] font-medium' + : 'text-[var(--color-error)] font-medium' + }> + {calculateOverallScore() >= selectedTemplate.passThreshold ? '✓ APROBADO' : '✗ REPROBADO'} + +
+ + {checkCriticalFailures().length > 0 && ( +
+

Fallas críticas detectadas:

+
    + {checkCriticalFailures().map((failure, index) => ( +
  • • {failure}
  • + ))} +
+
+ )} + +
+ ); + }; + + const renderQualityChecksTable = () => { + const columns = [ + { key: 'batch', label: 'Lote', sortable: true }, + { key: 'type', label: 'Tipo de control', sortable: true }, + { key: 'status', label: 'Estado', sortable: true }, + { key: 'inspector', label: 'Inspector', sortable: false }, + { key: 'scheduled_date', label: 'Fecha programada', sortable: true }, + { key: 'completed_date', label: 'Fecha completada', sortable: true }, + { key: 'actions', label: 'Acciones', sortable: false }, + ]; + + const data = qualityChecks.map(check => ({ + id: check.id, + batch: `#${check.batch?.batch_number || 'N/A'}`, + type: QUALITY_CHECK_TEMPLATES[check.check_type]?.spanishName || check.check_type, + status: ( + + {check.status === QualityCheckStatus.SCHEDULED && 'Programado'} + {check.status === QualityCheckStatus.IN_PROGRESS && 'En progreso'} + {check.status === QualityCheckStatus.PASSED && 'Aprobado'} + {check.status === QualityCheckStatus.FAILED && 'Reprobado'} + {check.status === QualityCheckStatus.REQUIRES_REVIEW && 'Requiere revisión'} + + ), + inspector: check.inspector || 'No asignado', + scheduled_date: check.scheduled_date + ? new Date(check.scheduled_date).toLocaleDateString('es-ES') + : '-', + completed_date: check.completed_date + ? new Date(check.completed_date).toLocaleDateString('es-ES') + : '-', + actions: ( +
+ {check.status === QualityCheckStatus.SCHEDULED && ( + + )} + +
+ ), + })); + + return
; + }; + + return ( +
+
+
+

Control de Calidad

+

Gestiona las inspecciones de calidad y cumplimiento

+
+ +
+ + + +
+
+ + {/* Quick Start Templates */} +
+ {Object.values(QUALITY_CHECK_TEMPLATES).map((template) => ( + startQualityCheck(template)} + > +
+

{template.spanishName}

+ {template.requiresPhotos && ( + + 📸 Fotos + + )} +
+ +

+ {template.criteria.length} criterios • Umbral: {template.passThreshold}% +

+ +
+ {template.productTypes.map((type) => ( + + {type === 'pan' && 'Pan'} + {type === 'bolleria' && 'Bollería'} + {type === 'reposteria' && 'Repostería'} + + ))} +
+ + {template.criticalPoints.length > 0 && ( +

+ {template.criticalPoints.length} puntos críticos +

+ )} +
+ ))} +
+ + {/* Quality Checks Table */} + +
+

Controles de calidad recientes

+ +
+ + + +
+
+ + {loading ? ( +
+
+
+ ) : ( + renderQualityChecksTable() + )} +
+ + {/* Quality Check Modal */} + { + setIsInspectionModalOpen(false); + setSelectedTemplate(null); + }} + title={`Control de Calidad: ${selectedTemplate?.spanishName}`} + size="lg" + > + {renderInspectionForm()} + +
+ + + +
+
+ + {/* Stats Cards */} +
+ +
+
+

Tasa de aprobación

+

94.2%

+
+ +
+
+ + +
+
+

Controles pendientes

+

7

+
+ +
+
+ + +
+
+

Fallas críticas

+

2

+
+ 🚨 +
+
+ + +
+
+

Controles hoy

+

23

+
+ 📋 +
+
+
+
+ ); +}; + +export default QualityControl; \ No newline at end of file diff --git a/frontend/src/components/domain/production/QualityControl.tsx.backup b/frontend/src/components/domain/production/QualityControl.tsx.backup new file mode 100644 index 00000000..673d22be --- /dev/null +++ b/frontend/src/components/domain/production/QualityControl.tsx.backup @@ -0,0 +1,882 @@ +import React, { useState, useCallback, useRef } from 'react'; +import { Card, Button, Badge, Input, Modal, Table, Select, DatePicker } from '../../ui'; +import { productionService, type QualityCheckResponse, QualityCheckStatus } from '../../../services/api/production.service'; +import type { QualityCheck, QualityCheckCriteria, ProductionBatch, QualityCheckType } from '../../../types/production.types'; + +interface QualityControlProps { + className?: string; + batchId?: string; + checkType?: QualityCheckType; + onQualityCheckCompleted?: (result: QualityCheck) => void; + onCorrectiveActionRequired?: (check: QualityCheck, actions: string[]) => void; +} + +interface QualityCheckTemplate { + id: string; + name: string; + spanishName: string; + productTypes: string[]; + criteria: QualityChecklistItem[]; + requiresPhotos: boolean; + passThreshold: number; + criticalPoints: string[]; +} + +interface QualityChecklistItem { + id: string; + category: string; + description: string; + spanishDescription: string; + type: 'boolean' | 'numeric' | 'scale' | 'text'; + required: boolean; + minValue?: number; + maxValue?: number; + unit?: string; + acceptableCriteria?: string; + weight: number; + isCritical: boolean; +} + +interface QualityInspectionResult { + checklistId: string; + value: string | number | boolean; + notes?: string; + photo?: File; + timestamp: string; + inspector: string; +} + +const QUALITY_CHECK_TEMPLATES: Record = { + visual_inspection: { + id: 'visual_inspection', + name: 'Visual Inspection', + spanishName: 'Inspección Visual', + productTypes: ['pan', 'bolleria', 'reposteria'], + requiresPhotos: true, + passThreshold: 80, + criticalPoints: ['color_defects', 'structural_defects'], + criteria: [ + { + id: 'color_uniformity', + category: 'appearance', + description: 'Color uniformity', + spanishDescription: 'Uniformidad del color', + type: 'scale', + required: true, + minValue: 1, + maxValue: 5, + acceptableCriteria: 'Score 3 or higher', + weight: 20, + isCritical: false, + }, + { + id: 'surface_texture', + category: 'appearance', + description: 'Surface texture quality', + spanishDescription: 'Calidad de la textura superficial', + type: 'scale', + required: true, + minValue: 1, + maxValue: 5, + acceptableCriteria: 'Score 3 or higher', + weight: 15, + isCritical: false, + }, + { + id: 'shape_integrity', + category: 'structure', + description: 'Shape and form integrity', + spanishDescription: 'Integridad de forma y estructura', + type: 'boolean', + required: true, + acceptableCriteria: 'Must be true', + weight: 25, + isCritical: true, + }, + { + id: 'size_consistency', + category: 'dimensions', + description: 'Size consistency within batch', + spanishDescription: 'Consistencia de tamaño en el lote', + type: 'scale', + required: true, + minValue: 1, + maxValue: 5, + acceptableCriteria: 'Score 3 or higher', + weight: 20, + isCritical: false, + }, + { + id: 'defects_presence', + category: 'defects', + description: 'Visible defects or imperfections', + spanishDescription: 'Defectos visibles o imperfecciones', + type: 'boolean', + required: true, + acceptableCriteria: 'Must be false', + weight: 20, + isCritical: true, + }, + ], + }, + weight_check: { + id: 'weight_check', + name: 'Weight Check', + spanishName: 'Control de Peso', + productTypes: ['pan', 'bolleria', 'reposteria'], + requiresPhotos: false, + passThreshold: 95, + criticalPoints: ['weight_variance'], + criteria: [ + { + id: 'individual_weight', + category: 'weight', + description: 'Individual piece weight', + spanishDescription: 'Peso individual de la pieza', + type: 'numeric', + required: true, + minValue: 0, + unit: 'g', + acceptableCriteria: 'Within ±5% of target', + weight: 40, + isCritical: true, + }, + { + id: 'batch_average_weight', + category: 'weight', + description: 'Batch average weight', + spanishDescription: 'Peso promedio del lote', + type: 'numeric', + required: true, + minValue: 0, + unit: 'g', + acceptableCriteria: 'Within ±3% of target', + weight: 30, + isCritical: true, + }, + { + id: 'weight_variance', + category: 'consistency', + description: 'Weight variance within batch', + spanishDescription: 'Variación de peso dentro del lote', + type: 'numeric', + required: true, + minValue: 0, + unit: '%', + acceptableCriteria: 'Less than 5%', + weight: 30, + isCritical: true, + }, + ], + }, + temperature_check: { + id: 'temperature_check', + name: 'Temperature Check', + spanishName: 'Control de Temperatura', + productTypes: ['pan', 'bolleria'], + requiresPhotos: false, + passThreshold: 90, + criticalPoints: ['core_temperature'], + criteria: [ + { + id: 'core_temperature', + category: 'temperature', + description: 'Core temperature', + spanishDescription: 'Temperatura del núcleo', + type: 'numeric', + required: true, + minValue: 85, + maxValue: 98, + unit: '°C', + acceptableCriteria: '88-95°C', + weight: 60, + isCritical: true, + }, + { + id: 'cooling_temperature', + category: 'temperature', + description: 'Cooling temperature', + spanishDescription: 'Temperatura de enfriado', + type: 'numeric', + required: false, + minValue: 18, + maxValue: 25, + unit: '°C', + acceptableCriteria: '20-23°C', + weight: 40, + isCritical: false, + }, + ], + }, + packaging_quality: { + id: 'packaging_quality', + name: 'Packaging Quality', + spanishName: 'Calidad del Empaquetado', + productTypes: ['pan', 'bolleria', 'reposteria'], + requiresPhotos: true, + passThreshold: 85, + criticalPoints: ['seal_integrity', 'labeling_accuracy'], + criteria: [ + { + id: 'seal_integrity', + category: 'packaging', + description: 'Package seal integrity', + spanishDescription: 'Integridad del sellado del envase', + type: 'boolean', + required: true, + acceptableCriteria: 'Must be true', + weight: 30, + isCritical: true, + }, + { + id: 'labeling_accuracy', + category: 'labeling', + description: 'Label accuracy and placement', + spanishDescription: 'Precisión y colocación de etiquetas', + type: 'scale', + required: true, + minValue: 1, + maxValue: 5, + acceptableCriteria: 'Score 4 or higher', + weight: 25, + isCritical: true, + }, + { + id: 'package_appearance', + category: 'appearance', + description: 'Overall package appearance', + spanishDescription: 'Apariencia general del envase', + type: 'scale', + required: true, + minValue: 1, + maxValue: 5, + acceptableCriteria: 'Score 3 or higher', + weight: 20, + isCritical: false, + }, + { + id: 'barcode_readability', + category: 'labeling', + description: 'Barcode readability', + spanishDescription: 'Legibilidad del código de barras', + type: 'boolean', + required: true, + acceptableCriteria: 'Must be true', + weight: 25, + isCritical: false, + }, + ], + }, +}; + +const STATUS_COLORS = { + [QualityCheckStatus.SCHEDULED]: 'bg-blue-100 text-blue-800', + [QualityCheckStatus.IN_PROGRESS]: 'bg-yellow-100 text-yellow-800', + [QualityCheckStatus.PASSED]: 'bg-green-100 text-green-800', + [QualityCheckStatus.FAILED]: 'bg-red-100 text-red-800', + [QualityCheckStatus.REQUIRES_REVIEW]: 'bg-orange-100 text-orange-800', + [QualityCheckStatus.CANCELLED]: 'bg-gray-100 text-gray-800', +}; + +export const QualityControl: React.FC = ({ + className = '', + batchId, + checkType, + onQualityCheckCompleted, + onCorrectiveActionRequired, +}) => { + const [qualityChecks, setQualityChecks] = useState([]); + const [selectedTemplate, setSelectedTemplate] = useState(null); + const [activeCheck, setActiveCheck] = useState(null); + const [inspectionResults, setInspectionResults] = useState>({}); + const [loading, setLoading] = useState(false); + const [isInspectionModalOpen, setIsInspectionModalOpen] = useState(false); + const [uploadedPhotos, setUploadedPhotos] = useState>({}); + const [currentInspector, setCurrentInspector] = useState('inspector-1'); + const fileInputRef = useRef(null); + + const loadQualityChecks = useCallback(async () => { + setLoading(true); + try { + const params: any = {}; + if (batchId) params.batch_id = batchId; + if (checkType) params.check_type = checkType; + + const response = await productionService.getQualityChecks(params); + + if (response.success && response.data) { + setQualityChecks(response.data.items || []); + } + } catch (error) { + console.error('Error loading quality checks:', error); + } finally { + setLoading(false); + } + }, [batchId, checkType]); + + const startQualityCheck = (template: QualityCheckTemplate, batch?: any) => { + setSelectedTemplate(template); + setInspectionResults({}); + setUploadedPhotos({}); + setIsInspectionModalOpen(true); + + // Initialize empty results for all criteria + template.criteria.forEach(criterion => { + setInspectionResults(prev => ({ + ...prev, + [criterion.id]: { + checklistId: criterion.id, + value: criterion.type === 'boolean' ? false : criterion.type === 'numeric' ? 0 : '', + timestamp: new Date().toISOString(), + inspector: currentInspector, + } + })); + }); + }; + + const updateInspectionResult = (criterionId: string, value: string | number | boolean, notes?: string) => { + setInspectionResults(prev => ({ + ...prev, + [criterionId]: { + ...prev[criterionId], + value, + notes, + timestamp: new Date().toISOString(), + } + })); + }; + + const handlePhotoUpload = (criterionId: string, file: File) => { + setUploadedPhotos(prev => ({ + ...prev, + [criterionId]: file, + })); + + // Update inspection result with photo reference + setInspectionResults(prev => ({ + ...prev, + [criterionId]: { + ...prev[criterionId], + photo: file, + } + })); + }; + + const calculateOverallScore = (): number => { + if (!selectedTemplate) return 0; + + let totalScore = 0; + let totalWeight = 0; + + selectedTemplate.criteria.forEach(criterion => { + const result = inspectionResults[criterion.id]; + if (result) { + let score = 0; + + if (criterion.type === 'boolean') { + score = result.value ? 100 : 0; + } else if (criterion.type === 'scale') { + const numValue = Number(result.value); + score = (numValue / (criterion.maxValue || 5)) * 100; + } else if (criterion.type === 'numeric') { + // For numeric values, assume pass/fail based on acceptable range + score = 100; // Simplified - would need more complex logic + } + + totalScore += score * criterion.weight; + totalWeight += criterion.weight; + } + }); + + return totalWeight > 0 ? totalScore / totalWeight : 0; + }; + + const checkCriticalFailures = (): string[] => { + if (!selectedTemplate) return []; + + const failures: string[] = []; + + selectedTemplate.criteria.forEach(criterion => { + if (criterion.isCritical) { + const result = inspectionResults[criterion.id]; + if (result) { + if (criterion.type === 'boolean' && !result.value) { + failures.push(criterion.spanishDescription); + } else if (criterion.type === 'scale' && Number(result.value) < 3) { + failures.push(criterion.spanishDescription); + } + } + } + }); + + return failures; + }; + + const completeQualityCheck = async () => { + if (!selectedTemplate) return; + + const overallScore = calculateOverallScore(); + const criticalFailures = checkCriticalFailures(); + const passed = overallScore >= selectedTemplate.passThreshold && criticalFailures.length === 0; + + const checkData = { + status: passed ? QualityCheckStatus.PASSED : QualityCheckStatus.FAILED, + results: { + overallScore, + criticalFailures, + individualResults: inspectionResults, + photos: Object.keys(uploadedPhotos), + }, + notes: `Inspección completada. Puntuación: ${overallScore.toFixed(1)}%`, + corrective_actions: criticalFailures.length > 0 ? [ + `Fallas críticas encontradas: ${criticalFailures.join(', ')}`, + 'Revisar proceso de producción', + 'Re-entrenar personal si es necesario' + ] : undefined, + }; + + try { + // This would be the actual check ID from the created quality check + const mockCheckId = 'check-id'; + const response = await productionService.completeQualityCheck(mockCheckId, checkData); + + if (response.success) { + if (onQualityCheckCompleted) { + onQualityCheckCompleted(response.data as unknown as QualityCheck); + } + + if (!passed && checkData.corrective_actions && onCorrectiveActionRequired) { + onCorrectiveActionRequired(response.data as unknown as QualityCheck, checkData.corrective_actions); + } + + setIsInspectionModalOpen(false); + setSelectedTemplate(null); + await loadQualityChecks(); + } + } catch (error) { + console.error('Error completing quality check:', error); + } + }; + + const renderInspectionForm = () => { + if (!selectedTemplate) return null; + + return ( +
+
+

{selectedTemplate.spanishName}

+

+ Umbral de aprobación: {selectedTemplate.passThreshold}% +

+ {selectedTemplate.criticalPoints.length > 0 && ( +

+ Puntos críticos: {selectedTemplate.criticalPoints.length} +

+ )} +
+ +
+ {selectedTemplate.criteria.map((criterion) => ( + +
+
+

{criterion.spanishDescription}

+

{criterion.acceptableCriteria}

+ {criterion.isCritical && ( + + Punto Crítico + + )} +
+ Peso: {criterion.weight}% +
+ +
+ {criterion.type === 'boolean' && ( +
+ + +
+ )} + + {criterion.type === 'scale' && ( +
+ 1 + updateInspectionResult(criterion.id, parseInt(e.target.value))} + className="flex-1" + /> + {criterion.maxValue || 5} + + {inspectionResults[criterion.id]?.value || criterion.minValue || 1} + +
+ )} + + {criterion.type === 'numeric' && ( +
+ updateInspectionResult(criterion.id, parseFloat(e.target.value) || 0)} + className="w-24" + /> + {criterion.unit && {criterion.unit}} +
+ )} + + { + const currentResult = inspectionResults[criterion.id]; + if (currentResult) { + updateInspectionResult(criterion.id, currentResult.value, e.target.value); + } + }} + /> + + {selectedTemplate.requiresPhotos && ( +
+ { + const file = e.target.files?.[0]; + if (file) { + handlePhotoUpload(criterion.id, file); + } + }} + className="hidden" + /> + + {uploadedPhotos[criterion.id] && ( +

+ ✓ Foto capturada: {uploadedPhotos[criterion.id].name} +

+ )} +
+ )} +
+
+ ))} +
+ + +
+ Puntuación general + + {calculateOverallScore().toFixed(1)}% + +
+ +
+
= selectedTemplate.passThreshold + ? 'bg-green-500' + : 'bg-red-500' + }`} + style={{ width: `${calculateOverallScore()}%` }} + /> +
+ +
+ Umbral: {selectedTemplate.passThreshold}% + = selectedTemplate.passThreshold + ? 'text-green-600 font-medium' + : 'text-red-600 font-medium' + }> + {calculateOverallScore() >= selectedTemplate.passThreshold ? '✓ APROBADO' : '✗ REPROBADO'} + +
+ + {checkCriticalFailures().length > 0 && ( +
+

Fallas críticas detectadas:

+
    + {checkCriticalFailures().map((failure, index) => ( +
  • • {failure}
  • + ))} +
+
+ )} + +
+ ); + }; + + const renderQualityChecksTable = () => { + const columns = [ + { key: 'batch', label: 'Lote', sortable: true }, + { key: 'type', label: 'Tipo de control', sortable: true }, + { key: 'status', label: 'Estado', sortable: true }, + { key: 'inspector', label: 'Inspector', sortable: false }, + { key: 'scheduled_date', label: 'Fecha programada', sortable: true }, + { key: 'completed_date', label: 'Fecha completada', sortable: true }, + { key: 'actions', label: 'Acciones', sortable: false }, + ]; + + const data = qualityChecks.map(check => ({ + id: check.id, + batch: `#${check.batch?.batch_number || 'N/A'}`, + type: QUALITY_CHECK_TEMPLATES[check.check_type]?.spanishName || check.check_type, + status: ( + + {check.status === QualityCheckStatus.SCHEDULED && 'Programado'} + {check.status === QualityCheckStatus.IN_PROGRESS && 'En progreso'} + {check.status === QualityCheckStatus.PASSED && 'Aprobado'} + {check.status === QualityCheckStatus.FAILED && 'Reprobado'} + {check.status === QualityCheckStatus.REQUIRES_REVIEW && 'Requiere revisión'} + + ), + inspector: check.inspector || 'No asignado', + scheduled_date: check.scheduled_date + ? new Date(check.scheduled_date).toLocaleDateString('es-ES') + : '-', + completed_date: check.completed_date + ? new Date(check.completed_date).toLocaleDateString('es-ES') + : '-', + actions: ( +
+ {check.status === QualityCheckStatus.SCHEDULED && ( + + )} + +
+ ), + })); + + return
; + }; + + return ( +
+
+
+

Control de Calidad

+

Gestiona las inspecciones de calidad y cumplimiento

+
+ +
+ + + +
+
+ + {/* Quick Start Templates */} +
+ {Object.values(QUALITY_CHECK_TEMPLATES).map((template) => ( + startQualityCheck(template)} + > +
+

{template.spanishName}

+ {template.requiresPhotos && ( + + 📸 Fotos + + )} +
+ +

+ {template.criteria.length} criterios • Umbral: {template.passThreshold}% +

+ +
+ {template.productTypes.map((type) => ( + + {type === 'pan' && 'Pan'} + {type === 'bolleria' && 'Bollería'} + {type === 'reposteria' && 'Repostería'} + + ))} +
+ + {template.criticalPoints.length > 0 && ( +

+ {template.criticalPoints.length} puntos críticos +

+ )} +
+ ))} +
+ + {/* Quality Checks Table */} + +
+

Controles de calidad recientes

+ +
+ + + +
+
+ + {loading ? ( +
+
+
+ ) : ( + renderQualityChecksTable() + )} +
+ + {/* Quality Check Modal */} + { + setIsInspectionModalOpen(false); + setSelectedTemplate(null); + }} + title={`Control de Calidad: ${selectedTemplate?.spanishName}`} + size="lg" + > + {renderInspectionForm()} + +
+ + + +
+
+ + {/* Stats Cards */} +
+ +
+
+

Tasa de aprobación

+

94.2%

+
+ +
+
+ + +
+
+

Controles pendientes

+

7

+
+ +
+
+ + +
+
+

Fallas críticas

+

2

+
+ 🚨 +
+
+ + +
+
+

Controles hoy

+

23

+
+ 📋 +
+
+
+
+ ); +}; + +export default QualityControl; \ No newline at end of file diff --git a/frontend/src/components/domain/production/QualityControl/index.ts b/frontend/src/components/domain/production/QualityControl/index.ts new file mode 100644 index 00000000..4e9db89d --- /dev/null +++ b/frontend/src/components/domain/production/QualityControl/index.ts @@ -0,0 +1 @@ +export { default } from '../QualityControl'; \ No newline at end of file diff --git a/frontend/src/components/domain/production/RecipeDisplay.tsx b/frontend/src/components/domain/production/RecipeDisplay.tsx new file mode 100644 index 00000000..27f7fcec --- /dev/null +++ b/frontend/src/components/domain/production/RecipeDisplay.tsx @@ -0,0 +1,802 @@ +import React, { useState, useCallback, useMemo } from 'react'; +import { Card, Button, Badge, Input, Modal, Table, Select } from '../../ui'; +import type { Recipe, RecipeIngredient, RecipeInstruction, NutritionalInfo } from '../../../types/production.types'; + +// Local enum definition to avoid import issues +enum DifficultyLevel { + BEGINNER = 'beginner', + INTERMEDIATE = 'intermediate', + ADVANCED = 'advanced', + EXPERT = 'expert', +} + +interface RecipeDisplayProps { + className?: string; + recipe: Recipe; + editable?: boolean; + showNutrition?: boolean; + showCosting?: boolean; + onScaleChange?: (scaleFactor: number, scaledRecipe: Recipe) => void; + onVersionUpdate?: (newVersion: Recipe) => void; + onCostCalculation?: (totalCost: number, costPerUnit: number) => void; +} + +interface ScaledIngredient extends RecipeIngredient { + scaledQuantity: number; + scaledCost?: number; +} + +interface ScaledInstruction extends RecipeInstruction { + scaledDuration?: number; +} + +interface ScaledRecipe extends Omit { + scaledYieldQuantity: number; + scaledTotalTime: number; + ingredients: ScaledIngredient[]; + instructions: ScaledInstruction[]; + scaleFactor: number; + estimatedTotalCost?: number; + costPerScaledUnit?: number; +} + +interface TimerState { + instructionId: string; + duration: number; + remaining: number; + isActive: boolean; + isComplete: boolean; +} + +const DIFFICULTY_COLORS = { + [DifficultyLevel.BEGINNER]: 'bg-[var(--color-success)]/10 text-[var(--color-success)]', + [DifficultyLevel.INTERMEDIATE]: 'bg-yellow-100 text-yellow-800', + [DifficultyLevel.ADVANCED]: 'bg-[var(--color-primary)]/10 text-[var(--color-primary)]', + [DifficultyLevel.EXPERT]: 'bg-[var(--color-error)]/10 text-[var(--color-error)]', +}; + +const DIFFICULTY_LABELS = { + [DifficultyLevel.BEGINNER]: 'Principiante', + [DifficultyLevel.INTERMEDIATE]: 'Intermedio', + [DifficultyLevel.ADVANCED]: 'Avanzado', + [DifficultyLevel.EXPERT]: 'Experto', +}; + +const ALLERGEN_ICONS: Record = { + gluten: '🌾', + milk: '🥛', + eggs: '🥚', + nuts: '🥜', + soy: '🫘', + sesame: '🌰', + fish: '🐟', + shellfish: '🦐', +}; + +const EQUIPMENT_ICONS: Record = { + oven: '🔥', + mixer: '🥄', + scale: '⚖️', + bowl: '🥣', + whisk: '🔄', + spatula: '🍴', + thermometer: '🌡️', + timer: '⏰', +}; + +export const RecipeDisplay: React.FC = ({ + className = '', + recipe, + editable = false, + showNutrition = true, + showCosting = false, + onScaleChange, + onVersionUpdate, + onCostCalculation, +}) => { + const [scaleFactor, setScaleFactor] = useState(1); + const [activeTimers, setActiveTimers] = useState>({}); + const [isNutritionModalOpen, setIsNutritionModalOpen] = useState(false); + const [isCostingModalOpen, setIsCostingModalOpen] = useState(false); + const [isEquipmentModalOpen, setIsEquipmentModalOpen] = useState(false); + const [selectedInstruction, setSelectedInstruction] = useState(null); + const [showAllergensDetail, setShowAllergensDetail] = useState(false); + + // Mock ingredient costs for demonstration + const ingredientCosts: Record = { + flour: 1.2, // €/kg + water: 0.001, // €/l + salt: 0.8, // €/kg + yeast: 8.5, // €/kg + sugar: 1.5, // €/kg + butter: 6.2, // €/kg + milk: 1.3, // €/l + eggs: 0.25, // €/unit + }; + + const scaledRecipe = useMemo((): ScaledRecipe => { + const scaledIngredients: ScaledIngredient[] = recipe.ingredients.map(ingredient => ({ + ...ingredient, + scaledQuantity: ingredient.quantity * scaleFactor, + scaledCost: ingredientCosts[ingredient.ingredient_id] + ? ingredientCosts[ingredient.ingredient_id] * ingredient.quantity * scaleFactor + : undefined, + })); + + const scaledInstructions: ScaledInstruction[] = recipe.instructions.map(instruction => ({ + ...instruction, + scaledDuration: instruction.duration_minutes ? Math.ceil(instruction.duration_minutes * scaleFactor) : undefined, + })); + + const estimatedTotalCost = scaledIngredients.reduce((total, ingredient) => + total + (ingredient.scaledCost || 0), 0 + ); + + const scaledYieldQuantity = recipe.yield_quantity * scaleFactor; + const costPerScaledUnit = scaledYieldQuantity > 0 ? estimatedTotalCost / scaledYieldQuantity : 0; + + return { + ...recipe, + scaledYieldQuantity, + scaledTotalTime: Math.ceil(recipe.total_time_minutes * scaleFactor), + ingredients: scaledIngredients, + instructions: scaledInstructions, + scaleFactor, + estimatedTotalCost, + costPerScaledUnit, + }; + }, [recipe, scaleFactor, ingredientCosts]); + + const handleScaleChange = useCallback((newScaleFactor: number) => { + setScaleFactor(newScaleFactor); + if (onScaleChange) { + onScaleChange(newScaleFactor, scaledRecipe as unknown as Recipe); + } + }, [scaledRecipe, onScaleChange]); + + const startTimer = (instruction: RecipeInstruction) => { + if (!instruction.duration_minutes) return; + + const timerState: TimerState = { + instructionId: instruction.step_number.toString(), + duration: instruction.duration_minutes * 60, // Convert to seconds + remaining: instruction.duration_minutes * 60, + isActive: true, + isComplete: false, + }; + + setActiveTimers(prev => ({ + ...prev, + [instruction.step_number.toString()]: timerState, + })); + + // Start countdown + const interval = setInterval(() => { + setActiveTimers(prev => { + const current = prev[instruction.step_number.toString()]; + if (!current || current.remaining <= 0) { + clearInterval(interval); + return { + ...prev, + [instruction.step_number.toString()]: { + ...current, + remaining: 0, + isActive: false, + isComplete: true, + } + }; + } + + return { + ...prev, + [instruction.step_number.toString()]: { + ...current, + remaining: current.remaining - 1, + } + }; + }); + }, 1000); + }; + + const stopTimer = (instructionId: string) => { + setActiveTimers(prev => ({ + ...prev, + [instructionId]: { + ...prev[instructionId], + isActive: false, + } + })); + }; + + const formatTime = (seconds: number): string => { + const hours = Math.floor(seconds / 3600); + const minutes = Math.floor((seconds % 3600) / 60); + const secs = seconds % 60; + + if (hours > 0) { + return `${hours}:${String(minutes).padStart(2, '0')}:${String(secs).padStart(2, '0')}`; + } + return `${minutes}:${String(secs).padStart(2, '0')}`; + }; + + const renderScalingControls = () => ( + +

Escalado de receta

+ +
+
+ + handleScaleChange(parseFloat(e.target.value) || 1)} + className="w-full" + /> +
+ +
+ +

+ {recipe.yield_quantity} {recipe.yield_unit} +

+
+ +
+ +

+ {scaledRecipe.scaledYieldQuantity} {recipe.yield_unit} +

+
+
+ +
+ + + + + +
+ + {scaleFactor !== 1 && ( +
+

+ Tiempo total escalado: {Math.ceil(scaledRecipe.scaledTotalTime / 60)}h {scaledRecipe.scaledTotalTime % 60}m +

+ {showCosting && scaledRecipe.estimatedTotalCost && ( +

+ Costo estimado: €{scaledRecipe.estimatedTotalCost.toFixed(2)} + (€{scaledRecipe.costPerScaledUnit?.toFixed(3)}/{recipe.yield_unit}) +

+ )} +
+ )} +
+ ); + + const renderRecipeHeader = () => ( + +
+
+
+
+

{recipe.name}

+

{recipe.description}

+
+ +
+ + {DIFFICULTY_LABELS[recipe.difficulty_level]} + + + v{recipe.version} + +
+
+ +
+
+

Preparación

+

{recipe.prep_time_minutes} min

+
+
+

Cocción

+

{recipe.cook_time_minutes} min

+
+
+

Total

+

{Math.ceil(recipe.total_time_minutes / 60)}h {recipe.total_time_minutes % 60}m

+
+
+

Rendimiento

+

{scaledRecipe.scaledYieldQuantity} {recipe.yield_unit}

+
+
+ + {recipe.allergen_warnings.length > 0 && ( +
+
+

Alérgenos:

+ +
+
+ {recipe.allergen_warnings.map((allergen) => ( + + {ALLERGEN_ICONS[allergen]} {allergen} + + ))} +
+ + {showAllergensDetail && ( +
+

+ ⚠️ Este producto contiene los siguientes alérgenos. Revisar cuidadosamente + antes del consumo si existe alguna alergia o intolerancia alimentaria. +

+
+ )} +
+ )} + + {recipe.storage_instructions && ( +
+

+ Conservación: {recipe.storage_instructions} + {recipe.shelf_life_hours && ( + + • Vida útil: {Math.ceil(recipe.shelf_life_hours / 24)} días + + )} +

+
+ )} +
+ +
+ {showNutrition && ( + + )} + + {showCosting && ( + + )} + + + + {editable && onVersionUpdate && ( + + )} +
+
+
+ ); + + const renderIngredients = () => ( + +

Ingredientes

+ +
+ {scaledRecipe.ingredients.map((ingredient, index) => ( +
+
+

{ingredient.ingredient_name}

+ {ingredient.preparation_notes && ( +

{ingredient.preparation_notes}

+ )} + {ingredient.is_optional && ( + + Opcional + + )} +
+ +
+

+ {ingredient.scaledQuantity.toFixed(ingredient.scaledQuantity < 1 ? 2 : 0)} {ingredient.unit} +

+ {scaleFactor !== 1 && ( +

+ (original: {ingredient.quantity} {ingredient.unit}) +

+ )} + {showCosting && ingredient.scaledCost && ( +

+ €{ingredient.scaledCost.toFixed(2)} +

+ )} +
+
+ ))} +
+ + {showCosting && scaledRecipe.estimatedTotalCost && ( +
+
+ Costo total estimado: + + €{scaledRecipe.estimatedTotalCost.toFixed(2)} + +
+

+ €{scaledRecipe.costPerScaledUnit?.toFixed(3)} por {recipe.yield_unit} +

+
+ )} +
+ ); + + const renderInstructions = () => ( + +

Instrucciones

+ +
+ {scaledRecipe.instructions.map((instruction, index) => { + const timer = activeTimers[instruction.step_number.toString()]; + + return ( +
+
+
+
+ {instruction.step_number} +
+
+ +
+
+
+ {instruction.duration_minutes && ( + + ⏱️ {instruction.scaledDuration || instruction.duration_minutes} min + + )} + + {instruction.temperature && ( + + 🌡️ {instruction.temperature}°C + + )} + + {instruction.critical_control_point && ( + + 🚨 PCC + + )} +
+ + {instruction.duration_minutes && ( +
+ {!timer?.isActive && !timer?.isComplete && ( + + )} + + {timer?.isActive && ( +
+ + {formatTime(timer.remaining)} + + +
+ )} + + {timer?.isComplete && ( + + ✅ Completado + + )} +
+ )} +
+ +

+ {instruction.instruction} +

+ + {instruction.equipment && instruction.equipment.length > 0 && ( +
+

Equipo necesario:

+
+ {instruction.equipment.map((equipment, idx) => ( + + {EQUIPMENT_ICONS[equipment.toLowerCase()] || '🔧'} {equipment} + + ))} +
+
+ )} + + {instruction.tips && ( +
+

+ 💡 Tip: {instruction.tips} +

+
+ )} +
+
+
+ ); + })} +
+
+ ); + + const renderNutritionModal = () => ( + setIsNutritionModalOpen(false)} + title="Información Nutricional" + > + {recipe.nutritional_info ? ( +
+
+
+

Calorías por porción

+

+ {recipe.nutritional_info.calories_per_serving || 'N/A'} +

+
+ +
+

Tamaño de porción

+

+ {recipe.nutritional_info.serving_size || 'N/A'} +

+
+
+ +
+
+

Proteínas

+

{recipe.nutritional_info.protein_g || 0}g

+
+
+

Carbohidratos

+

{recipe.nutritional_info.carbohydrates_g || 0}g

+
+
+

Grasas

+

{recipe.nutritional_info.fat_g || 0}g

+
+
+

Fibra

+

{recipe.nutritional_info.fiber_g || 0}g

+
+
+

Azúcares

+

{recipe.nutritional_info.sugar_g || 0}g

+
+
+

Sodio

+

{recipe.nutritional_info.sodium_mg || 0}mg

+
+
+ +
+

+ Porciones por lote escalado: { + recipe.nutritional_info.servings_per_batch + ? Math.ceil(recipe.nutritional_info.servings_per_batch * scaleFactor) + : 'N/A' + } +

+
+
+ ) : ( +

+ No hay información nutricional disponible para esta receta. +

+ )} +
+ ); + + const renderCostingModal = () => ( + setIsCostingModalOpen(false)} + title="Análisis de Costos" + > +
+
+
+ Costo total + + €{scaledRecipe.estimatedTotalCost?.toFixed(2)} + +
+

+ €{scaledRecipe.costPerScaledUnit?.toFixed(3)} por {recipe.yield_unit} +

+
+ +
+

Desglose por ingrediente

+
+ {scaledRecipe.ingredients.map((ingredient, index) => ( +
+
+

{ingredient.ingredient_name}

+

+ {ingredient.scaledQuantity.toFixed(2)} {ingredient.unit} +

+
+

+ €{ingredient.scaledCost?.toFixed(2) || '0.00'} +

+
+ ))} +
+
+ +
+

Análisis de rentabilidad

+
+

• Costo de ingredientes: €{scaledRecipe.estimatedTotalCost?.toFixed(2)}

+

• Margen sugerido (300%): €{((scaledRecipe.estimatedTotalCost || 0) * 3).toFixed(2)}

+

• Precio de venta sugerido por {recipe.yield_unit}: €{((scaledRecipe.costPerScaledUnit || 0) * 4).toFixed(2)}

+
+
+
+
+ ); + + const renderEquipmentModal = () => ( + setIsEquipmentModalOpen(false)} + title="Equipo Necesario" + > +
+
+

Equipo general de la receta

+
+ {recipe.equipment_needed.map((equipment, index) => ( +
+ + {EQUIPMENT_ICONS[equipment.toLowerCase()] || '🔧'} + + {equipment} +
+ ))} +
+
+ +
+

Equipo por instrucción

+
+ {recipe.instructions + .filter(instruction => instruction.equipment && instruction.equipment.length > 0) + .map((instruction) => ( +
+

+ Paso {instruction.step_number} +

+
+ {instruction.equipment?.map((equipment, idx) => ( + + {EQUIPMENT_ICONS[equipment.toLowerCase()] || '🔧'} {equipment} + + ))} +
+
+ ))} +
+
+
+
+ ); + + return ( +
+ {renderRecipeHeader()} + {renderScalingControls()} + +
+ {renderIngredients()} +
+ {renderInstructions()} +
+
+ + {renderNutritionModal()} + {renderCostingModal()} + {renderEquipmentModal()} +
+ ); +}; + +export default RecipeDisplay; \ No newline at end of file diff --git a/frontend/src/components/domain/production/RecipeDisplay.tsx.backup b/frontend/src/components/domain/production/RecipeDisplay.tsx.backup new file mode 100644 index 00000000..45f0ee13 --- /dev/null +++ b/frontend/src/components/domain/production/RecipeDisplay.tsx.backup @@ -0,0 +1,794 @@ +import React, { useState, useCallback, useMemo } from 'react'; +import { Card, Button, Badge, Input, Modal, Table, Select } from '../../ui'; +import type { Recipe, RecipeIngredient, RecipeInstruction, NutritionalInfo, DifficultyLevel } from '../../../types/production.types'; + +interface RecipeDisplayProps { + className?: string; + recipe: Recipe; + editable?: boolean; + showNutrition?: boolean; + showCosting?: boolean; + onScaleChange?: (scaleFactor: number, scaledRecipe: Recipe) => void; + onVersionUpdate?: (newVersion: Recipe) => void; + onCostCalculation?: (totalCost: number, costPerUnit: number) => void; +} + +interface ScaledIngredient extends RecipeIngredient { + scaledQuantity: number; + scaledCost?: number; +} + +interface ScaledInstruction extends RecipeInstruction { + scaledDuration?: number; +} + +interface ScaledRecipe extends Omit { + scaledYieldQuantity: number; + scaledTotalTime: number; + ingredients: ScaledIngredient[]; + instructions: ScaledInstruction[]; + scaleFactor: number; + estimatedTotalCost?: number; + costPerScaledUnit?: number; +} + +interface TimerState { + instructionId: string; + duration: number; + remaining: number; + isActive: boolean; + isComplete: boolean; +} + +const DIFFICULTY_COLORS = { + [DifficultyLevel.BEGINNER]: 'bg-green-100 text-green-800', + [DifficultyLevel.INTERMEDIATE]: 'bg-yellow-100 text-yellow-800', + [DifficultyLevel.ADVANCED]: 'bg-orange-100 text-orange-800', + [DifficultyLevel.EXPERT]: 'bg-red-100 text-red-800', +}; + +const DIFFICULTY_LABELS = { + [DifficultyLevel.BEGINNER]: 'Principiante', + [DifficultyLevel.INTERMEDIATE]: 'Intermedio', + [DifficultyLevel.ADVANCED]: 'Avanzado', + [DifficultyLevel.EXPERT]: 'Experto', +}; + +const ALLERGEN_ICONS: Record = { + gluten: '🌾', + milk: '🥛', + eggs: '🥚', + nuts: '🥜', + soy: '🫘', + sesame: '🌰', + fish: '🐟', + shellfish: '🦐', +}; + +const EQUIPMENT_ICONS: Record = { + oven: '🔥', + mixer: '🥄', + scale: '⚖️', + bowl: '🥣', + whisk: '🔄', + spatula: '🍴', + thermometer: '🌡️', + timer: '⏰', +}; + +export const RecipeDisplay: React.FC = ({ + className = '', + recipe, + editable = false, + showNutrition = true, + showCosting = false, + onScaleChange, + onVersionUpdate, + onCostCalculation, +}) => { + const [scaleFactor, setScaleFactor] = useState(1); + const [activeTimers, setActiveTimers] = useState>({}); + const [isNutritionModalOpen, setIsNutritionModalOpen] = useState(false); + const [isCostingModalOpen, setIsCostingModalOpen] = useState(false); + const [isEquipmentModalOpen, setIsEquipmentModalOpen] = useState(false); + const [selectedInstruction, setSelectedInstruction] = useState(null); + const [showAllergensDetail, setShowAllergensDetail] = useState(false); + + // Mock ingredient costs for demonstration + const ingredientCosts: Record = { + flour: 1.2, // €/kg + water: 0.001, // €/l + salt: 0.8, // €/kg + yeast: 8.5, // €/kg + sugar: 1.5, // €/kg + butter: 6.2, // €/kg + milk: 1.3, // €/l + eggs: 0.25, // €/unit + }; + + const scaledRecipe = useMemo((): ScaledRecipe => { + const scaledIngredients: ScaledIngredient[] = recipe.ingredients.map(ingredient => ({ + ...ingredient, + scaledQuantity: ingredient.quantity * scaleFactor, + scaledCost: ingredientCosts[ingredient.ingredient_id] + ? ingredientCosts[ingredient.ingredient_id] * ingredient.quantity * scaleFactor + : undefined, + })); + + const scaledInstructions: ScaledInstruction[] = recipe.instructions.map(instruction => ({ + ...instruction, + scaledDuration: instruction.duration_minutes ? Math.ceil(instruction.duration_minutes * scaleFactor) : undefined, + })); + + const estimatedTotalCost = scaledIngredients.reduce((total, ingredient) => + total + (ingredient.scaledCost || 0), 0 + ); + + const scaledYieldQuantity = recipe.yield_quantity * scaleFactor; + const costPerScaledUnit = scaledYieldQuantity > 0 ? estimatedTotalCost / scaledYieldQuantity : 0; + + return { + ...recipe, + scaledYieldQuantity, + scaledTotalTime: Math.ceil(recipe.total_time_minutes * scaleFactor), + ingredients: scaledIngredients, + instructions: scaledInstructions, + scaleFactor, + estimatedTotalCost, + costPerScaledUnit, + }; + }, [recipe, scaleFactor, ingredientCosts]); + + const handleScaleChange = useCallback((newScaleFactor: number) => { + setScaleFactor(newScaleFactor); + if (onScaleChange) { + onScaleChange(newScaleFactor, scaledRecipe as unknown as Recipe); + } + }, [scaledRecipe, onScaleChange]); + + const startTimer = (instruction: RecipeInstruction) => { + if (!instruction.duration_minutes) return; + + const timerState: TimerState = { + instructionId: instruction.step_number.toString(), + duration: instruction.duration_minutes * 60, // Convert to seconds + remaining: instruction.duration_minutes * 60, + isActive: true, + isComplete: false, + }; + + setActiveTimers(prev => ({ + ...prev, + [instruction.step_number.toString()]: timerState, + })); + + // Start countdown + const interval = setInterval(() => { + setActiveTimers(prev => { + const current = prev[instruction.step_number.toString()]; + if (!current || current.remaining <= 0) { + clearInterval(interval); + return { + ...prev, + [instruction.step_number.toString()]: { + ...current, + remaining: 0, + isActive: false, + isComplete: true, + } + }; + } + + return { + ...prev, + [instruction.step_number.toString()]: { + ...current, + remaining: current.remaining - 1, + } + }; + }); + }, 1000); + }; + + const stopTimer = (instructionId: string) => { + setActiveTimers(prev => ({ + ...prev, + [instructionId]: { + ...prev[instructionId], + isActive: false, + } + })); + }; + + const formatTime = (seconds: number): string => { + const hours = Math.floor(seconds / 3600); + const minutes = Math.floor((seconds % 3600) / 60); + const secs = seconds % 60; + + if (hours > 0) { + return `${hours}:${String(minutes).padStart(2, '0')}:${String(secs).padStart(2, '0')}`; + } + return `${minutes}:${String(secs).padStart(2, '0')}`; + }; + + const renderScalingControls = () => ( + +

Escalado de receta

+ +
+
+ + handleScaleChange(parseFloat(e.target.value) || 1)} + className="w-full" + /> +
+ +
+ +

+ {recipe.yield_quantity} {recipe.yield_unit} +

+
+ +
+ +

+ {scaledRecipe.scaledYieldQuantity} {recipe.yield_unit} +

+
+
+ +
+ + + + + +
+ + {scaleFactor !== 1 && ( +
+

+ Tiempo total escalado: {Math.ceil(scaledRecipe.scaledTotalTime / 60)}h {scaledRecipe.scaledTotalTime % 60}m +

+ {showCosting && scaledRecipe.estimatedTotalCost && ( +

+ Costo estimado: €{scaledRecipe.estimatedTotalCost.toFixed(2)} + (€{scaledRecipe.costPerScaledUnit?.toFixed(3)}/{recipe.yield_unit}) +

+ )} +
+ )} +
+ ); + + const renderRecipeHeader = () => ( + +
+
+
+
+

{recipe.name}

+

{recipe.description}

+
+ +
+ + {DIFFICULTY_LABELS[recipe.difficulty_level]} + + + v{recipe.version} + +
+
+ +
+
+

Preparación

+

{recipe.prep_time_minutes} min

+
+
+

Cocción

+

{recipe.cook_time_minutes} min

+
+
+

Total

+

{Math.ceil(recipe.total_time_minutes / 60)}h {recipe.total_time_minutes % 60}m

+
+
+

Rendimiento

+

{scaledRecipe.scaledYieldQuantity} {recipe.yield_unit}

+
+
+ + {recipe.allergen_warnings.length > 0 && ( +
+
+

Alérgenos:

+ +
+
+ {recipe.allergen_warnings.map((allergen) => ( + + {ALLERGEN_ICONS[allergen]} {allergen} + + ))} +
+ + {showAllergensDetail && ( +
+

+ ⚠️ Este producto contiene los siguientes alérgenos. Revisar cuidadosamente + antes del consumo si existe alguna alergia o intolerancia alimentaria. +

+
+ )} +
+ )} + + {recipe.storage_instructions && ( +
+

+ Conservación: {recipe.storage_instructions} + {recipe.shelf_life_hours && ( + + • Vida útil: {Math.ceil(recipe.shelf_life_hours / 24)} días + + )} +

+
+ )} +
+ +
+ {showNutrition && ( + + )} + + {showCosting && ( + + )} + + + + {editable && onVersionUpdate && ( + + )} +
+
+
+ ); + + const renderIngredients = () => ( + +

Ingredientes

+ +
+ {scaledRecipe.ingredients.map((ingredient, index) => ( +
+
+

{ingredient.ingredient_name}

+ {ingredient.preparation_notes && ( +

{ingredient.preparation_notes}

+ )} + {ingredient.is_optional && ( + + Opcional + + )} +
+ +
+

+ {ingredient.scaledQuantity.toFixed(ingredient.scaledQuantity < 1 ? 2 : 0)} {ingredient.unit} +

+ {scaleFactor !== 1 && ( +

+ (original: {ingredient.quantity} {ingredient.unit}) +

+ )} + {showCosting && ingredient.scaledCost && ( +

+ €{ingredient.scaledCost.toFixed(2)} +

+ )} +
+
+ ))} +
+ + {showCosting && scaledRecipe.estimatedTotalCost && ( +
+
+ Costo total estimado: + + €{scaledRecipe.estimatedTotalCost.toFixed(2)} + +
+

+ €{scaledRecipe.costPerScaledUnit?.toFixed(3)} por {recipe.yield_unit} +

+
+ )} +
+ ); + + const renderInstructions = () => ( + +

Instrucciones

+ +
+ {scaledRecipe.instructions.map((instruction, index) => { + const timer = activeTimers[instruction.step_number.toString()]; + + return ( +
+
+
+
+ {instruction.step_number} +
+
+ +
+
+
+ {instruction.duration_minutes && ( + + ⏱️ {instruction.scaledDuration || instruction.duration_minutes} min + + )} + + {instruction.temperature && ( + + 🌡️ {instruction.temperature}°C + + )} + + {instruction.critical_control_point && ( + + 🚨 PCC + + )} +
+ + {instruction.duration_minutes && ( +
+ {!timer?.isActive && !timer?.isComplete && ( + + )} + + {timer?.isActive && ( +
+ + {formatTime(timer.remaining)} + + +
+ )} + + {timer?.isComplete && ( + + ✅ Completado + + )} +
+ )} +
+ +

+ {instruction.instruction} +

+ + {instruction.equipment && instruction.equipment.length > 0 && ( +
+

Equipo necesario:

+
+ {instruction.equipment.map((equipment, idx) => ( + + {EQUIPMENT_ICONS[equipment.toLowerCase()] || '🔧'} {equipment} + + ))} +
+
+ )} + + {instruction.tips && ( +
+

+ 💡 Tip: {instruction.tips} +

+
+ )} +
+
+
+ ); + })} +
+
+ ); + + const renderNutritionModal = () => ( + setIsNutritionModalOpen(false)} + title="Información Nutricional" + > + {recipe.nutritional_info ? ( +
+
+
+

Calorías por porción

+

+ {recipe.nutritional_info.calories_per_serving || 'N/A'} +

+
+ +
+

Tamaño de porción

+

+ {recipe.nutritional_info.serving_size || 'N/A'} +

+
+
+ +
+
+

Proteínas

+

{recipe.nutritional_info.protein_g || 0}g

+
+
+

Carbohidratos

+

{recipe.nutritional_info.carbohydrates_g || 0}g

+
+
+

Grasas

+

{recipe.nutritional_info.fat_g || 0}g

+
+
+

Fibra

+

{recipe.nutritional_info.fiber_g || 0}g

+
+
+

Azúcares

+

{recipe.nutritional_info.sugar_g || 0}g

+
+
+

Sodio

+

{recipe.nutritional_info.sodium_mg || 0}mg

+
+
+ +
+

+ Porciones por lote escalado: { + recipe.nutritional_info.servings_per_batch + ? Math.ceil(recipe.nutritional_info.servings_per_batch * scaleFactor) + : 'N/A' + } +

+
+
+ ) : ( +

+ No hay información nutricional disponible para esta receta. +

+ )} +
+ ); + + const renderCostingModal = () => ( + setIsCostingModalOpen(false)} + title="Análisis de Costos" + > +
+
+
+ Costo total + + €{scaledRecipe.estimatedTotalCost?.toFixed(2)} + +
+

+ €{scaledRecipe.costPerScaledUnit?.toFixed(3)} por {recipe.yield_unit} +

+
+ +
+

Desglose por ingrediente

+
+ {scaledRecipe.ingredients.map((ingredient, index) => ( +
+
+

{ingredient.ingredient_name}

+

+ {ingredient.scaledQuantity.toFixed(2)} {ingredient.unit} +

+
+

+ €{ingredient.scaledCost?.toFixed(2) || '0.00'} +

+
+ ))} +
+
+ +
+

Análisis de rentabilidad

+
+

• Costo de ingredientes: €{scaledRecipe.estimatedTotalCost?.toFixed(2)}

+

• Margen sugerido (300%): €{((scaledRecipe.estimatedTotalCost || 0) * 3).toFixed(2)}

+

• Precio de venta sugerido por {recipe.yield_unit}: €{((scaledRecipe.costPerScaledUnit || 0) * 4).toFixed(2)}

+
+
+
+
+ ); + + const renderEquipmentModal = () => ( + setIsEquipmentModalOpen(false)} + title="Equipo Necesario" + > +
+
+

Equipo general de la receta

+
+ {recipe.equipment_needed.map((equipment, index) => ( +
+ + {EQUIPMENT_ICONS[equipment.toLowerCase()] || '🔧'} + + {equipment} +
+ ))} +
+
+ +
+

Equipo por instrucción

+
+ {recipe.instructions + .filter(instruction => instruction.equipment && instruction.equipment.length > 0) + .map((instruction) => ( +
+

+ Paso {instruction.step_number} +

+
+ {instruction.equipment?.map((equipment, idx) => ( + + {EQUIPMENT_ICONS[equipment.toLowerCase()] || '🔧'} {equipment} + + ))} +
+
+ ))} +
+
+
+
+ ); + + return ( +
+ {renderRecipeHeader()} + {renderScalingControls()} + +
+ {renderIngredients()} +
+ {renderInstructions()} +
+
+ + {renderNutritionModal()} + {renderCostingModal()} + {renderEquipmentModal()} +
+ ); +}; + +export default RecipeDisplay; \ No newline at end of file diff --git a/frontend/src/components/domain/production/RecipeDisplay/index.ts b/frontend/src/components/domain/production/RecipeDisplay/index.ts new file mode 100644 index 00000000..e5419ae3 --- /dev/null +++ b/frontend/src/components/domain/production/RecipeDisplay/index.ts @@ -0,0 +1 @@ +export { default } from '../RecipeDisplay'; \ No newline at end of file diff --git a/frontend/src/components/domain/production/index.ts b/frontend/src/components/domain/production/index.ts new file mode 100644 index 00000000..1ef6ddb5 --- /dev/null +++ b/frontend/src/components/domain/production/index.ts @@ -0,0 +1,12 @@ +// Production Domain Components +export { default as ProductionSchedule } from './ProductionSchedule'; +export { default as BatchTracker } from './BatchTracker'; +export { default as QualityControl } from './QualityControl'; +export { default as RecipeDisplay } from './RecipeDisplay'; + + +// Export component props types +export type { ProductionScheduleProps } from './ProductionSchedule'; +export type { BatchTrackerProps } from './BatchTracker'; +export type { QualityControlProps } from './QualityControl'; +export type { RecipeDisplayProps } from './RecipeDisplay'; \ No newline at end of file diff --git a/frontend/src/components/domain/sales/CustomerInfo.tsx b/frontend/src/components/domain/sales/CustomerInfo.tsx new file mode 100644 index 00000000..4aa32033 --- /dev/null +++ b/frontend/src/components/domain/sales/CustomerInfo.tsx @@ -0,0 +1,1272 @@ +import React, { useState, useEffect, useMemo } from 'react'; +import { + Card, + Button, + Badge, + Input, + Select, + Avatar, + Tooltip, + Modal +} from '../../ui'; +import { + SalesRecord, + SalesChannel, + PaymentMethod +} from '../../../types/sales.types'; +import { salesService } from '../../../services/api/sales.service'; +import { useSales } from '../../../hooks/api/useSales'; + +// Customer interfaces +interface Customer { + id: string; + name: string; + email?: string; + phone?: string; + address?: string; + city?: string; + postal_code?: string; + birth_date?: string; + registration_date: string; + status: CustomerStatus; + segment: CustomerSegment; + loyalty_points: number; + preferred_channel: SalesChannel; + notes?: string; + tags: string[]; + communication_preferences: CommunicationPreferences; +} + +interface CustomerOrder { + id: string; + date: string; + total: number; + items_count: number; + status: OrderStatus; + channel: SalesChannel; + payment_method?: PaymentMethod; + products: string[]; +} + +interface CustomerStats { + total_orders: number; + total_spent: number; + average_order_value: number; + last_order_date?: string; + favorite_products: string[]; + preferred_times: string[]; + loyalty_tier: LoyaltyTier; + lifetime_value: number; + churn_risk: ChurnRisk; +} + +interface CommunicationPreferences { + email_marketing: boolean; + sms_notifications: boolean; + push_notifications: boolean; + promotional_offers: boolean; + order_updates: boolean; + loyalty_updates: boolean; +} + +interface PaymentMethod { + id: string; + type: 'credit_card' | 'debit_card' | 'digital_wallet' | 'bank_account'; + last_four: string; + brand?: string; + is_default: boolean; + expires_at?: string; +} + +interface LoyaltyProgram { + current_points: number; + points_to_next_tier: number; + current_tier: LoyaltyTier; + benefits: string[]; + rewards_history: LoyaltyTransaction[]; +} + +interface LoyaltyTransaction { + id: string; + date: string; + type: 'earned' | 'redeemed'; + points: number; + description: string; + order_id?: string; +} + +enum CustomerStatus { + ACTIVE = 'active', + INACTIVE = 'inactive', + SUSPENDED = 'suspended', + VIP = 'vip' +} + +enum CustomerSegment { + NEW = 'new', + REGULAR = 'regular', + PREMIUM = 'premium', + ENTERPRISE = 'enterprise', + AT_RISK = 'at_risk' +} + +enum OrderStatus { + PENDING = 'pending', + CONFIRMED = 'confirmed', + PREPARING = 'preparing', + READY = 'ready', + DELIVERED = 'delivered', + CANCELLED = 'cancelled' +} + +enum LoyaltyTier { + BRONZE = 'bronze', + SILVER = 'silver', + GOLD = 'gold', + PLATINUM = 'platinum' +} + +enum ChurnRisk { + LOW = 'low', + MEDIUM = 'medium', + HIGH = 'high', + CRITICAL = 'critical' +} + +interface CustomerInfoProps { + customerId?: string; + onCustomerSelect?: (customer: Customer) => void; + onCustomerUpdate?: (customerId: string, updates: Partial) => void; + showOrderHistory?: boolean; + showLoyaltyProgram?: boolean; + allowEditing?: boolean; + className?: string; +} + +const StatusColors = { + [CustomerStatus.ACTIVE]: 'green', + [CustomerStatus.INACTIVE]: 'gray', + [CustomerStatus.SUSPENDED]: 'red', + [CustomerStatus.VIP]: 'purple' +} as const; + +const StatusLabels = { + [CustomerStatus.ACTIVE]: 'Activo', + [CustomerStatus.INACTIVE]: 'Inactivo', + [CustomerStatus.SUSPENDED]: 'Suspendido', + [CustomerStatus.VIP]: 'VIP' +} as const; + +const SegmentColors = { + [CustomerSegment.NEW]: 'blue', + [CustomerSegment.REGULAR]: 'gray', + [CustomerSegment.PREMIUM]: 'gold', + [CustomerSegment.ENTERPRISE]: 'purple', + [CustomerSegment.AT_RISK]: 'red' +} as const; + +const SegmentLabels = { + [CustomerSegment.NEW]: 'Nuevo', + [CustomerSegment.REGULAR]: 'Regular', + [CustomerSegment.PREMIUM]: 'Premium', + [CustomerSegment.ENTERPRISE]: 'Empresa', + [CustomerSegment.AT_RISK]: 'En Riesgo' +} as const; + +const TierColors = { + [LoyaltyTier.BRONZE]: 'orange', + [LoyaltyTier.SILVER]: 'gray', + [LoyaltyTier.GOLD]: 'yellow', + [LoyaltyTier.PLATINUM]: 'purple' +} as const; + +const TierLabels = { + [LoyaltyTier.BRONZE]: 'Bronce', + [LoyaltyTier.SILVER]: 'Plata', + [LoyaltyTier.GOLD]: 'Oro', + [LoyaltyTier.PLATINUM]: 'Platino' +} as const; + +const ChurnRiskColors = { + [ChurnRisk.LOW]: 'green', + [ChurnRisk.MEDIUM]: 'yellow', + [ChurnRisk.HIGH]: 'orange', + [ChurnRisk.CRITICAL]: 'red' +} as const; + +const ChurnRiskLabels = { + [ChurnRisk.LOW]: 'Bajo', + [ChurnRisk.MEDIUM]: 'Medio', + [ChurnRisk.HIGH]: 'Alto', + [ChurnRisk.CRITICAL]: 'Crítico' +} as const; + +const ChannelLabels = { + [SalesChannel.STORE_FRONT]: 'Tienda', + [SalesChannel.ONLINE]: 'Online', + [SalesChannel.PHONE_ORDER]: 'Teléfono', + [SalesChannel.DELIVERY]: 'Delivery', + [SalesChannel.CATERING]: 'Catering', + [SalesChannel.WHOLESALE]: 'Mayorista', + [SalesChannel.FARMERS_MARKET]: 'Mercado', + [SalesChannel.THIRD_PARTY]: 'Terceros' +} as const; + +export const CustomerInfo: React.FC = ({ + customerId, + onCustomerSelect, + onCustomerUpdate, + showOrderHistory = true, + showLoyaltyProgram = true, + allowEditing = true, + className = '' +}) => { + // State + const [customer, setCustomer] = useState(null); + const [customerStats, setCustomerStats] = useState(null); + const [orderHistory, setOrderHistory] = useState([]); + const [loyaltyProgram, setLoyaltyProgram] = useState(null); + const [paymentMethods, setPaymentMethods] = useState([]); + + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + // UI State + const [isEditing, setIsEditing] = useState(false); + const [editForm, setEditForm] = useState>({}); + const [activeTab, setActiveTab] = useState<'info' | 'orders' | 'loyalty' | 'communication'>('info'); + const [showAddNote, setShowAddNote] = useState(false); + const [newNote, setNewNote] = useState(''); + const [showLoyaltyModal, setShowLoyaltyModal] = useState(false); + const [rewardToRedeem, setRewardToRedeem] = useState(null); + + // Pagination for order history + const [orderPage, setOrderPage] = useState(1); + const [orderPageSize, setOrderPageSize] = useState(10); + + // Effects + useEffect(() => { + if (customerId) { + loadCustomerData(customerId); + } + }, [customerId]); + + // Load customer data + const loadCustomerData = async (id: string) => { + setLoading(true); + setError(null); + + try { + // In a real app, these would be separate API calls + const mockCustomer: Customer = { + id, + name: 'María García López', + email: 'maria.garcia@email.com', + phone: '+34 612 345 678', + address: 'Calle Mayor, 123', + city: 'Madrid', + postal_code: '28001', + birth_date: '1985-05-15', + registration_date: '2023-01-15T00:00:00Z', + status: CustomerStatus.VIP, + segment: CustomerSegment.PREMIUM, + loyalty_points: 2450, + preferred_channel: SalesChannel.ONLINE, + notes: 'Cliente preferente. Le gusta el pan integral sin gluten.', + tags: ['gluten-free', 'premium', 'weekly-order'], + communication_preferences: { + email_marketing: true, + sms_notifications: true, + push_notifications: false, + promotional_offers: true, + order_updates: true, + loyalty_updates: true + } + }; + + const mockStats: CustomerStats = { + total_orders: 47, + total_spent: 1247.85, + average_order_value: 26.55, + last_order_date: '2024-01-20T10:30:00Z', + favorite_products: ['Pan Integral', 'Croissant de Chocolate', 'Tarta de Santiago'], + preferred_times: ['09:00-11:00', '17:00-19:00'], + loyalty_tier: LoyaltyTier.GOLD, + lifetime_value: 1850.00, + churn_risk: ChurnRisk.LOW + }; + + const mockOrders: CustomerOrder[] = Array.from({ length: 47 }, (_, i) => ({ + id: `order_${i + 1}`, + date: new Date(Date.now() - i * 7 * 24 * 60 * 60 * 1000).toISOString(), + total: Math.random() * 50 + 15, + items_count: Math.floor(Math.random() * 5) + 1, + status: Object.values(OrderStatus)[Math.floor(Math.random() * Object.values(OrderStatus).length)], + channel: Object.values(SalesChannel)[Math.floor(Math.random() * Object.values(SalesChannel).length)], + payment_method: Object.values(PaymentMethod)[Math.floor(Math.random() * Object.values(PaymentMethod).length)], + products: ['Pan Integral', 'Croissant', 'Magdalenas'].slice(0, Math.floor(Math.random() * 3) + 1) + })); + + const mockLoyalty: LoyaltyProgram = { + current_points: 2450, + points_to_next_tier: 550, + current_tier: LoyaltyTier.GOLD, + benefits: [ + 'Descuento 10% en todos los productos', + 'Producto gratis cada 10 compras', + 'Reserva prioritaria para productos especiales', + 'Invitaciones exclusivas a eventos' + ], + rewards_history: Array.from({ length: 20 }, (_, i) => ({ + id: `loyalty_${i + 1}`, + date: new Date(Date.now() - i * 14 * 24 * 60 * 60 * 1000).toISOString(), + type: Math.random() > 0.7 ? 'redeemed' as const : 'earned' as const, + points: Math.floor(Math.random() * 200) + 50, + description: Math.random() > 0.7 ? 'Canje por producto gratis' : 'Puntos ganados por compra', + order_id: `order_${Math.floor(Math.random() * 47) + 1}` + })) + }; + + const mockPaymentMethods: PaymentMethod[] = [ + { + id: 'pm_1', + type: 'credit_card', + last_four: '4242', + brand: 'Visa', + is_default: true, + expires_at: '2026-12-31' + }, + { + id: 'pm_2', + type: 'digital_wallet', + last_four: 'PayPal', + is_default: false + } + ]; + + setCustomer(mockCustomer); + setCustomerStats(mockStats); + setOrderHistory(mockOrders); + setLoyaltyProgram(mockLoyalty); + setPaymentMethods(mockPaymentMethods); + } catch (err) { + setError('Error al cargar datos del cliente'); + console.error('Error loading customer data:', err); + } finally { + setLoading(false); + } + }; + + // Handle edit + const handleEditStart = () => { + if (customer) { + setEditForm({ ...customer }); + setIsEditing(true); + } + }; + + const handleEditSave = async () => { + if (!customer || !editForm) return; + + try { + const updatedCustomer = { ...customer, ...editForm }; + setCustomer(updatedCustomer); + onCustomerUpdate?.(customer.id, editForm); + setIsEditing(false); + setEditForm({}); + } catch (err) { + setError('Error al actualizar cliente'); + } + }; + + const handleEditCancel = () => { + setIsEditing(false); + setEditForm({}); + }; + + // Handle notes + const handleAddNote = () => { + if (!customer || !newNote.trim()) return; + + const updatedNotes = customer.notes ? `${customer.notes}\n\n${new Date().toLocaleDateString('es-ES')}: ${newNote}` : newNote; + const updatedCustomer = { ...customer, notes: updatedNotes }; + setCustomer(updatedCustomer); + onCustomerUpdate?.(customer.id, { notes: updatedNotes }); + setNewNote(''); + setShowAddNote(false); + }; + + // Handle loyalty redemption + const handleRedeemPoints = (rewardId: string, pointsCost: number) => { + if (!customer || !loyaltyProgram) return; + + if (loyaltyProgram.current_points >= pointsCost) { + const newTransaction: LoyaltyTransaction = { + id: `loyalty_${Date.now()}`, + date: new Date().toISOString(), + type: 'redeemed', + points: -pointsCost, + description: `Canje: ${rewardId}`, + }; + + const updatedLoyalty = { + ...loyaltyProgram, + current_points: loyaltyProgram.current_points - pointsCost, + rewards_history: [newTransaction, ...loyaltyProgram.rewards_history] + }; + + const updatedCustomer = { + ...customer, + loyalty_points: loyaltyProgram.current_points - pointsCost + }; + + setLoyaltyProgram(updatedLoyalty); + setCustomer(updatedCustomer); + onCustomerUpdate?.(customer.id, { loyalty_points: updatedCustomer.loyalty_points }); + } + }; + + // Filtered order history + const paginatedOrders = useMemo(() => { + const start = (orderPage - 1) * orderPageSize; + return orderHistory.slice(start, start + orderPageSize); + }, [orderHistory, orderPage, orderPageSize]); + + if (loading) { + return ( +
+
+ Cargando información del cliente... +
+ ); + } + + if (error || !customer) { + return ( +
+
+
+ + + +
+

Error

+

{error || 'Cliente no encontrado'}

+
+
+
+
+ ); + } + + return ( +
+ {/* Header */} + +
+
+ +
+

{customer.name}

+
+ + {StatusLabels[customer.status]} + + + {SegmentLabels[customer.segment]} + + {customerStats && ( + + {TierLabels[customerStats.loyalty_tier]} + + )} +
+
+
+ +
+ {allowEditing && ( + <> + {isEditing ? ( + <> + + + + ) : ( + + )} + + )} + + +
+
+
+ + {/* Stats Overview */} + {customerStats && ( +
+ +
+
+

Total Gastado

+

+ €{customerStats.total_spent.toFixed(2)} +

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

Pedidos Totales

+

{customerStats.total_orders}

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

Ticket Promedio

+

+ €{customerStats.average_order_value.toFixed(2)} +

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

Riesgo de Fuga

+
+ + {ChurnRiskLabels[customerStats.churn_risk]} + +
+
+
+ + + +
+
+
+
+ )} + + {/* Tabs */} +
+ +
+ + {/* Tab Content */} + {activeTab === 'info' && ( +
+ +

Datos Personales

+
+
+
+ + {isEditing ? ( + setEditForm(prev => ({ ...prev, name: e.target.value }))} + /> + ) : ( +

{customer.name}

+ )} +
+
+ + {isEditing ? ( + setEditForm(prev => ({ ...prev, email: e.target.value }))} + /> + ) : ( +

{customer.email}

+ )} +
+
+ +
+
+ + {isEditing ? ( + setEditForm(prev => ({ ...prev, phone: e.target.value }))} + /> + ) : ( +

{customer.phone}

+ )} +
+
+ + {isEditing ? ( + setEditForm(prev => ({ ...prev, birth_date: e.target.value }))} + /> + ) : ( +

+ {customer.birth_date ? new Date(customer.birth_date).toLocaleDateString('es-ES') : 'No especificado'} +

+ )} +
+
+ +
+ + {isEditing ? ( + setEditForm(prev => ({ ...prev, address: e.target.value }))} + /> + ) : ( +

{customer.address}

+ )} +
+ +
+
+ + {isEditing ? ( + setEditForm(prev => ({ ...prev, city: e.target.value }))} + /> + ) : ( +

{customer.city}

+ )} +
+
+ + {isEditing ? ( + setEditForm(prev => ({ ...prev, postal_code: e.target.value }))} + /> + ) : ( +

{customer.postal_code}

+ )} +
+
+
+
+ + +

Preferencias y Segmentación

+
+
+ + {isEditing ? ( + { + if (isEditing || allowEditing) { + const updated = { + ...customer, + communication_preferences: { + ...customer.communication_preferences, + email_marketing: e.target.checked + } + }; + setCustomer(updated); + onCustomerUpdate?.(customer.id, { communication_preferences: updated.communication_preferences }); + } + }} + className="rounded border-[var(--border-secondary)] text-[var(--color-info)] focus:ring-blue-500" + disabled={!isEditing && !allowEditing} + /> + Marketing por email + + + + + +
+
+ +
+

Tipos de Comunicación

+
+ + + + + +
+
+
+ + {paymentMethods.length > 0 && ( +
+

Métodos de Pago

+
+ {paymentMethods.map(method => ( +
+
+
+ + + +
+
+

+ {method.brand} •••• {method.last_four} +

+ {method.expires_at && ( +

+ Expira {new Date(method.expires_at).toLocaleDateString('es-ES')} +

+ )} +
+
+ {method.is_default && ( + Por defecto + )} +
+ ))} +
+
+ )} +
+ + )} + + {/* Add Note Modal */} + setShowAddNote(false)} + title="Agregar Nota" + > +
+
+ +