kaboot/components/Scoreboard.tsx
2026-01-13 11:28:39 -07:00

98 lines
No EOL
3.8 KiB
TypeScript

import React from 'react';
import { Player } from '../types';
import { motion } from 'framer-motion';
import { BarChart, Bar, XAxis, YAxis, ResponsiveContainer, Cell, LabelList } from 'recharts';
import { Loader2 } from 'lucide-react';
import { PlayerAvatar } from './PlayerAvatar';
interface ScoreboardProps {
players: Player[];
onNext: () => void;
isHost: boolean;
currentPlayerId: string | null;
}
export const Scoreboard: React.FC<ScoreboardProps> = ({ players, onNext, isHost, currentPlayerId }) => {
const playersWithDisplayName = players.map(p => ({
...p,
displayName: p.id === currentPlayerId ? `${p.name} (You)` : p.name
}));
const sortedPlayers = [...playersWithDisplayName].sort((a, b) => b.score - a.score).slice(0, 5);
return (
<div className="flex flex-col h-screen p-8">
<header className="text-center mb-12">
<h1 className="text-5xl font-black text-white font-display drop-shadow-md">Scoreboard</h1>
</header>
<div className="flex-1 bg-white rounded-[3rem] shadow-[0_20px_0_rgba(0,0,0,0.2)] p-12 flex text-gray-900 max-w-6xl w-full mx-auto relative z-10 border-8 border-white/50">
<div className="flex flex-col justify-around py-4 pr-4">
{sortedPlayers.map((player) => (
<div key={player.id} className="flex items-center gap-3 h-[50px]">
<PlayerAvatar seed={player.avatarSeed} size={24} />
<span className="font-black text-xl font-display whitespace-nowrap">{player.displayName}</span>
</div>
))}
</div>
<div className="flex-1">
<ResponsiveContainer width="100%" height="100%">
<BarChart
data={sortedPlayers}
layout="vertical"
margin={{ top: 20, right: 100, left: 0, bottom: 5 }}
>
<XAxis type="number" hide />
<YAxis type="category" dataKey="displayName" hide />
<Bar dataKey="score" radius={[0, 20, 20, 0]} barSize={50} animationDuration={1500}>
{sortedPlayers.map((entry, index) => (
<Cell
key={`cell-${index}`}
fill={entry.color}
className="filter drop-shadow-md"
/>
))}
<LabelList
dataKey="score"
position="right"
offset={15}
content={({ x, y, width, value, index }) => {
const player = sortedPlayers[index as number];
return (
<text
x={(x as number) + (width as number) + 15}
y={y as number}
dy={35}
fill={player?.color || 'var(--theme-primary)'}
fontSize={24}
fontWeight={900}
fontFamily="Fredoka"
>
{value}
</text>
);
}}
/>
</Bar>
</BarChart>
</ResponsiveContainer>
</div>
</div>
<div className="mt-8 flex justify-end max-w-6xl w-full mx-auto">
{isHost ? (
<button
onClick={onNext}
className="bg-white text-theme-primary px-12 py-4 rounded-2xl text-2xl font-black shadow-[0_8px_0_rgba(0,0,0,0.2)] hover:scale-105 active:shadow-none active:translate-y-[8px] transition-all"
>
Next
</button>
) : (
<div className="flex items-center gap-3 bg-white/10 px-8 py-4 rounded-2xl backdrop-blur-md border-2 border-white/20">
<Loader2 className="animate-spin w-8 h-8" />
<span className="text-xl font-bold">Waiting for host...</span>
</div>
)}
</div>
</div>
);
};