1583 lines
93 KiB
HTML
1583 lines
93 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>
|
|
|
|
<!-- ========================= Panel: Comisiones Sin Reserva (Semana/Mes) ========================= -->
|
|
<div class="card border-0 shadow-sm mb-4" id="bedelia-unassigned-commissions">
|
|
<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-calendar-x-fill text-danger fs-5"></i>
|
|
<div>
|
|
<h5 class="mb-0 h6 fw-bold">Comisiones Sin Reserva de Aula</h5>
|
|
<small class="text-muted">Comisiones activas sin clase reservada en el período seleccionado</small>
|
|
</div>
|
|
</div>
|
|
<div class="d-flex gap-2 align-items-center flex-wrap">
|
|
<div class="btn-group btn-group-sm" role="group" id="commPeriodSwitcher">
|
|
<button type="button" class="btn btn-outline-danger active" data-period="week" onclick="switchCommPeriod('week')">
|
|
<i class="bi bi-calendar-week me-1"></i>Esta Semana
|
|
<span class="badge bg-danger ms-1" id="commWeekBadge">{{ role_data.unassigned_commissions_week | length }}</span>
|
|
</button>
|
|
<button type="button" class="btn btn-outline-danger" data-period="month" onclick="switchCommPeriod('month')">
|
|
<i class="bi bi-calendar-month me-1"></i>Este Mes
|
|
<span class="badge bg-danger ms-1" id="commMonthBadge">{{ role_data.unassigned_commissions_month | length }}</span>
|
|
</button>
|
|
</div>
|
|
<a href="/admin/commissions_list" class="btn btn-sm btn-outline-secondary">
|
|
<i class="bi bi-list-ul me-1"></i>Ver Todas
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<!-- Tabla Semana -->
|
|
<div id="commTableWeek">
|
|
{% if role_data.unassigned_commissions_week and role_data.unassigned_commissions_week | length > 0 %}
|
|
<div class="table-responsive">
|
|
<table class="table table-hover align-middle mb-0 small">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th class="ps-3">Código</th>
|
|
<th>Materia</th>
|
|
<th>Docente</th>
|
|
<th>Horario Programado</th>
|
|
<th>Turno</th>
|
|
<th>Cupo</th>
|
|
<th class="text-end pe-3">Acción</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for comm in role_data.unassigned_commissions_week %}
|
|
<tr>
|
|
<td class="ps-3">
|
|
<span class="badge bg-danger-subtle text-danger border font-monospace">{{ comm.full_code }}</span>
|
|
</td>
|
|
<td>
|
|
<div class="fw-semibold text-body">{{ comm.subject_name }}</div>
|
|
<small class="text-muted font-monospace">{{ comm.subject_code }}</small>
|
|
</td>
|
|
<td class="small">{{ comm.teacher_name }}</td>
|
|
<td>
|
|
<span class="badge bg-body-tertiary text-body border"><i class="bi bi-clock me-1"></i>{{ comm.schedule }}</span>
|
|
</td>
|
|
<td><span class="badge bg-info-subtle text-info-emphasis border">{{ comm.shift }}</span></td>
|
|
<td class="font-monospace">{{ comm.current_students }}/{{ comm.max_students }}</td>
|
|
<td class="text-end pe-3">
|
|
<a href="/schedule/add_reservation?commission_id={{ comm.id }}" class="btn btn-sm btn-outline-success py-0 px-2" title="Asignar aula a esta comisión">
|
|
<i class="bi bi-plus-circle me-1"></i>Asignar
|
|
</a>
|
|
<a href="/admin/commission_detail?id={{ comm.id }}" class="btn btn-sm btn-outline-primary py-0 px-2 ms-1" title="Ver detalle">
|
|
<i class="bi bi-eye me-1"></i>Detalle
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center py-4">
|
|
<i class="bi bi-check-circle-fill text-success fs-1 d-block mb-2"></i>
|
|
<h6 class="text-success fw-bold">¡Todas las comisiones tienen reserva esta semana!</h6>
|
|
<p class="text-muted small mb-0">No hay comisiones activas sin aula reservada en la semana actual.</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Tabla Mes -->
|
|
<div id="commTableMonth" style="display: none;">
|
|
{% if role_data.unassigned_commissions_month and role_data.unassigned_commissions_month | length > 0 %}
|
|
<div class="table-responsive">
|
|
<table class="table table-hover align-middle mb-0 small">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th class="ps-3">Código</th>
|
|
<th>Materia</th>
|
|
<th>Docente</th>
|
|
<th>Horario Programado</th>
|
|
<th>Turno</th>
|
|
<th>Cupo</th>
|
|
<th class="text-end pe-3">Acción</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for comm in role_data.unassigned_commissions_month %}
|
|
<tr>
|
|
<td class="ps-3">
|
|
<span class="badge bg-danger-subtle text-danger border font-monospace">{{ comm.full_code }}</span>
|
|
</td>
|
|
<td>
|
|
<div class="fw-semibold text-body">{{ comm.subject_name }}</div>
|
|
<small class="text-muted font-monospace">{{ comm.subject_code }}</small>
|
|
</td>
|
|
<td class="small">{{ comm.teacher_name }}</td>
|
|
<td>
|
|
<span class="badge bg-body-tertiary text-body border"><i class="bi bi-clock me-1"></i>{{ comm.schedule }}</span>
|
|
</td>
|
|
<td><span class="badge bg-info-subtle text-info-emphasis border">{{ comm.shift }}</span></td>
|
|
<td class="font-monospace">{{ comm.current_students }}/{{ comm.max_students }}</td>
|
|
<td class="text-end pe-3">
|
|
<a href="/schedule/add_reservation?commission_id={{ comm.id }}" class="btn btn-sm btn-outline-success py-0 px-2" title="Asignar aula a esta comisión">
|
|
<i class="bi bi-plus-circle me-1"></i>Asignar
|
|
</a>
|
|
<a href="/admin/commission_detail?id={{ comm.id }}" class="btn btn-sm btn-outline-primary py-0 px-2 ms-1" title="Ver detalle">
|
|
<i class="bi bi-eye me-1"></i>Detalle
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center py-4">
|
|
<i class="bi bi-check-circle-fill text-success fs-1 d-block mb-2"></i>
|
|
<h6 class="text-success fw-bold">¡Todas las comisiones tienen reserva este mes!</h6>
|
|
<p class="text-muted small mb-0">No hay comisiones activas sin aula reservada en el mes actual.</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ========================= Panel: Aulas Disponibles (Semana/Mes) ========================= -->
|
|
<div class="card border-0 shadow-sm mb-4" id="bedelia-available-classrooms">
|
|
<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-door-open-fill text-success fs-5"></i>
|
|
<div>
|
|
<h5 class="mb-0 h6 fw-bold">Aulas Disponibles (Sin Reservas)</h5>
|
|
<small class="text-muted">Espacios sin ninguna reserva cargada en el período seleccionado</small>
|
|
</div>
|
|
</div>
|
|
<div class="d-flex gap-2 align-items-center flex-wrap">
|
|
<div class="btn-group btn-group-sm" role="group" id="classroomPeriodSwitcher">
|
|
<button type="button" class="btn btn-outline-success active" data-period="week" onclick="switchClassroomPeriod('week')">
|
|
<i class="bi bi-calendar-week me-1"></i>Esta Semana
|
|
<span class="badge bg-success ms-1" id="classWeekBadge">{{ role_data.available_classrooms_week | length }}</span>
|
|
</button>
|
|
<button type="button" class="btn btn-outline-success" data-period="month" onclick="switchClassroomPeriod('month')">
|
|
<i class="bi bi-calendar-month me-1"></i>Este Mes
|
|
<span class="badge bg-success ms-1" id="classMonthBadge">{{ role_data.available_classrooms_month | length }}</span>
|
|
</button>
|
|
</div>
|
|
<a href="/classrooms/list_classrooms" class="btn btn-sm btn-outline-secondary">
|
|
<i class="bi bi-list-ul me-1"></i>Catálogo Completo
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<div class="card-body p-3">
|
|
<!-- Vista Semana -->
|
|
<div id="classroomGridWeek">
|
|
{% if role_data.available_classrooms_week and role_data.available_classrooms_week | length > 0 %}
|
|
<div class="row g-2">
|
|
{% for aula in role_data.available_classrooms_week %}
|
|
<div class="col-sm-6 col-md-4 col-lg-3">
|
|
<a href="/schedule/add_reservation?classroom_id={{ aula.id }}" class="text-decoration-none text-reset d-block h-100">
|
|
<div class="p-3 rounded border h-100 transition-all {% if aula.is_virtual %}bg-info bg-opacity-10 border-info-subtle{% else %}bg-success bg-opacity-10 border-success-subtle{% endif %}">
|
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
|
<span class="badge {% if aula.is_virtual %}bg-info-subtle text-info{% else %}bg-success-subtle text-success{% endif %} border font-monospace fw-bold">
|
|
{{ aula.code }}
|
|
</span>
|
|
{% if aula.is_virtual %}
|
|
<i class="bi bi-camera-video text-info"></i>
|
|
{% else %}
|
|
<i class="bi bi-door-open text-success"></i>
|
|
{% endif %}
|
|
</div>
|
|
<div class="small text-muted mb-1">
|
|
<i class="bi bi-building me-1"></i>{{ aula.building }}{% if aula.floor %} · {{ aula.floor }}{% endif %}
|
|
</div>
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<span class="small fw-semibold text-body">
|
|
<i class="bi bi-people-fill me-1"></i>Cap: {{ aula.capacity }}
|
|
</span>
|
|
<span class="badge bg-success text-white small"><i class="bi bi-plus-circle me-1"></i>Asignar</span>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center py-4">
|
|
<i class="bi bi-calendar-check-fill text-primary fs-1 d-block mb-2"></i>
|
|
<h6 class="text-primary fw-bold">¡Todas las aulas están siendo utilizadas esta semana!</h6>
|
|
<p class="text-muted small mb-0">No hay aulas sin reservas activas en la semana actual.</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Vista Mes -->
|
|
<div id="classroomGridMonth" style="display: none;">
|
|
{% if role_data.available_classrooms_month and role_data.available_classrooms_month | length > 0 %}
|
|
<div class="row g-2">
|
|
{% for aula in role_data.available_classrooms_month %}
|
|
<div class="col-sm-6 col-md-4 col-lg-3">
|
|
<a href="/schedule/add_reservation?classroom_id={{ aula.id }}" class="text-decoration-none text-reset d-block h-100">
|
|
<div class="p-3 rounded border h-100 transition-all {% if aula.is_virtual %}bg-info bg-opacity-10 border-info-subtle{% else %}bg-success bg-opacity-10 border-success-subtle{% endif %}">
|
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
|
<span class="badge {% if aula.is_virtual %}bg-info-subtle text-info{% else %}bg-success-subtle text-success{% endif %} border font-monospace fw-bold">
|
|
{{ aula.code }}
|
|
</span>
|
|
{% if aula.is_virtual %}
|
|
<i class="bi bi-camera-video text-info"></i>
|
|
{% else %}
|
|
<i class="bi bi-door-open text-success"></i>
|
|
{% endif %}
|
|
</div>
|
|
<div class="small text-muted mb-1">
|
|
<i class="bi bi-building me-1"></i>{{ aula.building }}{% if aula.floor %} · {{ aula.floor }}{% endif %}
|
|
</div>
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<span class="small fw-semibold text-body">
|
|
<i class="bi bi-people-fill me-1"></i>Cap: {{ aula.capacity }}
|
|
</span>
|
|
<span class="badge bg-success text-white small"><i class="bi bi-plus-circle me-1"></i>Asignar</span>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center py-4">
|
|
<i class="bi bi-calendar-check-fill text-primary fs-1 d-block mb-2"></i>
|
|
<h6 class="text-primary fw-bold">¡Todas las aulas están siendo utilizadas este mes!</h6>
|
|
<p class="text-muted small mb-0">No hay aulas sin reservas activas en el mes actual.</p>
|
|
</div>
|
|
{% endif %}
|
|
</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');
|
|
});
|
|
|
|
// Bedelía: Switchers de período para paneles de Comisiones y Aulas
|
|
function switchCommPeriod(period) {
|
|
document.getElementById('commTableWeek').style.display = period === 'week' ? 'block' : 'none';
|
|
document.getElementById('commTableMonth').style.display = period === 'month' ? 'block' : 'none';
|
|
document.querySelectorAll('#commPeriodSwitcher button').forEach(function(btn) {
|
|
btn.classList.toggle('active', btn.getAttribute('data-period') === period);
|
|
});
|
|
}
|
|
|
|
function switchClassroomPeriod(period) {
|
|
document.getElementById('classroomGridWeek').style.display = period === 'week' ? 'block' : 'none';
|
|
document.getElementById('classroomGridMonth').style.display = period === 'month' ? 'block' : 'none';
|
|
document.querySelectorAll('#classroomPeriodSwitcher button').forEach(function(btn) {
|
|
btn.classList.toggle('active', btn.getAttribute('data-period') === period);
|
|
});
|
|
}
|
|
</script>
|
|
{% endblock %} |