import React, { useState, useRef } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { X, FileUp, FileJson, Check, AlertCircle, Loader2, BrainCircuit, PenTool } from 'lucide-react'; import { useBodyScrollLock } from '../hooks/useBodyScrollLock'; import type { ExportedQuiz, QuizExportFile } from '../types'; interface ImportQuizzesModalProps { isOpen: boolean; onClose: () => void; onImport: (quizzes: ExportedQuiz[]) => Promise; parseFile: (file: File) => Promise; importing: boolean; } type ImportStep = 'upload' | 'select'; export const ImportQuizzesModal: React.FC = ({ isOpen, onClose, onImport, parseFile, importing, }) => { const [step, setStep] = useState('upload'); const [parsedFile, setParsedFile] = useState(null); const [selectedIds, setSelectedIds] = useState>(new Set()); const [parseError, setParseError] = useState(null); const [dragActive, setDragActive] = useState(false); const fileInputRef = useRef(null); useBodyScrollLock(isOpen); const resetState = () => { setStep('upload'); setParsedFile(null); setSelectedIds(new Set()); setParseError(null); setDragActive(false); }; const handleClose = () => { resetState(); onClose(); }; const handleFileSelect = async (file: File) => { setParseError(null); if (!file.name.endsWith('.json')) { setParseError('Please select a JSON file'); return; } try { const parsed = await parseFile(file); setParsedFile(parsed); setSelectedIds(new Set(parsed.quizzes.map((_, i) => i))); setStep('select'); } catch (err) { setParseError(err instanceof Error ? err.message : 'Failed to parse file'); } }; const handleDrop = (e: React.DragEvent) => { e.preventDefault(); setDragActive(false); const file = e.dataTransfer.files[0]; if (file) handleFileSelect(file); }; const handleDragOver = (e: React.DragEvent) => { e.preventDefault(); setDragActive(true); }; const handleDragLeave = () => { setDragActive(false); }; const toggleQuiz = (index: number) => { const newSelected = new Set(selectedIds); if (newSelected.has(index)) { newSelected.delete(index); } else { newSelected.add(index); } setSelectedIds(newSelected); }; const toggleAll = () => { if (!parsedFile) return; if (selectedIds.size === parsedFile.quizzes.length) { setSelectedIds(new Set()); } else { setSelectedIds(new Set(parsedFile.quizzes.map((_, i) => i))); } }; const handleImport = async () => { if (!parsedFile || selectedIds.size === 0) return; const quizzesToImport = parsedFile.quizzes.filter((_, i) => selectedIds.has(i)); await onImport(quizzesToImport); handleClose(); }; return ( {isOpen && ( e.stopPropagation()} className="bg-white w-full max-w-2xl max-h-[80vh] flex flex-col rounded-[2rem] shadow-[0_10px_0_rgba(0,0,0,0.1)] border-4 border-white/50 relative overflow-hidden" >

Import Quizzes

{step === 'upload' ? 'Select a Kaboot export file' : `${selectedIds.size} of ${parsedFile?.quizzes.length} selected`}

{step === 'upload' && (
fileInputRef.current?.click()} className={`border-3 border-dashed rounded-2xl p-12 text-center cursor-pointer transition-all ${ dragActive ? 'border-theme-primary bg-theme-primary/5' : 'border-gray-200 hover:border-gray-300 hover:bg-gray-100' }`} > e.target.files?.[0] && handleFileSelect(e.target.files[0])} className="hidden" />

{dragActive ? 'Drop file here' : 'Drag & drop or click to select'}

Accepts .json export files

{parseError && (

{parseError}

)}
)} {step === 'select' && parsedFile && (

Exported {new Date(parsedFile.exportedAt).toLocaleDateString()}

{parsedFile.quizzes.map((quiz, index) => ( !importing && toggleQuiz(index)} className={`group bg-white p-4 rounded-2xl border-2 transition-all cursor-pointer ${ selectedIds.has(index) ? 'border-theme-primary shadow-md' : 'border-gray-100 hover:border-gray-200' } ${importing ? 'opacity-70 cursor-not-allowed' : ''}`} >
{selectedIds.has(index) && ( )}
{quiz.source === 'ai_generated' ? ( AI ) : ( Manual )}

{quiz.title}

{quiz.questions.length} question{quiz.questions.length !== 1 ? 's' : ''} {quiz.aiTopic && ยท {quiz.aiTopic}}

))}
)}
{step === 'select' && (
)}
)}
); };