Lobby zeigt konkret, was einem Team zum Veto-Start fehlt
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
568ccf9b09
commit
3999c4b111
3 files changed
+38
-12
No files matched your search
@@ -126,19 +126,26 @@ function Stage(ctx) {
|
|||||||
const missing = [A, B].filter((t) => !t.readiness.ready);
|
const missing = [A, B].filter((t) => !t.readiness.ready);
|
||||||
return (
|
return (
|
||||||
<div className="stage-note">
|
<div className="stage-note">
|
||||||
<h2>Warten auf beide Teams</h2>
|
<h2>{missing.length === 0 ? 'Beide Teams sind bereit' : 'Warten auf beide Teams'}</h2>
|
||||||
<p className="muted">
|
{missing.length > 0 && (
|
||||||
{missing.length === 0
|
<ul className="missing-list">
|
||||||
? 'Beide Teams sind bereit.'
|
{missing.map((t) => <li key={t.key}><strong className={`team-${t.key.toLowerCase()}`}>{t.name}:</strong> {readinessGap(t, state.requireFullTeams)}</li>)}
|
||||||
: missing.map((t) => `${t.name}: ${t.readiness.count}/${t.teamSize} Spieler, ${t.readiness.allReady ? 'alle bereit' : 'noch nicht alle bereit'}`).join(' – ')}
|
</ul>
|
||||||
</p>
|
)}
|
||||||
{isOrg ? (
|
{isOrg ? (
|
||||||
<button className="btn btn-primary btn-lg" disabled={!state.canStartVeto || !!busy}
|
<>
|
||||||
onClick={() => act('veto/start', null, 'Veto gestartet')}>
|
<button className="btn btn-primary btn-lg" disabled={!state.canStartVeto || !!busy}
|
||||||
Veto starten
|
onClick={() => act('veto/start', null, 'Veto gestartet')}>
|
||||||
</button>
|
Veto starten
|
||||||
|
</button>
|
||||||
|
{!state.canStartVeto && <p className="small muted">Der Knopf wird aktiv, sobald beide Teams bereit sind.</p>}
|
||||||
|
</>
|
||||||
) : (
|
) : (
|
||||||
<p className="small muted">Der Organisator startet das Veto, sobald beide Teams bereit sind.</p>
|
<p className="small muted">
|
||||||
|
{missing.length === 0
|
||||||
|
? 'Der Organisator kann das Veto jetzt starten.'
|
||||||
|
: 'Sobald beide Teams bereit sind, startet der Organisator das Veto.'}
|
||||||
|
</p>
|
||||||
)}
|
)}
|
||||||
<MapPoolPreview maps={state.maps} />
|
<MapPoolPreview maps={state.maps} />
|
||||||
</div>
|
</div>
|
||||||
@@ -153,6 +160,21 @@ function Stage(ctx) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Sagt konkret, was einem Team noch fehlt, damit es als bereit gilt. */
|
||||||
|
function readinessGap(team, requireFull) {
|
||||||
|
const r = team.readiness;
|
||||||
|
const gaps = [];
|
||||||
|
if (!r.enough) {
|
||||||
|
const need = requireFull ? team.teamSize - r.count : 1;
|
||||||
|
gaps.push(requireFull
|
||||||
|
? `noch ${need} von ${team.teamSize} Spielern fehlen (Team muss vollständig sein)`
|
||||||
|
: 'mindestens ein Spieler fehlt');
|
||||||
|
}
|
||||||
|
if (r.count > 0 && !r.allReady) gaps.push('noch nicht alle haben „bereit“ geklickt');
|
||||||
|
if (r.count > 0 && !r.hasCaptain) gaps.push('kein Captain');
|
||||||
|
return gaps.join(', ');
|
||||||
|
}
|
||||||
|
|
||||||
function MapPoolPreview({ maps }) {
|
function MapPoolPreview({ maps }) {
|
||||||
return (
|
return (
|
||||||
<div className="pool-preview" aria-label="Map-Pool">
|
<div className="pool-preview" aria-label="Map-Pool">
|
||||||
|
|||||||
@@ -173,7 +173,10 @@ input:focus, select:focus { border-color: var(--ct); outline: none; }
|
|||||||
.match-grid { display: grid; grid-template-columns: minmax(220px, 280px) minmax(0, 1fr) minmax(220px, 280px); gap: 20px; align-items: start; }
|
.match-grid { display: grid; grid-template-columns: minmax(220px, 280px) minmax(0, 1fr) minmax(220px, 280px); gap: 20px; align-items: start; }
|
||||||
.stage { min-width: 0; }
|
.stage { min-width: 0; }
|
||||||
.stage-note { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 24px; }
|
.stage-note { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 24px; }
|
||||||
.stage-note .btn { margin: 8px 0 20px; }
|
.stage-note .btn { margin: 8px 0 8px; }
|
||||||
|
.stage-note .pool-preview { margin-top: 16px; }
|
||||||
|
.missing-list { margin: 0 0 12px; padding-left: 1.1em; color: var(--muted); }
|
||||||
|
.missing-list li { margin-bottom: 4px; }
|
||||||
.pool-preview { display: flex; flex-wrap: wrap; gap: 6px; }
|
.pool-preview { display: flex; flex-wrap: wrap; gap: 6px; }
|
||||||
.pool-chip { padding: 4px 10px; border-left: 3px solid var(--map); background: var(--surface-2); font-size: .875rem; border-radius: 3px; }
|
.pool-chip { padding: 4px 10px; border-left: 3px solid var(--map); background: var(--surface-2); font-size: .875rem; border-radius: 3px; }
|
||||||
|
|
||||||
|
|||||||
@@ -531,6 +531,7 @@ export class MatchService extends EventEmitter {
|
|||||||
currentMapNumber: match.current_map_number,
|
currentMapNumber: match.current_map_number,
|
||||||
viewer: { role: viewer.role, playerId: viewer.playerId, team: viewer.team },
|
viewer: { role: viewer.role, playerId: viewer.playerId, team: viewer.team },
|
||||||
canStartVeto: match.status === 'lobby' && readiness.A.ready && readiness.B.ready,
|
canStartVeto: match.status === 'lobby' && readiness.A.ready && readiness.B.ready,
|
||||||
|
requireFullTeams: this.config.requireFullTeams,
|
||||||
maps: pool.maps,
|
maps: pool.maps,
|
||||||
veto: publicView
|
veto: publicView
|
||||||
? null
|
? null
|
||||||
|
|||||||
Reference in new issue
Block a user