// Queue Target Row // Purpose: Renders the shared queue-row visual language used by rover queues and PTZ. // Scope: Owns row chrome, queue chips, timer labels, and row/button event plumbing; // callers still own target-specific permission checks and request actions. import RoverLabel from '../RoverLabel/index.jsx'; function classNames(...values) { return values.filter(Boolean).join(' '); } function roleColors(role) { switch (role) { case 'admin': case 'lockdown': return 'text-amber-300'; case 'spectator': return 'text-slate-400'; default: return 'text-sky-300'; } } function formatQueueUserLabel(user, selfId) { /* Queue chips need to be readable even when a socket has no nickname yet. Keeping the socket-prefix fallback here means rover queues and PTZ queues degrade identically instead of each target inventing its own anonymous label. */ if (!user) return ''; const base = user.nickname || user.label || user.socketId?.slice(0, 6) || 'unknown'; if (user.socketId && user.socketId === selfId) { return `${base} (you)`; } return base; } export function QueueUserChips({ targetId, queue = [], currentId = null, nextId = null, selfId = null, lookupUser, }) { if (!queue.length) { return

No queue.

; } return (
{queue.map((socketId, idx) => { const user = lookupUser?.(socketId) || { socketId, nickname: null, role: null }; const isCurrent = socketId === currentId; const isNext = Boolean(nextId && socketId === nextId && !isCurrent); /* These classes intentionally mirror the original rover queue styling. PTZ feeds the same current/next model into this component, so the user does not have to learn a different visual vocabulary for camera turns. */ const highlightClass = isCurrent ? 'bg-sky-600 text-white ring-2 ring-amber-300' : isNext ? 'bg-emerald-700/60 text-emerald-100 ring-1 ring-emerald-300/70' : 'bg-slate-800 text-slate-200'; return ( {formatQueueUserLabel(user, selfId)} {isCurrent && now} {isNext && next} ); })}
); } export default function QueueTargetRow({ target, queue = [], currentId = null, nextId = null, selfId = null, lookupUser, canClick = false, pending = false, locked = false, lockedBlocked = false, privateOpen = false, buttonLabel = '', batteryLabel = '', batteryClassName = 'text-slate-400', timerLabel = '', thumbnailUrl = '', onRequest, showAction = true, }) { const targetId = String(target?.id || ''); const targetLabel = target?.label || target?.name || targetId; return (
  • { /* The whole row is a large target because queue selection is one of the main touch/click actions on the page. The caller still decides whether clicking is currently allowed, so disabled PTZ and locked rover states cannot accidentally request control through the shared renderer. */ if (!canClick) return; onRequest?.(targetId); }} > {thumbnailUrl ? ( ) : null}

    {target?.description ? ( {target.description} ) : null}

    {timerLabel ? ( {timerLabel} ) : null}
    {batteryLabel ? ( {batteryLabel} ) : null}
    {showAction ? ( ) : null}
  • ); }