usmle-exam-system / src /components /ExamInterface.jsx
jianlizhao's picture
Upload 16 files
6fe52ee verified
Raw
History Blame Contribute Delete
4.41 kB
import React, { useState, useEffect } from 'react'
import QuestionPanel from './QuestionPanel'
import SidebarPanel from './SidebarPanel'
import { Clock, X } from 'lucide-react'
export default function ExamInterface({ examType, questions, onEndExam }) {
const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0)
const [timeRemaining, setTimeRemaining] = useState(getBlockTime(examType))
const [answers, setAnswers] = useState({})
const [markedForReview, setMarkedForReview] = useState(new Set())
const [contrast, setContrast] = useState(100)
const [showSettings, setShowSettings] = useState(false)
function getBlockTime(type) {
return type === 'step1' ? 30 * 60 : 40 * 60
}
useEffect(() => {
const timer = setInterval(() => {
setTimeRemaining(prev => {
if (prev <= 0) {
clearInterval(timer)
return 0
}
return prev - 1
})
}, 1000)
return () => clearInterval(timer)
}, [])
const handleAnswerSelect = (option) => {
setAnswers({
...answers,
[currentQuestionIndex]: option
})
}
const handleMarkForReview = () => {
const newSet = new Set(markedForReview)
if (newSet.has(currentQuestionIndex)) {
newSet.delete(currentQuestionIndex)
} else {
newSet.add(currentQuestionIndex)
}
setMarkedForReview(newSet)
}
const goToQuestion = (index) => {
setCurrentQuestionIndex(index)
}
const goToPrevious = () => {
if (currentQuestionIndex > 0) {
setCurrentQuestionIndex(currentQuestionIndex - 1)
}
}
const goToNext = () => {
if (currentQuestionIndex < questions.length - 1) {
setCurrentQuestionIndex(currentQuestionIndex + 1)
}
}
const formatTime = (seconds) => {
const mins = Math.floor(seconds / 60)
const secs = seconds % 60
return `${mins}:${secs.toString().padStart(2, '0')}`
}
const currentQuestion = questions[currentQuestionIndex]
const isAnswered = answers.hasOwnProperty(currentQuestionIndex)
const isMarked = markedForReview.has(currentQuestionIndex)
const answeredCount = Object.keys(answers).length
const markedCount = markedForReview.size
return (
<div className="exam-container">
{/* Header */}
<div className="exam-header">
<div className="flex justify-between items-center max-w-7xl mx-auto w-full">
<div>
<h1 className="text-2xl font-bold">USMLE {examType.toUpperCase()} Exam</h1>
<p className="text-blue-200">Block {Math.floor(currentQuestionIndex / 20) + 1} of {Math.ceil(questions.length / 20)}</p>
</div>
<div className="flex items-center gap-6">
<div className="flex items-center gap-2 bg-white bg-opacity-20 px-4 py-2 rounded-lg">
<Clock className="w-5 h-5" />
<div>
<div className="text-sm text-blue-200">Time Remaining</div>
<div className="text-2xl font-bold">{formatTime(timeRemaining)}</div>
</div>
</div>
<button
onClick={onEndExam}
className="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-lg flex items-center gap-2 transition-colors"
>
<X className="w-4 h-4" />
Exit Exam
</button>
</div>
</div>
</div>
{/* Main Content */}
<div className="exam-content">
<QuestionPanel
question={currentQuestion}
questionNumber={currentQuestionIndex + 1}
totalQuestions={questions.length}
selectedAnswer={answers[currentQuestionIndex]}
onAnswerSelect={handleAnswerSelect}
onMarkForReview={handleMarkForReview}
isMarked={isMarked}
onPrevious={goToPrevious}
onNext={goToNext}
canGoPrevious={currentQuestionIndex > 0}
canGoNext={currentQuestionIndex < questions.length - 1}
contrast={contrast}
/>
<SidebarPanel
totalQuestions={questions.length}
currentQuestionIndex={currentQuestionIndex}
answers={answers}
markedForReview={markedForReview}
onGoToQuestion={goToQuestion}
answeredCount={answeredCount}
markedCount={markedCount}
contrast={contrast}
onContrastChange={setContrast}
/>
</div>
</div>
)
}