"use client"; import { useState } from "react"; import { useRouter } from "next/navigation"; import { Plus, Trash2, Save } from "lucide-react"; import { quizDefinitionSchema, DEFAULT_QUIZ_DEFINITION, type QuizDefinition, type QuizQuestion } from "@/lib/games/quiz"; export function QuizBuilder({ mode, gameId, initialTitle, initialDescription, initialDefinition, }: { mode: "create" | "edit"; gameId?: string; initialTitle?: string; initialDescription?: string; initialDefinition?: QuizDefinition; }) { const router = useRouter(); const [title, setTitle] = useState(initialTitle ?? ""); const [description, setDescription] = useState(initialDescription ?? ""); const [questions, setQuestions] = useState( initialDefinition?.questions ?? DEFAULT_QUIZ_DEFINITION.questions, ); const [timePerQuestionSec, setTimePerQuestionSec] = useState(initialDefinition?.timePerQuestionSec ?? 0); const [error, setError] = useState(null); const [saving, setSaving] = useState(false); function updateQuestion(index: number, patch: Partial) { setQuestions((qs) => qs.map((q, i) => (i === index ? { ...q, ...patch } : q))); } function updateOption(qIndex: number, optIndex: number, value: string) { setQuestions((qs) => qs.map((q, i) => (i === qIndex ? { ...q, options: q.options.map((o, j) => (j === optIndex ? value : o)) } : q)), ); } function addQuestion() { setQuestions((qs) => [...qs, { question: "", options: ["", ""], correctIndex: 0 }]); } function removeQuestion(index: number) { setQuestions((qs) => qs.filter((_, i) => i !== index)); } function addOption(qIndex: number) { setQuestions((qs) => qs.map((q, i) => (i === qIndex && q.options.length < 6 ? { ...q, options: [...q.options, ""] } : q))); } function removeOption(qIndex: number, optIndex: number) { setQuestions((qs) => qs.map((q, i) => { if (i !== qIndex || q.options.length <= 2) return q; const options = q.options.filter((_, j) => j !== optIndex); const correctIndex = q.correctIndex >= options.length ? 0 : q.correctIndex; return { ...q, options, correctIndex }; }), ); } async function handleSave() { setError(null); const definition: QuizDefinition = { questions, timePerQuestionSec }; const parsed = quizDefinitionSchema.safeParse(definition); if (!parsed.success) { setError(parsed.error.issues[0]?.message ?? "Проверьте вопросы — что-то заполнено неверно"); return; } if (!title.trim()) { setError("Укажите название игры"); return; } setSaving(true); const url = mode === "create" ? "/api/games" : `/api/games/${gameId}`; const method = mode === "create" ? "POST" : "PATCH"; const body = mode === "create" ? { title, description: description || undefined, templateType: "quiz", definition: parsed.data } : { title, description: description || null, definition: parsed.data }; const res = await fetch(url, { method, headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); setSaving(false); if (!res.ok) { const data = await res.json().catch(() => null); setError(data?.error ?? "Не удалось сохранить"); return; } if (mode === "create") { const { game } = await res.json(); router.push(`/my-games/${game.id}/edit`); } else { router.refresh(); } } return (
{questions.map((q, qIndex) => (
Вопрос {qIndex + 1} {questions.length > 1 && ( )}
updateQuestion(qIndex, { question: e.target.value })} placeholder="Текст вопроса" className="w-full rounded-md border border-border bg-surface px-3 py-2 text-sm outline-none focus:border-accent" /> {q.options.map((option, optIndex) => (
updateQuestion(qIndex, { correctIndex: optIndex })} title="Правильный ответ" /> updateOption(qIndex, optIndex, e.target.value)} placeholder={`Вариант ${optIndex + 1}`} className="flex-1 rounded-md border border-border bg-surface px-3 py-1.5 text-sm outline-none focus:border-accent" /> {q.options.length > 2 && ( )}
))} {q.options.length < 6 && ( )}
))} {error &&

{error}

}
); }