mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 09:31:20 -04:00
guh.,
This commit is contained in:
@@ -57,13 +57,13 @@ export default function AdminPanel() {
|
||||
if (!isAdmin) return null;
|
||||
|
||||
return (
|
||||
<section className="rounded-sm bg-[#242a32] p-2 text-base text-slate-100">
|
||||
<div className="flex items-center justify-between gap-2 text-sm">
|
||||
<section className="rounded-sm bg-[#242a32] p-1 text-base text-slate-100">
|
||||
<div className="flex items-center justify-between gap-1 text-sm">
|
||||
<span>Admin controls</span>
|
||||
<select
|
||||
value={currentMode}
|
||||
onChange={handleModeChange}
|
||||
className="rounded-sm bg-black/40 px-2 py-1 text-sm text-slate-100"
|
||||
className="rounded-sm bg-black/40 px-1 py-1 text-sm text-slate-100"
|
||||
>
|
||||
{MODES.map((mode) => (
|
||||
<option key={mode.key} value={mode.key}>
|
||||
@@ -73,9 +73,9 @@ export default function AdminPanel() {
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="mt-2 space-y-2 text-sm">
|
||||
<div className="mt-1 space-y-1 text-sm">
|
||||
{roster.map((rover) => (
|
||||
<div key={rover.id} className="flex flex-wrap items-center justify-between gap-2 rounded-sm bg-black/30 px-2 py-2">
|
||||
<div key={rover.id} className="flex flex-wrap items-center justify-between gap-1 rounded-sm bg-black/30 px-1 py-1">
|
||||
<div>
|
||||
<p className="text-slate-100">{rover.name}</p>
|
||||
<p className="text-xs text-slate-400">{lockMap[rover.id] ? 'locked' : 'unlocked'}</p>
|
||||
@@ -84,14 +84,14 @@ export default function AdminPanel() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleLockToggle(rover.id, !lockMap[rover.id])}
|
||||
className="rounded-sm bg-black/50 px-2 py-1 text-slate-100"
|
||||
className="rounded-sm bg-black/50 px-1 py-1 text-slate-100"
|
||||
>
|
||||
{lockMap[rover.id] ? 'Unlock' : 'Lock'}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleForceControl(rover.id)}
|
||||
className="rounded-sm bg-black/50 px-2 py-1 text-slate-100"
|
||||
className="rounded-sm bg-black/50 px-1 py-1 text-slate-100"
|
||||
>
|
||||
Force
|
||||
</button>
|
||||
|
||||
@@ -46,7 +46,7 @@ function AlertToast({ alert }) {
|
||||
? 'bg-amber-500/30 text-amber-100'
|
||||
: 'bg-emerald-500/30 text-emerald-100';
|
||||
return (
|
||||
<div className={`pointer-events-auto rounded-sm px-2 py-1 text-[0.75rem] shadow-lg shadow-black/60 ${colorClasses}`}>
|
||||
<div className={`pointer-events-auto rounded-sm px-1 py-1 text-[0.75rem] shadow-lg shadow-black/60 ${colorClasses}`}>
|
||||
<p className="text-[0.65rem] text-slate-300">{alert.title || 'Alert'}</p>
|
||||
<p className="text-sm text-white">{alert.message}</p>
|
||||
</div>
|
||||
|
||||
@@ -22,17 +22,17 @@ export default function AuthPanel() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rounded-sm bg-[#242a32] p-2 text-base text-slate-100">
|
||||
<div className="rounded-sm bg-[#242a32] p-1 text-base text-slate-100">
|
||||
<p className="text-sm text-slate-400">Admin login</p>
|
||||
<form className="mt-2 flex flex-col gap-2" onSubmit={handleLogin}>
|
||||
<form className="mt-1 flex flex-col gap-1" onSubmit={handleLogin}>
|
||||
<input
|
||||
className="rounded-sm bg-black/50 px-2 py-1 text-slate-100"
|
||||
className="rounded-sm bg-black/50 px-1 py-1 text-slate-100"
|
||||
placeholder="Username"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
/>
|
||||
<input
|
||||
className="rounded-sm bg-black/50 px-2 py-1 text-slate-100"
|
||||
className="rounded-sm bg-black/50 px-1 py-1 text-slate-100"
|
||||
placeholder="Password"
|
||||
type="password"
|
||||
value={password}
|
||||
@@ -41,23 +41,23 @@ export default function AuthPanel() {
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="rounded-sm bg-slate-200 px-2 py-1 text-sm font-semibold text-black disabled:opacity-50"
|
||||
className="rounded-sm bg-slate-200 px-1 py-1 text-sm font-semibold text-black disabled:opacity-50"
|
||||
>
|
||||
{loading ? 'Logging in…' : 'Login'}
|
||||
</button>
|
||||
</form>
|
||||
<div className="mt-2 flex gap-2 text-sm">
|
||||
<div className="mt-1 flex gap-1 text-sm">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setRole('user')}
|
||||
className="flex-1 rounded-sm bg-black/40 px-2 py-1 text-slate-200"
|
||||
className="flex-1 rounded-sm bg-black/40 px-1 py-1 text-slate-200"
|
||||
>
|
||||
Driver
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setRole('spectator')}
|
||||
className="flex-1 rounded-sm bg-black/40 px-2 py-1 text-slate-200"
|
||||
className="flex-1 rounded-sm bg-black/40 px-1 py-1 text-slate-200"
|
||||
>
|
||||
Spectator
|
||||
</button>
|
||||
|
||||
@@ -19,12 +19,12 @@ export default function DrivePanel() {
|
||||
const updated = frame?.receivedAt ? new Date(frame.receivedAt).toLocaleTimeString() : null;
|
||||
|
||||
return (
|
||||
<section className="rounded-sm bg-[#242a32] p-2 text-base text-slate-100">
|
||||
<section className="rounded-sm bg-[#242a32] p-1 text-base text-slate-100">
|
||||
<div className="flex items-center justify-between text-sm text-slate-300">
|
||||
<span>Drive control</span>
|
||||
<span>{roverId ? `Rover ${roverId}` : 'unassigned'}</span>
|
||||
</div>
|
||||
<div className="mt-2 space-y-2">
|
||||
<div className="mt-1 space-y-1">
|
||||
<ActionCard
|
||||
title="Start Driving"
|
||||
description="Press to enable driving mode, then start moving. The headlamps should illuminate."
|
||||
@@ -55,7 +55,7 @@ export default function DrivePanel() {
|
||||
type="button"
|
||||
onClick={() => sendOiCommand(btn.key)}
|
||||
disabled={!roverId}
|
||||
className="rounded-sm bg-black/40 px-2 py-0.5 text-sm text-slate-200 disabled:opacity-30"
|
||||
className="rounded-sm bg-black/40 px-1 py-0.5 text-sm text-slate-200 disabled:opacity-30"
|
||||
>
|
||||
{btn.label}
|
||||
</button>
|
||||
@@ -67,13 +67,13 @@ export default function DrivePanel() {
|
||||
type="button"
|
||||
onClick={stopMotors}
|
||||
disabled={!roverId}
|
||||
className="flex-1 rounded-sm bg-red-600/70 px-2 py-1 text-sm text-white disabled:opacity-40"
|
||||
className="flex-1 rounded-sm bg-red-600/70 px-1 py-1 text-sm text-white disabled:opacity-40"
|
||||
>
|
||||
Stop motors
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p className="mt-2 text-sm text-slate-400">Sensor streaming auto-starts after each OI change.</p>
|
||||
<p className="mt-1 text-sm text-slate-400">Sensor streaming auto-starts after each OI change.</p>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -85,15 +85,15 @@ function ActionCard({ title, description, statuses, tone, onClick, disabled, foo
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
className={`w-full rounded-lg ${baseColor} px-3 py-2 text-left text-white disabled:opacity-40`}
|
||||
className={`w-full rounded-lg ${baseColor} px-1 py-1 text-left text-white disabled:opacity-40`}
|
||||
>
|
||||
<p className="text-base font-semibold">{title}</p>
|
||||
<p className="text-sm text-white/90">{description}</p>
|
||||
<div className="mt-2 flex flex-wrap gap-1">
|
||||
<div className="mt-1 flex flex-wrap gap-1">
|
||||
{statuses.map((status) => (
|
||||
<span
|
||||
key={status.label}
|
||||
className={`rounded-full px-3 py-0.5 text-xs font-semibold ${
|
||||
className={`rounded-full px-1 py-0.5 text-xs font-semibold ${
|
||||
status.active ? 'bg-lime-300 text-emerald-900' : 'bg-emerald-900 text-emerald-100'
|
||||
}`}
|
||||
>
|
||||
@@ -108,7 +108,7 @@ function ActionCard({ title, description, statuses, tone, onClick, disabled, foo
|
||||
|
||||
function SpeedRow({ left, right }) {
|
||||
return (
|
||||
<div className="grid grid-cols-2 gap-1 rounded-sm bg-black/40 p-2 text-base">
|
||||
<div className="grid grid-cols-2 gap-1 rounded-sm bg-black/40 p-1 text-base">
|
||||
<div>
|
||||
<p className="text-sm text-slate-300">Left</p>
|
||||
<p className="font-semibold text-slate-100">{left}</p>
|
||||
@@ -124,7 +124,7 @@ function SpeedRow({ left, right }) {
|
||||
function StatusPill({ label, active }) {
|
||||
return (
|
||||
<span
|
||||
className={`rounded-sm px-2 py-0.5 text-xs ${
|
||||
className={`rounded-sm px-1 py-0.5 text-xs ${
|
||||
active ? 'bg-emerald-500/30 text-emerald-100' : 'bg-black/40 text-slate-500'
|
||||
}`}
|
||||
>
|
||||
|
||||
@@ -3,12 +3,12 @@ import { useSession } from '../context/SessionContext.jsx';
|
||||
export default function LogPanel() {
|
||||
const { logs } = useSession();
|
||||
return (
|
||||
<div className="rounded-sm bg-[#242a32] p-2 text-base text-slate-100">
|
||||
<div className="rounded-sm bg-[#242a32] p-1 text-base text-slate-100">
|
||||
<div className="flex items-center justify-between text-sm text-slate-400">
|
||||
<span>Server logs</span>
|
||||
<span>{logs.length}</span>
|
||||
</div>
|
||||
<div className="mt-2 h-48 overflow-y-auto rounded-sm bg-black/40 p-2 font-mono text-sm text-slate-300">
|
||||
<div className="mt-1 h-48 overflow-y-auto rounded-sm bg-black/40 p-1 font-mono text-sm text-slate-300">
|
||||
{logs.length === 0 ? (
|
||||
<p>No logs yet.</p>
|
||||
) : (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export default function RoomCameraPanel() {
|
||||
return (
|
||||
<div className="min-h-[8rem] rounded-sm bg-[#242a32] p-2 text-base text-slate-100">
|
||||
<div className="min-h-[8rem] rounded-sm bg-[#242a32] p-1 text-base text-slate-100">
|
||||
<p className="text-center text-sm text-slate-400">Room camera</p>
|
||||
<p className="mt-1 text-center">Feed placeholder. Wire upcoming room cam here.</p>
|
||||
</div>
|
||||
|
||||
@@ -38,7 +38,7 @@ export default function TelemetryPanel() {
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="rounded-sm bg-[#242a32] p-2 text-base text-slate-100">
|
||||
<section className="rounded-sm bg-[#242a32] p-1 text-base text-slate-100">
|
||||
<div className="text-sm text-slate-400">
|
||||
<span>{connected ? 'online' : 'offline'}</span>
|
||||
<span> · role {session?.role || 'unknown'}</span>
|
||||
@@ -50,7 +50,7 @@ export default function TelemetryPanel() {
|
||||
) : !frame ? (
|
||||
<p className="mt-1 text-[0.75rem] text-slate-400">Waiting for sensor frames…</p>
|
||||
) : (
|
||||
<div className="mt-2 space-y-1 text-base">
|
||||
<div className="mt-1 space-y-1 text-base">
|
||||
<Metric label="Charge" value={formatMetric(charge != null && capacity != null ? `${charge}/${capacity} mAh` : null)} />
|
||||
<Metric label="Charging" value={formatMetric(sensors.chargingState?.label)} />
|
||||
<Metric label="OI mode" value={formatMetric(sensors.oiMode?.label)} />
|
||||
@@ -59,16 +59,16 @@ export default function TelemetryPanel() {
|
||||
</div>
|
||||
)}
|
||||
{rawSnippet && (
|
||||
<pre className="mt-2 overflow-hidden rounded-sm bg-black/60 p-2 text-sm text-lime-300">
|
||||
<pre className="mt-1 overflow-hidden rounded-sm bg-black/60 p-1 text-sm text-lime-300">
|
||||
{rawSnippet}
|
||||
</pre>
|
||||
)}
|
||||
<div className="mt-2">
|
||||
<div className="mt-1">
|
||||
<p className="text-sm text-slate-400">Rovers</p>
|
||||
{roster.length === 0 ? (
|
||||
<p className="text-sm text-slate-500">No roster data.</p>
|
||||
) : (
|
||||
<ul className="mt-2 space-y-1 text-sm">
|
||||
<ul className="mt-1 space-y-1 text-sm">
|
||||
{roster.map((rover) => (
|
||||
<li key={rover.id} className="flex items-center justify-between gap-1">
|
||||
<div>
|
||||
@@ -82,7 +82,7 @@ export default function TelemetryPanel() {
|
||||
type="button"
|
||||
onClick={() => handleRequest(rover.id)}
|
||||
disabled={pending[rover.id]}
|
||||
className="rounded-sm bg-black/40 px-2 py-0.5 text-xs text-slate-200 disabled:opacity-40"
|
||||
className="rounded-sm bg-black/40 px-1 py-0.5 text-xs text-slate-200 disabled:opacity-40"
|
||||
>
|
||||
{pending[rover.id] ? '...' : 'request'}
|
||||
</button>
|
||||
|
||||
@@ -153,7 +153,7 @@ function BatteryBar({ charge, config, label, status }) {
|
||||
const urgent = config?.Urgent ?? null;
|
||||
if (charge == null || full == null || warn == null) {
|
||||
return (
|
||||
<div className="rounded-sm bg-[#1e1e1e] px-2 py-1 text-sm text-slate-200">
|
||||
<div className="rounded-sm bg-[#1e1e1e] px-1 py-1 text-sm text-slate-200">
|
||||
<div className="flex items-center justify-between text-xs text-slate-400">
|
||||
<span>{label}</span>
|
||||
<span>{status}</span>
|
||||
@@ -173,7 +173,7 @@ function BatteryBar({ charge, config, label, status }) {
|
||||
const warnTriggered = urgent != null && charge <= urgent;
|
||||
const barClass = depleted ? 'bg-red-500 animate-pulse' : warnTriggered ? 'bg-amber-400' : 'bg-emerald-500';
|
||||
return (
|
||||
<div className="space-y-2 rounded-sm bg-[#1e1e1e] px-2 py-2 text-sm text-slate-200">
|
||||
<div className="space-y-2 rounded-sm bg-[#1e1e1e] px-1 py-1 text-sm text-slate-200">
|
||||
<div className="flex items-center justify-between text-xs text-slate-400">
|
||||
<span>{label}</span>
|
||||
<span>{status}</span>
|
||||
|
||||
Reference in New Issue
Block a user