Files
examjam-frontend/app/exam/exam-screen/page.tsx
shafin-r 108d34988d fix(nav): fix exam flow navigation
chore(zustand): refactor auth code for zustand store
2025-09-09 20:45:30 +06:00

136 lines
3.9 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 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) return;
const initExam = async () => {
const fetchedTest = await startExam(type, test_id);
if (!fetchedTest) return;
setStatus("in-progress");
const timeLimit = fetchedTest.metadata.time_limit_minutes;
if (timeLimit) {
resetTimer(timeLimit * 60, async () => {
// Auto-submit when timer ends
stopTimer();
setStatus("finished");
await submitExam(type);
router.replace("/exam/results");
});
}
};
initExam();
}, [
type,
test_id,
startExam,
resetTimer,
stopTimer,
submitExam,
router,
setStatus,
]);
if (isSubmitting) {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<p className="text-lg font-medium text-gray-900">Submitting exam...</p>
</div>
);
}
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) => {
setIsSubmitting(true);
stopTimer();
try {
const result = await submitExam(type); // throws if fails
if (!result) throw new Error("Submission failed");
router.replace("/exam/results"); // navigate
} catch (err) {
console.error("Submit exam failed:", err);
alert("Failed to submit exam. Please try again.");
} 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>
);
}