Reason working

This commit is contained in:
Joey Yakimowich-Payne 2026-01-13 07:55:17 -07:00
commit 845e3ab3d6
No known key found for this signature in database
GPG key ID: 6BFE655FA5ABD1E1
4 changed files with 85 additions and 39 deletions

View file

@ -14,6 +14,7 @@ export const QuizCreator: React.FC<QuizCreatorProps> = ({ onFinalize, onCancel }
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 = () => {
@ -28,7 +29,8 @@ export const QuizCreator: React.FC<QuizCreatorProps> = ({ onFinalize, onCancel }
text,
isCorrect: idx === correctIdx,
shape: shapes[idx],
color: colors[idx]
color: colors[idx],
reason: reasons[idx].trim() || undefined
}));
const newQuestion: Question = {
@ -41,6 +43,7 @@ export const QuizCreator: React.FC<QuizCreatorProps> = ({ onFinalize, onCancel }
setQuestions([...questions, newQuestion]);
setQText('');
setOptions(['', '', '', '']);
setReasons(['', '', '', '']);
setCorrectIdx(0);
};
@ -123,26 +126,39 @@ export const QuizCreator: React.FC<QuizCreatorProps> = ({ onFinalize, onCancel }
const bgClass = COLORS[['red','blue','yellow','green'][idx] as any];
return (
<div key={idx} className={`flex items-center gap-3 p-3 rounded-2xl border-4 ${borderColor} bg-white shadow-sm transition-all`}>
<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-full rounded-full ${bgClass}`}></div>
<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={opt}
value={reasons[idx]}
onChange={(e) => {
const newOpts = [...options];
newOpts[idx] = e.target.value;
setOptions(newOpts);
const newReasons = [...reasons];
newReasons[idx] = e.target.value;
setReasons(newReasons);
}}
className="w-full p-2 outline-none font-bold text-gray-700 bg-transparent placeholder:font-normal"
placeholder={`Option ${idx + 1}`}
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>
)

View file

@ -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">
{correctOption.text}
</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>
</div>
);
@ -110,9 +120,16 @@ export const RevealScreen: React.FC<RevealScreenProps> = ({
className="flex flex-col items-center"
>
{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">
<span className="text-4xl font-black">+{pointsEarned}</span>
<span className="font-bold uppercase opacity-80">Points</span>
<div className="flex flex-col items-center gap-4 mb-8">
<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="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 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>
</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>
{!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>
);
};

View file

@ -13,7 +13,7 @@ const getClient = () => {
export const generateQuiz = async (topic: string): Promise<Quiz> => {
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({
model: "gemini-3-flash-preview",
@ -36,9 +36,10 @@ export const generateQuiz = async (topic: string): Promise<Quiz> => {
type: Type.OBJECT,
properties: {
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,
isCorrect: opt.isCorrect,
shape: shapes[index % 4],
color: colors[index % 4]
color: colors[index % 4],
reason: opt.reason
}));
return {

View file

@ -16,6 +16,7 @@ export interface AnswerOption {
isCorrect: boolean;
shape: 'triangle' | 'diamond' | 'circle' | 'square';
color: 'red' | 'blue' | 'yellow' | 'green';
reason?: string; // Explanation for why this answer is correct or incorrect
}
export interface Question {