import React from 'react' import { SUPPORTED_LANGUAGES, useTranslation } from '../i18n' import { Plane, Eye, EyeOff, Mail, Lock, MapPin, Calendar, Package, User, Globe, Zap, Users, Wallet, Map, CheckSquare, BookMarked, FolderOpen, Route, Shield, KeyRound, ChevronDown, Fingerprint } from 'lucide-react' import { useLogin } from './login/useLogin' import ToggleSwitch from '../components/Settings/ToggleSwitch' export default function LoginPage(): React.ReactElement { const { t, language } = useTranslation() // Page = wiring container: the whole auth surface lives in the useLogin hook. const { navigate, mode, setMode, username, setUsername, email, setEmail, password, setPassword, rememberMe, setRememberMe, showPassword, setShowPassword, isLoading, error, setError, appConfig, inviteToken, langDropdownOpen, setLangDropdownOpen, setLanguageLocal, showTakeoff, mfaStep, setMfaStep, mfaToken, setMfaToken, mfaCode, setMfaCode, passwordChangeStep, newPassword, setNewPassword, confirmPassword, setConfirmPassword, noRedirect, showRegisterOption, oidcOnly, handleDemoLogin, handleSubmit, handlePasskeyLogin, } = useLogin() const oidcButtonShown = !!(appConfig?.oidc_configured && appConfig?.oidc_login && !oidcOnly) const passkeyAvailable = !!(appConfig?.passkey_login && appConfig?.passkey_configured && !oidcOnly && mode === 'login' && !mfaStep && !passwordChangeStep) const inputBase: React.CSSProperties = { width: '100%', padding: '11px 12px 11px 40px', border: '1px solid #e5e7eb', borderRadius: 12, fontSize: 14, fontFamily: 'inherit', outline: 'none', color: '#111827', background: 'white', boxSizing: 'border-box', transition: 'border-color 0.15s', } if (showTakeoff) { return (
{/* Sky gradient */}
{/* Stars */} {Array.from({ length: 60 }, (_, i) => (
0.7 ? 3 : 1.5, height: Math.random() > 0.7 ? 3 : 1.5, borderRadius: '50%', background: 'white', top: `${Math.random() * 100}%`, left: `${Math.random() * 100}%`, animationDelay: `${0.3 + Math.random() * 0.5}s, ${Math.random() * 1}s`, }} /> ))} {/* Clouds rushing past */} {[0, 1, 2, 3, 4].map(i => (
))} {/* Speed lines */} {Array.from({ length: 12 }, (_, i) => (
))} {/* Plane */}
{/* Contrail */}
{/* Logo fade in + burst */}
TREK

{t('login.tagline')}

) } return (
{/* Language dropdown */}
{langDropdownOpen && (
e.stopPropagation()} style={{ position: 'absolute', top: '100%', right: 0, marginTop: 4, background: 'white', borderRadius: 12, boxShadow: '0 4px 24px rgba(0,0,0,0.12)', border: '1px solid rgba(0,0,0,0.08)', minWidth: 190, maxHeight: 320, overflowY: 'auto', }} > {SUPPORTED_LANGUAGES.map(({ value, label }) => ( ))}
)}
{/* Left — branding */}
{/* Stars */}
{Array.from({ length: 40 }, (_, i) => (
0.7 ? 2 : 1, height: Math.random() > 0.7 ? 2 : 1, borderRadius: '50%', background: 'white', opacity: 0.15 + Math.random() * 0.25, top: `${Math.random() * 70}%`, left: `${Math.random() * 100}%`, animationDelay: `${Math.random() * 4}s`, }} /> ))}
{/* Animated glow orbs */}
{/* Animated planes — realistic silhouettes at different sizes/speeds */}
{/* Plane 1 — large, slow, foreground */} {/* Plane 2 — small, faster, higher */} {/* Plane 3 — medium, mid-speed */} {/* Plane 4 — tiny, fast, high */} {/* Plane 5 — medium, right to left, lower */} {/* Plane 6 — tiny distant */}
{/* Logo */}
TREK

{t('login.tagline')}

{t('login.description')}

{[ { Icon: Map, label: t('login.features.maps'), desc: t('login.features.mapsDesc') }, { Icon: Zap, label: t('login.features.realtime'), desc: t('login.features.realtimeDesc') }, { Icon: Wallet, label: t('login.features.budget'), desc: t('login.features.budgetDesc') }, { Icon: Users, label: t('login.features.collab'), desc: t('login.features.collabDesc') }, { Icon: CheckSquare, label: t('login.features.packing'), desc: t('login.features.packingDesc') }, { Icon: BookMarked, label: t('login.features.bookings'), desc: t('login.features.bookingsDesc') }, { Icon: FolderOpen, label: t('login.features.files'), desc: t('login.features.filesDesc') }, { Icon: Route, label: t('login.features.routes'), desc: t('login.features.routesDesc') }, ].map(({ Icon, label, desc }) => (
) => { e.currentTarget.style.background = 'rgba(255,255,255,0.08)'; e.currentTarget.style.borderColor = 'rgba(255,255,255,0.12)' }} onMouseLeave={(e: React.MouseEvent) => { e.currentTarget.style.background = 'rgba(255,255,255,0.04)'; e.currentTarget.style.borderColor = 'rgba(255,255,255,0.06)' }}>
{label}
{desc}
))}

{t('login.selfHosted')}

{/* Right — form */}
{/* Mobile logo */}
TREK

{t('login.tagline')}

{oidcOnly ? ( <>

{t('login.title')}

{noRedirect ? t('login.oidcLoggedOut') : t('login.oidcOnly')}

{error && (
{error}
)} ) => { e.currentTarget.style.background = '#1f2937' }} onMouseLeave={(e: React.MouseEvent) => { e.currentTarget.style.background = '#111827' }} > {t('login.oidcSignIn', { name: appConfig?.oidc_display_name || 'SSO' })} ) : ( <>

{passwordChangeStep ? t('login.setNewPassword') : mode === 'login' && mfaStep ? t('login.mfaTitle') : mode === 'register' ? (!appConfig?.has_users ? t('login.createAdmin') : t('login.createAccount')) : t('login.title')}

{passwordChangeStep ? t('login.setNewPasswordHint') : mode === 'login' && mfaStep ? t('login.mfaSubtitle') : mode === 'register' ? (!appConfig?.has_users ? t('login.createAdminHint') : t('login.createAccountHint')) : t('login.subtitle')}

{error && (
{error}
)} {passwordChangeStep && ( <>
{t('settings.mustChangePassword')}
) => setNewPassword(e.target.value)} required placeholder={t('settings.newPassword')} style={inputBase} onFocus={(e: React.FocusEvent) => e.target.style.borderColor = '#111827'} onBlur={(e: React.FocusEvent) => e.target.style.borderColor = '#e5e7eb'} />
) => setConfirmPassword(e.target.value)} required placeholder={t('settings.confirmPassword')} style={inputBase} onFocus={(e: React.FocusEvent) => e.target.style.borderColor = '#111827'} onBlur={(e: React.FocusEvent) => e.target.style.borderColor = '#e5e7eb'} />
)} {mode === 'login' && mfaStep && !passwordChangeStep && (
) => setMfaCode(e.target.value.toUpperCase().slice(0, 24))} placeholder="000000 or XXXX-XXXX" required autoFocus style={inputBase} onFocus={(e: React.FocusEvent) => e.target.style.borderColor = '#111827'} onBlur={(e: React.FocusEvent) => e.target.style.borderColor = '#e5e7eb'} />

{t('login.mfaHint')}

)} {/* Username (register only) */} {mode === 'register' && !passwordChangeStep && (
) => setUsername(e.target.value)} required placeholder="admin" style={inputBase} onFocus={(e: React.FocusEvent) => e.target.style.borderColor = '#111827'} onBlur={(e: React.FocusEvent) => e.target.style.borderColor = '#e5e7eb'} />
)} {/* Email */} {!(mode === 'login' && mfaStep) && !passwordChangeStep && (
) => setEmail(e.target.value)} required placeholder={t('login.emailPlaceholder')} style={inputBase} onFocus={(e: React.FocusEvent) => e.target.style.borderColor = '#111827'} onBlur={(e: React.FocusEvent) => e.target.style.borderColor = '#e5e7eb'} />
)} {/* Password */} {!(mode === 'login' && mfaStep) && !passwordChangeStep && (
) => setPassword(e.target.value)} required placeholder="••••••••" style={{ ...inputBase, paddingRight: 44 }} onFocus={(e: React.FocusEvent) => e.target.style.borderColor = '#111827'} onBlur={(e: React.FocusEvent) => e.target.style.borderColor = '#e5e7eb'} />
{mode === 'login' && (
setRememberMe(!rememberMe)} label={t('login.rememberMe')} /> setRememberMe(!rememberMe)} style={{ cursor: 'pointer', color: '#374151', fontSize: 12.5, fontWeight: 500, userSelect: 'none' }} > {t('login.rememberMe')}
)}
)}
{/* Toggle login/register */} {showRegisterOption && appConfig?.has_users && !appConfig?.demo_mode && !passwordChangeStep && (

{mode === 'login' ? t('login.noAccount') + ' ' : t('login.hasAccount') + ' '}

)} )}
{/* OIDC / SSO login button (only when OIDC is configured, oidc_login enabled, not in oidc-only mode) */} {appConfig?.oidc_configured && appConfig?.oidc_login && !oidcOnly && ( <>
{t('common.or')}
) => { e.currentTarget.style.background = '#f9fafb'; e.currentTarget.style.borderColor = '#9ca3af' }} onMouseLeave={(e: React.MouseEvent) => { e.currentTarget.style.background = 'white'; e.currentTarget.style.borderColor = '#d1d5db' }} > {t('login.oidcSignIn', { name: appConfig.oidc_display_name })} )} {/* Passkey login button (instance toggle on + a usable RP ID resolves) */} {passkeyAvailable && ( <> {!oidcButtonShown && (
{t('common.or')}
)} )} {/* Demo login button */} {appConfig?.demo_mode && ( )}
) }