Files
Wdipl-react/components/MathVerificationPopup.tsx

176 lines
6.8 KiB
TypeScript
Raw Permalink Normal View History

2025-09-26 16:03:21 +05:30
import React, { useState, useEffect } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { Button } from "./ui/button";
import { X } from "lucide-react";
interface MathVerificationPopupProps {
isOpen: boolean;
onVerify: () => void;
onClose: () => void;
}
export const MathVerificationPopup: React.FC<MathVerificationPopupProps> = ({
isOpen,
onVerify,
onClose,
}) => {
const [num1, setNum1] = useState(0);
const [num2, setNum2] = useState(0);
const [operator, setOperator] = useState<"+" | "-">("+");
const [userAnswer, setUserAnswer] = useState("");
const [error, setError] = useState("");
const [attempts, setAttempts] = useState(0);
const generateNewProblem = () => {
let newNum1 = Math.floor(Math.random() * 10) + 1;
let newNum2 = Math.floor(Math.random() * 10) + 1;
const newOperator = Math.random() > 0.5 ? "+" : "-";
// Ensure subtraction always has the larger number first
if (newOperator === "-" && newNum1 < newNum2) {
[newNum1, newNum2] = [newNum2, newNum1];
}
setNum1(newNum1);
setNum2(newNum2);
setOperator(newOperator);
setUserAnswer("");
setError("");
};
useEffect(() => {
if (isOpen) {
generateNewProblem();
setAttempts(0);
}
}, [isOpen]);
const correctAnswer = operator === "+" ? num1 + num2 : num1 - num2;
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!userAnswer.trim()) {
setError("Please enter your answer");
return;
}
const answer = parseInt(userAnswer);
if (isNaN(answer)) {
setError("Please enter a valid number");
return;
}
if (answer === correctAnswer) {
onVerify();
} else {
const newAttempts = attempts + 1;
setAttempts(newAttempts);
setError(`Incorrect answer. Attempt ${newAttempts} of 3.`);
if (newAttempts >= 3) {
generateNewProblem();
setAttempts(0);
}
}
};
const handleOverlayClick = (e: React.MouseEvent) => {
if (e.target === e.currentTarget) {
onClose();
}
};
return (
<AnimatePresence>
{isOpen && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="fixed inset-0 bg-black/60 backdrop-blur-sm z-50 flex items-center justify-center p-4"
onClick={handleOverlayClick}
>
<motion.div
initial={{ scale: 0.9, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0.9, opacity: 0 }}
className="bg-gray-900 border border-gray-700 rounded-2xl max-w-md w-full p-6 shadow-2xl"
>
<div className="flex items-center justify-between mb-6">
<h3 className="text-xl font-semibold text-white">
Verify You're Human
</h3>
<Button
variant="ghost"
size="icon"
onClick={onClose}
className="text-gray-400 hover:text-white"
>
<X className="w-5 h-5" />
</Button>
</div>
<div className="space-y-6">
<div className="text-center">
<p className="text-gray-300 mb-4">
Solve this simple math problem to continue:
</p>
<div className="flex items-center justify-center space-x-4 text-3xl font-bold text-white mb-6">
<span className="bg-gray-800 px-4 py-3 rounded-lg min-w-[60px] text-center">
{num1}
</span>
<span className="text-[#E5195E]">{operator}</span>
<span className="bg-gray-800 px-4 py-3 rounded-lg min-w-[60px] text-center">
{num2}
</span>
<span>=</span>
<input
type="text"
inputMode="numeric"
pattern="[0-9]*"
value={userAnswer}
onChange={(e) => {
setUserAnswer(e.target.value.replace(/[^0-9-]/g, ""));
setError("");
}}
className="bg-gray-800 border border-gray-600 rounded-lg px-4 py-3 text-center w-20 text-white focus:outline-none focus:border-[#E5195E]"
autoFocus
maxLength={3}
/>
</div>
{error && (
<p className="text-red-400 text-sm mt-2">{error}</p>
)}
</div>
<div className="flex gap-3">
<Button
type="button"
variant="outline"
onClick={generateNewProblem}
className="flex-1 border-gray-600 text-gray-300 hover:text-white"
>
New Problem
</Button>
<Button
onClick={handleSubmit}
className="flex-1 bg-[#E5195E] hover:bg-[#c41450] text-white"
>
Verify Answer
</Button>
</div>
<p className="text-xs text-gray-500 text-center">
This helps us prevent spam and automated submissions.
</p>
</div>
</motion.div>
</motion.div>
)}
</AnimatePresence>
);
};