feat(login): improve error handling and show rate limit message
- Display meaningful error messages on login failure, including rate limiting (429) - Added fallback for unexpected JSON responses from the server - Integrated `Message` component for error display - Cleaned up form value clearing and error state management
This commit is contained in:
@@ -1,45 +1,63 @@
|
|||||||
import React, { useRef } from "react";
|
import React, { useRef, useState } from "react";
|
||||||
import { useDispatch } from "react-redux";
|
import { useDispatch } from "react-redux";
|
||||||
import { Link, useNavigate, useOutletContext } from "react-router-dom";
|
import { Link, useNavigate, useOutletContext } from "react-router-dom";
|
||||||
import { userSliceActions } from "../../store/userSlice";
|
import { userSliceActions } from "../../store/userSlice";
|
||||||
import { BACKEND_URL } from "../../constants";
|
import { BACKEND_URL } from "../../constants";
|
||||||
import { t } from "../../service/translation"; // Adjust path as needed
|
import { t } from "../../service/translation";
|
||||||
|
import Message from "../../components/Message"; // Import Message component
|
||||||
|
|
||||||
const LoginPage = () => {
|
const LoginPage = () => {
|
||||||
// Get language from outlet context
|
|
||||||
const { language } = useOutletContext();
|
const { language } = useOutletContext();
|
||||||
|
|
||||||
console.log("LoginPage language:", language);
|
|
||||||
|
|
||||||
const emailElement = useRef();
|
const emailElement = useRef();
|
||||||
const passwordElement = useRef();
|
const passwordElement = useRef();
|
||||||
|
|
||||||
|
const [error, setError] = useState(""); // For showing errors
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const handleLogin = async (event) => {
|
const handleLogin = async (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const responce = await fetch(`${BACKEND_URL}/api/v1/login`, {
|
setError(""); // Clear previous error
|
||||||
method: "POST",
|
|
||||||
credentials: "include",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
email: emailElement.current.value,
|
|
||||||
password: passwordElement.current.value,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
const user = await responce.json();
|
try {
|
||||||
|
const response = await fetch(`${BACKEND_URL}/api/v1/login`, {
|
||||||
|
method: "POST",
|
||||||
|
credentials: "include",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
email: emailElement.current.value,
|
||||||
|
password: passwordElement.current.value,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
dispatch(userSliceActions.addUser(user.data));
|
if (response.status === 429) {
|
||||||
|
setError("Too many login attempts. Please try again after 15 minutes.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
emailElement.current.value = "";
|
let data;
|
||||||
passwordElement.current.value = "";
|
try {
|
||||||
|
data = await response.json();
|
||||||
|
} catch (jsonError) {
|
||||||
|
setError("Unexpected server response. Please try again.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (user.success === true) {
|
if (data.success === true) {
|
||||||
navigate("/");
|
dispatch(userSliceActions.addUser(data.data));
|
||||||
|
navigate("/");
|
||||||
|
} else {
|
||||||
|
setError(data.message || "Login failed. Please check your credentials.");
|
||||||
|
}
|
||||||
|
|
||||||
|
emailElement.current.value = "";
|
||||||
|
passwordElement.current.value = "";
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Login error:", err);
|
||||||
|
setError("Something went wrong. Please try again.");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -55,9 +73,15 @@ const LoginPage = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="backdrop-blur-md bg-gradient-to-tr from-slate-300/10 to-slate-200/30 rounded-lg shadow-md lg:p-36">
|
<div className="backdrop-blur-md bg-gradient-to-tr from-slate-300/10 to-slate-200/30 rounded-lg shadow-md lg:p-36">
|
||||||
<h1 className="text-2xl font-bold text-gray-50 mb-4">{t("login_title", language)}</h1>
|
<h1 className="text-2xl font-bold text-gray-50 mb-4">{t("login_title", language)}</h1>
|
||||||
<p className="text-gray-100 mb-6">
|
<p className="text-gray-100 mb-6">{t("login_subtitle", language)}</p>
|
||||||
{t("login_subtitle", language)}
|
|
||||||
</p>
|
{/* Show error message */}
|
||||||
|
{error && (
|
||||||
|
<div className="my-4">
|
||||||
|
<Message message={error} type="error" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<form className="space-y-6" onSubmit={handleLogin}>
|
<form className="space-y-6" onSubmit={handleLogin}>
|
||||||
<div>
|
<div>
|
||||||
<label
|
<label
|
||||||
@@ -116,17 +140,14 @@ const LoginPage = () => {
|
|||||||
<div className="flex justify-center">
|
<div className="flex justify-center">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="text-white w-1/2 backdrop-blur-lg bg-gradient-to-tr from-slate-100/15 to-slate-200/15 shadow-lg hover:backdrop-blur-lg focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
|
className="text-white w-1/2 backdrop-blur-lg bg-gradient-to-tr from-slate-100/15 to-slate-200/15 shadow-lg hover:backdrop-blur-lg focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800"
|
||||||
>
|
>
|
||||||
{t("login_button", language)}
|
{t("login_button", language)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-gray-100 text-center mt-4">
|
<p className="text-gray-100 text-center mt-4">
|
||||||
{t("login_new_user", language)}{" "}
|
{t("login_new_user", language)}{" "}
|
||||||
<Link
|
<Link to={"/user/signup"} className="text-blue-600 hover:underline">
|
||||||
to={"/user/signup"}
|
|
||||||
className="text-blue-600 hover:underline"
|
|
||||||
>
|
|
||||||
{t("login_signup", language)}
|
{t("login_signup", language)}
|
||||||
</Link>
|
</Link>
|
||||||
</p>
|
</p>
|
||||||
@@ -138,4 +159,3 @@ const LoginPage = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default LoginPage;
|
export default LoginPage;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user