# Plan — Fase 1: catálogos base + system_configuration

## Contexto

DocenTrack es el sistema real (Laravel 13 + Livewire 4 + Tailwind 4 +
MySQL) de registro de horas lectivas docentes. La Fase 0 (setup Docker)
ya está completa y verificada — ver `../../CLAUDE.md`. El modelo de
datos completo ya fue diseñado y cerrado campo por campo con el usuario
en `../MODELO-DATOS.md` (12 tablas, identificadores en inglés,
confirmado explícitamente — no es la misma convención que
`tutoria-fichas`, que quedó en español pese a decir "inglés" en su
propio CLAUDE.md, inconsistencia detectada y no corregida
retroactivamente ahí).

Esta fase construye los catálogos que alimentan todo lo demás
(`assignments` en Fase 3, `attendance_records` en Fase 2) y el switch
`system_configuration` que controla si el kiosko opera. Es la misma
secuencia que ya siguió `../../../tutoria-fichas/` en su propia Fase 1
(catálogos base, sin auth todavía — auth llega recién cuando existe
`users`, en la Fase 2 de este proyecto).

**Referencia estructural obligatoria** (patrón de CRUD probado, no
inventar otro): `../../../tutoria-fichas/app/Livewire/Admin/CatalogoSimple.php`
(catálogo genérico solo-nombre) y `.../PeriodosAcademicos.php` (catálogo
con lógica propia — el patrón "activar desactiva a los demás" +
guard de borrado). Componentes UI reutilizables ya construidos ahí:
`resources/views/components/ui/{badge,button,confirm-modal,field,
icon-button,modal,pagination,sort-header,summary-cards,toast,toggle}.blade.php`.

## Alcance

**Incluye:**
- Migraciones de `cycles`, `academic_periods`, `classrooms`, `courses`,
  `topics`, `system_configuration` — exactamente los nombres/columnas
  de `../MODELO-DATOS.md` (secciones 3, 4, 5, 6, 7, 9).
- Modelos Eloquent correspondientes.
- Componentes UI base reutilizables, **adaptando la estructura/props**
  de los de `tutoria-fichas` (mismo comportamiento: modal de alta/
  edición, confirm-modal para acciones destructivas, toasts, cards de
  resumen, columnas ordenables, paginación) — **sin copiar sus tokens
  de color** (esos son los de `tutor-web` portados a `tutoria-fichas`;
  DocenTrack tiene su propia identidad visual azul navy + naranja en
  `../../../mockup-unamba/docentrack/css/tokens.css`). Usar estilos
  neutros de Tailwind por ahora — el porteo real de los tokens del
  mockup es una tarea aparte, posterior a esta (mismo orden que siguió
  `tutoria-fichas`: primero CRUD funcional, "Rediseño de UI" fue un
  esfuerzo separado después).
- CRUD de `cycles`, `courses` (patrón `CatalogoSimple`: solo
  `name`+`active`).
- CRUD de `academic_periods` (patrón `PeriodosAcademicos`: `name`+
  `active` con acción "activar" que desactiva a los demás — **sin
  fechas**, ya se evaluaron y se descartaron, no agregarlas).
- CRUD de `classrooms` (`name`+`capacity`+`active` — un campo más que
  el patrón simple, decidir entre extender el componente genérico o
  darle uno propio chico, igual criterio que se usó para
  `academic_periods` en `tutoria-fichas`).
- `topics` como sub-sección dentro de editar un `course` (el temario
  del curso) — no es una pantalla propia con URL aparte, vive dentro de
  la edición del curso.
- Pantalla de `system_configuration`: **no es un listado/CRUD**, es una
  fila única con un switch (reusar `x-ui.toggle`) que guarda `is_open`
  + `updated_by` (usuario autenticado — ver nota de auth abajo) +
  `updated_at`.
- Seeders de datos de ejemplo, claramente marcados como placeholder
  (nada de nombres institucionales reales inventados).
- Regla transversal en **todo** `eliminar()`: capturar `QueryException`
  código `23000` (violación de integridad referencial) → toast de
  error legible, nunca dejar pasar un 500 crudo — exactamente el mismo
  patrón que ya tiene `tutoria-fichas` en sus catálogos.

**No incluye:**
- Autenticación / `users` / roles — **a propósito**, llega en la Fase 2
  cuando exista la tabla `users`. Las rutas de esta fase van **sin**
  middleware de auth todavía, mismo orden que siguió `tutoria-fichas`
  en su propia Fase 1. `system_configuration.updated_by` puede quedar
  `nullable`/sin poblar por ahora si no hay usuario autenticado — se
  completa cuando exista auth real en Fase 2, no inventar un usuario
  falso para rellenarlo.
- Cualquier cosa de `assignments`, `attendance_records`,
  `attendance_evidence`, `attendance_corrections`, `fingerprints` —
  fases posteriores.
- Kiosko (pantallas de fichaje) — Fase 2 en adelante.
- Porteo del diseño visual real del mockup (colores, tipografía,
  sidebar/topbar con la identidad de DocenTrack) — tarea aparte
  posterior, no bloquea esta fase.

## Tareas

- [x] 1. Migraciones: `cycles`, `academic_periods`, `classrooms`,
      `courses`, `topics`, `system_configuration` — columnas exactas
      de `MODELO-DATOS.md` §3-7 y §9. `system_configuration` es de fila
      única: sembrar la fila `id=1` (`is_open = false` por defecto) en
      la propia migración o en el seeder, no dejarla vacía.
- [x] 2. Modelos Eloquent: `Cycle`, `AcademicPeriod`, `Classroom`,
      `Course`, `Topic`, `SystemConfiguration`. **Ojo con
      `SystemConfiguration`**: Eloquent pluralizaría a
      `system_configurations` por defecto — declarar
      `protected $table = 'system_configuration';` explícito (mismo
      tipo de cuidado que `tutoria-fichas` tuvo que tener con sus
      plurales en español, acá es un caso de tabla-singular-a-propósito,
      no de idioma).
- [x] 3. Componentes UI base (`resources/views/components/ui/`):
      `button`, `modal`, `confirm-modal`, `field`, `badge`,
      `icon-button`, `pagination`, `sort-header`, `summary-cards`,
      `toast`, `toggle` — mismas props/comportamiento que los de
      `tutoria-fichas`, estilos neutros de Tailwind (sin tokens de
      color todavía, ver Alcance).
      Nota: props en inglés (`open`/`title`/`column`/`sort`) para
      seguir la regla de identificadores de este repo; el
      comportamiento es el mismo.
- [x] 4. Layout admin base (`layouts.app` o equivalente) — sidebar +
      topbar mínimos, sin la identidad visual del mockup todavía, solo
      la estructura de navegación (para que las pantallas de esta fase
      tengan dónde vivir).
- [x] 5. `Livewire\Admin\CatalogSimple` (equivalente inglés del patrón
      `CatalogoSimple` de `tutoria-fichas`) parametrizado por tipo,
      cubre `cycles` y `courses`. Ruta `GET /admin/catalogs/{type}`.
- [x] 6. `Livewire\Admin\AcademicPeriods` — patrón dedicado (como
      `PeriodosAcademicos`, sin fechas): listado, alta/edición,
      "activar" (desactiva los demás), eliminar con guard de
      integridad referencial + guard de "no eliminar el activo".
- [x] 7. `Livewire\Admin\Classrooms` — decidir e implementar el patrón
      para el campo extra `capacity` (extender el componente genérico
      de la tarea 5, o componente propio chico); documentar en el
      propio código cuál se eligió y por qué, una sola línea de
      comentario alcanza.
      Decisión: componente propio (`Classrooms`) porque `capacity` es
      un campo extra que no cabe limpio en `CatalogSimple`.
- [x] 8. Temario (`topics`): sub-sección dentro de la edición de un
      `course` — listado simple con alta/edición/reordenar/eliminar,
      scoped a `course_id`.
      Implementado como componente anidado `CourseTopics` dentro del
      modal de edición del curso (sin URL propia).
- [x] 9. `Livewire\Admin\SystemConfiguration` (o nombre equivalente) —
      pantalla de switch único: muestra estado actual (`is_open`),
      quién y cuándo lo cambió por última vez, botón para alternar
      (reusa `x-ui.toggle`, con confirm-modal antes de aplicar el
      cambio dado que afecta producción real del kiosko).
- [x] 10. Rutas en `routes/web.php` para todo lo anterior — sin
      middleware de auth (ver Alcance).
- [x] 11. Seeder de datos de ejemplo para los 5 catálogos (placeholders
      claramente marcados, no inventar nombres institucionales reales).

## Criterios de aceptación

- [x] `docker compose exec app php artisan migrate:fresh --seed` sin
      errores.
- [x] Tests de feature si se escriben, en verde
      (`docker compose exec app php artisan test`).
      Suite: 10 passed (`tests/Feature/Fase1CatalogsTest.php`).
- [x] Verificado con HTTP real (no solo tests): crear/editar/eliminar
      un registro de cada catálogo desde el navegador o `curl` contra
      el endpoint Livewire real, igual disciplina que ya sigue
      `tutoria-fichas` (ver su `CLAUDE.md`, Fase 2: "probado de punta a
      punta con curl contra el endpoint real, no solo Livewire::test()").
      POST real a `/livewire-…/update` contra `:8020`: cycles, courses,
      academic-periods y classrooms (crear/editar/eliminar) + toggle
      del kiosko.
- [x] Intentar eliminar un catálogo referenciado por otro registro
      (una vez exista algo que lo referencie — si no hay forma de
      probarlo aún en esta fase por falta de datos relacionados, dejarlo
      anotado como pendiente de verificar en Fase 3, no fingir que se
      probó) muestra el toast de error, no un 500.
      Verificado en esta fase: borrar un `course` con `topics` dispara
      toast de error (FK 23000), no un 500. El resto de FKs
      (`assignments`, `attendance_records`) queda para Fase 3.
- [x] Activar un `academic_period` desactiva correctamente los demás;
      intentar eliminar el periodo activo se bloquea.
- [x] Alternar `system_configuration.is_open` cambia el estado
      correctamente y persiste `updated_at` (y `updated_by` si ya hay
      forma de tener un usuario "actual" en esta fase, aunque sea
      hardcodeado temporalmente — dejar anotado si se hizo así).
      `updated_by` queda `null`: no hay auth ni usuario inventado
      (Fase 2). `Auth::id()` se escribe cuando exista sesión.

## Notas para quien implemente

- **Identificadores en inglés** (tablas, columnas, clases Livewire,
  rutas) — regla confirmada explícitamente para este proyecto,
  **distinta** de `tutoria-fichas` (que quedó en español). No copiar
  nombres de campo en español de `tutoria-fichas` al adaptar el patrón,
  solo la lógica/estructura.
- Comentarios en el código: español, solo donde aporten un porqué no
  obvio (no explicar qué hace el código).
- UI 100% en español (esto no cambia con el idioma del código — lo ve
  el usuario final).
- `academic_periods` explícitamente **sin** `start_date`/`end_date` —
  se evaluaron con el usuario y se descartaron porque nada en la lógica
  del sistema las usa; no reintroducirlas "por si acaso".
- `courses` explícitamente **sin** `cycle_id` propio — el ciclo se fija
  recién en `assignments` (Fase 3), no en el curso. No agregar la FK
  acá aunque parezca natural.
- No agregar auth en esta fase aunque parezca "fácil ya que estamos" —
  fue una decisión explícita de secuenciar así, agregarla ahora
  duplicaría trabajo cuando llegue `users` en Fase 2 con el modelo de
  roles real (`enum role: ADMIN/TEACHER` en `users`, no multi-perfil
  como `tutoria-fichas`).
- Regla transversal del proyecto (`../../CLAUDE.md` §11): nunca
  eliminar físicamente algo con dependencias — bloquear y dirigir a
  desactivar. Ya cubierto por el guard de `QueryException` 23000 en
  `eliminar()`, pero confirmarlo explícitamente en cada catálogo de
  esta fase, no darlo por hecho solo porque se copió el patrón.
