import { useEffect } from "react"; import { Link, useRevalidator } from "react-router"; import { eq, inArray } from "drizzle-orm"; import type { Route } from "./+types/sports-seasons.$sportsSeasonId.tournament"; import { database } from "~/database/context"; import * as schema from "~/database/schema"; import { type PlayoffMatch } from "~/models/playoff-match"; import { findSeasonMatchesBySportsSeasonId } from "~/models/season-match"; import { getBracketTemplate, getOrderedRoundsFromMatches } from "~/lib/bracket-templates"; import { Cs2TournamentBracket } from "~/components/scoring/Cs2TournamentBracket"; import { MatchSchedule } from "~/components/scoring/MatchSchedule"; import { ArrowLeft } from "lucide-react"; export function meta({ data }: Route.MetaArgs): Route.MetaDescriptors { return [{ title: `${data?.sportsSeason?.name ?? "Tournament"} — Schedule & Results` }]; } export async function loader({ params }: Route.LoaderArgs) { const { sportsSeasonId } = params; const db = database(); const sportsSeason = await db.query.sportsSeasons.findFirst({ where: eq(schema.sportsSeasons.id, sportsSeasonId), with: { sport: true }, }); if (!sportsSeason) throw new Response("Sports season not found", { status: 404 }); const simulatorType = sportsSeason.sport?.simulatorType ?? null; const seasonMatches = await findSeasonMatchesBySportsSeasonId(sportsSeasonId); // Load playoff matches for bracket sports let playoffMatches: (PlayoffMatch & { participant1: { id: string; name: string } | null; participant2: { id: string; name: string } | null; winner: { id: string; name: string } | null; loser: { id: string; name: string } | null; })[] = []; let playoffRounds: string[] = []; let bracketTemplateId: string | null = null; if (simulatorType?.endsWith("_bracket") || simulatorType === "cs2_major_qualifying_points") { const events = await db.query.scoringEvents.findMany({ where: eq(schema.scoringEvents.sportsSeasonId, sportsSeasonId), }); if (events.length > 0) { const eventIds = events.map((e) => e.id); const matches = await db.query.playoffMatches.findMany({ where: (pm) => inArray(pm.scoringEventId, eventIds), with: { participant1: true, participant2: true, winner: true, loser: true, }, }); playoffMatches = matches as typeof playoffMatches; const templateId = events.find((e) => e.bracketTemplateId)?.bracketTemplateId ?? undefined; bracketTemplateId = templateId ?? null; const template = templateId ? getBracketTemplate(templateId) : undefined; playoffRounds = getOrderedRoundsFromMatches(matches, template); } } const hasLiveMatches = seasonMatches.some((m) => m.status === "in_progress") || playoffMatches.some((m) => !m.isComplete && (m.participant1Id !== null || m.participant2Id !== null)); return { sportsSeason, simulatorType, seasonMatches, playoffMatches, playoffRounds, bracketTemplateId, hasLiveMatches, }; } export default function TournamentPage({ loaderData }: Route.ComponentProps) { const { sportsSeason, simulatorType, seasonMatches, playoffMatches, playoffRounds, bracketTemplateId, hasLiveMatches, } = loaderData; const { revalidate } = useRevalidator(); // Poll every 30 seconds when any match is live useEffect(() => { if (!hasLiveMatches) return; const interval = setInterval(() => revalidate(), 30_000); return () => clearInterval(interval); }, [hasLiveMatches, revalidate]); const isCs2 = simulatorType === "cs2_major_qualifying_points"; return (
{sportsSeason.sport.name}
)} {hasLiveMatches && (