Files
admin-edu-space/CHANGELOG_SESSION.md
T

27 KiB

Resumen de Cambios - Sesión de Desarrollo (Rama dev)

Fecha: 2026-09-02 / 2026-09-03
Proyecto: Admin Edu-Space (Flask + PostgreSQL)
Rama: dev


1. Auditoría Git y Puesta en Producción

  • Informe Técnico Completo (DEPLOYMENT_AUDIT.md):
    • Análisis de ramas locales y remotas (main, dev, origin/planning, origin/commissions, origin/spanish).
    • Manual de despliegue en producción con arquitectura WSGI (Gunicorn), Nginx como proxy inverso, Systemd unit y contenedorización Docker / Docker Compose.
  • Archivos Base:
    • Creación de .env.example con todas las variables requeridas (PostgreSQL, Flask, Secret Key, Babel).
    • Creación y posterior actualización de init_db.py para bootstrap inicial seguro de base de datos, roles RBAC y usuario administrador por defecto (admin@edu-space.com / admin123).

2. Base de Datos y Modelos

  • Preferencia de Idioma Persistente:
    • Migración en PostgreSQL agregando la columna preferred_language VARCHAR(10) a la tabla users.
    • Actualización del modelo User en app/models/user.py con el campo preferred_language.
  • Preferencia de Tema Visual Persistente:
    • Migración agregando la columna theme_preference VARCHAR(10) DEFAULT 'auto' NOT NULL a la tabla users.
    • Soporte en el modelo User para valores 'auto', 'light' y 'dark'.
  • Control de Acceso Basado en Roles (RBAC):
    • Migración creando las tablas roles y permissions, y agregando la clave foránea role_id a la tabla users.
    • Modelos Role y Permission en app/models/role.py.
    • Métodos has_permission(module, min_level) y can_manage() en User.

3. Implementación de Vistas y Plantillas

  • Perfil de Usuario (app/templates/auth/profile.html):
    • Vista completa con datos del usuario, rol, estado, fecha de registro y reservas recientes.
    • Formulario integral para configurar Idioma de la interfaz (Detectar automáticamente del navegador, Español, English) y Tema visual (Automático, Modo Claro, Modo Oscuro).
  • Cambio de Contraseña (app/templates/auth/change_password.html):
    • Formulario seguro para actualización de credenciales.
  • Manejo de Errores (app/templates/auth/403.html):
    • Pantalla de error 403 Forbidden estilizada e integrada con el sistema de diseño.

4. Correcciones de UX, Linter y Bugs de Rutas

  • Navbar Limpio (app/templates/base.html):
    • Eliminación del bloque duplicado del menú de usuario System Administrator.
  • Corrección de BuildError en Comisiones:
    • En app/routes/classrooms.py, se agregó el alias de ruta @classrooms_bp.route('/', endpoint='index') para resolver enlaces a classrooms.index.
    • En app/templates/schedule/commissions.html, se reemplazó el filtro inválido de Django truncatewords(20) por el filtro estándar de Jinja2 truncate(100).
  • Corrección Visual en /genetic-optimizer (app/templates/genetic_optimizer.html):
    • Se agregó el espaciado pt-5 mt-4 evitando que la barra fija superior se monte sobre el contenido.
    • Rediseño de tarjeta hero con contraste óptimo y reemplazo de iconos faltantes por Bootstrap Icons (bi bi-...).
  • Corrección de Errores del Linter en schedule/add.html y schedule/view.html:
    • En add.html: Desacople de iteraciones Jinja a <script type="application/json" id="classroom-data"> y parseo con JSON.parse(), eliminando 19 errores sintácticos del editor.
    • En view.html: Formateo de la propiedad inline style en barras de progreso para cumplir con el validador CSS.

5. Rediseño UI/UX, Assets Institucionales y Modo Claro/Oscuro

  • Exploración e Integración de Assets Gráficos:
    • Sincronización desde app/resources hacia app/static/img/:
      • logo_unicaba.png: Logo institucional principal para el login.
      • logo_unicaba_mini.png: Isotipo oficial para el navbar.
      • favicon.png: Favicon vinculado en las plantillas base.
      • icons/: Paquete de resoluciones WebApp/PWA.
  • Sistema de Diseño y Tokens de Color (app/static/css/theme.css):
    • Paleta institucional basada en Magenta/Uva (#B43E8E), acentos violetas (#682CAD / #AE7FFF) y secundarios magentas.
    • Tokens CSS para :root (Modo Claro) y [data-bs-theme="dark"] (Modo Oscuro).
    • Clases especializadas: .auth-card, .navbar-app, .user-avatar-small, .theme-toggle-btn, .badge-brand.
  • Modo Claro / Modo Oscuro Dinámico (app/static/js/theme-toggle.js):
    • Script anti-flickering en <head> para evitar parpadeos visuales al cargar la página.
    • Botón toggle interactivo con persistencia en localStorage.
    • En Modo Claro: Isotipo del navbar con filtro blanco puro (brightness(0) invert(1)) sobre el fondo magenta.
    • Selector de idioma (#languageDropdown) con fondo y bordes de alto contraste y tilde de verificación blanca (bi-check-lg) visible únicamente en el idioma activo sobre gradiente magenta.

6. Internacionalización (i18n), Localización y Formateo de Fechas

  • Detección Inteligente de Subtags Regionales (get_locale en app/__init__.py):
    • Extracción del subtag primario (es a partir de es-419, es-AR, es-ES), resolviendo el fallo donde Werkzeug saltaba erróneamente a inglés.
    • Configuración institucional: BABEL_DEFAULT_LOCALE = 'es' y LANGUAGES = ['es', 'en'].
  • Reglas de Idioma para la Pantalla de Login:
    • Resuelve según el navegador del visitante o recurre a español por defecto.
    • Inmunidad contra cookies o sesiones residuales de otros usuarios.
    • Flash messages traducidos (¡Bienvenido de nuevo!, Has cerrado sesión correctamente).
  • Formateador de Fechas Localizadas en Jinja2:
    • Registro de format_date y format_datetime de Flask-Babel.
    • Fechas en español natural en schedule/today.html (Jueves, 3 de septiembre de 2026), view.html y list.html.
  • Traducción Integral de schedule/add.html:
    • Formulario y textos de ayuda 100% traducidos.
    • Caja dinámica de resumen (JavaScript e i18n-strings) con cálculo de duración y validación de sobrecupo en español.

7. Módulo de Gestión de Accesos (RBAC) con Matriz FortiGate

  • Modelos y Persistencia:
    • Módulo app/models/role.py con Role y Permission.
    • Roles iniciales sembrados: Admin (total, protegido), Docente (reservas y horarios), Operador (aulas y reservas), Consulta (solo lectura).
    • Vinculación del usuario inicial admin@edu-space.com al rol Admin.
  • Matriz de Permisos Estilo FortiGate (/admin/roles/edit/<id>):
    • Tabla interactiva con columnas Access Control y Permissions.
    • Segmented control por módulo con tres estados: 🚫 None, 👁️ Read y ✏️ Read/Write.
    • Color activo verde esmeralda FortiGate (#2D9F6F) con texto e icono en blanco puro.
    • Menú desplegable masivo Set All ▾ (Set All to None, Set All to Read, Set All to Read/Write).
  • Módulo de Administración (app/routes/admin.py):
    • /admin/roles: Listado de roles, conteo de miembros, visualización de permisos en badges (RW, R, -) y modal de borrado seguro.
    • /admin/users: Listado de cuentas con filtros por nombre, rol y estado (Activo/Inactivo), paginación y switch rápido para alternar accesos.
    • /admin/users/add y /admin/users/edit/<id>: Formularios de alta y edición con asignación de rol y contraseña opcional.
  • Seguridad y Decoradores (app/utils/decorators.py):
    • @permission_required(module, min_level) y @admin_required.
    • Pestaña Management en el navbar visible exclusivamente para administradores.
    • Botones de acción sensibles en el navbar (Add Classroom, New Reservation) condicionados a permisos de escritura.

8. Sincronización y Actualización de init_db.py

  • Migraciones DDL automáticas (ALTER TABLE ... ADD COLUMN IF NOT EXISTS) para preferred_language, theme_preference, role_id, y para la tabla careers y columna career_id en subjects.
  • Creación y siembra de roles institucionales y permisos en PostgreSQL.
  • Configuración de salida con codificación UTF-8 segura en Windows para evitar excepciones de cp1252.

9. Sincronización e Importación de Datos Académicos (Google Sheets)

  • Modelo Career (app/models/career.py):
    • Entidad de carreras universitarias vinculada con Subject vía relación uno a muchos.
    • Migración en PostgreSQL de la tabla careers y campo career_id en subjects.
  • Servicio de Extracción (app/services/sheets_importer.py):
    • Integración con la cartelera oficial UniCABA 2026 a través de 6 hojas CSV (Lunes a Sábado).
    • Normalización inteligente de espacios áulicos físicos (Aula 402, Aula 302), espacios virtuales (VIRTUAL), auditorios y salas de reunión.
    • Extracción y parseo de franjas horarias (8:00 a 9:00hs, 17:30 a 21:30hs).
    • Rutina de upsert transaccional e idempotente que previene la duplicación de datos.
    • Generación de reservas/clases en el calendario para poblar instantáneamente las vistas de horario diario y semanal.
  • Interfaz de Gestión (/admin/import):
    • Vista interactiva con métricas en tiempo real (total de carreras, asignaturas, aulas, comisiones y clases).
    • Botón de ejecución manual de sincronización con indicador visual de progreso (spinner).
    • Reporte detallado de resultados y tabla con vista previa de asignaturas y comisiones sincronizadas.
  • Acceso Rápido en Navbar (app/templates/base.html):
    • Nueva opción "Import from Google Sheets" incorporada en el menú desplegable Management, restringida a roles con permisos administrativos.

10. Correcciones en la Vista de Calendario (FullCalendar) y Códigos de Asignatura

  • Carga de Eventos y Corrección de Error 500 (/schedule/calendar-data):
    • Se identificó y corrigió el error AttributeError: 'str' object has no attribute 'value' provocado por llamadas a .status.value sobre campos de estado textuales.
    • Se implementó el TypeDecorator StatusType con la clase StatusString en app/models/reservation.py garantizando compatibilidad total tanto para .status como para .status.value.
    • Sanitización de fechas ISO 8601 con zona horaria (Z, -03:00) enviadas por FullCalendar para comparación adecuada con campos TIMESTAMP WITHOUT TIME ZONE en PostgreSQL.
    • Anclaje de fecha inicial con initialDate: '{{ today_date }}' para posicionar el calendario en la semana activa de cursada 2026.
  • Corrección del Código de Asignatura (subject_code):
    • Se reemplazó la concatenación de código extendido de comisión (Commission.get_full_code() que arrojaba cadenas distorsionadas como INGA0003-C1-2026-22026) por el código de asignatura real del Sheet (Subject.code, ej. INGA0003, ASIG00165).
    • Propiedades de acceso directo añadidas en Reservation: subject_code, subject_name y classroom_name.
  • Jerarquía Visual y Renderizado de Tarjetas (calendar.html):
    • Implementación del hook eventContent en FullCalendar 5 para construir tarjetas personalizadas.
    • Nombre de la Asignatura destacado como elemento principal con tipografía grande y en negrita (font-weight: 700; font-size: 0.88rem).
    • Código de Asignatura y Aula mostrados como datos secundarios en badges de alto contraste (código con fondo oscuro translúcido y aula con isotipo y fondo blanco translúcido).
    • Modal interactivo instantáneo al hacer clic en cualquier evento con todos los detalles académicos de la clase y enlace a la ficha completa.

11. Filtros por Aula y Materia en el Calendario de Reservas

  • Barra de Herramientas de Filtros Interactiva (calendar.html):
    • Filtrar por Aula: Desplegable dinámico con todas las aulas activas ordenadas por edificio y código (físicas y virtuales). Al seleccionar un aula (ej. Aula 402), se eliminan todas las superposiciones visuales mostrando el cronograma limpio de ese espacio.
    • Filtrar por Materia: Desplegable ordenado alfabéticamente con formato [Código] Nombre de la Asignatura. Permite aislar todas las clases de una asignatura en particular.
    • Filtrar por Carrera: Filtro adicional por plan académico/carrera.
    • Botón Limpiar Filtros: Restablece instantáneamente todos los controles y refresca la vista.
    • Insignias de Estado: Indicador en tiempo real de los filtros aplicados y contador de clases visibles (X clases visibles).
  • Soporte en Backend (app/routes/schedule.py):
    • calendar_view: Suministra las listas de classrooms, subjects y careers a la plantilla.
    • calendar_data: Procesa classroom_id, subject_id, career_id y search aplicando cláusulas filter() con JOIN en SQLAlchemy.
    • refetchEvents() de FullCalendar conectado a los eventos change de cada selector para recarga inmediata vía AJAX sin parpadeo de pantalla.

12. Módulo de Gestión de Edificios y Corrección de Vistas de Aulas

  • Corrección de Plantillas de Aulas Faltantes:
    • Se crearon las plantillas app/templates/classrooms/edit.html y app/templates/classrooms/view.html, solucionando el error HTTP 500 (TemplateNotFound: classrooms/edit.html) y la redirección 302 que ocurría al hacer clic en cualquier aula.
    • La vista de ficha técnica del aula ahora muestra detalles de capacidad, piso, estado, edificio y la tabla interactiva de próximas reservas.
  • Modelo y Base de Datos de Edificios (Building):
    • Se creó el modelo app/models/building.py (id, name, code, address, floors, description, is_active, timestamps y relación con Classroom).
    • Migración DDL en PostgreSQL (CREATE TABLE buildings, ALTER TABLE classrooms ADD COLUMN building_id).
    • Actualización de init_db.py y siembra de edificios institucionales iniciales (Edificio Central y Campus Virtual) con vinculación de las 25 aulas existentes.
  • Rutas y Gestión Completa (app/routes/buildings.py):
    • /buildings: Listado con métricas de infraestructura (total de edificios, aulas activas, capacidad total y plantas/pisos), barra de búsqueda y filtros por estado.
    • /buildings/<id>: Vista de detalle con distribución de aulas organizadas visualmente por piso (Planta Baja, Piso 1, Piso 2, etc.), capacidad por sala y acciones directas.
    • /buildings/add y /buildings/<id>/edit: Formularios con validación WTForms para alta y actualización.
    • /buildings/<id>/toggle y /buildings/<id>/delete: Control de activación y eliminación protegida (impide borrar edificios que contengan aulas asignadas).
  • Navegación (app/templates/base.html):
    • Enlace a Edificios (/buildings) incorporado en el menú Classrooms y en el menú administrativo Management.

13. Mapeo de Campos del Google Sheet y Concepto de Turno (Mañana / Tarde)

  • Uso de los 5 Campos de la Cartelera Oficial:
    • Codigo (Subject.code y Commission.code): Almacena el código académico real (INGA0003, ASIG00165). Se expone como badge monoespaciado en eventos de calendario, comisiones y vistas administrativas.
    • Asignatura (Subject.name): Título principal de la materia destacado en texto grande y en negrita en FullCalendar y en la cartelera diaria.
    • Aula (Classroom.code, Classroom.building, Classroom.floor, Classroom.capacity): Normalización de espacios físicos y virtuales vinculados al nuevo modelo Building.
    • Carrera (Career.name y Subject.career_id): Entidad de carrera universitaria con relaciones relacionales normalizadas.
    • Horarios (Commission.schedule, Reservation.start_time, Reservation.end_time): Rango horario parseado hacia objetos datetime nativos para el motor de reservas y el calendario.
  • Modelado y Captura de Turnos (TURNO MAÑANA y TURNO VESPERTINO):
    • Se agregó la columna shift en las tablas commissions y reservations de PostgreSQL y en sus respectivos modelos de SQLAlchemy.
    • El parser de app/services/sheets_importer.py identifica las filas delimitadoras de bloque (TURNO MAÑANA vs TURNO VESPERTINO / TARDE) y corrobora la hora de inicio (< 13:00hs = Mañana, \ge 14:00hs = Tarde).
    • Se sincronizaron todas las comisiones y reservas de cursada: 15 comisiones y 48 reservas en Turno Mañana; 191 comisiones y 364 reservas en Turno Tarde / Vespertino.
    • En el Calendario de Reservas (/schedule/calendar) se añadió el selector Filtrar por Turno (Todos, Mañana, Tarde / Vespertino), permitiendo a los usuarios aislar las franjas horarias y resolver la alta concentración de clases en el horario vespertino (17:30 a 21:30hs).

14. Accesos Directos en Panel, Ocultamiento de Virtuales, Filtro por Piso, Dirección Oficial y Traducción

  • Tarjetas Estadísticas como Accesos Directos Interactivos (app/templates/dashboard.html):
    • Total de Aulas (25): Redirige a /classrooms/list con feedback visual de hover y elevación.
    • Total de Materias (206): Redirige a /schedule/commissions.
    • Reservas de Hoy (18): Redirige a /schedule/today.
    • Esta Semana (103): Redirige a /schedule/calendar.
    • Se añadieron microinteracciones CSS en app/static/css/style.css (.dashboard-stat-card).
  • Ocultar Aulas Virtuales y Filtro por Piso:
    • Calendario (app/templates/schedule/calendar.html): Switch interactivo Ocultar Aulas Virtuales (indica capacidad ilimitada) y selector Filtrar por Piso (Planta Baja, Piso 1, Piso 2, Piso 3, Piso 4). El backend /schedule/calendar-data excluye las aulas virtuales o filtra por número de piso.
    • Cartelera del Día (app/templates/schedule/today.html): Barra de filtros completa con soporte para ocultar virtuales, piso y turno.
    • Listado de Reservas (app/templates/schedule/list.html): Endpoint /schedule/list totalmente operativo con paginación de 20 registros, preservación de query params y filtros de piso, turno, virtuales, estado y rango de fechas.
  • Dirección Oficial de la Sede Central:
    • Se configuró la dirección oficial de la Universidad de la Ciudad de Buenos Aires (UniCABA): Tte. Gral. Juan Domingo Perón 802, CABA en la base de datos, en init_db.py y en el importador app/services/sheets_importer.py.
  • Traducción Integral al Español y Catálogo Babel:
    • Se agregaron traducciones en translations/es/LC_MESSAGES/messages.po y se recompiló el binario .mo con pybabel.
    • Textos traducidos: Buildings \rightarrow Edificios, Management \rightarrow Gestión, Timeline View \rightarrow Vista Cronológica, Grid View \rightarrow Vista en Tabla, Active Classrooms \rightarrow Aulas Ocupadas, All Reservations \rightarrow Todas las Reservas, AI Room Optimizer \rightarrow Optimizador de Aulas, etc.

15. Actualización Integral de Roles RBAC, Capacidad Infinita (∞) y Horarios de Próximas Clases

  • Actualización de Roles, Grupos de Acceso y Matriz de Permisos (app/models/role.py, init_db.py):
    • Se amplió SYSTEM_MODULES para cubrir los 8 módulos del sistema:
      1. classrooms: Aulas y Espacios (físicas, virtuales y capacidades).
      2. buildings: Edificios y Sedes (Sede Central, Campus Virtual, plantas).
      3. reservations: Reservas de Aulas (aprobación, confirmación, cancelación).
      4. schedule: Cartelera y Cronograma (calendario, cartelera del día, comisiones).
      5. academic: Gestión Académica (carreras, asignaturas, códigos).
      6. import: Sincronización Sheets (importador Google Sheets).
      7. optimizer: Optimizador IA (algoritmo genético áulico).
      8. users: Usuarios y Accesos RBAC (cuentas, roles y matriz).
    • Se actualizaron los perfiles estándar (Admin, Docente, Operador, Consulta) en PostgreSQL y en init_db.py.
    • Se tradujeron completamente las plantillas app/templates/admin/roles/list.html y app/templates/admin/roles/edit.html al español.
  • Soporte de Capacidad Infinita (∞) y Aclaración (app/forms/classroom.py, app/templates/classrooms/edit.html):
    • Se actualizó ClassroomForm.capacity con validador personalizado que admite el símbolo ∞, inf, 0 o cualquier capacidad numérica positiva.
    • Se corrigió la validación de floor con InputRequired y NumberRange(min=0) para aceptar Planta Baja (Piso 0).
    • En la interfaz de edición y alta de aulas se incluyó el botón de acción rápida Ilimitada (∞) que rellena automáticamente el campo con el símbolo ∞.
    • Se añadió una leyenda explicativa directa: "Para aulas virtuales o eventos sin aforo restringido, ingrese ∞ o 0 (Capacidad ilimitada)".
    • Se tradujo el botón de guardado a "Guardar Cambios" (reemplazando "Save Changes").
    • El aula virtual institucional (Campus Virtual - VIRTUAL) se actualizó a capacidad 0 (∞), renderizando el badge ∞ Ilimitada en listados y fichas.
  • Próximas Clases: Visualización Exclusiva del Horario (app/templates/dashboard.html, app/templates/classrooms/view.html):
    • En la tabla de Próximas Reservas del panel y en la ficha del aula se cambió la columna a "Horario", removiendo la fecha del calendario y destacando únicamente el rango horario de cursada (ej. 17:30 - 21:30 hs).

16. Desacoplamiento de Hitos Académicos: Catálogo Dinámico de Tipificaciones y Matriz RBAC

  • Modelos de Datos (app/models/milestone.py, app/models/__init__.py):
    • MilestoneType: Catálogo dinámico y administrable con id, name, code (slug único), color (hexadecimal), description, sort_order e is_active.
    • AcademicMilestone: Relacionado mediante clave foránea milestone_type_id a MilestoneType en lugar de strings estáticos.
    • Sembrado de 12 categorías iniciales predeterminadas (Parcial 1, Parcial 2, TP 1, TP 2, TP 3, Recu. P1, Recu. P2, Recu. TP 1, Recu. TP 2, Recu. TP 3, Final, Otro).
  • Matriz de Permisos RBAC (app/models/role.py, init_db.py):
    • Se agregó el módulo milestone_types a SYSTEM_MODULES cubriendo los 9 módulos de la plataforma.
    • Niveles asignados: Admin (RW), Docente (R), Operador (RW), Consulta (R).
  • Vistas y ABM en Management (app/routes/admin.py, app/templates/admin/milestone_types/list.html):
    • Se añadió el ítem de navegación "Tipificaciones" al menú desplegable Management.
    • Tabla completa con badges de color, slugs, switches interactivos de activación/desactivación y modales para dar de alta y editar tipificaciones.
  • API y Modal del Docente (app/routes/main.py, example/aulas-unicaba-mvp/...):
    • Se expuso el endpoint GET /api/milestone-types para consumo desacoplado.
    • Se actualizó el modal de eventos académicos (evento-modal.component.html y .ts) desacoplando las opciones estáticas y cargando dinámicamente el catálogo de tipificaciones activas con fallback automático para funcionamiento offline.

17. Rediseño de Calendario Estilo Google Calendar y Corrección Visual en Eliminación de Roles

  • Manejo de Eventos Simultáneos y Densidad Visual (app/templates/schedule/calendar.html):
    • Se configuró FullCalendar con eventMaxStack: 3, evitando que decenas de reservas concurrentes reduzcan las columnas a franjas milimétricas del 3%.
    • Se habilitó dayMaxEvents: 3, dayMaxEventRows: true y moreLinkClick: 'popover' nativo de Google Calendar con enlaces "+X más".
    • Se fijaron slotMinTime: "07:00:00", slotMaxTime: "23:00:00", slotDuration: "00:30:00", slotLabelInterval: "01:00:00" y nowIndicator: true con la línea roja indicadora de hora actual.
  • Diseño de Tarjetas de Evento Píldora (.gc-event-pill / .gc-month-pill):
    • Estilo Google Calendar con barra lateral de color (magenta UniCABA #B43E8E, ámbar #F59E0B para pendientes, índigo #6366F1 para aulas virtuales).
    • Encabezado compacto con hora de inicio y badge de aula (bi bi-door-open o bi bi-camera-video con fondo suave para VIRTUAL).
    • Tipografía en 0.78rem con elipsis para evitar desbordes y sombras sutiles.
    • Compatibilidad completa con modo oscuro ([data-bs-theme="dark"]).
  • Modal Flotante Interactivo al Clic (#eventPreviewModal):
    • Reemplazo de la navegación directa por un popover flotante estilizado como Google Calendar.
    • Banner artístico superior con accesos rápidos: editar (✏️), eliminar/cancelar (🗑️) y cerrar (✕).
    • Fila principal con indicador cuadrado de color, título completo, código de materia y badge de estado/turno.
    • Lista con íconos: 🕒 horario y fecha completa, 📍 aula/piso/sede (o enlace directo al Campus Virtual), 👤 carrera/comisión/docente, 📝 hito o notas.
    • Botones de acción inferior: "Ver Ficha Completa" y "Cerrar".
  • Corrección de Modal de Eliminación de Roles (app/templates/admin/roles/list.html):
    • Se trasladó el modal de confirmación de eliminación #deleteRoleModal fuera de la celda <td> y de la tabla <table>, situándolo a nivel raíz de la plantilla.
    • Se corrigió el error visual donde el cuadro emergente temblaba, saltaba o se desplazaba por el recálculo de padding/scrollbar de Bootstrap en la celda de la tabla.