generated from muhtadeetaron/nextjs-template
chore(capacitor): refactor codebase for capacitor entry
This commit is contained in:
138
app/exam/exam-screen/page.tsx
Normal file
138
app/exam/exam-screen/page.tsx
Normal file
@ -0,0 +1,138 @@
|
||||
"use client";
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { 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) {
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user