feat: gestion RBAC granular, importacion sheets, filtrado de aulas virtuales y pisos, capacidad infinita y traduccion
This commit is contained in:
+147
-94
@@ -8,77 +8,105 @@
|
||||
{% endif %}
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<h1 class="h3 mb-0">
|
||||
<i class="bi bi-speedometer2 text-primary"></i> {% trans %}Dashboard{% endtrans %}
|
||||
<small class="text-muted ms-2">{% trans %}Overview{% endtrans %}</small>
|
||||
<h1 class="h3 mb-1">
|
||||
<i class="bi bi-speedometer2 text-primary me-2"></i>{% trans %}Panel de Control{% endtrans %}
|
||||
</h1>
|
||||
<p class="text-muted small mb-0">{% trans %}Resumen general de espacios, materias y reservas académicas{% endtrans %}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Statistics Cards -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h6 class="text-muted mb-2">{% trans %}Total Classrooms{% endtrans %}</h6>
|
||||
<h2 class="mb-0">{{ stats.total_classrooms }}</h2>
|
||||
<!-- Statistics Cards with Direct Shortcuts -->
|
||||
<div class="row mb-4 g-3">
|
||||
<!-- 1. Total Aulas -->
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<a href="{{ url_for('classrooms.list_classrooms') }}" class="text-decoration-none text-reset d-block h-100">
|
||||
<div class="card border-0 shadow-sm h-100 dashboard-stat-card transition-all">
|
||||
<div class="card-body p-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<span class="text-muted small fw-semibold text-uppercase">{% trans %}Total de Aulas{% endtrans %}</span>
|
||||
<h2 class="mb-0 fw-bold mt-1 text-primary">{{ stats.total_classrooms }}</h2>
|
||||
</div>
|
||||
<div class="bg-primary bg-opacity-10 rounded-circle p-3 text-primary">
|
||||
<i class="bi bi-door-open fs-3"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-primary bg-opacity-10 rounded-circle p-3">
|
||||
<i class="bi bi-door-open text-primary fs-4"></i>
|
||||
<div class="d-flex align-items-center justify-content-between pt-3 mt-2 border-top text-primary small fw-semibold">
|
||||
<span>{% trans %}Ver todas las aulas{% endtrans %}</span>
|
||||
<i class="bi bi-arrow-right"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h6 class="text-muted mb-2">{% trans %}Total Subjects{% endtrans %}</h6>
|
||||
<h2 class="mb-0">{{ stats.total_subjects }}</h2>
|
||||
<!-- 2. Total Materias -->
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<a href="{{ url_for('schedule.commissions_view') }}" class="text-decoration-none text-reset d-block h-100">
|
||||
<div class="card border-0 shadow-sm h-100 dashboard-stat-card transition-all">
|
||||
<div class="card-body p-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<span class="text-muted small fw-semibold text-uppercase">{% trans %}Total de Materias{% endtrans %}</span>
|
||||
<h2 class="mb-0 fw-bold mt-1 text-success">{{ stats.total_subjects }}</h2>
|
||||
</div>
|
||||
<div class="bg-success bg-opacity-10 rounded-circle p-3 text-success">
|
||||
<i class="bi bi-book fs-3"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-success bg-opacity-10 rounded-circle p-3">
|
||||
<i class="bi bi-book text-success fs-4"></i>
|
||||
<div class="d-flex align-items-center justify-content-between pt-3 mt-2 border-top text-success small fw-semibold">
|
||||
<span>{% trans %}Gestionar materias{% endtrans %}</span>
|
||||
<i class="bi bi-arrow-right"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h6 class="text-muted mb-2">{% trans %}Today's Reservations{% endtrans %}</h6>
|
||||
<h2 class="mb-0">{{ stats.today_reservations }}</h2>
|
||||
<!-- 3. Reservas de Hoy -->
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<a href="{{ url_for('schedule.today_schedule') }}" class="text-decoration-none text-reset d-block h-100">
|
||||
<div class="card border-0 shadow-sm h-100 dashboard-stat-card transition-all">
|
||||
<div class="card-body p-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<span class="text-muted small fw-semibold text-uppercase">{% trans %}Reservas de Hoy{% endtrans %}</span>
|
||||
<h2 class="mb-0 fw-bold mt-1 text-info">{{ stats.today_reservations }}</h2>
|
||||
</div>
|
||||
<div class="bg-info bg-opacity-10 rounded-circle p-3 text-info">
|
||||
<i class="bi bi-calendar-check fs-3"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-info bg-opacity-10 rounded-circle p-3">
|
||||
<i class="bi bi-calendar-today text-info fs-4"></i>
|
||||
<div class="d-flex align-items-center justify-content-between pt-3 mt-2 border-top text-info small fw-semibold">
|
||||
<span>{% trans %}Ver cartelera de hoy{% endtrans %}</span>
|
||||
<i class="bi bi-arrow-right"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3 mb-3">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h6 class="text-muted mb-2">{% trans %}This Week{% endtrans %}</h6>
|
||||
<h2 class="mb-0">{{ stats.week_reservations }}</h2>
|
||||
<!-- 4. Esta Semana -->
|
||||
<div class="col-md-6 col-xl-3">
|
||||
<a href="{{ url_for('schedule.calendar_view') }}" class="text-decoration-none text-reset d-block h-100">
|
||||
<div class="card border-0 shadow-sm h-100 dashboard-stat-card transition-all">
|
||||
<div class="card-body p-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<span class="text-muted small fw-semibold text-uppercase">{% trans %}Esta Semana{% endtrans %}</span>
|
||||
<h2 class="mb-0 fw-bold mt-1 text-warning">{{ stats.week_reservations }}</h2>
|
||||
</div>
|
||||
<div class="bg-warning bg-opacity-10 rounded-circle p-3 text-warning">
|
||||
<i class="bi bi-calendar-week fs-3"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-warning bg-opacity-10 rounded-circle p-3">
|
||||
<i class="bi bi-calendar-week text-warning fs-4"></i>
|
||||
<div class="d-flex align-items-center justify-content-between pt-3 mt-2 border-top text-warning small fw-semibold">
|
||||
<span>{% trans %}Abrir calendario semanal{% endtrans %}</span>
|
||||
<i class="bi bi-arrow-right"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -86,9 +114,9 @@
|
||||
<!-- Monthly Chart -->
|
||||
<div class="col-md-8 mb-4">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-header bg-white border-0">
|
||||
<h5 class="mb-0">
|
||||
<i class="bi bi-graph-up text-primary"></i> {% trans %}Monthly Reservations{% endtrans %}
|
||||
<div class="card-header border-0 py-3 bg-body-tertiary">
|
||||
<h5 class="mb-0 h6 fw-bold">
|
||||
<i class="bi bi-graph-up text-primary me-2"></i>{% trans %}Reservas Mensuales{% endtrans %}
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@@ -100,31 +128,35 @@
|
||||
<!-- Quick Actions -->
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-header bg-white border-0">
|
||||
<h5 class="mb-0">
|
||||
<i class="bi bi-lightning text-warning"></i> {% trans %}Quick Actions{% endtrans %}
|
||||
<div class="card-header border-0 py-3 bg-body-tertiary">
|
||||
<h5 class="mb-0 h6 fw-bold">
|
||||
<i class="bi bi-lightning text-warning me-2"></i>{% trans %}Accesos Rápidos{% endtrans %}
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="card-body p-3">
|
||||
<div class="d-grid gap-2">
|
||||
<a href="{{ url_for('classrooms.add_classroom') }}" class="btn btn-outline-primary">
|
||||
<i class="bi bi-plus-circle"></i> {% trans %}Add Classroom{% endtrans %}
|
||||
<a href="{{ url_for('schedule.calendar_view') }}" class="btn btn-outline-primary text-start">
|
||||
<i class="bi bi-calendar3 me-2"></i>{% trans %}Calendario de Reservas{% endtrans %}
|
||||
</a>
|
||||
<a href="{{ url_for('schedule.commissions_view') }}" class="btn btn-outline-warning">
|
||||
<i class="bi bi-people"></i> {% trans %}Manage Commissions{% endtrans %}
|
||||
<a href="{{ url_for('schedule.today_schedule') }}" class="btn btn-outline-info text-start">
|
||||
<i class="bi bi-calendar-day me-2"></i>{% trans %}Cartelera del Día{% endtrans %}
|
||||
</a>
|
||||
<a href="{{ url_for('schedule.add_reservation') }}" class="btn btn-outline-success">
|
||||
<i class="bi bi-calendar-plus"></i> {% trans %}New Reservation{% endtrans %}
|
||||
<a href="{{ url_for('classrooms.list_classrooms') }}" class="btn btn-outline-secondary text-start">
|
||||
<i class="bi bi-door-open me-2"></i>{% trans %}Listado de Aulas{% endtrans %}
|
||||
</a>
|
||||
<a href="{{ url_for('schedule.today_schedule') }}" class="btn btn-outline-info">
|
||||
<i class="bi bi-calendar-today"></i> {% trans %}Today's Schedule{% endtrans %}
|
||||
<a href="{{ url_for('buildings.list_buildings') }}" class="btn btn-outline-secondary text-start">
|
||||
<i class="bi bi-buildings me-2"></i>{% trans %}Gestión de Edificios{% endtrans %}
|
||||
</a>
|
||||
<a href="{{ url_for('genetic_algorithm_web.genetic_optimizer') }}" class="btn btn-info text-white">
|
||||
<i class="bi bi-cpu"></i> {% trans %}AI Room Optimizer{% endtrans %}
|
||||
{% if current_user.has_permission('reservations', 'read_write') %}
|
||||
<a href="{{ url_for('schedule.add_reservation') }}" class="btn btn-outline-success text-start">
|
||||
<i class="bi bi-calendar-plus me-2"></i>{% trans %}Nueva Reserva{% endtrans %}
|
||||
</a>
|
||||
<a href="{{ url_for('schedule.calendar_view') }}" class="btn btn-outline-secondary">
|
||||
<i class="bi bi-calendar3"></i> {% trans %}View Calendar{% endtrans %}
|
||||
{% endif %}
|
||||
{% if current_user.has_permission('classrooms', 'read_write') %}
|
||||
<a href="{{ url_for('classrooms.add_classroom') }}" class="btn btn-outline-primary text-start">
|
||||
<i class="bi bi-plus-circle me-2"></i>{% trans %}Crear Nueva Aula{% endtrans %}
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -135,46 +167,66 @@
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-header bg-white border-0">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">
|
||||
<i class="bi bi-clock-history text-info"></i> {% trans %}Upcoming Reservations (Next 7 Days){% endtrans %}
|
||||
</h5>
|
||||
<a href="{{ url_for('schedule.list_reservations') }}" class="btn btn-sm btn-outline-primary">
|
||||
{% trans %}View All{% endtrans %}
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-header border-0 py-3 bg-body-tertiary d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0 h6 fw-bold">
|
||||
<i class="bi bi-clock-history text-info me-2"></i>{% trans %}Próximas Reservas (Próximos 7 Días){% endtrans %}
|
||||
</h5>
|
||||
<a href="{{ url_for('schedule.calendar_view') }}" class="btn btn-sm btn-outline-primary">
|
||||
{% trans %}Ver en Calendario{% endtrans %}
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="card-body p-0">
|
||||
{% if upcoming_reservations %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="table-light small text-uppercase">
|
||||
<tr>
|
||||
<th>{% trans %}Date & Time{% endtrans %}</th>
|
||||
<th>{% trans %}Classroom{% endtrans %}</th>
|
||||
<th>{% trans %}Subject{% endtrans %}</th>
|
||||
<th>{% trans %}Teacher{% endtrans %}</th>
|
||||
<th>{% trans %}Status{% endtrans %}</th>
|
||||
<th>{% trans %}Actions{% endtrans %}</th>
|
||||
<th class="ps-3">{% trans %}Horario{% endtrans %}</th>
|
||||
<th>{% trans %}Aula{% endtrans %}</th>
|
||||
<th>{% trans %}Asignatura / Código{% endtrans %}</th>
|
||||
<th>{% trans %}Turno / Comisión{% endtrans %}</th>
|
||||
<th>{% trans %}Estado{% endtrans %}</th>
|
||||
<th class="text-end pe-3">{% trans %}Acciones{% endtrans %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for reservation in upcoming_reservations %}
|
||||
<tr>
|
||||
<td>{{ reservation.start_time.strftime('%Y-%m-%d %H:%M') }}</td>
|
||||
<td>{{ reservation.classroom.code }}</td>
|
||||
<td>{{ reservation.commission.get_full_code() }}</td>
|
||||
<td>{{ reservation.commission.teacher_name }}</td>
|
||||
<td class="ps-3">
|
||||
<div class="fw-bold font-monospace text-primary fs-6">
|
||||
<i class="bi bi-clock text-warning me-1"></i>{{ reservation.start_time.strftime('%H:%M') }} - {{ reservation.end_time.strftime('%H:%M') }} hs
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-{{ 'success' if reservation.status.value == 'CONFIRMED' else 'warning' }}">
|
||||
{{ reservation.status.value }}
|
||||
<span class="badge bg-secondary-subtle text-secondary border font-monospace">
|
||||
{{ reservation.classroom.code if reservation.classroom else 'Sin Aula' }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ url_for('schedule.list_reservations') }}"
|
||||
class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-eye"></i> {% trans %}View{% endtrans %}
|
||||
<div class="fw-semibold text-primary">{{ reservation.subject_name }}</div>
|
||||
<small class="text-muted font-monospace">{{ reservation.subject_code }}</small>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-info-subtle text-info border me-1">
|
||||
{{ reservation.shift or 'Tarde' }}
|
||||
</span>
|
||||
<small class="text-muted">{{ reservation.commission.code if reservation.commission else 'C1' }}</small>
|
||||
</td>
|
||||
<td>
|
||||
{% if reservation.status == 'CONFIRMED' or reservation.status.value == 'CONFIRMED' %}
|
||||
<span class="badge bg-success-subtle text-success border border-success-subtle">
|
||||
<i class="bi bi-check-circle me-1"></i>{% trans %}Confirmada{% endtrans %}
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="badge bg-warning-subtle text-warning border border-warning-subtle">
|
||||
<i class="bi bi-clock me-1"></i>{% trans %}Pendiente{% endtrans %}
|
||||
</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-end pe-3">
|
||||
<a href="{{ url_for('schedule.view_reservation', id=reservation.id) }}"
|
||||
class="btn btn-sm btn-outline-secondary py-0 px-2" title="{% trans %}Ver Ficha{% endtrans %}">
|
||||
<i class="bi bi-eye"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -183,9 +235,10 @@
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-center py-4">
|
||||
<i class="bi bi-calendar-x text-muted" style="font-size: 3rem;"></i>
|
||||
<p class="text-muted mt-3">{% trans %}No upcoming reservations in the next 7 days{% endtrans %}</p>
|
||||
<div class="text-center py-5">
|
||||
<i class="bi bi-calendar-x text-muted fs-1 d-block mb-2"></i>
|
||||
<h6 class="fw-bold">{% trans %}No hay reservas para los próximos 7 días{% endtrans %}</h6>
|
||||
<p class="text-muted small">{% trans %}Puedes programar nuevas reservas desde el calendario.{% endtrans %}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user