diff --git a/public/analytics.js b/public/analytics.js index 89d9555e..cd51714f 100644 --- a/public/analytics.js +++ b/public/analytics.js @@ -22,13 +22,13 @@ const y = h - 2 - (v / max) * (h - 4); return `${x},${y}`; }).join(' '); - return ``; + return `Sparkline showing trend of ${data.length} data points`; } function barChart(data, labels, colors, w = 800, h = 220, pad = 40) { const max = Math.max(...data, 1); const barW = Math.min((w - pad * 2) / data.length - 2, 30); - let svg = ``; + let svg = `Bar chart showing data distribution`; // Grid for (let i = 0; i <= 4; i++) { const y = pad + (h - pad * 2) * i / 4; @@ -81,7 +81,9 @@ `; // Tab handling - document.getElementById('analyticsTabs').addEventListener('click', e => { + const analyticsTabs = document.getElementById('analyticsTabs'); + initTabBar(analyticsTabs); + analyticsTabs.addEventListener('click', e => { const btn = e.target.closest('.tab-btn'); if (!btn) return; document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active')); @@ -289,7 +291,7 @@ function renderScatter(data) { const w = 600, h = 300, pad = 40; const snrMin = -12, snrMax = 15, rssiMin = -130, rssiMax = -5; - let svg = ``; + let svg = `SNR vs RSSI scatter plot showing signal quality distribution`; // Axes svg += ``; svg += ``; @@ -351,8 +353,7 @@ if (!data.length) return '
No data
'; const w = 400, h = 160, pad = 35; const maxPkts = Math.max(...data.map(d => d.count), 1); - let svg = ``; - // SNR line + let svg = `Signal quality over time showing SNR trend and packet volume`; const snrPts = data.map((d, i) => { const x = pad + i * ((w - pad * 2) / Math.max(data.length - 1, 1)); const y = h - pad - ((d.avgSnr + 12) / 27) * (h - pad * 2); @@ -442,6 +443,7 @@ // Observer selector event handling const selector = document.getElementById('obsSelector'); if (selector) { + initTabBar(selector); selector.addEventListener('click', e => { const btn = e.target.closest('.tab-btn'); if (!btn) return; @@ -486,7 +488,7 @@ if (!data.length) return '
No data
'; const w = 380, h = 160, pad = 40; const maxHop = Math.max(...data.map(d => d.hops)); - let svg = ``; + let svg = `Hops vs SNR bubble chart showing signal degradation over distance`; data.forEach(d => { const x = pad + (d.hops / maxHop) * (w - pad * 2); const y = h - pad - ((d.avgSnr + 12) / 27) * (h - pad * 2); @@ -619,7 +621,7 @@ const channels = [...new Set(data.map(d => d.channel))]; const colors = ['#ef4444','#22c55e','#3b82f6','#f59e0b','#8b5cf6','#ec4899','#14b8a6','#64748b']; const w = 600, h = 180, pad = 35; - let svg = ``; + let svg = `Channel message activity over time`; channels.forEach((ch, ci) => { const pts = hours.map((hr, i) => { const entry = data.find(d => d.hour === hr && d.channel === ch); @@ -748,7 +750,7 @@ const w = 800, h = 180, pad = 35; const maxVal = Math.max(...hourly.map(h => Math.max(h[1] || 0, h[2] || 0, h[3] || 0)), 1); const colors = { 1: '#ef4444', 2: '#22c55e', 3: '#3b82f6' }; - let svg = ``; + let svg = `Hash size distribution over time showing 1-byte, 2-byte, and 3-byte hash trends`; for (const size of [1, 2, 3]) { const pts = hourly.map((d, i) => { const x = pad + i * ((w - pad * 2) / Math.max(hourly.length - 1, 1)); @@ -813,17 +815,19 @@ bg = `rgb(200,${g},30)`; color = '#fff'; } const status = count === 0 ? 'available' : count === 1 ? `1 node: ${nodes[0].name || nodes[0].public_key.slice(0,12)}` : `${count} nodes — COLLISION`; - html += `${hex}`; + const countLabel = count === 0 ? '0' : count >= 3 ? '3+' : String(count); + const cellText = count >= 2 ? `⚠${countLabel}` : countLabel; + html += `${cellText}`; } html += ''; } html += ''; html += `
- Available - 1 node - 2 nodes - 3+ nodes (collision) + 0 — Available + 1 — One node + ⚠2 — Two nodes (collision) + ⚠3+ — Three+ nodes (collision)
`; el.innerHTML = html; diff --git a/public/observers.js b/public/observers.js index 0634676a..8c3ce74c 100644 --- a/public/observers.js +++ b/public/observers.js @@ -89,9 +89,9 @@ el.innerHTML = `
- ${online} Online - ${stale} Stale - ${offline} Offline + ${online} Online + ${stale} Stale + ${offline} Offline 📡 ${observers.length} Total
@@ -101,8 +101,9 @@ ${observers.map(o => { const h = healthStatus(o.last_seen); + const shape = h.cls === 'health-green' ? '●' : h.cls === 'health-yellow' ? '▲' : '✕'; return ` - +
${h.label}${shape} ${h.label} ${o.name || o.id} ${o.iata ? `${o.iata}` : '—'} ${timeAgo(o.last_seen)}