Files

383 lines
23 KiB
HTML

{% extends "base.html" %}
{% block title %}Cartelera de Hoy - Edu-Space Admin{% endblock %}
{% block content %}
<div class="container-fluid pt-5 mt-4">
<!-- Header -->
<div class="row mb-4 align-items-center">
<div class="col-md-7">
<h1 class="h3 mb-1">
<i class="bi bi-calendar-day text-info me-2"></i>Cartelera del Día
<small class="text-muted ms-2 fs-6">{{ format_date(today, format='full')|capitalize }}</small>
</h1>
<p class="text-muted small mb-0">Cronograma de clases y asignación de aulas activas para el día de hoy</p>
</div>
<div class="col-md-5 text-md-end mt-3 mt-md-0">
{% if user.has_permission('reservations', 'read_write') %}
<a href="/schedule/add_reservation" class="btn btn-success me-2">
<i class="bi bi-plus-circle me-1"></i>Nueva Reserva
</a>
{% endif %}
<a href="/schedule/calendar_view" class="btn btn-outline-primary me-2">
<i class="bi bi-calendar3 me-1"></i>Ver Calendario
</a>
<a href="/schedule/list_reservations" class="btn btn-outline-secondary">
<i class="bi bi-list me-1"></i>Todas las Reservas
</a>
</div>
</div>
<!-- Quick Stats Cards with Shortcuts -->
<div class="row mb-4 g-3">
<div class="col-sm-6 col-xl-3">
<div class="card border-0 shadow-sm h-100">
<div class="card-body p-3 d-flex justify-content-between align-items-center">
<div>
<span class="text-muted small fw-semibold text-uppercase">Total de Reservas</span>
<h2 class="mb-0 fw-bold mt-1 text-info">{{ stats.total_reservations }}</h2>
</div>
<div class="bg-info bg-opacity-10 rounded-circle p-3 text-info">
<i class="bi bi-calendar3 fs-3"></i>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card border-0 shadow-sm h-100">
<div class="card-body p-3 d-flex justify-content-between align-items-center">
<div>
<span class="text-muted small fw-semibold text-uppercase">Confirmadas</span>
<h2 class="mb-0 fw-bold mt-1 text-success">{{ stats.confirmed }}</h2>
</div>
<div class="bg-success bg-opacity-10 rounded-circle p-3 text-success">
<i class="bi bi-check-circle fs-3"></i>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card border-0 shadow-sm h-100">
<div class="card-body p-3 d-flex justify-content-between align-items-center">
<div>
<span class="text-muted small fw-semibold text-uppercase">Pendientes</span>
<h2 class="mb-0 fw-bold mt-1 text-warning">{{ stats.pending }}</h2>
</div>
<div class="bg-warning bg-opacity-10 rounded-circle p-3 text-warning">
<i class="bi bi-clock fs-3"></i>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card border-0 shadow-sm h-100">
<div class="card-body p-3 d-flex justify-content-between align-items-center">
<div>
<span class="text-muted small fw-semibold text-uppercase">Aulas Ocupadas</span>
<h2 class="mb-0 fw-bold mt-1 text-primary">{{ stats.occupied_classrooms }}/{{ 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>
</div>
</div>
<!-- Filters Toolbar (Piso, Turno y Ocultar Virtuales) -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-body p-3">
<form method="GET" action="/schedule/today_schedule" class="row g-2 align-items-center">
<!-- Filter by Floor -->
<div class="col-lg-3 col-md-4">
<label for="floorSelect" class="form-label small fw-bold text-muted mb-1">
<i class="bi bi-layers text-secondary me-1"></i>Piso / Planta
</label>
<select name="floor" id="floorSelect" class="form-select form-select-sm">
<option value="">Todos los pisos</option>
{% for f in available_floors %}
<option value="{{ f }}" {% if floor_filter == f|string %}selected{% endif %}>
{% if f == 0 %}Planta Baja (Piso 0){% else %}Piso {{ f }}{% endif %}
</option>
{% endfor %}
</select>
</div>
<!-- Filter by Turno -->
<div class="col-lg-3 col-md-4">
<label for="shiftSelect" class="form-label small fw-bold text-muted mb-1">
<i class="bi bi-brightness-high text-warning me-1"></i>Turno
</label>
<select name="shift" id="shiftSelect" class="form-select form-select-sm">
<option value="">Todos los turnos</option>
<option value="Mañana" {% if shift_filter == 'Mañana' %}selected{% endif %}>Turno Mañana</option>
<option value="Tarde" {% if shift_filter == 'Tarde' %}selected{% endif %}>Turno Tarde / Vespertino</option>
</select>
</div>
<!-- Hide Virtual Classrooms Toggle -->
<div class="col-lg-4 col-md-4 pt-md-3">
<div class="form-check form-switch pt-1">
<input class="form-check-input" type="checkbox" role="switch" name="hide_virtual" value="1" id="hideVirtualToday" {% if hide_virtual %}checked{% endif %} onchange="this.form.submit()">
<label class="form-check-label fw-semibold small text-dark" for="hideVirtualToday">
<i class="bi bi-laptop me-1 text-primary"></i>Ocultar Aulas Virtuales
<span class="badge bg-secondary-subtle text-secondary border ms-1" style="font-size: 0.72rem;">Capacidad ilimitada</span>
</label>
</div>
</div>
<!-- Actions -->
<div class="col-lg-2 col-md-12 pt-md-3 text-lg-end d-flex gap-2">
<button type="submit" class="btn btn-primary btn-sm flex-fill">
<i class="bi bi-funnel me-1"></i>Filtrar
</button>
<a href="/schedule/today_schedule" class="btn btn-outline-secondary btn-sm" title="Limpiar Filtros">
<i class="bi bi-arrow-counterclockwise"></i>
</a>
</div>
</form>
</div>
</div>
<!-- Today's Schedule -->
{% if time_blocks %}
<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">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2">
<h5 class="mb-0 h6 fw-bold">
<i class="bi bi-clock-history text-info me-2"></i>Vista Cronológica por Horario
</h5>
<div class="d-flex gap-2">
<button class="btn btn-sm btn-outline-primary" id="refresh-btn">
<i class="bi bi-arrow-clockwise me-1"></i>Actualizar
</button>
<button class="btn btn-sm btn-outline-secondary" id="toggle-view-btn">
<i class="bi bi-grid me-1"></i>Vista en Tabla
</button>
</div>
</div>
</div>
<div class="card-body p-3">
<!-- Timeline View -->
<div id="timeline-view">
{% for hour in range(7, 22) %}
<div class="row border-bottom py-3 align-items-start {% if time_blocks[hour] %}bg-light-subtle{% endif %}">
<div class="col-md-2 col-3">
<div class="text-center p-2 rounded bg-body-tertiary border">
<strong class="h5 text-primary mb-0 font-monospace">{% if hour < 10 %}0{% endif %}{{ hour }}:00</strong>
<div class="small text-muted font-monospace">{{ hour+1 }}:00 hs</div>
</div>
</div>
<div class="col-md-10 col-9">
{% if time_blocks[hour] %}
<div class="row g-2">
{% for item in time_blocks[hour] %}
<div class="col-md-6">
<div class="card border-0 shadow-sm h-100 border-start border-4 border-primary">
<div class="card-body p-3">
<div class="d-flex justify-content-between align-items-start mb-2">
<div>
<h6 class="card-title fw-bold mb-1 text-primary">
{{ item.reservation.subject_name }}
</h6>
<span class="badge bg-secondary-subtle text-secondary font-monospace border me-1">
{{ item.reservation.subject_code }}
</span>
<span class="badge bg-info-subtle text-info border">
{{ item.reservation.shift or 'Tarde' }}
</span>
</div>
<span class="badge bg-{% if item.reservation.status == 'CONFIRMED' or item.reservation.status.value == 'CONFIRMED' %}success-subtle text-success border border-success-subtle{% else %}warning-subtle text-warning border border-warning-subtle{% endif %}">
{% if item.reservation.status == 'CONFIRMED' or item.reservation.status.value == 'CONFIRMED' %}
Confirmada
{% else %}
Pendiente
{% endif %}
</span>
</div>
<div class="small text-muted mb-2">
<span class="fw-semibold text-dark me-2">
<i class="bi bi-door-open me-1 text-success"></i>{{ item.classroom.code }}
</span>
<span class="me-2">
<i class="bi bi-layers me-1"></i>{% if item.classroom.floor == 0 %}PB{% else %}Piso {{ item.classroom.floor }}{% endif %}
</span>
<span>
<i class="bi bi-clock me-1 text-warning"></i>{{ item.reservation.start_time }} - {{ item.reservation.end_time }} hs
</span>
</div>
<div class="d-flex justify-content-between align-items-center pt-2 border-top">
<span class="small text-muted">
<i class="bi bi-people me-1"></i>{{ item.reservation.expected_attendees }} estudiantes
</span>
<div class="d-flex gap-1">
{% if item.reservation.effective_virtual_link %}
<a href="{{ item.reservation.effective_virtual_link }}" target="_blank" class="btn btn-sm btn-outline-success py-0 px-2 d-inline-flex align-items-center" title="Unirse a Videollamada (Zoom / Meet)">
<i class="bi bi-camera-video me-1"></i>Zoom/Meet
</a>
{% endif %}
{% if user.can_edit_reservation(item.reservation) %}
<a href="/schedule/edit_reservation?id={{ item.reservation.id }}" class="btn btn-sm btn-outline-warning py-0 px-2" title="Modificar Reserva">
<i class="bi bi-pencil"></i>
</a>
{% endif %}
<a href="/schedule/view_reservation?id={{ item.reservation.id }}" class="btn btn-sm btn-outline-secondary py-0 px-2" title="Ver Ficha Detallada">
<i class="bi bi-eye me-1"></i>Detalles
</a>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="text-muted py-2 small ps-2">
<i class="bi bi-dash-circle me-1"></i>Sin clases programadas en este horario
</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>
<!-- Table / Grid View (hidden by default) -->
<div id="grid-view" style="display: none;">
<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">Horario</th>
<th>Aula / Piso</th>
<th>Asignatura / Código</th>
<th>Turno</th>
<th>Asistentes</th>
<th>Estado</th>
<th class="text-end pe-3">Acciones</th>
</tr>
</thead>
<tbody>
{% for reservation in reservations %}
<tr>
<td class="ps-3 font-monospace fw-bold">
{{ reservation.start_time }} - {{ reservation.end_time }} hs
</td>
<td>
<span class="badge bg-secondary-subtle text-secondary font-monospace border">
{{ reservation.classroom.code if reservation.classroom else 'Sin Aula' }}
</span>
<small class="text-muted d-block">
{% if reservation.classroom and reservation.classroom.floor == 0 %}Planta Baja{% elif reservation.classroom %}Piso {{ reservation.classroom.floor }}{% endif %}
</small>
</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">
{{ reservation.shift or 'Tarde' }}
</span>
</td>
<td>
<span class="small">{{ reservation.expected_attendees }} estudiantes</span>
</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">
{% if reservation.effective_virtual_link %}
<a href="{{ reservation.effective_virtual_link }}" target="_blank"
class="btn btn-sm btn-outline-success py-0 px-2 me-1" title="Unirse a Clase Virtual (Zoom/Meet)">
<i class="bi bi-camera-video"></i>
</a>
{% endif %}
{% if user.can_edit_reservation(reservation) %}
<a href="/schedule/edit_reservation?id={{ reservation.id }}"
class="btn btn-sm btn-outline-warning py-0 px-2 me-1" title="Modificar Reserva">
<i class="bi bi-pencil"></i>
</a>
{% endif %}
<a href="/schedule/view_reservation?id={{ reservation.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>
</div>
</div>
</div>
</div>
</div>
{% else %}
<div class="row">
<div class="col-12">
<div class="text-center py-5">
<i class="bi bi-calendar-x text-muted" style="font-size: 4rem;"></i>
<h4 class="mt-3 text-muted">No hay reservas programadas para hoy</h4>
<p class="text-muted">
No se encontraron clases para los filtros seleccionados.
</p>
<div class="mt-3">
{% if user.has_permission('reservations', 'read_write') %}
<a href="/schedule/add_reservation" class="btn btn-success">
<i class="bi bi-plus-circle me-1"></i>Crear Primera Reserva
</a>
{% endif %}
<a href="/schedule/calendar_view" class="btn btn-outline-primary ms-2">
<i class="bi bi-calendar-week me-1"></i>Ver Calendario Semanal
</a>
</div>
</div>
</div>
</div>
{% endif %}
</div>
{% endblock %}
{% block extra_js %}
<script>
document.addEventListener('DOMContentLoaded', function() {
var toggleBtn = document.getElementById('toggle-view-btn');
var timelineView = document.getElementById('timeline-view');
var gridView = document.getElementById('grid-view');
if (toggleBtn && timelineView && gridView) {
toggleBtn.addEventListener('click', function() {
if (timelineView.style.display === 'none') {
timelineView.style.display = 'block';
gridView.style.display = 'none';
toggleBtn.innerHTML = '<i class="bi bi-grid me-1"></i>Vista en Tabla';
} else {
timelineView.style.display = 'none';
gridView.style.display = 'block';
toggleBtn.innerHTML = '<i class="bi bi-clock-history me-1"></i>Vista Cronológica';
}
});
}
var refreshBtn = document.getElementById('refresh-btn');
if (refreshBtn) {
refreshBtn.addEventListener('click', function() {
location.reload();
});
}
});
</script>
{% endblock %}