From e2bfb0a17d59053e0f1f29ae2c31e930258a0e10 Mon Sep 17 00:00:00 2001 From: Andreas Knuth Date: Tue, 23 Jun 2026 16:45:36 -0500 Subject: [PATCH] fixes --- app/components/Bracket.tsx | 5 ++++- app/globals.css | 9 +++++++-- lib/resolve-bracket.ts | 37 ++++++++++++++++++++++++++++--------- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/app/components/Bracket.tsx b/app/components/Bracket.tsx index b90f211..78b1670 100644 --- a/app/components/Bracket.tsx +++ b/app/components/Bracket.tsx @@ -12,7 +12,10 @@ function Side({ // fix = ein echtes Team steht fest (nicht vorläufig). Eigene Klasse für klares Styling. const fix = s.teamId != null && !s.provisional; return ( -
+
{s.teamId ? ( <> diff --git a/app/globals.css b/app/globals.css index ed0f651..e485afa 100644 --- a/app/globals.css +++ b/app/globals.css @@ -189,10 +189,15 @@ table.standings { width: 100%; border-collapse: collapse; } font-family: var(--font-mono); font-size: 12px; color: var(--ink-dim); } .bracket-banner .v { font-family: var(--font-mono); font-size: 12px; color: var(--turf); } -.bracket-scroll { overflow-x: auto; padding-bottom: 36px; } +.bracket-scroll { + max-height: calc(100vh - 260px); + overflow: auto; + padding-bottom: 8px; + scrollbar-gutter: stable; +} .bracket { display: flex; gap: 26px; min-width: max-content; align-items: stretch; - padding-bottom: 16px; + padding-bottom: 8px; } .round { display: flex; flex-direction: column; min-width: 220px; } .round-label { diff --git a/lib/resolve-bracket.ts b/lib/resolve-bracket.ts index b0b7e8f..8d02dcc 100644 --- a/lib/resolve-bracket.ts +++ b/lib/resolve-bracket.ts @@ -21,6 +21,7 @@ export interface ResolvedSide { score: number | null; isWinner: boolean; provisional: boolean; // true = nur auf Basis aktueller Tabelle, noch nicht fix + tooltip: string | null; // Hover-Erklärung (Herkunft + ob fix) } export interface ResolvedTie { @@ -64,19 +65,23 @@ function resolveR32Slot( matches: Match[], teams: Team[], annexResolved: boolean, -): { teamId: string | null; provisional: boolean } { +): { teamId: string | null; provisional: boolean; tooltip: string | null } { const table = (g: GroupId) => tables.find((t) => t.group === g); if (slot.type === "W") { const t = table(slot.group!); const teamId = t?.rows.find((r) => r.rank === 1)?.teamId ?? null; // Sieger fix, sobald Platz 1 rechnerisch gesichert ist (auch vor Gruppenende). - return { teamId, provisional: !placeIsSecure(slot.group!, 1, teams, matches) }; + const provisional = !placeIsSecure(slot.group!, 1, teams, matches); + const prefix = provisional ? "aktuell " : ""; + return { teamId, provisional, tooltip: `${prefix}1. Gruppe ${slot.group}` }; } if (slot.type === "R") { const t = table(slot.group!); const teamId = t?.rows.find((r) => r.rank === 2)?.teamId ?? null; // Zweiter fix, sobald Platz 2 rechnerisch gesichert ist. - return { teamId, provisional: !placeIsSecure(slot.group!, 2, teams, matches) }; + const provisional = !placeIsSecure(slot.group!, 2, teams, matches); + const prefix = provisional ? "aktuell " : ""; + return { teamId, provisional, tooltip: `${prefix}2. Gruppe ${slot.group}` }; } // 3. Platz: braucht Annex-C-Zuordnung + den Gruppensieger dieses Spiels if (slot.type === "3" && assignment && winnerGroup) { @@ -86,16 +91,27 @@ function resolveR32Slot( // Fix nur, wenn Annex C aufgelöst UND die Quellgruppe abgeschlossen ist // (Drittplatzierten-Qualifikation ist gruppenübergreifend, bis zuletzt offen). const fix = annexResolved && groupFinished(thirdGroup, matches); - return { teamId: row?.teamId ?? null, provisional: !fix }; + const provisional = !fix; + const prefix = provisional ? "aktuell " : ""; + return { + teamId: row?.teamId ?? null, + provisional, + tooltip: `${prefix}3. der Gruppe ${thirdGroup}`, + }; } } - return { teamId: null, provisional: true }; + if (slot.type === "3") { + const pool = slot.thirdPool?.join("") ?? "?"; + return { teamId: null, provisional: true, tooltip: `aktuell 3. Gruppe ${pool}` }; + } + return { teamId: null, provisional: true, tooltip: null }; } function sideFrom( teamId: string | null, fallbackLabel: string, teams: Team[], feed: Match | undefined, which: "home" | "away", provisional: boolean, + tooltip: string | null, ): ResolvedSide { const team = teamId ? teams.find((t) => t.id === teamId) : undefined; const score = feed @@ -109,6 +125,7 @@ function sideFrom( score, isWinner: teamId != null && w === teamId, provisional: teamId != null && provisional, + tooltip, }; } @@ -133,8 +150,8 @@ export function resolveBracket( const h = resolveR32Slot(rm.home, winnerGroup, tables, assignment, thirds, matches, teams, annexResolved); const a = resolveR32Slot(rm.away, winnerGroup, tables, assignment, thirds, matches, teams, annexResolved); - const home = sideFrom(h.teamId, slotLabel(rm.home, assignment, winnerGroup), teams, feed, "home", h.provisional); - const away = sideFrom(a.teamId, slotLabel(rm.away, assignment, winnerGroup), teams, feed, "away", a.provisional); + const home = sideFrom(h.teamId, slotLabel(rm.home, assignment, winnerGroup), teams, feed, "home", h.provisional, h.tooltip); + const away = sideFrom(a.teamId, slotLabel(rm.away, assignment, winnerGroup), teams, feed, "away", a.provisional, a.tooltip); winners.set(rm.matchNumber, winnerOf(feed)); losers.set(rm.matchNumber, loserOf(feed)); @@ -156,8 +173,10 @@ export function resolveBracket( const awayProv = !(decided.get(km.fromAway) ?? false); const homeLabel = `${km.losers ? "Verlierer" : "Sieger"} ${km.fromHome}`; const awayLabel = `${km.losers ? "Verlierer" : "Sieger"} ${km.fromAway}`; - const home = sideFrom(homeId, homeLabel, teams, feed, "home", homeProv); - const away = sideFrom(awayId, awayLabel, teams, feed, "away", awayProv); + const homeTtip = `${km.losers ? "Verlierer" : "Sieger"} aus Spiel ${km.fromHome}`; + const awayTtip = `${km.losers ? "Verlierer" : "Sieger"} aus Spiel ${km.fromAway}`; + const home = sideFrom(homeId, homeLabel, teams, feed, "home", homeProv, homeTtip); + const away = sideFrom(awayId, awayLabel, teams, feed, "away", awayProv, awayTtip); winners.set(km.matchNumber, winnerOf(feed)); losers.set(km.matchNumber, loserOf(feed)); decided.set(km.matchNumber, feed?.status === "FINISHED");