This commit is contained in:
2026-06-23 16:45:36 -05:00
parent e4d9f901cb
commit e2bfb0a17d
3 changed files with 39 additions and 12 deletions

View File

@@ -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");