This repository has been archived on 2026-04-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
core/src/js/sw/surfaces/admin/dashboard.js
gobha 5f1c733002 Changeset 0.37.6 (#218)
Co-authored-by: gobha <jasafpro@gmail.com>
Co-committed-by: gobha <jasafpro@gmail.com>
2026-03-21 16:21:59 +00:00

91 lines
4.1 KiB
JavaScript

/**
* Admin > Monitoring > Dashboard
*/
const { html } = window;
const { useState, useEffect, useCallback, useRef } = hooks;
export default function DashboardSection() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const timerRef = useRef(null);
const load = useCallback(async () => {
try {
const d = await sw.api.admin.dashboard();
setData(d);
} catch (e) { sw.toast(e.message, 'error'); }
finally { setLoading(false); }
}, []);
useEffect(() => {
load();
timerRef.current = setInterval(load, 10000);
return () => clearInterval(timerRef.current);
}, []);
function formatUptime(seconds) {
if (!seconds) return '\u2014';
const d = Math.floor(seconds / 86400);
const h = Math.floor((seconds % 86400) / 3600);
const m = Math.floor((seconds % 3600) / 60);
if (d > 0) return `${d}d ${h}h`;
if (h > 0) return `${h}h ${m}m`;
return `${m}m`;
}
function formatBytes(b) {
if (!b) return '0 B';
if (b < 1048576) return (b / 1024).toFixed(0) + ' KB';
if (b < 1073741824) return (b / 1048576).toFixed(1) + ' MB';
return (b / 1073741824).toFixed(2) + ' GB';
}
if (loading) return html`<div class="settings-placeholder">Loading\u2026</div>`;
if (!data) return html`<div class="empty-hint">No dashboard data</div>`;
return html`
<div>
<p class="text-muted" style="font-size:12px;margin-bottom:12px;">Auto-refreshes every 10s</p>
<div class="stat-cards-grid" style="margin-bottom:16px;">
<div class="stat-card"><div class="stat-value">${formatUptime(data.uptime_seconds)}</div><div class="stat-label">Uptime</div></div>
<div class="stat-card"><div class="stat-value">${data.active_connections ?? '\u2014'}</div><div class="stat-label">Connections</div></div>
<div class="stat-card"><div class="stat-value">${data.goroutines ?? '\u2014'}</div><div class="stat-label">Goroutines</div></div>
<div class="stat-card"><div class="stat-value">${formatBytes(data.heap_alloc)}</div><div class="stat-label">Heap</div></div>
</div>
${data.db_pool && html`
<div class="stat-cards-grid" style="margin-bottom:16px;">
<div class="stat-card"><div class="stat-value">${data.db_pool.open || 0}</div><div class="stat-label">DB Open</div></div>
<div class="stat-card"><div class="stat-value">${data.db_pool.in_use || 0}</div><div class="stat-label">DB In Use</div></div>
<div class="stat-card"><div class="stat-value">${data.db_pool.idle || 0}</div><div class="stat-label">DB Idle</div></div>
</div>
`}
${data.providers && data.providers.length > 0 && html`
<h4 style="margin:16px 0 8px;">Provider Health</h4>
<div class="stat-cards-grid">
${data.providers.map(p => html`
<div class="dashboard-provider-card" key=${p.name || p.id}>
<div style="display:flex;align-items:center;gap:6px;margin-bottom:4px;">
<strong style="font-size:13px;">${p.name || p.id}</strong>
<span class="badge ${p.status === 'healthy' ? 'badge-active' : p.status === 'degraded' ? 'badge-pending' : 'badge-inactive'}">${p.status}</span>
</div>
<div style="font-size:11px;color:var(--text-3);">${Math.round(p.avg_latency_ms || 0)}ms avg</div>
</div>
`)}
</div>
`}
${data.recent_errors && data.recent_errors.length > 0 && html`
<h4 style="margin:16px 0 8px;">Recent Errors</h4>
<div class="admin-list">
${data.recent_errors.map((e, i) => html`
<div class="admin-surface-row" key=${i} style="font-size:12px;color:var(--danger);">${e}</div>
`)}
</div>
`}
</div>
`;
}