// LoginPage.jsx — server-backed JWT auth via /api/auth/login. const LoginPage = ({ onLogin }) => { const { useState, useEffect, useRef } = React; const [password, setPassword] = useState(''); const [error, setError] = useState(''); const [loading, setLoading] = useState(false); const inputRef = useRef(); useEffect(() => { inputRef.current?.focus(); }, []); const handleSubmit = async () => { if (loading || !password) return; setLoading(true); setError(''); try { await window.api.login(password); onLogin(); } catch (err) { const msg = err.status === 401 ? 'Incorrect password' : err.status === 503 ? 'No password configured on server — run `npm run set-password` on the VPS.' : err.status === 400 ? 'Password is required' : err.message || 'Login failed'; setError(msg); setLoading(false); } }; const inp = (extra = {}) => ({ background: 'var(--surface3)', border: `1px solid ${error ? '#f04060' : 'var(--border)'}`, borderRadius: '8px', color: 'var(--text)', padding: '12px 14px', fontSize: '14px', outline: 'none', fontFamily: 'Space Grotesk, sans-serif', width: '100%', boxSizing: 'border-box', transition: 'border-color 0.15s', ...extra }); return React.createElement('div', { style: { minHeight: '100vh', width: '100%', background: 'var(--bg)', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: '28px', padding: '20px', boxSizing: 'border-box' } }, [ // Brand React.createElement('div', { key: 'brand', style: { display: 'flex', alignItems: 'center', gap: '14px' } }, [ React.createElement('div', { key: 'icon', style: { width: '44px', height: '44px', background: 'linear-gradient(135deg,#5588ff,#a064ff)', borderRadius: '12px', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '18px', fontWeight: 800, color: '#fff', letterSpacing: '-0.5px' } }, 'TJ'), React.createElement('div', { key: 'txt' }, [ React.createElement('div', { key: 'n', style: { fontSize: '22px', fontWeight: 700, color: 'var(--text)', lineHeight: 1 } }, 'TradeJournal'), React.createElement('div', { key: 's', style: { fontSize: '12px', color: 'var(--muted)', marginTop: '3px' } }, 'Personal Trading Intelligence') ]) ]), // Card React.createElement('div', { key: 'card', style: { background: 'var(--surface)', border: '1px solid var(--border)', borderRadius: '16px', padding: '32px', width: '100%', maxWidth: '360px', boxShadow: '0 24px 64px rgba(0,0,0,0.6)' } }, [ React.createElement('div', { key: 'hdr', style: { marginBottom: '22px' } }, [ React.createElement('div', { key: 'h', style: { fontSize: '17px', fontWeight: 700, color: 'var(--text)', marginBottom: '4px' } }, 'Sign in'), React.createElement('div', { key: 's', style: { fontSize: '13px', color: 'var(--muted)' } }, 'Enter your password to access your journal'), ]), React.createElement('div', { key: 'form', style: { display: 'flex', flexDirection: 'column', gap: '10px' } }, [ React.createElement('div', { key: 'pwdwrap' }, [ React.createElement('label', { key: 'l', style: { fontSize: '11px', color: 'var(--muted)', fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.07em', display: 'block', marginBottom: '6px' } }, 'Password'), React.createElement('input', { ref: inputRef, key: 'pwd', type: 'password', value: password, placeholder: '••••••••', onChange: e => { setPassword(e.target.value); setError(''); }, onKeyDown: e => e.key === 'Enter' && handleSubmit(), style: inp({ border: `1px solid ${error ? '#f04060' : password.length > 0 ? '#5588ff' : 'var(--border)'}` }) }) ]), error && React.createElement('div', { key: 'err', style: { fontSize: '12px', color: '#f04060', display: 'flex', alignItems: 'flex-start', gap: '6px' } }, [ React.createElement('span', { key: 'i', style: { flexShrink: 0 } }, '⚠'), React.createElement('span', { key: 'm' }, error) ]), React.createElement('button', { key: 'btn', onClick: handleSubmit, disabled: loading || !password, style: { padding: '12px', background: loading ? '#3a5acc' : '#5588ff', border: 'none', borderRadius: '8px', color: '#fff', fontSize: '14px', fontWeight: 600, cursor: loading ? 'wait' : 'pointer', fontFamily: 'Space Grotesk, sans-serif', marginTop: '4px', transition: 'all 0.15s', opacity: !password ? 0.5 : 1 } }, loading ? 'Verifying…' : 'Sign In →'), ]) ]), // Security note React.createElement('div', { key: 'note', style: { fontSize: '11px', color: 'var(--dim)', textAlign: 'center', maxWidth: '340px', lineHeight: 1.5 } }, [ React.createElement('span', { key: 'icon', style: { marginRight: '4px' } }, '🔒'), 'Server-side authentication via JWT. To change your password, run ', React.createElement('code', { key: 'c', style: { background: 'var(--surface2)', padding: '1px 5px', borderRadius: '3px', fontFamily: 'JetBrains Mono, monospace', fontSize: '10px' } }, 'npm run set-password'), ' on the VPS.' ]) ]); }; Object.assign(window, { LoginPage });