import { useState } from 'react' import { questionsApi } from '../services/api' import './GameManagementModal.css' import './QuestionsModal.css' const GameManagementModal = ({ isOpen, onClose, room, participants, currentQuestion, currentQuestionIndex, totalQuestions, revealedAnswers, questions = [], onUpdateQuestions, availablePacks = [], onStartGame, onEndGame, onNextQuestion, onPreviousQuestion, onRevealAnswer, onHideAnswer, onShowAllAnswers, onHideAllAnswers, onAwardPoints, onPenalty, onUpdatePlayerName, onUpdatePlayerScore, onKickPlayer, }) => { const [activeTab, setActiveTab] = useState('players') // players | game | scoring | questions const [selectedPlayer, setSelectedPlayer] = useState(null) const [customPoints, setCustomPoints] = useState(10) // Player editing state const [editingPlayerId, setEditingPlayerId] = useState(null) const [editingPlayerName, setEditingPlayerName] = useState('') const [editingPlayerScore, setEditingPlayerScore] = useState('') const [editMode, setEditMode] = useState(null) // 'name' | 'score' // Questions management state const [editingQuestion, setEditingQuestion] = useState(null) const [questionText, setQuestionText] = useState('') const [answers, setAnswers] = useState([ { text: '', points: 100 }, { text: '', points: 80 }, { text: '', points: 60 }, { text: '', points: 40 }, { text: '', points: 20 }, { text: '', points: 10 }, ]) const [jsonError, setJsonError] = useState('') const [showPackImport, setShowPackImport] = useState(false) const [selectedPack, setSelectedPack] = useState(null) const [packQuestions, setPackQuestions] = useState([]) const [selectedQuestionIndices, setSelectedQuestionIndices] = useState(new Set()) const [searchQuery, setSearchQuery] = useState('') const [viewingQuestion, setViewingQuestion] = useState(null) const [showAnswers, setShowAnswers] = useState(false) if (!isOpen) return null const gameStatus = room?.status || 'WAITING' const areAllAnswersRevealed = currentQuestion ? revealedAnswers.length === currentQuestion.answers.length : false // Handlers const handleBackdropClick = (e) => { if (e.target === e.currentTarget) onClose() } const handleRevealAnswer = (answerId) => { if (revealedAnswers.includes(answerId)) { onHideAnswer(answerId) } else { onRevealAnswer(answerId) } } const handleAwardPoints = (points) => { if (selectedPlayer) { onAwardPoints(selectedPlayer, points) } } const handlePenalty = () => { if (selectedPlayer) { onPenalty(selectedPlayer) } } // Player editing handlers const handleStartEditName = (participant) => { setEditingPlayerId(participant.id) setEditingPlayerName(participant.name) setEditMode('name') } const handleStartEditScore = (participant) => { setEditingPlayerId(participant.id) setEditingPlayerScore(String(participant.score || 0)) setEditMode('score') } const handleCancelEdit = () => { setEditingPlayerId(null) setEditingPlayerName('') setEditingPlayerScore('') setEditMode(null) } const handleSavePlayerName = () => { if (editingPlayerName.trim() && onUpdatePlayerName) { onUpdatePlayerName(editingPlayerId, editingPlayerName.trim()) } handleCancelEdit() } const handleSavePlayerScore = () => { const score = parseInt(editingPlayerScore, 10) if (!isNaN(score) && onUpdatePlayerScore) { onUpdatePlayerScore(editingPlayerId, score) } handleCancelEdit() } const handleKeyDown = (e, type) => { if (e.key === 'Enter') { if (type === 'name') { handleSavePlayerName() } else if (type === 'score') { handleSavePlayerScore() } } else if (e.key === 'Escape') { handleCancelEdit() } } // Questions management handlers const resetQuestionForm = () => { setEditingQuestion(null) setQuestionText('') setAnswers([ { text: '', points: 100 }, { text: '', points: 80 }, { text: '', points: 60 }, { text: '', points: 40 }, { text: '', points: 20 }, { text: '', points: 10 }, ]) setJsonError('') } const handleEditQuestion = (question) => { setEditingQuestion(question) setQuestionText(question.text) setAnswers([...question.answers]) setJsonError('') } const handleCancelEditQuestion = () => { resetQuestionForm() } const handleAnswerChange = (index, field, value) => { const updatedAnswers = [...answers] if (field === 'text') { updatedAnswers[index].text = value } else if (field === 'points') { updatedAnswers[index].points = parseInt(value) || 0 } setAnswers(updatedAnswers) } const handleAddAnswer = () => { const minPoints = Math.min(...answers.map(a => a.points)) setAnswers([...answers, { text: '', points: Math.max(0, minPoints - 10) }]) } const handleRemoveAnswer = (index) => { if (answers.length > 1) { setAnswers(answers.filter((_, i) => i !== index)) } } const validateQuestionForm = () => { if (!questionText.trim()) { setJsonError('Введите текст вопроса') return false } if (answers.length === 0) { setJsonError('Добавьте хотя бы один ответ') return false } const hasEmptyAnswers = answers.some(a => !a.text.trim()) if (hasEmptyAnswers) { setJsonError('Заполните все ответы') return false } return true } const handleSaveQuestion = () => { if (!validateQuestionForm()) return const questionData = { id: editingQuestion ? editingQuestion.id : Date.now(), text: questionText.trim(), answers: answers .filter(a => a.text.trim()) .map(a => ({ text: a.text.trim(), points: a.points, })), } let updatedQuestions if (editingQuestion) { updatedQuestions = questions.map(q => q.id === editingQuestion.id ? questionData : q ) } else { updatedQuestions = [...questions, questionData] } onUpdateQuestions(updatedQuestions) resetQuestionForm() } const handleDeleteQuestion = (questionId) => { if (window.confirm('Вы уверены, что хотите удалить этот вопрос?')) { const updatedQuestions = questions.filter(q => q.id !== questionId) onUpdateQuestions(updatedQuestions) if (editingQuestion && editingQuestion.id === questionId) { resetQuestionForm() } } } const handleExportJson = () => { try { const jsonString = JSON.stringify(questions, null, 2) const blob = new Blob([jsonString], { type: 'application/json' }) const url = URL.createObjectURL(blob) const link = document.createElement('a') link.href = url link.download = 'questions.json' document.body.appendChild(link) link.click() document.body.removeChild(link) URL.revokeObjectURL(url) setJsonError('') } catch (error) { setJsonError('Ошибка при экспорте: ' + error.message) } } const handleDownloadTemplate = () => { const template = [ { text: 'Назовите самый популярный вид спорта в мире', answers: [ { text: 'Футбол', points: 100 }, { text: 'Баскетбол', points: 80 }, { text: 'Теннис', points: 60 }, { text: 'Хоккей', points: 40 }, { text: 'Волейбол', points: 20 }, { text: 'Бокс', points: 10 }, ] }, { text: 'Что люди обычно берут с собой на пляж?', answers: [ { text: 'Полотенце', points: 100 }, { text: 'Крем от солнца', points: 80 }, { text: 'Очки', points: 60 }, { text: 'Зонт', points: 40 }, { text: 'Книга', points: 20 }, { text: 'Еда', points: 10 }, ] } ] try { const jsonString = JSON.stringify(template, null, 2) const blob = new Blob([jsonString], { type: 'application/json' }) const url = URL.createObjectURL(blob) const link = document.createElement('a') link.href = url link.download = 'template_questions.json' document.body.appendChild(link) link.click() document.body.removeChild(link) URL.revokeObjectURL(url) setJsonError('') } catch (error) { setJsonError('Ошибка при скачивании шаблона: ' + error.message) } } const handleImportJson = () => { const input = document.createElement('input') input.type = 'file' input.accept = '.json' input.onchange = (e) => { const file = e.target.files[0] if (!file) return const reader = new FileReader() reader.onload = (event) => { try { const jsonContent = JSON.parse(event.target.result) if (!Array.isArray(jsonContent)) { setJsonError('JSON должен содержать массив вопросов') return } // Валидация - id опционален (будет сгенерирован автоматически) const isValid = jsonContent.every(q => typeof q.text === 'string' && Array.isArray(q.answers) && q.answers.every(a => a.text && typeof a.points === 'number') ) if (!isValid) { setJsonError('Неверный формат JSON. Ожидается массив объектов с полями: text, answers') return } // Добавляем id если его нет const questionsWithIds = jsonContent.map((q, idx) => ({ ...q, id: q.id || Date.now() + Math.random() + idx, answers: q.answers.map((a, aidx) => ({ ...a, id: a.id || `answer-${Date.now()}-${idx}-${aidx}` })) })) onUpdateQuestions(questionsWithIds) setJsonError('') alert(`Успешно импортировано ${questionsWithIds.length} вопросов`) } catch (error) { setJsonError('Ошибка при импорте: ' + error.message) } } reader.readAsText(file) } input.click() } const handleSelectPack = async (packId) => { if (!packId) { setPackQuestions([]) setSelectedPack(null) setSearchQuery('') setViewingQuestion(null) setShowAnswers(false) return } try { const response = await questionsApi.getPack(packId) setPackQuestions(response.data.questions || []) setSelectedPack(packId) setSelectedQuestionIndices(new Set()) setSearchQuery('') setViewingQuestion(null) setShowAnswers(false) } catch (error) { console.error('Error fetching pack:', error) setJsonError('Ошибка загрузки пака вопросов') } } // Фильтрация вопросов по поисковому запросу const filteredPackQuestions = packQuestions.filter((q) => { if (!searchQuery.trim()) return true const questionText = (q.text || q.question || '').toLowerCase() return questionText.includes(searchQuery.toLowerCase()) }) // Выбор всех видимых вопросов const handleSelectAll = () => { const allVisibleIndices = new Set( filteredPackQuestions.map((q) => { const originalIndex = packQuestions.findIndex(pq => pq === q) return originalIndex }).filter(idx => idx !== -1) ) const newSelected = new Set(selectedQuestionIndices) allVisibleIndices.forEach(idx => newSelected.add(idx)) setSelectedQuestionIndices(newSelected) } // Снятие выбора со всех видимых вопросов const handleDeselectAll = () => { const visibleIndices = new Set( filteredPackQuestions.map((q) => { const originalIndex = packQuestions.findIndex(pq => pq === q) return originalIndex }).filter(idx => idx !== -1) ) const newSelected = new Set(selectedQuestionIndices) visibleIndices.forEach(idx => newSelected.delete(idx)) setSelectedQuestionIndices(newSelected) } // Проверка, выбраны ли все видимые вопросы const areAllVisibleSelected = () => { if (filteredPackQuestions.length === 0) return false const visibleIndices = filteredPackQuestions.map((q) => { const originalIndex = packQuestions.findIndex(pq => pq === q) return originalIndex }).filter(idx => idx !== -1) return visibleIndices.every(idx => selectedQuestionIndices.has(idx)) } // Просмотр вопроса const handleViewQuestion = (question) => { setViewingQuestion(question) setShowAnswers(false) } // Закрытие просмотра вопроса const handleCloseViewer = () => { setViewingQuestion(null) setShowAnswers(false) } const handleToggleQuestion = (index) => { const newSelected = new Set(selectedQuestionIndices) if (newSelected.has(index)) { newSelected.delete(index) } else { newSelected.add(index) } setSelectedQuestionIndices(newSelected) } const handleImportSelected = () => { const indices = Array.from(selectedQuestionIndices) const questionsToImport = indices.map(idx => packQuestions[idx]).filter(Boolean) const copiedQuestions = questionsToImport.map((q, idx) => ({ id: Date.now() + Math.random() + idx, // Generate new ID text: q.text || q.question || '', answers: (q.answers || []).map(a => ({ text: a.text, points: a.points })), })) const updatedQuestions = [...questions, ...copiedQuestions] onUpdateQuestions(updatedQuestions) setSelectedQuestionIndices(new Set()) setSearchQuery('') setShowPackImport(false) setJsonError('') alert(`Импортировано ${copiedQuestions.length} вопросов`) } return (
Нет участников
) : ( participants.map((participant) => (Нет вопросов. Добавьте вопросы для игры.
) : (