nvj-turnierplaner2/src/components/ScoreboardModal.tsx
2026-01-23 10:32:02 +01:00

209 lines
8.3 KiB
TypeScript

import { useState } from 'react';
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { useTournament } from '@/context/TournamentContext';
import { Trophy, Star, BarChart3, TrendingUp, TrendingDown, Minus } from 'lucide-react';
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend } from 'recharts';
const CHART_COLORS = [
'hsl(211, 100%, 50%)',
'hsl(0, 72%, 51%)',
'hsl(142, 71%, 45%)',
'hsl(45, 93%, 47%)',
'hsl(280, 65%, 60%)',
'hsl(180, 65%, 45%)',
'hsl(320, 65%, 52%)',
'hsl(30, 80%, 55%)',
];
export const ScoreboardModal = () => {
const { getTeamScores, rounds } = useTournament();
const [open, setOpen] = useState(false);
const bundesligaScores = getTeamScores('bundesliga');
const championsScores = getTeamScores('champions');
const createChartData = (league: 'bundesliga' | 'champions') => {
const scores = league === 'bundesliga' ? bundesligaScores : championsScores;
if (rounds.length === 0) return [];
const data = [];
// Add starting point
const startPoint: Record<string, number | string> = { round: 'Start' };
scores.forEach((s) => {
startPoint[s.team.name] = 0;
});
data.push(startPoint);
// Add each round
for (let i = 0; i < rounds.length; i++) {
const point: Record<string, number | string> = { round: `Runde ${i + 1}` };
scores.forEach((s) => {
const cumulativePoints = s.pointsHistory.slice(0, i + 1).reduce((a, b) => a + b, 0);
point[s.team.name] = cumulativePoints;
});
data.push(point);
}
return data;
};
const renderScoreTable = (league: 'bundesliga' | 'champions') => {
const scores = league === 'bundesliga' ? bundesligaScores : championsScores;
const Icon = league === 'bundesliga' ? Trophy : Star;
const badgeClass = league === 'bundesliga' ? 'bg-bundesliga/10 text-bundesliga' : 'bg-champions/20 text-champions-foreground';
if (scores.length === 0) {
return (
<div className="text-center py-12 text-muted-foreground">
Keine Teams in dieser Liga
</div>
);
}
return (
<div className="space-y-4">
<div className="overflow-x-auto">
<table className="w-full">
<thead>
<tr className="border-b border-border">
<th className="text-left py-3 px-2 text-sm font-medium text-muted-foreground">#</th>
<th className="text-left py-3 px-2 text-sm font-medium text-muted-foreground">Team</th>
<th className="text-center py-3 px-2 text-sm font-medium text-muted-foreground">Spiele</th>
<th className="text-right py-3 px-2 text-sm font-medium text-muted-foreground">Punkte</th>
<th className="text-center py-3 px-2 text-sm font-medium text-muted-foreground">Trend</th>
</tr>
</thead>
<tbody>
{scores.map((score, index) => {
const lastRoundPoints = score.pointsHistory[score.pointsHistory.length - 1] ?? 0;
return (
<tr key={score.team.id} className="border-b border-border/50 hover:bg-secondary/30 transition-colors">
<td className="py-3 px-2">
<span className={`w-6 h-6 rounded-full flex items-center justify-center text-xs font-bold ${
index === 0 ? 'bg-champions text-champions-foreground' :
index === 1 ? 'bg-muted text-muted-foreground' :
index === 2 ? 'bg-orange-100 text-orange-700' :
'bg-secondary text-muted-foreground'
}`}>
{index + 1}
</span>
</td>
<td className="py-3 px-2">
<p className="font-semibold text-foreground">{score.team.name}</p>
<p className="text-xs text-muted-foreground">{score.team.club}</p>
</td>
<td className="py-3 px-2 text-center text-sm text-muted-foreground">
{score.matchesPlayed}
</td>
<td className="py-3 px-2 text-right">
<span className={`text-lg font-bold ${
score.totalPoints > 0 ? 'text-field' :
score.totalPoints < 0 ? 'text-destructive' :
'text-foreground'
}`}>
{score.totalPoints > 0 ? '+' : ''}{score.totalPoints}
</span>
</td>
<td className="py-3 px-2">
<div className="flex justify-center">
{lastRoundPoints > 0 ? (
<TrendingUp className="w-4 h-4 text-field" />
) : lastRoundPoints < 0 ? (
<TrendingDown className="w-4 h-4 text-destructive" />
) : (
<Minus className="w-4 h-4 text-muted-foreground" />
)}
</div>
</td>
</tr>
);
})}
</tbody>
</table>
</div>
{rounds.length > 0 && (
<div className="pt-4">
<h4 className="text-sm font-medium text-muted-foreground mb-4">Punkteverlauf</h4>
<div className="h-64 w-full">
<ResponsiveContainer width="100%" height="100%">
<LineChart data={createChartData(league)}>
<CartesianGrid strokeDasharray="3 3" stroke="hsl(var(--border))" />
<XAxis
dataKey="round"
tick={{ fontSize: 12, fill: 'hsl(var(--muted-foreground))' }}
stroke="hsl(var(--border))"
/>
<YAxis
tick={{ fontSize: 12, fill: 'hsl(var(--muted-foreground))' }}
stroke="hsl(var(--border))"
/>
<Tooltip
contentStyle={{
backgroundColor: 'hsl(var(--card))',
border: '1px solid hsl(var(--border))',
borderRadius: '0.75rem',
}}
labelStyle={{ color: 'hsl(var(--foreground))' }}
/>
<Legend />
{scores.map((score, index) => (
<Line
key={score.team.id}
type="monotone"
dataKey={score.team.name}
stroke={CHART_COLORS[index % CHART_COLORS.length]}
strokeWidth={2}
dot={{ r: 4 }}
activeDot={{ r: 6 }}
/>
))}
</LineChart>
</ResponsiveContainer>
</div>
</div>
)}
</div>
);
};
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button variant="outline" className="rounded-xl">
<BarChart3 className="w-4 h-4 mr-2" />
Scoreboard
</Button>
</DialogTrigger>
<DialogContent className="max-w-3xl max-h-[85vh] overflow-y-auto bg-card">
<DialogHeader>
<DialogTitle className="text-2xl font-bold">Scoreboard</DialogTitle>
</DialogHeader>
<Tabs defaultValue="bundesliga" className="mt-4">
<TabsList className="grid w-full grid-cols-2 rounded-xl">
<TabsTrigger value="bundesliga" className="rounded-lg gap-2">
<Trophy className="w-4 h-4" />
Bundesliga
</TabsTrigger>
<TabsTrigger value="champions" className="rounded-lg gap-2">
<Star className="w-4 h-4" />
Champions League
</TabsTrigger>
</TabsList>
<TabsContent value="bundesliga" className="mt-4">
{renderScoreTable('bundesliga')}
</TabsContent>
<TabsContent value="champions" className="mt-4">
{renderScoreTable('champions')}
</TabsContent>
</Tabs>
</DialogContent>
</Dialog>
);
};