feat: implement Bedelía dashboard panels and Phase 1 Automated Security Audit
This commit is contained in:
@@ -633,6 +633,254 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ========================= Panel: Comisiones Sin Reserva (Semana/Mes) ========================= -->
|
||||
<div class="card border-0 shadow-sm mb-4" id="bedelia-unassigned-commissions">
|
||||
<div class="card-header bg-body-tertiary border-0 py-3 d-flex justify-content-between align-items-center flex-wrap gap-2">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<i class="bi bi-calendar-x-fill text-danger fs-5"></i>
|
||||
<div>
|
||||
<h5 class="mb-0 h6 fw-bold">Comisiones Sin Reserva de Aula</h5>
|
||||
<small class="text-muted">Comisiones activas sin clase reservada en el período seleccionado</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex gap-2 align-items-center flex-wrap">
|
||||
<div class="btn-group btn-group-sm" role="group" id="commPeriodSwitcher">
|
||||
<button type="button" class="btn btn-outline-danger active" data-period="week" onclick="switchCommPeriod('week')">
|
||||
<i class="bi bi-calendar-week me-1"></i>Esta Semana
|
||||
<span class="badge bg-danger ms-1" id="commWeekBadge">{{ role_data.unassigned_commissions_week | length }}</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-danger" data-period="month" onclick="switchCommPeriod('month')">
|
||||
<i class="bi bi-calendar-month me-1"></i>Este Mes
|
||||
<span class="badge bg-danger ms-1" id="commMonthBadge">{{ role_data.unassigned_commissions_month | length }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<a href="/admin/commissions_list" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="bi bi-list-ul me-1"></i>Ver Todas
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<!-- Tabla Semana -->
|
||||
<div id="commTableWeek">
|
||||
{% if role_data.unassigned_commissions_week and role_data.unassigned_commissions_week | length > 0 %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0 small">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th class="ps-3">Código</th>
|
||||
<th>Materia</th>
|
||||
<th>Docente</th>
|
||||
<th>Horario Programado</th>
|
||||
<th>Turno</th>
|
||||
<th>Cupo</th>
|
||||
<th class="text-end pe-3">Acción</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for comm in role_data.unassigned_commissions_week %}
|
||||
<tr>
|
||||
<td class="ps-3">
|
||||
<span class="badge bg-danger-subtle text-danger border font-monospace">{{ comm.full_code }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="fw-semibold text-body">{{ comm.subject_name }}</div>
|
||||
<small class="text-muted font-monospace">{{ comm.subject_code }}</small>
|
||||
</td>
|
||||
<td class="small">{{ comm.teacher_name }}</td>
|
||||
<td>
|
||||
<span class="badge bg-body-tertiary text-body border"><i class="bi bi-clock me-1"></i>{{ comm.schedule }}</span>
|
||||
</td>
|
||||
<td><span class="badge bg-info-subtle text-info-emphasis border">{{ comm.shift }}</span></td>
|
||||
<td class="font-monospace">{{ comm.current_students }}/{{ comm.max_students }}</td>
|
||||
<td class="text-end pe-3">
|
||||
<a href="/schedule/add_reservation?commission_id={{ comm.id }}" class="btn btn-sm btn-outline-success py-0 px-2" title="Asignar aula a esta comisión">
|
||||
<i class="bi bi-plus-circle me-1"></i>Asignar
|
||||
</a>
|
||||
<a href="/admin/commission_detail?id={{ comm.id }}" class="btn btn-sm btn-outline-primary py-0 px-2 ms-1" title="Ver detalle">
|
||||
<i class="bi bi-eye me-1"></i>Detalle
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-center py-4">
|
||||
<i class="bi bi-check-circle-fill text-success fs-1 d-block mb-2"></i>
|
||||
<h6 class="text-success fw-bold">¡Todas las comisiones tienen reserva esta semana!</h6>
|
||||
<p class="text-muted small mb-0">No hay comisiones activas sin aula reservada en la semana actual.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Tabla Mes -->
|
||||
<div id="commTableMonth" style="display: none;">
|
||||
{% if role_data.unassigned_commissions_month and role_data.unassigned_commissions_month | length > 0 %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0 small">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th class="ps-3">Código</th>
|
||||
<th>Materia</th>
|
||||
<th>Docente</th>
|
||||
<th>Horario Programado</th>
|
||||
<th>Turno</th>
|
||||
<th>Cupo</th>
|
||||
<th class="text-end pe-3">Acción</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for comm in role_data.unassigned_commissions_month %}
|
||||
<tr>
|
||||
<td class="ps-3">
|
||||
<span class="badge bg-danger-subtle text-danger border font-monospace">{{ comm.full_code }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="fw-semibold text-body">{{ comm.subject_name }}</div>
|
||||
<small class="text-muted font-monospace">{{ comm.subject_code }}</small>
|
||||
</td>
|
||||
<td class="small">{{ comm.teacher_name }}</td>
|
||||
<td>
|
||||
<span class="badge bg-body-tertiary text-body border"><i class="bi bi-clock me-1"></i>{{ comm.schedule }}</span>
|
||||
</td>
|
||||
<td><span class="badge bg-info-subtle text-info-emphasis border">{{ comm.shift }}</span></td>
|
||||
<td class="font-monospace">{{ comm.current_students }}/{{ comm.max_students }}</td>
|
||||
<td class="text-end pe-3">
|
||||
<a href="/schedule/add_reservation?commission_id={{ comm.id }}" class="btn btn-sm btn-outline-success py-0 px-2" title="Asignar aula a esta comisión">
|
||||
<i class="bi bi-plus-circle me-1"></i>Asignar
|
||||
</a>
|
||||
<a href="/admin/commission_detail?id={{ comm.id }}" class="btn btn-sm btn-outline-primary py-0 px-2 ms-1" title="Ver detalle">
|
||||
<i class="bi bi-eye me-1"></i>Detalle
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-center py-4">
|
||||
<i class="bi bi-check-circle-fill text-success fs-1 d-block mb-2"></i>
|
||||
<h6 class="text-success fw-bold">¡Todas las comisiones tienen reserva este mes!</h6>
|
||||
<p class="text-muted small mb-0">No hay comisiones activas sin aula reservada en el mes actual.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ========================= Panel: Aulas Disponibles (Semana/Mes) ========================= -->
|
||||
<div class="card border-0 shadow-sm mb-4" id="bedelia-available-classrooms">
|
||||
<div class="card-header bg-body-tertiary border-0 py-3 d-flex justify-content-between align-items-center flex-wrap gap-2">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<i class="bi bi-door-open-fill text-success fs-5"></i>
|
||||
<div>
|
||||
<h5 class="mb-0 h6 fw-bold">Aulas Disponibles (Sin Reservas)</h5>
|
||||
<small class="text-muted">Espacios sin ninguna reserva cargada en el período seleccionado</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex gap-2 align-items-center flex-wrap">
|
||||
<div class="btn-group btn-group-sm" role="group" id="classroomPeriodSwitcher">
|
||||
<button type="button" class="btn btn-outline-success active" data-period="week" onclick="switchClassroomPeriod('week')">
|
||||
<i class="bi bi-calendar-week me-1"></i>Esta Semana
|
||||
<span class="badge bg-success ms-1" id="classWeekBadge">{{ role_data.available_classrooms_week | length }}</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-success" data-period="month" onclick="switchClassroomPeriod('month')">
|
||||
<i class="bi bi-calendar-month me-1"></i>Este Mes
|
||||
<span class="badge bg-success ms-1" id="classMonthBadge">{{ role_data.available_classrooms_month | length }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<a href="/classrooms/list_classrooms" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="bi bi-list-ul me-1"></i>Catálogo Completo
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-3">
|
||||
<!-- Vista Semana -->
|
||||
<div id="classroomGridWeek">
|
||||
{% if role_data.available_classrooms_week and role_data.available_classrooms_week | length > 0 %}
|
||||
<div class="row g-2">
|
||||
{% for aula in role_data.available_classrooms_week %}
|
||||
<div class="col-sm-6 col-md-4 col-lg-3">
|
||||
<a href="/schedule/add_reservation?classroom_id={{ aula.id }}" class="text-decoration-none text-reset d-block h-100">
|
||||
<div class="p-3 rounded border h-100 transition-all {% if aula.is_virtual %}bg-info bg-opacity-10 border-info-subtle{% else %}bg-success bg-opacity-10 border-success-subtle{% endif %}">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<span class="badge {% if aula.is_virtual %}bg-info-subtle text-info{% else %}bg-success-subtle text-success{% endif %} border font-monospace fw-bold">
|
||||
{{ aula.code }}
|
||||
</span>
|
||||
{% if aula.is_virtual %}
|
||||
<i class="bi bi-camera-video text-info"></i>
|
||||
{% else %}
|
||||
<i class="bi bi-door-open text-success"></i>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="small text-muted mb-1">
|
||||
<i class="bi bi-building me-1"></i>{{ aula.building }}{% if aula.floor %} · {{ aula.floor }}{% endif %}
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="small fw-semibold text-body">
|
||||
<i class="bi bi-people-fill me-1"></i>Cap: {{ aula.capacity }}
|
||||
</span>
|
||||
<span class="badge bg-success text-white small"><i class="bi bi-plus-circle me-1"></i>Asignar</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-center py-4">
|
||||
<i class="bi bi-calendar-check-fill text-primary fs-1 d-block mb-2"></i>
|
||||
<h6 class="text-primary fw-bold">¡Todas las aulas están siendo utilizadas esta semana!</h6>
|
||||
<p class="text-muted small mb-0">No hay aulas sin reservas activas en la semana actual.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Vista Mes -->
|
||||
<div id="classroomGridMonth" style="display: none;">
|
||||
{% if role_data.available_classrooms_month and role_data.available_classrooms_month | length > 0 %}
|
||||
<div class="row g-2">
|
||||
{% for aula in role_data.available_classrooms_month %}
|
||||
<div class="col-sm-6 col-md-4 col-lg-3">
|
||||
<a href="/schedule/add_reservation?classroom_id={{ aula.id }}" class="text-decoration-none text-reset d-block h-100">
|
||||
<div class="p-3 rounded border h-100 transition-all {% if aula.is_virtual %}bg-info bg-opacity-10 border-info-subtle{% else %}bg-success bg-opacity-10 border-success-subtle{% endif %}">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<span class="badge {% if aula.is_virtual %}bg-info-subtle text-info{% else %}bg-success-subtle text-success{% endif %} border font-monospace fw-bold">
|
||||
{{ aula.code }}
|
||||
</span>
|
||||
{% if aula.is_virtual %}
|
||||
<i class="bi bi-camera-video text-info"></i>
|
||||
{% else %}
|
||||
<i class="bi bi-door-open text-success"></i>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="small text-muted mb-1">
|
||||
<i class="bi bi-building me-1"></i>{{ aula.building }}{% if aula.floor %} · {{ aula.floor }}{% endif %}
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<span class="small fw-semibold text-body">
|
||||
<i class="bi bi-people-fill me-1"></i>Cap: {{ aula.capacity }}
|
||||
</span>
|
||||
<span class="badge bg-success text-white small"><i class="bi bi-plus-circle me-1"></i>Asignar</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-center py-4">
|
||||
<i class="bi bi-calendar-check-fill text-primary fs-1 d-block mb-2"></i>
|
||||
<h6 class="text-primary fw-bold">¡Todas las aulas están siendo utilizadas este mes!</h6>
|
||||
<p class="text-muted small mb-0">No hay aulas sin reservas activas en el mes actual.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bedelía Operational Launchpad -->
|
||||
<div class="card border-0 shadow-sm mb-4">
|
||||
<div class="card-header bg-body-tertiary border-0 py-3">
|
||||
@@ -1314,5 +1562,22 @@ document.querySelectorAll('[data-color]').forEach(function(el) {
|
||||
const color = el.getAttribute('data-color');
|
||||
if (color) el.style.setProperty('background-color', color, 'important');
|
||||
});
|
||||
|
||||
// Bedelía: Switchers de período para paneles de Comisiones y Aulas
|
||||
function switchCommPeriod(period) {
|
||||
document.getElementById('commTableWeek').style.display = period === 'week' ? 'block' : 'none';
|
||||
document.getElementById('commTableMonth').style.display = period === 'month' ? 'block' : 'none';
|
||||
document.querySelectorAll('#commPeriodSwitcher button').forEach(function(btn) {
|
||||
btn.classList.toggle('active', btn.getAttribute('data-period') === period);
|
||||
});
|
||||
}
|
||||
|
||||
function switchClassroomPeriod(period) {
|
||||
document.getElementById('classroomGridWeek').style.display = period === 'week' ? 'block' : 'none';
|
||||
document.getElementById('classroomGridMonth').style.display = period === 'month' ? 'block' : 'none';
|
||||
document.querySelectorAll('#classroomPeriodSwitcher button').forEach(function(btn) {
|
||||
btn.classList.toggle('active', btn.getAttribute('data-period') === period);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user