import { Form, Link } from "react-router"; import type { Route } from "./+types/admin.sports-seasons.$id.standings"; import { logger } from "~/lib/logger"; import { findSportsSeasonById } from "~/models/sports-season"; import { findParticipantsBySportsSeasonId } from "~/models/participant"; import { getSeasonResults, upsertSeasonResultsBulk } from "~/models/participant-season-result"; import { Button } from "~/components/ui/button"; import { Input } from "~/components/ui/input"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "~/components/ui/card"; import { ArrowLeft, Save, ListOrdered } from "lucide-react"; export function meta({ data }: Route.MetaArgs): Route.MetaDescriptors { return [{ title: `Standings — ${data?.sportsSeason?.name ?? "Sports Season"} - Brackt Admin` }]; } export async function loader({ params }: Route.LoaderArgs) { const sportsSeason = await findSportsSeasonById(params.id); if (!sportsSeason) { throw new Response("Sports season not found", { status: 404 }); } const participants = await findParticipantsBySportsSeasonId(params.id); const seasonResults = await getSeasonResults(params.id); // Build a map of existing results keyed by participantId const resultsMap = new Map( seasonResults.map((r) => [r.participantId, r]) ); return { sportsSeason: sportsSeason as typeof sportsSeason & { sport: { id: string; name: string } }, participants, resultsMap: Object.fromEntries(resultsMap), }; } export async function action({ request, params }: Route.ActionArgs) { const formData = await request.formData(); // Parse updates directly from form field names: position_ and points_ const byId = new Map(); for (const [key, value] of formData.entries()) { const posMatch = key.match(/^position_(.+)$/); const ptsMatch = key.match(/^points_(.+)$/); if (posMatch) { const id = posMatch[1]; const position = parseInt(value as string, 10); if (!isNaN(position)) { byId.set(id, { ...byId.get(id), position }); } } else if (ptsMatch) { const id = ptsMatch[1]; const points = parseFloat(value as string); if (!isNaN(points)) { byId.set(id, { ...byId.get(id), points }); } } } const updates = Array.from(byId.entries()).map(([participantId, { position, points }]) => ({ participantId, sportsSeasonId: params.id, currentPosition: position, currentPoints: points, })); try { await upsertSeasonResultsBulk(updates); return { success: true }; } catch (error) { logger.error("Error updating standings:", error); return { error: "Failed to save standings. Please try again." }; } } export default function ManageStandings({ loaderData, actionData }: Route.ComponentProps) { const { sportsSeason, participants, resultsMap } = loaderData; // Sort participants by current position (unranked at bottom) const sorted = [...participants].toSorted((a, b) => { const posA = resultsMap[a.id]?.currentPosition ?? Infinity; const posB = resultsMap[b.id]?.currentPosition ?? Infinity; return posA - posB; }); return (

Update Standings

{sportsSeason.sport.name} — {sportsSeason.name}

{actionData?.error && (
{actionData.error}
)} {actionData?.success && (
Standings saved.
)} {/* Standings table */} Championship Standings Enter each participant's current position and championship points. Leave blank to skip.
# Participant Position Points
{sorted.map((participant) => { const result = resultsMap[participant.id]; return (
{result?.currentPosition ?? "—"} {participant.name}
); })}
); }