import React, { useState, useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { useNavigate } from 'react-router-dom'; import { loginUser, clearError, updateCacheInfo } from '../store/reducers/authSlice'; const Login = () => { const dispatch = useDispatch(); const navigate = useNavigate(); const { isAuthenticated, loading, error } = useSelector(state => { console.log('Redux auth state updated:', state.auth); return state.auth; }); const [formData, setFormData] = useState({ email: '', password: '' }); const [showPassword, setShowPassword] = useState(false); const [rememberMe, setRememberMe] = useState(false); const [isFocused, setIsFocused] = useState({ email: false, password: false }); // Auto-fill remember me preference if available useEffect(() => { const rememberPref = localStorage.getItem('rememberMePreference'); if (rememberPref === 'true') { setRememberMe(true); } }, []); useEffect(() => { if (isAuthenticated) { navigate('/dashboard'); return; } // Only check for cached auth if not already loading if (loading === 'idle') { const checkAuthStatus = async () => { const token = localStorage.getItem('token'); if (!token) { return; // No token, nothing to check } try { // Check if token is expired const tokenData = JSON.parse(atob(token.split('.')[1])); const isExpired = tokenData.exp * 1000 < Date.now(); if (isExpired) { localStorage.removeItem('token'); } } catch (error) { // Invalid token format, remove it localStorage.removeItem('token'); } }; checkAuthStatus(); } }, [isAuthenticated, loading, navigate, dispatch]); const handleChange = (e) => { setFormData({ ...formData, [e.target.name]: e.target.value }); // Clear error when user starts typing if (error) { dispatch(clearError()); } }; const handleFocus = (field) => { setIsFocused({ ...isFocused, [field]: true }); }; const handleBlur = (field) => { setIsFocused({ ...isFocused, [field]: false }); }; const handleSubmit = async (e) => { e.preventDefault(); // Prevent form submission if already loading if (loading === 'pending') { return; } // Clear any existing errors before attempting login if (error) { dispatch(clearError()); } try { const result = await dispatch(loginUser({ ...formData, rememberMe: rememberMe // Pass remember me flag })).unwrap(); // Update Redux store with cache info dispatch(updateCacheInfo({ isRemembered: rememberMe, expiresAt: result.expiresAt, deviceFingerprint: result.deviceFingerprint })); navigate('/dashboard'); } catch (err) { // Error is handled by the Redux slice console.error('Login failed:', err); } }; const togglePasswordVisibility = () => { setShowPassword(!showPassword); }; const toggleForm = () => { dispatch(clearError()); navigate('/register'); }; if (isAuthenticated) { return null; // Redirect handled by useEffect } return (
{/* Logo and Brand */}
Lin

Welcome Back

Sign in to your Lin account

{/* Auth Card */}
{/* Error Message */} {error && (
{error}
)} {/* Login Form */}
{/* Email Field */}
handleFocus('email')} onBlur={() => handleBlur('email')} className={`w-full px-3 sm:px-4 py-2 sm:py-3 rounded-xl border-2 transition-all duration-200 ${ isFocused.email ? 'border-primary-500 shadow-md' : 'border-gray-200 hover:border-gray-300' } ${formData.email ? 'text-gray-900' : 'text-gray-500'} focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 touch-manipulation`} placeholder="Enter your email" required aria-required="true" aria-label="Email address" />
{/* Password Field */}
handleFocus('password')} onBlur={() => handleBlur('password')} className={`w-full px-3 sm:px-4 py-2 sm:py-3 rounded-xl border-2 transition-all duration-200 ${ isFocused.password ? 'border-primary-500 shadow-md' : 'border-gray-200 hover:border-gray-300' } ${formData.password ? 'text-gray-900' : 'text-gray-500'} focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 touch-manipulation`} placeholder="Enter your password" required aria-required="true" aria-label="Password" />
{/* Remember Me & Forgot Password */}
{ setRememberMe(e.target.checked); // Save preference localStorage.setItem('rememberMePreference', e.target.checked.toString()); }} className="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded transition-colors touch-manipulation" aria-label="Remember me" />
{/* Submit Button */}
{/* Register Link */}

Don't have an account?{' '}

{/* Footer */}

© 2024 Lin. All rights reserved.

); }; export default Login;