// 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 (
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}
{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.
);
}
// ─── 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' && (
)}
{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}
}
)}
);
}
Object.assign(window, { CartPage, LoginPage, InlineAuth });