feat: fecha y horario en aula, fix division by zero, soporte enlace virtual y mejora contrastes comisiones

This commit is contained in:
Carlos Tello
2026-09-03 10:12:25 -03:00
parent 5f0476be79
commit cde62c9b0a
10 changed files with 802 additions and 70 deletions
+85 -5
View File
@@ -152,7 +152,7 @@
</div>
<!-- 5. Notas -->
<div class="mb-4">
<div class="mb-3">
{{ form.notes.label(class="form-label fw-semibold") }}
{{ form.notes(class="form-control", rows="2", placeholder=_("Cualquier nota adicional o requerimientos especiales...")) }}
{% if form.notes.errors %}
@@ -161,8 +161,24 @@
</div>
{% endif %}
</div>
<!-- 6. Enlace de Aula Virtual (Zoom, Meet, Teams, Moodle) -->
<div class="mb-4" id="virtual-link-container">
<label for="virtual_link" class="form-label fw-semibold">
<i class="bi bi-camera-video text-primary me-1"></i>{% trans %}Enlace de Aula Virtual (Zoom, Google Meet, Teams, Moodle){% endtrans %}
</label>
{{ form.virtual_link(class="form-control", id="virtual_link", placeholder="https://zoom.us/j/... o https://meet.google.com/...") }}
{% if form.virtual_link.errors %}
<div class="text-danger small mt-1">
{{ form.virtual_link.errors[0] }}
</div>
{% endif %}
<div class="form-text small text-muted">
<i class="bi bi-info-circle me-1"></i>{% trans %}Obligatorio para Campus Virtual o clases remotas/híbridas. Se autocompleta con el enlace de la comisión seleccionada.{% endtrans %}
</div>
</div>
<!-- 6. TARJETA DE RESUMEN DESGLOSADA: Edificio, Piso, Aula -->
<!-- 7. TARJETA DE RESUMEN DESGLOSADA: Edificio, Piso, Aula, Modalidad -->
<div class="card bg-body-tertiary border-0 shadow-xs mb-4" id="reservation-summary">
<div class="card-body p-3">
<h6 class="card-subtitle mb-2 text-muted small fw-bold">
@@ -191,6 +207,21 @@
</div>
</div>
<!-- Fila de Enlace Virtual -->
<div class="row g-2 mb-3" id="summary-virtual-row">
<div class="col-12">
<div class="p-2 rounded bg-body border d-flex align-items-center justify-content-between">
<div class="text-truncate me-2">
<small class="text-muted d-block">🎥 {% trans %}Enlace Virtual (Videoconferencia){% endtrans %}</small>
<span id="summary-virtual-link" class="small font-monospace text-primary text-break">{% trans %}Sin enlace configurado{% endtrans %}</span>
</div>
<span class="badge bg-success-subtle text-success border" id="summary-virtual-badge" style="display:none;">
<i class="bi bi-camera-video me-1"></i>Virtual
</span>
</div>
</div>
</div>
<!-- Duración y Comisión -->
<div class="row g-2 small">
<div class="col-md-4">
@@ -243,6 +274,19 @@
}
</script>
<script type="application/json" id="commission-data">
{
{% for c in commission_list %}
"{{ c.id }}": {
"id": {{ c.id }},
"code": {{ c.code|tojson }},
"virtual_link": {{ (c.virtual_link or '')|tojson }},
"max_students": {{ c.max_students }}
}{% if not loop.last %},{% endif %}
{% endfor %}
}
</script>
<script type="application/json" id="i18n-strings">
{
"not_selected": "{% trans %}No seleccionada{% endtrans %}",
@@ -253,7 +297,8 @@
"minute": "{% trans %}minuto{% endtrans %}",
"minutes": "{% trans %}minutos{% endtrans %}",
"capacity": "{% trans %}Capacidad{% endtrans %}",
"capacity_exceeded": "{% trans %}Los asistentes esperados superan la capacidad del aula{% endtrans %}"
"capacity_exceeded": "{% trans %}Los asistentes esperados superan la capacidad del aula{% endtrans %}",
"no_virtual_link": "{% trans %}Sin enlace configurado (Clase presencial){% endtrans %}"
}
</script>
@@ -267,6 +312,14 @@ $(document).ready(function() {
console.error('Error parsing classroomData:', err);
}
let commissionData = {};
try {
const rawComm = document.getElementById('commission-data')?.textContent;
if (rawComm) commissionData = JSON.parse(rawComm);
} catch (err) {
console.error('Error parsing commissionData:', err);
}
let i18n = {};
try {
const rawI18n = document.getElementById('i18n-strings')?.textContent;
@@ -457,13 +510,28 @@ $(document).ready(function() {
$('#summary-classroom').text(i18n.not_selected || 'No seleccionada');
}
// Update commission summary
// Update commission summary and autofill virtual link if available
if (commissionId) {
const commissionText = $('#commission-select option:selected').text();
$('#summary-commission').text(commissionText);
} else {
$('#summary-commission').text(i18n.not_selected || 'No seleccionada');
}
// Update virtual link in summary
const vLink = ($('#virtual_link').val() || '').trim();
const isVirtual = (classroomId && classroomData[classroomId] && classroomData[classroomId].is_virtual);
if (vLink) {
$('#summary-virtual-link').text(vLink);
$('#summary-virtual-badge').show();
} else if (isVirtual) {
$('#summary-virtual-link').html('<span class="text-warning fw-semibold"><i class="bi bi-exclamation-triangle me-1"></i>Campus Virtual (Recomendado agregar enlace Zoom/Meet)</span>');
$('#summary-virtual-badge').hide();
} else {
$('#summary-virtual-link').text(i18n.no_virtual_link || 'Sin enlace configurado (Clase presencial)');
$('#summary-virtual-badge').hide();
}
// Calculate duration
if (startTime && endTime) {
@@ -526,7 +594,19 @@ $(document).ready(function() {
}
}
$('#commission-select, #start-time, #end-time').on('change', updateSummary);
$('#commission-select').on('change', function() {
const cId = $(this).val();
if (cId && commissionData[cId]) {
const comm = commissionData[cId];
if (comm.virtual_link && !$('#virtual_link').val().trim()) {
$('#virtual_link').val(comm.virtual_link);
}
}
updateSummary();
});
$('#virtual_link').on('input change', updateSummary);
$('#start-time, #end-time').on('change', updateSummary);
$('#expected_attendees').on('input', function() {
checkCapacity();
updateSummary();