AquaLearn

Theme

Sign in

Create account

Password strength: —

Your Cart

Course Level Tuition Qty Subtotal
Subtotal: $0.00

Available: AQUA10 (10% off), STUDENT15 (15% off, 2+ items), SPLASH20 ($20 off when subtotal ≥ $150)

Confirmed

Your order is confirmed (simulation).

'), fetch('./footer.html').then(r=>r.text()).catch(()=>' ') ]); document.querySelector('header').innerHTML=h; document.querySelector('footer').innerHTML=f; initHeaderActions(); initFooterActions(); } function initHeaderActions(){ const btn=document.querySelector('[data-nav-toggle]'); const menu=document.querySelector('[data-nav-menu]'); if(btn&&menu) btn.addEventListener('click',()=>menu.classList.toggle('hidden')); const tDialog=document.getElementById('themeDialog'); document.querySelectorAll('[data-open-theme]').forEach(el=>el.addEventListener('click',()=>tDialog?.showModal())); document.querySelectorAll('[data-theme]').forEach(b=>b.addEventListener('click',()=>{ const m=b.getAttribute('data-theme'); if(m==='dark')document.documentElement.classList.add('dark'); else document.documentElement.classList.remove('dark'); localStorage.setItem('theme',m); tDialog?.close(); })); if(localStorage.getItem('theme')==='dark') document.documentElement.classList.add('dark'); const s1=document.getElementById('signInDialog'); const s2=document.getElementById('signUpDialog'); document.querySelectorAll('[data-open-signin]').forEach(b=>b.addEventListener('click',()=>s1?.showModal())); document.querySelectorAll('[data-open-signup]').forEach(b=>b.addEventListener('click',()=>s2?.showModal())); document.querySelectorAll('form').forEach(f=>f.addEventListener('submit',(e)=>{ if(f.getAttribute('method')==='dialog' || f.hasAttribute('data-allow-submit')) return; e.preventDefault(); })); } function initFooterActions(){ const cookieDialog=document.getElementById('cookieDialog'); if(cookieDialog && !localStorage.getItem('cookie:accepted')) cookieDialog.showModal(); document.querySelectorAll('[data-accept-cookies]').forEach(b=>b.addEventListener('click',()=>{localStorage.setItem('cookie:accepted','1'); cookieDialog?.close();})); document.getElementById('toTop')?.addEventListener('click',()=>window.scrollTo({top:0,behavior:'smooth'})); } function getCart(){ try { return JSON.parse(localStorage.getItem('cart')||'{}'); } catch(e){ return {}; } } function setCart(obj){ localStorage.setItem('cart', JSON.stringify(obj)); } function getPromo(){ try { return JSON.parse(localStorage.getItem('cart:promo')||'null'); } catch(e){ return null; } } function setPromo(p){ if(p) localStorage.setItem('cart:promo', JSON.stringify(p)); else localStorage.removeItem('cart:promo'); } async function loadData(){ try{ data = await fetch('./catalog.json', { cache: 'no-store' }).then(r=>r.json()); }catch(e){ data = []; } render(); } function priceToCents(price){ const n = Number(price||0); return Math.round(n*100); } function applyPromoRules(subtotalCents, itemsCount, codeRaw){ const code = String(codeRaw||'').trim().toUpperCase(); if(!code) return { code: null, discountCents: 0, message: '' }; if(code === 'AQUA10'){ const discount = Math.round(subtotalCents * 0.10); return { code, discountCents: discount, message: 'AQUA10 applied: 10% off.' }; } if(code === 'STUDENT15'){ if(itemsCount >= 2){ const discount = Math.round(subtotalCents * 0.15); return { code, discountCents: discount, message: 'STUDENT15 applied: 15% off for 2+ items.' }; } return { code: null, discountCents: 0, message: 'Add at least 2 items to use STUDENT15.' }; } if(code === 'SPLASH20'){ if(subtotalCents >= 15000){ const discount = 2000; return { code, discountCents: discount, message: 'SPLASH20 applied: $20 off orders over $150.' }; } return { code: null, discountCents: 0, message: 'Subtotal must be at least $150 for SPLASH20.' }; } return { code: null, discountCents: 0, message: 'Unknown promo code.' }; } function render(){ const cart = getCart(); const ids = Object.keys(cart); const tbody = document.getElementById('cartBody'); // Build rows if (!ids.length){ tbody.innerHTML = 'Your cart is empty.'; updateSummary(0, 0); document.getElementById('checkout').setAttribute('disabled','true'); return; } let subtotalCents = 0; let itemsCount = 0; const rows = ids.map(id=>{ const it = data.find(x=>String(x.id)===String(id)) || null; const qty = Math.max(1, parseInt(cart[id]||1, 10)); const unitCents = priceToCents(it?.price||0); const subCents = unitCents * qty; subtotalCents += subCents; itemsCount += qty; const title = it?.title || `Course ${id}`; const level = it?.level || '-'; return ` ${escapeHtml(title)} ${escapeHtml(level)} ${fmt.format(unitCents/100)} ${fmt.format(subCents/100)} `; }).join(''); tbody.innerHTML = rows; // Bind qty change/remove tbody.querySelectorAll('[data-qty]').forEach(inp=>{ let t; inp.addEventListener('input', ()=>{ clearTimeout(t); t = setTimeout(()=>{ const id = inp.getAttribute('data-qty'); const v = Math.max(1, parseInt(inp.value||'1',10)); const c=getCart(); c[id]=v; setCart(c); render(); }, 120); }); inp.addEventListener('blur', ()=>{ const id = inp.getAttribute('data-qty'); let v = Math.max(1, parseInt(inp.value||'1',10)); inp.value = v; const c=getCart(); c[id]=v; setCart(c); render(); }); }); tbody.querySelectorAll('[data-rm]').forEach(btn=>btn.addEventListener('click', ()=>{ const id = btn.getAttribute('data-rm'); const c=getCart(); delete c[id]; setCart(c); render(); })); updateSummary(subtotalCents, itemsCount); document.getElementById('checkout').removeAttribute('disabled'); } function updateSummary(subtotalCents, itemsCount){ const promo = getPromo(); const rule = applyPromoRules(subtotalCents, itemsCount, promo?.code || ''); const discountCents = rule.code ? rule.discountCents : 0; const totalCents = Math.max(0, subtotalCents - discountCents); document.getElementById('subtotal').textContent = fmt.format(subtotalCents/100); document.getElementById('sumSubtotal').textContent = fmt.format(subtotalCents/100); document.getElementById('sumDiscount').textContent = `-${fmt.format(discountCents/100)}`; document.getElementById('total').textContent = fmt.format(totalCents/100); document.getElementById('itemsCount').textContent = String(itemsCount); const promoInput = document.getElementById('promoInput'); const promoFeedback = document.getElementById('promoFeedback'); if(rule.code){ promoInput.value = rule.code; promoFeedback.textContent = rule.message; promoFeedback.className = 'mt-2 text-sm text-emerald-600 dark:text-emerald-400'; setPromo({ code: rule.code }); }else{ if(promo && promo.code){ promoFeedback.textContent = rule.message || 'Promo removed.'; promoFeedback.className = 'mt-2 text-sm text-red-600'; }else{ promoFeedback.textContent = ''; promoFeedback.className = 'mt-2 text-sm'; } if(!rule.code && (!rule.message || rule.message==='')) setPromo(null); } } function escapeHtml(s){ return String(s).replace(/[&<>"']/g, m => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[m])); } function escapeAttr(s){ return escapeHtml(s).replace(/"/g,'"'); } document.addEventListener('DOMContentLoaded', ()=>{ if(localStorage.getItem('theme')==='dark'){ document.documentElement.classList.add('dark'); } }); document.getElementById('clearCart').addEventListener('click', ()=>{ localStorage.removeItem('cart'); setPromo(null); render(); const modal = document.getElementById('modalInfo'); const t = document.getElementById('modalTitle'); const txt = document.getElementById('modalInfoText'); t.textContent='Cart cleared'; txt.textContent='All items were removed from your cart.'; modal.showModal(); }); document.getElementById('checkout').addEventListener('click', ()=>{ const cart = getCart(); const ids = Object.keys(cart); const modal = document.getElementById('modalInfo'); const text = document.getElementById('modalInfoText'); const title = document.getElementById('modalTitle'); if (!ids.length){ title.textContent='Cart is empty'; text.textContent='Add courses before checkout.'; modal.showModal(); return; } // Compose summary let itemsCount = 0; let subtotalCents = 0; ids.forEach(id=>{ const it = data.find(x=>String(x.id)===String(id)); const qty = Math.max(1, parseInt(cart[id]||1,10)); itemsCount += qty; const unit = priceToCents(it?.price||0); subtotalCents += unit * qty; }); const rule = applyPromoRules(subtotalCents, itemsCount, (getPromo()?.code)||''); const discount = rule.code ? rule.discountCents : 0; const totalCents = Math.max(0, subtotalCents - discount); title.textContent='Checkout'; text.innerHTML = `Your enrollment is confirmed (client-side simulation).
Items: ${itemsCount}
Total tuition: ${fmt.format(totalCents/100)}`; modal.showModal(); }); document.getElementById('copyCart').addEventListener('click', async ()=>{ const payload = JSON.stringify(getCart(), null, 2); try { await navigator.clipboard.writeText(payload); } catch(e){} const modal = document.getElementById('modalInfo'); const title = document.getElementById('modalTitle'); const text = document.getElementById('modalInfoText'); title.textContent='Copied'; text.textContent='Cart JSON copied to clipboard.'; modal.showModal(); }); document.getElementById('promoForm').addEventListener('submit',(e)=>{ e.preventDefault(); const input = document.getElementById('promoInput'); const code = (input.value||'').trim().toUpperCase(); const cart = getCart(); const ids = Object.keys(cart); let subtotalCents = 0; let itemsCount = 0; ids.forEach(id=>{ const it = data.find(x=>String(x.id)===String(id)); const qty = Math.max(1, parseInt(cart[id]||1,10)); itemsCount += qty; subtotalCents += priceToCents(it?.price||0) * qty; }); const result = applyPromoRules(subtotalCents, itemsCount, code); const feedback = document.getElementById('promoFeedback'); if(result.code){ setPromo({ code: result.code }); feedback.textContent = result.message; feedback.className = 'mt-2 text-sm text-emerald-600 dark:text-emerald-400'; }else{ setPromo(null); feedback.textContent = result.message; feedback.className = 'mt-2 text-sm text-red-600'; } render(); }); window.addEventListener('storage', (e)=>{ if(e.key==='cart' || e.key==='cart:promo'){ render(); } }); document.addEventListener('keydown', (e)=>{ if(e.target && ['INPUT','TEXTAREA'].includes(e.target.tagName)) return; if(e.key.toLowerCase()==='c'){ document.getElementById('checkout').click(); } if(e.key.toLowerCase()==='x'){ document.getElementById('clearCart').click(); } }); loadPartials().then(loadData);