"use client"; import { useEffect, useState } from "react"; import Link from "next/link"; import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Activity, Clock, PlayCircle, Search, Lock, CheckSquare } from "lucide-react"; import { useActiveUser } from "@/hooks/useActiveUser"; interface TestData { id: string; title: string; type: string; exam_pattern: string; total_time: number; difficulty: string; scheduled_date?: string; end_date?: string; } export default function CBTPage() { const [exams, setExams] = useState([]); const [loading, setLoading] = useState(true); const [search, setSearch] = useState(""); const [targetExam, setTargetExam] = useState(""); const [attempts, setAttempts] = useState([]); const [showUpgradeModal, setShowUpgradeModal] = useState(false); const [showAllExams, setShowAllExams] = useState(false); const [selectedPattern, setSelectedPattern] = useState(""); const [dateFilter, setDateFilter] = useState(""); const { userId, user, hasAccessTo, loading: userLoading } = useActiveUser(); useEffect(() => { if (userLoading || !userId) return; async function loadUser() { try { const userRes = await fetch(`/api/users/${userId}`); if (!userRes.ok) throw new Error("Failed to load user target exam"); const userData = await userRes.json(); const pattern = userData.target_exam; setTargetExam(pattern === "NEET" ? "NEET" : pattern === "JEE_Advanced" ? "JEE Advanced" : "JEE"); // Only set the initial pattern if it hasn't been changed by the user if (!selectedPattern) { setSelectedPattern(pattern.startsWith("JEE") ? "JEE" : "NEET"); } } catch (err) { console.error("Error loading user:", err); } } loadUser(); }, [userId, userLoading]); useEffect(() => { if (!userId || !selectedPattern) return; async function loadTests() { setLoading(true); try { let url = `/api/tests?user_id=${userId}`; if (selectedPattern !== "JEE") { url += `&exam_pattern=${selectedPattern}`; } const testRes = await fetch(url); if (testRes.ok) { let testsData = await testRes.json(); if (selectedPattern === "JEE") { testsData = testsData.filter((t: any) => t.exam_pattern?.includes("JEE")); } else if (selectedPattern === "NEET") { testsData = testsData.filter((t: any) => t.exam_pattern === "NEET"); } setExams(testsData); } const attemptsRes = await fetch(`/api/users/${userId}/attempts`); if (attemptsRes.ok) { const attemptsData = await attemptsRes.json(); setAttempts(attemptsData); } } catch (err) { console.error("Error loading exams:", err); } finally { setLoading(false); } } loadTests(); }, [userId, selectedPattern]); const filteredExams = exams.filter(exam => { const matchesSearch = exam.title.toLowerCase().includes(search.toLowerCase()); if (!matchesSearch) return false; if (dateFilter) { if (!exam.scheduled_date) return false; const scheduledDate = new Date(exam.scheduled_date).toISOString().split('T')[0]; if (scheduledDate !== dateFilter) return false; } return true; }); const formatDate = (dateStr: string) => { return new Date(dateStr).toLocaleDateString("en-US", { year: 'numeric', month: 'short', day: 'numeric' }); } // Get the highest score for each test if attempted multiple times const attemptsByTest = attempts.reduce((acc, curr) => { // Isolate analytics to first attempt only - ignore retakes if (curr.is_retake) return acc; if (!acc[curr.source_id] || acc[curr.source_id].score < curr.score) { acc[curr.source_id] = curr; } return acc; }, {} as Record); if (userLoading || loading) { return (

Loading exams...

) } return (

Computer Based Tests (CBT)

Real exam environment simulations to conquer {selectedPattern === "NEET" ? "NEET" : (targetExam || "JEE")}.

setDateFilter(e.target.value)} className="w-full px-4 py-2 bg-background border border-border rounded-xl text-sm focus:ring-2 focus:ring-[var(--cyan-ai)] outline-none text-foreground dark:[color-scheme:dark] [color-scheme:light]" />
setSearch(e.target.value)} className="w-full pl-10 pr-4 py-2 bg-background border border-border rounded-xl text-sm focus:ring-2 focus:ring-[var(--cyan-ai)] outline-none" />
{["JEE", "NEET"].map(p => { return ( ) })}

Upcoming & Active Exams

{/* BUG-08 fix: Filter out already attempted exams from Upcoming & Active list */} {(() => { const unattemptedExams = filteredExams.filter(exam => !attemptsByTest[exam.id]); if (loading) { return
Loading test catalog...
; } if (unattemptedExams.length === 0) { return (
{filteredExams.length > 0 ? ( <>

All available exams in this section have been attempted!

Review your scores, solution explanations, and performance in the Past Attempts section below.

) : (

No active exams found for {selectedPattern === "NEET" ? "NEET" : "JEE"}. Try adjusting your filters!

)}
); } return (
{(showAllExams ? unattemptedExams : unattemptedExams.slice(0, 3)).map((exam) => { const isLocked = !hasAccessTo("weekly-test-series"); // Date logic const now = new Date(); const scheduledTime = exam.scheduled_date ? new Date(exam.scheduled_date) : now; const endTime = exam.end_date ? new Date(exam.end_date) : null; const isUpcoming = scheduledTime > now; const isExpired = endTime ? now > endTime : false; return (
{exam.type} Test {exam.exam_pattern && ( {exam.exam_pattern === 'JEE_Main' ? 'JEE Main' : exam.exam_pattern === 'JEE_Advanced' ? 'JEE Advanced' : exam.exam_pattern} )}
{isUpcoming ? ( Upcoming ) : isLocked ? ( Premium ) : isExpired ? ( Expired ) : null}

{exam.title}

{isUpcoming ? `Available on ${formatDate(exam.scheduled_date!)}` : isExpired ? `Expired on ${formatDate(exam.end_date!)}` : 'Available Now'}
{exam.total_time} Min • {exam.difficulty} Difficulty
{isUpcoming ? ( ) : isLocked ? ( ) : isExpired ? ( ) : ( )}
)})}
{unattemptedExams.length > 3 && !showAllExams && (
)}
); })()}

Past Attempts

{attempts.filter(a => a.source_type === "Test" && exams.some(e => e.id === a.source_id)).length === 0 ? (
You haven't attempted any CBT exams yet.
) : attempts.filter(a => a.source_type === "Test" && exams.some(e => e.id === a.source_id)).map((attempt, i) => { const exam = exams.find(e => e.id === attempt.source_id); // BUG-07 fix: use actual final_score from backend if available, else derive correctly // backend stores final_score = (correct*4) - (incorrect*1) const correctCount = attempt.answers ? attempt.answers.filter((a: any) => a.is_correct).length : attempt.score; const incorrectCount = attempt.answers ? (attempt.answers.filter((a: any) => a.selected_answer !== null).length - correctCount) : 0; const scoreCalc = attempt.final_score ?? ((correctCount * 4) - (incorrectCount * 1)); const maxScore = attempt.total_questions * 4; const scorePercent = maxScore > 0 ? (scoreCalc / maxScore) * 100 : 0; const scoreColor = scorePercent >= 60 ? 'text-green-500' : scorePercent >= 35 ? 'text-amber-500' : 'text-red-500'; return (

{exam?.title || 'Unknown Test'}

{formatDate(attempt.submitted_at)}

Score
{scoreCalc} /{maxScore}
Accuracy
{Math.round(attempt.accuracy)}%
)})}
{showUpgradeModal && (

Upgrade to Premium Plan

This CBT Mock Test is a premium feature. Free users get access to 1 Daily Practice Sheet and 5 Topic-wise Micro-tests. Upgrade to Pro or AI Coach to unlock unlimited practice and full-length weekly test series!

)}
); }