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 (
)
}
// 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