// screens-home.jsx function Home({ navigate }) { const { RECORDERS, JOBS, ASSETS, NODES } = window.ZAMPP_DATA; 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 nodesOnline = NODES.filter(n => n.status === 'online' || n.online === true).length; const spark = (n, base = 10) => Array.from({ length: 13 }, (_, i) => base + Math.round(Math.sin(i * 0.7 + n) * base * 0.3)); return (

Dragonflight

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

Library
{ASSETS.length.toLocaleString()}
Total assets
Live feeds
{liveRecorders.length}
{RECORDERS.length} recorders configured
Jobs
{runningJobs.length} / {JOBS.filter(j => j.status === 'done').length} done
0 ? 'var(--warning)' : '' }}>{failedJobs > 0 ? failedJobs + ' failed' : 'All clear'}
Cluster nodes
{nodesOnline} / {NODES.length} online
Last heartbeat <30s
{liveRecorders.length > 0 && ( <> navigate('recorders')} moreLabel="All recorders" />
{liveRecorders.map(r => (
navigate('recorders')}>
REC
{r.name} {r.elapsed}
))}
)} navigate('library')} moreLabel="All assets" />
{recentAssets.length === 0 ? (
No assets yet.
) : recentAssets.map(a => (
{a.name} {a.project && <> in {a.project}}
{a.updated}
))}
navigate('jobs')} moreLabel="View all" />
{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 => { 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; return (
{nodeId} {cpuPct != null && CPU {Math.round(cpuPct)}%} {memGb != null && {memGb < 1 ? Math.round(memGb * 1024) + 'MB' : memGb.toFixed(1) + 'GB'} RAM}
); })}
); } function SectionHead({ title, onMore, moreLabel = 'View all' }) { return (
{title}
{onMore && ( )}
); } function MiniJobRow({ job }) { return (
{job.kind} · {job.asset}
{job.status === 'running' && (
)} {job.status === 'failed' &&
{job.error}
}
{job.status === 'running' ? job.eta : job.status}
); } window.Home = Home;