251 lines
7.8 KiB
TypeScript
251 lines
7.8 KiB
TypeScript
import { useState, useEffect, useRef } from 'react'
|
|
import type { InputButtonsQuestion, TestButton } from '@/types/questions'
|
|
import { QuestionType } from '@/types/questions'
|
|
import { Label } from '@/components/ui/label'
|
|
import { Input } from '@/components/ui/input'
|
|
import { Textarea } from '@/components/ui/textarea'
|
|
import { Button } from '@/components/ui/button'
|
|
import { ImageUpload } from '@/components/ui/image-upload'
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from '@/components/ui/select'
|
|
import { Plus, Trash2 } from 'lucide-react'
|
|
import { Card, CardContent } from '@/components/ui/card'
|
|
|
|
interface InputButtonsQuestionFormProps {
|
|
question: Partial<InputButtonsQuestion>
|
|
onChange: (question: Partial<InputButtonsQuestion>) => void
|
|
}
|
|
|
|
export function InputButtonsQuestionForm({
|
|
question,
|
|
onChange,
|
|
}: InputButtonsQuestionFormProps) {
|
|
const [word, setWord] = useState(question.word || '')
|
|
const [text, setText] = useState(question.text || '')
|
|
const [image, setImage] = useState(question.image || '')
|
|
const [audio, setAudio] = useState(question.audio || '')
|
|
const [buttons, setButtons] = useState<TestButton[]>(question.options || [])
|
|
const [answer, setAnswer] = useState(question.answer || '')
|
|
const [template, setTemplate] = useState(question.template || '')
|
|
const onChangeRef = useRef(onChange)
|
|
|
|
// Keep onChange ref up to date
|
|
useEffect(() => {
|
|
onChangeRef.current = onChange
|
|
}, [onChange])
|
|
|
|
// Notify parent of changes
|
|
useEffect(() => {
|
|
onChangeRef.current({
|
|
questionType: QuestionType.INPUT_BUTTONS,
|
|
word,
|
|
text: text || undefined,
|
|
image: image || undefined,
|
|
audio: audio || undefined,
|
|
options: buttons,
|
|
answer,
|
|
template,
|
|
})
|
|
}, [word, text, image, audio, buttons, answer, template])
|
|
|
|
const addButton = () => {
|
|
const newButton: TestButton = {
|
|
id: `btn_${Date.now()}`,
|
|
text: '',
|
|
}
|
|
setButtons([...buttons, newButton])
|
|
}
|
|
|
|
const removeButton = (index: number) => {
|
|
setButtons(buttons.filter((_, i) => i !== index))
|
|
if (answer === buttons[index]?.id) {
|
|
setAnswer('')
|
|
}
|
|
}
|
|
|
|
const updateButton = (index: number, updates: Partial<TestButton>) => {
|
|
const newButtons = [...buttons]
|
|
newButtons[index] = { ...newButtons[index], ...updates }
|
|
setButtons(newButtons)
|
|
}
|
|
|
|
// Генерация template из answer
|
|
const generateTemplate = () => {
|
|
if (answer) {
|
|
setTemplate(answer.replace(/[^ ]/g, '_'))
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="word">
|
|
Word <span className="text-red-500">*</span>
|
|
</Label>
|
|
<Input
|
|
id="word"
|
|
value={word}
|
|
onChange={(e) => setWord(e.target.value)}
|
|
placeholder="Word or phrase to learn"
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="text">Question Text</Label>
|
|
<Textarea
|
|
id="text"
|
|
value={text}
|
|
onChange={(e) => setText(e.target.value)}
|
|
placeholder="Optional question text"
|
|
rows={3}
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<ImageUpload
|
|
label="Question Image"
|
|
value={image}
|
|
onChange={(value) => setImage(value || '')}
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="audio">Audio URL</Label>
|
|
<Input
|
|
id="audio"
|
|
value={audio}
|
|
onChange={(e) => setAudio(e.target.value)}
|
|
placeholder="https://..."
|
|
type="url"
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<div className="flex items-center justify-between">
|
|
<Label>
|
|
Answer Options <span className="text-red-500">*</span>
|
|
</Label>
|
|
<Button type="button" variant="outline" size="sm" onClick={addButton}>
|
|
<Plus className="h-4 w-4 mr-2" />
|
|
Add Button
|
|
</Button>
|
|
</div>
|
|
|
|
{buttons.length === 0 ? (
|
|
<Card>
|
|
<CardContent className="pt-6">
|
|
<p className="text-sm text-muted-foreground text-center py-4">
|
|
No buttons yet. Add at least one button.
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
) : (
|
|
<div className="space-y-3">
|
|
{buttons.map((button, index) => (
|
|
<Card key={button.id}>
|
|
<CardContent className="pt-6">
|
|
<div className="space-y-3">
|
|
<div className="flex items-center justify-between">
|
|
<Label className="text-sm font-medium">
|
|
Button {index + 1} (ID: {button.id})
|
|
</Label>
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={() => removeButton(index)}
|
|
>
|
|
<Trash2 className="h-4 w-4" />
|
|
</Button>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 gap-3">
|
|
<div className="space-y-2">
|
|
<Label htmlFor={`button-text-${index}`}>Text</Label>
|
|
<Input
|
|
id={`button-text-${index}`}
|
|
value={button.text || ''}
|
|
onChange={(e) =>
|
|
updateButton(index, { text: e.target.value })
|
|
}
|
|
placeholder="Button text"
|
|
/>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<ImageUpload
|
|
label="Image"
|
|
value={button.image || ''}
|
|
onChange={(value) =>
|
|
updateButton(index, { image: value || undefined })
|
|
}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<Label htmlFor="answer">
|
|
Correct Answer <span className="text-red-500">*</span>
|
|
</Label>
|
|
<Select value={answer} onValueChange={setAnswer} required>
|
|
<SelectTrigger id="answer">
|
|
<SelectValue placeholder="Select correct answer" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{buttons.map((button) => (
|
|
<SelectItem key={button.id} value={button.id}>
|
|
{button.text || button.image || button.id}
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
{buttons.length === 0 && (
|
|
<p className="text-xs text-muted-foreground">
|
|
Add at least one button to select an answer
|
|
</p>
|
|
)}
|
|
</div>
|
|
|
|
<div className="space-y-2">
|
|
<div className="flex items-center justify-between">
|
|
<Label htmlFor="template">
|
|
Template <span className="text-red-500">*</span>
|
|
</Label>
|
|
{answer && (
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={generateTemplate}
|
|
>
|
|
Generate from answer
|
|
</Button>
|
|
)}
|
|
</div>
|
|
<Input
|
|
id="template"
|
|
value={template}
|
|
onChange={(e) => setTemplate(e.target.value)}
|
|
placeholder="e.g., a___e (underscores for letters to input)"
|
|
required
|
|
/>
|
|
<p className="text-xs text-muted-foreground">
|
|
Template defines which letters the user needs to input. Use underscores
|
|
(_) for letters to input, spaces for word separators.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|