// frontend/src/components/Forms/TournamentForm.tsx import { Loader2, Plus } from 'lucide-react'; import React, { useEffect, useState } from 'react'; import api from '../../services/api'; interface Court { id: number; name: string; } interface TournamentSettings { id: string | number; name: string; code: string; type: string; timestamp: string; duration: number; courts: Court[]; teams: { name?: string }[]; } interface TournamentFormProps { tournamentId?: string | number | null; onSuccess: () => void; onDelete?: (id: string | number) => void; } export default function TournamentForm({ tournamentId, onSuccess, onDelete }: TournamentFormProps) { const [initialData, setInitialData] = useState(null); const [globalCourts, setGlobalCourts] = useState([]); const [selectedCourts, setSelectedCourts] = useState([]); const [newCourtName, setNewCourtName] = useState(''); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const [isSubmitting, setIsSubmitting] = useState(false); useEffect(() => { const fetchDependencies = async () => { try { const courtsRes = await api.get('/courts'); setGlobalCourts(courtsRes); if (tournamentId) { const data = await api.get(`/tournaments/${tournamentId}/settings`); setInitialData(data); setSelectedCourts(data.courts.map(c => c.id)); } } catch (err) { console.error(err); setError("Failed to load tournament data."); } finally { setIsLoading(false); } }; void fetchDependencies(); }, [tournamentId]); const handleAddCourt = async () => { if (!newCourtName.trim()) return; try { const added = await api.post('/courts', { name: newCourtName.trim() }); setGlobalCourts([...globalCourts, added]); setSelectedCourts([...selectedCourts, added.id]); setNewCourtName(''); } catch { setError("Failed to create new court."); } }; const toggleCourt = (id: number) => { setSelectedCourts(prev => prev.includes(id) ? prev.filter(c => c !== id) : [...prev, id] ); }; const handleSubmit = async (e: React.SyntheticEvent) => { e.preventDefault(); setIsSubmitting(true); setError(null); const formData = new FormData(e.currentTarget); const rawTeams = formData.get('teams') as string; const date = formData.get('date') as string; const startTime = formData.get('start_time') as string; const typeRaw = formData.get('type') as string; const duration = formData.get('duration') as string; const name = formData.get('name') as string; const code = formData.get('code') as string; const teams = rawTeams.split('\n').map(t => t.trim()).filter(t => t.length > 0); if (teams.length < 2) { setError("At least 2 teams required."); setIsSubmitting(false); return; } if (selectedCourts.length === 0) { setError("Please select at least one court."); setIsSubmitting(false); return; } const timestamp = `${date}T${startTime}:00`; const formattedType = typeRaw.charAt(0).toUpperCase() + typeRaw.slice(1); const parsedDuration = parseInt(duration, 10); try { if (tournamentId) { const basePayload = { name, code, type: formattedType, timestamp, duration: parsedDuration }; await Promise.all([ api.patch(`/tournaments/${tournamentId}`, basePayload), api.patch(`/tournaments/${tournamentId}/teams`, teams), api.patch(`/tournaments/${tournamentId}/courts`, selectedCourts) ]); } else { const fullPayload = { name, code, type: formattedType, timestamp, duration: parsedDuration, teams, courts: selectedCourts }; await api.post('/tournaments', fullPayload); } onSuccess(); } catch (err: unknown) { console.error(err); setError("Error saving tournament"); } finally { setIsSubmitting(false); } }; if (isLoading) { return (
); } let defaultDate = new Date().toISOString().split('T')[0]; let defaultTime = "09:00"; if (initialData?.timestamp) { const [d, t] = initialData.timestamp.split('T'); defaultDate = d; defaultTime = t ? t.substring(0, 5) : "09:00"; } return (
{error && (
{error}
)}
{/* --- NEW: GLOBAL COURT SELECTOR --- */}
{globalCourts.map(c => ( ))} {globalCourts.length === 0 && No courts registered yet.}
setNewCourtName(e.target.value)} placeholder="Add new court..." className="flex-1 bg-white dark:bg-zinc-950 border border-zinc-300 dark:border-zinc-700 rounded px-3 py-1 text-sm focus:border-orange-500 outline-none text-zinc-900 dark:text-white" />