From 0cd4738d09ca7e0a2ab372e79ccc3b241d664bed Mon Sep 17 00:00:00 2001 From: Atharva Ombase <94031822+atharvaombase@users.noreply.github.com> Date: Sun, 3 Aug 2025 19:27:07 +0530 Subject: [PATCH] Enhance Login component with improved validation, loading state, and UI updates --- Frontend/src/pages/Authentication/Login.jsx | 212 +++++++++++--------- 1 file changed, 122 insertions(+), 90 deletions(-) diff --git a/Frontend/src/pages/Authentication/Login.jsx b/Frontend/src/pages/Authentication/Login.jsx index 20a8476..e6039a2 100644 --- a/Frontend/src/pages/Authentication/Login.jsx +++ b/Frontend/src/pages/Authentication/Login.jsx @@ -1,78 +1,76 @@ import { useState, useEffect } from "react"; -import { FiEye, FiEyeOff } from "react-icons/fi"; +import { FiEye, FiEyeOff, FiLoader } from "react-icons/fi"; import { Link, useNavigate } from "react-router-dom"; -import toast from "react-hot-toast"; // Import React Hot Toast +import toast, { Toaster } from "react-hot-toast"; // Import React Hot Toast import { useTranslation } from "react-i18next"; // for multilinguality const API_URL = import.meta.env.VITE_API_URL; // Using .env variable const Login = () => { const { t } = useTranslation(); // for multilinguality - const [showPassword, setShowPassword] = useState(false); - const [email, setEmail] = useState(""); - const [password, setPassword] = useState(""); - const [loading, setLoading] = useState(false); const navigate = useNavigate(); // For navigation + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [showPassword, setShowPassword] = useState(false); + const [loading, setLoading] = useState(false); + const [errors, setErrors] = useState({}); + + // Redirect if already logged in useEffect(() => { - // Check if token is present in localStorage and redirect to Dashboard if (localStorage.getItem("token")) { - navigate("/dashboard"); // Redirect to Dashboard + navigate("/dashboard"); } }, [navigate]); - const togglePassword = () => { - setShowPassword(!showPassword); + const togglePassword = () => setShowPassword((prev) => !prev); + + const validate = () => { + const errs = {}; + if (!email.trim()) errs.email = t("email_required"); + else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) + errs.email = t("invalid_email"); + if (!password) errs.password = t("password_required"); + return errs; }; const handleSubmit = async (e) => { e.preventDefault(); - setLoading(true); + const validation = validate(); + if (Object.keys(validation).length) { + setErrors(validation); + return; + } - // Show loading toast + setLoading(true); const toastId = toast.loading(t("logging_in_toast")); try { - const response = await fetch(`${API_URL}/api/login`, { + const response = await fetch(`${API_URL}/api/auth/login`, { method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - email, - password, - }), + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ email, password }), }); - const data = await response.json(); - - // Dismiss the loading toast after the response toast.dismiss(toastId); if (response.ok) { - // On success, store the token in localStorage localStorage.setItem("token", data.token); localStorage.setItem("expiresIn", data.expiresIn); + // fetch username asynchronously fetch(`${API_URL}/api/hdfs/getUsernameByEmail?email=${email}`) - .then((response) => response.text()) - .then((username) => { - localStorage.setItem("username", username); - }) - .catch((error) => { - console.error("Error fetching username:", error); - }); + .then((res) => res.text()) + .then((username) => localStorage.setItem("username", username)) + .catch((err) => console.error("Error fetching username:", err)); - // Show success toast toast.success(t("login_successful")); - // Redirect to Dashboard navigate("/dashboard"); } else { - // Show error toast if login fails toast.error(data.message || t("login_failed")); } } catch (error) { - // Dismiss the loading toast and show error toast.dismiss(toastId); + console.error(error); toast.error(t("an_error_occurred")); } finally { setLoading(false); @@ -80,73 +78,107 @@ const Login = () => { }; return ( -
-
-

+
+ +
+

{t("login_title")}

- -
-
-
- setEmail(e.target.value)} - required - /> -
-
-
-
- setPassword(e.target.value)} - required - /> - -
-
-
- + {/* Email Field */} +
+ + { + setEmail(e.target.value); + setErrors((prev) => ({ ...prev, email: undefined })); + }} + className={`w-full border ${ + errors.email ? "border-red-500" : "border-gray-300" + } rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500`} + placeholder={t("email_placeholder")} + required + /> + {errors.email && ( +

{errors.email}

+ )} +
+ + {/* Password Field */} +
+ + { + setPassword(e.target.value); + setErrors((prev) => ({ ...prev, password: undefined })); + }} + className={`w-full border ${ + errors.password ? "border-red-500" : "border-gray-300" + } rounded-lg px-4 py-2 pr-10 focus:outline-none focus:ring-2 focus:ring-blue-500`} + placeholder={t("password_placeholder")} + required + /> + + {errors.password && ( +

{errors.password}

+ )} +
+ + {/* Forgot & Submit */} +
+ {t("forgot_password")}
+ -
-

- {t("dont_have_account")}{" "} - - {t("sign_up")} - -

-
+ +

+ {t("dont_have_account")}{" "} + + {t("sign_up")} + +

);