Typscript final

This commit is contained in:
2026-03-11 16:44:09 +01:00 Verified
parent 42b28350de
commit edd9b064ec
24 changed files with 486 additions and 419 deletions
+17 -12
View File
@@ -1,7 +1,7 @@
// frontend/src/pages/Dashboard.tsx
import { Calendar, ChevronDown, ChevronUp, History, Plus, PlusCircle, SlidersHorizontal } from 'lucide-react';
import React, { useEffect, useState } from 'react';
import { useEffect, useState, useCallback } from 'react';
import { useNavigate, useOutletContext } from 'react-router-dom';
import DashCard from '../components/Dashboard/DashCard';
import TournamentForm from '../components/Forms/TournamentForm';
@@ -11,8 +11,11 @@ import api, { WS_URL } from '../services/api';
export interface TournamentType {
id: string | number;
name: string;
timestamp: string;
[key: string]: any;
type: string;
team_count: number;
court_count: number;
}
interface OutletContextType {
@@ -32,20 +35,22 @@ export default function Dashboard() {
const [showAllFuture, setShowAllFuture] = useState<boolean>(false);
const navigate = useNavigate();
const loadDashboard = async () => {
const loadDashboard = useCallback(async () => {
try {
const res: any = await api.get('/tournaments');
const list = Array.isArray(res) ? res : (res.items || []);
setTournaments(list);
const res = await api.get<{ items?: TournamentType[] } | TournamentType[]>('/tournaments');
const list = Array.isArray(res) ? res : (res?.items || []);
setTournaments(list as TournamentType[]);
} catch (e) {
console.error(e);
}
};
}, []);
useEffect(() => {
setNavTitle('Dashboard');
setNavSubtitle('');
loadDashboard();
// eslint-disable-next-line react-hooks/set-state-in-effect
void loadDashboard();
localStorage.removeItem('volley_view');
let ws: WebSocket;
@@ -63,7 +68,7 @@ export default function Dashboard() {
connect();
return () => { if (ws) ws.close(); };
}, [setNavTitle, setNavSubtitle]);
}, [setNavTitle, setNavSubtitle, loadDashboard]);
const handleEdit = (t: TournamentType) => {
setEditTarget(t);
@@ -126,7 +131,7 @@ export default function Dashboard() {
</span> Live Events
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{groups.live.map(t => <DashCard key={t.id} t={t} isAdmin={isAdmin} onSelect={(id: string) => navigate(`/tournaments/${id}`)} onEdit={handleEdit} />)}
{groups.live.map(t => <DashCard key={t.id} t={t} isAdmin={isAdmin} onSelect={(id) => navigate(`/tournaments/${id}`)} onEdit={handleEdit} />)}
</div>
</section>
)}
@@ -138,7 +143,7 @@ export default function Dashboard() {
{groups.future.length > 0 ? (
<>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{futureShow.map(t => <DashCard key={t.id} t={t} isAdmin={isAdmin} onSelect={(id: string) => navigate(`/tournaments/${id}`)} onEdit={handleEdit} />)}
{futureShow.map(t => <DashCard key={t.id} t={t} isAdmin={isAdmin} onSelect={(id) => navigate(`/tournaments/${id}`)} onEdit={handleEdit} />)}
</div>
{groups.future.length > 4 && (
<div className="mt-8 text-center">
@@ -160,7 +165,7 @@ export default function Dashboard() {
</button>
{showPast && (
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 opacity-75 hover:opacity-100 transition-opacity">
{groups.past.map(t => <DashCard key={t.id} t={t} isAdmin={isAdmin} onSelect={(id: string) => navigate(`/tournaments/${id}`)} onEdit={handleEdit} />)}
{groups.past.map(t => <DashCard key={t.id} t={t} isAdmin={isAdmin} onSelect={(id) => navigate(`/tournaments/${id}`)} onEdit={handleEdit} />)}
</div>
)}
</section>
+1 -1
View File
@@ -26,7 +26,7 @@ export default function Login() {
const res = await api.postForm<TokenResponse>('/auth/token', formData);
localStorage.setItem('volleyToken', res.access_token);
navigate('/');
} catch (err) {
} catch {
setError('Invalid credentials.');
} finally {
setLoading(false);
+10 -10
View File
@@ -1,5 +1,6 @@
// frontend/src/pages/Tournament.tsx
import { type SetData } from '../types';
import { CalendarDays, Loader2, Network, SlidersHorizontal } from 'lucide-react';
import React, { useEffect, useRef, useState } from 'react';
import { useOutletContext, useParams } from 'react-router-dom';
@@ -134,9 +135,9 @@ export default function Tournament() {
});
};
const fetchData = async () => {
const fetchData = React.useCallback(async () => {
try {
const res: any = await api.get(`/tournaments/${id}`);
const res = await api.get<{ name: string, timestamp: string, matches: RawMatch[], courts: Court[], teams: Team[] }>(`/tournaments/${id}`);
setNavTitle(res.name);
setNavSubtitle(new Date(res.timestamp).toLocaleDateString());
setMatches(processMatches(res.matches, res.courts, res.teams));
@@ -145,10 +146,10 @@ export default function Tournament() {
} finally {
setLoading(false);
}
};
}, [id, setNavTitle, setNavSubtitle]);
useEffect(() => {
fetchData();
void fetchData();
if (wsRef.current) return;
const connect = () => {
@@ -156,7 +157,7 @@ export default function Tournament() {
wsRef.current = ws;
ws.onmessage = (e: MessageEvent) => {
const msg = JSON.parse(e.data);
if (msg.type === 'tournament_update' && msg.id === id) fetchData();
if (msg.type === 'tournament_update' && msg.id === id) void fetchData();
};
ws.onclose = () => { wsRef.current = null; };
};
@@ -167,7 +168,7 @@ export default function Tournament() {
if (wsRef.current?.readyState === 1) wsRef.current.close();
wsRef.current = null;
};
}, [id, setNavTitle, setNavSubtitle]);
}, [id, fetchData]);
const handleDeleteTournament = async (tId: string | number) => {
if (window.confirm("Purge this tournament?")) {
@@ -194,8 +195,8 @@ export default function Tournament() {
<div className="flex-1 overflow-hidden relative print:overflow-visible print:h-auto print:block">
{view === 'bracket'
? <BracketView matches={matches} onMatchClick={setScoreMatch} />
: <ScheduleView schedule={matches} onMatchClick={setScoreMatch} />
? <BracketView matches={matches} onMatchClick={(m) => setScoreMatch(m as ProcessedMatch)} />
: <ScheduleView schedule={matches} onMatchClick={(m) => setScoreMatch(m as ProcessedMatch)} />
}
</div>
@@ -209,9 +210,8 @@ export default function Tournament() {
await api.delete(`/tournaments/${id}/matches/${mid}/score?code=${encodeURIComponent(c || '')}`);
setScoreMatch(null);
}}
onSubmit={async (mid: string | number, s: any, c: string) => {
onSubmit={async (mid: string | number, s: SetData[], c: string) => {
const method = scoreMatch.isFinished ? 'patch' : 'post';
// @ts-ignore - Indexing api dynamically
await api[method](`/tournaments/${id}/matches/${mid}/score`, { sets: s, code: c });
setScoreMatch(null);
}}