import { useEffect, useState } from 'react'; import { clock } from '../lib/labels.js'; import MapImage from './MapImage.jsx'; import SideIcon from './SideIcon.jsx'; const STEP_SHORT = { ban: 'Ban', pick: 'Pick', side: 'Seite', decider: 'Decider' }; export default function VetoBoard({ state, viewer, act, busy, compact = false }) { const { veto, teams } = state; const { current } = veto; const [selected, setSelected] = useState(null); useEffect(() => setSelected(null), [current?.index]); const left = useCountdown(veto.remainingMs); const mapsById = Object.fromEntries(state.maps.map((m) => [m.id, m])); const yourTurn = !!current && viewer.isCaptain && viewer.team === current.team; const choosingMap = yourTurn && (current.type === 'ban' || current.type === 'pick'); const sideMap = current?.type === 'side' ? mapsById[current.map] : null; const submitMap = () => act('veto/action', { stepIndex: current.index, map: selected }); const submitSide = (side) => act('veto/action', { stepIndex: current.index, side }); return (
{current && (
{teams[current.team].name} {current.type === 'ban' && 'bannt eine Map'} {current.type === 'pick' && 'wählt eine Map'} {current.type === 'side' && `wählt die Startseite auf ${sideMap?.name}`} {yourTurn ? 'Du bist am Zug.' : viewer.team === current.team ? 'Dein Captain entscheidet.' : !viewer.team ? 'Live-Ansicht – nur Captains können wählen.' : 'Das andere Team ist am Zug.'} {left !== null && Noch {Math.ceil(left / 1000)} s}
)} {!current && veto.done && !compact &&
Veto abgeschlossen
}
{veto.maps.map((m) => { const meta = mapsById[m.id]; const selectable = choosingMap && m.status === 'available'; const isSel = selected === m.id; return ( ); })}
{choosingMap && (
)} {yourTurn && current.type === 'side' && (

Auf welcher Seite startet {teams[viewer.team].name} auf {sideMap?.name}?

)}
    {veto.steps.map((s) => (
  1. {STEP_SHORT[s.type]} {s.team ? teams[s.team].name : 'automatisch'}
  2. ))}
{veto.history.length > 0 && !compact && (
    {veto.history.map((h) => (
  1. {h.text}
  2. ))}
)}
); } function statusText(m, teams) { switch (m.status) { case 'banned': return `Gebannt von ${teams[m.by].name}`; case 'picked': return `Gewählt von ${teams[m.by].name}`; case 'decider': return 'Entscheidungsmap'; default: return 'Verfügbar'; } } /** Verbleibende Zeit des Veto-Schritts: der Server setzt sie bei jeder Änderung, danach zählt die Anzeige lokal herunter. */ function useCountdown(ms) { const [left, setLeft] = useState(ms ?? null); useEffect(() => { setLeft(ms ?? null); if (ms == null) return undefined; const started = Date.now(); const t = setInterval(() => setLeft(Math.max(0, ms - (Date.now() - started))), 250); return () => clearInterval(t); }, [ms]); return left; }