feat: add AI options to landing mock sidebar and wire database-backed stats & leaderboard with fresh zero states
This commit is contained in:
@@ -21,7 +21,9 @@ interface Doubt {
|
||||
}
|
||||
|
||||
export default function CommunityPage() {
|
||||
const { hasAccessTo, loading } = useActiveUser()
|
||||
const { user, hasAccessTo, loading } = useActiveUser()
|
||||
const currentUserName = user?.name || "Neha Verma"
|
||||
const currentUserInitials = currentUserName.substring(0, 2).toUpperCase()
|
||||
const [activeFilter, setActiveFilter] = useState<'all' | 'my-doubts' | 'solved' | 'unsolved'>('all')
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
const [isAskModalOpen, setIsAskModalOpen] = useState(false)
|
||||
@@ -58,8 +60,8 @@ export default function CommunityPage() {
|
||||
},
|
||||
{
|
||||
id: "3",
|
||||
author: "Demo Student",
|
||||
avatar: "DS",
|
||||
author: currentUserName,
|
||||
avatar: currentUserInitials,
|
||||
timeAgo: "1 day ago",
|
||||
subject: "Mathematics",
|
||||
topic: "Definite Integration",
|
||||
@@ -73,7 +75,7 @@ export default function CommunityPage() {
|
||||
]
|
||||
|
||||
const filteredDoubts = mockDoubts.filter(d => {
|
||||
if (activeFilter === 'my-doubts' && d.author !== "Demo Student") return false
|
||||
if (activeFilter === 'my-doubts' && d.author !== currentUserName) return false
|
||||
if (activeFilter === 'solved' && !d.isSolved) return false
|
||||
if (activeFilter === 'unsolved' && d.isSolved) return false
|
||||
if (searchQuery && !d.title.toLowerCase().includes(searchQuery.toLowerCase())) return false
|
||||
|
||||
@@ -1,28 +1,46 @@
|
||||
"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"
|
||||
import { useState, useEffect } from "react"
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { Trophy, Medal, Star, Shield } from "lucide-react"
|
||||
import { useActiveUser } from "@/hooks/useActiveUser"
|
||||
|
||||
type LeaderboardTab = 'all-india' | 'state' | 'school'
|
||||
|
||||
export default function LeaderboardPage() {
|
||||
const [activeTab, setActiveTab] = useState<LeaderboardTab>('all-india')
|
||||
const [isAnonymous, setIsAnonymous] = useState(false)
|
||||
const { user } = useActiveUser()
|
||||
const [rankings, setRankings] = useState<any[]>([])
|
||||
const [loadingRankings, setLoadingRankings] = useState(true)
|
||||
|
||||
// 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 },
|
||||
]
|
||||
useEffect(() => {
|
||||
async function fetchLeaderboard() {
|
||||
setLoadingRankings(true)
|
||||
try {
|
||||
const url = user?.id
|
||||
? `/api/leaderboard?tab=${activeTab}&user_id=${user.id}`
|
||||
: `/api/leaderboard?tab=${activeTab}`
|
||||
const res = await fetch(url)
|
||||
if (res.ok) {
|
||||
const data = await res.json()
|
||||
setRankings(data.rankings || [])
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Failed to load leaderboard", err)
|
||||
} finally {
|
||||
setLoadingRankings(false)
|
||||
}
|
||||
}
|
||||
fetchLeaderboard()
|
||||
}, [activeTab, user?.id])
|
||||
|
||||
const currentStudent = rankings.find(r => r.isCurrentUser) || {
|
||||
rank: rankings.length + 1,
|
||||
score: 0,
|
||||
streak: user?.streak_count || 1,
|
||||
name: user?.name || "You"
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="p-8 space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-500 max-w-7xl mx-auto">
|
||||
@@ -72,50 +90,57 @@ export default function LeaderboardPage() {
|
||||
</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()}
|
||||
{loadingRankings ? (
|
||||
<div className="flex flex-col items-center justify-center p-12 bg-card rounded-2xl border border-border">
|
||||
<div className="size-8 border-4 border-[#00F0FF] border-t-transparent rounded-full animate-spin mb-3"></div>
|
||||
<p className="text-sm text-muted-foreground font-medium">Calculating live rankings...</p>
|
||||
</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>
|
||||
<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 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 || "U").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 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>
|
||||
|
||||
<div className="space-y-6">
|
||||
@@ -124,10 +149,14 @@ export default function LeaderboardPage() {
|
||||
<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>
|
||||
<div className="text-4xl font-black text-[#00F0FF] mb-1">#{currentStudent.rank}</div>
|
||||
<p className="text-sm font-medium text-foreground mb-4">
|
||||
{currentStudent.score === 0 ? "Fresh Start · Day 1 of your journey" : `Rank #${currentStudent.rank} overall`}
|
||||
</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!
|
||||
{currentStudent.score === 0
|
||||
? "Complete your first Daily Practice Problem (DPP) or CBT mock test to earn points and climb the rankings!"
|
||||
: `You have earned ${currentStudent.score.toLocaleString()} points. Keep up daily practice to boost your streak bonus!`}
|
||||
</p>
|
||||
|
||||
<div className="mt-6 flex items-center justify-between border-t border-border pt-4">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client"
|
||||
|
||||
import { useRef } from "react"
|
||||
import { useRef, useState, useEffect } from "react"
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Button } from "@/components/ui/button"
|
||||
@@ -13,6 +13,24 @@ import { useActiveUser } from "@/hooks/useActiveUser"
|
||||
export default function DashboardPage() {
|
||||
const { user, hasAccessTo, loading } = useActiveUser()
|
||||
const progressRef = useRef<HTMLDivElement>(null)
|
||||
const [userStats, setUserStats] = useState<any>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const userId = user?.id
|
||||
if (!userId) return
|
||||
async function fetchStats() {
|
||||
try {
|
||||
const res = await fetch(`/api/users/${userId}/stats`)
|
||||
if (res.ok) {
|
||||
const data = await res.json()
|
||||
setUserStats(data)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to load user stats", e)
|
||||
}
|
||||
}
|
||||
fetchStats()
|
||||
}, [user?.id])
|
||||
|
||||
const handleExport = async () => {
|
||||
if (progressRef.current) {
|
||||
@@ -37,6 +55,8 @@ export default function DashboardPage() {
|
||||
)
|
||||
}
|
||||
|
||||
const isNew = userStats ? userStats.is_new_user : true
|
||||
|
||||
return (
|
||||
<div className="p-8 max-w-7xl mx-auto space-y-10 animate-in fade-in duration-500 slide-in-from-bottom-4">
|
||||
|
||||
@@ -59,30 +79,49 @@ export default function DashboardPage() {
|
||||
</Button>
|
||||
</div>
|
||||
<div ref={progressRef} className="grid grid-cols-2 md:grid-cols-4 gap-4 p-4 -mx-4 bg-background rounded-2xl">
|
||||
{/* Today's Score */}
|
||||
<Card>
|
||||
<CardContent className="p-5 flex flex-col items-center text-center">
|
||||
<div className="p-3 bg-secondary rounded-full mb-3 text-primary"><Target size={24} /></div>
|
||||
<p className="text-sm font-bold text-muted-foreground uppercase tracking-wider mb-1">Today's Score</p>
|
||||
<h3 className="text-2xl font-bold">42 / 60</h3>
|
||||
<p className="text-xs text-green-500 font-bold mt-2 flex items-center gap-1"><TrendingUp size={12}/> +5 from yesterday</p>
|
||||
<h3 className="text-2xl font-bold">
|
||||
{isNew ? "0 / 0" : `${userStats.today_score} / ${userStats.today_max_score || 60}`}
|
||||
</h3>
|
||||
<p className="text-xs text-muted-foreground font-medium mt-2">
|
||||
{isNew ? "No tests taken today" : userStats.today_score > 0 ? "Daily session score" : "Start today's DPP"}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Overall Accuracy */}
|
||||
<Card>
|
||||
<CardContent className="p-5 flex flex-col items-center text-center">
|
||||
<div className="p-3 bg-secondary rounded-full mb-3 text-amber-500"><Activity size={24} /></div>
|
||||
<p className="text-sm font-bold text-muted-foreground uppercase tracking-wider mb-1">Overall Accuracy</p>
|
||||
<h3 className="text-2xl font-bold">68%</h3>
|
||||
<p className="text-xs text-amber-500 font-bold mt-2">Needs Improvement</p>
|
||||
<h3 className="text-2xl font-bold">
|
||||
{isNew ? "0%" : `${userStats.overall_accuracy}%`}
|
||||
</h3>
|
||||
<p className={`text-xs font-bold mt-2 ${isNew ? 'text-muted-foreground font-normal' : userStats.overall_accuracy >= 70 ? 'text-green-500' : 'text-amber-500'}`}>
|
||||
{isNew ? "Start a DPP to calculate" : userStats.overall_accuracy >= 70 ? "On Track" : "Needs Improvement"}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Qs Attempted */}
|
||||
<Card>
|
||||
<CardContent className="p-5 flex flex-col items-center text-center">
|
||||
<div className="p-3 bg-secondary rounded-full mb-3 text-[#00F0FF]"><CheckCircle2 size={24} /></div>
|
||||
<p className="text-sm font-bold text-muted-foreground uppercase tracking-wider mb-1">Qs Attempted</p>
|
||||
<h3 className="text-2xl font-bold">1,245</h3>
|
||||
<p className="text-xs text-muted-foreground font-medium mt-2">Lifetime total</p>
|
||||
<h3 className="text-2xl font-bold">
|
||||
{isNew ? "0" : (userStats.total_questions_attempted || 0).toLocaleString()}
|
||||
</h3>
|
||||
<p className="text-xs text-muted-foreground font-medium mt-2">
|
||||
{isNew ? "0 questions answered" : "Lifetime total"}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* City Rank */}
|
||||
<Card className="relative overflow-hidden group">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-background to-secondary/50 z-0"></div>
|
||||
<CardContent className="p-5 flex flex-col items-center text-center relative z-10">
|
||||
@@ -90,8 +129,16 @@ export default function DashboardPage() {
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<p className="text-sm font-bold text-muted-foreground uppercase tracking-wider">City Rank</p>
|
||||
</div>
|
||||
<h3 className="text-2xl font-bold">#42 <span className="text-base text-muted-foreground font-medium">/ 1,200</span></h3>
|
||||
<p className="text-xs text-muted-foreground font-medium mt-2 text-[#00F0FF]">Top 5% in Jaipur</p>
|
||||
<h3 className="text-2xl font-bold">
|
||||
{isNew ? (
|
||||
<span className="text-muted-foreground text-xl">#--</span>
|
||||
) : (
|
||||
<>#{userStats.city_rank || 1} <span className="text-base text-muted-foreground font-medium">/ {userStats.city_total || 50}</span></>
|
||||
)}
|
||||
</h3>
|
||||
<p className="text-xs text-muted-foreground font-medium mt-2 text-[#00F0FF]">
|
||||
{isNew ? "Unranked · Take a test" : `Top rank in ${user?.city || "your city"}`}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
@@ -2,17 +2,19 @@
|
||||
|
||||
import { Card, CardContent } from "@/components/ui/card"
|
||||
import { Flame, Gift, Star, Trophy, Target, ArrowRight, CheckCircle2 } from "lucide-react"
|
||||
import { useActiveUser } from "@/hooks/useActiveUser"
|
||||
|
||||
export default function StreaksPage() {
|
||||
const currentStreak = 12;
|
||||
const bestStreak = 45;
|
||||
const { user } = useActiveUser()
|
||||
const currentStreak = user?.streak_count ?? 1
|
||||
const bestStreak = Math.max(currentStreak, user?.longest_streak ?? 1)
|
||||
|
||||
const rewards = [
|
||||
{ streak: 7, title: "Consistent Learner", reward: "1 Free Premium DPP", unlocked: true },
|
||||
{ streak: 14, title: "Dedicated Scholar", reward: "AI Performance Analysis Report", unlocked: false },
|
||||
{ streak: 30, title: "Exam Warrior", reward: "1 Full Length Mock Test", unlocked: false },
|
||||
{ streak: 50, title: "PragatiVeer Champion", reward: "1-on-1 AI Tutor Session", unlocked: false },
|
||||
{ streak: 100, title: "Unstoppable", reward: "Merch + 50% Off Next Renewal", unlocked: false },
|
||||
{ streak: 7, title: "Consistent Learner", reward: "1 Free Premium DPP", unlocked: currentStreak >= 7 },
|
||||
{ streak: 14, title: "Dedicated Scholar", reward: "AI Performance Analysis Report", unlocked: currentStreak >= 14 },
|
||||
{ streak: 30, title: "Exam Warrior", reward: "1 Full Length Mock Test", unlocked: currentStreak >= 30 },
|
||||
{ streak: 50, title: "PragatiVeer Champion", reward: "1-on-1 AI Tutor Session", unlocked: currentStreak >= 50 },
|
||||
{ streak: 100, title: "Unstoppable", reward: "Merch + 50% Off Next Renewal", unlocked: currentStreak >= 100 },
|
||||
];
|
||||
|
||||
return (
|
||||
@@ -55,7 +57,7 @@ export default function StreaksPage() {
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-muted-foreground mb-1">Total Streak Points Earned</p>
|
||||
<p className="text-3xl font-bold text-[#00F0FF]">1,450</p>
|
||||
<p className="text-3xl font-bold text-[#00F0FF]">{(currentStreak * 50).toLocaleString()}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Card, CardContent } from "@/components/ui/card"
|
||||
import { BrainCircuit, Activity, Zap, CheckCircle2, Shield, ArrowRight, Play, BookOpen, LineChart, Trophy, Sparkles, MessageSquare, X, Minus } from "lucide-react"
|
||||
import { BrainCircuit, Activity, Zap, CheckCircle2, Shield, ArrowRight, Play, BookOpen, LineChart, Trophy, Sparkles, MessageSquare, X, Minus, Target, Calendar } from "lucide-react"
|
||||
import Image from "next/image"
|
||||
import Link from "next/link"
|
||||
import { motion, useScroll, useTransform, Variants } from "framer-motion"
|
||||
@@ -185,13 +185,25 @@ export default function MarketingPage() {
|
||||
</div>
|
||||
<div className="pt-6 border-t border-border/50">
|
||||
<p className="px-3 text-[10px] font-bold uppercase tracking-widest text-muted-foreground mb-3">AI Intelligence</p>
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-3 px-3 py-2.5 rounded-xl text-muted-foreground hover:bg-secondary/30 hover:text-foreground text-sm font-semibold transition-all">
|
||||
<LineChart size={18} />
|
||||
<div className="space-y-1.5">
|
||||
<div className="flex items-center gap-3 px-3 py-2 rounded-xl text-muted-foreground hover:bg-secondary/30 hover:text-foreground text-sm font-semibold transition-all">
|
||||
<Zap size={17} className="text-yellow-500 shrink-0" />
|
||||
Prep Pilot AI
|
||||
</div>
|
||||
<div className="flex items-center gap-3 px-3 py-2 rounded-xl text-muted-foreground hover:bg-secondary/30 hover:text-foreground text-sm font-semibold transition-all">
|
||||
<Target size={17} className="text-[#00F0FF] shrink-0" />
|
||||
Exam Strategy
|
||||
</div>
|
||||
<div className="flex items-center gap-3 px-3 py-2 rounded-xl text-muted-foreground hover:bg-secondary/30 hover:text-foreground text-sm font-semibold transition-all">
|
||||
<Calendar size={17} className="text-primary shrink-0" />
|
||||
AI Study Plan
|
||||
</div>
|
||||
<div className="flex items-center gap-3 px-3 py-2 rounded-xl text-muted-foreground hover:bg-secondary/30 hover:text-foreground text-sm font-semibold transition-all">
|
||||
<LineChart size={17} className="shrink-0" />
|
||||
Analytics
|
||||
</div>
|
||||
<div className="flex items-center gap-3 px-3 py-2.5 rounded-xl text-muted-foreground hover:bg-secondary/30 hover:text-foreground text-sm font-semibold transition-all">
|
||||
<Trophy size={18} />
|
||||
<div className="flex items-center gap-3 px-3 py-2 rounded-xl text-muted-foreground hover:bg-secondary/30 hover:text-foreground text-sm font-semibold transition-all">
|
||||
<Trophy size={17} className="shrink-0" />
|
||||
Rank Predictor
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user