diff --git a/meshview/templates/packet.html b/meshview/templates/packet.html index 8cf795e..9b9a3b7 100644 --- a/meshview/templates/packet.html +++ b/meshview/templates/packet.html @@ -380,103 +380,137 @@ document.addEventListener("DOMContentLoaded", async () => { } /* --------------------------------------------- - Load packets_seen - ----------------------------------------------*/ - const seenRes = await fetch(`/api/packets_seen/${packetId}`); - const seenData = await seenRes.json(); - const seenList = seenData.seen ?? []; + Load packets_seen +----------------------------------------------*/ +const seenRes = await fetch(`/api/packets_seen/${packetId}`); +const seenData = await seenRes.json(); +const seenList = seenData.seen ?? []; - const seenSorted = seenList.slice().sort((a,b)=>{ - return (b.hop_start ?? -999) - (a.hop_start ?? -999); - }); +/* --------------------------------------------- + Sort by hop count (highest first) +----------------------------------------------*/ +const seenSorted = seenList.slice().sort((a,b)=>{ + const ha = (a.hop_start ?? 0) - (a.hop_limit ?? 0); + const hb = (b.hop_start ?? 0) - (b.hop_limit ?? 0); + return hb - ha; +}); - if (seenSorted.length){ - seenContainer.classList.remove("d-none"); - seenCountSpan.textContent = `(${seenSorted.length})`; - } +if (seenSorted.length){ + seenContainer.classList.remove("d-none"); + seenCountSpan.textContent = `(${seenSorted.length})`; +} - /* --------------------------------------------- - Render gateway table + map markers - ----------------------------------------------*/ - seenTableBody.innerHTML = seenSorted.map(s=>{ - const node = nodeLookup[s.node_id]; - const label = node?.long_name || s.node_id; +/* --------------------------------------------- + GROUP BY HOP COUNT +----------------------------------------------*/ +const hopGroups = {}; - const timeStr = s.import_time_us - ? new Date(s.import_time_us/1000).toLocaleTimeString() - : "—"; +seenSorted.forEach(s => { + const hopValue = Math.max( + 0, + (s.hop_start ?? 0) - (s.hop_limit ?? 0) + ); + if (!hopGroups[hopValue]) hopGroups[hopValue] = []; + hopGroups[hopValue].push(s); +}); - if (node?.last_lat && node.last_long){ - const rlat = node.last_lat/1e7; - const rlon = node.last_long/1e7; - allBounds.push([rlat, rlon]); +/* --------------------------------------------- + Render grouped gateway table + map markers +----------------------------------------------*/ +seenTableBody.innerHTML = Object.keys(hopGroups) + .sort((a,b) => Number(a) - Number(b)) // 0 hop first + .map(hopKey => { - const hopValue = (s.hop_start ?? 0) - (s.hop_limit ?? 0); - const color = hopColor(hopValue); + const hopLabel = + hopKey === "0" + ? (packetTranslations.direct || "Direct (0 hops)") + : `${hopKey} ${packetTranslations.hops || "hops"}`; - const marker = L.marker([rlat,rlon],{ - icon: L.divIcon({ - html: ` -