diff --git a/.gitignore b/.gitignore index 5ae49c6..5a58438 100644 --- a/.gitignore +++ b/.gitignore @@ -38,5 +38,3 @@ authentik/certs/* # Backend data server/data/ *.db -*.db-shm -*.db-wal diff --git a/App.tsx b/App.tsx index 18cecb2..47ed8e4 100644 --- a/App.tsx +++ b/App.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React from 'react'; import { useAuth } from 'react-oidc-context'; import { useGame } from './hooks/useGame'; import { useQuizLibrary } from './hooks/useQuizLibrary'; @@ -10,9 +10,6 @@ import { Podium } from './components/Podium'; import { QuizCreator } from './components/QuizCreator'; import { RevealScreen } from './components/RevealScreen'; import { SaveQuizPrompt } from './components/SaveQuizPrompt'; -import { QuizEditor } from './components/QuizEditor'; -import { SaveOptionsModal } from './components/SaveOptionsModal'; -import type { Quiz } from './types'; const seededRandom = (seed: number) => { const x = Math.sin(seed * 9999) * 10000; @@ -41,9 +38,7 @@ const FloatingShapes = React.memo(() => { function App() { const auth = useAuth(); - const { saveQuiz, updateQuiz, saving } = useQuizLibrary(); - const [showSaveOptions, setShowSaveOptions] = useState(false); - const [pendingEditedQuiz, setPendingEditedQuiz] = useState(null); + const { saveQuiz } = useQuizLibrary(); const { role, gameState, @@ -70,11 +65,7 @@ function App() { currentStreak, currentPlayerId, pendingQuizToSave, - dismissSavePrompt, - sourceQuizId, - updateQuizFromEditor, - startGameFromEditor, - backFromEditor + dismissSavePrompt } = useGame(); const handleSaveQuiz = async () => { @@ -85,36 +76,6 @@ function App() { dismissSavePrompt(); }; - const handleEditorSave = async (editedQuiz: Quiz) => { - updateQuizFromEditor(editedQuiz); - if (auth.isAuthenticated) { - if (sourceQuizId) { - // Quiz was loaded from library - show options modal - setPendingEditedQuiz(editedQuiz); - setShowSaveOptions(true); - } else { - // New quiz (AI-generated or manual) - save as new - const source = pendingQuizToSave?.topic ? 'ai_generated' : 'manual'; - const topic = pendingQuizToSave?.topic || undefined; - await saveQuiz(editedQuiz, source, topic); - } - } - }; - - const handleOverwriteQuiz = async () => { - if (!pendingEditedQuiz || !sourceQuizId) return; - await updateQuiz(sourceQuizId, pendingEditedQuiz); - setShowSaveOptions(false); - setPendingEditedQuiz(null); - }; - - const handleSaveAsNew = async () => { - if (!pendingEditedQuiz) return; - await saveQuiz(pendingEditedQuiz, 'manual'); - setShowSaveOptions(false); - setPendingEditedQuiz(null); - }; - const currentQ = quiz?.questions[currentQuestionIndex]; // Logic to find correct option, handling both Host (has isCorrect flag) and Client (masked, needs shape) @@ -146,16 +107,6 @@ function App() { /> ) : null} - {gameState === 'EDITING' && quiz ? ( - - ) : null} - {gameState === 'LOBBY' ? ( <> ) : null} - - { - setShowSaveOptions(false); - setPendingEditedQuiz(null); - }} - onOverwrite={handleOverwriteQuiz} - onSaveNew={handleSaveAsNew} - isSaving={saving} - /> ); } diff --git a/components/Landing.tsx b/components/Landing.tsx index b82c94d..a1b56c0 100644 --- a/components/Landing.tsx +++ b/components/Landing.tsx @@ -12,7 +12,7 @@ type GenerateMode = 'topic' | 'document'; interface LandingProps { onGenerate: (options: { topic?: string; questionCount?: number; files?: File[]; useOcr?: boolean }) => void; onCreateManual: () => void; - onLoadQuiz: (quiz: Quiz, quizId?: string) => void; + onLoadQuiz: (quiz: Quiz) => void; onJoin: (pin: string, name: string) => void; isLoading: boolean; error: string | null; @@ -121,7 +121,7 @@ export const Landing: React.FC = ({ onGenerate, onCreateManual, on try { const quiz = await loadQuiz(id); setLibraryOpen(false); - onLoadQuiz(quiz, id); + onLoadQuiz(quiz); } catch (err) { if (err instanceof Error && err.message.includes('redirecting')) { return; diff --git a/components/QuestionCard.tsx b/components/QuestionCard.tsx deleted file mode 100644 index b3c35b5..0000000 --- a/components/QuestionCard.tsx +++ /dev/null @@ -1,156 +0,0 @@ -import React from 'react'; -import { motion, AnimatePresence } from 'framer-motion'; -import { GripVertical, ChevronDown, ChevronUp, Pencil, Trash2, Check, X, Triangle, Diamond, Circle, Square } from 'lucide-react'; -import { Question } from '../types'; - -interface QuestionCardProps { - question: Question; - index: number; - onEdit: () => void; - onDelete: () => void; - isExpanded: boolean; - onToggleExpand: () => void; - isDragging?: boolean; - dragListeners?: any; -} - -const ShapeIcon: React.FC<{ shape: string; className?: string }> = ({ shape, className }) => { - const props = { size: 16, className }; - switch (shape) { - case 'triangle': return ; - case 'diamond': return ; - case 'circle': return ; - case 'square': return ; - default: return ; - } -}; - -const colorMap: Record = { - red: 'bg-red-500', - blue: 'bg-blue-500', - yellow: 'bg-yellow-500', - green: 'bg-green-500' -}; - -export const QuestionCard: React.FC = ({ - question, - index, - onEdit, - onDelete, - isExpanded, - onToggleExpand, - isDragging, - dragListeners -}) => { - const correctOption = question.options.find(o => o.isCorrect); - const truncatedText = question.text.length > 60 - ? question.text.slice(0, 60) + '...' - : question.text; - - return ( - -
-
- -
- -
- {index + 1} -
- -
-

{truncatedText}

-
- - {correctOption && ( -
- - {correctOption.text} -
- )} - -
- - -
- {isExpanded ? : } -
-
-
- - - {isExpanded && ( - -
-

{question.text}

- -
- {question.options.map((option, idx) => ( -
-
-
- -
- - {option.text} - - {option.isCorrect ? ( - - ) : ( - - )} -
- {option.reason && ( -

{option.reason}

- )} -
- ))} -
- -
- Time limit: {question.timeLimit}s -
-
-
- )} -
-
- ); -}; diff --git a/components/QuestionEditModal.tsx b/components/QuestionEditModal.tsx deleted file mode 100644 index 653fbba..0000000 --- a/components/QuestionEditModal.tsx +++ /dev/null @@ -1,258 +0,0 @@ -import React, { useState } from 'react'; -import { motion } from 'framer-motion'; -import { X, Plus, Trash2, Triangle, Diamond, Circle, Square, Clock } from 'lucide-react'; -import { Question, AnswerOption } from '../types'; -import { useBodyScrollLock } from '../hooks/useBodyScrollLock'; - -interface QuestionEditModalProps { - question: Question; - onSave: (question: Question) => void; - onClose: () => void; -} - -const ShapeIcon: React.FC<{ shape: string; className?: string; size?: number }> = ({ shape, className, size = 20 }) => { - const props = { size, className }; - switch (shape) { - case 'triangle': return ; - case 'diamond': return ; - case 'circle': return ; - case 'square': return ; - default: return ; - } -}; - -const colorMap: Record = { - red: 'bg-red-500 border-red-500', - blue: 'bg-blue-500 border-blue-500', - yellow: 'bg-yellow-500 border-yellow-500', - green: 'bg-green-500 border-green-500' -}; - -const shapes = ['triangle', 'diamond', 'circle', 'square'] as const; -const colors = ['red', 'blue', 'yellow', 'green'] as const; - -export const QuestionEditModal: React.FC = ({ - question, - onSave, - onClose -}) => { - const [text, setText] = useState(question.text); - const [options, setOptions] = useState(question.options); - const [timeLimit, setTimeLimit] = useState(question.timeLimit); - - useBodyScrollLock(true); - - const handleOptionTextChange = (index: number, newText: string) => { - setOptions(prev => prev.map((opt, i) => - i === index ? { ...opt, text: newText } : opt - )); - }; - - const handleReasonChange = (index: number, reason: string) => { - setOptions(prev => prev.map((opt, i) => - i === index ? { ...opt, reason } : opt - )); - }; - - const handleCorrectChange = (index: number) => { - setOptions(prev => prev.map((opt, i) => ({ - ...opt, - isCorrect: i === index - }))); - }; - - const handleDeleteOption = (index: number) => { - if (options.length <= 2) return; - - const deletedWasCorrect = options[index].isCorrect; - const newOptions = options.filter((_, i) => i !== index); - - if (deletedWasCorrect && newOptions.length > 0) { - newOptions[0].isCorrect = true; - } - - setOptions(newOptions.map((opt, i) => ({ - ...opt, - shape: shapes[i % 4], - color: colors[i % 4] - }))); - }; - - const handleAddOption = () => { - if (options.length >= 6) return; - - const newIndex = options.length; - setOptions(prev => [...prev, { - text: '', - isCorrect: false, - shape: shapes[newIndex % 4], - color: colors[newIndex % 4], - reason: '' - }]); - }; - - const handleSave = () => { - if (!text.trim()) return; - if (options.some(o => !o.text.trim())) return; - if (!options.some(o => o.isCorrect)) return; - - onSave({ - ...question, - text: text.trim(), - options, - timeLimit - }); - }; - - const isValid = text.trim() && - options.every(o => o.text.trim()) && - options.some(o => o.isCorrect) && - options.length >= 2; - - return ( - - e.stopPropagation()} - > -
-

Edit Question

- -
- -
-
- -