711ef41e5f
Live Measurements panel no longer sits blank until you click Start Live Stream:
- On open it fills the KPI cards from the cached /status snapshot (lp/leq/lmax/
L1/L10) and backfills the chart from the /history DOD trail — both pure cache
reads, no device hit.
- Shows measuring state (● Measuring / ■ Stopped) and a freshness stamp
("as of 2:14 PM (12m ago)") that turns amber + "cached" when stale, so a cached
value is never mistaken for a live reading.
- Polls the cache every 15s while open so the cards stay current without opening
a device stream; Start Live Stream takes over (and no longer wipes the
backfilled trail). Chart cap raised 60 -> 600 so the 2h backfill isn't truncated.
Refresh buttons (on-demand, user-initiated single device read via GET /live,
which also updates the cache):
- one per device row in the list, and one in the panel header. Spinner while in
flight; toast on success/failure; reloads the list so badges + last-check update.
Layout fix: the status badge (Measuring/Active/Idle/Benched) was rendered at the
top-right of the card, colliding with the absolutely-positioned chart/gear icons.
Moved it to the bottom meta row next to "Last check", padded the card content
clear of the action icons, and added the refresh icon to that group.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
79 lines
5.7 KiB
HTML
79 lines
5.7 KiB
HTML
<!-- SLM Device List -->
|
|
{% if units %}
|
|
{% for unit in units %}
|
|
<div class="bg-gray-50 dark:bg-gray-800 rounded-lg p-4 border border-transparent hover:border-seismo-orange transition-colors relative">
|
|
<div class="absolute top-3 right-3 flex gap-2 z-10">
|
|
<button onclick="event.preventDefault(); event.stopPropagation(); refreshSlmUnit('{{ unit.id }}', this);"
|
|
class="text-gray-500 hover:text-seismo-orange dark:text-gray-400 dark:hover:text-seismo-orange"
|
|
title="Refresh {{ unit.id }} from device">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
|
|
</svg>
|
|
</button>
|
|
<button onclick="event.preventDefault(); event.stopPropagation(); showLiveChart('{{ unit.id }}');"
|
|
class="text-gray-500 hover:text-seismo-orange dark:text-gray-400 dark:hover:text-seismo-orange"
|
|
title="View live chart">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path>
|
|
</svg>
|
|
</button>
|
|
<button onclick="event.preventDefault(); event.stopPropagation(); openDeviceConfigModal('{{ unit.id }}');"
|
|
class="text-gray-500 hover:text-seismo-orange dark:text-gray-400 dark:hover:text-seismo-orange"
|
|
title="Configure {{ unit.id }}">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"></path>
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
|
|
<a href="/slm/{{ unit.id }}" class="block pr-24">
|
|
<div class="min-w-0">
|
|
<div class="flex items-center gap-2">
|
|
<span class="font-semibold text-gray-900 dark:text-white">{{ unit.id }}</span>
|
|
{% if unit.slm_model %}
|
|
<span class="text-xs text-gray-500 dark:text-gray-400">• {{ unit.slm_model }}</span>
|
|
{% endif %}
|
|
</div>
|
|
{% if unit.address %}
|
|
<p class="text-sm text-gray-600 dark:text-gray-400 truncate mt-1">{{ unit.address }}</p>
|
|
{% elif unit.location %}
|
|
<p class="text-sm text-gray-600 dark:text-gray-400 truncate mt-1">{{ unit.location }}</p>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Status badge + last-check on one line (moved off the top-right so it
|
|
no longer collides with the refresh/chart/gear action icons). -->
|
|
<div class="mt-2 flex items-center gap-2 flex-wrap">
|
|
{% if unit.retired %}
|
|
<span class="px-2 py-0.5 text-xs font-medium bg-gray-200 text-gray-700 dark:bg-gray-700 dark:text-gray-300 rounded-full">Retired</span>
|
|
{% elif not unit.deployed %}
|
|
<span class="px-2 py-0.5 text-xs font-medium bg-amber-100 text-amber-800 dark:bg-amber-900/30 dark:text-amber-300 rounded-full">Benched</span>
|
|
{% elif unit.measurement_state == "Start" %}
|
|
<span class="px-2 py-0.5 text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300 rounded-full">Measuring</span>
|
|
{% elif unit.is_recent %}
|
|
<span class="px-2 py-0.5 text-xs font-medium bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300 rounded-full">Active</span>
|
|
{% else %}
|
|
<span class="px-2 py-0.5 text-xs font-medium bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-300 rounded-full">Idle</span>
|
|
{% endif %}
|
|
<span class="text-xs text-gray-500 dark:text-gray-400">
|
|
{% if unit.slm_last_check %}
|
|
Last check: {{ unit.slm_last_check|local_datetime }}
|
|
{% else %}
|
|
No recent check-in
|
|
{% endif %}
|
|
</span>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
{% endfor %}
|
|
{% else %}
|
|
<div class="text-center py-8 text-gray-500 dark:text-gray-400">
|
|
<svg class="w-12 h-12 mx-auto mb-3 opacity-50" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 13V6a2 2 0 00-2-2H6a2 2 0 00-2 2v7m16 0v5a2 2 0 01-2 2H6a2 2 0 01-2-2v-5m16 0h-2.586a1 1 0 00-.707.293l-2.414 2.414a1 1 0 01-.707.293h-3.172a1 1 0 01-.707-.293l-2.414-2.414A1 1 0 006.586 13H4"></path>
|
|
</svg>
|
|
<p>No sound level meters found</p>
|
|
<p class="text-sm mt-1">Add units from the Fleet Roster</p>
|
|
</div>
|
|
{% endif %}
|