SME-Web/src/components/auth/LoginForm.tsx

246 lines
9.1 KiB
TypeScript

// import OAuthGoogle from "./OAuthGoogle";
import axios from "axios";
import { useRef, useState } from "react";
// import { FcGoogle } from "react-icons/fc";
import { Link, useNavigate } from "react-router-dom";
import OAuthGoogle from "./OAuthGoogle";
// import Cookies from "js-cookie";
import { BsEye } from "react-icons/bs";
import { FiEyeOff } from "react-icons/fi";
import { TokenStorage } from "../../config/api";
// import api from "../../config/api";
interface FormDataState {
email: string;
password: string;
}
const LoginForm = ({ onSwitchToRegister }) => {
const [formData, setForm] = useState({ email: "", password: "" });
const [cacheKey, setCacheKey] = useState("");
const [nextClick, setNextClick] = useState(false);
const [showPassword, setShowPassword] = useState(false);
const [errors, setErrors] = useState({ email: "", password: "" });
const [isLoading, setIsLoading] = useState(false);
const formErrorRef = useRef<{ message: string; error: boolean }>({ message: "", error: false });
const navigate = useNavigate()
const validateEmail = (email) => {
const trimmedEmail = email.trim();
if (!trimmedEmail) {
return "Email address is required";
}
if (trimmedEmail.length > 254) {
return "Email address is too long";
}
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(trimmedEmail)) {
return "Please enter a valid email address";
}
// Check for common typos
const commonDomains = ['gmail.com', 'yahoo.com', 'hotmail.com', 'outlook.com'];
const domain = trimmedEmail.split('@')[1];
const commonTypos = {
'gmial.com': 'gmail.com',
'gmai.com': 'gmail.com',
'yahooo.com': 'yahoo.com',
'hotmial.com': 'hotmail.com'
};
if (commonTypos[domain]) {
return `Did you mean ${trimmedEmail.replace(domain, commonTypos[domain])}?`;
}
return "";
};
const validatePassword = (password) => {
if (!password) {
return "Password is required";
}
return "";
};
const handleEmailChange = (e) => {
const email = e.target.value;
setForm({ ...formData, email });
// Clear error when user starts typing
if (errors.email) {
setErrors({ ...errors, email: "" });
}
};
const handlePasswordChange = (e) => {
const password = e.target.value;
setForm({ ...formData, password });
// Clear error when user starts typing
if (errors.password) {
setErrors({ ...errors, password: "" });
}
};
const handleFormLoginClick = async () => {
const emailError = validateEmail(formData.email);
const passwordError = validatePassword(formData.password);
if (emailError || passwordError) {
setErrors({ email: emailError, password: passwordError });
return;
}
setIsLoading(true);
setErrors({ email: "", password: "" });
try {
const res = await axios.post(
`${import.meta.env.VITE_BASE_URI}/api/v1/auth/login`,
{ email: formData.email, password: formData.password },
{
withCredentials: true,
headers: {
"ngrok-skip-browser-warning": "69420",
"Access-Control-Allow-Origin": "*",
},
}
);
if (res.status == 200) {
TokenStorage.setTokens(res.data.accessToken, res.data.refreshToken)
formErrorRef.current = { message: "Login Successfully", error: false }
navigate("/");
}
} catch (err) {
if (err.response?.status == 400 || err.response?.status == 401) {
formErrorRef.current = { message: "The email or password you entered is invalid.", error: true }
} else if(err.response?.status == 404) {
formErrorRef.current = { message: "Email not found. Please sign up to continue.", error: true }
} else {
formErrorRef.current = { message: "Something went wrong. Please refresh and try again.", error: true }
}
} finally {
setIsLoading(false);
}
};
const handleGoogleLogin = () => {
// Your Google OAuth logic here
console.log("Google login clicked");
};
return (
<div className="bg-white p-8 rounded-lg w-full max-w-md">
<div className="text-center mb-6">
<span className="text-orange-500 text-sm">WELCOME BACK 👋</span>
<h2 className="text-2xl font-bold text-gray-800 mt-2">Login to your Account</h2>
</div>
<div className="flex justify-center">
<OAuthGoogle onGoogleLogin={handleGoogleLogin} />
</div>
<div className="flex items-center my-6">
<hr className="flex-1 border-gray-300" />
<span className="px-4 text-gray-500 text-sm">or</span>
<hr className="flex-1 border-gray-300" />
</div>
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Email Address
</label>
<input
type="email"
value={formData.email}
onChange={handleEmailChange}
placeholder="Enter your email"
className={`w-full px-4 py-1.5 border rounded-lg focus:ring-2 focus:border-transparent outline-none ${errors.email
? "border-red-500 focus:ring-red-500"
: "border-gray-300 focus:ring-blue-500"
}`}
disabled={nextClick || isLoading}
/>
{errors.email && (
<p className="text-red-500 text-sm mt-1">{errors.email}</p>
)}
</div>
{/* {nextClick && ( */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Password
</label>
<div className="relative">
<input
type={showPassword ? "text" : "password"}
value={formData.password}
onChange={handlePasswordChange}
placeholder="Enter your password"
className={`w-full px-4 py-1.5 border rounded-lg focus:ring-2 focus:border-transparent outline-none pr-12 ${errors.password
? "border-red-500 focus:ring-red-500"
: "border-gray-300 focus:ring-blue-500"
}`}
disabled={isLoading}
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-500 hover:text-gray-700"
disabled={isLoading}
>
{showPassword ? <FiEyeOff size={20} /> : <BsEye size={20} />}
</button>
</div>
{errors.password && (
<p className="text-red-500 text-sm mt-1">{errors.password}</p>
)}
{/* <div className="text-right mt-2">
<a href="#" className="text-blue-500 text-sm hover:underline">
Forgot password?
</a>
</div> */}
</div>
{/* )} */}
<p className={`${formErrorRef.current.error ? 'text-red-500 text-center' : 'text-green-500 text-center'} `}>{formErrorRef.current.message}</p>
<button
onClick={handleFormLoginClick}
disabled={isLoading}
className={`w-full py-2 rounded-lg font-medium transition-colors ${isLoading
? "bg-gray-400 cursor-not-allowed"
: "bg-blue-500 hover:bg-blue-600 text-white"
}`}
>
{isLoading ? "Logging in..." : "Login"}
</button>
{/* )} */}
<div className="text-center text-sm text-gray-600">
Create an Account?{" "}
<button
onClick={onSwitchToRegister}
className="text-blue-500 font-medium hover:underline"
>
Register
</button>
</div>
</div>
</div>
);
};
export default LoginForm