import React, { useState, useEffect } from "react"; import { Link } from "react-router-dom"; import { supabase } from "../lib/supabaseClient"; import { BrainCircuit, Mail, ArrowLeft, Loader2, CheckCircle2, ShieldCheck, Lock, KeyRound, AlertCircle } from "lucide-react"; import { motion, AnimatePresence } from "framer-motion"; function ForgotPassword() { const [step, setStep] = useState(1); const [email, setEmail] = useState(""); const [otp, setOtp] = useState(""); const [newPassword, setNewPassword] = useState(""); const [loading, setLoading] = useState(false); const [message, setMessage] = useState(""); const [error, setError] = useState(""); const [timeLeft, setTimeLeft] = useState(900); const [timerExpired, setTimerExpired] = useState(false); // Only re-run when step changes. DO NOT add timeLeft to deps array. useEffect(() => { if (step !== 2) return; setTimerExpired(false); setTimeLeft(900); const timer = setInterval(() => { setTimeLeft((prev) => { if (prev <= 1) { clearInterval(timer); setTimerExpired(true); return 0; } return prev - 1; }); }, 1000); return () => clearInterval(timer); }, [step]); const formatTime = (seconds) => { const mins = Math.floor(seconds / 60); const secs = seconds % 60; return `${mins}:${secs.toString().padStart(2, '0')}`; }; const handleSendOtp = async (e) => { e.preventDefault(); if (!email) { setError("Please enter your email address"); return; } setLoading(true); setError(""); try { // With Email OTP enabled in Supabase, resetPasswordForEmail sends // a 6-digit numeric OTP instead of a magic link. const { error } = await supabase.auth.resetPasswordForEmail(email); if (error) throw error; setMessage("Check your email for the 6-digit recovery code!"); setStep(2); } catch (err) { console.error("Password reset error:", err); setError(err.message || "An error occurred. Please try again."); } finally { setLoading(false); } }; const handleVerifyOtp = async (e) => { e.preventDefault(); if (!otp || otp.length !== 6) { setError("Please enter the 6-digit code"); return; } if (timerExpired) { setError("Your code has expired. Please request a new one."); return; } setLoading(true); setError(""); try { const { error } = await supabase.auth.verifyOtp({ email, token: otp, type: 'recovery' }); if (error) throw error; setStep(3); setMessage("Code verified. Please enter your new password."); } catch (err) { console.error("OTP verification error:", err); setError("Invalid or expired code. Please check your email and try again."); } finally { setLoading(false); } }; const handleUpdatePassword = async (e) => { e.preventDefault(); if (!newPassword || newPassword.length < 6) { setError("Password must be at least 6 characters long."); return; } setLoading(true); setError(""); try { const { error } = await supabase.auth.updateUser({ password: newPassword }); if (error) throw error; setStep(4); } catch (err) { console.error("Update password error:", err); setError(err.message || "Failed to update password."); } finally { setLoading(false); } }; return (
{step === 1 ? "Enter your email to initiate recovery." : step === 2 ? "Enter the 6-digit code sent to your email." : "Set a new high-security password."}
Your security credentials have been updated across all nodes.
Proceed to Terminal{error}
{message}