# Plan — Fase 5: Dashboard y Reportes exportables

## Contexto

Fases 0 a 4 están completas, revisadas y commiteadas (`b63c910`,
`d9a03f1`, `b3381d8`/`43830da`, `0ed04ef`, `b9d86e0`) — ver
`../../CLAUDE.md`. El sistema ya registra fichajes reales, tiene
horarios/asignaciones, y un panel de registros con corrección
auditada. Esta fase agrega las dos pantallas que faltaban del alcance
original del mockup: **Dashboard** (vista rápida del estado del día) y
**Reportes** (horas dictadas, exportable).

**No hay migraciones nuevas** — esta fase solo lee lo que ya existe,
no agrega tablas.

## Alcance

**Incluye:**
- `Livewire\Admin\Dashboard` en `/admin/dashboard` — **pasa a ser la
  pantalla de aterrizaje tras el login** (hoy es
  `/admin/catalogs/cycles`, cambia en esta fase: `bootstrap/app.php`
  `redirectUsersTo`, el redirect de `Livewire\Auth\Login`, y el enlace
  del logo en el sidebar). Tarjetas:
  - Registros de hoy (cuenta de `attendance_records` con
    `class_date` = hoy).
  - Fuera de horario (cuenta global de `out_of_schedule = true`).
  - Declarado tarde (cuenta global de `late_declaration = true`).
  - Modo manual (cuenta global de `identification_method = manual`) —
    sigue siendo el requisito no negociable de `../../CLAUDE.md` §10,
    ya visible también en el listado de Fase 4, acá se repite porque
    el dashboard es lo primero que ve el admin al entrar.
  - Estado del kiosko (`system_configuration.is_open`), con enlace
    directo a `/admin/system-configuration`.
  - Docentes activos y asignaciones activas para hoy (conteos simples).
- `Livewire\Admin\Reports` en `/admin/reports` — filtros: periodo
  académico (select, por defecto el activo), rango de fecha de clase
  (opcional), docente (opcional). Tabla **agrupada por docente**:
  cantidad de registros, total de horas dictadas (suma de
  `check_out_time - check_in_time`), cantidad fuera de horario,
  cantidad declarado tarde, cantidad modo manual.
- Exportar a **Excel** (`maatwebsite/excel`) y **PDF**
  (`barryvdh/laravel-dompdf`) — mismos filtros aplicados, mismas
  columnas que la tabla en pantalla.
- Enlaces "Dashboard" (arriba de todo) y "Reportes" en el sidebar.

**No incluye:**
- **Comparativo horas programadas (`assignments`) vs. horas dictadas**
  — lo menciona el propio mockup como ampliación futura, no alcance
  mínimo. No se construye acá.
- **Concepto de "alerta revisada/no revisada"** — el dashboard y los
  reportes muestran conteos totales (igual criterio que ya usan los
  summary cards de Fase 4), no hay un flag de "ya lo revisé" en
  `attendance_records` — no existía en el diseño original
  (`../MODELO-DATOS.md`) y no se agrega sin que se pida. Si se
  necesita después, es un campo nuevo + su propia decisión de diseño,
  no algo a inferir acá.
- **"Docentes con huella pendiente de enrolar"** — lo menciona el
  mockup para el dashboard, pero no aplica todavía: no hay
  enrolamiento de huella hasta la Fase 6. Se agrega ese widget
  entonces, no ahora (mostrarlo hoy sería siempre "0 de 0", sin
  sentido).
- Huella / lector real — Fase 6.
- Porteo del diseño visual del mockup — sigue pendiente.

## Tareas

- [x] 1. `composer require maatwebsite/excel barryvdh/laravel-dompdf`
      dentro del contenedor. Publicar configuración si hace falta.
- [x] 2. `Livewire\Admin\Dashboard` — las tarjetas descritas en
      Alcance. Reusar `x-ui.summary-cards` con el prop `cards` que ya
      se agregó en Fase 4 (array de tarjetas custom).
- [x] 3. Ruta `/admin/dashboard`. Cambiar landing post-login: `Login.php`
      redirige acá en vez de a catálogos, `bootstrap/app.php`
      (`redirectUsersTo`) idem, y el logo del sidebar enlaza acá.
- [x] 4. `Livewire\Admin\Reports` — filtros y tabla agrupada por
      docente descritos en Alcance. Calcular horas dictadas en PHP
      (sumar duración de cada registro con Carbon), no con SQL crudo —
      la escala de esta Facultad no lo justifica, más simple de leer.
- [x] 5. `App\Exports\AttendanceReportExport` (Excel) — mismos filtros
      y columnas que la tabla en pantalla.
- [x] 6. Vista dedicada + generación de PDF con los mismos datos
      (`barryvdh/laravel-dompdf`).
- [x] 7. Botones "Exportar Excel"/"Exportar PDF" en `Reports` —
      devolver la descarga directo desde la acción del componente
      (Livewire soporta `response()->streamDownload(...)` como
      retorno de una acción), no una ruta de controlador aparte.
- [x] 8. Rutas `/admin/dashboard` y `/admin/reports`, protegidas igual
      que el resto. Enlaces nuevos en el sidebar.

## Criterios de aceptación

- [x] `docker compose exec app php artisan migrate:fresh --seed` sin
      errores (no cambia el esquema, pero confirmar que las
      dependencias nuevas no rompen nada del arranque).
- [x] `docker compose exec app php artisan test` en verde, incluye:
      - Dashboard muestra los conteos correctos (registros de hoy,
        fuera de horario, declarado tarde, modo manual, estado del
        kiosko).
      - Login redirige a `/admin/dashboard`, no a catálogos.
      - Reportes agrupa por docente y suma horas correctamente
        (probar con datos donde se conozca la suma esperada).
      - Filtros de Reportes (periodo, rango de fecha, docente)
        devuelven los resultados correctos.
      - Exportar Excel genera un archivo con las filas esperadas
        (usar el fake de Excel/`Storage` para verificar sin generar
        un archivo real en disco).
      - Exportar PDF no lanza error y genera contenido.
      - Regresión: suite completa de fases anteriores sigue en verde.
- [x] Verificado con HTTP real contra `:8020`: `/admin/dashboard` y
      `/admin/reports` redirigen (302) sin sesión; el flujo completo
      (login → dashboard → reportes → exportar) queda cubierto por
      `Fase5DashboardReportsTest` vía el cliente HTTP real de Laravel,
      mismo criterio que las fases anteriores.

## Notas para quien implemente

- Identificadores en inglés, comentarios en español solo si aportan un
  porqué no obvio, UI 100% en español — igual que fases anteriores.
- No inventar un concepto de "alerta revisada" — se evaluó y se dejó
  fuera a propósito (ver Alcance). Si en algún momento se pide, es
  tarea aparte con su propio diseño de campo, no algo que se cuele acá
  como parte de las tarjetas del dashboard.
- El widget de "huella pendiente" del mockup queda fuera hasta Fase 6
  — no construir una versión vacía/placeholder de eso ahora.
- Reusar `x-ui.summary-cards` (prop `cards`, agregado en Fase 4) para
  las tarjetas del dashboard — no crear un componente de tarjetas
  nuevo y paralelo.

### Verificación independiente (Claude) y desviación real

Implementado directamente por Claude (mismo criterio que Fase 4).
Corrido de nuevo por mí: `migrate:fresh --seed` sin errores, **60/60
tests en verde**. Un hallazgo real durante la verificación: el cambio
de landing post-login rompió una aserción de `Fase2aAuthUsersTest`
(esperaba `admin.catalogs`, ahora es `admin.dashboard`) — corregido en
ese archivo, es la consecuencia esperada de un cambio de comportamiento
intencional de esta fase, no un bug. HTTP real contra `:8020` confirma
`/admin/dashboard` y `/admin/reports` protegidos (302 sin sesión).
