Übersicht: laufende Matches als Karten mit Map, Stand und Serie
This commit is contained in:
1 parent
d2625d23ab
commit
f90351dd8d
3 files changed
+62
-2
No files matched your search
@@ -4,6 +4,7 @@ import { api } from '../lib/api.js';
|
||||
import { FORMAT_LABEL, serverLabel, timeAgo } from '../lib/labels.js';
|
||||
import { getAccess } from '../lib/session.js';
|
||||
import { useOverview } from '../lib/useMatch.js';
|
||||
import MapImage from '../components/MapImage.jsx';
|
||||
|
||||
const GROUPS = [
|
||||
{ key: 'open', title: 'Offen', empty: 'Keine offenen Lobbys. Erstelle ein Match, um zu starten.' },
|
||||
@@ -32,7 +33,11 @@ export default function Overview() {
|
||||
return (
|
||||
<section key={g.key} className={`group group-${g.key}`}>
|
||||
<h2>{g.title} <span className="count">{list.length}</span></h2>
|
||||
{list.length === 0 ? <p className="muted small">{g.empty}</p> : (
|
||||
{list.length === 0 ? <p className="muted small">{g.empty}</p> : g.key === 'running' ? (
|
||||
<div className="run-grid">
|
||||
{list.map((m) => <RunningCard key={m.id} match={m} />)}
|
||||
</div>
|
||||
) : (
|
||||
<ul className="match-list">
|
||||
{list.map((m) => <MatchRow key={m.id} match={m} />)}
|
||||
</ul>
|
||||
@@ -79,6 +84,34 @@ function MatchRow({ match: m }) {
|
||||
);
|
||||
}
|
||||
|
||||
/** Karte für ein laufendes Match: Map als Hintergrund, Stand groß, Serie und Status darunter. */
|
||||
function RunningCard({ match: m }) {
|
||||
const server = serverLabel(m.server);
|
||||
const map = m.currentMap;
|
||||
const live = map && !map.finished;
|
||||
const bo1 = m.format === 'bo1';
|
||||
return (
|
||||
<Link to={`/m/${m.id}`} className={`run-card ${live ? 'is-live' : ''}`} style={{ '--map': map?.color ?? '#3a4150' }}>
|
||||
{map?.image && <MapImage map={{ image: map.image, color: map.color, name: map.name }} className="run-art" />}
|
||||
<div className="run-top">
|
||||
<span className="run-mode">{m.modeLabel}, {FORMAT_LABEL[m.format]}</span>
|
||||
{live ? <span className="live-badge run-live">Live</span> : <span className={`run-status tone-${server.tone}`}>{m.statusLabel}</span>}
|
||||
</div>
|
||||
<div className="run-teams">
|
||||
<span className="run-team a">{m.teamA}</span>
|
||||
<span className="run-score">
|
||||
{map ? <>{map.score.A}<i>:</i>{map.score.B}</> : <i className="run-vs">vs.</i>}
|
||||
</span>
|
||||
<span className="run-team b">{m.teamB}</span>
|
||||
</div>
|
||||
<div className="run-foot">
|
||||
<span>{map ? `Map ${map.number}: ${map.name}` : 'Veto läuft'}</span>
|
||||
{!bo1 && m.seriesScore && <span className="run-series">Serie {m.seriesScore.A}:{m.seriesScore.B}</span>}
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
function CodeEntry() {
|
||||
const [code, setCode] = useState('');
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
@@ -534,3 +534,23 @@ input:focus, select:focus { border-color: var(--link); outline: none; }
|
||||
.admin-badge { display: inline-block; margin-top: 6px; padding: 2px 10px; border-radius: 999px; font-size: .8rem; font-weight: 600; }
|
||||
.admin-badge.is-paused { background: color-mix(in srgb, var(--pending) 22%, transparent); color: var(--pending); border: 1px solid var(--pending); }
|
||||
.btn-warn { border-color: var(--pending); color: var(--pending); }
|
||||
/* Laufende Matches in der Übersicht */
|
||||
.run-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 16px; }
|
||||
.run-card { position: relative; display: flex; flex-direction: column; justify-content: space-between; gap: 10px; min-height: 190px; padding: 16px 18px; border-radius: var(--radius); border: 1px solid var(--line); background: var(--surface); color: var(--text); text-decoration: none; overflow: hidden; isolation: isolate; transition: transform .15s ease, border-color .15s ease; }
|
||||
.run-card:hover { transform: translateY(-2px); border-color: var(--map); }
|
||||
.run-card.is-live { box-shadow: inset 3px 0 0 var(--brand-red); }
|
||||
.run-art { position: absolute; inset: 0; z-index: -1; opacity: .28; }
|
||||
.run-art::after { background: linear-gradient(180deg, rgba(0,0,0,.2), rgba(0,0,0,.92)); }
|
||||
.run-top { display: flex; justify-content: space-between; align-items: center; gap: 8px; font-size: .8rem; color: var(--muted); }
|
||||
.run-live { animation: run-pulse 1.6s ease-in-out infinite; }
|
||||
@keyframes run-pulse { 50% { opacity: .55; } }
|
||||
.run-status { font-weight: 600; }
|
||||
.run-teams { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 8px; text-align: center; }
|
||||
.run-team { font: 700 1.25rem var(--display); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.run-team.a { color: var(--team-a); text-align: right; }
|
||||
.run-team.b { color: var(--team-b); text-align: left; }
|
||||
.run-score { font: 700 2.25rem var(--display); font-variant-numeric: tabular-nums; }
|
||||
.run-score i { font-style: normal; color: var(--muted); padding: 0 4px; }
|
||||
.run-vs { font-size: 1rem; color: var(--muted); font-style: normal; }
|
||||
.run-foot { display: flex; justify-content: space-between; gap: 8px; font-size: .875rem; color: var(--muted); }
|
||||
.run-series { font-variant-numeric: tabular-nums; }
|
||||
@@ -791,7 +791,14 @@ export class MatchService extends EventEmitter {
|
||||
return {
|
||||
seriesScore: maps.length ? seriesScore(maps) : null,
|
||||
currentMap: current && (current.status === 'live' || current.status === 'done')
|
||||
? { number: current.number, name: this.mapName(m, current.map), score: { A: current.score_a, B: current.score_b }, finished: current.status === 'done' }
|
||||
? {
|
||||
number: current.number,
|
||||
name: this.mapName(m, current.map),
|
||||
image: JSON.parse(m.maps).find((x) => x.id === current.map)?.image ?? null,
|
||||
color: JSON.parse(m.maps).find((x) => x.id === current.map)?.color ?? null,
|
||||
score: { A: current.score_a, B: current.score_b },
|
||||
finished: current.status === 'done',
|
||||
}
|
||||
: null,
|
||||
id: m.id,
|
||||
teamA: m.team_a_name,
|
||||
|
||||
Reference in new issue
Block a user