feat: complete pragativeer v2 frontend and backend, docker setup, and cbt engine

This commit is contained in:
2026-07-06 17:08:13 +05:30
parent ce01795aa9
commit 426b98ddab
66 changed files with 7949 additions and 363 deletions

View File

@@ -0,0 +1,142 @@
"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-5xl mx-auto">
<div className="flex flex-col md:flex-row justify-between items-start md:items-end gap-4">
<div>
<h1 className="text-3xl font-bold tracking-tight text-foreground flex items-center gap-3">
<Trophy className="text-yellow-500" size={32} />
Leaderboard
</h1>
<p className="text-muted-foreground mt-2 font-medium">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>
)
}