Matches are positioned per round with SVG connector lines between feeder and next match, winners in bold, live matches tagged with their court, empty slots labelled with what fills them (W R1.4, L R2.1), and the viewer's team highlighted along its path. Double elimination renders winners, losers and finals trees. Narrow screens default to a scaled fit-to-width view; a round-by-round list is also available. Seed script gains --stage=bracket|double. Release 0.3.0. Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01MB7nCCAscYsb3zzkT6LHZi
31 lines
2.0 KiB
JavaScript
31 lines
2.0 KiB
JavaScript
// The one JSON document every public page renders from. No phone numbers, no team codes.
|
|
export function publicState({ t, engine }) {
|
|
const name = id => t.teamName(id);
|
|
const view = m => ({
|
|
id: m.id, stage: m.stage, round: m.round, slot: m.slot, label: m.label ?? null, status: m.status, court: m.court,
|
|
a: m.teamA ? { id: m.teamA, name: name(m.teamA) } : null,
|
|
b: m.teamB ? { id: m.teamB, name: name(m.teamB) } : null,
|
|
winner: m.winner, sets: m.sets, live: m.live ?? null, feeds: m.feeds, feedsSide: m.feedsSide, loserFeeds: m.loserFeeds ?? null, loserFeedsSide: m.loserFeedsSide ?? null, conditional: !!m.conditional,
|
|
});
|
|
const board = t.phase === 'live' ? engine.board() : null;
|
|
const pools = t.pools.map(p => ({
|
|
id: p.id,
|
|
standings: t.standings(p.id).map(r => ({ teamId: r.teamId, name: r.name, w: r.w, l: r.l, setsW: r.setsW, setsL: r.setsL, pf: r.pf, pa: r.pa, pointDiff: r.pointDiff, decidedBy: r.decidedBy, status: r.status })),
|
|
matches: t.matches.filter(m => m.poolId === p.id).map(view),
|
|
}));
|
|
const bracket = t.matches.filter(m => m.stage !== 'pool').map(view);
|
|
const teams = t.activeTeams().map(x => t.publicTeam(x.id));
|
|
const withdrawn = [...t.teams.values()].filter(x => x.status === 'withdrawn').map(x => t.publicTeam(x.id));
|
|
return {
|
|
slug: t.slug, name: t.name, date: t.date, notes: t.notes ?? '', phase: t.phase, banner: t.banner ?? null,
|
|
rules: t.rules, stages: t.stages, courtCount: t.courtCount,
|
|
teams, withdrawn, pools, bracket,
|
|
courts: board ? board.courts.map(c => ({ ...c, match: c.match ? view(t.match(c.match.id)) : null })) : [],
|
|
upNext: board ? board.upNext.map(u => ({ ...view(t.match(u.id)), court: u.court, etaMin: u.etaMin })) : [],
|
|
avgMatchMin: board?.avgMatchMin ?? null,
|
|
champion: t.phase === 'final' ? t.champion() : null,
|
|
recentAlerts: engine.alerts.slice(-20).map(a => ({ kind: a.kind, text: a.text, at: a.at, teams: a.teams ?? [], match: a.match ?? null })),
|
|
generatedAt: Date.now(),
|
|
};
|
|
}
|