import { useLoaderData, Link } from "react-router"; import { getAuth } from "@clerk/react-router/server"; import { database } from "~/database/context"; import { eq, and } from "drizzle-orm"; import * as schema from "~/database/schema"; import { Button } from "~/components/ui/button"; import { logger } from "~/lib/logger"; import { getSevenDayStandingsChange, getSeasonPointProgression } from "~/models/standings"; import { isSeasonComplete, getSeasonCompletionPercentage } from "~/lib/season-helpers.server"; import { getRecentTeamScoreEvents } from "~/models/team-score-events"; import { PointProgressionChart } from "~/components/standings/PointProgressionChart"; import { findUsersByClerkIds, getUserDisplayName } from "~/models/user"; import { StandingsPreview } from "~/components/league/StandingsPreview"; import { buildTiedRankChecker, getDisplayRank } from "~/lib/standings-display"; import { RecentScoresCard } from "~/components/standings/RecentScoresCard"; import type { Route } from "./+types/$leagueId.standings.$seasonId"; export function meta({ data }: Route.MetaArgs): Route.MetaDescriptors { return [{ title: `Standings — ${data?.league?.name ?? "League"} - Brackt` }]; } export async function loader(args: Route.LoaderArgs) { try { const { userId } = await getAuth(args); const { params } = args; const { leagueId, seasonId } = params; const db = database(); // Fetch league const league = await db.query.leagues.findFirst({ where: eq(schema.leagues.id, leagueId), }); if (!league) { throw new Response("League not found", { status: 404 }); } // Fetch season const season = await db.query.seasons.findFirst({ where: eq(schema.seasons.id, seasonId), }); if (!season || season.leagueId !== leagueId) { throw new Response("Season not found", { status: 404 }); } // Check access if (!userId) { throw new Response("You must be logged in to view standings", { status: 401, }); } // Check if user is a commissioner const isUserCommissioner = await db.query.commissioners.findFirst({ where: and( eq(schema.commissioners.leagueId, leagueId), eq(schema.commissioners.userId, userId) ), }); // Check if user has a team in this season const hasTeam = await db.query.teams.findFirst({ where: and( eq(schema.teams.seasonId, seasonId), eq(schema.teams.ownerId, userId) ), }); if (!isUserCommissioner && !hasTeam) { throw new Response("You do not have access to this league", { status: 403, }); } // Fetch standings, teams (for owner lookup), and independent data in parallel const [standingsWithComparison, teams, progressionData, seasonComplete, completionPercentage, recentScoreEvents] = await Promise.all([ getSevenDayStandingsChange(seasonId, db), db.query.teams.findMany({ where: eq(schema.teams.seasonId, seasonId), columns: { id: true, ownerId: true }, }), getSeasonPointProgression(seasonId, db), isSeasonComplete(seasonId, db), getSeasonCompletionPercentage(seasonId, db), getRecentTeamScoreEvents(seasonId, 10, db), ]); // Build teamId -> ownerName map const ownerIds = [...new Set(teams.map((t) => t.ownerId).filter((id): id is string => id !== null))]; const userRows = await findUsersByClerkIds(ownerIds); const userByClerkId = new Map(userRows.map((u) => [u.clerkId, u])); const ownerNameByTeamId = new Map( teams .filter((t): t is typeof t & { ownerId: string } => t.ownerId !== null) .map((t) => { const user = userByClerkId.get(t.ownerId); return [t.id, user ? getUserDisplayName(user) : null] as const; }) ); // Map to format expected by component (use sevenDayRankChange instead of previousRank) const formattedStandings = standingsWithComparison.map((standing) => ({ ...standing, rankChange: standing.sevenDayRankChange, ownerName: ownerNameByTeamId.get(standing.teamId) ?? null, })); // Enrich recentScoreEvents with owner names const enrichedScoreEvents = recentScoreEvents.map((event) => ({ ...event, ownerName: ownerNameByTeamId.get(event.teamId) ?? null, })); return { league, season, standings: formattedStandings, progressionData, seasonComplete, completionPercentage, recentScoreEvents: enrichedScoreEvents, }; } catch (error) { logger.error("Error loading standings:", error); logger.error("Error details:", { message: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined, }); throw error; } } export default function LeagueStandings() { const { league, season, standings, progressionData, seasonComplete, completionPercentage, recentScoreEvents } = useLoaderData(); const isTied = buildTiedRankChecker(standings.map((s) => s.currentRank)); const previewEntries = standings.map((s) => ({ teamId: s.teamId, teamName: s.teamName, ownerName: s.ownerName, displayRank: getDisplayRank(s, standings.length, isTied(s.currentRank)), currentRank: s.currentRank, points: s.totalPoints, rankChange: s.rankChange, pointChange: s.sevenDayPointChange, href: `/leagues/${league.id}/standings/${season.id}/teams/${s.teamId}`, })); return (
{/* Header */}

{league.name}

{season.year} Season Standings

{seasonComplete ? (
✓ Season Complete
) : (
{completionPercentage}% Complete
)}
{/* Two-column layout */}
{/* Left: Standings + Point Progression (2/3 width) */}
{progressionData.chartData.length > 0 && ( )}
{/* Right: Recent Scores (1/3 width) */}
); }