feat: uniform staggered page-load animations across all dashboard pages
This commit is contained in:
@@ -132,7 +132,7 @@ export default function DPPPage() {
|
||||
const cleanTitle = (title: string) => title.replace(/\s*\(ExamEnum\.[^)]+\)/, '');
|
||||
|
||||
return (
|
||||
<div className="p-8 space-y-8 animate-in fade-in zoom-in-95 origin-top duration-500 max-w-7xl mx-auto">
|
||||
<div className="p-8 space-y-6 animate-in fade-in zoom-in-95 origin-top duration-500 max-w-7xl mx-auto">
|
||||
<div className="flex flex-col md:flex-row md:items-center justify-between p-8 bg-gradient-to-r from-secondary/50 to-background border border-border rounded-2xl shadow-sm gap-6">
|
||||
<div>
|
||||
<h1 className="text-3xl md:text-4xl font-black tracking-tight flex items-center gap-3 mb-2">
|
||||
@@ -175,7 +175,7 @@ export default function DPPPage() {
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 animate-section animate-section-2">
|
||||
{loading ? (
|
||||
<div className="col-span-2 text-center py-12 text-muted-foreground">Loading AI-generated DPPs...</div>
|
||||
) : filteredDpps.length === 0 ? (
|
||||
|
||||
@@ -91,7 +91,7 @@ export default function CollegePredictorPage() {
|
||||
return (
|
||||
<div className="p-6 md:p-8 space-y-8 animate-in fade-in zoom-in-95 origin-top duration-500 max-w-7xl mx-auto min-h-screen">
|
||||
{/* Page Header */}
|
||||
<div className="border-b border-border/50 pb-6">
|
||||
<div className="border-b border-border/50 pb-6 animate-section animate-section-1">
|
||||
<div className="flex items-start justify-between gap-4 flex-wrap">
|
||||
<div>
|
||||
<h1 className="text-3xl md:text-4xl font-black tracking-tight flex items-center gap-3 mb-2">
|
||||
@@ -110,7 +110,7 @@ export default function CollegePredictorPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 animate-section animate-section-1">
|
||||
{/* Left Box: Graph */}
|
||||
<Card className="col-span-1 lg:col-span-2 border-border shadow-sm bg-background">
|
||||
<CardHeader>
|
||||
|
||||
@@ -37,7 +37,7 @@ export default function HeatmapPage() {
|
||||
return (
|
||||
<div className="p-6 md:p-8 max-w-7xl mx-auto space-y-8 min-h-screen animate-in fade-in zoom-in-95 origin-top duration-500">
|
||||
{/* Page Header */}
|
||||
<div className="border-b border-border/50 pb-6">
|
||||
<div className="border-b border-border/50 pb-6 animate-section animate-section-1">
|
||||
<div className="flex items-start justify-between gap-4 flex-wrap">
|
||||
<div>
|
||||
<h1 className="text-3xl md:text-4xl font-black tracking-tight flex items-center gap-3 mb-2">
|
||||
@@ -57,7 +57,7 @@ export default function HeatmapPage() {
|
||||
</div>
|
||||
|
||||
{/* KPI Strip */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 animate-section animate-section-1">
|
||||
{summaryStats.map((kpi) => (
|
||||
<Card key={kpi.label} className={`rounded-2xl border ${kpi.color} hover:scale-[1.02] transition-transform`}>
|
||||
<CardContent className="p-5 flex flex-col gap-2">
|
||||
@@ -70,7 +70,7 @@ export default function HeatmapPage() {
|
||||
</div>
|
||||
|
||||
{/* Legend / Guidance Row */}
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4 animate-section animate-section-2">
|
||||
{[
|
||||
{ label: "Critical (> 50%)", color: "bg-red-500/20 border-red-500/30 text-red-400", dot: "bg-red-500", desc: "Immediate revision needed" },
|
||||
{ label: "Warning (30–50%)", color: "bg-amber-500/20 border-amber-500/30 text-amber-400", dot: "bg-amber-500", desc: "Focus on these next" },
|
||||
@@ -90,7 +90,7 @@ export default function HeatmapPage() {
|
||||
</div>
|
||||
|
||||
{/* Main 2-col layout */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8 animate-section animate-section-3">
|
||||
{/* Heatmap — takes 2/3 */}
|
||||
<div className="lg:col-span-2 space-y-3">
|
||||
<div className="flex items-center gap-2 text-sm font-black uppercase tracking-wider text-muted-foreground">
|
||||
|
||||
@@ -92,7 +92,7 @@ export default function AICoachDashboard() {
|
||||
<div className="p-6 max-w-6xl mx-auto space-y-8 animate-in fade-in zoom-in-95 origin-top duration-500">
|
||||
|
||||
{/* ── METRICS BAR ── */}
|
||||
<div className="flex flex-wrap items-center justify-end gap-2 pt-2">
|
||||
<div className="flex flex-wrap items-center justify-end gap-2 pt-2 animate-section animate-section-1">
|
||||
<div className="inline-flex items-center gap-1.5 bg-yellow-500/10 text-yellow-500 px-3 py-1.5 rounded-xl text-xs font-bold border border-yellow-500/20">
|
||||
<Flame size={14} className="fill-yellow-500" />
|
||||
{user?.streak_count || 1}-Day Streak
|
||||
@@ -108,10 +108,10 @@ export default function AICoachDashboard() {
|
||||
</div>
|
||||
|
||||
{/* ── DAILY CHALLENGE BANNER ── */}
|
||||
<DailyChallengeBanner />
|
||||
<div className="animate-section animate-section-2"><DailyChallengeBanner /></div>
|
||||
|
||||
{/* ── QUICK STATS ── */}
|
||||
<section className="grid grid-cols-2 md:grid-cols-5 gap-4">
|
||||
<section className="grid grid-cols-2 md:grid-cols-5 gap-4 animate-section animate-section-3">
|
||||
{[
|
||||
{ label: "Questions Solved", value: questionsSolved, color: "text-[var(--cyan-ai)]", bg: "bg-[var(--cyan-ai-10)]", border: "border-[var(--cyan-ai-20)]", icon: <FileText size={32} /> },
|
||||
{ label: "Overall Accuracy", value: overallAccuracy, color: "text-amber-500", bg: "bg-amber-500/5", border: "border-amber-500/20", icon: <Target size={32} /> },
|
||||
@@ -130,7 +130,7 @@ export default function AICoachDashboard() {
|
||||
</section>
|
||||
|
||||
{/* ── PREDICTIVE INTELLIGENCE ROW ── */}
|
||||
<section className="mt-10">
|
||||
<section className="mt-10 animate-section animate-section-4">
|
||||
<div className="flex items-center justify-between gap-3 mb-6">
|
||||
<div className="flex items-center gap-2 text-base md:text-lg font-black uppercase tracking-widest text-[var(--cyan-ai)]">
|
||||
<Zap size={20} />
|
||||
@@ -152,7 +152,7 @@ export default function AICoachDashboard() {
|
||||
|
||||
|
||||
{/* ── HEATMAP / STUDY PLAN / WEEKLY REPORT ── */}
|
||||
<section className="grid grid-cols-1 lg:grid-cols-2 gap-8 pt-10 border-t border-border">
|
||||
<section className="grid grid-cols-1 lg:grid-cols-2 gap-8 pt-10 border-t border-border animate-section animate-section-5">
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="flex items-center gap-2 text-base font-extrabold">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"use client"
|
||||
"use client"
|
||||
|
||||
import { ReadinessGauge } from "@/components/AI/ReadinessGauge"
|
||||
import { Card, CardContent } from "@/components/ui/card"
|
||||
@@ -34,7 +34,7 @@ export default function ReadinessPage() {
|
||||
return (
|
||||
<div className="p-6 md:p-8 space-y-8 animate-in fade-in zoom-in-95 origin-top duration-500 max-w-7xl mx-auto min-h-screen">
|
||||
{/* Page Header */}
|
||||
<div className="border-b border-border/50 pb-6">
|
||||
<div className="border-b border-border/50 pb-6 animate-section animate-section-1">
|
||||
<div className="flex items-start justify-between gap-4 flex-wrap">
|
||||
<div>
|
||||
<h1 className="text-3xl md:text-4xl font-black tracking-tight flex items-center gap-3 mb-2">
|
||||
@@ -44,7 +44,7 @@ export default function ReadinessPage() {
|
||||
Exam Readiness Analytics
|
||||
</h1>
|
||||
<p className="text-muted-foreground text-base font-medium max-w-2xl pl-1">
|
||||
Real-time algorithmic prediction of your exam readiness — based on speed, accuracy, syllabus coverage, and test history.
|
||||
Real-time algorithmic prediction of your exam readiness <EFBFBD> based on speed, accuracy, syllabus coverage, and test history.
|
||||
</p>
|
||||
</div>
|
||||
<span className="text-xs font-bold uppercase tracking-wider text-[var(--cyan-ai)] bg-cyan-50 dark:bg-[var(--cyan-ai-10)] border border-cyan-300/60 dark:border-[var(--cyan-ai-20)] px-3 py-1.5 rounded-full flex items-center gap-1.5 shrink-0 self-start">
|
||||
@@ -54,11 +54,11 @@ export default function ReadinessPage() {
|
||||
</div>
|
||||
|
||||
{/* KPI Strip */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 animate-section animate-section-1">
|
||||
{[
|
||||
{ label: "Overall Score", value: isDemo ? "72 / 100" : "0 / 100", trend: isDemo ? "+5 this week" : "Baseline", trendUp: true, icon: <Gauge size={20} className="text-[var(--cyan-ai)]" /> },
|
||||
{ label: "Accuracy Rate", value: isDemo ? "88%" : "0%", trend: isDemo ? "+3% vs last week" : "0 attempts", trendUp: isDemo, icon: <Target size={20} className="text-amber-400" /> },
|
||||
{ label: "Avg Speed / Q", value: isDemo ? "45 sec" : "-- sec", trend: isDemo ? "↓ Needs work" : "Not tested", trendUp: false, icon: <Clock size={20} className="text-red-400" /> },
|
||||
{ label: "Avg Speed / Q", value: isDemo ? "45 sec" : "-- sec", trend: isDemo ? "? Needs work" : "Not tested", trendUp: false, icon: <Clock size={20} className="text-red-400" /> },
|
||||
{ label: "Syllabus Covered", value: isDemo ? "64%" : "0%", trend: isDemo ? "+8% this month" : "Day 1", trendUp: true, icon: <BookOpen size={20} className="text-emerald-400" /> },
|
||||
].map((kpi) => (
|
||||
<Card key={kpi.label} className="border-border/60 bg-secondary/10 rounded-2xl hover:border-[var(--cyan-ai-30)] transition-colors">
|
||||
@@ -76,7 +76,7 @@ export default function ReadinessPage() {
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8 animate-section animate-section-2">
|
||||
<div className="lg:col-span-2 space-y-8">
|
||||
{/* Readiness Score Card */}
|
||||
<Card className="p-6 bg-secondary/10 border-border/60 rounded-3xl">
|
||||
@@ -105,7 +105,7 @@ export default function ReadinessPage() {
|
||||
<div key={sub.subject} className="space-y-2">
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-sm font-black">{sub.subject}</span>
|
||||
<span className={`text-xs font-bold ${sub.statusColor}`}>{sub.score}% · {sub.status}</span>
|
||||
<span className={`text-xs font-bold ${sub.statusColor}`}>{sub.score}% <EFBFBD> {sub.status}</span>
|
||||
</div>
|
||||
<div className="h-2.5 w-full bg-secondary rounded-full overflow-hidden">
|
||||
<div className={`h-full ${sub.color} rounded-full transition-all duration-700`} style={{ width: `${sub.score}%` }} />
|
||||
@@ -159,7 +159,7 @@ export default function ReadinessPage() {
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 size={16} className="text-amber-400 shrink-0 mt-0.5" />
|
||||
<span>Maintain daily streak — consistency adds directly to your readiness algorithm.</span>
|
||||
<span>Maintain daily streak <EFBFBD> consistency adds directly to your readiness algorithm.</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-2">
|
||||
<CheckCircle2 size={16} className="text-amber-400 shrink-0 mt-0.5" />
|
||||
@@ -182,7 +182,7 @@ export default function ReadinessPage() {
|
||||
<Link key={a.href} href={a.href}
|
||||
className="flex items-center justify-between p-2.5 rounded-xl border border-border/40 hover:border-[var(--cyan-ai-30)] hover:bg-white/5 transition-all group text-sm font-bold">
|
||||
<span className={a.color}>{a.label}</span>
|
||||
<span className="text-muted-foreground group-hover:translate-x-1 transition-transform">→</span>
|
||||
<span className="text-muted-foreground group-hover:translate-x-1 transition-transform">?</span>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"use client"
|
||||
"use client"
|
||||
|
||||
import { ExamStrategyCard } from "@/components/AI/ExamStrategyCard"
|
||||
import { StudyTimeOptimizationCard } from "@/components/AI/StudyTimeOptimizationCard"
|
||||
@@ -26,7 +26,7 @@ export default function StrategyPage() {
|
||||
return (
|
||||
<div className="p-6 md:p-8 space-y-8 animate-in fade-in zoom-in-95 origin-top duration-500 max-w-7xl mx-auto min-h-screen">
|
||||
{/* Page Header */}
|
||||
<div className="border-b border-border/50 pb-6">
|
||||
<div className="border-b border-border/50 pb-6 animate-section animate-section-1">
|
||||
<div className="flex items-start justify-between gap-4 flex-wrap">
|
||||
<div>
|
||||
<h1 className="text-3xl md:text-4xl font-black tracking-tight flex items-center gap-3 mb-2">
|
||||
@@ -46,10 +46,10 @@ export default function StrategyPage() {
|
||||
</div>
|
||||
|
||||
{/* KPI Strip */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 animate-section animate-section-1">
|
||||
{[
|
||||
{ label: "Time Per Question", value: isDemo ? "45 sec" : "-- sec", note: isDemo ? "Target: 38 sec" : "Not calibrated", icon: <Clock size={18} className="text-red-400" />, color: "border-red-400/20 bg-red-400/5" },
|
||||
{ label: "Negative Marks Lost", value: isDemo ? "−29" : "0", note: isDemo ? "Last mock test" : "0 negative marks", icon: <AlertTriangle size={18} className="text-amber-400" />, color: "border-amber-400/20 bg-amber-400/5" },
|
||||
{ label: "Negative Marks Lost", value: isDemo ? "-29" : "0", note: isDemo ? "Last mock test" : "0 negative marks", icon: <AlertTriangle size={18} className="text-amber-400" />, color: "border-amber-400/20 bg-amber-400/5" },
|
||||
{ label: "Attempted Questions", value: isDemo ? "93 / 120" : "0 / 120", note: isDemo ? "+7 vs last test" : "0 attempted", icon: <Target size={18} className="text-[var(--cyan-ai)]" />, color: "border-[var(--cyan-ai-20)] bg-[var(--cyan-ai-10)]" },
|
||||
{ label: "Score Yield", value: isDemo ? "214" : "0", note: isDemo ? "Predicted score" : "Baseline", icon: <TrendingUp size={18} className="text-green-400" />, color: "border-green-400/20 bg-green-400/5" },
|
||||
].map((kpi) => (
|
||||
@@ -65,7 +65,7 @@ export default function StrategyPage() {
|
||||
</div>
|
||||
|
||||
{/* Main Cards Row */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 animate-section animate-section-2">
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-2 text-sm font-black uppercase tracking-wider text-muted-foreground">
|
||||
<Target size={16} className="text-[var(--cyan-ai)]" />
|
||||
@@ -89,7 +89,7 @@ export default function StrategyPage() {
|
||||
<AlertTriangle size={20} className="text-amber-400" />
|
||||
<h3 className="text-lg font-extrabold tracking-tight">Negative Marking Analysis</h3>
|
||||
<span className="ml-auto text-xs text-muted-foreground font-semibold">
|
||||
{isDemo ? "Last Mock Test · Aug 1" : "Day 1 Initializing"}
|
||||
{isDemo ? "Last Mock Test <EFBFBD> Aug 1" : "Day 1 Initializing"}
|
||||
</span>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
@@ -101,7 +101,7 @@ export default function StrategyPage() {
|
||||
<div className="text-[11px] text-muted-foreground font-semibold">Attempted</div>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<div className="text-sm font-black text-red-400">{row.negMarks === 0 ? "0" : `−${row.negMarks}`}</div>
|
||||
<div className="text-sm font-black text-red-400">{row.negMarks === 0 ? "0" : `-${row.negMarks}`}</div>
|
||||
<div className="text-[11px] text-muted-foreground font-semibold">Neg. Marks</div>
|
||||
</div>
|
||||
<div>
|
||||
@@ -123,7 +123,7 @@ export default function StrategyPage() {
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground mt-4 font-medium pl-1">
|
||||
<span className="font-black text-amber-400">Insight:</span> {isDemo ? (
|
||||
<>You're losing ~29 marks to wrong answers. Switching to "skip and revisit" for uncertain Physics questions could recover 15–20 marks.</>
|
||||
<>You're losing ~29 marks to wrong answers. Switching to "skip and revisit" for uncertain Physics questions could recover 15<EFBFBD>20 marks.</>
|
||||
) : (
|
||||
<>Complete your first mock test or timed DPP to calculate your negative marking profile and fine-tune your subject pacing.</>
|
||||
)}
|
||||
@@ -136,11 +136,11 @@ export default function StrategyPage() {
|
||||
<Lightbulb size={20} className="text-yellow-400" />
|
||||
<h3 className="text-base font-black tracking-tight">AI Strategy Recommendations</h3>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 animate-section animate-section-3">
|
||||
{[
|
||||
{ tip: "Start with Chemistry (highest accuracy) to build momentum and bank easy marks in the first 40 minutes.", color: "text-green-400", label: "Opening Strategy" },
|
||||
{ tip: "Attempt Physics only if you are 80%+ confident per question. Otherwise, mark and skip — recover in last 10 min buffer.", color: "text-red-400", label: "Physics Guard" },
|
||||
{ tip: "Reserve 10–15 minutes at the end to revisit starred questions. Your review accuracy is historically 20% higher than first-attempt.", color: "text-[var(--cyan-ai)]", label: "Review Window" },
|
||||
{ tip: "Attempt Physics only if you are 80%+ confident per question. Otherwise, mark and skip <EFBFBD> recover in last 10 min buffer.", color: "text-red-400", label: "Physics Guard" },
|
||||
{ tip: "Reserve 10<EFBFBD>15 minutes at the end to revisit starred questions. Your review accuracy is historically 20% higher than first-attempt.", color: "text-[var(--cyan-ai)]", label: "Review Window" },
|
||||
].map((s) => (
|
||||
<div key={s.label} className="p-4 rounded-xl bg-white/5 border border-border/30 space-y-2">
|
||||
<div className={`text-xs font-black uppercase tracking-wider ${s.color}`}>{s.label}</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"use client"
|
||||
"use client"
|
||||
|
||||
import { StudyPlanCard } from "@/components/AI/StudyPlanCard"
|
||||
import { WeeklyProgressReport } from "@/components/AI/WeeklyProgressReport"
|
||||
@@ -14,7 +14,7 @@ export default function StudyPlanPage() {
|
||||
return (
|
||||
<div className="p-6 md:p-8 space-y-8 animate-in fade-in zoom-in-95 origin-top duration-500 max-w-7xl mx-auto min-h-screen">
|
||||
{/* Page Header */}
|
||||
<div className="border-b border-border/50 pb-6">
|
||||
<div className="border-b border-border/50 pb-6 animate-section animate-section-1">
|
||||
<div className="flex items-start justify-between gap-4 flex-wrap">
|
||||
<div>
|
||||
<h1 className="text-3xl md:text-4xl font-black tracking-tight flex items-center gap-3 mb-2">
|
||||
@@ -34,7 +34,7 @@ export default function StudyPlanPage() {
|
||||
</div>
|
||||
|
||||
{/* KPI Strip */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 animate-section animate-section-1">
|
||||
{[
|
||||
{ label: "Tasks Today", value: isDemo ? "5" : "3", sub: isDemo ? "2 completed" : "0 completed", icon: <CheckCircle2 size={18} className="text-green-400" />, color: "border-green-400/20 bg-green-400/5" },
|
||||
{ label: "Study Hours Today", value: isDemo ? "3.8 hrs" : "0.0 hrs", sub: "Target: 5 hrs", icon: <Clock size={18} className="text-[var(--cyan-ai)]" />, color: "border-[var(--cyan-ai-20)] bg-[var(--cyan-ai-10)]" },
|
||||
@@ -52,7 +52,7 @@ export default function StudyPlanPage() {
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8 animate-section animate-section-2">
|
||||
<div className="lg:col-span-2 space-y-8">
|
||||
{/* Today's Schedule */}
|
||||
<div className="space-y-3">
|
||||
@@ -139,9 +139,9 @@ export default function StudyPlanPage() {
|
||||
<h3 className="text-sm font-black uppercase tracking-wider text-muted-foreground">Upcoming This Week</h3>
|
||||
<div className="space-y-2">
|
||||
{[
|
||||
{ day: "Tue, Aug 5", task: "Full-Length Mock — JEE Mains Pattern", type: "Test", color: "text-purple-400 bg-purple-400/10" },
|
||||
{ day: "Tue, Aug 5", task: "Full-Length Mock <EFBFBD> JEE Mains Pattern", type: "Test", color: "text-purple-400 bg-purple-400/10" },
|
||||
{ day: "Wed, Aug 6", task: "Rotational Dynamics Intensive Drill", type: "Practice", color: "text-[var(--cyan-ai)] bg-[var(--cyan-ai-10)]" },
|
||||
{ day: "Thu, Aug 7", task: "Chemistry Organic — Mistake Review", type: "Review", color: "text-red-400 bg-red-400/10" },
|
||||
{ day: "Thu, Aug 7", task: "Chemistry Organic <EFBFBD> Mistake Review", type: "Review", color: "text-red-400 bg-red-400/10" },
|
||||
{ day: "Fri, Aug 8", task: "Optics + Wave Optics Revision", type: "Revision", color: "text-yellow-400 bg-yellow-400/10" },
|
||||
].map((item) => (
|
||||
<div key={item.day} className="flex items-center gap-2 p-2.5 rounded-lg bg-white/5 border border-border/30">
|
||||
|
||||
@@ -71,7 +71,7 @@ export default function TimeOptimizationPage() {
|
||||
return (
|
||||
<div className="p-6 md:p-8 max-w-7xl mx-auto space-y-8 min-h-screen animate-in fade-in zoom-in-95 origin-top duration-500">
|
||||
{/* Page Header */}
|
||||
<div className="border-b border-border/50 pb-6">
|
||||
<div className="border-b border-border/50 pb-6 animate-section animate-section-1">
|
||||
<div className="flex items-start justify-between gap-4 flex-wrap">
|
||||
<div>
|
||||
<h1 className="text-3xl md:text-4xl font-black tracking-tight flex items-center gap-3 mb-2">
|
||||
@@ -91,7 +91,7 @@ export default function TimeOptimizationPage() {
|
||||
</div>
|
||||
|
||||
{/* KPI Strip */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 animate-section animate-section-1">
|
||||
{[
|
||||
{ label: "Peak Efficiency", value: isDemo ? "95%" : "--", sub: isDemo ? "2–5 PM window" : "Calibrating", color: "border-yellow-400/20 bg-yellow-400/5", icon: <Sun size={18} className="text-yellow-400" /> },
|
||||
{ label: "Study Hrs Today", value: isDemo ? "3.8 h" : "0.0 h", sub: "Target: 6 h", color: "border-[var(--cyan-ai-20)] bg-[var(--cyan-ai-10)]", icon: <Clock size={18} className="text-[var(--cyan-ai)]" /> },
|
||||
@@ -109,7 +109,7 @@ export default function TimeOptimizationPage() {
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8 animate-section animate-section-2">
|
||||
{/* Chart — 2/3 */}
|
||||
<div className="lg:col-span-2 space-y-4">
|
||||
<div className="flex items-center gap-2 text-sm font-black uppercase tracking-wider text-muted-foreground">
|
||||
|
||||
@@ -43,7 +43,7 @@ export default function WeeklyProgressPage() {
|
||||
return (
|
||||
<div className="p-6 md:p-8 max-w-7xl mx-auto space-y-8 min-h-screen animate-in fade-in zoom-in-95 origin-top duration-500">
|
||||
{/* Page Header */}
|
||||
<div className="border-b border-border/50 pb-6">
|
||||
<div className="border-b border-border/50 pb-6 animate-section animate-section-1">
|
||||
<div className="flex items-start justify-between gap-4 flex-wrap">
|
||||
<div>
|
||||
<h1 className="text-3xl md:text-4xl font-black tracking-tight flex items-center gap-3 mb-1">
|
||||
@@ -65,7 +65,7 @@ export default function WeeklyProgressPage() {
|
||||
</div>
|
||||
|
||||
{/* KPI Stats Row */}
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4 animate-section animate-section-1">
|
||||
{stats.map((stat) => (
|
||||
<Card key={stat.label} className="bg-secondary/20 border-border/60 rounded-2xl">
|
||||
<CardContent className="p-4">
|
||||
@@ -80,7 +80,7 @@ export default function WeeklyProgressPage() {
|
||||
</div>
|
||||
|
||||
{/* Main Content Grid */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8 animate-section animate-section-2">
|
||||
{/* Weekly Bar Chart */}
|
||||
<div className="lg:col-span-2 space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
|
||||
@@ -59,7 +59,7 @@ export default function AnalyticsPage() {
|
||||
return (
|
||||
<PremiumGate featureKey="detailed-performance-analytics">
|
||||
<div className="p-8 space-y-8 animate-in fade-in zoom-in-95 origin-top duration-500 max-w-7xl mx-auto">
|
||||
<div className="flex flex-col md:flex-row md:items-end justify-between gap-4">
|
||||
<div className="flex flex-col md:flex-row md:items-end justify-between gap-4 animate-section animate-section-1">
|
||||
<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)]">
|
||||
@@ -73,7 +73,7 @@ export default function AnalyticsPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 animate-section animate-section-2">
|
||||
<Card className="col-span-1 lg:col-span-2 border-border shadow-sm bg-background">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2"><LineChartIcon className="text-[var(--cyan-ai)]" size={20} /> Projected Score Trajectory</CardTitle>
|
||||
|
||||
@@ -164,7 +164,7 @@ export default function CBTPage() {
|
||||
return (
|
||||
<>
|
||||
<div className="p-8 space-y-8 animate-in fade-in zoom-in-95 origin-top duration-500 max-w-7xl mx-auto">
|
||||
<div className="flex flex-col md:flex-row md:items-end justify-between gap-4">
|
||||
<div className="flex flex-col md:flex-row md:items-end justify-between gap-4 animate-section animate-section-1">
|
||||
<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)]">
|
||||
@@ -198,7 +198,7 @@ export default function CBTPage() {
|
||||
|
||||
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="space-y-6 animate-section animate-section-2">
|
||||
<h2 className="text-xl font-bold border-b border-border pb-2 flex items-center gap-2">
|
||||
<Clock size={20} className="text-[var(--cyan-ai)]" /> Upcoming & Active Exams
|
||||
</h2>
|
||||
@@ -334,7 +334,7 @@ export default function CBTPage() {
|
||||
})()}
|
||||
</div>
|
||||
|
||||
<div className="space-y-6 pt-8">
|
||||
<div className="space-y-6 pt-8 animate-section animate-section-3">
|
||||
<h2 className="text-xl font-bold border-b border-border pb-2 flex items-center gap-2">
|
||||
<Activity size={20} className="text-muted-foreground" /> Past Attempts
|
||||
</h2>
|
||||
|
||||
@@ -116,7 +116,7 @@ export default function CommunityPage() {
|
||||
return (
|
||||
<PremiumGate featureKey="doubt-resolution-community">
|
||||
<div className="p-8 space-y-6 animate-in fade-in zoom-in-95 origin-top 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 className="flex flex-col md:flex-row justify-between items-start md:items-end gap-4 animate-section animate-section-1">
|
||||
<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)]">
|
||||
@@ -136,7 +136,7 @@ export default function CommunityPage() {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col md:flex-row gap-4">
|
||||
<div className="flex flex-col md:flex-row gap-4 animate-section animate-section-2">
|
||||
<div className="relative flex-1">
|
||||
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
<Search size={18} className="text-muted-foreground" />
|
||||
@@ -158,7 +158,7 @@ export default function CommunityPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-4 animate-section animate-section-3">
|
||||
{filteredDoubts.map((doubt) => (
|
||||
<Card key={doubt.id} className="border-border shadow-sm hover:border-[var(--cyan-ai-10)] transition-colors cursor-pointer">
|
||||
<CardContent className="p-0 flex">
|
||||
|
||||
@@ -136,7 +136,7 @@ export default function AIDailyPracticePage() {
|
||||
return (
|
||||
<div className="p-6 md:p-8 space-y-6 animate-in fade-in zoom-in-95 origin-top duration-500 max-w-7xl mx-auto">
|
||||
{/* Page Header */}
|
||||
<div className="border-b border-border/50 pb-6">
|
||||
<div className="border-b border-border/50 pb-6 animate-section animate-section-1">
|
||||
<div className="flex items-start justify-between gap-4 flex-wrap">
|
||||
<div>
|
||||
<h1 className="text-3xl md:text-4xl font-black tracking-tight flex items-center gap-3 mb-1">
|
||||
@@ -152,7 +152,7 @@ export default function AIDailyPracticePage() {
|
||||
</div>
|
||||
|
||||
{/* Calendar Filter & Controller Bar */}
|
||||
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 border-b border-border pb-6">
|
||||
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 border-b border-border pb-6 animate-section animate-section-2">
|
||||
<div className="flex items-center gap-3 flex-wrap">
|
||||
<CalendarFilter
|
||||
selectedDate={dateFilter}
|
||||
@@ -171,7 +171,7 @@ export default function AIDailyPracticePage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 animate-section animate-section-3">
|
||||
{loading ? (
|
||||
<div className="col-span-2 text-center py-12 text-muted-foreground">Loading AI-generated DPPs...</div>
|
||||
) : filteredDpps.length === 0 ? (
|
||||
|
||||
@@ -47,7 +47,7 @@ export default function LeaderboardPage() {
|
||||
|
||||
return (
|
||||
<div className="p-8 space-y-8 animate-in fade-in zoom-in-95 origin-top 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 className="flex flex-col md:flex-row justify-between items-start md:items-end gap-4 animate-section animate-section-1">
|
||||
<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)]">
|
||||
@@ -82,7 +82,7 @@ export default function LeaderboardPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 animate-section animate-section-2">
|
||||
<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>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"use client"
|
||||
"use client"
|
||||
|
||||
import { useState } from "react"
|
||||
import { Card, CardContent } from "@/components/ui/card"
|
||||
@@ -33,7 +33,7 @@ export default function MaterialsPage() {
|
||||
|
||||
return (
|
||||
<div className="p-8 space-y-8 animate-in fade-in zoom-in-95 origin-top duration-500 max-w-7xl mx-auto">
|
||||
<div className="flex flex-col md:flex-row md:items-end justify-between gap-4">
|
||||
<div className="flex flex-col md:flex-row md:items-end justify-between gap-4 animate-section animate-section-1">
|
||||
<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)]">
|
||||
@@ -47,7 +47,7 @@ export default function MaterialsPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 animate-section animate-section-2">
|
||||
{folders.map((folder, i) => (
|
||||
<Card key={i} className="hover:bg-secondary/50 cursor-pointer transition-colors border-border shadow-none">
|
||||
<CardContent className="p-6">
|
||||
@@ -59,7 +59,7 @@ export default function MaterialsPage() {
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="pt-6">
|
||||
<div className="pt-6 animate-section animate-section-3">
|
||||
<h2 className="text-xl font-bold border-b border-border pb-2 mb-4 flex items-center justify-between">
|
||||
<span>Recently Accessed Study Notes</span>
|
||||
<span className="text-xs font-normal text-muted-foreground">Click any PDF to launch fullscreen reader</span>
|
||||
|
||||
@@ -124,7 +124,7 @@ export default function DashboardPage() {
|
||||
<div className="p-8 max-w-7xl mx-auto space-y-10 animate-in fade-in zoom-in-95 origin-top duration-500">
|
||||
|
||||
{/* Basic Performance Dashboard (F-05) */}
|
||||
<section>
|
||||
<section className="animate-section animate-section-1">
|
||||
<div className="flex flex-col md:flex-row md:items-end justify-between gap-4 mb-8">
|
||||
<div>
|
||||
<h1 className="text-3xl md:text-4xl font-black tracking-tight flex items-center gap-3 mb-1">
|
||||
@@ -228,7 +228,7 @@ export default function DashboardPage() {
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8 mb-8">
|
||||
{/* Dynamic DPP Modules */}
|
||||
<section>
|
||||
<section className="animate-section animate-section-2">
|
||||
<div className="flex items-center justify-between mb-5">
|
||||
<h2 className="text-xl font-semibold tracking-tight flex items-center gap-2">
|
||||
Today's DPPs
|
||||
@@ -264,7 +264,7 @@ export default function DashboardPage() {
|
||||
</section>
|
||||
|
||||
{/* Upcoming CBTs */}
|
||||
<section>
|
||||
<section className="animate-section animate-section-3">
|
||||
<div className="flex items-center justify-between mb-5">
|
||||
<h2 className="text-xl font-semibold tracking-tight flex items-center gap-2">
|
||||
Upcoming CBTs
|
||||
@@ -301,10 +301,10 @@ export default function DashboardPage() {
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
<section>
|
||||
<section className="animate-section animate-section-4">
|
||||
<AdaptiveStudyPlan />
|
||||
</section>
|
||||
<section>
|
||||
<section className="animate-section animate-section-5">
|
||||
<MistakeHeatmap />
|
||||
</section>
|
||||
</div>
|
||||
|
||||
@@ -262,7 +262,7 @@ export default function ProgressTrackerPage() {
|
||||
return (
|
||||
<PremiumGate featureKey="chapter-wise-progress-tracking">
|
||||
<div className="p-8 space-y-8 animate-in fade-in zoom-in-95 origin-top duration-500 max-w-7xl mx-auto">
|
||||
<div className="flex flex-col md:flex-row md:items-end justify-between gap-4">
|
||||
<div className="flex flex-col md:flex-row md:items-end justify-between gap-4 animate-section animate-section-1">
|
||||
<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)]">
|
||||
@@ -276,7 +276,7 @@ export default function ProgressTrackerPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={`grid grid-cols-1 sm:grid-cols-2 ${subjects.length >= 4 ? 'lg:grid-cols-4' : 'md:grid-cols-3'} gap-6 mb-8`}>
|
||||
<div className={`grid grid-cols-1 sm:grid-cols-2 ${subjects.length >= 4 ? 'lg:grid-cols-4' : 'md:grid-cols-3'} gap-6 mb-8 animate-section animate-section-2`}>
|
||||
{subjects.map((sub, i) => (
|
||||
<Card key={i} className="border-border shadow-sm">
|
||||
<CardContent className="p-6 text-center">
|
||||
@@ -290,7 +290,7 @@ export default function ProgressTrackerPage() {
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="space-y-6 animate-section animate-section-3">
|
||||
{subjects.map((subject, subIndex) => (
|
||||
<Card key={subIndex} className="border-border shadow-sm overflow-hidden">
|
||||
<CardHeader className="bg-secondary/30 pb-4 border-b border-border">
|
||||
|
||||
@@ -108,7 +108,7 @@ export default function SettingsPage() {
|
||||
|
||||
return (
|
||||
<div className="p-8 space-y-8 animate-in fade-in zoom-in-95 origin-top duration-500 max-w-7xl mx-auto">
|
||||
<div className="flex flex-col md:flex-row md:items-end justify-between gap-4">
|
||||
<div className="flex flex-col md:flex-row md:items-end justify-between gap-4 animate-section animate-section-1">
|
||||
<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)]">
|
||||
@@ -122,7 +122,7 @@ export default function SettingsPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 animate-section animate-section-2">
|
||||
{/* Profile photo upload tile */}
|
||||
<Card className="border-border bg-background shadow-sm md:col-span-1 h-fit">
|
||||
<CardHeader>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"use client"
|
||||
"use client"
|
||||
|
||||
import { fetchWithAuth } from "@/lib/apiClient";
|
||||
import { useState, useEffect } from "react"
|
||||
@@ -47,7 +47,7 @@ export default function StreaksPage() {
|
||||
|
||||
return (
|
||||
<div className="p-8 space-y-8 animate-in fade-in zoom-in-95 origin-top duration-500 max-w-7xl mx-auto">
|
||||
<div className="flex flex-col md:flex-row md:items-end justify-between gap-4">
|
||||
<div className="flex flex-col md:flex-row md:items-end justify-between gap-4 animate-section animate-section-1">
|
||||
<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)]">
|
||||
@@ -61,7 +61,7 @@ export default function StreaksPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 animate-section animate-section-2">
|
||||
<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" />
|
||||
@@ -106,7 +106,7 @@ export default function StreaksPage() {
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="pt-8">
|
||||
<div className="pt-8 animate-section animate-section-3">
|
||||
<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) => (
|
||||
|
||||
@@ -121,6 +121,20 @@ body {
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
/* ─── Uniform staggered section entry animations ─── */
|
||||
/* Usage: add animate-section to each section/card group */
|
||||
.animate-section {
|
||||
animation: sectionFadeUp 0.45s cubic-bezier(0.16, 1, 0.3, 1) both;
|
||||
}
|
||||
.animate-section-1 { animation-delay: 0ms; }
|
||||
.animate-section-2 { animation-delay: 80ms; }
|
||||
.animate-section-3 { animation-delay: 160ms; }
|
||||
.animate-section-4 { animation-delay: 240ms; }
|
||||
.animate-section-5 { animation-delay: 320ms; }
|
||||
.animate-section-6 { animation-delay: 400ms; }
|
||||
|
||||
|
||||
|
||||
.bg-grid {
|
||||
background-size: 40px 40px;
|
||||
background-image: linear-gradient(to right, rgba(255, 255, 255, 0.03) 1px, transparent 1px),
|
||||
@@ -173,3 +187,14 @@ body {
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--muted-foreground);
|
||||
}
|
||||
|
||||
@keyframes sectionFadeUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(18px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user