import { useEffect, useState } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { supabase } from "@/integrations/supabase/client"; import { useQuery } from "@tanstack/react-query"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { useToast } from "@/components/ui/use-toast"; import { Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, } from "@/components/ui/pagination"; interface HighScore { id: string; player_name: string; score: number; avg_words_per_round: number; created_at: string; } interface HighScoreBoardProps { currentScore: number; avgWordsPerRound: number; onClose: () => void; onPlayAgain: () => void; } const ITEMS_PER_PAGE = 5; const MAX_PAGES = 4; const getRankMedal = (rank: number) => { switch (rank) { case 1: return "🥇"; case 2: return "🥈"; case 3: return "🥉"; default: return null; } }; export const HighScoreBoard = ({ currentScore, avgWordsPerRound, onClose, }: HighScoreBoardProps) => { const [playerName, setPlayerName] = useState(""); const [isSubmitting, setIsSubmitting] = useState(false); const [hasSubmitted, setHasSubmitted] = useState(false); const [currentPage, setCurrentPage] = useState(1); const { toast } = useToast(); const { data: highScores, refetch } = useQuery({ queryKey: ["highScores"], queryFn: async () => { const { data, error } = await supabase .from("high_scores") .select("*") .order("score", { ascending: false }) .order("avg_words_per_round", { ascending: true }); if (error) throw error; return data as HighScore[]; }, }); const handleSubmitScore = async () => { if (!playerName.trim()) { toast({ title: "Error", description: "Please enter your name", variant: "destructive", }); return; } if (currentScore < 1) { toast({ title: "Error", description: "You need to complete at least one round to submit a score", variant: "destructive", }); return; } if (hasSubmitted) { toast({ title: "Error", description: "You have already submitted your score for this game", variant: "destructive", }); return; } setIsSubmitting(true); try { // Check if player already exists const { data: existingScores } = await supabase .from("high_scores") .select("*") .eq("player_name", playerName.trim()); const existingScore = existingScores?.[0]; if (existingScore) { // Only update if the new score is better if (currentScore > existingScore.score) { const { error } = await supabase .from("high_scores") .update({ score: currentScore, avg_words_per_round: avgWordsPerRound, }) .eq("id", existingScore.id); if (error) throw error; toast({ title: "New High Score!", description: `You beat your previous record of ${existingScore.score} rounds!`, }); } else { toast({ title: "Score Not Updated", description: `Your current score (${currentScore}) is not higher than your best score (${existingScore.score})`, variant: "destructive", }); setIsSubmitting(false); return; } } else { // Insert new score const { error } = await supabase.from("high_scores").insert({ player_name: playerName.trim(), score: currentScore, avg_words_per_round: avgWordsPerRound, }); if (error) throw error; toast({ title: "Success!", description: "Your score has been recorded", }); } setHasSubmitted(true); await refetch(); setPlayerName(""); } catch (error) { console.error("Error submitting score:", error); toast({ title: "Error", description: "Failed to submit score. Please try again.", variant: "destructive", }); } finally { setIsSubmitting(false); } }; const handleKeyDown = async (e: React.KeyboardEvent) => { if (e.key === 'Enter') { e.preventDefault(); await handleSubmitScore(); } }; const totalPages = highScores ? Math.min(Math.ceil(highScores.length / ITEMS_PER_PAGE), MAX_PAGES) : 0; const startIndex = (currentPage - 1) * ITEMS_PER_PAGE; const paginatedScores = highScores?.slice(startIndex, startIndex + ITEMS_PER_PAGE); const handlePreviousPage = () => { if (currentPage > 1) { setCurrentPage(p => p - 1); } }; const handleNextPage = () => { if (currentPage < totalPages) { setCurrentPage(p => p + 1); } }; useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'ArrowLeft') { handlePreviousPage(); } else if (e.key === 'ArrowRight') { handleNextPage(); } }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [currentPage, totalPages]); return (

Leaderboard

Your score: {currentScore} rounds {currentScore > 0 && ` (${avgWordsPerRound.toFixed(1)} words/round)`}

{!hasSubmitted && currentScore > 0 && (
setPlayerName(e.target.value)} onKeyDown={handleKeyDown} className="flex-1" />
)}
Rank Player Rounds Avg Words/Round {paginatedScores?.map((score, index) => { const absoluteRank = startIndex + index + 1; const medal = getRankMedal(absoluteRank); return ( {absoluteRank} {medal && {medal}} {score.player_name} {score.score} {score.avg_words_per_round.toFixed(1)} ); })} {!paginatedScores?.length && ( No high scores yet. Be the first! )}
{totalPages > 1 && ( Previous {Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => ( setCurrentPage(page)} isActive={currentPage === page} > {page} ))} Next )}
); };