password reset
This commit is contained in:
175
frontend/src/components/ForgotPasswordModal.tsx
Normal file
175
frontend/src/components/ForgotPasswordModal.tsx
Normal file
@@ -0,0 +1,175 @@
|
||||
import React, { useState } from "react";
|
||||
import { authAPI } from "../services/api";
|
||||
|
||||
interface ForgotPasswordModalProps {
|
||||
show: boolean;
|
||||
onHide: () => void;
|
||||
onBackToLogin?: () => void;
|
||||
}
|
||||
|
||||
const ForgotPasswordModal: React.FC<ForgotPasswordModalProps> = ({
|
||||
show,
|
||||
onHide,
|
||||
onBackToLogin,
|
||||
}) => {
|
||||
const [email, setEmail] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
const [success, setSuccess] = useState(false);
|
||||
|
||||
const resetModal = () => {
|
||||
setEmail("");
|
||||
setError("");
|
||||
setSuccess(false);
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
setError("");
|
||||
|
||||
try {
|
||||
await authAPI.forgotPassword(email);
|
||||
setSuccess(true);
|
||||
} catch (err: any) {
|
||||
console.error('Forgot password error:', err);
|
||||
const errorData = err.response?.data;
|
||||
|
||||
// Check for rate limiting
|
||||
if (err.response?.status === 429) {
|
||||
const retryAfter = errorData?.retryAfter;
|
||||
if (retryAfter) {
|
||||
const minutes = Math.ceil(retryAfter / 60);
|
||||
setError(`Too many password reset requests. Please try again in ${minutes} minute${minutes > 1 ? 's' : ''}.`);
|
||||
} else {
|
||||
setError('Too many password reset requests. Please try again later.');
|
||||
}
|
||||
} else if (errorData?.details) {
|
||||
// Validation errors
|
||||
const validationErrors = errorData.details.map((d: any) => d.message).join(', ');
|
||||
setError(validationErrors);
|
||||
} else {
|
||||
setError(errorData?.error || "Failed to send reset email. Please try again.");
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (!show) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="modal show d-block"
|
||||
tabIndex={-1}
|
||||
style={{ backgroundColor: "rgba(0,0,0,0.5)" }}
|
||||
>
|
||||
<div className="modal-dialog modal-dialog-centered">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header border-0 pb-0">
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close"
|
||||
onClick={() => {
|
||||
resetModal();
|
||||
onHide();
|
||||
}}
|
||||
></button>
|
||||
</div>
|
||||
<div className="modal-body px-4 pb-4">
|
||||
{success ? (
|
||||
<>
|
||||
<div className="text-center">
|
||||
<i
|
||||
className="bi bi-envelope-check text-success"
|
||||
style={{ fontSize: "3rem" }}
|
||||
></i>
|
||||
<h4 className="mt-3">Check Your Email</h4>
|
||||
<p className="text-muted">
|
||||
If an account exists with that email address, you will
|
||||
receive password reset instructions shortly.
|
||||
</p>
|
||||
<p className="text-muted small">
|
||||
Please check your spam folder if you don't see the email
|
||||
within a few minutes.
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-primary mt-3"
|
||||
onClick={() => {
|
||||
resetModal();
|
||||
onHide();
|
||||
}}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<h4 className="text-center mb-2">Forgot Password?</h4>
|
||||
<p className="text-center text-muted mb-4">
|
||||
Enter your email address and we'll send you instructions to
|
||||
reset your password.
|
||||
</p>
|
||||
|
||||
{error && (
|
||||
<div className="alert alert-danger" role="alert">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="mb-3">
|
||||
<label className="form-label">Email Address</label>
|
||||
<input
|
||||
type="email"
|
||||
className="form-control"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="your@email.com"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-primary w-100 py-3"
|
||||
disabled={loading || !email}
|
||||
>
|
||||
{loading ? "Sending..." : "Send Reset Instructions"}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div className="text-center mt-3">
|
||||
<small className="text-muted">
|
||||
Remember your password?{" "}
|
||||
<a
|
||||
href="#"
|
||||
className="text-primary text-decoration-none"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
resetModal();
|
||||
if (onBackToLogin) {
|
||||
onBackToLogin();
|
||||
} else {
|
||||
onHide();
|
||||
}
|
||||
}}
|
||||
>
|
||||
Back to Login
|
||||
</a>
|
||||
</small>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ForgotPasswordModal;
|
||||
Reference in New Issue
Block a user