# XIV. ARQUITECTURA DEL SISTEMA

=======================================================================
  SISTEMA DE REGISTRO DIARIO DOCENTE (v2 - CodeIgniter Shield)
  Documentación Técnica Completa
=======================================================================


1. DESCRIPCIÓN GENERAL
-----------------------------------------------------------------------
  Sistema web para el registro diario de sesiones académicas. Permite a
  los docentes registrar las clases que dictan cada día y al coordinador
  supervisar que todos los registros se realicen correctamente.

  Esta versión (v2) es un refactor del sistema original usando
  CodeIgniter Shield para autenticación profesional (RBAC, grupos,
  permisos, magic links, remember-me).

  Roles del sistema:
  - Admin:         Gestor del sistema. Administra usuarios, programas,
                   periodos, franjas, feriados, horario académico,
                   configuración, auditoría y reportes globales.
                   Grupo Shield: "admin"
  - Coordinador:   Gestión académica por programa: cursos, horarios,
                   asignaciones, dashboard con
                   KPIs, registros, reportes.
                   Grupo Shield: "coordinador"
  - Docente:       Registro de sesiones, consulta de horarios, historial
                   personal, perfil, solicitud de contraseña.
                   Grupo Shield: "docente"

  Framework:     CodeIgniter 4.7+ (PHP 8.2+)
  Auth:          CodeIgniter Shield 1.3+ (session auth, grupos, permisos)
  Frontend:      Tailwind CSS 3.4 (build local con npm), HTMX 2.0,
                 Chart.js 4.4, SweetAlert2, Google Material Symbols
  BD:            MySQL/MariaDB ("parte-diario-docente")
  PDF:           Dompdf 3.1
  Excel:         PhpSpreadsheet 5.8
  Tests:         PHPUnit 10.5
  Git:           Repositorio activo (github.com/jyhirozth/parte-diario-docente)


2. ESTRUCTURA DEL PROYECTO
-----------------------------------------------------------------------
  parte-diario-docente/
  ├── .env                     # Config: DB, entorno, app
  ├── app/
  │   ├── Config/
  │   │   ├── App.php          # URL base, locale (es), timezone (UTC)
  │   │   ├── Database.php     # Conexión MySQLi / SQLite3 tests
  │   │   ├── Routes.php       # ~162 líneas, 80+ rutas
  │   │   ├── Filters.php      # Filtros: session, group, permission
  │   │   ├── Auth.php         # Config Shield (session, magic link)
  │   │   ├── AuthGroups.php   # Grupos: admin, coordinador, docente
  │   │   ├── AuthToken.php    # Token/HMAC auth
  │   │   ├── Constants.php    # Constantes del sistema
  │   │   ├── Generators.php   # Generadores de comandos
  │   │   └── Email.php        # Config de correo
  │   ├── Controllers/         # 28 archivos
  │   │   ├── BaseController.php
  │   │   ├── Dashboard.php
  │   │   ├── Home.php
  │   │   ├── NotificacionesController.php
  │   │   ├── Admin/           # 11 controladores
  │   │   ├── Coordinador/     # 9 controladores
  │   │   └── Docente/         # 7 controladores
  │   ├── Models/              # 16 modelos
  │   ├── Views/               # 35+ archivos
  │   │   ├── layouts/dashboard.php
  │   │   ├── auth/            # 5 vistas (Shield)
  │   │   ├── admin/           # 11 vistas
  │   │   ├── coordinador/     # 8 vistas
  │   │   ├── docente/         # 7 vistas
  │   │   ├── notificaciones/
  │   │   └── errors/
  │   └── Database/
  │       ├── Migrations/      # 18 migraciones
  │       └── Seeds/           # 14 seeders
  ├── docs/                    # Documentación del proyecto
  ├── public/                  # Web root
  │   └── assets/css/
  │       ├── input.css        # Tailwind input
  │       └── style.css        # Tailwind output (build)
  ├── tests/                   # PHPUnit tests
  ├── vendor/                  # Composer dependencies
  ├── writable/                # Logs, cache, session, uploads
  ├── composer.json
  ├── package.json
  ├── tailwind.config.js
  └── spark                   # CLI CodeIgniter


  3. CONTROLADORES (32 archivos)
-----------------------------------------------------------------------

  3.1 Home.php
  ─────────────────────────────────────────────────────────────────────
  - GET /    -> index()
    Landing page. Si el usuario ya inició sesión, redirige al dashboard.

  3.2 Dashboard.php
  ─────────────────────────────────────────────────────────────────────
  - GET /dashboard                       -> index()
    Dashboard principal. Redirige según el rol del usuario autenticado.
    La vista carga datos dinámicamente vía fetch() desde endpoints JSON.
  - GET /dashboard/estadisticas          -> estadisticasJson()
    Devuelve KPIs del día según el rol: admin (global), coordinador
    (por programa), docente (personal). Skeleton loaders en la vista.
  - GET /dashboard/proximas-clases       -> proximasClases()
    Devuelve JSON con las próximas 5 clases del usuario autenticado.

  3.3 NotificacionesController.php
  ─────────────────────────────────────────────────────────────────────
  - GET  /notificaciones/contar        -> contarNoLeidas()
  - GET  /notificaciones/listar        -> listar()
  - GET  /notificaciones/bandeja       -> bandeja()
  - POST /notificaciones/marcar-leida/{num} -> marcarLeida()
  - POST /notificaciones/marcar-todas  -> marcarTodasLeidas()

  3.4 Admin\UsuariosController.php
  ─────────────────────────────────────────────────────────────────────
  - GET  /admin/usuarios                   -> index()
  - POST /admin/usuarios                   -> store()
  - POST /admin/usuarios/actualizar/{num}  -> update()
  - POST /admin/usuarios/eliminar/{num}    -> destroy()
  - CRUD completo de usuarios Shield (admin, coordinador, docente)

  3.5 Admin\DocentesController.php
  ─────────────────────────────────────────────────────────────────────
  - GET  /admin/docentes                   -> index()
  - POST /admin/docentes                   -> store()
  - POST /admin/docentes/actualizar/{num}  -> update()
  - POST /admin/docentes/eliminar/{num}    -> destroy()
  - CRUD completo usando Shield + creación en cadena (users -> personal -> docentes)
  - Transacción: crea user Shield, luego personal, luego docente

  3.6 Admin\ProgramasController.php
  ─────────────────────────────────────────────────────────────────────
  - GET  /admin/programas                  -> index()
  - POST /admin/programas                  -> store()
  - POST /admin/programas/actualizar/{num} -> update()
  - POST /admin/programas/eliminar/{num}   -> destroy()

  3.7 Admin\PeriodosController.php
  ─────────────────────────────────────────────────────────────────────
  - GET  /admin/periodos                   -> index()
  - GET  /admin/periodos/{num}             -> show()
  - POST /admin/periodos                   -> store()
  - POST /admin/periodos/actualizar/{num}  -> update()
  - POST /admin/periodos/eliminar/{num}    -> destroy()
  - POST /admin/periodos/actual/{num}      -> setPeriodoActual()
  - Protección contra modificación de fechas con registros asociados

  3.8 Admin\FranjasController.php
  ─────────────────────────────────────────────────────────────────────
  - GET  /admin/franjas                    -> index()
  - GET  /admin/franjas/{num}              -> show()
  - POST /admin/franjas                    -> store()
  - POST /admin/franjas/actualizar/{num}   -> update()
  - POST /admin/franjas/eliminar/{num}     -> destroy()
  - CRUD de franjas académicas con cálculo automático de hora_fin

  3.9 Admin\FeriadosController.php
  ─────────────────────────────────────────────────────────────────────
  - GET  /admin/feriados                   -> index()
  - GET  /admin/feriados/{num}             -> show()
  - POST /admin/feriados                   -> store()
  - POST /admin/feriados/actualizar/{num}  -> update()
  - POST /admin/feriados/eliminar/{num}    -> destroy()

  3.10 Configuración de horario académico (unificada en ConfigController)
  ─────────────────────────────────────────────────────────────────────
  El horario académico (horas de jornada, días laborables, tolerancia
  por defecto y uso de franjas) se configura desde `Admin\ConfigController`
  (sección 3.11) a través de la ruta `GET/POST /admin/configuracion`.
  El controlador separado `HorarioAcademicoController` fue eliminado por
  duplicidad: `ConfigController::guardar()` persiste los mismos campos en
  la tabla singleton `horario_academico` y además bloquea los cambios si
  ya existen horarios o registros de sesión dependientes.

  3.11 Admin\ConfigController.php
  ─────────────────────────────────────────────────────────────────────
  - GET  /admin/configuracion              -> index()
  - POST /admin/configuracion              -> guardar()
  - Configuración clave-valor del sistema + horario académico (jornada,
    días laborables, tolerancia, uso de franjas) en la tabla singleton
    `horario_academico`

  3.12 Admin\RegistrosController.php
  ─────────────────────────────────────────────────────────────────────
  - GET  /admin/registros                  -> index()
  - POST /admin/registros/revisar/{num}    -> revisar()
  - Filtros combinados y acciones de revisión (aprobar/observar)

  3.13 Admin\SolicitudesController.php
  ─────────────────────────────────────────────────────────────────────
  - GET  /admin/solicitudes                -> index()
  - POST /admin/solicitudes/atender        -> atender()
  - Gestión de solicitudes de recuperación de contraseña

  3.14 Admin\ReportesController.php
  ─────────────────────────────────────────────────────────────────────
  - GET  /admin/reportes                   -> index()
  - GET  /admin/reportes/pdf               -> exportarPdf()
  - GET  /admin/reportes/csv               -> exportarCsv()
  - GET  /admin/reportes/excel             -> exportarExcel()
  - Filtros combinados: docente, curso, periodo, fechas, estado

  3.15 Admin\AuditoriaController.php
  ─────────────────────────────────────────────────────────────────────
  - GET  /admin/auditoria                  -> index()
  - Panel de auditoría del sistema con filtros combinados:
    búsqueda libre (usuario, nombre, IP, descripción), acción, tabla,
    rango de fechas. Paginación de 50 registros por página.
  - Muestra registros de la tabla `auditoria` con JOIN a `users` y
    `personal` para mostrar nombre del usuario.
  - Requiere permiso `admin.access`.

  3.16 Coordinador\DashboardController.php
  ─────────────────────────────────────────────────────────────────────
  - GET  /coordinador/dashboard             -> index()
  - KPIs del día, filtrado por programa del coordinador
  - Las estadísticas JSON y docentes pendientes se sirven desde
    Dashboard.php (endpoints compartidos por rol)

  3.17 Coordinador\CursosController.php
  ─────────────────────────────────────────────────────────────────────
  - GET  /coordinador/cursos               -> index()
  - GET  /coordinador/cursos/{num}         -> show()
  - POST /coordinador/cursos               -> store()
  - POST /coordinador/cursos/actualizar/{num} -> update()
  - POST /coordinador/cursos/eliminar/{num}   -> destroy()
  - Filtrado por programa del coordinador

  3.18 Coordinador\DocentesController.php
  ─────────────────────────────────────────────────────────────────────
  - GET  /coordinador/docentes             -> index()
  - Vista de solo lectura filtrada por programa

  3.19 Coordinador\HorariosController.php
  ─────────────────────────────────────────────────────────────────────
  - GET  /coordinador/horarios             -> index()
  - GET  /coordinador/horarios/{num}       -> show()
  - POST /coordinador/horarios             -> store()
  - POST /coordinador/horarios/actualizar/{num} -> update()
  - POST /coordinador/horarios/eliminar/{num}   -> destroy()
  - Validación de cruce horario y horas máximas semanales

  3.20 Coordinador\AsignacionesController.php
  ─────────────────────────────────────────────────────────────────────
  - GET  /coordinador/asignaciones         -> index()
  - GET  /coordinador/asignaciones/{num}   -> show()
  - POST /coordinador/asignaciones         -> store()
  - POST /coordinador/asignaciones/actualizar/{num} -> update()
  - POST /coordinador/asignaciones/eliminar/{num}   -> destroy()
  - Vinculación curso-docente por periodo

  3.22 Coordinador\HorarioSemanalController.php
  ─────────────────────────────────────────────────────────────────────
  - GET  /coordinador/horario-semanal      -> index()
  - Vista completa del horario semanal con franjas

  3.23 Coordinador\RegistrosController.php
  ─────────────────────────────────────────────────────────────────────
  - GET  /coordinador/registros            -> index()
  - POST /coordinador/registros/revisar/{num} -> revisar()
  - Filtros combinados y acciones de revisión

  3.24 Coordinador\ReportesController.php
  ─────────────────────────────────────────────────────────────────────
  - GET  /coordinador/reportes             -> index()
  - GET  /coordinador/reportes/pdf         -> exportarPdf()
  - GET  /coordinador/reportes/csv         -> exportarCsv()
  - GET  /coordinador/reportes/excel       -> exportarExcel()
  - Filtrado por programa del coordinador

  3.25 Docente\LoginController.php
  ─────────────────────────────────────────────────────────────────────
  - GET  /docente/login                    -> index()
  - POST /docente/login                    -> dniLogin()
  - GET  /docente/logout                   -> logout()
  - Login con DNI + contraseña, autentica contra Shield vía email

  3.26 Docente\ClasesController.php
  ─────────────────────────────────────────────────────────────────────
  - GET  /docente/clases                   -> index()
  - GET  /docente/clases/hoy               -> clasesHoy() (JSON)
  - Consulta de clases del día con estado del registro y estadísticas

  3.27 Docente\RegistroController.php
  ─────────────────────────────────────────────────────────────────────
  - GET  /docente/registrar                -> index()
  - POST /docente/registrar/guardar        -> store()
  - GET  /docente/registrar/verificar/{num} -> verificar()
  - Validación: Hora_Actual >= Hora_Fin_Clase
  - Clasificación automática: en_tiempo / fuera_de_plazo

  3.28 Docente\HistorialController.php
  ─────────────────────────────────────────────────────────────────────
  - GET  /docente/historial                -> index()
  - Historial con filtros por curso y rango de fechas

  3.29 Docente\HorarioController.php
  ─────────────────────────────────────────────────────────────────────
  - GET  /docente/horario                  -> index()
  - Horario semanal con franjas y filtro por periodo

  3.30 Docente\PerfilController.php
  ─────────────────────────────────────────────────────────────────────
  - GET  /docente/perfil                   -> index()
  - GET  /docente/perfil/cursos            -> misCursos()

  3.31 Docente\SolicitudPasswordController.php
  ─────────────────────────────────────────────────────────────────────
  - GET  /docente/solicitar-password       -> index()
  - POST /docente/solicitar-password       -> solicitar()
  - Flujo: docente solicita -> admin/coordinador atiende

  TOTAL: 31 controladores implementados


4. TRAITS
-----------------------------------------------------------------------
  4.1 App\Traits\AuditLogger.php
  ─────────────────────────────────────────────────────────────────────
  Trait reutilizable para registrar acciones de auditoría en la tabla
  `auditoria`. Utilizado por todos los controladores que realizan
  operaciones CRUD o autenticación.

  Método principal:
    logAuditoria(string $tabla, string $accion, string $descripcion,
                 ?int $userId = null): void

  Parámetros:
    - $tabla:      Nombre de la tabla afectada (ej: 'users', 'registros_sesion')
    - $accion:     Tipo de acción: 'insercion', 'actualizacion', 'eliminacion',
                   'login', 'logout'
    - $descripcion: Texto descriptivo de la acción realizada
    - $userId:     ID del usuario (opcional, usa auth()->id() si es null)

  Implementación:
    - Inserta registro en tabla `auditoria` con user_id, tabla_afectada,
      accion, descripcion_cambio, fecha_evento y direccion_ip
    - Obtiene la IP del cliente vía $this->request->getIPAddress()
    - Usa el helper db_connect() para inserción directa

  Controladores que lo utilizan (12):
    Admin\UsuariosController, Admin\DocentesController,
    Admin\ProgramasController, Admin\PeriodosController,
    Admin\ConfigController, Admin\SolicitudesController,
    Admin\FranjasController, Admin\FeriadosController,
    Admin\RegistrosController,
    Coordinador\AsignacionesController, Coordinador\RegistrosController,
    Docente\LoginController, Docente\RegistroController

  4.2 App\Traits\DiaHelper.php
  ─────────────────────────────────────────────────────────────────────
  Trait reutilizable para normalizar los días de la semana en español
  y capitalizar correctamente los nombres de días.

  Métodos:
    diaEspanol(string $day): string
      Convierte día en español a formato consistente (minúsculas).
      Acepta: Lunes, Martes, ..., Domingo -> lunes, martes, ..., domingo

    diaEspanolCapitalizado(string $day): string
      Convierte día en español a formato capitalizado.
      Acepta: lunes, martes, ..., domingo -> Lunes, Martes, ..., Domingo

  Controladores que lo utilizan (4):
    Dashboard, Admin\DashboardController,
    Coordinador\DashboardController, Coordinador\HorarioSemanalController

  BaseController.php:
    La clase base del sistema ahora usa este trait (`use \App\Traits\DiaHelper`).
    También centraliza el método `getProgramaId()` que retorna el
    programa_id del coordinador autenticado (requerido por los 9
    controladores de Coordinador).
    Admin\ConfigController, Admin\SolicitudesController,
    Admin\FranjasController, Admin\FeriadosController,
    Admin\RegistrosController,
    Coordinador\AsignacionesController, Coordinador\RegistrosController,
    Docente\LoginController, Docente\RegistroController


5. MODELOS (16 archivos)
-----------------------------------------------------------------------
  #   Modelo                    Tabla                     Propósito
  --  ------------------------  ------------------------  ------------------------
   1  AuthModel                 users (Shield)            Gestión de usuarios
                                                          Shield
   2  DocenteModel              docentes                  Docentes (personal_id,
                                                          programa_id)
   3  CursoModel                cursos                    Cursos (nombre, código,
                                                          ciclo, créditos,
                                                          horas, tipo, programa)
   4  PeriodoAcademicoModel     periodos_academicos       Periodos (código,
                                                          fechas, activo, actual)
   5  HorarioModel              horarios                  Horarios (asignacion,
                                                           dia, hora_inicio/fin,
                                                           activo)
   6  AsignacionModel           asignaciones              Asignación curso-docente
                                                          por periodo
   7  RegistroSesionModel       registros_sesion          Registros diarios
                                                          (horario, fecha_clase,
                                                          tema, actividades,
                                                          estado, revisión,
                                                          origen)
   8  FeriadoModel              feriados                  Feriados (fecha,
                                                          descripción, laborable)
   9  FranjaAcademicaModel      franjas_academicas        Franjas horarias
                                                          (nombre, duración,
                                                          hora_inicio/fin, tipo)
  10  HorarioAcademicoModel     horario_academico         Config singleton
                                                          (horas jornada, días,
                                                          tolerancia, franjas)
  11  DisponibilidadModel       disponibilidad_docente    Disponibilidad docente
                                                          (docente, día, hora)
  12  ConfigSistemaModel        config_sistema            Config clave-valor
  13  AuditoriaModel            auditoria                 Auditoría (usuario,
                                                          tabla, acción, IP)
  14  NotificacionModel         notificaciones            Notificaciones a
                                                          docentes
  15  ProgramaModel             programas                 Programas académicos
                                                          (nombre,
                                                          coordinador_personal_id)
  16  SolicitudPasswordModel    solicitudes_password      Solicitudes de
                                                           recuperación

  ADEMÁS: Todos los 16 modelos del sistema incluyen validaciones
  declarativas con `$validationRules` y `$validationMessages` para
  integridad de datos en cada operación CRUD.

  ADEMÁS: Shield gestiona usuarios y autenticación mediante:
    - users
    - auth_identities
    - auth_groups (admin, coordinador, docente)
    - auth_groups_users
    - auth_permissions
    - auth_permissions_users
    - auth_logins
    - auth_token_logins
    - auth_remember_tokens

  TOTAL: 16 modelos del sistema + 9 tablas de Shield


6. VISTAS (36+ archivos)
-----------------------------------------------------------------------
  5.1 Layouts (1)
  ──────────────
    layouts/dashboard.php       - Layout principal con sidebar responsivo,
                                  HTMX, Chart.js, SweetAlert2, notificaciones

  5.2 Auth / Shield (5)
  ────────────────────
    auth/layout.php             - Layout de páginas de autenticación
    auth/login.php              - Login (email + password)
    auth/register.php           - Registro de nuevo usuario
    auth/magic_link_form.php    - Solicitud de magic link
    auth/magic_link_message.php - Confirmación de envío

  5.3 Generales (3)
  ────────────────
    home.php                    - Landing page / presentación
    dashboard.php               - Dashboard principal (redirección por rol).
                                  Carga datos dinámicamente vía fetch() con
                                  skeleton loaders. Endpoints: /dashboard/estadisticas
                                  y /dashboard/proximas-clases
    welcome_message.php         - Página de bienvenida CI4

  5.4 Admin (12)
  ──────────────
    admin/usuarios.php          - CRUD usuarios (tabla + modal unificado crear/editar + filtros desplegables)
    admin/docentes.php          - CRUD docentes (tabla + modal unificado + filtros desplegables)
    admin/programas.php         - CRUD programas (tabla + modal)
    admin/periodos.php          - CRUD periodos académicos (tabla + modal + filtros desplegables)
    admin/franjas.php           - Franjas académicas (tabla + modal + filtros desplegables)
    admin/feriados.php          - Feriados (tabla + modal + filtros desplegables)
    admin/configuracion.php     - Configuración del sistema (incluye horario académico)
    admin/registros.php         - Supervisión de registros de clase
    admin/solicitudes.php       - Solicitudes de contraseña
    admin/reportes.php          - Reportes y exportación
    admin/reportes_pdf.php      - Plantilla PDF para reportes
    admin/auditoria.php         - Panel de auditoría del sistema (filtros, tabla, paginación)

  5.5 Coordinador (8)
  ──────────────────
    coordinador/dashboard.php   - KPIs del día con Chart.js
    coordinador/cursos.php      - CRUD cursos (tabla + modal + filtros desplegables)
    coordinador/docentes.php    - Lista docentes (solo lectura, filtros desplegables)
    coordinador/horarios.php    - CRUD horarios (tabla + modal + filtros desplegables)
    coordinador/asignaciones.php - Asignación curso-docente (tabla + modal + filtros desplegables)
    coordinador/horario_semanal.php - Horario semanal general
    coordinador/registros.php   - Auditoría general

  5.6 Docente (7)
  ──────────────
    docente/login.php           - Login con DNI (Tailwind, unificado con auth/login.php)
    docente/clases.php          - Clases del día con indicadores
    docente/registrar.php       - Formulario de registro de sesión
    docente/historial.php       - Historial personal con filtros
    docente/horario.php         - Horario semanal
    docente/perfil.php          - Perfil del docente
    docente/solicitar_password.php - Solicitar recuperación

  5.7 Notificaciones (2)
  ────────────────────
    notificaciones/bandeja.php   - Bandeja de notificaciones
    notificaciones/dropdown.php  - Dropdown para el menú superior

  TOTAL: 39 vistas implementadas (+8 de errores)


6. RUTAS
-----------------------------------------------------------------------
  SIN AUTENTICACIÓN:
    GET  /                          -> Home::index
    GET  /dashboard                 -> Dashboard::index
    GET  /dashboard/estadisticas    -> Dashboard::estadisticasJson
    GET  /dashboard/proximas-clases -> Dashboard::proximasClases

  AUTENTICACIÓN (Shield - rutas automáticas):
    GET/POST  /login                -> Shield LoginController
    GET/POST  /register             -> Shield RegisterController
    GET/POST  /magic-link           -> Shield MagicLinkController
    GET       /logout               -> Shield LoginController::logout

  GRUPO admin (filtro permission:admin.access):
    GET    /admin/usuarios                    -> index()
    POST   /admin/usuarios                    -> store()
    POST   /admin/usuarios/actualizar/{num}   -> update()
    POST   /admin/usuarios/eliminar/{num}     -> destroy()
    GET    /admin/docentes                    -> index()
    POST   /admin/docentes                    -> store()
    POST   /admin/docentes/actualizar/{num}   -> update()
    POST   /admin/docentes/eliminar/{num}     -> destroy()
    GET    /admin/programas                   -> index()
    POST   /admin/programas                   -> store()
    POST   /admin/programas/actualizar/{num}  -> update()
    POST   /admin/programas/eliminar/{num}    -> destroy()
    GET    /admin/periodos                    -> index()
    GET    /admin/periodos/{num}              -> show()
    POST   /admin/periodos                    -> store()
    POST   /admin/periodos/actualizar/{num}   -> update()
    POST   /admin/periodos/eliminar/{num}     -> destroy()
    POST   /admin/periodos/actual/{num}       -> setPeriodoActual()
    GET    /admin/franjas                     -> index()
    GET    /admin/franjas/{num}               -> show()
    POST   /admin/franjas                     -> store()
    POST   /admin/franjas/actualizar/{num}    -> update()
    POST   /admin/franjas/eliminar/{num}      -> destroy()
    GET    /admin/feriados                    -> index()
    GET    /admin/feriados/{num}              -> show()
    POST   /admin/feriados                    -> store()
    POST   /admin/feriados/actualizar/{num}   -> update()
    POST   /admin/feriados/eliminar/{num}     -> destroy()
    GET    /admin/configuracion               -> index()
    POST   /admin/configuracion               -> guardar()
    GET    /admin/registros                   -> index()
    POST   /admin/registros/revisar/{num}     -> revisar()
    GET    /admin/solicitudes                 -> index()
    POST   /admin/solicitudes/atender         -> atender()
    GET    /admin/reportes                    -> index()
    GET    /admin/reportes/pdf                -> exportarPdf()
    GET    /admin/reportes/csv                -> exportarCsv()
    GET    /admin/reportes/excel              -> exportarExcel()
    GET    /admin/auditoria                   -> index()

  GRUPO coordinador (filtro permission:coordinador.access):
    GET    /coordinador/dashboard             -> index()
    GET    /coordinador/cursos                -> index()
    GET    /coordinador/cursos/{num}          -> show()
    POST   /coordinador/cursos                -> store()
    POST   /coordinador/cursos/actualizar/{num} -> update()
    POST   /coordinador/cursos/eliminar/{num} -> destroy()
    GET    /coordinador/docentes              -> index()
    GET    /coordinador/horarios              -> index()
    GET    /coordinador/horarios/{num}        -> show()
    POST   /coordinador/horarios              -> store()
    POST   /coordinador/horarios/actualizar/{num} -> update()
    POST   /coordinador/horarios/eliminar/{num} -> destroy()
    GET    /coordinador/asignaciones          -> index()
    GET    /coordinador/asignaciones/{num}    -> show()
    POST   /coordinador/asignaciones          -> store()
    POST   /coordinador/asignaciones/actualizar/{num} -> update()
    POST   /coordinador/asignaciones/eliminar/{num} -> destroy()
    GET    /coordinador/horario-semanal       -> index()
    GET    /coordinador/registros             -> index()
    POST   /coordinador/registros/revisar/{num} -> revisar()
    GET    /coordinador/reportes              -> index()
    GET    /coordinador/reportes/pdf          -> exportarPdf()
    GET    /coordinador/reportes/csv          -> exportarCsv()
    GET    /coordinador/reportes/excel        -> exportarExcel()

  SIN FILTRO (acceso público):
    GET    /docente/login                     -> LoginController::index
    POST   /docente/login                     -> LoginController::dniLogin
    GET    /docente/logout                    -> LoginController::logout

  GRUPO docente (filtro permission:docente.access):
    GET    /docente/clases                    -> index()
    GET    /docente/clases/hoy                -> clasesHoy()
    GET    /docente/historial                 -> index()
    GET    /docente/horario                   -> index()
    GET    /docente/perfil                    -> index()
    GET    /docente/perfil/cursos             -> misCursos()
    GET    /docente/solicitar-password        -> index()
    POST   /docente/solicitar-password        -> solicitar()
    GET    /docente/registrar                 -> index()
    POST   /docente/registrar/guardar         -> store()
    GET    /docente/registrar/verificar/{num} -> verificar()

  NOTIFICACIONES (filtro session, cualquier rol autenticado):
    GET    /notificaciones/contar             -> contarNoLeidas()
    GET    /notificaciones/listar             -> listar()
    GET    /notificaciones/bandeja            -> bandeja()
    POST   /notificaciones/marcar-leida/{num} -> marcarLeida()
    POST   /notificaciones/marcar-todas       -> marcarTodasLeidas()

  TOTAL: ~84 rutas definidas (incluyendo las automáticas de Shield)


8. FUNCIONALIDADES CLAVE IMPLEMENTADAS
-----------------------------------------------------------------------
  Módulo de Administración (Admin):
  - Autenticación con CodeIgniter Shield (session auth, magic links,
    remember-me, bloqueo por intentos)
  - CRUD completo de usuarios Shield (admin, coordinador, docente)
  - CRUD de docentes con integración Shield (creación en cadena)
  - CRUD de programas académicos
  - CRUD de periodos académicos con protección de integridad
  - CRUD de franjas académicas con cálculo automático
  - CRUD de feriados
  - Configuración del horario académico (jornada, días, tolerancia)
  - Configuración clave-valor del sistema
  - Auditoría de registros con revisión (aprobar/observar)
  - Gestión de solicitudes de recuperación de contraseña
  - Reportes con filtros y exportación PDF/CSV/Excel
  - **Sistema de auditoría completa** con panel de visualización
  - **Modales unificados** para crear/editar en todas las vistas CRUD
  - **Filtros desplegables** en todas las tablas de datos

  Módulo Coordinador:
  - Dashboard con KPIs del día vía fetch() (Chart.js, skeleton loaders)
  - Docentes con registros pendientes
  - CRUD de cursos (filtrado por programa)
  - Vista de docentes (solo lectura, filtrado por programa)
  - CRUD de horarios con validación de cruce horario y horas máximas
  - CRUD de asignaciones curso-docente por periodo
  - Vista de horario semanal general
  - Auditoría de registros con revisión
  - Reportes con filtros y exportación PDF/CSV/Excel
  - **Modales unificados** para crear/editar en todas las vistas CRUD
  - **Filtros desplegables** en todas las tablas de datos

  Módulo Docente:
  - Login con DNI + contraseña (autentica contra Shield)
    Nota: Vista unificada en Tailwind con auth/login.php
  - Dashboard con clases del día (fetch() dinámico con skeleton loaders)
  - Registro de sesión con validación horaria
  - Clasificación automática: "en_tiempo" / "fuera_de_plazo"
  - Verificación en tiempo real si ya puede registrar
  - Historial personal con filtros por curso y fechas
  - Horario semanal con franjas
  - Cursos asignados
  - Solicitud de recuperación de contraseña

  Sistema de Notificaciones:
  - Notificaciones por usuario
  - Conteo de no leídas (vía AJAX)
  - Bandeja de notificaciones
  - Marcar como leída / todas leídas

  Sistema de Auditoría:
  - Trait `AuditLogger` reutilizable en todos los controladores
  - Registro automático de: crear, actualizar, eliminar, login, logout
  - Descripción detallada de cada acción (tabla, usuario, IP, fecha)
  - Panel de visualización en `admin/auditoria` con filtros:
    búsqueda libre, acción, tabla, rango de fechas
  - Paginación de 50 registros por página
  - Badges coloreados por tipo de acción
  - Tablas instrumentadas: users, personal, programas, periodos_academicos,
    config_sistema, solicitudes_password, franjas_academicas, feriados,
    horario_academico, asignaciones, registros_sesion


9. BASE DE DATOS
-----------------------------------------------------------------------
  8.1 Tablas del Sistema (16)

  ┌──────────────────────────────────────────────────────────────────┐
  │                          PERSONAL                                 │
  ├──────────────┬─────────────────────┬─────────────────────────────┤
  │ Columna       │ Tipo                │ Descripción                 │
  ├──────────────┼─────────────────────┼─────────────────────────────┤
  │ id           │ INT AUTO_INCREMENT  │ PK                           │
  │ user_id      │ INT UNSIGNED        │ FK -> users(id) (UK)        │
  │ dni          │ VARCHAR(20)         │ Documento único              │
  │ apellido_paterno│ VARCHAR(75)      │ Apellido paterno             │
  │ apellido_materno│ VARCHAR(75)      │ Apellido materno             │
  │ primer_nombre│ VARCHAR(75)         │ Primer nombre                │
  │ segundo_nombre│ VARCHAR(75)        │ Segundo nombre (nullable)    │
  │ fecha_nacimiento│ DATE            │ Fecha de nacimiento          │
  │ genero       │ ENUM('M','F','Otro')│ Género                       │
  │ celular      │ VARCHAR(20)         │ Celular (nullable)           │
  │ telefono_fijo│ VARCHAR(20)         │ Teléfono fijo (nullable)     │
  │ correo_personal│ VARCHAR(255)      │ Correo personal (nullable)   │
  │ direccion_domicilio│ VARCHAR(255)  │ Dirección (nullable)         │
  └──────────────┴─────────────────────┴─────────────────────────────┘

  ┌──────────────────────────────────────────────────────────────────┐
  │                          PROGRAMAS                                │
  ├──────────────┬─────────────────────┬─────────────────────────────┤
  │ id           │ INT AUTO_INCREMENT  │ PK                           │
  │ nombre       │ VARCHAR(150)        │ Nombre del programa          │
  │ coordinador_personal_id│ INT       │ FK -> personal(id) (nullable)│
  │ estado_activo│ TINYINT(1)          │ 1=activo, 0=inactivo         │
  └──────────────┴─────────────────────┴─────────────────────────────┘

  ┌──────────────────────────────────────────────────────────────────┐
  │                          DOCENTES                                 │
  ├──────────────┬─────────────────────┬─────────────────────────────┤
  │ id           │ INT AUTO_INCREMENT  │ PK                           │
  │ personal_id  │ INT UNSIGNED        │ FK -> personal(id) (UK)     │
  │ programa_id  │ INT UNSIGNED        │ FK -> programas(id) (nulleable)│
  └──────────────┴─────────────────────┴─────────────────────────────┘

  ┌──────────────────────────────────────────────────────────────────┐
  │                          CURSOS                                   │
  ├──────────────┬─────────────────────┬─────────────────────────────┤
  │ id           │ INT AUTO_INCREMENT  │ PK                           │
  │ codigo_curso │ VARCHAR(20)         │ Código único                 │
  │ nombre       │ VARCHAR(150)        │ Nombre del curso             │
  │ ciclo        │ ENUM('I'..'X')      │ Ciclo académico              │
  │ creditos     │ TINYINT UNSIGNED    │ Créditos                     │
  │ horas_teoria_semanales│ TINYINT    │ Horas teoría por semana      │
  │ horas_practica_semanales│ TINYINT  │ Horas práctica por semana    │
  │ tipo_materia │ ENUM('Teórico','Práctico','Laboratorio','Virtual')│
  │ programa_id  │ INT UNSIGNED        │ FK -> programas(id)          │
  │ estado_activo│ TINYINT(1)          │ 1=activo, 0=inactivo         │
  └──────────────┴─────────────────────┴─────────────────────────────┘

  ┌──────────────────────────────────────────────────────────────────┐
  │                      PERIODOS ACADEMICOS                          │
  ├──────────────┬─────────────────────┬─────────────────────────────┤
  │ id           │ INT AUTO_INCREMENT  │ PK                           │
  │ codigo       │ VARCHAR(20)         │ Ej: "2026-I"                 │
  │ fecha_inicio │ DATE                │ Inicio del periodo           │
  │ fecha_fin    │ DATE                │ Fin del periodo              │
  │ es_actual    │ TINYINT(1)          │ 1=periodo actual             │
  │ estado_activo│ TINYINT(1)          │ 1=activo                     │
  └──────────────┴─────────────────────┴─────────────────────────────┘

  ┌──────────────────────────────────────────────────────────────────┐
  │                        ASIGNACIONES                               │
  ├──────────────┬─────────────────────┬─────────────────────────────┤
  │ id           │ INT AUTO_INCREMENT  │ PK                           │
  │ curso_id     │ INT UNSIGNED        │ FK -> cursos(id)             │
  │ docente_id   │ INT UNSIGNED        │ FK -> docentes(id)           │
  │ periodo_id   │ INT UNSIGNED        │ FK -> periodos_academicos(id)│
  │ fecha_asignacion│ DATETIME         │ Default CURRENT_TIMESTAMP    │
  │ UK           │ (curso_id, docente_id, periodo_id)                 │
  └──────────────┴─────────────────────┴─────────────────────────────┘

  ┌──────────────────────────────────────────────────────────────────┐
  │                        HORARIOS                                   │
  ├──────────────┬─────────────────────┬─────────────────────────────┤
  │ id           │ INT AUTO_INCREMENT  │ PK                           │
  │ asignacion_id│ INT UNSIGNED        │ FK -> asignaciones(id)       │
  │ dia_semana   │ ENUM('lunes'..'domingo')                           │
  │ hora_inicio  │ TIME                │ Inicio de clase              │
  │ hora_fin     │ TIME                │ Fin de clase                 │
  │ estado_activo│ TINYINT(1)          │ 1=activo, 0=inactivo         │
  └──────────────┴─────────────────────┴─────────────────────────────┘

  ┌──────────────────────────────────────────────────────────────────┐
  │                     REGISTROS_SESION                               │
  ├──────────────┬─────────────────────┬─────────────────────────────┤
  │ id           │ INT AUTO_INCREMENT  │ PK                           │
  │ horario_id   │ INT UNSIGNED        │ FK -> horarios(id)           │
  │ fecha_clase  │ DATE                │ Fecha de la clase            │
  │ hora_envio_sistema│ DATETIME       │ Fecha/hora del registro      │
  │ tema_silabo_semana│ TINYINT        │ Semana del sílabo            │
  │ actividad_realizada│ TEXT          │ Actividad realizada          │
  │ tareas_dejadas│ TEXT               │ Tareas (nullable)            │
  │ observacion  │ TEXT                │ Observación (nullable)       │
  │ estado_registro│ ENUM('en_tiempo','fuera_de_plazo')               │
  │ estado_revision_coordinador│ ENUM('Pendiente','Aprobado','Observado')│
  │ comentario_coordinador│ TEXT       │ Comentario (nullable)        │
  │ dispositivo_origen│ ENUM('web','app')│ Origen del registro        │
  │ UK           │ (horario_id, fecha_clase)                          │
  └──────────────┴─────────────────────┴─────────────────────────────┘

  8.2 Tablas Adicionales

  franjas_academicas:    id, nombre, duracion_minutos, hora_inicio,
                          hora_fin, tipo (hora_clase/recreo/almuerzo),
                          estado_activo
  horario_academico:     id, hora_inicio_jornada, hora_fin_jornada,
                          dias_laborales (SET), tolerancia_default,
                          usar_franjas_academicas (singleton)
  feriados:              id, fecha, descripcion, es_laborable
  disponibilidad_docente: id, docente_id, dia_semana, hora_inicio,
                          hora_fin, estado_activo
  config_sistema:        id, clave, valor, descripcion
  auditoria:             id, user_id (FK→users, nullable),
                          tabla_afectada (VARCHAR 50),
                          accion (ENUM: insercion, actualizacion,
                                  eliminacion, login, logout),
                          descripcion_cambio (TEXT, nullable),
                          fecha_evento (DATETIME, DEFAULT CURRENT_TIMESTAMP),
                          direccion_ip (VARCHAR 45, nullable)
  notificaciones:        id, user_id, titulo, mensaje, fue_leida,
                          fecha_envio
  solicitudes_password:  id, docente_id, estado, fecha_solicitud,
                          fecha_atencion, atendido_por

  8.3 Tablas de CodeIgniter Shield (autenticación)

  users:                 id, username, status, status_message, active,
                          last_active, created_at, updated_at, deleted_at
  auth_identities:       id, user_id, type, name, secret, secret2,
                          expires_at, force_reset, last_used_at,
                          created_at, updated_at
  auth_groups:           id, name, description
  auth_groups_users:     id, user_id, group
  auth_permissions:      id, name, description
  auth_permissions_users: id, permission_id, user_id
  auth_logins:           id, ip_address, email, user_id, date, success
  auth_token_logins:     id, ip_address, email, user_id, date, success
  auth_remember_tokens:  id, user_id, selector, hash, expires, issued_at

  TOTAL: 16 tablas del sistema + 9 tablas de Shield


  10. REGLAS DE NEGOCIO (implementadas)
-----------------------------------------------------------------------
  RN-01: Restricción de Registro Anticipado
         No registrar si Hora_Actual < Hora_Fin_Clase.
         Implementado en: RegistroController::store()

  RN-02: Determinación de Puntualidad
         en_tiempo si Hora_Reg <= Hora_Fin + Tolerancia,
         fuera_de_plazo en caso contrario.
         Implementado en: RegistroController::store()

  RN-03: Privacidad y Aislamiento de Datos
         El docente solo ve su propia carga académica.
         Filtrado por docente autenticado en todas las consultas.

  RN-04: Seguridad de Sesión
         Shield gestiona sesión, bloqueo tras intentos, roles.

  RN-05: Dependencia de Entidades Maestras
         Solo horarios/periodos activos son considerados.

  RN-06: Integridad de Periodos
         No modificar fechas si hay horarios/registros asociados.

  RN-07: Validación de Cruce Horario
         No permitir horarios solapados en el mismo día.

  RN-08: Límite de Horas Semanales
         Suma de horarios <= horas totales del curso.

  RN-09: Contenido Mínimo del Registro
         Descripción no vacía obligatoria (min_length[10]).

  RN-10: Origen del Registro
         Todo registro debe tener un origen identificable (web/app).


  11. ESTADO DEL PROYECTO
-----------------------------------------------------------------------
  Versión:     1.0.0 (en desarrollo activo)
  Framework:   CodeIgniter 4.7+ con Shield 1.3+
  Autenticación:  100% (Shield configurado con grupos y permisos)
  Módulo Admin:   100% (11 controladores funcionales)
  Módulo Coordinador: 100% (9 controladores funcionales)
  Módulo Docente: 100% (7 controladores funcionales)
  Base de Datos:  100% (18 migraciones, 14 seeders)
  Vistas/UI:      95% (35+ vistas funcionales)
  Tests:          25% (PHPUnit configurado, tests unitarios iniciales)
  Documentación:  100% (docs completas del proyecto)
  Notificaciones: 100% (sistema completo de notificaciones)

  Implementación de User Stories:
  - Sprint 1 (HU01-HU05): ✅ 100% — Gestión Administrativa
  - Sprint 2 (HU06-HU11): ✅ 100% — Operación Transaccional
  - Sprint 3 (HU12-HU16): ✅ 100% — Monitoreo y Supervisión
  - Sprint 4 (HU17-HU20): ✅ 100% — Seguridad y Reportes
  - Sprint 5 (Refactorización): ✅ 100% — Calidad de Código, Dashboard Real, Bugfix


  12. COMPARATIVA: v1 (registro-diario) vs v2 (parte-diario-docente)
-----------------------------------------------------------------------
  Característica              v1 (registro-diario)    v2 (parte-diario-docente)
  ──────────────────────────  ──────────────────────  ─────────────────────────
  Autenticación              Casera (sessions hash)  CodeIgniter Shield (RBAC)
  Roles                      2 (docente/coordinador) 3 (admin/coordinador/docente)
  Magic links                No                      Sí
  Remember-me                No                      Sí
  Bloqueo de cuenta          5 intentos -> 15 min    Configurable vía Shield
  Recuperación contraseña    Solicitud -> Coordinador Magic links + Solicitudes
  Gestión de permisos        Por código              Grupos y permisos Shield
  Frontend                   Tailwind CDN            Tailwind build local (npm)
  HTMX                       No                      Sí (carga asíncrona)
  Gráficos                   No                      Chart.js integrado
  Alertas                    No                      SweetAlert2 integrado
  Iconos                     No                      Google Material Symbols
  Tests                      No                      PHPUnit configurado
  Git                        No                      Repositorio activo
  Migraciones                24 archivos             18 archivos
  Seeders                    4 archivos              14 archivos
  Controladores              12                      28
  Modelos                    17                      16
  Vistas                     37+                     35+
  API                        Endpoint básico          No (todo vía web)
  Notificaciones             No                      Sistema completo
  Segregación por programa   No                      Sí (coordinador por programa)
  Tabla personal             No                      Sí (datos personales separados)

=======================================================================
  Fin del documento técnico
=======================================================================

---
- Regresar al [[index|Inicio]]
