Complete main functionallity of backend and frontend.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
// frontend/src/pages/Tournament.jsx
|
||||
|
||||
import { CalendarDays, Loader2, Network, Settings } from 'lucide-react';
|
||||
import { useEffect, useState, useRef } from 'react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useOutletContext, useParams } from 'react-router-dom';
|
||||
import BracketView from '../components/Bracket/BracketView';
|
||||
import TournamentForm from '../components/Forms/TournamentForm';
|
||||
@@ -14,15 +14,18 @@ export default function Tournament() {
|
||||
const { id } = useParams();
|
||||
const { setNavTitle, setNavSubtitle, isAdmin } = useOutletContext();
|
||||
|
||||
const [details, setDetails] = useState(null);
|
||||
const [matches, setMatches] = useState([]);
|
||||
const [view, setView] = useState('bracket');
|
||||
const [view, setView] = useState(() => localStorage.getItem('volley_view') || 'bracket');
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [showSettings, setShowSettings] = useState(false);
|
||||
const [scoreMatch, setScoreMatch] = useState(null);
|
||||
const wsRef = useRef(null);
|
||||
|
||||
// --- DATA PROCESSOR ---
|
||||
const handleViewChange = (newView) => {
|
||||
setView(newView);
|
||||
localStorage.setItem('volley_view', newView);
|
||||
};
|
||||
|
||||
const processMatches = (rawMatches, courts, teams) => {
|
||||
if (!rawMatches) return [];
|
||||
|
||||
@@ -43,10 +46,9 @@ export default function Tournament() {
|
||||
return rawMatches.map(m => {
|
||||
const sources = incoming[m.id] || [];
|
||||
|
||||
const p1 = m.p1_team_id ? teamMap[m.p1_team_id] : (sources[0]?.label || 'TBD');
|
||||
const p2 = m.p2_team_id ? teamMap[m.p2_team_id] : (sources[1]?.label || 'TBD');
|
||||
|
||||
const winnerName = m.winner_team_id ? teamMap[m.winner_team_id] : null;
|
||||
let sourceIndex = 0;
|
||||
const p1 = m.p1_team_id ? teamMap[m.p1_team_id] : (sources[sourceIndex++]?.label || 'TBD');
|
||||
const p2 = m.p2_team_id ? teamMap[m.p2_team_id] : (sources[sourceIndex++]?.label || 'TBD');
|
||||
|
||||
const hasTeams = !!(m.p1_team_id && m.p2_team_id);
|
||||
const isFinished = m.status === "Finished";
|
||||
@@ -61,7 +63,6 @@ export default function Tournament() {
|
||||
p2,
|
||||
p1_is_real: !!m.p1_team_id,
|
||||
p2_is_real: !!m.p2_team_id,
|
||||
winnerName,
|
||||
isReady,
|
||||
court: courtMap[m.court_id] || 'TBD',
|
||||
time: m.start_time ? new Date(m.start_time).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) : '',
|
||||
@@ -76,7 +77,6 @@ export default function Tournament() {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const res = await api.get(`/tournaments/${id}`);
|
||||
setDetails(res);
|
||||
setNavTitle(res.name);
|
||||
setNavSubtitle(new Date(res.timestamp).toLocaleDateString());
|
||||
setMatches(processMatches(res.matches, res.courts, res.teams));
|
||||
@@ -112,10 +112,10 @@ export default function Tournament() {
|
||||
<div className="h-full flex flex-col">
|
||||
<div className="border-b border-zinc-200 dark:border-zinc-800 bg-white/50 dark:bg-zinc-900/50 backdrop-blur px-6 py-3 flex justify-between items-center shrink-0 z-20">
|
||||
<div className="flex gap-2">
|
||||
<button onClick={() => setView('bracket')} className={`flex items-center gap-2 px-4 py-2 rounded-xl text-xs font-black uppercase tracking-wider transition ${view === 'bracket' ? 'bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-400' : 'text-zinc-500 hover:bg-zinc-100 dark:hover:bg-zinc-800'}`}>
|
||||
<button onClick={() => handleViewChange('bracket')} className={`flex items-center gap-2 px-4 py-2 rounded-xl text-xs font-black uppercase tracking-wider transition ${view === 'bracket' ? 'bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-400' : 'text-zinc-500 hover:bg-zinc-100 dark:hover:bg-zinc-800'}`}>
|
||||
<Network size={16} /> Bracket
|
||||
</button>
|
||||
<button onClick={() => setView('schedule')} className={`flex items-center gap-2 px-4 py-2 rounded-xl text-xs font-black uppercase tracking-wider transition ${view === 'schedule' ? 'bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-400' : 'text-zinc-500 hover:bg-zinc-100 dark:hover:bg-zinc-800'}`}>
|
||||
<button onClick={() => handleViewChange('schedule')} className={`flex items-center gap-2 px-4 py-2 rounded-xl text-xs font-black uppercase tracking-wider transition ${view === 'schedule' ? 'bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-400' : 'text-zinc-500 hover:bg-zinc-100 dark:hover:bg-zinc-800'}`}>
|
||||
<CalendarDays size={16} /> Schedule
|
||||
</button>
|
||||
</div>
|
||||
@@ -137,7 +137,11 @@ export default function Tournament() {
|
||||
/>
|
||||
)}
|
||||
<Modal isOpen={showSettings} onClose={() => setShowSettings(false)} title="Edit Tournament">
|
||||
<TournamentForm tournament={details} onSuccess={() => { setShowSettings(false); fetchData(); }} onDelete={handleDeleteTournament} />
|
||||
<TournamentForm
|
||||
tournamentId={id}
|
||||
onSuccess={() => { setShowSettings(false); fetchData(); }}
|
||||
onDelete={handleDeleteTournament}
|
||||
/>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user