function AuthDrawer({ isOpen, onClose }) { const [tab, setTab] = React.useState('signup'); // 'signup' | 'signin' const [submitted, setSubmitted] = React.useState(false); const [loading, setLoading] = React.useState(false); const [form, setForm] = React.useState({ firstName: '', lastName: '', email: '', phone: '', password: '', street: '', city: '', postal: '', country: 'Indonesia', newsletter: true, }); const [errors, setErrors] = React.useState({}); function set(k, v) { setForm(f => ({ ...f, [k]: v })); setErrors(e => ({ ...e, [k]: '' })); } function validate() { const e = {}; if (tab === 'signup') { if (!form.firstName.trim()) e.firstName = 'Required'; if (!form.lastName.trim()) e.lastName = 'Required'; if (!form.street.trim()) e.street = 'Required'; if (!form.city.trim()) e.city = 'Required'; if (!form.postal.trim()) e.postal = 'Required'; } if (!form.email.includes('@')) e.email = 'Valid email required'; if (form.password.length < 8) e.password = 'Minimum 8 characters'; return e; } function handleSubmit(e) { e.preventDefault(); const errs = validate(); if (Object.keys(errs).length) { setErrors(errs); return; } setLoading(true); setTimeout(() => { if (tab === 'signup') { const result = window.UmbraDB?.saveAccount?.(form); if (result === null) { setErrors({ email: 'Account already exists' }); setLoading(false); return; } window.UmbraDB?.setSession?.(result); } else { const account = window.UmbraDB?.findAccount?.(form.email); if (account) window.UmbraDB?.setSession?.(account); } setLoading(false); setSubmitted(true); }, 1100); } function reset() { setSubmitted(false); setLoading(false); setForm({ firstName: '', lastName: '', email: '', phone: '', password: '', street: '', city: '', postal: '', country: 'Indonesia', newsletter: true }); setErrors({}); } // Shared input style factory function inp(key) { return { width: '100%', background: 'none', border: 'none', borderBottom: `1px solid ${errors[key] ? 'rgba(0,0,0,0.6)' : 'rgba(0,0,0,0.18)'}`, padding: '10px 0', outline: 'none', fontFamily: "'Montserrat', sans-serif", fontSize: 12, color: '#000000', letterSpacing: '0.04em', transition: 'border-color 0.2s', }; } function lbl(txt, key) { return (
); } const drawerStyle = { position: 'fixed', top: 0, right: 0, bottom: 0, width: 460, zIndex: 2001, background: '#ffffff', color: '#000000', display: 'flex', flexDirection: 'column', transform: isOpen ? 'translateX(0)' : 'translateX(100%)', transition: 'transform 0.42s cubic-bezier(0.16, 1, 0.3, 1)', borderLeft: '1px solid rgba(0,0,0,0.12)', }; return ( <> {isOpen &&
}
{/* Header */}
{tab === 'signup' ? 'Join Umbra' : 'Welcome Back'}
{tab === 'signup' ? 'CREATE AN ACCOUNT' : 'SIGN INTO YOUR ACCOUNT'}
{/* Tabs */} {!submitted && (
{[['signup', 'SIGN UP'], ['signin', 'SIGN IN']].map(([id, label]) => ( ))}
)}
{/* Body */}
{submitted ? ( // Success state
{tab === 'signup' ? <>Welcome
to Umbra. : <>You're
back.}
{tab === 'signup' ? `Your account has been created${form.newsletter ? ' and you\'re subscribed to The Dispatch' : ''}. New arrivals and roast updates will reach you first.` : 'You are now signed in. Your order history and delivery details are ready.'}
) : (
{tab === 'signup' && ( <> {/* Name */}
{lbl('First Name', 'firstName')} set('firstName', e.target.value)} style={inp('firstName')} onFocus={e => e.target.style.borderBottomColor = '#000000'} onBlur={e => e.target.style.borderBottomColor = errors.firstName ? 'rgba(0,0,0,0.6)' : 'rgba(0,0,0,0.18)'} />
{lbl('Last Name', 'lastName')} set('lastName', e.target.value)} style={inp('lastName')} onFocus={e => e.target.style.borderBottomColor = '#000000'} onBlur={e => e.target.style.borderBottomColor = errors.lastName ? 'rgba(0,0,0,0.6)' : 'rgba(0,0,0,0.18)'} />
)} {/* Email */}
{lbl('Email Address', 'email')} set('email', e.target.value)} style={inp('email')} onFocus={e => e.target.style.borderBottomColor = '#000000'} onBlur={e => e.target.style.borderBottomColor = errors.email ? 'rgba(0,0,0,0.6)' : 'rgba(0,0,0,0.18)'} />
{tab === 'signup' && (
{lbl('Phone Number', 'phone')} set('phone', e.target.value)} style={inp('phone')} placeholder="+62 ···" onFocus={e => e.target.style.borderBottomColor = '#000000'} onBlur={e => e.target.style.borderBottomColor = 'rgba(0,0,0,0.18)'} />
)} {/* Password */}
{lbl('Password', 'password')} set('password', e.target.value)} style={inp('password')} onFocus={e => e.target.style.borderBottomColor = '#000000'} onBlur={e => e.target.style.borderBottomColor = errors.password ? 'rgba(0,0,0,0.6)' : 'rgba(0,0,0,0.18)'} />
{tab === 'signup' && ( <> {/* Delivery address */}
DELIVERY ADDRESS
{lbl('Street Address', 'street')} set('street', e.target.value)} style={inp('street')} onFocus={e => e.target.style.borderBottomColor = '#000000'} onBlur={e => e.target.style.borderBottomColor = errors.street ? 'rgba(0,0,0,0.6)' : 'rgba(0,0,0,0.18)'} />
{lbl('City', 'city')} set('city', e.target.value)} style={inp('city')} onFocus={e => e.target.style.borderBottomColor = '#000000'} onBlur={e => e.target.style.borderBottomColor = errors.city ? 'rgba(0,0,0,0.6)' : 'rgba(0,0,0,0.18)'} />
{lbl('Postal Code', 'postal')} set('postal', e.target.value)} style={inp('postal')} onFocus={e => e.target.style.borderBottomColor = '#000000'} onBlur={e => e.target.style.borderBottomColor = errors.postal ? 'rgba(0,0,0,0.6)' : 'rgba(0,0,0,0.18)'} />
{lbl('Country', 'country')}
{/* Newsletter opt-in */}
set('newsletter', !form.newsletter)}>
{form.newsletter &&
}
Subscribe to The Dispatch
New arrivals, roast dates, and the occasional manifesto. Never frequent, always worth reading.
)} {/* Submit */} {tab === 'signin' && ( )} )}
); } Object.assign(window, { AuthDrawer });