import React, { useState, useEffect, useCallback } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { ArrowLeft, Save, Plus, Play, AlertTriangle, List, Settings } from 'lucide-react'; import { DndContext, closestCenter, KeyboardSensor, PointerSensor, useSensor, useSensors, DragEndEvent } from '@dnd-kit/core'; import { arrayMove, SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy } from '@dnd-kit/sortable'; import { Quiz, Question, GameConfig, DEFAULT_GAME_CONFIG } from '../types'; import { SortableQuestionCard } from './SortableQuestionCard'; import { QuestionEditModal } from './QuestionEditModal'; import { GameConfigPanel } from './GameConfigPanel'; import { useBodyScrollLock } from '../hooks/useBodyScrollLock'; import { v4 as uuidv4 } from 'uuid'; interface QuizEditorProps { quiz: Quiz; onSave: (quiz: Quiz) => void; onStartGame: (quiz: Quiz, config: GameConfig) => void; onConfigChange?: (config: GameConfig) => void; onBack: () => void; showSaveButton?: boolean; isSaving?: boolean; defaultConfig?: GameConfig; maxPlayersLimit?: number; } export const QuizEditor: React.FC = ({ quiz: initialQuiz, onSave, onStartGame, onConfigChange, onBack, showSaveButton = true, isSaving, defaultConfig, maxPlayersLimit = 150, }) => { const [quiz, setQuiz] = useState(initialQuiz); const [expandedId, setExpandedId] = useState(null); const [editingQuestion, setEditingQuestion] = useState(null); const [showDeleteConfirm, setShowDeleteConfirm] = useState(null); const [titleEditing, setTitleEditing] = useState(false); const [config, setConfig] = useState( initialQuiz.config || defaultConfig || DEFAULT_GAME_CONFIG ); const [hasAppliedDefaultConfig, setHasAppliedDefaultConfig] = useState(!!initialQuiz.config); const [activeTab, setActiveTab] = useState<'questions' | 'settings'>('questions'); useEffect(() => { if (!hasAppliedDefaultConfig && defaultConfig && defaultConfig !== DEFAULT_GAME_CONFIG) { setConfig(defaultConfig); setHasAppliedDefaultConfig(true); } }, [defaultConfig, hasAppliedDefaultConfig]); useBodyScrollLock(!!showDeleteConfirm); const handleConfigChange = useCallback((newConfig: GameConfig) => { setConfig(newConfig); setHasAppliedDefaultConfig(true); onConfigChange?.(newConfig); }, [onConfigChange]); const sensors = useSensors( useSensor(PointerSensor, { activationConstraint: { distance: 8 } }), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }) ); const handleDragEnd = (event: DragEndEvent) => { const { active, over } = event; if (over && active.id !== over.id) { setQuiz(prev => { const oldIndex = prev.questions.findIndex(q => q.id === active.id); const newIndex = prev.questions.findIndex(q => q.id === over.id); return { ...prev, questions: arrayMove(prev.questions, oldIndex, newIndex) }; }); } }; const handleTitleChange = (newTitle: string) => { setQuiz(prev => ({ ...prev, title: newTitle })); }; const handleQuestionUpdate = (updated: Question) => { setQuiz(prev => ({ ...prev, questions: prev.questions.map(q => q.id === updated.id ? updated : q) })); setEditingQuestion(null); }; const handleQuestionDelete = (id: string) => { if (quiz.questions.length <= 1) { return; } setQuiz(prev => ({ ...prev, questions: prev.questions.filter(q => q.id !== id) })); setShowDeleteConfirm(null); }; const handleAddQuestion = () => { const shapes = ['triangle', 'diamond', 'circle', 'square'] as const; const colors = ['red', 'blue', 'yellow', 'green'] as const; const newQuestion: Question = { id: uuidv4(), text: '', options: shapes.map((shape, idx) => ({ text: '', isCorrect: idx === 0, shape, color: colors[idx], reason: '' })), timeLimit: 20 }; setQuiz(prev => ({ ...prev, questions: [...prev.questions, newQuestion] })); setEditingQuestion(newQuestion); }; const canStartGame = quiz.title.trim() && quiz.questions.length > 0 && quiz.questions.every(q => q.text.trim() && q.options.every(o => o.text.trim()) && q.options.some(o => o.isCorrect) ); const handleStartGame = () => { let questions = [...quiz.questions]; if (config.shuffleQuestions) { questions = questions.sort(() => Math.random() - 0.5); } if (config.shuffleAnswers) { const shapes = ['triangle', 'diamond', 'circle', 'square'] as const; const colors = ['red', 'blue', 'yellow', 'green'] as const; questions = questions.map(q => { const shuffledOptions = [...q.options].sort(() => Math.random() - 0.5); return { ...q, options: shuffledOptions.map((opt, idx) => ({ ...opt, shape: shapes[idx % 4], color: colors[idx % 4] })) }; }); } onStartGame({ ...quiz, questions, config }, config); }; return (
{titleEditing ? ( handleTitleChange(e.target.value)} onBlur={() => setTitleEditing(false)} onKeyDown={(e) => e.key === 'Enter' && setTitleEditing(false)} autoFocus className="bg-white/20 text-white text-xl md:text-3xl font-black text-center w-full max-w-md px-2 md:px-4 py-1 md:py-2 rounded-xl outline-none placeholder:text-white/50" placeholder="Quiz Title" /> ) : (

setTitleEditing(true)} className="text-xl md:text-3xl font-black cursor-pointer hover:bg-white/10 px-2 md:px-4 py-1 md:py-2 rounded-xl transition inline-block" > {quiz.title || 'Untitled Quiz'}

)}

{quiz.questions.length} question{quiz.questions.length !== 1 ? 's' : ''}

{showSaveButton && ( )} {!showSaveButton &&
}
{activeTab === 'questions' ? ( <>

Questions

q.id)} strategy={verticalListSortingStrategy} >
{quiz.questions.map((question, index) => ( setExpandedId(expandedId === question.id ? null : question.id)} onEdit={() => setEditingQuestion(question)} onDelete={() => quiz.questions.length > 1 ? setShowDeleteConfirm(question.id) : null} /> ))}
{quiz.questions.length === 0 && (

No questions yet

Click "Add Question" to get started

)} ) : (

Game Settings

)}
{!canStartGame && quiz.questions.length > 0 && (

Some questions are incomplete

)}
{editingQuestion && ( setEditingQuestion(null)} /> )} {showDeleteConfirm && ( setShowDeleteConfirm(null)} > e.stopPropagation()} >

Delete Question?

This action cannot be undone.

)}
); };