/* global React, Card, StatusGlyph, Ic, Button, VS, LoadingState, ErrorState */ function ComputersScreen() { const [list, setList] = React.useState(null); const [err, setErr] = React.useState(''); const [busyHost, setBusyHost] = React.useState(''); const [confirmDelete, setConfirmDelete] = React.useState(null); const [, force] = React.useState(0); async function load() { try { const d = await VS.computers(); setList(Array.isArray(d) ? d : (d.computers || [])); setErr(''); } catch (e) { setErr(e.message); } } React.useEffect(() => { load(); const t = setInterval(load, 3000); const tick = setInterval(() => force(x => x + 1), 1000); return () => { clearInterval(t); clearInterval(tick); }; }, []); async function restart(host) { setBusyHost(host); try { await VS.restartComputer(host); } catch (e) { setErr(e.message); } finally { setBusyHost(''); load(); } } async function remove(host) { setBusyHost(host); try { await VS.deleteComputer(host); setConfirmDelete(null); } catch (e) { setErr(e.message); } finally { setBusyHost(''); load(); } } if (err && !list) return ; if (!list) return ; return (
Infrastruktura

Komputery klienckie

Stanowiska z agentem synchronizacji. Heartbeat co 10 s, autoreset na zdalne polecenie.
{err && (
{err}
)} {list.length === 0 && (
Brak komputerów
Żaden agent nie zarejestrował jeszcze heartbeatu.
)} {list.length > 0 && (
Hostname
Ostatni heartbeat
Pomiarów
Wersja
Akcje
{list.map((c, i) => { const secAgo = effectiveSeconds(c); const online = secAgo !== null && secAgo < 30; const st = online ? 'ok' : (secAgo !== null && secAgo < 120 ? 'warn' : 'alarm'); const pendingRestart = !!c.restart_requested; return (
{c.hostname}
pid {c.pid || '—'} {c.uptime_seconds ? <> · uptime {formatUptime(c.uptime_seconds)} : null} {c.last_measid ? <> · measid {c.last_measid} : null}
{formatAgoLabel(secAgo)}
{c.last_error && (
{c.last_error}
)}
{c.measurements_sent_total || 0}
{c.version || '—'}
{pendingRestart ? ( restart pending ) : ( )}
); })}
)} {confirmDelete && (
setConfirmDelete(null)}>
e.stopPropagation()} style={{ background: 'var(--surface)', border: '1px solid var(--border-strong)', borderRadius: 'var(--r-md)', padding: 20, width: 360, }}>
Usunąć wpis?
Usuniesz rekord {confirmDelete} z bazy. Jeśli agent nadal działa na tej maszynie, pojawi się ponownie po kolejnym heartbeacie.
)}
); } function effectiveSeconds(c) { if (typeof c.seconds_ago === 'number') { const d = c.last_seen ? new Date(c.last_seen) : null; if (d && !isNaN(d.getTime())) { return Math.max(c.seconds_ago, Math.floor((Date.now() - d.getTime()) / 1000)); } return c.seconds_ago; } if (c.last_seen) { const d = new Date(c.last_seen); if (!isNaN(d.getTime())) return Math.max(0, Math.floor((Date.now() - d.getTime()) / 1000)); } return null; } function formatAgoLabel(secAgo) { if (secAgo === null || secAgo === undefined) return 'nigdy'; if (secAgo < 60) return `${secAgo} s temu`; if (secAgo < 3600) return `${Math.floor(secAgo / 60)} min temu`; if (secAgo < 86400) return `${Math.floor(secAgo / 3600)} h temu`; return `${Math.floor(secAgo / 86400)} d temu`; } function formatUptime(sec) { if (!sec) return '0 s'; if (sec < 60) return `${sec} s`; if (sec < 3600) return `${Math.floor(sec / 60)} min`; if (sec < 86400) return `${Math.floor(sec / 3600)} h`; return `${Math.floor(sec / 86400)} d`; } Object.assign(window, { ComputersScreen });