Files

474 lines
26 KiB
HTML

{% extends 'base.html' %}
{% block title %}Comisión {{ commission.full_code or commission.code }} — Edu-Space{% endblock %}
{% block content %}
<div class="container-fluid py-4">
<!-- Breadcrumb -->
<nav aria-label="breadcrumb" class="mb-3">
<ol class="breadcrumb small">
<li class="breadcrumb-item"><a href="/main/dashboard">Inicio</a></li>
<li class="breadcrumb-item"><a href="/admin/commissions_list">Comisiones</a></li>
<li class="breadcrumb-item active">{{ commission.full_code or commission.code }}</li>
</ol>
</nav>
<!-- Flash & Action Alerts -->
{% if error %}
<div class="alert alert-danger alert-dismissible fade show d-flex align-items-center gap-2 shadow-sm mb-4" role="alert">
<i class="bi bi-exclamation-triangle-fill fs-5 text-danger flex-shrink-0"></i>
<div class="small fw-semibold flex-grow-1">{{ error }}</div>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endif %}
{% if success %}
<div class="alert alert-success alert-dismissible fade show d-flex align-items-center gap-2 shadow-sm mb-4" role="alert">
<i class="bi bi-check-circle-fill fs-5 text-success flex-shrink-0"></i>
<div class="small fw-semibold flex-grow-1">Alumno matriculado exitosamente en la comisión.</div>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endif %}
{% if unregistered %}
<div class="alert alert-info alert-dismissible fade show d-flex align-items-center gap-2 shadow-sm mb-4" role="alert">
<i class="bi bi-info-circle-fill fs-5 text-info flex-shrink-0"></i>
<div class="small fw-semibold flex-grow-1">El alumno ha sido desvinculado de la comisión.</div>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endif %}
{% if updated %}
<div class="alert alert-info alert-dismissible fade show d-flex align-items-center gap-2 shadow-sm mb-4" role="alert">
<i class="bi bi-info-circle-fill fs-5 text-info flex-shrink-0"></i>
<div class="small fw-semibold flex-grow-1">Estado de matrícula actualizado correctamente.</div>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endif %}
<div class="row g-4">
<!-- Left: Commission Info + Teacher Assignment -->
<div class="col-12 col-lg-4">
<!-- Commission Card -->
<div class="card shadow-sm border-0 mb-4">
<div class="card-header border-0 bg-primary text-white rounded-top py-3 d-flex justify-content-between align-items-center">
<div class="d-flex align-items-center gap-2">
<i class="bi bi-diagram-3 fs-4"></i>
<div>
<div class="fw-bold font-monospace">{{ commission.full_code or commission.code }}</div>
<small class="opacity-75">{{ commission.semester }} {{ commission.year }}</small>
</div>
</div>
{% if can_edit %}
<button type="button" class="btn btn-sm btn-light py-1 px-2 fw-semibold text-primary" data-bs-toggle="modal" data-bs-target="#modalEditCommissionDetail">
<i class="bi bi-pencil me-1"></i>Editar
</button>
{% endif %}
</div>
<div class="card-body">
<h5 class="fw-bold mb-1">{{ commission.subject.name }}</h5>
<p class="text-muted small mb-3">{{ commission.subject.code }}
{% if commission.subject.career_obj %}— {{ commission.subject.career_obj.name }}{% endif %}
</p>
<div class="d-flex flex-wrap gap-2 mb-3">
{% if commission.shift %}
{% set shift_colors = {'Mañana': 'warning', 'Tarde': 'info', 'Vespertino': 'secondary', 'Noche': 'dark'} %}
<span class="badge bg-{{ shift_colors[commission.shift] or 'secondary' }}-subtle text-{{ shift_colors[commission.shift] or 'secondary' }} border border-{{ shift_colors[commission.shift] or 'secondary' }}-subtle">
<i class="bi bi-clock me-1"></i>{{ commission.shift }}
</span>
{% endif %}
<span class="badge {% if commission.active %}bg-success-subtle text-success{% else %}bg-secondary-subtle text-secondary{% endif %}">
{{ 'Activa' if commission.active else 'Inactiva' }}
</span>
</div>
<ul class="list-unstyled small mb-0">
<li class="d-flex justify-content-between py-1 border-bottom">
<span class="text-muted">Cupo máximo</span>
<strong>{{ commission.max_students }}</strong>
</li>
<li class="d-flex justify-content-between py-1 border-bottom">
<span class="text-muted">Inscriptos</span>
<strong class="{% if (commission.current_students or 0) >= commission.max_students %}text-danger{% else %}text-success{% endif %}">
{{ commission.current_students or 0 }}
</strong>
</li>
{% if commission.schedule %}
<li class="d-flex justify-content-between py-1 border-bottom">
<span class="text-muted">Horario</span>
<span>{{ commission.schedule }}</span>
</li>
{% endif %}
{% if commission.virtual_link %}
<li class="py-1">
<a href="{{ commission.virtual_link }}" target="_blank" class="btn btn-sm btn-outline-info w-100">
<i class="bi bi-camera-video me-1"></i>Acceso Virtual
</a>
</li>
{% endif %}
</ul>
<div class="mt-3 pt-2 border-top">
<a href="/schedule/gradebook/{{ commission.id }}" class="btn btn-primary btn-sm w-100 shadow-sm d-flex align-items-center justify-content-center gap-2">
<i class="bi bi-journal-check fs-6"></i>
<span class="fw-semibold">Libro de Calificaciones (Actas)</span>
</a>
{% if commission.grades_closed %}
<div class="badge bg-danger-subtle text-danger border border-danger-subtle w-100 mt-2 py-2 text-center">
<i class="bi bi-lock-fill me-1"></i>Acta Cerrada: {{ commission.acta_number }}
</div>
{% endif %}
</div>
</div>
</div>
<!-- Co-Docencia / Equipo Docente -->
<div class="card shadow-sm border-0">
<div class="card-header border-0 bg-transparent py-2 d-flex align-items-center justify-content-between">
<div class="d-flex align-items-center gap-2">
<i class="bi bi-people-fill text-info"></i>
<span class="fw-semibold">Equipo Docente / Cátedra</span>
</div>
<span class="badge bg-info-subtle text-info border border-info-subtle">
{{ commission.teachers|length if commission.teachers else (1 if commission.teacher else 0) }} docente(s)
</span>
</div>
<div class="card-body">
{% if commission.teachers and commission.teachers|length > 0 %}
<div class="d-flex flex-column gap-2 mb-3">
{% for t in commission.teachers %}
<div class="d-flex align-items-center justify-content-between p-2 rounded bg-body-tertiary border">
<div class="d-flex align-items-center gap-2 overflow-hidden">
<div class="avatar-sm {% if t.role == 'Titular' %}bg-primary{% elif t.role == 'Adjunto' %}bg-info{% else %}bg-secondary{% endif %} rounded-circle text-white d-flex align-items-center justify-content-center" style="width:34px;height:34px;flex-shrink:0;">
<i class="bi bi-person-fill small"></i>
</div>
<div class="text-truncate">
<div class="d-flex align-items-center gap-1">
<span class="fw-semibold small text-truncate">{{ t.name }}</span>
<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>
</div>
<div class="text-muted text-truncate" style="font-size:.75rem;">{{ t.email or 'Sin email' }}</div>
</div>
</div>
{% if can_edit %}
<form method="POST" action="/admin/commission_remove_teacher" class="ms-2 flex-shrink-0" onsubmit="return confirm('¿Quitar a {{ t.name }} de la comisión?');">
<input type="hidden" name="id" value="{{ commission.id }}">
<input type="hidden" name="teacher_id" value="{{ t.user_id }}">
<button type="submit" class="btn btn-outline-danger btn-sm py-0 px-1" title="Quitar de la cátedra">
<i class="bi bi-trash" style="font-size: 0.75rem;"></i>
</button>
</form>
{% endif %}
</div>
{% endfor %}
</div>
{% elif commission.teacher %}
<div class="d-flex align-items-center justify-content-between p-2 rounded bg-body-tertiary border mb-3">
<div class="d-flex align-items-center gap-2">
<div class="avatar-sm bg-primary rounded-circle text-white d-flex align-items-center justify-content-center" style="width:34px;height:34px;flex-shrink:0;">
<i class="bi bi-person-fill small"></i>
</div>
<div>
<div class="fw-semibold small">{{ commission.teacher.name }} <span class="badge bg-primary-subtle text-primary border py-0 px-1" style="font-size:0.65rem;">Titular</span></div>
<div class="text-muted" style="font-size:.75rem;">{{ commission.teacher.email }}</div>
</div>
</div>
</div>
{% else %}
<div class="alert alert-warning py-2 small mb-3">
<i class="bi bi-exclamation-triangle me-1"></i>Sin docentes asignados a la comisión.
</div>
{% endif %}
{% if can_edit %}
<div class="border-top pt-3 mt-2">
<h6 class="small fw-bold text-muted text-uppercase mb-2">
<i class="bi bi-person-plus me-1"></i>Agregar Docente a la Cátedra
</h6>
<form method="POST" action="/admin/commission_add_teacher" id="add-teacher-form">
<input type="hidden" name="id" value="{{ commission.id }}">
<div class="mb-2">
<label class="form-label small fw-semibold mb-1">Docente</label>
<select name="teacher_id" class="form-select form-select-sm" required id="teacher-select">
<option value="">— Seleccionar Docente —</option>
{% for teacher in teachers %}
<option value="{{ teacher.id }}">{{ teacher.name }}</option>
{% endfor %}
</select>
</div>
<div class="mb-3">
<label class="form-label small fw-semibold mb-1">Rol en la Cátedra</label>
<select name="role" class="form-select form-select-sm" required>
<option value="Titular">Profesor Titular</option>
<option value="Adjunto" selected>Profesor Adjunto</option>
<option value="JTP">Jefe de Trabajos Prácticos (JTP)</option>
<option value="Ayudante">Ayudante Diplomado / Alumno</option>
</select>
</div>
<button type="submit" class="btn btn-primary btn-sm w-100" id="assign-teacher-btn">
<i class="bi bi-plus-circle me-1"></i>Asignar a Comisión
</button>
</form>
</div>
{% endif %}
</div>
</div>
</div>
<!-- Right: Students -->
<div class="col-12 col-lg-8">
<!-- Enrolled Students -->
<div class="card shadow-sm border-0 mb-4">
<div class="card-header border-0 bg-transparent py-2 d-flex align-items-center justify-content-between">
<div class="d-flex align-items-center gap-2">
<i class="bi bi-mortarboard text-success"></i>
<span class="fw-semibold">Alumnos Inscriptos</span>
<span class="badge bg-success-subtle text-success">{{ enrollments | length }}</span>
</div>
{% if can_edit %}
<button class="btn btn-sm btn-success" data-bs-toggle="collapse" data-bs-target="#enroll-form" id="add-student-btn">
<i class="bi bi-person-plus me-1"></i>Inscribir Alumno
</button>
{% endif %}
</div>
<!-- Enroll Student Form (collapsible) -->
{% if can_edit %}
<div class="collapse" id="enroll-form">
<div class="card-body border-top bg-success-subtle py-3">
<form method="POST" action="/admin/commission_enroll_student" id="enroll-student-form">
<input type="hidden" name="id" value="{{ commission.id }}">
<div class="row g-2 align-items-end">
<div class="col-12 col-md-6">
<label class="form-label small fw-semibold mb-1">Seleccionar Alumno</label>
<select name="student_id" class="form-select form-select-sm" required id="student-select">
<option value="">— Elegir alumno —</option>
{% for student in students %}
{% if student.id not in enrolled_ids %}
<option value="{{ student.id }}">{{ student.name }} — {{ student.email }}</option>
{% endif %}
{% endfor %}
</select>
</div>
<div class="col-12 col-md-4">
<label class="form-label small fw-semibold mb-1">Observaciones (opcional)</label>
<input type="text" name="notes" class="form-control form-control-sm" placeholder="Notas…" id="enroll-notes">
</div>
<div class="col-12 col-md-2">
<button type="submit" class="btn btn-success btn-sm w-100" id="enroll-submit-btn">
<i class="bi bi-plus-circle me-1"></i>Inscribir
</button>
</div>
<!-- Regla de Restricción Diaria - Excepción de Bedelía (Fase 2 MVP) -->
<div class="col-12 mt-2 pt-2 border-top border-success-subtle">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" name="allow_same_day_exception" value="1" id="sameDayCheck"
onchange="document.getElementById('reasonRow').classList.toggle('d-none', !this.checked)">
<label class="form-check-label small fw-semibold text-success-emphasis" for="sameDayCheck">
<i class="bi bi-shield-check me-1 text-primary"></i>Autorizar excepción de cursada en el mismo día (Bedelía)
</label>
</div>
</div>
<div class="col-12 mt-1 d-none" id="reasonRow">
<input type="text" name="exception_reason" class="form-control form-control-sm border-warning"
placeholder="Motivo o expediente de excepción de Bedelía (ej: Autorizado por Bedelía Central)..." id="exception-reason-input">
</div>
</div>
</form>
</div>
</div>
{% endif %}
<div class="card-body p-0">
{% if enrollments %}
<div class="table-responsive">
<table class="table table-hover mb-0 align-middle" id="enrollments-table">
<thead class="table-light">
<tr>
<th class="ps-3">#</th>
<th>Alumno</th>
<th class="text-center">Estado</th>
<th class="text-center">Fecha Inscripción</th>
<th class="text-center">Excepciones</th>
<th class="text-center">Notas</th>
{% if can_edit %}<th class="text-end pe-3">Acciones</th>{% endif %}
</tr>
</thead>
<tbody>
{% for e in enrollments %}
<tr id="enrollment-row-{{ e.student_id }}">
<td class="ps-3 text-muted small">{{ loop.index }}</td>
<td>
<div class="fw-semibold small">{{ e.student.name if e.student else '—' }}</div>
<div class="text-muted" style="font-size:.75rem;">{{ e.student.email if e.student else '' }}</div>
</td>
<td class="text-center">
{% set st_colors = {'activo': 'success', 'retirado': 'danger', 'condicional': 'warning'} %}
<span class="badge bg-{{ st_colors[e.status] or 'secondary' }}-subtle text-{{ st_colors[e.status] or 'secondary' }} border border-{{ st_colors[e.status] or 'secondary' }}-subtle">
{{ e.status | capitalize }}
</span>
</td>
<td class="text-center small text-muted">
{{ e.enrolled_at if e.enrolled_at else '—' }}
</td>
<td class="text-center small">
{% if e.allow_same_day_exception %}
<span class="badge bg-warning-subtle text-warning border border-warning-subtle py-1 px-2" title="{{ e.exception_reason or 'Excepción autorizada de Bedelía' }}">
<i class="bi bi-shield-check me-1"></i>Excepción Diaria
</span>
{% else %}
<span class="text-muted opacity-50">—</span>
{% endif %}
</td>
<td class="text-center small text-muted">{{ e.notes or '—' }}</td>
{% if can_edit %}
<td class="text-end pe-3">
<div class="d-flex gap-1 justify-content-end">
<!-- Quick status update -->
<form method="POST" action="/admin/commission_update_enrollment" class="d-inline">
<input type="hidden" name="id" value="{{ commission.id }}">
<input type="hidden" name="student_id" value="{{ e.student_id }}">
<select name="status" class="form-select form-select-sm d-inline-block w-auto"
onchange="this.form.submit()" title="Cambiar estado"
id="status-select-{{ e.student_id }}">
<option value="activo" {% if e.status == 'activo' %}selected{% endif %}>Activo</option>
<option value="condicional" {% if e.status == 'condicional' %}selected{% endif %}>Condicional</option>
<option value="retirado" {% if e.status == 'retirado' %}selected{% endif %}>Retirado</option>
</select>
</form>
<!-- Remove button -->
{% set st_display = e.student.name if e.student else 'este alumno' %}
<form method="POST" action="/admin/commission_unenroll_student"
onsubmit="return confirm('¿Dar de baja a {{ st_display }} de la comisión?')">
<input type="hidden" name="id" value="{{ commission.id }}">
<input type="hidden" name="student_id" value="{{ e.student_id }}">
<button type="submit" class="btn btn-sm btn-outline-danger" title="Dar de baja"
id="unenroll-btn-{{ e.student_id }}">
<i class="bi bi-person-dash"></i>
</button>
</form>
</div>
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="text-center py-5">
<i class="bi bi-people display-5 text-muted opacity-40"></i>
<p class="text-muted mt-3 small">No hay alumnos inscriptos en esta comisión.</p>
{% if can_edit %}
<button class="btn btn-outline-success btn-sm" data-bs-toggle="collapse" data-bs-target="#enroll-form">
<i class="bi bi-person-plus me-1"></i>Inscribir primer alumno
</button>
{% endif %}
</div>
{% endif %}
</div>
</div>
<!-- Capacity Bar -->
{% set enrolled_count = enrollments | length %}
{% set max_students = commission.max_students or 1 %}
{% set cap_pct = [((enrolled_count / max_students) * 100) | round | int, 100] | min %}
<div class="card shadow-sm border-0">
<div class="card-body py-3">
<div class="d-flex justify-content-between small fw-semibold mb-1">
<span><i class="bi bi-bar-chart-fill me-1 text-primary"></i>Ocupación del cupo</span>
<span>{{ enrolled_count }} / {{ max_students }} ({{ cap_pct }}%)</span>
</div>
<div class="progress" style="height:10px;">
<div class="progress-bar {% if cap_pct >= 90 %}bg-danger{% elif cap_pct >= 70 %}bg-warning{% else %}bg-success{% endif %}"
{{ ("style=\"width:" ~ cap_pct ~ "%;\"") | safe }} role="progressbar"
aria-valuenow="{{ cap_pct }}" aria-valuemin="0" aria-valuemax="100">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Modal: Editar Datos de la Comisión -->
{% if can_edit %}
<div class="modal fade" id="modalEditCommissionDetail" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content border-0 shadow">
<form action="/admin/commission_edit" method="POST">
<input type="hidden" name="id" value="{{ commission.id }}">
<div class="modal-header bg-body-tertiary border-bottom py-3">
<h5 class="modal-title fw-bold">
<i class="bi bi-pencil-square text-primary me-2"></i>Editar Datos de la Comisión
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body p-4">
<div class="row g-3 mb-3">
<div class="col-md-6">
<label class="form-label fw-semibold small">Identificador / Código</label>
<input type="text" class="form-control form-control-sm font-monospace" name="code" value="{{ commission.code }}" required>
</div>
<div class="col-md-3">
<label class="form-label fw-semibold small">Cuatrimestre</label>
<select class="form-select form-select-sm" name="semester">
<option value="1C" {% if commission.semester == '1C' %}selected{% endif %}>1C (Primer Cuatrimestre)</option>
<option value="2C" {% if commission.semester == '2C' %}selected{% endif %}>2C (Segundo Cuatrimestre)</option>
<option value="Anual" {% if commission.semester == 'Anual' %}selected{% endif %}>Anual</option>
</select>
</div>
<div class="col-md-3">
<label class="form-label fw-semibold small">Año</label>
<input type="number" class="form-control form-control-sm" name="year" value="{{ commission.year }}" min="2020" max="2035">
</div>
</div>
<div class="row g-3 mb-3">
<div class="col-md-6">
<label class="form-label fw-semibold small">Turno</label>
<select class="form-select form-select-sm" name="shift">
<option value="Mañana" {% if commission.shift == 'Mañana' %}selected{% endif %}>Mañana (08:00 - 12:00)</option>
<option value="Tarde" {% if commission.shift == 'Tarde' %}selected{% endif %}>Tarde (17:30 - 21:30)</option>
<option value="Vespertino" {% if commission.shift == 'Vespertino' %}selected{% endif %}>Vespertino</option>
<option value="Noche" {% if commission.shift == 'Noche' %}selected{% endif %}>Noche (19:00 - 23:00)</option>
</select>
</div>
<div class="col-md-6">
<label class="form-label fw-semibold small">Cupo Máximo de Alumnos</label>
<input type="number" class="form-control form-control-sm" name="max_students" value="{{ commission.max_students }}" min="1" max="300">
</div>
</div>
<div class="mb-3">
<label class="form-label fw-semibold small">Días y Horarios</label>
<input type="text" class="form-control form-control-sm" name="schedule" value="{{ commission.schedule or '' }}" placeholder="Ej: Lun y Mié 17:30 a 21:30 hs">
</div>
<div class="mb-3">
<label class="form-label fw-semibold small">Enlace Aula Virtual (Meet, Teams, Zoom)</label>
<input type="url" class="form-control form-control-sm" name="virtual_link" value="{{ commission.virtual_link or '' }}">
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="active" id="detail_edit_active" value="on" {% if commission.active %}checked{% endif %}>
<label class="form-check-label small fw-semibold" for="detail_edit_active">
Comisión Activa
</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">Guardar Cambios</button>
</div>
</form>
</div>
</div>
</div>
{% endif %}
{% endblock %}