feat: implementacion de dashboards por rol, soporte de clases virtuales concurrentes en importador de sheets y cartelera del dia

This commit is contained in:
2026-09-18 23:04:48 -03:00
parent 47b50ede7e
commit 1080acd43a
240 changed files with 8354 additions and 8886 deletions
+200
View File
@@ -0,0 +1,200 @@
{% extends 'base.html' %}
{% block title %}Gestión de Comisiones — Edu-Space{% endblock %}
{% block content %}
<div class="container-fluid py-4">
<!-- Header -->
<div class="d-flex align-items-center justify-content-between mb-4 flex-wrap gap-2">
<div>
<h1 class="h3 fw-bold mb-1">
<i class="bi bi-diagram-3 me-2 text-primary"></i>Gestión de Comisiones
</h1>
<p class="text-muted mb-0 small">Asignación de docentes e inscripción de alumnos por comisión</p>
</div>
<a href="/admin/subjects_list" class="btn btn-outline-secondary btn-sm">
<i class="bi bi-book me-1"></i>Ver Asignaturas
</a>
</div>
<!-- Flash Messages -->
<!-- Filters Card -->
<div class="card shadow-sm border-0 mb-4">
<div class="card-body py-3">
<form method="GET" class="row g-2 align-items-end" id="filter-form">
<div class="col-12 col-md-3">
<label class="form-label small fw-semibold mb-1">Buscar</label>
<div class="input-group input-group-sm">
<span class="input-group-text"><i class="bi bi-search"></i></span>
<input type="text" name="search" class="form-control" placeholder="Código o asignatura…"
value="{{ search or '' }}" id="search-input">
</div>
</div>
<div class="col-6 col-md-3">
<label class="form-label small fw-semibold mb-1">Asignatura</label>
<select name="subject_id" class="form-select form-select-sm" id="subject-filter">
<option value="">Todas</option>
{% for subj in subjects %}
<option value="{{ subj.id }}" {% if subject_filter == subj.id %}selected{% endif %}>
{{ subj.name }}
</option>
{% endfor %}
</select>
</div>
<div class="col-6 col-md-2">
<label class="form-label small fw-semibold mb-1">Turno</label>
<select name="shift" class="form-select form-select-sm" id="shift-filter">
<option value="">Todos</option>
{% for shift in ['Mañana', 'Tarde', 'Vespertino', 'Noche'] %}
<option value="{{ shift }}" {% if shift_filter == shift %}selected{% endif %}>{{ shift }}</option>
{% endfor %}
</select>
</div>
<div class="col-6 col-md-2">
<label class="form-label small fw-semibold mb-1">Estado</label>
<select name="active" class="form-select form-select-sm" id="active-filter">
<option value="true" {% if active_filter == 'true' %}selected{% endif %}>Activas</option>
<option value="false" {% if active_filter == 'false' %}selected{% endif %}>Inactivas</option>
<option value="" {% if not active_filter %}selected{% endif %}>Todas</option>
</select>
</div>
<div class="col-6 col-md-2 d-flex gap-2">
<button type="submit" class="btn btn-primary btn-sm flex-fill" id="filter-btn">
<i class="bi bi-funnel me-1"></i>Filtrar
</button>
<a href="/admin/commissions_list" class="btn btn-outline-secondary btn-sm">
<i class="bi bi-x"></i>
</a>
</div>
</form>
</div>
</div>
<!-- Commissions Table -->
<div class="card shadow-sm border-0">
<div class="card-header bg-transparent border-bottom d-flex align-items-center justify-content-between py-2">
<span class="fw-semibold small">
<i class="bi bi-list-ul me-1 text-primary"></i>
{{ pagination.total }} comisión(es) encontrada(s)
</span>
<span class="badge bg-primary-subtle text-primary">Página {{ pagination.page }} / {{ pagination.pages or 1 }}</span>
</div>
<div class="card-body p-0">
{% if commissions %}
<div class="table-responsive">
<table class="table table-hover mb-0 align-middle" id="commissions-table">
<thead class="table-light">
<tr>
<th class="ps-3">Comisión</th>
<th>Asignatura</th>
<th>Docente</th>
<th class="text-center">Turno</th>
<th class="text-center">Alumnos</th>
<th class="text-center">Estado</th>
<th class="text-end pe-3">Acción</th>
</tr>
</thead>
<tbody>
{% for comm in commissions %}
<tr>
<td class="ps-3">
<span class="badge bg-primary-subtle text-primary fw-semibold font-monospace">
{{ comm.get_full_code() }}
</span>
</td>
<td>
<div class="fw-semibold">{{ comm.subject.name }}</div>
<small class="text-muted">{{ comm.subject.code }} — {{ comm.semester }} {{ comm.year }}</small>
</td>
<td>
{% if comm.teacher %}
<div class="d-flex align-items-center gap-2">
<div class="avatar-xs bg-info-subtle rounded-circle d-flex align-items-center justify-content-center" style="width:28px;height:28px;">
<i class="bi bi-person-video3 small text-info"></i>
</div>
<span class="small">{{ comm.teacher.name }}</span>
</div>
{% else %}
<span class="text-muted small fst-italic"><i class="bi bi-exclamation-circle me-1 text-warning"></i>Sin asignar</span>
{% endif %}
</td>
<td class="text-center">
{% if comm.shift %}
{% set shift_colors = {'Mañana': 'warning', 'Tarde': 'info', 'Vespertino': 'secondary', 'Noche': 'dark'} %}
<span class="badge bg-{{ shift_colors[comm.shift] or 'secondary' }}-subtle text-{{ shift_colors[comm.shift] or 'secondary' }} border border-{{ shift_colors[comm.shift] or 'secondary' }}-subtle">
{{ comm.shift }}
</span>
{% else %}
<span class="text-muted small">—</span>
{% endif %}
</td>
<td class="text-center">
<span class="fw-semibold">{{ comm.current_students or 0 }}</span>
<span class="text-muted small">/ {{ comm.max_students }}</span>
</td>
<td class="text-center">
{% if comm.active %}
<span class="badge bg-success-subtle text-success border border-success-subtle">Activa</span>
{% else %}
<span class="badge bg-secondary-subtle text-secondary border border-secondary-subtle">Inactiva</span>
{% endif %}
</td>
<td class="text-end pe-3">
<a href="/admin/commission_detail?id={{ comm.id }}"
class="btn btn-sm btn-outline-primary" title="Ver y gestionar comisión">
<i class="bi bi-gear me-1"></i>Gestionar
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Pagination -->
{% if pagination.pages > 1 %}
<div class="d-flex justify-content-center py-3">
<nav>
<ul class="pagination pagination-sm mb-0">
{% if pagination.has_prev %}
<li class="page-item">
<a class="page-link" href="/admin/commissions_list">
<i class="bi bi-chevron-left"></i>
</a>
</li>
{% endif %}
{% for pg in pagination.iter_pages(left_edge=1, right_edge=1, left_current=2, right_current=2) %}
{% if pg %}
<li class="page-item {% if pg == pagination.page %}active{% endif %}">
<a class="page-link" href="/admin/commissions_list">{{ pg }}</a>
</li>
{% else %}
<li class="page-item disabled"><span class="page-link">…</span></li>
{% endif %}
{% endfor %}
{% if pagination.has_next %}
<li class="page-item">
<a class="page-link" href="/admin/commissions_list">
<i class="bi bi-chevron-right"></i>
</a>
</li>
{% endif %}
</ul>
</nav>
</div>
{% endif %}
{% else %}
<div class="text-center py-5">
<i class="bi bi-diagram-3 display-4 text-muted opacity-50"></i>
<p class="mt-3 text-muted">No se encontraron comisiones con los filtros aplicados.</p>
<a href="/admin/commissions_list" class="btn btn-outline-primary btn-sm">
<i class="bi bi-arrow-clockwise me-1"></i>Limpiar filtros
</a>
</div>
{% endif %}
</div>
</div>
</div>
{% endblock %}