import { Form, useLoaderData, useActionData, useNavigation, Link } from 'react-router'; import type { Route } from './+types/admin.sports-seasons.$id.events.$eventId.swiss-matches'; import { findSportsSeasonById } from '~/models/sports-season'; import { getScoringEventById } from '~/models/scoring-event'; import { findParticipantsBySportsSeasonId } from '~/models/season-participant'; import { findSeasonMatchesByScoringEventId, upsertSeasonMatch, updateSeasonMatch, deleteSeasonMatch, upsertMatchSubGame, } from '~/models/season-match'; import type { MatchStatus } from '~/models/season-match'; import { Button } from '~/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '~/components/ui/card'; import { Badge } from '~/components/ui/badge'; import { ArrowLeft, Plus, Save, Trash2 } from 'lucide-react'; export function meta({ data }: Route.MetaArgs): Route.MetaDescriptors { return [{ title: `Swiss Matches — ${data?.event?.name ?? "Event"} - Brackt Admin` }]; } export async function loader({ params }: Route.LoaderArgs) { const sportsSeasonId = params.id; const eventId = params.eventId; const [sportsSeason, event] = await Promise.all([ findSportsSeasonById(sportsSeasonId), getScoringEventById(eventId), ]); if (!sportsSeason) throw new Response('Sports season not found', { status: 404 }); if (!event) throw new Response('Event not found', { status: 404 }); const [participants, matches] = await Promise.all([ findParticipantsBySportsSeasonId(sportsSeasonId), findSeasonMatchesByScoringEventId(eventId), ]); return { sportsSeason, event, participants, matches }; } interface ActionData { success?: boolean; message?: string; } export async function action({ request, params }: Route.ActionArgs) { const eventId = params.eventId; const sportsSeasonId = params.id; const formData = await request.formData(); const intent = formData.get('intent') as string; if (intent === 'create-match') { const participant1Id = formData.get('participant1Id') as string; const participant2Id = formData.get('participant2Id') as string; const matchStage = parseInt(formData.get('matchStage') as string, 10); const matchRound = parseInt(formData.get('matchRound') as string, 10); const isSeries = formData.get('isSeries') === 'true'; if (!participant1Id || !participant2Id || isNaN(matchStage) || isNaN(matchRound)) { return { success: false, message: 'Missing required fields.' }; } const externalMatchId = `manual_${eventId}_s${matchStage}_r${matchRound}_${participant1Id}_${participant2Id}`; await upsertSeasonMatch({ sportsSeasonId, scoringEventId: eventId, participant1Id, participant2Id, matchStage, matchRound, isSeries, status: 'scheduled', externalMatchId, }); return { success: true, message: 'Match created.' }; } if (intent === 'update-result') { const matchId = formData.get('matchId') as string; const participant1Score = formData.get('participant1Score'); const participant2Score = formData.get('participant2Score'); const winnerId = formData.get('winnerId') as string | null; const status = formData.get('status') as MatchStatus; const p1Score = participant1Score ? parseInt(participant1Score as string, 10) : null; const p2Score = participant2Score ? parseInt(participant2Score as string, 10) : null; await updateSeasonMatch(matchId, { participant1Score: p1Score, participant2Score: p2Score, winnerId: winnerId || null, status, completedAt: status === 'complete' ? new Date() : null, }); // Handle sub-games (maps) if provided const mapCount = parseInt(formData.get('mapCount') as string ?? '0', 10); for (let i = 1; i <= mapCount; i++) { const mapLabel = formData.get(`map${i}_label`) as string | null; const map1Score = formData.get(`map${i}_p1`) as string | null; const map2Score = formData.get(`map${i}_p2`) as string | null; if (map1Score !== null && map2Score !== null) { await upsertMatchSubGame({ seasonMatchId: matchId, gameNumber: i, gameLabel: mapLabel || null, participant1Score: parseInt(map1Score, 10), participant2Score: parseInt(map2Score, 10), status: 'complete', }); } } return { success: true, message: 'Result saved.' }; } if (intent === 'delete-match') { const matchId = formData.get('matchId') as string; await deleteSeasonMatch(matchId); return { success: true, message: 'Match deleted.' }; } return { success: false, message: 'Unknown action.' }; } const STAGE_NAMES: Record = { 1: 'Opening Stage', 2: 'Challengers Stage', 3: 'Legends Stage', }; const STATUS_BADGE: Record = { scheduled: { label: 'Scheduled', variant: 'outline' }, in_progress: { label: 'Live', variant: 'default' }, complete: { label: 'Complete', variant: 'secondary' }, canceled: { label: 'Canceled', variant: 'destructive' }, postponed: { label: 'Postponed', variant: 'outline' }, }; export default function AdminSwissMatches() { const { sportsSeason, event, participants, matches } = useLoaderData(); const actionData = useActionData(); const navigation = useNavigation(); const isSubmitting = navigation.state === 'submitting'; // Group matches by stage → round const byStage = new Map>(); for (const m of matches) { const stage = m.matchStage ?? 0; const round = m.matchRound ?? 0; let stageMap = byStage.get(stage); if (!stageMap) { stageMap = new Map(); byStage.set(stage, stageMap); } let roundList = stageMap.get(round); if (!roundList) { roundList = []; stageMap.set(round, roundList); } roundList.push(m); } return (
Back to CS2 Setup

Swiss Match Entry

{event.name} — {sportsSeason.name}

{actionData?.message && (
{actionData.message}
)} {/* Create new match */} Add Match Manually add a Swiss round match.
{/* Existing matches grouped by stage/round */} {matches.length === 0 ? (

No matches yet.

) : ( [...byStage.entries()].toSorted(([a], [b]) => a - b).map(([stage, rounds]) => ( Stage {stage} — {STAGE_NAMES[stage] ?? ''} {[...rounds.entries()].toSorted(([a], [b]) => a - b).map(([round, roundMatches]) => (

Round {round}

{roundMatches.map(match => (
{match.participant1?.name ?? '?'} vs {match.participant2?.name ?? '?'}
{STATUS_BADGE[match.status]?.label ?? match.status}
{match.isSeries && ( <> {[1, 2, 3].map(mapNum => { const subGame = match.subGames.find(g => g.gameNumber === mapNum); return (
); })} )}
))}
))}
)) )}
); }