volleyball-dev-frontend/src/pages/AlleNeuigkeiten.tsx
Marc Wieland cc0c8c95f5
Some checks are pending
Deploy Volleyball Dev / deploy (push) Waiting to run
Aktuellste VErsion
2025-05-16 18:38:48 +02:00

139 lines
4.4 KiB
TypeScript

import { useEffect, useState } from "react";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import DOMPurify from "dompurify";
const apiBase = import.meta.env.VITE_API_URL;
type NewsItem = {
id: number;
title: string;
description: string;
image_url: string;
team: string;
created_at: string;
};
const AlleNeuigkeitenPage = () => {
const [news, setNews] = useState<NewsItem[]>([]);
const [expandedIds, setExpandedIds] = useState<number[]>([]);
const [activeCardId, setActiveCardId] = useState<number | null>(null);
const [selectedTeam, setSelectedTeam] = useState<string>("Alle Teams");
const defaultImage = "/images/tgl-ball.png";
//Filtern nach Teams
const teams = Array.from(new Set(news.map((n) => n.team).filter(Boolean)));
const filteredNews = selectedTeam === "Alle Teams" ? news : news.filter((n) => n.team === selectedTeam);
const groupedNews = filteredNews.reduce((acc: Record<string, NewsItem[]>, item) => {
const year = new Date(item.created_at).getFullYear().toString();
if (!acc[year]) acc[year] = [];
acc[year].push(item);
return acc;
}, {});
useEffect(() => {
fetch(`${apiBase}/api/news`)
.then((res) => res.json())
.then((data) => setNews(data))
.catch((err) => console.error("Fehler beim Laden der News:", err));
}, []);
const toggleCard = (id: number) => {
setActiveCardId((prev) => (prev === id ? null : id));
};
//Toggle der Cards
const toggleExpand = (id: number) => {
setExpandedIds((prev) =>
prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]
);
}
return (
<div className="max-w-5xl mx-auto px-4 py-12">
<h1 className="text-3xl font-bold text-center mb-8 text-frog-600">Alle Neuigkeiten</h1>
<div className="mb-8 flex justify-center">
<select
value={selectedTeam}
onChange={(e) => setSelectedTeam(e.target.value)}
className="border border-gray-300 rounded-md px-4 py-2 text-gray-700"
>
<option value="Alle Teams">Alle Teams</option>
{teams.map((team) => (
<option key={team} value={team}>
{team}
</option>
))}
</select>
</div>
{/* News nach Jahren gruppiert */}
{Object.entries(groupedNews).map(([year, items]) => (
<div key={year} className="mb-12">
<h2 className="text-2xl font-bold text-frog-500 mb-4">{year}</h2>
<div className="grid md:grid-cols-2 gap-6">
{items.map((item) => (
<Card
key={item.id}
className={`overflow-hidden transition-all duration-500 ease-in-out ${
activeCardId === item.id
? "col-span-2 lg:col-span-2 xl:col-span-2 scale-[1.02] shadow-xl"
: ""
}`}
>
<div className="h-48 overflow-hidden">
<img
src={
item.image_url
? `${apiBase}${item.image_url}`
: defaultImage
}
alt={item.title}
className="w-full h-full object-cover"
/>
</div>
<CardHeader>
<CardTitle>{item.title}</CardTitle>
<CardDescription>{new Date(item.created_at).toLocaleDateString("de-DE")}</CardDescription>
</CardHeader>
<CardContent>
<div
className={`prose max-w-none text-gray-700 transition-all duration-300 prose-p:my-1 prose-a:text-frog-600 ${
activeCardId === item.id ? "" : "line-clamp-3 overflow-hidden"
}`}
>
<div
dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(item.description),
}}
/>
</div>
<button
onClick={() => toggleCard(item.id)}
className="mt-2 text-frog-600 text-sm font-medium hover:underline"
>
{activeCardId === item.id ? "Weniger anzeigen" : "Weiterlesen"}
</button>
</CardContent>
</Card>
))}
</div>
</div>
))}
</div>
);
};
export default AlleNeuigkeitenPage;