This commit is contained in:
parent
1d5dea8ff1
commit
19bd2ee41d
@ -1,55 +1,30 @@
|
|||||||
import { useKeenSlider } from "keen-slider/react";
|
import { useKeenSlider } from "keen-slider/react";
|
||||||
import { useRef } from "react";
|
import { useRef, useEffect, useState } from "react";
|
||||||
import { ChevronLeft, ChevronRight, Users } from "lucide-react";
|
import { ChevronLeft, ChevronRight, Users } from "lucide-react";
|
||||||
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardFooter,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "@/components/ui/card";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
|
const apiBase = import.meta.env.VITE_API_URL;
|
||||||
|
|
||||||
|
type Team = {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
liga?: string;
|
||||||
|
beschreibung?: string;
|
||||||
|
trainingszeiten?: string;
|
||||||
|
};
|
||||||
|
|
||||||
const TeamSection = () => {
|
const TeamSection = () => {
|
||||||
const teams = [
|
const [teams, setTeams] = useState<Team[]>([]);
|
||||||
{
|
const [loading, setLoading] = useState(true);
|
||||||
id: 1,
|
|
||||||
name: "Damen I",
|
|
||||||
league: "Landesliga Nord",
|
|
||||||
description: "Unsere erste Damenmannschaft spielt in der Landesliga Nord und konnte in der letzten Saison den 3. Platz erreichen.",
|
|
||||||
trainingTimes: "Di & Do 19:00 - 21:00 Uhr"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
name: "Herren I",
|
|
||||||
league: "Bezirksliga",
|
|
||||||
description: "Das Herrenteam kämpft in der Bezirksliga und hat das Ziel, in dieser Saison den Aufstieg zu schaffen.",
|
|
||||||
trainingTimes: "Mo & Mi 20:00 - 22:00 Uhr"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
name: "Jugend U16",
|
|
||||||
league: "Jugendliga",
|
|
||||||
description: "Unsere Nachwuchsspielerinnen und -spieler entwickeln sich prächtig und nehmen an der Jugendliga teil.",
|
|
||||||
trainingTimes: "Fr 17:00 - 19:00 Uhr"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
name: "Freizeitgruppe",
|
|
||||||
league: "Hobby",
|
|
||||||
description: "Für alle, die Volleyball zum Spaß spielen möchten. Anfänger und Fortgeschrittene sind willkommen!",
|
|
||||||
trainingTimes: "Mo 18:00 - 20:00 Uhr"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
name: "U12",
|
|
||||||
league: "Jugendliga",
|
|
||||||
description: "Unsere kleinsten Stars sammeln erste Spielerfahrung in der U12-Jugendliga.",
|
|
||||||
trainingTimes: "Mi 16:00 - 17:30 Uhr"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 6,
|
|
||||||
name: "Mixed-Team",
|
|
||||||
league: "Hobbyliga",
|
|
||||||
description: "Spaß, Gemeinschaft und gemischte Teams – unser Mixed-Team steht für Vielfalt!",
|
|
||||||
trainingTimes: "Fr 19:00 - 21:00 Uhr"
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const [sliderRef, slider] = useKeenSlider<HTMLDivElement>({
|
const [sliderRef, slider] = useKeenSlider<HTMLDivElement>({
|
||||||
slides: {
|
slides: {
|
||||||
@ -69,14 +44,35 @@ const TeamSection = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
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);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchTeams();
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="team" className="py-16">
|
<section id="team" className="py-16">
|
||||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
<div className="text-center mb-12">
|
<div className="text-center mb-12">
|
||||||
<h2 className="text-3xl font-bold text-gray-900">Unsere Teams</h2>
|
<h2 className="text-3xl font-bold text-gray-900">Unsere Teams</h2>
|
||||||
<p className="mt-4 text-xl text-gray-600">Von Anfängern bis Leistungssport – für jeden ist etwas dabei</p>
|
<p className="mt-4 text-xl text-gray-600">
|
||||||
|
Von Anfängern bis Leistungssport – für jeden ist etwas dabei
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{loading ? (
|
||||||
|
<p className="text-center text-gray-500">Lade Teams...</p>
|
||||||
|
) : (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
{/* Navigation Buttons */}
|
{/* Navigation Buttons */}
|
||||||
<button
|
<button
|
||||||
@ -101,17 +97,24 @@ const TeamSection = () => {
|
|||||||
<Users className="h-5 w-5 mr-2 text-frog-500" />
|
<Users className="h-5 w-5 mr-2 text-frog-500" />
|
||||||
{team.name}
|
{team.name}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardDescription>{team.league}</CardDescription>
|
<CardDescription>{team.liga}</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<p className="text-sm text-gray-600 mb-4">{team.description}</p>
|
<p className="text-sm text-gray-600 mb-4">
|
||||||
|
{team.beschreibung ?? "Keine Beschreibung vorhanden."}
|
||||||
|
</p>
|
||||||
<div className="text-sm font-medium">
|
<div className="text-sm font-medium">
|
||||||
<span className="text-frog-600">Training:</span> {team.trainingTimes}
|
<span className="text-frog-600">Training:</span>{" "}
|
||||||
|
{team.trainingszeiten ?? "Nicht angegeben"}
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardFooter>
|
<CardFooter>
|
||||||
<Link to={`/teams/${team.id}`} className="w-full">
|
<Link to={`/teams/${team.id}`} className="w-full">
|
||||||
<Button variant="outline" size="sm" className="w-full border-frog-500 text-frog-600 hover:bg-frog-50">
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
className="w-full border-frog-500 text-frog-600 hover:bg-frog-50"
|
||||||
|
>
|
||||||
Team Details
|
Team Details
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
@ -121,6 +124,7 @@ const TeamSection = () => {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,64 +1,63 @@
|
|||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Carousel } from "react-responsive-carousel"; // Neue Library!
|
import { Carousel } from "react-responsive-carousel";
|
||||||
import "react-responsive-carousel/lib/styles/carousel.min.css"; // Wichtig!
|
import "react-responsive-carousel/lib/styles/carousel.min.css";
|
||||||
|
|
||||||
const teamData = [
|
const apiBase = import.meta.env.VITE_API_URL;
|
||||||
{
|
|
||||||
id: "1",
|
type Player = {
|
||||||
name: "Damen I",
|
id: number;
|
||||||
league: "Landesliga Nord",
|
name: string;
|
||||||
description: "Unsere erste Damenmannschaft spielt auf hohem Niveau und liebt den Volleyballsport.",
|
position: string;
|
||||||
trainingTimes: "Dienstag & Donnerstag 19:00 - 21:00 Uhr",
|
nickname?: string;
|
||||||
images: [
|
image_url?: string;
|
||||||
"/images/damen1-1.jpg",
|
};
|
||||||
"/images/damen1-2.jpg",
|
|
||||||
"/images/damen1-3.jpg",
|
type Team = {
|
||||||
],
|
id: number;
|
||||||
canJoin: false,
|
name: string;
|
||||||
players: [
|
liga?: string;
|
||||||
{ name: "Anna", position: "Außenangriff", nickname: "Speedy", image: "/images/anna.jpg" },
|
beschreibung?: string;
|
||||||
{ name: "Lea", position: "Libera", nickname: "Wall-E", image: "/images/lea.jpg" },
|
trainingszeiten?: string;
|
||||||
{ name: "Sophie", position: "Zuspiel", nickname: "Magic", image: "/images/sophie.jpg" },
|
sucht_spieler?: boolean;
|
||||||
],
|
karussell_bilder?: string[]; // Wichtig: als Array erwartet!
|
||||||
},
|
players: Player[];
|
||||||
{
|
};
|
||||||
id: "2",
|
|
||||||
name: "Herren I",
|
|
||||||
league: "Landesliga",
|
|
||||||
description: "Das Team der Herren 1 ist tritt in der Saison 25/26 in der Landesliga beim NVV an.",
|
|
||||||
trainingTimes: "Montag & Freitag 20:00 - 22:00 Uhr",
|
|
||||||
images: [
|
|
||||||
"/images/players/herren1/carousel/carousel2.jpg",
|
|
||||||
"/images/players/herren1/carousel/carousel2.jpg",
|
|
||||||
"/images/players/herren1/carousel/carousel2.jpg",
|
|
||||||
],
|
|
||||||
canJoin: true,
|
|
||||||
players: [
|
|
||||||
{ name: "Samuel", position: "Mittelblock", nickname: "BeSamu", image: "/images/players/herren1/samuel.jpg" },
|
|
||||||
{ name: "Sten", position: "Außen/Annahme", nickname: "Stenislav", image: "/images/players/herren1/sten.jpg" },
|
|
||||||
{ name: "David", position: "Mittelblock", nickname: "Blocki", image: "/images/players/herren1/david.jpg" },
|
|
||||||
{ name: "Erik", position: "Libero", nickname: "Erika", image: "/images/players/herren1/erik.jpg" },
|
|
||||||
{ name: "Lasse", position: "Außen/Annahme", nickname: "Lass es!", image: "/images/players/herren1/lasse.jpg" },
|
|
||||||
{ name: "Peter", position: "Zuspieler", nickname: "-", image: "/images/players/herren1/peter.jpg" },
|
|
||||||
{ name: "Tony", position: "Zuspieler", nickname: "Tony Mahony", image: "/images/players/herren1/tony.jpg" },
|
|
||||||
{ name: "Marc", position: "Diagonal", nickname: "Marci", image: "/images/players/herren1/marc.jpg" },
|
|
||||||
{ name: "Phillip", position: "Libero", nickname: "Hifi", image: "/images/players/herren1/phillip.jpg" },
|
|
||||||
{ name: "Kathrin", position: "Trainerin", nickname: "Mutti", image: "/images/players/herren1/phillip.jpg" },
|
|
||||||
{ name: "DU?", position: "Alles", },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const TeamDetailPage = () => {
|
const TeamDetailPage = () => {
|
||||||
const { id } = useParams();
|
const { id } = useParams<{ id: string }>();
|
||||||
const team = teamData.find((t) => t.id === id);
|
const [team, setTeam] = useState<Team | null>(null);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
if (!team) return <p>Team nicht gefunden 🐸</p>;
|
useEffect(() => {
|
||||||
|
const fetchTeam = async () => {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${apiBase}/api/teams/${id}`);
|
||||||
|
const data = await res.json();
|
||||||
|
|
||||||
|
// Falls karussell_bilder als String (JSON) kommt → parsen
|
||||||
|
if (typeof data.karussell_bilder === "string") {
|
||||||
|
data.karussell_bilder = JSON.parse(data.karussell_bilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
setTeam(data);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Fehler beim Laden des Teams:", err);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchTeam();
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
if (loading) return <p className="text-center py-12">Lade Teamdaten...</p>;
|
||||||
|
if (!team) return <p className="text-center py-12">Team nicht gefunden 🐸</p>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="max-w-7xl mx-auto py-8 px-4">
|
<div className="max-w-7xl mx-auto py-8 px-4">
|
||||||
{/* Karussell */}
|
{/* Karussell */}
|
||||||
|
{team.karussell_bilder && team.karussell_bilder.length > 0 && (
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
<Carousel
|
<Carousel
|
||||||
showThumbs={false}
|
showThumbs={false}
|
||||||
@ -66,30 +65,33 @@ const TeamDetailPage = () => {
|
|||||||
infiniteLoop
|
infiniteLoop
|
||||||
autoPlay
|
autoPlay
|
||||||
interval={4000}
|
interval={4000}
|
||||||
dynamicHeight={false}
|
|
||||||
className="rounded-lg overflow-hidden"
|
className="rounded-lg overflow-hidden"
|
||||||
>
|
>
|
||||||
{team.images.map((src, index) => (
|
{team.karussell_bilder.map((src, index) => (
|
||||||
<div key={index} className="h-96">
|
<div key={index} className="h-96">
|
||||||
<img
|
<img
|
||||||
src={src}
|
src={`${apiBase}${src}`}
|
||||||
alt={`Team ${team.name} Bild ${index + 1}`}
|
alt={`Bild ${index + 1}`}
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</Carousel>
|
</Carousel>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Team Info */}
|
{/* Team Info */}
|
||||||
<div className="text-center mb-12">
|
<div className="text-center mb-12">
|
||||||
<h1 className="text-4xl font-bold text-frog-600">{team.name}</h1>
|
<h1 className="text-4xl font-bold text-frog-600">{team.name}</h1>
|
||||||
<p className="text-lg text-gray-700 mt-2">{team.league}</p>
|
<p className="text-lg text-gray-700 mt-2">{team.liga}</p>
|
||||||
<p className="text-gray-600 mt-4">{team.description}</p>
|
<p className="text-gray-600 mt-4">{team.beschreibung}</p>
|
||||||
<p className="text-sm text-frog-700 font-medium mt-2">Training: {team.trainingTimes}</p>
|
<p className="text-sm text-frog-700 font-medium mt-2">
|
||||||
|
Training: {team.trainingszeiten || "Nicht angegeben"}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Join Button */}
|
{/* Join Button */}
|
||||||
{team.canJoin && (
|
{team.sucht_spieler && (
|
||||||
<div className="mt-6 mb-6 flex justify-center">
|
<div className="mt-6 mb-6 flex justify-center">
|
||||||
<button className="bg-frog-500 hover:bg-frog-600 text-white font-semibold py-2 px-6 rounded-full shadow-lg transition">
|
<button className="bg-frog-500 hover:bg-frog-600 text-white font-semibold py-2 px-6 rounded-full shadow-lg transition">
|
||||||
Mitmachen
|
Mitmachen
|
||||||
@ -99,16 +101,25 @@ const TeamDetailPage = () => {
|
|||||||
|
|
||||||
{/* Spielerübersicht */}
|
{/* Spielerübersicht */}
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-6">
|
||||||
{team.players.map((player, idx) => (
|
{team.players.map((player) => (
|
||||||
<div key={idx} className="bg-white rounded-lg shadow-md p-4 flex flex-col items-center text-center hover:shadow-lg transition">
|
<div
|
||||||
|
key={player.id}
|
||||||
|
className="bg-white rounded-lg shadow-md p-4 flex flex-col items-center text-center hover:shadow-lg transition"
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
src={player.image}
|
src={
|
||||||
|
player.image_url
|
||||||
|
? `${apiBase}${player.image_url}`
|
||||||
|
: "/images/default-player.png"
|
||||||
|
}
|
||||||
alt={player.name}
|
alt={player.name}
|
||||||
className="w-full h-108 object-cover rounded-lg mb-4"
|
className="w-full h-108 object-cover rounded-lg mb-4"
|
||||||
/>
|
/>
|
||||||
<h3 className="text-xl font-semibold text-frog-700">{player.name}</h3>
|
<h3 className="text-xl font-semibold text-frog-700">{player.name}</h3>
|
||||||
<p className="text-gray-500">{player.position}</p>
|
<p className="text-gray-500">{player.position}</p>
|
||||||
|
{player.nickname && (
|
||||||
<p className="text-sm text-frog-500 mt-1">„{player.nickname}“</p>
|
<p className="text-sm text-frog-500 mt-1">„{player.nickname}“</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user