Skip to content

Instantly share code, notes, and snippets.

@aeither
Created August 6, 2024 16:21
Show Gist options
  • Save aeither/d73a29d1d4a5e9b19927660317c5105d to your computer and use it in GitHub Desktop.
Save aeither/d73a29d1d4a5e9b19927660317c5105d to your computer and use it in GitHub Desktop.
"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