CreaRack-SL

Reorden manual de secciones del Dashboard por drag&drop (v1.50.0)

Funcionalidadactivecreado Mon Jul 13#dashboard#ux#saas#html5-dnd#multi-tenancy#v1.50.0

Descripción

A partir de v1.50.0 (13 de julio de 2026), los usuarios pueden reordenar manualmente las secciones del Dashboard (Maps y Filas) simplemente arrastrando su cabecera hacia arriba o abajo.

El orden es por organización (decisión arquitectónica): es información del datacenter compartida por todo el equipo, no una preferencia personal. Solo quienes tienen permiso blueprints:edit pueden reordenar; los lectores ven el resultado pero no pueden moverlo.

Comportamiento

  • Arrastra cualquier sección (Maps/Rows) por su cabecera (draggable="true" en HTML5 nativo).
  • El orden se guarda al instante en el servidor (POST /api/blueprints/reorder).
  • “Rooms” (sala madre) y “No Map” (racks sin blueprint) no se mueven: son fijas al inicio y final respectivamente.
  • La acción de plegar/desplegar secciones (click en flecha) funciona igual, independiente del drag.
  • El navegador sobrevive a los swaps HTMX: delegación de eventos en document, no en elementos que pueden ser reemplazados.

Inicialización y persistencia

  • Al deploy: el orden de partida es el alfabético anterior (via migración blueprints/0010 con backfill). Los maps nuevos aparecen al final (sort_order=NULL) hasta que se reordenan.
  • Sort en dos renders:
    • racks/views.py (first render, GET /).
    • core/htmx_views.py (refresh partial HTMX, para {% regroup %}).
  • Key de sort: (no_map, sin_ordenar, sort_order, nombre_blueprint, nombre_rack) — garantiza que el orden se aplica consistentemente en ambos path.

Arquitectura técnica

Backend

Modelo: Blueprint.sort_order (IntegerField, nullable)

  • Campo por organización (FK Organization).
  • NULL = sin ordenar todavía → va al final.
  • 0..N = posición manual asignada por drag&drop.

Endpoint: POST /api/blueprints/reorder

  • Payload: BlueprintsReorderSchema { blueprint_ids: list[int] }
  • Response: { "ok": true, "count": 3 }
  • Permisos: require_perm(request, "blueprints", "edit") — reader 403.
  • Lógica:
    • select_for_update() (transacción atómica).
    • Filtra por organization=current_org, omite deleted_at != NULL (soft-delete).
    • IDs ajenos o borrados se ignoran silenciosamente (no son un error).
    • bulk_update() en un sola query.

Frontend

JavaScript: static/js/dashboard.js

  • Handlers delegados en document (dragstart, dragover, drop, dragend).
  • Dragstart: marca la sección con clase section-dragging (opacidad 0.5).
  • Dragover: reordena en vivo en el DOM (visual feedback); extrae todos los data-blueprint-id ordenados.
  • Dragend: POST /api/blueprints/reorder con la lista nueva; toast “Orden de secciones guardado” (o error); si falla, window.location.reload().

CSS: static/css/pages/dashboard.css

  • .blueprint-section-header[draggable="true"] → cursor: grab.
  • .blueprint-section.section-dragging → opacity: 0.5.

HTML: Data attributes en templates

  • data-blueprint-name="{{ bp_group.grouper }}" — clave de agrupación (regroup).
  • data-blueprint-id="{{ bp_group.list.0.blueprint_id }}" — para serializar el orden nuevo.
  • draggable="true" en cabecera.
  • title="{% trans 'Drag to reorder sections' %}" — tooltip.

Internacionalización

3 strings nuevos en catálogo ES (locale/es/LC_MESSAGES):

  1. django.po: "Drag to reorder sections" → "Arrastra para reordenar las secciones"
  2. djangojs.po: "Section order saved" → "Orden de secciones guardado"
  3. djangojs.po: "Could not save the section order" → "No se pudo guardar el orden de las secciones"

Testing

tests/api/test_blueprints_reorder.py (3 test cases, todos verdes):

  1. test_reorder_applies_index_order: verifica que los blueprint_ids se mapean a sort_order = índice.
  2. test_foreign_and_deleted_ids_ignored: confirma que IDs de otra org y blueprints borrados no afectan.
  3. test_viewer_cannot_reorder: viewer (readonly) recibe 403, sort_order no cambia.

Migración: makemigrations --check sin drift; backfill seguro por organización.

Notas de implementación

  • Sin dependencias nuevas: HTML5 DnD nativo, sin librerías externas.
  • Orden previo alfabético restaurable: si se quiere revertir a orden alfabético, reset sort_order=NULL + reload.
  • Concurrencia: select_for_update() previene race conditions; múltiples usuarios reordenando simultáneamente van en serie.

Véase también

  • [[entity—blueprints—endpoint—reorder]]
  • [[entity—blueprints—model—blueprint-sort-order]]
  • [[concept—saas—multi-tenancy]]
  • [[entity—blueprints—model—blueprint]]
  • [[entity—core—model—organization]]