feat: complete pragativeer v2 frontend and backend, docker setup, and cbt engine

This commit is contained in:
2026-07-06 17:08:13 +05:30
parent ce01795aa9
commit 426b98ddab
66 changed files with 7949 additions and 363 deletions

View File

@@ -0,0 +1,192 @@
"use client"
import { useState } from "react"
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"
import { ChevronDown, ChevronRight, CheckCircle2, Circle, Clock, Activity, Target } from "lucide-react"
type ChapterStatus = 'Mastered' | 'Practiced' | 'In Progress' | 'Not Started'
interface Topic {
name: string
status: ChapterStatus
accuracy: number
questionsAttempted: number
}
interface Chapter {
name: string
topics: Topic[]
isExpanded: boolean
}
interface Subject {
name: string
chapters: Chapter[]
completionPct: number
}
const getStatusIcon = (status: ChapterStatus) => {
switch (status) {
case 'Mastered': return <CheckCircle2 className="text-emerald-500" size={16} />
case 'Practiced': return <CheckCircle2 className="text-amber-500" size={16} />
case 'In Progress': return <Clock className="text-[#00F0FF]" size={16} />
case 'Not Started': return <Circle className="text-muted-foreground" size={16} />
}
}
export default function ProgressTrackerPage() {
const [subjects, setSubjects] = useState<Subject[]>([
{
name: "Physics",
completionPct: 42,
chapters: [
{
name: "Kinematics",
isExpanded: true,
topics: [
{ name: "Motion in One Dimension", status: "Mastered", accuracy: 92, questionsAttempted: 150 },
{ name: "Projectile Motion", status: "Practiced", accuracy: 75, questionsAttempted: 85 },
{ name: "Relative Velocity", status: "In Progress", accuracy: 45, questionsAttempted: 20 },
]
},
{
name: "Laws of Motion",
isExpanded: false,
topics: [
{ name: "Newton's First Law", status: "Practiced", accuracy: 88, questionsAttempted: 40 },
{ name: "Friction", status: "In Progress", accuracy: 50, questionsAttempted: 35 },
]
},
{
name: "Thermodynamics",
isExpanded: false,
topics: [
{ name: "First Law", status: "Not Started", accuracy: 0, questionsAttempted: 0 },
{ name: "Carnot Engine", status: "Not Started", accuracy: 0, questionsAttempted: 0 },
]
}
]
},
{
name: "Chemistry",
completionPct: 68,
chapters: [
{
name: "Atomic Structure",
isExpanded: false,
topics: [
{ name: "Bohr Model", status: "Mastered", accuracy: 95, questionsAttempted: 110 },
{ name: "Quantum Numbers", status: "Mastered", accuracy: 88, questionsAttempted: 95 },
]
}
]
},
{
name: "Mathematics",
completionPct: 22,
chapters: [
{
name: "Calculus",
isExpanded: false,
topics: [
{ name: "Limits", status: "In Progress", accuracy: 60, questionsAttempted: 45 },
{ name: "Derivatives", status: "Not Started", accuracy: 0, questionsAttempted: 0 },
]
}
]
}
])
const toggleChapter = (subjectIndex: number, chapterIndex: number) => {
const newSubjects = [...subjects]
newSubjects[subjectIndex].chapters[chapterIndex].isExpanded = !newSubjects[subjectIndex].chapters[chapterIndex].isExpanded
setSubjects(newSubjects)
}
return (
<div className="p-8 space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-500 max-w-5xl mx-auto">
<div>
<h1 className="text-3xl font-bold tracking-tight text-foreground flex items-center gap-3">
<Target className="text-[#00F0FF]" size={32} />
Syllabus Tracker
</h1>
<p className="text-muted-foreground mt-2 font-medium">Track your syllabus coverage and mastery per chapter to ensure no gaps.</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
{subjects.map((sub, i) => (
<Card key={i} className="border-border shadow-sm">
<CardContent className="p-6 text-center">
<h3 className="font-bold text-lg mb-2">{sub.name}</h3>
<div className="text-3xl font-black text-[#00F0FF] mb-3">{sub.completionPct}%</div>
<div className="h-2 w-full bg-secondary rounded-full overflow-hidden">
<div className="h-full bg-[#00F0FF]" style={{ width: `${sub.completionPct}%` }}></div>
</div>
</CardContent>
</Card>
))}
</div>
<div className="space-y-6">
{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">
<CardTitle className="text-xl">{subject.name} Syllabus</CardTitle>
</CardHeader>
<CardContent className="p-0">
<div className="divide-y divide-border">
{subject.chapters.map((chapter, chapIndex) => (
<div key={chapIndex} className="flex flex-col">
<button
onClick={() => toggleChapter(subIndex, chapIndex)}
className="flex items-center justify-between p-4 hover:bg-secondary/20 transition-colors text-left"
>
<div className="flex items-center gap-3">
{chapter.isExpanded ? <ChevronDown size={18} className="text-muted-foreground" /> : <ChevronRight size={18} className="text-muted-foreground" />}
<span className="font-bold">{chapter.name}</span>
</div>
<div className="text-xs font-medium bg-secondary px-3 py-1 rounded-full">
{chapter.topics.length} Topics
</div>
</button>
{chapter.isExpanded && (
<div className="bg-background px-6 pb-4 pt-1 space-y-2">
<div className="flex justify-between items-center text-xs font-bold text-muted-foreground px-2 py-2">
<span className="w-1/2">TOPIC</span>
<span className="w-1/4 text-center">ACCURACY</span>
<span className="w-1/4 text-right">QUESTIONS</span>
</div>
{chapter.topics.map((topic, topIndex) => (
<div key={topIndex} className="flex justify-between items-center bg-secondary/10 p-3 rounded-lg border border-border/50">
<div className="w-1/2 flex items-center gap-3">
{getStatusIcon(topic.status)}
<div>
<p className="font-medium text-sm">{topic.name}</p>
<p className="text-[10px] uppercase tracking-wider text-muted-foreground">{topic.status}</p>
</div>
</div>
<div className="w-1/4 text-center">
{topic.accuracy > 0 ? (
<span className={`font-mono text-sm font-bold ${topic.accuracy > 80 ? 'text-emerald-500' : topic.accuracy > 60 ? 'text-amber-500' : 'text-red-500'}`}>
{topic.accuracy}%
</span>
) : <span className="text-muted-foreground text-sm">-</span>}
</div>
<div className="w-1/4 text-right">
<span className="font-mono text-sm">{topic.questionsAttempted > 0 ? topic.questionsAttempted : '-'}</span>
</div>
</div>
))}
</div>
)}
</div>
))}
</div>
</CardContent>
</Card>
))}
</div>
</div>
)
}