// pages-account.jsx — AuthModal, AccountPage, AdminDashboard — all live data const { useState: useStateA, useEffect: useEffectA } = React; // ─── AUTH MODAL ─────────────────────────────────────────────────────────────── function AuthModal({ open, onClose, onLogin, onRegister, refreshUser }) { const [mode, setMode] = useStateA('login'); // signup steps: 'email' → 'otp' → 'details' → done // login steps: 'login' const [step, setStep] = useStateA('login'); const [email, setEmail] = useStateA(''); const [emailVerified, setEmailVerified]= useStateA(false); const [otp, setOtp] = useStateA(''); const [otpHint, setOtpHint] = useStateA(''); const [resendCd, setResendCd] = useStateA(0); const [name, setName] = useStateA(''); const [phone, setPhone] = useStateA(''); const [password, setPassword] = useStateA(''); const [pwStrength, setPwStrength] = useStateA({ upper:false, lower:false, num:false, len:false }); const [error, setError] = useStateA(''); const [loading, setLoading] = useStateA(false); useEffectA(() => { if (resendCd <= 0) return; const t = setTimeout(() => setResendCd(c => c-1), 1000); return () => clearTimeout(t); }, [resendCd]); const switchMode = (m) => { setMode(m); setStep(m === 'signup' ? 'email' : 'login'); setError(''); setEmail(''); setPassword(''); setName(''); setPhone(''); setEmailVerified(false); setOtp(''); }; if (!open) return null; // ── Step handlers ───────────────────────────────────────── const sendOtp = async () => { if (!email || !email.includes('@')) { setError('Enter a valid email'); return; } setError(''); setLoading(true); try { const res = await fetch('/api/auth.php?action=send-pre-otp', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({email}) }); const d = await res.json(); if (!res.ok) { setError(d.error || 'Could not send code'); return; } setOtpHint(d.email || email); setStep('otp'); setResendCd(30); } finally { setLoading(false); } }; const verifyOtp = async () => { if (otp.length < 6) return; setError(''); setLoading(true); try { const res = await fetch('/api/auth.php?action=verify-pre-otp', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({email, code: otp}) }); const d = await res.json(); if (!res.ok) { setError(d.error || 'Wrong code. Try again.'); return; } setEmailVerified(true); setStep('details'); setOtp(''); } finally { setLoading(false); } }; const checkPw = (pw) => { setPassword(pw); setPwStrength({ len: pw.length >= 8, upper: /[A-Z]/.test(pw), lower: /[a-z]/.test(pw), num: /[0-9]/.test(pw), }); }; const createAccount = async () => { if (!name.trim()) { setError('Name is required'); return; } if (!pwStrength.len || !pwStrength.upper || !pwStrength.lower || !pwStrength.num) { setError('Password does not meet requirements'); return; } setError(''); setLoading(true); try { const result = await onRegister(email, password, name, phone); if (!result.ok) { setError(result.error || 'Registration failed'); return; } // Mark email verified immediately — user already proved ownership try { const token = localStorage.getItem('ff_access_token'); await fetch('/api/auth.php?action=mark-verified', { method:'POST', headers:{'Content-Type':'application/json','Authorization':'Bearer '+token} }); if (refreshUser) await refreshUser(); } catch {} onClose(); } finally { setLoading(false); } }; const loginSubmit = async () => { if (!email || !password) { setError('Enter email and password'); return; } setError(''); setLoading(true); try { const result = await onLogin(email, password); if (!result.ok) { setError(result.error || 'Wrong email or password'); return; } onClose(); } finally { setLoading(false); } }; const resendCode = async () => { if (resendCd > 0) return; setLoading(true); try { const res = await fetch('/api/auth.php?action=send-pre-otp', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({email}) }); if (res.ok) { setResendCd(60); setOtp(''); setError(''); } } finally { setLoading(false); } }; const HEADER = { login: { title:'Welcome back, fussy', sub:'Your bag, orders and wishlists are waiting.' }, email: { title:'Join Fussy Finds', sub:'Enter your email to get started.' }, otp: { title:'Check your inbox', sub:`We sent a 6-digit code to ${otpHint}` }, details: { title:'Almost done!', sub:`${email} ✓ — now set your password.` }, }; const h = HEADER[step] || HEADER.login; return (

{h.title}

{h.sub}

{/* ── Mode tabs (login/signup) — only on first step ── */} {(step === 'login' || step === 'email') && (
{[{k:'login',l:'Log in'},{k:'signup',l:'Sign up'}].map(t => ( ))}
)} {/* ── STEP: login ── */} {step === 'login' && (
setEmail(e.target.value)} autoFocus/> setPassword(e.target.value)} onKeyDown={e=>e.key==='Enter'&&loginSubmit()}/> {error &&
{error}
}
By continuing you agree to our{' '} Terms {' '}&{' '} Privacy Policy.
)} {/* ── STEP: email (signup step 1) ── */} {step === 'email' && (
Step 1 of 3 {' '} Verify your email address
setEmail(e.target.value)} onKeyDown={e=>e.key==='Enter'&&sendOtp()} autoFocus/> {error &&
{error}
}
)} {/* ── STEP: otp (signup step 2) ── */} {step === 'otp' && (
Step 2 of 3 {' '} Enter the code sent to {otpHint}
setOtp(e.target.value.replace(/\D/g,'').slice(0,6))} onKeyDown={e=>e.key==='Enter'&&verifyOtp()} style={{ textAlign:'center', fontSize:32, fontFamily:'monospace', fontWeight:700, letterSpacing:'.25em' }} autoFocus/> {error &&
{error}
}
)} {/* ── STEP: details (signup step 3) ── */} {step === 'details' && (
Step 3 of 3 {' '} ✓ {email} verified!
setName(e.target.value)} autoFocus/> setPhone(e.target.value.replace(/\D/g,'').slice(0,10))}/> checkPw(e.target.value)}/> {/* Password strength */} {password.length > 0 && (
{[ ['8+ characters', pwStrength.len], ['Uppercase letter', pwStrength.upper], ['Lowercase letter', pwStrength.lower], ['Number', pwStrength.num], ].map(([label, ok]) => (
{ok?'✓':'○'} {label}
))}
)} {error &&
{error}
}
By continuing you agree to our{' '} Terms {' '}&{' '} Privacy Policy.
)}
); } Object.assign(window, { AuthModal });