Resumen
Mejora iterativa de la v1.50.0 (drag&drop de blueprints). La v1.51.0 extiende el reorden manual a todas las secciones del Dashboard:
- ✅ Caja de Salas (Rooms, DCIM Fase 3)
- ✅ Cada Blueprint (Map o Row)
- ✅ Sección No Map (racks sin asignar)
Feedback: Respuesta directa a la observación de Edu: “no solo hay Maps en el dashboard — las Salas y los racks sin asignar también deberían moverse”.
Versión: v1.51.0 (2026-07-13)
Línea: ↕️ Dashboard: TODAS las secciones reordenables — Rooms y No Map incluidas
Lo que cambia
Antes (v1.50.0)
- Solo los blueprints (Maps/Rows) se podían arrastrar.
- Rooms y No Map estaban fijas: Rooms siempre primera, No Map siempre última.
- El reorden se aplicaba via
POST /api/blueprints/reordercon contrato{"blueprint_ids": [1, 2, 3]}. - Los templates usaban
{% regroup %}dobles (incómodo y poco flexible).
Después (v1.51.0)
- Todas las secciones son reordenables — arrastra la cabecera de cualquier sección (Rooms, Maps, Rows, No Map).
- El orden se persiste por organización:
- Blueprints: en
Blueprint.sort_order(como antes). - Rooms/No Map: en
OrgUIPreference(nuevas clavesdashboard_rooms_sort/dashboard_nomap_sort).
- Blueprints: en
- Contrato del endpoint evoluciona a
{"section_keys": ["rooms", "1", "nomap", "2"]}(tokens mixtos). - Refactor de render: lista única tipada de secciones (
racks/services/dashboard_sections.py), consumida por ambos flujos (render completo + partial HTMX). - Templates más limpios (adiós a
{% regroup %}internos).
Comportamiento sin reorden previo
Si una org nunca ha arrastrado las secciones, el orden sigue siendo el histórico:
- Rooms (si hay)
- Blueprints (por
sort_order, alfabético si no tienen) - No Map
El cambio solo actúa cuando el usuario lo usa (drag&drop). Retrocompatible.
Cambios técnicos
Nuevos/Modificados
racks/services/dashboard_sections.py (NEW, 86 LOC)
Servicio que centraliza la lógica de ordenamiento:
build_sections(racks_by_group, group_rollups, org_id, rooms=None)— Ordena todas las secciones según posiciones manual + defaults.int_pref(org_id, key)— Lee ui_pref como entero.section_sort_key(kind, sort_value, name)— Tupla de orden uniforme para los 3 tipos de sección.- Constantes:
ROOMS_SORT_KEY = "dashboard_rooms_sort",NOMAP_SORT_KEY = "dashboard_nomap_sort".
Consumidores: racks/views.index() y core/htmx_views.racks_list().
blueprints/api/blueprints.py — reorder_blueprints()
Endpoint actualizado:
- Contrato:
blueprint_ids: list[int]→section_keys: list[str]. - Lógica: itera los tokens, delega a
set_ui_pref()para Rooms/NoMap, actualizasort_orderpara blueprints. - Permanece transaccional y scoped a la org.
blueprints/api/schemas.py — BlueprintsReorderSchema
class BlueprintsReorderSchema(Schema):
"""Tokens en orden visual: "rooms", "<blueprint_id>", "nomap"."""
section_keys: list[str]
racks/views.py — index()
Cambio: racks (lista plana, ordenada) → sections (lista tipada de secciones) + racks_list (sin ordenar, para compatibilidad).
sections = build_sections(racks_by_group, group_rollups, org.id if org else None, rooms=rooms)
context = {"sections": sections, "racks_list": racks_list, ...}
core/htmx_views.py — racks_list()
Similares cambios: construye sections y lo pasa al template.
sections = build_sections(racks_by_group, {}, org.id if org else None)
return render(request, "htmx/racks/list.html", {"sections": sections, ...})
templates/index.html (REFACTOR, ~50 LOC)
- Adiós a
{% regroup racks by blueprint_name %}(doble loop). - Nuevo:
{% for section in sections %}con condicionalessection.kind == 'rooms','bp','nomap'. - Rooms, blueprints y No Map ahora todos tienen
data-section-keyy cabecera condraggable="true". - No Map pasa de ser una “excepción” a una sección de primera clase.
Ejemplo:
<!-- OLD v1.50.0 -->
{% regroup racks by blueprint_name as racks_by_blueprint %}
{% for bp_group in racks_by_blueprint %}
{% if bp_group.grouper %}
<div data-blueprint-id="{{ bp_group.list.0.blueprint_id }}">
{% else %}
{# No Map inline, sin draggable #}
{% endif %}
{% endfor %}
<!-- NEW v1.51.0 -->
{% for section in sections %}
{% if section.kind == 'rooms' %}
<div data-section-key="rooms" draggable="true">...
{% elif section.kind == 'bp' %}
<div data-section-key="{{ section.blueprint_id }}" draggable="true">...
{% elif section.kind == 'nomap' %}
<div data-section-key="nomap" draggable="true">...
{% endif %}
{% endfor %}
templates/htmx/racks/list.html (REFACTOR)
Similar refactor: {% regroup %} → {% for section in sections %}.
static/js/dashboard.js — Drag&drop
Cambios menores:
- Selector:
[data-blueprint-id]→[data-section-key](más genérico). - Payload:
blueprint_ids: [1, 2]→section_keys: ["rooms", "1", "nomap"]. - Comentario actualizado: “Reordenables TODAS las secciones”.
tests/api/test_blueprints_reorder.py (REFACTOR)
Pruebas expandidas:
- Reorden de blueprints (existentes).
- Persistencia de Rooms/NoMap en ui_prefs (nuevas).
- Ignora ids foráneos y borrados (existentes, confirmado).
- Viewer sin permiso (existente, confirmado).
- Helper
_pref_db()para leer prefs directo de PostgreSQL (crucial en tests, evita caché Valkey).
Versionado y dependencias
- APP_VERSION: 1.50.0 → 1.51.0
- README.md: Versión actualizada.
- CHANGELOG.md: Entrada detallada con notas de E2E.
- RELEASE_NOTES.md: Entrada en lenguaje usuario.
- Sin migraciones de BD (ui_prefs ya existía; solo nuevas claves si se arrastran).
Verificación
E2E local (Docker)
- Dashboard con 4 tipos de sección (Rooms, Map, Row, No Map).
- Drag&drop mixto: reordenar todas las secciones.
- POST
/api/blueprints/reordercon tokens["nomap", "1", "rooms", "2"]. - HTML re-renderizado exacto en ese orden → ✓
Tests
- 4/4 nuevos asserts pasan (reorden + prefs).
- 85 tests del área dashboard/racks/htmx en verde → ✓
Nota sobre “Rows no se mueven” (v1.50.0)
El reporte se reprodujo localmente: las Rows salían con draggable y data-blueprint-id correctos, indistinguibles de los Maps. Probable caché de estáticos al probar recién desplegado. Esta v1.51.0 unififica el mecanismo, eliminando cualquier excepción.
Impacto para desarrolladores
Cambio breaking para consumidores externos del endpoint (si los hay, que no los hay):
POST /api/blueprints/reordercambió de contrato.- Pero es un endpoint interno (sin docs públicas, sin consumidores conocidos).
Cambio en contexto de template (importante para sobrecargas custom):
context["racks"]pasa de ser ordenada a desordenada; ahora escontext["sections"]la lista tipada.- El partial HTMX también recibe
sections, noracks.
Migration: No requiere migración de BD.
Retrocompatibilidad
✅ Totalmente retrocompatible:
- Org sin
dashboard_rooms_sort/dashboard_nomap_sort→ defaults (Rooms primero, No Map último). - Blueprints sin
sort_order→ orden alfabético. - Cambio es opt-in: solo actúa si el usuario arrastra algo.
Próximos pasos (fuera de esta PR)
- Monitor: ¿usuarios re-usando el drag&drop en todas las secciones? (analytics en OpenObserve).
- Considerar persistir el orden también a nivel de usuario (no solo org) si hay demanda.
- Tests E2E en Playwright (smoke test del drag&drop).
Véase también
- [[entity—racks—service—dashboard-sections]]
- [[entity—blueprints—endpoint—reorder-secciones]]
- [[entity—blueprints—model—blueprint]]
- [[entity—core—service—ui-pref]]
- [[entity—blueprints—model—room]]