import { useState, useEffect } from 'react' import type { Question } from '@/types/questions' import { questionToJson, questionFromJson } from '@/types/questions' import { Label } from '@/components/ui/label' import { Textarea } from '@/components/ui/textarea' import { Button } from '@/components/ui/button' import { Alert, AlertDescription } from '@/components/ui/alert' import { AlertCircle } from 'lucide-react' interface JSONQuestionEditorProps { question: Partial | null onChange: (question: Partial | null, isValid: boolean) => void } export function JSONQuestionEditor({ question, onChange, }: JSONQuestionEditorProps) { const [jsonText, setJsonText] = useState('') const [error, setError] = useState(null) const [isValid, setIsValid] = useState(false) // Инициализация JSON из вопроса useEffect(() => { if (question) { try { const json = questionToJson(question as Question) setJsonText(JSON.stringify(json, null, 2)) setError(null) setIsValid(true) } catch (e) { setError(`Failed to serialize question: ${e}`) setIsValid(false) } } else { setJsonText('') setError(null) setIsValid(false) } }, [question]) // Валидация и парсинг JSON const handleJsonChange = (value: string) => { setJsonText(value) if (!value.trim()) { setError(null) setIsValid(false) onChange(null, false) return } try { const parsed: unknown = JSON.parse(value) if (!parsed || typeof parsed !== 'object') { throw new Error('JSON must be an object') } const obj = parsed as Record // Базовая валидация структуры if (typeof obj.questionType !== 'string') { throw new Error('Missing required field: questionType') } const isMatrix = obj.questionType === 'matrix' if (!isMatrix && !obj.word) { throw new Error('Missing required field: word') } if (!isMatrix && !obj.answer) { throw new Error('Missing required field: answer') } if (!isMatrix && (!obj.buttons || !Array.isArray(obj.buttons))) { throw new Error('Missing or invalid field: buttons (must be an array)') } if (isMatrix && obj.matrixSize == null) { throw new Error('Missing required field for matrix: matrixSize') } // Проверка что answer существует в buttons if (!isMatrix) { const buttons = Array.isArray(obj.buttons) ? obj.buttons : [] const buttonIds = buttons .map((b) => { const bObj: Record = b && typeof b === 'object' ? (b as Record) : {} return typeof bObj.id === 'string' ? bObj.id : undefined }) .filter((id): id is string => typeof id === 'string') if (typeof obj.answer === 'string' && !buttonIds.includes(obj.answer)) { throw new Error( `Answer "${obj.answer}" not found in buttons. Available IDs: ${buttonIds.join(', ')}`, ) } } // Проверка для input_buttons if (obj.questionType === 'input_buttons' && !obj.template) { throw new Error('Missing required field for input_buttons: template') } // Парсинг в Question объект const questionObj = questionFromJson(obj) setError(null) setIsValid(true) onChange(questionObj, true) } catch (e) { const errorMessage = e instanceof Error ? e.message : 'Invalid JSON' setError(errorMessage) setIsValid(false) onChange(null, false) } } // Форматирование JSON const formatJson = () => { try { const parsed = JSON.parse(jsonText) setJsonText(JSON.stringify(parsed, null, 2)) handleJsonChange(JSON.stringify(parsed, null, 2)) } catch { // Если невалидный JSON, просто показываем ошибку } } return (