Add shuffle options
This commit is contained in:
parent
683cd039e7
commit
90fba17a1e
1 changed files with 78 additions and 3 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import { ArrowLeft, Save, Plus, Play, AlertTriangle } from 'lucide-react';
|
import { ArrowLeft, Save, Plus, Play, AlertTriangle, Shuffle } from 'lucide-react';
|
||||||
import { DndContext, closestCenter, KeyboardSensor, PointerSensor, useSensor, useSensors, DragEndEvent } from '@dnd-kit/core';
|
import { DndContext, closestCenter, KeyboardSensor, PointerSensor, useSensor, useSensors, DragEndEvent } from '@dnd-kit/core';
|
||||||
import { arrayMove, SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy } from '@dnd-kit/sortable';
|
import { arrayMove, SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy } from '@dnd-kit/sortable';
|
||||||
import { Quiz, Question } from '../types';
|
import { Quiz, Question } from '../types';
|
||||||
|
|
@ -31,6 +31,8 @@ export const QuizEditor: React.FC<QuizEditorProps> = ({
|
||||||
const [editingQuestion, setEditingQuestion] = useState<Question | null>(null);
|
const [editingQuestion, setEditingQuestion] = useState<Question | null>(null);
|
||||||
const [showDeleteConfirm, setShowDeleteConfirm] = useState<string | null>(null);
|
const [showDeleteConfirm, setShowDeleteConfirm] = useState<string | null>(null);
|
||||||
const [titleEditing, setTitleEditing] = useState(false);
|
const [titleEditing, setTitleEditing] = useState(false);
|
||||||
|
const [shuffleQuestions, setShuffleQuestions] = useState(false);
|
||||||
|
const [shuffleAnswers, setShuffleAnswers] = useState(false);
|
||||||
|
|
||||||
useBodyScrollLock(!!showDeleteConfirm);
|
useBodyScrollLock(!!showDeleteConfirm);
|
||||||
|
|
||||||
|
|
@ -112,6 +114,33 @@ export const QuizEditor: React.FC<QuizEditorProps> = ({
|
||||||
q.options.some(o => o.isCorrect)
|
q.options.some(o => o.isCorrect)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleStartGame = () => {
|
||||||
|
let questions = [...quiz.questions];
|
||||||
|
|
||||||
|
if (shuffleQuestions) {
|
||||||
|
questions = questions.sort(() => Math.random() - 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shuffleAnswers) {
|
||||||
|
const shapes = ['triangle', 'diamond', 'circle', 'square'] as const;
|
||||||
|
const colors = ['red', 'blue', 'yellow', 'green'] as const;
|
||||||
|
|
||||||
|
questions = questions.map(q => {
|
||||||
|
const shuffledOptions = [...q.options].sort(() => Math.random() - 0.5);
|
||||||
|
return {
|
||||||
|
...q,
|
||||||
|
options: shuffledOptions.map((opt, idx) => ({
|
||||||
|
...opt,
|
||||||
|
shape: shapes[idx % 4],
|
||||||
|
color: colors[idx % 4]
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onStartGame({ ...quiz, questions });
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-100 text-gray-900 p-4 md:p-8 flex flex-col items-center">
|
<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="max-w-4xl w-full bg-white rounded-[2rem] shadow-xl overflow-hidden border-4 border-white">
|
||||||
|
|
@ -212,9 +241,55 @@ export const QuizEditor: React.FC<QuizEditorProps> = ({
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="p-6 bg-gray-50 border-t-2 border-gray-100">
|
<div className="p-6 bg-gray-50 border-t-2 border-gray-100 space-y-4">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="flex items-center justify-between p-4 bg-white rounded-xl border-2 border-gray-200 cursor-pointer hover:border-theme-primary transition group">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className={`p-2 rounded-lg transition ${shuffleQuestions ? 'bg-theme-primary text-white' : 'bg-gray-100 text-gray-500 group-hover:bg-gray-200'}`}>
|
||||||
|
<Shuffle size={20} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="font-bold text-gray-900">Shuffle Questions</p>
|
||||||
|
<p className="text-sm text-gray-500">Randomize question order when starting</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="relative">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={shuffleQuestions}
|
||||||
|
onChange={(e) => setShuffleQuestions(e.target.checked)}
|
||||||
|
className="sr-only peer"
|
||||||
|
/>
|
||||||
|
<div className="w-11 h-6 bg-gray-200 rounded-full peer peer-checked:bg-theme-primary transition-colors"></div>
|
||||||
|
<div className="absolute left-0.5 top-0.5 w-5 h-5 bg-white rounded-full shadow transition-transform peer-checked:translate-x-5"></div>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label className="flex items-center justify-between p-4 bg-white rounded-xl border-2 border-gray-200 cursor-pointer hover:border-theme-primary transition group">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className={`p-2 rounded-lg transition ${shuffleAnswers ? 'bg-theme-primary text-white' : 'bg-gray-100 text-gray-500 group-hover:bg-gray-200'}`}>
|
||||||
|
<Shuffle size={20} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="font-bold text-gray-900">Shuffle Answers</p>
|
||||||
|
<p className="text-sm text-gray-500">Randomize answer positions for each question</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="relative">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={shuffleAnswers}
|
||||||
|
onChange={(e) => setShuffleAnswers(e.target.checked)}
|
||||||
|
className="sr-only peer"
|
||||||
|
/>
|
||||||
|
<div className="w-11 h-6 bg-gray-200 rounded-full peer peer-checked:bg-theme-primary transition-colors"></div>
|
||||||
|
<div className="absolute left-0.5 top-0.5 w-5 h-5 bg-white rounded-full shadow transition-transform peer-checked:translate-x-5"></div>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={() => onStartGame(quiz)}
|
onClick={handleStartGame}
|
||||||
disabled={!canStartGame}
|
disabled={!canStartGame}
|
||||||
className="w-full bg-green-500 text-white py-4 rounded-2xl text-xl font-black shadow-[0_6px_0_#16a34a] active:shadow-none active:translate-y-[6px] transition-all hover:bg-green-600 flex items-center justify-center gap-3 disabled:opacity-50 disabled:cursor-not-allowed disabled:active:translate-y-0 disabled:active:shadow-[0_6px_0_#16a34a]"
|
className="w-full bg-green-500 text-white py-4 rounded-2xl text-xl font-black shadow-[0_6px_0_#16a34a] active:shadow-none active:translate-y-[6px] transition-all hover:bg-green-600 flex items-center justify-center gap-3 disabled:opacity-50 disabled:cursor-not-allowed disabled:active:translate-y-0 disabled:active:shadow-[0_6px_0_#16a34a]"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue