const peersContainer = document.getElementById('peers'); const callsContainer = document.getElementById('calls'); const baseURL = `${window.location.protocol}//${window.location.host}`; function formatTimestamp(ts) { if (!ts) return "--" const date = new Date(ts * 1000); const now = new Date(); const isToday = date.getDate() === now.getDate() && date.getMonth() === now.getMonth() && date.getFullYear() === now.getFullYear(); const timeStr = date.toLocaleTimeString('en-US', { hour12: false }); const dateStr = date.toLocaleDateString(); const secondsAgo = Math.floor((now - date) / 1000); const rel = secondsAgo < 60 ? `${secondsAgo}s ago` : secondsAgo < 3600 ? `${Math.floor(secondsAgo / 60)}m ago` : secondsAgo < 86400 ? `${Math.floor(secondsAgo / 3600)}h ago` : `${Math.floor(secondsAgo / 86400)}d ago`; return `${isToday ? timeStr : `${dateStr} ${timeStr}`} (${rel})`; } async function fetchData() { try { const dashboardRes = await fetch(`${baseURL}/api/dashboard`); const dashboardData = await dashboardRes.json(); renderPeers(dashboardData.peers || []); renderCallsTable(dashboardData.calls || []); } catch (err) { console.error('Error fetching data:', err); } } function getStatusColor(status) { switch (status) { case 'ACTIVE': return 'bg-green-900 text-green-200'; case 'TIMEOUT': return 'bg-yellow-900 text-yellow-200'; case 'DEAD': return 'bg-red-950 text-red-200'; default: return 'bg-cyan-900 text-cyan-200'; } } function toggleVisibility(id) { const el = document.getElementById(id); if (el) { el.classList.toggle('hidden'); } } let nextExpandedSectionId = 0; function renderPeers(peers) { peersContainer.innerHTML = ''; if (peers.length === 0) { peersContainer.innerHTML = '
No peers connected.
'; return; } peers.forEach(peer => { const div = document.createElement('div'); div.className = 'bg-gray-800 rounded-md shadow overflow-hidden'; const cardTitleColor = getStatusColor(peer.status); const unitsHTML = (peer.units || []) .map(unit => `${unit}`) .join(''); const callsign = peer.config.callsign || "--" const expandedSectionId = `expandable-${nextExpandedSectionId++}`; div.innerHTML = `