// frontend/src/components/Bracket/BracketView.tsx import React, { useEffect, useRef, useState } from 'react'; import { type MatchData } from '../../types'; import Podium from "../Tournament/Podium"; import MatchCard from "./MatchCard"; interface BracketViewProps { matches: MatchData[]; onMatchClick: (match: MatchData) => void; } export default function BracketView({ matches, onMatchClick }: BracketViewProps) { const containerRef = useRef(null); const [lines, setLines] = useState([]); useEffect(() => { const draw = () => { if (!containerRef.current) return; const container = containerRef.current.getBoundingClientRect(); const newLines: React.ReactElement[] = []; matches.forEach(m => { if (m.winner_next_match_id) { const sEl = document.getElementById(`match-${m.id}`); const eEl = document.getElementById(`match-${m.winner_next_match_id}`); if (sEl && eEl) { const r1 = sEl.getBoundingClientRect(), r2 = eEl.getBoundingClientRect(); const sx = r1.right - container.left, sy = r1.top + r1.height / 2 - container.top; const ex = r2.left - container.left, ey = r2.top + r2.height / 2 - container.top; const c1 = sx + (ex - sx) / 2; newLines.push( ); } } if (m.loser_next_match_id) { const targetMatch = matches.find(x => x.id === m.loser_next_match_id); if (targetMatch && targetMatch.bracket === 'Finals') { const sEl = document.getElementById(`match-${m.id}`); const eEl = document.getElementById(`match-${targetMatch.id}`); if (sEl && eEl) { const r1 = sEl.getBoundingClientRect(), r2 = eEl.getBoundingClientRect(); const sx = r1.right - container.left, sy = r1.top + r1.height / 2 - container.top; const ex = r2.left - container.left, ey = r2.top + r2.height / 2 - container.top; const c1 = sx + (ex - sx) / 2; newLines.push( ); } } } }); setLines(newLines); }; const t = setTimeout(draw, 100); window.addEventListener('resize', draw); const handlePrint = () => { draw(); setTimeout(draw, 100); }; const mql = window.matchMedia('print'); mql.addEventListener('change', handlePrint); window.addEventListener('beforeprint', handlePrint); window.addEventListener('afterprint', draw); return () => { clearTimeout(t); window.removeEventListener('resize', draw); mql.removeEventListener('change', handlePrint); window.removeEventListener('beforeprint', handlePrint); window.removeEventListener('afterprint', draw); }; }, [matches]); const renderRound = (list: MatchData[]) => { const rounds: Record = {}; list.forEach(m => (rounds[m.round] = rounds[m.round] || []).push(m)); const roundKeys = Object.keys(rounds).map(Number).sort((a, b) => a - b); return roundKeys.map((r) => { const matchesInRound = rounds[r]; matchesInRound.sort((a, b) => a.number - b.number); return (
{matchesInRound.map(m => )}
); }); }; const isDoubleElim = matches.some(m => m.bracket === 'Loser'); const wb = matches.filter(m => m.bracket === 'Winner'); const lb = matches.filter(m => m.bracket === 'Loser'); const finals = matches.filter(m => m.bracket === 'Finals'); const displayWb = [...wb]; const displayFinals = [...finals]; if (!isDoubleElim && displayWb.length > 0) { const maxRound = Math.max(...displayWb.map(m => m.round)); const gfIndex = displayWb.findIndex(m => m.round === maxRound); if (gfIndex !== -1) { const gfMatch = displayWb.splice(gfIndex, 1)[0]; displayFinals.unshift(gfMatch); } } return (
{lines}
Winners Bracket
{renderRound(displayWb)}
{isDoubleElim && (
Losers Bracket
{renderRound(lb)}
)}
{displayFinals.length > 0 && (
Championship
{displayFinals.map(m => )}
)}
); }