Shih-hungg's picture
Fix type errors
c7d096e
'use client';
import { GeneratedQuestion, QuestionType } from '@/types/quiz';
import { Card, CardContent, CardHeader } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { cn } from '@/lib/utils';
export interface QuestionPreviewProps {
question: GeneratedQuestion;
questionType?: QuestionType;
showActions?: boolean;
onEdit?: (question: GeneratedQuestion) => void;
onDuplicate?: (question: GeneratedQuestion) => void;
onRemove?: (questionId: string) => void;
className?: string;
}
export default function QuestionPreview({
question,
questionType,
showActions = true,
onEdit,
onDuplicate,
onRemove,
className = '',
}: QuestionPreviewProps) {
const renderQuestionContent = () => {
switch (question.type) {
case 'multiple-choice':
return (
<MultipleChoicePreview
question={question.stem}
options={question.content?.Options || { A: '', B: '', C: '', D: '' }}
correctAnswer={question.content?.Answer || 'A'}
explanation={undefined}
/>
);
case 'cloze':
return (
<ClozePreview
question={question.stem}
blanks={[]}
explanation={undefined}
/>
);
case 'grammar':
return (
<GrammarPreview
question={question.stem}
errors={[]}
explanation={undefined}
/>
);
case 'reading-comp':
return (
<ReadingCompPreview
passage={''}
question={question.stem}
answer={''}
explanation={undefined}
/>
);
case 'essay':
return (
<EssayPreview
prompt={question.stem}
guidelines={[]}
rubric={[]}
/>
);
default:
return (
<div className="text-gray-600 dark:text-gray-400">
<p>{question.stem}</p>
</div>
);
}
};
return (
<Card className={cn(className)}>
<CardHeader className="border-b">
<div className="flex items-start justify-between">
<div>
<div className="flex items-center space-x-2 mb-1">
<Badge variant="default">
{questionType?.name || 'Question'}
</Badge>
<span className="text-xs text-muted-foreground">
{question.points} points
</span>
</div>
<p className="text-xs text-muted-foreground">
Created: {new Date(question.createdAt).toLocaleString()}
</p>
</div>
{showActions && (
<div className="flex items-center space-x-2">
{onEdit && (
<Button
onClick={() => onEdit(question)}
variant="outline"
size="sm"
>
Edit
</Button>
)}
{onDuplicate && (
<Button
onClick={() => onDuplicate(question)}
variant="outline"
size="sm"
className="text-green-600 hover:text-green-700"
>
Duplicate
</Button>
)}
{onRemove && (
<Button
onClick={() => onRemove(question.id)}
variant="outline"
size="sm"
className="text-destructive hover:text-destructive"
>
Remove
</Button>
)}
</div>
)}
</div>
</CardHeader>
{/* Content */}
<CardContent className="p-4">
{renderQuestionContent()}
</CardContent>
</Card>
);
}
// Individual question type preview components
function MultipleChoicePreview({ question, options, correctAnswer, explanation }: {
question: string;
options: { A: string; B: string; C: string; D: string };
correctAnswer: string;
explanation?: string;
}) {
return (
<div className="space-y-4">
<div>
<h3 className="font-medium text-gray-900 dark:text-white mb-2">Question:</h3>
<p className="text-gray-700 dark:text-gray-300">{question}</p>
</div>
<div>
<h4 className="font-medium text-gray-900 dark:text-white mb-2">Options:</h4>
<div className="space-y-1">
{Object.entries(options).map(([key, value]) => (
<div
key={key}
className={`p-2 rounded ${
key === correctAnswer
? 'bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-700'
: 'bg-gray-50 dark:bg-gray-700'
}`}
>
<span className="font-medium mr-2">{key}.</span>
{value}
{key === correctAnswer && (
<span className="ml-2 text-green-600 dark:text-green-400 text-sm">✓ Correct</span>
)}
</div>
))}
</div>
</div>
{explanation && (
<div>
<h4 className="font-medium text-gray-900 dark:text-white mb-2">Explanation:</h4>
<p className="text-gray-600 dark:text-gray-400 text-sm">{explanation}</p>
</div>
)}
</div>
);
}
function ClozePreview({ question, blanks, explanation }: {
question: string;
blanks: { text: string; answer?: string }[];
explanation?: string;
}) {
return (
<div className="space-y-4">
<div>
<h3 className="font-medium text-gray-900 dark:text-white mb-2">Question:</h3>
<p className="text-gray-700 dark:text-gray-300">{question}</p>
</div>
{blanks.length > 0 && (
<div>
<h4 className="font-medium text-gray-900 dark:text-white mb-2">Answers:</h4>
<div className="space-y-1">
{blanks.map((blank: { text: string; answer?: string }, index: number) => (
<div key={index} className="bg-gray-50 dark:bg-gray-700 p-2 rounded">
<span className="font-medium">Blank {index + 1}:</span> {blank.answer}
</div>
))}
</div>
</div>
)}
{explanation && (
<div>
<h4 className="font-medium text-gray-900 dark:text-white mb-2">Explanation:</h4>
<p className="text-gray-600 dark:text-gray-400 text-sm">{explanation}</p>
</div>
)}
</div>
);
}
function GrammarPreview({ question, errors, explanation }: {
question: string;
errors: { text: string; correction?: string }[];
explanation?: string;
}) {
return (
<div className="space-y-4">
<div>
<h3 className="font-medium text-gray-900 dark:text-white mb-2">Sentence to Correct:</h3>
<p className="text-gray-700 dark:text-gray-300">{question}</p>
</div>
{errors.length > 0 && (
<div>
<h4 className="font-medium text-gray-900 dark:text-white mb-2">Errors & Corrections:</h4>
<div className="space-y-2">
{errors.map((error: { text: string; correction?: string }, index: number) => (
<div key={index} className="bg-red-50 dark:bg-red-900/20 p-3 rounded border border-red-200 dark:border-red-700">
<p className="text-sm"><strong>Error:</strong> {error.text}</p>
<p className="text-sm"><strong>Correction:</strong> {error.correction}</p>
</div>
))}
</div>
</div>
)}
{explanation && (
<div>
<h4 className="font-medium text-gray-900 dark:text-white mb-2">Explanation:</h4>
<p className="text-gray-600 dark:text-gray-400 text-sm">{explanation}</p>
</div>
)}
</div>
);
}
function ReadingCompPreview({ passage, question, answer, explanation }: {
passage: string;
question: string;
answer: string;
explanation?: string;
}) {
return (
<div className="space-y-4">
<div>
<h3 className="font-medium text-gray-900 dark:text-white mb-2">Reading Passage:</h3>
<div className="bg-gray-50 dark:bg-gray-700 p-3 rounded">
<p className="text-gray-700 dark:text-gray-300 text-sm leading-relaxed">{passage}</p>
</div>
</div>
<div>
<h3 className="font-medium text-gray-900 dark:text-white mb-2">Question:</h3>
<p className="text-gray-700 dark:text-gray-300">{question}</p>
</div>
{answer && (
<div>
<h4 className="font-medium text-gray-900 dark:text-white mb-2">Answer:</h4>
<p className="bg-green-50 dark:bg-green-900/20 p-2 rounded text-gray-700 dark:text-gray-300">{answer}</p>
</div>
)}
{explanation && (
<div>
<h4 className="font-medium text-gray-900 dark:text-white mb-2">Explanation:</h4>
<p className="text-gray-600 dark:text-gray-400 text-sm">{explanation}</p>
</div>
)}
</div>
);
}
function EssayPreview({ prompt, guidelines, rubric }: {
prompt: string;
guidelines: string[];
rubric: { criteria: string; points: number }[];
}) {
return (
<div className="space-y-4">
<div>
<h3 className="font-medium text-gray-900 dark:text-white mb-2">Essay Prompt:</h3>
<p className="text-gray-700 dark:text-gray-300">{prompt}</p>
</div>
{guidelines.length > 0 && (
<div>
<h4 className="font-medium text-gray-900 dark:text-white mb-2">Guidelines:</h4>
<ul className="space-y-1">
{guidelines.map((guideline: string, index: number) => (
<li key={index} className="text-gray-600 dark:text-gray-400 text-sm flex items-start">
<span className="mr-2"></span>
{guideline}
</li>
))}
</ul>
</div>
)}
{rubric && rubric.length > 0 && (
<div>
<h4 className="font-medium text-gray-900 dark:text-white mb-2">Grading Rubric:</h4>
<div className="bg-gray-50 dark:bg-gray-700 p-3 rounded">
{rubric.map((item, index) => (
<div key={index} className="flex justify-between items-center mb-1">
<span className="text-gray-600 dark:text-gray-400 text-sm">{item.criteria}</span>
<span className="text-gray-900 dark:text-white text-sm font-medium">{item.points} points</span>
</div>
))}
</div>
</div>
)}
</div>
);
}