CreaRack-SL

Versión 1.51.0 — Dashboard 100% reordenable (Rooms y No Map incluidas)

Funcionalidadactivecreado Mon Jul 13#dashboard#ui#drag-drop#refactor#ux#v1.51.0

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/reorder con 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 claves dashboard_rooms_sort / dashboard_nomap_sort).
  • 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:

  1. Rooms (si hay)
  2. Blueprints (por sort_order, alfabético si no tienen)
  3. 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, actualiza sort_order para 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 condicionales section.kind == 'rooms', 'bp', 'nomap'.
  • Rooms, blueprints y No Map ahora todos tienen data-section-key y cabecera con draggable="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/reorder con 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/reorder cambió 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 es context["sections"] la lista tipada.
  • El partial HTMX también recibe sections, no racks.

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]]