245 lines
7.3 KiB
TypeScript
245 lines
7.3 KiB
TypeScript
'use client';
|
|
|
|
import { useState } from 'react';
|
|
import {
|
|
Box,
|
|
TextField,
|
|
Button,
|
|
Typography,
|
|
Paper,
|
|
Alert,
|
|
CircularProgress,
|
|
Link as MuiLink,
|
|
} from '@mui/material';
|
|
import { Email, ArrowBack } from '@mui/icons-material';
|
|
import { motion } from 'framer-motion';
|
|
import Link from 'next/link';
|
|
import apiClient from '@/lib/api/client';
|
|
|
|
export default function ForgotPasswordPage() {
|
|
const [email, setEmail] = useState('');
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState('');
|
|
const [success, setSuccess] = useState(false);
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
|
|
if (!email.trim()) {
|
|
setError('Please enter your email address');
|
|
return;
|
|
}
|
|
|
|
setLoading(true);
|
|
setError('');
|
|
|
|
try {
|
|
await apiClient.post('/api/v1/auth/password/forgot', { email });
|
|
setSuccess(true);
|
|
} catch (err: any) {
|
|
console.error('Forgot password error:', err);
|
|
setError(err.response?.data?.message || 'Failed to send reset email. Please try again.');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Box
|
|
sx={{
|
|
minHeight: '100vh',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
background: 'linear-gradient(135deg, #FFE4E1 0%, #FFDAB9 100%)',
|
|
px: 2,
|
|
}}
|
|
>
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.5 }}
|
|
>
|
|
<Paper
|
|
elevation={0}
|
|
sx={{
|
|
maxWidth: 480,
|
|
width: '100%',
|
|
p: 4,
|
|
borderRadius: 4,
|
|
background: 'rgba(255, 255, 255, 0.95)',
|
|
backdropFilter: 'blur(10px)',
|
|
}}
|
|
>
|
|
{!success ? (
|
|
<>
|
|
<Box sx={{ textAlign: 'center', mb: 4 }}>
|
|
<Box
|
|
sx={{
|
|
width: 64,
|
|
height: 64,
|
|
borderRadius: '50%',
|
|
bgcolor: 'primary.light',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
mx: 'auto',
|
|
mb: 2,
|
|
}}
|
|
>
|
|
<Email sx={{ fontSize: 32, color: 'primary.main' }} />
|
|
</Box>
|
|
<Typography variant="h4" fontWeight="600" gutterBottom>
|
|
Forgot Password?
|
|
</Typography>
|
|
<Typography variant="body2" color="text.secondary">
|
|
No worries! Enter your email address and we'll send you a link to reset your password.
|
|
</Typography>
|
|
</Box>
|
|
|
|
{error && (
|
|
<Alert severity="error" sx={{ mb: 3, borderRadius: 2 }}>
|
|
{error}
|
|
</Alert>
|
|
)}
|
|
|
|
<form onSubmit={handleSubmit}>
|
|
<TextField
|
|
fullWidth
|
|
label="Email Address"
|
|
type="email"
|
|
value={email}
|
|
onChange={(e) => setEmail(e.target.value)}
|
|
disabled={loading}
|
|
required
|
|
sx={{
|
|
mb: 3,
|
|
'& .MuiOutlinedInput-root': {
|
|
borderRadius: 3,
|
|
},
|
|
}}
|
|
/>
|
|
|
|
<Button
|
|
fullWidth
|
|
type="submit"
|
|
variant="contained"
|
|
size="large"
|
|
disabled={loading}
|
|
sx={{
|
|
borderRadius: 3,
|
|
py: 1.5,
|
|
textTransform: 'none',
|
|
fontSize: 16,
|
|
fontWeight: 600,
|
|
}}
|
|
>
|
|
{loading ? <CircularProgress size={24} color="inherit" /> : 'Send Reset Link'}
|
|
</Button>
|
|
</form>
|
|
|
|
<Box sx={{ mt: 3, textAlign: 'center' }}>
|
|
<MuiLink
|
|
component={Link}
|
|
href="/login"
|
|
sx={{
|
|
display: 'inline-flex',
|
|
alignItems: 'center',
|
|
gap: 0.5,
|
|
color: 'primary.main',
|
|
textDecoration: 'none',
|
|
fontWeight: 500,
|
|
'&:hover': {
|
|
textDecoration: 'underline',
|
|
},
|
|
}}
|
|
>
|
|
<ArrowBack sx={{ fontSize: 18 }} />
|
|
Back to Login
|
|
</MuiLink>
|
|
</Box>
|
|
</>
|
|
) : (
|
|
<Box sx={{ textAlign: 'center' }}>
|
|
<Box
|
|
sx={{
|
|
width: 64,
|
|
height: 64,
|
|
borderRadius: '50%',
|
|
bgcolor: 'success.light',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
mx: 'auto',
|
|
mb: 3,
|
|
}}
|
|
>
|
|
<Email sx={{ fontSize: 32, color: 'success.main' }} />
|
|
</Box>
|
|
|
|
<Typography variant="h5" fontWeight="600" gutterBottom>
|
|
Check Your Email 📧
|
|
</Typography>
|
|
|
|
<Typography variant="body1" color="text.secondary" sx={{ mb: 3 }}>
|
|
If an account with that email exists, we've sent you a password reset link. Please check your inbox and follow the instructions.
|
|
</Typography>
|
|
|
|
<Alert severity="info" sx={{ mb: 3, borderRadius: 2, textAlign: 'left' }}>
|
|
<Typography variant="body2" fontWeight="600" gutterBottom>
|
|
Didn't receive the email?
|
|
</Typography>
|
|
<Typography variant="body2">
|
|
• Check your spam or junk folder
|
|
<br />
|
|
• Make sure you entered the correct email
|
|
<br />
|
|
• The link expires in 1 hour
|
|
</Typography>
|
|
</Alert>
|
|
|
|
<Button
|
|
fullWidth
|
|
variant="outlined"
|
|
onClick={() => {
|
|
setSuccess(false);
|
|
setEmail('');
|
|
}}
|
|
sx={{
|
|
borderRadius: 3,
|
|
py: 1.5,
|
|
textTransform: 'none',
|
|
fontSize: 16,
|
|
fontWeight: 600,
|
|
mb: 2,
|
|
}}
|
|
>
|
|
Try Another Email
|
|
</Button>
|
|
|
|
<MuiLink
|
|
component={Link}
|
|
href="/login"
|
|
sx={{
|
|
display: 'inline-flex',
|
|
alignItems: 'center',
|
|
gap: 0.5,
|
|
color: 'primary.main',
|
|
textDecoration: 'none',
|
|
fontWeight: 500,
|
|
'&:hover': {
|
|
textDecoration: 'underline',
|
|
},
|
|
}}
|
|
>
|
|
<ArrowBack sx={{ fontSize: 18 }} />
|
|
Back to Login
|
|
</MuiLink>
|
|
</Box>
|
|
)}
|
|
</Paper>
|
|
</motion.div>
|
|
</Box>
|
|
);
|
|
}
|