// workflow-monitor.js — v0.35.0 Workflow Monitoring Dashboard // // Admin monitoring tab showing active instances, stage funnels, // SLA status indicators, and stale instance detection. export function mountMonitorTab(container, { basePath }) { container.innerHTML = `

Workflow Monitor

Loading…
`; let refreshTimer = null; async function loadInstances() { try { const resp = await fetch(`${basePath}/api/v1/admin/workflows/monitor/instances`); if (!resp.ok) throw new Error(resp.statusText); const { data } = await resp.json(); renderInstances(data); } catch (e) { document.getElementById('monInstances').textContent = 'Failed to load: ' + e.message; } } async function loadStale() { try { const resp = await fetch(`${basePath}/api/v1/admin/workflows/monitor/stale`); if (!resp.ok) return; const { data } = await resp.json(); renderStale(data); } catch (_) {} } function renderInstances(instances) { const el = document.getElementById('monInstances'); if (!instances.length) { el.innerHTML = '

No active workflow instances.

'; return; } let html = ''; html += ''; html += ''; html += ''; html += ''; for (const inst of instances) { const age = formatDuration(inst.age_seconds); const sla = renderSLA(inst); html += ``; html += ``; html += ``; html += ``; html += ``; html += ''; } html += '
WorkflowStageAgeSLA
${esc(inst.workflow_name)}
${esc(inst.channel_title)}
${esc(inst.stage_name)} (${inst.current_stage})${age}${sla}
'; el.innerHTML = html; } function renderSLA(inst) { if (!inst.sla_seconds) return ''; if (inst.sla_breached) { return 'BREACHED'; } if (inst.sla_remaining_seconds != null) { const remaining = inst.sla_remaining_seconds; const pct = Math.max(0, remaining / inst.sla_seconds); const color = pct > 0.5 ? 'var(--success,#2ecc71)' : pct > 0.2 ? 'var(--warning,#f39c12)' : 'var(--danger,#e74c3c)'; return `${formatDuration(remaining)}`; } return '—'; } function renderStale(instances) { const el = document.getElementById('monStale'); if (!instances.length) { el.innerHTML = ''; return; } let html = '

Stale Instances (' + instances.length + ')

'; html += ''; el.innerHTML = html; } function formatDuration(seconds) { if (seconds < 60) return seconds + 's'; if (seconds < 3600) return Math.floor(seconds / 60) + 'm'; if (seconds < 86400) return Math.floor(seconds / 3600) + 'h ' + Math.floor((seconds % 3600) / 60) + 'm'; return Math.floor(seconds / 86400) + 'd ' + Math.floor((seconds % 86400) / 3600) + 'h'; } function esc(s) { const d = document.createElement('div'); d.textContent = s || ''; return d.innerHTML; } // Initial load loadInstances(); loadStale(); // Auto-refresh every 30s refreshTimer = setInterval(() => { loadInstances(); loadStale(); }, 30000); document.getElementById('monRefresh')?.addEventListener('click', () => { loadInstances(); loadStale(); }); // Return cleanup function return () => { if (refreshTimer) clearInterval(refreshTimer); }; }