Neue Teamuebersicht
Some checks are pending
Deploy Volleyball Dev / deploy (push) Waiting to run

This commit is contained in:
Marc Wieland 2025-04-29 13:16:54 +02:00
parent 6f5d0a0fd0
commit 1d5dea8ff1
6 changed files with 338 additions and 66 deletions

View File

@ -18,8 +18,8 @@ const AdminDashboard = () => {
</Button> </Button>
</div> </div>
<div className="grid md:grid-cols-2 gap-6"> <div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
{/* Jeder eingeloggt Benutzer darf News verwalten */} {/* Jeder Benutzer darf News verwalten */}
<Link to="/admin/news"> <Link to="/admin/news">
<Card className="hover:shadow-lg transition-shadow cursor-pointer"> <Card className="hover:shadow-lg transition-shadow cursor-pointer">
<CardContent className="p-6 text-center"> <CardContent className="p-6 text-center">
@ -49,6 +49,15 @@ const AdminDashboard = () => {
</CardContent> </CardContent>
</Card> </Card>
</Link> </Link>
<Link to="/admin/players">
<Card className="hover:shadow-lg transition-shadow cursor-pointer">
<CardContent className="p-6 text-center">
<CardTitle className="text-frog-600">Spieler verwalten</CardTitle>
<p className="text-gray-600 mt-2 text-sm">Alle Spieler sehen und bearbeiten</p>
</CardContent>
</Card>
</Link>
</div> </div>
</div> </div>
); );

View File

@ -2,14 +2,21 @@ import { useEffect, useState } from "react";
import { useParams, useNavigate, Link } from "react-router-dom"; import { useParams, useNavigate, Link } from "react-router-dom";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { useToast } from "@/components/ui//use-toast"; import { useToast } from "@/components/ui/use-toast";
import { Label } from "@/components/ui/label";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
const apiBase = import.meta.env.VITE_API_URL; const apiBase = import.meta.env.VITE_API_URL;
type Team = {
id: number;
name: string;
};
const PlayerEdit = () => { const PlayerEdit = () => {
const { id } = useParams<{ id: string }>(); const { id } = useParams<{ id: string }>();
const navigate = useNavigate(); const navigate = useNavigate();
const {toast} = useToast(); const { toast } = useToast();
const [name, setName] = useState(""); const [name, setName] = useState("");
const [nickname, setNickname] = useState(""); const [nickname, setNickname] = useState("");
@ -21,8 +28,12 @@ const PlayerEdit = () => {
const [imageUrl, setImageUrl] = useState<string | null>(null); const [imageUrl, setImageUrl] = useState<string | null>(null);
const [uploading, setUploading] = useState(false); const [uploading, setUploading] = useState(false);
const [teams, setTeams] = useState<Team[]>([]);
const [selectedTeamId, setSelectedTeamId] = useState<number | null>(null);
useEffect(() => { useEffect(() => {
fetchPlayer(); fetchPlayer();
fetchTeams();
}, [id]); }, [id]);
const fetchPlayer = async () => { const fetchPlayer = async () => {
@ -38,11 +49,24 @@ const PlayerEdit = () => {
setFavoriteFood(data.favorite_food || ""); setFavoriteFood(data.favorite_food || "");
setStatus(data.status || "aktiv"); setStatus(data.status || "aktiv");
setImageUrl(data.image_url ? `${apiBase}${data.image_url}` : null); setImageUrl(data.image_url ? `${apiBase}${data.image_url}` : null);
if (data.team_ids && data.team_ids.length > 0) {
setSelectedTeamId(data.team_ids[0]);
}
} catch (err) { } catch (err) {
console.error("Fehler beim Laden des Spielers:", err); console.error("Fehler beim Laden des Spielers:", err);
} }
}; };
const fetchTeams = async () => {
try {
const res = await fetch(`${apiBase}/api/teams`);
const data = await res.json();
setTeams(data);
} catch (err) {
console.error("Fehler beim Laden der Teams:", err);
}
};
const handleImageUpload = async (file: File) => { const handleImageUpload = async (file: File) => {
const formData = new FormData(); const formData = new FormData();
formData.append("image", file); formData.append("image", file);
@ -67,6 +91,7 @@ const PlayerEdit = () => {
const handleSave = async () => { const handleSave = async () => {
try { try {
// Spieler speichern
const res = await fetch(`${apiBase}/api/players/${id}`, { const res = await fetch(`${apiBase}/api/players/${id}`, {
method: "PUT", method: "PUT",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
@ -78,27 +103,34 @@ const PlayerEdit = () => {
birthdate, birthdate,
favorite_food: favoriteFood, favorite_food: favoriteFood,
status, status,
image_url: imageUrl?.replace(apiBase, ""), // Nur Pfad speichern image_url: imageUrl?.replace(apiBase, ""),
}), }),
}); });
if (res.ok) { if (!res.ok) throw new Error("Fehler beim Speichern");
// Teamzuordnung aktualisieren
if (selectedTeamId) {
await fetch(`${apiBase}/api/players/${id}/assign-team`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ team_id: selectedTeamId }),
});
}
toast({ toast({
title: "Spieler gespeichert", title: "Spieler gespeichert",
description: "Die Änderungen wurden erfolgreich übernommen.", description: "Die Änderungen wurden erfolgreich übernommen.",
}); });
setTimeout(() => { setTimeout(() => {
navigate(-1); navigate(-1);
}, 500); // Warte kurz, damit der Toast sichtbar bleibt }, 500);
} else {
console.error("Fehler beim Speichern");
}
} catch (err) { } catch (err) {
console.error("Fehler beim Speichern:", err); console.error("Fehler beim Speichern:", err);
} }
}; };
return ( return (
<div className="max-w-3xl mx-auto py-12 px-4"> <div className="max-w-3xl mx-auto py-12 px-4">
<h1 className="text-3xl font-bold text-frog-600 mb-6">Spieler bearbeiten</h1> <h1 className="text-3xl font-bold text-frog-600 mb-6">Spieler bearbeiten</h1>
@ -130,6 +162,26 @@ const PlayerEdit = () => {
onChange={(e) => setStatus(e.target.value)} onChange={(e) => setStatus(e.target.value)}
/> />
{/* Team Auswahl */}
<div>
<Label>Teamzugehörigkeit</Label>
<Select
value={selectedTeamId?.toString()}
onValueChange={(value) => setSelectedTeamId(Number(value))}
>
<SelectTrigger>
<SelectValue placeholder="Team auswählen" />
</SelectTrigger>
<SelectContent>
{teams.map((team) => (
<SelectItem key={team.id} value={team.id.toString()}>
{team.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{/* Bild-Upload */} {/* Bild-Upload */}
<Input <Input
type="file" type="file"
@ -151,7 +203,7 @@ const PlayerEdit = () => {
{/* Buttons */} {/* Buttons */}
<div className="flex justify-end gap-4 mt-6"> <div className="flex justify-end gap-4 mt-6">
<Button variant="outline" asChild> <Button variant="outline" asChild>
<Link to="/admin/teams">Abbrechen</Link> <Link to="/admin/players">Abbrechen</Link>
</Button> </Button>
<Button onClick={handleSave} className="bg-frog-500 hover:bg-frog-600 text-white"> <Button onClick={handleSave} className="bg-frog-500 hover:bg-frog-600 text-white">
Speichern Speichern

View File

@ -0,0 +1,101 @@
import { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import { Card, CardContent, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { toast } from "sonner";
const apiBase = import.meta.env.VITE_API_URL;
type Player = {
id: number;
name: string;
nickname?: string;
position: string;
jersey_number?: number;
image_url?: string;
};
const PlayerManagementPage = () => {
const [players, setPlayers] = useState<Player[]>([]);
const [loading, setLoading] = useState(true);
const fetchPlayers = async () => {
try {
const res = await fetch(`${apiBase}/api/players`);
const data = await res.json();
setPlayers(data);
} catch (err) {
console.error("Fehler beim Laden der Spieler:", err);
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchPlayers();
}, []);
const handleDelete = async (id: number) => {
if (!confirm("Willst du diesen Spieler wirklich löschen?")) return;
try {
const res = await fetch(`${apiBase}/api/players/${id}`, {
method: "DELETE",
});
if (res.ok) {
setPlayers((prev) => prev.filter((p) => p.id !== id));
toast.success("Spieler erfolgreich gelöscht!");
} else {
toast.error("Fehler beim Löschen des Spielers");
}
} catch (err) {
console.error("Fehler beim Löschen:", err);
toast.error("Serverfehler");
}
};
if (loading) return <p className="text-center py-12">Lade Spieler...</p>;
return (
<div className="max-w-6xl mx-auto py-12 px-4">
<h1 className="text-3xl font-bold text-frog-600 mb-8">Spieler verwalten</h1>
{players.length === 0 ? (
<p className="text-gray-600 text-center">Noch keine Spieler vorhanden.</p>
) : (
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
{players.map((player) => (
<Card key={player.id} className="hover:shadow-md transition-shadow">
<CardContent className="flex flex-col items-center p-6">
<img
src={player.image_url ? `${apiBase}${player.image_url}` : "/images/default-player.png"}
alt={player.name}
className="w-24 h-24 rounded-full object-cover border-2 border-frog-500 shadow-md mb-4"
/>
<CardTitle className="text-center">
{player.name} {player.nickname && `(${player.nickname})`}
</CardTitle>
<p className="text-gray-500 text-sm mt-2">{player.position}</p>
{player.jersey_number && (
<p className="text-gray-500 text-sm"># {player.jersey_number}</p>
)}
<div className="flex gap-2 mt-4">
<Button asChild variant="outline" className="text-frog-600 border-frog-500 hover:bg-frog-50">
<Link to={`/admin/players/${player.id}/edit`}>Bearbeiten</Link>
</Button>
<Button variant="destructive" onClick={() => handleDelete(player.id)}>
Löschen
</Button>
</div>
</CardContent>
</Card>
))}
</div>
)}
</div>
);
};
export default PlayerManagementPage;

View File

@ -2,11 +2,26 @@ import { useState } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
const apiBase = import.meta.env.VITE_API_URL; const apiBase = import.meta.env.VITE_API_URL;
const TeamCreate = () => { const TeamCreate = () => {
const [teamName, setTeamName] = useState(""); const [teamName, setTeamName] = useState("");
const [liga, setLiga] = useState("");
const [suchtSpieler, setSuchtSpieler] = useState(false);
const [socialMedia, setSocialMedia] = useState("");
const [trainerName, setTrainerName] = useState("");
const [carouselImages, setCarouselImages] = useState<string>("");
const [trainingszeiten, setTrainingszeiten] = useState("");
const [trainingsort, setTrainingsort] = useState("");
const [kontaktName, setKontaktName] = useState("");
const [kontaktEmail, setKontaktEmail] = useState("");
const [teamfarben, setTeamfarben] = useState("");
const [beschreibung, setBeschreibung] = useState("");
const [tabellenlink, setTabellenlink] = useState("");
const [saving, setSaving] = useState(false); const [saving, setSaving] = useState(false);
const [errorMsg, setErrorMsg] = useState(""); const [errorMsg, setErrorMsg] = useState("");
@ -22,7 +37,21 @@ const TeamCreate = () => {
const res = await fetch(`${apiBase}/api/teams`, { const res = await fetch(`${apiBase}/api/teams`, {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: teamName }), body: JSON.stringify({
name: teamName,
liga,
sucht_spieler: suchtSpieler,
social_media: socialMedia,
karussell_bilder: carouselImages.trim() ? JSON.parse(carouselImages) : [],
trainer_name: trainerName,
trainingszeiten,
trainingsort,
kontakt_name: kontaktName,
kontakt_email: kontaktEmail,
teamfarben,
beschreibung,
tabellenlink,
}),
}); });
if (!res.ok) { if (!res.ok) {
@ -39,21 +68,49 @@ const TeamCreate = () => {
} }
}; };
return ( return (
<div className="max-w-2xl mx-auto py-12 px-4"> <div className="max-w-2xl mx-auto py-12 px-4">
<h1 className="text-3xl font-bold text-frog-600 mb-6">Neues Team anlegen</h1> <h1 className="text-3xl font-bold text-frog-600 mb-6">Neues Team anlegen</h1>
<div className="space-y-4"> <div className="space-y-4">
<Input <Input placeholder="Teamname" value={teamName} onChange={(e) => setTeamName(e.target.value)} />
placeholder="Teamname" <Input placeholder="Liga" value={liga} onChange={(e) => setLiga(e.target.value)} />
value={teamName} <Input placeholder="Trainer/in" value={trainerName} onChange={(e) => setTrainerName(e.target.value)} />
onChange={(e) => setTeamName(e.target.value)} <Input placeholder="Trainingszeiten" value={trainingszeiten} onChange={(e) => setTrainingszeiten(e.target.value)} />
<Input placeholder="Trainingsort" value={trainingsort} onChange={(e) => setTrainingsort(e.target.value)} />
<Input placeholder="Kontaktname" value={kontaktName} onChange={(e) => setKontaktName(e.target.value)} />
<Input placeholder="Kontakt E-Mail" value={kontaktEmail} onChange={(e) => setKontaktEmail(e.target.value)} />
<Input placeholder="Teamfarben" value={teamfarben} onChange={(e) => setTeamfarben(e.target.value)} />
<Textarea placeholder="Beschreibung" value={beschreibung} onChange={(e) => setBeschreibung(e.target.value)} />
<Input placeholder="Link zur Tabelle" value={tabellenlink} onChange={(e) => setTabellenlink(e.target.value)} />
<Input placeholder="Social Media" value={socialMedia} onChange={(e) => setSocialMedia(e.target.value)} />
<div className="flex items-center gap-2">
<input
id="suchtSpieler"
type="checkbox"
checked={suchtSpieler}
onChange={(e) => setSuchtSpieler(e.target.checked)}
/> />
<Label htmlFor="suchtSpieler">Spieler/in gesucht?</Label>
</div>
<div>
<Label htmlFor="carouselImages">Karussell-Bilder (als JSON-Array)</Label>
<Textarea
id="carouselImages"
placeholder='z.B. ["/uploads/x.jpg", "/uploads/y.jpg"]'
value={carouselImages}
onChange={(e) => setCarouselImages(e.target.value)}
/>
</div>
{errorMsg && ( {errorMsg && (
<p className="text-sm text-red-600 bg-red-50 border border-red-200 px-3 py-2 rounded-md"> <p className="text-sm text-red-600 bg-red-50 border border-red-200 px-3 py-2 rounded-md">
{errorMsg} {errorMsg}
</p> </p>
)} )}
<Button <Button
onClick={handleSave} onClick={handleSave}
disabled={saving} disabled={saving}

View File

@ -1,9 +1,10 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useParams, useNavigate } from "react-router-dom"; import { useParams, useNavigate, Link } from "react-router-dom";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Link } from "react-router-dom"; import { Textarea } from "@/components/ui/textarea";
import { toast } from "sonner";
const apiBase = import.meta.env.VITE_API_URL; const apiBase = import.meta.env.VITE_API_URL;
@ -21,6 +22,18 @@ const TeamDetail = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const [teamName, setTeamName] = useState(""); const [teamName, setTeamName] = useState("");
const [liga, setLiga] = useState("");
const [trainerName, setTrainerName] = useState("");
const [socialMedia, setSocialMedia] = useState("");
const [spielerGesucht, setSpielerGesucht] = useState(false);
const [carouselImages, setCarouselImages] = useState("");
const [trainingszeiten, setTrainingszeiten] = useState("");
const [trainingsort, setTrainingsort] = useState("");
const [kontaktName, setKontaktName] = useState("");
const [kontaktEmail, setKontaktEmail] = useState("");
const [teamfarben, setTeamfarben] = useState("");
const [beschreibung, setBeschreibung] = useState("");
const [tabellenlink, setTabellenlink] = useState("");
const [players, setPlayers] = useState<Player[]>([]); const [players, setPlayers] = useState<Player[]>([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
@ -28,7 +41,20 @@ const TeamDetail = () => {
try { try {
const res = await fetch(`${apiBase}/api/teams/${id}`); const res = await fetch(`${apiBase}/api/teams/${id}`);
const data = await res.json(); const data = await res.json();
setTeamName(data.name ?? ""); setTeamName(data.name ?? "");
setLiga(data.liga ?? "");
setTrainerName(data.trainer_name ?? "");
setSocialMedia(data.social_media ?? "");
setSpielerGesucht(data.sucht_spieler ?? false);
setCarouselImages(data.karussell_bilder ? JSON.stringify(data.karussell_bilder) : "");
setTrainingszeiten(data.trainingszeiten ?? "");
setTrainingsort(data.trainingsort ?? "");
setKontaktName(data.kontakt_name ?? "");
setKontaktEmail(data.kontakt_email ?? "");
setTeamfarben(data.teamfarben ?? "");
setBeschreibung(data.beschreibung ?? "");
setTabellenlink(data.tabellenlink ?? "");
setPlayers(data.players ?? []); setPlayers(data.players ?? []);
} catch (err) { } catch (err) {
console.error("Fehler beim Laden des Teams:", err); console.error("Fehler beim Laden des Teams:", err);
@ -41,19 +67,35 @@ const TeamDetail = () => {
fetchTeam(); fetchTeam();
}, [id]); }, [id]);
const handleUpdateName = async () => { const handleUpdateTeam = async () => {
try { try {
const res = await fetch(`${apiBase}/api/teams/${id}`, { const res = await fetch(`${apiBase}/api/teams/${id}`, {
method: "PUT", method: "PUT",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: teamName }), body: JSON.stringify({
name: teamName,
liga,
sucht_spieler: spielerGesucht,
social_media: socialMedia,
karussell_bilder: carouselImages ? JSON.parse(carouselImages) : [],
trainer_name: trainerName,
trainingszeiten,
trainingsort,
kontakt_name: kontaktName,
kontakt_email: kontaktEmail,
teamfarben,
beschreibung,
tabellenlink,
}),
}); });
if (!res.ok) throw new Error("Fehler beim Aktualisieren des Teamnamens"); if (!res.ok) throw new Error("Fehler beim Aktualisieren des Teams");
alert("Teamname aktualisiert!"); toast.success("Team erfolgreich aktualisiert!");
fetchTeam(); // Aktuelle Daten neu laden
} catch (err) { } catch (err) {
console.error(err); console.error(err);
toast.error("Fehler beim Aktualisieren des Teams.");
} }
}; };
@ -76,20 +118,40 @@ const TeamDetail = () => {
if (loading) return <p className="text-center py-12">Lade Team...</p>; if (loading) return <p className="text-center py-12">Lade Team...</p>;
return ( return (
<div className="max-w-4xl mx-auto py-12 px-4"> <div className="max-w-5xl mx-auto py-12 px-4">
<h1 className="text-3xl font-bold text-frog-600 mb-6">Team bearbeiten</h1> <h1 className="text-3xl font-bold text-frog-600 mb-6">Team bearbeiten</h1>
<div className="space-y-4 mb-8"> <div className="space-y-4 mb-8">
<Input <Input value={teamName} onChange={(e) => setTeamName(e.target.value)} placeholder="Teamname" />
value={teamName} <Input value={liga} onChange={(e) => setLiga(e.target.value)} placeholder="Liga" />
onChange={(e) => setTeamName(e.target.value)} <Input value={trainerName} onChange={(e) => setTrainerName(e.target.value)} placeholder="Trainer/in" />
placeholder="Teamname" <Input value={trainingszeiten} onChange={(e) => setTrainingszeiten(e.target.value)} placeholder="Trainingszeiten" />
<Input value={trainingsort} onChange={(e) => setTrainingsort(e.target.value)} placeholder="Trainingsort" />
<Input value={kontaktName} onChange={(e) => setKontaktName(e.target.value)} placeholder="Kontaktname" />
<Input value={kontaktEmail} onChange={(e) => setKontaktEmail(e.target.value)} placeholder="Kontakt E-Mail" />
<Input value={teamfarben} onChange={(e) => setTeamfarben(e.target.value)} placeholder="Teamfarben" />
<Textarea value={beschreibung} onChange={(e) => setBeschreibung(e.target.value)} placeholder="Beschreibung" />
<Input value={tabellenlink} onChange={(e) => setTabellenlink(e.target.value)} placeholder="Link zur Tabelle" />
<Input value={socialMedia} onChange={(e) => setSocialMedia(e.target.value)} placeholder="Social Media Link" />
<div className="flex items-center gap-2">
<input
type="checkbox"
checked={spielerGesucht}
onChange={() => setSpielerGesucht(!spielerGesucht)}
className="w-5 h-5 text-frog-500"
/> />
<Button <label className="text-gray-700">Neue Spieler gesucht?</label>
onClick={handleUpdateName} </div>
className="bg-frog-500 hover:bg-frog-600 text-white"
> <Textarea
Teamname speichern value={carouselImages}
onChange={(e) => setCarouselImages(e.target.value)}
placeholder="Karussell-Bilder (JSON-Array z.B. [\"//uploads/x.jpg\"])"
/>
<Button onClick={handleUpdateTeam} className="bg-frog-500 hover:bg-frog-600 text-white">
Team speichern
</Button> </Button>
</div> </div>
@ -124,19 +186,10 @@ const TeamDetail = () => {
<p className="text-gray-600">Nummer: {player.jersey_number}</p> <p className="text-gray-600">Nummer: {player.jersey_number}</p>
)} )}
<div className="flex gap-2 mt-4"> <div className="flex gap-2 mt-4">
<Button <Button asChild variant="outline" className="text-frog-600 border-frog-500 hover:bg-frog-50">
asChild <Link to={`/admin/players/${player.id}/edit`}>Bearbeiten</Link>
variant="outline"
className="text-frog-600 border-frog-500 hover:bg-frog-50"
>
<Link to={`/admin/players/${player.id}/edit`}>
Bearbeiten
</Link>
</Button> </Button>
<Button <Button onClick={() => handleRemovePlayer(player.id)} variant="destructive">
onClick={() => handleRemovePlayer(player.id)}
variant="destructive"
>
Entfernen Entfernen
</Button> </Button>
</div> </div>
@ -147,7 +200,7 @@ const TeamDetail = () => {
)} )}
</div> </div>
<div className="text-center"> <div className="text-center mt-8">
<Button asChild> <Button asChild>
<Link to={`/admin/teams/${id}/add-player`}> <Link to={`/admin/teams/${id}/add-player`}>
+ Spieler hinzufügen + Spieler hinzufügen
@ -156,8 +209,6 @@ const TeamDetail = () => {
</div> </div>
</div> </div>
); );
}; };
export default TeamDetail; export default TeamDetail;

View File

@ -24,6 +24,7 @@ import TeamCreate from "./admin/TeamCreate";
import TeamDetail from "./admin/TeamDetail"; import TeamDetail from "./admin/TeamDetail";
import TeamAddPlayer from "./admin/TeamAddPlayer"; import TeamAddPlayer from "./admin/TeamAddPlayer";
import PlayerEdit from "./admin/PlayerEdit"; import PlayerEdit from "./admin/PlayerEdit";
import PlayerManagementPage from "./admin/PlayerManagementPage";
@ -69,6 +70,7 @@ ReactDOM.createRoot(document.getElementById("root")!).render(
<Route path="/admin/teams/:id" element={<PrivateRoute><Layout><TeamDetail /></Layout></PrivateRoute>} /> <Route path="/admin/teams/:id" element={<PrivateRoute><Layout><TeamDetail /></Layout></PrivateRoute>} />
<Route path="/admin/teams/:id/add-player" element={<PrivateRoute><Layout><TeamAddPlayer /></Layout></PrivateRoute>} /> <Route path="/admin/teams/:id/add-player" element={<PrivateRoute><Layout><TeamAddPlayer /></Layout></PrivateRoute>} />
<Route path="/admin/players/:id/edit" element={<PrivateRoute><Layout><PlayerEdit /></Layout></PrivateRoute>} /> <Route path="/admin/players/:id/edit" element={<PrivateRoute><Layout><PlayerEdit /></Layout></PrivateRoute>} />
<Route path="/admin/players" element={<PrivateRoute><Layout><PlayerManagementPage /></Layout></PrivateRoute>} />
</Routes> </Routes>
</AuthProvider> </AuthProvider>