diff --git a/components/Landing.tsx b/components/Landing.tsx index 8e69678..0523c38 100644 --- a/components/Landing.tsx +++ b/components/Landing.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from 'react'; -import { motion } from 'framer-motion'; -import { BrainCircuit, Loader2, Play, PenTool, BookOpen } from 'lucide-react'; +import { motion, AnimatePresence } from 'framer-motion'; +import { BrainCircuit, Loader2, Play, PenTool, BookOpen, Upload, ChevronDown, ChevronUp, X, FileText, Settings, Image } from 'lucide-react'; import { useAuth } from 'react-oidc-context'; import { AuthButton } from './AuthButton'; import { QuizLibrary } from './QuizLibrary'; @@ -8,7 +8,7 @@ import { useQuizLibrary } from '../hooks/useQuizLibrary'; import type { Quiz } from '../types'; interface LandingProps { - onGenerate: (topic: string) => void; + onGenerate: (options: { topic?: string; questionCount?: number; file?: File }) => void; onCreateManual: () => void; onLoadQuiz: (quiz: Quiz) => void; onJoin: (pin: string, name: string) => void; @@ -24,6 +24,11 @@ export const Landing: React.FC = ({ onGenerate, onCreateManual, on const [name, setName] = useState(''); const [libraryOpen, setLibraryOpen] = useState(false); + const [selectedFile, setSelectedFile] = useState(null); + const [questionCount, setQuestionCount] = useState(10); + const [showAdvanced, setShowAdvanced] = useState(false); + const [isDragging, setIsDragging] = useState(false); + const { quizzes, loading: libraryLoading, @@ -42,9 +47,49 @@ export const Landing: React.FC = ({ onGenerate, onCreateManual, on } }, [libraryOpen, auth.isAuthenticated, fetchQuizzes]); + const handleFileSelect = (e: React.ChangeEvent) => { + if (e.target.files && e.target.files[0]) { + setSelectedFile(e.target.files[0]); + } + }; + + const handleDrop = (e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(false); + if (e.dataTransfer.files && e.dataTransfer.files[0]) { + const file = e.dataTransfer.files[0]; + const acceptedTypes = ['.pdf', '.txt', '.md', '.docx', '.jpg', '.jpeg', '.png', '.gif', '.webp']; + const fileExtension = '.' + file.name.split('.').pop()?.toLowerCase(); + + if (acceptedTypes.includes(fileExtension) || file.type.startsWith('image/')) { + setSelectedFile(file); + } + } + }; + + const handleDragOver = (e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(true); + }; + + const handleDragLeave = (e: React.DragEvent) => { + e.preventDefault(); + setIsDragging(false); + }; + + const removeFile = () => { + setSelectedFile(null); + }; + const handleHostSubmit = (e: React.FormEvent) => { e.preventDefault(); - if (topic.trim()) onGenerate(topic); + if ((topic.trim() || selectedFile) && !isLoading) { + onGenerate({ + topic: topic.trim(), + questionCount, + file: selectedFile || undefined + }); + } }; const handleJoinSubmit = (e: React.FormEvent) => { @@ -110,12 +155,110 @@ export const Landing: React.FC = ({ onGenerate, onCreateManual, on className="w-full p-4 text-xl font-bold border-2 border-gray-200 rounded-2xl focus:border-theme-primary focus:ring-4 focus:ring-theme-primary/20 outline-none transition-all placeholder:font-medium text-center" disabled={isLoading} /> + +
+ + + + {showAdvanced && ( + +
+
+
+ Question Count + {questionCount} +
+ setQuestionCount(Number(e.target.value))} + className="w-full accent-theme-primary h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer" + /> +
+ 5 + 30 +
+
+ +
document.getElementById('file-upload')?.click()} + className={`border-2 border-dashed rounded-xl p-4 text-center cursor-pointer transition-all ${ + isDragging + ? 'border-theme-primary bg-theme-primary/5 scale-[1.02]' + : 'border-gray-300 hover:border-gray-400 hover:bg-gray-50' + }`} + > + + + {selectedFile ? ( +
+
+ {selectedFile.type.startsWith('image/') ? ( + + ) : ( + + )} +
+ {selectedFile.name} + {(selectedFile.size / 1024).toFixed(1)} KB +
+
+ +
+ ) : ( +
+ +

+ {isDragging ? 'Drop file here' : 'Upload Document'} +

+

PDF, TXT, DOCX, Images

+
+ )} +
+
+
+ )} +
+
+