fix
This commit is contained in:
@@ -4,10 +4,10 @@ import { useState, useMemo, useCallback, useEffect } from "react";
|
|||||||
import { Match, Team } from "@/lib/types";
|
import { Match, Team } from "@/lib/types";
|
||||||
import { computeGroupTables, computeThirdPlaceTable } from "@/lib/standings";
|
import { computeGroupTables, computeThirdPlaceTable } from "@/lib/standings";
|
||||||
import { qualifiedThirdGroups, resolveAnnexC } from "@/lib/bracket";
|
import { qualifiedThirdGroups, resolveAnnexC } from "@/lib/bracket";
|
||||||
import { resolveBracket, ResolvedTie } from "@/lib/resolve-bracket";
|
import { resolveBracket } from "@/lib/resolve-bracket";
|
||||||
import {
|
import {
|
||||||
loadOverrides, saveOverrides, buildSimMatches,
|
loadOverrides, saveOverrides, buildSimMatches,
|
||||||
propagateSimWinners, defaultScore, SimOverrides,
|
defaultScore, SimOverrides,
|
||||||
} from "@/lib/simulation";
|
} from "@/lib/simulation";
|
||||||
import Bracket from "./Bracket";
|
import Bracket from "./Bracket";
|
||||||
import Flag from "./Flag";
|
import Flag from "./Flag";
|
||||||
@@ -76,11 +76,11 @@ export default function Simulation({ teams, matches }: { teams: Team[]; matches:
|
|||||||
);
|
);
|
||||||
const simAnnexResolved = simAnnex != null;
|
const simAnnexResolved = simAnnex != null;
|
||||||
|
|
||||||
const { r32, later: laterRaw } = useMemo(
|
const simBracket = useMemo(
|
||||||
() => resolveBracket(simMatches, teams, simTables, simThirds, simAnnex, simAnnexResolved),
|
() => resolveBracket(simMatches, teams, simTables, simThirds, simAnnex, simAnnexResolved, true),
|
||||||
[simMatches, teams, simTables, simThirds, simAnnex, simAnnexResolved],
|
[simMatches, teams, simTables, simThirds, simAnnex, simAnnexResolved],
|
||||||
);
|
);
|
||||||
const later = useMemo(() => propagateSimWinners(r32, laterRaw), [r32, laterRaw]);
|
const { r32, later } = simBracket;
|
||||||
const preResolved = useMemo(() => ({ r32, later }), [r32, later]);
|
const preResolved = useMemo(() => ({ r32, later }), [r32, later]);
|
||||||
|
|
||||||
// ---- Eingabeliste: offene Spiele aus dem echten Feed ----
|
// ---- Eingabeliste: offene Spiele aus dem echten Feed ----
|
||||||
@@ -95,6 +95,11 @@ export default function Simulation({ teams, matches }: { teams: Team[]; matches:
|
|||||||
const groupPhase = openMatches.filter((m) => m.stage === "GROUP");
|
const groupPhase = openMatches.filter((m) => m.stage === "GROUP");
|
||||||
const koPhase = openMatches.filter((m) => m.stage !== "GROUP");
|
const koPhase = openMatches.filter((m) => m.stage !== "GROUP");
|
||||||
|
|
||||||
|
// KO-Eingaben erst freigeben, wenn alle Gruppenspiele beendet sind.
|
||||||
|
const groupPhaseComplete = matches
|
||||||
|
.filter((m) => m.group != null)
|
||||||
|
.every((m) => m.status === "FINISHED");
|
||||||
|
|
||||||
const hasChanges = Object.keys(overrides).length > 0;
|
const hasChanges = Object.keys(overrides).length > 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -144,11 +149,17 @@ export default function Simulation({ teams, matches }: { teams: Team[]; matches:
|
|||||||
<div style={{ fontFamily: "var(--font-display)", fontSize: 12, textTransform: "uppercase", letterSpacing: "0.06em", color: "var(--ink-faint)", marginBottom: 8 }}>
|
<div style={{ fontFamily: "var(--font-display)", fontSize: 12, textTransform: "uppercase", letterSpacing: "0.06em", color: "var(--ink-faint)", marginBottom: 8 }}>
|
||||||
K.o.-Phase
|
K.o.-Phase
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
|
{groupPhaseComplete ? (
|
||||||
{koPhase.map((m) => (
|
<div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
|
||||||
<SimRow key={m.id} match={m} teams={teams} overrides={overrides} onScore={handleScore} onClear={handleClear} />
|
{koPhase.map((m) => (
|
||||||
))}
|
<SimRow key={m.id} match={m} teams={teams} overrides={overrides} onScore={handleScore} onClear={handleClear} />
|
||||||
</div>
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="notice" style={{ fontSize: 13 }}>
|
||||||
|
K.o.-Spiele werden zur Eingabe freigegeben, sobald die Gruppenphase abgeschlossen ist.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -130,10 +130,15 @@ function sideFrom(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Baut die komplette aufgelöste Bracket-Struktur.
|
// Baut die komplette aufgelöste Bracket-Struktur.
|
||||||
|
// resolveWinners: wenn true, werden die Gewinner-TeamIDs aus den aufgelösten
|
||||||
|
// R32-Slots abgeleitet (basierend auf den Spiel-Scores), statt aus den Team-IDs
|
||||||
|
// des Feeds. Nötig für die Simulation, wo KO-Matches Scores aber keine Team-IDs
|
||||||
|
// im Feed haben.
|
||||||
export function resolveBracket(
|
export function resolveBracket(
|
||||||
matches: Match[], teams: Team[], tables: GroupTable[],
|
matches: Match[], teams: Team[], tables: GroupTable[],
|
||||||
thirds: ThirdPlaceRow[], assignment: ThirdAssignment | null,
|
thirds: ThirdPlaceRow[], assignment: ThirdAssignment | null,
|
||||||
annexResolved: boolean,
|
annexResolved: boolean,
|
||||||
|
resolveWinners = false,
|
||||||
): { r32: ResolvedTie[]; later: Record<number, ResolvedTie> } {
|
): { r32: ResolvedTie[]; later: Record<number, ResolvedTie> } {
|
||||||
// Map: Match-Nummer -> Sieger-Team-ID (für Propagation in Folgerunden)
|
// Map: Match-Nummer -> Sieger-Team-ID (für Propagation in Folgerunden)
|
||||||
const winners = new Map<number, string | null>();
|
const winners = new Map<number, string | null>();
|
||||||
@@ -153,8 +158,25 @@ export function resolveBracket(
|
|||||||
const home = sideFrom(h.teamId, slotLabel(rm.home, assignment, winnerGroup), teams, feed, "home", h.provisional, h.tooltip);
|
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);
|
const away = sideFrom(a.teamId, slotLabel(rm.away, assignment, winnerGroup), teams, feed, "away", a.provisional, a.tooltip);
|
||||||
|
|
||||||
winners.set(rm.matchNumber, winnerOf(feed));
|
const feedWinner = winnerOf(feed);
|
||||||
losers.set(rm.matchNumber, loserOf(feed));
|
if (resolveWinners && !feedWinner && feed && h.teamId && a.teamId
|
||||||
|
&& feed.homeScore != null && feed.awayScore != null) {
|
||||||
|
// Simulation: Gewinner aus Scores und aufgelösten Team-IDs ableiten
|
||||||
|
if (feed.homeScore > feed.awayScore) {
|
||||||
|
winners.set(rm.matchNumber, h.teamId);
|
||||||
|
losers.set(rm.matchNumber, loserOf(feed) ?? a.teamId);
|
||||||
|
} else if (feed.awayScore > feed.homeScore) {
|
||||||
|
winners.set(rm.matchNumber, a.teamId);
|
||||||
|
losers.set(rm.matchNumber, loserOf(feed) ?? h.teamId);
|
||||||
|
} else {
|
||||||
|
// Unentschieden → Heim gewinnt
|
||||||
|
winners.set(rm.matchNumber, h.teamId);
|
||||||
|
losers.set(rm.matchNumber, loserOf(feed) ?? a.teamId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
winners.set(rm.matchNumber, feedWinner);
|
||||||
|
losers.set(rm.matchNumber, loserOf(feed));
|
||||||
|
}
|
||||||
decided.set(rm.matchNumber, feed?.status === "FINISHED");
|
decided.set(rm.matchNumber, feed?.status === "FINISHED");
|
||||||
return {
|
return {
|
||||||
matchNumber: rm.matchNumber, stage: "R32",
|
matchNumber: rm.matchNumber, stage: "R32",
|
||||||
@@ -177,8 +199,24 @@ export function resolveBracket(
|
|||||||
const awayTtip = `${km.losers ? "Verlierer" : "Sieger"} aus Spiel ${km.fromAway}`;
|
const awayTtip = `${km.losers ? "Verlierer" : "Sieger"} aus Spiel ${km.fromAway}`;
|
||||||
const home = sideFrom(homeId, homeLabel, teams, feed, "home", homeProv, homeTtip);
|
const home = sideFrom(homeId, homeLabel, teams, feed, "home", homeProv, homeTtip);
|
||||||
const away = sideFrom(awayId, awayLabel, teams, feed, "away", awayProv, awayTtip);
|
const away = sideFrom(awayId, awayLabel, teams, feed, "away", awayProv, awayTtip);
|
||||||
winners.set(km.matchNumber, winnerOf(feed));
|
|
||||||
losers.set(km.matchNumber, loserOf(feed));
|
const feedWinner = winnerOf(feed);
|
||||||
|
if (resolveWinners && !feedWinner && feed && homeId && awayId
|
||||||
|
&& feed.homeScore != null && feed.awayScore != null) {
|
||||||
|
if (feed.homeScore > feed.awayScore) {
|
||||||
|
winners.set(km.matchNumber, homeId);
|
||||||
|
losers.set(km.matchNumber, loserOf(feed) ?? awayId);
|
||||||
|
} else if (feed.awayScore > feed.homeScore) {
|
||||||
|
winners.set(km.matchNumber, awayId);
|
||||||
|
losers.set(km.matchNumber, loserOf(feed) ?? homeId);
|
||||||
|
} else {
|
||||||
|
winners.set(km.matchNumber, homeId);
|
||||||
|
losers.set(km.matchNumber, loserOf(feed) ?? awayId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
winners.set(km.matchNumber, feedWinner);
|
||||||
|
losers.set(km.matchNumber, loserOf(feed));
|
||||||
|
}
|
||||||
decided.set(km.matchNumber, feed?.status === "FINISHED");
|
decided.set(km.matchNumber, feed?.status === "FINISHED");
|
||||||
later[km.matchNumber] = {
|
later[km.matchNumber] = {
|
||||||
matchNumber: km.matchNumber, stage: km.stage,
|
matchNumber: km.matchNumber, stage: km.stage,
|
||||||
|
|||||||
Reference in New Issue
Block a user