Files
pragativeer-v2/src/app/dashboard/streaks/page.tsx

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>
)
}