From 2772d1fc4d061138590ca1059876e732db3f858e Mon Sep 17 00:00:00 2001 From: ScreenTinker Date: Wed, 8 Jul 2026 13:56:18 -0500 Subject: [PATCH] fix(dashboard): liveness badge filter regression + list-view legibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two follow-ups from the alpha diagnosis: - FIX A (regression): filterDevices() compared badge TEXT to the option values 'online'/'offline', but the badge text is now "Healthy"/"Reconnecting"/"Offline" — so selecting a status filter matched nothing and emptied the dashboard. Now compares the liveness STATE via a data-liveness attribute, and the filter is upgraded to All / Healthy / Reconnecting / Offline (an admin can filter TO reconnecting devices — the point of the Degraded distinction). - FIX B (legibility): the list rendered liveness as a status-dot where healthy=green/offline=red were visually identical to the old indicator, so it didn't read as new. The list now renders the same device-status-badge PILL as device-detail (3 distinct colors; amber Reconnecting visible on the list), scoped with an is-liveness modifier so video-wall cards keep their dark "NxN wall" pill. Frontend-only. 14/14 filter+render tests; full ES-module parse clean. Co-Authored-By: Claude Opus 4.8 (1M context) --- frontend/css/main.css | 13 +++++++++++++ frontend/js/views/dashboard.js | 24 ++++++++++++++---------- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/frontend/css/main.css b/frontend/css/main.css index 7168493..4edacf1 100644 --- a/frontend/css/main.css +++ b/frontend/css/main.css @@ -421,6 +421,19 @@ body { font-size: 11px; font-weight: 500; } +/* Device cards render the reused liveness pill (.device-status-badge from device-detail), which brings + its own bg/padding/shape — so neutralize the dark wrapper for those. Wall cards keep the dark pill + above for their "NxN wall" label (they share .device-card-status but have no .is-liveness). */ +.device-card-status.is-liveness { + background: none; + backdrop-filter: none; + padding: 0; + border-radius: 0; +} +/* Lift the list pill off the screenshot so it stays legible over any image. */ +.device-card-status.is-liveness .device-status-badge { + box-shadow: 0 1px 4px rgba(0,0,0,0.6); +} .device-card-select { position: absolute; diff --git a/frontend/js/views/dashboard.js b/frontend/js/views/dashboard.js index 4f88a75..8f811eb 100644 --- a/frontend/js/views/dashboard.js +++ b/frontend/js/views/dashboard.js @@ -100,8 +100,8 @@ function renderDeviceCard(device) { ${t('dashboard.no_preview')} ` } -
- ${(() => { const b = livenessBadge(device); return `${esc(b.label)}`; })()} +
+ ${(() => { const b = livenessBadge(device); return `${esc(b.label)}`; })()}
${device.status === 'provisioning' && device.pairing_code ? `
@@ -268,10 +268,11 @@ export function render(container) {
- - - + + +
@@ -291,13 +292,16 @@ export function render(container) { function filterDevices() { const search = document.getElementById('deviceSearch').value.toLowerCase(); - const status = document.getElementById('deviceFilter').value; + // Compare against the liveness STATE ('healthy'|'degraded'|'offline'), NOT the display label: + // the badge text is now "Healthy"/"Reconnecting"/"Offline", so the old text-vs-'online' compare + // matched nothing and emptied the list. data-liveness carries the state for a robust match. + const state = document.getElementById('deviceFilter').value; document.querySelectorAll('.device-card').forEach(card => { const name = card.querySelector('.device-card-name')?.textContent.toLowerCase() || ''; - const deviceStatus = card.querySelector('.device-card-status span:last-child')?.textContent || ''; + const cardState = card.querySelector('.device-card-status [data-liveness]')?.dataset.liveness || ''; const matchSearch = !search || name.includes(search); - const matchStatus = !status || deviceStatus === status; - card.style.display = (matchSearch && matchStatus) ? '' : 'none'; + const matchState = !state || cardState === state; + card.style.display = (matchSearch && matchState) ? '' : 'none'; }); } @@ -362,7 +366,7 @@ export function render(container) { const cards = document.querySelectorAll(`[data-device-id="${data.device_id}"]`); cards.forEach(card => { const statusEl = card.querySelector('.device-card-status'); - if (statusEl) statusEl.innerHTML = `${esc(b.label)}`; + if (statusEl) statusEl.innerHTML = `${esc(b.label)}`; }); };