Skip to content

Componentes y frontend

El frontend es una SPA Vue 3 con <script setup> y defineComponent según el módulo, Pinia para estado y Bootstrap 5 para estilos. Punto de entrada frontend/src/main.ts → monta App.vue, que renderiza <router-view/> más los overlays globales (ModalContainer, Toasts, LoadingOverlay, RouteProgress).

Árbol de componentes

Rutas (Vue Router)

frontend/src/router/index.ts, historial HTML5, componentes con carga diferida.

RutaNombreComponenteAccesoPermiso (meta.can)
/auth/loginLoginpages/auth/LoginPage.vuePúblico
/auth/forgot-passwordForgotPasswordpages/auth/ForgotPasswordPage.vuePúblico
/auth/reset-passwordResetPasswordpages/auth/ResetPasswordPage.vuePúblico
/auth/verify-emailVerifyEmailpages/auth/VerifyEmailPage.vuePúblico
/dashboardDashboardpages/dashboard/DashboardPage.vuePrivadadashboard:read
/dashboard/vigilantesVigilantespages/dashboard/VigilantesPage.vuePrivadavigilantes:read
/dashboard/sedesSedespages/dashboard/SedesPage.vuePrivadasedes:read
/dashboard/sedes/:sedeId/edificiosEdificiospages/dashboard/EdificiosPage.vuePrivadaedificios:read
/dashboard/edificios/:edificioId/aulasAulaspages/dashboard/AulasPage.vuePrivadaaulas:read
/dashboard/convocatoriasConvocatoriaspages/dashboard/ConvocatoriasPage.vuePrivadaconvocatorias:read
/dashboard/convocatorias/:convocatoriaId/miembrosMiembrospages/dashboard/MiembrosPage.vuePrivadatribunalMiembros:read
/dashboard/convocatorias/:convocatoriaId/ejerciciosEjerciciospages/dashboard/EjerciciosPage.vuePrivadaejercicios:read
/dashboard/templatesTemplatespages/dashboard/TemplatesPage.vuePrivadatemplates:read
/dashboard/configuracionConfiguracionpages/dashboard/ConfiguracionPage.vuePrivadaconfiguracion:read
/dashboard/usersUserspages/dashboard/UsersPage.vuePrivadausers:read
/forbiddenForbiddenpages/errors/ForbiddenPage.vuePública
/redirección a /dashboard
/:pathMatch(.*)*NotFoundpages/errors/NotFoundPage.vuePública

Guards

  • beforeEach:
    1. Si hay accessToken en localStorage y el store no está autenticado → authStore.initialize() (GET /auth/me).
    2. requiresAuth y sin sesión → Login.
    3. Ruta /auth/* con sesión → Dashboard.
    4. meta.can y useAcl().can(permiso) falso → Forbidden.
  • afterEach: cierra la barra de progreso y fija document.title.

Control de acceso (ACL)

FicheroContenido
auth/permissions.tsCatálogo Permissions con claves recurso:acción.
auth/role-permissions.tsMatriz rol→permisos. SUPERADMIN y ADMIN = ["*"]; SUPERVISOR tiene todos los permisos operativos (lectura y CRUD de catálogos, convocatorias, ejercicios, tribunal y asignaciones, además de ASIGNACIONES_READ y CONFIGURACION_READ), excepto TEMPLATES_* y USERS_*. En Configuración, el backend y ConfiguracionPage bloquean las claves de CLAVES_CONFIGURACION_SOLO_ADMIN.
composables/useAcl.tscan(permiso); los roles con "*" siempre pueden.

Se usa en el guard del router, en Sidebar (menú desde config/sidebarMenu.ts) y en las páginas para ocultar botones de acción. El menú admite entradas externas (external: true), como Ayuda (tras Usuarios): en lugar de navegar ejecuta una acción que comprueba url_pagina_ayuda con configuracionStore.checkPaginaAyuda() y abre la página en una pestaña nueva sin cambiar de foco.

Estado global (Pinia)

frontend/src/stores/ contiene 23 stores:

StoreFicheroResponsabilidad
authauthStore.tsLogin/logout, initialize, cambio/recuperación de contraseña, usuario actual. En logout resetea el resto de stores.
useruserStore.tsPerfil propio, avatar y usuarios.
summarysummaryStore.tsKPIs del dashboard.
sedesedeStore.tsCRUD de sedes, municipios/provincias, modales.
edificioedificioStore.tsCRUD de edificios y asignación de plantas.
aulaaulaStore.tsCRUD de aulas y etiquetas.
plantaplantaStore.tsCRUD de plantas y asignación a edificios.
tagtagStore.tsEtiquetas (scope vigilante/aula) y conflictos al borrar.
convocatoriaconvocatoriaStore.tsCRUD y cambio de estado.
miembromiembroStore.tsTribunal: CRUD y renuncias.
vigilantevigilanteStore.tsCRUD, bloqueo, historial, penalizaciones e importación.
ejercicioejercicioStore.tsStore central: CRUD, ubicaciones, grupos, llamamientos, confirmaciones, asignaciones, asistencia, notificaciones y documentos.
tipoEjerciciotipoEjercicioStore.tsCatálogo de tipos de ejercicio.
templatetemplateStore.tsPlantillas de email.
documentTemplatedocumentTemplateStore.tsPlantillas de documento.
simulationStoresimulationStore.tsMotor de asignación rápida: meta, configuración, propuesta, fijados, presets.
configuracionconfiguracionStore.tsParámetros, mapas de colores y comprobación de la URL de ayuda (checkPaginaAyuda).
modalStoremodalStore.tsPila de modales dinámicos (openModal/closeModal).
loadingStoreloadingStore.tsOverlay de carga con contador.
toastStoretoastStore.tsCola de notificaciones toast.
sidebarStoresidebarStore.tsEstado abierto/colapsado del menú.
routeProgressStorerouteProgressStore.tsBarra de progreso de navegación.
preferenciasStorepreferenciasStore.tsPreferencias de UI (pestaña de asignación rápida).
groupClipboardStoregroupClipboardStore.tsPortapapeles de grupos de vigilantes.

Flujo de datos

  • No existe capa services/: los stores invocan directamente api (utils/api.ts).
  • Cachés locales por store (p. ej. edificioStore.sedeEdificios, aulaStore.edificioAulas).
  • simulationStore persiste presets y orden de reglas en localStorage.

Componentes clave

Base

ComponenteResponsabilidad
base/BaseModal.vueModal genérico. El componente interno puede exponer footerButtons, footerLeftText y footerLeftIcon desde su setup para controlar el pie. Z-index apilable y gestión de foco/Escape.
base/ModalContainer.vueRenderiza los modales de modalStore.
base/SkeletonBlock/Cards/Table.vueEstados de carga.
base/ErrorPage.vueMarco de error.

Ejercicios (components/ejercicios/modals/)

ComponenteResponsabilidad
CreateEjercicioModal / EditEjercicioModal / DetailsEjercicioModalAlta, edición y detalle de ejercicio.
TipoEjercicioManagerModal / EditTipoEjercicioModalCatálogo de tipos.
GestionarUbicacionesModal (+ UbicacionSedeBody, UbicacionEdificioBody, EdificioUbicacionCard)Configuración de sedes/edificios/aulas (calcular_aulas/manual, ratio/fijo), validación de impacto y export Excel cliente.
GestionarVigilantesModal (+ GrupoVigilantesBody)Selección de vigilantes, grupos, llamamientos, confirmaciones e importación/exportación.
GestionarAsignacionesModalAsignaciones y asistencia (responsables, anulaciones, penalizaciones), export PDF/XLSX y control de asistencia.
QuickAssignModal + QuickAssignScopeSelector, QuickAssignRulesList, QuickAssignOptionsPanel, QuickAssignSlotsList, QuickAssignResultsPanelAsignación rápida: alcance, reglas, opciones, propuesta y drag&drop. Usa simulationStore.
RealizarNotificacionDialog / PreviewEmailModalRedacción, adjuntos y previsualización de notificaciones.
ConfirmarExportacionLlamamientoDialog / ExportVigilantesFieldsModalExportación Excel de llamamientos y vigilantes.
ImportarConfirmacionesExcelModalImportación de confirmaciones desde Excel.
QuickSelectDialogSelección rápida reutilizable.

Correo

Las plantillas de correo (email_template) llevan el logo embebido como adjunto inline (<img src="cid:siva-logo" />). backend/src/utils/email-assets.ts expone getLogoAttachment() (public/images/logo.png como adjunto inline con Content-ID) y attachLogoIfNeeded(); backend/src/config/email.config.ts envuelve el sendMail del transporter para añadirlo automáticamente cuando el HTML contiene cid:siva-logo. En las vistas previas el cid: se sustituye por la URL remota /images/logo.png (email-template.service.ts en backend y frontend/src/utils/plantilla-email.ts en los diálogos de notificación), por lo que los assets de /public se sirven con Cross-Origin-Resource-Policy: cross-origin (backend/src/app.ts).

Otros dominios

  • components/vigilantes/modals/: CreateVigilanteModal, EditVigilanteModal, DetailsVigilanteModal, ToggleBloqueadoModal, TagManagerModal, EditTagModal, ImportVigilantesModal (+ import/ImportConflictModal, ImportEditRowModal, SplitNameModal).
  • components/sedes/modals/, edificios/modals/, aulas/modals/, convocatorias/modals/, miembros/modals/, users/modals/, templates/modals/: CRUD, detalle y cambios de estado por dominio.

Compartidos

shared/Breadcrumb, ConfirmDialog, DateRangeFilter, YearRangeFilter, SelectFilter, PaginationBar, TagBadge, LoadingOverlay, RouteProgress, Toasts.

Capa de API (utils/api.ts)

  • api: instancia Axios principal, baseURL = VITE_API_URL || "/api".
  • refreshApi: instancia auxiliar sin interceptores, solo para el refresco.
  • Interceptor de request: añade Authorization: Bearer <accessToken> salvo en los endpoints de auth; para FormData elimina Content-Type.
  • Interceptor de response: ante 401 intenta POST /auth/refresh-token, encola peticiones concurrentes y reintenta; si el refresh falla, limpia los tokens y redirige a /auth/login.
  • Tokens: en localStorage (accessToken, refreshToken).

Tipos (frontend/src/types/)

Interfaces por dominio: User/UserProfile, ModalOptions, quickAssign, ejercicio, ejercicio-ubicacion (+ -local), ejercicio-grupo-vigilante, vigilante (+ import), asignacion, llamamiento, notificacion, sede, edificio, aula, planta, convocatoria, miembro, tag, template, document-template, tipo-ejercicio, configuracion, summary, ubicacion-uso. El envoltorio común ApiResponse<T> está en types/index.ts.

Convenciones de UI

El sistema de diseño está descrito en DESIGN.md (paleta "Sala de Exámenes", tipografía Poppins, modales apilables). Requisito de accesibilidad WCAG 2.1 AA (RD 1112/2018), con foco visible, aria-label en botones icon-only y skip-link en el layout.

SIVA — Sistema Integral de Vigilantes de Aulas