feat(leaderboard): add separate rankings for daily and blitz modes

This commit is contained in:
jessy-david-dev
2026-04-14 15:40:44 +02:00
parent b889f3fd10
commit 7ecf268191
2 changed files with 73 additions and 13 deletions
+24 -1
View File
@@ -20,6 +20,8 @@ export async function GET() {
const all = u.games; const all = u.games;
const solo = all.filter((g) => g.mode === "solo"); const solo = all.filter((g) => g.mode === "solo");
const multi = all.filter((g) => g.mode === "multi"); const multi = all.filter((g) => g.mode === "multi");
const daily = all.filter((g) => g.mode === "daily");
const blitz = all.filter((g) => g.mode === "blitz");
const wins = all.filter((g) => g.won); const wins = all.filter((g) => g.won);
const wonGames = wins.filter((g) => g.clicks > 0); const wonGames = wins.filter((g) => g.clicks > 0);
const avgClicks = wonGames.length const avgClicks = wonGames.length
@@ -31,15 +33,36 @@ export async function GET() {
? Math.min(...wins.map((g) => g.timeSeconds)) ? Math.min(...wins.map((g) => g.timeSeconds))
: null; : null;
const soloWins = solo.filter((g) => g.won);
const dailyWins = daily.filter((g) => g.won);
const blitzWins = blitz.filter((g) => g.won);
const soloAvgClicks = soloWins.filter((g) => g.clicks > 0).length
? Math.round(soloWins.reduce((s, g) => s + g.clicks, 0) / soloWins.filter((g) => g.clicks > 0).length)
: null;
const dailyAvgClicks = dailyWins.filter((g) => g.clicks > 0).length
? Math.round(dailyWins.reduce((s, g) => s + g.clicks, 0) / dailyWins.filter((g) => g.clicks > 0).length)
: null;
const blitzBestTime = blitzWins.length
? Math.min(...blitzWins.map((g) => g.timeSeconds))
: null;
return { return {
id: u.id, id: u.id,
name: u.name, name: u.name,
totalGames: all.length, totalGames: all.length,
wins: wins.length, wins: wins.length,
soloGames: solo.length, soloGames: solo.length,
soloWins: solo.filter((g) => g.won).length, soloWins: soloWins.length,
soloAvgClicks,
multiGames: multi.length, multiGames: multi.length,
multiWins: multi.filter((g) => g.won).length, multiWins: multi.filter((g) => g.won).length,
dailyGames: daily.length,
dailyWins: dailyWins.length,
dailyAvgClicks,
blitzGames: blitz.length,
blitzWins: blitzWins.length,
blitzBestTime,
avgClicks, avgClicks,
bestTime, bestTime,
}; };
+49 -12
View File
@@ -10,13 +10,20 @@ type LeaderboardRow = {
wins: number; wins: number;
soloGames: number; soloGames: number;
soloWins: number; soloWins: number;
soloAvgClicks: number | null;
multiGames: number; multiGames: number;
multiWins: number; multiWins: number;
dailyGames: number;
dailyWins: number;
dailyAvgClicks: number | null;
blitzGames: number;
blitzWins: number;
blitzBestTime: number | null;
avgClicks: number | null; avgClicks: number | null;
bestTime: number | null; bestTime: number | null;
}; };
type Mode = "all" | "solo" | "multi"; type Mode = "all" | "solo" | "multi" | "daily" | "blitz";
function fmt(s: number): string { function fmt(s: number): string {
const m = Math.floor(s / 60); const m = Math.floor(s / 60);
@@ -56,13 +63,21 @@ export function LeaderboardScreen({ onBack }: { onBack: () => void }) {
? r.soloGames > 0 ? r.soloGames > 0
: mode === "multi" : mode === "multi"
? r.multiGames > 0 ? r.multiGames > 0
: true, : mode === "daily"
? r.dailyGames > 0
: mode === "blitz"
? r.blitzGames > 0
: true,
) )
.sort((a, b) => { .sort((a, b) => {
if (mode === "solo") if (mode === "solo")
return b.soloWins - a.soloWins || b.soloGames - a.soloGames; return b.soloWins - a.soloWins || b.soloGames - a.soloGames;
if (mode === "multi") if (mode === "multi")
return b.multiWins - a.multiWins || b.multiGames - a.multiGames; return b.multiWins - a.multiWins || b.multiGames - a.multiGames;
if (mode === "daily")
return b.dailyWins - a.dailyWins || b.dailyGames - a.dailyGames;
if (mode === "blitz")
return b.blitzWins - a.blitzWins || b.blitzGames - a.blitzGames;
return b.wins - a.wins || b.totalGames - a.totalGames; return b.wins - a.wins || b.totalGames - a.totalGames;
}); });
@@ -80,14 +95,14 @@ export function LeaderboardScreen({ onBack }: { onBack: () => void }) {
</div> </div>
{/* Tabs */} {/* Tabs */}
<div className="flex gap-2 mb-4 sm:mb-5"> <div className="flex gap-2 mb-4 sm:mb-5 flex-wrap">
{(["all", "solo", "multi"] as Mode[]).map((m) => ( {(["all", "solo", "daily", "blitz", "multi"] as Mode[]).map((m) => (
<button <button
key={m} key={m}
className={`flex-1 py-2 rounded-xl text-xs sm:text-sm font-semibold border cursor-pointer transition-colors ${mode === m ? "bg-[#7c3aed] border-[#7c3aed] text-white" : "bg-[#1a1a1a] border-[#2e2e2e] text-[#888] hover:text-[#f0f0f0]"}`} className={`flex-1 py-2 rounded-xl text-xs sm:text-sm font-semibold border cursor-pointer transition-colors ${mode === m ? "bg-[#7c3aed] border-[#7c3aed] text-white" : "bg-[#1a1a1a] border-[#2e2e2e] text-[#888] hover:text-[#f0f0f0]"}`}
onClick={() => setMode(m)} onClick={() => setMode(m)}
> >
{m === "all" ? "Global" : m === "solo" ? "Solo" : "Multi"} {m === "all" ? "Global" : m === "solo" ? "Solo" : m === "multi" ? "Multi" : m === "daily" ? "Daily" : "Blitz"}
</button> </button>
))} ))}
</div> </div>
@@ -108,13 +123,35 @@ export function LeaderboardScreen({ onBack }: { onBack: () => void }) {
? row.soloGames ? row.soloGames
: mode === "multi" : mode === "multi"
? row.multiGames ? row.multiGames
: row.totalGames; : mode === "daily"
? row.dailyGames
: mode === "blitz"
? row.blitzGames
: row.totalGames;
const wins = const wins =
mode === "solo" mode === "solo"
? row.soloWins ? row.soloWins
: mode === "multi" : mode === "multi"
? row.multiWins ? row.multiWins
: row.wins; : mode === "daily"
? row.dailyWins
: mode === "blitz"
? row.blitzWins
: row.wins;
const avgClicks =
mode === "solo"
? row.soloAvgClicks
: mode === "daily"
? row.dailyAvgClicks
: mode === "all"
? row.avgClicks
: null;
const bestTime =
mode === "blitz"
? row.blitzBestTime
: mode === "all"
? row.bestTime
: null;
return ( return (
<div <div
@@ -136,20 +173,20 @@ export function LeaderboardScreen({ onBack }: { onBack: () => void }) {
<span className="font-bold text-[#f0f0f0]">{games}</span>{" "} <span className="font-bold text-[#f0f0f0]">{games}</span>{" "}
parties parties
</span> </span>
{row.avgClicks != null && ( {avgClicks != null && (
<span className="text-xs text-[#888]"> <span className="text-xs text-[#888]">
<span className="font-bold text-[#f0f0f0]"> <span className="font-bold text-[#f0f0f0]">
{row.avgClicks} {avgClicks}
</span>{" "} </span>{" "}
clics moy. clics moy.
</span> </span>
)} )}
{row.bestTime != null && ( {bestTime != null && (
<span className="text-xs text-[#888]"> <span className="text-xs text-[#888]">
<span className="font-bold text-[#f0f0f0]"> <span className="font-bold text-[#f0f0f0]">
{fmt(row.bestTime)} {fmt(bestTime)}
</span>{" "} </span>{" "}
meilleur {mode === "blitz" ? "meilleur temps" : "meilleur"}
</span> </span>
)} )}
</div> </div>