183 lines
6 KiB
TypeScript
183 lines
6 KiB
TypeScript
import { useState } from 'react'
|
|
import type { Question } from '@/types/questions'
|
|
import { QuestionType, isInputButtonsQuestion } from '@/types/questions'
|
|
import { Card, CardContent } from '@/components/ui/card'
|
|
import { Button } from '@/components/ui/button'
|
|
import { Badge } from '@/components/ui/badge'
|
|
import { Edit, Trash2, FileText, Type } from 'lucide-react'
|
|
import {
|
|
AlertDialog,
|
|
AlertDialogAction,
|
|
AlertDialogCancel,
|
|
AlertDialogContent,
|
|
AlertDialogDescription,
|
|
AlertDialogFooter,
|
|
AlertDialogHeader,
|
|
AlertDialogTitle,
|
|
} from '@/components/ui/alert-dialog'
|
|
|
|
interface TestQuestionsManagerProps {
|
|
questions: Question[]
|
|
onChange: (questions: Question[]) => void
|
|
onEdit: (question: Question, index: number) => void
|
|
disabled?: boolean
|
|
}
|
|
|
|
export function TestQuestionsManager({
|
|
questions,
|
|
onChange,
|
|
onEdit,
|
|
disabled = false,
|
|
}: TestQuestionsManagerProps) {
|
|
const [questionToDelete, setQuestionToDelete] = useState<{
|
|
question: Question
|
|
index: number
|
|
} | null>(null)
|
|
|
|
const handleDelete = (question: Question, index: number) => {
|
|
setQuestionToDelete({ question, index })
|
|
}
|
|
|
|
const confirmDelete = () => {
|
|
if (questionToDelete) {
|
|
const newQuestions = questions.filter((_, i) => i !== questionToDelete.index)
|
|
onChange(newQuestions)
|
|
setQuestionToDelete(null)
|
|
}
|
|
}
|
|
|
|
const getQuestionPreview = (question: Question): string => {
|
|
if (question.text) return question.text
|
|
if (question.image) return '📷 Image question'
|
|
return `Word: ${question.word}`
|
|
}
|
|
|
|
const getQuestionTypeLabel = (type: QuestionType): string => {
|
|
switch (type) {
|
|
case QuestionType.SIMPLE:
|
|
return 'Simple'
|
|
case QuestionType.INPUT_BUTTONS:
|
|
return 'Input Buttons'
|
|
default:
|
|
return type
|
|
}
|
|
}
|
|
|
|
const getQuestionTypeColor = (type: QuestionType): string => {
|
|
switch (type) {
|
|
case QuestionType.SIMPLE:
|
|
return 'bg-blue-500'
|
|
case QuestionType.INPUT_BUTTONS:
|
|
return 'bg-green-500'
|
|
default:
|
|
return 'bg-gray-500'
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h3 className="text-lg font-semibold">Questions</h3>
|
|
<p className="text-sm text-muted-foreground">
|
|
{questions.length} question{questions.length !== 1 ? 's' : ''} in this test
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
{questions.length === 0 ? (
|
|
<Card>
|
|
<CardContent className="pt-6">
|
|
<div className="text-center py-8">
|
|
<FileText className="mx-auto h-12 w-12 text-muted-foreground mb-4" />
|
|
<p className="text-sm text-muted-foreground">
|
|
No questions yet. Add your first question to get started.
|
|
</p>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
) : (
|
|
<div className="space-y-3">
|
|
{questions.map((question, index) => (
|
|
<Card key={question.id || index}>
|
|
<CardContent className="pt-6">
|
|
<div className="flex items-start justify-between">
|
|
<div className="flex-1 space-y-2">
|
|
<div className="flex items-center gap-2">
|
|
<Badge
|
|
className={`${getQuestionTypeColor(
|
|
question.questionType,
|
|
)} text-white border-transparent`}
|
|
>
|
|
<Type className="h-3 w-3 mr-1" />
|
|
{getQuestionTypeLabel(question.questionType)}
|
|
</Badge>
|
|
<span className="text-sm text-muted-foreground">
|
|
#{index + 1}
|
|
</span>
|
|
</div>
|
|
<div>
|
|
<p className="font-medium">{question.word}</p>
|
|
<p className="text-sm text-muted-foreground">
|
|
{getQuestionPreview(question)}
|
|
</p>
|
|
</div>
|
|
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
|
<span>{question.options.length} option(s)</span>
|
|
{isInputButtonsQuestion(question) && (
|
|
<span>• Template: {question.template}</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center gap-2 ml-4">
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={() => onEdit(question, index)}
|
|
disabled={disabled}
|
|
>
|
|
<Edit className="h-4 w-4" />
|
|
</Button>
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={() => handleDelete(question, index)}
|
|
disabled={disabled}
|
|
>
|
|
<Trash2 className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{/* Delete Confirmation Dialog */}
|
|
<AlertDialog
|
|
open={questionToDelete !== null}
|
|
onOpenChange={(open) => !open && setQuestionToDelete(null)}
|
|
>
|
|
<AlertDialogContent>
|
|
<AlertDialogHeader>
|
|
<AlertDialogTitle>Delete Question</AlertDialogTitle>
|
|
<AlertDialogDescription>
|
|
Are you sure you want to delete this question? This action cannot be
|
|
undone.
|
|
</AlertDialogDescription>
|
|
</AlertDialogHeader>
|
|
<AlertDialogFooter>
|
|
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
|
<AlertDialogAction
|
|
onClick={confirmDelete}
|
|
className="bg-red-600 hover:bg-red-700"
|
|
>
|
|
Delete
|
|
</AlertDialogAction>
|
|
</AlertDialogFooter>
|
|
</AlertDialogContent>
|
|
</AlertDialog>
|
|
</div>
|
|
)
|
|
}
|