import React, { useState, useEffect } from 'react' import { Eye, EyeOff, Loader2, ArrowRight } from 'lucide-react' import { useNavigate } from 'react-router-dom' import { toast } from 'sonner' import { useAuth } from '../../contexts/AuthContext' import { useLanguage } from '../../contexts/LanguageContext' import { t } from '../../i18n/translations' import { DeepVoidBackground } from '../common/DeepVoidBackground' export function LoginPage() { const { language } = useLanguage() const { login } = useAuth() const navigate = useNavigate() const [email, setEmail] = useState('') const [password, setPassword] = useState('') const [showPassword, setShowPassword] = useState(false) const [error, setError] = useState('') const [loading, setLoading] = useState(false) const [expiredToastId, setExpiredToastId] = useState( null ) useEffect(() => { localStorage.removeItem('auth_token') localStorage.removeItem('auth_user') localStorage.removeItem('user_id') }, []) useEffect(() => { if (sessionStorage.getItem('from401') === 'true') { const id = toast.warning(t('sessionExpired', language), { duration: Infinity, }) setExpiredToastId(id) sessionStorage.removeItem('from401') } }, [language]) // Account wipe was removed from the public API (it was an unauthenticated // destructive endpoint). It now runs as a local CLI command on the server, // so we surface the instruction instead of calling an endpoint. const handleResetAccount = () => { toast(t('resetAccountCliIntro', language), { description: 'nofx reset-account', duration: 10000, }) } const handleLogin = async (e: React.FormEvent) => { e.preventDefault() setError('') setLoading(true) const result = await login(email, password) setLoading(false) if (result.success) { if (expiredToastId) toast.dismiss(expiredToastId) } else { const msg = result.message || t('loginFailed', language) setError(msg) toast.error(msg) } } return ( {/* Self-contained centering grid — works regardless of parent flex setup */}
{/* ───────── LEFT: brand panel (desktop only) ───────── */}
{/* Ambient vermilion halo */}
{/* Brand mark */}
NOFX
NOFX.
{/* Headline */}
Terminal Online

{language === 'zh' ? ( <> AI-Powered
Multi-Market Trading Terminal ) : language === 'id' ? ( <> Terminal Trading
Multi-Pasar AI ) : ( <> AI-Powered
Trading Terminal )}

{language === 'zh' ? 'Plug into 10+ exchanges including Hyperliquid, OKX, Aster, and 7 LLM models. Deploy 24/7 automated strategies with natural language.' : language === 'id' ? 'Hubungkan ke 10+ bursa termasuk Hyperliquid, OKX, Aster dan 7 model LLM. Terapkan strategi otomatis 24/7 dengan bahasa alami.' : 'Plug into 10+ exchanges including Hyperliquid, OKX, Aster, and 7 LLM models. Deploy 24/7 automated strategies with natural language.'}

{/* Stats strip */}
{/* ───────── RIGHT: form panel ───────── */}
{/* Mobile brand */}
NOFX
NOFX.
{/* Form header */}

{t('signIn', language)}

{language === 'zh' ? 'Continue with your email' : language === 'id' ? 'Lanjutkan dengan email Anda' : 'Continue with your email'}

{/* Form */}
{/* Email */}
setEmail(e.target.value)} className="w-full bg-nofx-bg-lighter border border-[rgba(26,24,19,0.14)] rounded-lg px-4 py-[11px] text-[14px] text-nofx-text placeholder-nofx-text-muted focus:outline-none focus:border-nofx-gold/50 focus:bg-nofx-bg-lighter focus:ring-2 focus:ring-nofx-gold/20 transition-all" placeholder="you@example.com" required autoFocus autoComplete="email" />
{/* Password */}
setPassword(e.target.value)} className="w-full bg-nofx-bg-lighter border border-[rgba(26,24,19,0.14)] rounded-lg px-4 py-[11px] pr-11 text-[14px] text-nofx-text placeholder-nofx-text-muted focus:outline-none focus:border-nofx-gold/50 focus:bg-nofx-bg-lighter focus:ring-2 focus:ring-nofx-gold/20 transition-all" placeholder="••••••••" required autoComplete="current-password" />
{/* Error banner */} {error && (
! {error}
)} {/* Submit */}
{/* Footer */}
v1.0
) } function Stat({ value, label }: { value: string; label: string }) { return (
{value}
{label}
) }