A multi-IATA region filters globally-paged rows client-side, and scroll
was the only trigger for the next page — an empty or too-short filtered
list could never fire it, so routes deeper in the cursor stream were
unreachable ('No routes' with routes that exist). Pull pages until the
region has a screenful or the cap calls it sparse.
The donut suits node types after all — few slices, and the total reads
better in the ring than on three bars. The center text is a title block
with textAlign center now, which ECharts actually centers (the old
graphic text anchored its left edge at the ring's x and clipped);
TitleComponent registered in the tree-shaken setup. Payload types stays
a bar chart.
Leaderboard names (top nodes/observers, radio presets) are left-aligned
at the card edge and ellipsized instead of running off the grid's left
side.
The WS patch only updated nodes already in the paged cache, and with
staleTime Infinity nothing ever refetched — a node that came up after
page load could never appear on the map. The event carries a full
summary, so unknown nodes are appended to the cache directly. The
Nodes table keeps patch-only behavior since its cache is filter-scoped.
microToDeg guessed microdegrees from integer values, but api/nodes.go
has always sent *float64 degrees — so a node sitting at an exact whole
degree got divided by 1e6 and rendered at the equator. Drop the shim
from the geojson builder, the nodes table and the detail panel, and fix
the ws.ts comment that repeated the wrong premise.
Also key neighbor rows by id+iata: the neighbors endpoint returns one
row per (neighbor, iata), so id alone collides for nodes heard in
several regions (live React duplicate-key error).
The payload-types donut was buckling: 10+ slivers, a scrollable legend
with truncated names, and a center total that clipped at narrower
widths. Bars label every category inline, show counts on top, and the
total moved into the card header. Node types gets the same treatment
for consistency.
Also fix charts starting on the fallback palette when a saved theme
loads: themeId is already the saved id before the CSS vars land, so
useChartColors now keys on a paletteRev that bumps on every applyTheme.
The active tab was state seeded from ?tab once, so back/forward moved
the URL without moving the UI. The URL is the source of truth now, and
unknown ?tab values fall back to Packets instead of a blank pane.
Packets history also healed wrong: while the tab is unmounted its WS
handler is down, and staleTime Infinity meant the gap never closed.
Mounting now resets to a fresh first page. Same for lag notices, which
used to invalidate (and sequentially replay) up to 20 cached pages —
together with the ws reconnect notice this covers outage recovery too.
useLiveOverview keyed its setQueryData on the whole RegionFilter object
while the overview query keys on the regionKey string, so every WS bump
silently missed the cache and the counters only moved on the 60s
refetch. Also drop pending counts when the region changes so old-region
increments don't bleed into the new KPIs.
The fresh [] fallback minted a new array each render while telemetry
loaded, which rebuilt all five chart options and forced a full ECharts
teardown (notMerge). Also silence the intentional themeId dep warning
in useChartColors.
scripts/ was caught by a stray .gitignore entry, so the npm start/stop
helpers pointed at a file fresh clones never got. Also sweep the last
tower-* references out of comments, compose and the map test fixture.
Search the /observers name endpoint (region-scoped) instead of filtering
the loaded top-15, so any observer is findable. Top observers remain the
default list; search results show name + IATA + status.
- center the payload/node-type donut center value+label in the ring
- add a name search box to the Stats → Observer list
- add a Statistics button on the Observers detail panel that opens
Stats → Observer preselected to that observer
Integrate the stats feature onto main: live mesh KPIs, observations
time-series, payload/node-type donuts, top-nodes/observers leaderboards,
radio presets, scope stats, and per-observer telemetry (battery, airtime,
noise, queue, receive errors) with 1h/6h/24h bucketing.
- adapt to the multi-IATA region model (useRegion -> single iata for /stats/*)
- rename stats getScopes -> getStatsScopes (avoids /scopes name-list collision)
- donut legend kept right but scrollable + truncating so it never clips
- balanced mesh grid; observer empty-state for telemetry-less observers
- normalize telemetry t to ms (raw path emits seconds, bucketed emits ms)
- add echarts 5.5.1; lazy-load StatsOverview chunk
* feat: replace getScopeStats with getScopes and update related tests
bug: lastSeen/sentAt was set to string instead of number by mistake
* feat: update timestamp handling and formatting across components
- WS messages carry epoch ms now instead of ISO strings
- new Timestamp component (relative/absolute), used in RouteTable and RouteDetailPanel
- IataChip and ScopeTag chips
- TraceDetailPanel and TraceList for trace drill-down
- tests updated for the timestamp change
- LoadingPill shows loading/error state in MapView, NodeTable and ObserverTable
- new useInfinitePages hook shares the pagination logic between them
- patchNodeSummary/patchObserverSummary now keep references stable
- tests for loading states, observer updates and infinite paging
- usePackets pulls a bigger first page (INITIAL_PACKET_PAGE_SIZE)
- ModalOverlay + useFocusTrap, used by NodeDetailOverlay and PacketAnalyzerOverlay
- PathData popovers for path resolutions; ObserverIcon and Tooltip
- tests for packet structure and the focus trap; tidied types in api.ts/enums.ts