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 (
setSelected(isSel ? null : m.id)}
aria-pressed={isSel}
aria-label={`${meta.name}: ${statusText(m, teams)}`}
>
{meta.name}
{statusText(m, teams)}
);
})}
{choosingMap && (
{selected
? `${mapsById[selected].name} ${current.type === 'ban' ? 'bannen' : 'wählen'}`
: `Map zum ${current.type === 'ban' ? 'Bannen' : 'Wählen'} antippen`}
)}
{yourTurn && current.type === 'side' && (
Auf welcher Seite startet {teams[viewer.team].name} auf {sideMap?.name} ?
submitSide('CT')}>
Counter-Terrorists
Start auf CT, Gegner beginnt als T
submitSide('T')}>
Terrorists
Start auf T, Gegner beginnt als CT
)}
{veto.steps.map((s) => (
{STEP_SHORT[s.type]}
{s.team ? teams[s.team].name : 'automatisch'}
))}
{veto.history.length > 0 && !compact && (
{veto.history.map((h) => (
{clock(h.at)} {h.text}
))}
)}
);
}
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;
}