diff --git a/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.events.$eventId.server.ts b/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.events.$eventId.server.ts index b40feb0..7450967 100644 --- a/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.events.$eventId.server.ts +++ b/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.events.$eventId.server.ts @@ -14,6 +14,7 @@ import { getCs2StageResultsForEvent } from "~/models/cs2-major-stage"; import { findSeasonMatchesByScoringEventId } from "~/models/season-match"; import { getEventResults } from "~/models/event-result"; import { getBracketTemplate, getOrderedRoundsFromMatches } from "~/lib/bracket-templates"; +import { isBracketMajor } from "~/lib/event-utils"; import { findGroupsByEventId } from "~/models/tournament-group"; import { findMatchesByGroupIds, computeGroupStandings } from "~/models/group-stage-match"; import type { PlayoffMatch } from "~/models/playoff-match"; @@ -205,6 +206,69 @@ export async function loader({ params, request }: Route.LoaderArgs) { }; } + // Tennis Grand Slam major — bracket draw (primary-keyed) + this window's QP results. + // major_tournament + tennis_qualifying_points. CS2 (also a bracket major) is handled + // above; golf (golf_qualifying_points) returns false from isBracketMajor and falls + // through to the plain QP results branch. + if (isBracketMajor(simulatorType) && scoringEvent.eventType === "major_tournament") { + const [playoffMatchRows, eventResultRows] = await Promise.all([ + db.query.playoffMatches.findMany({ + where: eq(schema.playoffMatches.scoringEventId, structureEventId), + with: { participant1: true, participant2: true, winner: true, loser: true }, + }), + // QP/results rows live on this window (local eventId), not the primary structure. + getEventResults(eventId), + ]); + const playoffMatches: PlayoffMatchWithRelations[] = playoffMatchRows.map((m) => ({ + ...m, + createdAt: m.createdAt.toISOString(), + updatedAt: m.updatedAt.toISOString(), + })); + const templateId = structureBracketTemplateId ?? undefined; + const template = templateId ? getBracketTemplate(templateId) : undefined; + const playoffRounds = getOrderedRoundsFromMatches(playoffMatches, template); + + const allResults = await db.query.seasonParticipantResults.findMany({ + where: and( + eq(schema.seasonParticipantResults.sportsSeasonId, sportsSeasonId), + eq(schema.seasonParticipantResults.finalPosition, 0) + ), + with: { participant: true }, + }); + const preEliminatedParticipants = allResults + .filter((r): r is typeof r & { participant: NonNullable } => r.participant !== null) + .map((r) => ({ id: r.participant.id, name: r.participant.name })); + + const eventResults = eventResultRows + .filter((r): r is typeof r & { placement: number } => r.placement !== null && !r.notParticipating) + .map((r) => ({ + id: r.id, + placement: r.placement, + qualifyingPointsAwarded: r.qualifyingPointsAwarded, + rawScore: r.rawScore, + seasonParticipantId: r.seasonParticipantId, + participantName: r.seasonParticipant?.name ?? null, + })); + + return { + kind: "tennis" as const, + league, + sportsSeason, + scoringEvent, + // Bracket structure + ownership keyed to the primary window (shared majors). + playoffMatches, + playoffRounds, + bracketTemplateId: templateId ?? null, + preEliminatedParticipants, + bracketTeamOwnerships, + bracketUserParticipantIds, + // QP results table uses this window's local ownership + participant ids. + teamOwnerships, + userParticipantIds, + eventResults, + }; + } + // Bracket events (tennis/golf bracket majors, soccer/UCL group+bracket) if (scoringEvent.eventType === "playoff_game") { const playoffMatchRows = await db.query.playoffMatches.findMany({ diff --git a/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.events.$eventId.tsx b/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.events.$eventId.tsx index ce01de8..121a842 100644 --- a/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.events.$eventId.tsx +++ b/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.events.$eventId.tsx @@ -30,6 +30,79 @@ export function meta({ data }: Route.MetaArgs) { export { loader }; +type QpResult = { + id: string; + placement: number; + qualifyingPointsAwarded: string | null; + rawScore: string | null; + seasonParticipantId: string; + participantName: string | null; +}; + +function QpResultsTable({ + eventResults, + ownershipMap, + userParticipantIds, +}: { + eventResults: QpResult[]; + ownershipMap: Record; + userParticipantIds: string[]; +}) { + if (eventResults.length === 0) { + return ( + + +

Results not yet available for this event.

+
+
+ ); + } + return ( + + + Results + + + + + + # + Participant + Manager + QP + + + + {eventResults.map((r) => { + const ownership = ownershipMap[r.seasonParticipantId]; + const isUser = userParticipantIds.includes(r.seasonParticipantId); + return ( + + {r.placement} + + {r.participantName ?? "—"} + {isUser && } + + + {ownership?.teamName ?? "—"} + + + {r.qualifyingPointsAwarded !== null + ? parseFloat(r.qualifyingPointsAwarded).toFixed(2) + : r.rawScore !== null + ? r.rawScore + : "—"} + + + ); + })} + +
+
+
+ ); +} + export default function EventDetailPage({ loaderData }: Route.ComponentProps) { const { league, sportsSeason, scoringEvent } = loaderData; const leagueId = league.id; @@ -114,58 +187,42 @@ export default function EventDetailPage({ loaderData }: Route.ComponentProps) { )} + {/* Tennis Grand Slam major — draw bracket + qualifying-points results */} + {loaderData.kind === "tennis" && ( +
+ {loaderData.playoffMatches.length > 0 ? ( + + ) : ( + + +

Bracket not yet available.

+
+
+ )} + +
+ )} + {/* Results table (QP tournaments, racing events) */} {loaderData.kind === "results" && ( - loaderData.eventResults.length > 0 ? ( - - - Results - - - - - - # - Participant - Manager - QP - - - - {loaderData.eventResults.map((r) => { - const ownership = ownershipMap[r.seasonParticipantId]; - const isUser = loaderData.userParticipantIds.includes(r.seasonParticipantId); - return ( - - {r.placement} - - {r.participantName ?? "—"} - {isUser && } - - - {ownership?.teamName ?? "—"} - - - {r.qualifyingPointsAwarded !== null - ? parseFloat(r.qualifyingPointsAwarded).toFixed(2) - : r.rawScore !== null - ? r.rawScore - : "—"} - - - ); - })} - -
-
-
- ) : ( - - -

Results not yet available for this event.

-
-
- ) + )} );