// Admin Panel Content // Purpose: Defines the Admin Panel Content 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 { useEffect, useMemo, useState } from 'react'; import { useSession } from '../../context/SessionContext.jsx'; import RoverRoster from '../RoverRoster/index.jsx'; import LlmCommentaryPanel from './LlmCommentaryPanel.jsx'; import OverseerControlPanel from './OverseerControlPanel.jsx'; import ReplaySnapshotHealth from './ReplaySnapshotHealth.jsx'; import AdminIpLogPanel from './AdminIpLogPanel.jsx'; const MODES = [ { key: 'open', label: 'Open' }, { key: 'turns', label: 'Turns' }, { key: 'admin', label: 'Admin' }, { key: 'lockdown', label: 'Lockdown' }, ]; export default function AdminPanelContent() { const { session, lockRover, setMode, requestControl, setGlobalObjective, setAdminReason, rebootRover, rebootServer, setAudioLevels, setPrivateSafety, llmControl, overseerControl, adminLogs, llmCommentaryState, overseerControlState, } = useSession(); const roster = useMemo(() => session?.roster ?? [], [session?.roster]); const [lockStates, setLockStates] = useState({}); const [rebootStates, setRebootStates] = useState({}); const [serverRebooting, setServerRebooting] = useState(false); const [clearingLlmHistory, setClearingLlmHistory] = useState(false); const [clearingOverseerHistory, setClearingOverseerHistory] = useState(false); const health = session?.health || null; const currentGoal = session?.globalObjective?.text || ''; const goalUpdatedAt = session?.globalObjective?.updatedAt || null; const [goalDraft, setGoalDraft] = useState(currentGoal); const currentReason = session?.adminReason?.text || ''; const reasonUpdatedAt = session?.adminReason?.updatedAt || null; const [reasonDraft, setReasonDraft] = useState(currentReason); const currentAudioLevels = session?.audioLevels || {}; const [audioLevelDraft, setAudioLevelDraft] = useState({ hornGain: Number.isFinite(currentAudioLevels.hornGain) ? currentAudioLevels.hornGain : 1, ttsGain: Number.isFinite(currentAudioLevels.ttsGain) ? currentAudioLevels.ttsGain : 1, forwardGain: Number.isFinite(currentAudioLevels.forwardGain) ? currentAudioLevels.forwardGain : 1, }); const [privateSafetyDrafts, setPrivateSafetyDrafts] = useState({}); const isAdmin = session?.role === 'admin' || session?.role === 'lockdown' || session?.role === 'lockdown-admin'; const isLockdownAdmin = session?.role === 'lockdown' || session?.role === 'lockdown-admin'; const currentMode = session?.mode ?? 'open'; const handleLockToggle = async (roverId, locked) => { try { await lockRover(roverId, locked); setLockStates((prev) => ({ ...prev, [roverId]: locked })); } catch (err) { alert(err.message); } }; const handleModeChange = async (event) => { const mode = event.target.value; try { await setMode(mode); } catch (err) { alert(err.message); } }; const handleForceControl = async (roverId) => { try { await requestControl(roverId, { force: true }); } catch (err) { alert(err.message); } }; const handleReboot = async (rover) => { if (!rover?.id) return; const ok = window.confirm(`Reboot rover "${rover.name || rover.id}" now?`); if (!ok) return; setRebootStates((prev) => ({ ...prev, [rover.id]: true })); try { await rebootRover(rover.id); } catch (err) { alert(err.message); } finally { setRebootStates((prev) => ({ ...prev, [rover.id]: false })); } }; const handleServerReboot = async () => { const ok = window.confirm('Reboot the server host now? This will disconnect all users.'); if (!ok) return; setServerRebooting(true); try { await rebootServer(); } catch (err) { alert(err.message); setServerRebooting(false); } }; const handleClearLlmHistory = async () => { const ok = window.confirm( 'Clear LLM commentary history now? This resets chat context, bot memory, and rover activity metrics for narration.', ); if (!ok) return; setClearingLlmHistory(true); try { await llmControl('clearHistory'); } catch (err) { alert(err.message); } finally { setClearingLlmHistory(false); } }; const handleClearOverseerHistory = async () => { const ok = window.confirm('Clear Overseer Control history now?'); if (!ok) return; setClearingOverseerHistory(true); try { await overseerControl('clearHistory'); } catch (err) { alert(err.message); } finally { setClearingOverseerHistory(false); } }; const handleGoalSave = async () => { try { await setGlobalObjective(goalDraft); } catch (err) { alert(err.message); } }; const handleGoalClear = async () => { try { await setGlobalObjective(null); } catch (err) { alert(err.message); } }; const handleReasonSave = async () => { try { await setAdminReason(reasonDraft); } catch (err) { alert(err.message); } }; const handleReasonClear = async () => { try { await setAdminReason(null); } catch (err) { alert(err.message); } }; const handleAudioLevelDraft = (key) => (event) => { const next = Number(event.target.value); setAudioLevelDraft((current) => ({ ...(current || {}), [key]: Number.isFinite(next) ? next : 1 })); }; const handleAudioLevelsSave = async () => { try { await setAudioLevels(audioLevelDraft); } catch (err) { alert(err.message); } }; const handleTestRewardOverlay = async () => { window.dispatchEvent( new CustomEvent('buttonBox:rewardRunLocalTest', { detail: { rewardName: 'Admin Mode', count: 1000, goal: 1000, }, }), ); }; useEffect(() => { setGoalDraft(currentGoal); }, [currentGoal]); useEffect(() => { setReasonDraft(currentReason); }, [currentReason]); useEffect(() => { setAudioLevelDraft({ hornGain: Number.isFinite(currentAudioLevels.hornGain) ? currentAudioLevels.hornGain : 1, ttsGain: Number.isFinite(currentAudioLevels.ttsGain) ? currentAudioLevels.ttsGain : 1, forwardGain: Number.isFinite(currentAudioLevels.forwardGain) ? currentAudioLevels.forwardGain : 1, }); }, [currentAudioLevels.forwardGain, currentAudioLevels.hornGain, currentAudioLevels.ttsGain]); useEffect(() => { const next = {}; (roster || []).forEach((rover) => { if (!rover?.private?.enabled) return; next[rover.id] = { speedLimitEnabled: Boolean(rover?.private?.safety?.speedLimitEnabled), speedLimitMaxWheelSpeed: Number.isFinite(rover?.private?.safety?.speedLimitMaxWheelSpeed) ? rover.private.safety.speedLimitMaxWheelSpeed : 250, hardOvercurrentEnabled: Boolean(rover?.private?.safety?.hardOvercurrentEnabled), overcurrentStopMs: Number.isFinite(rover?.private?.safety?.overcurrentStopMs) ? rover.private.safety.overcurrentStopMs : 300, hardBumpEnabled: Boolean(rover?.private?.safety?.hardBumpEnabled), bumpBackoffSpeed: Number.isFinite(rover?.private?.safety?.bumpBackoffSpeed) ? rover.private.safety.bumpBackoffSpeed : 250, bumpBackoffMs: Number.isFinite(rover?.private?.safety?.bumpBackoffMs) ? rover.private.safety.bumpBackoffMs : 350, cliffEnabled: Boolean(rover?.private?.safety?.cliffEnabled), cliffBackoffSpeed: Number.isFinite(rover?.private?.safety?.cliffBackoffSpeed) ? rover.private.safety.cliffBackoffSpeed : 250, cliffBackoffMs: Number.isFinite(rover?.private?.safety?.cliffBackoffMs) ? rover.private.safety.cliffBackoffMs : 500, triggerCooldownMs: Number.isFinite(rover?.private?.safety?.triggerCooldownMs) ? rover.private.safety.triggerCooldownMs : 800, }; }); setPrivateSafetyDrafts(next); }, [roster]); const lockMap = useMemo(() => { const map = {}; roster.forEach((rover) => { map[rover.id] = lockStates[rover.id] ?? rover.locked; }); return map; }, [roster, lockStates]); const updatePrivateSafetyDraft = (roverId, patch = {}) => { setPrivateSafetyDrafts((current) => ({ ...(current || {}), [roverId]: { ...(current?.[roverId] || {}), ...(patch || {}) }, })); }; const handlePrivateSafetySave = async (roverId) => { try { const draft = privateSafetyDrafts?.[roverId]; if (!draft) return; await setPrivateSafety(roverId, draft); } catch (err) { alert(err.message); } }; if (!isAdmin) return null; return (
Admin controls
Global audio levels {session?.audioLevels?.updatedAt ? ( Updated {new Date(session.audioLevels.updatedAt).toLocaleString()} ) : null}
Global objective {goalUpdatedAt ? ( Updated {new Date(goalUpdatedAt).toLocaleString()} ) : null}
setGoalDraft(event.target.value)} placeholder="Set a global objective" className="field-input text-sm" />
Admin mode reason {reasonUpdatedAt ? ( Updated {new Date(reasonUpdatedAt).toLocaleString()} ) : null}