feat: setup admin JSON generation and pdf viewer for free plan DPPs

This commit is contained in:
2026-07-27 14:56:25 +05:30
parent c214ca41b6
commit a5bb2d867c
74 changed files with 8546 additions and 915 deletions

View File

@@ -5,16 +5,33 @@ import { MistakeHeatmap } from "@/components/AI/MistakeHeatmap"
import { StudyPlanCard } from "@/components/AI/StudyPlanCard"
import { ReadinessGauge } from "@/components/AI/ReadinessGauge"
import { CollegePredictorCard } from "@/components/AI/CollegePredictorCard"
import { ExamStrategyCard } from "@/components/AI/ExamStrategyCard"
import { StudyTimeOptimizationCard } from "@/components/AI/StudyTimeOptimizationCard"
import { TutorChat } from "@/components/AI/TutorChat"
import { ReferralCard } from "@/components/AI/ReferralCard"
import { ProgressShare } from "@/components/AI/ProgressShare"
import { MistakeReviewCard, MistakePayload } from "@/components/AI/MistakeReviewCard"
import { DailyChallengeBanner } from "@/components/AI/DailyChallengeBanner"
import { WeeklyProgressReport } from "@/components/AI/WeeklyProgressReport"
import { Card, CardContent } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { BrainCircuit, BookOpen, Calculator, Target, RefreshCw, CheckCircle2, Lock } from "lucide-react"
import { BrainCircuit, RefreshCw, CheckCircle2, Flame, Trophy, Target, Zap, BookOpen, Bot, FileText } from "lucide-react"
import { useActiveUser } from "@/hooks/useActiveUser"
import Link from "next/link"
// Days until NEET 2026 (May 3, 2026)
function getDaysToExam(exam: string) {
const targets: Record<string, Date> = {
"NEET": new Date("2026-05-03"),
"JEE_Main": new Date("2026-01-22"),
"JEE_Advanced": new Date("2026-05-24"),
}
const t = targets[exam] || targets["NEET"]
const diff = Math.ceil((t.getTime() - Date.now()) / 86400000)
return diff > 0 ? diff : 0
}
export default function AICoachDashboard() {
const [mistakes, setMistakes] = useState<MistakePayload[]>([])
const [currentIndex, setCurrentIndex] = useState(0)
@@ -32,7 +49,6 @@ export default function AICoachDashboard() {
setMistakes(data)
} catch (error) {
console.error(error)
// If backend fails, fallback to empty array
setMistakes([])
} finally {
setLoading(false)
@@ -51,7 +67,6 @@ export default function AICoachDashboard() {
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ mistake_id: mistakeId, selected_answer: selectedAnswer })
})
if (!res.ok) throw new Error("Failed to submit review")
return await res.json()
}
@@ -60,13 +75,19 @@ export default function AICoachDashboard() {
if (currentIndex < mistakes.length - 1) {
setCurrentIndex(prev => prev + 1)
} else {
// Session finished
setCurrentIndex(mistakes.length) // Push out of bounds to trigger complete state
setCurrentIndex(mistakes.length)
}
}
if (userLoading) {
return <div className="p-8 text-center text-muted-foreground animate-pulse">Loading AI Coach...</div>
return (
<div className="h-screen flex items-center justify-center">
<div className="flex flex-col items-center gap-3 text-muted-foreground">
<BrainCircuit size={32} className="animate-pulse text-[#00F0FF]" />
<span className="text-sm font-medium">Loading AI Coach...</span>
</div>
</div>
)
}
if (!hasAccessTo("ai-mistake-tracking")) {
@@ -78,7 +99,7 @@ export default function AICoachDashboard() {
<div className="space-y-4">
<h1 className="text-4xl font-extrabold tracking-tight">AI Coach Workspace</h1>
<p className="text-muted-foreground text-lg max-w-xl mx-auto leading-relaxed">
Mistake tracking, Spaced Repetition (SM-2), AI Tutor Chats, and Readiness scores are premium features reserved for **AI Coach** subscribers.
Mistake tracking, Spaced Repetition (SM-2), AI Tutor Chats, and Readiness scores are premium features reserved for <strong>AI Coach</strong> subscribers.
</p>
</div>
<div className="pt-4">
@@ -92,81 +113,113 @@ 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
return (
<div className="p-8 max-w-6xl mx-auto space-y-10 animate-in fade-in duration-500">
<header className="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div>
<h1 className="text-3xl font-bold tracking-tight flex items-center gap-3">
<BrainCircuit className="text-primary" size={32}/> AI Coach
</h1>
<p className="text-muted-foreground mt-1 font-medium">Your personalized SM-2 spaced repetition and analytics engine.</p>
</div>
</header>
<div className="p-6 max-w-6xl mx-auto space-y-8 animate-in fade-in duration-500">
{/* Summary Metrics */}
<section className="grid grid-cols-1 md:grid-cols-3 gap-4">
<Card className="border-border shadow-sm">
<CardContent className="p-5 flex items-center gap-4">
<div className="p-3 bg-secondary rounded-xl text-primary"><Calculator size={20}/></div>
<div>
<p className="text-sm font-bold text-muted-foreground uppercase tracking-wider">Physics Errors</p>
<h3 className="text-2xl font-bold">{mistakes.filter(m => m.question.subject === 'Physics').length}</h3>
</div>
</CardContent>
</Card>
<Card className="border-border shadow-sm">
<CardContent className="p-5 flex items-center gap-4">
<div className="p-3 bg-secondary rounded-xl text-emerald-500"><BookOpen size={20}/></div>
<div>
<p className="text-sm font-bold text-muted-foreground uppercase tracking-wider">Chemistry Errors</p>
<h3 className="text-2xl font-bold">{mistakes.filter(m => m.question.subject === 'Chemistry').length}</h3>
</div>
</CardContent>
</Card>
<Card className="border-border shadow-sm">
<CardContent className="p-5 flex items-center gap-4">
<div className="p-3 bg-secondary rounded-xl text-blue-500"><Target size={20}/></div>
<div>
<p className="text-sm font-bold text-muted-foreground uppercase tracking-wider">Maths Errors</p>
<h3 className="text-2xl font-bold">{mistakes.filter(m => m.question.subject === 'Maths').length}</h3>
</div>
</CardContent>
</Card>
{/* ── METRICS BAR ── */}
<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
</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} />
{daysLeft} days to {user?.target_exam?.replace("_", " ") || "Exam"}
</div>
<div className="inline-flex items-center gap-1.5 bg-purple-500/10 text-purple-400 px-3 py-1.5 rounded-xl text-xs font-bold border border-purple-500/20">
<Trophy size={13} />
AI Coach
</div>
</div>
{/* ── DAILY CHALLENGE BANNER ── */}
<DailyChallengeBanner />
{/* ── 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} /> },
].map((stat) => (
<Card key={stat.label} className={`border ${stat.border} ${stat.bg} backdrop-blur-sm shadow-sm hover:shadow-[0_0_20px_rgba(0,240,255,0.2)] transition-all duration-300 transform hover:-translate-y-1 rounded-2xl`}>
<CardContent className="p-6 flex flex-col items-center justify-center text-center">
<div className="text-3xl mb-2 filter drop-shadow-md">{stat.icon}</div>
<div className={`text-4xl font-black ${stat.color} leading-none tracking-tight mb-2 filter drop-shadow-sm`}>{stat.value}</div>
<div className="text-xs font-extrabold text-muted-foreground uppercase tracking-wider">{stat.label}</div>
</CardContent>
</Card>
))}
</section>
{/* Predictive Intelligence */}
<section className="grid grid-cols-1 lg:grid-cols-2 gap-8 mb-8">
<ReadinessGauge />
<CollegePredictorCard />
{/* ── PREDICTIVE INTELLIGENCE ROW ── */}
<section className="mt-10">
<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-[#00F0FF]">
<Zap size={20} />
Predictive Intelligence & Strategy
</div>
<div className="flex items-center gap-3">
<Link href="/dashboard/ai-coach/readiness" className="text-xs font-bold text-[#00F0FF] hover:underline flex items-center gap-1">
Full Analytics →
</Link>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<ReadinessGauge />
<CollegePredictorCard />
<ExamStrategyCard />
<StudyTimeOptimizationCard />
</div>
</section>
{/* Mistake Review Workspace */}
<section>
<div className="flex items-center justify-between mb-5">
<h2 className="text-xl font-semibold tracking-tight">Daily Mistake Review Queue</h2>
<span className="text-sm font-bold text-muted-foreground bg-secondary px-3 py-1 rounded-full">
{currentIndex < mistakes.length ? `${currentIndex + 1} / ${mistakes.length}` : "Finished"}
</span>
{/* ── MISTAKE REVIEW WORKSPACE ── */}
<section className="mt-12">
<div className="flex items-center justify-between mb-6">
<div className="flex items-center gap-3">
<div className="p-2.5 bg-[#00F0FF]/10 rounded-xl border border-[#00F0FF]/20">
<BrainCircuit size={24} className="text-[#00F0FF]" />
</div>
<div>
<h2 className="text-2xl md:text-3xl font-black tracking-tight">Daily Mistake Review Queue</h2>
<p className="text-sm text-muted-foreground font-medium">SM-2 Spaced Repetition Flashcard Engine</p>
</div>
</div>
<div className="flex items-center gap-3">
<Link href="/dashboard/ai-coach/mistakes" className="hidden sm:inline-flex text-xs font-extrabold text-[#00F0FF] bg-[#00F0FF]/10 px-4 py-2 rounded-xl border border-[#00F0FF]/30 hover:bg-[#00F0FF] hover:text-black transition-all">
Open Full Mistake Vault Page →
</Link>
<span className="text-sm font-extrabold text-[#00F0FF] bg-[#00F0FF]/10 px-4 py-1.5 rounded-full border border-[#00F0FF]/30 shadow-[0_0_10px_rgba(0,240,255,0.15)]">
{currentIndex < mistakes.length ? `${currentIndex + 1} / ${mistakes.length}` : "Finished"}
</span>
</div>
</div>
{loading ? (
<Card className="p-12 flex items-center justify-center border-dashed">
<RefreshCw className="animate-spin text-muted-foreground mr-3" /> Loading queue...
</Card>
) : mistakes.length === 0 ? (
<Card className="p-12 text-center border-dashed bg-secondary/20">
<Card className="p-12 text-center border-dashed bg-secondary/20 rounded-3xl">
<CheckCircle2 size={48} className="mx-auto text-green-500 mb-4 opacity-50" />
<h3 className="text-xl font-bold mb-2">You're all caught up!</h3>
<p className="text-muted-foreground">No mistakes scheduled for review today. Go take a test!</p>
</Card>
) : currentIndex < mistakes.length ? (
<MistakeReviewCard
data={mistakes[currentIndex]}
<MistakeReviewCard
data={mistakes[currentIndex]}
onNext={handleNext}
onSubmitReview={submitReview}
/>
) : (
<Card className="p-12 text-center border-dashed bg-primary/5 border-primary/20">
<Card className="p-12 text-center border-dashed bg-primary/5 border-primary/20 rounded-3xl">
<BrainCircuit size={48} className="mx-auto text-primary mb-4 opacity-80" />
<h3 className="text-xl font-bold mb-2 text-primary">Session Complete</h3>
<p className="text-muted-foreground mb-6">Great job clearing your error queue! The SM-2 engine has scheduled your next reviews.</p>
@@ -175,23 +228,57 @@ export default function AICoachDashboard() {
)}
</section>
{/* Heatmap & Timeline */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8 mb-8 pt-8 border-t border-border">
<section>
{/* ── HEATMAP / STUDY PLAN / WEEKLY REPORT ── */}
<section className="grid grid-cols-1 lg:grid-cols-2 gap-8 pt-10 border-t border-border">
<div>
<div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-2 text-base font-extrabold">
<BookOpen size={18} className="text-[#00F0FF]" /> Study Plan
</div>
<Link href="/dashboard/ai-coach/study-plan" className="text-xs font-bold text-[#00F0FF] hover:underline">
Full Schedule →
</Link>
</div>
<StudyPlanCard />
</section>
<section>
</div>
<div>
<div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-2 text-base font-extrabold">
<Flame size={18} className="text-yellow-500" /> Mistake Heatmap
</div>
<Link href="/dashboard/ai-coach/mistakes" className="text-xs font-bold text-[#00F0FF] hover:underline">
Heatmap Vault →
</Link>
</div>
<MistakeHeatmap />
</section>
</div>
</div>
</section>
{/* Tutor Chat Workspace */}
<section className="pt-8 border-t border-border">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-2 text-base font-extrabold">
<Target size={18} className="text-[#00F0FF]" /> Weekly Progress Overview
</div>
<Link href="/dashboard/ai-coach/study-plan" className="text-xs font-bold text-[#00F0FF] hover:underline">
View Details →
</Link>
</div>
<WeeklyProgressReport />
</section>
{/* ── TUTOR CHAT ── */}
<section className="pt-8 border-t border-border">
<div className="flex items-center justify-between mb-4">
<h2 className="text-2xl font-extrabold tracking-tight">24/7 AI Tutor Chat Assistant</h2>
<Link href="/dashboard/ai-coach/tutor" className="text-xs font-extrabold text-[#00F0FF] bg-[#00F0FF]/10 px-3.5 py-1.5 rounded-xl border border-[#00F0FF]/30 hover:bg-[#00F0FF] hover:text-black transition-all">
Full Desk Mode →
</Link>
</div>
<TutorChat />
</section>
{/* Growth & Sharing Hub */}
<section className="grid grid-cols-1 lg:grid-cols-2 gap-8 pt-8 border-t border-border mt-8">
{/* ── GROWTH HUB ── */}
<section className="grid grid-cols-1 lg:grid-cols-2 gap-6 pt-6 border-t border-border">
<ReferralCard />
<ProgressShare />
</section>
@@ -199,3 +286,4 @@ export default function AICoachDashboard() {
</div>
)
}