From 969500f5a4462abdf36d9be17c33c1e7a86275a7 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Sat, 7 Feb 2026 15:44:38 +0100 Subject: [PATCH] Working leaderboard --- .../components/LeaderboardRow.tsx | 72 ++++--- src/app/leaderboards/page.tsx | 65 +++--- src/modules/leaderboard/actions.ts | 197 +----------------- src/modules/leaderboard/core.ts | 18 ++ src/modules/leaderboard/types.ts | 13 +- 5 files changed, 99 insertions(+), 266 deletions(-) diff --git a/src/app/leaderboards/components/LeaderboardRow.tsx b/src/app/leaderboards/components/LeaderboardRow.tsx index 884bc1b..fde8889 100644 --- a/src/app/leaderboards/components/LeaderboardRow.tsx +++ b/src/app/leaderboards/components/LeaderboardRow.tsx @@ -1,25 +1,33 @@ -import { LeaderboardRank } from "@/modules/leaderboard/types"; import { LinkIcon, Trophy } from "lucide-react"; import Link from "next/link"; +interface GlobalLeaderboardUser { + rank: number; + user_id: string; + username: string; + wins: number; + podiums: number; + problems_completed: number; +} + const LeaderboardRow = ({ index = 0, run, }: { index?: number; - run: LeaderboardRank; + run: GlobalLeaderboardUser; }) => { const getRankStyle = (rank: number) => { - if (run.rank === 1) return "rank-gold"; - if (run.rank === 2) return "rank-silver"; - if (run.rank === 3) return "rank-bronze"; + if (rank === 1) return "rank-gold"; + if (rank === 2) return "rank-silver"; + if (rank === 3) return "rank-bronze"; return "bg-secondary text-muted-foreground"; }; const getRankGlow = (rank: number) => { - if (run.rank === 1) return "glow-gold"; - if (run.rank === 2) return "glow-silver"; - if (run.rank === 3) return "glow-bronze"; + if (rank === 1) return "glow-gold"; + if (rank === 2) return "glow-silver"; + if (rank === 3) return "glow-bronze"; return ""; }; @@ -27,7 +35,9 @@ const LeaderboardRow = ({ return (
{isTopThree ? : run.rank}
@@ -46,41 +58,37 @@ const LeaderboardRow = ({
{run.username}
- {/* Time */} - - - {run.pb} - + {/* Wins */} + + {run.wins} - {/* Category */} - - - {run.category} - + {/* Podiums */} + + {run.podiums} - {/* Assisted */} - - - {run.assisted ? "Assisted" : "Unassisted"} - + {/* Problems Completed */} + + {run.problems_completed} - {/* Runs */} - {run.runsCount} - - {/* Problem */} + {/* Profile Link */} - - {run.problem} + + View Profile diff --git a/src/app/leaderboards/page.tsx b/src/app/leaderboards/page.tsx index 8487913..51e704a 100644 --- a/src/app/leaderboards/page.tsx +++ b/src/app/leaderboards/page.tsx @@ -3,8 +3,7 @@ import { Trophy, Calendar, Globe, TrendingUp } from "lucide-react"; import { useState } from "react"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { Button } from "@/components/ui/button"; +import { Card } from "@/components/ui/card"; import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Table, @@ -13,7 +12,6 @@ import { TableHeader, TableRow, } from "@/components/ui/table"; -import { Badge } from "@/components/ui/badge"; import { Pagination, PaginationContent, @@ -30,11 +28,10 @@ import { getGlobalLeaderboard, getWeeklyLeaderboard, } from "@/modules/leaderboard/actions"; -import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; export default function Leaderboards() { - const [global, globalLoaded, globalError] = useAction(getGlobalLeaderboard); - const [weekly, weeklyLoaded, weeklyError] = useAction(getWeeklyLeaderboard); + const [global] = useAction(getGlobalLeaderboard); + const [weekly] = useAction(getWeeklyLeaderboard); const [activeTab, setActiveTab] = useState<"global" | "weekly">("global"); const data = (activeTab === "global" ? global : weekly) || []; @@ -95,26 +92,6 @@ export default function Leaderboards() { - -
- - - any% - - - 100% - - - - - - Assisted - - - Unassisted - - -
{/* Table */} @@ -122,28 +99,38 @@ export default function Leaderboards() { - -

Rank

+ {/* Rank */} + + Rank - -

Runner

+ + {/* Runner / Username */} + Runner + + {/* Wins */} + + Wins - -

Time

+ + {/* Podiums */} + + Podiums - -

Category

+ + {/* Problems Completed */} + + Problems Completed - -

Assisted

+ + {/* User ID (optional) */} + + User ID - Runs - Problem
{data.map((entry, i) => ( - + ))}
diff --git a/src/modules/leaderboard/actions.ts b/src/modules/leaderboard/actions.ts index c62bab4..4e395f6 100644 --- a/src/modules/leaderboard/actions.ts +++ b/src/modules/leaderboard/actions.ts @@ -1,194 +1,17 @@ "use server"; +import { createClient } from "@/lib/supabase/server"; import * as Core from "./core"; -import { LeaderboardRank } from "./types"; // updated interface +import { LeaderboardUser } from "./types"; // updated interface -export async function getGlobalLeaderboard(): Promise { - return [ - { - rank: 1, - username: "speedmaster_x", - pb: "2:34.21", - runsCount: 2341, - problem: "Fix broken login flow", - category: "any%", - assisted: false, - }, - { - rank: 2, - username: "algo_ninja", - pb: "2:38.54", - runsCount: 1876, - problem: "Refactor legacy API endpoints", - category: "any%", - assisted: false, - }, - { - rank: 3, - username: "clockwork", - pb: "2:41.02", - runsCount: 3102, - problem: "Optimize image upload pipeline", - category: "any%", - assisted: false, - }, - { - rank: 4, - username: "fastfingers99", - pb: "2:45.33", - runsCount: 987, - problem: "Build a mini chat server", - category: "any%", - assisted: false, - }, - { - rank: 5, - username: "recursion_queen", - pb: "2:47.89", - runsCount: 1543, - problem: "Fix CSS layout bug in dashboard", - category: "any%", - assisted: false, - }, - { - rank: 6, - username: "bitshifter", - pb: "2:52.11", - runsCount: 2109, - problem: "Implement JWT authentication", - category: "any%", - assisted: false, - }, - { - rank: 7, - username: "nullpointer", - pb: "2:54.67", - runsCount: 876, - problem: "Create REST endpoint for user data", - category: "any%", - assisted: false, - }, - { - rank: 8, - username: "stackattack", - pb: "2:58.23", - runsCount: 1234, - problem: "Fix off-by-one bug in sorting function", - category: "any%", - assisted: false, - }, - { - rank: 9, - username: "looplegend", - pb: "3:01.45", - runsCount: 654, - problem: "Optimize SQL query for reports", - category: "any%", - assisted: false, - }, - { - rank: 10, - username: "coderunner", - pb: "3:05.78", - runsCount: 1098, - problem: "Add file upload validation", - category: "any%", - assisted: false, - }, - ]; +export async function getGlobalLeaderboard(): Promise { + const supabase = await createClient(); + + return await Core.getGlobalLeaderboard(supabase); } -export async function getWeeklyLeaderboard(): Promise { - return [ - { - rank: 1, - username: "weekly_warrior", - pb: "2:51.33", - runsCount: 156, - problem: "Build notifications API", - category: "any%", - assisted: false, - }, - { - rank: 2, - username: "speedmaster_x", - pb: "2:53.12", - runsCount: 89, - problem: "Build notifications API", - category: "any%", - assisted: false, - }, - { - rank: 3, - username: "newbie_grinder", - pb: "2:58.67", - runsCount: 234, - problem: "Build notifications API", - category: "any%", - assisted: false, - }, - { - rank: 4, - username: "algo_ninja", - pb: "3:02.45", - runsCount: 67, - problem: "Build notifications API", - category: "any%", - assisted: false, - }, - { - rank: 5, - username: "fresh_runner", - pb: "3:05.89", - runsCount: 45, - problem: "Build notifications API", - category: "any%", - assisted: false, - }, - { - rank: 6, - username: "practice_makes", - pb: "3:08.12", - runsCount: 112, - problem: "Build notifications API", - category: "any%", - assisted: false, - }, - { - rank: 7, - username: "splitmaster", - pb: "3:12.34", - runsCount: 78, - problem: "Build notifications API", - category: "any%", - assisted: false, - }, - { - rank: 8, - username: "grindtime", - pb: "3:15.56", - runsCount: 92, - problem: "Build notifications API", - category: "any%", - assisted: false, - }, - { - rank: 9, - username: "pb_hunter", - pb: "3:18.90", - runsCount: 56, - problem: "Build notifications API", - category: "any%", - assisted: false, - }, - { - rank: 10, - username: "reset_king", - pb: "3:22.11", - runsCount: 187, - problem: "Build notifications API", - category: "any%", - assisted: false, - }, - ]; +export async function getWeeklyLeaderboard(): Promise { + const supabase = await createClient(); + + return await Core.getWeeklyLeaderboard(supabase); } diff --git a/src/modules/leaderboard/core.ts b/src/modules/leaderboard/core.ts index a59f71d..d981b6c 100644 --- a/src/modules/leaderboard/core.ts +++ b/src/modules/leaderboard/core.ts @@ -1,3 +1,21 @@ "use server"; +import { SupabaseClient } from "@supabase/supabase-js"; import "server-only"; +import { LeaderboardUser } from "./types"; + +export async function getGlobalLeaderboard( + supabase: SupabaseClient, +): Promise { + const { data, error } = await supabase.rpc("get_global_leaderboard"); + if (error) throw error; + return data; +} + +export async function getWeeklyLeaderboard( + supabase: SupabaseClient, +): Promise { + const { data, error } = await supabase.rpc("get_weekly_leaderboard"); + if (error) throw error; + return data; +} diff --git a/src/modules/leaderboard/types.ts b/src/modules/leaderboard/types.ts index 0f1cc51..c5edd3e 100644 --- a/src/modules/leaderboard/types.ts +++ b/src/modules/leaderboard/types.ts @@ -1,11 +1,8 @@ -export type Leaderboard = LeaderboardRank[]; - -export interface LeaderboardRank { +export interface LeaderboardUser { rank: number; + user_id: string; username: string; - pb: string; - runsCount: number; - problem: string; - category: "any%" | "100%"; - assisted: boolean; + wins: number; + podiums: number; + problems_completed: number; }