commit 773bfab393717a44d62bc9ed948884ec31337748 Author: Marcelo Date: Tue Mar 31 13:21:48 2026 +0000 merca y ch diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..3722418 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": ["next/core-web-vitals", "next/typescript"] +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ef6a52 --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/PDF KPI breakdown.pdf b/PDF KPI breakdown.pdf new file mode 100644 index 0000000..89b69ce Binary files /dev/null and b/PDF KPI breakdown.pdf differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..592cac0 --- /dev/null +++ b/README.md @@ -0,0 +1,99 @@ +# Casa Benell - Dashboard ejecutivo + +Dashboard en Next.js + TypeScript para Casa Benell, con base PostgreSQL para auth/roles/invitaciones. + +## Stack +- Next.js (App Router) + TypeScript +- TailwindCSS +- Recharts (Sankey) +- Zustand (estado UI persistido en `localStorage`) +- Prisma + PostgreSQL +- NextAuth (credentials + adapter Prisma) +- Nodemailer (SMTP invitations) + +## Ejecutar +```bash +npm install +npm run dev +``` +Abre `https://benell.maliountech.com.mx`. + +## Configuración backend (Fase 3/4) +1. Crea tu archivo de entorno: +```bash +cp .env.example .env.local +``` +2. Ajusta valores reales (DB, `NEXTAUTH_SECRET`, SMTP). + Importante: define `APP_URL` con `https://benell.maliountech.com.mx` para enlaces por correo. +3. Genera cliente Prisma y aplica migraciones: +```bash +npm run prisma:generate +npm run prisma:migrate +npm run prisma:seed +``` +Para servidor (sin permisos de create database), usa: +```bash +npm run prisma:deploy +``` +4. Inicia el proyecto: +```bash +npm run dev +``` + +Build de producción: +```bash +npm run build +npm run start +``` + +## Sync automático Meta (Marketing) diario 06:00 +1. Define en `.env` o `.env.local`: +```bash +MARKETING_SYNC_CRON_SECRET="un-secreto-largo" +INSTAGRAM_TOKEN="..." +INSTAGRAM_USER_ID="..." +``` +2. Instala unidades `systemd` (si despliegas con `systemd`): +```bash +sudo cp deploy/systemd/benell-marketing-sync.service /etc/systemd/system/ +sudo cp deploy/systemd/benell-marketing-sync.timer /etc/systemd/system/ +sudo systemctl daemon-reload +sudo systemctl enable --now benell-marketing-sync.timer +``` +3. Verifica: +```bash +systemctl status benell-marketing-sync.timer +systemctl list-timers | grep benell-marketing-sync +``` +Nota: `OnCalendar=*-*-* 06:00:00` usa la zona horaria del servidor. + +## Estructura relevante +- `src/app`: rutas App Router (`/login`, `/dashboard`, `/financial-flow`, etc.) +- `src/components`: shell y componentes UI reutilizables +- `src/lib/mock`: datos mock tipados (`locations`, `departments`, `kpis`, `initiatives`, `meetings`, `people`) +- `src/lib/store/ui-store.ts`: rol/filtros globales (persistidos) +- `src/styles/tokens.ts`: tokens de diseño (paleta, radios, sombras, spacing) +- `public/brand/logo.webp`, `public/brand/mascot.png`: assets de marca +- `prisma/schema.prisma`: modelos DB (users, roles, user_roles, invitations, next-auth tables) +- `src/app/api/invitations/route.ts`: crear invitaciones + envío SMTP +- `src/app/api/invitations/accept/route.ts`: aceptar invitación y crear/actualizar usuario +- `src/app/api/auth/register/route.ts`: crear cuenta y enviar verificación por correo +- `src/app/api/auth/verify-email/route.ts`: validar token de verificación y activar login + +## Estado UI (sin backend) +- Rol de vista en topbar es informativo y se toma de la sesión autenticada (no editable en UI). +- Filtros de rango de fecha y ubicación en topbar +- Búsqueda global local para filtrar listas visibles + +## Estado backend actual +1. Login con credenciales reales desde PostgreSQL. +2. Rutas de app protegidas con middleware (`/dashboard`, `/financial-flow`, etc). +3. Rol en sesión (`owner|leader|employee`) como fuente de verdad para la UI. +4. Invitaciones por email con token seguro hash + expiración + aceptación. +5. Botón "Crear cuenta" y verificación de correo antes de login. +6. Recuperación de contraseña por correo (`/forgot-password` + `/reset-password`). + +## Pendiente +1. Migrar KPIs y métricas mock a tablas reales. +2. Agregar UI admin para listar/reenviar invitaciones. +3. Fortalecer manejo de errores SMTP/DB en panel de configuración. diff --git a/Reference KPI dashboard plan.md b/Reference KPI dashboard plan.md new file mode 100644 index 0000000..8c61fdd --- /dev/null +++ b/Reference KPI dashboard plan.md @@ -0,0 +1,55 @@ +### Weekly KPI Board V1 (Owner Dashboard + Scoped Leader View + PDF Export) + +### Summary +- Build a new weekly KPI visualization layer on the main dashboard using the KPI structure from `PDF KPI breakdown.pdf` (responsabilidad, objetivo/indicador, quantity/quality, % cumplimiento, fecha/compromiso). +- Power V1 with weekly snapshot data in Prisma (seeded from the extracted PDF rows first), exposed via internal API. +- Show full board to `owner`, scoped board to `leader` by department, and keep `employee` without dashboard access. +- Include a print-optimized export flow for “Save as PDF”. + +### Implementation Changes +- Add weekly KPI persistence models in [schema.prisma](/home/mdares03/benell/prisma/schema.prisma): + - `WeeklyKpiSnapshot` (weekStart, weekEnd, source, timestamps). + - `WeeklyKpiSection` (raw section label, optional mapped `DepartmentKey`, owner/team labels, sort order). + - `WeeklyKpiRow` (KPI text fields + parsed compliance/value fields + derived status + sort order). +- Seed initial snapshot from the PDF baseline into the week of **March 16–22, 2026** (PDF creation date is March 18, 2026), preserving raw labels exactly. +- Add KPI API surface: + - `GET /api/kpis/weekly?weekStart=YYYY-MM-DD` (grouped board payload with role-based scoping). + - `POST /api/kpis/weekly/ingest` (upsert snapshot payload for future platform-generated data). + - `PATCH /api/kpis/weekly/rows/:id` (owner + relevant leader edits for label/target/compliance text corrections). +- Update access control so `/dashboard` is allowed for `owner` and `leader` (leaders receive department-scoped dataset). +- Extend [dashboard/page.tsx](/home/mdares03/benell/src/app/(app)/dashboard/page.tsx) with a hybrid KPI board: + - Header KPI summary cards (total rows, on-track %, at-risk count, due-soon count). + - Department/section blocks with sortable table rows and visual status chips/progress bars. + - Search + status + section filters. +- Add print export mode: + - Print-focused dashboard route/view with A4 CSS and hidden interactive controls. + - “Exportar PDF” button triggers print flow (`window.print`) for browser Save-as-PDF. + +### Public APIs / Interfaces / Types +- New Prisma entities: `WeeklyKpiSnapshot`, `WeeklyKpiSection`, `WeeklyKpiRow`. +- New shared DTOs for KPI board payload (`KpiBoardResponse`, `KpiSectionDTO`, `KpiRowDTO`). +- New status enum for visualization and filtering (`on_track`, `watch`, `risk`, `no_score`). +- Ingest contract supports raw text + optional parsed fields so platform integration can incrementally mature without breaking UI. + +### Test Plan +- Unit tests: + - compliance/value parser from mixed text (`"90%"`, `"800,000"`, `"PLAN 50%"`). + - row status derivation and due-soon/risk classification. +- API tests: + - authz for owner/leader/employee. + - leader scoping returns only mapped department sections. + - ingest upsert idempotency by `(weekStart, section, row key)`. +- UI tests: + - filters/sorting behavior and risk highlighting. + - dashboard renders sectioned KPI table without text overflow. + - print view renders cleanly in desktop/mobile and produces expected PDF layout. +- Regression checks: + - existing non-KPI dashboard blocks still render. + - projects/marketing routes unaffected. + +### Assumptions And Defaults +- Week cadence is Monday–Sunday in `America/Mexico_City`. +- V1 uses seeded PDF-derived data plus API-ready schema; external platform wiring uses the ingest endpoint in the next step. +- Raw PDF labels remain source of truth in V1; optional enum mapping is additive. +- Leader visibility is department-scoped only; owner is global. +- PDF export in V1 is print-layout based (not backend file rendering). diff --git a/deploy/systemd/benell-marketing-sync.service b/deploy/systemd/benell-marketing-sync.service new file mode 100644 index 0000000..38783d5 --- /dev/null +++ b/deploy/systemd/benell-marketing-sync.service @@ -0,0 +1,15 @@ +[Unit] +Description=Casa Benell Marketing Meta Daily Sync +After=network.target benell.service +Wants=benell.service + +[Service] +Type=oneshot +User=mdares03 +Group=mdares03 +WorkingDirectory=/home/mdares03/benell +EnvironmentFile=-/home/mdares03/benell/.env +EnvironmentFile=-/home/mdares03/benell/.env.local +ExecStart=/bin/bash -lc 'set -euo pipefail; test -n "${MARKETING_SYNC_CRON_SECRET:-}"; curl -fsS -X POST "http://127.0.0.1:${PORT:-3000}/api/marketing/meta/sync" -H "Authorization: Bearer ${MARKETING_SYNC_CRON_SECRET}" -H "Content-Type: application/json" --data "{}"' +StandardOutput=journal +StandardError=journal diff --git a/deploy/systemd/benell-marketing-sync.timer b/deploy/systemd/benell-marketing-sync.timer new file mode 100644 index 0000000..2c3af81 --- /dev/null +++ b/deploy/systemd/benell-marketing-sync.timer @@ -0,0 +1,11 @@ +[Unit] +Description=Run Casa Benell Marketing Meta Sync every day at 06:00 + +[Timer] +OnCalendar=*-*-* 06:00:00 +Persistent=true +RandomizedDelaySec=120 +Unit=benell-marketing-sync.service + +[Install] +WantedBy=timers.target diff --git a/deploy/systemd/benell.service b/deploy/systemd/benell.service new file mode 100644 index 0000000..d5b4a53 --- /dev/null +++ b/deploy/systemd/benell.service @@ -0,0 +1,21 @@ +[Unit] +Description=Casa Benell Next.js App +After=network.target + +[Service] +Type=simple +User=mdares03 +Group=mdares03 +WorkingDirectory=/home/mdares03/benell +Environment=NODE_ENV=production +Environment=PORT=3000 +ExecStart=/usr/bin/npm run start -- --port=3000 --hostname=0.0.0.0 +Restart=always +RestartSec=5 +TimeoutStopSec=30 +KillSignal=SIGINT +StandardOutput=journal +StandardError=journal + +[Install] +WantedBy=multi-user.target diff --git a/dev_plan_1.md b/dev_plan_1.md new file mode 100644 index 0000000..13a66e1 --- /dev/null +++ b/dev_plan_1.md @@ -0,0 +1,185 @@ +# Casa Benell - Dev Plan 1 + +## Goal +Stabilize UI quality (responsive + proportional typography), adopt final brand assets, and start backend foundations for users/roles/invitations with PostgreSQL and SMTP. + +## Current pain points (from first pass) +- Typography scale is too large in several views. +- Numeric values overflow cards and break layout. +- Desktop-first layout is not fully adaptive on mobile. +- Sidebar needs a true mobile hamburger/sheet pattern. +- Brand assets in use are placeholders, not final logo/character files. +- No DB/auth/invite flow yet (all role switching is local UI state). + +## Inputs confirmed +- Final logo source: `/home/mdares03/benell/example_ui_img/benell_logo.webp` +- Theme character source: `/home/mdares03/benell/example_ui_img/Logo Benito.png` + +## Implementation strategy (phased) + +### Phase 1 - Responsive UI hardening +Scope: +- Normalize type scale and spacing so content always fits card boundaries. +- Prevent number overflow in KPI cards, tables, and right panels. +- Add mobile nav with hamburger + slide-out menu. +- Improve responsive behavior for top bar filters/search/role selector. + +Tasks: +1. Define responsive typography tokens with `clamp(...)` and semantic utility classes. +2. Replace oversized heading/body classes with tokenized scale: + - H1/H2/H3/body/label/caption. +3. Apply overflow-safe text rules: + - `min-w-0` in flex/grid children. + - `truncate`/`break-words` where needed. + - `tabular-nums` for KPI numeric alignment. +4. Refactor cards/tables to use adaptive grids: + - desktop: 4 columns where available + - tablet: 2 columns + - mobile: 1 column +5. Build mobile sidebar pattern: + - top-left hamburger button + - drawer/sheet navigation + - keyboard and focus support +6. Make Sankey containers responsive with controlled heights by breakpoint. +7. Validate at widths: 360, 390, 768, 1024, 1280, 1536. + +Acceptance criteria: +- No text or numbers overflow containers at target breakpoints. +- Dashboard, Financial Flow, Meetings, People remain usable on mobile. +- Navigation works via sidebar (desktop) and hamburger drawer (mobile). + +--- + +### Phase 2 - Final brand asset integration +Scope: +- Replace placeholder brand files with provided official assets. + +Tasks: +1. Copy and normalize assets into `/public/brand/`: + - `logo.webp` from `benell_logo.webp` + - `mascot.png` from `Logo Benito.png` +2. Update components to use final files in: + - sidebar/header brand block + - login page + - empty states +3. Verify object-fit/crop so assets render clean on desktop + mobile. + +Acceptance criteria: +- Only final brand assets are used in app shell and empty states. +- No stretching, clipping, or quality loss in common viewport sizes. + +--- + +### Phase 3 - PostgreSQL foundation + RBAC data model +Scope: +- Start real user data layer with psql. +- Keep mock business metrics for now, but migrate auth/user/role from local store to DB-backed model. + +Tech decisions (proposed): +- ORM: Prisma +- DB: PostgreSQL +- Auth/session: NextAuth/Auth.js with DB adapter +- Password hashing: bcrypt +- Email: Nodemailer SMTP transport + +Core schema (initial): +1. `users` + - `id`, `name`, `email` (unique), `password_hash`, `status`, `created_at`, `updated_at` +2. `roles` + - `id`, `key` (`owner|leader|employee`), `name` +3. `user_roles` + - `user_id`, `role_id` (many-to-many) +4. `invitations` + - `id`, `email`, `role_key`, `token_hash`, `expires_at`, `accepted_at`, `invited_by`, `created_at` +5. `user_locations` (optional in this phase, recommended) + - `user_id`, `location_id` + +Tasks: +1. Add Prisma and configure `DATABASE_URL`. +2. Create schema + first migration. +3. Add seed script for default roles and bootstrap owner account. +4. Add auth route handlers and session callback to include role. +5. Add middleware route protection for authenticated app routes. +6. Replace UI-only role switcher with role from session (keep optional dev override flag). + +Acceptance criteria: +- App can connect to PostgreSQL locally. +- At least one owner user can login from DB. +- Protected routes reject anonymous requests. +- Role can be read from DB session payload. + +--- + +### Phase 4 - Invitation flow via SMTP +Scope: +- Admin/owner can invite users by email and assign role. + +Tasks: +1. Add env variables for SMTP and app URLs. +2. Create server actions or API routes: + - `POST /api/invitations` + - `POST /api/invitations/accept` +3. Generate secure random invitation tokens; store hash only. +4. Send email with invitation link (token + expiry). +5. Create acceptance page: + - set password + - confirm profile basics + - finalize user + role assignment +6. Add invitation status UI in admin/settings page. + +Acceptance criteria: +- Owner can send invite with selected role. +- Recipient can accept invite and create account before expiry. +- Used/expired tokens are rejected. + +--- + +### Phase 5 - Operational readiness and migration cleanup +Scope: +- Make deployment process stable under existing systemd flow. + +Tasks: +1. Add `.env.example` with required vars only (no secrets). +2. Add startup checks for missing env values. +3. Document production sequence: + - `npm run build` + - `sudo systemctl restart benell.service` +4. Add backup/rollback notes for DB migrations. + +Acceptance criteria: +- Clear runbook for local/prod env setup. +- No manual guessing for env keys or migration order. + +## Environment variables to add (planned) +- `DATABASE_URL=postgresql://...` +- `NEXTAUTH_URL=https://benell.maliountech.com.mx` +- `NEXTAUTH_SECRET=...` +- `SMTP_HOST=...` +- `SMTP_PORT=587` +- `SMTP_USER=...` +- `SMTP_PASS=...` +- `SMTP_FROM="Casa Benell "` +- `INVITE_TTL_HOURS=72` + +## Risks and mitigations +- Risk: auth migration blocks UI progress. + - Mitigation: complete UI responsive fixes first, then backend integration. +- Risk: role logic duplicated in client and server. + - Mitigation: single source of truth from session role, with temporary dev override only. +- Risk: invite token leakage. + - Mitigation: store hashed tokens, set short expiry, single-use tokens. + +## Execution order +1. Phase 1 (UI hardening) +2. Phase 2 (brand assets) +3. Phase 3 (DB + auth + RBAC) +4. Phase 4 (SMTP invites) +5. Phase 5 (runbook + stability) + +## Deliverables for this plan +- Responsive and proportional UI baseline. +- Mobile hamburger navigation. +- Final branding applied. +- PostgreSQL user/role foundation. +- SMTP invitation workflow. +- Updated docs for env and deployment. diff --git a/example_ui_img/Logo Benito.png b/example_ui_img/Logo Benito.png new file mode 100644 index 0000000..d5d8a08 Binary files /dev/null and b/example_ui_img/Logo Benito.png differ diff --git a/example_ui_img/Screenshot 2026-02-16 214334.png b/example_ui_img/Screenshot 2026-02-16 214334.png new file mode 100644 index 0000000..b3221f3 Binary files /dev/null and b/example_ui_img/Screenshot 2026-02-16 214334.png differ diff --git a/example_ui_img/Screenshot 2026-02-16 214357.png b/example_ui_img/Screenshot 2026-02-16 214357.png new file mode 100644 index 0000000..3a6505f Binary files /dev/null and b/example_ui_img/Screenshot 2026-02-16 214357.png differ diff --git a/example_ui_img/Screenshot 2026-02-16 214412.png b/example_ui_img/Screenshot 2026-02-16 214412.png new file mode 100644 index 0000000..1729c6b Binary files /dev/null and b/example_ui_img/Screenshot 2026-02-16 214412.png differ diff --git a/example_ui_img/Screenshot 2026-02-16 214420.png b/example_ui_img/Screenshot 2026-02-16 214420.png new file mode 100644 index 0000000..0c7093c Binary files /dev/null and b/example_ui_img/Screenshot 2026-02-16 214420.png differ diff --git a/example_ui_img/Screenshot 2026-02-16 214427.png b/example_ui_img/Screenshot 2026-02-16 214427.png new file mode 100644 index 0000000..4b02b75 Binary files /dev/null and b/example_ui_img/Screenshot 2026-02-16 214427.png differ diff --git a/example_ui_img/Screenshot 2026-02-16 214441.png b/example_ui_img/Screenshot 2026-02-16 214441.png new file mode 100644 index 0000000..670dce5 Binary files /dev/null and b/example_ui_img/Screenshot 2026-02-16 214441.png differ diff --git a/example_ui_img/Screenshot 2026-02-16 214448.png b/example_ui_img/Screenshot 2026-02-16 214448.png new file mode 100644 index 0000000..34407c9 Binary files /dev/null and b/example_ui_img/Screenshot 2026-02-16 214448.png differ diff --git a/example_ui_img/Screenshot 2026-02-16 214454.png b/example_ui_img/Screenshot 2026-02-16 214454.png new file mode 100644 index 0000000..e4725e7 Binary files /dev/null and b/example_ui_img/Screenshot 2026-02-16 214454.png differ diff --git a/example_ui_img/Screenshot 2026-02-16 214459.png b/example_ui_img/Screenshot 2026-02-16 214459.png new file mode 100644 index 0000000..fdb0c82 Binary files /dev/null and b/example_ui_img/Screenshot 2026-02-16 214459.png differ diff --git a/example_ui_img/Screenshot 2026-02-16 214506.png b/example_ui_img/Screenshot 2026-02-16 214506.png new file mode 100644 index 0000000..fca8306 Binary files /dev/null and b/example_ui_img/Screenshot 2026-02-16 214506.png differ diff --git a/example_ui_img/Screenshot 2026-02-16 214515.png b/example_ui_img/Screenshot 2026-02-16 214515.png new file mode 100644 index 0000000..5fc8550 Binary files /dev/null and b/example_ui_img/Screenshot 2026-02-16 214515.png differ diff --git a/example_ui_img/Screenshot 2026-02-16 214522.png b/example_ui_img/Screenshot 2026-02-16 214522.png new file mode 100644 index 0000000..10d4e8b Binary files /dev/null and b/example_ui_img/Screenshot 2026-02-16 214522.png differ diff --git a/example_ui_img/Screenshot 2026-02-16 214533.png b/example_ui_img/Screenshot 2026-02-16 214533.png new file mode 100644 index 0000000..3f32cc1 Binary files /dev/null and b/example_ui_img/Screenshot 2026-02-16 214533.png differ diff --git a/example_ui_img/benell_logo.webp b/example_ui_img/benell_logo.webp new file mode 100644 index 0000000..0bd4bee Binary files /dev/null and b/example_ui_img/benell_logo.webp differ diff --git a/experiencometro.md b/experiencometro.md new file mode 100644 index 0000000..883b381 --- /dev/null +++ b/experiencometro.md @@ -0,0 +1,293 @@ +Quiero que me ayudes a diseñar e implementar un apartado separado dentro de mi plataforma llamado Experienciómetro. + +Contexto del negocio + +El Experienciómetro es un módulo pensado para medir la experiencia que vive un cliente en cada sucursal/local de la marca. La idea no es solo calificar si “lo atendieron bien”, sino evaluar todo el recorrido del cliente dentro y alrededor de la sucursal: desde la llegada, la imagen exterior, la bienvenida, el ambiente, el servicio, la limpieza y los detalles que hacen que la experiencia suba o baje. + +Este módulo debe funcionar como una mezcla entre: + +dashboard de sucursales +sistema de evaluación tipo survey/checklist +scoring/rating por sucursal +seguimiento de hallazgos detectados + +La intención es que cada sucursal tenga una calificación visible y dinámica, alimentada por evaluaciones que llenan ciertos usuarios internos (por ejemplo padrinos de casa o responsables operativos). Esa calificación debe representar la experiencia general de la sucursal. + +Objetivo del módulo + +Construir un apartado independiente dentro de la plataforma donde: + +Se puedan ver todas las sucursales con su score de experiencia. +Se pueda entrar al detalle de cada sucursal. +Exista una encuesta/checklist estructurada para evaluar la experiencia. +Cada evaluación impacte automáticamente la calificación general de la sucursal. +Se puedan registrar observaciones, hallazgos y áreas de mejora. +El gerente o responsable pueda ver retroalimentación clara de su sucursal. +Concepto del Experienciómetro + +El módulo debe representar la experiencia del cliente como un sistema medible. +La experiencia no depende de una sola cosa, sino de varias dimensiones operativas. + +Las categorías base a evaluar serían: + +Exterior y llegada +limpieza exterior +fachada +acceso +señalización +Entrada y bienvenida +puerta abierta / acceso disponible +recepción +saludo +actitud +tiempo de respuesta +Ambiente +orden visual +aroma +música +limpieza general +temperatura +mobiliario / confort +Servicio +rapidez +amabilidad +seguimiento +atención al detalle +actitud del personal +Hospitalidad / detalles extra +detalles memorables +anticipación a necesidades +pequeños extras que elevan la experiencia +Operación visible +baños +mesas / áreas comunes +objetos fuera de lugar +mantenimiento visible + +Cada evaluación debe convertirse en un score cuantificable. + +Cómo debe funcionar el scoring + +Cada categoría tiene varios reactivos/preguntas. +Cada reactivo debe calificarse con una escala simple, por ejemplo: + +0 = mal +1 = regular +2 = bien +3 = excelente + +El sistema debe: + +calcular un score por categoría +calcular un score general por evaluación +actualizar el score general histórico de la sucursal + +La calificación general de la sucursal puede ser un promedio de evaluaciones recientes o un promedio ponderado. Diseña la estructura para que esto se pueda ajustar fácilmente después. + +Flujo del módulo +1. Vista general del Experienciómetro + +Debe existir una pantalla principal separada llamada Experienciómetro. + +En esta vista se deben mostrar todas las sucursales como cards o filas de tabla con información como: + +nombre de sucursal +score general actual +color o semáforo +última evaluación realizada +cantidad de evaluaciones +tendencia (subió, bajó o se mantuvo) +hallazgos abiertos + +Esta pantalla debe permitir: + +buscar sucursales +filtrar por score +filtrar por estatus/semaforo +entrar al detalle de cada sucursal +iniciar una nueva evaluación +2. Detalle de sucursal + +Cada sucursal debe tener una vista individual donde se vea: + +Header +nombre de sucursal +score actual +semáforo visual +última evaluación +gerente / responsable +cantidad total de evaluaciones +Secciones principales +resumen general +historial de evaluaciones +breakdown por categorías +hallazgos / observaciones +acciones o retroalimentación +Visualización recomendada +score general grande +barras por categoría +gráfica de tendencia histórica +lista de últimas evaluaciones +lista de hallazgos abiertos +3. Formulario de evaluación / survey + +Debe existir una pantalla o modal para llenar una nueva evaluación. + +Esta evaluación debe estar organizada por bloques/categorías: + +Ejemplo de bloques + +Exterior y llegada + +fachada limpia +acceso libre +señalización clara + +Bienvenida + +puerta abierta +alguien recibe al cliente +saludo adecuado +actitud positiva + +Ambiente + +música adecuada +aroma agradable +limpieza visual +orden general +temperatura cómoda + +Servicio + +rapidez +atención +seguimiento +trato amable + +Hospitalidad + +hubo algún detalle extra +el servicio fue memorable +se anticiparon necesidades + +Operación visible + +baños limpios +áreas comunes ordenadas +mantenimiento visible correcto + +Cada reactivo debe permitir: + +seleccionar score +agregar comentario opcional +marcar observación +adjuntar evidencia si aplica + +Al final del formulario: + +observaciones generales +fortalezas detectadas +áreas de mejora +score automático calculado por el sistema +4. Impacto en la calificación de la sucursal + +Cada vez que se envía una evaluación: + +se guarda el registro completo +se recalcula el score promedio de la sucursal +se actualiza la vista general +se reflejan las nuevas métricas en dashboard y detalle +5. Retroalimentación y hallazgos + +Además del survey, el módulo debe permitir guardar hallazgos/observaciones. + +Ejemplos: + +baño sucio +recepción vacía +bocina sin funcionar +objeto fuera de lugar +mala presentación visual + +Cada hallazgo puede tener: + +título +descripción +categoría +prioridad +estatus +fecha +responsable +relación con una evaluación específica + +Esto servirá para que el gerente vea retroalimentación más accionable, no solo un número. + +Cómo debe verse en UI/UX +Vista principal + +Quiero un módulo visual, ejecutivo y fácil de leer. +Debe sentirse como un dashboard operativo moderno. + +Elementos visuales sugeridos +cards por sucursal +score grande y destacado +badges tipo semáforo: +verde = excelente +amarillo = atención +rojo = crítico +barras de progreso por categoría +gráficas de tendencia +tabla o timeline de evaluaciones +Navegación + +El Experienciómetro debe ser un apartado separado del resto de la plataforma, no algo escondido dentro de otra sección. + +Idealmente tendría navegación como: + +Experienciómetro / Resumen +Sucursales +Evaluaciones +Hallazgos +Requerimientos funcionales + +Diseña este módulo con arquitectura limpia y escalable. + +Debe contemplar: + +listado de sucursales +detalle de sucursal +formulario de evaluación +historial de evaluaciones +cálculo automático de score +almacenamiento de comentarios y observaciones +visualización de hallazgos +dashboard general +Requerimientos técnicos + +Quiero que propongas la mejor forma de estructurarlo en frontend y backend. + +Necesito que plantees: + +componentes necesarios +vistas/páginas +modelos de datos +lógica de score +relaciones entre sucursal, evaluación, categorías y hallazgos + +Piensa el módulo de forma reusable y mantenible. + +Resultado esperado + +Quiero que me entregues una propuesta completa para este módulo, incluyendo: + +estructura del apartado +flujo del usuario +componentes de interfaz +modelo de datos recomendado +lógica de scoring +propuesta visual del dashboard +cómo conectar la evaluación con la calificación general de cada sucursal + +No uses datos hardcodeados en la solución final. +Diseña todo para que sea dinámico y escalable. \ No newline at end of file diff --git a/middleware.ts b/middleware.ts new file mode 100644 index 0000000..d1a65cc --- /dev/null +++ b/middleware.ts @@ -0,0 +1,42 @@ +import { withAuth } from "next-auth/middleware"; +import { NextResponse } from "next/server"; +import { canAccessPath, getDepartmentHomeRoute } from "@/lib/access-control"; +import type { DepartmentKey, UserRole } from "@/lib/types"; + +export default withAuth( + function middleware(req) { + const token = req.nextauth.token; + const role = token?.role as UserRole | undefined; + const department = (token?.department as DepartmentKey | null | undefined) ?? null; + const pathname = req.nextUrl.pathname; + + if (!canAccessPath({ role, department }, pathname)) { + const fallbackPath = role === "owner" ? "/dashboard" : getDepartmentHomeRoute(department); + const safeFallbackPath = pathname === fallbackPath ? "/settings" : fallbackPath; + return NextResponse.redirect(new URL(safeFallbackPath, req.url)); + } + + return NextResponse.next(); + }, + { + pages: { + signIn: "/login", + }, + } +); + +export const config = { + matcher: [ + "/dashboard/:path*", + "/financial-flow/:path*", + "/experienciometro/:path*", + "/departments/:path*", + "/initiatives/:path*", + "/meetings/:path*", + "/people/:path*", + "/data-entry/:path*", + "/settings/:path*", + "/api/invitations", + "/api/experienciometro/:path*", + ], +}; diff --git a/next.config.mjs b/next.config.mjs new file mode 100644 index 0000000..d5456a1 --- /dev/null +++ b/next.config.mjs @@ -0,0 +1,6 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: true, +}; + +export default nextConfig; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..0ed09e0 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,7117 @@ +{ + "name": "casa-benell", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "casa-benell", + "version": "0.1.0", + "dependencies": { + "@next-auth/prisma-adapter": "^1.0.7", + "@prisma/client": "^6.16.2", + "bcryptjs": "^3.0.3", + "clsx": "^2.1.1", + "lucide-react": "^0.564.0", + "next": "14.2.33", + "next-auth": "^4.24.13", + "nodemailer": "^7.0.13", + "prisma": "^6.16.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "recharts": "^3.7.0", + "zustand": "^5.0.11" + }, + "devDependencies": { + "@types/node": "^20", + "@types/nodemailer": "^7.0.10", + "@types/react": "^18", + "@types/react-dom": "^18", + "autoprefixer": "^10.4.20", + "eslint": "^8.57.1", + "eslint-config-next": "14.2.33", + "postcss": "^8.4.49", + "tailwindcss": "^3.4.17", + "tsx": "^4.21.0", + "typescript": "^5" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", + "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz", + "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==", + "dev": true, + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", + "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "dev": true, + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", + "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "dev": true, + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "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, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.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, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "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 + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "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, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, + "node_modules/@next-auth/prisma-adapter": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@next-auth/prisma-adapter/-/prisma-adapter-1.0.7.tgz", + "integrity": "sha512-Cdko4KfcmKjsyHFrWwZ//lfLUbcLqlyFqjd/nYE2m3aZ7tjMNUjpks47iw7NTCnXf+5UWz5Ypyt1dSs1EP5QJw==", + "peerDependencies": { + "@prisma/client": ">=2.26.0 || >=3", + "next-auth": "^4" + } + }, + "node_modules/@next/env": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.33.tgz", + "integrity": "sha512-CgVHNZ1fRIlxkLhIX22flAZI/HmpDaZ8vwyJ/B0SDPTBuLZ1PJ+DWMjCHhqnExfmSQzA/PbZi8OAc7PAq2w9IA==" + }, + "node_modules/@next/eslint-plugin-next": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.33.tgz", + "integrity": "sha512-DQTJFSvlB+9JilwqMKJ3VPByBNGxAGFTfJ7BuFj25cVcbBy7jm88KfUN+dngM4D3+UxZ8ER2ft+WH9JccMvxyg==", + "dev": true, + "dependencies": { + "glob": "10.3.10" + } + }, + "node_modules/@next/eslint-plugin-next/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, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@next/eslint-plugin-next/node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@next/eslint-plugin-next/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.33.tgz", + "integrity": "sha512-HqYnb6pxlsshoSTubdXKu15g3iivcbsMXg4bYpjL2iS/V6aQot+iyF4BUc2qA/J/n55YtvE4PHMKWBKGCF/+wA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.33.tgz", + "integrity": "sha512-8HGBeAE5rX3jzKvF593XTTFg3gxeU4f+UWnswa6JPhzaR6+zblO5+fjltJWIZc4aUalqTclvN2QtTC37LxvZAA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.33.tgz", + "integrity": "sha512-JXMBka6lNNmqbkvcTtaX8Gu5by9547bukHQvPoLe9VRBx1gHwzf5tdt4AaezW85HAB3pikcvyqBToRTDA4DeLw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.33.tgz", + "integrity": "sha512-Bm+QulsAItD/x6Ih8wGIMfRJy4G73tu1HJsrccPW6AfqdZd0Sfm5Imhgkgq2+kly065rYMnCOxTBvmvFY1BKfg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.33.tgz", + "integrity": "sha512-FnFn+ZBgsVMbGDsTqo8zsnRzydvsGV8vfiWwUo1LD8FTmPTdV+otGSWKc4LJec0oSexFnCYVO4hX8P8qQKaSlg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.33.tgz", + "integrity": "sha512-345tsIWMzoXaQndUTDv1qypDRiebFxGYx9pYkhwY4hBRaOLt8UGfiWKr9FSSHs25dFIf8ZqIFaPdy5MljdoawA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.33.tgz", + "integrity": "sha512-nscpt0G6UCTkrT2ppnJnFsYbPDQwmum4GNXYTeoTIdsmMydSKFz9Iny2jpaRupTb+Wl298+Rh82WKzt9LCcqSQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.33.tgz", + "integrity": "sha512-pc9LpGNKhJ0dXQhZ5QMmYxtARwwmWLpeocFmVG5Z0DzWq5Uf0izcI8tLc+qOpqxO1PWqZ5A7J1blrUIKrIFc7Q==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.33.tgz", + "integrity": "sha512-nOjfZMy8B94MdisuzZo9/57xuFVLHJaDj5e/xrduJp9CV2/HrfxTRH2fbyLe+K9QT41WBLUd4iXX3R7jBp0EUg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nolyfill/is-core-module": { + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", + "dev": true, + "engines": { + "node": ">=12.4.0" + } + }, + "node_modules/@panva/hkdf": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.2.1.tgz", + "integrity": "sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@prisma/client": { + "version": "6.16.2", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.16.2.tgz", + "integrity": "sha512-E00PxBcalMfYO/TWnXobBVUai6eW/g5OsifWQsQDzJYm7yaY+IRLo7ZLsaefi0QkTpxfuhFcQ/w180i6kX3iJw==", + "hasInstallScript": true, + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "prisma": "*", + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@prisma/config": { + "version": "6.16.2", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.16.2.tgz", + "integrity": "sha512-mKXSUrcqXj0LXWPmJsK2s3p9PN+aoAbyMx7m5E1v1FufofR1ZpPoIArjjzOIm+bJRLLvYftoNYLx1tbHgF9/yg==", + "dependencies": { + "c12": "3.1.0", + "deepmerge-ts": "7.1.5", + "effect": "3.16.12", + "empathic": "2.0.0" + } + }, + "node_modules/@prisma/debug": { + "version": "6.16.2", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.16.2.tgz", + "integrity": "sha512-bo4/gA/HVV6u8YK2uY6glhNsJ7r+k/i5iQ9ny/3q5bt9ijCj7WMPUwfTKPvtEgLP+/r26Z686ly11hhcLiQ8zA==" + }, + "node_modules/@prisma/engines": { + "version": "6.16.2", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.16.2.tgz", + "integrity": "sha512-7yf3AjfPUgsg/l7JSu1iEhsmZZ/YE00yURPjTikqm2z4btM0bCl2coFtTGfeSOWbQMmq45Jab+53yGUIAT1sjA==", + "hasInstallScript": true, + "dependencies": { + "@prisma/debug": "6.16.2", + "@prisma/engines-version": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", + "@prisma/fetch-engine": "6.16.2", + "@prisma/get-platform": "6.16.2" + } + }, + "node_modules/@prisma/engines-version": { + "version": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43.tgz", + "integrity": "sha512-ThvlDaKIVrnrv97ujNFDYiQbeMQpLa0O86HFA2mNoip4mtFqM7U5GSz2ie1i2xByZtvPztJlNRgPsXGeM/kqAA==" + }, + "node_modules/@prisma/fetch-engine": { + "version": "6.16.2", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.16.2.tgz", + "integrity": "sha512-wPnZ8DMRqpgzye758ZvfAMiNJRuYpz+rhgEBZi60ZqDIgOU2694oJxiuu3GKFeYeR/hXxso4/2oBC243t/whxQ==", + "dependencies": { + "@prisma/debug": "6.16.2", + "@prisma/engines-version": "6.16.0-7.1c57fdcd7e44b29b9313256c76699e91c3ac3c43", + "@prisma/get-platform": "6.16.2" + } + }, + "node_modules/@prisma/get-platform": { + "version": "6.16.2", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.16.2.tgz", + "integrity": "sha512-U/P36Uke5wS7r1+omtAgJpEB94tlT4SdlgaeTc6HVTTT93pXj7zZ+B/cZnmnvjcNPfWddgoDx8RLjmQwqGDYyA==", + "dependencies": { + "@prisma/debug": "6.16.2" + } + }, + "node_modules/@reduxjs/toolkit": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz", + "integrity": "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@standard-schema/utils": "^0.3.0", + "immer": "^11.0.0", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", + "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } + } + }, + "node_modules/@reduxjs/toolkit/node_modules/immer": { + "version": "11.1.4", + "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.4.tgz", + "integrity": "sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.15.0.tgz", + "integrity": "sha512-ojSshQPKwVvSMR8yT2L/QtUkV5SXi/IfDiJ4/8d6UbTPjiHVmxZzUAzGD8Tzks1b9+qQkZa0isUOvYObedITaw==", + "dev": true + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==" + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==" + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" + }, + "node_modules/@swc/helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", + "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "dependencies": { + "@swc/counter": "^0.1.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==" + }, + "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==" + }, + "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==" + }, + "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==", + "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==" + }, + "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==", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", + "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==" + }, + "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==" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.19.33", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.33.tgz", + "integrity": "sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw==", + "dev": true, + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/nodemailer": { + "version": "7.0.10", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-7.0.10.tgz", + "integrity": "sha512-tP+9WggTFN22Zxh0XFyst7239H0qwiRCogsk7v9aQS79sYAJY+WEbTHbNYcxUMaalHKmsNpxmoTe35hBEMMd6g==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "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 + }, + "node_modules/@types/react": { + "version": "18.3.28", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz", + "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", + "devOptional": true, + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "dev": true, + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.0.tgz", + "integrity": "sha512-lRyPDLzNCuae71A3t9NEINBiTn7swyOhvUj3MyUOxb8x6g6vPEFoOU+ZRmGMusNC3X3YMhqMIX7i8ShqhT74Pw==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.56.0", + "@typescript-eslint/type-utils": "8.56.0", + "@typescript-eslint/utils": "8.56.0", + "@typescript-eslint/visitor-keys": "8.56.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.56.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.0.tgz", + "integrity": "sha512-IgSWvLobTDOjnaxAfDTIHaECbkNlAlKv2j5SjpB2v7QHKv1FIfjwMy8FsDbVfDX/KjmCmYICcw7uGaXLhtsLNg==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.56.0", + "@typescript-eslint/types": "8.56.0", + "@typescript-eslint/typescript-estree": "8.56.0", + "@typescript-eslint/visitor-keys": "8.56.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.0.tgz", + "integrity": "sha512-M3rnyL1vIQOMeWxTWIW096/TtVP+8W3p/XnaFflhmcFp+U4zlxUxWj4XwNs6HbDeTtN4yun0GNTTDBw/SvufKg==", + "dev": true, + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.56.0", + "@typescript-eslint/types": "^8.56.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.0.tgz", + "integrity": "sha512-7UiO/XwMHquH+ZzfVCfUNkIXlp/yQjjnlYUyYz7pfvlK3/EyyN6BK+emDmGNyQLBtLGaYrTAI6KOw8tFucWL2w==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.56.0", + "@typescript-eslint/visitor-keys": "8.56.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.0.tgz", + "integrity": "sha512-bSJoIIt4o3lKXD3xmDh9chZcjCz5Lk8xS7Rxn+6l5/pKrDpkCwtQNQQwZ2qRPk7TkUYhrq3WPIHXOXlbXP0itg==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.0.tgz", + "integrity": "sha512-qX2L3HWOU2nuDs6GzglBeuFXviDODreS58tLY/BALPC7iu3Fa+J7EOTwnX9PdNBxUI7Uh0ntP0YWGnxCkXzmfA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.56.0", + "@typescript-eslint/typescript-estree": "8.56.0", + "@typescript-eslint/utils": "8.56.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.0.tgz", + "integrity": "sha512-DBsLPs3GsWhX5HylbP9HNG15U0bnwut55Lx12bHB9MpXxQ+R5GC8MwQe+N1UFXxAeQDvEsEDY6ZYwX03K7Z6HQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.0.tgz", + "integrity": "sha512-ex1nTUMWrseMltXUHmR2GAQ4d+WjkZCT4f+4bVsps8QEdh0vlBsaCokKTPlnqBFqqGaxilDNJG7b8dolW2m43Q==", + "dev": true, + "dependencies": { + "@typescript-eslint/project-service": "8.56.0", + "@typescript-eslint/tsconfig-utils": "8.56.0", + "@typescript-eslint/types": "8.56.0", + "@typescript-eslint/visitor-keys": "8.56.0", + "debug": "^4.4.3", + "minimatch": "^9.0.5", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.0.tgz", + "integrity": "sha512-RZ3Qsmi2nFGsS+n+kjLAYDPVlrzf7UhTffrDIKr+h2yzAlYP/y5ZulU0yeDEPItos2Ph46JAL5P/On3pe7kDIQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.56.0", + "@typescript-eslint/types": "8.56.0", + "@typescript-eslint/typescript-estree": "8.56.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.0.tgz", + "integrity": "sha512-q+SL+b+05Ud6LbEE35qe4A99P+htKTKVbyiNEe45eCbJFyh/HVK9QXwlrbz+Q4L8SOW4roxSVwXYj4DMBT7Ieg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.56.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.0.tgz", + "integrity": "sha512-A0XeIi7CXU7nPlfHS9loMYEKxUaONu/hTEzHTGba9Huu94Cq1hPivf+DE5erJozZOky0LfvXAyrV/tcswpLI0Q==", + "dev": true, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/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 + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", + "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", + "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", + "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", + "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", + "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", + "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", + "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", + "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", + "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", + "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", + "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.11" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "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, + "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, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.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, + "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, + "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, + "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 + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true + }, + "node_modules/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 + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "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, + "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-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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, + "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/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true + }, + "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, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/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, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.1.tgz", + "integrity": "sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", + "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", + "dev": true, + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/bcryptjs": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.3.tgz", + "integrity": "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==", + "bin": { + "bcrypt": "bin/bcrypt" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/c12": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", + "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", + "dependencies": { + "chokidar": "^4.0.3", + "confbox": "^0.2.2", + "defu": "^6.1.4", + "dotenv": "^16.6.1", + "exsolve": "^1.0.7", + "giget": "^2.0.0", + "jiti": "^2.4.2", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^1.0.0", + "pkg-types": "^2.2.0", + "rc9": "^2.1.2" + }, + "peerDependencies": { + "magicast": "^0.3.5" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, + "node_modules/c12/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/c12/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "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, + "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, + "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, + "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, + "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, + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001770", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001770.tgz", + "integrity": "sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "dependencies": { + "consola": "^3.2.3" + } + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "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, + "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 + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/confbox": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", + "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "engines": { + "node": ">= 0.6" + } + }, + "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, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "devOptional": true + }, + "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==", + "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==", + "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==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "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==", + "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==", + "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==", + "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==", + "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==", + "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==", + "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==", + "engines": { + "node": ">=12" + } + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true + }, + "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, + "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, + "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, + "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/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "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==" + }, + "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 + }, + "node_modules/deepmerge-ts": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", + "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", + "engines": { + "node": ">=16.0.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", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "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, + "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/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==" + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==" + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "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 + }, + "node_modules/effect": { + "version": "3.16.12", + "resolved": "https://registry.npmjs.org/effect/-/effect-3.16.12.tgz", + "integrity": "sha512-N39iBk0K71F9nb442TLbTkjl24FLUzuvx2i1I2RsEAQsdAdUTuUoW0vlfUXgkMTUOnYqKnWcFfqw4hK4Pw27hg==", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "fast-check": "^3.23.1" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.286", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", + "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/empathic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", + "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/es-abstract": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", + "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==", + "dev": true, + "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, + "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, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.2.tgz", + "integrity": "sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.1", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.1.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.3.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", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.5", + "safe-array-concat": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "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-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "dependencies": { + "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, + "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/es-toolkit": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.44.0.tgz", + "integrity": "sha512-6penXeZalaV88MM3cGkFZZfOoLGWshWWfdy0tWw/RlVVyhvMaWSBTOvXNeiW3e5FwdS5ePW0LGEu17zT139ktg==" + }, + "node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "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, + "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-config-next": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.2.33.tgz", + "integrity": "sha512-e2W+waB+I5KuoALAtKZl3WVDU4Q1MS6gF/gdcwHh0WOAkHf4TZI6dPjd25wKhlZFAsFrVKy24Z7/IwOhn8dHBw==", + "dev": true, + "dependencies": { + "@next/eslint-plugin-next": "14.2.33", + "@rushstack/eslint-patch": "^1.3.3", + "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-import-resolver-typescript": "^3.5.2", + "eslint-plugin-import": "^2.28.1", + "eslint-plugin-jsx-a11y": "^6.7.1", + "eslint-plugin-react": "^7.33.2", + "eslint-plugin-react-hooks": "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705" + }, + "peerDependencies": { + "eslint": "^7.23.0 || ^8.0.0", + "typescript": ">=3.3.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-typescript": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", + "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", + "dev": true, + "dependencies": { + "@nolyfill/is-core-module": "1.0.39", + "debug": "^4.4.0", + "get-tsconfig": "^4.10.0", + "is-bun-module": "^2.0.0", + "stable-hash": "^0.0.5", + "tinyglobby": "^0.2.13", + "unrs-resolver": "^1.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-import-resolver-typescript" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*", + "eslint-plugin-import-x": "*" + }, + "peerDependenciesMeta": { + "eslint-plugin-import": { + "optional": true + }, + "eslint-plugin-import-x": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", + "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", + "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", + "dev": true, + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.1", + "hasown": "^2.0.2", + "is-core-module": "^2.16.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.1", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.9", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", + "dev": true, + "dependencies": { + "aria-query": "^5.3.2", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.1" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.9", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.0.0-canary-7118f5dd7-20230705", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0-canary-7118f5dd7-20230705.tgz", + "integrity": "sha512-AZYbMo/NW9chdL7vk6HQzQhT+PvTAEVqWk9ziruUoW2kAOcN5qNyelv70e0F1VNQAbvutOC9oc+xfWycI9FxDw==", + "dev": true, + "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/node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-plugin-react/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/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, + "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/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, + "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/node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/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, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/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, + "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/eslint/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, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "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, + "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, + "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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==" + }, + "node_modules/exsolve": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", + "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==" + }, + "node_modules/fast-check": { + "version": "3.23.2", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", + "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "dependencies": { + "pure-rand": "^6.1.0" + }, + "engines": { + "node": ">=8.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 + }, + "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 + }, + "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 + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "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, + "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, + "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, + "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, + "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 + }, + "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, + "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, + "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, + "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 + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "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" + } + }, + "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, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "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, + "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, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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, + "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/get-tsconfig": { + "version": "4.13.6", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", + "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/giget": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", + "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "defu": "^6.1.4", + "node-fetch-native": "^1.6.6", + "nypm": "^0.6.0", + "pathe": "^2.0.3" + }, + "bin": { + "giget": "dist/cli.mjs" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "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, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "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, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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, + "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==" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "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, + "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, + "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, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "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, + "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, + "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, + "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, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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, + "engines": { + "node": ">= 4" + } + }, + "node_modules/immer": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.2.0.tgz", + "integrity": "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==", + "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, + "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, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "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 + }, + "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, + "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==", + "engines": { + "node": ">=12" + } + }, + "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, + "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-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, + "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", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "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, + "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, + "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-bun-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", + "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", + "dev": true, + "dependencies": { + "semver": "^7.7.1" + } + }, + "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, + "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, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "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, + "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", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "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, + "engines": { + "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, + "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", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "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-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-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, + "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, + "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, + "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, + "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, + "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, + "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, + "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, + "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, + "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, + "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-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, + "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", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "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, + "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", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "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 + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/jose": { + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "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 + }, + "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 + }, + "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 + }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "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, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "dev": true + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dev": true, + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, + "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, + "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, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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 + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/lucide-react": { + "version": "0.564.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.564.0.tgz", + "integrity": "sha512-JJ8GVTQqFwuliifD48U6+h7DXEHdkhJ/E87kksGByII3qHxtPciVb8T8woQONHBQgHVOl7rSMrrip3SeVNy7Fg==", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.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, + "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, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/napi-postinstall": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", + "dev": true, + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, + "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 + }, + "node_modules/next": { + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.33.tgz", + "integrity": "sha512-GiKHLsD00t4ACm1p00VgrI0rUFAC9cRDGReKyERlM57aeEZkOQGcZTpIbsGn0b562FTPJWmYfKwplfO9EaT6ng==", + "deprecated": "This version has a security vulnerability. Please upgrade to a patched version. See https://nextjs.org/blog/security-update-2025-12-11 for more details.", + "dependencies": { + "@next/env": "14.2.33", + "@swc/helpers": "0.5.5", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001579", + "graceful-fs": "^4.2.11", + "postcss": "8.4.31", + "styled-jsx": "5.1.1" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=18.17.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "14.2.33", + "@next/swc-darwin-x64": "14.2.33", + "@next/swc-linux-arm64-gnu": "14.2.33", + "@next/swc-linux-arm64-musl": "14.2.33", + "@next/swc-linux-x64-gnu": "14.2.33", + "@next/swc-linux-x64-musl": "14.2.33", + "@next/swc-win32-arm64-msvc": "14.2.33", + "@next/swc-win32-ia32-msvc": "14.2.33", + "@next/swc-win32-x64-msvc": "14.2.33" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.41.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next-auth": { + "version": "4.24.13", + "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.13.tgz", + "integrity": "sha512-sgObCfcfL7BzIK76SS5TnQtc3yo2Oifp/yIpfv6fMfeBOiBJkDWF3A2y9+yqnmJ4JKc2C+nMjSjmgDeTwgN1rQ==", + "dependencies": { + "@babel/runtime": "^7.20.13", + "@panva/hkdf": "^1.0.2", + "cookie": "^0.7.0", + "jose": "^4.15.5", + "oauth": "^0.9.15", + "openid-client": "^5.4.0", + "preact": "^10.6.3", + "preact-render-to-string": "^5.1.19", + "uuid": "^8.3.2" + }, + "peerDependencies": { + "@auth/core": "0.34.3", + "next": "^12.2.5 || ^13 || ^14 || ^15 || ^16", + "nodemailer": "^7.0.7", + "react": "^17.0.2 || ^18 || ^19", + "react-dom": "^17.0.2 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "@auth/core": { + "optional": true + }, + "nodemailer": { + "optional": true + } + } + }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==" + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true + }, + "node_modules/nodemailer": { + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.13.tgz", + "integrity": "sha512-PNDFSJdP+KFgdsG3ZzMXCgquO7I6McjY2vlqILjtJd0hy8wEvtugS9xKRF2NWlPNGxvLCXlTNIae4serI7dinw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nypm": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.5.tgz", + "integrity": "sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ==", + "dependencies": { + "citty": "^0.2.0", + "pathe": "^2.0.3", + "tinyexec": "^1.0.2" + }, + "bin": { + "nypm": "dist/cli.mjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/nypm/node_modules/citty": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.2.1.tgz", + "integrity": "sha512-kEV95lFBhQgtogAPlQfJJ0WGVSokvLr/UEoFPiKKOXF7pl98HfUVUD0ejsuTCld/9xH9vogSywZ5KqHzXrZpqg==" + }, + "node_modules/oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/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, + "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, + "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, + "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/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==" + }, + "node_modules/oidc-token-hash": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.2.0.tgz", + "integrity": "sha512-6gj2m8cJZ+iSW8bm0FXdGF0YhIQbKrfP4yWTNzxc31U6MOjfEmB1rHvlYvxI1B7t7BCi1F2vYTT6YhtQRG4hxw==", + "engines": { + "node": "^10.13.0 || >=12.0.0" + } + }, + "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, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/openid-client": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.7.1.tgz", + "integrity": "sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==", + "dependencies": { + "jose": "^4.15.9", + "lru-cache": "^6.0.0", + "object-hash": "^2.2.0", + "oidc-token-hash": "^5.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/openid-client/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/openid-client/node_modules/object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "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/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, + "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", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "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, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "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, + "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, + "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, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==" + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-types": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", + "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, + "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, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.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, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/preact": { + "version": "10.28.3", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.28.3.tgz", + "integrity": "sha512-tCmoRkPQLpBeWzpmbhryairGnhW9tKV6c6gr/w+RhoRoKEJwsjzipwp//1oCpGPOchvSLaAPlpcJi9MwMmoPyA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/preact-render-to-string": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz", + "integrity": "sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==", + "dependencies": { + "pretty-format": "^3.8.0" + }, + "peerDependencies": { + "preact": ">=10" + } + }, + "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, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-format": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz", + "integrity": "sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==" + }, + "node_modules/prisma": { + "version": "6.16.2", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.16.2.tgz", + "integrity": "sha512-aRvldGE5UUJTtVmFiH3WfNFNiqFlAtePUxcI0UEGlnXCX7DqhiMT5TRYwncHFeA/Reca5W6ToXXyCMTeFPdSXA==", + "hasInstallScript": true, + "dependencies": { + "@prisma/config": "6.16.2", + "@prisma/engines": "6.16.2" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "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==", + "dev": true, + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "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, + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/rc9": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", + "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "dependencies": { + "defu": "^6.1.4", + "destr": "^2.0.3" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-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/react-redux": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", + "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", + "dependencies": { + "@types/use-sync-external-store": "^0.0.6", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "@types/react": "^18.2.25 || ^19", + "react": "^18.0 || ^19", + "redux": "^5.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/recharts": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.7.0.tgz", + "integrity": "sha512-l2VCsy3XXeraxIID9fx23eCb6iCBsxUQDnE8tWm6DFdszVAO7WVY/ChAD9wVit01y6B2PMupYiMmQwhgPHc9Ew==", + "dependencies": { + "@reduxjs/toolkit": "1.x.x || 2.x.x", + "clsx": "^2.1.1", + "decimal.js-light": "^2.5.1", + "es-toolkit": "^1.39.3", + "eventemitter3": "^5.0.1", + "immer": "^10.1.1", + "react-redux": "8.x.x || 9.x.x", + "reselect": "5.1.1", + "tiny-invariant": "^1.3.3", + "use-sync-external-store": "^1.2.2", + "victory-vendor": "^37.0.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==" + }, + "node_modules/redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "peerDependencies": { + "redux": "^5.0.0" + } + }, + "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, + "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/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, + "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": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==" + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "dependencies": { + "is-core-module": "^2.16.1", + "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, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "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, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/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, + "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-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, + "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", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "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==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "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, + "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, + "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/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, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "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, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "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, + "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, + "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, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stable-hash": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", + "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", + "dev": true + }, + "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, + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/string.prototype.includes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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, + "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.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "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, + "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, + "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, + "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/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "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, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "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, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "dev": true, + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/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, + "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/tailwindcss/node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tailwindcss/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, + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/tailwindcss/node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/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 + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/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==" + }, + "node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", + "dev": true, + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.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==" + }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "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, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "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, + "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, + "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, + "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.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "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, + "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": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true + }, + "node_modules/unrs-resolver": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", + "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "napi-postinstall": "^0.3.0" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.11.1", + "@unrs/resolver-binding-android-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-x64": "1.11.1", + "@unrs/resolver-binding-freebsd-x64": "1.11.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-musl": "1.11.1", + "@unrs/resolver-binding-wasm32-wasi": "1.11.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.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, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "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 + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/victory-vendor": { + "version": "37.3.6", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz", + "integrity": "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==", + "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/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/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, + "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-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, + "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", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "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.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", + "dev": true, + "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/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "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, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "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 + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yaml": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "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, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zustand": { + "version": "5.0.11", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.11.tgz", + "integrity": "sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg==", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..6297d7b --- /dev/null +++ b/package.json @@ -0,0 +1,47 @@ +{ + "name": "casa-benell", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint", + "test": "node --import tsx --test src/lib/**/*.test.ts", + "prisma:generate": "prisma generate", + "prisma:migrate": "prisma migrate dev", + "prisma:deploy": "prisma migrate deploy", + "prisma:seed": "prisma db seed" + }, + "prisma": { + "seed": "tsx prisma/seed.ts" + }, + "dependencies": { + "@next-auth/prisma-adapter": "^1.0.7", + "@prisma/client": "^6.16.2", + "bcryptjs": "^3.0.3", + "clsx": "^2.1.1", + "lucide-react": "^0.564.0", + "next": "14.2.33", + "next-auth": "^4.24.13", + "nodemailer": "^7.0.13", + "prisma": "^6.16.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "recharts": "^3.7.0", + "zustand": "^5.0.11" + }, + "devDependencies": { + "@types/node": "^20", + "@types/nodemailer": "^7.0.10", + "@types/react": "^18", + "@types/react-dom": "^18", + "autoprefixer": "^10.4.20", + "eslint": "^8.57.1", + "eslint-config-next": "14.2.33", + "postcss": "^8.4.49", + "tailwindcss": "^3.4.17", + "tsx": "^4.21.0", + "typescript": "^5" + } +} diff --git a/pending/nextsteps.md b/pending/nextsteps.md new file mode 100644 index 0000000..4905e5f --- /dev/null +++ b/pending/nextsteps.md @@ -0,0 +1,84 @@ +# V1.1 Handoff: Done vs Pending + +Date: 2026-03-19 + +## Completed in this pass + +### Owner KPI (scan-first) +- [x] Snapshot freshness now updates when KPI rows are edited (`patchWeeklyKpiRow` touches parent snapshot `updatedAt`). +- [x] Owner board metadata now shows `source` together with `lastUpdatedAt` and coverage. +- [x] Drill-down `
` sections are no longer forced closed on rerender. + +### Human Capital +- [x] Removed write side-effect from `GET /api/human-capital/dashboard` (read no longer auto-publishes KPIs). +- [x] `people` endpoint now requires owner/HC leader (`canManageHumanCapital`) for employee-level visibility. +- [x] `lifecycle-events` GET now requires owner/HC leader. +- [x] `updates` GET now hides non-published items from non-managers. +- [x] CSV export fixed to real newlines (`\n`) instead of escaped `\\n` text. +- [x] People endpoint now calls `ensureEmployeeProfiles()` to reduce first-load/race gaps. +- [x] HC compliance input excludes auto-published HC KPI section (`human-capital-auto-v1`) to prevent KPI self-feedback. +- [x] Churn trend denominator now uses month-specific estimated headcount rather than current headcount for all months. + +### Operations / Maintenance +- [x] Removed write side-effect from `GET /api/operations/dashboard` (read no longer auto-publishes KPIs). +- [x] Added owner approval action endpoint: `PATCH /api/operations/approvals/[id]`. +- [x] Added policy endpoint: `GET/PATCH /api/operations/policy`. +- [x] Work-order read/list is department-scoped. +- [x] Template read/list is department-scoped. +- [x] Reminder read/list is department-scoped. +- [x] Asset update/delete now validates department ownership before mutating. +- [x] Work-order patch now validates department scope and blocks non-owner state transitions when owner approval is pending. +- [x] Production-plan patch now validates department scope and enforces owner-only transitions for `approved/released`. +- [x] Production-plan create enforces owner-only for privileged states and sets approval metadata accordingly. +- [x] Maintenance policy bootstrap now backfills missing owner approver when possible. +- [x] Work-order create now validates asset/template department ownership and resolves owner approver fallback. +- [x] Operations dashboard payload now includes `workOrderId` / `productionPlanId` in approval inbox entries. +- [x] Operations UI owner inbox now has Approve/Reject actions wired to approval API. +- [x] Operations UI now shows down-assets in summary and an upcoming maintenance list. + +### Marketing +- [x] Meta connection/disconnect now avoids inactive-row unique collisions by clearing stale inactive rows before state flips. +- [x] Meta sync now attempts both Facebook and Instagram ingestion (best-effort Instagram discovery/fetch). +- [x] Social snapshot writes now dedupe same `department+channel+range+capturedAt` before create. +- [x] SocialPanel no longer computes local fallback growth math from snapshots; it displays server insights only. +- [x] Initiative drawer now includes attribution fields in UI: channel, page ID, campaign tag, date window. +- [x] Initiative drawer includes "Use auto values" action and displays last auto-fill summary. +- [x] Marketing scoring and initiative mapping now consume auto-attributed actuals when manual values are still zero. +- [x] Sync auto-fill now respects attribution page/date windows; campaign-tagged records are intentionally skipped until campaign-level data is ingested. + +### Database / schema +- [x] Prisma migration created: `prisma/migrations/20260319180300_0012_v11_scan_first_owner_department_dashboards/migration.sql`. + +## Validation status +- [x] `npm test` passes (19/19). +- [x] `npm run build` passes. +- [ ] Recharts static-generation warnings remain (`width(-1)/height(-1)`), non-blocking but should be cleaned later. + +## Remaining / Follow-up items + +### High-priority follow-up +- [ ] Add campaign-level attribution ingestion for Meta sync (currently campaign-tagged initiatives are skipped to avoid bad attribution). +- [ ] Add explicit CRUD coverage for operations entities still missing full lifecycle endpoints (e.g., delete/update routes for every list entity as originally envisioned). +- [ ] Add automated API tests for new authorization hardening and owner-approval transition guards. + +### Medium-priority follow-up +- [ ] Add UI controls for operations policy thresholds (`costThreshold`, `downtimeThresholdHours`, approver owner). +- [ ] Improve Instagram metrics quality (current implementation is best-effort and may use limited fields depending on account permissions). +- [ ] Normalize/chunk dashboard chart containers to remove Recharts build-time width warnings. + +### Optional / V1.2 candidates +- [ ] Improve attribution model to support campaign/source dimensions end-to-end in stored metrics. +- [ ] Add audit trail events for owner approval actions (who/when/why) in UI timeline. +- [ ] Add integration tests for Meta sync -> initiative auto-fill -> publish -> owner KPI chain. + +## Quick resume commands + +```bash +cd /home/mdares03/benell +npm test +npm run build +``` + +## Notes for next person +- Projects module was intentionally left unchanged per V1.1 scope. +- Department dashboards now feed owner KPI via explicit publish endpoints (read endpoints no longer mutate KPI data). diff --git a/postcss.config.mjs b/postcss.config.mjs new file mode 100644 index 0000000..a982c64 --- /dev/null +++ b/postcss.config.mjs @@ -0,0 +1,8 @@ +const config = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; + +export default config; diff --git a/prisma/migrations/0001_init/migration.sql b/prisma/migrations/0001_init/migration.sql new file mode 100644 index 0000000..3e288de --- /dev/null +++ b/prisma/migrations/0001_init/migration.sql @@ -0,0 +1,135 @@ +-- CreateSchema +CREATE SCHEMA IF NOT EXISTS "public"; + +-- CreateEnum +CREATE TYPE "public"."RoleKey" AS ENUM ('owner', 'leader', 'employee'); + +-- CreateTable +CREATE TABLE "public"."User" ( + "id" TEXT NOT NULL, + "name" TEXT, + "email" TEXT NOT NULL, + "emailVerified" TIMESTAMP(3), + "image" TEXT, + "passwordHash" TEXT, + "status" TEXT NOT NULL DEFAULT 'active', + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."Role" ( + "id" TEXT NOT NULL, + "key" "public"."RoleKey" NOT NULL, + "name" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Role_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."UserRole" ( + "userId" TEXT NOT NULL, + "roleId" TEXT NOT NULL, + "assignedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "UserRole_pkey" PRIMARY KEY ("userId","roleId") +); + +-- CreateTable +CREATE TABLE "public"."Invitation" ( + "id" TEXT NOT NULL, + "email" TEXT NOT NULL, + "roleKey" "public"."RoleKey" NOT NULL, + "tokenHash" TEXT NOT NULL, + "expiresAt" TIMESTAMP(3) NOT NULL, + "acceptedAt" TIMESTAMP(3), + "invitedById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "Invitation_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."Account" ( + "id" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "type" TEXT NOT NULL, + "provider" TEXT NOT NULL, + "providerAccountId" TEXT NOT NULL, + "refresh_token" TEXT, + "access_token" TEXT, + "expires_at" INTEGER, + "token_type" TEXT, + "scope" TEXT, + "id_token" TEXT, + "session_state" TEXT, + + CONSTRAINT "Account_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."Session" ( + "id" TEXT NOT NULL, + "sessionToken" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "expires" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Session_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."VerificationToken" ( + "identifier" TEXT NOT NULL, + "token" TEXT NOT NULL, + "expires" TIMESTAMP(3) NOT NULL +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_email_key" ON "public"."User"("email"); + +-- CreateIndex +CREATE UNIQUE INDEX "Role_key_key" ON "public"."Role"("key"); + +-- CreateIndex +CREATE UNIQUE INDEX "Invitation_tokenHash_key" ON "public"."Invitation"("tokenHash"); + +-- CreateIndex +CREATE INDEX "Invitation_email_expiresAt_idx" ON "public"."Invitation"("email", "expiresAt"); + +-- CreateIndex +CREATE INDEX "Account_userId_idx" ON "public"."Account"("userId"); + +-- CreateIndex +CREATE UNIQUE INDEX "Account_provider_providerAccountId_key" ON "public"."Account"("provider", "providerAccountId"); + +-- CreateIndex +CREATE UNIQUE INDEX "Session_sessionToken_key" ON "public"."Session"("sessionToken"); + +-- CreateIndex +CREATE INDEX "Session_userId_idx" ON "public"."Session"("userId"); + +-- CreateIndex +CREATE UNIQUE INDEX "VerificationToken_token_key" ON "public"."VerificationToken"("token"); + +-- CreateIndex +CREATE UNIQUE INDEX "VerificationToken_identifier_token_key" ON "public"."VerificationToken"("identifier", "token"); + +-- AddForeignKey +ALTER TABLE "public"."UserRole" ADD CONSTRAINT "UserRole_roleId_fkey" FOREIGN KEY ("roleId") REFERENCES "public"."Role"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."UserRole" ADD CONSTRAINT "UserRole_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."Invitation" ADD CONSTRAINT "Invitation_invitedById_fkey" FOREIGN KEY ("invitedById") REFERENCES "public"."User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."Account" ADD CONSTRAINT "Account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."Session" ADD CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + diff --git a/prisma/migrations/0002_invitation_department_metadata/migration.sql b/prisma/migrations/0002_invitation_department_metadata/migration.sql new file mode 100644 index 0000000..2e52bc6 --- /dev/null +++ b/prisma/migrations/0002_invitation_department_metadata/migration.sql @@ -0,0 +1,19 @@ +-- CreateEnum +CREATE TYPE "public"."DepartmentKey" AS ENUM ('marketing', 'administracion', 'capital_humano', 'operaciones'); + +-- AlterTable +ALTER TABLE "public"."User" +ADD COLUMN "department" "public"."DepartmentKey", +ADD COLUMN "departmentRole" TEXT; + +-- AlterTable +ALTER TABLE "public"."Invitation" +ADD COLUMN "inviteeName" TEXT NOT NULL DEFAULT 'Usuario invitado', +ADD COLUMN "department" "public"."DepartmentKey" NOT NULL DEFAULT 'marketing', +ADD COLUMN "departmentRole" TEXT NOT NULL DEFAULT 'Miembro'; + +-- RemoveDefaults +ALTER TABLE "public"."Invitation" +ALTER COLUMN "inviteeName" DROP DEFAULT, +ALTER COLUMN "department" DROP DEFAULT, +ALTER COLUMN "departmentRole" DROP DEFAULT; diff --git a/prisma/migrations/0003_marketing_meetings_persistence/migration.sql b/prisma/migrations/0003_marketing_meetings_persistence/migration.sql new file mode 100644 index 0000000..994c916 --- /dev/null +++ b/prisma/migrations/0003_marketing_meetings_persistence/migration.sql @@ -0,0 +1,48 @@ +-- CreateEnum +CREATE TYPE "public"."MarketingMeetingStatus" AS ENUM ('requested', 'scheduled', 'completed', 'cancelled'); + +-- CreateTable +CREATE TABLE "public"."MarketingMeeting" ( + "id" TEXT NOT NULL, + "department" "public"."DepartmentKey" NOT NULL, + "title" TEXT NOT NULL, + "agenda" TEXT NOT NULL, + "status" "public"."MarketingMeetingStatus" NOT NULL DEFAULT 'requested', + "requestedById" TEXT, + "requestedByName" TEXT NOT NULL, + "participantNames" JSONB NOT NULL, + "suggestedTimes" JSONB NOT NULL, + "scheduledFor" TIMESTAMP(3), + "completedAt" TIMESTAMP(3), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "MarketingMeeting_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."MarketingCommitment" ( + "id" TEXT NOT NULL, + "meetingId" TEXT NOT NULL, + "title" TEXT NOT NULL, + "description" TEXT, + "ownerName" TEXT, + "dueDate" TIMESTAMP(3), + "status" TEXT NOT NULL DEFAULT 'pendiente', + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "MarketingCommitment_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE INDEX "MarketingMeeting_department_status_scheduledFor_idx" ON "public"."MarketingMeeting"("department", "status", "scheduledFor"); + +-- CreateIndex +CREATE INDEX "MarketingCommitment_meetingId_dueDate_idx" ON "public"."MarketingCommitment"("meetingId", "dueDate"); + +-- AddForeignKey +ALTER TABLE "public"."MarketingMeeting" ADD CONSTRAINT "MarketingMeeting_requestedById_fkey" FOREIGN KEY ("requestedById") REFERENCES "public"."User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."MarketingCommitment" ADD CONSTRAINT "MarketingCommitment_meetingId_fkey" FOREIGN KEY ("meetingId") REFERENCES "public"."MarketingMeeting"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/0004_password_reset_tokens/migration.sql b/prisma/migrations/0004_password_reset_tokens/migration.sql new file mode 100644 index 0000000..a75d6e0 --- /dev/null +++ b/prisma/migrations/0004_password_reset_tokens/migration.sql @@ -0,0 +1,17 @@ +-- CreateTable +CREATE TABLE "public"."PasswordResetToken" ( + "id" TEXT NOT NULL, + "email" TEXT NOT NULL, + "tokenHash" TEXT NOT NULL, + "expiresAt" TIMESTAMP(3) NOT NULL, + "usedAt" TIMESTAMP(3), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "PasswordResetToken_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "PasswordResetToken_tokenHash_key" ON "public"."PasswordResetToken"("tokenHash"); + +-- CreateIndex +CREATE INDEX "PasswordResetToken_email_expiresAt_idx" ON "public"."PasswordResetToken"("email", "expiresAt"); diff --git a/prisma/migrations/0005_marketing_functional_foundation/migration.sql b/prisma/migrations/0005_marketing_functional_foundation/migration.sql new file mode 100644 index 0000000..cf6cc1c --- /dev/null +++ b/prisma/migrations/0005_marketing_functional_foundation/migration.sql @@ -0,0 +1,216 @@ +-- CreateEnum +CREATE TYPE "public"."MarketingInitiativeType" AS ENUM ('evento', 'campania', 'cambio', 'implementacion', 'otro'); + +-- CreateEnum +CREATE TYPE "public"."MarketingInitiativeStatus" AS ENUM ('planning', 'in_progress', 'completion', 'results', 'evaluation'); + +-- CreateEnum +CREATE TYPE "public"."MarketingTaskStatus" AS ENUM ('todo', 'in_progress', 'blocked', 'done'); + +-- CreateEnum +CREATE TYPE "public"."MarketingPublicOpinion" AS ENUM ('positive', 'mixed', 'negative'); + +-- CreateTable +CREATE TABLE "public"."MarketingInitiative" ( + "id" TEXT NOT NULL, + "department" "public"."DepartmentKey" NOT NULL, + "name" TEXT NOT NULL, + "type" "public"."MarketingInitiativeType" NOT NULL, + "status" "public"."MarketingInitiativeStatus" NOT NULL DEFAULT 'planning', + "ownerId" TEXT, + "dueDate" TIMESTAMP(3) NOT NULL, + "completedAt" TIMESTAMP(3), + "importanceWeight" DOUBLE PRECISION NOT NULL DEFAULT 1, + "leadRating1to5" INTEGER NOT NULL DEFAULT 3, + "target" DOUBLE PRECISION NOT NULL DEFAULT 0, + "actual" DOUBLE PRECISION NOT NULL DEFAULT 0, + "ticketsTarget" INTEGER NOT NULL DEFAULT 0, + "ticketsActual" INTEGER NOT NULL DEFAULT 0, + "revenueTarget" DOUBLE PRECISION NOT NULL DEFAULT 0, + "revenueActual" DOUBLE PRECISION NOT NULL DEFAULT 0, + "opinionPublica" "public"."MarketingPublicOpinion" NOT NULL DEFAULT 'mixed', + "queFunciono" TEXT NOT NULL DEFAULT '', + "queNo" TEXT NOT NULL DEFAULT '', + "proximoIntento" TEXT NOT NULL DEFAULT '', + "updatedById" TEXT, + "updatedByName" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "MarketingInitiative_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."MarketingInitiativeContributor" ( + "initiativeId" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "MarketingInitiativeContributor_pkey" PRIMARY KEY ("initiativeId","userId") +); + +-- CreateTable +CREATE TABLE "public"."MarketingInitiativeLocation" ( + "id" TEXT NOT NULL, + "initiativeId" TEXT NOT NULL, + "locationId" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "MarketingInitiativeLocation_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."MarketingInitiativeEvidence" ( + "id" TEXT NOT NULL, + "initiativeId" TEXT NOT NULL, + "url" TEXT NOT NULL, + "createdById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "MarketingInitiativeEvidence_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."MarketingTask" ( + "id" TEXT NOT NULL, + "initiativeId" TEXT NOT NULL, + "title" TEXT NOT NULL, + "description" TEXT NOT NULL DEFAULT '', + "assigneeId" TEXT, + "status" "public"."MarketingTaskStatus" NOT NULL DEFAULT 'todo', + "dueDate" TIMESTAMP(3) NOT NULL, + "updatedAt" TIMESTAMP(3) NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "MarketingTask_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."MarketingTaskEvidence" ( + "id" TEXT NOT NULL, + "taskId" TEXT NOT NULL, + "url" TEXT NOT NULL, + "createdById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "MarketingTaskEvidence_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."MarketingInitiativeEdit" ( + "id" TEXT NOT NULL, + "initiativeId" TEXT NOT NULL, + "editedById" TEXT, + "editedByName" TEXT NOT NULL, + "summary" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "MarketingInitiativeEdit_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."MarketingSocialSnapshot" ( + "id" TEXT NOT NULL, + "department" "public"."DepartmentKey" NOT NULL, + "channel" TEXT NOT NULL, + "range" TEXT NOT NULL, + "followersStart" INTEGER NOT NULL, + "followersEnd" INTEGER NOT NULL, + "engagementRate" DOUBLE PRECISION NOT NULL, + "reach" INTEGER NOT NULL, + "impressions" INTEGER NOT NULL, + "capturedAt" TIMESTAMP(3) NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "MarketingSocialSnapshot_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."MarketingBrandPulse" ( + "id" TEXT NOT NULL, + "department" "public"."DepartmentKey" NOT NULL, + "month" TEXT NOT NULL, + "rating1to5" INTEGER NOT NULL, + "notes" TEXT NOT NULL DEFAULT '', + "updatedById" TEXT, + "updatedByName" TEXT, + "updatedAt" TIMESTAMP(3) NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "MarketingBrandPulse_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE INDEX "MarketingInitiative_department_status_dueDate_idx" ON "public"."MarketingInitiative"("department", "status", "dueDate"); + +-- CreateIndex +CREATE INDEX "MarketingInitiativeContributor_userId_idx" ON "public"."MarketingInitiativeContributor"("userId"); + +-- CreateIndex +CREATE UNIQUE INDEX "MarketingInitiativeLocation_initiativeId_locationId_key" ON "public"."MarketingInitiativeLocation"("initiativeId", "locationId"); + +-- CreateIndex +CREATE INDEX "MarketingInitiativeLocation_locationId_idx" ON "public"."MarketingInitiativeLocation"("locationId"); + +-- CreateIndex +CREATE INDEX "MarketingInitiativeEvidence_initiativeId_createdAt_idx" ON "public"."MarketingInitiativeEvidence"("initiativeId", "createdAt"); + +-- CreateIndex +CREATE INDEX "MarketingTask_initiativeId_status_dueDate_idx" ON "public"."MarketingTask"("initiativeId", "status", "dueDate"); + +-- CreateIndex +CREATE INDEX "MarketingTask_assigneeId_idx" ON "public"."MarketingTask"("assigneeId"); + +-- CreateIndex +CREATE INDEX "MarketingTaskEvidence_taskId_createdAt_idx" ON "public"."MarketingTaskEvidence"("taskId", "createdAt"); + +-- CreateIndex +CREATE INDEX "MarketingInitiativeEdit_initiativeId_createdAt_idx" ON "public"."MarketingInitiativeEdit"("initiativeId", "createdAt"); + +-- CreateIndex +CREATE INDEX "MarketingSocialSnapshot_department_range_channel_capturedAt_idx" ON "public"."MarketingSocialSnapshot"("department", "range", "channel", "capturedAt"); + +-- CreateIndex +CREATE INDEX "MarketingBrandPulse_department_month_idx" ON "public"."MarketingBrandPulse"("department", "month"); + +-- AddForeignKey +ALTER TABLE "public"."MarketingInitiative" ADD CONSTRAINT "MarketingInitiative_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "public"."User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."MarketingInitiative" ADD CONSTRAINT "MarketingInitiative_updatedById_fkey" FOREIGN KEY ("updatedById") REFERENCES "public"."User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."MarketingInitiativeContributor" ADD CONSTRAINT "MarketingInitiativeContributor_initiativeId_fkey" FOREIGN KEY ("initiativeId") REFERENCES "public"."MarketingInitiative"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."MarketingInitiativeContributor" ADD CONSTRAINT "MarketingInitiativeContributor_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."MarketingInitiativeLocation" ADD CONSTRAINT "MarketingInitiativeLocation_initiativeId_fkey" FOREIGN KEY ("initiativeId") REFERENCES "public"."MarketingInitiative"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."MarketingInitiativeEvidence" ADD CONSTRAINT "MarketingInitiativeEvidence_initiativeId_fkey" FOREIGN KEY ("initiativeId") REFERENCES "public"."MarketingInitiative"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."MarketingInitiativeEvidence" ADD CONSTRAINT "MarketingInitiativeEvidence_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "public"."User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."MarketingTask" ADD CONSTRAINT "MarketingTask_initiativeId_fkey" FOREIGN KEY ("initiativeId") REFERENCES "public"."MarketingInitiative"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."MarketingTask" ADD CONSTRAINT "MarketingTask_assigneeId_fkey" FOREIGN KEY ("assigneeId") REFERENCES "public"."User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."MarketingTaskEvidence" ADD CONSTRAINT "MarketingTaskEvidence_taskId_fkey" FOREIGN KEY ("taskId") REFERENCES "public"."MarketingTask"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."MarketingTaskEvidence" ADD CONSTRAINT "MarketingTaskEvidence_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "public"."User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."MarketingInitiativeEdit" ADD CONSTRAINT "MarketingInitiativeEdit_initiativeId_fkey" FOREIGN KEY ("initiativeId") REFERENCES "public"."MarketingInitiative"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."MarketingInitiativeEdit" ADD CONSTRAINT "MarketingInitiativeEdit_editedById_fkey" FOREIGN KEY ("editedById") REFERENCES "public"."User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."MarketingBrandPulse" ADD CONSTRAINT "MarketingBrandPulse_updatedById_fkey" FOREIGN KEY ("updatedById") REFERENCES "public"."User"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/migrations/0006_add_projects_department/migration.sql b/prisma/migrations/0006_add_projects_department/migration.sql new file mode 100644 index 0000000..6eedd4e --- /dev/null +++ b/prisma/migrations/0006_add_projects_department/migration.sql @@ -0,0 +1,12 @@ +DO $$ +BEGIN + IF NOT EXISTS ( + SELECT 1 + FROM pg_type t + JOIN pg_enum e ON t.oid = e.enumtypid + WHERE t.typname = 'DepartmentKey' + AND e.enumlabel = 'proyectos' + ) THEN + ALTER TYPE "public"."DepartmentKey" ADD VALUE 'proyectos'; + END IF; +END $$; diff --git a/prisma/migrations/0006_marketing_milestones/migration.sql b/prisma/migrations/0006_marketing_milestones/migration.sql new file mode 100644 index 0000000..1d9f31b --- /dev/null +++ b/prisma/migrations/0006_marketing_milestones/migration.sql @@ -0,0 +1,49 @@ +-- CreateEnum +CREATE TYPE "public"."MarketingMilestoneStatus" AS ENUM ('pending', 'in_progress', 'completed'); + +-- CreateTable +CREATE TABLE "public"."MarketingMilestone" ( + "id" TEXT NOT NULL, + "initiativeId" TEXT NOT NULL, + "title" TEXT NOT NULL, + "description" TEXT NOT NULL DEFAULT '', + "dueDate" TIMESTAMP(3) NOT NULL, + "status" "public"."MarketingMilestoneStatus" NOT NULL DEFAULT 'pending', + "sortOrder" INTEGER NOT NULL DEFAULT 0, + "createdById" TEXT, + "createdByName" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "MarketingMilestone_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."MarketingMilestoneCheckpoint" ( + "id" TEXT NOT NULL, + "milestoneId" TEXT NOT NULL, + "note" TEXT NOT NULL, + "createdById" TEXT, + "createdByName" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "MarketingMilestoneCheckpoint_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE INDEX "MarketingMilestone_initiativeId_dueDate_status_idx" ON "public"."MarketingMilestone"("initiativeId", "dueDate", "status"); + +-- CreateIndex +CREATE INDEX "MarketingMilestoneCheckpoint_milestoneId_createdAt_idx" ON "public"."MarketingMilestoneCheckpoint"("milestoneId", "createdAt"); + +-- AddForeignKey +ALTER TABLE "public"."MarketingMilestone" ADD CONSTRAINT "MarketingMilestone_initiativeId_fkey" FOREIGN KEY ("initiativeId") REFERENCES "public"."MarketingInitiative"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."MarketingMilestone" ADD CONSTRAINT "MarketingMilestone_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "public"."User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."MarketingMilestoneCheckpoint" ADD CONSTRAINT "MarketingMilestoneCheckpoint_milestoneId_fkey" FOREIGN KEY ("milestoneId") REFERENCES "public"."MarketingMilestone"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."MarketingMilestoneCheckpoint" ADD CONSTRAINT "MarketingMilestoneCheckpoint_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "public"."User"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/migrations/0007_marketing_initiative_global_scope/migration.sql b/prisma/migrations/0007_marketing_initiative_global_scope/migration.sql new file mode 100644 index 0000000..069ed3a --- /dev/null +++ b/prisma/migrations/0007_marketing_initiative_global_scope/migration.sql @@ -0,0 +1,2 @@ +ALTER TABLE "public"."MarketingInitiative" +ADD COLUMN "isGlobal" BOOLEAN NOT NULL DEFAULT false; diff --git a/prisma/migrations/0008_marketing_initiative_metric_selection/migration.sql b/prisma/migrations/0008_marketing_initiative_metric_selection/migration.sql new file mode 100644 index 0000000..65d48f9 --- /dev/null +++ b/prisma/migrations/0008_marketing_initiative_metric_selection/migration.sql @@ -0,0 +1,4 @@ +ALTER TABLE "public"."MarketingInitiative" +ADD COLUMN "trackScore" BOOLEAN NOT NULL DEFAULT true, +ADD COLUMN "trackTickets" BOOLEAN NOT NULL DEFAULT true, +ADD COLUMN "trackRevenue" BOOLEAN NOT NULL DEFAULT true; diff --git a/prisma/migrations/0009_projects_financials_v2/migration.sql b/prisma/migrations/0009_projects_financials_v2/migration.sql new file mode 100644 index 0000000..f2fee4e --- /dev/null +++ b/prisma/migrations/0009_projects_financials_v2/migration.sql @@ -0,0 +1,6 @@ +ALTER TABLE "MarketingInitiative" + ADD COLUMN IF NOT EXISTS "plannedCost" DOUBLE PRECISION NOT NULL DEFAULT 0, + ADD COLUMN IF NOT EXISTS "actualCost" DOUBLE PRECISION NOT NULL DEFAULT 0, + ADD COLUMN IF NOT EXISTS "targetTicketPrice" DOUBLE PRECISION NOT NULL DEFAULT 0, + ADD COLUMN IF NOT EXISTS "actualTicketPrice" DOUBLE PRECISION NOT NULL DEFAULT 0, + ADD COLUMN IF NOT EXISTS "projectsSchemaVersion" INTEGER NOT NULL DEFAULT 1; diff --git a/prisma/migrations/0010_projects_navigation_v12/migration.sql b/prisma/migrations/0010_projects_navigation_v12/migration.sql new file mode 100644 index 0000000..b7fb4bf --- /dev/null +++ b/prisma/migrations/0010_projects_navigation_v12/migration.sql @@ -0,0 +1,88 @@ +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'MeetingResponseStatus') THEN + CREATE TYPE "public"."MeetingResponseStatus" AS ENUM ('pending', 'accepted', 'declined'); + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'ProjectCalendarVisibility') THEN + CREATE TYPE "public"."ProjectCalendarVisibility" AS ENUM ('personal', 'team'); + END IF; +END $$; + +DO $$ +BEGIN + IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'ProjectCaptureEvidenceKind') THEN + CREATE TYPE "public"."ProjectCaptureEvidenceKind" AS ENUM ('photo', 'document', 'link'); + END IF; +END $$; + +CREATE TABLE IF NOT EXISTS "public"."MarketingMeetingParticipant" ( + "id" TEXT NOT NULL, + "meetingId" TEXT NOT NULL, + "userId" TEXT, + "displayName" TEXT NOT NULL, + "responseStatus" "public"."MeetingResponseStatus" NOT NULL DEFAULT 'pending', + "respondedAt" TIMESTAMP(3), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "MarketingMeetingParticipant_pkey" PRIMARY KEY ("id"), + CONSTRAINT "MarketingMeetingParticipant_meetingId_fkey" FOREIGN KEY ("meetingId") REFERENCES "public"."MarketingMeeting"("id") ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT "MarketingMeetingParticipant_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE SET NULL ON UPDATE CASCADE +); + +CREATE TABLE IF NOT EXISTS "public"."ProjectCalendarEvent" ( + "id" TEXT NOT NULL, + "department" "public"."DepartmentKey" NOT NULL DEFAULT 'proyectos', + "ownerUserId" TEXT NOT NULL, + "meetingId" TEXT, + "title" TEXT NOT NULL, + "notes" TEXT, + "startAt" TIMESTAMP(3) NOT NULL, + "endAt" TIMESTAMP(3) NOT NULL, + "visibility" "public"."ProjectCalendarVisibility" NOT NULL DEFAULT 'personal', + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "ProjectCalendarEvent_pkey" PRIMARY KEY ("id"), + CONSTRAINT "ProjectCalendarEvent_ownerUserId_fkey" FOREIGN KEY ("ownerUserId") REFERENCES "public"."User"("id") ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT "ProjectCalendarEvent_meetingId_fkey" FOREIGN KEY ("meetingId") REFERENCES "public"."MarketingMeeting"("id") ON DELETE SET NULL ON UPDATE CASCADE +); + +CREATE TABLE IF NOT EXISTS "public"."ProjectCaptureEvidence" ( + "id" TEXT NOT NULL, + "department" "public"."DepartmentKey" NOT NULL DEFAULT 'proyectos', + "initiativeId" TEXT, + "taskId" TEXT, + "uploadedById" TEXT NOT NULL, + "kind" "public"."ProjectCaptureEvidenceKind" NOT NULL, + "title" TEXT NOT NULL, + "note" TEXT, + "url" TEXT, + "storagePath" TEXT, + "mimeType" TEXT, + "sizeBytes" INTEGER, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "ProjectCaptureEvidence_pkey" PRIMARY KEY ("id"), + CONSTRAINT "ProjectCaptureEvidence_initiativeId_fkey" FOREIGN KEY ("initiativeId") REFERENCES "public"."MarketingInitiative"("id") ON DELETE SET NULL ON UPDATE CASCADE, + CONSTRAINT "ProjectCaptureEvidence_taskId_fkey" FOREIGN KEY ("taskId") REFERENCES "public"."MarketingTask"("id") ON DELETE SET NULL ON UPDATE CASCADE, + CONSTRAINT "ProjectCaptureEvidence_uploadedById_fkey" FOREIGN KEY ("uploadedById") REFERENCES "public"."User"("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +CREATE UNIQUE INDEX IF NOT EXISTS "MarketingMeetingParticipant_meetingId_userId_key" ON "public"."MarketingMeetingParticipant"("meetingId", "userId"); +CREATE INDEX IF NOT EXISTS "MarketingMeetingParticipant_meetingId_responseStatus_idx" ON "public"."MarketingMeetingParticipant"("meetingId", "responseStatus"); +CREATE INDEX IF NOT EXISTS "MarketingMeetingParticipant_userId_idx" ON "public"."MarketingMeetingParticipant"("userId"); + +CREATE UNIQUE INDEX IF NOT EXISTS "ProjectCalendarEvent_meetingId_key" ON "public"."ProjectCalendarEvent"("meetingId"); +CREATE INDEX IF NOT EXISTS "ProjectCalendarEvent_department_startAt_endAt_idx" ON "public"."ProjectCalendarEvent"("department", "startAt", "endAt"); +CREATE INDEX IF NOT EXISTS "ProjectCalendarEvent_ownerUserId_startAt_idx" ON "public"."ProjectCalendarEvent"("ownerUserId", "startAt"); + +CREATE INDEX IF NOT EXISTS "ProjectCaptureEvidence_department_createdAt_idx" ON "public"."ProjectCaptureEvidence"("department", "createdAt"); +CREATE INDEX IF NOT EXISTS "ProjectCaptureEvidence_initiativeId_createdAt_idx" ON "public"."ProjectCaptureEvidence"("initiativeId", "createdAt"); +CREATE INDEX IF NOT EXISTS "ProjectCaptureEvidence_taskId_createdAt_idx" ON "public"."ProjectCaptureEvidence"("taskId", "createdAt"); +CREATE INDEX IF NOT EXISTS "ProjectCaptureEvidence_uploadedById_createdAt_idx" ON "public"."ProjectCaptureEvidence"("uploadedById", "createdAt"); diff --git a/prisma/migrations/0011_weekly_kpi_board_v1/migration.sql b/prisma/migrations/0011_weekly_kpi_board_v1/migration.sql new file mode 100644 index 0000000..8336dfc --- /dev/null +++ b/prisma/migrations/0011_weekly_kpi_board_v1/migration.sql @@ -0,0 +1,60 @@ +CREATE TYPE "public"."WeeklyKpiStatus" AS ENUM ('on_track', 'watch', 'risk', 'no_score'); + +CREATE TABLE "public"."WeeklyKpiSnapshot" ( + "id" TEXT NOT NULL, + "weekStart" TIMESTAMP(3) NOT NULL, + "weekEnd" TIMESTAMP(3) NOT NULL, + "source" TEXT NOT NULL DEFAULT 'platform', + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "WeeklyKpiSnapshot_pkey" PRIMARY KEY ("id") +); + +CREATE TABLE "public"."WeeklyKpiSection" ( + "id" TEXT NOT NULL, + "snapshotId" TEXT NOT NULL, + "sectionKey" TEXT NOT NULL, + "rawSectionLabel" TEXT NOT NULL, + "mappedDepartment" "public"."DepartmentKey", + "ownerTeamLabel" TEXT, + "sortOrder" INTEGER NOT NULL DEFAULT 0, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "WeeklyKpiSection_pkey" PRIMARY KEY ("id") +); + +CREATE TABLE "public"."WeeklyKpiRow" ( + "id" TEXT NOT NULL, + "sectionId" TEXT NOT NULL, + "rowKey" TEXT NOT NULL, + "responsibilityText" TEXT NOT NULL, + "objectiveIndicatorText" TEXT, + "quantityQualityText" TEXT, + "complianceText" TEXT, + "dueCommitmentText" TEXT, + "targetValue" DOUBLE PRECISION, + "quantityValue" DOUBLE PRECISION, + "compliancePct" DOUBLE PRECISION, + "dueDate" TIMESTAMP(3), + "status" "public"."WeeklyKpiStatus" NOT NULL DEFAULT 'no_score', + "sortOrder" INTEGER NOT NULL DEFAULT 0, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + CONSTRAINT "WeeklyKpiRow_pkey" PRIMARY KEY ("id") +); + +CREATE UNIQUE INDEX "WeeklyKpiSnapshot_weekStart_key" ON "public"."WeeklyKpiSnapshot"("weekStart"); +CREATE INDEX "WeeklyKpiSnapshot_weekStart_weekEnd_idx" ON "public"."WeeklyKpiSnapshot"("weekStart", "weekEnd"); +CREATE UNIQUE INDEX "WeeklyKpiSection_snapshotId_sectionKey_key" ON "public"."WeeklyKpiSection"("snapshotId", "sectionKey"); +CREATE INDEX "WeeklyKpiSection_snapshotId_mappedDepartment_sortOrder_idx" ON "public"."WeeklyKpiSection"("snapshotId", "mappedDepartment", "sortOrder"); +CREATE UNIQUE INDEX "WeeklyKpiRow_sectionId_rowKey_key" ON "public"."WeeklyKpiRow"("sectionId", "rowKey"); +CREATE INDEX "WeeklyKpiRow_sectionId_sortOrder_idx" ON "public"."WeeklyKpiRow"("sectionId", "sortOrder"); +CREATE INDEX "WeeklyKpiRow_status_dueDate_idx" ON "public"."WeeklyKpiRow"("status", "dueDate"); + +ALTER TABLE "public"."WeeklyKpiSection" +ADD CONSTRAINT "WeeklyKpiSection_snapshotId_fkey" +FOREIGN KEY ("snapshotId") REFERENCES "public"."WeeklyKpiSnapshot"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +ALTER TABLE "public"."WeeklyKpiRow" +ADD CONSTRAINT "WeeklyKpiRow_sectionId_fkey" +FOREIGN KEY ("sectionId") REFERENCES "public"."WeeklyKpiSection"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20260319180300_0012_v11_scan_first_owner_department_dashboards/migration.sql b/prisma/migrations/20260319180300_0012_v11_scan_first_owner_department_dashboards/migration.sql new file mode 100644 index 0000000..2afbd4d --- /dev/null +++ b/prisma/migrations/20260319180300_0012_v11_scan_first_owner_department_dashboards/migration.sql @@ -0,0 +1,411 @@ +-- CreateEnum +CREATE TYPE "public"."MarketingSyncRunStatus" AS ENUM ('queued', 'running', 'success', 'failed'); + +-- CreateEnum +CREATE TYPE "public"."EmploymentStatus" AS ENUM ('active', 'leave', 'terminated'); + +-- CreateEnum +CREATE TYPE "public"."EmployeeLifecycleEventType" AS ENUM ('hire', 'transfer', 'leave', 'termination', 'rehire'); + +-- CreateEnum +CREATE TYPE "public"."HrUpdateSeverity" AS ENUM ('info', 'warning', 'critical'); + +-- CreateEnum +CREATE TYPE "public"."FactoryAssetState" AS ENUM ('draft', 'active', 'down', 'retired'); + +-- CreateEnum +CREATE TYPE "public"."PmTemplateState" AS ENUM ('draft', 'active', 'archived'); + +-- CreateEnum +CREATE TYPE "public"."PmWorkOrderState" AS ENUM ('draft', 'scheduled', 'in_progress', 'completed', 'verified', 'overdue', 'cancelled'); + +-- CreateEnum +CREATE TYPE "public"."ReminderEventState" AS ENUM ('queued', 'sent', 'acknowledged', 'escalated', 'failed'); + +-- CreateEnum +CREATE TYPE "public"."ApprovalState" AS ENUM ('draft', 'submitted', 'pending_owner', 'approved', 'rejected', 'changes_requested', 'cancelled'); + +-- CreateEnum +CREATE TYPE "public"."SalesForecastState" AS ENUM ('draft', 'published', 'superseded'); + +-- CreateEnum +CREATE TYPE "public"."ProductionPlanState" AS ENUM ('draft', 'simulated', 'locked', 'approved', 'released', 'replanned'); + +-- AlterTable +ALTER TABLE "public"."MarketingInitiative" ADD COLUMN "attributionCampaign" TEXT, +ADD COLUMN "attributionChannel" TEXT, +ADD COLUMN "attributionEnd" TIMESTAMP(3), +ADD COLUMN "attributionPageId" TEXT, +ADD COLUMN "attributionStart" TIMESTAMP(3), +ADD COLUMN "autoActual" DOUBLE PRECISION, +ADD COLUMN "autoRevenueActual" DOUBLE PRECISION, +ADD COLUMN "autoTicketsActual" INTEGER, +ADD COLUMN "autoUpdatedAt" TIMESTAMP(3); + +-- CreateTable +CREATE TABLE "public"."MarketingMetaConnection" ( + "id" TEXT NOT NULL, + "department" "public"."DepartmentKey" NOT NULL DEFAULT 'marketing', + "accountId" TEXT NOT NULL, + "pageId" TEXT NOT NULL, + "pageName" TEXT NOT NULL, + "pageAccessToken" TEXT NOT NULL, + "tokenExpiresAt" TIMESTAMP(3), + "connectedById" TEXT, + "isActive" BOOLEAN NOT NULL DEFAULT true, + "lastSyncedAt" TIMESTAMP(3), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "MarketingMetaConnection_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."MarketingSocialMetricDaily" ( + "id" TEXT NOT NULL, + "department" "public"."DepartmentKey" NOT NULL DEFAULT 'marketing', + "connectionId" TEXT NOT NULL, + "channel" TEXT NOT NULL, + "metricDate" TIMESTAMP(3) NOT NULL, + "followers" INTEGER NOT NULL, + "reach" INTEGER NOT NULL, + "impressions" INTEGER NOT NULL, + "engagements" INTEGER NOT NULL, + "engagementRate" DOUBLE PRECISION NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "MarketingSocialMetricDaily_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."MarketingSyncRun" ( + "id" TEXT NOT NULL, + "department" "public"."DepartmentKey" NOT NULL DEFAULT 'marketing', + "connectionId" TEXT, + "status" "public"."MarketingSyncRunStatus" NOT NULL DEFAULT 'queued', + "message" TEXT, + "rowsIngested" INTEGER NOT NULL DEFAULT 0, + "triggeredById" TEXT, + "startedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "completedAt" TIMESTAMP(3), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "MarketingSyncRun_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."EmployeeProfile" ( + "id" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "employeeCode" TEXT, + "hireDate" TIMESTAMP(3), + "employmentType" TEXT, + "managerUserId" TEXT, + "locationId" TEXT, + "fte" DOUBLE PRECISION NOT NULL DEFAULT 1, + "employmentStatus" "public"."EmploymentStatus" NOT NULL DEFAULT 'active', + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "EmployeeProfile_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."EmployeeLifecycleEvent" ( + "id" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "eventType" "public"."EmployeeLifecycleEventType" NOT NULL, + "effectiveAt" TIMESTAMP(3) NOT NULL, + "reason" TEXT, + "isVoluntary" BOOLEAN, + "metadata" JSONB, + "createdById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "EmployeeLifecycleEvent_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."HrMetricSnapshot" ( + "id" TEXT NOT NULL, + "department" "public"."DepartmentKey", + "snapshotDate" TIMESTAMP(3) NOT NULL, + "headcount" INTEGER NOT NULL, + "hires" INTEGER NOT NULL, + "exits" INTEGER NOT NULL, + "churnPct" DOUBLE PRECISION NOT NULL, + "medianTenureMonths" DOUBLE PRECISION NOT NULL, + "peopleHealthScore" DOUBLE PRECISION NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "HrMetricSnapshot_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."HrUpdate" ( + "id" TEXT NOT NULL, + "title" TEXT NOT NULL, + "body" TEXT NOT NULL, + "severity" "public"."HrUpdateSeverity" NOT NULL DEFAULT 'info', + "audience" TEXT NOT NULL DEFAULT 'hc_leadership', + "status" TEXT NOT NULL DEFAULT 'draft', + "publishedAt" TIMESTAMP(3), + "authorId" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "HrUpdate_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."FactoryAsset" ( + "id" TEXT NOT NULL, + "department" "public"."DepartmentKey" NOT NULL DEFAULT 'operaciones', + "assetCode" TEXT NOT NULL, + "name" TEXT NOT NULL, + "category" TEXT NOT NULL, + "locationId" TEXT, + "criticality" INTEGER NOT NULL DEFAULT 3, + "state" "public"."FactoryAssetState" NOT NULL DEFAULT 'active', + "serviceStrategy" TEXT, + "lastMaintenanceAt" TIMESTAMP(3), + "nextMaintenanceAt" TIMESTAMP(3), + "createdById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "FactoryAsset_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."PmTemplate" ( + "id" TEXT NOT NULL, + "assetId" TEXT NOT NULL, + "title" TEXT NOT NULL, + "description" TEXT, + "cadenceDays" INTEGER, + "cadenceHours" INTEGER, + "state" "public"."PmTemplateState" NOT NULL DEFAULT 'active', + "createdById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "PmTemplate_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."PmWorkOrder" ( + "id" TEXT NOT NULL, + "assetId" TEXT NOT NULL, + "templateId" TEXT, + "title" TEXT NOT NULL, + "description" TEXT, + "scheduledFor" TIMESTAMP(3) NOT NULL, + "dueBy" TIMESTAMP(3) NOT NULL, + "startedAt" TIMESTAMP(3), + "completedAt" TIMESTAMP(3), + "estimatedCost" DOUBLE PRECISION NOT NULL DEFAULT 0, + "expectedDowntimeHours" DOUBLE PRECISION NOT NULL DEFAULT 0, + "actualCost" DOUBLE PRECISION, + "actualDowntimeHours" DOUBLE PRECISION, + "state" "public"."PmWorkOrderState" NOT NULL DEFAULT 'scheduled', + "requestedById" TEXT, + "approvedById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "PmWorkOrder_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."ReminderEvent" ( + "id" TEXT NOT NULL, + "workOrderId" TEXT NOT NULL, + "remindAt" TIMESTAMP(3) NOT NULL, + "channel" TEXT NOT NULL DEFAULT 'in_app', + "message" TEXT, + "state" "public"."ReminderEventState" NOT NULL DEFAULT 'queued', + "acknowledgedAt" TIMESTAMP(3), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "ReminderEvent_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."MaintenanceApprovalPolicy" ( + "id" TEXT NOT NULL, + "department" "public"."DepartmentKey" NOT NULL DEFAULT 'operaciones', + "costThreshold" DOUBLE PRECISION NOT NULL DEFAULT 5000, + "downtimeThresholdHours" DOUBLE PRECISION NOT NULL DEFAULT 4, + "ownerUserId" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "MaintenanceApprovalPolicy_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."SalesForecast" ( + "id" TEXT NOT NULL, + "department" "public"."DepartmentKey" NOT NULL DEFAULT 'operaciones', + "weekStart" TIMESTAMP(3) NOT NULL, + "weekEnd" TIMESTAMP(3) NOT NULL, + "locationId" TEXT, + "sku" TEXT, + "forecastUnits" DOUBLE PRECISION NOT NULL, + "multiplier" DOUBLE PRECISION NOT NULL DEFAULT 1.2, + "state" "public"."SalesForecastState" NOT NULL DEFAULT 'draft', + "createdById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "SalesForecast_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."ProductionPlan" ( + "id" TEXT NOT NULL, + "department" "public"."DepartmentKey" NOT NULL DEFAULT 'operaciones', + "weekStart" TIMESTAMP(3) NOT NULL, + "weekEnd" TIMESTAMP(3) NOT NULL, + "lineName" TEXT NOT NULL, + "plannedUnits" DOUBLE PRECISION NOT NULL, + "forecastUnits" DOUBLE PRECISION NOT NULL, + "capacityUnits" DOUBLE PRECISION NOT NULL, + "varianceUnits" DOUBLE PRECISION NOT NULL, + "state" "public"."ProductionPlanState" NOT NULL DEFAULT 'draft', + "createdById" TEXT, + "approvedById" TEXT, + "approvedAt" TIMESTAMP(3), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "ProductionPlan_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."ApprovalRequest" ( + "id" TEXT NOT NULL, + "department" "public"."DepartmentKey" NOT NULL DEFAULT 'operaciones', + "requestType" TEXT NOT NULL, + "workOrderId" TEXT, + "productionPlanId" TEXT, + "submittedById" TEXT, + "approverId" TEXT, + "state" "public"."ApprovalState" NOT NULL DEFAULT 'submitted', + "reason" TEXT, + "decidedAt" TIMESTAMP(3), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "ApprovalRequest_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE INDEX "MarketingMetaConnection_department_isActive_idx" ON "public"."MarketingMetaConnection"("department", "isActive"); + +-- CreateIndex +CREATE UNIQUE INDEX "MarketingMetaConnection_department_pageId_isActive_key" ON "public"."MarketingMetaConnection"("department", "pageId", "isActive"); + +-- CreateIndex +CREATE INDEX "MarketingSocialMetricDaily_department_channel_metricDate_idx" ON "public"."MarketingSocialMetricDaily"("department", "channel", "metricDate"); + +-- CreateIndex +CREATE UNIQUE INDEX "MarketingSocialMetricDaily_connectionId_channel_metricDate_key" ON "public"."MarketingSocialMetricDaily"("connectionId", "channel", "metricDate"); + +-- CreateIndex +CREATE INDEX "MarketingSyncRun_department_createdAt_idx" ON "public"."MarketingSyncRun"("department", "createdAt"); + +-- CreateIndex +CREATE INDEX "MarketingSyncRun_status_createdAt_idx" ON "public"."MarketingSyncRun"("status", "createdAt"); + +-- CreateIndex +CREATE UNIQUE INDEX "EmployeeProfile_userId_key" ON "public"."EmployeeProfile"("userId"); + +-- CreateIndex +CREATE INDEX "EmployeeProfile_employmentStatus_hireDate_idx" ON "public"."EmployeeProfile"("employmentStatus", "hireDate"); + +-- CreateIndex +CREATE INDEX "EmployeeLifecycleEvent_userId_effectiveAt_idx" ON "public"."EmployeeLifecycleEvent"("userId", "effectiveAt"); + +-- CreateIndex +CREATE INDEX "EmployeeLifecycleEvent_eventType_effectiveAt_idx" ON "public"."EmployeeLifecycleEvent"("eventType", "effectiveAt"); + +-- CreateIndex +CREATE INDEX "HrMetricSnapshot_snapshotDate_department_idx" ON "public"."HrMetricSnapshot"("snapshotDate", "department"); + +-- CreateIndex +CREATE INDEX "HrUpdate_status_publishedAt_idx" ON "public"."HrUpdate"("status", "publishedAt"); + +-- CreateIndex +CREATE UNIQUE INDEX "FactoryAsset_assetCode_key" ON "public"."FactoryAsset"("assetCode"); + +-- CreateIndex +CREATE INDEX "FactoryAsset_department_state_idx" ON "public"."FactoryAsset"("department", "state"); + +-- CreateIndex +CREATE INDEX "FactoryAsset_nextMaintenanceAt_state_idx" ON "public"."FactoryAsset"("nextMaintenanceAt", "state"); + +-- CreateIndex +CREATE INDEX "PmTemplate_assetId_state_idx" ON "public"."PmTemplate"("assetId", "state"); + +-- CreateIndex +CREATE INDEX "PmWorkOrder_assetId_state_dueBy_idx" ON "public"."PmWorkOrder"("assetId", "state", "dueBy"); + +-- CreateIndex +CREATE INDEX "PmWorkOrder_state_scheduledFor_idx" ON "public"."PmWorkOrder"("state", "scheduledFor"); + +-- CreateIndex +CREATE INDEX "ReminderEvent_workOrderId_state_idx" ON "public"."ReminderEvent"("workOrderId", "state"); + +-- CreateIndex +CREATE INDEX "ReminderEvent_remindAt_state_idx" ON "public"."ReminderEvent"("remindAt", "state"); + +-- CreateIndex +CREATE UNIQUE INDEX "MaintenanceApprovalPolicy_department_key" ON "public"."MaintenanceApprovalPolicy"("department"); + +-- CreateIndex +CREATE INDEX "SalesForecast_department_weekStart_state_idx" ON "public"."SalesForecast"("department", "weekStart", "state"); + +-- CreateIndex +CREATE INDEX "ProductionPlan_department_weekStart_state_idx" ON "public"."ProductionPlan"("department", "weekStart", "state"); + +-- CreateIndex +CREATE INDEX "ApprovalRequest_department_state_createdAt_idx" ON "public"."ApprovalRequest"("department", "state", "createdAt"); + +-- AddForeignKey +ALTER TABLE "public"."MarketingSocialMetricDaily" ADD CONSTRAINT "MarketingSocialMetricDaily_connectionId_fkey" FOREIGN KEY ("connectionId") REFERENCES "public"."MarketingMetaConnection"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."MarketingSyncRun" ADD CONSTRAINT "MarketingSyncRun_connectionId_fkey" FOREIGN KEY ("connectionId") REFERENCES "public"."MarketingMetaConnection"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."EmployeeProfile" ADD CONSTRAINT "EmployeeProfile_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."EmployeeLifecycleEvent" ADD CONSTRAINT "EmployeeLifecycleEvent_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."EmployeeLifecycleEvent" ADD CONSTRAINT "EmployeeLifecycleEvent_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "public"."User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."HrUpdate" ADD CONSTRAINT "HrUpdate_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "public"."User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."PmTemplate" ADD CONSTRAINT "PmTemplate_assetId_fkey" FOREIGN KEY ("assetId") REFERENCES "public"."FactoryAsset"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."PmWorkOrder" ADD CONSTRAINT "PmWorkOrder_assetId_fkey" FOREIGN KEY ("assetId") REFERENCES "public"."FactoryAsset"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."PmWorkOrder" ADD CONSTRAINT "PmWorkOrder_templateId_fkey" FOREIGN KEY ("templateId") REFERENCES "public"."PmTemplate"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."ReminderEvent" ADD CONSTRAINT "ReminderEvent_workOrderId_fkey" FOREIGN KEY ("workOrderId") REFERENCES "public"."PmWorkOrder"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."ApprovalRequest" ADD CONSTRAINT "ApprovalRequest_workOrderId_fkey" FOREIGN KEY ("workOrderId") REFERENCES "public"."PmWorkOrder"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."ApprovalRequest" ADD CONSTRAINT "ApprovalRequest_productionPlanId_fkey" FOREIGN KEY ("productionPlanId") REFERENCES "public"."ProductionPlan"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/migrations/20260322183442_experienciometro/migration.sql b/prisma/migrations/20260322183442_experienciometro/migration.sql new file mode 100644 index 0000000..2e4afa9 --- /dev/null +++ b/prisma/migrations/20260322183442_experienciometro/migration.sql @@ -0,0 +1,327 @@ +-- CreateEnum +CREATE TYPE "public"."ExperienceTemplateState" AS ENUM ('draft', 'published', 'archived'); + +-- CreateEnum +CREATE TYPE "public"."ExperienceFindingPriority" AS ENUM ('low', 'medium', 'high', 'critical'); + +-- CreateEnum +CREATE TYPE "public"."ExperienceFindingStatus" AS ENUM ('open', 'in_progress', 'resolved', 'closed'); + +-- CreateEnum +CREATE TYPE "public"."ExperienceScoreAggregationMode" AS ENUM ('weighted_recent', 'moving_average', 'full_average'); + +-- CreateEnum +CREATE TYPE "public"."ExperienceTrendDirection" AS ENUM ('up', 'down', 'flat'); + +-- CreateEnum +CREATE TYPE "public"."ExperienceSignalStatus" AS ENUM ('green', 'yellow', 'red'); + +-- CreateEnum +CREATE TYPE "public"."ExperienceEvidenceKind" AS ENUM ('photo', 'document', 'link'); + +-- CreateTable +CREATE TABLE "public"."Location" ( + "id" TEXT NOT NULL, + "code" TEXT NOT NULL, + "name" TEXT NOT NULL, + "city" TEXT, + "managerUserId" TEXT, + "isActive" BOOLEAN NOT NULL DEFAULT true, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Location_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."ExperienceTemplate" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "version" INTEGER NOT NULL, + "state" "public"."ExperienceTemplateState" NOT NULL DEFAULT 'draft', + "isDefault" BOOLEAN NOT NULL DEFAULT false, + "createdById" TEXT, + "publishedAt" TIMESTAMP(3), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "ExperienceTemplate_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."ExperienceTemplateCategory" ( + "id" TEXT NOT NULL, + "templateId" TEXT NOT NULL, + "key" TEXT NOT NULL, + "name" TEXT NOT NULL, + "weight" DOUBLE PRECISION NOT NULL DEFAULT 1, + "sortOrder" INTEGER NOT NULL DEFAULT 0, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "ExperienceTemplateCategory_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."ExperienceTemplateItem" ( + "id" TEXT NOT NULL, + "categoryId" TEXT NOT NULL, + "key" TEXT NOT NULL, + "label" TEXT NOT NULL, + "weight" DOUBLE PRECISION NOT NULL DEFAULT 1, + "sortOrder" INTEGER NOT NULL DEFAULT 0, + "allowsComment" BOOLEAN NOT NULL DEFAULT true, + "requiresObservation" BOOLEAN NOT NULL DEFAULT false, + "allowsEvidence" BOOLEAN NOT NULL DEFAULT true, + "isActive" BOOLEAN NOT NULL DEFAULT true, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "ExperienceTemplateItem_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."ExperienceEvaluation" ( + "id" TEXT NOT NULL, + "locationId" TEXT NOT NULL, + "templateId" TEXT NOT NULL, + "createdById" TEXT, + "evaluatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "generalObservations" TEXT NOT NULL DEFAULT '', + "strengths" TEXT NOT NULL DEFAULT '', + "improvementAreas" TEXT NOT NULL DEFAULT '', + "totalScore" DOUBLE PRECISION NOT NULL, + "signal" "public"."ExperienceSignalStatus" NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "ExperienceEvaluation_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."ExperienceEvaluationResponse" ( + "id" TEXT NOT NULL, + "evaluationId" TEXT NOT NULL, + "categoryId" TEXT NOT NULL, + "itemId" TEXT NOT NULL, + "score" INTEGER NOT NULL, + "scorePct" DOUBLE PRECISION NOT NULL, + "comment" TEXT, + "observation" TEXT, + "hasObservation" BOOLEAN NOT NULL DEFAULT false, + "categoryLabelSnapshot" TEXT NOT NULL, + "itemLabelSnapshot" TEXT NOT NULL, + "categoryWeightSnapshot" DOUBLE PRECISION NOT NULL, + "itemWeightSnapshot" DOUBLE PRECISION NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "ExperienceEvaluationResponse_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."ExperienceFinding" ( + "id" TEXT NOT NULL, + "locationId" TEXT NOT NULL, + "evaluationId" TEXT, + "createdById" TEXT, + "responsibleUserId" TEXT, + "title" TEXT NOT NULL, + "description" TEXT NOT NULL DEFAULT '', + "categoryKey" TEXT, + "categoryLabel" TEXT, + "priority" "public"."ExperienceFindingPriority" NOT NULL DEFAULT 'medium', + "status" "public"."ExperienceFindingStatus" NOT NULL DEFAULT 'open', + "dueDate" TIMESTAMP(3), + "resolvedAt" TIMESTAMP(3), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "ExperienceFinding_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."ExperienceEvidence" ( + "id" TEXT NOT NULL, + "locationId" TEXT NOT NULL, + "evaluationId" TEXT, + "findingId" TEXT, + "responseId" TEXT, + "uploadedById" TEXT, + "kind" "public"."ExperienceEvidenceKind" NOT NULL, + "title" TEXT NOT NULL, + "note" TEXT, + "url" TEXT, + "storagePath" TEXT, + "mimeType" TEXT, + "sizeBytes" INTEGER, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "ExperienceEvidence_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."ExperienceLocationMetric" ( + "id" TEXT NOT NULL, + "locationId" TEXT NOT NULL, + "currentScore" DOUBLE PRECISION NOT NULL DEFAULT 0, + "previousScore" DOUBLE PRECISION NOT NULL DEFAULT 0, + "trendDelta" DOUBLE PRECISION NOT NULL DEFAULT 0, + "trendDirection" "public"."ExperienceTrendDirection" NOT NULL DEFAULT 'flat', + "signal" "public"."ExperienceSignalStatus" NOT NULL DEFAULT 'yellow', + "totalEvaluations" INTEGER NOT NULL DEFAULT 0, + "openFindings" INTEGER NOT NULL DEFAULT 0, + "lastEvaluationAt" TIMESTAMP(3), + "lastEvaluationId" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "ExperienceLocationMetric_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."ExperienceScoringPolicy" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "isActive" BOOLEAN NOT NULL DEFAULT true, + "aggregationMode" "public"."ExperienceScoreAggregationMode" NOT NULL DEFAULT 'weighted_recent', + "recentWindow" INTEGER NOT NULL DEFAULT 3, + "recentWeightsCsv" TEXT NOT NULL DEFAULT '0.5,0.3,0.2', + "greenThreshold" DOUBLE PRECISION NOT NULL DEFAULT 85, + "yellowThreshold" DOUBLE PRECISION NOT NULL DEFAULT 70, + "createdById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "ExperienceScoringPolicy_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "Location_code_key" ON "public"."Location"("code"); + +-- CreateIndex +CREATE INDEX "Location_isActive_name_idx" ON "public"."Location"("isActive", "name"); + +-- CreateIndex +CREATE INDEX "ExperienceTemplate_state_updatedAt_idx" ON "public"."ExperienceTemplate"("state", "updatedAt"); + +-- CreateIndex +CREATE UNIQUE INDEX "ExperienceTemplate_name_version_key" ON "public"."ExperienceTemplate"("name", "version"); + +-- CreateIndex +CREATE INDEX "ExperienceTemplateCategory_templateId_sortOrder_idx" ON "public"."ExperienceTemplateCategory"("templateId", "sortOrder"); + +-- CreateIndex +CREATE UNIQUE INDEX "ExperienceTemplateCategory_templateId_key_key" ON "public"."ExperienceTemplateCategory"("templateId", "key"); + +-- CreateIndex +CREATE INDEX "ExperienceTemplateItem_categoryId_sortOrder_idx" ON "public"."ExperienceTemplateItem"("categoryId", "sortOrder"); + +-- CreateIndex +CREATE UNIQUE INDEX "ExperienceTemplateItem_categoryId_key_key" ON "public"."ExperienceTemplateItem"("categoryId", "key"); + +-- CreateIndex +CREATE INDEX "ExperienceEvaluation_locationId_evaluatedAt_idx" ON "public"."ExperienceEvaluation"("locationId", "evaluatedAt"); + +-- CreateIndex +CREATE INDEX "ExperienceEvaluation_templateId_evaluatedAt_idx" ON "public"."ExperienceEvaluation"("templateId", "evaluatedAt"); + +-- CreateIndex +CREATE INDEX "ExperienceEvaluationResponse_evaluationId_categoryId_idx" ON "public"."ExperienceEvaluationResponse"("evaluationId", "categoryId"); + +-- CreateIndex +CREATE UNIQUE INDEX "ExperienceEvaluationResponse_evaluationId_itemId_key" ON "public"."ExperienceEvaluationResponse"("evaluationId", "itemId"); + +-- CreateIndex +CREATE INDEX "ExperienceFinding_locationId_status_createdAt_idx" ON "public"."ExperienceFinding"("locationId", "status", "createdAt"); + +-- CreateIndex +CREATE INDEX "ExperienceFinding_evaluationId_idx" ON "public"."ExperienceFinding"("evaluationId"); + +-- CreateIndex +CREATE INDEX "ExperienceFinding_responsibleUserId_status_idx" ON "public"."ExperienceFinding"("responsibleUserId", "status"); + +-- CreateIndex +CREATE INDEX "ExperienceEvidence_locationId_createdAt_idx" ON "public"."ExperienceEvidence"("locationId", "createdAt"); + +-- CreateIndex +CREATE INDEX "ExperienceEvidence_evaluationId_createdAt_idx" ON "public"."ExperienceEvidence"("evaluationId", "createdAt"); + +-- CreateIndex +CREATE INDEX "ExperienceEvidence_findingId_createdAt_idx" ON "public"."ExperienceEvidence"("findingId", "createdAt"); + +-- CreateIndex +CREATE UNIQUE INDEX "ExperienceLocationMetric_locationId_key" ON "public"."ExperienceLocationMetric"("locationId"); + +-- CreateIndex +CREATE INDEX "ExperienceLocationMetric_signal_currentScore_idx" ON "public"."ExperienceLocationMetric"("signal", "currentScore"); + +-- CreateIndex +CREATE INDEX "ExperienceScoringPolicy_isActive_updatedAt_idx" ON "public"."ExperienceScoringPolicy"("isActive", "updatedAt"); + +-- AddForeignKey +ALTER TABLE "public"."Location" ADD CONSTRAINT "Location_managerUserId_fkey" FOREIGN KEY ("managerUserId") REFERENCES "public"."User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."ExperienceTemplate" ADD CONSTRAINT "ExperienceTemplate_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "public"."User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."ExperienceTemplateCategory" ADD CONSTRAINT "ExperienceTemplateCategory_templateId_fkey" FOREIGN KEY ("templateId") REFERENCES "public"."ExperienceTemplate"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."ExperienceTemplateItem" ADD CONSTRAINT "ExperienceTemplateItem_categoryId_fkey" FOREIGN KEY ("categoryId") REFERENCES "public"."ExperienceTemplateCategory"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."ExperienceEvaluation" ADD CONSTRAINT "ExperienceEvaluation_locationId_fkey" FOREIGN KEY ("locationId") REFERENCES "public"."Location"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."ExperienceEvaluation" ADD CONSTRAINT "ExperienceEvaluation_templateId_fkey" FOREIGN KEY ("templateId") REFERENCES "public"."ExperienceTemplate"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."ExperienceEvaluation" ADD CONSTRAINT "ExperienceEvaluation_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "public"."User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."ExperienceEvaluationResponse" ADD CONSTRAINT "ExperienceEvaluationResponse_evaluationId_fkey" FOREIGN KEY ("evaluationId") REFERENCES "public"."ExperienceEvaluation"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."ExperienceEvaluationResponse" ADD CONSTRAINT "ExperienceEvaluationResponse_categoryId_fkey" FOREIGN KEY ("categoryId") REFERENCES "public"."ExperienceTemplateCategory"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."ExperienceEvaluationResponse" ADD CONSTRAINT "ExperienceEvaluationResponse_itemId_fkey" FOREIGN KEY ("itemId") REFERENCES "public"."ExperienceTemplateItem"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."ExperienceFinding" ADD CONSTRAINT "ExperienceFinding_locationId_fkey" FOREIGN KEY ("locationId") REFERENCES "public"."Location"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."ExperienceFinding" ADD CONSTRAINT "ExperienceFinding_evaluationId_fkey" FOREIGN KEY ("evaluationId") REFERENCES "public"."ExperienceEvaluation"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."ExperienceFinding" ADD CONSTRAINT "ExperienceFinding_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "public"."User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."ExperienceFinding" ADD CONSTRAINT "ExperienceFinding_responsibleUserId_fkey" FOREIGN KEY ("responsibleUserId") REFERENCES "public"."User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."ExperienceEvidence" ADD CONSTRAINT "ExperienceEvidence_locationId_fkey" FOREIGN KEY ("locationId") REFERENCES "public"."Location"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."ExperienceEvidence" ADD CONSTRAINT "ExperienceEvidence_evaluationId_fkey" FOREIGN KEY ("evaluationId") REFERENCES "public"."ExperienceEvaluation"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."ExperienceEvidence" ADD CONSTRAINT "ExperienceEvidence_findingId_fkey" FOREIGN KEY ("findingId") REFERENCES "public"."ExperienceFinding"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."ExperienceEvidence" ADD CONSTRAINT "ExperienceEvidence_responseId_fkey" FOREIGN KEY ("responseId") REFERENCES "public"."ExperienceEvaluationResponse"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."ExperienceEvidence" ADD CONSTRAINT "ExperienceEvidence_uploadedById_fkey" FOREIGN KEY ("uploadedById") REFERENCES "public"."User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."ExperienceLocationMetric" ADD CONSTRAINT "ExperienceLocationMetric_locationId_fkey" FOREIGN KEY ("locationId") REFERENCES "public"."Location"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."ExperienceLocationMetric" ADD CONSTRAINT "ExperienceLocationMetric_lastEvaluationId_fkey" FOREIGN KEY ("lastEvaluationId") REFERENCES "public"."ExperienceEvaluation"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."ExperienceScoringPolicy" ADD CONSTRAINT "ExperienceScoringPolicy_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "public"."User"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/migrations/20260323195000_capture_v1/migration.sql b/prisma/migrations/20260323195000_capture_v1/migration.sql new file mode 100644 index 0000000..7973c9f --- /dev/null +++ b/prisma/migrations/20260323195000_capture_v1/migration.sql @@ -0,0 +1,158 @@ +-- CreateEnum +CREATE TYPE "public"."CaptureMode" AS ENUM ('manual', 'auto', 'hybrid'); + +-- CreateEnum +CREATE TYPE "public"."CaptureAutomationRunStatus" AS ENUM ('queued', 'running', 'success', 'failed'); + +-- AlterTable +ALTER TABLE "public"."WeeklyKpiRow" +ADD COLUMN "captureNote" TEXT, +ADD COLUMN "lastAutomationAt" TIMESTAMP(3), +ADD COLUMN "lastCapturedAt" TIMESTAMP(3), +ADD COLUMN "lastCapturedById" TEXT; + +-- CreateTable +CREATE TABLE "public"."KpiCaptureCatalog" ( + "id" TEXT NOT NULL, + "weekStart" TIMESTAMP(3) NOT NULL, + "sectionKey" TEXT NOT NULL, + "rowKey" TEXT NOT NULL, + "weeklyKpiRowId" TEXT, + "department" "public"."DepartmentKey" NOT NULL, + "captureMode" "public"."CaptureMode" NOT NULL DEFAULT 'manual', + "automationSource" TEXT, + "ownerUserId" TEXT, + "freshnessSlaHours" INTEGER NOT NULL DEFAULT 168, + "captureNote" TEXT, + "lastCapturedAt" TIMESTAMP(3), + "lastCapturedById" TEXT, + "lastAutomationAt" TIMESTAMP(3), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "KpiCaptureCatalog_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."KpiCaptureEvidence" ( + "id" TEXT NOT NULL, + "department" "public"."DepartmentKey" NOT NULL, + "weekStart" TIMESTAMP(3) NOT NULL, + "sectionKey" TEXT NOT NULL, + "rowKey" TEXT NOT NULL, + "catalogId" TEXT, + "weeklyKpiRowId" TEXT, + "initiativeId" TEXT, + "taskId" TEXT, + "uploadedById" TEXT NOT NULL, + "kind" "public"."ProjectCaptureEvidenceKind" NOT NULL, + "title" TEXT NOT NULL, + "note" TEXT, + "url" TEXT, + "storagePath" TEXT, + "mimeType" TEXT, + "sizeBytes" INTEGER, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "KpiCaptureEvidence_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "public"."KpiCaptureAutomationRun" ( + "id" TEXT NOT NULL, + "department" "public"."DepartmentKey" NOT NULL, + "weekStart" TIMESTAMP(3) NOT NULL, + "source" TEXT NOT NULL, + "status" "public"."CaptureAutomationRunStatus" NOT NULL DEFAULT 'queued', + "rowsTouched" INTEGER NOT NULL DEFAULT 0, + "errorMessage" TEXT, + "triggeredById" TEXT, + "startedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "completedAt" TIMESTAMP(3), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "KpiCaptureAutomationRun_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "KpiCaptureCatalog_weekStart_sectionKey_rowKey_key" ON "public"."KpiCaptureCatalog"("weekStart", "sectionKey", "rowKey"); + +-- CreateIndex +CREATE UNIQUE INDEX "KpiCaptureCatalog_weeklyKpiRowId_key" ON "public"."KpiCaptureCatalog"("weeklyKpiRowId"); + +-- CreateIndex +CREATE INDEX "KpiCaptureCatalog_department_weekStart_idx" ON "public"."KpiCaptureCatalog"("department", "weekStart"); + +-- CreateIndex +CREATE INDEX "KpiCaptureCatalog_ownerUserId_weekStart_idx" ON "public"."KpiCaptureCatalog"("ownerUserId", "weekStart"); + +-- CreateIndex +CREATE INDEX "KpiCaptureEvidence_department_weekStart_createdAt_idx" ON "public"."KpiCaptureEvidence"("department", "weekStart", "createdAt"); + +-- CreateIndex +CREATE INDEX "KpiCaptureEvidence_sectionKey_rowKey_weekStart_idx" ON "public"."KpiCaptureEvidence"("sectionKey", "rowKey", "weekStart"); + +-- CreateIndex +CREATE INDEX "KpiCaptureEvidence_catalogId_createdAt_idx" ON "public"."KpiCaptureEvidence"("catalogId", "createdAt"); + +-- CreateIndex +CREATE INDEX "KpiCaptureEvidence_initiativeId_createdAt_idx" ON "public"."KpiCaptureEvidence"("initiativeId", "createdAt"); + +-- CreateIndex +CREATE INDEX "KpiCaptureEvidence_taskId_createdAt_idx" ON "public"."KpiCaptureEvidence"("taskId", "createdAt"); + +-- CreateIndex +CREATE INDEX "KpiCaptureEvidence_uploadedById_createdAt_idx" ON "public"."KpiCaptureEvidence"("uploadedById", "createdAt"); + +-- CreateIndex +CREATE INDEX "KpiCaptureAutomationRun_department_weekStart_createdAt_idx" ON "public"."KpiCaptureAutomationRun"("department", "weekStart", "createdAt"); + +-- CreateIndex +CREATE INDEX "KpiCaptureAutomationRun_status_createdAt_idx" ON "public"."KpiCaptureAutomationRun"("status", "createdAt"); + +-- AddForeignKey +ALTER TABLE "public"."KpiCaptureCatalog" +ADD CONSTRAINT "KpiCaptureCatalog_weeklyKpiRowId_fkey" +FOREIGN KEY ("weeklyKpiRowId") REFERENCES "public"."WeeklyKpiRow"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."KpiCaptureCatalog" +ADD CONSTRAINT "KpiCaptureCatalog_ownerUserId_fkey" +FOREIGN KEY ("ownerUserId") REFERENCES "public"."User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."KpiCaptureCatalog" +ADD CONSTRAINT "KpiCaptureCatalog_lastCapturedById_fkey" +FOREIGN KEY ("lastCapturedById") REFERENCES "public"."User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."KpiCaptureEvidence" +ADD CONSTRAINT "KpiCaptureEvidence_catalogId_fkey" +FOREIGN KEY ("catalogId") REFERENCES "public"."KpiCaptureCatalog"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."KpiCaptureEvidence" +ADD CONSTRAINT "KpiCaptureEvidence_weeklyKpiRowId_fkey" +FOREIGN KEY ("weeklyKpiRowId") REFERENCES "public"."WeeklyKpiRow"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."KpiCaptureEvidence" +ADD CONSTRAINT "KpiCaptureEvidence_initiativeId_fkey" +FOREIGN KEY ("initiativeId") REFERENCES "public"."MarketingInitiative"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."KpiCaptureEvidence" +ADD CONSTRAINT "KpiCaptureEvidence_taskId_fkey" +FOREIGN KEY ("taskId") REFERENCES "public"."MarketingTask"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."KpiCaptureEvidence" +ADD CONSTRAINT "KpiCaptureEvidence_uploadedById_fkey" +FOREIGN KEY ("uploadedById") REFERENCES "public"."User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "public"."KpiCaptureAutomationRun" +ADD CONSTRAINT "KpiCaptureAutomationRun_triggeredById_fkey" +FOREIGN KEY ("triggeredById") REFERENCES "public"."User"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/migrations/20260330001000_hc_workspace_v1/migration.sql b/prisma/migrations/20260330001000_hc_workspace_v1/migration.sql new file mode 100644 index 0000000..6b9545d --- /dev/null +++ b/prisma/migrations/20260330001000_hc_workspace_v1/migration.sql @@ -0,0 +1,399 @@ +-- CreateEnum +CREATE TYPE "WorkspaceConfigStatus" AS ENUM ('draft', 'published', 'archived'); + +-- CreateEnum +CREATE TYPE "HrFileRecordStatus" AS ENUM ('missing', 'submitted', 'verified', 'rejected'); + +-- CreateEnum +CREATE TYPE "HrVacancyStatus" AS ENUM ('draft', 'open', 'interviewing', 'offered', 'hired', 'closed', 'cancelled'); + +-- CreateEnum +CREATE TYPE "HrPayrollFrequency" AS ENUM ('weekly', 'biweekly'); + +-- CreateEnum +CREATE TYPE "HrPayrollLineType" AS ENUM ('percepciones', 'deducciones', 'aportaciones'); + +-- CreateEnum +CREATE TYPE "HrImportJobStatus" AS ENUM ('queued', 'success', 'partial', 'failed'); + +-- CreateEnum +CREATE TYPE "HrCareerContentType" AS ENUM ('announcement', 'course'); + +-- CreateEnum +CREATE TYPE "HrCareerAssignmentStatus" AS ENUM ('not_started', 'in_progress', 'completed'); + +-- CreateEnum +CREATE TYPE "HrComplianceBody" AS ENUM ('imss', 'infonavit', 'fonacot', 'other'); + +-- CreateEnum +CREATE TYPE "HrComplianceStatus" AS ENUM ('on_time', 'due_soon', 'overdue', 'paid', 'blocked'); + +-- CreateEnum +CREATE TYPE "HrCompliancePaymentStatus" AS ENUM ('scheduled', 'paid', 'late', 'failed'); + +-- CreateEnum +CREATE TYPE "HrAutomationTaskStatus" AS ENUM ('queued', 'success', 'warning', 'failed'); + +-- CreateTable +CREATE TABLE "HrWorkspaceConfigVersion" ( + "id" TEXT NOT NULL, + "department" "DepartmentKey" NOT NULL DEFAULT 'capital_humano', + "version" INTEGER NOT NULL, + "status" "WorkspaceConfigStatus" NOT NULL DEFAULT 'draft', + "name" TEXT NOT NULL DEFAULT 'Capital Humano Workspace', + "tabs" JSONB NOT NULL, + "changeSummary" TEXT, + "createdById" TEXT, + "publishedById" TEXT, + "publishedAt" TIMESTAMP(3), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "HrWorkspaceConfigVersion_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "HrFileRequirement" ( + "id" TEXT NOT NULL, + "department" "DepartmentKey" NOT NULL DEFAULT 'capital_humano', + "key" TEXT NOT NULL, + "label" TEXT NOT NULL, + "description" TEXT, + "fieldType" TEXT NOT NULL DEFAULT 'document', + "isRequired" BOOLEAN NOT NULL DEFAULT true, + "isActive" BOOLEAN NOT NULL DEFAULT true, + "sortOrder" INTEGER NOT NULL DEFAULT 0, + "createdById" TEXT, + "updatedById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "HrFileRequirement_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "HrEmployeeFileRecord" ( + "id" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "requirementId" TEXT NOT NULL, + "status" "HrFileRecordStatus" NOT NULL DEFAULT 'missing', + "valueText" TEXT, + "evidenceUrl" TEXT, + "note" TEXT, + "verifiedAt" TIMESTAMP(3), + "verifiedById" TEXT, + "updatedById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "HrEmployeeFileRecord_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "HrIdealOrgTarget" ( + "id" TEXT NOT NULL, + "department" "DepartmentKey" NOT NULL DEFAULT 'capital_humano', + "locationCode" TEXT NOT NULL, + "roleKey" TEXT NOT NULL, + "targetCount" INTEGER NOT NULL, + "isActive" BOOLEAN NOT NULL DEFAULT true, + "updatedById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "HrIdealOrgTarget_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "HrVacancy" ( + "id" TEXT NOT NULL, + "department" "DepartmentKey" NOT NULL DEFAULT 'capital_humano', + "locationCode" TEXT NOT NULL, + "roleKey" TEXT NOT NULL, + "title" TEXT NOT NULL, + "status" "HrVacancyStatus" NOT NULL DEFAULT 'open', + "priority" TEXT NOT NULL DEFAULT 'medium', + "openedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "targetStartAt" TIMESTAMP(3), + "closedAt" TIMESTAMP(3), + "hiringManagerUserId" TEXT, + "ownerUserId" TEXT, + "notes" TEXT, + "createdById" TEXT, + "updatedById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "HrVacancy_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "HrPayrollRun" ( + "id" TEXT NOT NULL, + "department" "DepartmentKey" NOT NULL DEFAULT 'capital_humano', + "source" TEXT NOT NULL DEFAULT 'contpaqi_csv', + "externalRef" TEXT, + "periodStart" TIMESTAMP(3) NOT NULL, + "periodEnd" TIMESTAMP(3) NOT NULL, + "frequency" "HrPayrollFrequency" NOT NULL, + "locationCode" TEXT, + "status" TEXT NOT NULL DEFAULT 'imported', + "rawHash" TEXT, + "importedById" TEXT, + "importedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "HrPayrollRun_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "HrPayrollLine" ( + "id" TEXT NOT NULL, + "runId" TEXT NOT NULL, + "userId" TEXT, + "personIdentifier" TEXT NOT NULL, + "lineType" "HrPayrollLineType" NOT NULL, + "concept" TEXT NOT NULL, + "amount" DOUBLE PRECISION NOT NULL, + "currency" TEXT NOT NULL DEFAULT 'MXN', + "locationCode" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "HrPayrollLine_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "HrPayrollImportJob" ( + "id" TEXT NOT NULL, + "department" "DepartmentKey" NOT NULL DEFAULT 'capital_humano', + "status" "HrImportJobStatus" NOT NULL DEFAULT 'queued', + "sourceFileName" TEXT, + "requestHash" TEXT, + "message" TEXT, + "rowsRead" INTEGER NOT NULL DEFAULT 0, + "rowsImported" INTEGER NOT NULL DEFAULT 0, + "failedRows" INTEGER NOT NULL DEFAULT 0, + "runId" TEXT, + "triggeredById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "completedAt" TIMESTAMP(3), + + CONSTRAINT "HrPayrollImportJob_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "HrCareerContent" ( + "id" TEXT NOT NULL, + "department" "DepartmentKey" NOT NULL DEFAULT 'capital_humano', + "contentType" "HrCareerContentType" NOT NULL, + "title" TEXT NOT NULL, + "body" TEXT NOT NULL, + "sourceDepartment" "DepartmentKey", + "sourceRef" TEXT, + "startsAt" TIMESTAMP(3), + "endsAt" TIMESTAMP(3), + "isActive" BOOLEAN NOT NULL DEFAULT true, + "createdById" TEXT, + "updatedById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "HrCareerContent_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "HrCareerAssignment" ( + "id" TEXT NOT NULL, + "contentId" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "status" "HrCareerAssignmentStatus" NOT NULL DEFAULT 'not_started', + "progressPct" DOUBLE PRECISION NOT NULL DEFAULT 0, + "assignedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "dueAt" TIMESTAMP(3), + "completedAt" TIMESTAMP(3), + "notes" TEXT, + "updatedById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "HrCareerAssignment_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "HrComplianceObligation" ( + "id" TEXT NOT NULL, + "department" "DepartmentKey" NOT NULL DEFAULT 'capital_humano', + "body" "HrComplianceBody" NOT NULL, + "title" TEXT NOT NULL, + "status" "HrComplianceStatus" NOT NULL DEFAULT 'due_soon', + "locationCode" TEXT, + "referencePeriod" TEXT, + "dueDate" TIMESTAMP(3), + "lastPaymentAt" TIMESTAMP(3), + "lastPaymentAmount" DOUBLE PRECISION, + "notes" TEXT, + "createdById" TEXT, + "updatedById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "HrComplianceObligation_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "HrCompliancePayment" ( + "id" TEXT NOT NULL, + "obligationId" TEXT NOT NULL, + "paymentDate" TIMESTAMP(3) NOT NULL, + "amount" DOUBLE PRECISION NOT NULL, + "status" "HrCompliancePaymentStatus" NOT NULL DEFAULT 'paid', + "referencePeriod" TEXT, + "receiptUrl" TEXT, + "note" TEXT, + "createdById" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "HrCompliancePayment_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "HrAutomationTask" ( + "id" TEXT NOT NULL, + "department" "DepartmentKey" NOT NULL DEFAULT 'capital_humano', + "taskType" TEXT NOT NULL, + "status" "HrAutomationTaskStatus" NOT NULL DEFAULT 'queued', + "title" TEXT NOT NULL, + "message" TEXT, + "payload" JSONB, + "runAt" TIMESTAMP(3) NOT NULL, + "resolvedAt" TIMESTAMP(3), + "assignedToId" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "HrAutomationTask_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "HrExceptionQueueItem" ( + "id" TEXT NOT NULL, + "department" "DepartmentKey" NOT NULL DEFAULT 'capital_humano', + "source" TEXT NOT NULL, + "errorCode" TEXT, + "message" TEXT NOT NULL, + "payload" JSONB, + "status" TEXT NOT NULL DEFAULT 'open', + "retryCount" INTEGER NOT NULL DEFAULT 0, + "lastRetriedAt" TIMESTAMP(3), + "resolvedAt" TIMESTAMP(3), + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "HrExceptionQueueItem_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "HrWorkspaceConfigVersion_department_version_key" ON "HrWorkspaceConfigVersion"("department", "version"); + +-- CreateIndex +CREATE INDEX "HrWorkspaceConfigVersion_department_status_version_idx" ON "HrWorkspaceConfigVersion"("department", "status", "version"); + +-- CreateIndex +CREATE UNIQUE INDEX "HrFileRequirement_department_key_key" ON "HrFileRequirement"("department", "key"); + +-- CreateIndex +CREATE INDEX "HrFileRequirement_department_isActive_sortOrder_idx" ON "HrFileRequirement"("department", "isActive", "sortOrder"); + +-- CreateIndex +CREATE UNIQUE INDEX "HrEmployeeFileRecord_userId_requirementId_key" ON "HrEmployeeFileRecord"("userId", "requirementId"); + +-- CreateIndex +CREATE INDEX "HrEmployeeFileRecord_userId_status_idx" ON "HrEmployeeFileRecord"("userId", "status"); + +-- CreateIndex +CREATE INDEX "HrEmployeeFileRecord_requirementId_status_idx" ON "HrEmployeeFileRecord"("requirementId", "status"); + +-- CreateIndex +CREATE UNIQUE INDEX "HrIdealOrgTarget_department_locationCode_roleKey_key" ON "HrIdealOrgTarget"("department", "locationCode", "roleKey"); + +-- CreateIndex +CREATE INDEX "HrIdealOrgTarget_department_locationCode_roleKey_idx" ON "HrIdealOrgTarget"("department", "locationCode", "roleKey"); + +-- CreateIndex +CREATE INDEX "HrVacancy_department_status_openedAt_idx" ON "HrVacancy"("department", "status", "openedAt"); + +-- CreateIndex +CREATE INDEX "HrVacancy_department_locationCode_roleKey_idx" ON "HrVacancy"("department", "locationCode", "roleKey"); + +-- CreateIndex +CREATE INDEX "HrPayrollRun_department_periodStart_periodEnd_idx" ON "HrPayrollRun"("department", "periodStart", "periodEnd"); + +-- CreateIndex +CREATE INDEX "HrPayrollRun_department_frequency_locationCode_idx" ON "HrPayrollRun"("department", "frequency", "locationCode"); + +-- CreateIndex +CREATE INDEX "HrPayrollRun_rawHash_idx" ON "HrPayrollRun"("rawHash"); + +-- CreateIndex +CREATE INDEX "HrPayrollLine_runId_lineType_idx" ON "HrPayrollLine"("runId", "lineType"); + +-- CreateIndex +CREATE INDEX "HrPayrollLine_userId_idx" ON "HrPayrollLine"("userId"); + +-- CreateIndex +CREATE INDEX "HrPayrollLine_locationCode_lineType_idx" ON "HrPayrollLine"("locationCode", "lineType"); + +-- CreateIndex +CREATE INDEX "HrPayrollImportJob_department_status_createdAt_idx" ON "HrPayrollImportJob"("department", "status", "createdAt"); + +-- CreateIndex +CREATE INDEX "HrPayrollImportJob_requestHash_idx" ON "HrPayrollImportJob"("requestHash"); + +-- CreateIndex +CREATE INDEX "HrCareerContent_department_contentType_isActive_idx" ON "HrCareerContent"("department", "contentType", "isActive"); + +-- CreateIndex +CREATE INDEX "HrCareerContent_sourceDepartment_createdAt_idx" ON "HrCareerContent"("sourceDepartment", "createdAt"); + +-- CreateIndex +CREATE UNIQUE INDEX "HrCareerAssignment_contentId_userId_key" ON "HrCareerAssignment"("contentId", "userId"); + +-- CreateIndex +CREATE INDEX "HrCareerAssignment_userId_status_idx" ON "HrCareerAssignment"("userId", "status"); + +-- CreateIndex +CREATE INDEX "HrComplianceObligation_department_body_status_dueDate_idx" ON "HrComplianceObligation"("department", "body", "status", "dueDate"); + +-- CreateIndex +CREATE INDEX "HrComplianceObligation_department_locationCode_body_idx" ON "HrComplianceObligation"("department", "locationCode", "body"); + +-- CreateIndex +CREATE INDEX "HrCompliancePayment_obligationId_paymentDate_idx" ON "HrCompliancePayment"("obligationId", "paymentDate"); + +-- CreateIndex +CREATE INDEX "HrAutomationTask_department_status_runAt_idx" ON "HrAutomationTask"("department", "status", "runAt"); + +-- CreateIndex +CREATE INDEX "HrExceptionQueueItem_department_status_createdAt_idx" ON "HrExceptionQueueItem"("department", "status", "createdAt"); + +-- CreateIndex +CREATE INDEX "HrExceptionQueueItem_source_status_idx" ON "HrExceptionQueueItem"("source", "status"); + +-- AddForeignKey +ALTER TABLE "HrEmployeeFileRecord" ADD CONSTRAINT "HrEmployeeFileRecord_requirementId_fkey" FOREIGN KEY ("requirementId") REFERENCES "HrFileRequirement"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "HrPayrollLine" ADD CONSTRAINT "HrPayrollLine_runId_fkey" FOREIGN KEY ("runId") REFERENCES "HrPayrollRun"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "HrPayrollImportJob" ADD CONSTRAINT "HrPayrollImportJob_runId_fkey" FOREIGN KEY ("runId") REFERENCES "HrPayrollRun"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "HrCareerAssignment" ADD CONSTRAINT "HrCareerAssignment_contentId_fkey" FOREIGN KEY ("contentId") REFERENCES "HrCareerContent"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "HrCompliancePayment" ADD CONSTRAINT "HrCompliancePayment_obligationId_fkey" FOREIGN KEY ("obligationId") REFERENCES "HrComplianceObligation"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..044d57c --- /dev/null +++ b/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (e.g., Git) +provider = "postgresql" diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 0000000..b83af28 --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,1635 @@ +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +enum RoleKey { + owner + leader + employee +} + +enum DepartmentKey { + marketing + administracion + capital_humano + operaciones + proyectos +} + +enum MarketingMeetingStatus { + requested + scheduled + completed + cancelled +} + +enum MarketingInitiativeType { + evento + campania + cambio + implementacion + otro +} + +enum MarketingInitiativeStatus { + planning + in_progress + completion + results + evaluation +} + +enum MarketingTaskStatus { + todo + in_progress + blocked + done +} + +enum MarketingPublicOpinion { + positive + mixed + negative +} + +enum MarketingMilestoneStatus { + pending + in_progress + completed +} + +enum MeetingResponseStatus { + pending + accepted + declined +} + +enum ProjectCalendarVisibility { + personal + team +} + +enum ProjectCaptureEvidenceKind { + photo + document + link +} + +enum CaptureMode { + manual + auto + hybrid +} + +enum CaptureAutomationRunStatus { + queued + running + success + failed +} + +enum WeeklyKpiStatus { + on_track + watch + risk + no_score +} + +enum MarketingSyncRunStatus { + queued + running + success + failed +} + +enum EmploymentStatus { + active + leave + terminated +} + +enum EmployeeLifecycleEventType { + hire + transfer + leave + termination + rehire +} + +enum HrUpdateSeverity { + info + warning + critical +} + +enum WorkspaceConfigStatus { + draft + published + archived +} + +enum HrFileRecordStatus { + missing + submitted + verified + rejected +} + +enum HrVacancyStatus { + draft + open + interviewing + offered + hired + closed + cancelled +} + +enum HrPayrollFrequency { + weekly + biweekly +} + +enum HrPayrollLineType { + percepciones + deducciones + aportaciones +} + +enum HrImportJobStatus { + queued + success + partial + failed +} + +enum HrCareerContentType { + announcement + course +} + +enum HrCareerAssignmentStatus { + not_started + in_progress + completed +} + +enum HrComplianceBody { + imss + infonavit + fonacot + other +} + +enum HrComplianceStatus { + on_time + due_soon + overdue + paid + blocked +} + +enum HrCompliancePaymentStatus { + scheduled + paid + late + failed +} + +enum HrAutomationTaskStatus { + queued + success + warning + failed +} + +enum FactoryAssetState { + draft + active + down + retired +} + +enum PmTemplateState { + draft + active + archived +} + +enum PmWorkOrderState { + draft + scheduled + in_progress + completed + verified + overdue + cancelled +} + +enum ReminderEventState { + queued + sent + acknowledged + escalated + failed +} + +enum ApprovalState { + draft + submitted + pending_owner + approved + rejected + changes_requested + cancelled +} + +enum SalesForecastState { + draft + published + superseded +} + +enum ProductionPlanState { + draft + simulated + locked + approved + released + replanned +} + +enum ExperienceTemplateState { + draft + published + archived +} + +enum ExperienceFindingPriority { + low + medium + high + critical +} + +enum ExperienceFindingStatus { + open + in_progress + resolved + closed +} + +enum ExperienceScoreAggregationMode { + weighted_recent + moving_average + full_average +} + +enum ExperienceTrendDirection { + up + down + flat +} + +enum ExperienceSignalStatus { + green + yellow + red +} + +enum ExperienceEvidenceKind { + photo + document + link +} + +model User { + id String @id @default(cuid()) + name String? + email String @unique + emailVerified DateTime? + image String? + passwordHash String? + status String @default("active") + department DepartmentKey? + departmentRole String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + accounts Account[] + sessions Session[] + userRoles UserRole[] + invitations Invitation[] @relation("InvitedByUser") + marketingMeetingsRequested MarketingMeeting[] @relation("RequestedByUser") + marketingInitiativesOwned MarketingInitiative[] @relation("MarketingInitiativeOwner") + marketingInitiativesUpdated MarketingInitiative[] @relation("MarketingInitiativeUpdatedBy") + marketingInitiativeContributorLinks MarketingInitiativeContributor[] + marketingInitiativeEvidenceCreated MarketingInitiativeEvidence[] @relation("MarketingInitiativeEvidenceCreatedBy") + marketingTasksAssigned MarketingTask[] @relation("MarketingTaskAssignee") + marketingTaskEvidenceCreated MarketingTaskEvidence[] @relation("MarketingTaskEvidenceCreatedBy") + marketingInitiativeEdits MarketingInitiativeEdit[] @relation("MarketingInitiativeEditBy") + marketingBrandPulsesUpdated MarketingBrandPulse[] @relation("MarketingBrandPulseUpdatedBy") + marketingMilestonesCreated MarketingMilestone[] @relation("MarketingMilestoneCreatedBy") + marketingMilestoneCheckpointsCreated MarketingMilestoneCheckpoint[] @relation("MarketingMilestoneCheckpointCreatedBy") + projectMeetingParticipants MarketingMeetingParticipant[] @relation("MarketingMeetingParticipantUser") + projectCalendarEventsOwned ProjectCalendarEvent[] @relation("ProjectCalendarEventOwner") + projectCaptureEvidenceUploaded ProjectCaptureEvidence[] @relation("ProjectCaptureEvidenceUploadedBy") + kpiCaptureCatalogOwned KpiCaptureCatalog[] @relation("KpiCaptureCatalogOwner") + kpiCaptureCatalogLastCaptured KpiCaptureCatalog[] @relation("KpiCaptureCatalogLastCapturedBy") + kpiCaptureEvidenceUploaded KpiCaptureEvidence[] @relation("KpiCaptureEvidenceUploadedBy") + kpiCaptureAutomationRunsTriggered KpiCaptureAutomationRun[] @relation("KpiCaptureAutomationRunTriggeredBy") + employeeProfile EmployeeProfile? + employeeLifecycleTarget EmployeeLifecycleEvent[] @relation("EmployeeLifecycleTarget") + employeeLifecycleCreated EmployeeLifecycleEvent[] @relation("EmployeeLifecycleCreatedBy") + hrUpdatesAuthored HrUpdate[] @relation("HrUpdateAuthor") + managedLocations Location[] @relation("LocationManager") + experienceTemplatesCreated ExperienceTemplate[] @relation("ExperienceTemplateCreatedBy") + experienceEvaluationsCreated ExperienceEvaluation[] @relation("ExperienceEvaluationCreatedBy") + experienceFindingsCreated ExperienceFinding[] @relation("ExperienceFindingCreatedBy") + experienceFindingsAssigned ExperienceFinding[] @relation("ExperienceFindingAssignedTo") + experienceEvidenceUploaded ExperienceEvidence[] @relation("ExperienceEvidenceUploadedBy") + experienceScoringPoliciesCreated ExperienceScoringPolicy[] @relation("ExperienceScoringPolicyCreatedBy") +} + +model Role { + id String @id @default(cuid()) + key RoleKey @unique + name String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + userRoles UserRole[] +} + +model UserRole { + userId String + roleId String + assignedAt DateTime @default(now()) + role Role @relation(fields: [roleId], references: [id], onDelete: Cascade) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@id([userId, roleId]) +} + +model Invitation { + id String @id @default(cuid()) + inviteeName String + email String + roleKey RoleKey + department DepartmentKey + departmentRole String + tokenHash String @unique + expiresAt DateTime + acceptedAt DateTime? + invitedById String? + createdAt DateTime @default(now()) + invitedBy User? @relation("InvitedByUser", fields: [invitedById], references: [id], onDelete: SetNull) + + @@index([email, expiresAt]) +} + +model PasswordResetToken { + id String @id @default(cuid()) + email String + tokenHash String @unique + expiresAt DateTime + usedAt DateTime? + createdAt DateTime @default(now()) + + @@index([email, expiresAt]) +} + +model MarketingMeeting { + id String @id @default(cuid()) + department DepartmentKey + title String + agenda String + status MarketingMeetingStatus @default(requested) + requestedById String? + requestedByName String + participantNames Json + suggestedTimes Json + scheduledFor DateTime? + completedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + requestedBy User? @relation("RequestedByUser", fields: [requestedById], references: [id], onDelete: SetNull) + commitments MarketingCommitment[] + participants MarketingMeetingParticipant[] + calendarEvent ProjectCalendarEvent? @relation("ProjectMeetingCalendarEvent") + + @@index([department, status, scheduledFor]) +} + +model MarketingCommitment { + id String @id @default(cuid()) + meetingId String + title String + description String? + ownerName String? + dueDate DateTime? + status String @default("pendiente") + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + meeting MarketingMeeting @relation(fields: [meetingId], references: [id], onDelete: Cascade) + + @@index([meetingId, dueDate]) +} + +model MarketingInitiative { + id String @id @default(cuid()) + department DepartmentKey + name String + type MarketingInitiativeType + status MarketingInitiativeStatus @default(planning) + isGlobal Boolean @default(false) + ownerId String? + dueDate DateTime + completedAt DateTime? + importanceWeight Float @default(1) + leadRating1to5 Int @default(3) + trackScore Boolean @default(true) + trackTickets Boolean @default(true) + trackRevenue Boolean @default(true) + target Float @default(0) + actual Float @default(0) + ticketsTarget Int @default(0) + ticketsActual Int @default(0) + revenueTarget Float @default(0) + revenueActual Float @default(0) + plannedCost Float @default(0) + actualCost Float @default(0) + targetTicketPrice Float @default(0) + actualTicketPrice Float @default(0) + attributionChannel String? + attributionPageId String? + attributionCampaign String? + attributionStart DateTime? + attributionEnd DateTime? + autoActual Float? + autoTicketsActual Int? + autoRevenueActual Float? + autoUpdatedAt DateTime? + projectsSchemaVersion Int @default(1) + opinionPublica MarketingPublicOpinion @default(mixed) + queFunciono String @default("") + queNo String @default("") + proximoIntento String @default("") + updatedById String? + updatedByName String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + owner User? @relation("MarketingInitiativeOwner", fields: [ownerId], references: [id], onDelete: SetNull) + updatedBy User? @relation("MarketingInitiativeUpdatedBy", fields: [updatedById], references: [id], onDelete: SetNull) + contributors MarketingInitiativeContributor[] + locations MarketingInitiativeLocation[] + evidenceLinks MarketingInitiativeEvidence[] + tasks MarketingTask[] + edits MarketingInitiativeEdit[] + milestones MarketingMilestone[] + captureEvidence ProjectCaptureEvidence[] + kpiCaptureEvidence KpiCaptureEvidence[] + + @@index([department, status, dueDate]) +} + +model MarketingInitiativeContributor { + initiativeId String + userId String + createdAt DateTime @default(now()) + initiative MarketingInitiative @relation(fields: [initiativeId], references: [id], onDelete: Cascade) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@id([initiativeId, userId]) + @@index([userId]) +} + +model MarketingInitiativeLocation { + id String @id @default(cuid()) + initiativeId String + locationId String + createdAt DateTime @default(now()) + initiative MarketingInitiative @relation(fields: [initiativeId], references: [id], onDelete: Cascade) + + @@unique([initiativeId, locationId]) + @@index([locationId]) +} + +model MarketingInitiativeEvidence { + id String @id @default(cuid()) + initiativeId String + url String + createdById String? + createdAt DateTime @default(now()) + initiative MarketingInitiative @relation(fields: [initiativeId], references: [id], onDelete: Cascade) + createdBy User? @relation("MarketingInitiativeEvidenceCreatedBy", fields: [createdById], references: [id], onDelete: SetNull) + + @@index([initiativeId, createdAt]) +} + +model MarketingTask { + id String @id @default(cuid()) + initiativeId String + title String + description String @default("") + assigneeId String? + status MarketingTaskStatus @default(todo) + dueDate DateTime + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + initiative MarketingInitiative @relation(fields: [initiativeId], references: [id], onDelete: Cascade) + assignee User? @relation("MarketingTaskAssignee", fields: [assigneeId], references: [id], onDelete: SetNull) + evidenceLinks MarketingTaskEvidence[] + captureEvidence ProjectCaptureEvidence[] + kpiCaptureEvidence KpiCaptureEvidence[] + + @@index([initiativeId, status, dueDate]) + @@index([assigneeId]) +} + +model MarketingTaskEvidence { + id String @id @default(cuid()) + taskId String + url String + createdById String? + createdAt DateTime @default(now()) + task MarketingTask @relation(fields: [taskId], references: [id], onDelete: Cascade) + createdBy User? @relation("MarketingTaskEvidenceCreatedBy", fields: [createdById], references: [id], onDelete: SetNull) + + @@index([taskId, createdAt]) +} + +model MarketingInitiativeEdit { + id String @id @default(cuid()) + initiativeId String + editedById String? + editedByName String + summary String + createdAt DateTime @default(now()) + initiative MarketingInitiative @relation(fields: [initiativeId], references: [id], onDelete: Cascade) + editedBy User? @relation("MarketingInitiativeEditBy", fields: [editedById], references: [id], onDelete: SetNull) + + @@index([initiativeId, createdAt]) +} + +model MarketingMilestone { + id String @id @default(cuid()) + initiativeId String + title String + description String @default("") + dueDate DateTime + status MarketingMilestoneStatus @default(pending) + sortOrder Int @default(0) + createdById String? + createdByName String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + initiative MarketingInitiative @relation(fields: [initiativeId], references: [id], onDelete: Cascade) + createdBy User? @relation("MarketingMilestoneCreatedBy", fields: [createdById], references: [id], onDelete: SetNull) + checkpoints MarketingMilestoneCheckpoint[] + + @@index([initiativeId, dueDate, status]) +} + +model MarketingMilestoneCheckpoint { + id String @id @default(cuid()) + milestoneId String + note String + createdById String? + createdByName String? + createdAt DateTime @default(now()) + milestone MarketingMilestone @relation(fields: [milestoneId], references: [id], onDelete: Cascade) + createdBy User? @relation("MarketingMilestoneCheckpointCreatedBy", fields: [createdById], references: [id], onDelete: SetNull) + + @@index([milestoneId, createdAt]) +} + +model MarketingMeetingParticipant { + id String @id @default(cuid()) + meetingId String + userId String? + displayName String + responseStatus MeetingResponseStatus @default(pending) + respondedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + meeting MarketingMeeting @relation(fields: [meetingId], references: [id], onDelete: Cascade) + user User? @relation("MarketingMeetingParticipantUser", fields: [userId], references: [id], onDelete: SetNull) + + @@unique([meetingId, userId]) + @@index([meetingId, responseStatus]) + @@index([userId]) +} + +model ProjectCalendarEvent { + id String @id @default(cuid()) + department DepartmentKey @default(proyectos) + ownerUserId String + meetingId String? @unique + title String + notes String? + startAt DateTime + endAt DateTime + visibility ProjectCalendarVisibility @default(personal) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + ownerUser User @relation("ProjectCalendarEventOwner", fields: [ownerUserId], references: [id], onDelete: Cascade) + meeting MarketingMeeting? @relation("ProjectMeetingCalendarEvent", fields: [meetingId], references: [id], onDelete: SetNull) + + @@index([department, startAt, endAt]) + @@index([ownerUserId, startAt]) +} + +model ProjectCaptureEvidence { + id String @id @default(cuid()) + department DepartmentKey @default(proyectos) + initiativeId String? + taskId String? + uploadedById String + kind ProjectCaptureEvidenceKind + title String + note String? + url String? + storagePath String? + mimeType String? + sizeBytes Int? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + initiative MarketingInitiative? @relation(fields: [initiativeId], references: [id], onDelete: SetNull) + task MarketingTask? @relation(fields: [taskId], references: [id], onDelete: SetNull) + uploadedBy User @relation("ProjectCaptureEvidenceUploadedBy", fields: [uploadedById], references: [id], onDelete: Restrict) + + @@index([department, createdAt]) + @@index([initiativeId, createdAt]) + @@index([taskId, createdAt]) + @@index([uploadedById, createdAt]) +} + +model KpiCaptureCatalog { + id String @id @default(cuid()) + weekStart DateTime + sectionKey String + rowKey String + weeklyKpiRowId String? @unique + department DepartmentKey + captureMode CaptureMode @default(manual) + automationSource String? + ownerUserId String? + freshnessSlaHours Int @default(168) + captureNote String? + lastCapturedAt DateTime? + lastCapturedById String? + lastAutomationAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + weeklyKpiRow WeeklyKpiRow? @relation(fields: [weeklyKpiRowId], references: [id], onDelete: SetNull) + ownerUser User? @relation("KpiCaptureCatalogOwner", fields: [ownerUserId], references: [id], onDelete: SetNull) + lastCapturedBy User? @relation("KpiCaptureCatalogLastCapturedBy", fields: [lastCapturedById], references: [id], onDelete: SetNull) + evidence KpiCaptureEvidence[] + + @@unique([weekStart, sectionKey, rowKey]) + @@index([department, weekStart]) + @@index([ownerUserId, weekStart]) +} + +model KpiCaptureEvidence { + id String @id @default(cuid()) + department DepartmentKey + weekStart DateTime + sectionKey String + rowKey String + catalogId String? + weeklyKpiRowId String? + initiativeId String? + taskId String? + uploadedById String + kind ProjectCaptureEvidenceKind + title String + note String? + url String? + storagePath String? + mimeType String? + sizeBytes Int? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + catalog KpiCaptureCatalog? @relation(fields: [catalogId], references: [id], onDelete: SetNull) + weeklyKpiRow WeeklyKpiRow? @relation(fields: [weeklyKpiRowId], references: [id], onDelete: SetNull) + initiative MarketingInitiative? @relation(fields: [initiativeId], references: [id], onDelete: SetNull) + task MarketingTask? @relation(fields: [taskId], references: [id], onDelete: SetNull) + uploadedBy User @relation("KpiCaptureEvidenceUploadedBy", fields: [uploadedById], references: [id], onDelete: Restrict) + + @@index([department, weekStart, createdAt]) + @@index([sectionKey, rowKey, weekStart]) + @@index([catalogId, createdAt]) + @@index([initiativeId, createdAt]) + @@index([taskId, createdAt]) + @@index([uploadedById, createdAt]) +} + +model KpiCaptureAutomationRun { + id String @id @default(cuid()) + department DepartmentKey + weekStart DateTime + source String + status CaptureAutomationRunStatus @default(queued) + rowsTouched Int @default(0) + errorMessage String? + triggeredById String? + startedAt DateTime @default(now()) + completedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + triggeredBy User? @relation("KpiCaptureAutomationRunTriggeredBy", fields: [triggeredById], references: [id], onDelete: SetNull) + + @@index([department, weekStart, createdAt]) + @@index([status, createdAt]) +} + +model MarketingSocialSnapshot { + id String @id @default(cuid()) + department DepartmentKey + channel String + range String + followersStart Int + followersEnd Int + engagementRate Float + reach Int + impressions Int + capturedAt DateTime + createdAt DateTime @default(now()) + + @@index([department, range, channel, capturedAt]) +} + +model MarketingBrandPulse { + id String @id @default(cuid()) + department DepartmentKey + month String + rating1to5 Int + notes String @default("") + updatedById String? + updatedByName String? + updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + updatedBy User? @relation("MarketingBrandPulseUpdatedBy", fields: [updatedById], references: [id], onDelete: SetNull) + + @@index([department, month]) +} + +model MarketingMetaConnection { + id String @id @default(cuid()) + department DepartmentKey @default(marketing) + accountId String + pageId String + pageName String + pageAccessToken String + tokenExpiresAt DateTime? + connectedById String? + isActive Boolean @default(true) + lastSyncedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + metrics MarketingSocialMetricDaily[] + syncRuns MarketingSyncRun[] + + @@unique([department, pageId, isActive]) + @@index([department, isActive]) +} + +model MarketingSocialMetricDaily { + id String @id @default(cuid()) + department DepartmentKey @default(marketing) + connectionId String + channel String + metricDate DateTime + followers Int + reach Int + impressions Int + engagements Int + engagementRate Float + createdAt DateTime @default(now()) + connection MarketingMetaConnection @relation(fields: [connectionId], references: [id], onDelete: Cascade) + + @@unique([connectionId, channel, metricDate]) + @@index([department, channel, metricDate]) +} + +model MarketingSyncRun { + id String @id @default(cuid()) + department DepartmentKey @default(marketing) + connectionId String? + status MarketingSyncRunStatus @default(queued) + message String? + rowsIngested Int @default(0) + triggeredById String? + startedAt DateTime @default(now()) + completedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + connection MarketingMetaConnection? @relation(fields: [connectionId], references: [id], onDelete: SetNull) + + @@index([department, createdAt]) + @@index([status, createdAt]) +} + +model EmployeeProfile { + id String @id @default(cuid()) + userId String @unique + employeeCode String? + hireDate DateTime? + employmentType String? + managerUserId String? + locationId String? + fte Float @default(1) + employmentStatus EmploymentStatus @default(active) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@index([employmentStatus, hireDate]) +} + +model EmployeeLifecycleEvent { + id String @id @default(cuid()) + userId String + eventType EmployeeLifecycleEventType + effectiveAt DateTime + reason String? + isVoluntary Boolean? + metadata Json? + createdById String? + createdAt DateTime @default(now()) + user User @relation("EmployeeLifecycleTarget", fields: [userId], references: [id], onDelete: Cascade) + createdBy User? @relation("EmployeeLifecycleCreatedBy", fields: [createdById], references: [id], onDelete: SetNull) + + @@index([userId, effectiveAt]) + @@index([eventType, effectiveAt]) +} + +model HrMetricSnapshot { + id String @id @default(cuid()) + department DepartmentKey? + snapshotDate DateTime + headcount Int + hires Int + exits Int + churnPct Float + medianTenureMonths Float + peopleHealthScore Float + createdAt DateTime @default(now()) + + @@index([snapshotDate, department]) +} + +model HrUpdate { + id String @id @default(cuid()) + title String + body String + severity HrUpdateSeverity @default(info) + audience String @default("hc_leadership") + status String @default("draft") + publishedAt DateTime? + authorId String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + author User? @relation("HrUpdateAuthor", fields: [authorId], references: [id], onDelete: SetNull) + + @@index([status, publishedAt]) +} + +model HrWorkspaceConfigVersion { + id String @id @default(cuid()) + department DepartmentKey @default(capital_humano) + version Int + status WorkspaceConfigStatus @default(draft) + name String @default("Capital Humano Workspace") + tabs Json + changeSummary String? + createdById String? + publishedById String? + publishedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@unique([department, version]) + @@index([department, status, version]) +} + +model HrFileRequirement { + id String @id @default(cuid()) + department DepartmentKey @default(capital_humano) + key String + label String + description String? + fieldType String @default("document") + isRequired Boolean @default(true) + isActive Boolean @default(true) + sortOrder Int @default(0) + createdById String? + updatedById String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + records HrEmployeeFileRecord[] + + @@unique([department, key]) + @@index([department, isActive, sortOrder]) +} + +model HrEmployeeFileRecord { + id String @id @default(cuid()) + userId String + requirementId String + status HrFileRecordStatus @default(missing) + valueText String? + evidenceUrl String? + note String? + verifiedAt DateTime? + verifiedById String? + updatedById String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + requirement HrFileRequirement @relation(fields: [requirementId], references: [id], onDelete: Cascade) + + @@unique([userId, requirementId]) + @@index([userId, status]) + @@index([requirementId, status]) +} + +model HrIdealOrgTarget { + id String @id @default(cuid()) + department DepartmentKey @default(capital_humano) + locationCode String + roleKey String + targetCount Int + isActive Boolean @default(true) + updatedById String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@unique([department, locationCode, roleKey]) + @@index([department, locationCode, roleKey]) +} + +model HrVacancy { + id String @id @default(cuid()) + department DepartmentKey @default(capital_humano) + locationCode String + roleKey String + title String + status HrVacancyStatus @default(open) + priority String @default("medium") + openedAt DateTime @default(now()) + targetStartAt DateTime? + closedAt DateTime? + hiringManagerUserId String? + ownerUserId String? + notes String? + createdById String? + updatedById String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([department, status, openedAt]) + @@index([department, locationCode, roleKey]) +} + +model HrPayrollRun { + id String @id @default(cuid()) + department DepartmentKey @default(capital_humano) + source String @default("contpaqi_csv") + externalRef String? + periodStart DateTime + periodEnd DateTime + frequency HrPayrollFrequency + locationCode String? + status String @default("imported") + rawHash String? + importedById String? + importedAt DateTime @default(now()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + lines HrPayrollLine[] + importJobs HrPayrollImportJob[] + + @@index([department, periodStart, periodEnd]) + @@index([department, frequency, locationCode]) + @@index([rawHash]) +} + +model HrPayrollLine { + id String @id @default(cuid()) + runId String + userId String? + personIdentifier String + lineType HrPayrollLineType + concept String + amount Float + currency String @default("MXN") + locationCode String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + run HrPayrollRun @relation(fields: [runId], references: [id], onDelete: Cascade) + + @@index([runId, lineType]) + @@index([userId]) + @@index([locationCode, lineType]) +} + +model HrPayrollImportJob { + id String @id @default(cuid()) + department DepartmentKey @default(capital_humano) + status HrImportJobStatus @default(queued) + sourceFileName String? + requestHash String? + message String? + rowsRead Int @default(0) + rowsImported Int @default(0) + failedRows Int @default(0) + runId String? + triggeredById String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + completedAt DateTime? + run HrPayrollRun? @relation(fields: [runId], references: [id], onDelete: SetNull) + + @@index([department, status, createdAt]) + @@index([requestHash]) +} + +model HrCareerContent { + id String @id @default(cuid()) + department DepartmentKey @default(capital_humano) + contentType HrCareerContentType + title String + body String + sourceDepartment DepartmentKey? + sourceRef String? + startsAt DateTime? + endsAt DateTime? + isActive Boolean @default(true) + createdById String? + updatedById String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + assignments HrCareerAssignment[] + + @@index([department, contentType, isActive]) + @@index([sourceDepartment, createdAt]) +} + +model HrCareerAssignment { + id String @id @default(cuid()) + contentId String + userId String + status HrCareerAssignmentStatus @default(not_started) + progressPct Float @default(0) + assignedAt DateTime @default(now()) + dueAt DateTime? + completedAt DateTime? + notes String? + updatedById String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + content HrCareerContent @relation(fields: [contentId], references: [id], onDelete: Cascade) + + @@unique([contentId, userId]) + @@index([userId, status]) +} + +model HrComplianceObligation { + id String @id @default(cuid()) + department DepartmentKey @default(capital_humano) + body HrComplianceBody + title String + status HrComplianceStatus @default(due_soon) + locationCode String? + referencePeriod String? + dueDate DateTime? + lastPaymentAt DateTime? + lastPaymentAmount Float? + notes String? + createdById String? + updatedById String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + payments HrCompliancePayment[] + + @@index([department, body, status, dueDate]) + @@index([department, locationCode, body]) +} + +model HrCompliancePayment { + id String @id @default(cuid()) + obligationId String + paymentDate DateTime + amount Float + status HrCompliancePaymentStatus @default(paid) + referencePeriod String? + receiptUrl String? + note String? + createdById String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + obligation HrComplianceObligation @relation(fields: [obligationId], references: [id], onDelete: Cascade) + + @@index([obligationId, paymentDate]) +} + +model HrAutomationTask { + id String @id @default(cuid()) + department DepartmentKey @default(capital_humano) + taskType String + status HrAutomationTaskStatus @default(queued) + title String + message String? + payload Json? + runAt DateTime + resolvedAt DateTime? + assignedToId String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([department, status, runAt]) +} + +model HrExceptionQueueItem { + id String @id @default(cuid()) + department DepartmentKey @default(capital_humano) + source String + errorCode String? + message String + payload Json? + status String @default("open") + retryCount Int @default(0) + lastRetriedAt DateTime? + resolvedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([department, status, createdAt]) + @@index([source, status]) +} + +model FactoryAsset { + id String @id @default(cuid()) + department DepartmentKey @default(operaciones) + assetCode String @unique + name String + category String + locationId String? + criticality Int @default(3) + state FactoryAssetState @default(active) + serviceStrategy String? + lastMaintenanceAt DateTime? + nextMaintenanceAt DateTime? + createdById String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + templates PmTemplate[] + workOrders PmWorkOrder[] + + @@index([department, state]) + @@index([nextMaintenanceAt, state]) +} + +model PmTemplate { + id String @id @default(cuid()) + assetId String + title String + description String? + cadenceDays Int? + cadenceHours Int? + state PmTemplateState @default(active) + createdById String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + asset FactoryAsset @relation(fields: [assetId], references: [id], onDelete: Cascade) + workOrders PmWorkOrder[] + + @@index([assetId, state]) +} + +model PmWorkOrder { + id String @id @default(cuid()) + assetId String + templateId String? + title String + description String? + scheduledFor DateTime + dueBy DateTime + startedAt DateTime? + completedAt DateTime? + estimatedCost Float @default(0) + expectedDowntimeHours Float @default(0) + actualCost Float? + actualDowntimeHours Float? + state PmWorkOrderState @default(scheduled) + requestedById String? + approvedById String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + asset FactoryAsset @relation(fields: [assetId], references: [id], onDelete: Cascade) + template PmTemplate? @relation(fields: [templateId], references: [id], onDelete: SetNull) + reminders ReminderEvent[] + approvalRequests ApprovalRequest[] + + @@index([assetId, state, dueBy]) + @@index([state, scheduledFor]) +} + +model ReminderEvent { + id String @id @default(cuid()) + workOrderId String + remindAt DateTime + channel String @default("in_app") + message String? + state ReminderEventState @default(queued) + acknowledgedAt DateTime? + createdAt DateTime @default(now()) + workOrder PmWorkOrder @relation(fields: [workOrderId], references: [id], onDelete: Cascade) + + @@index([workOrderId, state]) + @@index([remindAt, state]) +} + +model MaintenanceApprovalPolicy { + id String @id @default(cuid()) + department DepartmentKey @default(operaciones) + costThreshold Float @default(5000) + downtimeThresholdHours Float @default(4) + ownerUserId String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@unique([department]) +} + +model SalesForecast { + id String @id @default(cuid()) + department DepartmentKey @default(operaciones) + weekStart DateTime + weekEnd DateTime + locationId String? + sku String? + forecastUnits Float + multiplier Float @default(1.2) + state SalesForecastState @default(draft) + createdById String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([department, weekStart, state]) +} + +model ProductionPlan { + id String @id @default(cuid()) + department DepartmentKey @default(operaciones) + weekStart DateTime + weekEnd DateTime + lineName String + plannedUnits Float + forecastUnits Float + capacityUnits Float + varianceUnits Float + state ProductionPlanState @default(draft) + createdById String? + approvedById String? + approvedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + approvalRequests ApprovalRequest[] + + @@index([department, weekStart, state]) +} + +model ApprovalRequest { + id String @id @default(cuid()) + department DepartmentKey @default(operaciones) + requestType String + workOrderId String? + productionPlanId String? + submittedById String? + approverId String? + state ApprovalState @default(submitted) + reason String? + decidedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + workOrder PmWorkOrder? @relation(fields: [workOrderId], references: [id], onDelete: SetNull) + productionPlan ProductionPlan? @relation(fields: [productionPlanId], references: [id], onDelete: SetNull) + + @@index([department, state, createdAt]) +} + +model WeeklyKpiSnapshot { + id String @id @default(cuid()) + weekStart DateTime @unique + weekEnd DateTime + source String @default("platform") + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + sections WeeklyKpiSection[] + + @@index([weekStart, weekEnd]) +} + +model WeeklyKpiSection { + id String @id @default(cuid()) + snapshotId String + sectionKey String + rawSectionLabel String + mappedDepartment DepartmentKey? + ownerTeamLabel String? + sortOrder Int @default(0) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + snapshot WeeklyKpiSnapshot @relation(fields: [snapshotId], references: [id], onDelete: Cascade) + rows WeeklyKpiRow[] + + @@unique([snapshotId, sectionKey]) + @@index([snapshotId, mappedDepartment, sortOrder]) +} + +model WeeklyKpiRow { + id String @id @default(cuid()) + sectionId String + rowKey String + responsibilityText String + objectiveIndicatorText String? + quantityQualityText String? + complianceText String? + dueCommitmentText String? + targetValue Float? + quantityValue Float? + compliancePct Float? + dueDate DateTime? + lastCapturedAt DateTime? + lastCapturedById String? + lastAutomationAt DateTime? + captureNote String? + status WeeklyKpiStatus @default(no_score) + sortOrder Int @default(0) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + section WeeklyKpiSection @relation(fields: [sectionId], references: [id], onDelete: Cascade) + captureCatalog KpiCaptureCatalog? + captureEvidence KpiCaptureEvidence[] + + @@unique([sectionId, rowKey]) + @@index([sectionId, sortOrder]) + @@index([status, dueDate]) +} + +model Location { + id String @id @default(cuid()) + code String @unique + name String + city String? + managerUserId String? + isActive Boolean @default(true) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + managerUser User? @relation("LocationManager", fields: [managerUserId], references: [id], onDelete: SetNull) + evaluations ExperienceEvaluation[] + findings ExperienceFinding[] + evidence ExperienceEvidence[] + metrics ExperienceLocationMetric? + + @@index([isActive, name]) +} + +model ExperienceTemplate { + id String @id @default(cuid()) + name String + version Int + state ExperienceTemplateState @default(draft) + isDefault Boolean @default(false) + createdById String? + publishedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + createdBy User? @relation("ExperienceTemplateCreatedBy", fields: [createdById], references: [id], onDelete: SetNull) + categories ExperienceTemplateCategory[] + evaluations ExperienceEvaluation[] + + @@unique([name, version]) + @@index([state, updatedAt]) +} + +model ExperienceTemplateCategory { + id String @id @default(cuid()) + templateId String + key String + name String + weight Float @default(1) + sortOrder Int @default(0) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + template ExperienceTemplate @relation(fields: [templateId], references: [id], onDelete: Cascade) + items ExperienceTemplateItem[] + responses ExperienceEvaluationResponse[] + + @@unique([templateId, key]) + @@index([templateId, sortOrder]) +} + +model ExperienceTemplateItem { + id String @id @default(cuid()) + categoryId String + key String + label String + weight Float @default(1) + sortOrder Int @default(0) + allowsComment Boolean @default(true) + requiresObservation Boolean @default(false) + allowsEvidence Boolean @default(true) + isActive Boolean @default(true) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + category ExperienceTemplateCategory @relation(fields: [categoryId], references: [id], onDelete: Cascade) + responses ExperienceEvaluationResponse[] + + @@unique([categoryId, key]) + @@index([categoryId, sortOrder]) +} + +model ExperienceEvaluation { + id String @id @default(cuid()) + locationId String + templateId String + createdById String? + evaluatedAt DateTime @default(now()) + generalObservations String @default("") + strengths String @default("") + improvementAreas String @default("") + totalScore Float + signal ExperienceSignalStatus + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + location Location @relation(fields: [locationId], references: [id], onDelete: Restrict) + template ExperienceTemplate @relation(fields: [templateId], references: [id], onDelete: Restrict) + createdBy User? @relation("ExperienceEvaluationCreatedBy", fields: [createdById], references: [id], onDelete: SetNull) + responses ExperienceEvaluationResponse[] + findings ExperienceFinding[] + evidence ExperienceEvidence[] + metricAsLatest ExperienceLocationMetric[] @relation("ExperienceLocationMetricLastEvaluation") + + @@index([locationId, evaluatedAt]) + @@index([templateId, evaluatedAt]) +} + +model ExperienceEvaluationResponse { + id String @id @default(cuid()) + evaluationId String + categoryId String + itemId String + score Int + scorePct Float + comment String? + observation String? + hasObservation Boolean @default(false) + categoryLabelSnapshot String + itemLabelSnapshot String + categoryWeightSnapshot Float + itemWeightSnapshot Float + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + evaluation ExperienceEvaluation @relation(fields: [evaluationId], references: [id], onDelete: Cascade) + category ExperienceTemplateCategory @relation(fields: [categoryId], references: [id], onDelete: Restrict) + item ExperienceTemplateItem @relation(fields: [itemId], references: [id], onDelete: Restrict) + evidence ExperienceEvidence[] + + @@unique([evaluationId, itemId]) + @@index([evaluationId, categoryId]) +} + +model ExperienceFinding { + id String @id @default(cuid()) + locationId String + evaluationId String? + createdById String? + responsibleUserId String? + title String + description String @default("") + categoryKey String? + categoryLabel String? + priority ExperienceFindingPriority @default(medium) + status ExperienceFindingStatus @default(open) + dueDate DateTime? + resolvedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + location Location @relation(fields: [locationId], references: [id], onDelete: Restrict) + evaluation ExperienceEvaluation? @relation(fields: [evaluationId], references: [id], onDelete: SetNull) + createdBy User? @relation("ExperienceFindingCreatedBy", fields: [createdById], references: [id], onDelete: SetNull) + responsibleUser User? @relation("ExperienceFindingAssignedTo", fields: [responsibleUserId], references: [id], onDelete: SetNull) + evidence ExperienceEvidence[] + + @@index([locationId, status, createdAt]) + @@index([evaluationId]) + @@index([responsibleUserId, status]) +} + +model ExperienceEvidence { + id String @id @default(cuid()) + locationId String + evaluationId String? + findingId String? + responseId String? + uploadedById String? + kind ExperienceEvidenceKind + title String + note String? + url String? + storagePath String? + mimeType String? + sizeBytes Int? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + location Location @relation(fields: [locationId], references: [id], onDelete: Restrict) + evaluation ExperienceEvaluation? @relation(fields: [evaluationId], references: [id], onDelete: SetNull) + finding ExperienceFinding? @relation(fields: [findingId], references: [id], onDelete: SetNull) + response ExperienceEvaluationResponse? @relation(fields: [responseId], references: [id], onDelete: SetNull) + uploadedBy User? @relation("ExperienceEvidenceUploadedBy", fields: [uploadedById], references: [id], onDelete: SetNull) + + @@index([locationId, createdAt]) + @@index([evaluationId, createdAt]) + @@index([findingId, createdAt]) +} + +model ExperienceLocationMetric { + id String @id @default(cuid()) + locationId String @unique + currentScore Float @default(0) + previousScore Float @default(0) + trendDelta Float @default(0) + trendDirection ExperienceTrendDirection @default(flat) + signal ExperienceSignalStatus @default(yellow) + totalEvaluations Int @default(0) + openFindings Int @default(0) + lastEvaluationAt DateTime? + lastEvaluationId String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + location Location @relation(fields: [locationId], references: [id], onDelete: Cascade) + lastEvaluation ExperienceEvaluation? @relation("ExperienceLocationMetricLastEvaluation", fields: [lastEvaluationId], references: [id], onDelete: SetNull) + + @@index([signal, currentScore]) +} + +model ExperienceScoringPolicy { + id String @id @default(cuid()) + name String + isActive Boolean @default(true) + aggregationMode ExperienceScoreAggregationMode @default(weighted_recent) + recentWindow Int @default(3) + recentWeightsCsv String @default("0.5,0.3,0.2") + greenThreshold Float @default(85) + yellowThreshold Float @default(70) + createdById String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + createdBy User? @relation("ExperienceScoringPolicyCreatedBy", fields: [createdById], references: [id], onDelete: SetNull) + + @@index([isActive, updatedAt]) +} + +model Account { + id String @id @default(cuid()) + userId String + type String + provider String + providerAccountId String + refresh_token String? @db.Text + access_token String? @db.Text + expires_at Int? + token_type String? + scope String? + id_token String? @db.Text + session_state String? + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@unique([provider, providerAccountId]) + @@index([userId]) +} + +model Session { + id String @id @default(cuid()) + sessionToken String @unique + userId String + expires DateTime + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@index([userId]) +} + +model VerificationToken { + identifier String + token String @unique + expires DateTime + + @@unique([identifier, token]) +} diff --git a/prisma/seed.ts b/prisma/seed.ts new file mode 100644 index 0000000..2aa0c3b --- /dev/null +++ b/prisma/seed.ts @@ -0,0 +1,76 @@ +import bcrypt from "bcryptjs"; +import { PrismaClient, RoleKey } from "@prisma/client"; +import { seedWeeklyKpiBaseline } from "../src/lib/kpis/persistence"; +import { ensureExperienceBaseline } from "../src/lib/experienciometro/persistence"; +import { ensureHumanCapitalWorkspaceBootstrap } from "../src/lib/human-capital/workspace"; + +const prisma = new PrismaClient(); + +async function main() { + const roles: Array<{ key: RoleKey; name: string }> = [ + { key: "owner", name: "Dueño" }, + { key: "leader", name: "Líder" }, + { key: "employee", name: "Empleado" }, + ]; + + for (const role of roles) { + await prisma.role.upsert({ + where: { key: role.key }, + update: { name: role.name }, + create: role, + }); + } + + const ownerEmail = (process.env.BOOTSTRAP_OWNER_EMAIL ?? "owner@casabenell.com").toLowerCase().trim(); + const ownerName = process.env.BOOTSTRAP_OWNER_NAME ?? "Owner Casa Benell"; + const ownerPassword = process.env.BOOTSTRAP_OWNER_PASSWORD ?? "ChangeMe123!"; + const ownerPasswordHash = await bcrypt.hash(ownerPassword, 12); + + const owner = await prisma.user.upsert({ + where: { email: ownerEmail }, + update: { + name: ownerName, + passwordHash: ownerPasswordHash, + status: "active", + emailVerified: new Date(), + }, + create: { + email: ownerEmail, + name: ownerName, + passwordHash: ownerPasswordHash, + status: "active", + emailVerified: new Date(), + }, + }); + + const ownerRole = await prisma.role.findUniqueOrThrow({ where: { key: "owner" } }); + + await prisma.userRole.upsert({ + where: { + userId_roleId: { + userId: owner.id, + roleId: ownerRole.id, + }, + }, + update: {}, + create: { + userId: owner.id, + roleId: ownerRole.id, + }, + }); + + await seedWeeklyKpiBaseline(prisma); + await ensureExperienceBaseline(prisma, owner.id); + await ensureHumanCapitalWorkspaceBootstrap(); + + console.log(`Seed ready. Owner: ${ownerEmail}`); +} + +main() + .catch((error) => { + console.error(error); + process.exit(1); + }) + .finally(async () => { + await prisma.$disconnect(); + }); diff --git a/project_plan.md b/project_plan.md new file mode 100644 index 0000000..58cff0a --- /dev/null +++ b/project_plan.md @@ -0,0 +1,472 @@ + +Plan + +project_plan.md — Projects Navigation V1.2 (Dedicated Pages + Weekly Calendar + Capture) +Summary +We will replace the current Projects “single long page + scroll-to-section” behavior with true page-based navigation and a Projects-specific information architecture. + +V1.2 will deliver: + +A coherent Projects sidebar with dedicated routes. +A real Meetings page with weekly calendar view. +Auto-populated calendar meetings with attendee confirmation (pending/accepted/declined). +A new Capture page for evidence (links + local file uploads on server). +Backward-compatible redirects from old alias routes. +This keeps the existing Projects operational backend and removes the current marketing-style navigation confusion. + +Current-State Findings (what we are fixing) +/departments/projects/initiatives and /departments/projects/meetings are aliases to the same page. +Projects page uses scrollIntoView based on pathname instead of real page navigation. +Sidebar/mobile nav currently repurposes generic “initiatives/meetings” keys and points to alias routes. +Meetings UI is embedded as a section (MeetingsWidget) instead of a dedicated planning surface. +Capture currently exists only as generic /data-entry placeholder, not Projects-specific evidence operations. +V1.2 Information Architecture (locked) +Projects Sidebar (for department=proyectos) +Overview → /departments/projects +Projects → /departments/projects/projects +Meetings → /departments/projects/meetings +Capture → /departments/projects/capture +Team → /departments/projects/team +Behavior rules +No sidebar item may trigger section scrolling. +Each sidebar item maps to its own page route and URL state. +Active nav state is based on pathname prefix, not hash/scroll. +Mobile nav uses identical route map and active logic. +Compatibility routes +/departments/projects/initiatives redirects to /departments/projects/projects. +Existing /departments/projects/meetings alias is replaced with a real page (no re-export). +Old intra-page anchor behavior is removed. +Page Specifications +1) Overview Page (/departments/projects) +Purpose: operational snapshot. + +Header: title, date/location filters, Nuevo proyecto. +KPI strip: active projects, milestones on-time, blocked tasks, overdue tasks, cost variance, profit/margin. +Alerts: blocked tasks, overdue milestones, cost variance risk. +Top portfolio summary table (read-only quick scan, row opens project drawer). +“My workload” panel for assigned tasks and upcoming commitments. +No meetings form here; only compact “next meetings” preview with link to Meetings page. +2) Projects Page (/departments/projects/projects) +Purpose: project execution workspace. + +Full portfolio table and filters. +Project detail drawer for definition, stages, financials, milestones, tasks. +Milestone board + task board with execution updates. +Leaders/owners can create/edit/delete project objects. +Employees see execution-only controls as currently enforced. +3) Meetings Page (/departments/projects/meetings) +Purpose: weekly planning and scheduling. + +Weekly calendar view (Mon-Sun), 30-min slots, default 07:00-21:00. +Hybrid feed: +My personal events. +Team meetings where user is organizer/participant. +Meeting cards display status + attendee response summary. +Create event modal: +Tipo: meeting or personal_event +title, agenda/notes, start/end, participants. +Meetings created from Projects are auto-added to calendar. +Confirmation flow: +Invitees start as pending. +Invitee can accept or decline. +Organizer sees response counts and unresolved attendees. +Filters: All, Mine, Team, Pending response. +4) Capture Page (/departments/projects/capture) +Purpose: evidence intake and traceability. + +Tabs: +Task Evidence +Project Evidence +My Uploads +Capture form: +association: project/task +evidence type: photo, document, link +title, note +link URL OR local file upload +Evidence list: +preview thumbnail (images), file badge, link badge +uploader, timestamp, associated project/task +Search/filter by project, task, uploader, date range. +Deletion permissions: +uploader can delete own evidence +leaders/owners can delete any project evidence. +5) Team Page (/departments/projects/team) +Purpose: project people visibility. + +Team members list, role, current assigned tasks, blocked items. +Quick links to person detail (/people/:id) and assigned projects. +Read-only in V1.2 (management actions remain in People module). +Data Model / Persistence Changes +Prisma additions +MarketingMeetingParticipant (new table): +id +meetingId +userId (nullable for external participants if needed) +displayName +responseStatus enum: pending | accepted | declined +respondedAt +unique composite (meetingId, userId) when userId is present +ProjectCalendarEvent (new table): +id, department (default proyectos) +ownerUserId +title, notes +startAt, endAt +visibility (personal|team) +createdAt, updatedAt +ProjectCaptureEvidence (new table): +id, department +initiativeId nullable +taskId nullable +uploadedById +kind (photo|document|link) +title, note +url nullable +storagePath nullable +mimeType nullable +sizeBytes nullable +createdAt, updatedAt +Local upload storage defaults (locked) +Storage mode: server local uploads. +Base directory: PROJECT_UPLOAD_DIR (default /home/mdares03/benell/uploads/projects). +Max file size: 10 MB. +Allowed mime/extensions: image/jpeg, image/png, image/webp, application/pdf. +Files served through authenticated API route, not direct public static path. +Filenames replaced with generated IDs (no user filename trust). +Public API / Interface / Type Changes +Navigation/access +Update nav mapping so Projects gets dedicated route set and labels. +Keep shared nav for other departments unchanged. +Update active-state logic in Sidebar and MobileNav for new subroutes. +Meetings + calendar APIs +GET /api/projects/meetings +include participants with responseStatus and counts. +POST /api/projects/meetings +creates meeting + participant rows (pending). +calendar inclusion metadata returned. +PATCH /api/projects/meetings/:id +schedule/complete/cancel as today, plus participant updates when needed. +PATCH /api/projects/meetings/:id/respond (new) +attendee sets accepted|declined. +GET /api/projects/calendar/events (new) +POST /api/projects/calendar/events (new) +PATCH /api/projects/calendar/events/:id (new) +DELETE /api/projects/calendar/events/:id (new) +Capture APIs +GET /api/projects/capture (new, filterable list) +POST /api/projects/capture/link (new) +POST /api/projects/capture/upload (new, multipart/form-data) +DELETE /api/projects/capture/:id (new) +GET /api/projects/capture/assets/:id (new, secure file stream) +Type updates +Add Projects nav type map for page keys/labels/routes. +Add MeetingParticipant, MeetingResponseStatus. +Add ProjectCalendarEvent. +Add ProjectCaptureEvidence + upload response payloads. +RBAC Rules (explicit) +Owner: +full access across Projects pages/APIs. +Projects leader: +full management in Projects module. +Projects employee: +can view all Projects pages. +can update assigned tasks. +can respond to meeting invitations involving them. +can create personal calendar events. +can upload evidence tied to own tasks/assigned projects. +Non-project departments: +cannot access /departments/projects/* pages or Projects APIs. +Implementation Sequence (decision complete) +Foundation refactor: +extract Projects page sections into reusable components. +remove scrollIntoView route behavior. +Route scaffolding: +add real pages for projects, meetings, capture, team. +add redirect from /departments/projects/initiatives. +Nav/access updates: +route maps, labels, active-state logic for desktop/mobile. +Meetings participant model: +Prisma migration + API updates + response endpoint. +Calendar events model: +Prisma migration + CRUD APIs + weekly calendar UI. +Capture model + local uploads: +Prisma migration + upload/link endpoints + secure asset serving. +Capture UI: +forms, association selectors, evidence feed, filters. +Regression pass: +ensure marketing routes remain unaffected and no broken links. +Production readiness: +upload dir creation/permissions + env vars + service restart checklist. +File Touchpoints (expected) +src/components/layout/nav-items.ts +src/components/layout/Sidebar.tsx +src/components/layout/MobileNav.tsx +src/lib/access-control.ts +src/app/(app)/departments/projects/page.tsx (overview only) +src/app/(app)/departments/projects/initiatives/page.tsx (redirect) +src/app/(app)/departments/projects/projects/page.tsx (new) +src/app/(app)/departments/projects/meetings/page.tsx (real calendar page) +src/app/(app)/departments/projects/capture/page.tsx (new) +src/app/(app)/departments/projects/team/page.tsx (new) +src/app/api/projects/meetings/route.ts +src/app/api/projects/meetings/[id]/route.ts +src/app/api/projects/meetings/[id]/respond/route.ts (new) +src/app/api/projects/calendar/events/route.ts (new) +src/app/api/projects/calendar/events/[id]/route.ts (new) +src/app/api/projects/capture/* (new route group) +src/lib/projects/types.ts +src/lib/projects/persistence.ts +prisma/schema.prisma +Prisma migration files for new models/enums. +Test Cases and Scenarios +Navigation correctness +Sidebar Overview/Projects/Meetings/Capture/Team each open distinct page. +No route causes in-page scroll jumps. +/departments/projects/initiatives redirects to /departments/projects/projects. +Mobile nav mirrors desktop routes and active states. +Meetings/calendar flow +Create Projects meeting from UI appears on weekly calendar. +Invitee sees meeting as pending. +Invitee accepts; organizer sees updated counts and status badge. +Invitee declines; event remains visible with declined marker. +Personal event creation appears only in owner’s view unless visibility is team. +Capture flow +Upload image/pdf successfully and metadata persists. +Add link evidence successfully and renders in feed. +Evidence can be tied to project and/or task. +Unauthorized user cannot fetch another department’s capture asset. +Delete permissions enforce uploader-or-manager rule. +RBAC +Employee cannot alter project definition/stage/targets. +Employee can update assigned tasks and submit evidence. +Non-project user gets 403 on projects APIs and pages. +Owner retains full access. +Regression +Marketing and other departments still navigate correctly. +Existing Projects financial logic remains intact. +Build/lint/typecheck pass. +No broken internal links in sidebar/mobile and page CTAs. +Acceptance Criteria +Projects navigation is page-based, not section-scroll based. +Meetings has a weekly calendar and confirmation workflow. +Meetings auto-populate calendar on creation. +Capture page supports links and local file uploads with evidence traceability. +Sidebar labels and routes are coherent for Projects operations. +No broken links and no cross-department permission leakage. +Assumptions and Defaults Chosen +Storage for Capture in V1.2 is local server filesystem (as selected). +Timezone display defaults to app/user locale; stored timestamps remain UTC. +Existing meeting statuses remain (requested/scheduled/completed/cancelled); attendee response is separate participant state. +Marketing module behavior and routes are not redesigned in this phase. +Team page is read-only in V1.2; People module remains source of truth for personnel edits. + +Execution Checklist (Projects Lead Runbook) +Objective +Ship V1.2 with stable navigation, calendar + attendee confirmations, and evidence capture without regressions in other departments. + +Operating cadence +Monday planning: +- review Overview KPIs and alerts. +- lock weekly priorities and milestone owners. +- publish all team meetings for the week in Meetings page. +Daily control loop: +- triage blocked and overdue items in Overview. +- unblock/escalate anything aging >24h. +- enforce project/task updates in Projects page only. +Friday close: +- verify evidence completeness in Capture. +- close unresolved meeting responses. +- publish weekly delivery summary and carry-over risks. + +Definition of done (workflow) +- A task cannot be marked complete without evidence (link or upload) tied to task or project. +- Every milestone has owner, due date, status, and risk flag. +- Every meeting has agenda, participants, and decision notes. +- Every blocker has escalation owner and next action date. + +Phase-based implementation checklist +Phase 0 - preflight (0.5 day) +- confirm V1.2 scope lock with engineering + design. +- confirm env var defaults for uploads and filesystem permissions. +- capture baseline behavior videos/screenshots for regression comparison. +- define feature flags if rollout will be staged. +Exit criteria: +- scope is frozen, rollout owner assigned, baseline captured. + +Phase 1 - navigation foundation (1 day) +- remove in-page scroll navigation behavior from Projects. +- implement dedicated Projects route map in desktop and mobile nav. +- add compatibility redirect `/departments/projects/initiatives -> /departments/projects/projects`. +- validate active-state logic by pathname prefix. +Exit criteria: +- each sidebar item opens a distinct page; no scroll jump behavior remains. + +Phase 2 - route scaffolding and page split (1 to 1.5 days) +- keep Overview at `/departments/projects`. +- create real pages: `/projects`, `/meetings`, `/capture`, `/team`. +- move existing reusable sections/components out of monolithic page. +- ensure role-based access gates on new routes. +Exit criteria: +- all five pages render and route correctly for allowed roles. + +Phase 3 - meetings participants model (1 day) +- Prisma migration for `MarketingMeetingParticipant`. +- update meetings list/create/update APIs to include participant rows and counts. +- add response endpoint `PATCH /api/projects/meetings/:id/respond`. +- display pending/accepted/declined summaries in meetings UI. +Exit criteria: +- invitation lifecycle works end-to-end with correct counts. + +Phase 4 - calendar events model + weekly UI (1.5 to 2 days) +- Prisma migration for `ProjectCalendarEvent`. +- CRUD APIs for calendar events. +- weekly calendar UI (Mon-Sun, 30-min slots, 07:00-21:00 default). +- auto-create calendar event when Projects meeting is created. +- filters: All, Mine, Team, Pending response. +Exit criteria: +- new meetings appear in calendar; personal and team events behave as expected. + +Phase 5 - capture model + secure local uploads (2 days) +- Prisma migration for `ProjectCaptureEvidence`. +- implement link create/list/delete APIs. +- implement upload endpoint with MIME/size validation (10 MB max). +- implement secure asset stream endpoint (authenticated, RBAC checked). +- implement capture UI tabs and filterable evidence feed. +Exit criteria: +- links and files are persisted, visible, downloadable, and permission-protected. + +Phase 6 - regression + hardening (1 day) +- run navigation, RBAC, meetings/calendar, and capture scenarios. +- run typecheck/lint/build and fix all blocking defects. +- verify non-project departments unchanged. +- verify no broken internal links. +Exit criteria: +- acceptance criteria pass and release notes are ready. + +Implementation Tickets (V1.2 Backlog) +PJT-1201 - Projects nav route map refactor +Scope: +- update `nav-items.ts`, `Sidebar.tsx`, `MobileNav.tsx` for dedicated Projects IA. +- remove alias/section-scroll assumptions. +Depends on: +- none +Acceptance: +- Projects shows Overview, Projects, Meetings, Capture, Team routes with correct active state. + +PJT-1202 - Redirect compatibility for legacy initiatives route +Scope: +- implement redirect from `/departments/projects/initiatives` to `/departments/projects/projects`. +Depends on: +- PJT-1201 +Acceptance: +- direct visits and in-app links always land on `/projects`. + +PJT-1203 - Split Projects monolith into dedicated pages +Scope: +- scaffold and render page-specific components for overview/projects/meetings/capture/team. +Depends on: +- PJT-1201 +Acceptance: +- each page has unique URL and no in-page scroll coupling. + +PJT-1204 - Meeting participant persistence model +Scope: +- add `MarketingMeetingParticipant` model and migration. +- wire participants into meetings APIs. +Depends on: +- PJT-1203 +Acceptance: +- meetings return participant response states and summary counts. + +PJT-1205 - Meeting invite response endpoint +Scope: +- add `PATCH /api/projects/meetings/:id/respond`. +- enforce participant-only response updates. +Depends on: +- PJT-1204 +Acceptance: +- invitees can accept/decline; organizer sees live status changes. + +PJT-1206 - Calendar events persistence + APIs +Scope: +- add `ProjectCalendarEvent` model + CRUD routes. +- enforce personal/team visibility rules. +Depends on: +- PJT-1203 +Acceptance: +- create/edit/delete works with role constraints and list filters. + +PJT-1207 - Weekly calendar UI +Scope: +- render weekly grid in Meetings page with 30-min slots and default hours. +- add create event modal. +Depends on: +- PJT-1206 +Acceptance: +- events show in correct slots and respect filter mode. + +PJT-1208 - Auto-populate calendar from meetings +Scope: +- create/maintain linked calendar records for project meetings. +Depends on: +- PJT-1204, PJT-1206 +Acceptance: +- meeting creation always appears in calendar without manual duplication. + +PJT-1209 - Capture evidence persistence model +Scope: +- add `ProjectCaptureEvidence` model + migration. +Depends on: +- PJT-1203 +Acceptance: +- evidence rows support project/task relation, metadata, and ownership. + +PJT-1210 - Capture link and upload APIs +Scope: +- implement list/create/delete routes for link and file evidence. +- enforce 10 MB and MIME allowlist. +Depends on: +- PJT-1209 +Acceptance: +- valid uploads succeed, invalid files are rejected with clear errors. + +PJT-1211 - Secure capture asset streaming +Scope: +- authenticated download route by evidence id. +- prevent cross-department and unauthorized access. +Depends on: +- PJT-1210 +Acceptance: +- authorized users can fetch assets; unauthorized users receive 403. + +PJT-1212 - Capture UI tabs and evidence feed +Scope: +- implement Task Evidence / Project Evidence / My Uploads tabs. +- implement preview badges and filters. +Depends on: +- PJT-1210, PJT-1211 +Acceptance: +- users can upload, filter, preview, and delete per permission rules. + +PJT-1213 - Projects RBAC enforcement sweep +Scope: +- verify page/API protections for owner, leader, employee, non-project users. +Depends on: +- PJT-1203 through PJT-1212 +Acceptance: +- RBAC matrix in plan passes all listed scenarios. + +PJT-1214 - Regression, QA, and release checklist +Scope: +- execute test scenarios and fix defects. +- finalize deployment steps: upload directory, env vars, service restart checks. +Depends on: +- PJT-1201 through PJT-1213 +Acceptance: +- all acceptance criteria pass; no navigation regressions outside Projects. + +KPIs to track during rollout +- Navigation defects: target 0 critical. +- Meeting invite response within 24h: target >=90%. +- Evidence completeness on completed tasks: target >=95%. +- Blocker aging >48h: target 0 without escalation owner. +- Milestones delivered on time: upward trend week-over-week. diff --git a/public/brand/logo.png b/public/brand/logo.png new file mode 100644 index 0000000..3a6505f Binary files /dev/null and b/public/brand/logo.png differ diff --git a/public/brand/logo.webp b/public/brand/logo.webp new file mode 100644 index 0000000..0bd4bee Binary files /dev/null and b/public/brand/logo.webp differ diff --git a/public/brand/mascot.png b/public/brand/mascot.png new file mode 100644 index 0000000..d5d8a08 Binary files /dev/null and b/public/brand/mascot.png differ diff --git a/public/file.svg b/public/file.svg new file mode 100644 index 0000000..004145c --- /dev/null +++ b/public/file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/globe.svg b/public/globe.svg new file mode 100644 index 0000000..567f17b --- /dev/null +++ b/public/globe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/next.svg b/public/next.svg new file mode 100644 index 0000000..5174b28 --- /dev/null +++ b/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/vercel.svg b/public/vercel.svg new file mode 100644 index 0000000..7705396 --- /dev/null +++ b/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/window.svg b/public/window.svg new file mode 100644 index 0000000..b2b2a44 --- /dev/null +++ b/public/window.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/app/(app)/dashboard/page.tsx b/src/app/(app)/dashboard/page.tsx new file mode 100644 index 0000000..0732cef --- /dev/null +++ b/src/app/(app)/dashboard/page.tsx @@ -0,0 +1,218 @@ +"use client"; + +import { useEffect, useMemo, useState } from "react"; +import Link from "next/link"; +import { useRouter } from "next/navigation"; +import { useSession } from "next-auth/react"; +import KPIStatCard from "@/components/KPIStatCard"; +import WeeklyKpiBoard from "@/components/kpis/WeeklyKpiBoard"; +import SankeyChart from "@/components/SankeyChart"; +import { financialPreview } from "@/lib/mock"; +import { useUIStore } from "@/lib/store/ui-store"; +import { getDepartmentHomeRoute } from "@/lib/access-control"; +import { applyRangeMultiplier, formatCurrencyK, ROLE_LABELS } from "@/lib/utils"; +import type { DepartmentHealth, DepartmentKey, UserRole } from "@/lib/types"; +import type { OwnerCaptureRollupDTO } from "@/lib/capture"; + +const OWNER_CARD_ORDER: Array<"marketing" | "capital_humano" | "operaciones" | "proyectos"> = [ + "marketing", + "capital_humano", + "operaciones", + "proyectos", +]; +const OWNER_CARD_ROUTE: Record<(typeof OWNER_CARD_ORDER)[number], string> = { + marketing: "/data-entry?department=marketing", + capital_humano: "/data-entry?department=capital_humano", + operaciones: "/data-entry?department=operaciones", + proyectos: "/data-entry?department=proyectos", +}; + +export default function DashboardPage() { + const router = useRouter(); + const { data: session, status } = useSession(); + const dateRange = useUIStore((state) => state.dateRange); + const role = (session?.user?.role ?? "employee") as UserRole; + const isOwner = role === "owner"; + const [captureRollup, setCaptureRollup] = useState(null); + const [rollupError, setRollupError] = useState(null); + const [isRollupLoading, setIsRollupLoading] = useState(false); + + useEffect(() => { + if (status !== "authenticated") { + return; + } + + if (session.user.role === "owner" || session.user.role === "leader") { + return; + } + + const department = (session.user.department as DepartmentKey | null | undefined) ?? null; + router.replace(getDepartmentHomeRoute(department)); + }, [router, session, status]); + + useEffect(() => { + if (status !== "authenticated" || !isOwner) { + return; + } + + let cancelled = false; + + async function loadRollup() { + setIsRollupLoading(true); + try { + const response = await fetch("/api/owner/capture-rollup", { + method: "GET", + cache: "no-store", + }); + const payload = (await response.json()) as OwnerCaptureRollupDTO & { error?: string }; + if (!response.ok) { + throw new Error(payload.error ?? "No se pudo cargar capture rollup."); + } + + if (!cancelled) { + setCaptureRollup(payload); + setRollupError(null); + } + } catch (error) { + if (!cancelled) { + setRollupError(error instanceof Error ? error.message : "No se pudo cargar capture rollup."); + } + } finally { + if (!cancelled) { + setIsRollupLoading(false); + } + } + } + + void loadRollup(); + + return () => { + cancelled = true; + }; + }, [isOwner, status]); + + const ownerDepartmentCards = useMemo(() => { + const rollupByDepartment = new Map(captureRollup?.departments.map((department) => [department.department, department]) ?? []); + + return OWNER_CARD_ORDER.map((department) => { + const rollup = rollupByDepartment.get(department); + if (!rollup) { + return null; + } + + const summary = + rollup.automationHealth === "healthy" + ? "Automation healthy" + : rollup.automationHealth === "attention" + ? "Automation attention" + : "Automation down"; + + const card: DepartmentHealth = { + id: rollup.department, + name: rollup.label, + score: Math.round(rollup.capturePct), + summary, + metrics: [ + { label: "Captured", value: `${rollup.capturedRows}/${rollup.totalRows}` }, + { label: "Stale", value: String(rollup.staleRows) }, + { label: "At risk", value: String(rollup.atRiskRows) }, + ], + }; + + return { + ...card, + href: OWNER_CARD_ROUTE[department], + }; + }).filter((entry): entry is DepartmentHealth & { href: string } => Boolean(entry)); + }, [captureRollup]); + + const previewData = useMemo( + () => ({ + nodes: financialPreview.nodes.map((node) => { + const value = applyRangeMultiplier(node.value, dateRange); + + return { + ...node, + value, + name: + node.name === "Ingreso" + ? `${node.name} (${formatCurrencyK(value)})` + : node.name === "Utilidad" + ? `${node.name} (${formatCurrencyK(value)})` + : `${node.name}`, + }; + }), + links: financialPreview.links.map((link) => ({ + ...link, + value: applyRangeMultiplier(link.value, dateRange), + })), + }), + [dateRange] + ); + + if (status === "loading") { + return

Cargando...

; + } + + if (status === "authenticated" && session?.user.role === "employee") { + return

Redirigiendo...

; + } + + return ( +
+
+

Hola, {session?.user?.name?.trim() || "Dueño"}

+

+ Resumen semanal de Casa Benell · Vista {ROLE_LABELS[role]} +

+
+ + {isOwner ? ( + <> +
+
+

+ Resumen de Sankey · Últimas 4 semanas +

+ + Ver detalle completo → + +
+ + { + const selected = nodeName.split(" (")[0].toLowerCase(); + router.push(`/financial-flow?node=${encodeURIComponent(selected)}`); + }} + /> +
+ + +

+ KPIs por área +

+ {isRollupLoading ?

Cargando capture rollups...

: null} + {rollupError ?

{rollupError}

: null} +
+ {ownerDepartmentCards.map((card) => ( + + ))} +
+ + } + /> + + ) : ( + + )} +
+ ); +} diff --git a/src/app/(app)/dashboard/print/page.tsx b/src/app/(app)/dashboard/print/page.tsx new file mode 100644 index 0000000..9b0a5c8 --- /dev/null +++ b/src/app/(app)/dashboard/print/page.tsx @@ -0,0 +1,146 @@ +import { getServerSession } from "next-auth"; +import { redirect } from "next/navigation"; +import PrintToolbar from "@/components/kpis/PrintToolbar"; +import { authOptions } from "@/lib/auth"; +import { getDepartmentHomeRoute } from "@/lib/access-control"; +import { canViewWeeklyKpiBoard, getWeeklyKpiBoard } from "@/lib/kpis/persistence"; +import type { KpiRowStatus } from "@/lib/kpis/types"; + +export const dynamic = "force-dynamic"; + +const STATUS_LABELS: Record = { + on_track: "En ruta", + watch: "Seguimiento", + risk: "Riesgo", + no_score: "Sin score", +}; + +function formatDate(isoDate: string | null): string { + if (!isoDate) { + return "-"; + } + + const parsed = new Date(`${isoDate}T00:00:00.000Z`); + return new Intl.DateTimeFormat("es-MX", { + day: "2-digit", + month: "short", + year: "numeric", + timeZone: "UTC", + }).format(parsed); +} + +export default async function DashboardPrintPage({ + searchParams, +}: { + searchParams?: { weekStart?: string }; +}) { + const session = await getServerSession(authOptions); + + if (!session?.user) { + redirect("/login"); + } + + const viewer = { + role: session.user.role, + department: session.user.department ?? null, + }; + + if (!canViewWeeklyKpiBoard(viewer)) { + redirect(getDepartmentHomeRoute(viewer.department)); + } + + const board = await getWeeklyKpiBoard({ + weekStartInput: searchParams?.weekStart, + viewer, + }); + + return ( +
+ + + + +
+

Weekly KPI Board

+

+ Semana {board.weekStart} a {board.weekEnd} · Fuente: {board.source} +

+

+ Actualizado: {board.lastUpdatedAt ? new Date(board.lastUpdatedAt).toLocaleString("es-MX") : "Sin actualización"} +

+

+ Total KPIs: {board.summary.totalRows} · En ruta: {board.summary.onTrackPct.toFixed(1)}% · Riesgo: {board.summary.atRiskRows} +

+

+ Cobertura: {board.coverage.mappedSections}/{board.coverage.totalSections} secciones mapeadas ({board.coverage.mappedPct.toFixed(1)}%) +

+
+ +
+
+

KPIs Totales

+

{board.summary.totalRows}

+
+
+

En Ruta

+

{board.summary.onTrackPct.toFixed(1)}%

+
+
+

Riesgo

+

{board.summary.atRiskRows}

+
+
+

Vencen pronto

+

{board.summary.dueSoonRows}

+
+
+ +
+ {board.sections.map((section) => ( +
+
+

{section.rawSectionLabel}

+

{section.ownerTeamLabel || "Sin owner"}

+
+
+ + + + + + + + + + + + + {section.rows.map((row) => ( + + + + + + + + + ))} + +
ResponsabilidadObjetivo / IndicadorQuantity & Quality% CumplimientoFecha / CompromisoEstatus
{row.responsibility}{row.objectiveIndicator || "-"}{row.quantityQuality || "-"} + {row.compliancePct !== null ? `${row.compliancePct.toFixed(1)}%` : row.compliance || "-"} + +

{row.dueCommitment || "-"}

+

{formatDate(row.dueDate)}

+
{STATUS_LABELS[row.status]}
+
+
+ ))} +
+
+ ); +} diff --git a/src/app/(app)/data-entry/page.tsx b/src/app/(app)/data-entry/page.tsx new file mode 100644 index 0000000..44eca0f --- /dev/null +++ b/src/app/(app)/data-entry/page.tsx @@ -0,0 +1,11 @@ +import CaptureWorkspace from "@/components/capture/CaptureWorkspace"; + +export default function DataEntryPage({ + searchParams, +}: { + searchParams?: { + department?: string; + }; +}) { + return ; +} diff --git a/src/app/(app)/departments/admin/page.tsx b/src/app/(app)/departments/admin/page.tsx new file mode 100644 index 0000000..62d56c7 --- /dev/null +++ b/src/app/(app)/departments/admin/page.tsx @@ -0,0 +1,5 @@ +import { redirect } from "next/navigation"; + +export default function AdminDepartmentPage() { + redirect("/financial-flow"); +} diff --git a/src/app/(app)/departments/finance/page.tsx b/src/app/(app)/departments/finance/page.tsx new file mode 100644 index 0000000..e60d05a --- /dev/null +++ b/src/app/(app)/departments/finance/page.tsx @@ -0,0 +1,5 @@ +import { redirect } from "next/navigation"; + +export default function FinanceDepartmentPage() { + redirect("/financial-flow"); +} diff --git a/src/app/(app)/departments/human-capital/[tab]/page.tsx b/src/app/(app)/departments/human-capital/[tab]/page.tsx new file mode 100644 index 0000000..b466a8b --- /dev/null +++ b/src/app/(app)/departments/human-capital/[tab]/page.tsx @@ -0,0 +1,1341 @@ +"use client"; + +import Link from "next/link"; +import { useCallback, useEffect, useMemo, useState } from "react"; +import { useParams } from "next/navigation"; +import { Bar, BarChart, CartesianGrid, Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts"; +import { useSession } from "next-auth/react"; +import DepartmentKpiTracker from "@/components/kpis/DepartmentKpiTracker"; +import type { + HumanCapitalComplianceObligationDTO, + HumanCapitalDashboardResponse, + HumanCapitalFilesListItemDTO, + HumanCapitalOrgTargetDTO, + HumanCapitalPayrollSummaryDTO, + HumanCapitalPersonFilesDTO, + HumanCapitalVacancyDTO, + HumanCapitalCareerContentDTO, + HumanCapitalWorkspaceTabConfig, +} from "@/lib/human-capital/types"; + +const DEFAULT_TABS: HumanCapitalWorkspaceTabConfig[] = [ + { key: "resumen", label: "Resumen", order: 0, visible: true, widgets: ["summary_metrics"] }, + { key: "expedientes", label: "Expedientes", order: 1, visible: true, widgets: ["people_files_table"] }, + { key: "nomina", label: "Nomina", order: 2, visible: true, widgets: ["payroll_summary"] }, + { + key: "reclutamiento", + label: "Reclutamiento", + order: 3, + visible: true, + widgets: ["recruitment_headcount", "recruitment_vacancies", "recruitment_org_target"], + }, + { key: "desarrollo", label: "Desarrollo", order: 4, visible: true, widgets: ["career_feed"] }, + { key: "cumplimiento", label: "Cumplimiento", order: 5, visible: true, widgets: ["compliance_overview"] }, +]; + +type HumanCapitalPayrollRunDTO = { + id: string; + periodStart: string; + periodEnd: string; + frequency: string; + locationCode: string | null; + status: string; + source: string | null; + externalRef: string | null; + importedAt: string; +}; + +type HumanCapitalCompliancePaymentDTO = { + id: string; + obligationId: string; + paymentDate: string; + amount: number; + status: string; + referencePeriod: string | null; + receiptUrl: string | null; + note: string | null; +}; + +type HumanCapitalFileEditorItem = HumanCapitalPersonFilesDTO["items"][number]; + +function normalizeTab(value: string | string[] | undefined) { + if (!value || Array.isArray(value)) { + return "resumen"; + } + return value; +} + +export default function HumanCapitalWorkspaceTabPage() { + const params = useParams<{ tab: string }>(); + const activeTab = normalizeTab(params?.tab); + const { data: session } = useSession(); + const canManage = + session?.user?.role === "owner" || + (session?.user?.role === "leader" && + (session?.user?.department === "capital_humano" || session?.user?.department === "administracion")); + + const [dashboard, setDashboard] = useState(null); + const [workspaceTabs, setWorkspaceTabs] = useState(DEFAULT_TABS); + const [peopleFiles, setPeopleFiles] = useState([]); + const [vacancies, setVacancies] = useState([]); + const [orgTargets, setOrgTargets] = useState([]); + const [headcount, setHeadcount] = useState<{ total: number; breakdown: Array<{ locationCode: string; roleKey: string; count: number }> } | null>(null); + const [payrollSummary, setPayrollSummary] = useState(null); + const [payrollRuns, setPayrollRuns] = useState([]); + const [announcements, setAnnouncements] = useState([]); + const [courses, setCourses] = useState([]); + const [compliance, setCompliance] = useState([]); + const [payments, setPayments] = useState([]); + const [error, setError] = useState(null); + const [loading, setLoading] = useState(false); + + const [payrollDraft, setPayrollDraft] = useState({ + periodStart: "", + periodEnd: "", + frequency: "weekly" as "weekly" | "biweekly", + locationCode: "", + externalRef: "", + csvText: "", + }); + + const [vacancyDraft, setVacancyDraft] = useState({ locationCode: "", roleKey: "", title: "" }); + const [orgDraft, setOrgDraft] = useState({ locationCode: "", roleKey: "", targetCount: "" }); + const [announcementDraft, setAnnouncementDraft] = useState({ title: "", body: "" }); + const [courseDraft, setCourseDraft] = useState({ title: "", body: "" }); + const [complianceDraft, setComplianceDraft] = useState({ body: "imss", title: "", dueDate: "" }); + const [paymentDraft, setPaymentDraft] = useState({ obligationId: "", paymentDate: "", amount: 0 }); + const [selectedFileUserId, setSelectedFileUserId] = useState(null); + const [selectedPersonFile, setSelectedPersonFile] = useState(null); + const [loadingPersonFile, setLoadingPersonFile] = useState(false); + const [savingPersonFile, setSavingPersonFile] = useState(false); + const [uploadingByRequirement, setUploadingByRequirement] = useState>({}); + + const activeTabConfig = useMemo( + () => workspaceTabs.find((tab) => tab.key === activeTab), + [workspaceTabs, activeTab] + ); + + const activeWidgets = useMemo(() => activeTabConfig?.widgets ?? [], [activeTabConfig]); + + const showSummary = activeWidgets.includes("summary_metrics"); + const showFiles = activeWidgets.includes("people_files_table"); + const showPayroll = activeWidgets.includes("payroll_summary"); + const showRecruitmentHeadcount = activeWidgets.includes("recruitment_headcount"); + const showRecruitmentVacancies = activeWidgets.includes("recruitment_vacancies"); + const showRecruitmentOrg = activeWidgets.includes("recruitment_org_target"); + const showCareer = activeWidgets.includes("career_feed"); + const showCompliance = activeWidgets.includes("compliance_overview"); + + const loadResumen = useCallback(async () => { + setLoading(true); + try { + const response = await fetch("/api/human-capital/dashboard", { method: "GET", cache: "no-store" }); + const payload = (await response.json()) as HumanCapitalDashboardResponse & { error?: string }; + if (!response.ok) { + throw new Error(payload.error ?? "No se pudo cargar Capital Humano."); + } + setDashboard(payload); + } catch (loadError) { + setError(loadError instanceof Error ? loadError.message : "No se pudo cargar Capital Humano."); + } finally { + setLoading(false); + } + }, []); + + const loadFiles = useCallback(async () => { + setLoading(true); + try { + const response = await fetch("/api/human-capital/files", { method: "GET", cache: "no-store" }); + const payload = (await response.json().catch(() => ({}))) as { files?: HumanCapitalFilesListItemDTO[]; error?: string }; + if (!response.ok) { + throw new Error(payload.error ?? "No se pudieron cargar expedientes."); + } + setPeopleFiles(payload.files ?? []); + } catch (loadError) { + setError(loadError instanceof Error ? loadError.message : "No se pudieron cargar expedientes."); + } finally { + setLoading(false); + } + }, []); + + const loadRecruitment = useCallback(async (includeHeadcount = false) => { + if (!canManage) { + return; + } + setLoading(true); + try { + const requests = [ + fetch("/api/human-capital/recruitment/vacancies", { method: "GET", cache: "no-store" }), + fetch("/api/human-capital/recruitment/org-targets", { method: "GET", cache: "no-store" }), + ]; + if (includeHeadcount) { + requests.push(fetch("/api/human-capital/recruitment/headcount", { method: "GET", cache: "no-store" })); + } + + const [vacanciesResponse, orgResponse, headcountResponse] = await Promise.all(requests); + const vacanciesPayload = (await vacanciesResponse.json()) as { vacancies?: HumanCapitalVacancyDTO[]; error?: string }; + if (!vacanciesResponse.ok) { + throw new Error(vacanciesPayload.error ?? "No se pudieron cargar vacantes."); + } + const orgPayload = (await orgResponse.json()) as { targets?: HumanCapitalOrgTargetDTO[]; error?: string }; + if (!orgResponse.ok) { + throw new Error(orgPayload.error ?? "No se pudieron cargar organigramas."); + } + if (includeHeadcount && headcountResponse) { + const headcountPayload = (await headcountResponse.json()) as { total?: number; breakdown?: Array<{ locationCode: string; roleKey: string; count: number }>; error?: string }; + if (!headcountResponse.ok) { + throw new Error(headcountPayload.error ?? "No se pudo cargar headcount."); + } + setHeadcount({ total: headcountPayload.total ?? 0, breakdown: headcountPayload.breakdown ?? [] }); + } + setVacancies(vacanciesPayload.vacancies ?? []); + setOrgTargets(orgPayload.targets ?? []); + } catch (loadError) { + setError(loadError instanceof Error ? loadError.message : "No se pudo cargar reclutamiento."); + } finally { + setLoading(false); + } + }, [canManage]); + + const loadPayroll = useCallback(async () => { + setLoading(true); + try { + const [summaryResponse, runsResponse] = await Promise.all([ + fetch("/api/human-capital/payroll/summary", { method: "GET", cache: "no-store" }), + fetch("/api/human-capital/payroll/runs", { method: "GET", cache: "no-store" }), + ]); + + const summaryPayload = (await summaryResponse.json()) as { summary?: HumanCapitalPayrollSummaryDTO | null; error?: string }; + if (!summaryResponse.ok) { + throw new Error(summaryPayload.error ?? "No se pudo cargar nomina."); + } + const runsPayload = (await runsResponse.json()) as { runs?: HumanCapitalPayrollRunDTO[]; error?: string }; + if (!runsResponse.ok) { + throw new Error(runsPayload.error ?? "No se pudieron cargar corridas."); + } + setPayrollSummary(summaryPayload.summary ?? null); + setPayrollRuns(runsPayload.runs ?? []); + } catch (loadError) { + setError(loadError instanceof Error ? loadError.message : "No se pudo cargar nomina."); + } finally { + setLoading(false); + } + }, []); + + const loadDevelopment = useCallback(async () => { + setLoading(true); + try { + const [annResponse, courseResponse] = await Promise.all([ + fetch("/api/human-capital/career/announcements", { method: "GET", cache: "no-store" }), + fetch("/api/human-capital/career/courses", { method: "GET", cache: "no-store" }), + ]); + + const annPayload = (await annResponse.json()) as { announcements?: HumanCapitalCareerContentDTO[]; error?: string }; + if (!annResponse.ok) { + throw new Error(annPayload.error ?? "No se pudieron cargar anuncios."); + } + const coursePayload = (await courseResponse.json()) as { courses?: HumanCapitalCareerContentDTO[]; error?: string }; + if (!courseResponse.ok) { + throw new Error(coursePayload.error ?? "No se pudieron cargar cursos."); + } + setAnnouncements(annPayload.announcements ?? []); + setCourses(coursePayload.courses ?? []); + } catch (loadError) { + setError(loadError instanceof Error ? loadError.message : "No se pudo cargar desarrollo."); + } finally { + setLoading(false); + } + }, []); + + const loadCompliance = useCallback(async () => { + setLoading(true); + try { + const [obligationResponse, paymentResponse] = await Promise.all([ + fetch("/api/human-capital/compliance/obligations", { method: "GET", cache: "no-store" }), + fetch("/api/human-capital/compliance/payments", { method: "GET", cache: "no-store" }), + ]); + + const obligationPayload = (await obligationResponse.json()) as { obligations?: HumanCapitalComplianceObligationDTO[]; error?: string }; + if (!obligationResponse.ok) { + throw new Error(obligationPayload.error ?? "No se pudo cargar cumplimiento."); + } + const paymentPayload = (await paymentResponse.json()) as { payments?: HumanCapitalCompliancePaymentDTO[]; error?: string }; + if (!paymentResponse.ok) { + throw new Error(paymentPayload.error ?? "No se pudo cargar pagos."); + } + setCompliance(obligationPayload.obligations ?? []); + setPayments(paymentPayload.payments ?? []); + } catch (loadError) { + setError(loadError instanceof Error ? loadError.message : "No se pudo cargar cumplimiento."); + } finally { + setLoading(false); + } + }, []); + + useEffect(() => { + setError(null); + if (showSummary) { + void loadResumen(); + } + if (showFiles && canManage) { + void loadFiles(); + } + if ((showRecruitmentHeadcount || showRecruitmentVacancies || showRecruitmentOrg) && canManage) { + void loadRecruitment(showRecruitmentHeadcount); + } + if (showPayroll && canManage) { + void loadPayroll(); + } + if (showCareer) { + void loadDevelopment(); + } + if (showCompliance && canManage) { + void loadCompliance(); + } + }, [canManage, loadCompliance, loadDevelopment, loadFiles, loadPayroll, loadRecruitment, loadResumen, showCareer, showCompliance, showFiles, showPayroll, showRecruitmentHeadcount, showRecruitmentOrg, showRecruitmentVacancies, showSummary]); + + useEffect(() => { + let cancelled = false; + + async function loadConfig() { + try { + const response = await fetch("/api/human-capital/workspace/config", { method: "GET", cache: "no-store" }); + const payload = (await response.json()) as { config?: { tabs?: HumanCapitalWorkspaceTabConfig[] } }; + if (!cancelled && response.ok && payload.config?.tabs) { + setWorkspaceTabs(payload.config.tabs); + } + } catch { + if (!cancelled) { + setWorkspaceTabs(DEFAULT_TABS); + } + } + } + + void loadConfig(); + return () => { + cancelled = true; + }; + }, []); + + const topPeople = useMemo(() => peopleFiles.slice(0, 8), [peopleFiles]); + + const locationOptions = useMemo(() => { + const values = new Set(); + for (const person of peopleFiles) { + if (person.locationId) { + values.add(person.locationId); + } + } + for (const vacancy of vacancies) { + values.add(vacancy.locationCode); + } + for (const target of orgTargets) { + values.add(target.locationCode); + } + for (const row of headcount?.breakdown ?? []) { + values.add(row.locationCode); + } + return Array.from(values).sort((a, b) => a.localeCompare(b)); + }, [headcount, orgTargets, peopleFiles, vacancies]); + + const roleOptions = useMemo(() => { + const values = new Set(); + for (const person of peopleFiles) { + if (person.departmentRole) { + values.add(person.departmentRole); + } + } + for (const vacancy of vacancies) { + values.add(vacancy.roleKey); + } + for (const target of orgTargets) { + values.add(target.roleKey); + } + for (const row of headcount?.breakdown ?? []) { + values.add(row.roleKey); + } + return Array.from(values).sort((a, b) => a.localeCompare(b)); + }, [headcount, orgTargets, peopleFiles, vacancies]); + + const loadPersonFile = useCallback(async (userId: string) => { + setSelectedFileUserId(userId); + setLoadingPersonFile(true); + setError(null); + try { + const response = await fetch(`/api/human-capital/files/${userId}`, { method: "GET", cache: "no-store" }); + const payload = (await response.json().catch(() => ({}))) as { file?: HumanCapitalPersonFilesDTO; error?: string }; + if (!response.ok || !payload.file) { + throw new Error(payload.error ?? "No se pudo cargar el expediente."); + } + setSelectedPersonFile(payload.file); + } catch (loadError) { + setSelectedPersonFile(null); + setError(loadError instanceof Error ? loadError.message : "No se pudo cargar el expediente."); + } finally { + setLoadingPersonFile(false); + } + }, []); + + const updatePersonFileItem = useCallback( + ( + requirementId: string, + patch: Partial> + ) => { + setSelectedPersonFile((previous) => { + if (!previous) { + return previous; + } + return { + ...previous, + items: previous.items.map((item) => (item.requirementId === requirementId ? { ...item, ...patch } : item)), + }; + }); + }, + [] + ); + + const uploadPersonFileEvidence = useCallback( + async (requirementId: string, file: File) => { + if (!selectedPersonFile) { + return; + } + + setUploadingByRequirement((previous) => ({ ...previous, [requirementId]: true })); + setError(null); + + try { + const formData = new FormData(); + formData.append("file", file); + + const response = await fetch("/api/human-capital/files/upload", { + method: "POST", + body: formData, + }); + const payload = (await response.json().catch(() => ({}))) as { fileUrl?: string; error?: string }; + + if (!response.ok || !payload.fileUrl) { + throw new Error(payload.error ?? "No se pudo subir el archivo."); + } + + const currentItem = selectedPersonFile.items.find((item) => item.requirementId === requirementId); + updatePersonFileItem(requirementId, { + evidenceUrl: payload.fileUrl, + status: currentItem?.status === "missing" ? "submitted" : currentItem?.status, + }); + } catch (uploadError) { + setError(uploadError instanceof Error ? uploadError.message : "No se pudo subir el archivo."); + } finally { + setUploadingByRequirement((previous) => ({ ...previous, [requirementId]: false })); + } + }, + [selectedPersonFile, updatePersonFileItem] + ); + + const savePersonFile = useCallback(async () => { + if (!selectedPersonFile) { + return; + } + + setSavingPersonFile(true); + setError(null); + + try { + const response = await fetch(`/api/human-capital/files/${selectedPersonFile.userId}`, { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + items: selectedPersonFile.items.map((item) => ({ + requirementId: item.requirementId, + status: item.status, + valueText: item.valueText?.trim() || null, + evidenceUrl: item.evidenceUrl?.trim() || null, + note: item.note?.trim() || null, + })), + }), + }); + const payload = (await response.json().catch(() => ({}))) as { error?: string }; + if (!response.ok) { + throw new Error(payload.error ?? "No se pudo guardar el expediente."); + } + + await loadFiles(); + await loadPersonFile(selectedPersonFile.userId); + } catch (saveError) { + setError(saveError instanceof Error ? saveError.message : "No se pudo guardar el expediente."); + } finally { + setSavingPersonFile(false); + } + }, [loadFiles, loadPersonFile, selectedPersonFile]); + + const publishPayroll = async () => { + setError(null); + try { + const response = await fetch("/api/human-capital/payroll/import", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + periodStart: payrollDraft.periodStart, + periodEnd: payrollDraft.periodEnd, + frequency: payrollDraft.frequency, + locationCode: payrollDraft.locationCode || null, + externalRef: payrollDraft.externalRef || null, + csvText: payrollDraft.csvText, + }), + }); + const payload = (await response.json()) as { error?: string }; + if (!response.ok) { + throw new Error(payload.error ?? "No se pudo importar nomina."); + } + setPayrollDraft({ periodStart: "", periodEnd: "", frequency: "weekly", locationCode: "", externalRef: "", csvText: "" }); + await loadPayroll(); + } catch (publishError) { + setError(publishError instanceof Error ? publishError.message : "No se pudo importar nomina."); + } + }; + + const createVacancy = async () => { + if (!vacancyDraft.locationCode || !vacancyDraft.roleKey || !vacancyDraft.title) { + return; + } + setError(null); + try { + const response = await fetch("/api/human-capital/recruitment/vacancies", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(vacancyDraft), + }); + const payload = (await response.json()) as { error?: string }; + if (!response.ok) { + throw new Error(payload.error ?? "No se pudo crear vacante."); + } + setVacancyDraft({ locationCode: "", roleKey: "", title: "" }); + await loadRecruitment(showRecruitmentHeadcount); + } catch (err) { + setError(err instanceof Error ? err.message : "No se pudo crear vacante."); + } + }; + + const createOrgTarget = async () => { + const targetCount = Number(orgDraft.targetCount); + if (!orgDraft.locationCode || !orgDraft.roleKey || orgDraft.targetCount.trim() === "" || !Number.isFinite(targetCount)) { + setError("Completa ubicacion, rol y objetivo numerico."); + return; + } + setError(null); + try { + const response = await fetch("/api/human-capital/recruitment/org-targets", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + locationCode: orgDraft.locationCode, + roleKey: orgDraft.roleKey, + targetCount, + }), + }); + const payload = (await response.json()) as { error?: string }; + if (!response.ok) { + throw new Error(payload.error ?? "No se pudo crear objetivo."); + } + setOrgDraft({ locationCode: "", roleKey: "", targetCount: "" }); + await loadRecruitment(showRecruitmentHeadcount); + } catch (err) { + setError(err instanceof Error ? err.message : "No se pudo crear objetivo."); + } + }; + + const publishAnnouncement = async () => { + if (!announcementDraft.title.trim() || !announcementDraft.body.trim()) { + return; + } + setError(null); + try { + const response = await fetch("/api/human-capital/career/announcements", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(announcementDraft), + }); + const payload = (await response.json()) as { error?: string }; + if (!response.ok) { + throw new Error(payload.error ?? "No se pudo publicar anuncio."); + } + setAnnouncementDraft({ title: "", body: "" }); + await loadDevelopment(); + } catch (err) { + setError(err instanceof Error ? err.message : "No se pudo publicar anuncio."); + } + }; + + const publishCourse = async () => { + if (!courseDraft.title.trim() || !courseDraft.body.trim()) { + return; + } + setError(null); + try { + const response = await fetch("/api/human-capital/career/courses", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(courseDraft), + }); + const payload = (await response.json()) as { error?: string }; + if (!response.ok) { + throw new Error(payload.error ?? "No se pudo publicar curso."); + } + setCourseDraft({ title: "", body: "" }); + await loadDevelopment(); + } catch (err) { + setError(err instanceof Error ? err.message : "No se pudo publicar curso."); + } + }; + + const createObligation = async () => { + if (!complianceDraft.title.trim()) { + return; + } + setError(null); + try { + const response = await fetch("/api/human-capital/compliance/obligations", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(complianceDraft), + }); + const payload = (await response.json()) as { error?: string }; + if (!response.ok) { + throw new Error(payload.error ?? "No se pudo crear obligacion."); + } + setComplianceDraft({ body: "imss", title: "", dueDate: "" }); + await loadCompliance(); + } catch (err) { + setError(err instanceof Error ? err.message : "No se pudo crear obligacion."); + } + }; + + const createPayment = async () => { + if (!paymentDraft.obligationId || !paymentDraft.paymentDate || !paymentDraft.amount) { + return; + } + setError(null); + try { + const response = await fetch("/api/human-capital/compliance/payments", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(paymentDraft), + }); + const payload = (await response.json()) as { error?: string }; + if (!response.ok) { + throw new Error(payload.error ?? "No se pudo registrar pago."); + } + setPaymentDraft({ obligationId: "", paymentDate: "", amount: 0 }); + await loadCompliance(); + } catch (err) { + setError(err instanceof Error ? err.message : "No se pudo registrar pago."); + } + }; + + return ( +
+
+

Capital Humano

+

Workspace dinamico por tab.

+
+ + {error ?

{error}

: null} + {loading ?

Cargando...

: null} + + {!activeTabConfig ? ( +
+

Tab no configurado

+

+ Este tab no esta habilitado en el workspace. Ajusta la configuracion para activarlo. +

+ {canManage ? ( + + Ir a configuracion → + + ) : null} +
+ ) : null} + + {showSummary ? ( + <> + + +
+
+

People KPI

+

{dashboard?.aggregateScore.totalScore.toFixed(1) ?? "0.0"}%

+
+
+

Headcount activo

+

{dashboard?.summary.activeHeadcount ?? 0}

+

Δ {dashboard?.summary.netHeadcountChange ?? 0} vs previo

+
+
+

Rotacion mensual

+

{dashboard?.summary.monthlyChurnPct.toFixed(1) ?? "0.0"}%

+
+
+

Tenure mediano

+

{dashboard?.summary.medianTenureMonths.toFixed(1) ?? "0.0"}m

+
+
+ +
+
+

Tendencia de rotacion (6 meses)

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

Distribucion de tenure

+
+ + + + + + + + + +
+
+
+ + ) : null} + + {showFiles ? ( +
+
+

Expedientes por persona

+ + Ver directorio completo → + +
+ {!canManage ? ( +

Solo lideres pueden ver expedientes detallados.

+ ) : null} +
+ + + + + + + + + + + + + {canManage ? ( + topPeople.length > 0 ? ( + topPeople.map((person) => ( + + + + + + + + + )) + ) : ( + + + + ) + ) : ( + + + + )} + +
PersonaDepartamentoUbicacionCompletitudFaltantesAcciones
+

{person.name}

+

{person.email}

+
{person.department ?? "-"} · {person.departmentRole ?? "-"}{person.locationId ?? "-"}{person.completionPct}%{person.requiredMissing} + +
+ No hay personas disponibles. +
+ Sin permiso para ver expedientes. +
+
+ + {canManage ? ( +
+
+

Edicion de expediente

+ {selectedPersonFile ? ( +

+ {selectedPersonFile.name} · {selectedPersonFile.completionPct}% completo +

+ ) : null} +
+ + {loadingPersonFile ?

Cargando expediente...

: null} + + {!loadingPersonFile && !selectedPersonFile ? ( +

Selecciona una persona en la tabla para cargar y actualizar su expediente.

+ ) : null} + + {selectedPersonFile ? ( +
+ {selectedPersonFile.items.map((item) => ( +
+
+

+ {item.requirementLabel} + {item.isRequired ? " *" : ""} +

+ +
+
+ updatePersonFileItem(item.requirementId, { valueText: event.target.value })} + placeholder="Dato / folio / texto" + className="rounded-lg border border-benell-stroke bg-white px-3 py-2 text-sm" + disabled={savingPersonFile} + /> + updatePersonFileItem(item.requirementId, { evidenceUrl: event.target.value })} + placeholder="Archivo o URL de evidencia" + className="rounded-lg border border-benell-stroke bg-white px-3 py-2 text-sm" + disabled={savingPersonFile} + /> + updatePersonFileItem(item.requirementId, { note: event.target.value })} + placeholder="Nota" + className="rounded-lg border border-benell-stroke bg-white px-3 py-2 text-sm" + disabled={savingPersonFile} + /> +
+
{ + event.preventDefault(); + }} + onDrop={(event) => { + event.preventDefault(); + const file = event.dataTransfer.files?.[0]; + if (file) { + void uploadPersonFileEvidence(item.requirementId, file); + } + }} + > +

Arrastra y suelta archivo aqui, o selecciona uno.

+
+ + {uploadingByRequirement[item.requirementId] ? ( +

Subiendo...

+ ) : null} + {item.evidenceUrl ? ( + + Abrir archivo + + ) : null} +
+
+
+ ))} +
+ + +
+
+ ) : null} +
+ ) : null} +
+ ) : null} + + {showPayroll ? ( +
+
+

Resumen de nomina

+ {!canManage ? ( +

Solo lideres pueden ver nomina.

+ ) : null} + {payrollSummary ? ( +
+

Periodo: {payrollSummary.periodStart} → {payrollSummary.periodEnd}

+

Frecuencia: {payrollSummary.frequency}

+

Ubicacion: {payrollSummary.locationCode ?? "todas"}

+

Percepciones: {payrollSummary.percepciones.toFixed(2)}

+

Deducciones: {payrollSummary.deducciones.toFixed(2)}

+

Aportaciones: {payrollSummary.aportaciones.toFixed(2)}

+

Neto: {payrollSummary.neto.toFixed(2)}

+

Personas: {payrollSummary.peopleCount}

+
+ ) : ( +

No hay corridas.

+ )} +
+ +
+

Importar CSV

+
+ setPayrollDraft((prev) => ({ ...prev, periodStart: event.target.value }))} + placeholder="Periodo inicio (YYYY-MM-DD)" + className="rounded-lg border border-benell-stroke bg-white px-3 py-2 text-sm" + disabled={!canManage} + /> + setPayrollDraft((prev) => ({ ...prev, periodEnd: event.target.value }))} + placeholder="Periodo fin (YYYY-MM-DD)" + className="rounded-lg border border-benell-stroke bg-white px-3 py-2 text-sm" + disabled={!canManage} + /> + + setPayrollDraft((prev) => ({ ...prev, locationCode: event.target.value }))} + placeholder="Ubicacion (opcional)" + className="rounded-lg border border-benell-stroke bg-white px-3 py-2 text-sm" + disabled={!canManage} + /> +