141 lines
6.9 KiB
TypeScript
141 lines
6.9 KiB
TypeScript
"use client"
|
|
|
|
import { fetchWithAuth } from "@/lib/apiClient";
|
|
import { useState, useEffect } from "react"
|
|
import { Card, CardContent } from "@/components/ui/card"
|
|
import { Flame, Gift, Star, Trophy, Target, ArrowRight, CheckCircle2 } from "lucide-react"
|
|
import { useActiveUser, isDemoUser } from "@/hooks/useActiveUser"
|
|
|
|
export default function StreaksPage() {
|
|
const { user } = useActiveUser()
|
|
const isDemo = isDemoUser(user?.id)
|
|
const currentStreak = user?.streak_count ?? 1
|
|
const bestStreak = isDemo ? (user?.longest_streak ?? 45) : Math.max(currentStreak, user?.longest_streak ?? 1)
|
|
const [streakPoints, setStreakPoints] = useState(0)
|
|
|
|
useEffect(() => {
|
|
if (!user?.id) return
|
|
if (isDemo) {
|
|
setStreakPoints(currentStreak * 50)
|
|
return
|
|
}
|
|
async function loadStats() {
|
|
try {
|
|
const res = await fetchWithAuth(`/api/users/${user?.id}/stats`)
|
|
if (res.ok) {
|
|
const stats = await res.json()
|
|
if (!stats.is_new_user) {
|
|
setStreakPoints(currentStreak * 50)
|
|
} else {
|
|
setStreakPoints(0)
|
|
}
|
|
}
|
|
} catch {
|
|
setStreakPoints(0)
|
|
}
|
|
}
|
|
loadStats()
|
|
}, [user?.id, isDemo, currentStreak])
|
|
|
|
const rewards = [
|
|
{ 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 (
|
|
<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 md:items-end justify-between 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-yellow-50 dark:bg-[var(--cyan-ai-10)] border border-yellow-300/60 dark:border-[var(--cyan-ai-20)]">
|
|
<Flame className="text-yellow-600 dark:text-[var(--cyan-ai)]" size={28} />
|
|
</span>
|
|
Your Learning Streak
|
|
</h1>
|
|
<p className="text-muted-foreground text-base font-medium max-w-2xl pl-1">
|
|
Build daily habits to unlock premium rewards and boost your rank.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<Card className="bg-gradient-to-br from-orange-500/20 to-rose-500/5 border-orange-500/30">
|
|
<CardContent className="p-8 flex flex-col items-center justify-center text-center h-full">
|
|
<Flame size={64} className="text-orange-500 mb-4 animate-pulse" />
|
|
<h2 className="text-6xl font-black text-foreground mb-2">{currentStreak} <span className="text-2xl text-muted-foreground">Days</span></h2>
|
|
<p className="text-sm font-bold text-orange-500">CURRENT STREAK</p>
|
|
<p className="text-xs text-muted-foreground mt-4 max-w-xs">
|
|
{streakPoints === 0
|
|
? "Complete your first Daily Practice Problem (DPP) or CBT Exam today to start earning streak points."
|
|
: "Complete a Daily Practice Problem (DPP) or CBT Exam every day to keep your streak alive."}
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card className="bg-secondary/20 border-border">
|
|
<CardContent className="p-8 h-full flex flex-col justify-between">
|
|
<div>
|
|
<div className="flex justify-between items-center mb-6">
|
|
<p className="text-sm font-bold text-muted-foreground">STREAK STATS</p>
|
|
<Target size={20} className="text-primary" />
|
|
</div>
|
|
<div className="space-y-6">
|
|
<div>
|
|
<p className="text-sm text-muted-foreground mb-1">Personal Best</p>
|
|
<p className="text-3xl font-bold text-foreground">{bestStreak} <span className="text-lg text-muted-foreground">Days</span></p>
|
|
</div>
|
|
<div>
|
|
<p className="text-sm text-muted-foreground mb-1">Total Streak Points Earned</p>
|
|
<p className="text-3xl font-bold text-[var(--cyan-ai)]">{streakPoints.toLocaleString()}</p>
|
|
{streakPoints === 0 && (
|
|
<p className="text-xs text-muted-foreground mt-1">Take a test to activate consistency bonus points</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mt-8 pt-6 border-t border-border">
|
|
<p className="text-xs text-muted-foreground leading-relaxed">
|
|
<strong className="text-foreground">Pro Tip:</strong> Your streak directly multiplies your leaderboard score. A longer streak gives you a higher Consistency Bonus.
|
|
</p>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
<div className="pt-8">
|
|
<h2 className="text-2xl font-bold mb-6 flex items-center gap-2"><Gift className="text-[var(--cyan-ai)]" /> Streak Rewards</h2>
|
|
<div className="space-y-4">
|
|
{rewards.map((reward, i) => (
|
|
<div key={i} className={`p-5 rounded-2xl border flex items-center justify-between transition-colors ${reward.unlocked ? 'bg-primary/5 border-primary/30' : 'bg-secondary/10 border-border opacity-70'}`}>
|
|
<div className="flex items-center gap-6">
|
|
<div className={`size-14 rounded-full flex items-center justify-center font-bold text-xl ${reward.unlocked ? 'bg-primary text-primary-foreground shadow-[0_0_15px_-5px_var(--color-primary)]' : 'bg-secondary text-muted-foreground'}`}>
|
|
{reward.streak}
|
|
</div>
|
|
<div>
|
|
<h3 className={`font-bold ${reward.unlocked ? 'text-foreground' : 'text-muted-foreground'}`}>{reward.title}</h3>
|
|
<p className={`text-sm ${reward.unlocked ? 'text-primary font-medium' : 'text-muted-foreground'}`}>{reward.reward}</p>
|
|
</div>
|
|
</div>
|
|
<div className="hidden sm:block">
|
|
{reward.unlocked ? (
|
|
<div className="flex items-center gap-2 text-primary font-bold text-sm bg-primary/10 px-4 py-2 rounded-xl">
|
|
<CheckCircle2 size={18} /> Unlocked
|
|
</div>
|
|
) : (
|
|
<div className="text-xs font-bold text-muted-foreground bg-secondary px-4 py-2 rounded-xl">
|
|
{reward.streak - currentStreak} days to go
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|