migrant-web/resources/js/Pages/Employer/Auth/ForgotPassword.jsx
2026-07-07 14:16:17 +05:30

297 lines
14 KiB
JavaScript

import React, { useState } from 'react';
import { Head, Link, router } from '@inertiajs/react';
import {
KeyRound,
Eye,
EyeOff,
Loader2,
AlertTriangle,
CheckCircle2,
ArrowLeft
} from 'lucide-react';
import axios from 'axios';
export default function ForgotPassword() {
const [step, setStep] = useState(1); // 1 = Request Code, 2 = Verify & Reset, 3 = Success
const [email, setEmail] = useState('');
const [otp, setOtp] = useState('');
const [password, setPassword] = useState('');
const [passwordConfirmation, setPasswordConfirmation] = useState('');
const [showPassword, setShowPassword] = useState(false);
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const [successMessage, setSuccessMessage] = useState('');
const handleSendCode = async (e) => {
e.preventDefault();
setError('');
if (!email.trim()) {
setError('Please enter your email address.');
return;
}
setLoading(true);
try {
const response = await axios.post('/employer/forgot-password', { email });
if (response.data.success) {
setStep(2);
setSuccessMessage('A 6-digit verification code has been sent to your email.');
} else {
setError(response.data.message || 'Failed to send reset code. Please try again.');
}
} catch (err) {
setError(err.response?.data?.message || 'Failed to send reset code. Please verify your email address.');
} finally {
setLoading(false);
}
};
const handleResetPassword = async (e) => {
e.preventDefault();
setError('');
if (!otp.trim() || otp.length !== 6) {
setError('Please enter the 6-digit verification code.');
return;
}
if (!password) {
setError('Please enter a new password.');
return;
}
if (password.length < 8) {
setError('Password must be at least 8 characters long.');
return;
}
if (password !== passwordConfirmation) {
setError('Passwords do not match.');
return;
}
// Validate password strength: 1 uppercase, 1 lowercase, 1 number, 1 special character
const hasUppercase = /[A-Z]/.test(password);
const hasLowercase = /[a-z]/.test(password);
const hasNumber = /[0-9]/.test(password);
const hasSpecial = /[^A-Za-z0-9]/.test(password);
if (!hasUppercase || !hasLowercase || !hasNumber || !hasSpecial) {
setError('Password must contain at least 1 uppercase, 1 lowercase, 1 number, and 1 special character.');
return;
}
setLoading(true);
try {
const response = await axios.post('/employer/reset-password', {
email,
otp,
password,
password_confirmation: passwordConfirmation
});
if (response.data.success) {
setStep(3);
} else {
setError(response.data.message || 'Failed to reset password. Please check your verification code.');
}
} catch (err) {
setError(err.response?.data?.message || 'Failed to reset password. Please check your inputs and try again.');
} finally {
setLoading(false);
}
};
return (
<div className="min-h-screen flex flex-col justify-center items-center px-4 bg-slate-50 font-sans">
<Head title="Reset Password - Sponsor Portal" />
<div className="w-full max-w-md bg-white rounded-2xl shadow-sm border border-slate-200 p-8 space-y-6">
{/* Header Icon & Title */}
{step !== 3 && (
<div className="text-center space-y-4">
<div className="w-16 h-16 bg-blue-50 rounded-full flex items-center justify-center mx-auto border border-blue-200">
<KeyRound className="w-8 h-8 text-[#185FA5]" />
</div>
<div className="space-y-2">
<h1 className="text-2xl font-bold text-gray-900 tracking-tight">
{step === 1 ? 'Forgot Password' : 'Reset Password'}
</h1>
<p className="text-sm text-gray-600">
{step === 1
? "Enter your registration email address and we'll send you a verification code."
: `Enter the 6-digit code sent to ${email} and your new password.`
}
</p>
</div>
</div>
)}
{/* Status/Error Messages */}
{error && (
<div className="flex items-start space-x-3 p-4 bg-red-50 border border-red-200 rounded-xl text-red-800 text-xs font-medium">
<AlertTriangle className="w-5 h-5 text-red-600 flex-shrink-0 mt-0.5" />
<span>{error}</span>
</div>
)}
{successMessage && step === 2 && (
<div className="flex items-start space-x-3 p-4 bg-emerald-50 border border-emerald-200 rounded-xl text-emerald-800 text-xs font-medium animate-pulse">
<CheckCircle2 className="w-5 h-5 text-emerald-600 flex-shrink-0 mt-0.5" />
<span>{successMessage}</span>
</div>
)}
{/* Step 1: Request Code Form */}
{step === 1 && (
<form onSubmit={handleSendCode} className="space-y-5">
<div>
<label className="block text-xs font-medium text-gray-700 mb-1">Email Address</label>
<input
type="email"
placeholder="name@company.com"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="w-full px-3.5 py-2.5 rounded-xl border border-slate-300 text-sm focus:outline-none focus:ring-2 focus:ring-[#185FA5]/20 focus:border-[#185FA5]"
autoFocus
required
/>
</div>
<button
type="submit"
disabled={loading}
className="w-full bg-[#185FA5] hover:bg-[#0C447C] active:bg-[#08305c] text-white rounded-xl h-11 font-semibold text-sm transition-colors shadow-sm flex items-center justify-center disabled:opacity-70 disabled:cursor-not-allowed"
>
{loading ? <Loader2 className="w-5 h-5 animate-spin mr-2" /> : 'Send Reset Code'}
</button>
</form>
)}
{/* Step 2: Verification and Reset Form */}
{step === 2 && (
<form onSubmit={handleResetPassword} className="space-y-5">
{/* OTP Input */}
<div>
<label className="block text-xs font-medium text-gray-700 mb-1">6-Digit Code</label>
<input
type="text"
placeholder="123456"
maxLength={6}
value={otp}
onChange={(e) => setOtp(e.target.value.replace(/\D/g, ''))}
className="w-full px-3.5 py-2.5 rounded-xl border border-slate-300 text-sm font-semibold tracking-wider text-center focus:outline-none focus:ring-2 focus:ring-[#185FA5]/20 focus:border-[#185FA5]"
autoFocus
required
/>
</div>
{/* Password Input */}
<div>
<label className="block text-xs font-medium text-gray-700 mb-1">New Password</label>
<div className="relative">
<input
type={showPassword ? 'text' : 'password'}
placeholder="••••••••"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full pl-3.5 pr-10 py-2.5 rounded-xl border border-slate-300 text-sm focus:outline-none focus:ring-2 focus:ring-[#185FA5]/20 focus:border-[#185FA5]"
required
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute inset-y-0 right-0 pr-3 flex items-center text-slate-400 hover:text-slate-600 focus:outline-none"
>
{showPassword ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
</button>
</div>
<p className="mt-1 text-[10px] text-gray-500 leading-normal">
Must be at least 8 characters with 1 uppercase, 1 lowercase, 1 number, and 1 special symbol.
</p>
</div>
{/* Confirm Password Input */}
<div>
<label className="block text-xs font-medium text-gray-700 mb-1">Confirm Password</label>
<div className="relative">
<input
type={showConfirmPassword ? 'text' : 'password'}
placeholder="••••••••"
value={passwordConfirmation}
onChange={(e) => setPasswordConfirmation(e.target.value)}
className="w-full pl-3.5 pr-10 py-2.5 rounded-xl border border-slate-300 text-sm focus:outline-none focus:ring-2 focus:ring-[#185FA5]/20 focus:border-[#185FA5]"
required
/>
<button
type="button"
onClick={() => setShowConfirmPassword(!showConfirmPassword)}
className="absolute inset-y-0 right-0 pr-3 flex items-center text-slate-400 hover:text-slate-600 focus:outline-none"
>
{showConfirmPassword ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
</button>
</div>
</div>
<button
type="submit"
disabled={loading}
className="w-full bg-[#185FA5] hover:bg-[#0C447C] active:bg-[#08305c] text-white rounded-xl h-11 font-semibold text-sm transition-colors shadow-sm flex items-center justify-center disabled:opacity-70 disabled:cursor-not-allowed"
>
{loading ? <Loader2 className="w-5 h-5 animate-spin mr-2" /> : 'Reset Password'}
</button>
<button
type="button"
onClick={() => {
setStep(1);
setError('');
setSuccessMessage('');
}}
className="w-full border border-slate-300 hover:bg-slate-50 text-gray-700 rounded-xl h-11 font-semibold text-sm transition-colors shadow-sm flex items-center justify-center"
>
<ArrowLeft className="w-4 h-4 mr-2" /> Back
</button>
</form>
)}
{/* Step 3: Success Screen */}
{step === 3 && (
<div className="text-center space-y-6 py-4">
<div className="w-16 h-16 bg-emerald-50 rounded-full flex items-center justify-center mx-auto border border-emerald-200">
<CheckCircle2 className="w-8 h-8 text-emerald-600" />
</div>
<div className="space-y-2">
<h2 className="text-2xl font-bold text-gray-900 tracking-tight">Password Reset Success</h2>
<p className="text-sm text-gray-600">
Your password has been successfully updated. You can now use your new password to sign in.
</p>
</div>
<button
onClick={() => router.visit('/employer/login')}
className="w-full bg-[#185FA5] hover:bg-[#0C447C] active:bg-[#08305c] text-white rounded-xl h-11 font-semibold text-sm transition-colors shadow-sm"
>
Sign In
</button>
</div>
)}
{/* Footer Back Link */}
{step !== 3 && (
<div className="pt-2 text-center">
<Link href="/employer/login" className="text-xs text-[#185FA5] font-bold hover:underline">
Return to Sign in
</Link>
</div>
)}
</div>
</div>
);
}