// RIFTR storefront — checkout: details + fulfillment, then TNG payment + receipt upload. (() => { const { Button, Input, Label, Card } = window.RIFTRDesignSystem_b64b32; const FULFILLMENT = [ { type: 'pickup', location: 'Boards & Brew, Sunway', fee: 0, sub: 'Self-collect' }, { type: 'meetup', location: 'Subang Jaya', fee: 0, sub: 'Meetup point' }, { type: 'meetup', location: 'Kota Kemuning', fee: 0, sub: 'Meetup point' }, { type: 'delivery', location: 'Peninsular Malaysia', fee: 10, sub: 'Courier · +RM10', needsAddress: true }, ]; const TYPE_LABEL = { pickup: 'Pickup', meetup: 'COD meetup', delivery: 'Delivery' }; // Peninsular Malaysia only (excludes Labuan, Sabah, Sarawak). const STATES = [ 'Johor', 'Kedah', 'Kelantan', 'Kuala Lumpur', 'Melaka', 'Negeri Sembilan', 'Pahang', 'Penang', 'Perak', 'Perlis', 'Putrajaya', 'Selangor', 'Terengganu', ]; function Field({ children }) { return
{children}
; } function Checkout({ cart, onBack, onClearCart }) { const RM = window.RIFTR_RM; const Ic = window.Ic; const [name, setName] = React.useState(''); const [email, setEmail] = React.useState(''); const [phone, setPhone] = React.useState(''); const [sel, setSel] = React.useState(null); const [addr1, setAddr1] = React.useState(''); const [addr2, setAddr2] = React.useState(''); const [city, setCity] = React.useState(''); const [postcode, setPostcode] = React.useState(''); const [stateVal, setStateVal] = React.useState(''); const [stage, setStage] = React.useState('form'); // form → pay → done const [order, setOrder] = React.useState(null); const [busy, setBusy] = React.useState(false); const [err, setErr] = React.useState(''); const [file, setFile] = React.useState(null); const [upBusy, setUpBusy] = React.useState(false); const [upErr, setUpErr] = React.useState(''); const [copied, setCopied] = React.useState(false); React.useEffect(() => { window.lucide && window.lucide.createIcons(); }); const subtotal = cart.reduce((a, b) => a + b.price * b.qty, 0); const fee = sel != null ? FULFILLMENT[sel].fee : 0; const total = subtotal + fee; async function placeOrder() { setErr(''); if (!name.trim()) return setErr('Please enter your name.'); if (!/^\S+@\S+\.\S+$/.test(email)) return setErr('Please enter a valid email.'); if (!phone.trim()) return setErr('Please enter your phone number.'); if (sel == null) return setErr('Please choose a fulfillment option.'); const opt = FULFILLMENT[sel]; if (opt.needsAddress) { if (!addr1.trim()) return setErr('Please enter Address line 1.'); if (!city.trim()) return setErr('Please enter your city.'); if (!postcode.trim()) return setErr('Please enter your postcode.'); if (!stateVal) return setErr('Please select your state.'); } const fullAddress = [addr1, addr2, city, `${postcode} ${stateVal}`.trim()] .filter((s) => s && s.trim()).join(', '); setBusy(true); try { const res = await fetch('/api/create_order.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name, email, phone, fulfillment_type: opt.type, fulfillment_location: opt.location, delivery_address: opt.needsAddress ? fullAddress : '', items: cart.map((c) => ({ id: c.id, qty: c.qty })), }), }); const data = await res.json(); if (!data.ok) throw new Error(data.error || 'Could not place order.'); setOrder(data); onClearCart(); setStage('pay'); window.scrollTo({ top: 0 }); } catch (e) { setErr(e.message || 'Network error. Please try again.'); } finally { setBusy(false); } } async function uploadReceipt() { setUpErr(''); if (!file) return setUpErr('Please choose your receipt image first.'); setUpBusy(true); try { const fd = new FormData(); fd.append('order_id', order.order_id); fd.append('receipt', file); const res = await fetch('/api/upload_receipt.php', { method: 'POST', body: fd }); const data = await res.json(); if (!data.ok) throw new Error(data.error || 'Upload failed.'); setStage('done'); window.scrollTo({ top: 0 }); } catch (e) { setUpErr(e.message || 'Network error. Please try again.'); } finally { setUpBusy(false); } } // ---------- DONE ---------- if (stage === 'done') { const copyId = async () => { try { await navigator.clipboard.writeText(order.order_id); } catch (e) { const ta = document.createElement('textarea'); ta.value = order.order_id; document.body.appendChild(ta); ta.select(); document.execCommand('copy'); ta.remove(); } setCopied(true); setTimeout(() => setCopied(false), 1500); }; return (

Receipt received

We're verifying your payment now. You'll get an email once it's confirmed — usually within a few hours.

{order.order_id}
{copied &&
Copied!
}

Save this ID to track your order anytime.

); } // ---------- PAY ---------- if (stage === 'pay') { return (

Pay with TNG

Scan the QR with Touch ’n Go eWallet and pay the exact total. Then upload your receipt below.

{RM(order.total)}
TNG QR

ORDER {order.order_id}

{upErr &&

{upErr}

}

Save your order ID — {order.order_id}

); } // ---------- FORM ---------- return (

Checkout

{cart.length === 0 ? (

Your cart is empty.

) : (
{/* LEFT — details */}

Your details

setName(e.target.value)} placeholder="Your name" /> setEmail(e.target.value)} placeholder="you@email.com" /> setPhone(e.target.value)} placeholder="01X-XXXXXXX" />

Fulfillment

{FULFILLMENT.map((o, i) => { const on = sel === i; return ( ); })}
{sel != null && FULFILLMENT[sel].needsAddress && (

Delivery address

setAddr1(e.target.value)} placeholder="Address Line 1" /> setAddr2(e.target.value)} placeholder="Address Line 2" /> setCity(e.target.value)} placeholder="City" />
setPostcode(e.target.value)} placeholder="Postcode" />
)}
{/* RIGHT — summary */}

Order summary

{cart.map((it) => (
{it.qty}× {it.name} {RM(it.price * it.qty)}
))}
{RM(subtotal)}
{sel == null ? '—' : (fee ? RM(fee) : 'FREE')}
Total {RM(total)}
{err &&

{err}

}

Pay via TNG QR on the next step

)}
); } Object.assign(window, { Checkout }); })();