import React, { useState, useRef, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import axios from 'axios';
import toast, { Toaster } from 'react-hot-toast';
import { Eye, EyeOff, Moon, Sun, User, Lock, CheckCircle, Loader2 } from 'lucide-react';
// 2025 UL/UX: Glassmorphism, animated transitions, mobile-first, accessibility, color palette, focus ring, haptic feedback (vibration), and more
const COLORS = {
primary: '#2563eb',
secondary: '#60a5fa',
accent: '#fbbf24',
error: '#ef4444',
bgLight: 'rgba(255,255,255,0.85)',
bgDark: 'rgba(30,41,59,0.92)',
glassLight: 'bg-gradient-to-br from-white/80 via-blue-50/70 to-white/60',
glassDark: 'bg-gradient-to-br from-gray-900/80 via-gray-800/70 to-gray-900/60',
borderLight: 'border-blue-200/60',
borderDark: 'border-gray-700/60',
shadowLight: 'shadow-blue-200/40',
shadowDark: 'shadow-blue-900/30',
};
function Login() {
const [isLoginMode, setIsLoginMode] = useState(true);
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [isDarkMode, setIsDarkMode] = useState(() => {
// Persist dark mode
return localStorage.getItem('ulx2025_dark') === 'true' || window.matchMedia('(prefers-color-scheme: dark)').matches;
});
const [isSubmitting, setIsSubmitting] = useState(false);
const [showPassword, setShowPassword] = useState(false);
const [errors, setErrors] = useState({});
const [animCard, setAnimCard] = useState(false);
const navigate = useNavigate();
const cardRef = useRef(null);
// Animate card on mount
useEffect(() => {
setTimeout(() => setAnimCard(true), 80);
}, []);
// Persist dark mode
useEffect(() => {
localStorage.setItem('ulx2025_dark', isDarkMode);
if (isDarkMode) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
}, [isDarkMode]);
// Haptic feedback for mobile
const vibrate = (pattern = [20]) => {
if (window.navigator.vibrate) window.navigator.vibrate(pattern);
};
// Accessibility: focus first input on mode switch
const userInputRef = useRef(null);
useEffect(() => {
if (userInputRef.current) userInputRef.current.focus();
}, [isLoginMode]);
const validateForm = () => {
const newErrors = {};
if (!username || username.length < 3 || username.length > 20) {
newErrors.username = 'Tên người dùng phải từ 3 đến 20 ký tự';
} else if (!/^[a-zA-Z0-9]+$/.test(username)) {
newErrors.username = 'Tên người dùng chỉ chứa chữ cái và số';
}
if (!password || password.length < 6 || password.length > 20) {
newErrors.password = 'Mật khẩu phải từ 6 đến 20 ký tự';
} else if (!/[0-9]/.test(password)) {
newErrors.password = 'Mật khẩu phải chứa ít nhất một số';
}
if (!isLoginMode && password !== confirmPassword) {
newErrors.confirmPassword = 'Mật khẩu xác nhận không khớp';
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleSubmit = async (e) => {
e.preventDefault();
vibrate([10, 10]);
if (!validateForm()) {
toast.error('Vui lòng kiểm tra lại thông tin!');
return;
}
setIsSubmitting(true);
try {
const endpoint = isLoginMode
? 'https://backend-rockefeller-finance.onrender.com/api/login'
: 'https://backend-rockefeller-finance.onrender.com/api/register';
const response = await axios.post(endpoint, { username, password });
if (isLoginMode) {
localStorage.setItem('token', response.data.token);
toast.success('Đăng nhập thành công!', { icon: });
setTimeout(() => navigate('/home'), 800);
} else {
toast.success('Đăng ký thành công! Đang chuyển sang đăng nhập...', { icon: });
setIsLoginMode(true);
setPassword('');
setConfirmPassword('');
setTimeout(async () => {
try {
const loginResponse = await axios.post(
'https://backend-rockefeller-finance.onrender.com/api/login',
{ username, password }
);
localStorage.setItem('token', loginResponse.data.token);
navigate('/home');
} catch (err) {
toast.error(err.response?.data?.error || 'Lỗi đăng nhập tự động');
}
}, 900);
}
setErrors({});
} catch (err) {
toast.error(err.response?.data?.error || (isLoginMode ? 'Lỗi đăng nhập' : 'Lỗi đăng ký'));
} finally {
setIsSubmitting(false);
}
};
const handleForgotPassword = () => {
vibrate([30, 10, 30]);
toast('Vui lòng liên hệ hỗ trợ tại support@rockefeller-finance.com để đặt lại mật khẩu.', {
duration: 5000,
icon: '🛡️',
});
};
const toggleDarkMode = () => {
vibrate([10]);
setIsDarkMode((d) => !d);
};
// 2025: Animated background
const AnimatedBg = () => (
);
// 2025: Card glassmorphism, shadow, border, animation
const cardClass = `
relative z-10 w-full max-w-md mx-auto
px-6 py-8 sm:px-10 sm:py-10
rounded-3xl
shadow-2xl
border
${isDarkMode ? COLORS.glassDark : COLORS.glassLight}
${isDarkMode ? COLORS.borderDark : COLORS.borderLight}
${isDarkMode ? COLORS.shadowDark : COLORS.shadowLight}
backdrop-blur-xl
transition-all duration-700
${animCard ? 'scale-100 opacity-100' : 'scale-95 opacity-0'}
ring-1 ring-blue-400/10
focus-within:ring-2 focus-within:ring-blue-500/60
`;
// 2025: Input field with icon, error, and focus ring
const InputField = ({
label,
type,
value,
onChange,
error,
icon,
autoFocus,
inputRef,
autoComplete,
disabled,
placeholder,
rightIcon,
...rest
}) => (
);
// 2025: Mode switcher with animation
const ModeSwitcher = () => (
);
// 2025: Dark mode toggle with icon
const DarkModeToggle = () => (
);
// 2025: Inspirational quote
const Quote = () => (
{isLoginMode && }
);
}
export default Login;
{icon}
{rightIcon && (
)}
{error && {error}
}
32 Lá Thư: "Kỷ luật tài chính bắt đầu từ việc quản lý thông tin cá nhân an toàn. Hãy sử dụng mật khẩu mạnh!"
);
// 2025: Submit button with animation
const SubmitButton = () => (
);
// 2025: Forgot password link
const ForgotPassword = () => (
);
// 2025: Responsive, glass, animated, accessible, mobile-first, best-of-the-galaxy login
return (
{isLoginMode ? 'Đăng nhập' : 'Đăng ký'}
© {new Date().getFullYear()} Rockefeller Finance. ULX 2025 – Đỉnh cao nhân loại.