209 lines
8.3 KiB
TypeScript
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>
|
|
);
|
|
};
|