Reason working
This commit is contained in:
parent
c87ebf0a74
commit
845e3ab3d6
4 changed files with 85 additions and 39 deletions
|
|
@ -14,6 +14,7 @@ export const QuizCreator: React.FC<QuizCreatorProps> = ({ onFinalize, onCancel }
|
||||||
const [questions, setQuestions] = useState<Question[]>([]);
|
const [questions, setQuestions] = useState<Question[]>([]);
|
||||||
const [qText, setQText] = useState('');
|
const [qText, setQText] = useState('');
|
||||||
const [options, setOptions] = useState<string[]>(['', '', '', '']);
|
const [options, setOptions] = useState<string[]>(['', '', '', '']);
|
||||||
|
const [reasons, setReasons] = useState<string[]>(['', '', '', '']);
|
||||||
const [correctIdx, setCorrectIdx] = useState<number>(0);
|
const [correctIdx, setCorrectIdx] = useState<number>(0);
|
||||||
|
|
||||||
const handleAddQuestion = () => {
|
const handleAddQuestion = () => {
|
||||||
|
|
@ -28,7 +29,8 @@ export const QuizCreator: React.FC<QuizCreatorProps> = ({ onFinalize, onCancel }
|
||||||
text,
|
text,
|
||||||
isCorrect: idx === correctIdx,
|
isCorrect: idx === correctIdx,
|
||||||
shape: shapes[idx],
|
shape: shapes[idx],
|
||||||
color: colors[idx]
|
color: colors[idx],
|
||||||
|
reason: reasons[idx].trim() || undefined
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const newQuestion: Question = {
|
const newQuestion: Question = {
|
||||||
|
|
@ -41,6 +43,7 @@ export const QuizCreator: React.FC<QuizCreatorProps> = ({ onFinalize, onCancel }
|
||||||
setQuestions([...questions, newQuestion]);
|
setQuestions([...questions, newQuestion]);
|
||||||
setQText('');
|
setQText('');
|
||||||
setOptions(['', '', '', '']);
|
setOptions(['', '', '', '']);
|
||||||
|
setReasons(['', '', '', '']);
|
||||||
setCorrectIdx(0);
|
setCorrectIdx(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -123,26 +126,39 @@ export const QuizCreator: React.FC<QuizCreatorProps> = ({ onFinalize, onCancel }
|
||||||
const bgClass = COLORS[['red','blue','yellow','green'][idx] as any];
|
const bgClass = COLORS[['red','blue','yellow','green'][idx] as any];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={idx} className={`flex items-center gap-3 p-3 rounded-2xl border-4 ${borderColor} bg-white shadow-sm transition-all`}>
|
<div key={idx} className={`flex flex-col gap-2 p-3 rounded-2xl border-4 ${borderColor} bg-white shadow-sm transition-all`}>
|
||||||
<button
|
<div className="flex items-center gap-3">
|
||||||
onClick={() => setCorrectIdx(idx)}
|
<button
|
||||||
className={`p-1 rounded-full ${isSelected ? 'text-green-500' : 'text-gray-200 hover:text-gray-400'}`}
|
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>
|
{isSelected ? <CheckCircle size={32} fill="currentColor" className="text-white" /> : <Circle size={32} />}
|
||||||
|
</button>
|
||||||
|
|
||||||
<div className={`w-4 h-full rounded-full ${bgClass}`}></div>
|
<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
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={opt}
|
value={reasons[idx]}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const newOpts = [...options];
|
const newReasons = [...reasons];
|
||||||
newOpts[idx] = e.target.value;
|
newReasons[idx] = e.target.value;
|
||||||
setOptions(newOpts);
|
setReasons(newReasons);
|
||||||
}}
|
}}
|
||||||
className="w-full p-2 outline-none font-bold text-gray-700 bg-transparent placeholder:font-normal"
|
className="w-full p-2 ml-12 text-sm outline-none text-gray-500 bg-gray-50 rounded-lg placeholder:text-gray-400"
|
||||||
placeholder={`Option ${idx + 1}`}
|
placeholder={isSelected ? "Why is this correct? (optional)" : "Why is this wrong? (optional)"}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,16 @@ export const RevealScreen: React.FC<RevealScreenProps> = ({
|
||||||
<h1 className="text-5xl md:text-7xl font-black font-display text-center drop-shadow-md leading-tight">
|
<h1 className="text-5xl md:text-7xl font-black font-display text-center drop-shadow-md leading-tight">
|
||||||
{correctOption.text}
|
{correctOption.text}
|
||||||
</h1>
|
</h1>
|
||||||
|
{correctOption.reason && (
|
||||||
|
<motion.p
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
transition={{ delay: 0.3 }}
|
||||||
|
className="mt-6 text-xl md:text-2xl text-center opacity-90 bg-black/20 px-6 py-4 rounded-2xl max-w-2xl"
|
||||||
|
>
|
||||||
|
{correctOption.reason}
|
||||||
|
</motion.p>
|
||||||
|
)}
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
@ -110,9 +120,16 @@ export const RevealScreen: React.FC<RevealScreenProps> = ({
|
||||||
className="flex flex-col items-center"
|
className="flex flex-col items-center"
|
||||||
>
|
>
|
||||||
{isCorrect ? (
|
{isCorrect ? (
|
||||||
<div className="bg-black/20 px-8 py-4 rounded-3xl backdrop-blur-sm border-4 border-white/30 flex items-center gap-4 mb-8">
|
<div className="flex flex-col items-center gap-4 mb-8">
|
||||||
<span className="text-4xl font-black">+{pointsEarned}</span>
|
<div className="bg-black/20 px-8 py-4 rounded-3xl backdrop-blur-sm border-4 border-white/30 flex items-center gap-4">
|
||||||
<span className="font-bold uppercase opacity-80">Points</span>
|
<span className="text-4xl font-black">+{pointsEarned}</span>
|
||||||
|
<span className="font-bold uppercase opacity-80">Points</span>
|
||||||
|
</div>
|
||||||
|
{correctOption.reason && (
|
||||||
|
<p className="text-center text-lg opacity-90 max-w-md bg-black/10 px-4 py-2 rounded-xl">
|
||||||
|
{correctOption.reason}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="text-2xl font-bold opacity-90 mb-8 max-w-md text-center">
|
<div className="text-2xl font-bold opacity-90 mb-8 max-w-md text-center">
|
||||||
|
|
@ -133,24 +150,34 @@ export const RevealScreen: React.FC<RevealScreenProps> = ({
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{!isCorrect && (
|
|
||||||
<motion.div
|
|
||||||
initial={{ y: 100 }}
|
|
||||||
animate={{ y: 0 }}
|
|
||||||
transition={{ delay: 0.5, type: 'spring' }}
|
|
||||||
className="absolute bottom-0 left-0 right-0 bg-black/30 backdrop-blur-md p-6 pb-12"
|
|
||||||
>
|
|
||||||
<p className="text-center text-sm font-bold uppercase tracking-widest mb-4 opacity-70">The correct answer was</p>
|
|
||||||
<div className="flex items-center justify-center gap-4">
|
|
||||||
<div className={`${COLORS[correctOption.color]} p-3 rounded-xl shadow-lg`}>
|
|
||||||
<ShapeIcon size={24} fill="currentColor" />
|
|
||||||
</div>
|
|
||||||
<span className="text-2xl font-black">{correctOption.text}</span>
|
|
||||||
</div>
|
|
||||||
</motion.div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
|
{!isCorrect && (
|
||||||
|
<div className="absolute bottom-0 left-0 right-0 overflow-hidden z-20">
|
||||||
|
<motion.div
|
||||||
|
initial={{ y: "100%" }}
|
||||||
|
animate={{ y: 0 }}
|
||||||
|
transition={{ delay: 0.5, type: 'spring' }}
|
||||||
|
className="bg-black/30 backdrop-blur-md p-6 pb-12"
|
||||||
|
>
|
||||||
|
<p className="text-center text-sm font-bold uppercase tracking-widest mb-4 opacity-70">The correct answer was</p>
|
||||||
|
<div className="flex flex-col items-center gap-3">
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<div className={`${COLORS[correctOption.color]} p-3 rounded-xl shadow-lg`}>
|
||||||
|
<ShapeIcon size={24} fill="currentColor" />
|
||||||
|
</div>
|
||||||
|
<span className="text-2xl font-black">{correctOption.text}</span>
|
||||||
|
</div>
|
||||||
|
{correctOption.reason && (
|
||||||
|
<p className="text-center text-sm opacity-80 max-w-md mt-2">
|
||||||
|
{correctOption.reason}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ const getClient = () => {
|
||||||
export const generateQuiz = async (topic: string): Promise<Quiz> => {
|
export const generateQuiz = async (topic: string): Promise<Quiz> => {
|
||||||
const ai = getClient();
|
const ai = getClient();
|
||||||
|
|
||||||
const prompt = `Generate a trivia quiz about "${topic}". Create 10 engaging multiple-choice questions. Each question must have exactly 4 options, and exactly one correct answer. Vary the difficulty.`;
|
const prompt = `Generate a trivia quiz about "${topic}". Create 10 engaging multiple-choice questions. Each question must have exactly 4 options, and exactly one correct answer. Vary the difficulty. For each option, provide a brief reason explaining why it is correct or incorrect - this helps players learn.`;
|
||||||
|
|
||||||
const response = await ai.models.generateContent({
|
const response = await ai.models.generateContent({
|
||||||
model: "gemini-3-flash-preview",
|
model: "gemini-3-flash-preview",
|
||||||
|
|
@ -36,9 +36,10 @@ export const generateQuiz = async (topic: string): Promise<Quiz> => {
|
||||||
type: Type.OBJECT,
|
type: Type.OBJECT,
|
||||||
properties: {
|
properties: {
|
||||||
text: { type: Type.STRING },
|
text: { type: Type.STRING },
|
||||||
isCorrect: { type: Type.BOOLEAN }
|
isCorrect: { type: Type.BOOLEAN },
|
||||||
|
reason: { type: Type.STRING, description: "Brief explanation of why this answer is correct or incorrect" }
|
||||||
},
|
},
|
||||||
required: ["text", "isCorrect"]
|
required: ["text", "isCorrect", "reason"]
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -68,7 +69,8 @@ export const generateQuiz = async (topic: string): Promise<Quiz> => {
|
||||||
text: opt.text,
|
text: opt.text,
|
||||||
isCorrect: opt.isCorrect,
|
isCorrect: opt.isCorrect,
|
||||||
shape: shapes[index % 4],
|
shape: shapes[index % 4],
|
||||||
color: colors[index % 4]
|
color: colors[index % 4],
|
||||||
|
reason: opt.reason
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
1
types.ts
1
types.ts
|
|
@ -16,6 +16,7 @@ export interface AnswerOption {
|
||||||
isCorrect: boolean;
|
isCorrect: boolean;
|
||||||
shape: 'triangle' | 'diamond' | 'circle' | 'square';
|
shape: 'triangle' | 'diamond' | 'circle' | 'square';
|
||||||
color: 'red' | 'blue' | 'yellow' | 'green';
|
color: 'red' | 'blue' | 'yellow' | 'green';
|
||||||
|
reason?: string; // Explanation for why this answer is correct or incorrect
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Question {
|
export interface Question {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue