generated from muhtadeetaron/nextjs-template
fix(ui): fix timer reappearing after exam submission
This commit is contained in:
@ -27,8 +27,9 @@ export default function ExamPage() {
|
||||
if (fetchedTest?.metadata.time_limit_minutes) {
|
||||
resetTimer(fetchedTest.metadata.time_limit_minutes * 60, () => {
|
||||
// Timer ended → auto-submit exam
|
||||
stopTimer();
|
||||
submitExam(type);
|
||||
router.push(`/categories/${type}s`);
|
||||
router.push(`/exam/results`);
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -56,6 +57,7 @@ export default function ExamPage() {
|
||||
|
||||
const handleSubmitExam = async (type: string) => {
|
||||
try {
|
||||
stopTimer();
|
||||
setIsSubmitting(true);
|
||||
await submitExam(type);
|
||||
router.push(`/exam/results`);
|
||||
|
||||
@ -15,7 +15,7 @@ export default function ResultsPage() {
|
||||
if (!result) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center">
|
||||
<p className="text-lg font-medium">No results to display.</p>
|
||||
<p className="text-lg font-medium">Redirecting...</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -46,16 +46,19 @@ export default function ResultsPage() {
|
||||
const correctAnswer = result.correct_answers[idx];
|
||||
|
||||
return (
|
||||
<div key={q.question_id} className={`rounded-3xl mb-6`}>
|
||||
<div key={q.question_id} className="rounded-3xl mb-6">
|
||||
<QuestionItem
|
||||
question={q}
|
||||
index={idx}
|
||||
selectedAnswer={userAnswer}
|
||||
onSelect={() => {}} // disabled in results
|
||||
onSelect={() => {}}
|
||||
userAnswer={userAnswer}
|
||||
correctAnswer={correctAnswer}
|
||||
showResults={true}
|
||||
/>
|
||||
|
||||
{/* Answer feedback */}
|
||||
<div className="mt-2 text-sm">
|
||||
{/* Optional answer feedback below the question */}
|
||||
{/* <div className="mt-2 text-sm">
|
||||
{userAnswer === null ? (
|
||||
<span className="text-yellow-600 font-medium">
|
||||
Skipped — Correct: {String.fromCharCode(65 + correctAnswer)}
|
||||
@ -68,7 +71,7 @@ export default function ResultsPage() {
|
||||
Correct Answer: {String.fromCharCode(65 + correctAnswer)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
@ -9,6 +9,9 @@ interface QuestionItemProps {
|
||||
index: number;
|
||||
selectedAnswer: Answer;
|
||||
onSelect: (answer: Answer) => void;
|
||||
userAnswer?: Answer; // new
|
||||
correctAnswer?: Answer; // new
|
||||
showResults?: boolean; // control whether to highlight or not
|
||||
}
|
||||
|
||||
const letters = ["A", "B", "C", "D"]; // extend if needed
|
||||
@ -18,6 +21,9 @@ const QuestionItem: React.FC<QuestionItemProps> = ({
|
||||
index,
|
||||
selectedAnswer,
|
||||
onSelect,
|
||||
userAnswer,
|
||||
correctAnswer,
|
||||
showResults = false,
|
||||
}) => {
|
||||
return (
|
||||
<div className="border border-blue-100 p-6 bg-slate-100 rounded-3xl mb-6">
|
||||
@ -25,10 +31,12 @@ const QuestionItem: React.FC<QuestionItemProps> = ({
|
||||
{index + 1}. {question.question}
|
||||
</p>
|
||||
|
||||
<div className="w-full flex justify-between">
|
||||
<div></div>
|
||||
<Bookmark size={24} />
|
||||
</div>
|
||||
{!showResults && (
|
||||
<div className="w-full flex justify-between">
|
||||
<div></div>
|
||||
<Bookmark size={24} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-col gap-3">
|
||||
{question.options.map((opt, optIdx) => {
|
||||
@ -38,10 +46,41 @@ const QuestionItem: React.FC<QuestionItemProps> = ({
|
||||
: Array.isArray(selectedAnswer) &&
|
||||
selectedAnswer.includes(optIdx);
|
||||
|
||||
// ✅ logic for coloring after results
|
||||
let btnClasses = "bg-gray-100 text-gray-900 border-gray-400";
|
||||
if (isSelected) {
|
||||
btnClasses = "bg-blue-600 text-white border-blue-600";
|
||||
}
|
||||
|
||||
if (showResults && correctAnswer !== undefined) {
|
||||
if (question.type === "Single") {
|
||||
if (userAnswer === optIdx && userAnswer !== correctAnswer) {
|
||||
btnClasses = "bg-red-500 text-white border-red-600"; // wrong
|
||||
}
|
||||
if (correctAnswer === optIdx) {
|
||||
btnClasses = "bg-green-500 text-white border-green-600"; // correct
|
||||
}
|
||||
} else {
|
||||
// Multi-select case
|
||||
const userSelected =
|
||||
Array.isArray(userAnswer) && userAnswer.includes(optIdx);
|
||||
const isCorrect =
|
||||
Array.isArray(correctAnswer) && correctAnswer.includes(optIdx);
|
||||
|
||||
if (userSelected && !isCorrect) {
|
||||
btnClasses = "bg-red-500 text-white border-red-600";
|
||||
}
|
||||
if (isCorrect) {
|
||||
btnClasses = "bg-green-500 text-white border-green-600";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={optIdx} className="flex items-center gap-3">
|
||||
<button
|
||||
onClick={() => {
|
||||
if (showResults) return; // disable changes in results mode
|
||||
if (question.type === "Single") {
|
||||
onSelect(optIdx);
|
||||
} else {
|
||||
@ -58,12 +97,8 @@ const QuestionItem: React.FC<QuestionItemProps> = ({
|
||||
}}
|
||||
className={`w-7 h-7 rounded-full border font-bold
|
||||
flex items-center justify-center
|
||||
${
|
||||
isSelected
|
||||
? "bg-blue-600 text-white border-blue-600"
|
||||
: "bg-gray-100 text-gray-900 border-gray-400"
|
||||
}
|
||||
hover:bg-blue-500 hover:text-white transition-colors`}
|
||||
${btnClasses}
|
||||
transition-colors`}
|
||||
>
|
||||
{letters[optIdx]}
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user