Voxelant

Cart

A considered learning plan.

`; const footerHTML = ``; function insertHeaderFooter() { document.querySelector('header').innerHTML = headerHTML; document.querySelector('footer').innerHTML = footerHTML; } function initThemeAndMobileAndAuth() { const html = document.documentElement; const saved = localStorage.getItem('voxelant-theme') || 'light'; if (saved === 'dark') { html.classList.add('dark'); document.body.style.backgroundColor = '#0f172a'; } else { document.body.style.backgroundColor = '#f8fafc'; } const toggle = document.querySelector('[data-theme-toggle]'); if (toggle) toggle.addEventListener('click', () => { const isDark = html.classList.contains('dark'); const next = isDark ? 'light' : 'dark'; if (next === 'dark') { html.classList.add('dark'); document.body.style.backgroundColor = '#0f172a'; } else { html.classList.remove('dark'); document.body.style.backgroundColor = '#f8fafc'; } localStorage.setItem('voxelant-theme', next); }); const mb = document.getElementById('mobile-menu-btn'); const nav = document.getElementById('site-navigation'); if (mb && nav) mb.addEventListener('click', () => { const hidden = nav.classList.contains('hidden'); if (hidden) { nav.classList.remove('hidden'); nav.classList.add('flex', 'flex-col', 'w-full', 'mt-4', 'md:hidden'); mb.setAttribute('aria-expanded', 'true'); mb.textContent = 'Close'; } else { nav.classList.add('hidden'); nav.classList.remove('flex', 'flex-col', 'w-full', 'mt-4', 'md:hidden'); mb.setAttribute('aria-expanded', 'false'); mb.textContent = 'Menu'; } }); const authModal = document.getElementById('auth-modal'); document.querySelectorAll('[data-open-auth]').forEach(btn => btn.addEventListener('click', () => { authModal.removeAttribute('hidden'); })); const closeBtn = document.querySelector('[data-close-modal]'); if (closeBtn) closeBtn.addEventListener('click', () => authModal.setAttribute('hidden', '')); } let catalog = []; let cart = JSON.parse(localStorage.getItem('voxelant-cart') || '{}'); async function loadData() { const res = await fetch('./catalog.json'); catalog = await res.json(); renderCart(); } function saveCart() { localStorage.setItem('voxelant-cart', JSON.stringify(cart)); } function renderCart() { const list = document.getElementById('cart-list'); const empty = document.getElementById('cart-empty'); const summary = document.getElementById('cart-summary'); list.innerHTML = ''; let total = 0; let hasItems = false; Object.keys(cart).forEach(id => { const qty = cart[id]; const item = catalog.find(p => p.id === id); if (!item || qty < 1) return; hasItems = true; const subtotal = item.price * qty; total += subtotal; const row = document.createElement('div'); row.className = 'p9k2l flex items-center justify-between border border-slate-200 bg-white px-6 py-5 rounded-xl'; row.innerHTML = `
${item.title}
${item.duration}
${qty}
$${subtotal}
`; list.appendChild(row); }); if (hasItems) { empty.classList.add('hidden'); summary.classList.remove('hidden'); document.getElementById('cart-total').textContent = `Total: $${total}`; } else { empty.classList.remove('hidden'); summary.classList.add('hidden'); } attachCartListeners(); } function attachCartListeners() { document.querySelectorAll('[data-inc]').forEach(btn => { btn.onclick = () => { const id = btn.getAttribute('data-inc'); cart[id] = (cart[id] || 0) + 1; saveCart(); renderCart(); }; }); document.querySelectorAll('[data-dec]').forEach(btn => { btn.onclick = () => { const id = btn.getAttribute('data-dec'); if (cart[id] > 1) cart[id]--; saveCart(); renderCart(); }; }); document.querySelectorAll('[data-remove]').forEach(btn => { btn.onclick = () => { const id = btn.getAttribute('data-remove'); delete cart[id]; saveCart(); renderCart(); }; }); } function initCheckout() { const modal = document.getElementById('checkout-modal'); const btn = document.getElementById('checkout-btn'); const close = document.getElementById('close-checkout'); if (btn) btn.onclick = () => modal.removeAttribute('hidden'); if (close) close.onclick = () => modal.setAttribute('hidden', ''); } function initCookie() { const b = document.getElementById('cookie-banner'); if (!b) return; const key = 'voxelant-cookie-choice'; if (localStorage.getItem(key) === 'accepted') { b.style.display = 'none'; return; } b.querySelector('[data-close-cookie]').onclick = () => { localStorage.setItem(key, 'accepted'); b.style.display = 'none'; }; } function boot() { insertHeaderFooter(); initThemeAndMobileAndAuth(); initCheckout(); initCookie(); loadData(); } boot();