| 'use client'; |
|
|
| import { useState, useEffect } from 'react'; |
| import { GeneratedQuestion, QuestionType } from '@/types/quiz'; |
| import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@/components/ui/dialog'; |
| import { Button } from '@/components/ui/button'; |
| import { Label } from '@/components/ui/label'; |
| import { Textarea } from '@/components/ui/textarea'; |
| import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; |
|
|
| export interface QuestionEditModalProps { |
| question: GeneratedQuestion | null; |
| questionTypes: QuestionType[]; |
| isOpen: boolean; |
| onClose: () => void; |
| onSave: (updatedQuestion: GeneratedQuestion) => void; |
| } |
|
|
| export default function QuestionEditModal({ |
| question, |
| questionTypes, |
| isOpen, |
| onClose, |
| onSave, |
| }: QuestionEditModalProps) { |
| const [editedQuestion, setEditedQuestion] = useState<GeneratedQuestion | null>(null); |
| const [isSaving, setIsSaving] = useState(false); |
|
|
| |
| useEffect(() => { |
| if (question) { |
| setEditedQuestion({ ...question }); |
| } |
| }, [question]); |
|
|
| const handleSave = async () => { |
| if (!editedQuestion) return; |
| |
| |
| if (!editedQuestion.stem.trim()) { |
| alert('Please enter a question'); |
| return; |
| } |
| |
| if (!editedQuestion.content.Options.A.trim() || |
| !editedQuestion.content.Options.B.trim() || |
| !editedQuestion.content.Options.C.trim() || |
| !editedQuestion.content.Options.D.trim()) { |
| alert('Please fill in all answer options'); |
| return; |
| } |
| |
| setIsSaving(true); |
| try { |
| |
| const response = await fetch('/api/update-question', { |
| method: 'PUT', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify(editedQuestion), |
| }); |
|
|
| if (response.ok) { |
| const updatedQuestion = await response.json(); |
| onSave(updatedQuestion); |
| onClose(); |
| } else { |
| const errorData = await response.json(); |
| alert(`Failed to update question: ${errorData.error || 'Unknown error'}`); |
| } |
| } catch (error) { |
| console.error('Error updating question:', error); |
| alert('Failed to update question. Please try again.'); |
| } finally { |
| setIsSaving(false); |
| } |
| }; |
|
|
| const handleCancel = () => { |
| setEditedQuestion(question ? { ...question } : null); |
| onClose(); |
| }; |
|
|
| const updateQuestionField = (field: string, value: string | number | boolean) => { |
| if (!editedQuestion) return; |
| |
| setEditedQuestion(prev => { |
| if (!prev) return null; |
| |
| if (field === 'stem') { |
| return { ...prev, stem: String(value) }; |
| } else if (field.startsWith('content.Options.')) { |
| const optionKey = field.replace('content.Options.', ''); |
| return { |
| ...prev, |
| content: { |
| ...prev.content, |
| Options: { |
| ...prev.content.Options, |
| [optionKey]: String(value) |
| } |
| } |
| }; |
| } else if (field.startsWith('content.')) { |
| const contentField = field.replace('content.', ''); |
| return { |
| ...prev, |
| content: { |
| ...prev.content, |
| [contentField]: String(value) |
| } |
| }; |
| } |
| |
| return prev; |
| }); |
| }; |
|
|
| if (!editedQuestion) return null; |
|
|
| return ( |
| <Dialog open={isOpen} onOpenChange={onClose}> |
| <DialogContent className="max-w-4xl max-h-[90vh] overflow-y-auto"> |
| <DialogHeader> |
| <DialogTitle>Edit Question</DialogTitle> |
| </DialogHeader> |
| |
| <div className="space-y-6 py-4"> |
| {/* Question Type - Read Only */} |
| <div className="space-y-2"> |
| <Label htmlFor="question-type">Question Type</Label> |
| <div className="flex items-center space-x-2 p-3 bg-gray-50 dark:bg-gray-800 rounded-md border w-full"> |
| <span className="text-lg flex-shrink-0"> |
| {questionTypes.find(t => t.id === editedQuestion.type)?.icon || '📝'} |
| </span> |
| <span className="text-sm font-medium break-words"> |
| {questionTypes.find(t => t.id === editedQuestion.type)?.name || 'Unknown Type'} |
| </span> |
| </div> |
| </div> |
| |
| {/* Points - Read Only */} |
| <div className="space-y-2"> |
| <Label htmlFor="question-points">Points</Label> |
| <div className="flex items-center space-x-2 p-3 bg-gray-50 dark:bg-gray-800 rounded-md border w-20"> |
| <span className="text-sm font-medium">{editedQuestion.points} pts</span> |
| </div> |
| </div> |
| |
| {/* Question Stem */} |
| <div className="space-y-2"> |
| <Label htmlFor="question-stem">Question</Label> |
| <Textarea |
| id="question-stem" |
| value={editedQuestion.stem} |
| onChange={(e) => updateQuestionField('stem', e.target.value)} |
| placeholder="Enter your question here..." |
| className="min-h-[120px] w-full resize-y" |
| rows={4} |
| /> |
| </div> |
| |
| {/* Options */} |
| <div className="space-y-4"> |
| <Label>Options</Label> |
| <div className="grid grid-cols-1 gap-4"> |
| {Object.entries(editedQuestion.content.Options).map(([key, value]) => ( |
| <div key={key} className="flex items-start space-x-3"> |
| <Label className="w-8 text-sm font-medium mt-2">{key})</Label> |
| <Textarea |
| value={value} |
| onChange={(e) => updateQuestionField(`content.Options.${key}`, e.target.value)} |
| placeholder={`Option ${key}`} |
| className="flex-1 min-h-[40px] resize-y" |
| rows={2} |
| /> |
| </div> |
| ))} |
| </div> |
| </div> |
| |
| {/* Correct Answer */} |
| <div className="space-y-2"> |
| <Label htmlFor="correct-answer">Correct Answer</Label> |
| <Select |
| value={editedQuestion.content.Answer} |
| onValueChange={(value) => updateQuestionField('content.Answer', value)} |
| > |
| <SelectTrigger className="w-full"> |
| <SelectValue placeholder="Select correct answer" /> |
| </SelectTrigger> |
| <SelectContent className="max-w-full"> |
| {Object.keys(editedQuestion.content.Options).map((key) => ( |
| <SelectItem key={key} value={key} className="whitespace-normal"> |
| <span className="font-medium">{key})</span> {editedQuestion.content.Options[key as keyof typeof editedQuestion.content.Options]} |
| </SelectItem> |
| ))} |
| </SelectContent> |
| </Select> |
| </div> |
| </div> |
| |
| <DialogFooter> |
| <Button |
| variant="outline" |
| onClick={handleCancel} |
| disabled={isSaving} |
| > |
| Cancel |
| </Button> |
| <Button |
| onClick={handleSave} |
| disabled={isSaving} |
| > |
| {isSaving ? 'Saving...' : 'Save Changes'} |
| </Button> |
| </DialogFooter> |
| </DialogContent> |
| </Dialog> |
| ); |
| } |
|
|