Files
examjam-frontend/app/exam/results/page.tsx
2025-07-07 21:01:11 +06:00

152 lines
4.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import { useRouter } from "next/navigation";
import { useExam, useExamResults } from "@/context/ExamContext";
import { useEffect } from "react";
import React from "react";
import { ArrowLeft } from "lucide-react";
interface Question {
solution: string;
id: number;
question: string;
options: Record<string, string>;
}
interface QuestionItemProps {
question: Question;
selectedAnswer: string | undefined;
}
const QuestionItem = React.memo<QuestionItemProps>(
({ question, selectedAnswer }) => (
<div className="border border-[#8abdff]/50 rounded-2xl p-4 flex flex-col gap-7">
<h3 className="text-xl font-medium">
{question.id}. {question.question}
</h3>
<div className="flex flex-col gap-4 items-start">
{Object.entries(question.options).map(([key, value]) => (
<button key={key} className="flex items-center gap-3">
<span
className={`flex items-center rounded-full border px-1.5 ${
selectedAnswer === key
? "text-white bg-[#113768] border-[#113768]"
: ""
}`}
>
{key.toUpperCase()}
</span>
<span className="option-description">{value}</span>
</button>
))}
</div>
<div className="flex flex-col gap-4">
<h3 className="text-xl font-bold text-black/40">Solution:</h3>
<p className="text-lg font-medium">{question.solution}</p>
</div>
</div>
)
);
export default function ResultsPage() {
const router = useRouter();
const { clearExam, isExamCompleted, getApiResponse } = useExam();
useEffect(() => {
// Redirect if no completed exam
if (!isExamCompleted()) {
router.push("/unit");
return;
}
}, [isExamCompleted, router]);
let examResults;
try {
examResults = useExamResults();
} catch (error) {
// Handle case where there's no completed exam
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-center">
<div className="mt-60 flex flex-col items-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500 mb-4"></div>
<p className="text-xl font-medium text-center">Loading...</p>
</div>
</div>
</div>
);
}
// Get API response data
const apiResponse = getApiResponse();
const handleBackToHome = () => {
clearExam();
// Give time for state to fully reset before pushing new route
setTimeout(() => {
router.push("/unit");
}, 400); // 50100ms is usually enough
};
const timeTaken =
examResults.endTime && examResults.startTime
? Math.round(
(examResults.endTime.getTime() - examResults.startTime.getTime()) /
1000 /
60
)
: 0;
return (
<div className="min-h-screen bg-white">
<button className="p-10" onClick={() => router.push("/unit")}>
<ArrowLeft size={30} color="black" />
</button>
<div className="bg-white rounded-lg shadow-lg px-10 pb-20">
<h1 className="text-2xl font-bold text-gray-900 mb-2 text-center">
Keep up the good work!
</h1>
{/* Score Display */}
<div className="mb-8">
<div className="bg-blue-50/60 border border-[#113678]/50 rounded-4xl h-[150px] flex flex-col items-center justify-center">
<div className="text-xl text-black mb-2">Accuracy:</div>
<div className="text-5xl font-bold text-[#113678]">
{((examResults.score / examResults.totalQuestions) * 100).toFixed(
1
)}
%
</div>
</div>
</div>
{apiResponse && (
<div className="mb-8">
<h3 className="text-2xl font-bold text-[#113768] mb-4">
Solutions
</h3>
<div className="flex flex-col gap-7">
{apiResponse.questions?.map((question) => (
<QuestionItem
key={question.id}
question={question}
selectedAnswer={undefined}
/>
))}
</div>
</div>
)}
{/* Action Buttons */}
</div>
<button
onClick={handleBackToHome}
className="fixed bottom-0 w-full bg-blue-900 text-white h-[74px] font-bold text-lg disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
>
Finish
</button>
</div>
);
}