Files
Cs2-Mapveto/client/src/components/TeamCard.jsx
T
SPP UserandClaude Opus 5.5 4152f9c0a6 Organisator kann im selben Browser selbst mitspielen
Ein Aufrufer darf Organisator- und Spieler-Zugang kombinieren
(Authorization + X-Player-Token). Berechtigungen prüfen jetzt einzelne
Merkmale statt einer Rangstufe. In der Lobby gibt es „Selbst mitspielen?“,
Teamlinks funktionieren auch im Organisator-Browser. Online-Status wird
direkt nach dem Beitritt aktualisiert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 17:53:39 +01:00

65 lines
2.9 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
export default function TeamCard({ team, side, state, viewer, isOrg, act, busy }) {
const isOwnTeam = viewer.team === side;
const inLobby = state.status === 'lobby';
const me = team.players.find((p) => p.isYou);
const emptySlots = Math.max(0, team.teamSize - team.players.length);
const canManageCaptain = isOrg || (viewer.isCaptain && isOwnTeam);
const { readiness } = team;
return (
<section className={`team-card team-${side} ${readiness.ready ? 'is-ready' : ''}`} aria-label={`Team ${team.name}`}>
<header>
<h2>{team.name}</h2>
<span className={`ready-state ${readiness.ready ? 'on' : ''}`}>
{readiness.ready ? 'Bereit' : `${readiness.count}/${team.teamSize}`}
</span>
</header>
<ul className="roster">
{team.players.map((p) => (
<li key={p.id} className={`${p.isYou ? 'is-you' : ''}`}>
<span className={`presence ${p.online ? 'on' : ''}`} title={p.online ? 'Online' : 'Offline'} />
<span className="nick">
{p.nickname}
{p.isYou && <small> (du)</small>}
</span>
{p.isCaptain && <span className="captain" title="Captain – führt das Veto">Captain</span>}
{inLobby && <span className={`ready-dot ${p.ready ? 'on' : ''}`}>{p.ready ? 'bereit' : 'wartet'}</span>}
{(canManageCaptain && !p.isCaptain) || (isOrg && inLobby && !p.isYou) ? (
<span className="row-actions">
{canManageCaptain && !p.isCaptain && (
<button className="link-btn" disabled={!!busy} onClick={() => act('captain', { playerId: p.id }, `${p.nickname} ist jetzt Captain`)}>
Zum Captain machen
</button>
)}
{isOrg && inLobby && !p.isYou && (
<button className="link-btn danger" disabled={!!busy} onClick={() => act('kick', { playerId: p.id }, `${p.nickname} entfernt`)}>
Entfernen
</button>
)}
</span>
) : null}
</li>
))}
{Array.from({ length: emptySlots }, (_, i) => (
<li key={`empty-${i}`} className="slot-empty">Freier Platz</li>
))}
</ul>
{me && inLobby && (
<div className="team-actions">
<button className={`btn ${me.ready ? '' : 'btn-primary'}`} disabled={!!busy}
onClick={() => act('ready', { ready: !me.ready })}>
{me.ready ? 'Doch nicht bereit' : 'Ich bin bereit'}
</button>
<button className="link-btn" disabled={!!busy}
onClick={() => window.confirm('Lobby wirklich verlassen?') && act('leave', null, 'Du hast das Team verlassen')}>
Team verlassen
</button>
</div>
)}
{inLobby && !readiness.enough && <p className="small muted">Es fehlen noch {team.teamSize - readiness.count} Spieler.</p>}
</section>
);
}