Files
examjam-frontend/app/exam/exam-screen/page.tsx
2025-09-09 00:54:06 +06:00

143 lines
4.5 KiB
TypeScript

"use client";
import React, { useEffect, useState } from "react";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import Header from "@/components/Header";
import QuestionItem from "@/components/QuestionItem";
import BackgroundWrapper from "@/components/BackgroundWrapper";
import { useExamStore } from "@/stores/examStore";
import { useTimerStore } from "@/stores/timerStore";
export default function ExamPage() {
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
const test_id = searchParams.get("test_id") || "";
const type = searchParams.get("type") || "";
const { setStatus, test, answers, startExam, setAnswer, submitExam } =
useExamStore();
const { resetTimer, stopTimer } = useTimerStore();
const [isSubmitting, setIsSubmitting] = useState(false);
// Start exam + timer automatically
useEffect(() => {
if (type && test_id) {
startExam(type, test_id).then((fetchedTest) => {
if (fetchedTest?.metadata.time_limit_minutes) {
setStatus("in-progress"); // ✅ make sure exam status is set here
resetTimer(fetchedTest.metadata.time_limit_minutes * 60, () => {
// Timer ended → auto-submit
setStatus("finished");
stopTimer();
submitExam(type);
router.replace(`/exam/results`);
});
}
});
}
}, [
type,
test_id,
startExam,
resetTimer,
submitExam,
router,
setStatus,
stopTimer,
]);
// useEffect(() => {
// const handlePopState = (event: PopStateEvent) => {
// if (status === "in-progress") {
// const confirmExit = window.confirm(
// "Are you sure you want to quit the exam?"
// );
// if (confirmExit) {
// setStatus("finished");
// stopTimer();
// cancelExam();
// router.replace(`/categories/${type}s`);
// } else {
// // User canceled → push them back to current page
// router.replace(pathname, { scroll: false });
// }
// } else {
// router.replace(`/categories/${type}s`);
// }
// };
// window.addEventListener("popstate", handlePopState);
// return () => window.removeEventListener("popstate", handlePopState);
// }, [status, router, pathname, type, setStatus, stopTimer, cancelExam]);
if (!test) {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="flex flex-col items-center justify-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-900 mb-4"></div>
<p className="text-lg font-medium text-gray-900">Loading exam...</p>
</div>
</div>
);
}
const handleSubmitExam = async (type: string) => {
try {
setStatus("finished"); // ✅ mark exam finished
stopTimer();
setIsSubmitting(true);
await submitExam(type);
router.replace(`/exam/results`); // ✅ replace to prevent back nav
} finally {
setIsSubmitting(false);
}
};
return (
<div className="min-h-screen bg-gray-50">
{/* Header with live timer */}
<Header />
{/* Questions */}
<BackgroundWrapper>
<div className="container mx-auto px-6 py-8 mb-20">
{test.questions.map((q, idx) => (
<div id={`question-${idx}`} key={q.question_id}>
<QuestionItem
question={q}
index={idx}
selectedAnswer={answers[idx]}
onSelect={(answer) => setAnswer(idx, answer)}
/>
</div>
))}
{/* Bottom submit bar */}
<div className="fixed bottom-0 left-0 right-0 bg-white border-t border-gray-200 flex">
<button
onClick={() => handleSubmitExam(type)}
disabled={isSubmitting}
className="flex-1 bg-blue-900 text-white p-6 font-bold text-lg disabled:opacity-50 disabled:cursor-not-allowed hover:bg-blue-800 transition-colors flex justify-center items-center gap-2"
>
{isSubmitting ? (
<>
<span className="animate-spin rounded-full h-5 w-5 border-b-2 border-white"></span>
Submitting...
</>
) : (
"Submit"
)}
</button>
</div>
</div>
</BackgroundWrapper>
</div>
);
}
function cancelExam() {
throw new Error("Function not implemented.");
}