695 lines
36 KiB
HTML
695 lines
36 KiB
HTML
{% 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 asignaturas, docentes e inscripción de alumnos por comisión</p>
|
|
</div>
|
|
<div class="d-flex gap-2">
|
|
<button type="button" class="btn btn-primary btn-sm px-3 shadow-sm" data-bs-toggle="modal" data-bs-target="#modalAddCommission">
|
|
<i class="bi bi-plus-circle me-1"></i>Nueva Comisión
|
|
</button>
|
|
<a href="/admin/subjects_list" class="btn btn-outline-secondary btn-sm">
|
|
<i class="bi bi-book me-1"></i>Ver Asignaturas
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Flash Messages & Feedback -->
|
|
{% if msg == 'created' or created %}
|
|
<div class="alert alert-success alert-dismissible fade show d-flex align-items-center gap-2 mb-4 shadow-sm" role="alert">
|
|
<i class="bi bi-check-circle-fill fs-5 text-success"></i>
|
|
<div>
|
|
<strong>¡Comisión registrada!</strong> La comisión ha sido creada y asociada correctamente a la asignatura elegida.
|
|
</div>
|
|
<button type="button" class="btn-close ms-auto" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
</div>
|
|
{% elif msg == 'updated' %}
|
|
<div class="alert alert-success alert-dismissible fade show d-flex align-items-center gap-2 mb-4 shadow-sm" role="alert">
|
|
<i class="bi bi-check-circle-fill fs-5 text-success"></i>
|
|
<div>
|
|
<strong>¡Comisión actualizada!</strong> Los datos de la comisión, cupos y horarios fueron guardados correctamente.
|
|
</div>
|
|
<button type="button" class="btn-close ms-auto" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
</div>
|
|
{% elif msg == 'toggled' %}
|
|
<div class="alert alert-info alert-dismissible fade show d-flex align-items-center gap-2 mb-4 shadow-sm" role="alert">
|
|
<i class="bi bi-info-circle-fill fs-5 text-info"></i>
|
|
<div>
|
|
El estado de actividad de la comisión ha sido modificado.
|
|
</div>
|
|
<button type="button" class="btn-close ms-auto" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
</div>
|
|
{% elif msg == 'deleted' %}
|
|
<div class="alert alert-success alert-dismissible fade show d-flex align-items-center gap-2 mb-4 shadow-sm" role="alert">
|
|
<i class="bi bi-check-circle-fill fs-5 text-success"></i>
|
|
<div>
|
|
La comisión ha sido eliminada permanentemente del sistema.
|
|
</div>
|
|
<button type="button" class="btn-close ms-auto" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
</div>
|
|
{% elif msg == 'deactivated' %}
|
|
<div class="alert alert-warning alert-dismissible fade show d-flex align-items-center gap-2 mb-4 shadow-sm" role="alert">
|
|
<i class="bi bi-exclamation-triangle-fill fs-5 text-warning"></i>
|
|
<div>
|
|
La comisión posee reservas horarias o alumnos inscriptos. Por integridad del historial académico fue <strong>desactivada</strong>.
|
|
</div>
|
|
<button type="button" class="btn-close ms-auto" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
</div>
|
|
{% elif error == 'create' %}
|
|
<div class="alert alert-danger alert-dismissible fade show d-flex align-items-center gap-2 mb-4 shadow-sm" role="alert">
|
|
<i class="bi bi-x-circle-fill fs-5 text-danger"></i>
|
|
<div>No se pudo crear la comisión. Verifique que no exista otra comisión con el mismo código en el mismo período.</div>
|
|
<button type="button" class="btn-close ms-auto" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
</div>
|
|
{% elif error == 'edit' %}
|
|
<div class="alert alert-danger alert-dismissible fade show d-flex align-items-center gap-2 mb-4 shadow-sm" role="alert">
|
|
<i class="bi bi-x-circle-fill fs-5 text-danger"></i>
|
|
<div>Ocurrió un error al actualizar los datos de la comisión.</div>
|
|
<button type="button" class="btn-close ms-auto" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
</div>
|
|
{% elif error == 'delete' %}
|
|
<div class="alert alert-danger alert-dismissible fade show d-flex align-items-center gap-2 mb-4 shadow-sm" role="alert">
|
|
<i class="bi bi-x-circle-fill fs-5 text-danger"></i>
|
|
<div>Ocurrió un error al intentar eliminar la comisión.</div>
|
|
<button type="button" class="btn-close ms-auto" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
</div>
|
|
{% elif error %}
|
|
<div class="alert alert-danger alert-dismissible fade show d-flex align-items-center gap-2 mb-4 shadow-sm" role="alert">
|
|
<i class="bi bi-x-circle-fill fs-5 text-danger"></i>
|
|
<div>Ocurrió una eventualidad procesando la comisión.</div>
|
|
<button type="button" class="btn-close ms-auto" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
</div>
|
|
{% endif %}
|
|
|
|
|
|
<!-- 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 text-primary">{{ comm.subject.name }}</div>
|
|
<div class="d-flex align-items-center gap-1 mt-1 flex-wrap">
|
|
<span class="badge bg-secondary-subtle text-secondary border font-monospace">{{ comm.subject.code }}</span>
|
|
<span class="badge bg-body-tertiary text-muted border">{{ comm.subject.career_name or 'Carrera General' }}</span>
|
|
<small class="text-muted ms-1">{{ comm.semester }} {{ comm.year }}</small>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
{% if comm.teachers and comm.teachers | length > 0 %}
|
|
<div class="d-flex flex-column gap-1">
|
|
{% for t in comm.teachers %}
|
|
<div class="d-flex align-items-center gap-1">
|
|
<span class="badge {% if t.role == 'Titular' %}bg-primary-subtle text-primary border border-primary-subtle{% elif t.role == 'Adjunto' %}bg-info-subtle text-info border border-info-subtle{% elif t.role == 'JTP' %}bg-warning-subtle text-warning border border-warning-subtle{% else %}bg-secondary-subtle text-secondary border{% endif %} py-0 px-1" style="font-size: 0.65rem;">
|
|
{{ t.role or 'Docente' }}
|
|
</span>
|
|
<span class="small text-truncate" style="max-width: 150px;" title="{{ t.name }}">{{ t.name }}</span>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% elif 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">
|
|
<div class="btn-group btn-group-sm">
|
|
<a href="/admin/commission_detail?id={{ comm.id }}"
|
|
class="btn btn-outline-primary py-0 px-2" title="Gestionar e Inscribir Alumnos">
|
|
<i class="bi bi-gear"></i>
|
|
</a>
|
|
<a href="/schedule/add_reservation?commission_id={{ comm.id }}"
|
|
class="btn btn-outline-info py-0 px-2" title="Asignar Aula / Horario a esta Comisión">
|
|
<i class="bi bi-calendar-plus"></i>
|
|
</a>
|
|
<button type="button" class="btn btn-outline-secondary py-0 px-2"
|
|
data-bs-toggle="modal" data-bs-target="#modalEditCommission"
|
|
data-id="{{ comm.id }}"
|
|
data-code="{{ comm.code }}"
|
|
data-subject-id="{{ comm.subject_id }}"
|
|
data-academic-term-id="{{ comm.academic_term_id or '' }}"
|
|
data-semester="{{ comm.semester }}"
|
|
data-year="{{ comm.year }}"
|
|
data-shift="{{ comm.shift }}"
|
|
data-teacher-id="{{ comm.teacher_id or '' }}"
|
|
data-max-students="{{ comm.max_students }}"
|
|
data-schedule="{{ comm.schedule or '' }}"
|
|
data-virtual-link="{{ comm.virtual_link or '' }}"
|
|
data-active="{{ 'true' if comm.active else 'false' }}"
|
|
title="Editar Comisión">
|
|
<i class="bi bi-pencil"></i>
|
|
</button>
|
|
<form method="POST" action="/admin/commission_toggle" class="d-inline">
|
|
<input type="hidden" name="id" value="{{ comm.id }}">
|
|
<button type="submit" class="btn btn-outline-warning py-0 px-2"
|
|
title="{% if comm.active %}Desactivar comisión{% else %}Activar comisión{% endif %}">
|
|
<i class="bi {% if comm.active %}bi-pause{% else %}bi-play{% endif %}"></i>
|
|
</button>
|
|
</form>
|
|
<button type="button" class="btn btn-outline-danger py-0 px-2"
|
|
data-bs-toggle="modal" data-bs-target="#modalDeleteCommission"
|
|
data-id="{{ comm.id }}"
|
|
data-code="{{ comm.get_full_code() }}"
|
|
data-subject-name="{{ comm.subject.name }}"
|
|
title="Eliminar Comisión">
|
|
<i class="bi bi-trash"></i>
|
|
</button>
|
|
</div>
|
|
</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>
|
|
<h5 class="mt-3 fw-bold text-secondary">No se encontraron comisiones</h5>
|
|
<p class="text-muted small">Crea una nueva comisión para asociarla a una materia y asignarle horarios, cupos y docentes.</p>
|
|
<div class="d-flex justify-content-center gap-2 mt-3">
|
|
<button type="button" class="btn btn-primary btn-sm px-3 shadow-sm" data-bs-toggle="modal" data-bs-target="#modalAddCommission">
|
|
<i class="bi bi-plus-circle me-1"></i>Crear Primera Comisión
|
|
</button>
|
|
<a href="/admin/commissions_list" class="btn btn-outline-secondary btn-sm">
|
|
<i class="bi bi-arrow-clockwise me-1"></i>Limpiar filtros
|
|
</a>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal: Nueva Comisión y Asociación con Materia -->
|
|
<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 border-bottom py-3">
|
|
<h5 class="modal-title fw-bold" id="modalAddCommissionLabel">
|
|
<i class="bi bi-plus-circle text-primary me-2"></i>Nueva Comisión de Cursada
|
|
</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<form action="/admin/commission_add" method="POST">
|
|
<div class="modal-body p-4">
|
|
<div class="alert alert-primary bg-primary-subtle border-primary-subtle py-2 px-3 small d-flex align-items-center gap-2 mb-4">
|
|
<i class="bi bi-info-circle-fill fs-5 text-primary"></i>
|
|
<div>
|
|
Cada comisión debe pertenecer a una <strong>Asignatura</strong> específica para organizar sus horarios, inscripción de alumnos y enlaces de aulas virtuales.
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 1. Asignatura y Código -->
|
|
<div class="row g-3 mb-3">
|
|
<div class="col-md-7">
|
|
<label for="add_subject_id" class="form-label fw-semibold small">
|
|
<i class="bi bi-book text-primary me-1"></i>Asignatura / Materia Asociada <span class="text-danger">*</span>
|
|
</label>
|
|
<select class="form-select" id="add_subject_id" name="subject_id" required>
|
|
<option value="">-- Seleccione la materia a vincular... --</option>
|
|
{% for s in subjects %}
|
|
<option value="{{ s.id }}">
|
|
{{ s.code }} · {{ s.name }} ({{ s.career_name or s.career or 'Carrera General' }})
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
<div class="form-text small">La comisión heredará el plan de estudios y carrera de la materia.</div>
|
|
</div>
|
|
<div class="col-md-5">
|
|
<label for="add_code" class="form-label fw-semibold small">
|
|
<i class="bi bi-tag text-secondary me-1"></i>Código / Identificador <span class="text-danger">*</span>
|
|
</label>
|
|
<input type="text" class="form-control" id="add_code" name="code" placeholder="Ej: Comisión A, B, N1, Única" required>
|
|
<div class="form-text small">Identificador visible para docentes y alumnos.</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 2. Ciclo Lectivo, Período, Año y Turno -->
|
|
<div class="row g-3 mb-3">
|
|
<div class="col-md-6">
|
|
<label for="add_academic_term_id" class="form-label fw-semibold small">
|
|
<i class="bi bi-calendar-range text-primary me-1"></i>Ciclo Lectivo
|
|
</label>
|
|
<select class="form-select form-select-sm" id="add_academic_term_id" name="academic_term_id">
|
|
<option value="">-- Seleccionar Ciclo Lectivo --</option>
|
|
{% for term in academic_terms %}
|
|
<option value="{{ term.id }}" data-year="{{ term.year }}" data-period="{{ term.period_type }}" data-code="{{ term.code }}" {% if term.is_current %}selected{% endif %}>
|
|
{{ term.name }} ({{ term.code }}){% if term.is_current %} [Actual]{% endif %}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
<div class="form-text small">Asocia la comisión al calendario académico.</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label for="add_semester" class="form-label fw-semibold small">Cuatrimestre / Período</label>
|
|
<select class="form-select form-select-sm" 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-3">
|
|
<label for="add_year" class="form-label fw-semibold small">Año Académico</label>
|
|
<input type="number" class="form-control form-control-sm" id="add_year" name="year" value="2026" min="2020" max="2035">
|
|
</div>
|
|
</div>
|
|
<div class="row g-3 mb-3">
|
|
<div class="col-md-6">
|
|
<label for="add_shift" class="form-label fw-semibold small">Turno de Cursada</label>
|
|
<select class="form-select form-select-sm" id="add_shift" name="shift">
|
|
<option value="Mañana">Mañana (08:00 - 12:00)</option>
|
|
<option value="Tarde" selected>Tarde (17:30 - 21:30)</option>
|
|
<option value="Vespertino">Vespertino</option>
|
|
<option value="Noche">Noche (19:00 - 23:00)</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 3. Docente a Cargo y Cupo -->
|
|
<div class="row g-3 mb-3">
|
|
<div class="col-md-8">
|
|
<label for="add_teacher_id" class="form-label fw-semibold small">
|
|
<i class="bi bi-person-video3 text-info me-1"></i>Docente Responsable (Titular)
|
|
</label>
|
|
<select class="form-select form-select-sm" id="add_teacher_id" name="teacher_id">
|
|
<option value="">-- Sin asignar (se puede asignar más tarde) --</option>
|
|
{% for t in teachers %}
|
|
<option value="{{ t.id }}">{{ t.name }} ({{ t.email }})</option>
|
|
{% endfor %}
|
|
</select>
|
|
<div class="form-text small">Docente titular principal. Podrás agregar más docentes (co-docencia, adjuntos, JTP) desde el detalle de la comisión.</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label for="add_max_students" class="form-label fw-semibold small">Cupo Máximo de Alumnos</label>
|
|
<input type="number" class="form-control form-control-sm" id="add_max_students" name="max_students" value="40" min="1" max="300">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 4. Días y Horarios -->
|
|
<div class="mb-3">
|
|
<label for="add_schedule" class="form-label fw-semibold small">
|
|
<i class="bi bi-calendar-week text-warning me-1"></i>Días y Horarios de Cursada
|
|
</label>
|
|
<input type="text" class="form-control form-control-sm" id="add_schedule" name="schedule" placeholder="Ej: Lun y Mié 17:30 a 21:30 hs">
|
|
</div>
|
|
|
|
<!-- 5. Enlace a Aula Virtual -->
|
|
<div class="mb-2">
|
|
<label for="add_virtual_link" class="form-label fw-semibold small">
|
|
<i class="bi bi-camera-video text-primary me-1"></i>Enlace a Aula Virtual (Meet, Zoom, Teams, Moodle)
|
|
</label>
|
|
<input type="url" class="form-control form-control-sm" id="add_virtual_link" name="virtual_link" placeholder="https://meet.google.com/... (opcional, se auto-generará si se omite)">
|
|
<div class="form-text small">Si no se especifica, el sistema creará un enlace institucional directo a Google Meet.</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer bg-body-tertiary border-top py-2">
|
|
<button type="button" class="btn btn-outline-secondary btn-sm" data-bs-dismiss="modal">Cancelar</button>
|
|
<button type="submit" class="btn btn-primary btn-sm px-4 shadow-sm">
|
|
<i class="bi bi-save me-1"></i>Crear y Asociar Comisión
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal: 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 border-bottom py-3">
|
|
<h5 class="modal-title fw-bold" id="modalEditCommissionLabel">
|
|
<i class="bi bi-pencil-square text-primary me-2"></i>Editar Comisión de Cursada
|
|
</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<form action="/admin/commission_edit" method="POST" id="formEditCommission">
|
|
<input type="hidden" name="id" id="edit_commission_id">
|
|
<div class="modal-body p-4">
|
|
<!-- 1. Asignatura y Código -->
|
|
<div class="row g-3 mb-3">
|
|
<div class="col-md-7">
|
|
<label for="edit_subject_id" class="form-label fw-semibold small">
|
|
<i class="bi bi-book text-primary me-1"></i>Asignatura Asociada <span class="text-danger">*</span>
|
|
</label>
|
|
<select class="form-select form-select-sm" id="edit_subject_id" name="subject_id" required>
|
|
{% for subj in subjects %}
|
|
<option value="{{ subj.id }}">{{ subj.code }} — {{ subj.name }} ({{ subj.career_name or 'General' }})</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-5">
|
|
<label for="edit_code" class="form-label fw-semibold small">
|
|
<i class="bi bi-tag text-primary me-1"></i>Identificador / Código <span class="text-danger">*</span>
|
|
</label>
|
|
<input type="text" class="form-control form-control-sm font-monospace" id="edit_code" name="code" required>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 2. Ciclo Lectivo, Semestre y Año -->
|
|
<div class="row g-3 mb-3">
|
|
<div class="col-md-5">
|
|
<label for="edit_academic_term_id" class="form-label fw-semibold small">
|
|
<i class="bi bi-calendar-event text-info me-1"></i>Ciclo Lectivo
|
|
</label>
|
|
<select class="form-select form-select-sm" id="edit_academic_term_id" name="academic_term_id">
|
|
<option value="">-- Sin ciclo específico --</option>
|
|
{% for term in academic_terms %}
|
|
<option value="{{ term.id }}" data-year="{{ term.year }}" data-period="{{ term.period_type or '' }}" data-code="{{ term.code }}">
|
|
{{ term.name }} ({{ term.code }}) - {{ term.year }}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label for="edit_semester" class="form-label fw-semibold small">Período / Cuatrimestre</label>
|
|
<select class="form-select form-select-sm" id="edit_semester" name="semester">
|
|
<option value="1C">1C (Primer Cuatrimestre)</option>
|
|
<option value="2C">2C (Segundo Cuatrimestre)</option>
|
|
<option value="Anual">Anual</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label for="edit_year" class="form-label fw-semibold small">Año Académico</label>
|
|
<input type="number" class="form-control form-control-sm" id="edit_year" name="year" min="2020" max="2035">
|
|
</div>
|
|
</div>
|
|
<div class="row g-3 mb-3">
|
|
<div class="col-md-6">
|
|
<label for="edit_shift" class="form-label fw-semibold small">Turno de Cursada</label>
|
|
<select class="form-select form-select-sm" id="edit_shift" name="shift">
|
|
<option value="Mañana">Mañana (08:00 - 12:00)</option>
|
|
<option value="Tarde">Tarde (17:30 - 21:30)</option>
|
|
<option value="Vespertino">Vespertino</option>
|
|
<option value="Noche">Noche (19:00 - 23:00)</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label for="edit_max_students" class="form-label fw-semibold small">Cupo Máximo de Alumnos</label>
|
|
<input type="number" class="form-control form-control-sm" id="edit_max_students" name="max_students" min="1" max="300">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 3. Docente Responsable -->
|
|
<div class="row g-3 mb-3">
|
|
<div class="col-md-12">
|
|
<label for="edit_teacher_id" class="form-label fw-semibold small">
|
|
<i class="bi bi-person-video3 text-info me-1"></i>Docente Responsable (Titular)
|
|
</label>
|
|
<select class="form-select form-select-sm" id="edit_teacher_id" name="teacher_id">
|
|
<option value="">-- Sin asignar (se puede asignar más tarde) --</option>
|
|
{% for t in teachers %}
|
|
<option value="{{ t.id }}">{{ t.name }} ({{ t.email }})</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 4. Días y Horarios -->
|
|
<div class="mb-3">
|
|
<label for="edit_schedule" class="form-label fw-semibold small">
|
|
<i class="bi bi-calendar-week text-warning me-1"></i>Días y Horarios de Cursada
|
|
</label>
|
|
<input type="text" class="form-control form-control-sm" id="edit_schedule" name="schedule" placeholder="Ej: Lun y Mié 17:30 a 21:30 hs">
|
|
</div>
|
|
|
|
<!-- 5. Enlace Virtual -->
|
|
<div class="mb-3">
|
|
<label for="edit_virtual_link" class="form-label fw-semibold small">
|
|
<i class="bi bi-camera-video text-primary me-1"></i>Enlace a Aula Virtual (Meet, Zoom, Teams)
|
|
</label>
|
|
<input type="url" class="form-control form-control-sm" id="edit_virtual_link" name="virtual_link">
|
|
</div>
|
|
|
|
<!-- 6. Estado Activo -->
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="active" id="edit_active" value="on">
|
|
<label class="form-check-label small fw-semibold" for="edit_active">
|
|
Comisión Activa para Cursada y Reservas
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer bg-body-tertiary border-top py-2">
|
|
<button type="button" class="btn btn-outline-secondary btn-sm" data-bs-dismiss="modal">Cancelar</button>
|
|
<button type="submit" class="btn btn-primary btn-sm px-4 shadow-sm">
|
|
<i class="bi bi-save me-1"></i>Guardar Cambios
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal: Eliminar Comisión -->
|
|
<div class="modal fade" id="modalDeleteCommission" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content border-0 shadow">
|
|
<form action="/admin/commission_delete" method="POST">
|
|
<input type="hidden" name="id" id="delete_commission_id">
|
|
<div class="modal-header bg-danger text-white py-3">
|
|
<h5 class="modal-title h6 fw-bold">
|
|
<i class="bi bi-exclamation-triangle-fill me-2"></i>Eliminar Comisión
|
|
</h5>
|
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body py-4">
|
|
<p class="mb-2">¿Estás seguro de que deseas eliminar la comisión <strong id="delete_commission_code" class="font-monospace"></strong> vinculada a <strong id="delete_commission_subject"></strong>?</p>
|
|
<div class="alert alert-warning small mb-0">
|
|
<i class="bi bi-info-circle me-1"></i>
|
|
<strong>Integridad Académica:</strong> Si la comisión posee reservas en aulas o alumnos inscriptos, el sistema la <strong>desactivará automáticamente</strong> para salvaguardar las actas y calificaciones.
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer bg-body-tertiary border-top py-2">
|
|
<button type="button" class="btn btn-outline-secondary btn-sm" data-bs-dismiss="modal">Cancelar</button>
|
|
<button type="submit" class="btn btn-danger btn-sm px-3">
|
|
<i class="bi bi-trash me-1"></i>Confirmar Eliminación
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Sincronización Ciclo Lectivo en Alta
|
|
const addTermSelect = document.getElementById('add_academic_term_id');
|
|
const addYearInput = document.getElementById('add_year');
|
|
const addSemSelect = document.getElementById('add_semester');
|
|
|
|
function syncTerm(termSelect, yearInput, semSelect) {
|
|
if (!termSelect) return;
|
|
const opt = termSelect.options[termSelect.selectedIndex];
|
|
if (!opt || !opt.value) return;
|
|
const yr = opt.getAttribute('data-year');
|
|
const period = (opt.getAttribute('data-period') || opt.text || '').toLowerCase();
|
|
const code = (opt.getAttribute('data-code') || '').toLowerCase();
|
|
if (yr && yearInput) yearInput.value = yr;
|
|
if (semSelect) {
|
|
if (period.includes('segundo') || code.includes('2c') || code.includes('2º') || code.includes('2do') || code.includes('-2')) {
|
|
semSelect.value = '2C';
|
|
} else if (period.includes('anual') || code.includes('anual')) {
|
|
semSelect.value = 'Anual';
|
|
} else if (period.includes('primer') || code.includes('1c') || code.includes('1º') || code.includes('1ro') || code.includes('-1')) {
|
|
semSelect.value = '1C';
|
|
}
|
|
}
|
|
}
|
|
|
|
if (addTermSelect) {
|
|
addTermSelect.addEventListener('change', () => syncTerm(addTermSelect, addYearInput, addSemSelect));
|
|
if (addTermSelect.value) syncTerm(addTermSelect, addYearInput, addSemSelect);
|
|
}
|
|
|
|
// Sincronización Ciclo Lectivo en Edición
|
|
const editTermSelect = document.getElementById('edit_academic_term_id');
|
|
const editYearInput = document.getElementById('edit_year');
|
|
const editSemSelect = document.getElementById('edit_semester');
|
|
|
|
if (editTermSelect) {
|
|
editTermSelect.addEventListener('change', () => syncTerm(editTermSelect, editYearInput, editSemSelect));
|
|
}
|
|
|
|
// Poblar Modal Editar Comisión
|
|
const modalEdit = document.getElementById('modalEditCommission');
|
|
if (modalEdit) {
|
|
modalEdit.addEventListener('show.bs.modal', function(event) {
|
|
const btn = event.relatedTarget;
|
|
if (!btn) return;
|
|
document.getElementById('edit_commission_id').value = btn.getAttribute('data-id') || '';
|
|
document.getElementById('edit_code').value = btn.getAttribute('data-code') || '';
|
|
document.getElementById('edit_subject_id').value = btn.getAttribute('data-subject-id') || '';
|
|
document.getElementById('edit_academic_term_id').value = btn.getAttribute('data-academic-term-id') || '';
|
|
document.getElementById('edit_semester').value = btn.getAttribute('data-semester') || '1C';
|
|
document.getElementById('edit_year').value = btn.getAttribute('data-year') || '2026';
|
|
document.getElementById('edit_shift').value = btn.getAttribute('data-shift') || 'Mañana';
|
|
document.getElementById('edit_teacher_id').value = btn.getAttribute('data-teacher-id') || '';
|
|
document.getElementById('edit_max_students').value = btn.getAttribute('data-max-students') || '40';
|
|
document.getElementById('edit_schedule').value = btn.getAttribute('data-schedule') || '';
|
|
document.getElementById('edit_virtual_link').value = btn.getAttribute('data-virtual-link') || '';
|
|
document.getElementById('edit_active').checked = (btn.getAttribute('data-active') === 'true');
|
|
});
|
|
}
|
|
|
|
// Poblar Modal Eliminar Comisión
|
|
const modalDel = document.getElementById('modalDeleteCommission');
|
|
if (modalDel) {
|
|
modalDel.addEventListener('show.bs.modal', function(event) {
|
|
const btn = event.relatedTarget;
|
|
if (!btn) return;
|
|
document.getElementById('delete_commission_id').value = btn.getAttribute('data-id') || '';
|
|
document.getElementById('delete_commission_code').textContent = btn.getAttribute('data-code') || '';
|
|
document.getElementById('delete_commission_subject').textContent = btn.getAttribute('data-subject-name') || '';
|
|
});
|
|
}
|
|
});
|
|
</script>
|
|
{% endblock %}
|