From e5e0656a6a0e15c39d80c89d5aa5308ebbbf5b46 Mon Sep 17 00:00:00 2001 From: ZGaetano Date: Tue, 26 May 2026 22:54:45 -0400 Subject: [PATCH] dashboard: redesign stat cards, compress header, improve density --- services/web-ui/public/screens-home.jsx | 160 ++++++++++++++++-------- 1 file changed, 107 insertions(+), 53 deletions(-) diff --git a/services/web-ui/public/screens-home.jsx b/services/web-ui/public/screens-home.jsx index badc885..187a47d 100644 --- a/services/web-ui/public/screens-home.jsx +++ b/services/web-ui/public/screens-home.jsx @@ -200,48 +200,51 @@ function Dashboard({ navigate }) { return (
-
-

Dashboard

-

- {liveCount > 0 ? liveCount + ' live · ' : ''} - {runningCount > 0 ? runningCount + ' job' + (runningCount > 1 ? 's running' : ' running') + ' · ' : ''} - {assetsTotal.toLocaleString()} assets -

-
- -
-
navigate('library')} style={{ cursor: 'pointer' }}> -
Library
-
{assetsTotal.toLocaleString()}
-
- {sumWindow(cards.assets?.series) > 0 - ? '+' + sumWindow(cards.assets?.series) + ' added in last 24h' - : 'Total assets'} +
+
navigate('library')} style={{ cursor: 'pointer' }}> +
+ Library +
- +
{assetsTotal.toLocaleString()}
+
+ {sumWindow(cards.assets?.series) > 0 + ? '+' + sumWindow(cards.assets?.series) + ' in 24h' + : 'total assets'} +
+
-
navigate('recorders')} style={{ cursor: 'pointer' }}> -
Live feeds
-
{liveCount}
-
{totalRecs} recorder{totalRecs === 1 ? '' : 's'} configured
- +
navigate('recorders')} style={{ cursor: 'pointer' }}> +
+ Live feeds + +
+
{liveCount}
+
{totalRecs} configured
+
-
navigate('jobs')} style={{ cursor: 'pointer' }}> -
Jobs
-
{runningCount} / {doneCount} done
-
0 ? 'var(--warning)' : '' }}> +
navigate('jobs')} style={{ cursor: 'pointer' }}> +
+ Jobs + +
+
{runningCount} / {doneCount}
+
0 ? 'var(--warning)' : '' }}> {failedCount > 0 ? failedCount + ' failed' : 'All clear'} {sumWindow(cards.jobs?.series_done) > 0 && ( - <> · {sumWindow(cards.jobs?.series_done)} completed in last 24h + <> · {sumWindow(cards.jobs?.series_done)} in 24h )}
- +
-
navigate('cluster')} style={{ cursor: 'pointer' }}> -
Cluster nodes
-
{nodesOnline} / {nodesTotal} online
-
Heartbeat within 2 min
- +
navigate('cluster')} style={{ cursor: 'pointer' }}> +
+ Cluster + +
+
{nodesOnline} / {nodesTotal}
+
nodes online
+
@@ -259,8 +262,15 @@ function Dashboard({ navigate }) { : }
{r.name} + {r.source && {r.source}} {r.elapsed}
+ {r.project && ( +
+ + {r.project} +
+ )}
))}
@@ -271,7 +281,7 @@ function Dashboard({ navigate }) { navigate('library')} moreLabel="All assets" />
{recentAssets.length === 0 ? ( -
No assets yet.
+
No assets yet.
) : recentAssets.map(a => (
@@ -281,6 +291,10 @@ function Dashboard({ navigate }) { {a.name} {a.project && <> in {a.project}}
+
+ {a.duration && {a.duration}} + {a.res && {a.res}} +
{a.updated}
))} @@ -291,7 +305,7 @@ function Dashboard({ navigate }) { navigate('jobs')} moreLabel="View all" />
{JOBS.length === 0 - ?
No jobs.
+ ?
No jobs.
: JOBS.slice(0, 5).map(j => )}
@@ -299,20 +313,38 @@ function Dashboard({ navigate }) { navigate('cluster')} moreLabel="View all" />
{NODES.length === 0 - ?
No nodes found.
+ ?
No nodes found.
: NODES.slice(0, 4).map(n => { const nodeId = n.id || n.hostname || n.name || 'node'; const isOnline = n.status === 'online' || n.online === true; const cpuPct = n.cpu_percent ?? n.cpu ?? n.cpu_usage ?? null; const memRaw = n.memory_used_gb ?? n.mem ?? (n.mem_used_mb != null ? n.mem_used_mb / 1024 : null); const memGb = memRaw != null ? Number(memRaw) : null; + const memTotal = n.memory_total_gb ?? n.mem_total_gb ?? null; + const memPct = memGb && memTotal ? Math.round((memGb / memTotal) * 100) : null; return ( -
+
- {nodeId} - - {cpuPct != null && CPU {Math.round(cpuPct)}%} - {memGb != null && {memGb < 1 ? Math.round(memGb * 1024) + 'MB' : memGb.toFixed(1) + 'GB'} RAM} + {nodeId} + + {cpuPct != null && ( + + CPU + + 80 ? 'var(--warning)' : 'var(--text-3)' }} /> + + {Math.round(cpuPct)}% + + )} + {memGb != null && ( + + MEM + + 85 ? 'var(--warning)' : 'var(--text-3)' }} /> + + {memGb < 1 ? Math.round(memGb * 1024) + 'MB' : memGb.toFixed(1) + 'GB'} + + )}
); })} @@ -323,6 +355,26 @@ function Dashboard({ navigate }) { ); } +function DashSparkline({ data, color }) { + if (!data || data.length < 2) return
; + const max = Math.max(...data, 1); + const min = Math.min(...data, 0); + const range = max - min || 1; + const h = 24; + const w = 80; + const step = w / (data.length - 1); + const pts = data.map((d, i) => (i * step) + ',' + (h - ((d - min) / range) * h)).join(' '); + const area = '0,' + h + ' ' + pts + ' ' + w + ',' + h; + return ( +
+ + + + +
+ ); +} + function SectionHead({ title, onMore, moreLabel = 'View all' }) { return (
@@ -338,23 +390,25 @@ function SectionHead({ title, onMore, moreLabel = 'View all' }) { function MiniJobRow({ job }) { return ( -
+
-
-
- {job.kind} - · - {job.asset} +
+
+ {job.kind} + · + {job.asset} + {job.node && on {job.node}}
{job.status === 'running' && ( -
-
+
+
+ {job.progress || 0}%
)} - {job.status === 'failed' &&
{job.error}
} + {job.status === 'failed' &&
{job.error}
}
-
- {job.status === 'running' ? job.eta : job.status} +
+ {job.status === 'running' && job.eta ? job.eta : {job.status}}
);