"use client" import { useState, useEffect } from "react" import { useRouter } from "next/navigation" import Navigation from "@/components/Navigation" interface Player { id: number name: string currentElo: number } interface Team { id: number player1: Player player2: Player } interface BracketMatchup { id: number roundId: number team1Id: number | null team2Id: number | null tableNumber: number | null status: string team1: Team | null team2: Team | null matchId: number | null } interface TournamentRound { id: number roundNumber: number status: string matchups: BracketMatchup[] } interface Schedule { rounds: TournamentRound[] } interface Match { id: number player1P1Id: number player1P2Id: number player2P1Id: number player2P2Id: number team1Score: number team2Score: number status: string } interface Tournament { id: number name: string eventDate: string | null format: string tournamentType: string participants: { player: Player }[] } export default function TournamentEntryPage({ params }: { params: Promise<{ id: string }> }) { const router = useRouter() const [tournament, setTournament] = useState(null) const [tournamentId, setTournamentId] = useState(null) const [schedule, setSchedule] = useState(null) const [matches, setMatches] = useState([]) const [error, setError] = useState("") const [success, setSuccess] = useState("") const [isLoading, setIsLoading] = useState(false) const [selectedRoundId, setSelectedRoundId] = useState(null) const [selectedMatchupId, setSelectedMatchupId] = useState(null) const [team1Score, setTeam1Score] = useState("") const [team2Score, setTeam2Score] = useState("") // Parse params and validate tournamentId, check for matchup query param useEffect(() => { async function parseParams() { const { id } = await params const parsedId = parseInt(id, 10) if (isNaN(parsedId)) { router.push('/admin/tournaments') } else { setTournamentId(parsedId) } } parseParams() }, [params, router]) // Handle pre-selection of matchup from query param useEffect(() => { if (schedule && selectedMatchupId === null) { const searchParams = new URLSearchParams(window.location.search) const matchupIdParam = searchParams.get('matchup') if (matchupIdParam) { const matchupId = parseInt(matchupIdParam, 10) // Find which round contains this matchup for (const round of schedule.rounds) { const matchup = round.matchups.find(m => m.id === matchupId) if (matchup) { setSelectedRoundId(round.id) setSelectedMatchupId(matchupId) break } } } } }, [schedule, selectedMatchupId]) // Load tournament, schedule, and matches useEffect(() => { if (tournamentId) { loadTournament() loadSchedule() loadMatches() } // eslint-disable-next-line react-hooks/exhaustive-deps }, [tournamentId]) const loadTournament = async () => { try { const response = await fetch(`/api/tournaments/${tournamentId}`) if (response.ok) { const data = await response.json() setTournament(data.tournament) } } catch (err) { console.error("Failed to load tournament:", err) } } const loadSchedule = async () => { try { const response = await fetch(`/api/tournaments/${tournamentId}/schedule`) if (response.ok) { const data = await response.json() // API returns { rounds: [...] }, wrap in schedule object const rounds = data.rounds || [] setSchedule({ rounds }) if (rounds.length > 0) { setSelectedRoundId(rounds[0].id) } } } catch (err) { console.error("Failed to load schedule:", err) } } const loadMatches = async () => { try { const response = await fetch(`/api/tournaments/${tournamentId}/matches`) if (response.ok) { const data = await response.json() setMatches(data.matches || []) } } catch (err) { console.error("Failed to load matches:", err) } } const selectedRound = schedule?.rounds.find(r => r.id === selectedRoundId) const selectedMatchup = selectedRound?.matchups.find(m => m.id === selectedMatchupId) const getMatchupMatch = (matchup: BracketMatchup): Match | undefined => { if (!matchup.matchId) return undefined return matches.find(m => m.id === matchup.matchId) } const isMatchupCompleted = (matchup: BracketMatchup): boolean => { return getMatchupMatch(matchup) !== undefined } const handleSelectMatchup = (matchup: BracketMatchup) => { setSelectedMatchupId(matchup.id) setTeam1Score("") setTeam2Score("") } const handleSubmitScore = async () => { if (!selectedMatchup || !tournamentId) return const score1 = parseInt(team1Score) const score2 = parseInt(team2Score) if (isNaN(score1) || isNaN(score2)) { setError("Please enter valid scores for both teams") return } setError("") setSuccess("") setIsLoading(true) try { // Get team player IDs const team1 = selectedMatchup.team1 const team2 = selectedMatchup.team2 if (!team1 || !team2) { throw new Error("Teams not found for this matchup") } // Create match via bulk API const matchData = { round: selectedRound?.roundNumber || 1, table: selectedMatchup.tableNumber || 1, player1: team1.player1.name, player2: team1.player2.name, score1: score1, player3: team2.player1.name, player4: team2.player2.name, score2: score2, } const response = await fetch(`/api/tournaments/${tournamentId}/games/bulk`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ games: [matchData], }), }) if (!response.ok) { try { const errorData = await response.json() throw new Error(errorData.error || "Failed to submit score") } catch (jsonError) { if (jsonError instanceof Error && jsonError.message !== "Failed to submit score") { throw jsonError } throw new Error(`Failed to submit score: ${response.status} ${response.statusText}`) } } const data = await response.json() setSuccess(`Score recorded: ${team1.player1.name} & ${team1.player2.name} ${score1} - ${score2} ${team2.player1.name} & ${team2.player2.name}`) setTeam1Score("") setTeam2Score("") setSelectedMatchupId(null) loadMatches() // Refresh matches } catch (err) { if (err instanceof Error) { setError(err.message) } else { setError("An unknown error occurred") } } finally { setIsLoading(false) } } if (!tournament) { return (
) } const completedMatchups = schedule?.rounds.flatMap(r => r.matchups).filter(m => isMatchupCompleted(m)).length || 0 const totalMatchups = schedule?.rounds.flatMap(r => r.matchups).length || 0 return (

{tournament.name}

{tournament.eventDate && (

{new Date(tournament.eventDate).toLocaleDateString()}

)}
{error && (
{error}
)} {success && (
{success}
)} {/* Progress Summary */} {schedule && (

Tournament Progress

{completedMatchups} of {totalMatchups} games completed

{totalMatchups > 0 ? Math.round((completedMatchups / totalMatchups) * 100) : 0}%

complete

0 ? (completedMatchups / totalMatchups) * 100 : 0}%` }} />
)}
{/* Rounds Panel */}

Rounds

{schedule?.rounds ? (
{schedule.rounds.map(round => { const roundCompleted = round.matchups.every(m => isMatchupCompleted(m)) const roundInProgress = round.matchups.some(m => isMatchupCompleted(m)) && !roundCompleted return ( ) })}
) : (

No schedule generated yet.

)}
{/* Participants Panel */}

Participants ({tournament.participants.length})

{tournament.participants.map(({ player }) => (
{player.name}
))}
{/* Round Detail Panel */}

{selectedRound ? `Round ${selectedRound.roundNumber} Matchups` : 'Select a Round'}

{selectedRound ? (
{selectedRound.matchups.map((matchup, index) => { const completed = isMatchupCompleted(matchup) const match = getMatchupMatch(matchup) const isSelected = selectedMatchupId === matchup.id return (
!completed && handleSelectMatchup(matchup)} >
Match {index + 1} {matchup.tableNumber && ` • Table ${matchup.tableNumber}`} {completed && ( Completed )}
{matchup.team1 && matchup.team2 ? (

{matchup.team1.player1.name} & {matchup.team1.player2.name}

Team {matchup.team1.id}

{completed && match ? (
match.team2Score ? 'text-green-600' : 'text-gray-600'}`}> {match.team1Score} - match.team1Score ? 'text-green-600' : 'text-gray-600'}`}> {match.team2Score}
) : ( vs )}

{matchup.team2.player1.name} & {matchup.team2.player2.name}

Team {matchup.team2.id}

) : (

Teams not assigned

)} {/* Score Entry Form */} {isSelected && !completed && matchup.team1 && matchup.team2 && (

Enter Score

setTeam1Score(e.target.value)} placeholder="0" />
-
setTeam2Score(e.target.value)} placeholder="0" />
)}
) })}
) : (
Select a round to view matchups
)}
) }