Gateprep / frontend /src /components /test /QuestionView.jsx
banu4prasad's picture
del checklist and add more admin controls
68c74e3
Raw
History Blame Contribute Delete
9.17 kB
import ChevronLeft from 'lucide-react/dist/esm/icons/chevron-left'
import ChevronRight from 'lucide-react/dist/esm/icons/chevron-right'
import Flag from 'lucide-react/dist/esm/icons/flag'
import clsx from 'clsx'
import MathText from '../shared/MathText'
export default function QuestionView({
answers,
clearResponse,
commitNAT,
current,
markAndNext,
natInput,
question,
questions,
saveAndNext,
setCurrent,
setMCQ,
setNatInput,
toggleMSQ,
}) {
return (
<div className="flex-1 flex flex-col overflow-hidden">
<div className="px-3 sm:px-4 py-1.5 text-xs border-b flex items-center flex-wrap gap-2 sm:gap-4 flex-shrink-0 theme-card-surface">
<span className="theme-muted">
Question Type:{' '}
<strong className="theme-text">
{question.question_type === 'mcq'
? 'MCQ Single'
: question.question_type === 'msq'
? 'MSQ Multiple'
: 'NAT Numerical'}
</strong>
</span>
<span className="theme-muted">
Marks: <strong className="text-green-400">+{question.marks}</strong>
{question.negative_marks > 0 && <span className="text-red-400 ml-1">/ -{question.negative_marks}</span>}
</span>
</div>
<div className="flex-1 overflow-y-auto p-4">
<p className="text-sm font-semibold mb-3 theme-text">
Question No. {current + 1}
</p>
<div className="mb-4">
<div className="leading-relaxed whitespace-pre-wrap theme-text">
<MathText>{question.question_text}</MathText>
</div>
{question.question_image_url && (
<img
src={question.question_image_url}
alt="question"
loading="eager"
fetchpriority="high"
decoding="async"
className="mt-3 w-full max-w-2xl max-h-64 object-contain rounded cursor-pointer border bg-slate-50 dark:bg-slate-800/50 aspect-[21/9] theme-border"
onClick={() => window.open(question.question_image_url, '_blank')}
/>
)}
</div>
{question.question_type === 'mcq' && (
<div className="space-y-2">
{question.options.map((option, i) => {
const letter = 'ABCD'[i]
const selected = answers[question.id] === letter
return (
<div
key={i}
onClick={() => setMCQ(letter)}
role="button"
tabIndex={0}
onKeyDown={(event) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault()
setMCQ(letter)
}
}}
className={clsx('q-option cursor-pointer', selected && 'selected', 'focus-visible:ring-2 focus-visible:ring-sky-500 focus-visible:outline-none')}
>
<div className={clsx(
'w-5 h-5 rounded-full border-2 flex items-center justify-center flex-shrink-0 mt-0.5',
selected ? 'border-sky-500 bg-sky-500' : 'border-slate-500'
)}>
{selected && <div className="w-2 h-2 rounded-full bg-white" />}
</div>
<div className="flex-1">
<span className="text-xs font-semibold mr-2 theme-muted">{letter}.</span>
<div className="text-sm theme-text"><MathText>{option}</MathText></div>
{question.option_images?.[letter] && (
<img
src={question.option_images[letter]}
alt={`option ${letter}`}
loading="lazy"
decoding="async"
className="mt-2 w-full max-w-sm max-h-32 object-contain rounded cursor-pointer bg-slate-50 dark:bg-slate-800/50 aspect-[21/9]"
onClick={event => {
event.stopPropagation()
window.open(question.option_images[letter], '_blank')
}}
/>
)}
</div>
</div>
)
})}
</div>
)}
{question.question_type === 'msq' && (
<div className="space-y-2">
<p className="text-xs text-amber-400 mb-2">One or more correct answers. No negative marking.</p>
{question.options.map((option, i) => {
const letter = 'ABCD'[i]
const selected = (answers[question.id] || '').split(',').includes(letter)
return (
<div
key={i}
onClick={() => toggleMSQ(letter)}
role="button"
tabIndex={0}
onKeyDown={(event) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault()
toggleMSQ(letter)
}
}}
className={clsx('q-option cursor-pointer', selected && 'selected-msq', 'focus-visible:ring-2 focus-visible:ring-sky-500 focus-visible:outline-none')}
>
<div className={clsx(
'w-5 h-5 rounded border-2 flex items-center justify-center flex-shrink-0 mt-0.5',
selected ? 'border-amber-500 bg-amber-500' : 'border-slate-500'
)}>
{selected && <span className="text-slate-900 dark:text-white text-xs font-bold"></span>}
</div>
<span className="text-xs font-semibold mr-2 theme-muted">{letter}.</span>
<div className="flex-1">
<div className="text-sm theme-text"><MathText>{option}</MathText></div>
{question.option_images?.[letter] && (
<img
src={question.option_images[letter]}
alt={`option ${letter}`}
loading="lazy"
decoding="async"
className="mt-2 w-full max-w-sm max-h-32 object-contain rounded cursor-pointer bg-slate-50 dark:bg-slate-800/50 aspect-[21/9]"
onClick={event => {
event.stopPropagation()
window.open(question.option_images[letter], '_blank')
}}
/>
)}
</div>
</div>
)
})}
</div>
)}
{question.question_type === 'nat' && (
<div>
<p className="text-xs text-green-400 mb-3">Enter numerical answer. No negative marking.</p>
<div className="flex gap-3 items-center max-w-xs">
<input
type="number"
step="any"
className="input font-mono text-lg flex-1"
value={natInput}
onChange={event => setNatInput(event.target.value)}
onBlur={commitNAT}
placeholder="Enter answer..."
aria-label="Numerical Answer"
/>
<button onClick={commitNAT} className="btn-primary px-4">Save</button>
</div>
{answers[question.id] && (
<p className="text-green-400 text-sm mt-2">
✓ Saved: <span className="font-mono font-semibold">{answers[question.id]}</span>
</p>
)}
</div>
)}
</div>
<div className="flex items-center justify-between px-2 sm:px-4 py-2 sm:py-2.5 border-t flex-shrink-0 gap-1 theme-card-surface">
<div className="flex items-center gap-1 sm:gap-2">
<button onClick={markAndNext} className="theme-panel-button flex items-center gap-1 sm:gap-1.5 px-2 sm:px-3 py-2.5 sm:py-2 rounded border text-xs font-medium">
<Flag size={14} className="text-purple-400" /> <span className="hidden sm:inline">Mark & Next</span>
</button>
<button onClick={clearResponse} className="theme-panel-button px-2 sm:px-3 py-2.5 sm:py-2 rounded border text-xs font-medium">
Clear
</button>
</div>
<div className="flex items-center gap-1 sm:gap-2">
<button
onClick={() => current > 0 && setCurrent(c => c - 1)}
disabled={current === 0}
className="theme-panel-button flex items-center gap-1 px-2 sm:px-3 py-2.5 sm:py-2 rounded border text-xs font-medium disabled:opacity-40"
>
<ChevronLeft size={13} /> <span className="hidden sm:inline">Prev</span>
</button>
<button onClick={saveAndNext} className="flex items-center gap-1 px-3 sm:px-4 py-2.5 sm:py-2 rounded text-xs font-semibold bg-sky-700 hover:bg-sky-600 text-white transition-colors">
Save & Next <ChevronRight size={13} />
</button>
</div>
</div>
</div>
)
}