From 85cd05177e2040d8d8295825b6c43f595cbe5433 Mon Sep 17 00:00:00 2001 From: pablorevilla-meshtastic Date: Sat, 23 May 2026 09:59:25 -0700 Subject: [PATCH] added filter for extending the range of active nodes to nodelist --- meshview/templates/nodelist.html | 45 +++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/meshview/templates/nodelist.html b/meshview/templates/nodelist.html index 5002d17..ca86070 100644 --- a/meshview/templates/nodelist.html +++ b/meshview/templates/nodelist.html @@ -108,6 +108,14 @@ select, .export-btn, .search-box, .clear-btn { margin: -4px auto 10px; color: rgba(255, 255, 255, 0.68); font-size: 0.9em; + display: flex; + align-items: center; + gap: 8px; + flex-wrap: wrap; +} +.node-list-note select { + padding: 3px 6px; + min-width: 70px; } .node-status { margin-left: 10px; @@ -258,8 +266,14 @@ select, .export-btn, .search-box, .clear-btn { nodes -
- Showing all nodes active in the last 3 days. +
+ Showing nodes active in the last + + days.
@@ -416,16 +430,18 @@ document.addEventListener("DOMContentLoaded", async function() { const searchBox = document.getElementById("search-box"); const countSpan = document.getElementById("node-count"); const statusSpan = document.getElementById("node-status"); + const daysActiveFilter = document.getElementById("days-active-filter"); const exportBtn = document.getElementById("export-btn"); const clearBtn = document.getElementById("clear-btn"); const favoritesBtn = document.getElementById("favorites-btn"); let lastIsMobile = (window.innerWidth <= 768); - try { + async function loadNodesForActiveDays() { + const daysActive = Number(daysActiveFilter.value) || 3; setStatus("Loading nodes…"); await nextFrame(); - const res = await fetch("/api/nodes?days_active=3"); + const res = await fetch(`/api/nodes?days_active=${encodeURIComponent(daysActive)}`); if (!res.ok) throw new Error("Failed to fetch nodes"); const data = await res.json(); @@ -456,6 +472,10 @@ document.addEventListener("DOMContentLoaded", async function() { applyFilters(); // ensures initial sort + render uses same path updateSortIcons(); setStatus(""); + } + + try { + await loadNodesForActiveDays(); } catch (err) { tbody.innerHTML = ` @@ -469,6 +489,20 @@ document.addEventListener("DOMContentLoaded", async function() { channelFilter.addEventListener("change", applyFilters); hwFilter.addEventListener("change", applyFilters); firmwareFilter.addEventListener("change", applyFilters); + daysActiveFilter.addEventListener("change", async () => { + try { + await loadNodesForActiveDays(); + } catch (err) { + console.error("Failed to reload nodes:", err); + tbody.innerHTML = ` + + ${nodelistTranslations.error_loading_nodes || "Error loading nodes"} + `; + mobileList.innerHTML = ""; + countSpan.textContent = 0; + setStatus(""); + } + }); // Debounced only for search typing searchBox.addEventListener("input", debounce(applyFilters, 250)); @@ -535,12 +569,15 @@ document.addEventListener("DOMContentLoaded", async function() { } function fillSelect(select, values) { + const currentValue = select.value; + select.querySelectorAll("option:not(:first-child)").forEach(opt => opt.remove()); [...values].sort().forEach(v => { const opt = document.createElement("option"); opt.value = v; opt.textContent = v; select.appendChild(opt); }); + select.value = values.has(currentValue) ? currentValue : ""; } function toggleFavoritesFilter() {