|
import { useState } from "react"; |
|
import { supabase } from "@/integrations/supabase/client"; |
|
import { useQuery, useQueryClient } from "@tanstack/react-query"; |
|
import { useToast } from "@/components/ui/use-toast"; |
|
import { useTranslation } from "@/hooks/useTranslation"; |
|
import { ThemeFilter } from "./game/leaderboard/ThemeFilter"; |
|
import { ScoreSubmissionForm } from "./game/leaderboard/ScoreSubmissionForm"; |
|
import { ScoresTable } from "./game/leaderboard/ScoresTable"; |
|
import { LeaderboardHeader } from "./game/leaderboard/LeaderboardHeader"; |
|
import { LeaderboardPagination } from "./game/leaderboard/LeaderboardPagination"; |
|
|
|
interface HighScore { |
|
id: string; |
|
player_name: string; |
|
score: number; |
|
avg_words_per_round: number; |
|
created_at: string; |
|
session_id: string; |
|
theme: string; |
|
} |
|
|
|
interface HighScoreBoardProps { |
|
currentScore?: number; |
|
avgWordsPerRound?: number; |
|
onClose?: () => void; |
|
onPlayAgain?: () => void; |
|
sessionId?: string; |
|
onScoreSubmitted?: () => void; |
|
showThemeFilter?: boolean; |
|
initialTheme?: string; |
|
} |
|
|
|
const ITEMS_PER_PAGE = 5; |
|
const STANDARD_THEMES = ['standard', 'sports', 'food']; |
|
|
|
export const HighScoreBoard = ({ |
|
currentScore = 0, |
|
avgWordsPerRound = 0, |
|
onClose, |
|
sessionId = "", |
|
onScoreSubmitted, |
|
showThemeFilter = true, |
|
initialTheme = "standard", |
|
}: HighScoreBoardProps) => { |
|
const [playerName, setPlayerName] = useState(""); |
|
const [isSubmitting, setIsSubmitting] = useState(false); |
|
const [hasSubmitted, setHasSubmitted] = useState(false); |
|
const [currentPage, setCurrentPage] = useState(1); |
|
const [selectedTheme, setSelectedTheme] = useState<'standard' | 'sports' | 'food' | 'custom'>( |
|
initialTheme as 'standard' | 'sports' | 'food' | 'custom' |
|
); |
|
const { toast } = useToast(); |
|
const t = useTranslation(); |
|
const queryClient = useQueryClient(); |
|
|
|
const showScoreInfo = sessionId !== "" && currentScore > 0; |
|
|
|
const { data: highScores } = useQuery({ |
|
queryKey: ["highScores", selectedTheme], |
|
queryFn: async () => { |
|
console.log("Fetching high scores for theme:", selectedTheme); |
|
let query = supabase |
|
.from("high_scores") |
|
.select("*") |
|
.order("score", { ascending: false }) |
|
.order("avg_words_per_round", { ascending: true }); |
|
|
|
if (selectedTheme === 'custom') { |
|
const filterValue = `(${STANDARD_THEMES.join(',')})`; |
|
query = query.filter('theme', 'not.in', filterValue); |
|
} else { |
|
query = query.eq('theme', selectedTheme); |
|
} |
|
|
|
const { data, error } = await query; |
|
|
|
if (error) { |
|
console.error("Error fetching high scores:", error); |
|
throw error; |
|
} |
|
console.log("Fetched high scores:", data); |
|
return data as HighScore[]; |
|
}, |
|
}); |
|
|
|
const handleSubmitScore = async () => { |
|
if (!playerName.trim() || !/^[a-zA-ZÀ-ÿ0-9-]+$/u.test(playerName.trim())) { |
|
toast({ |
|
title: t.leaderboard.error.invalidName, |
|
description: t.leaderboard.error.invalidName, |
|
variant: "destructive", |
|
}); |
|
return; |
|
} |
|
|
|
if (currentScore < 1) { |
|
toast({ |
|
title: t.leaderboard.error.noRounds, |
|
description: t.leaderboard.error.noRounds, |
|
variant: "destructive", |
|
}); |
|
return; |
|
} |
|
|
|
if (hasSubmitted) { |
|
toast({ |
|
title: t.leaderboard.error.alreadySubmitted, |
|
description: t.leaderboard.error.alreadySubmitted, |
|
variant: "destructive", |
|
}); |
|
return; |
|
} |
|
|
|
setIsSubmitting(true); |
|
try { |
|
console.log("Submitting score via Edge Function..."); |
|
const { data, error } = await supabase.functions.invoke('submit-high-score', { |
|
body: { |
|
playerName: playerName.trim(), |
|
score: currentScore, |
|
avgWordsPerRound, |
|
sessionId, |
|
theme: selectedTheme |
|
} |
|
}); |
|
|
|
if (error) { |
|
console.error("Error submitting score:", error); |
|
throw error; |
|
} |
|
|
|
console.log("Score submitted successfully:", data); |
|
|
|
if (data.success) { |
|
toast({ |
|
title: t.leaderboard.success, |
|
description: t.leaderboard.success, |
|
}); |
|
|
|
setHasSubmitted(true); |
|
onScoreSubmitted?.(); |
|
setPlayerName(""); |
|
await queryClient.invalidateQueries({ queryKey: ["highScores"] }); |
|
} |
|
} catch (error) { |
|
console.error("Error submitting score:", error); |
|
toast({ |
|
title: t.leaderboard.error.submitError, |
|
description: t.leaderboard.error.submitError, |
|
variant: "destructive", |
|
}); |
|
} finally { |
|
setIsSubmitting(false); |
|
} |
|
}; |
|
|
|
const handleKeyDown = async (e: React.KeyboardEvent<HTMLInputElement>) => { |
|
if (e.key === 'Enter') { |
|
e.preventDefault(); |
|
await handleSubmitScore(); |
|
} |
|
}; |
|
|
|
const totalPages = highScores ? Math.ceil(highScores.length / ITEMS_PER_PAGE) : 0; |
|
const startIndex = (currentPage - 1) * ITEMS_PER_PAGE; |
|
const paginatedScores = highScores?.slice(startIndex, startIndex + ITEMS_PER_PAGE); |
|
|
|
return ( |
|
<div className="space-y-6"> |
|
<LeaderboardHeader |
|
currentScore={currentScore} |
|
avgWordsPerRound={avgWordsPerRound} |
|
showScoreInfo={showScoreInfo} |
|
/> |
|
|
|
{showThemeFilter && ( |
|
<ThemeFilter |
|
selectedTheme={selectedTheme} |
|
onThemeChange={setSelectedTheme} |
|
/> |
|
)} |
|
|
|
{!hasSubmitted && currentScore > 0 && ( |
|
<ScoreSubmissionForm |
|
playerName={playerName} |
|
setPlayerName={setPlayerName} |
|
isSubmitting={isSubmitting} |
|
hasSubmitted={hasSubmitted} |
|
onSubmit={handleSubmitScore} |
|
onKeyDown={handleKeyDown} |
|
/> |
|
)} |
|
|
|
<ScoresTable |
|
scores={paginatedScores || []} |
|
startIndex={startIndex} |
|
showThemeColumn={selectedTheme === 'custom'} |
|
/> |
|
|
|
<LeaderboardPagination |
|
currentPage={currentPage} |
|
totalPages={totalPages} |
|
onPreviousPage={() => setCurrentPage(p => Math.max(1, p - 1))} |
|
onNextPage={() => setCurrentPage(p => Math.min(totalPages, p + 1))} |
|
/> |
|
</div> |
|
); |
|
}; |