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:
K
2025-07-18 02:12:46 +05:30
parent aaf88fda56
commit 9c3feca6a7
+56 -36
View File
@@ -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", try {
headers: { const response = await fetch(`${BACKEND_URL}/api/v1/login`, {
"Content-Type": "application/json", method: "POST",
}, credentials: "include",
body: JSON.stringify({ headers: {
email: emailElement.current.value, "Content-Type": "application/json",
password: passwordElement.current.value, },
}), body: JSON.stringify({
}); email: emailElement.current.value,
password: passwordElement.current.value,
const user = await responce.json(); }),
});
dispatch(userSliceActions.addUser(user.data));
if (response.status === 429) {
emailElement.current.value = ""; setError("Too many login attempts. Please try again after 15 minutes.");
passwordElement.current.value = ""; return;
}
if (user.success === true) {
navigate("/"); let data;
try {
data = await response.json();
} catch (jsonError) {
setError("Unexpected server response. Please try again.");
return;
}
if (data.success === true) {
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;