1379 lines
68 KiB
HTML
1379 lines
68 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}{% trans %}Comisiones y Cursadas{% endtrans %} - Edu-Space{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
/* ==========================================================================
|
|
GESTIÓN DE COMISIONES - SISTEMA DE COLOR Y CONTRASTE (CLARO & OSCURO)
|
|
========================================================================== */
|
|
|
|
/* --- 1. MODO CLARO (Default / Light) --- */
|
|
.comm-stat-card {
|
|
border: 1px solid rgba(0, 0, 0, 0.06) !important;
|
|
background-color: #FFFFFF !important;
|
|
border-radius: 12px;
|
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
}
|
|
.comm-stat-card:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 8px 20px rgba(180, 62, 142, 0.08) !important;
|
|
}
|
|
.stat-icon-magenta {
|
|
background: rgba(180, 62, 142, 0.12) !important;
|
|
color: #B43E8E !important;
|
|
}
|
|
.stat-icon-purple {
|
|
background: rgba(104, 44, 173, 0.12) !important;
|
|
color: #682CAD !important;
|
|
}
|
|
.stat-icon-teal {
|
|
background: rgba(16, 185, 129, 0.12) !important;
|
|
color: #059669 !important;
|
|
}
|
|
.stat-icon-amber {
|
|
background: rgba(245, 158, 11, 0.14) !important;
|
|
color: #D97706 !important;
|
|
}
|
|
|
|
.comm-stat-val {
|
|
color: #0F172A;
|
|
font-weight: 800;
|
|
}
|
|
.comm-stat-lbl {
|
|
color: #64748B;
|
|
font-size: 0.8rem;
|
|
font-weight: 600;
|
|
}
|
|
|
|
/* Barra de Búsqueda */
|
|
.comm-search-card {
|
|
background-color: #FFFFFF !important;
|
|
border: 1px solid rgba(0, 0, 0, 0.07) !important;
|
|
border-radius: 12px;
|
|
}
|
|
.comm-search-input {
|
|
background-color: #F8FAFC !important;
|
|
color: #0F172A !important;
|
|
border: 1px solid #E2E8F0 !important;
|
|
}
|
|
.comm-search-input:focus {
|
|
border-color: #B43E8E !important;
|
|
box-shadow: 0 0 0 0.2rem rgba(180, 62, 142, 0.2) !important;
|
|
}
|
|
.comm-search-icon {
|
|
background-color: #F8FAFC !important;
|
|
color: #B43E8E !important;
|
|
border: 1px solid #E2E8F0 !important;
|
|
border-right: none !important;
|
|
}
|
|
.comm-info-hint {
|
|
color: #475569 !important;
|
|
font-size: 0.82rem;
|
|
}
|
|
|
|
/* Tarjeta de Asignatura */
|
|
.subject-card .card {
|
|
border-radius: 12px;
|
|
border: 1px solid rgba(0, 0, 0, 0.07) !important;
|
|
background-color: #FFFFFF !important;
|
|
overflow: hidden;
|
|
}
|
|
.comm-subject-header {
|
|
background: linear-gradient(90deg, rgba(180, 62, 142, 0.06) 0%, rgba(104, 44, 173, 0.03) 50%, #FFFFFF 100%) !important;
|
|
border-bottom: 1px solid rgba(180, 62, 142, 0.12) !important;
|
|
}
|
|
.comm-subject-badge {
|
|
background: linear-gradient(135deg, #B43E8E 0%, #9E367D 100%) !important;
|
|
color: #FFFFFF !important;
|
|
font-weight: 700;
|
|
box-shadow: 0 2px 6px rgba(180, 62, 142, 0.25);
|
|
}
|
|
.comm-subject-title {
|
|
color: #0F172A !important;
|
|
font-weight: 700;
|
|
}
|
|
.comm-subject-meta {
|
|
color: #64748B !important;
|
|
}
|
|
.comm-count-badge {
|
|
background-color: rgba(180, 62, 142, 0.08) !important;
|
|
color: #9E367D !important;
|
|
border: 1px solid rgba(180, 62, 142, 0.2) !important;
|
|
font-weight: 600;
|
|
}
|
|
.comm-btn-subject-add {
|
|
color: #B43E8E !important;
|
|
border-color: rgba(180, 62, 142, 0.35) !important;
|
|
background: transparent !important;
|
|
font-weight: 600;
|
|
}
|
|
.comm-btn-subject-add:hover {
|
|
background-color: #B43E8E !important;
|
|
color: #FFFFFF !important;
|
|
}
|
|
|
|
/* Encabezados de Tabla */
|
|
.commissions-table-head th {
|
|
background-color: #F8FAFC !important;
|
|
color: #334155 !important;
|
|
font-size: 0.76rem !important;
|
|
font-weight: 700 !important;
|
|
text-transform: uppercase !important;
|
|
letter-spacing: 0.04em !important;
|
|
border-bottom: 2px solid #E2E8F0 !important;
|
|
border-top: none !important;
|
|
padding-top: 11px !important;
|
|
padding-bottom: 11px !important;
|
|
}
|
|
|
|
/* Filas de la Tabla */
|
|
.commission-row {
|
|
transition: background-color 0.15s ease;
|
|
}
|
|
.commission-row:hover {
|
|
background-color: rgba(180, 62, 142, 0.035) !important;
|
|
}
|
|
.comm-code {
|
|
color: #B43E8E !important;
|
|
font-weight: 800;
|
|
font-size: 0.95rem;
|
|
}
|
|
.comm-shift-badge {
|
|
background-color: rgba(104, 44, 173, 0.10) !important;
|
|
color: #682CAD !important;
|
|
border: 1px solid rgba(104, 44, 173, 0.22) !important;
|
|
font-weight: 600;
|
|
}
|
|
.comm-schedule-badge {
|
|
background-color: #F1F5F9 !important;
|
|
color: #334155 !important;
|
|
border: 1px solid #CBD5E1 !important;
|
|
font-weight: 500;
|
|
}
|
|
.comm-semester-badge {
|
|
background-color: rgba(180, 62, 142, 0.08) !important;
|
|
color: #9E367D !important;
|
|
border: 1px solid rgba(180, 62, 142, 0.22) !important;
|
|
font-weight: 600;
|
|
}
|
|
.comm-teacher-name {
|
|
color: #0F172A !important;
|
|
font-weight: 600;
|
|
}
|
|
.comm-teacher-icon {
|
|
color: #682CAD !important;
|
|
}
|
|
.comm-teacher-email {
|
|
color: #64748B !important;
|
|
}
|
|
.comm-unassigned-badge {
|
|
background-color: rgba(245, 158, 11, 0.12) !important;
|
|
color: #B45309 !important;
|
|
border: 1px solid rgba(245, 158, 11, 0.3) !important;
|
|
}
|
|
|
|
/* Aula Virtual */
|
|
.comm-btn-virtual {
|
|
background-color: rgba(16, 185, 129, 0.12) !important;
|
|
color: #059669 !important;
|
|
border: 1px solid rgba(16, 185, 129, 0.35) !important;
|
|
font-weight: 600;
|
|
}
|
|
.comm-btn-virtual:hover {
|
|
background-color: #10B981 !important;
|
|
color: #FFFFFF !important;
|
|
}
|
|
.comm-btn-action-sm {
|
|
color: #64748B !important;
|
|
border-color: #CBD5E1 !important;
|
|
}
|
|
.comm-btn-action-sm:hover {
|
|
background-color: #F1F5F9 !important;
|
|
color: #0F172A !important;
|
|
}
|
|
.comm-no-link {
|
|
color: #94A3B8 !important;
|
|
}
|
|
|
|
/* Hitos Académicos */
|
|
.comm-milestone-chip {
|
|
box-shadow: 0 1px 3px rgba(0,0,0,0.12);
|
|
border: 1px solid rgba(255, 255, 255, 0.35);
|
|
font-weight: 600;
|
|
letter-spacing: 0.01em;
|
|
}
|
|
.comm-no-milestone {
|
|
color: #94A3B8 !important;
|
|
}
|
|
.comm-btn-add-milestone {
|
|
background-color: rgba(245, 158, 11, 0.10) !important;
|
|
color: #B45309 !important;
|
|
border: 1px solid rgba(245, 158, 11, 0.32) !important;
|
|
font-weight: 600;
|
|
}
|
|
.comm-btn-add-milestone:hover {
|
|
background-color: #F59E0B !important;
|
|
color: #FFFFFF !important;
|
|
}
|
|
|
|
/* Botones de Acción */
|
|
.comm-actions-group .btn {
|
|
border-color: #E2E8F0;
|
|
}
|
|
.comm-btn-reserve {
|
|
color: #B43E8E !important;
|
|
}
|
|
.comm-btn-reserve:hover {
|
|
background-color: #B43E8E !important;
|
|
color: #FFFFFF !important;
|
|
}
|
|
.comm-btn-edit {
|
|
color: #682CAD !important;
|
|
}
|
|
.comm-btn-edit:hover {
|
|
background-color: #682CAD !important;
|
|
color: #FFFFFF !important;
|
|
}
|
|
.comm-btn-delete {
|
|
color: #DC2626 !important;
|
|
}
|
|
.comm-btn-delete:hover {
|
|
background-color: #DC2626 !important;
|
|
color: #FFFFFF !important;
|
|
}
|
|
|
|
|
|
/* ==========================================================================
|
|
2. MODO OSCURO ([data-bs-theme="dark"])
|
|
========================================================================== */
|
|
[data-bs-theme="dark"] .comm-stat-card {
|
|
background-color: #1A2035 !important;
|
|
border: 1px solid rgba(255, 255, 255, 0.08) !important;
|
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.35) !important;
|
|
}
|
|
[data-bs-theme="dark"] .stat-icon-magenta {
|
|
background: rgba(180, 62, 142, 0.24) !important;
|
|
color: #FF8AB6 !important;
|
|
}
|
|
[data-bs-theme="dark"] .stat-icon-purple {
|
|
background: rgba(174, 127, 255, 0.20) !important;
|
|
color: #AE7FFF !important;
|
|
}
|
|
[data-bs-theme="dark"] .stat-icon-teal {
|
|
background: rgba(52, 211, 153, 0.20) !important;
|
|
color: #34D399 !important;
|
|
}
|
|
[data-bs-theme="dark"] .stat-icon-amber {
|
|
background: rgba(251, 191, 36, 0.20) !important;
|
|
color: #FBBF24 !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-stat-val {
|
|
color: #F8FAFC !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-stat-lbl {
|
|
color: #94A3B8 !important;
|
|
}
|
|
|
|
/* Barra de Búsqueda Oscuro */
|
|
[data-bs-theme="dark"] .comm-search-card {
|
|
background-color: #161B30 !important;
|
|
border: 1px solid rgba(255, 255, 255, 0.08) !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-search-input {
|
|
background-color: #101424 !important;
|
|
color: #F8FAFC !important;
|
|
border: 1px solid rgba(255, 255, 255, 0.12) !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-search-input::placeholder {
|
|
color: #64748B !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-search-input:focus {
|
|
border-color: #FF8AB6 !important;
|
|
box-shadow: 0 0 0 0.2rem rgba(180, 62, 142, 0.35) !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-search-icon {
|
|
background-color: #101424 !important;
|
|
color: #FF8AB6 !important;
|
|
border: 1px solid rgba(255, 255, 255, 0.12) !important;
|
|
border-right: none !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-info-hint {
|
|
color: #94A3B8 !important;
|
|
}
|
|
|
|
/* Tarjeta de Asignatura Oscuro */
|
|
[data-bs-theme="dark"] .subject-card .card {
|
|
background-color: #161B30 !important;
|
|
border: 1px solid rgba(255, 255, 255, 0.08) !important;
|
|
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4) !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-subject-header {
|
|
background: linear-gradient(90deg, rgba(180, 62, 142, 0.18) 0%, rgba(104, 44, 173, 0.10) 50%, #161B30 100%) !important;
|
|
border-bottom: 1px solid rgba(255, 255, 255, 0.08) !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-subject-badge {
|
|
background: rgba(180, 62, 142, 0.30) !important;
|
|
color: #FF8AB6 !important;
|
|
border: 1px solid rgba(180, 62, 142, 0.55) !important;
|
|
box-shadow: 0 0 10px rgba(180, 62, 142, 0.2);
|
|
}
|
|
[data-bs-theme="dark"] .comm-subject-title {
|
|
color: #F8FAFC !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-subject-meta {
|
|
color: #94A3B8 !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-count-badge {
|
|
background-color: rgba(255, 255, 255, 0.07) !important;
|
|
color: #CBD5E1 !important;
|
|
border: 1px solid rgba(255, 255, 255, 0.12) !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-btn-subject-add {
|
|
color: #FF8AB6 !important;
|
|
border-color: rgba(180, 62, 142, 0.5) !important;
|
|
background: rgba(180, 62, 142, 0.12) !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-btn-subject-add:hover {
|
|
background-color: #B43E8E !important;
|
|
color: #FFFFFF !important;
|
|
}
|
|
|
|
/* Encabezados de Tabla Oscuro (NO MÁS FRANJA BLANCA) */
|
|
[data-bs-theme="dark"] .commissions-table-head th {
|
|
background-color: #121526 !important;
|
|
color: #CBD5E1 !important;
|
|
border-bottom: 2px solid rgba(255, 255, 255, 0.10) !important;
|
|
border-top: none !important;
|
|
}
|
|
|
|
/* Filas de la Tabla Oscuro */
|
|
[data-bs-theme="dark"] .table {
|
|
--bs-table-bg: transparent;
|
|
--bs-table-color: #E2E8F0;
|
|
--bs-table-hover-bg: rgba(180, 62, 142, 0.08);
|
|
}
|
|
[data-bs-theme="dark"] .commission-row td {
|
|
border-bottom-color: rgba(255, 255, 255, 0.06) !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-code {
|
|
color: #FF8AB6 !important; /* Alto contraste neon pink */
|
|
}
|
|
[data-bs-theme="dark"] .comm-shift-badge {
|
|
background-color: rgba(174, 127, 255, 0.20) !important;
|
|
color: #D8B4FE !important;
|
|
border: 1px solid rgba(174, 127, 255, 0.40) !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-schedule-badge {
|
|
background-color: rgba(255, 255, 255, 0.08) !important;
|
|
color: #E2E8F0 !important;
|
|
border: 1px solid rgba(255, 255, 255, 0.15) !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-semester-badge {
|
|
background-color: rgba(180, 62, 142, 0.24) !important;
|
|
color: #FF96B4 !important;
|
|
border: 1px solid rgba(180, 62, 142, 0.45) !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-teacher-name {
|
|
color: #F1F5F9 !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-teacher-icon {
|
|
color: #AE7FFF !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-teacher-email {
|
|
color: #94A3B8 !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-unassigned-badge {
|
|
background-color: rgba(245, 158, 11, 0.18) !important;
|
|
color: #FCD34D !important;
|
|
border: 1px solid rgba(245, 158, 11, 0.38) !important;
|
|
}
|
|
|
|
/* Aula Virtual Oscuro */
|
|
[data-bs-theme="dark"] .comm-btn-virtual {
|
|
background-color: rgba(16, 185, 129, 0.20) !important;
|
|
color: #34D399 !important;
|
|
border: 1px solid rgba(16, 185, 129, 0.45) !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-btn-virtual:hover {
|
|
background-color: #059669 !important;
|
|
color: #FFFFFF !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-btn-action-sm {
|
|
background-color: rgba(255, 255, 255, 0.06) !important;
|
|
color: #CBD5E1 !important;
|
|
border-color: rgba(255, 255, 255, 0.14) !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-btn-action-sm:hover {
|
|
background-color: rgba(255, 255, 255, 0.12) !important;
|
|
color: #FFFFFF !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-no-link {
|
|
color: #64748B !important;
|
|
}
|
|
|
|
/* Hitos Académicos Oscuro */
|
|
[data-bs-theme="dark"] .comm-milestone-chip {
|
|
box-shadow: 0 2px 6px rgba(0,0,0,0.4);
|
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
|
}
|
|
[data-bs-theme="dark"] .comm-no-milestone {
|
|
color: #64748B !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-btn-add-milestone {
|
|
background-color: rgba(245, 158, 11, 0.18) !important;
|
|
color: #FCD34D !important;
|
|
border: 1px solid rgba(245, 158, 11, 0.45) !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-btn-add-milestone:hover {
|
|
background-color: #D97706 !important;
|
|
color: #FFFFFF !important;
|
|
}
|
|
|
|
/* Botones de Acción Oscuro */
|
|
[data-bs-theme="dark"] .comm-actions-group .btn {
|
|
border-color: rgba(255, 255, 255, 0.12);
|
|
}
|
|
[data-bs-theme="dark"] .comm-btn-reserve {
|
|
background-color: rgba(180, 62, 142, 0.16) !important;
|
|
color: #FF8AB6 !important;
|
|
border-color: rgba(180, 62, 142, 0.4) !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-btn-reserve:hover {
|
|
background-color: #B43E8E !important;
|
|
color: #FFFFFF !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-btn-edit {
|
|
background-color: rgba(104, 44, 173, 0.16) !important;
|
|
color: #C4B5FD !important;
|
|
border-color: rgba(104, 44, 173, 0.38) !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-btn-edit:hover {
|
|
background-color: #682CAD !important;
|
|
color: #FFFFFF !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-btn-delete {
|
|
background-color: rgba(239, 68, 68, 0.16) !important;
|
|
color: #FCA5A5 !important;
|
|
border-color: rgba(239, 68, 68, 0.38) !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-btn-delete:hover {
|
|
background-color: #DC2626 !important;
|
|
color: #FFFFFF !important;
|
|
}
|
|
|
|
/* Modales en Modo Oscuro */
|
|
[data-bs-theme="dark"] .modal-content {
|
|
background-color: #161B30 !important;
|
|
border: 1px solid rgba(180, 62, 142, 0.35) !important;
|
|
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.6) !important;
|
|
}
|
|
[data-bs-theme="dark"] .modal-header {
|
|
background: linear-gradient(135deg, rgba(180, 62, 142, 0.18) 0%, rgba(22, 27, 48, 0.95) 100%) !important;
|
|
border-bottom: 1px solid rgba(255, 255, 255, 0.08) !important;
|
|
}
|
|
[data-bs-theme="dark"] .modal-footer {
|
|
background-color: #121526 !important;
|
|
border-top: 1px solid rgba(255, 255, 255, 0.08) !important;
|
|
}
|
|
[data-bs-theme="dark"] .modal-title {
|
|
color: #F8FAFC !important;
|
|
}
|
|
[data-bs-theme="dark"] .form-label {
|
|
color: #E2E8F0 !important;
|
|
}
|
|
[data-bs-theme="dark"] .form-control,
|
|
[data-bs-theme="dark"] .form-select {
|
|
background-color: #101424 !important;
|
|
border: 1px solid rgba(255, 255, 255, 0.14) !important;
|
|
color: #F8FAFC !important;
|
|
}
|
|
[data-bs-theme="dark"] .form-control:focus,
|
|
[data-bs-theme="dark"] .form-select:focus {
|
|
border-color: #FF8AB6 !important;
|
|
box-shadow: 0 0 0 0.2rem rgba(180, 62, 142, 0.35) !important;
|
|
}
|
|
[data-bs-theme="dark"] .input-group-text {
|
|
background-color: #101424 !important;
|
|
border-color: rgba(255, 255, 255, 0.14) !important;
|
|
color: #FF8AB6 !important;
|
|
}
|
|
[data-bs-theme="dark"] .comm-modal-alert {
|
|
background-color: rgba(104, 44, 173, 0.22) !important;
|
|
border: 1px solid rgba(174, 127, 255, 0.35) !important;
|
|
color: #E2E8F0 !important;
|
|
}
|
|
[data-bs-theme="dark"] .alert-light {
|
|
background-color: rgba(255, 255, 255, 0.05) !important;
|
|
border-color: rgba(255, 255, 255, 0.1) !important;
|
|
color: #CBD5E1 !important;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid pt-5 mt-4">
|
|
<!-- Header -->
|
|
<div class="d-flex flex-column flex-md-row justify-content-between align-items-md-center gap-3 mb-4">
|
|
<div>
|
|
<nav aria-label="breadcrumb">
|
|
<ol class="breadcrumb mb-1 small">
|
|
<li class="breadcrumb-item"><a href="{{ url_for('main.dashboard') }}">{% trans %}Dashboard{% endtrans %}</a></li>
|
|
<li class="breadcrumb-item active" aria-current="page">{% trans %}Comisiones{% endtrans %}</li>
|
|
</ol>
|
|
</nav>
|
|
<h1 class="h3 fw-bold mb-1">
|
|
<i class="bi bi-diagram-3 text-primary me-2"></i>{% trans %}Gestión de Comisiones y Cursadas{% endtrans %}
|
|
</h1>
|
|
<p class="text-muted mb-0 small">
|
|
{% trans %}Administración de comisiones académicas, enlaces a aulas virtuales (Zoom/Teams/Meet) e hitos de evaluación.{% endtrans %}
|
|
</p>
|
|
</div>
|
|
<div class="d-flex flex-wrap gap-2">
|
|
{% if can_edit_commissions %}
|
|
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#modalAddCommission">
|
|
<i class="bi bi-plus-lg me-1"></i>{% trans %}Nueva Comisión{% endtrans %}
|
|
</button>
|
|
{% endif %}
|
|
<a href="{{ url_for('schedule.add_reservation') }}" class="btn btn-outline-success">
|
|
<i class="bi bi-calendar-plus me-1"></i>{% trans %}Nueva Reserva{% endtrans %}
|
|
</a>
|
|
<a href="{{ url_for('schedule.today_schedule') }}" class="btn btn-outline-secondary">
|
|
<i class="bi bi-calendar-day me-1"></i>{% trans %}Hoy{% endtrans %}
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Statistics Cards -->
|
|
<div class="row g-3 mb-4">
|
|
<div class="col-6 col-md-3">
|
|
<div class="card comm-stat-card shadow-sm h-100">
|
|
<div class="card-body d-flex align-items-center">
|
|
<div class="badge stat-icon-magenta p-3 rounded-3 me-3 fs-4">
|
|
<i class="bi bi-journal-bookmark"></i>
|
|
</div>
|
|
<div>
|
|
<div class="h3 comm-stat-val mb-0">{{ subjects|length }}</div>
|
|
<div class="comm-stat-lbl">{% trans %}Materias Activas{% endtrans %}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-6 col-md-3">
|
|
<div class="card comm-stat-card shadow-sm h-100">
|
|
<div class="card-body d-flex align-items-center">
|
|
<div class="badge stat-icon-purple p-3 rounded-3 me-3 fs-4">
|
|
<i class="bi bi-diagram-3"></i>
|
|
</div>
|
|
<div>
|
|
<div class="h3 comm-stat-val mb-0">{{ total_commissions }}</div>
|
|
<div class="comm-stat-lbl">{% trans %}Total Comisiones{% endtrans %}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-6 col-md-3">
|
|
<div class="card comm-stat-card shadow-sm h-100">
|
|
<div class="card-body d-flex align-items-center">
|
|
<div class="badge stat-icon-teal p-3 rounded-3 me-3 fs-4">
|
|
<i class="bi bi-people"></i>
|
|
</div>
|
|
<div>
|
|
<div class="h3 comm-stat-val mb-0">{{ total_capacity }}</div>
|
|
<div class="comm-stat-lbl">{% trans %}Capacidad Total{% endtrans %}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-6 col-md-3">
|
|
<div class="card comm-stat-card shadow-sm h-100">
|
|
<div class="card-body d-flex align-items-center">
|
|
<div class="badge stat-icon-amber p-3 rounded-3 me-3 fs-4">
|
|
<i class="bi bi-camera-video"></i>
|
|
</div>
|
|
<div>
|
|
{% set ns = namespace(virtual_count=0) %}
|
|
{% for s in subjects %}
|
|
{% for c in s.commissions %}
|
|
{% if c.virtual_link %}{% set ns.virtual_count = ns.virtual_count + 1 %}{% endif %}
|
|
{% endfor %}
|
|
{% endfor %}
|
|
<div class="h3 comm-stat-val mb-0">{{ ns.virtual_count }}</div>
|
|
<div class="comm-stat-lbl">{% trans %}Aulas Virtuales Config.{% endtrans %}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Search / Filter Bar -->
|
|
<div class="card comm-search-card shadow-sm mb-4">
|
|
<div class="card-body py-2">
|
|
<div class="row g-2 align-items-center">
|
|
<div class="col-md-5">
|
|
<div class="input-group">
|
|
<span class="input-group-text comm-search-icon"><i class="bi bi-search"></i></span>
|
|
<input type="text" id="filterInput" class="form-control comm-search-input" placeholder="{% trans %}Filtrar por asignatura, código de comisión o docente...{% endtrans %}">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-7 text-md-end comm-info-hint">
|
|
<i class="bi bi-info-circle me-1"></i>{% trans %}Profesores, Bedelía y Administradores pueden actualizar el enlace de videollamada y agendar hitos de examen.{% endtrans %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Subjects & Commissions List -->
|
|
<div class="row g-4" id="commissionsListContainer">
|
|
{% for subject in subjects %}
|
|
<div class="col-12 subject-card" data-subject-name="{{ subject.name|lower }}" data-subject-code="{{ subject.code|lower }}">
|
|
<div class="card shadow-sm">
|
|
<div class="card-header comm-subject-header border-0 py-3">
|
|
<div class="d-flex flex-column flex-md-row justify-content-between align-items-md-center gap-2">
|
|
<div class="d-flex align-items-center">
|
|
<span class="badge comm-subject-badge fs-6 me-2 font-monospace">{{ subject.code }}</span>
|
|
<div>
|
|
<h5 class="mb-0 comm-subject-title">{{ subject.name }}</h5>
|
|
<small class="comm-subject-meta">
|
|
<i class="bi bi-building me-1"></i>{{ subject.department or 'UniCABA' }}
|
|
{% if subject.career %} • <i class="bi bi-mortarboard me-1"></i>{{ subject.career }}{% endif %}
|
|
</small>
|
|
</div>
|
|
</div>
|
|
<div class="d-flex align-items-center gap-2">
|
|
<span class="badge comm-count-badge">
|
|
{{ subject.commissions|length }} {% trans %}comisiones{% endtrans %}
|
|
</span>
|
|
{% if can_edit_commissions %}
|
|
<button type="button" class="btn btn-sm comm-btn-subject-add btn-add-comm-subject"
|
|
data-subject-id="{{ subject.id }}"
|
|
data-subject-name="{{ subject.name }} ({{ subject.code }})"
|
|
title="{% trans %}Agregar comisión a esta materia{% endtrans %}">
|
|
<i class="bi bi-plus-circle me-1"></i>{% trans %}Comisión{% endtrans %}
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card-body p-0">
|
|
{% if subject.commissions %}
|
|
<div class="table-responsive">
|
|
<table class="table table-hover align-middle mb-0">
|
|
<thead class="commissions-table-head">
|
|
<tr>
|
|
<th class="ps-4" style="min-width: 140px;">{% trans %}Comisión / Turno{% endtrans %}</th>
|
|
<th style="min-width: 130px;">{% trans %}Ciclo Lectivo{% endtrans %}</th>
|
|
<th style="min-width: 160px;">{% trans %}Docente{% endtrans %}</th>
|
|
<th style="min-width: 220px;">{% trans %}Aula Virtual{% endtrans %}</th>
|
|
<th style="min-width: 240px;">{% trans %}Hitos Académicos{% endtrans %}</th>
|
|
<th class="text-center" style="min-width: 110px;">{% trans %}Cupo{% endtrans %}</th>
|
|
<th class="text-end pe-4" style="min-width: 130px;">{% trans %}Acciones{% endtrans %}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for commission in subject.commissions %}
|
|
<tr class="commission-row" data-search-text="{{ commission.code|lower }} {{ commission.teacher_name|lower }} {{ subject.name|lower }}">
|
|
<td class="ps-4">
|
|
<div class="comm-code">{{ commission.code }}</div>
|
|
<div class="d-flex gap-1 mt-1">
|
|
{% if commission.shift %}
|
|
<span class="badge comm-shift-badge" style="font-size: 0.70rem;">
|
|
<i class="bi bi-clock me-1"></i>{{ commission.shift }}
|
|
</span>
|
|
{% endif %}
|
|
{% if commission.schedule %}
|
|
<span class="badge comm-schedule-badge" style="font-size: 0.70rem;" title="{{ commission.schedule }}">
|
|
{{ commission.schedule|truncate(15) }}
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<span class="badge comm-semester-badge">
|
|
{{ commission.semester }} {{ commission.year }}
|
|
</span>
|
|
</td>
|
|
<td>
|
|
{% if commission.teacher %}
|
|
<div class="fw-semibold small comm-teacher-name"><i class="bi bi-person me-1 comm-teacher-icon"></i>{{ commission.teacher.name }}</div>
|
|
<div class="comm-teacher-email small font-monospace" style="font-size: 0.75rem;">{{ commission.teacher.email }}</div>
|
|
{% else %}
|
|
<span class="badge comm-unassigned-badge small">
|
|
<i class="bi bi-question-circle me-1"></i>{% trans %}Sin asignar{% endtrans %}
|
|
</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
<!-- Enlace a Aula Virtual (Zoom, Teams, Meet) -->
|
|
<div class="d-flex align-items-center gap-1">
|
|
{% if commission.virtual_link %}
|
|
<a href="{{ commission.virtual_link }}" target="_blank" class="btn btn-sm comm-btn-virtual text-truncate d-inline-flex align-items-center" style="max-width: 150px;" title="{{ commission.virtual_link }}">
|
|
<i class="bi bi-camera-video me-1"></i>{% trans %}Unirse{% endtrans %}
|
|
</a>
|
|
{% else %}
|
|
<span class="comm-no-link small fst-italic me-1">{% trans %}Sin enlace{% endtrans %}</span>
|
|
{% endif %}
|
|
|
|
{% if can_manage_milestones %}
|
|
<button type="button" class="btn btn-sm comm-btn-action-sm btn-virtual-link"
|
|
data-id="{{ commission.id }}"
|
|
data-name="{{ commission.get_full_code() }}"
|
|
data-link="{{ commission.virtual_link or '' }}"
|
|
title="{% trans %}Configurar enlace Zoom/Teams/Meet{% endtrans %}">
|
|
<i class="bi bi-link-45deg"></i>
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<!-- Hitos Académicos (Parciales, TPs) -->
|
|
<div class="d-flex flex-wrap align-items-center gap-1">
|
|
{% for m in commission.academic_milestones %}
|
|
<span class="badge comm-milestone-chip text-white d-inline-flex align-items-center py-1 px-2"
|
|
style="background-color: {{ m.color }}; font-size: 0.72rem;"
|
|
title="{{ m.title }} - {{ m.date.strftime('%d/%m/%Y %H:%M') if m.date else '' }} ({{ m.classroom or 'Campus Virtual' }})">
|
|
<i class="bi bi-flag-fill me-1" style="font-size: 0.65rem;"></i>
|
|
{{ m.title or m.tipo }}
|
|
<span class="ms-1 opacity-75">({{ m.date.strftime('%d/%m') if m.date else '' }})</span>
|
|
{% if can_manage_milestones %}
|
|
<form action="{{ url_for('schedule.commission_milestone_delete', commission_id=commission.id, milestone_id=m.id) }}" method="POST" class="d-inline ms-1" onsubmit="return confirm('¿Eliminar este hito?');">
|
|
<button type="submit" class="btn btn-link text-white p-0 m-0 border-0" style="line-height: 1; text-decoration: none;" title="Eliminar hito">×</button>
|
|
</form>
|
|
{% endif %}
|
|
</span>
|
|
{% else %}
|
|
<span class="comm-no-milestone small fst-italic">{% trans %}Sin hitos{% endtrans %}</span>
|
|
{% endfor %}
|
|
|
|
{% if can_manage_milestones %}
|
|
<button type="button" class="btn btn-sm comm-btn-add-milestone py-0 px-2 btn-add-milestone"
|
|
data-id="{{ commission.id }}"
|
|
data-name="{{ commission.get_full_code() }}"
|
|
data-subject-code="{{ subject.code }}"
|
|
data-virtual-link="{{ commission.virtual_link or '' }}"
|
|
title="{% trans %}Agregar Hito de Evaluación{% endtrans %}"
|
|
style="font-size: 0.72rem; line-height: 1.5;">
|
|
<i class="bi bi-plus-lg me-1"></i>{% trans %}Hito{% endtrans %}
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
</td>
|
|
<td class="text-center">
|
|
<div class="small fw-semibold comm-stat-val">{{ commission.current_students }}/{{ commission.max_students }}</div>
|
|
{% set fill_pct = (commission.current_students / commission.max_students * 100) if commission.max_students > 0 else 0 %}
|
|
<div class="progress mt-1 mx-auto" style="height: 4px; width: 50px;">
|
|
<div class="progress-bar {% if fill_pct > 90 %}bg-danger{% elif fill_pct > 70 %}bg-warning{% else %}bg-success{% endif %}"
|
|
style="width: {{ [fill_pct, 100]|min }}%;"></div>
|
|
</div>
|
|
</td>
|
|
<td class="text-end pe-4">
|
|
<div class="btn-group btn-group-sm comm-actions-group">
|
|
<button type="button" class="btn comm-btn-reserve quick-reserve-btn"
|
|
data-commission-id="{{ commission.id }}"
|
|
data-max-students="{{ commission.max_students }}"
|
|
data-commission-name="{{ commission.get_full_code() }}"
|
|
data-subject-name="{{ subject.name }}"
|
|
title="{% trans %}Reserva Rápida de Aula{% endtrans %}">
|
|
<i class="bi bi-calendar-plus"></i>
|
|
</button>
|
|
{% if can_edit_commissions %}
|
|
<button type="button" class="btn comm-btn-edit btn-edit-comm"
|
|
data-id="{{ commission.id }}"
|
|
data-code="{{ commission.code }}"
|
|
data-semester="{{ commission.semester }}"
|
|
data-year="{{ commission.year }}"
|
|
data-teacher-id="{{ commission.teacher_id or '' }}"
|
|
data-max-students="{{ commission.max_students }}"
|
|
data-schedule="{{ commission.schedule or '' }}"
|
|
data-shift="{{ commission.shift or 'Tarde' }}"
|
|
data-virtual-link="{{ commission.virtual_link or '' }}"
|
|
data-active="{{ 'true' if commission.active else 'false' }}"
|
|
title="{% trans %}Editar Comisión{% endtrans %}">
|
|
<i class="bi bi-pencil"></i>
|
|
</button>
|
|
<button type="button" class="btn comm-btn-delete btn-delete-comm"
|
|
data-id="{{ commission.id }}"
|
|
data-name="{{ commission.get_full_code() }}"
|
|
data-action-url="{{ url_for('schedule.commission_delete', id=commission.id) }}"
|
|
title="{% trans %}Eliminar Comisión{% endtrans %}">
|
|
<i class="bi bi-trash"></i>
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center text-muted py-4">
|
|
<i class="bi bi-inbox fs-2 d-block mb-1 text-secondary"></i>
|
|
{% trans %}No hay comisiones registradas para esta asignatura.{% endtrans %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal 1: Nueva Comisión -->
|
|
{% if can_edit_commissions %}
|
|
<div class="modal fade" id="modalAddCommission" tabindex="-1" aria-labelledby="modalAddCommissionLabel" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered modal-lg">
|
|
<div class="modal-content border-0 shadow">
|
|
<div class="modal-header bg-body-tertiary">
|
|
<h5 class="modal-title fw-bold" id="modalAddCommissionLabel">
|
|
<i class="bi bi-plus-circle text-primary me-2"></i>{% trans %}Nueva Comisión de Cursada{% endtrans %}
|
|
</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<form action="{{ url_for('schedule.commission_add') }}" method="POST">
|
|
<div class="modal-body p-4">
|
|
<div class="row g-3 mb-3">
|
|
<div class="col-md-7">
|
|
<label for="add_subject_id" class="form-label fw-semibold">{% trans %}Asignatura{% endtrans %} <span class="text-danger">*</span></label>
|
|
<select class="form-select" id="add_subject_id" name="subject_id" required>
|
|
<option value="">{% trans %}Seleccione la materia...{% endtrans %}</option>
|
|
{% for s in subjects %}
|
|
<option value="{{ s.id }}">{{ s.code }} - {{ s.name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-5">
|
|
<label for="add_code" class="form-label fw-semibold">{% trans %}Código de Comisión{% endtrans %} <span class="text-danger">*</span></label>
|
|
<input type="text" class="form-control" id="add_code" name="code" placeholder="Ej: A, B, N1, Única" required>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row g-3 mb-3">
|
|
<div class="col-md-4">
|
|
<label for="add_semester" class="form-label fw-semibold">{% trans %}Cuatrimestre{% endtrans %}</label>
|
|
<select class="form-select" id="add_semester" name="semester">
|
|
<option value="1C" selected>1C (Primer Cuatrimestre)</option>
|
|
<option value="2C">2C (Segundo Cuatrimestre)</option>
|
|
<option value="Anual">Anual</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label for="add_year" class="form-label fw-semibold">{% trans %}Año{% endtrans %}</label>
|
|
<input type="number" class="form-control" id="add_year" name="year" value="2026" min="2020" max="2035">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label for="add_shift" class="form-label fw-semibold">{% trans %}Turno{% endtrans %}</label>
|
|
<select class="form-select" id="add_shift" name="shift">
|
|
<option value="Mañana">Mañana (08:00 - 12:00)</option>
|
|
<option value="Tarde" selected>Tarde / Noche (17:30 - 21:30)</option>
|
|
<option value="Vespertino">Vespertino</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row g-3 mb-3">
|
|
<div class="col-md-8">
|
|
<label for="add_teacher_id" class="form-label fw-semibold">{% trans %}Docente a Cargo{% endtrans %}</label>
|
|
<select class="form-select" id="add_teacher_id" name="teacher_id">
|
|
<option value="">-- {% trans %}Sin asignar{% endtrans %} --</option>
|
|
{% for t in teachers %}
|
|
<option value="{{ t.id }}">{{ t.name }} ({{ t.email }})</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label for="add_max_students" class="form-label fw-semibold">{% trans %}Cupo Máximo{% endtrans %}</label>
|
|
<input type="number" class="form-control" id="add_max_students" name="max_students" value="40" min="1">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="add_schedule" class="form-label fw-semibold">{% trans %}Días y Horarios{% endtrans %}</label>
|
|
<input type="text" class="form-control" id="add_schedule" name="schedule" placeholder="Ej: Lun y Mié 17:30 a 21:30 hs">
|
|
</div>
|
|
|
|
<div class="mb-2">
|
|
<label for="add_virtual_link" class="form-label fw-semibold">
|
|
<i class="bi bi-camera-video text-primary me-1"></i>{% trans %}Enlace de Aula Virtual (Zoom, Meet, Teams, Moodle){% endtrans %}
|
|
</label>
|
|
<input type="url" class="form-control" id="add_virtual_link" name="virtual_link" placeholder="https://meet.google.com/... o https://teams.microsoft.com/...">
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer bg-body-tertiary">
|
|
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">{% trans %}Cancelar{% endtrans %}</button>
|
|
<button type="submit" class="btn btn-primary px-4">
|
|
<i class="bi bi-save me-1"></i>{% trans %}Crear Comisión{% endtrans %}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal 2: Editar Comisión -->
|
|
<div class="modal fade" id="modalEditCommission" tabindex="-1" aria-labelledby="modalEditCommissionLabel" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered modal-lg">
|
|
<div class="modal-content border-0 shadow">
|
|
<div class="modal-header bg-body-tertiary">
|
|
<h5 class="modal-title fw-bold" id="modalEditCommissionLabel">
|
|
<i class="bi bi-pencil-square text-primary me-2"></i>{% trans %}Editar Comisión{% endtrans %}
|
|
</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<form id="formEditCommission" method="POST">
|
|
<div class="modal-body p-4">
|
|
<div class="row g-3 mb-3">
|
|
<div class="col-md-6">
|
|
<label for="edit_code" class="form-label fw-semibold">{% trans %}Código de Comisión{% endtrans %} <span class="text-danger">*</span></label>
|
|
<input type="text" class="form-control" id="edit_code" name="code" required>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label for="edit_semester" class="form-label fw-semibold">{% trans %}Cuatrimestre{% endtrans %}</label>
|
|
<select class="form-select" id="edit_semester" name="semester">
|
|
<option value="1C">1C</option>
|
|
<option value="2C">2C</option>
|
|
<option value="Anual">Anual</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label for="edit_year" class="form-label fw-semibold">{% trans %}Año{% endtrans %}</label>
|
|
<input type="number" class="form-control" id="edit_year" name="year" min="2020" max="2035">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row g-3 mb-3">
|
|
<div class="col-md-8">
|
|
<label for="edit_teacher_id" class="form-label fw-semibold">{% trans %}Docente a Cargo{% endtrans %}</label>
|
|
<select class="form-select" id="edit_teacher_id" name="teacher_id">
|
|
<option value="">-- {% trans %}Sin asignar{% endtrans %} --</option>
|
|
{% for t in teachers %}
|
|
<option value="{{ t.id }}">{{ t.name }} ({{ t.email }})</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label for="edit_shift" class="form-label fw-semibold">{% trans %}Turno{% endtrans %}</label>
|
|
<select class="form-select" id="edit_shift" name="shift">
|
|
<option value="Mañana">Mañana</option>
|
|
<option value="Tarde">Tarde / Noche</option>
|
|
<option value="Vespertino">Vespertino</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row g-3 mb-3">
|
|
<div class="col-md-8">
|
|
<label for="edit_schedule" class="form-label fw-semibold">{% trans %}Días y Horarios{% endtrans %}</label>
|
|
<input type="text" class="form-control" id="edit_schedule" name="schedule">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label for="edit_max_students" class="form-label fw-semibold">{% trans %}Cupo Máximo{% endtrans %}</label>
|
|
<input type="number" class="form-control" id="edit_max_students" name="max_students" min="1">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="edit_virtual_link" class="form-label fw-semibold">
|
|
<i class="bi bi-camera-video text-primary me-1"></i>{% trans %}Enlace de Aula Virtual{% endtrans %}
|
|
</label>
|
|
<input type="url" class="form-control" id="edit_virtual_link" name="virtual_link">
|
|
</div>
|
|
|
|
<div class="form-check form-switch pt-2">
|
|
<input class="form-check-input" type="checkbox" id="edit_active" name="active" value="1">
|
|
<label class="form-check-label fw-semibold" for="edit_active">{% trans %}Comisión Activa{% endtrans %}</label>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer bg-body-tertiary">
|
|
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">{% trans %}Cancelar{% endtrans %}</button>
|
|
<button type="submit" class="btn btn-primary px-4">
|
|
<i class="bi bi-check-circle me-1"></i>{% trans %}Guardar Cambios{% endtrans %}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal 3: Configurar Aula Virtual (Zoom, Teams, Meet) -->
|
|
<div class="modal fade" id="modalVirtualLink" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content border-0 shadow-lg">
|
|
<div class="modal-header bg-body-tertiary">
|
|
<h5 class="modal-title fw-bold">
|
|
<i class="bi bi-camera-video text-primary me-2"></i>{% trans %}Aula Virtual / Videoconferencia{% endtrans %}
|
|
</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<form id="formVirtualLink" method="POST">
|
|
<div class="modal-body p-4">
|
|
<p class="small text-muted mb-3">
|
|
{% trans %}Asigna o actualiza el enlace de acceso directo al aula virtual para la comisión{% endtrans %} <strong id="virtualLinkCommName" class="comm-code"></strong>:
|
|
</p>
|
|
<div class="mb-3">
|
|
<label for="inputVirtualLink" class="form-label fw-semibold">URL de la reunión / sala</label>
|
|
<div class="input-group">
|
|
<span class="input-group-text"><i class="bi bi-link-45deg"></i></span>
|
|
<input type="url" class="form-control" id="inputVirtualLink" name="virtual_link" placeholder="https://meet.google.com/... o https://teams.microsoft.com/...">
|
|
</div>
|
|
</div>
|
|
<div class="alert alert-light border small text-muted mb-0">
|
|
<i class="bi bi-lightbulb text-warning me-1"></i>
|
|
{% trans %}Soporta enlaces de Zoom, Google Meet, Microsoft Teams, Webex o Campus Virtual Moodle.{% endtrans %}
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer bg-body-tertiary">
|
|
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">{% trans %}Cancelar{% endtrans %}</button>
|
|
<button type="submit" class="btn btn-primary px-4">
|
|
<i class="bi bi-check-circle me-1"></i>{% trans %}Guardar Enlace{% endtrans %}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal 4: Agregar Hito Académico a Comisión -->
|
|
<div class="modal fade" id="modalAddMilestone" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content border-0 shadow-lg">
|
|
<div class="modal-header bg-body-tertiary">
|
|
<h5 class="modal-title fw-bold">
|
|
<i class="bi bi-flag text-warning me-2"></i>{% trans %}Programar Hito Académico{% endtrans %}
|
|
</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<form id="formAddMilestone" method="POST">
|
|
<div class="modal-body p-4">
|
|
<div class="alert comm-modal-alert border-0 py-2 small mb-3">
|
|
{% trans %}Agendando evento de evaluación para la comisión{% endtrans %} <strong id="milestoneCommName" class="comm-code"></strong>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="milestone_type_id" class="form-label fw-semibold">{% trans %}Tipo de Hito / Categoría{% endtrans %} <span class="text-danger">*</span></label>
|
|
<select class="form-select" id="milestone_type_id" name="milestone_type_id" required>
|
|
<option value="">{% trans %}Seleccionar tipificación...{% endtrans %}</option>
|
|
{% for mt in milestone_types %}
|
|
<option value="{{ mt.id }}" data-color="{{ mt.color }}">{{ mt.name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="milestone_title" class="form-label fw-semibold">{% trans %}Título Específico (Opcional){% endtrans %}</label>
|
|
<input type="text" class="form-control" id="milestone_title" name="title" placeholder="Ej: Parcial 1 - Unidades 1 a 4">
|
|
</div>
|
|
|
|
<div class="row g-3 mb-3">
|
|
<div class="col-md-7">
|
|
<label for="milestone_date" class="form-label fw-semibold">{% trans %}Fecha{% endtrans %} <span class="text-danger">*</span></label>
|
|
<input type="date" class="form-control" id="milestone_date" name="date" required>
|
|
</div>
|
|
<div class="col-md-5">
|
|
<label for="milestone_time" class="form-label fw-semibold">{% trans %}Hora Inicio{% endtrans %}</label>
|
|
<input type="time" class="form-control" id="milestone_time" name="time" value="18:00">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="milestone_classroom" class="form-label fw-semibold">{% trans %}Espacio / Aula Física o Virtual{% endtrans %}</label>
|
|
<select class="form-select" id="milestone_classroom" name="classroom">
|
|
<option value="Campus Virtual">🌐 Campus Virtual / Remoto</option>
|
|
{% for room in classrooms %}
|
|
<option value="{{ room.code }} - {{ room.name }} (Piso {{ room.floor }})">📍 {{ room.code }} - {{ room.name }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="milestone_link" class="form-label fw-semibold">{% trans %}Enlace de Conexión (si aplica){% endtrans %}</label>
|
|
<input type="url" class="form-control" id="milestone_link" name="link" placeholder="https://zoom.us/... o https://meet.google.com/...">
|
|
</div>
|
|
|
|
<div class="mb-2">
|
|
<label for="milestone_notes" class="form-label fw-semibold">{% trans %}Consignas o Temario{% endtrans %}</label>
|
|
<textarea class="form-control" id="milestone_notes" name="notes" rows="2" placeholder="Temas evaluados, requisitos, material permitido..."></textarea>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer bg-body-tertiary">
|
|
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">{% trans %}Cancelar{% endtrans %}</button>
|
|
<button type="submit" class="btn btn-primary px-4">
|
|
<i class="bi bi-save me-1"></i>{% trans %}Registrar Hito{% endtrans %}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal 5: Eliminar Comisión (fuera de la tabla) -->
|
|
<div class="modal fade" id="deleteCommissionModal" tabindex="-1" aria-hidden="true" data-bs-backdrop="static">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content border-0 shadow-lg" style="border-radius: 14px;">
|
|
<div class="modal-header border-0 pb-0">
|
|
<div class="d-flex align-items-center">
|
|
<div class="badge bg-danger-subtle text-danger p-2 rounded-circle me-2">
|
|
<i class="bi bi-exclamation-triangle-fill fs-5"></i>
|
|
</div>
|
|
<h5 class="modal-title fw-bold text-danger mb-0">{% trans %}Confirmar Eliminación{% endtrans %}</h5>
|
|
</div>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body text-start py-3">
|
|
<p class="mb-2">{% trans %}¿Está seguro de que desea eliminar permanentemente la comisión{% endtrans %} <strong id="deleteCommName" class="comm-code"></strong>?</p>
|
|
<p class="text-muted small mb-0"><i class="bi bi-info-circle me-1"></i>{% trans %}Esta acción eliminará sus reservas e hitos académicos asociados.{% endtrans %}</p>
|
|
</div>
|
|
<div class="modal-footer border-0 pt-0">
|
|
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">{% trans %}Cancelar{% endtrans %}</button>
|
|
<form id="deleteCommForm" method="POST" class="d-inline">
|
|
<button type="submit" class="btn btn-danger px-3">
|
|
<i class="bi bi-trash me-1"></i>{% trans %}Eliminar Comisión{% endtrans %}
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quick Reservation Modal -->
|
|
<div class="modal fade" id="quickReservationModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered modal-lg">
|
|
<div class="modal-content border-0 shadow">
|
|
<div class="modal-header bg-body-tertiary">
|
|
<h5 class="modal-title fw-bold"><i class="bi bi-calendar-plus text-primary me-2"></i>{% trans %}Reserva Rápida de Aula{% endtrans %}</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body p-4">
|
|
<form id="quick-reservation-form">
|
|
<input type="hidden" id="quick-commission-id" name="commission_id">
|
|
|
|
<div class="alert comm-modal-alert py-2 mb-3" id="commission-details"></div>
|
|
|
|
<div class="row g-3 mb-3">
|
|
<div class="col-md-6">
|
|
<label for="quick-date" class="form-label fw-semibold">{% trans %}Fecha{% endtrans %}</label>
|
|
<input type="date" class="form-control" id="quick-date" required>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label for="quick-start-time" class="form-label fw-semibold">{% trans %}Inicio{% endtrans %}</label>
|
|
<input type="time" class="form-control" id="quick-start-time" required>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label for="quick-end-time" class="form-label fw-semibold">{% trans %}Fin{% endtrans %}</label>
|
|
<input type="time" class="form-control" id="quick-end-time" required>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="quick-purpose" class="form-label fw-semibold">{% trans %}Propósito{% endtrans %}</label>
|
|
<input type="text" class="form-control" id="quick-purpose" placeholder="Clase habitual, parcial, recuperatorio, etc." required>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label fw-semibold">{% trans %}Seleccionar Aula Disponible{% endtrans %}</label>
|
|
<select class="form-select" id="quick-classroom-select" required>
|
|
<option value="">{% trans %}Elige fecha y horarios primero{% endtrans %}</option>
|
|
{% for c in classrooms %}
|
|
<option value="{{ c.id }}">{{ c.code }} - {{ c.name }} (Cap: {{ c.capacity }}) - Piso {{ c.floor }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer bg-body-tertiary">
|
|
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">{% trans %}Cancelar{% endtrans %}</button>
|
|
<button type="button" class="btn btn-success px-4" id="create-reservation-btn">
|
|
<i class="bi bi-calendar-check me-1"></i>{% trans %}Confirmar Reserva{% endtrans %}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Search Filter
|
|
var filterInput = document.getElementById('filterInput');
|
|
if (filterInput) {
|
|
filterInput.addEventListener('input', function() {
|
|
var q = this.value.toLowerCase().trim();
|
|
document.querySelectorAll('.subject-card').forEach(function(card) {
|
|
var sName = card.getAttribute('data-subject-name') || '';
|
|
var sCode = card.getAttribute('data-subject-code') || '';
|
|
var rowMatches = 0;
|
|
card.querySelectorAll('.commission-row').forEach(function(row) {
|
|
var text = row.getAttribute('data-search-text') || '';
|
|
if (q === '' || text.indexOf(q) !== -1 || sName.indexOf(q) !== -1 || sCode.indexOf(q) !== -1) {
|
|
row.style.display = '';
|
|
rowMatches++;
|
|
} else {
|
|
row.style.display = 'none';
|
|
}
|
|
});
|
|
card.style.display = (q === '' || rowMatches > 0 || sName.indexOf(q) !== -1 || sCode.indexOf(q) !== -1) ? '' : 'none';
|
|
});
|
|
});
|
|
}
|
|
|
|
// Modal Edit Commission
|
|
var modalEditEl = document.getElementById('modalEditCommission');
|
|
var modalEdit = modalEditEl ? new bootstrap.Modal(modalEditEl) : null;
|
|
var formEdit = document.getElementById('formEditCommission');
|
|
|
|
document.querySelectorAll('.btn-edit-comm').forEach(function(btn) {
|
|
btn.addEventListener('click', function() {
|
|
var id = this.getAttribute('data-id');
|
|
if (formEdit) {
|
|
formEdit.action = "/schedule/commissions/" + id + "/edit";
|
|
document.getElementById('edit_code').value = this.getAttribute('data-code');
|
|
document.getElementById('edit_semester').value = this.getAttribute('data-semester');
|
|
document.getElementById('edit_year').value = this.getAttribute('data-year');
|
|
document.getElementById('edit_teacher_id').value = this.getAttribute('data-teacher-id');
|
|
document.getElementById('edit_max_students').value = this.getAttribute('data-max-students');
|
|
document.getElementById('edit_schedule').value = this.getAttribute('data-schedule');
|
|
document.getElementById('edit_shift').value = this.getAttribute('data-shift');
|
|
document.getElementById('edit_virtual_link').value = this.getAttribute('data-virtual-link');
|
|
document.getElementById('edit_active').checked = this.getAttribute('data-active') === 'true';
|
|
if (modalEdit) modalEdit.show();
|
|
}
|
|
});
|
|
});
|
|
|
|
// Modal Add Commission to Specific Subject
|
|
var modalAddEl = document.getElementById('modalAddCommission');
|
|
var modalAdd = modalAddEl ? new bootstrap.Modal(modalAddEl) : null;
|
|
document.querySelectorAll('.btn-add-comm-subject').forEach(function(btn) {
|
|
btn.addEventListener('click', function() {
|
|
var subjectId = this.getAttribute('data-subject-id');
|
|
var selectSubj = document.getElementById('add_subject_id');
|
|
if (selectSubj) selectSubj.value = subjectId;
|
|
if (modalAdd) modalAdd.show();
|
|
});
|
|
});
|
|
|
|
// Modal Virtual Link
|
|
var modalVirtualEl = document.getElementById('modalVirtualLink');
|
|
var modalVirtual = modalVirtualEl ? new bootstrap.Modal(modalVirtualEl) : null;
|
|
var formVirtual = document.getElementById('formVirtualLink');
|
|
var virtualNameEl = document.getElementById('virtualLinkCommName');
|
|
var inputVirtual = document.getElementById('inputVirtualLink');
|
|
|
|
document.querySelectorAll('.btn-virtual-link').forEach(function(btn) {
|
|
btn.addEventListener('click', function() {
|
|
var id = this.getAttribute('data-id');
|
|
var name = this.getAttribute('data-name');
|
|
var link = this.getAttribute('data-link');
|
|
if (formVirtual) formVirtual.action = "/schedule/commissions/" + id + "/virtual-link";
|
|
if (virtualNameEl) virtualNameEl.textContent = name;
|
|
if (inputVirtual) inputVirtual.value = link;
|
|
if (modalVirtual) modalVirtual.show();
|
|
});
|
|
});
|
|
|
|
// Modal Add Milestone
|
|
var modalMilestoneEl = document.getElementById('modalAddMilestone');
|
|
var modalMilestone = modalMilestoneEl ? new bootstrap.Modal(modalMilestoneEl) : null;
|
|
var formMilestone = document.getElementById('formAddMilestone');
|
|
var milestoneCommName = document.getElementById('milestoneCommName');
|
|
var milestoneLink = document.getElementById('milestone_link');
|
|
|
|
document.querySelectorAll('.btn-add-milestone').forEach(function(btn) {
|
|
btn.addEventListener('click', function() {
|
|
var id = this.getAttribute('data-id');
|
|
var name = this.getAttribute('data-name');
|
|
var vLink = this.getAttribute('data-virtual-link');
|
|
if (formMilestone) formMilestone.action = "/schedule/commissions/" + id + "/milestones/add";
|
|
if (milestoneCommName) milestoneCommName.textContent = name;
|
|
if (milestoneLink) milestoneLink.value = vLink || '';
|
|
if (modalMilestone) modalMilestone.show();
|
|
});
|
|
});
|
|
|
|
// Modal Delete Commission
|
|
var modalDeleteEl = document.getElementById('deleteCommissionModal');
|
|
var modalDelete = modalDeleteEl ? new bootstrap.Modal(modalDeleteEl) : null;
|
|
var deleteCommName = document.getElementById('deleteCommName');
|
|
var deleteCommForm = document.getElementById('deleteCommForm');
|
|
|
|
document.querySelectorAll('.btn-delete-comm').forEach(function(btn) {
|
|
btn.addEventListener('click', function() {
|
|
var name = this.getAttribute('data-name');
|
|
var actionUrl = this.getAttribute('data-action-url');
|
|
if (deleteCommName) deleteCommName.textContent = name;
|
|
if (deleteCommForm) deleteCommForm.action = actionUrl;
|
|
if (modalDelete) modalDelete.show();
|
|
});
|
|
});
|
|
|
|
// Quick Reservation
|
|
var quickModalEl = document.getElementById('quickReservationModal');
|
|
var quickModal = quickModalEl ? new bootstrap.Modal(quickModalEl) : null;
|
|
|
|
document.querySelectorAll('.quick-reserve-btn').forEach(function(btn) {
|
|
btn.addEventListener('click', function() {
|
|
var commId = this.getAttribute('data-commission-id');
|
|
var maxStudents = this.getAttribute('data-max-students');
|
|
var commName = this.getAttribute('data-commission-name');
|
|
var subjName = this.getAttribute('data-subject-name');
|
|
|
|
document.getElementById('quick-commission-id').value = commId;
|
|
document.getElementById('commission-details').innerHTML =
|
|
'<strong>' + commName + '</strong> - ' + subjName + ' <span class="badge bg-secondary ms-2">' + maxStudents + ' alumnos</span>';
|
|
|
|
// Set today's date by default
|
|
var today = new Date().toISOString().split('T')[0];
|
|
document.getElementById('quick-date').value = today;
|
|
document.getElementById('quick-start-time').value = '17:30';
|
|
document.getElementById('quick-end-time').value = '21:30';
|
|
document.getElementById('quick-purpose').value = 'Clase Cursada';
|
|
|
|
if (quickModal) quickModal.show();
|
|
});
|
|
});
|
|
|
|
var createResBtn = document.getElementById('create-reservation-btn');
|
|
if (createResBtn) {
|
|
createResBtn.addEventListener('click', function() {
|
|
var commId = document.getElementById('quick-commission-id').value;
|
|
var roomId = document.getElementById('quick-classroom-select').value;
|
|
var date = document.getElementById('quick-date').value;
|
|
var start = document.getElementById('quick-start-time').value;
|
|
var end = document.getElementById('quick-end-time').value;
|
|
var purpose = document.getElementById('quick-purpose').value;
|
|
|
|
if (!date || !start || !end || !roomId) {
|
|
alert('Por favor complete la fecha, los horarios y seleccione un aula.');
|
|
return;
|
|
}
|
|
|
|
var formData = new FormData();
|
|
formData.append('commission_id', commId);
|
|
formData.append('classroom_id', roomId);
|
|
formData.append('purpose', purpose);
|
|
formData.append('start_time', date + ' ' + start);
|
|
formData.append('end_time', date + ' ' + end);
|
|
|
|
fetch('/schedule/reservation/add', {
|
|
method: 'POST',
|
|
body: formData
|
|
}).then(function(res) {
|
|
if (res.ok) {
|
|
if (quickModal) quickModal.hide();
|
|
window.location.reload();
|
|
} else {
|
|
return res.json().then(function(data) {
|
|
alert(data.error || 'Error al crear la reserva');
|
|
});
|
|
}
|
|
}).catch(function(err) {
|
|
window.location.reload();
|
|
});
|
|
});
|
|
}
|
|
});
|
|
</script>
|
|
{% endblock %} |