| import { useState, FormEvent } from "react"; | |
| import { Link } from "react-router-dom"; | |
| import api from "@/lib/api"; | |
| import { toast } from "react-toastify"; | |
| import { motion } from "framer-motion"; | |
| import { User, ArrowRight, Loader2, CheckCircle } from "lucide-react"; | |
| export function ForgotPasswordForm() { | |
| const [username, setUsername] = useState(""); | |
| const [loading, setLoading] = useState(false); | |
| const [submitted, setSubmitted] = useState(false); | |
| const handleSubmit = async (e: FormEvent) => { | |
| e.preventDefault(); | |
| if (!username.trim()) { | |
| toast.warning("Please enter your username"); | |
| return; | |
| } | |
| setLoading(true); | |
| try { | |
| await api.post("/auth/forgot-password", { username: username.trim() }); | |
| setSubmitted(true); | |
| toast.success("Security question loaded. Continue with reset."); | |
| } catch (err: any) { | |
| toast.error(err.response?.data?.error || "Something went wrong. Please try again."); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| return ( | |
| <motion.div | |
| initial={{ opacity: 0, y: 20 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| className="w-full max-w-md mx-auto" | |
| > | |
| <div className="bg-white dark:bg-gray-800 rounded-2xl shadow-2xl p-8"> | |
| <div className="text-center mb-6"> | |
| <h2 className="text-2xl font-bold text-gray-900 dark:text-white">Forgot password?</h2> | |
| <p className="text-gray-500 dark:text-gray-400 mt-1"> | |
| Enter your username to load your security question. | |
| </p> | |
| </div> | |
| {submitted ? ( | |
| <div className="text-center space-y-4"> | |
| <div className="bg-green-50 dark:bg-green-900/20 p-4 rounded-xl"> | |
| <CheckCircle className="w-12 h-12 text-green-500 mx-auto mb-2" /> | |
| <p className="text-green-700 dark:text-green-400 font-medium">Security question loaded!</p> | |
| <p className="text-sm text-gray-600 dark:text-gray-400 mt-1"> | |
| If an account exists for {username}, you can continue with the reset flow. | |
| </p> | |
| </div> | |
| <Link to="/login" className="text-primary hover:underline text-sm"> | |
| Return to login | |
| </Link> | |
| </div> | |
| ) : ( | |
| <form onSubmit={handleSubmit} className="space-y-4"> | |
| <div className="relative"> | |
| <User className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 w-5 h-5" /> | |
| <input | |
| type="text" | |
| className="w-full pl-10 pr-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-primary focus:border-transparent transition" | |
| placeholder="your username" | |
| value={username} | |
| onChange={(e) => setUsername(e.target.value)} | |
| required | |
| disabled={loading} | |
| /> | |
| </div> | |
| <button | |
| type="submit" | |
| disabled={loading} | |
| className="w-full bg-primary hover:bg-primary/90 text-white font-semibold py-2 rounded-lg transition disabled:opacity-50 flex items-center justify-center gap-2" | |
| > | |
| {loading ? <Loader2 className="animate-spin w-5 h-5" /> : <ArrowRight className="w-5 h-5" />} | |
| {loading ? "Loading..." : "Load security question"} | |
| </button> | |
| <div className="text-center text-sm"> | |
| <Link to="/login" className="text-primary hover:underline">Back to login</Link> | |
| </div> | |
| </form> | |
| )} | |
| </div> | |
| </motion.div> | |
| ); | |
| } | |
Xet Storage Details
- Size:
- 3.59 kB
- Xet hash:
- 89464d003fc356391d7eb6cc01fc449944d092d98a21a39fec45f5e5920a7c25
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.