Your Cart

Update quantities, apply a promo code, and confirm your checkout.

    Checkout confirmation
    Review summary and confirm.
    Items 0
    Subtotal $0.00
    Discount − $0.00
    Total $0.00
    Your order is prepared. This demo does not process payments. Clicking “Confirm” will clear your cart.
    const raw = JSON.parse(localStorage.getItem(couponTtlKey) || 'null'); if (!raw || typeof raw !== 'object') return; const now = Date.now(); if (!raw.expiresAt || typeof raw.expiresAt !== 'number') return; const left = Math.max(0, Math.floor((raw.expiresAt - now) / 1000)); couponSecondsLeft = Math.min(3600, left); if (left <= 0) { clearCoupon('Coupon expired. Discount removed.'); } } catch {} } function writeCouponTTL(secondsFromNow) { const expiresAt = Date.now() + Math.max(0, secondsFromNow) * 1000; localStorage.setItem(couponTtlKey, JSON.stringify({ expiresAt })); } function computeSummary(cart) { const items = cart.reduce((a, c) => a + c.qty, 0); const subtotal = cart.reduce((a, c) => { const it = DATA.find(x => String(x.id) === String(c.id)); const p = it && Number.isFinite(Number(it.price)) ? Number(it.price) : 0; return a + p * c.qty; }, 0); let disc = 0; const code = (couponState.code || '').toUpperCase(); if (code && subtotal > 0) { if (couponState.type === 'flat') { disc = Math.max(0, Number(couponState.value || 0)); } else if (couponState.type === 'percent') { const pct = Math.max(0, Math.min(100, Number(couponState.value || 0))); disc = subtotal * (pct / 100); const cap = Math.max(0, Number(couponState.max || 0)); if (cap > 0) disc = Math.min(disc, cap); } else if (couponState.type === 'conditional_flat') { const need = 2; const val = Math.max(0, Number(couponState.value || 0)); disc = items >= need ? val : 0; } } disc = Math.min(subtotal, Math.max(0, disc)); const total = subtotal - disc; return { items, subtotal, disc, total, code }; } function setCouponMsg(text, type) { const el = document.getElementById('coupon-msg'); el.textContent = text || ''; el.className = 'mt-2 text-sm'; if (type === 'ok') el.classList.add('text-emerald-700'); else if (type === 'warn') el.classList.add('text-amber-700'); else if (type === 'err') el.classList.add('text-red-700'); else el.classList.add('text-slate-600'); } function clearCoupon(message) { couponState = { code: '', type: 'none', value: 0, max: 0 }; writeCouponState(); localStorage.removeItem(couponTtlKey); couponSecondsLeft = 300; document.getElementById('coupon').value = ''; if (message) setCouponMsg(message, 'warn'); render(); } function applyCouponByCode(codeRaw) { const code = String(codeRaw || '').trim().toUpperCase(); const cart = normalizeCart(getCart()); const { subtotal } = computeSummary(cart); if (!code) { clearCoupon('Enter a coupon code.'); return; } if (subtotal <= 0) { couponState = { code, type: 'none', value: 0, max: 0 }; writeCouponState(); writeCouponTTL(300); couponSecondsLeft = 300; setCouponMsg('Add items to your cart before applying coupons.', 'warn'); render(); return; } if (code === 'BUBBLES10') { couponState = { code, type: 'flat', value: 10, max: 0 }; writeCouponState(); writeCouponTTL(300); couponSecondsLeft = 300; setCouponMsg('Coupon applied: $10 off.', 'ok'); render(); return; } if (code === 'LATHER20') { couponState = { code, type: 'percent', value: 20, max: 35 }; writeCouponState(); writeCouponTTL(300); couponSecondsLeft = 300; setCouponMsg('Coupon applied: 20% off (max $35).', 'ok'); render(); return; } if (code === 'KIT5') { couponState = { code, type: 'conditional_flat', value: 5, max: 0 }; writeCouponState(); writeCouponTTL(300); couponSecondsLeft = 300; setCouponMsg('Coupon applied: $5 off when you have 2+ items.', 'ok'); render(); return; } couponState = { code: '', type: 'none', value: 0, max: 0 }; writeCouponState(); writeCouponTTL(300); couponSecondsLeft = 300; setCouponMsg('Invalid code.', 'err'); render(); } function render() { const rawCart = getCart(); const cart = normalizeCart(rawCart); if (JSON.stringify(cart) !== JSON.stringify(rawCart)) setCart(cart); const empty = document.getElementById('empty'); const wrap = document.getElementById('cart-wrap'); const checkoutBtn = document.getElementById('checkout'); if (!cart.length) { empty.classList.remove('hidden'); wrap.classList.add('hidden'); checkoutBtn.disabled = true; } else { empty.classList.add('hidden'); wrap.classList.remove('hidden'); checkoutBtn.disabled = false; } const body = document.getElementById('cart-body'); const rows = cart.map(item => { const it = DATA.find(x => String(x.id) === String(item.id)); if (!it) return ''; const price = Number(it.price || 0); const title = String(it.title || 'Untitled course'); const sub = price * item.qty; const safeTitle = title.replaceAll('&','&').replaceAll('<','<').replaceAll('>','>'); const unit = money(price); const subStr = money(sub); return `
    ${safeTitle}
    ID: ${String(it.id).replaceAll('&','&').replaceAll('<','<').replaceAll('>','>')}
    $${unit}
    ${item.qty}
    $${subStr}
    `; }).join(''); body.innerHTML = rows || ''; const s = computeSummary(cart); document.getElementById('sum-items').textContent = String(s.items); document.getElementById('sum-sub').textContent = money(s.subtotal); document.getElementById('sum-disc').textContent = money(s.disc); document.getElementById('sum-total').textContent = money(s.total); document.getElementById('confirm-total').textContent = money(s.total); document.getElementById('summary-coupon').textContent = s.code ? s.code : 'NONE'; const summaryBody = document.getElementById('summary-body'); summaryBody.innerHTML = cart.map(item => { const it = DATA.find(x => String(x.id) === String(item.id)); if (!it) return ''; const title = String(it.title || 'Untitled course').replaceAll('&','&').replaceAll('<','<').replaceAll('>','>'); const price = Number(it.price || 0); const sub = price * item.qty; return ` ${title} ${item.qty} $${money(price)} $${money(sub)} `; }).join('') || `No items.`; updateSavedMeta(); } function updateSavedMeta() { const el = document.getElementById('saved-meta'); try { const raw = JSON.parse(localStorage.getItem(savedKey) || 'null'); if (!raw || typeof raw !== 'object' || !raw.savedAt || !raw.cart) { el.textContent = 'No saved snapshot yet.'; return; } const dt = new Date(raw.savedAt); const cart = normalizeCart(raw.cart); const items = cart.reduce((a,c)=>a+c.qty,0); el.textContent = `Saved: ${dt.toLocaleString()} • Items: ${items}`; } catch { el.textContent = 'No saved snapshot yet.'; } } function tickCouponTimer() { const el = document.getElementById('coupon-timer'); const mm = String(Math.floor(couponSecondsLeft / 60)).padStart(2, '0'); const ss = String(couponSecondsLeft % 60).padStart(2, '0'); el.textContent = `${mm}:${ss}`; if (couponState.code) { if (couponSecondsLeft <= 0) { clearCoupon('Coupon expired. Discount removed.'); return; } couponSecondsLeft -= 1; } else { couponSecondsLeft = Math.min(300, couponSecondsLeft + 1); } } function validateEmail(v) { const s = String(v || '').trim(); if (!s) return { ok: false, msg: 'Email is required.' }; const re = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/i; if (!re.test(s)) return { ok: false, msg: 'Enter a valid email address.' }; return { ok: true, msg: '' }; } function validatePhone(v) { const s = String(v || '').trim(); if (!s) return { ok: false, msg: 'Phone is required.' }; const re = /^\+\d{1,3}\s?\(\d{2,4}\)\s?\d{3}[-\s]?\d{4}$/; if (!re.test(s)) return { ok: false, msg: 'Use format like +1 (415) 782-6049.' }; return { ok: true, msg: '' }; } function setFieldMsg(elId, msg, ok) { const el = document.getElementById(elId); el.textContent = msg || ''; el.className = 'mt-1 text-xs'; el.classList.add(ok ? 'text-emerald-700' : 'text-red-700'); if (!msg) el.className = 'mt-1 text-xs'; } function openConfirmModal() { const cart = normalizeCart(getCart()); const s = computeSummary(cart); document.getElementById('confirm-total').textContent = money(s.total); document.getElementById('confirm-result').textContent = ''; document.getElementById('demo-email-msg').textContent = ''; document.getElementById('demo-phone-msg').textContent = ''; document.getElementById('demo-agree-msg').textContent = ''; document.getElementById('confirm-modal').showModal(); } function confirmOrder() { const cart = normalizeCart(getCart()); if (!cart.length) { const el = document.getElementById('confirm-result'); el.textContent = 'Your cart is empty.'; el.className = 'text-sm text-red-700'; return; } const email = document.getElementById('demo-email').value; const phone = document.getElementById('demo-phone').value; const agree = document.getElementById('demo-agree').checked; const ve = validateEmail(email); const vp = validatePhone(phone); setFieldMsg('demo-email-msg', ve.msg, ve.ok); setFieldMsg('demo-phone-msg', vp.msg, vp.ok); if (!agree) { const el = document.getElementById('demo-agree-msg'); el.textContent = 'Please acknowledge the demo terms.'; el.className = 'text-xs text-red-700'; } else { document.getElementById('demo-agree-msg').textContent = ''; document.getElementById('demo-agree-msg').className = 'text-xs'; } if (!ve.ok || !vp.ok || !agree) return; const s = computeSummary(cart); const id = `SCA-${Math.random().toString(16).slice(2, 6).toUpperCase()}-${Date.now().toString().slice(-6)}`; const payload = { id, createdAt: new Date().toISOString(), email: String(email).trim(), phone: String(phone).trim(), coupon: couponState.code || '', items: cart, summary: { items: s.items, subtotal: Number(s.subtotal.toFixed(2)), discount: Number(s.disc.toFixed(2)), total: Number(s.total.toFixed(2)) } }; try { localStorage.setItem('last_demo_order_v1', JSON.stringify(payload)); } catch {} setCart([]); clearCoupon(); document.getElementById('confirm-result').textContent = `Confirmed. Demo confirmation ID: ${id}`; document.getElementById('confirm-result').className = 'text-sm text-emerald-700'; setTimeout(() => { const dlg = document.getElementById('confirm-modal'); if (dlg.open) dlg.close(); }, 1100); } document.addEventListener('click', (e) => { const btn = e.target.closest('[data-id][data-act]'); if (!btn) return; const id = String(btn.getAttribute('data-id') || ''); const act = String(btn.getAttribute('data-act') || ''); if (!id || !act) return; const cart = normalizeCart(getCart()); const idx = cart.findIndex(x => String(x.id) === id); if (idx < 0) return; if (act === 'inc') cart[idx].qty = Math.min(99, cart[idx].qty + 1); if (act === 'dec') cart[idx].qty = Math.max(0, cart[idx].qty - 1); if (act === 'one') cart[idx].qty = 1; if (act === 'rm') cart.splice(idx, 1); setCart(cart); render(); }); document.addEventListener('input', (e) => { const inp = e.target.closest('input[data-id][data-act="set"]'); if (!inp) return; const id = String(inp.getAttribute('data-id') || ''); const cart = normalizeCart(getCart()); const idx = cart.findIndex(x => String(x.id) === id); if (idx < 0) return; const v = String(inp.value || '').replace(/[^\d]/g, ''); if (v !== String(inp.value)) inp.value = v; const n = v ? Math.max(0, Math.min(99, parseInt(v, 10))) : 0; if (Number.isFinite(n)) { if (n === 0) cart.splice(idx, 1); else cart[idx].qty = n; setCart(cart); render(); } }); document.getElementById('apply-coupon').addEventListener('click', () => { applyCouponByCode(document.getElementById('coupon').value); }); document.getElementById('remove-coupon').addEventListener('click', () => { clearCoupon('Coupon removed.'); }); document.getElementById('coupon').addEventListener('keydown', (e) => { if (e.key === 'Enter') { e.preventDefault(); applyCouponByCode(document.getElementById('coupon').value); } }); document.getElementById('checkout').addEventListener('click', () => { openConfirmModal(); }); document.getElementById('confirm-submit').addEventListener('click', () => { confirmOrder(); }); document.getElementById('summary-print').addEventListener('click', () => { window.print(); }); document.getElementById('clear-cart').addEventListener('click', () => { const dlg = document.getElementById('confirm-modal'); if (dlg.open) dlg.close(); setCart([]); render(); }); document.getElementById('save-for-later').addEventListener('click', () => { const cart = normalizeCart(getCart()); const payload = { savedAt: Date.now(), cart }; localStorage.setItem(savedKey, JSON.stringify(payload)); updateSavedMeta(); }); document.getElementById('restore-saved').addEventListener('click', () => { try { const raw = JSON.parse(localStorage.getItem(savedKey) || 'null'); if (!raw || !raw.cart) return; const cart = normalizeCart(raw.cart); setCart(cart); render(); } catch {} }); window.addEventListener('storage', () => { render(); }); (async function () { await injectPartials(); await loadData(); readCouponState(); readCouponTTL(); if (couponState.code) { document.getElementById('coupon').value = couponState.code; setCouponMsg(`Restored coupon: ${couponState.code.toUpperCase()}`, 'ok'); } else { setCouponMsg('', ''); } render(); setInterval(() => { tickCouponTimer(); }, 1000); })();