import { useEffect, useMemo, useState } from 'react'; import { useSession } from '../context/SessionContext.jsx'; function classNames(...values) { return values.filter(Boolean).join(' '); } function formatBattery(rover) { const percent = rover?.batteryState?.percentDisplay; if (percent == null) return '--'; return `${percent}%`; } function batteryClass(rover) { if (!rover?.batteryState) return 'text-slate-400'; if (rover.batteryState.urgentActive) return 'text-red-400'; if (rover.batteryState.warnActive) return 'text-amber-300'; return 'text-emerald-300'; } function roleColors(role) { switch (role) { case 'admin': case 'lockdown': case 'lockdown-admin': return 'text-amber-300'; case 'spectator': return 'text-slate-400'; default: return 'text-sky-300'; } } function formatLabel(user, selfId) { if (!user) return ''; const base = user.nickname || user.socketId?.slice(0, 6) || 'unknown'; if (user.socketId && user.socketId === selfId) { return `${base} (you)`; } return base; } export default function RoverQueuesPanel({ title = 'Rovers' }) { const { session, requestControl } = useSession(); const [pending, setPending] = useState({}); const [now, setNow] = useState(() => Date.now()); const canRequest = useMemo(() => session?.role && session.role !== 'spectator', [session?.role]); const roster = session?.roster ?? []; const turnQueues = session?.turnQueues ?? {}; const users = session?.users ?? []; const selfId = session?.socketId || null; const hasDeadlines = useMemo( () => Object.values(turnQueues || {}).some((info) => info?.deadline || info?.idleDeadline), [turnQueues], ); useEffect(() => { if (!hasDeadlines) return undefined; const timer = setInterval(() => { setNow(Date.now()); }, 1000); return () => clearInterval(timer); }, [hasDeadlines]); const rosterItems = useMemo(() => { const known = new Set(roster.map((rover) => String(rover.id))); const extra = Object.keys(turnQueues || {}) .filter((id) => !known.has(String(id))) .map((id) => ({ id, name: id, locked: false, batteryState: null })); return [...roster, ...extra]; }, [roster, turnQueues]); async function handleRequest(targetRoverId) { if (!targetRoverId) return; setPending((prev) => ({ ...prev, [targetRoverId]: true })); try { await requestControl(targetRoverId); } catch (err) { alert(err.message); } finally { setPending((prev) => ({ ...prev, [targetRoverId]: false })); } } const lookupUser = (socketId) => users.find((u) => u.socketId === socketId) || { socketId, nickname: null, role: null }; return (
{title &&

{title}

} {rosterItems.length === 0 ? (

No rovers registered.

) : ( )}
); }