diff --git a/docs/CLIENT_PORTAL.md b/docs/CLIENT_PORTAL.md index 0fc2e88..c489567 100644 --- a/docs/CLIENT_PORTAL.md +++ b/docs/CLIENT_PORTAL.md @@ -118,10 +118,19 @@ live data, all from cache. page or a script for now). ### M2 — Dashboard + alerts -- Richer client dashboard (multi-location at-a-glance, map, status rollup). +- Richer client dashboard (multi-location at-a-glance, status rollup). +- **Live project map** — upgrade the overview's basic location pins into a real + project map: pins colored by measuring/level, popups showing each location's + current reading, centered/zoomed to the project. (M1 ships the plain pin map; + this makes it a live status map.) - Surface each location's **threshold-alert status** (read-only) + an event/inbox view. Leans on the SLMM alert engine + dispatch. +### Notes carried from M1 +- Tile headline metric is **Leq** (energy-average, the sound-monitoring compliance + metric) — chosen over the twitchy instantaneous Lp. If clients ever want a + different headline (e.g. Lmax for peaks), make it a per-deployment setting. + ### M3 — Reports - Client-facing list + download of the daily baseline-comparison reports. - Depends on the FTP report pipeline (`feat/ftp-report-pipeline`) landing and diff --git a/templates/portal/overview.html b/templates/portal/overview.html index a5b0994..57539be 100644 --- a/templates/portal/overview.html +++ b/templates/portal/overview.html @@ -20,8 +20,8 @@
{{ loc.address or loc.project_name or '' }}
- -- - dB Lp + -- + dB Leq
 
@@ -51,7 +51,7 @@ function fmtAgo(iso) { async function loadTile(loc) { const el = document.querySelector(`.loc-tile[data-loc="${loc.id}"]`); if (!el) return; - const lp = el.querySelector('.loc-lp'), badge = el.querySelector('.loc-badge'), + const leq = el.querySelector('.loc-leq'), badge = el.querySelector('.loc-badge'), fresh = el.querySelector('.loc-fresh'); try { const j = await (await fetch(`/portal/api/location/${encodeURIComponent(loc.id)}/live`)).json(); @@ -60,10 +60,10 @@ async function loadTile(loc) { if (!d) { badge.textContent = j.reason === 'no_device' ? 'No device' : 'Offline'; badge.className = 'loc-badge shrink-0 px-2 py-0.5 text-xs rounded-full bg-slate-700 text-gray-300'; - lp.textContent = '--'; fresh.innerHTML = ' '; + leq.textContent = '--'; fresh.innerHTML = ' '; return; } - lp.textContent = (d.lp == null || d.lp === '') ? '--' : d.lp; + leq.textContent = (d.leq == null || d.leq === '') ? '--' : d.leq; const measuring = d.measurement_state === 'Start' || d.measurement_state === 'Measure'; badge.textContent = measuring ? '● Live' : 'Stopped'; badge.className = 'loc-badge shrink-0 px-2 py-0.5 text-xs rounded-full ' +