feat(ui): implementar selector de vista universal (tabla/grilla vs tarjetas/mosaico) con persistencia

This commit is contained in:
Carlos Tello
2026-09-05 01:54:55 -03:00
parent 69b8b7ccef
commit 7d342bf8cc
9 changed files with 637 additions and 141 deletions
+115 -6
View File
@@ -253,13 +253,30 @@
</div>
</div>
<!-- Grilla de Aulas Físicas -->
<!-- Barra de Resumen y Selector de Vista -->
<div class="d-flex justify-content-between align-items-center mb-3 flex-wrap gap-2">
<div class="small text-muted">
<i class="bi bi-door-open me-1"></i>
<span>{% trans %}Total:{% endtrans %} <strong class="text-body">{{ classrooms.total if classrooms else 0 }}</strong> {% trans %}aulas encontradas{% endtrans %}</span>
</div>
<div class="d-flex align-items-center gap-2">
<span class="small text-muted d-none d-sm-inline">{% trans %}Modo de visualización:{% endtrans %}</span>
{% with context_id='classrooms', default_view='cards' %}
{% include 'partials/view_switcher.html' %}
{% endwith %}
</div>
</div>
<!-- Grilla y Tabla de Aulas Físicas -->
{% if classrooms and classrooms.items %}
<div class="row g-3 mt-1">
{% for classroom in classrooms.items %}
<div class="col-xxl-3 col-xl-3 col-lg-4 col-md-6 col-sm-12 mb-2">
<div class="card classroom-card border shadow-sm h-100">
<div class="card-body p-3 d-flex flex-column justify-content-between">
<!-- 1. VISTA DE TARJETAS / MOSAICO -->
<div data-view-context="classrooms" data-view-container="cards">
<div class="row g-3 mt-1">
{% for classroom in classrooms.items %}
<div class="col-xxl-3 col-xl-3 col-lg-4 col-md-6 col-sm-12 mb-2">
<div class="card classroom-card border shadow-sm h-100">
<div class="card-body p-3 d-flex flex-column justify-content-between">
<div>
<!-- Encabezado con Código y Estado -->
<div class="d-flex justify-content-between align-items-start mb-2">
@@ -364,6 +381,98 @@
</div>
</div>
{% endfor %}
</div>
</div>
<!-- 2. VISTA DE TABLA / GRILLA (AULAS FÍSICAS) -->
<div data-view-context="classrooms" data-view-container="table" class="d-none">
<div class="card border-0 shadow-sm mt-1">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead class="table-light small text-uppercase">
<tr>
<th class="ps-3">{% trans %}Aula / Código{% endtrans %}</th>
<th>{% trans %}Edificio / Sede{% endtrans %}</th>
<th>{% trans %}Piso{% endtrans %}</th>
<th>{% trans %}Capacidad{% endtrans %}</th>
<th>{% trans %}Equipamiento{% endtrans %}</th>
<th>{% trans %}Ocupación (D / S / M){% endtrans %}</th>
<th>{% trans %}Estado{% endtrans %}</th>
<th class="text-end pe-3">{% trans %}Acciones{% endtrans %}</th>
</tr>
</thead>
<tbody>
{% for classroom in classrooms.items %}
{% set metrics = occupancy_metrics.get(classroom.id, {'day_pct': 0, 'day_count': 0, 'week_pct': 0, 'week_count': 0, 'month_pct': 0, 'month_count': 0}) %}
<tr>
<td class="ps-3">
<a href="{{ url_for('classrooms.view_classroom', id=classroom.id) }}" class="fw-bold text-primary text-decoration-none">
<i class="bi bi-door-open me-1"></i>{{ classroom.code }}
</a>
{% if classroom.description %}
<small class="text-muted d-block text-truncate" style="max-width: 200px;">{{ classroom.description }}</small>
{% endif %}
</td>
<td>
<span class="badge bg-primary-subtle text-primary border">
<i class="bi bi-buildings me-1"></i>{{ classroom.building_name }}
</span>
</td>
<td>
<span class="badge bg-secondary-subtle text-secondary border">
{{ classroom.floor_display }}
</span>
</td>
<td>
<strong class="text-body">{{ classroom.capacity }}</strong> <span class="small text-muted">{% trans %}asientos{% endtrans %}</span>
</td>
<td>
<span class="badge bg-body-tertiary text-body border">
<i class="bi bi-tools me-1"></i>{{ classroom.resources|length if classroom.resources else 0 }} {% trans %}recursos{% endtrans %}
</span>
</td>
<td>
<div class="d-flex align-items-center gap-1">
<span class="badge border {% if metrics.day_pct >= 80 %}metric-high{% elif metrics.day_pct >= 40 %}metric-medium{% else %}metric-low{% endif %}" title="{% trans %}Ocupación Día{% endtrans %}">
D: {{ metrics.day_pct }}%
</span>
<span class="badge border {% if metrics.week_pct >= 80 %}metric-high{% elif metrics.week_pct >= 40 %}metric-medium{% else %}metric-low{% endif %}" title="{% trans %}Ocupación Semana{% endtrans %}">
S: {{ metrics.week_pct }}%
</span>
<span class="badge border {% if metrics.month_pct >= 80 %}metric-high{% elif metrics.month_pct >= 40 %}metric-medium{% else %}metric-low{% endif %}" title="{% trans %}Ocupación Mes{% endtrans %}">
M: {{ metrics.month_pct }}%
</span>
</div>
</td>
<td>
<span class="badge rounded-pill {% if classroom.is_active %}bg-success-subtle text-success border border-success-subtle{% else %}bg-secondary-subtle text-secondary border{% endif %}">
{% if classroom.is_active %}{% trans %}Activa{% endtrans %}{% else %}{% trans %}Inactiva{% endtrans %}{% endif %}
</span>
</td>
<td class="text-end pe-3">
<div class="btn-group btn-group-sm">
<a href="{{ url_for('classrooms.view_classroom', id=classroom.id) }}" class="btn btn-outline-primary" title="{% trans %}Ver Detalles{% endtrans %}">
<i class="bi bi-eye"></i>
</a>
{% if current_user.has_permission('classrooms', 'read_write') %}
<a href="{{ url_for('classrooms.edit_classroom', id=classroom.id) }}" class="btn btn-outline-secondary" title="{% trans %}Editar{% endtrans %}">
<i class="bi bi-pencil"></i>
</a>
{% endif %}
<form method="POST" action="{{ url_for('classrooms.toggle_classroom', id=classroom.id) }}" class="d-inline mb-0">
<button type="submit" class="btn btn-outline-{% if classroom.is_active %}warning{% else %}success{% endif %}"
title="{% if classroom.is_active %}{% trans %}Desactivar{% endtrans %}{% else %}{% trans %}Activar{% endtrans %}{% endif %}">
<i class="bi bi-{% if classroom.is_active %}pause{% else %}play{% endif %}"></i>
</button>
</form>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<!-- Paginación Aulas Físicas -->