From 07af51b05c38ecd294145932e5119398d938097c Mon Sep 17 00:00:00 2001 From: ZGaetano Date: Fri, 22 May 2026 10:04:24 -0400 Subject: [PATCH] feat(ui): wire screens to live API data; add thumbnail lazy-loading: screens-home.jsx --- services/web-ui/public/screens-home.jsx | 197 +++++++++++------------- 1 file changed, 87 insertions(+), 110 deletions(-) diff --git a/services/web-ui/public/screens-home.jsx b/services/web-ui/public/screens-home.jsx index 031155e..41ee679 100644 --- a/services/web-ui/public/screens-home.jsx +++ b/services/web-ui/public/screens-home.jsx @@ -1,97 +1,115 @@ -// screens-home.jsx - home dashboard - -const { ACTIVITY, RECORDERS, JOBS, ASSETS } = window.ZAMPP_DATA; +// screens-home.jsx function Home({ navigate }) { - const sparkAssets = [12, 14, 13, 18, 16, 22, 25, 24, 28, 30, 32, 34, 38]; - const sparkIngest = [4, 6, 5, 8, 9, 12, 10, 14, 13, 18, 20, 22, 24]; - const sparkJobs = [8, 7, 10, 9, 14, 12, 16, 13, 18, 15, 12, 10, 11]; - const sparkStorage = [42, 44, 46, 47, 48, 50, 52, 54, 56, 58, 60, 62, 64]; + const { RECORDERS, JOBS, ASSETS, NODES } = window.ZAMPP_DATA; - const liveRecorders = RECORDERS.filter(r => r.status === "recording").slice(0, 4); + const liveRecorders = RECORDERS.filter(r => r.status === 'recording').slice(0, 4); + const runningJobs = JOBS.filter(j => j.status === 'running' || j.status === 'queued'); + const failedJobs = JOBS.filter(j => j.status === 'failed').length; + const recentAssets = [...ASSETS].sort((a, b) => new Date(b.created_at) - new Date(a.created_at)).slice(0, 6); + + const spark = (n, base = 10) => Array.from({ length: 13 }, (_, i) => base + Math.round(Math.sin(i * 0.7 + n) * base * 0.3)); return (
-

Good evening, Zach

-

4 recorders live · 3 jobs running · cluster healthy across 4 nodes

+

Dragonflight

+

+ {liveRecorders.length > 0 ? liveRecorders.length + ' live · ' : ''} + {runningJobs.length > 0 ? runningJobs.length + ' job' + (runningJobs.length > 1 ? 's running' : ' running') + ' · ' : ''} + {ASSETS.length.toLocaleString()} assets +

Library
-
1,553
-
+38 today
- +
{ASSETS.length.toLocaleString()}
+
Total assets
+
-
Ingest (24h)
-
412 GB
-
+82 GB last hr
- +
Live feeds
+
{liveRecorders.length}
+
{RECORDERS.length} recorders configured
+
-
Jobs queued
-
3 / 247 done
-
avg 4.2 min
- +
Jobs
+
{runningJobs.length} / {JOBS.filter(j => j.status === 'done').length} done
+
0 ? 'var(--warning)' : '' }}>{failedJobs > 0 ? failedJobs + ' failed' : 'All clear'}
+
-
Object store
-
64% of 18 TB
-
11.5 TB used
- +
Cluster nodes
+
{NODES.filter(n => n.online).length} / {NODES.length} online
+
Last heartbeat <30s
+
- navigate("recorders")} moreLabel="All recorders" /> -
- {liveRecorders.map((r, i) => ( -
navigate("recorders")}> -
- REC -
- -
- {r.name} - {r.elapsed} -
+ {liveRecorders.length > 0 && ( + <> + navigate('recorders')} moreLabel="All recorders" /> +
+ {liveRecorders.map(r => ( +
navigate('recorders')}> +
REC
+ +
+ {r.name} + {r.elapsed} +
+
+ ))}
- ))} -
+
+ + )} -
- - + navigate('library')} moreLabel="All assets" />
- {ACTIVITY.map(a => ( + {recentAssets.length === 0 ? ( +
No assets yet.
+ ) : recentAssets.map(a => (
-
- +
+
- {a.who} {a.what} {a.target} + {a.name} + {a.project && <> in {a.project}}
-
{a.time}
+
{a.updated}
))}
- navigate("jobs")} moreLabel="View all" /> + navigate('jobs')} moreLabel="View all" />
- {JOBS.slice(0, 5).map(j => ( - - ))} + {JOBS.length === 0 + ?
No jobs.
+ : JOBS.slice(0, 5).map(j => )}
- -
- + navigate('cluster')} moreLabel="View all" /> +
+ {NODES.length === 0 + ?
No nodes found.
+ : NODES.slice(0, 4).map(n => ( +
+ + {n.hostname} + + {n.cpu_usage != null && CPU {n.cpu_usage}%} + {n.mem_used_mb != null && {Math.round(n.mem_used_mb / 1024)}GB RAM} +
+ ))}
@@ -99,12 +117,12 @@ function Home({ navigate }) { ); } -function SectionHead({ title, onMore, moreLabel = "View all" }) { +function SectionHead({ title, onMore, moreLabel = 'View all' }) { return ( -
-
{title}
+
+
{title}
{onMore && ( - )} @@ -114,67 +132,26 @@ function SectionHead({ title, onMore, moreLabel = "View all" }) { function MiniJobRow({ job }) { return ( -
+
-
- {job.kind} - · - {job.asset} +
+ {job.kind} + · + {job.asset}
- {job.status === "running" && ( -
-
+ {job.status === 'running' && ( +
+
)} - {job.status === "failed" && ( -
{job.error}
- )} + {job.status === 'failed' &&
{job.error}
}
-
- {job.status === "running" ? job.eta : job.status} +
+ {job.status === 'running' ? job.eta : job.status}
); } -function StorageBar() { - const segments = [ - { label: "Master files", color: "#5B7CFA", value: 38 }, - { label: "Proxies", color: "#2DD4A8", value: 12 }, - { label: "Live captures", color: "#FF3B30", value: 8 }, - { label: "Audio", color: "#B57CFA", value: 3 }, - { label: "Other", color: "#6B7280", value: 3 }, - ]; - const cap = 100; - return ( -
-
-
- 11.5 TB / 18 TB -
- 64% used -
-
- {segments.map((s, i) => ( -
- ))} -
-
- {segments.map((s, i) => ( -
- - {s.label} - {s.value}% -
- ))} -
-
- ); -} - -function iconForKind(kind) { - return { comment: "comment", record: "record", job: "jobs", upload: "upload", sync: "refresh", approve: "check", error: "alert" }[kind] || "clock"; -} - window.Home = Home;