Files
admin-edu-space/frontend/views/dashboard.html
T

1318 lines
76 KiB
HTML

{% extends "base.html" %}
{% block title %}
{% if not role or role == 'admin' %}
Panel de Administración - Edu-Space Admin
{% elif role == 'bedelia' %}
Panel Operativo de Bedelía - Edu-Space Admin
{% elif role == 'docente' %}
Panel Docente - Edu-Space Admin
{% else %}
Mi Portal de Cursada - Edu-Space Admin
{% endif %}
{% endblock %}
{% block content %}
{% if user.is_authenticated %}
<div class="container-fluid pt-5 mt-4">
{% endif %}
<!-- ========================================================================= -->
<!-- 1. HEADER & ROLE PERSPECTIVE SWITCHER -->
<!-- ========================================================================= -->
<div class="dashboard-role-banner mb-4 shadow-sm">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-3">
<div>
<div class="d-flex align-items-center gap-2 mb-1 flex-wrap">
<h1 class="h4 mb-0 fw-bold">
¡Hola, {{ user.first_name or user.name }}!
</h1>
<span class="badge {{ user.role_badge_display.class }} role-pill">
<i class="bi {{ user.role_badge_display.icon }}"></i>
{{ user.role_badge_display.label }}
</span>
{% if role != actual_role %}
<span class="badge bg-warning text-dark border font-monospace small">
<i class="bi bi-eye-fill me-1"></i>Modo Vista Previa: {{ role|capitalize }}
</span>
{% endif %}
</div>
<p class="text-muted small mb-0">
{% if not role or role == 'admin' %}
<i class="bi bi-shield-check text-primary me-1"></i>Gestión centralizada de infraestructura, oferta académica, seguridad y trazabilidad institucional.
{% elif role == 'bedelia' %}
<i class="bi bi-building-gear text-warning me-1"></i>Control operativo de campus: asignación de aulas físicas y virtuales, cartelera del día y aprobaciones.
{% elif role == 'docente' %}
<i class="bi bi-person-video3 text-info me-1"></i>Espacio docente: gestión de cursadas, horarios de clase, solicitudes de aula y enlaces de videoconferencia.
{% else %}
<i class="bi bi-mortarboard text-success me-1"></i>Portal de cursada: consulta tu aula de hoy, cronograma semanal de clases y fechas de exámenes.
{% endif %}
</p>
</div>
<!-- Role Perspective Switcher for Admins & Managers -->
{% if user.is_admin() or user.can_manage() %}
<div class="d-flex flex-column align-items-end">
<small class="text-muted fw-semibold mb-1" style="font-size: 0.72rem; letter-spacing: 0.3px;">
<i class="bi bi-person-bounding-box me-1"></i>VER PANEL COMO ROL:
</small>
<div class="btn-group role-view-selector bg-body-tertiary p-1 rounded-pill border" role="group">
<a href="/dashboard?role=admin"
class="btn btn-sm {% if not role or role == 'admin' %}btn-primary active{% else %}btn-outline-secondary border-0{% endif %}">
<i class="bi bi-shield-check me-1"></i>Admin
</a>
<a href="/dashboard?role=bedelia"
class="btn btn-sm {% if role == 'bedelia' %}btn-warning active text-dark{% else %}btn-outline-secondary border-0{% endif %}">
<i class="bi bi-building-gear me-1"></i>Bedelía
</a>
<a href="/dashboard?role=docente"
class="btn btn-sm {% if role == 'docente' %}btn-info active text-white{% else %}btn-outline-secondary border-0{% endif %}">
<i class="bi bi-person-video3 me-1"></i>Docente
</a>
<a href="/dashboard?role=alumno"
class="btn btn-sm {% if role == 'alumno' %}btn-success active text-white{% else %}btn-outline-secondary border-0{% endif %}">
<i class="bi bi-mortarboard me-1"></i>Alumno
</a>
</div>
</div>
{% endif %}
</div>
</div>
<!-- ========================================================================= -->
<!-- 2. ROLE SPECIFIC DASHBOARDS -->
<!-- ========================================================================= -->
{% if not role or role == 'admin' %}
<!-- ========================= [A] ADMIN DASHBOARD ========================= -->
<!-- Admin KPI Cards -->
<div class="row mb-4 g-3">
<div class="col-md-6 col-xl-3">
<a href="/classrooms/list_classrooms" class="text-decoration-none text-reset d-block h-100">
<div class="card border-0 shadow-sm h-100 dashboard-stat-card transition-all glass-card">
<div class="card-body p-3">
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="text-muted small fw-semibold text-uppercase">Aulas Totales</span>
<h2 class="mb-0 fw-bold mt-1 text-primary">{{ stats.total_classrooms }}</h2>
</div>
<div class="bg-primary bg-opacity-10 rounded-circle p-3 text-primary">
<i class="bi bi-door-open fs-3"></i>
</div>
</div>
<div class="d-flex align-items-center justify-content-between pt-3 mt-2 border-top text-primary small fw-semibold">
<span>Ver catálogo de espacios</span>
<i class="bi bi-arrow-right"></i>
</div>
</div>
</div>
</a>
</div>
<div class="col-md-6 col-xl-3">
<a href="/admin/subjects_list" class="text-decoration-none text-reset d-block h-100">
<div class="card border-0 shadow-sm h-100 dashboard-stat-card transition-all glass-card">
<div class="card-body p-3">
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="text-muted small fw-semibold text-uppercase">Oferta de Materias</span>
<h2 class="mb-0 fw-bold mt-1 text-success">{{ stats.total_subjects }}</h2>
</div>
<div class="bg-success bg-opacity-10 rounded-circle p-3 text-success">
<i class="bi bi-book fs-3"></i>
</div>
</div>
<div class="d-flex align-items-center justify-content-between pt-3 mt-2 border-top text-success small fw-semibold">
<span>Gestión académica</span>
<i class="bi bi-arrow-right"></i>
</div>
</div>
</div>
</a>
</div>
<div class="col-md-6 col-xl-3">
<a href="/admin/users_list" class="text-decoration-none text-reset d-block h-100">
<div class="card border-0 shadow-sm h-100 dashboard-stat-card transition-all glass-card">
<div class="card-body p-3">
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="text-muted small fw-semibold text-uppercase">Usuarios Activos</span>
<h2 class="mb-0 fw-bold mt-1 text-info">{{ stats.total_users or 1 }}</h2>
</div>
<div class="bg-info bg-opacity-10 rounded-circle p-3 text-info">
<i class="bi bi-people fs-3"></i>
</div>
</div>
<div class="d-flex align-items-center justify-content-between pt-3 mt-2 border-top text-info small fw-semibold">
<span>Control de usuarios y roles</span>
<i class="bi bi-arrow-right"></i>
</div>
</div>
</div>
</a>
</div>
<div class="col-md-6 col-xl-3">
<a href="/schedule/today_schedule" class="text-decoration-none text-reset d-block h-100">
<div class="card border-0 shadow-sm h-100 dashboard-stat-card transition-all glass-card">
<div class="card-body p-3">
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="text-muted small fw-semibold text-uppercase">Reservas de Hoy</span>
<h2 class="mb-0 fw-bold mt-1 text-warning">{{ stats.today_reservations }}</h2>
</div>
<div class="bg-warning bg-opacity-10 rounded-circle p-3 text-warning">
<i class="bi bi-calendar-check fs-3"></i>
</div>
</div>
<div class="d-flex align-items-center justify-content-between pt-3 mt-2 border-top text-warning small fw-semibold">
<span>Cartelera en tiempo real</span>
<i class="bi bi-arrow-right"></i>
</div>
</div>
</div>
</a>
</div>
</div>
<!-- Panel de Balance y Percentiles de Asignación de Aulas (Admin) -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-body-tertiary border-0 py-3 d-flex justify-content-between align-items-center flex-wrap gap-2">
<div class="d-flex align-items-center gap-2">
<i class="bi bi-pie-chart-fill text-primary fs-5"></i>
<div>
<h5 class="mb-0 h6 fw-bold">Estado y Percentiles de Asignación de Aulas Físicas</h5>
<small class="text-muted">Balance de capacidad instalada en Edificio Central (Total: {{ stats.physical_classrooms }} aulas físicas)</small>
</div>
</div>
<div class="d-flex gap-2 align-items-center flex-wrap">
<span class="badge bg-success-subtle text-success border border-success-subtle px-2 py-1">
<i class="bi bi-check2-circle me-1"></i>{{ stats.assigned_classrooms }} Asignadas ({{ stats.assigned_classrooms_pct }}%)
</span>
<span class="badge bg-warning-subtle text-warning-emphasis border border-warning-subtle px-2 py-1">
<i class="bi bi-door-open me-1"></i>{{ stats.unassigned_classrooms }} Sin Asignar ({{ stats.unassigned_classrooms_pct }}%)
</span>
</div>
</div>
<div class="card-body p-3">
<div class="row g-3 align-items-center">
<!-- Tarjeta 1: Asignadas -->
<div class="col-md-3">
<a href="/classrooms/list_classrooms?view_mode=physical" class="text-decoration-none text-reset d-block h-100">
<div class="p-3 rounded bg-success bg-opacity-10 border border-success-subtle h-100 text-center transition-all">
<div class="d-flex justify-content-between align-items-center mb-1">
<span class="text-success small fw-bold text-uppercase">Aulas Asignadas</span>
<span class="badge bg-success text-white font-monospace">{{ stats.assigned_classrooms_pct }}%</span>
</div>
<div class="d-flex align-items-baseline justify-content-center gap-2 my-2">
<h2 class="mb-0 fw-bold text-success">{{ stats.assigned_classrooms }}</h2>
<span class="text-muted small">/ {{ stats.physical_classrooms }} aulas</span>
</div>
<div class="text-success small pt-2 border-top border-success-subtle d-flex align-items-center justify-content-between">
<span>Con cursadas activas</span>
<i class="bi bi-arrow-right"></i>
</div>
</div>
</a>
</div>
<!-- Tarjeta 2: Sin Asignar -->
<div class="col-md-3">
<a href="/classrooms/list_classrooms?view_mode=physical" class="text-decoration-none text-reset d-block h-100">
<div class="p-3 rounded bg-warning bg-opacity-10 border border-warning-subtle h-100 text-center transition-all">
<div class="d-flex justify-content-between align-items-center mb-1">
<span class="text-warning-emphasis small fw-bold text-uppercase">Aulas Sin Asignar</span>
<span class="badge bg-warning text-dark font-monospace">{{ stats.unassigned_classrooms_pct }}%</span>
</div>
<div class="d-flex align-items-baseline justify-content-center gap-2 my-2">
<h2 class="mb-0 fw-bold text-warning-emphasis">{{ stats.unassigned_classrooms }}</h2>
<span class="text-muted small">/ {{ stats.physical_classrooms }} aulas</span>
</div>
<div class="text-warning-emphasis small pt-2 border-top border-warning-subtle d-flex align-items-center justify-content-between">
<span>Espacios disponibles</span>
<i class="bi bi-arrow-right"></i>
</div>
</div>
</a>
</div>
<!-- Tarjeta 3: Barras Comparativas de Percentil (Cuatrimestre y Hoy) -->
<div class="col-md-6">
<div class="p-3 rounded bg-body-tertiary border h-100 d-flex flex-column justify-content-center">
<!-- Nivel 1: Cuatrimestre -->
<div class="d-flex justify-content-between align-items-center mb-1">
<span class="small fw-semibold text-body">
<i class="bi bi-calendar-check me-1 text-primary"></i>Planificación Cuatrimestral:
</span>
<span class="small font-monospace text-muted">
<strong class="text-success">{{ stats.assigned_classrooms_pct }}%</strong> Asignado ({{ stats.assigned_classrooms }} aulas) · <strong class="text-warning-emphasis">{{ stats.unassigned_classrooms_pct }}%</strong> Libre ({{ stats.unassigned_classrooms }} aula)
</span>
</div>
<div class="progress mb-3" style="height: 14px;">
<div class="progress-bar bg-success progress-bar-striped" role="progressbar" style="width: {{ stats.assigned_classrooms_pct }}%;"
aria-valuenow="{{ stats.assigned_classrooms_pct }}" aria-valuemin="0" aria-valuemax="100"
title="Asignadas: {{ stats.assigned_classrooms }} ({{ stats.assigned_classrooms_pct }}%)">
{{ stats.assigned_classrooms_pct }}%
</div>
<div class="progress-bar bg-warning" role="progressbar" style="width: {{ stats.unassigned_classrooms_pct }}%;"
aria-valuenow="{{ stats.unassigned_classrooms_pct }}" aria-valuemin="0" aria-valuemax="100"
title="Sin Asignar: {{ stats.unassigned_classrooms }} ({{ stats.unassigned_classrooms_pct }}%)">
{{ stats.unassigned_classrooms_pct }}%
</div>
</div>
<!-- Nivel 2: Día Operativo -->
<div class="d-flex justify-content-between align-items-center mb-1">
<span class="small fw-semibold text-body">
<i class="bi bi-clock-history me-1 text-info"></i>Ocupación Operativa de Hoy:
</span>
<span class="small font-monospace text-muted">
<strong class="text-info">{{ stats.assigned_today_classrooms }}</strong> en uso ({{ stats.assigned_today_pct }}%) · <strong class="text-secondary">{{ stats.unassigned_today_classrooms }}</strong> libres ({{ stats.unassigned_today_pct }}%)
</span>
</div>
<div class="progress" style="height: 10px;">
<div class="progress-bar bg-info" role="progressbar" style="width: {{ stats.assigned_today_pct }}%;"
aria-valuenow="{{ stats.assigned_today_pct }}" aria-valuemin="0" aria-valuemax="100"
title="En uso hoy: {{ stats.assigned_today_classrooms }}">
</div>
<div class="progress-bar bg-secondary-subtle" role="progressbar" style="width: {{ stats.unassigned_today_pct }}%;"
aria-valuenow="{{ stats.unassigned_today_pct }}" aria-valuemin="0" aria-valuemax="100"
title="Libres hoy: {{ stats.unassigned_today_classrooms }}">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Admin Visual Menu Launchpad -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-body-tertiary border-0 py-3">
<div class="d-flex justify-content-between align-items-center">
<h5 class="mb-0 h6 fw-bold">
<i class="bi bi-grid-3x3-gap-fill text-primary me-2"></i>Módulos de Gestión & Visualización de Menús
</h5>
<span class="badge bg-primary-subtle text-primary border border-primary-subtle">Vista Administrador</span>
</div>
</div>
<div class="card-body p-3">
<div class="row g-3">
<div class="col-md-6 col-lg-3">
<a href="/classrooms/list_classrooms" class="card h-100 p-3 action-menu-card bg-body">
<div class="d-flex align-items-center">
<div class="quick-icon-circle bg-primary-subtle text-primary me-3">
<i class="bi bi-door-open fs-4"></i>
</div>
<div>
<h6 class="mb-1 fw-bold text-body">Espacios & Sedes</h6>
<p class="text-muted small mb-0">Aulas físicas, virtuales y edificios</p>
</div>
</div>
</a>
</div>
<div class="col-md-6 col-lg-3">
<a href="/schedule/calendar_view" class="card h-100 p-3 action-menu-card bg-body">
<div class="d-flex align-items-center">
<div class="quick-icon-circle bg-info-subtle text-info me-3">
<i class="bi bi-calendar-week fs-4"></i>
</div>
<div>
<h6 class="mb-1 fw-bold text-body">Cronograma & Reservas</h6>
<p class="text-muted small mb-0">Calendario, cartelera y reservas</p>
</div>
</div>
</a>
</div>
<div class="col-md-6 col-lg-3">
<a href="/genetic_algorithm_web/genetic_optimizer" class="card h-100 p-3 action-menu-card bg-body">
<div class="d-flex align-items-center">
<div class="quick-icon-circle bg-warning-subtle text-warning me-3">
<i class="bi bi-cpu fs-4"></i>
</div>
<div>
<h6 class="mb-1 fw-bold text-body">Optimizador IA</h6>
<p class="text-muted small mb-0">Asignación genética inteligente</p>
</div>
</div>
</a>
</div>
<div class="col-md-6 col-lg-3">
<a href="/admin/audit_logs" class="card h-100 p-3 action-menu-card bg-body">
<div class="d-flex align-items-center">
<div class="quick-icon-circle bg-danger-subtle text-danger me-3">
<i class="bi bi-shield-lock fs-4"></i>
</div>
<div>
<h6 class="mb-1 fw-bold text-body">Auditoría & RBAC</h6>
<p class="text-muted small mb-0">Trazabilidad y control de permisos</p>
</div>
</div>
</a>
</div>
</div>
</div>
</div>
<!-- Admin Analytics & Audit Row -->
<div class="row mb-4">
<!-- Monthly Chart -->
<div class="col-lg-8 mb-4 mb-lg-0">
<div class="card border-0 shadow-sm h-100 glass-card">
<div class="card-header border-0 py-3 bg-body-tertiary d-flex justify-content-between align-items-center">
<h5 class="mb-0 h6 fw-bold">
<i class="bi bi-graph-up text-primary me-2"></i>Tendencia de Ocupación y Reservas Mensuales
</h5>
<a href="/admin/occupancy_metrics" class="btn btn-sm btn-outline-primary">
<i class="bi bi-bar-chart me-1"></i>Métricas Detalladas
</a>
</div>
<div class="card-body">
<canvas id="monthlyChart" height="110"></canvas>
</div>
</div>
</div>
<!-- Recent Audit Logs -->
<div class="col-lg-4">
<div class="card border-0 shadow-sm h-100 glass-card">
<div class="card-header border-0 py-3 bg-body-tertiary d-flex justify-content-between align-items-center">
<h5 class="mb-0 h6 fw-bold">
<i class="bi bi-shield-check text-success me-2"></i>Últimos Eventos de Auditoría
</h5>
<a href="/admin/audit_logs" class="btn btn-sm btn-outline-secondary py-0 px-2">
Ver todo
</a>
</div>
<div class="card-body p-0">
{% if role_data.recent_audits %}
<ul class="list-group list-group-flush small">
{% for audit in role_data.recent_audits %}
<li class="list-group-item d-flex justify-content-between align-items-center py-2 px-3">
<div>
<div class="fw-semibold text-truncate" style="max-width: 180px;">
{{ audit.user_email or 'Sistema' }}
</div>
<span class="text-muted font-monospace" style="font-size: 0.72rem;">
{{ audit.created_at }} · {{ audit.module }}
</span>
</div>
<div>
{% if audit.action == 'CREATE' %}
<span class="badge bg-success-subtle text-success border">{{ audit.action }}</span>
{% elif audit.action == 'UPDATE' %}
<span class="badge bg-primary-subtle text-primary border">{{ audit.action }}</span>
{% elif audit.action == 'DELETE' %}
<span class="badge bg-danger-subtle text-danger border">{{ audit.action }}</span>
{% else %}
<span class="badge bg-secondary-subtle text-secondary border">{{ audit.action }}</span>
{% endif %}
</div>
</li>
{% endfor %}
</ul>
{% else %}
<div class="text-center py-4 text-muted">
<i class="bi bi-shield-check fs-2 d-block mb-1 text-secondary"></i>
<span class="small">No hay registros recientes.</span>
</div>
{% endif %}
</div>
</div>
</div>
</div>
{% elif role == 'bedelia' %}
<!-- ========================= [B] BEDELÍA DASHBOARD ========================= -->
<!-- Bedelía KPI Cards -->
<div class="row mb-4 g-3">
<div class="col-md-6 col-xl-3">
<a href="/classrooms/list_classrooms" class="text-decoration-none text-reset d-block h-100">
<div class="card border-0 shadow-sm h-100 dashboard-stat-card transition-all glass-card">
<div class="card-body p-3">
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="text-muted small fw-semibold text-uppercase">Aulas Presenciales</span>
<h2 class="mb-0 fw-bold mt-1 text-primary">{{ stats.physical_classrooms }}</h2>
</div>
<div class="bg-primary bg-opacity-10 rounded-circle p-3 text-primary">
<i class="bi bi-door-open fs-3"></i>
</div>
</div>
<div class="d-flex align-items-center justify-content-between pt-3 mt-2 border-top text-primary small fw-semibold">
<span>Ver por piso y edificio</span>
<i class="bi bi-arrow-right"></i>
</div>
</div>
</div>
</a>
</div>
<div class="col-md-6 col-xl-3">
<a href="/classrooms/list_classrooms" class="text-decoration-none text-reset d-block h-100">
<div class="card border-0 shadow-sm h-100 dashboard-stat-card transition-all glass-card">
<div class="card-body p-3">
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="text-muted small fw-semibold text-uppercase">Salas Virtuales</span>
<h2 class="mb-0 fw-bold mt-1 text-info">{{ stats.virtual_classrooms }}</h2>
</div>
<div class="bg-info bg-opacity-10 rounded-circle p-3 text-info">
<i class="bi bi-camera-video fs-3"></i>
</div>
</div>
<div class="d-flex align-items-center justify-content-between pt-3 mt-2 border-top text-info small fw-semibold">
<span>Salas Meet / Teams / Zoom</span>
<i class="bi bi-arrow-right"></i>
</div>
</div>
</div>
</a>
</div>
<div class="col-md-6 col-xl-3">
<a href="/schedule/today_schedule" class="text-decoration-none text-reset d-block h-100">
<div class="card border-0 shadow-sm h-100 dashboard-stat-card transition-all glass-card">
<div class="card-body p-3">
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="text-muted small fw-semibold text-uppercase">Clases de Hoy</span>
<h2 class="mb-0 fw-bold mt-1 text-success">{{ stats.today_reservations }}</h2>
</div>
<div class="bg-success bg-opacity-10 rounded-circle p-3 text-success">
<i class="bi bi-calendar-check fs-3"></i>
</div>
</div>
<div class="d-flex align-items-center justify-content-between pt-3 mt-2 border-top text-success small fw-semibold">
<span>Control operativo de hoy</span>
<i class="bi bi-arrow-right"></i>
</div>
</div>
</div>
</a>
</div>
<div class="col-md-6 col-xl-3">
<a href="/schedule/list_reservations" class="text-decoration-none text-reset d-block h-100">
<div class="card border-0 shadow-sm h-100 dashboard-stat-card transition-all {% if stats.pending_count > 0 %}border-warning{% endif %}">
<div class="card-body p-3">
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="text-muted small fw-semibold text-uppercase">Pendientes de Confirmar</span>
<h2 class="mb-0 fw-bold mt-1 {% if stats.pending_count > 0 %}text-danger{% else %}text-muted{% endif %}">
{{ stats.pending_count }}
</h2>
</div>
<div class="{% if stats.pending_count > 0 %}bg-danger bg-opacity-10 text-danger{% else %}bg-secondary bg-opacity-10 text-secondary{% endif %} rounded-circle p-3">
<i class="bi bi-clock-history fs-3"></i>
</div>
</div>
<div class="d-flex align-items-center justify-content-between pt-3 mt-2 border-top small fw-semibold {% if stats.pending_count > 0 %}text-danger{% else %}text-muted{% endif %}">
<span>{% if stats.pending_count > 0 %}Requieren confirmación{% else %}Al día{% endif %}</span>
<i class="bi bi-arrow-right"></i>
</div>
</div>
</div>
</a>
</div>
</div>
<!-- Panel de Balance y Percentiles de Asignación de Aulas (Bedelía) -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-body-tertiary border-0 py-3 d-flex justify-content-between align-items-center flex-wrap gap-2">
<div class="d-flex align-items-center gap-2">
<i class="bi bi-pie-chart-fill text-warning fs-5"></i>
<div>
<h5 class="mb-0 h6 fw-bold">Gestión y Percentiles de Asignación de Aulas (Bedelía)</h5>
<small class="text-muted">Control de asignación de espacios físicos (Total: {{ stats.physical_classrooms }} aulas físicas en Sede Central)</small>
</div>
</div>
<div class="d-flex gap-2 align-items-center flex-wrap">
<span class="badge bg-success-subtle text-success border border-success-subtle px-2 py-1">
<i class="bi bi-check2-circle me-1"></i>{{ stats.assigned_classrooms }} Asignadas ({{ stats.assigned_classrooms_pct }}%)
</span>
<span class="badge bg-warning-subtle text-warning-emphasis border border-warning-subtle px-2 py-1">
<i class="bi bi-door-open me-1"></i>{{ stats.unassigned_classrooms }} Sin Asignar ({{ stats.unassigned_classrooms_pct }}%)
</span>
</div>
</div>
<div class="card-body p-3">
<div class="row g-3 align-items-center">
<!-- Tarjeta 1: Asignadas -->
<div class="col-md-3">
<a href="/classrooms/list_classrooms?view_mode=physical" class="text-decoration-none text-reset d-block h-100">
<div class="p-3 rounded bg-success bg-opacity-10 border border-success-subtle h-100 text-center transition-all">
<div class="d-flex justify-content-between align-items-center mb-1">
<span class="text-success small fw-bold text-uppercase">Aulas Asignadas</span>
<span class="badge bg-success text-white font-monospace">{{ stats.assigned_classrooms_pct }}%</span>
</div>
<div class="d-flex align-items-baseline justify-content-center gap-2 my-2">
<h2 class="mb-0 fw-bold text-success">{{ stats.assigned_classrooms }}</h2>
<span class="text-muted small">/ {{ stats.physical_classrooms }} aulas</span>
</div>
<div class="text-success small pt-2 border-top border-success-subtle d-flex align-items-center justify-content-between">
<span>Ver aulas con cursada</span>
<i class="bi bi-arrow-right"></i>
</div>
</div>
</a>
</div>
<!-- Tarjeta 2: Sin Asignar -->
<div class="col-md-3">
<a href="/schedule/add_reservation" class="text-decoration-none text-reset d-block h-100">
<div class="p-3 rounded bg-warning bg-opacity-10 border border-warning-subtle h-100 text-center transition-all">
<div class="d-flex justify-content-between align-items-center mb-1">
<span class="text-warning-emphasis small fw-bold text-uppercase">Aulas Sin Asignar</span>
<span class="badge bg-warning text-dark font-monospace">{{ stats.unassigned_classrooms_pct }}%</span>
</div>
<div class="d-flex align-items-baseline justify-content-center gap-2 my-2">
<h2 class="mb-0 fw-bold text-warning-emphasis">{{ stats.unassigned_classrooms }}</h2>
<span class="text-muted small">/ {{ stats.physical_classrooms }} aulas</span>
</div>
<div class="text-warning-emphasis small pt-2 border-top border-warning-subtle d-flex align-items-center justify-content-between">
<span>Asignar espacio libre</span>
<i class="bi bi-plus-circle"></i>
</div>
</div>
</a>
</div>
<!-- Tarjeta 3: Barras Comparativas de Percentil (Cuatrimestre y Hoy) -->
<div class="col-md-6">
<div class="p-3 rounded bg-body-tertiary border h-100 d-flex flex-column justify-content-center">
<!-- Nivel 1: Cuatrimestre -->
<div class="d-flex justify-content-between align-items-center mb-1">
<span class="small fw-semibold text-body">
<i class="bi bi-calendar-check me-1 text-primary"></i>Asignación Cuatrimestral:
</span>
<span class="small font-monospace text-muted">
<strong class="text-success">{{ stats.assigned_classrooms_pct }}%</strong> Asignado ({{ stats.assigned_classrooms }} aulas) · <strong class="text-warning-emphasis">{{ stats.unassigned_classrooms_pct }}%</strong> Disponible ({{ stats.unassigned_classrooms }} aula)
</span>
</div>
<div class="progress mb-3" style="height: 14px;">
<div class="progress-bar bg-success progress-bar-striped" role="progressbar" style="width: {{ stats.assigned_classrooms_pct }}%;"
aria-valuenow="{{ stats.assigned_classrooms_pct }}" aria-valuemin="0" aria-valuemax="100"
title="Asignadas: {{ stats.assigned_classrooms }} ({{ stats.assigned_classrooms_pct }}%)">
{{ stats.assigned_classrooms_pct }}%
</div>
<div class="progress-bar bg-warning" role="progressbar" style="width: {{ stats.unassigned_classrooms_pct }}%;"
aria-valuenow="{{ stats.unassigned_classrooms_pct }}" aria-valuemin="0" aria-valuemax="100"
title="Sin Asignar: {{ stats.unassigned_classrooms }} ({{ stats.unassigned_classrooms_pct }}%)">
{{ stats.unassigned_classrooms_pct }}%
</div>
</div>
<!-- Nivel 2: Día Operativo -->
<div class="d-flex justify-content-between align-items-center mb-1">
<span class="small fw-semibold text-body">
<i class="bi bi-clock-history me-1 text-info"></i>Ocupación Operativa de Hoy:
</span>
<span class="small font-monospace text-muted">
<strong class="text-info">{{ stats.assigned_today_classrooms }}</strong> en uso ({{ stats.assigned_today_pct }}%) · <strong class="text-secondary">{{ stats.unassigned_today_classrooms }}</strong> libres ({{ stats.unassigned_today_pct }}%)
</span>
</div>
<div class="progress" style="height: 10px;">
<div class="progress-bar bg-info" role="progressbar" style="width: {{ stats.assigned_today_pct }}%;"
aria-valuenow="{{ stats.assigned_today_pct }}" aria-valuemin="0" aria-valuemax="100"
title="En uso hoy: {{ stats.assigned_today_classrooms }}">
</div>
<div class="progress-bar bg-secondary-subtle" role="progressbar" style="width: {{ stats.unassigned_today_pct }}%;"
aria-valuenow="{{ stats.unassigned_today_pct }}" aria-valuemin="0" aria-valuemax="100"
title="Libres hoy: {{ stats.unassigned_today_classrooms }}">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Bedelía Operational Launchpad -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-body-tertiary border-0 py-3">
<div class="d-flex justify-content-between align-items-center">
<h5 class="mb-0 h6 fw-bold">
<i class="bi bi-lightning-charge-fill text-warning me-2"></i>Acciones Rápidas & Menús Operativos de Bedelía
</h5>
<span class="badge bg-warning-subtle text-warning border border-warning-subtle">Vista Bedelía</span>
</div>
</div>
<div class="card-body p-3">
<div class="row g-3">
<div class="col-md-6 col-lg-3">
<a href="/schedule/today_schedule" class="card h-100 p-3 action-menu-card bg-body">
<div class="d-flex align-items-center">
<div class="quick-icon-circle bg-info-subtle text-info me-3">
<i class="bi bi-calendar-day fs-4"></i>
</div>
<div>
<h6 class="mb-1 fw-bold text-body">Cartelera del Día</h6>
<p class="text-muted small mb-0">Supervisión en vivo de aulas</p>
</div>
</div>
</a>
</div>
<div class="col-md-6 col-lg-3">
<a href="/schedule/add_reservation" class="card h-100 p-3 action-menu-card bg-body">
<div class="d-flex align-items-center">
<div class="quick-icon-circle bg-success-subtle text-success me-3">
<i class="bi bi-calendar-plus fs-4"></i>
</div>
<div>
<h6 class="mb-1 fw-bold text-body">Asignar / Nueva Reserva</h6>
<p class="text-muted small mb-0">Cargar reserva de espacio</p>
</div>
</div>
</a>
</div>
<div class="col-md-6 col-lg-3">
<a href="/admin/google_sheets_import" class="card h-100 p-3 action-menu-card bg-body">
<div class="d-flex align-items-center">
<div class="quick-icon-circle bg-primary-subtle text-primary me-3">
<i class="bi bi-file-earmark-spreadsheet fs-4"></i>
</div>
<div>
<h6 class="mb-1 fw-bold text-body">Sincronizar Sheets</h6>
<p class="text-muted small mb-0">Importación automática de cursadas</p>
</div>
</div>
</a>
</div>
<div class="col-md-6 col-lg-3">
<a href="/admin/occupancy_metrics" class="card h-100 p-3 action-menu-card bg-body">
<div class="d-flex align-items-center">
<div class="quick-icon-circle bg-warning-subtle text-warning me-3">
<i class="bi bi-graph-up-arrow fs-4"></i>
</div>
<div>
<h6 class="mb-1 fw-bold text-body">Métricas de Ocupación</h6>
<p class="text-muted small mb-0">Monitoreo de saturación y picos</p>
</div>
</div>
</a>
</div>
</div>
</div>
</div>
<!-- Pending Approvals & Today's Schedule -->
{% if role_data.pending_reservations %}
<div class="card border-warning shadow-sm mb-4">
<div class="card-header bg-warning bg-opacity-10 border-warning py-3 d-flex justify-content-between align-items-center">
<h5 class="mb-0 h6 fw-bold text-warning-emphasis">
<i class="bi bi-exclamation-triangle-fill me-2"></i>Solicitudes de Reserva Pendientes de Confirmación ({{ role_data.pending_reservations|length }})
</h5>
<a href="/schedule/list_reservations" class="btn btn-sm btn-warning">
Gestionar Todas
</a>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead class="table-light small">
<tr>
<th class="ps-3">Horario / Fecha</th>
<th>Aula</th>
<th>Asignatura</th>
<th>Docente / Solicitante</th>
<th class="text-end pe-3">Acción</th>
</tr>
</thead>
<tbody>
{% for pres in role_data.pending_reservations %}
<tr>
<td class="ps-3 font-monospace small">
<strong>{{ pres.start_time }}</strong> {{ pres.start_time }} - {{ pres.end_time }} hs
</td>
<td>
<span class="badge bg-secondary-subtle text-secondary border font-monospace">
{{ pres.classroom.code if pres.classroom else 'Sin Aula' }}
</span>
</td>
<td>
<div class="fw-semibold text-primary">{{ pres.subject_name }}</div>
</td>
<td class="small">{{ pres.user.name if pres.user else 'Docente' }}</td>
<td class="text-end pe-3">
<a href="/schedule/view_reservation?id={{ pres.id }}" class="btn btn-sm btn-outline-primary py-0 px-2">
<i class="bi bi-check-circle me-1"></i>Revisar
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endif %}
{% elif role == 'docente' %}
<!-- ========================= [C] DOCENTE DASHBOARD ========================= -->
<!-- Docente KPI Cards -->
<div class="row mb-4 g-3">
<div class="col-md-4 col-xl-4">
<div class="card border-0 shadow-sm h-100 dashboard-stat-card transition-all glass-card">
<div class="card-body p-3">
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="text-muted small fw-semibold text-uppercase">Mis Clases Hoy</span>
<h2 class="mb-0 fw-bold mt-1 text-primary">{{ stats.my_today_count }}</h2>
</div>
<div class="bg-primary bg-opacity-10 rounded-circle p-3 text-primary">
<i class="bi bi-clock-history fs-3"></i>
</div>
</div>
<div class="d-flex align-items-center justify-content-between pt-3 mt-2 border-top text-primary small fw-semibold">
<span>Sesiones del día</span>
<i class="bi bi-check2-circle"></i>
</div>
</div>
</div>
</div>
<div class="col-md-4 col-xl-4">
<a href="/schedule/list_reservations" class="text-decoration-none text-reset d-block h-100">
<div class="card border-0 shadow-sm h-100 dashboard-stat-card transition-all glass-card">
<div class="card-body p-3">
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="text-muted small fw-semibold text-uppercase">Mis Reservas Activas</span>
<h2 class="mb-0 fw-bold mt-1 text-success">{{ stats.my_total_reservations }}</h2>
</div>
<div class="bg-success bg-opacity-10 rounded-circle p-3 text-success">
<i class="bi bi-calendar-check fs-3"></i>
</div>
</div>
<div class="d-flex align-items-center justify-content-between pt-3 mt-2 border-top text-success small fw-semibold">
<span>Ver todas mis reservas</span>
<i class="bi bi-arrow-right"></i>
</div>
</div>
</div>
</a>
</div>
<div class="col-md-4 col-xl-4">
<a href="/classrooms/list_classrooms?view_mode=virtual" class="text-decoration-none text-reset d-block h-100">
<div class="card border-0 shadow-sm h-100 dashboard-stat-card transition-all glass-card">
<div class="card-body p-3">
<div class="d-flex justify-content-between align-items-center">
<div>
<span class="text-muted small fw-semibold text-uppercase">Aulas Virtuales</span>
<h2 class="mb-0 fw-bold mt-1 text-info">{{ stats.virtual_rooms_count }}</h2>
</div>
<div class="bg-info bg-opacity-10 rounded-circle p-3 text-info">
<i class="bi bi-camera-video fs-3"></i>
</div>
</div>
<div class="d-flex align-items-center justify-content-between pt-3 mt-2 border-top text-info small fw-semibold">
<span>Acceso a salas virtuales</span>
<i class="bi bi-arrow-right"></i>
</div>
</div>
</div>
</a>
</div>
</div>
<!-- Docente Launchpad -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-body-tertiary border-0 py-3">
<div class="d-flex justify-content-between align-items-center">
<h5 class="mb-0 h6 fw-bold">
<i class="bi bi-compass text-info me-2"></i>Accesos Directos de Docencia & Visualización de Menús
</h5>
<span class="badge bg-info-subtle text-info border border-info-subtle">Vista Profesor</span>
</div>
</div>
<div class="card-body p-3">
<div class="row g-3">
<div class="col-md-6 col-lg-3">
<a href="/schedule/add_reservation" class="card h-100 p-3 action-menu-card bg-body">
<div class="d-flex align-items-center">
<div class="quick-icon-circle bg-success-subtle text-success me-3">
<i class="bi bi-calendar-plus fs-4"></i>
</div>
<div>
<h6 class="mb-1 fw-bold text-body">Solicitar Aula</h6>
<p class="text-muted small mb-0">Reserva de espacio o laboratorio</p>
</div>
</div>
</a>
</div>
<div class="col-md-6 col-lg-3">
<a href="/schedule/commissions_view" class="card h-100 p-3 action-menu-card bg-body">
<div class="d-flex align-items-center">
<div class="quick-icon-circle bg-primary-subtle text-primary me-3">
<i class="bi bi-diagram-3 fs-4"></i>
</div>
<div>
<h6 class="mb-1 fw-bold text-body">Mis Comisiones</h6>
<p class="text-muted small mb-0">Ver comisiones y turnos asignados</p>
</div>
</div>
</a>
</div>
<div class="col-md-6 col-lg-3">
<a href="/schedule/today_schedule" class="card h-100 p-3 action-menu-card bg-body">
<div class="d-flex align-items-center">
<div class="quick-icon-circle bg-info-subtle text-info me-3">
<i class="bi bi-calendar-day fs-4"></i>
</div>
<div>
<h6 class="mb-1 fw-bold text-body">Cartelera del Día</h6>
<p class="text-muted small mb-0">Consulta de aulas asignadas</p>
</div>
</div>
</a>
</div>
<div class="col-md-6 col-lg-3">
<a href="/classrooms/list_classrooms" class="card h-100 p-3 action-menu-card bg-body">
<div class="d-flex align-items-center">
<div class="quick-icon-circle bg-warning-subtle text-warning me-3">
<i class="bi bi-camera-video fs-4"></i>
</div>
<div>
<h6 class="mb-1 fw-bold text-body">Aulas Virtuales</h6>
<p class="text-muted small mb-0">Salas y enlaces de reunión</p>
</div>
</div>
</a>
</div>
</div>
</div>
</div>
<!-- Docente: Mis Clases de Hoy -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-body-tertiary border-0 py-3 d-flex justify-content-between align-items-center">
<h5 class="mb-0 h6 fw-bold">
<i class="bi bi-calendar-check text-primary me-2"></i>Mis Clases Programadas para Hoy
</h5>
<span class="badge bg-primary-subtle text-primary border">{{ role_data.my_today_classes|length }} hoy</span>
</div>
<div class="card-body p-0">
{% if role_data.my_today_classes %}
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead class="table-light small">
<tr>
<th class="ps-3">Horario</th>
<th>Materia & Código</th>
<th>Comisión / Turno</th>
<th>Ubicación / Aula</th>
<th class="text-end pe-3">Acción</th>
</tr>
</thead>
<tbody>
{% for c in role_data.my_today_classes %}
<tr>
<td class="ps-3 font-monospace fw-bold text-primary">
<i class="bi bi-clock me-1 text-warning"></i>{{ c.start_time }} - {{ c.end_time }} hs
</td>
<td>
<div class="fw-semibold">{{ c.subject_name }}</div>
<small class="text-muted font-monospace">{{ c.subject_code }}</small>
</td>
<td>
<span class="badge bg-secondary-subtle text-secondary border">{{ c.commission.code if c.commission else 'C1' }}</span>
</td>
<td>
{% if c.classroom and c.classroom.is_virtual %}
<span class="badge bg-info-subtle text-info border">
<i class="bi bi-camera-video me-1"></i>Aula Virtual
</span>
{% elif c.classroom %}
<span class="badge bg-primary-subtle text-primary border">
<i class="bi bi-geo-alt me-1"></i>{{ c.classroom.location_display }}
</span>
{% else %}
<span class="text-muted small">Sin aula asignada</span>
{% endif %}
</td>
<td class="text-end pe-3">
{% if c.virtual_link %}
<a href="{{ c.virtual_link }}" target="_blank" class="btn btn-sm btn-info text-white py-1 px-2">
<i class="bi bi-box-arrow-up-right me-1"></i>Ingresar a Reunión
</a>
{% else %}
<a href="/schedule/view_reservation?id={{ c.id }}" class="btn btn-sm btn-outline-secondary py-1 px-2">
<i class="bi bi-eye"></i>
</a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="text-center py-4 text-muted">
<i class="bi bi-cup-hot fs-2 d-block mb-1 text-primary"></i>
<p class="mb-0 fw-semibold">No tienes clases programadas para hoy.</p>
<small class="text-muted">Puedes revisar tus reservas futuras en el calendario institucional.</small>
</div>
{% endif %}
</div>
</div>
{% else %}
<!-- ========================= [D] ALUMNO DASHBOARD ========================= -->
<!-- Alumno Welcome Compass: ¿Dónde curso hoy? -->
<div class="card border-primary border-opacity-25 shadow-sm mb-4">
<div class="card-header bg-primary bg-opacity-10 border-0 py-3 d-flex justify-content-between align-items-center">
<h5 class="mb-0 h6 fw-bold text-primary">
<i class="bi bi-compass-fill me-2"></i>¿Dónde curso hoy? - Cartelera del Alumno
</h5>
<a href="/schedule/today_schedule" class="btn btn-sm btn-primary">
<i class="bi bi-calendar-day me-1"></i>Cartelera Completa
</a>
</div>
<div class="card-body p-0">
{% if role_data.today_classes %}
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead class="table-light small">
<tr>
<th class="ps-3">Horario</th>
<th>Asignatura / Carrera</th>
<th>Comisión</th>
<th>Edificio · Piso · Aula</th>
<th class="text-end pe-3">Acceso</th>
</tr>
</thead>
<tbody>
{% for cls in role_data.today_classes %}
<tr>
<td class="ps-3 font-monospace fw-bold text-primary">
<i class="bi bi-clock-fill text-warning me-1"></i>{{ cls.start_time }} - {{ cls.end_time }} hs
</td>
<td>
<div class="fw-semibold text-body">{{ cls.subject_name }}</div>
<small class="text-muted font-monospace">{{ cls.subject_code }}</small>
</td>
<td>
<span class="badge bg-secondary-subtle text-secondary border">
{{ cls.commission.code if cls.commission else 'C1' }}
</span>
</td>
<td>
{% if cls.classroom and cls.classroom.is_virtual %}
<span class="badge bg-info-subtle text-info border font-monospace">
<i class="bi bi-camera-video me-1"></i>Campus Virtual (Videollamada)
</span>
{% elif cls.classroom %}
<span class="badge bg-success-subtle text-success border font-monospace">
<i class="bi bi-building me-1"></i>{{ cls.classroom.location_display }}
</span>
{% else %}
<span class="badge bg-warning-subtle text-warning border">A confirmar</span>
{% endif %}
</td>
<td class="text-end pe-3">
{% if cls.virtual_link %}
<a href="{{ cls.virtual_link }}" target="_blank" class="btn btn-sm btn-info text-white py-1 px-2">
<i class="bi bi-box-arrow-up-right me-1"></i>Unirse a Clase
</a>
{% else %}
<span class="badge bg-body-tertiary text-muted border">
<i class="bi bi-geo-alt"></i> Presencial
</span>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="text-center py-4 text-muted">
<i class="bi bi-calendar-check fs-2 d-block mb-1 text-success"></i>
<p class="mb-0 fw-semibold">¡No tienes clases asignadas para el día de hoy!</p>
<small class="text-muted">Consulta el cronograma semanal para ver tus próximas fechas y horarios.</small>
</div>
{% endif %}
</div>
</div>
<!-- Alumno Visual Menu Launchpad -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-body-tertiary border-0 py-3">
<div class="d-flex justify-content-between align-items-center">
<h5 class="mb-0 h6 fw-bold">
<i class="bi bi-mortarboard text-success me-2"></i>Menú del Alumno & Herramientas de Cursada
</h5>
<span class="badge bg-success-subtle text-success border border-success-subtle">Vista Alumno</span>
</div>
</div>
<div class="card-body p-3">
<div class="row g-3">
<div class="col-md-6 col-lg-3">
<a href="/schedule/today_schedule" class="card h-100 p-3 action-menu-card bg-body">
<div class="d-flex align-items-center">
<div class="quick-icon-circle bg-primary-subtle text-primary me-3">
<i class="bi bi-geo-alt fs-4"></i>
</div>
<div>
<h6 class="mb-1 fw-bold text-body">Cartelera de Hoy</h6>
<p class="text-muted small mb-0">Dónde cursas hoy, piso y aula</p>
</div>
</div>
</a>
</div>
<div class="col-md-6 col-lg-3">
<a href="/schedule/calendar_view" class="card h-100 p-3 action-menu-card bg-body">
<div class="d-flex align-items-center">
<div class="quick-icon-circle bg-success-subtle text-success me-3">
<i class="bi bi-calendar3 fs-4"></i>
</div>
<div>
<h6 class="mb-1 fw-bold text-body">Cronograma Semanal</h6>
<p class="text-muted small mb-0">Calendario general de cursadas</p>
</div>
</div>
</a>
</div>
<div class="col-md-6 col-lg-3">
<a href="/classrooms/list_classrooms" class="card h-100 p-3 action-menu-card bg-body">
<div class="d-flex align-items-center">
<div class="quick-icon-circle bg-info-subtle text-info me-3">
<i class="bi bi-camera-video fs-4"></i>
</div>
<div>
<h6 class="mb-1 fw-bold text-body">Aulas Virtuales</h6>
<p class="text-muted small mb-0">Salas y videollamadas Meet/Teams</p>
</div>
</div>
</a>
</div>
<div class="col-md-6 col-lg-3">
<a href="/classrooms/list_classrooms" class="card h-100 p-3 action-menu-card bg-body">
<div class="d-flex align-items-center">
<div class="quick-icon-circle bg-warning-subtle text-warning me-3">
<i class="bi bi-buildings fs-4"></i>
</div>
<div>
<h6 class="mb-1 fw-bold text-body">Espacios del Campus</h6>
<p class="text-muted small mb-0">Sedes, edificios y planos</p>
</div>
</div>
</a>
</div>
</div>
</div>
</div>
<!-- Alumno Upcoming Milestones (Exámenes y Entregas) -->
{% if role_data.upcoming_milestones %}
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-body-tertiary border-0 py-3">
<h5 class="mb-0 h6 fw-bold">
<i class="bi bi-bell-fill text-warning me-2"></i>Próximos Hitos Académicos & Exámenes
</h5>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead class="table-light small">
<tr>
<th class="ps-3">Fecha</th>
<th>Tipo de Hito</th>
<th>Materia / Código</th>
<th>Aula / Enlace</th>
<th class="pe-3">Notas</th>
</tr>
</thead>
<tbody>
{% for m in role_data.upcoming_milestones %}
<tr>
<td class="ps-3 font-monospace fw-bold text-primary">
{{ m.date }}
</td>
<td>
<span class="badge" data-color="{{ m.type_obj.color if m.type_obj else '#0d6efd' }}" style="background-color:var(--badge-bg,#0d6efd);color:#fff;">
{{ m.type_obj.name if m.type_obj else 'Evaluación' }}
</span>
</td>
<td>
<div class="fw-semibold">{{ m.title or m.materia_codigo }}</div>
<small class="text-muted font-monospace">{{ m.materia_codigo }}</small>
</td>
<td>
{% if m.link %}
<a href="{{ m.link }}" target="_blank" class="btn btn-sm btn-outline-info py-0 px-2">
<i class="bi bi-box-arrow-up-right me-1"></i>Enlace
</a>
{% else %}
<span class="text-muted small">{{ m.classroom or 'Sede Central' }}</span>
{% endif %}
</td>
<td class="pe-3 small text-muted">{{ m.notes or '-' }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endif %}
{% endif %}
<!-- ========================================================================= -->
<!-- 3. UPCOMING RESERVATIONS (ALL ROLES GENERAL FEED) -->
<!-- ========================================================================= -->
<div class="row">
<div class="col-12">
<div class="card border-0 shadow-sm">
<div class="card-header border-0 py-3 bg-body-tertiary d-flex justify-content-between align-items-center">
<h5 class="mb-0 h6 fw-bold">
<i class="bi bi-clock-history text-info me-2"></i>Próximas Reservas del Campus (Próximos 7 Días)
</h5>
<a href="/schedule/calendar_view" class="btn btn-sm btn-outline-primary">
<i class="bi bi-calendar3 me-1"></i>Ver en Calendario
</a>
</div>
<div class="card-body p-0">
{% if upcoming_reservations %}
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead class="table-light small text-uppercase">
<tr>
<th class="ps-3">Fecha & Horario</th>
<th>Aula</th>
<th>Asignatura / Código</th>
<th>Turno / Comisión</th>
<th>Estado</th>
<th class="text-end pe-3">Acciones</th>
</tr>
</thead>
<tbody>
{% for reservation in upcoming_reservations %}
<tr>
<td class="ps-3">
<div class="fw-bold font-monospace text-primary small">
<i class="bi bi-calendar-event text-secondary me-1"></i>{{ reservation.start_time }}
<span class="ms-1 text-body">{{ reservation.start_time }} - {{ reservation.end_time }} hs</span>
</div>
</td>
<td>
<span class="badge bg-secondary-subtle text-secondary border font-monospace">
{{ reservation.classroom.code if reservation.classroom else 'Sin Aula' }}
</span>
</td>
<td>
<div class="fw-semibold text-primary">{{ reservation.subject_name }}</div>
<small class="text-muted font-monospace">{{ reservation.subject_code }}</small>
</td>
<td>
<span class="badge bg-info-subtle text-info border me-1">
{{ reservation.shift or 'Tarde' }}
</span>
<small class="text-muted">{{ reservation.commission.code if reservation.commission else 'C1' }}</small>
</td>
<td>
{% if reservation.status == 'CONFIRMED' or reservation.status.value == 'CONFIRMED' %}
<span class="badge bg-success-subtle text-success border border-success-subtle">
<i class="bi bi-check-circle me-1"></i>Confirmada
</span>
{% else %}
<span class="badge bg-warning-subtle text-warning border border-warning-subtle">
<i class="bi bi-clock me-1"></i>Pendiente
</span>
{% endif %}
</td>
<td class="text-end pe-3">
<a href="/schedule/view_reservation?id={{ res.id }}"
class="btn btn-sm btn-outline-secondary py-0 px-2" title="Ver Ficha">
<i class="bi bi-eye"></i>
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="text-center py-5">
<i class="bi bi-calendar-x text-muted fs-1 d-block mb-2"></i>
<h6 class="fw-bold">No hay reservas para los próximos 7 días</h6>
<p class="text-muted small">Puedes programar nuevas reservas desde el calendario.</p>
</div>
{% endif %}
</div>
</div>
</div>
</div>
{% if user.is_authenticated %}
</div>
{% endif %}
{% endblock %}
{% block extra_js %}
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
$(document).ready(function() {
const chartElem = document.getElementById('monthlyChart');
if (chartElem) {
$.get('/api/dashboard-stats')
.done(function(data) {
const ctx = chartElem.getContext('2d');
new Chart(ctx, {
type: 'line',
data: {
labels: data.monthly_data.map(item => item.month),
datasets: [{
label: 'Reservas',
data: data.monthly_data.map(item => item.reservations),
borderColor: '#B43E8E',
backgroundColor: 'rgba(180, 62, 142, 0.12)',
borderWidth: 2,
fill: true,
tension: 0.4,
pointBackgroundColor: '#B43E8E',
pointRadius: 3
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
}
},
scales: {
y: {
beginAtZero: true,
ticks: {
stepSize: 1
}
}
}
}
});
});
}
});
// Apply dynamic colors from data-color attributes (milestone type badges)
document.querySelectorAll('[data-color]').forEach(function(el) {
const color = el.getAttribute('data-color');
if (color) el.style.setProperty('background-color', color, 'important');
});
</script>
{% endblock %}