From d560c15fef95c7264a09c921f8cd2e867753e73c Mon Sep 17 00:00:00 2001 From: SPP User Date: Mon, 26 Oct 2026 14:02:46 +0100 Subject: [PATCH] Veto-Timer als Leiste in Teamfarbe Die verbleibende Zeit des Veto-Schritts erscheint als Leiste mit Sekundenanzeige, in den letzten 10 Sekunden rot. Der Server schickt die Schrittdauer mit, damit die Leiste zu VETO_STEP_SECONDS passt. Co-Authored-By: Claude Opus 5.5 --- client/src/components/VetoBoard.jsx | 14 +++++++++++++- client/src/styles.css | 7 ++++++- server/src/matchService.js | 1 + 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/client/src/components/VetoBoard.jsx b/client/src/components/VetoBoard.jsx index cc07d00..cd8c84a 100644 --- a/client/src/components/VetoBoard.jsx +++ b/client/src/components/VetoBoard.jsx @@ -39,7 +39,7 @@ export default function VetoBoard({ state, viewer, act, busy, compact = false }) ? 'Live-Ansicht – nur Captains können wählen.' : 'Das andere Team ist am Zug.'} - {left !== null && Noch {Math.ceil(left / 1000)} s} + {left !== null && } )} {!current && veto.done && !compact &&
Veto abgeschlossen
} @@ -132,6 +132,18 @@ function statusText(m, teams) { } } +/** Leiste für die verbleibende Zeit des Veto-Schritts. In den letzten 10 Sekunden wird sie rot. */ +function TimerBar({ left, totalMs }) { + const seconds = Math.ceil(left / 1000); + const percent = Math.max(0, Math.min(100, (left / totalMs) * 100)); + return ( +
+
+ {seconds} s +
+ ); +} + /** 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); diff --git a/client/src/styles.css b/client/src/styles.css index 87695de..3a4b89a 100644 --- a/client/src/styles.css +++ b/client/src/styles.css @@ -523,4 +523,9 @@ input:focus, select:focus { border-color: var(--link); outline: none; } .admin-tab:hover { color: var(--text); } .admin-tab.is-active { color: var(--text); border-bottom-color: var(--primary); } -.turn-timer { margin-left: auto; color: var(--muted); font-variant-numeric: tabular-nums; } +.turn-timer { grid-column: 1 / -1; display: flex; align-items: center; gap: 10px; margin-top: 8px; } +.turn-bar { flex: 1; height: 6px; border-radius: 3px; background: color-mix(in srgb, var(--team) 18%, transparent); overflow: hidden; } +.turn-bar span { display: block; height: 100%; background: var(--team); transition: width .25s linear; } +.turn-seconds { min-width: 3ch; text-align: right; color: var(--muted); font-size: .875rem; font-variant-numeric: tabular-nums; } +.turn-timer.is-low .turn-bar span { background: var(--error); } +.turn-timer.is-low .turn-seconds { color: var(--error); font-weight: 600; } diff --git a/server/src/matchService.js b/server/src/matchService.js index a1822e6..40b3d80 100644 --- a/server/src/matchService.js +++ b/server/src/matchService.js @@ -894,6 +894,7 @@ export class MatchService extends EventEmitter { maps: veto.maps, current: match.status === 'veto' ? veto.current : null, remainingMs: this.vetoRemainingMs(match), + stepSeconds: this.config.vetoStepSeconds, done: veto.done, history: actions.map((a) => ({ index: a.idx,