Created
August 6, 2024 16:21
-
-
Save aeither/d73a29d1d4a5e9b19927660317c5105d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use client"; | |
import { Button } from "@/components/ui/button"; | |
import { Card, CardContent } from "@/components/ui/card"; | |
import React, { useEffect, useState } from "react"; | |
type Match = [number | null, number | null]; | |
const TournamentBracket = () => { | |
const [teams, setTeams] = useState<string[]>(Array(8).fill("")); | |
const [matches, setMatches] = useState<Match[]>([ | |
[0, 1], | |
[2, 3], | |
[4, 5], | |
[6, 7], | |
[null, null], | |
[null, null], | |
[null, null], | |
]); | |
const [winner, setWinner] = useState<number | null>(null); | |
useEffect(() => { | |
resetTournament(); | |
}, []); | |
const resetTournament = () => { | |
setTeams([ | |
"Team 1", | |
"Team 2", | |
"Team 3", | |
"Team 4", | |
"Team 5", | |
"Team 6", | |
"Team 7", | |
"Team 8", | |
]); | |
setMatches([ | |
[0, 1], | |
[2, 3], | |
[4, 5], | |
[6, 7], | |
[null, null], | |
[null, null], | |
[null, null], | |
]); | |
setWinner(null); | |
}; | |
const handleWin = (matchIndex: number, winnerIndex: number) => { | |
const newMatches = [...matches]; | |
if (matchIndex < 4) { | |
// First round | |
if (matchIndex < 4) { | |
// First round | |
const semifinalIndex = Math.floor(matchIndex / 2) + 4; | |
const semifinalPosition = matchIndex % 2; | |
if ( | |
newMatches[semifinalIndex] && | |
newMatches[semifinalIndex][semifinalPosition] === null | |
) { | |
newMatches[semifinalIndex][semifinalPosition] = winnerIndex; | |
} | |
} else if (matchIndex < 6) { | |
// Semi-finals | |
if (newMatches[6] && newMatches[6][matchIndex - 4] === null) { | |
newMatches[6][matchIndex - 4] = winnerIndex; | |
} | |
} else { | |
// Final | |
setWinner(winnerIndex); | |
// Final | |
setWinner(winnerIndex); | |
} | |
setMatches(newMatches); | |
} | |
const isDisabled = (matchIndex: number, teamIndex: number | null) => { | |
if (teamIndex === null) return true; | |
if (matchIndex < 4) return false; // First round is always enabled | |
if (matchIndex < 6) { | |
// Semi-finals are enabled if the corresponding first round matches are decided | |
const firstRoundIndex1 = (matchIndex - 4) * 2; | |
const firstRoundIndex2 = firstRoundIndex1 + 1; | |
return ( | |
matches[firstRoundIndex1]?.[0] === null || | |
matches[firstRoundIndex1]?.[1] === null || | |
matches[firstRoundIndex2]?.[0] === null || | |
matches[firstRoundIndex2]?.[1] === null | |
); | |
} | |
// Final is enabled if both semi-finals are decided | |
return ( | |
matches[4]?.[0] === null || | |
matches[4]?.[1] === null || | |
matches[5]?.[0] === null || | |
matches[5]?.[1] === null | |
); | |
}; | |
const renderTeam = ( | |
teamIndex: number | null, | |
matchIndex: number, | |
position: number, | |
) => ( | |
<div className="my-1"> | |
<Button | |
onClick={() => teamIndex !== null && handleWin(matchIndex, teamIndex)} | |
disabled={isDisabled(matchIndex, teamIndex)} | |
className="h-10 w-40 bg-gray-800 text-white hover:bg-gray-700" | |
> | |
{teamIndex !== null ? teams[teamIndex] : "TBD"} | |
</Button> | |
</div> | |
); | |
const renderMatch = (matchIndex: number, teamIndices: Match) => ( | |
<Card className="mb-4 w-44" key={matchIndex}> | |
<CardContent className="p-2"> | |
{renderTeam(teamIndices[0], matchIndex, 0)} | |
{renderTeam(teamIndices[1], matchIndex, 1)} | |
</CardContent> | |
</Card> | |
); | |
return ( | |
<div className="flex p-4"> | |
<div className="mr-4"> | |
<h2 className="mb-2 text-lg font-bold">First Round</h2> | |
<div className="flex"> | |
<div className="mr-4"> | |
{matches.slice(0, 2).map((match, i) => renderMatch(i, match))} | |
</div> | |
<div> | |
{matches.slice(2, 4).map((match, i) => renderMatch(i + 2, match))} | |
</div> | |
</div> | |
</div> | |
<div className="mr-4"> | |
<h2 className="mb-2 text-lg font-bold">Semi-Finals</h2> | |
<div className="flex"> | |
<div className="mr-4"> | |
{matches[4] ? renderMatch(4, matches[4]) : null} | |
</div> | |
<div>{matches[5] ? renderMatch(5, matches[5]) : null}</div> | |
</div> | |
</div> | |
<div> | |
<h2 className="mb-2 text-lg font-bold">Final</h2> | |
{matches[6] ? renderMatch(6, matches[6]) : null} | |
</div> | |
<div className="ml-4"> | |
<h2 className="mb-2 text-lg font-bold">Winner</h2> | |
<Card className="w-44"> | |
<CardContent className="p-2 text-center"> | |
{winner !== null ? teams[winner] : "TBD"} | |
</CardContent> | |
</Card> | |
</div> | |
</div> | |
); | |
}; | |
}; | |
export default TournamentBracket; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment