From 12fe0e43eb8f2e8c8c27e6af3ab43cb248b60b36 Mon Sep 17 00:00:00 2001 From: ScreenTinker Date: Thu, 14 May 2026 21:26:58 -0500 Subject: [PATCH] fix(zones): frontend assignment-flow picker + missed devices.js zone_id projection Follow-up to 73f41c3 (server-side zone_id wiring). With this commit the zone feature is verified working end-to-end: dashboard zone picker renders correctly, zone_id saves and persists, the per-row zone dropdown reflects the saved zone after reload, and a live player run with computed-style inspection confirmed zone divs and video elements size correctly within their geometry. Frontend (device-detail.js, en.js): - Add-content modal: zone picker slot now renders in all four states (has_zones / no_layout / fetch_failed / empty_layout) instead of silently vanishing when zones.length === 0. Informational rows match form-group styling and tell the user which control to use next. Closes the gate-4 symptom where 38-of-42 devices (no layout assigned) silently dropped zone_id on every assignment. - Both /api/layouts/:id fetches (add modal, edit-path) now have !res.ok throw guards and surface failures via console.warn instead of swallowing them. The add modal additionally exposes the failure state to the user via the fetch_failed info row. - Edit-path zone dropdown: replaced brittle DOM-scraping (reading the i18n label text and matching z.id.slice(0,8) against rendered meta HTML) with a data-current-zone-id attribute stashed at row render from a.zone_id. Removes the i18n-format coupling and gives exact UUID match. - 3 new i18n keys in en.js (other locales fall back). Server (devices.js): - The GET /api/devices/:id assignments query had its own ad-hoc SELECT projection that was missed during the 73f41c3 site survey. Without pi.zone_id in this projection, loadDevice() got assignments without zone_id and the edit-path dropdown displayed "No zone" after every save+reload even though the DB had the correct value. One-line fix: add pi.zone_id, mirroring the ITEM_SELECT change in routes/assignments.js. Listed as the 8th site that 73f41c3's original survey missed; this commit closes it. Verification: - JS parse + en.js ESM load + server module load all clean. - Live SQL probe: GET /api/devices/:id projection now returns zone_id for the test rows (id=31 zone_id=z-sh-1, id=54 zone_id=z-sh-2). - Browser test by hand: zone picker renders per state, zone_id persists, reload shows saved zone, computed styles on rendered .zone divs match expected geometry. Co-Authored-By: Claude Opus 4.7 (1M context) --- frontend/js/i18n/en.js | 3 ++ frontend/js/views/device-detail.js | 68 ++++++++++++++++++++---------- server/routes/devices.js | 2 +- 3 files changed, 50 insertions(+), 23 deletions(-) diff --git a/frontend/js/i18n/en.js b/frontend/js/i18n/en.js index b2e9b40..8fc3797 100644 --- a/frontend/js/i18n/en.js +++ b/frontend/js/i18n/en.js @@ -334,6 +334,9 @@ export default { 'device.assign.modal_title': 'Add to Playlist', 'device.assign.zone_label': 'Zone', 'device.assign.zone_default': 'Default (fullscreen)', + 'device.assign.zone_no_layout': 'This device has no layout assigned. Content will play fullscreen. Pick a layout from the Layout dropdown on this device to use zones.', + 'device.assign.zone_load_failed': 'Layout zones could not be loaded. Try refreshing the page.', + 'device.assign.zone_empty_layout': 'This layout has no zones defined.', 'device.assign.duration_label': 'Display Duration (seconds, for images/widgets)', 'device.assign.tab.media': 'Media ({n})', 'device.assign.tab.widgets': 'Widgets ({n})', diff --git a/frontend/js/views/device-detail.js b/frontend/js/views/device-detail.js index c44ba51..07d4436 100644 --- a/frontend/js/views/device-detail.js +++ b/frontend/js/views/device-detail.js @@ -504,7 +504,7 @@ function renderPlaylist(assignments) {
-