Speakelio Plumbing School

Your cart

Adjust quantities, remove items, or continue browsing the catalog.

`; const footerHTML = ``; document.querySelector('header').innerHTML = headerHTML; document.querySelector('footer').innerHTML = footerHTML; // mobile nav const mobileBtn = document.getElementById('mobileMenuBtn'); const mobilePanel = document.getElementById('mobileNavPanel'); if(mobileBtn && mobilePanel){ mobileBtn.addEventListener('click',()=>{ const exp = mobileBtn.getAttribute('aria-expanded')==='true'; mobileBtn.setAttribute('aria-expanded',!exp); mobilePanel.classList.toggle('hidden',exp); }); } // theme modal handlers const themeModal = document.getElementById('themeModal'); const openTheme = document.getElementById('openTheme'); const openThemeM = document.getElementById('openThemeM'); const closeTheme = document.getElementById('closeTheme'); function bindTheme(btn){ if(btn) btn.addEventListener('click',()=>themeModal.classList.remove('hidden')); } bindTheme(openTheme); bindTheme(openThemeM); if(closeTheme) closeTheme.addEventListener('click',()=>themeModal.classList.add('hidden')); if(themeModal) themeModal.addEventListener('click',e=>{if(e.target===themeModal) themeModal.classList.add('hidden');}); const themeLight = document.getElementById('themeLight'); const themeDark = document.getElementById('themeDark'); const themeSystem = document.getElementById('themeSystem'); function setTheme(mode){ if(mode==='dark'){ document.documentElement.classList.add('dark'); localStorage.setItem('sps-theme','dark'); } else if(mode==='light'){ document.documentElement.classList.remove('dark'); localStorage.setItem('sps-theme','light'); } else { document.documentElement.classList.remove('dark'); localStorage.removeItem('sps-theme'); } if(themeModal) themeModal.classList.add('hidden'); } if(themeLight) themeLight.addEventListener('click',()=>setTheme('light')); if(themeDark) themeDark.addEventListener('click',()=>setTheme('dark')); if(themeSystem) themeSystem.addEventListener('click',()=>setTheme('system')); (function initTheme(){ const saved = localStorage.getItem('sps-theme'); if(saved==='dark') document.documentElement.classList.add('dark'); else if(saved==='light') document.documentElement.classList.remove('dark'); else if(window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) document.documentElement.classList.add('dark'); })(); // auth modal const authModal = document.getElementById('authModal'); const openAuth = document.getElementById('openAuth'); const openAuthM = document.getElementById('openAuthM'); const closeAuth = document.getElementById('closeAuth'); function bindAuth(btn){ if(btn) btn.addEventListener('click',()=>authModal.classList.remove('hidden')); } bindAuth(openAuth); bindAuth(openAuthM); if(closeAuth) closeAuth.addEventListener('click',()=>authModal.classList.add('hidden')); if(authModal) authModal.addEventListener('click',e=>{if(e.target===authModal) authModal.classList.add('hidden');}); } injectHeaderFooter(); function readLS(key, fallback){ try{ return JSON.parse(localStorage.getItem(key)) ?? fallback; }catch(e){ return fallback; } } function writeLS(key, val){ localStorage.setItem(key, JSON.stringify(val)); } let all = []; let cart = readLS('cart', []); function qtyControl(id, qty){ return `
`; } function render(){ const wrap = document.getElementById('cartList'); wrap.innerHTML = ''; const items = cart.map(ci => ({...ci, data: all.find(d=>d.id===ci.id)})).filter(x=>x.data); let subtotal = 0; items.forEach(row => { const line = document.createElement('div'); line.className = 'p-4 rounded-lg border border-slate-200 bg-white flex items-center justify-between gap-4'; const price = row.data.price * row.qty; subtotal += price; line.innerHTML = `

${row.data.title}

${row.data.category} • ${row.data.level} • ${row.data.durationHours}h
$${row.data.price} each
${qtyControl(row.id, row.qty)}
$${price.toFixed(2)}
`; wrap.appendChild(line); }); const sum = document.getElementById('summary'); const st = document.getElementById('subtotal'); st.textContent = `$${subtotal.toFixed(2)}`; sum.classList.toggle('hidden', items.length===0); if(items.length===0){ wrap.innerHTML = '
Your cart is empty. Explore the catalog.
'; } } document.getElementById('cartList').addEventListener('click', (e)=>{ const b = e.target.closest('button[data-id]'); if(!b) return; const id = Number(b.dataset.id); const act = b.dataset.act; const ix = cart.findIndex(x=>x.id===id); if(ix<0) return; if(act==='inc'){ cart[ix].qty += 1; } if(act==='dec'){ cart[ix].qty = Math.max(1, cart[ix].qty - 1); } if(act==='remove'){ cart.splice(ix,1); } writeLS('cart', cart); render(); }); document.getElementById('cartList').addEventListener('input', (e)=>{ const inp = e.target.closest('input[data-id][data-act=set]'); if(!inp) return; const id = Number(inp.dataset.id); const ix = cart.findIndex(x=>x.id===id); if(ix<0) return; const val = Math.max(1, parseInt(inp.value||'1',10)); cart[ix].qty = val; writeLS('cart', cart); render(); }); const modal = document.getElementById('coModal'); document.getElementById('checkout').addEventListener('click', ()=> modal.classList.remove('hidden')); document.getElementById('coClose').addEventListener('click', ()=> modal.classList.add('hidden')); modal.addEventListener('click', (e)=>{ if(e.target===modal) modal.classList.add('hidden'); }); fetch('./catalog.json').then(r=>r.json()).then(data=>{ all=data; render(); });