147 lines
8.2 KiB
TypeScript
147 lines
8.2 KiB
TypeScript
"use client"
|
||
|
||
import { useState } from "react"
|
||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"
|
||
import { Trophy, Medal, Star, Shield, Search } from "lucide-react"
|
||
|
||
type LeaderboardTab = 'all-india' | 'state' | 'school'
|
||
|
||
export default function LeaderboardPage() {
|
||
const [activeTab, setActiveTab] = useState<LeaderboardTab>('all-india')
|
||
const [isAnonymous, setIsAnonymous] = useState(false)
|
||
|
||
// Mock data representing F-07 requirements (Accuracy x Consistency)
|
||
const rankings = [
|
||
{ rank: 1, name: "Rahul Verma", score: 9850, accuracy: "92%", streak: 45, isCurrentUser: false },
|
||
{ rank: 2, name: "Priya Sharma", score: 9420, accuracy: "89%", streak: 42, isCurrentUser: false },
|
||
{ rank: 3, name: "Amit Kumar", score: 9100, accuracy: "87%", streak: 38, isCurrentUser: false },
|
||
{ rank: 4, name: "Sneha Patel", score: 8950, accuracy: "85%", streak: 30, isCurrentUser: false },
|
||
{ rank: 5, name: "Vikram Singh", score: 8800, accuracy: "84%", streak: 28, isCurrentUser: false },
|
||
{ rank: 6, name: "Demo Student", score: 8650, accuracy: "82%", streak: 25, isCurrentUser: true },
|
||
{ rank: 7, name: "Ananya Desai", score: 8500, accuracy: "80%", streak: 20, isCurrentUser: false },
|
||
{ rank: 8, name: "Karan Johar", score: 8450, accuracy: "79%", streak: 18, isCurrentUser: false },
|
||
{ rank: 9, name: "Neha Gupta", score: 8300, accuracy: "78%", streak: 15, isCurrentUser: false },
|
||
{ rank: 10, name: "Rohan Das", score: 8100, accuracy: "75%", streak: 12, isCurrentUser: false },
|
||
]
|
||
|
||
return (
|
||
<div className="p-8 space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-500 max-w-7xl mx-auto">
|
||
<div className="flex flex-col md:flex-row justify-between items-start md:items-end gap-4">
|
||
<div>
|
||
<h1 className="text-3xl md:text-4xl font-black tracking-tight flex items-center gap-3 mb-1">
|
||
<span className="p-2.5 rounded-xl bg-[#00F0FF]/10 border border-[#00F0FF]/20">
|
||
<Trophy className="text-[#00F0FF]" size={28} />
|
||
</span>
|
||
Leaderboard
|
||
</h1>
|
||
<p className="text-muted-foreground text-base font-medium max-w-2xl pl-1">
|
||
Rankings are based on your Accuracy × Consistency (Streak Bonus).
|
||
</p>
|
||
</div>
|
||
|
||
<div className="flex items-center gap-3 bg-secondary/50 p-1.5 rounded-xl border border-border">
|
||
<button
|
||
onClick={() => setActiveTab('all-india')}
|
||
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${activeTab === 'all-india' ? 'bg-background shadow-sm border border-border text-foreground' : 'text-muted-foreground hover:text-foreground'}`}
|
||
>
|
||
All India
|
||
</button>
|
||
<button
|
||
onClick={() => setActiveTab('state')}
|
||
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${activeTab === 'state' ? 'bg-background shadow-sm border border-border text-foreground' : 'text-muted-foreground hover:text-foreground'}`}
|
||
>
|
||
State
|
||
</button>
|
||
<button
|
||
onClick={() => setActiveTab('school')}
|
||
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${activeTab === 'school' ? 'bg-background shadow-sm border border-border text-foreground' : 'text-muted-foreground hover:text-foreground'}`}
|
||
>
|
||
School
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||
<div className="lg:col-span-2 space-y-4">
|
||
<div className="flex justify-between items-center bg-card border border-border rounded-xl p-4 shadow-sm sticky top-0 z-10 backdrop-blur-md bg-opacity-90">
|
||
<span className="text-sm font-bold text-muted-foreground w-12 text-center">RANK</span>
|
||
<span className="text-sm font-bold text-muted-foreground flex-1 px-4">STUDENT</span>
|
||
<div className="flex gap-8 w-48 justify-end pr-4">
|
||
<span className="text-sm font-bold text-muted-foreground text-center">SCORE</span>
|
||
<span className="text-sm font-bold text-muted-foreground text-center">STREAK</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="space-y-3">
|
||
{rankings.map((student) => (
|
||
<div
|
||
key={student.rank}
|
||
className={`flex justify-between items-center p-4 rounded-xl border transition-all ${
|
||
student.isCurrentUser
|
||
? 'border-[#00F0FF] bg-[#00F0FF]/5 shadow-[0_0_15px_-3px_rgba(0,240,255,0.2)]'
|
||
: 'border-border bg-card hover:bg-secondary/20'
|
||
}`}
|
||
>
|
||
<div className="w-12 flex justify-center">
|
||
{student.rank === 1 ? <Medal className="text-yellow-500" size={24} /> :
|
||
student.rank === 2 ? <Medal className="text-gray-400" size={24} /> :
|
||
student.rank === 3 ? <Medal className="text-amber-700" size={24} /> :
|
||
<span className={`font-bold text-lg ${student.isCurrentUser ? 'text-[#00F0FF]' : 'text-muted-foreground'}`}>#{student.rank}</span>}
|
||
</div>
|
||
|
||
<div className="flex-1 px-4 flex items-center gap-3">
|
||
<div className={`h-10 w-10 rounded-full flex items-center justify-center font-bold text-sm ${
|
||
student.isCurrentUser ? 'bg-[#00F0FF]/20 text-[#00F0FF]' : 'bg-secondary text-muted-foreground'
|
||
}`}>
|
||
{student.name.substring(0, 2).toUpperCase()}
|
||
</div>
|
||
<div>
|
||
<p className={`font-bold ${student.isCurrentUser ? 'text-[#00F0FF]' : 'text-foreground'}`}>
|
||
{student.isCurrentUser ? (isAnonymous ? "Anonymous User" : student.name + " (You)") : student.name}
|
||
</p>
|
||
<p className="text-xs text-muted-foreground flex items-center gap-1">
|
||
<Shield size={12} /> Accuracy: {student.accuracy}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex gap-8 w-48 justify-end pr-4 items-center">
|
||
<div className="text-center font-mono font-bold text-foreground">
|
||
{student.score.toLocaleString()}
|
||
</div>
|
||
<div className="text-center flex items-center gap-1 font-bold text-orange-500">
|
||
<Star size={14} className="fill-orange-500" /> {student.streak}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="space-y-6">
|
||
<Card className="border-[#00F0FF]/30 bg-gradient-to-br from-[#00F0FF]/10 to-transparent">
|
||
<CardHeader className="pb-2">
|
||
<CardTitle className="text-xl flex items-center gap-2">Your Standing</CardTitle>
|
||
</CardHeader>
|
||
<CardContent>
|
||
<div className="text-4xl font-black text-[#00F0FF] mb-1">#6</div>
|
||
<p className="text-sm font-medium text-foreground mb-4">Top 15% of users</p>
|
||
<p className="text-xs text-muted-foreground leading-relaxed">
|
||
You are 150 points away from breaking into the Top 5. Complete today's DPS to earn your streak bonus!
|
||
</p>
|
||
|
||
<div className="mt-6 flex items-center justify-between border-t border-border pt-4">
|
||
<span className="text-sm font-medium text-foreground">Anonymous Mode</span>
|
||
<label className="relative inline-flex items-center cursor-pointer">
|
||
<input type="checkbox" className="sr-only peer" checked={isAnonymous} onChange={() => setIsAnonymous(!isAnonymous)} />
|
||
<div className="w-11 h-6 bg-secondary peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[#00F0FF]"></div>
|
||
</label>
|
||
</div>
|
||
</CardContent>
|
||
</Card>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|