// pages-extra.jsx — CartPage, LoginPage, SearchPage, CategoryPage, NotFoundPage // ALL wired to real FF_API — no FF_PRODUCTS / FF_ORDERS mock data const { useState: useStateX, useEffect: useEffectX, useMemo: useMemoX } = React; // ─── CART PAGE ──────────────────────────────────────────────────────────────── function CartPage({ cart, updateQty, removeItem, goto, user }) { const subtotal = cart.reduce((s, i) => s + (parseInt(i.price)||0) * (parseInt(i.qty)||0), 0); const shipping = subtotal >= 50000 ? 0 : 4900; const total = subtotal + shipping; if (cart.length === 0) return (
🛍️

Your bag is empty

You haven't added anything yet. The good stuff awaits.

); return (
goto({page:'home'})} style={{ cursor:'pointer' }}>Home › Cart

Your bag ({cart.length})

{/* Items */}
{cart.map(item => { const price = parseInt(item.price)||0; const qty = parseInt(item.qty)||1; return (
{item.image ? ( {item.name} ) : ( )}
{item.name}
{fmtINR(price)} each
{item.stock <= 5 &&
Only {item.stock} left!
}
{qty}
{fmtINR(price * qty)}
); })}
{/* Summary */}

Order summary

Subtotal ({cart.length} items){fmtINR(subtotal)}
Shipping {shipping === 0 ? 'Free 🎉' : fmtINR(shipping)}
{shipping > 0 && (
Add {fmtINR(50000 - subtotal)} more for free shipping
)}
Total{fmtINR(total)}
Secured by Razorpay · All sales final
); } // ─── LOGIN PAGE ─────────────────────────────────────────────────────────────── function LoginPage({ onLogin }) { const next = FF_QP('next') || FF_URLS.home; // Reuse the AuthModal logic inline on a full page const handleLogin = (user) => { location.href = next; }; const handleRegister = async (email, password, name, phone) => { const result = await FF_API.auth.register(email, password, name, phone); if (result.ok) { handleLogin(result.data?.user); return { ok:true }; } return { ok:false, error: result.data?.error || 'Registration failed' }; }; return (

Fussy Finds

Log in or create an account to continue.

← Continue as guest
); } // ─── INLINE AUTH — full 3-step OTP signup + login ─────────────────────────── function InlineAuth({ onLogin, onRegister }) { const [mode, setMode] = useStateX('login'); const [step, setStep] = useStateX('login'); // login | email | otp | details const [email, setEmail] = useStateX(''); const [otp, setOtp] = useStateX(''); const [otpHint, setOtpHint] = useStateX(''); const [name, setName] = useStateX(''); const [phone, setPhone] = useStateX(''); const [pass, setPass] = useStateX(''); const [pw, setPw] = useStateX({len:false,upper:false,lower:false,num:false}); const [resendCd,setResendCd]= useStateX(0); const [error, setError] = useStateX(''); const [loading, setLoading] = useStateX(false); const { useState: useStateX2, useEffect: useEffectX } = React; useEffectX(() => { 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(''); setPass(''); setName(''); setPhone(''); setOtp(''); }; const sendOtp = async () => { if (!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'); return; } setStep('details'); setOtp(''); } finally { setLoading(false); } }; const checkPw = (v) => { setPass(v); setPw({len:v.length>=8,upper:/[A-Z]/.test(v),lower:/[a-z]/.test(v),num:/[0-9]/.test(v)}); }; const createAccount = async () => { if (!name.trim()) { setError('Name required'); return; } if (!pw.len||!pw.upper||!pw.lower||!pw.num) { setError('Password does not meet requirements'); return; } setError(''); setLoading(true); try { const result = await FF_API.auth.register(email, pass, name, phone); if (!result.ok) { setError(result.data?.error||'Registration failed'); return; } 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}}); } catch {} onLogin && onLogin(result.data?.user); } finally { setLoading(false); } }; const loginSubmit = async () => { if (!email||!pass) { setError('Email and password required'); return; } setError(''); setLoading(true); try { const result = await FF_API.auth.login(email, pass); if (result.ok) { onLogin && onLogin(result.data?.user); } else setError(result.data?.error||'Wrong email or password'); } 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); } }; return (
{/* Mode tabs */} {(step==='login'||step==='email') && (
{[{k:'login',l:'Log in'},{k:'signup',l:'Sign up'}].map(t=>( ))}
)} {step==='login' && (
setEmail(e.target.value)} autoFocus/> setPass(e.target.value)} onKeyDown={e=>e.key==='Enter'&&loginSubmit()}/> {error &&
{error}
}
)} {step==='email' && (
Step 1 of 3 Verify your email
setEmail(e.target.value)} onKeyDown={e=>e.key==='Enter'&&sendOtp()} autoFocus/> {error &&
{error}
}
)} {step==='otp' && (
Step 2 of 3 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' && (
Step 3 of 3 ✓ {email}
setName(e.target.value)} autoFocus/> setPhone(e.target.value.replace(/\D/g,'').slice(0,10))}/> checkPw(e.target.value)}/> {pass.length>0 && (
{[['8+ characters',pw.len],['Uppercase',pw.upper],['Lowercase',pw.lower],['Number',pw.num]].map(([l,ok])=>(
{ok?'✓':'○'}{l}
))}
)} {error &&
{error}
}
)}
By continuing you agree to our{' '} Terms {' '}&{' '} Privacy Policy.
); } Object.assign(window, { CartPage, LoginPage, InlineAuth });