feat: station search in side panel; DB stats cover HII tables
CI/CD Pipeline - Northern Thailand Ping River Monitor / Test Suite (3.11) (push) Failing after 38s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Build Docker Image (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Integration Test with Services (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Deploy to Staging (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Deploy to Production (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Performance Test (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Code Quality (push) Successful in 16s
Documentation / Validate Documentation (push) Failing after 9s
Documentation / Generate API Documentation (push) Successful in 10s
Documentation / Build Sphinx Documentation (push) Successful in 16s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Cleanup (push) Successful in 0s
Documentation / Documentation Summary (push) Successful in 2s

A search box above the station lists filters both the RID flow list and
the basin-station list by code, name (Thai included), or river, and
persists across refreshes. /api/stats now reports whole-DB totals — RID
+ hii_rainfall + hii_waterlevel counts, combined station count, and a
date range spanning all sources — with per-source breakdown fields the
stats strip shows as tile notes. Sensor section renamed to 'Additional
basin stations · ThaiWater/HII'.
This commit is contained in:
2026-08-11 16:06:39 +07:00
parent ba4710b243
commit e27d418a1a
3 changed files with 81 additions and 9 deletions
+26 -6
View File
@@ -234,9 +234,11 @@
</article>
<aside class="side-card">
<div class="side-head"><h2>Current station flow</h2><p>Select a station to locate it and load its history</p></div>
<div class="side-head"><h2>Current station flow</h2><p>Select a station to locate it and load its history</p>
<input type="search" id="station-search" placeholder="Search stations · code, name, river…" aria-label="Search stations" style="margin-top:10px;width:100%;box-sizing:border-box;padding:9px 12px;border:1px solid var(--border);border-radius:10px;background:white;font-size:.8rem">
</div>
<div class="station-list" id="river-flow" aria-live="polite"></div>
<div class="side-head"><h2>Additional ThaiWater sensors</h2><p id="thaiwater-count">Loading Ping basin sensors…</p></div>
<div class="side-head"><h2>Additional basin stations</h2><p id="thaiwater-count">Loading ThaiWater/HII stations…</p></div>
<div class="station-list" id="thaiwater-sensors" aria-live="polite"></div>
</aside>
</section>
@@ -273,11 +275,11 @@
</section>
<section class="stats" id="db-stats" aria-label="Database statistics" style="margin-top:14px;display:none;grid-template-columns:repeat(auto-fit,minmax(170px,1fr))">
<article class="stat"><div class="stat-label">Total datapoints</div><div class="stat-value" id="db-total"></div><div class="stat-note">measurements stored</div></article>
<article class="stat"><div class="stat-label">Total datapoints</div><div class="stat-value" id="db-total"></div><div class="stat-note" id="db-total-note">measurements stored</div></article>
<article class="stat"><div class="stat-label">Date range</div><div class="stat-value" id="db-range" style="font-size:1.05rem;line-height:1.3;white-space:normal"></div><div class="stat-note">first to last record</div></article>
<article class="stat"><div class="stat-label">Days spanned</div><div class="stat-value" id="db-days"></div><div class="stat-note">between first and last</div></article>
<article class="stat"><div class="stat-label">Stations</div><div class="stat-value" id="db-stations"></div><div class="stat-note">distinct in database</div></article>
<article class="stat"><div class="stat-label">Coverage</div><div class="stat-value" id="db-coverage"></div><div class="stat-note">of hourly slots recorded</div></article>
<article class="stat"><div class="stat-label">Stations</div><div class="stat-value" id="db-stations"></div><div class="stat-note" id="db-stations-note">distinct in database</div></article>
<article class="stat"><div class="stat-label">Coverage</div><div class="stat-value" id="db-coverage"></div><div class="stat-note">of RID hourly slots recorded</div></article>
</section>
</main>
@@ -623,6 +625,17 @@
}
}
// Filters both station lists by any text on the row (code, names, river —
// Thai included, since it matches against the rendered text).
function applyStationSearch() {
const query = $('station-search').value.trim().toLowerCase();
['river-flow', 'thaiwater-sensors'].forEach((id) => {
[...$(id).children].forEach((row) => {
row.style.display = !query || row.textContent.toLowerCase().includes(query) ? '' : 'none';
});
});
}
function renderList(stations, readings) {
const container = $('river-flow');
container.replaceChildren();
@@ -654,7 +667,7 @@
const additional = sensors
.filter((sensor) => !existingCodes.has(sensor.station_code))
.sort((a, b) => (Number(b.bank_percent) || -1) - (Number(a.bank_percent) || -1));
$('thaiwater-count').textContent = `${additional.length} additional Ping basin stations · water level`;
$('thaiwater-count').textContent = `${additional.length} Ping basin stations · ThaiWater/HII water level`;
additional.forEach((sensor) => {
const percent = sensor.bank_percent == null ? null : Number(sensor.bank_percent);
// ThaiWater situation bands by % of bank capacity: over-bank red,
@@ -751,6 +764,7 @@
renderRainLayer(rainRecords);
renderList(stations, readings);
renderThaiWaterSensors(sensors, new Set(stations.map((station) => station.station_code)));
applyStationSearch(); // keep an active search applied across refreshes
loadCustomMarkers();
renderSummary(stations, readings);
$('loading').style.display = 'none';
@@ -1134,6 +1148,11 @@
const stats = await response.json();
const fmtDate = (value) => new Date(value).toLocaleDateString([], { day: 'numeric', month: 'short', year: 'numeric' });
$('db-total').textContent = Number(stats.total_measurements).toLocaleString();
if (stats.rid_measurements != null) {
const fmtNum = (value) => Number(value || 0).toLocaleString();
$('db-total-note').textContent = `${fmtNum(stats.rid_measurements)} RID · ${fmtNum(stats.hii_waterlevel_measurements)} HII level · ${fmtNum(stats.hii_rainfall_measurements)} rain`;
$('db-stations-note').textContent = `${stats.rid_station_count} RID · ${stats.hii_station_count} ThaiWater/HII`;
}
$('db-range').textContent = `${fmtDate(stats.first_timestamp)} ${fmtDate(stats.last_timestamp)}`;
$('db-days').textContent = Number(stats.days_spanned).toLocaleString();
$('db-stations').textContent = String(stats.station_count);
@@ -1154,6 +1173,7 @@
? 'Hide station forecasts ▴'
: `Show all ${count} station forecasts ▾`;
});
$('station-search').addEventListener('input', applyStationSearch);
$('rain-toggle').addEventListener('change', (event) => {
if (!state.rainLayer) return;
if (event.target.checked) state.rainLayer.addTo(state.map);