feat: complete comprehensive zero-state metrics across all dashboard pages for new accounts

This commit is contained in:
2026-09-13 22:17:40 +05:30
parent d9bd142e6a
commit afab06d33c
22 changed files with 771 additions and 299 deletions

View File

@@ -5,6 +5,8 @@ import { Card, CardContent } from "@/components/ui/card"
import { GraduationCap, Building2, Award, CheckCircle2, TrendingUp, MapPin, Users, BarChart2 } from "lucide-react"
import Link from "next/link"
import { useActiveUser, isDemoUser } from "@/hooks/useActiveUser"
const CUTOFF_TRENDS = [
{ year: "2021", rank: 3800 },
{ year: "2022", rank: 3500 },
@@ -14,6 +16,8 @@ const CUTOFF_TRENDS = [
]
export default function CollegePredictorPage() {
const { userId } = useActiveUser()
const isDemo = isDemoUser(userId)
const maxRank = Math.max(...CUTOFF_TRENDS.map(d => d.rank))
return (
@@ -41,10 +45,10 @@ export default function CollegePredictorPage() {
{/* Summary KPI Strip */}
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{[
{ label: "Predicted Rank", value: "~4,200", icon: <TrendingUp size={18} className="text-[#00F0FF]" />, color: "border-[#00F0FF]/20 bg-[#00F0FF]/5" },
{ label: "Target Colleges", value: "12", icon: <Building2 size={18} className="text-amber-400" />, color: "border-amber-400/20 bg-amber-400/5" },
{ label: "Reach Colleges", value: "5", icon: <MapPin size={18} className="text-purple-400" />, color: "border-purple-400/20 bg-purple-400/5" },
{ label: "Safe Colleges", value: "18", icon: <CheckCircle2 size={18} className="text-green-400" />, color: "border-green-400/20 bg-green-400/5" },
{ label: "Predicted Rank", value: isDemo ? "~4,200" : "--", icon: <TrendingUp size={18} className="text-[#00F0FF]" />, color: "border-[#00F0FF]/20 bg-[#00F0FF]/5" },
{ label: "Target Colleges", value: isDemo ? "12" : "0", icon: <Building2 size={18} className="text-amber-400" />, color: "border-amber-400/20 bg-amber-400/5" },
{ label: "Reach Colleges", value: isDemo ? "5" : "0", icon: <MapPin size={18} className="text-purple-400" />, color: "border-purple-400/20 bg-purple-400/5" },
{ label: "Safe Colleges", value: isDemo ? "18" : "0", icon: <CheckCircle2 size={18} className="text-green-400" />, color: "border-green-400/20 bg-green-400/5" },
].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">

View File

@@ -1,11 +1,12 @@
"use client"
import { MistakeHeatmap } from "@/components/AI/MistakeHeatmap"
import { Flame, AlertTriangle, TrendingDown, BookOpen, ArrowRight, BarChart2, Target } from "lucide-react"
import { Flame, AlertTriangle, TrendingDown, BookOpen, ArrowRight, BarChart2, Target, CheckCircle2 } from "lucide-react"
import { Card, CardContent } from "@/components/ui/card"
import Link from "next/link"
import { useActiveUser, isDemoUser } from "@/hooks/useActiveUser"
const CRITICAL_TOPICS = [
const DEMO_CRITICAL_TOPICS = [
{ name: "Rotational Dynamics", rate: "80%", rateNum: 80, subject: "Physics" },
{ name: "Optics", rate: "60%", rateNum: 60, subject: "Physics" },
{ name: "Electromagnetism", rate: "30%", rateNum: 30, subject: "Physics" },
@@ -13,14 +14,26 @@ const CRITICAL_TOPICS = [
{ name: "Thermodynamics", rate: "25%", rateNum: 25, subject: "Physics" },
]
const SUMMARY_STATS = [
const DEMO_SUMMARY_STATS = [
{ label: "Total Errors (Last 5 Tests)", value: "87", icon: <AlertTriangle size={18} className="text-red-400" />, color: "border-red-400/20 bg-red-400/5" },
{ label: "Critical Topics", value: "4", icon: <Flame size={18} className="text-orange-400" />, color: "border-orange-400/20 bg-orange-400/5" },
{ label: "Improving Topics", value: "3", icon: <TrendingDown size={18} className="text-green-400" />, color: "border-green-400/20 bg-green-400/5" },
{ label: "Avg Error Rate", value: "38%", icon: <Target size={18} className="text-amber-400" />, color: "border-amber-400/20 bg-amber-400/5" },
]
const ZERO_SUMMARY_STATS = [
{ label: "Total Errors (Last 5 Tests)", value: "0", icon: <AlertTriangle size={18} className="text-muted-foreground" />, color: "border-white/10 bg-white/5" },
{ label: "Critical Topics", value: "0", icon: <Flame size={18} className="text-muted-foreground" />, color: "border-white/10 bg-white/5" },
{ label: "Improving Topics", value: "0", icon: <TrendingDown size={18} className="text-muted-foreground" />, color: "border-white/10 bg-white/5" },
{ label: "Avg Error Rate", value: "0%", icon: <Target size={18} className="text-green-400" />, color: "border-green-400/20 bg-green-400/5" },
]
export default function HeatmapPage() {
const { userId } = useActiveUser()
const isDemo = isDemoUser(userId)
const summaryStats = isDemo ? DEMO_SUMMARY_STATS : ZERO_SUMMARY_STATS
const criticalTopics = isDemo ? DEMO_CRITICAL_TOPICS : []
return (
<div className="p-6 md:p-8 max-w-7xl mx-auto space-y-8 animate-in fade-in duration-500 min-h-screen">
{/* Page Header */}
@@ -45,7 +58,7 @@ export default function HeatmapPage() {
{/* KPI Strip */}
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{SUMMARY_STATS.map((kpi) => (
{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">
{kpi.icon}
@@ -96,25 +109,35 @@ export default function HeatmapPage() {
<BarChart2 size={15} className="text-red-400" /> Priority Topics by Error Rate
</h3>
<div className="space-y-3">
{CRITICAL_TOPICS.map((t) => (
<div key={t.name}>
<div className="flex items-center justify-between mb-1">
<div>
<p className="text-xs font-bold leading-tight">{t.name}</p>
<p className="text-[11px] text-muted-foreground">{t.subject}</p>
</div>
<span className={`text-xs font-black px-2 py-0.5 rounded-md ${t.rateNum >= 50 ? "text-red-400 bg-red-400/10" : t.rateNum >= 30 ? "text-amber-400 bg-amber-400/10" : "text-yellow-400 bg-yellow-400/10"}`}>
{t.rate}
</span>
</div>
<div className="h-1.5 w-full bg-secondary rounded-full overflow-hidden">
<div
className={`h-full rounded-full ${t.rateNum >= 50 ? "bg-red-500" : t.rateNum >= 30 ? "bg-amber-500" : "bg-yellow-500"}`}
style={{ width: `${t.rateNum}%` }}
/>
</div>
{criticalTopics.length === 0 ? (
<div className="p-5 rounded-xl bg-white/5 border border-border/40 text-center space-y-1.5">
<CheckCircle2 size={24} className="mx-auto text-green-400 opacity-80" />
<p className="text-xs font-bold text-foreground">Clean Error Log</p>
<p className="text-[11px] text-muted-foreground">
No mistake hotspots detected yet. Practice questions to see priority areas here.
</p>
</div>
))}
) : (
criticalTopics.map((t) => (
<div key={t.name}>
<div className="flex items-center justify-between mb-1">
<div>
<p className="text-xs font-bold leading-tight">{t.name}</p>
<p className="text-[11px] text-muted-foreground">{t.subject}</p>
</div>
<span className={`text-xs font-black px-2 py-0.5 rounded-md ${t.rateNum >= 50 ? "text-red-400 bg-red-400/10" : t.rateNum >= 30 ? "text-amber-400 bg-amber-400/10" : "text-yellow-400 bg-yellow-400/10"}`}>
{t.rate}
</span>
</div>
<div className="h-1.5 w-full bg-secondary rounded-full overflow-hidden">
<div
className={`h-full rounded-full ${t.rateNum >= 50 ? "bg-red-500" : t.rateNum >= 30 ? "bg-amber-500" : "bg-yellow-500"}`}
style={{ width: `${t.rateNum}%` }}
/>
</div>
</div>
))
)}
</div>
</CardContent>
</Card>

View File

@@ -54,6 +54,7 @@ export default function MistakeTrackingPage() {
const completedCount = Math.min(currentIndex, mistakes.length)
const progressPct = mistakes.length > 0 ? Math.round((completedCount / mistakes.length) * 100) : 0
const avgErrorRate = mistakes.length > 0 ? "38%" : "0%"
return (
<div className="p-6 md:p-8 space-y-8 max-w-7xl mx-auto animate-in fade-in duration-500 min-h-screen">
@@ -84,7 +85,7 @@ export default function MistakeTrackingPage() {
{ label: "In Today's Queue", value: mistakes.length.toString(), icon: <Flame size={18} className="text-amber-400" />, color: "border-amber-400/20 bg-amber-400/5" },
{ label: "Reviewed This Session", value: completedCount.toString(), icon: <CheckCircle2 size={18} className="text-green-400" />, color: "border-green-400/20 bg-green-400/5" },
{ label: "Session Progress", value: `${progressPct}%`, icon: <Target size={18} className="text-[#00F0FF]" />, color: "border-[#00F0FF]/20 bg-[#00F0FF]/5" },
{ label: "Avg Error Rate", value: "38%", icon: <TrendingDown size={18} className="text-red-400" />, color: "border-red-400/20 bg-red-400/5" },
{ label: "Avg Error Rate", value: avgErrorRate, icon: <TrendingDown size={18} className="text-red-400" />, color: "border-red-400/20 bg-red-400/5" },
].map(kpi => (
<Card key={kpi.label} className={`rounded-2xl border ${kpi.color}`}>
<CardContent className="p-4 flex flex-col gap-2">

View File

@@ -16,7 +16,7 @@ import { WeeklyProgressReport } from "@/components/AI/WeeklyProgressReport"
import { Card, CardContent } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { BrainCircuit, RefreshCw, CheckCircle2, Flame, Trophy, Target, Zap, BookOpen, Bot, FileText } from "lucide-react"
import { useActiveUser } from "@/hooks/useActiveUser"
import { useActiveUser, isDemoUser } from "@/hooks/useActiveUser"
import Link from "next/link"
@@ -36,8 +36,10 @@ export default function AICoachDashboard() {
const [mistakes, setMistakes] = useState<MistakePayload[]>([])
const [currentIndex, setCurrentIndex] = useState(0)
const [loading, setLoading] = useState(true)
const [stats, setStats] = useState<any>(null)
const { userId, user, hasAccessTo, loading: userLoading } = useActiveUser();
const isDemo = isDemoUser(userId);
const fetchMistakes = async () => {
if (!userId) return
@@ -58,6 +60,10 @@ export default function AICoachDashboard() {
useEffect(() => {
if (!userLoading && userId) {
fetchMistakes()
fetch(`/api/users/${userId}/stats`)
.then(res => res.ok ? res.json() : null)
.then(data => setStats(data))
.catch(() => setStats(null))
}
}, [userId, userLoading])
@@ -114,9 +120,11 @@ export default function AICoachDashboard() {
}
const daysLeft = getDaysToExam(user?.target_exam || "NEET")
const physicsErrors = mistakes.filter(m => m.question.subject === "Physics").length
const chemErrors = mistakes.filter(m => m.question.subject === "Chemistry").length
const mathErrors = mistakes.filter(m => m.question.subject === "Maths" || m.question.subject === "Mathematics").length
const questionsSolved = isDemo ? "1,247" : (stats?.total_questions_attempted ?? 0).toLocaleString()
const overallAccuracy = isDemo ? "74%" : `${stats?.overall_accuracy ?? 0}%`
const mistakesMastered = isDemo ? 38 : 0
const aiSessions = isDemo ? 124 : 0
const currentStreakDays = `${user?.streak_count || (isDemo ? 12 : 1)} d`
return (
<div className="p-6 max-w-6xl mx-auto space-y-8 animate-in fade-in duration-500">
@@ -125,7 +133,7 @@ export default function AICoachDashboard() {
<div className="flex flex-wrap items-center justify-end gap-2 pt-2">
<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 || 0}-Day Streak
{user?.streak_count || 1}-Day Streak
</div>
<div className="inline-flex items-center gap-1.5 bg-red-500/10 text-red-400 px-3 py-1.5 rounded-xl text-xs font-bold border border-red-500/20">
<Target size={13} />
@@ -143,11 +151,11 @@ export default function AICoachDashboard() {
{/* ── QUICK STATS ── */}
<section className="grid grid-cols-2 md:grid-cols-5 gap-4">
{[
{ label: "Questions Solved", value: "1,247", color: "text-[#00F0FF]", bg: "bg-[#00F0FF]/5", border: "border-[#00F0FF]/20", icon: <FileText size={32} /> },
{ label: "Overall Accuracy", value: "74%", color: "text-amber-500", bg: "bg-amber-500/5", border: "border-amber-500/20", icon: <Target size={32} /> },
{ label: "Current Streak", value: `${user?.streak_count || 0} d`, color: "text-yellow-400", bg: "bg-yellow-400/5", border: "border-yellow-400/20", icon: <Flame size={32} className="fill-yellow-400" /> },
{ label: "Mistakes Mastered", value: 38, color: "text-green-400", bg: "bg-green-400/5", border: "border-green-400/20", icon: <CheckCircle2 size={32} /> },
{ label: "AI Sessions", value: 124, color: "text-purple-400", bg: "bg-purple-400/5", border: "border-purple-400/20", icon: <Bot size={32} /> },
{ label: "Questions Solved", value: questionsSolved, color: "text-[#00F0FF]", bg: "bg-[#00F0FF]/5", border: "border-[#00F0FF]/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} /> },
{ label: "Current Streak", value: currentStreakDays, color: "text-yellow-400", bg: "bg-yellow-400/5", border: "border-yellow-400/20", icon: <Flame size={32} className="fill-yellow-400" /> },
{ label: "Mistakes Mastered", value: mistakesMastered, color: "text-green-400", bg: "bg-green-400/5", border: "border-green-400/20", icon: <CheckCircle2 size={32} /> },
{ label: "AI Sessions", value: aiSessions, color: "text-purple-400", bg: "bg-purple-400/5", border: "border-purple-400/20", icon: <Bot size={32} /> },
].map((stat) => (
<Card key={stat.label} className={`border ${stat.border} ${stat.bg} backdrop-blur-sm transition-all duration-300 transform hover:-translate-y-1 rounded-2xl`}>
<CardContent className="p-6 flex flex-col items-center justify-center text-center">

View File

@@ -5,8 +5,9 @@ import { Card, CardContent } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Gauge, TrendingUp, Target, Award, ShieldAlert, CheckCircle2, BookOpen, Zap, BarChart2, Clock } from "lucide-react"
import Link from "next/link"
import { useActiveUser, isDemoUser } from "@/hooks/useActiveUser"
const SCORE_HISTORY = [
const DEMO_SCORE_HISTORY = [
{ week: "Wk 1", score: 54 },
{ week: "Wk 2", score: 60 },
{ week: "Wk 3", score: 63 },
@@ -15,8 +16,20 @@ const SCORE_HISTORY = [
{ week: "Wk 6", score: 78 },
]
const ZERO_SCORE_HISTORY = [
{ week: "Wk 1", score: 0 },
{ week: "Wk 2", score: 0 },
{ week: "Wk 3", score: 0 },
{ week: "Wk 4", score: 0 },
{ week: "Wk 5", score: 0 },
{ week: "Wk 6", score: 0 },
]
export default function ReadinessPage() {
const maxScore = Math.max(...SCORE_HISTORY.map(d => d.score))
const { userId } = useActiveUser()
const isDemo = isDemoUser(userId)
const scoreHistory = isDemo ? DEMO_SCORE_HISTORY : ZERO_SCORE_HISTORY
const maxScore = Math.max(1, ...scoreHistory.map(d => d.score))
return (
<div className="p-6 md:p-8 space-y-8 max-w-7xl mx-auto animate-in fade-in duration-500 min-h-screen">
@@ -43,16 +56,16 @@ export default function ReadinessPage() {
{/* KPI Strip */}
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{[
{ label: "Overall Score", value: "72 / 100", trend: "+5 this week", trendUp: true, icon: <Gauge size={20} className="text-[#00F0FF]" /> },
{ label: "Accuracy Rate", value: "88%", trend: "+3% vs last week", trendUp: true, icon: <Target size={20} className="text-amber-400" /> },
{ label: "Avg Speed / Q", value: "45 sec", trend: "↓ Needs work", trendUp: false, icon: <Clock size={20} className="text-red-400" /> },
{ label: "Syllabus Covered", value: "64%", trend: "+8% this month", trendUp: true, icon: <BookOpen size={20} className="text-emerald-400" /> },
{ label: "Overall Score", value: isDemo ? "72 / 100" : "0 / 100", trend: isDemo ? "+5 this week" : "Baseline", trendUp: true, icon: <Gauge size={20} className="text-[#00F0FF]" /> },
{ 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: "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-[#00F0FF]/30 transition-colors">
<CardContent className="p-5">
<div className="flex items-center justify-between mb-3">
{kpi.icon}
<span className={`text-xs font-bold px-2 py-0.5 rounded-full ${kpi.trendUp ? "bg-green-500/10 text-green-400" : "bg-red-500/10 text-red-400"}`}>
<span className={`text-xs font-bold px-2 py-0.5 rounded-full ${kpi.trendUp ? "bg-green-500/10 text-green-400" : "bg-white/5 text-muted-foreground"}`}>
{kpi.trend}
</span>
</div>
@@ -70,7 +83,9 @@ export default function ReadinessPage() {
<div className="flex items-center gap-2 mb-5">
<Award className="text-amber-400" size={22} />
<h2 className="text-xl font-extrabold tracking-tight">Composite Readiness Index</h2>
<span className="ml-auto text-xs font-bold text-[#00F0FF] bg-cyan-50 dark:bg-[#00F0FF]/10 border border-cyan-300/60 dark:border-[#00F0FF]/20 px-2.5 py-1 rounded-full">Updated Daily</span>
<span className="ml-auto text-xs font-bold text-[#00F0FF] bg-cyan-50 dark:bg-[#00F0FF]/10 border border-cyan-300/60 dark:border-[#00F0FF]/20 px-2.5 py-1 rounded-full">
{isDemo ? "Updated Daily" : "Day 1 Initializing"}
</span>
</div>
<ReadinessGauge />
</Card>
@@ -83,9 +98,9 @@ export default function ReadinessPage() {
</div>
<div className="space-y-4">
{[
{ subject: "Biology / Maths", score: 82, color: "bg-[#00F0FF]", status: "High Readiness", statusColor: "text-[#00F0FF]" },
{ subject: "Chemistry", score: 74, color: "bg-amber-400", status: "Moderate Readiness", statusColor: "text-amber-400" },
{ subject: "Physics", score: 61, color: "bg-red-400", status: "Needs Intensive Drill", statusColor: "text-red-400" },
{ subject: "Biology / Maths", score: isDemo ? 82 : 0, color: "bg-[#00F0FF]", status: isDemo ? "High Readiness" : "Not Started", statusColor: isDemo ? "text-[#00F0FF]" : "text-muted-foreground" },
{ subject: "Chemistry", score: isDemo ? 74 : 0, color: "bg-amber-400", status: isDemo ? "Moderate Readiness" : "Not Started", statusColor: isDemo ? "text-amber-400" : "text-muted-foreground" },
{ subject: "Physics", score: isDemo ? 61 : 0, color: "bg-red-400", status: isDemo ? "Needs Intensive Drill" : "Not Started", statusColor: isDemo ? "text-red-400" : "text-muted-foreground" },
].map((sub) => (
<div key={sub.subject} className="space-y-2">
<div className="flex justify-between items-center">
@@ -107,12 +122,18 @@ export default function ReadinessPage() {
<h3 className="text-lg font-extrabold tracking-tight">6-Week Readiness Trend</h3>
</div>
<div className="flex items-end gap-3 h-28">
{SCORE_HISTORY.map((d, i) => (
{scoreHistory.map((d, i) => (
<div key={d.week} className="flex flex-col items-center gap-1 flex-1">
<span className="text-xs font-black text-muted-foreground">{d.score}</span>
<div
className={`w-full rounded-t-md transition-all duration-700 ${i === SCORE_HISTORY.length - 1 ? "bg-gradient-to-t from-[#00F0FF]/60 to-[#00F0FF]" : "bg-gradient-to-t from-white/10 to-white/25"}`}
style={{ height: `${(d.score / maxScore) * 80}px` }}
className={`w-full rounded-t-md transition-all duration-700 ${
!isDemo
? "bg-white/5 border border-white/10"
: i === scoreHistory.length - 1
? "bg-gradient-to-t from-[#00F0FF]/60 to-[#00F0FF]"
: "bg-gradient-to-t from-white/10 to-white/25"
}`}
style={{ height: isDemo ? `${(d.score / maxScore) * 80}px` : "4px" }}
/>
<span className="text-xs text-muted-foreground font-semibold">{d.week}</span>
</div>
@@ -130,19 +151,19 @@ export default function ReadinessPage() {
<ul className="space-y-3.5 text-sm font-semibold text-muted-foreground">
<li className="flex items-start gap-2.5">
<CheckCircle2 size={16} className="text-amber-400 shrink-0 mt-0.5" />
<span>Complete 2 full-length mock tests this week to boost consistency score.</span>
<span>{isDemo ? "Complete 2 full-length mock tests this week to boost consistency score." : "Complete your first diagnostic test to calculate your initial readiness baseline."}</span>
</li>
<li className="flex items-start gap-2">
<CheckCircle2 size={16} className="text-amber-400 shrink-0 mt-0.5" />
<span>Clear 15 pending errors in your Physics queue to gain ~4 readiness points.</span>
<span>{isDemo ? "Clear 15 pending errors in your Physics queue to gain ~4 readiness points." : "Attempt daily DPPs to populate your question error log."}</span>
</li>
<li className="flex items-start gap-2">
<CheckCircle2 size={16} className="text-amber-400 shrink-0 mt-0.5" />
<span>Maintain a 7+ day streak — consistency adds 6% to your readiness index.</span>
<span>Maintain daily streak — 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" />
<span>Reduce avg time-per-question below 40s with daily timed DPP sessions.</span>
<span>Target under 45s per question in timed test series practice.</span>
</li>
</ul>
</Card>

View File

@@ -4,14 +4,25 @@ import { ExamStrategyCard } from "@/components/AI/ExamStrategyCard"
import { StudyTimeOptimizationCard } from "@/components/AI/StudyTimeOptimizationCard"
import { Card, CardContent } from "@/components/ui/card"
import { Zap, Clock, Target, Lightbulb, CheckCircle2, AlertTriangle, TrendingUp } from "lucide-react"
import { useActiveUser, isDemoUser } from "@/hooks/useActiveUser"
const NEG_MARKING_DATA = [
const DEMO_NEG_MARKING_DATA = [
{ subject: "Physics", attempts: 30, negMarks: 12, accuracy: 60 },
{ subject: "Chemistry", attempts: 35, negMarks: 7, accuracy: 80 },
{ subject: "Maths", attempts: 28, negMarks: 10, accuracy: 64 },
{ subject: "Maths / Biology", attempts: 28, negMarks: 10, accuracy: 64 },
]
const ZERO_NEG_MARKING_DATA = [
{ subject: "Physics", attempts: 0, negMarks: 0, accuracy: 0 },
{ subject: "Chemistry", attempts: 0, negMarks: 0, accuracy: 0 },
{ subject: "Maths / Biology", attempts: 0, negMarks: 0, accuracy: 0 },
]
export default function StrategyPage() {
const { userId } = useActiveUser()
const isDemo = isDemoUser(userId)
const negMarkingData = isDemo ? DEMO_NEG_MARKING_DATA : ZERO_NEG_MARKING_DATA
return (
<div className="p-6 md:p-8 space-y-8 max-w-7xl mx-auto animate-in fade-in duration-500 min-h-screen">
{/* Page Header */}
@@ -37,10 +48,10 @@ export default function StrategyPage() {
{/* KPI Strip */}
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{[
{ label: "Time Per Question", value: "45 sec", note: "Target: 38 sec", icon: <Clock size={18} className="text-red-400" />, color: "border-red-400/20 bg-red-400/5" },
{ label: "Negative Marks Lost", value: "−29", note: "Last mock test", icon: <AlertTriangle size={18} className="text-amber-400" />, color: "border-amber-400/20 bg-amber-400/5" },
{ label: "Attempted Questions", value: "93 / 120", note: "+7 vs last test", icon: <Target size={18} className="text-[#00F0FF]" />, color: "border-[#00F0FF]/20 bg-[#00F0FF]/5" },
{ label: "Score Yield", value: "214", note: "Predicted score", icon: <TrendingUp size={18} className="text-green-400" />, color: "border-green-400/20 bg-green-400/5" },
{ 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: "Attempted Questions", value: isDemo ? "93 / 120" : "0 / 120", note: isDemo ? "+7 vs last test" : "0 attempted", icon: <Target size={18} className="text-[#00F0FF]" />, color: "border-[#00F0FF]/20 bg-[#00F0FF]/5" },
{ 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) => (
<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">
@@ -77,10 +88,12 @@ export default function StrategyPage() {
<div className="flex items-center gap-2 mb-5">
<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">Last Mock Test · Aug 1</span>
<span className="ml-auto text-xs text-muted-foreground font-semibold">
{isDemo ? "Last Mock Test · Aug 1" : "Day 1 Initializing"}
</span>
</div>
<div className="space-y-4">
{NEG_MARKING_DATA.map((row) => (
{negMarkingData.map((row) => (
<div key={row.subject} className="grid grid-cols-4 gap-4 items-center p-3 rounded-xl bg-white/5 border border-border/40">
<div className="font-black text-sm">{row.subject}</div>
<div className="text-center">
@@ -88,17 +101,19 @@ 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}</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>
<div className="flex justify-between text-xs font-bold mb-1">
<span className="text-muted-foreground">Accuracy</span>
<span className={row.accuracy >= 75 ? "text-green-400" : row.accuracy >= 60 ? "text-amber-400" : "text-red-400"}>{row.accuracy}%</span>
<span className={row.accuracy >= 75 ? "text-green-400" : row.accuracy >= 60 ? "text-amber-400" : row.accuracy === 0 ? "text-muted-foreground" : "text-red-400"}>
{row.accuracy}%
</span>
</div>
<div className="h-2 w-full bg-secondary rounded-full overflow-hidden">
<div
className={`h-full rounded-full ${row.accuracy >= 75 ? "bg-green-400" : row.accuracy >= 60 ? "bg-amber-400" : "bg-red-400"}`}
className={`h-full rounded-full ${row.accuracy >= 75 ? "bg-green-400" : row.accuracy >= 60 ? "bg-amber-400" : isDemo ? "bg-red-400" : "bg-white/10"}`}
style={{ width: `${row.accuracy}%` }}
/>
</div>
@@ -107,7 +122,11 @@ 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> You&apos;re losing ~29 marks to wrong answers. Switching to &quot;skip and revisit&quot; for uncertain Physics questions could recover 15–20 marks.
<span className="font-black text-amber-400">Insight:</span> {isDemo ? (
<>You&apos;re losing ~29 marks to wrong answers. Switching to &quot;skip and revisit&quot; for uncertain Physics questions could recover 15–20 marks.</>
) : (
<>Complete your first mock test or timed DPP to calculate your negative marking profile and fine-tune your subject pacing.</>
)}
</p>
</Card>

View File

@@ -4,8 +4,13 @@ import { StudyPlanCard } from "@/components/AI/StudyPlanCard"
import { WeeklyProgressReport } from "@/components/AI/WeeklyProgressReport"
import { Card, CardContent } from "@/components/ui/card"
import { Calendar, BookOpen, Clock, Target, CheckCircle2, TrendingUp, Flame } from "lucide-react"
import { useActiveUser, isDemoUser } from "@/hooks/useActiveUser"
export default function StudyPlanPage() {
const { user, userId } = useActiveUser()
const isDemo = isDemoUser(userId)
const streakCount = user?.streak_count || (isDemo ? 12 : 1)
return (
<div className="p-6 md:p-8 space-y-8 max-w-7xl mx-auto animate-in fade-in duration-500 min-h-screen">
{/* Page Header */}
@@ -31,10 +36,10 @@ export default function StudyPlanPage() {
{/* KPI Strip */}
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{[
{ label: "Tasks Today", value: "5", sub: "2 completed", icon: <CheckCircle2 size={18} className="text-green-400" />, color: "border-green-400/20 bg-green-400/5" },
{ label: "Study Hours Today", value: "3.8 hrs", sub: "Target: 5 hrs", icon: <Clock size={18} className="text-[#00F0FF]" />, color: "border-[#00F0FF]/20 bg-[#00F0FF]/5" },
{ label: "Syllabus Coverage", value: "64%", sub: "+8% this week", icon: <Target size={18} className="text-amber-400" />, color: "border-amber-400/20 bg-amber-400/5" },
{ label: "Current Streak", value: "12 Days", sub: "Keep it up!", icon: <Flame size={18} className="text-orange-400" />, color: "border-orange-400/20 bg-orange-400/5" },
{ 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-[#00F0FF]" />, color: "border-[#00F0FF]/20 bg-[#00F0FF]/5" },
{ label: "Syllabus Coverage", value: isDemo ? "64%" : "0%", sub: isDemo ? "+8% this week" : "Day 1 Baseline", icon: <Target size={18} className="text-amber-400" />, color: "border-amber-400/20 bg-amber-400/5" },
{ label: "Current Streak", value: `${streakCount} Day${streakCount === 1 ? '' : 's'}`, sub: isDemo ? "Keep it up!" : "Day 1 active", icon: <Flame size={18} className="text-orange-400" />, color: "border-orange-400/20 bg-orange-400/5" },
].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">

View File

@@ -4,8 +4,9 @@ import { StudyTimeOptimizationCard } from "@/components/AI/StudyTimeOptimization
import { Clock, Zap, Moon, Sun, AlertCircle, ArrowRight, BarChart2 } from "lucide-react"
import { Card, CardContent } from "@/components/ui/card"
import Link from "next/link"
import { useActiveUser, isDemoUser } from "@/hooks/useActiveUser"
const INSIGHTS = [
const DEMO_INSIGHTS = [
{
icon: <Sun size={16} className="text-yellow-400" />,
title: "Peak Cognitive Window",
@@ -26,7 +27,7 @@ const INSIGHTS = [
},
]
const HOURLY_EFFICIENCY = [
const DEMO_HOURLY_EFFICIENCY = [
{ slot: "6–9 AM", pct: 60, label: "Morning" },
{ slot: "9–12 PM", pct: 72, label: "Mid-Morning" },
{ slot: "12–2 PM", pct: 55, label: "Post-Lunch dip" },
@@ -37,7 +38,36 @@ const HOURLY_EFFICIENCY = [
{ slot: "11 PM+", pct: 32, label: "Avoid" },
]
const ZERO_HOURLY_EFFICIENCY = [
{ slot: "6–9 AM", pct: 0, label: "Pending" },
{ slot: "9–12 PM", pct: 0, label: "Pending" },
{ slot: "12–2 PM", pct: 0, label: "Pending" },
{ slot: "2–5 PM", pct: 0, label: "Calibrating", highlight: false },
{ slot: "5–7 PM", pct: 0, label: "Pending" },
{ slot: "7–9 PM", pct: 0, label: "Pending" },
{ slot: "9–11 PM", pct: 0, label: "Pending" },
{ slot: "11 PM+", pct: 0, label: "Pending" },
]
export default function TimeOptimizationPage() {
const { userId } = useActiveUser()
const isDemo = isDemoUser(userId)
const hourlySlots = isDemo ? DEMO_HOURLY_EFFICIENCY : ZERO_HOURLY_EFFICIENCY
const insights = isDemo ? DEMO_INSIGHTS : [
{
icon: <Sun size={16} className="text-yellow-400" />,
title: "Peak Window Calibrating",
desc: "Complete 3 test sessions at different times of day to establish your circadian peak focus hours.",
color: "border-yellow-400/20 bg-yellow-400/5",
},
{
icon: <Moon size={16} className="text-indigo-400" />,
title: "Consistency Booster",
desc: "Start with a 30-minute daily review routine to jumpstart your spaced repetition learning schedule.",
color: "border-indigo-400/20 bg-indigo-400/5",
},
]
return (
<div className="p-6 md:p-8 max-w-7xl mx-auto space-y-8 animate-in fade-in duration-500 min-h-screen">
{/* Page Header */}
@@ -63,10 +93,10 @@ export default function TimeOptimizationPage() {
{/* KPI Strip */}
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
{[
{ label: "Peak Efficiency", value: "95%", sub: "2–5 PM window", color: "border-yellow-400/20 bg-yellow-400/5", icon: <Sun size={18} className="text-yellow-400" /> },
{ label: "Study Hrs Today", value: "3.8 h", sub: "Target: 6 h", color: "border-[#00F0FF]/20 bg-[#00F0FF]/5", icon: <Clock size={18} className="text-[#00F0FF]" /> },
{ label: "Morning Block", value: "60%", sub: "6–9 AM", color: "border-indigo-400/20 bg-indigo-400/5", icon: <Moon size={18} className="text-indigo-400" /> },
{ label: "Evening Block", value: "74%", sub: "7–9 PM", color: "border-green-400/20 bg-green-400/5", icon: <BarChart2 size={18} className="text-green-400" /> },
{ 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-[#00F0FF]/20 bg-[#00F0FF]/5", icon: <Clock size={18} className="text-[#00F0FF]" /> },
{ label: "Morning Block", value: isDemo ? "60%" : "0%", sub: "6–9 AM", color: "border-indigo-400/20 bg-indigo-400/5", icon: <Moon size={18} className="text-indigo-400" /> },
{ label: "Evening Block", value: isDemo ? "74%" : "0%", sub: "7–9 PM", color: "border-green-400/20 bg-green-400/5", icon: <BarChart2 size={18} className="text-green-400" /> },
].map(kpi => (
<Card key={kpi.label} className={`rounded-2xl border ${kpi.color}`}>
<CardContent className="p-4 flex flex-col gap-2">
@@ -96,12 +126,12 @@ export default function TimeOptimizationPage() {
<CardContent className="p-5 space-y-3">
<h3 className="text-sm font-black uppercase tracking-wider text-muted-foreground">Time Slot Breakdown</h3>
<div className="space-y-2">
{HOURLY_EFFICIENCY.map(slot => (
{hourlySlots.map(slot => (
<div key={slot.slot} className={`flex items-center gap-3 p-2 rounded-lg ${slot.highlight ? "bg-yellow-400/10 border border-yellow-400/20" : "bg-white/5"}`}>
<div className="w-16 text-xs font-bold text-muted-foreground shrink-0">{slot.slot}</div>
<div className="flex-1 h-1.5 bg-secondary rounded-full overflow-hidden">
<div
className={`h-full rounded-full ${slot.pct >= 80 ? (slot.highlight ? "bg-yellow-400" : "bg-green-400") : slot.pct >= 60 ? "bg-[#00F0FF]" : "bg-red-400"}`}
className={`h-full rounded-full ${slot.pct >= 80 ? (slot.highlight ? "bg-yellow-400" : "bg-green-400") : slot.pct >= 60 ? "bg-[#00F0FF]" : isDemo ? "bg-red-400" : "bg-white/10"}`}
style={{ width: `${slot.pct}%` }}
/>
</div>
@@ -115,7 +145,7 @@ export default function TimeOptimizationPage() {
{/* AI Insights */}
<div className="space-y-3">
{INSIGHTS.map(insight => (
{insights.map(insight => (
<Card key={insight.title} className={`rounded-2xl border ${insight.color}`}>
<CardContent className="p-4 flex items-start gap-3">
<div className="mt-0.5 shrink-0">{insight.icon}</div>

View File

@@ -4,22 +4,42 @@ import { WeeklyProgressReport } from "@/components/AI/WeeklyProgressReport"
import { Target, TrendingUp, Calendar, Zap, ArrowRight, BookOpen, CheckCircle2 } from "lucide-react"
import { Card, CardContent } from "@/components/ui/card"
import Link from "next/link"
import { useActiveUser, isDemoUser } from "@/hooks/useActiveUser"
const STATS = [
const DEMO_STATS = [
{ label: "Avg. Accuracy", value: "85%", delta: "+6%", positive: true },
{ label: "Days Active", value: "7 / 7", delta: "Perfect week", positive: true },
{ label: "Questions Solved", value: "214", delta: "+38 vs last week", positive: true },
{ label: "Topics Covered", value: "12", delta: "3 remaining", positive: false },
]
const SYLLABUS_PROGRESS = [
const ZERO_STATS = [
{ label: "Avg. Accuracy", value: "0%", delta: "Baseline", positive: true },
{ label: "Days Active", value: "0 / 7", delta: "Day 1 Active", positive: true },
{ label: "Questions Solved", value: "0", delta: "0 vs last week", positive: true },
{ label: "Topics Covered", value: "0", delta: "0 completed", positive: false },
]
const DEMO_SYLLABUS_PROGRESS = [
{ subject: "Physics", completed: 42, total: 60, percentage: 70, color: "bg-blue-500", text: "text-blue-500" },
{ subject: "Chemistry", completed: 35, total: 55, percentage: 63, color: "bg-amber-500", text: "text-amber-500" },
{ subject: "Mathematics", completed: 50, total: 65, percentage: 76, color: "bg-purple-500", text: "text-purple-500" },
{ subject: "Biology", completed: 20, total: 40, percentage: 50, color: "bg-emerald-500", text: "text-emerald-500" },
]
const ZERO_SYLLABUS_PROGRESS = [
{ subject: "Physics", completed: 0, total: 60, percentage: 0, color: "bg-blue-500", text: "text-blue-500" },
{ subject: "Chemistry", completed: 0, total: 55, percentage: 0, color: "bg-amber-500", text: "text-amber-500" },
{ subject: "Mathematics", completed: 0, total: 65, percentage: 0, color: "bg-purple-500", text: "text-purple-500" },
{ subject: "Biology", completed: 0, total: 40, percentage: 0, color: "bg-emerald-500", text: "text-emerald-500" },
]
export default function WeeklyProgressPage() {
const { userId } = useActiveUser()
const isDemo = isDemoUser(userId)
const stats = isDemo ? DEMO_STATS : ZERO_STATS
const syllabusProgress = isDemo ? DEMO_SYLLABUS_PROGRESS : ZERO_SYLLABUS_PROGRESS
return (
<div className="p-6 md:p-8 max-w-7xl mx-auto space-y-8 animate-in fade-in duration-500 min-h-screen">
{/* Page Header */}
@@ -46,7 +66,7 @@ export default function WeeklyProgressPage() {
{/* KPI Stats Row */}
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
{STATS.map((stat) => (
{stats.map((stat) => (
<Card key={stat.label} className="bg-secondary/20 border-border/60 rounded-2xl">
<CardContent className="p-4">
<p className="text-xs font-bold uppercase tracking-wider text-muted-foreground mb-1">{stat.label}</p>
@@ -80,7 +100,7 @@ export default function WeeklyProgressPage() {
</h2>
<Card className="bg-secondary/10 border-border/60 rounded-2xl">
<CardContent className="p-5 space-y-4">
{SYLLABUS_PROGRESS.map((item) => (
{syllabusProgress.map((item) => (
<div key={item.subject} className="space-y-1.5">
<div className="flex items-center justify-between text-sm">
<span className="font-bold">{item.subject}</span>
@@ -107,10 +127,14 @@ export default function WeeklyProgressPage() {
<h3 className="text-sm font-black uppercase tracking-wider text-[#00F0FF]">Weekly Goals</h3>
<div className="space-y-2">
<div className="flex items-start gap-2 p-2 rounded-lg bg-white/5">
<CheckCircle2 size={16} className="text-green-400 mt-0.5 shrink-0" />
{isDemo ? (
<CheckCircle2 size={16} className="text-green-400 mt-0.5 shrink-0" />
) : (
<div className="w-4 h-4 rounded-full border-2 border-muted-foreground mt-0.5 shrink-0" />
)}
<div>
<div className="text-xs font-bold">Complete 200 Questions</div>
<div className="text-[10px] text-muted-foreground">Goal Met (214/200)</div>
<div className="text-[10px] text-muted-foreground">{isDemo ? "Goal Met (214/200)" : "Pending (0/200)"}</div>
</div>
</div>
<div className="flex items-start gap-2 p-2 rounded-lg bg-white/5 opacity-70">