"use client"; import { useParams, useRouter, useSearchParams } from "next/navigation"; import React, { useEffect, useState, useCallback } from "react"; import { useTimer } from "@/context/TimerContext"; import { useExam } from "@/context/ExamContext"; import { API_URL, getToken } from "@/lib/auth"; import Header from "@/components/Header"; // Types interface Question { id: number; question: string; options: Record; } interface QuestionItemProps { question: Question; selectedAnswer: string | undefined; handleSelect: (questionId: number, option: string) => void; } // Components const QuestionItem = React.memo( ({ question, selectedAnswer, handleSelect }) => (

{question.id}. {question.question}

{Object.entries(question.options).map(([key, value]) => ( ))}
) ); QuestionItem.displayName = "QuestionItem"; export default function ExamPage() { const router = useRouter(); const params = useParams(); const searchParams = useSearchParams(); const [isSubmitting, setIsSubmitting] = useState(false); const id = params.id as string; const time = searchParams.get("time"); const { setInitialTime, stopTimer } = useTimer(); // Use exam context instead of local state const { currentAttempt, setAnswer, getAnswer, submitExam: submitExamContext, setApiResponse, isExamStarted, isExamCompleted, isHydrated, isInitialized, } = useExam(); const [questions, setQuestions] = useState(null); const [loading, setLoading] = useState(true); const [submissionLoading, setSubmissionLoading] = useState(false); // Check if exam is properly started useEffect(() => { if (!isHydrated) return; if (!isInitialized) return; if (isSubmitting) return; // Don't redirect while submitting if (!isExamStarted()) { router.push("/unit"); return; } if (isExamCompleted()) { router.push("/exam/results"); return; } }, [ isHydrated, isExamStarted, isExamCompleted, router, isInitialized, isSubmitting, ]); const fetchQuestions = async () => { try { const response = await fetch(`${API_URL}/mock/${id}`, { method: "GET", }); const data = await response.json(); setQuestions(data.questions); } catch (error) { console.error("Error fetching questions:", error); } finally { setLoading(false); } }; useEffect(() => { fetchQuestions(); if (time) { setInitialTime(Number(time)); } }, [id, time, setInitialTime]); const handleSelect = useCallback( (questionId: number, option: string) => { // Store answer in context instead of local reducer setAnswer(questionId.toString(), option); }, [setAnswer] ); const handleSubmit = async () => { if (!currentAttempt) { console.error("No exam attempt found"); return; } stopTimer(); setSubmissionLoading(true); setIsSubmitting(true); // Add this line // Convert context answers to the format your API expects const answersForAPI = currentAttempt.answers.reduce((acc, answer) => { acc[parseInt(answer.questionId)] = answer.answer; return acc; }, {} as Record); const payload = { mock_id: id, data: answersForAPI, }; try { const response = await fetch(`${API_URL}/submit`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${await getToken()}`, }, body: JSON.stringify(payload), }); if (!response.ok) { const errorData = await response.json(); console.error( "Submission failed:", errorData.message || "Unknown error" ); setIsSubmitting(false); // Reset on error return; } const responseData = await response.json(); // Submit exam in context (this will store the completed attempt) const completedAttempt = submitExamContext(); // Store API response in context for results page setApiResponse(responseData); // Navigate to results without URL parameters router.push("/exam/results"); console.log("I'm here"); } catch (error) { console.error("Error submitting answers:", error); setIsSubmitting(false); // Reset on error } finally { setSubmissionLoading(false); } }; const showExitDialog = () => { if (window.confirm("Are you sure you want to quit the exam?")) { stopTimer(); router.push("/unit"); } }; // Handle browser back button useEffect(() => { const handleBeforeUnload = (e: BeforeUnloadEvent) => { e.preventDefault(); e.returnValue = ""; return ""; }; const handlePopState = (e: PopStateEvent) => { e.preventDefault(); showExitDialog(); }; window.addEventListener("beforeunload", handleBeforeUnload); window.addEventListener("popstate", handlePopState); return () => { window.removeEventListener("beforeunload", handleBeforeUnload); window.removeEventListener("popstate", handlePopState); }; }, []); if (submissionLoading) { return (

Submitting...

); } return (
{loading ? (
) : (
{questions?.map((question) => ( ))}
)}
); }