From 6e10913380aa4692fc9b72b948c4af79ec0faab5 Mon Sep 17 00:00:00 2001 From: Mitchell R Date: Thu, 21 May 2026 15:12:55 +0200 Subject: [PATCH] fix(admin): cell edit no longer corrupts grid when spans change Use hx-retarget/hx-reswap response headers to replace full grid when cell dimensions change (overlap resolution may move other cells). Single-cell swap when only content fields change. --- .../service-admin-http/routes-admin.ts | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/server/src/plugins/service-admin-http/routes-admin.ts b/server/src/plugins/service-admin-http/routes-admin.ts index f2f7cfa..e907457 100644 --- a/server/src/plugins/service-admin-http/routes-admin.ts +++ b/server/src/plugins/service-admin-http/routes-admin.ts @@ -1083,9 +1083,11 @@ export function registerAdminRoutes(app: H3, deps: AdminDeps): void { if (rowSpanRaw != null && String(rowSpanRaw).trim() !== "") { dimsPatch["row_span"] = Math.max(1, Number(rowSpanRaw) || 1); } + let spansChanged = false; if (Object.keys(dimsPatch).length > 0) { deps.repo.updateLayoutCell(cellId, dimsPatch as any); if ("col_span" in dimsPatch || "row_span" in dimsPatch) { + spansChanged = true; const axis = "col_span" in dimsPatch ? "col" as const : "row" as const; resolveOverlaps(deps, layoutId, cellId, axis); } @@ -1093,10 +1095,24 @@ export function registerAdminRoutes(app: H3, deps: AdminDeps): void { notifyKiosks(); if (isHtmxRequest(event)) { - const cells = deps.repo.layoutCells(layoutId); + if (spansChanged) { + const cells = deps.repo.layoutCells(layoutId); + const cameras = deps.repo.listCameras(); + const entities = deps.repo.listEntities(); + const body = String(renderGrid(layoutId, cells, entities, cameras)); + return new Response(body, { + headers: { + "content-type": "text/html; charset=utf-8", + "hx-retarget": "#layout-grid", + "hx-reswap": "innerHTML", + }, + }); + } + const cell = deps.repo.getLayoutCellById(cellId); + if (!cell) return new Response("", { headers: { "content-type": "text/html; charset=utf-8" } }); const cameras = deps.repo.listCameras(); const entities = deps.repo.listEntities(); - return htmlFragment(renderGrid(layoutId, cells, entities, cameras)); + return htmlFragment(renderCell(layoutId, cell, entities, cameras, "read")); } return new Response(null, { status: 302, headers: { location: `/admin/layouts/${layoutId}` } }); });