generated from muhtadeetaron/nextjs-template
139 lines
4.3 KiB
TypeScript
139 lines
4.3 KiB
TypeScript
import { Question } from "@/types/exam";
|
|
import { BookmarkCheck, Bookmark } from "lucide-react";
|
|
import React, { useState } from "react";
|
|
import { Badge } from "./ui/badge";
|
|
|
|
interface ResultItemProps {
|
|
mode: "result";
|
|
question: Question;
|
|
selectedAnswer: { answer: string } | undefined;
|
|
}
|
|
|
|
interface ExamItemProps {
|
|
mode: "exam";
|
|
question: Question;
|
|
selectedAnswer?: string;
|
|
handleSelect: (questionId: number, option: string) => void;
|
|
}
|
|
|
|
type QuestionItemProps = ResultItemProps | ExamItemProps;
|
|
|
|
const QuestionItem = (props: QuestionItemProps) => {
|
|
const [bookmark, setBookmark] = useState(false);
|
|
|
|
const { question } = props;
|
|
|
|
const isExam = props.mode === "exam";
|
|
|
|
// Extract correct type-safe selectedAnswer
|
|
const selectedAnswer = isExam
|
|
? props.selectedAnswer
|
|
: props.selectedAnswer?.answer;
|
|
|
|
const handleOptionSelect = (key: string) => {
|
|
if (isExam && props.handleSelect) {
|
|
props.handleSelect(parseInt(question.id), key);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="border-[0.5px] border-[#8abdff]/60 rounded-2xl p-4 flex flex-col">
|
|
<h3 className="text-xl font-semibold ">
|
|
{question.id}. {question.question}
|
|
</h3>
|
|
|
|
{isExam && (
|
|
<div className="flex justify-between items-center mb-4">
|
|
<div></div>
|
|
<button onClick={() => setBookmark(!bookmark)}>
|
|
{bookmark ? (
|
|
<BookmarkCheck size={25} color="#113768" />
|
|
) : (
|
|
<Bookmark size={25} color="#113768" />
|
|
)}
|
|
</button>
|
|
</div>
|
|
)}
|
|
|
|
{isExam ? (
|
|
<div className="flex flex-col gap-4 items-start">
|
|
{Object.entries(question.options ?? {}).map(([key, value]) => {
|
|
const isSelected = selectedAnswer === key;
|
|
|
|
return (
|
|
<button
|
|
key={key}
|
|
className="flex items-center gap-3"
|
|
onClick={() => handleOptionSelect(key)}
|
|
>
|
|
<span
|
|
className={`flex items-center rounded-full border px-1.5 ${
|
|
isSelected ? "text-white bg-[#113768] border-[#113768]" : ""
|
|
}`}
|
|
>
|
|
{key.toUpperCase()}
|
|
</span>
|
|
<span className="option-description">{value}</span>
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
) : (
|
|
<div className="flex flex-col gap-3">
|
|
<div className="flex justify-between items-center">
|
|
<div></div>
|
|
|
|
{!selectedAnswer ? (
|
|
<Badge className="bg-yellow-500" variant="destructive">
|
|
Skipped
|
|
</Badge>
|
|
) : selectedAnswer === question.correctAnswer ? (
|
|
<Badge className="bg-green-500 text-white" variant="default">
|
|
Correct
|
|
</Badge>
|
|
) : (
|
|
<Badge className="bg-red-500 text-white" variant="default">
|
|
Incorrect
|
|
</Badge>
|
|
)}
|
|
</div>
|
|
|
|
<div className="flex flex-col gap-4 items-start">
|
|
{Object.entries(question.options ?? {}).map(([key, value]) => {
|
|
const isCorrect = key === question.correctAnswer;
|
|
const isSelected = key === selectedAnswer;
|
|
|
|
let optionStyle =
|
|
"px-2 py-1 flex items-center rounded-full border font-medium text-sm";
|
|
|
|
if (isCorrect) {
|
|
optionStyle += " bg-green-600 text-white border-green-600";
|
|
} else if (isSelected && !isCorrect) {
|
|
optionStyle += " bg-red-600 text-white border-red-600";
|
|
} else {
|
|
optionStyle += " border-gray-300 text-gray-700";
|
|
}
|
|
|
|
return (
|
|
<div key={key} className="flex items-center gap-3">
|
|
<span className={optionStyle}>{key.toUpperCase()}</span>
|
|
<span className="option-description">{value}</span>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
<div className="h-[0.5px] border-[0.5px] border-dashed border-black/20"></div>
|
|
|
|
<div className="flex flex-col gap-2">
|
|
<h3 className="text-lg font-bold text-black/40">Solution:</h3>
|
|
<p className="text-lg">{question.solution}</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default QuestionItem;
|