Files
got-gaming/components/LeaderboardSection.tsx

179 lines
6.6 KiB
TypeScript

"use client";
import { useEffect, useState } from "react";
import { useBackgroundImage } from "@/hooks/usePreferences";
interface LeaderboardEntry {
rank: number;
username: string;
score: number;
level: number;
avatar?: string | null;
}
// Format number with consistent locale to avoid hydration mismatch
const formatScore = (score: number): string => {
return score.toLocaleString("en-US");
};
export default function LeaderboardSection() {
const [leaderboard, setLeaderboard] = useState<LeaderboardEntry[]>([]);
const [loading, setLoading] = useState(true);
const backgroundImage = useBackgroundImage(
"leaderboard",
"/leaderboard-bg.jpg"
);
useEffect(() => {
fetch("/api/leaderboard")
.then((res) => res.json())
.then((data) => {
setLeaderboard(data);
setLoading(false);
})
.catch((err) => {
console.error("Error fetching leaderboard:", err);
setLoading(false);
});
}, []);
if (loading) {
return (
<section className="relative w-full min-h-screen flex flex-col items-center justify-center overflow-hidden pt-24 pb-16">
<div className="text-pixel-gold text-xl">Chargement...</div>
</section>
);
}
return (
<section className="relative w-full min-h-screen flex flex-col items-center justify-center overflow-hidden pt-24 pb-16">
{/* Background Image */}
<div
className="absolute inset-0 bg-cover bg-center bg-no-repeat"
style={{
backgroundImage: `url('${backgroundImage}')`,
}}
>
{/* Dark overlay for readability */}
<div className="absolute inset-0 bg-gradient-to-b from-black/70 via-black/60 to-black/80"></div>
</div>
{/* Content */}
<div className="relative z-10 w-full max-w-6xl mx-auto px-8 py-16">
{/* Title Section */}
<div className="text-center mb-12">
<h1 className="text-5xl md:text-7xl font-gaming font-black mb-4 tracking-tight">
<span
className="bg-gradient-to-r from-pixel-gold via-orange-400 to-pixel-gold bg-clip-text text-transparent"
style={{
textShadow: "0 0 30px rgba(218, 165, 32, 0.5)",
}}
>
LEADERBOARD
</span>
</h1>
<div className="text-pixel-gold text-lg md:text-xl font-gaming-subtitle font-semibold flex items-center justify-center gap-2 tracking-wide">
<span></span>
<span>Top Players</span>
<span></span>
</div>
</div>
{/* Leaderboard Table */}
<div className="bg-black/60 border border-pixel-gold/30 rounded-lg overflow-hidden backdrop-blur-sm">
{/* Header */}
<div className="bg-gray-900/80 border-b border-pixel-gold/30 grid grid-cols-12 gap-4 p-4 font-bold text-xs uppercase tracking-widest text-gray-300">
<div className="col-span-1 text-center">Rank</div>
<div className="col-span-6">Player</div>
<div className="col-span-3 text-right">Score</div>
<div className="col-span-2 text-right">Level</div>
</div>
{/* Entries */}
<div className="divide-y divide-pixel-gold/10">
{leaderboard.map((entry) => (
<div
key={entry.rank}
className={`grid grid-cols-12 gap-4 p-4 hover:bg-gray-900/50 transition ${
entry.rank <= 3
? "bg-gradient-to-r from-pixel-gold/10 via-pixel-gold/5 to-transparent"
: "bg-black/40"
}`}
>
{/* Rank */}
<div className="col-span-1 flex items-center justify-center">
<span
className={`inline-flex items-center justify-center w-10 h-10 rounded-full font-bold text-sm ${
entry.rank === 1
? "bg-gradient-to-br from-pixel-gold to-orange-500 text-black shadow-lg shadow-pixel-gold/50"
: entry.rank === 2
? "bg-gradient-to-br from-gray-400 to-gray-500 text-black"
: entry.rank === 3
? "bg-gradient-to-br from-orange-700 to-orange-800 text-white"
: "bg-gray-900 text-gray-400 border border-gray-800"
}`}
>
{entry.rank}
</span>
</div>
{/* Player */}
<div className="col-span-6 flex items-center gap-3">
{entry.avatar ? (
<div className="w-10 h-10 rounded-full border border-pixel-gold/30 overflow-hidden">
<img
src={entry.avatar}
alt={entry.username}
className="w-full h-full object-cover"
/>
</div>
) : (
<div className="w-10 h-10 rounded-full bg-gradient-to-br from-gray-800 to-gray-900 border border-pixel-gold/30 flex items-center justify-center">
<span className="text-pixel-gold text-xs font-bold">
{entry.username.charAt(0).toUpperCase()}
</span>
</div>
)}
<span
className={`font-bold ${
entry.rank <= 3 ? "text-pixel-gold" : "text-white"
}`}
>
{entry.username}
</span>
{entry.rank <= 3 && (
<span className="text-pixel-gold text-xs"></span>
)}
</div>
{/* Score */}
<div className="col-span-3 flex items-center justify-end">
<span className="font-mono text-gray-300">
{formatScore(entry.score)}
</span>
</div>
{/* Level */}
<div className="col-span-2 flex items-center justify-end">
<span className="font-bold text-gray-400">
Lv.{entry.level}
</span>
</div>
</div>
))}
</div>
</div>
{/* Footer Info */}
<div className="mt-8 text-center">
<p className="text-gray-500 text-sm">
Compete with players worldwide and climb the ranks!
</p>
<p className="text-gray-600 text-xs mt-2">
Rankings update every hour
</p>
</div>
</div>
</section>
);
}