/** * static/js/status.js * Renders the server status and agent activity panels, * and the config settings panel. */ import { state } from './state.js'; // ── Time helpers ────────────────────────────────────────────────────────── function fmtTime(isoStr) { if (!isoStr) return '–'; const d = new Date(isoStr); return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' }); } function fmtRelative(isoStr) { if (!isoStr) return '–'; const d = new Date(isoStr); const diff = Math.floor((Date.now() - d.getTime()) / 1000); if (diff < 5) return 'just now'; if (diff < 60) return `${diff}s ago`; if (diff < 3600) return `${Math.floor(diff / 60)}m ago`; return `${Math.floor(diff / 3600)}h ago`; } // ── Server online indicator (header) ───────────────────────────────────── function updateHeaderLeds(serverOnline, status) { const serverLed = document.getElementById('led-server'); const agentLed = document.getElementById('led-agent'); if (!serverLed || !agentLed) return; if (state.get('sseReconnecting')) { serverLed.className = 'led led--amber led--pulse'; serverLed.querySelector('.led__label').textContent = 'Reconnecting…'; } else if (serverOnline) { serverLed.className = 'led led--green led--pulse'; serverLed.querySelector('.led__label').textContent = 'Server Online'; } else if (!serverOnline) { serverLed.className = 'led led--red'; serverLed.querySelector('.led__label').textContent = 'Server Offline'; } if (status?.agent?.connected) { agentLed.className = 'led led--cyan led--pulse'; agentLed.querySelector('.led__label').textContent = 'Agent Connected'; } else { agentLed.className = 'led led--muted'; agentLed.querySelector('.led__label').textContent = 'Agent Idle'; } } // ── Status sidebar panel ────────────────────────────────────────────────── function renderStatusPanel(status) { const el = document.getElementById('status-panel-body'); if (!el || !status) return; const agent = status.agent ?? {}; const queue = { pending_count: status.queue?.pending_count ?? 0, consumed_count: status.queue?.consumed_count ?? 0, }; const serverType = status.server?.type || 'unknown'; const version = status.server?.version || '–'; el.innerHTML = `
Server Up ${fmtTime(status.server?.started_at)}
Version v${escapeHtml(version)}
Server Type ${escapeHtml(serverType)}
Pending ${queue.pending_count}
Consumed ${queue.consumed_count}
Agent ${agent.agent_id ? escapeHtml(agent.agent_id) : '–'}
Last Seen ${fmtRelative(agent.last_seen_at)}
Last Fetch ${fmtRelative(agent.last_fetch_at)}
`; } /** Called by app.js on a timer to keep relative times fresh. */ export function refreshStatusTimestamps() { document.querySelectorAll('[data-ts-rel]').forEach(el => { const iso = el.dataset.tsRel; if (iso) el.textContent = fmtRelative(iso); }); } export function initStatus() { state.subscribe('serverOnline', (online) => { updateHeaderLeds(online, state.get('status')); }); state.subscribe('status', (status) => { updateHeaderLeds(state.get('serverOnline'), status); renderStatusPanel(status); }); } // ── Config panel ────────────────────────────────────────────────────────── export function initConfig() { // Intentionally empty: the web UI no longer exposes editable settings. } function escapeHtml(str) { return String(str) .replace(/&/g, '&') .replace(//g, '>'); }