import { Box, Card, Typography, Button, // Link, Container, Dialog, DialogContent, Zoom, Fade, Stepper, Step, StepLabel, IconButton, CircularProgress, } from '@mui/material' import CheckCircleIcon from '@mui/icons-material/CheckCircle' import ArrowBackIcon from '@mui/icons-material/ArrowBack' import LockResetIcon from '@mui/icons-material/LockReset' import PhoneAndroidIcon from '@mui/icons-material/PhoneAndroid' import VpnKeyIcon from '@mui/icons-material/VpnKey' import { keyframes } from '@mui/system' import { useEffect, useRef, useState } from 'react' import MuiDynamicInput from '../../utills/MuiDynamicInput' import axiosInstance from '../../api/axiosInstance' import toast from 'react-hot-toast' import { Link } from 'react-router-dom' // Keyframe animations const scaleIn = keyframes` 0% { transform: scale(0); opacity: 0; } 50% { transform: scale(1.2); } 100% { transform: scale(1); opacity: 1; } ` const pulse = keyframes` 0% { box-shadow: 0 0 0 0 rgba(76, 175, 80, 0.4); } 70% { box-shadow: 0 0 0 20px rgba(76, 175, 80, 0); } 100% { box-shadow: 0 0 0 0 rgba(76, 175, 80, 0); } ` const fadeInUp = keyframes` 0% { opacity: 0; transform: translateY(20px); } 100% { opacity: 1; transform: translateY(0); } ` const shake = keyframes` 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-5px); } 75% { transform: translateX(5px); } ` // Success Modal Component function SuccessModal({ open, onClose, title, message }) { return ( {title} {message} ) } // OTP Input Component function OTPInput({ value, onChange, error, disabled }) { const inputRefs = useRef([]) const otpLength = 4 const handleChange = (index, e) => { const val = e.target.value if (!/^\d*$/.test(val)) return const newOtp = value.split('') newOtp[index] = val.slice(-1) const otpString = newOtp.join('') onChange({ target: { name: 'otp', value: otpString } }) // Move to next input if (val && index < otpLength - 1) { inputRefs.current[index + 1]?.focus() } } const handleKeyDown = (index, e) => { if (e.key === 'Backspace' && !value[index] && index > 0) { inputRefs.current[index - 1]?.focus() } } const handlePaste = (e) => { e.preventDefault() const pastedData = e.clipboardData.getData('text').slice(0, otpLength) if (/^\d+$/.test(pastedData)) { onChange({ target: { name: 'otp', value: pastedData } }) const focusIndex = Math.min(pastedData.length, otpLength - 1) inputRefs.current[focusIndex]?.focus() } } return ( {Array.from({ length: otpLength }).map((_, index) => ( (inputRefs.current[index] = el)} type="text" inputMode="numeric" maxLength={1} value={value[index] || ''} onChange={(e) => handleChange(index, e)} onKeyDown={(e) => handleKeyDown(index, e)} onPaste={handlePaste} disabled={disabled} style={{ width: '45px', height: '55px', textAlign: 'center', fontSize: '24px', fontWeight: 600, border: `2px solid ${error ? '#f44336' : value[index] ? '#4CAF50' : '#ddd'}`, borderRadius: '12px', outline: 'none', transition: 'all 0.2s', backgroundColor: disabled ? '#f5f5f5' : 'white', }} onFocus={(e) => { e.target.style.borderColor = '#4CAF50' e.target.style.boxShadow = '0 0 0 3px rgba(76, 175, 80, 0.2)' }} onBlur={(e) => { e.target.style.borderColor = value[index] ? '#4CAF50' : '#ddd' e.target.style.boxShadow = 'none' }} /> ))} {error && ( {error} )} ) } const ForgotPassworForm = () => { const steps = ['Enter Mobile', 'Verify OTP', 'Reset Password'] const [activeStep, setActiveStep] = useState(0) const [loading, setLoading] = useState(false) const [successModal, setSuccessModal] =useState(false) const [resendTimer, setResendTimer] = useState(0) const [formData, setFormData] = useState({ mobile: '', otp: '', newPassword: '', confirmPassword: '', }) const [errors, setErrors] = useState({}) // Resend OTP Timer useEffect(() => { let interval if (resendTimer > 0) { interval = setInterval(() => { setResendTimer((prev) => prev - 1) }, 1000) } return () => clearInterval(interval) }, [resendTimer]) const handleChange = (e) => { const { name, value } = e.target setFormData((prev) => ({ ...prev, [name]: value })) if (errors[name]) { setErrors((prev) => ({ ...prev, [name]: '' })) } } // Step 1: Validate Mobile const validateMobile = () => { const newErrors = {} if (!formData.mobile) { newErrors.mobile = 'Mobile number is required' } else if (!/^\d{10}$/.test(formData.mobile)) { newErrors.mobile = 'Enter a valid 10-digit mobile number' } setErrors(newErrors) return Object.keys(newErrors).length === 0 } // Step 2: Validate OTP const validateOTP = () => { const newErrors = {} if (!formData.otp || formData.otp.length !== 4) { newErrors.otp = 'Please enter the 6-digit OTP' } setErrors(newErrors) return Object.keys(newErrors).length === 0 } // Step 3: Validate Password const validatePassword = () => { const newErrors = {} if (!formData.newPassword) { newErrors.newPassword = 'New password is required' } else if (formData.newPassword.length < 8) { newErrors.newPassword = 'Password must be at least 8 characters' } else if (!/(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/.test(formData.newPassword)) { newErrors.newPassword = 'Must include uppercase, lowercase, and number' } if (!formData.confirmPassword) { newErrors.confirmPassword = 'Please confirm your password' } else if (formData.newPassword !== formData.confirmPassword) { newErrors.confirmPassword = 'Passwords do not match' } setErrors(newErrors) return Object.keys(newErrors).length === 0 } // Submit Mobile const handleMobileSubmit = async () => { if (!validateMobile()) return setLoading(true) try { const response = await axiosInstance.post(`/forgot_password_send_otp?mobile=${formData.mobile}`) toast.success(response.data?.message || "OTP sent successfully") setLoading(false) setActiveStep(1) setResendTimer(120) // 2 minutes timer } catch (error) { setLoading(false) console.error("Forgot password error:", error) const errorMessage = error.response?.data?.message || error.response?.data?.error || "Failed to send OTP" toast.error(errorMessage) } } // Verify OTP const handleOTPSubmit = async () => { if (!validateOTP()) return setLoading(true) try { const response = await axiosInstance.post(`/forgot_password_verify_otp?mobile=${formData.mobile}&otp=${formData.otp}`) toast.success(response.data?.message || "OTP Verified Successfully") setLoading(false) setActiveStep(2) } catch (error) { setLoading(false) console.error("OTP verification error:", error) const errorMessage = error.response?.data?.message || error.response?.data?.error || "Invalid OTP" toast.error(errorMessage) } } // Resend OTP const handleResendOTP = () => { handleMobileSubmit() } // Submit New Password const handlePasswordSubmit = async () => { if (!validatePassword()) return setLoading(true) try { const encodedPassword = encodeURIComponent(formData.newPassword) const response = await axiosInstance.post(`/forgot_password_update?mobile=${formData.mobile}&otp=${formData.otp}&password=${encodedPassword}`) toast.success(response.data?.message || "Password updated successfully") setLoading(false) setSuccessModal(true) } catch (error) { setLoading(false) console.error("Password update error:", error) const errorMessage = error.response?.data?.message || error.response?.data?.error || "Failed to update password" toast.error(errorMessage) } } const handleBack = () => { if (activeStep > 0) { setActiveStep((prev) => prev - 1) setErrors({}) } } const handleSuccessClose = () => { setSuccessModal(false) setActiveStep(0) setFormData({ mobile: '', otp: '', newPassword: '', confirmPassword: '' }) // Navigate to login page console.log('Navigate to login...') } const formatTime = (seconds) => { const mins = Math.floor(seconds / 60) const secs = seconds % 60 return `${mins}:${secs.toString().padStart(2, '0')}` } // Render step icon const getStepIcon = (step) => { switch (step) { case 0: return case 1: return case 2: return default: return null } } return ( <> {/* Header */} {activeStep > 0 && ( )} Forgot Password {/* Stepper */} {steps.map((label, index) => ( {label} ))} {/* Form Content */} {/* Step 1: Mobile */} {activeStep === 0 && ( Enter your mobile number and we'll send you an OTP to reset your password. Back to Login )} {/* Step 2: OTP Verification */} {activeStep === 1 && ( We've sent a 4-digit OTP to {formData.mobile} {/* Timer and Resend */} {resendTimer > 0 ? ( Resend OTP in{' '} {formatTime(resendTimer)} ) : ( Resend OTP )} )} {/* Step 3: Reset Password */} {activeStep === 2 && ( Create a strong password with at least 8 characters including uppercase, lowercase, and numbers. )} {/* Success Modal */} ) } export default ForgotPassworForm