login
import React, { useState } from 'react';
import { authService } from '../Services/authService';
import { Button } from '../Shared/Components/Button/Button';
import { Input } from '../Shared/Components/Input/Input';
import { ForgotPasswordModal } from './ForgotPassword/ForgotPassword';
import { toast } from 'sonner';
import { apiService } from '../Services/apiService';
import { JWT_DECODE } from '../Services/jwtUtils'
import { EyeOff, Eye } from 'lucide-react';
import { AESCipher } from '../Services/cryptoUtil';
import { ENVIRONMENT_CONFIG } from '../Config/apiConfig';
import fordLogo from '../../src/Assets/images/ford-logo.png';
import blueSwirlA from '../../src/Assets/images/BlueSwirl1.png';
import blueSwirlB from '../../src/Assets/images/BlueSwirl2.png';
interface LoginProps {
onLogin: (user: any) => void;
}
export default function UserLogin({ onLogin }: LoginProps) {
const [email, setUsername] = useState('');
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [rememberMe] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [isChecked, setIsChecked] = useState(false);
const [, setError] = useState('');
const [showForgotPasswordModal, setShowForgotPasswordModal] = useState(false);
const [emailError, setEmailError] = useState("");
const [showPassword, setShowPassword] = useState(false);
const [showNewPassword, setShowNewPassword] = useState(false);
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
const [newUser, setNewUser] = useState(false);
const validateEmail = (value: string) => {
if (!value.includes("@") || !value.includes(".com")) {
setEmailError("Please enter a valid email (must contain @ and .com)");
} else {
setEmailError("");
}
};
const handleLogin = async (e: React.FormEvent) => {
e.preventDefault();
setIsLoading(true);
setError('');
try {
const tokenResult = await apiService.getToken();
let jwtTokenDecode = null;
if (ENVIRONMENT_CONFIG.dataFormat !== 'application/json') {
jwtTokenDecode = JWT_DECODE(tokenResult.data);
} else {
jwtTokenDecode = typeof(tokenResult.data) === 'string' ? JSON.parse(tokenResult.data) : tokenResult.data;
}
if (tokenResult.status !== "success" || !tokenResult.data) {
toast.error("An unexpected error occurred");
setError(tokenResult.message || "Failed to get token");
return;
}
const uniqueKey = jwtTokenDecode['unique_key'];
const secret_key = email.substring(0, 3);
const cipher = new AESCipher(uniqueKey + secret_key);
const hashedPassword = cipher.encrypt(password);
if (!hashedPassword) {
setError("Failed to encrypt password");
return;
}
const result = await authService.login({
email,
hashedPassword,
rememberMe
});
if (result.status === "success") {
if((localStorage.getItem('new_user') === 'false') || localStorage.getItem('new_user') === undefined){
onLogin(result.user);
toast.success(result.message || "Logged in successfully");
setNewUser(false)
console.log("Login successful:", result.user);
}
else{
setNewUser(true);
setPassword('');
}
} else {
toast.error(result.message || "Login failed");
setError(result.message || "Login failed");
}
} catch (error) {
console.error("Login error:", error);
toast.error("An unexpected error occurred");
setError("An unexpected error occurred");
} finally {
setIsLoading(false);
}
};
const handleCreatePassword = async (e: React.FormEvent) => {
e.preventDefault();
setIsLoading(true);
setError('');
if (password !== confirmPassword) {
toast.error("Password does not match!");
setIsLoading(false);
return;
}
try {
const tokenResult = await apiService.getToken();
let jwtTokenDecode = null;
if (ENVIRONMENT_CONFIG.dataFormat !== 'application/json') {
jwtTokenDecode = JWT_DECODE(tokenResult.data);
} else {
jwtTokenDecode = typeof(tokenResult.data) === 'string' ? JSON.parse(tokenResult.data) : tokenResult.data;
}
if (tokenResult.status !== "success" || !tokenResult.data) {
setError(tokenResult.message || "Failed to get token");
setIsLoading(false);
return;
}
const uniqueKey = jwtTokenDecode['unique_key'];
const secret_key = email.substring(0, 3);
const cipher = new AESCipher(uniqueKey + secret_key);
const hashedPassword = cipher.encrypt(password);
const payload = {
email: email,
password: hashedPassword,
reset: false
};
const result = await apiService.create_password(payload);
if (result.status === "success") {
toast.success(result.message);
setPassword('');
setConfirmPassword('');
setShowNewPassword(false);
setShowConfirmPassword(false);
setIsChecked(true);
localStorage.removeItem('new_user');
setNewUser(false);
} else {
setError(result.message || 'Password creation failed!');
toast.error(result.message || 'Password creation failed!');
}
} catch (error) {
console.error("Create password error:", error);
toast.error("An unexpected error occurred");
setError("An unexpected error occurred");
} finally {
setIsLoading(false);
}
};
const handleSSO = async () => {
setIsLoading(true);
setError('');
try {
const result = await authService.loginWithSSO();
if (!result.status) {
setError(result.message || 'SSO login failed');
}
} catch (error) {
console.error('SSO error:', error);
setError('SSO service unavailable');
} finally {
setIsLoading(false);
}
};
const handleForgotPassword = () => {
setShowForgotPasswordModal(true);
};
return (
<div className="bg-[#00052e] min-h-screen relative flex flex-col items-center justify-center">
{/* Background Pattern */}
<div className="flex justify-around absolute inset-0 opacity-100">
<img src={blueSwirlA} className="w-170 h-120 pl-15"/>
<img src={blueSwirlB} className="w-150 h-70 mt-100"/>
</div>
{/* Main Content */}
<div className="relative flex flex-col items-center justify-center flex-1 w-full px-m">
{/* Login Form Container */}
<div className="mb-2xl">
<div
className="bg-[#00052e] rounded-br-[16px] rounded-tl-[16px] p-5 w-full max-w-md shadow-2xl min-w-[30vw]"
style={{ minHeight: '420px' }}
>
{/* Ford Logo */}
<div className="text-center mb-6">
<div className="flex justify-around text-white text-5xl font-bold mb-3 tracking-wide" style={{ fontFamily: 'serif' }}>
<img
src={fordLogo}
alt="Ford"
className="w-45 h-20"/>
</div>
{/* Dimensional Intelligence */}
<h1 className="text-[#ffffff] text-[30px] font-['Roboto:Medium',_sans-serif] tracking-[0.1px] leading-[20px] mb-2">
Dimensional Intelligence
</h1>
<p className="text-[#ffffff] opacity-70 text-[18px] font-['Roboto:Regular',_sans-serif]">
Ford Motor Company
</p>
</div>
{!newUser? (
<>
{/* Login Tab */}
<div className="text-center mb-xl">
<h2 className="text-[#ffffff] text-[25px] font-['Noto_Sans_Display:Medium',_sans-serif] leading-[30px]">
User Login
</h2>
</div>
{/* Login Form */}
<form
onSubmit={handleLogin}
className="space-y-2 mt-2"
>
{/* Username Field */}
<div>
<label className="block text-left text-[#f7fefe] text-[14px] mb-1">
Email
</label>
<Input
type="text"
value={email}
placeholder="Enter your email address"
onChange={(e) => {
setUsername(e.target.value);
validateEmail(e.target.value);
}}
onBlur={(e) => validateEmail(e.target.value)}
className={`w-full bg-transparent text-white border-b ${
emailError ? "border-red-500" : "border-white"
}`}
disabled={isLoading || isChecked}
/>
{emailError && (
<p className="text-red-400 text-[10px] mt-1">{emailError}</p>
)}
</div>
{/* Password Field */}
<div className="relative w-full">
<label className="block text-left text-[#f7fefe] text-[14px] mb-1">
Password
</label>
<Input
type={showPassword ? 'text' : 'password'}
value={password}
required
onChange={(e) => setPassword(e.target.value)}
className="w-full bg-transparent text-white border-b border-white pr-10 py-2"
disabled={isLoading}
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute top-[35px] right-2 transform -translate-y-1/2 text-white p-2 mt-1.5"
tabIndex={-1}
aria-label={showPassword ? "Hide password" : "Show password"}
>
{showPassword ? <EyeOff size={14} /> : <Eye size={14} />}
</button>
</div>
{/* Buttons */}
<div className="flex justify-between mt-6">
<button
type="button"
onClick={handleForgotPassword}
className="text-sm text-[#f7fefe] underline hover:text-gray-300"
>
Forgot Password?
</button>
<Button
type="submit"
className="bg-[#ffffff] float-right text-[#000000] text-[14px] font-['Noto_Sans_Display:Regular',_sans-serif] tracking-[0.4px] leading-[22px] px-5 py-0 rounded-br-[4px] rounded-tl-[4px] cursor-pointer hover:bg-gray-100 transition-all duration-200 transform hover:scale-105"
disabled={isLoading}
>
Login
</Button>
</div>
<div className="flex justify-end mt-6">
<button
onClick={handleSSO}
disabled={isLoading}
className="w-full bg-[#0078d4] hover:bg-[#106ebe] text-[#ffffff] text-[14px] font-['Noto_Sans_Display:Medium',_sans-serif] tracking-[0.25px] leading-[20px] py-2 px-4 rounded-br-[4px] rounded-tl-[4px] cursor-pointer transition-all duration-200 transform hover:scale-105 flex items-center justify-center space-s"
>
<span>{isLoading ? 'Redirecting...' : 'Sign In with SSO'}</span>
</button>
</div>
</form>
</>
) : (
<>
<div className="text-center mb-xl">
<h2 className="text-[#ffffff] text-[25px] font-['Noto_Sans_Display:Medium',_sans-serif] leading-[30px]">
Reset Password
</h2>
</div>
{/* Reser Password Form */}
<form
onSubmit={handleCreatePassword}
className="space-y-2 mt-2"
>
{/* New Password */}
<div className="relative w-full mb-4">
<label className="block text-left text-[#f7fefe] text-[14px] mb-1">
New Password
</label>
<Input
type={showNewPassword ? 'text' : 'password'}
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full bg-transparent text-white border-b border-white pr-10 py-2"
disabled={isLoading}
/>
<button
type="button"
onClick={() => setShowNewPassword(!showNewPassword)}
className="absolute top-[35px] right-2 transform -translate-y-1/2 text-white p-2 mt-1.5"
tabIndex={-1}
aria-label={showNewPassword ? "Hide new password" : "Show new password"}
>
{showNewPassword ? <EyeOff size={14} /> : <Eye size={14} />}
</button>
</div>
{/* Confirm New Password */}
<div className="relative w-full">
<label className="block text-left text-[#f7fefe] text-[14px] mb-1">
Confirm New Password
</label>
<Input
type={showConfirmPassword ? 'text' : 'password'}
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
className="w-full bg-transparent text-white border-b border-white pr-10 py-2"
disabled={isLoading}
/>
<button
type="button"
onClick={() => setShowConfirmPassword(!showConfirmPassword)}
className="absolute top-[35px] right-2 transform -translate-y-1/2 text-white p-2 mt-1.5"
tabIndex={-1}
aria-label={showConfirmPassword ? "Hide confirm password" : "Show confirm password"}
>
{showConfirmPassword ? <EyeOff size={14} /> : <Eye size={14} />}
</button>
</div>
<div className="flex justify-end mt-6">
<Button
type="submit"
className="bg-[#ffffff] float-right text-[#000000] text-[14px] font-['Noto_Sans_Display:Regular',_sans-serif] tracking-[0.4px] leading-[22px] px-5 py-0 rounded-br-[4px] rounded-tl-[4px] cursor-pointer hover:bg-gray-100 transition-all duration-200 transform hover:scale-105"
disabled={isLoading}
>
Create Password
</Button>
</div>
</form>
</>
)}
</div>
</div>
</div>
<ForgotPasswordModal
isOpen={showForgotPasswordModal}
onClose={() => setShowForgotPasswordModal(false)}
/>
{/* Footer */}
<div className="absolute bottom-0 left-0 right-0 bg-[#f1f4f6] flex items-center justify-between px-m py-s text-[12px] font-['Noto_Sans_Display:Regular',_sans-serif] tracking-[0.4px] leading-[22px] border-t border-gray-200">
<div className="text-[#000000]">
Copyright ©2025 Ford Motor Company
</div>
<div className="text-[#235d9f] cursor-pointer hover:underline transition-all">
View Terms & Conditions
</div>
</div>
</div>
);
}
Comments
Post a Comment