Complete main functionallity of backend and frontend.
This commit is contained in:
@@ -1,47 +1,9 @@
|
||||
// frontend/src/components/Bracket/BracketView.jsx
|
||||
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { Check } from 'lucide-react';
|
||||
import { stringToColor } from '../../utils/helpers';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import MatchCard from "./MatchCard";
|
||||
import Podium from './Podium';
|
||||
|
||||
const MatchCard = ({ match, onClick }) => {
|
||||
const isFinished = match.status === "Finished";
|
||||
const badgeColor = match.time ? stringToColor(match.court) : null;
|
||||
|
||||
let borderClass = 'border-zinc-300 dark:border-zinc-700';
|
||||
if (isFinished) borderClass = 'border-orange-500 ring-2 ring-orange-500/10';
|
||||
|
||||
const canInteract = match.hasTeams;
|
||||
|
||||
const cursorClass = canInteract
|
||||
? 'cursor-pointer hover:shadow-md hover:-translate-y-0.5'
|
||||
: 'cursor-default opacity-100';
|
||||
|
||||
return (
|
||||
<div
|
||||
id={`match-${match.id}`}
|
||||
onClick={() => canInteract && onClick(match)}
|
||||
className={`w-64 bg-white dark:bg-zinc-900 rounded-lg border ${borderClass} shadow-sm transition-all duration-200 relative z-10 flex flex-col ${cursorClass}`}
|
||||
>
|
||||
<div className="bg-zinc-50 dark:bg-zinc-900/50 px-3 py-2 flex justify-between items-center border-b border-zinc-200 dark:border-zinc-800 rounded-t-lg">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-mono text-[10px] font-bold text-zinc-400"># {match.number}</span>
|
||||
{match.time && <span className="text-[9px] font-black text-white px-1.5 py-0.5 rounded-sm uppercase" style={{ background: badgeColor }}>{match.court}</span>}
|
||||
</div>
|
||||
{isFinished ? <Check className="text-orange-500" size={14} strokeWidth={3} /> : <span className="text-[10px] font-bold text-zinc-500 font-mono">{match.time || 'TBD'}</span>}
|
||||
</div>
|
||||
<div className="p-3 space-y-2">
|
||||
{[{ n: match.p1, s: match.p1_sets, win: match.winner === match.p1, real: match.p1_is_real },
|
||||
{ n: match.p2, s: match.p2_sets, win: match.winner === match.p2, real: match.p2_is_real }].map((p, i) => (
|
||||
<div key={i} className={`flex justify-between items-center ${p.win ? 'text-zinc-900 dark:text-white font-black' : p.real ? 'text-zinc-600 dark:text-zinc-300' : 'text-zinc-400 italic'}`}>
|
||||
<span className="truncate text-xs uppercase tracking-tight">{p.n}</span>
|
||||
<span className={`px-2 py-0.5 rounded text-[10px] font-bold ${p.win ? 'bg-orange-100 dark:bg-orange-900/30 text-orange-700' : 'bg-zinc-100 dark:bg-zinc-800 text-zinc-500'}`}>{p.s}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default function BracketView({ matches, onMatchClick }) {
|
||||
const containerRef = useRef(null);
|
||||
@@ -85,7 +47,7 @@ export default function BracketView({ matches, onMatchClick }) {
|
||||
} else {
|
||||
matchesInRound.sort((a, b) => {
|
||||
const getSourceAvg = (match) => {
|
||||
const sources = list.filter(x => x.next_win === match.id);
|
||||
const sources = list.filter(x => x.winner_next_match_id === match.id);
|
||||
if (sources.length === 0) return 9999;
|
||||
const indices = sources.map(s => prevRoundMap.get(s.id)).filter(i => i !== undefined);
|
||||
if (indices.length === 0) return 9999;
|
||||
@@ -110,9 +72,6 @@ export default function BracketView({ matches, onMatchClick }) {
|
||||
|
||||
return (
|
||||
<div className="w-full h-full overflow-auto bg-[#f8f9fa] dark:bg-zinc-950 bg-[radial-gradient(#e5e7eb_1px,transparent_1px)] dark:bg-[radial-gradient(#27272a_1px,transparent_1px)] [background-size:24px_24px]">
|
||||
{/* UPDATED: items-center ensures Finals (right col) are centered vertically
|
||||
relative to the Winners/Losers block (left col).
|
||||
*/}
|
||||
<div ref={containerRef} className="relative min-w-max min-h-full p-12 flex gap-20 items-center">
|
||||
<svg className="absolute inset-0 w-full h-full pointer-events-none z-0">
|
||||
{lines}
|
||||
@@ -130,12 +89,20 @@ export default function BracketView({ matches, onMatchClick }) {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{matches.some(m => m.bracket === 'Finals') && (
|
||||
<div className="flex flex-col justify-center gap-6">
|
||||
<div className="text-[10px] font-black uppercase bg-orange-100 dark:bg-orange-900/30 text-orange-600 px-4 py-1.5 rounded-full border border-orange-200 dark:border-orange-800 shadow-sm mx-auto">Championship</div>
|
||||
{finals.map(m => <MatchCard key={m.id} match={m} onClick={onMatchClick} />)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-col justify-center gap-6 z-10">
|
||||
{matches.some(m => m.bracket === 'Finals') && (
|
||||
<>
|
||||
<div className="text-[10px] font-black uppercase bg-orange-100 dark:bg-orange-900/30 text-orange-600 px-4 py-1.5 rounded-full border border-orange-200 dark:border-orange-800 shadow-sm mx-auto">Championship</div>
|
||||
{finals.map(m => <MatchCard key={m.id} match={m} onClick={onMatchClick} />)}
|
||||
</>
|
||||
)}
|
||||
|
||||
</div>
|
||||
<div className="flex flex-col justify-center gap-6 z-10">
|
||||
<Podium matches={matches} />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user