// 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' };
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 [address, setAddress] = 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('');
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 && !address.trim()) return setErr('Please enter your delivery address.');
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 ? address : '',
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') {
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}
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)}
ORDER {order.order_id}
{upErr && {upErr}
}
Save your order ID — {order.order_id}
);
}
// ---------- FORM ----------
return (
} style={{ padding: '8px 0', marginBottom: 14 }}>Back
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 && (
setAddress(e.target.value)} placeholder="Full address, postcode, state" />
)}
{/* 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 });
})();