diff --git a/public/analytics.js b/public/analytics.js
index 73aefbe6..6349f7fe 100644
--- a/public/analytics.js
+++ b/public/analytics.js
@@ -65,6 +65,7 @@
`;
+ RegionFilter.init(document.getElementById('nodesRegionFilter'));
+ regionChangeHandler = RegionFilter.onChange(function () { loadNodes(); });
+
document.getElementById('nodeSearch').addEventListener('input', debounce(e => {
search = e.target.value;
loadNodes();
@@ -256,10 +262,12 @@
}
}
- function destroy() {
+ function destroy() {
if (wsHandler) offWS(wsHandler);
wsHandler = null;
if (detailMap) { detailMap.remove(); detailMap = null; }
+ if (regionChangeHandler) RegionFilter.offChange(regionChangeHandler);
+ regionChangeHandler = null;
nodes = [];
selectedKey = null;
}
@@ -270,6 +278,8 @@
if (activeTab !== 'all') params.set('role', activeTab);
if (search) params.set('search', search);
if (lastHeard) params.set('lastHeard', lastHeard);
+ const rp = RegionFilter.getRegionParam();
+ if (rp) params.set('region', rp);
const data = await api('/nodes?' + params, { ttl: CLIENT_TTL.nodeList });
nodes = data.nodes || [];
counts = data.counts || {};
diff --git a/public/observers.js b/public/observers.js
index 8a4d854d..b3b1a534 100644
--- a/public/observers.js
+++ b/public/observers.js
@@ -5,6 +5,7 @@
let observers = [];
let wsHandler = null;
let refreshTimer = null;
+ let regionChangeHandler = null;
function init(app) {
app.innerHTML = `
@@ -13,8 +14,11 @@
Observer Status
+
`;
+ RegionFilter.init(document.getElementById('obsRegionFilter'));
+ regionChangeHandler = RegionFilter.onChange(function () { render(); });
loadObservers();
// Event delegation for data-action buttons
app.addEventListener('click', function (e) {
@@ -33,6 +37,8 @@
wsHandler = null;
if (refreshTimer) clearInterval(refreshTimer);
refreshTimer = null;
+ if (regionChangeHandler) RegionFilter.offChange(regionChangeHandler);
+ regionChangeHandler = null;
observers = [];
}
@@ -78,24 +84,30 @@
const el = document.getElementById('obsContent');
if (!el) return;
- if (observers.length === 0) {
+ // Apply region filter
+ const selectedRegions = RegionFilter.getSelected();
+ const filtered = selectedRegions
+ ? observers.filter(o => o.iata && selectedRegions.includes(o.iata))
+ : observers;
+
+ if (filtered.length === 0) {
el.innerHTML = 'No observers found.
';
return;
}
- const maxPktsHr = Math.max(1, ...observers.map(o => o.packetsLastHour || 0));
+ const maxPktsHr = Math.max(1, ...filtered.map(o => o.packetsLastHour || 0));
// Summary counts
- const online = observers.filter(o => healthStatus(o.last_seen).cls === 'health-green').length;
- const stale = observers.filter(o => healthStatus(o.last_seen).cls === 'health-yellow').length;
- const offline = observers.filter(o => healthStatus(o.last_seen).cls === 'health-red').length;
+ const online = filtered.filter(o => healthStatus(o.last_seen).cls === 'health-green').length;
+ const stale = filtered.filter(o => healthStatus(o.last_seen).cls === 'health-yellow').length;
+ const offline = filtered.filter(o => healthStatus(o.last_seen).cls === 'health-red').length;
el.innerHTML = `
â—Ź ${online} Online
â–˛ ${stale} Stale
âś• ${offline} Offline
- 📡 ${observers.length} Total
+ 📡 ${filtered.length} Total