feat(portal): tile headline is Leq, not Lp; note live-map for M2
Lp (instantaneous) twitches every reading and makes a poor at-a-glance headline; Leq (energy-average) is the stable, standard sound-monitoring/compliance metric. Overview tiles now lead with Leq. Design doc: live project map (status-colored pins + current-reading popups) recorded as an M2 item; headline-metric rationale noted. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+10
-1
@@ -118,10 +118,19 @@ live data, all from cache.
|
|||||||
page or a script for now).
|
page or a script for now).
|
||||||
|
|
||||||
### M2 — Dashboard + alerts
|
### 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
|
- Surface each location's **threshold-alert status** (read-only) + an event/inbox
|
||||||
view. Leans on the SLMM alert engine + dispatch.
|
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
|
### M3 — Reports
|
||||||
- Client-facing list + download of the daily baseline-comparison reports.
|
- Client-facing list + download of the daily baseline-comparison reports.
|
||||||
- Depends on the FTP report pipeline (`feat/ftp-report-pipeline`) landing and
|
- Depends on the FTP report pipeline (`feat/ftp-report-pipeline`) landing and
|
||||||
|
|||||||
@@ -20,8 +20,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="text-xs text-gray-400 mt-0.5 truncate">{{ loc.address or loc.project_name or '' }}</div>
|
<div class="text-xs text-gray-400 mt-0.5 truncate">{{ loc.address or loc.project_name or '' }}</div>
|
||||||
<div class="mt-3 flex items-baseline gap-1">
|
<div class="mt-3 flex items-baseline gap-1">
|
||||||
<span class="loc-lp text-3xl font-bold text-seismo-orange">--</span>
|
<span class="loc-leq text-3xl font-bold text-seismo-orange">--</span>
|
||||||
<span class="text-sm text-gray-400">dB Lp</span>
|
<span class="text-sm text-gray-400">dB Leq</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="loc-fresh text-xs text-gray-500 mt-1"> </div>
|
<div class="loc-fresh text-xs text-gray-500 mt-1"> </div>
|
||||||
</a>
|
</a>
|
||||||
@@ -51,7 +51,7 @@ function fmtAgo(iso) {
|
|||||||
async function loadTile(loc) {
|
async function loadTile(loc) {
|
||||||
const el = document.querySelector(`.loc-tile[data-loc="${loc.id}"]`);
|
const el = document.querySelector(`.loc-tile[data-loc="${loc.id}"]`);
|
||||||
if (!el) return;
|
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');
|
fresh = el.querySelector('.loc-fresh');
|
||||||
try {
|
try {
|
||||||
const j = await (await fetch(`/portal/api/location/${encodeURIComponent(loc.id)}/live`)).json();
|
const j = await (await fetch(`/portal/api/location/${encodeURIComponent(loc.id)}/live`)).json();
|
||||||
@@ -60,10 +60,10 @@ async function loadTile(loc) {
|
|||||||
if (!d) {
|
if (!d) {
|
||||||
badge.textContent = j.reason === 'no_device' ? 'No device' : 'Offline';
|
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';
|
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;
|
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';
|
const measuring = d.measurement_state === 'Start' || d.measurement_state === 'Measure';
|
||||||
badge.textContent = measuring ? '● Live' : 'Stopped';
|
badge.textContent = measuring ? '● Live' : 'Stopped';
|
||||||
badge.className = 'loc-badge shrink-0 px-2 py-0.5 text-xs rounded-full ' +
|
badge.className = 'loc-badge shrink-0 px-2 py-0.5 text-xs rounded-full ' +
|
||||||
|
|||||||
Reference in New Issue
Block a user