188 lines
8 KiB
TypeScript
188 lines
8 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { Quiz, Question, AnswerOption } from '../types';
|
|
import { v4 as uuidv4 } from 'uuid';
|
|
import { Plus, Save, Trash2, CheckCircle, Circle, X } from 'lucide-react';
|
|
import { COLORS, SHAPES } from '../constants';
|
|
|
|
interface QuizCreatorProps {
|
|
onFinalize: (quiz: Quiz) => void;
|
|
onCancel: () => void;
|
|
}
|
|
|
|
export const QuizCreator: React.FC<QuizCreatorProps> = ({ onFinalize, onCancel }) => {
|
|
const [title, setTitle] = useState('');
|
|
const [questions, setQuestions] = useState<Question[]>([]);
|
|
const [qText, setQText] = useState('');
|
|
const [options, setOptions] = useState<string[]>(['', '', '', '']);
|
|
const [reasons, setReasons] = useState<string[]>(['', '', '', '']);
|
|
const [correctIdx, setCorrectIdx] = useState<number>(0);
|
|
|
|
const handleAddQuestion = () => {
|
|
if (!qText.trim() || options.some(o => !o.trim())) {
|
|
alert("Please fill in the question and all 4 options.");
|
|
return;
|
|
}
|
|
const shapes = ['triangle', 'diamond', 'circle', 'square'] as const;
|
|
const colors = ['red', 'blue', 'yellow', 'green'] as const;
|
|
|
|
const newOptions: AnswerOption[] = options.map((text, idx) => ({
|
|
text,
|
|
isCorrect: idx === correctIdx,
|
|
shape: shapes[idx],
|
|
color: colors[idx],
|
|
reason: reasons[idx].trim() || undefined
|
|
}));
|
|
|
|
const newQuestion: Question = {
|
|
id: uuidv4(),
|
|
text: qText,
|
|
options: newOptions,
|
|
timeLimit: 20
|
|
};
|
|
|
|
setQuestions([...questions, newQuestion]);
|
|
setQText('');
|
|
setOptions(['', '', '', '']);
|
|
setReasons(['', '', '', '']);
|
|
setCorrectIdx(0);
|
|
};
|
|
|
|
const handleRemoveQuestion = (id: string) => {
|
|
setQuestions(questions.filter(q => q.id !== id));
|
|
};
|
|
|
|
const handleFinalize = () => {
|
|
if (!title.trim() || questions.length === 0) return;
|
|
onFinalize({ title, questions });
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen bg-gray-100 text-gray-900 p-4 md:p-8 flex flex-col items-center">
|
|
<div className="max-w-4xl w-full bg-white rounded-[2rem] shadow-xl overflow-hidden border-4 border-white">
|
|
<div className="bg-[#46178f] p-8 text-white flex justify-between items-center relative overflow-hidden">
|
|
<div className="relative z-10">
|
|
<h2 className="text-4xl font-black font-display">Create Quiz</h2>
|
|
<p className="opacity-80 font-bold">Build your masterpiece</p>
|
|
</div>
|
|
<button
|
|
onClick={onCancel}
|
|
className="bg-white/20 p-3 rounded-full hover:bg-white/30 transition relative z-10"
|
|
>
|
|
<X size={24} />
|
|
</button>
|
|
|
|
{/* Decorative Circles */}
|
|
<div className="absolute -right-10 -top-10 w-40 h-40 bg-white/10 rounded-full"></div>
|
|
<div className="absolute right-20 bottom-[-50px] w-24 h-24 bg-white/10 rounded-full"></div>
|
|
</div>
|
|
|
|
<div className="p-8 space-y-8">
|
|
<div>
|
|
<label className="block text-sm font-black uppercase tracking-wider text-gray-500 mb-2 ml-2">Quiz Title</label>
|
|
<input
|
|
type="text"
|
|
value={title}
|
|
onChange={(e) => setTitle(e.target.value)}
|
|
className="w-full p-4 border-4 border-gray-200 rounded-2xl text-2xl font-bold focus:border-[#46178f] outline-none transition-colors"
|
|
placeholder="e.g., The Ultimate Trivia"
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-4">
|
|
{questions.map((q, idx) => (
|
|
<div key={q.id} className="border-4 border-gray-100 p-4 rounded-2xl bg-gray-50 flex justify-between items-center group hover:border-gray-200 transition">
|
|
<div className="flex items-center gap-4">
|
|
<span className="bg-[#46178f] text-white w-10 h-10 flex items-center justify-center rounded-full font-black">
|
|
{idx + 1}
|
|
</span>
|
|
<span className="font-bold text-lg">{q.text}</span>
|
|
</div>
|
|
<button onClick={() => handleRemoveQuestion(q.id)} className="text-gray-400 hover:text-red-500 p-2 rounded-xl hover:bg-red-50 transition">
|
|
<Trash2 size={24} />
|
|
</button>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
<div className="bg-blue-50/50 p-6 md:p-8 rounded-[2rem] border-4 border-blue-100">
|
|
<h3 className="text-xl font-black mb-6 flex items-center gap-2 text-blue-900">
|
|
<Plus size={24} /> New Question
|
|
</h3>
|
|
|
|
<div className="mb-6">
|
|
<input
|
|
type="text"
|
|
value={qText}
|
|
onChange={(e) => setQText(e.target.value)}
|
|
className="w-full p-4 border-4 border-white shadow-sm rounded-2xl font-bold text-lg focus:border-blue-400 outline-none"
|
|
placeholder="What is the question?"
|
|
/>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
{options.map((opt, idx) => {
|
|
const isSelected = correctIdx === idx;
|
|
const borderColor = isSelected ? 'border-green-500' : 'border-white';
|
|
const bgClass = COLORS[['red','blue','yellow','green'][idx] as any];
|
|
|
|
return (
|
|
<div key={idx} className={`flex flex-col gap-2 p-3 rounded-2xl border-4 ${borderColor} bg-white shadow-sm transition-all`}>
|
|
<div className="flex items-center gap-3">
|
|
<button
|
|
onClick={() => setCorrectIdx(idx)}
|
|
className={`p-1 rounded-full ${isSelected ? 'text-green-500' : 'text-gray-200 hover:text-gray-400'}`}
|
|
>
|
|
{isSelected ? <CheckCircle size={32} fill="currentColor" className="text-white" /> : <Circle size={32} />}
|
|
</button>
|
|
|
|
<div className={`w-4 h-8 rounded-full ${bgClass}`}></div>
|
|
|
|
<input
|
|
type="text"
|
|
value={opt}
|
|
onChange={(e) => {
|
|
const newOpts = [...options];
|
|
newOpts[idx] = e.target.value;
|
|
setOptions(newOpts);
|
|
}}
|
|
className="w-full p-2 outline-none font-bold text-gray-700 bg-transparent placeholder:font-normal"
|
|
placeholder={`Option ${idx + 1}`}
|
|
/>
|
|
</div>
|
|
<input
|
|
type="text"
|
|
value={reasons[idx]}
|
|
onChange={(e) => {
|
|
const newReasons = [...reasons];
|
|
newReasons[idx] = e.target.value;
|
|
setReasons(newReasons);
|
|
}}
|
|
className="w-full p-2 ml-12 text-sm outline-none text-gray-500 bg-gray-50 rounded-lg placeholder:text-gray-400"
|
|
placeholder={isSelected ? "Why is this correct? (optional)" : "Why is this wrong? (optional)"}
|
|
/>
|
|
</div>
|
|
)
|
|
})}
|
|
</div>
|
|
|
|
<button
|
|
onClick={handleAddQuestion}
|
|
className="mt-8 w-full bg-blue-600 text-white py-4 rounded-2xl font-black text-lg hover:bg-blue-700 shadow-[0_6px_0_#1e40af] active:shadow-none active:translate-y-[6px] transition-all"
|
|
>
|
|
Add Question
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="p-6 bg-gray-50 border-t-2 border-gray-100 flex justify-end">
|
|
<button
|
|
onClick={handleFinalize}
|
|
className="flex items-center gap-2 bg-green-500 text-white px-10 py-4 rounded-2xl text-xl font-black hover:bg-green-600 shadow-[0_6px_0_#15803d] active:shadow-none active:translate-y-[6px] transition-all"
|
|
>
|
|
<Save size={24} /> Finish & Play
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|