mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 09:31:20 -04:00
76 lines
3.5 KiB
React
76 lines
3.5 KiB
React
// Replay Snapshot Health
|
|
// Purpose: Defines the Replay Snapshot Health module and the local helpers/components used in this file.
|
|
// Scope: Keeps behavior unchanged while isolating this concern into a clear, single-responsibility unit.
|
|
import { roverNameChromeStyle } from '../../lib/roverColor.js';
|
|
|
|
export default function ReplaySnapshotHealth({ health, roster = [] }) {
|
|
if (!health) return null;
|
|
const replay = health.replay || { sources: [], readyCount: 0, totalCount: 0 };
|
|
const snapshots = health.snapshots || { rovers: [], rooms: [] };
|
|
const roverColorFor = (id) => roster.find((entry) => String(entry.id) === String(id))?.color || null;
|
|
const replaySummary = `${replay.readyCount}/${replay.totalCount} sources ready`;
|
|
const roverStale = snapshots.rovers.filter((entry) => entry.stale).length;
|
|
const roomStale = snapshots.rooms.filter((entry) => entry.stale).length;
|
|
return (
|
|
<div className="space-y-0.5">
|
|
<div className="panel-muted text-xs uppercase">Health</div>
|
|
<div className="surface space-y-0.5 text-xs text-slate-200">
|
|
<div className="flex items-center justify-between">
|
|
<span>Replay segments</span>
|
|
<span className="text-slate-400">{replaySummary}</span>
|
|
</div>
|
|
<div className="flex items-center justify-between">
|
|
<span>Rover snapshots</span>
|
|
<span className={roverStale ? 'text-amber-300' : 'text-emerald-300'}>
|
|
{snapshots.rovers.length - roverStale}/{snapshots.rovers.length} ok
|
|
</span>
|
|
</div>
|
|
<div className="flex items-center justify-between">
|
|
<span>Room cameras</span>
|
|
<span className={roomStale ? 'text-amber-300' : 'text-emerald-300'}>
|
|
{snapshots.rooms.length - roomStale}/{snapshots.rooms.length} ok
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div className="space-y-0.5 text-xs text-slate-300">
|
|
{replay.sources.map((source) => (
|
|
<div key={`${source.type}:${source.id}`} className="flex items-center justify-between">
|
|
<span
|
|
className={`${source.type === 'rover' ? 'rounded px-1 py-[1px] border border-transparent' : ''}`}
|
|
style={source.type === 'rover' ? roverNameChromeStyle(roverColorFor(source.id), 0.16) : undefined}
|
|
>
|
|
{source.label}
|
|
</span>
|
|
<span className={source.ready ? 'text-emerald-300' : 'text-amber-300'}>
|
|
{source.recentCount}/{source.neededCount}
|
|
</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className="space-y-0.5 text-xs text-slate-300">
|
|
{snapshots.rovers.map((entry) => (
|
|
<div key={`rover:${entry.id}`} className="flex items-center justify-between">
|
|
<span
|
|
className="rounded px-1 py-[1px] border border-transparent"
|
|
style={roverNameChromeStyle(roverColorFor(entry.id), 0.16)}
|
|
>
|
|
{entry.name}
|
|
</span>
|
|
<span className={entry.stale ? 'text-amber-300' : 'text-emerald-300'}>{entry.stale ? 'stale' : 'ok'}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className="space-y-0.5 text-xs text-slate-300">
|
|
{snapshots.rooms.map((entry) => (
|
|
<div key={`room:${entry.id}`} className="flex items-center justify-between">
|
|
<span>{entry.name}</span>
|
|
<span className={entry.stale ? 'text-amber-300' : 'text-emerald-300'}>
|
|
{entry.error ? 'error' : entry.stale ? 'stale' : 'ok'}
|
|
</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|