| Course |
Price |
Qty |
Subtotal |
Action |
Coupon SAVE10 applied (10% off)
Total: $0.00
Your cart is empty. Add some courses from the catalog.
Order placed successfully
')
]);
document.querySelector('header').innerHTML = h;
document.querySelector('footer').innerHTML = f;
bindHeader();
maybeShowCookies();
}
function bindHeader(){
document.body.addEventListener('click',(e)=>{ if (e.target.closest('[data-close]')) e.target.closest('dialog')?.close(); });
const themeBtn=document.getElementById('btn-theme'); const themeDialog=document.getElementById('modal-theme'); const themeClose=document.getElementById('theme-close');
if (themeBtn&&themeDialog) themeBtn.addEventListener('click',()=>themeDialog.showModal());
if (themeClose&&themeDialog) themeClose.addEventListener('click',()=>themeDialog.close());
document.getElementById('theme-form')?.addEventListener('submit',(e)=>{
e.preventDefault(); const val=new FormData(e.target).get('theme');
if (val==='dark') document.documentElement.classList.add('dark'); else document.documentElement.classList.remove('dark');
localStorage.setItem('theme', val); themeDialog?.close();
});
}
function maybeShowCookies(){
const ok=localStorage.getItem('cookiesAccepted'); const dlg=document.getElementById('modal-cookies');
if (!ok && dlg && typeof dlg.showModal==='function') dlg.showModal();
document.getElementById('btn-accept-cookies')?.addEventListener('click',()=>{localStorage.setItem('cookiesAccepted','1'); document.getElementById('modal-cookies')?.close();});
}
(function initThemeOnLoad(){
const saved=localStorage.getItem('theme')||'light';
if (saved==='dark') document.documentElement.classList.add('dark'); else document.documentElement.classList.remove('dark');
})();
async function init(){
await injectPartials();
try{
catalog = await fetch('catalog.json').then(r=>r.json());
}catch(e){
catalog = [];
}
cart = JSON.parse(localStorage.getItem('cart')||'[]');
normalizeCart();
render();
document.getElementById('coupon-form').addEventListener('submit',(e)=>{
e.preventDefault();
const code = new FormData(e.target).get('coupon')?.toString().trim().toUpperCase();
const note = document.createElement('div');
if (code === 'SAVE10') {
localStorage.setItem('coupon','SAVE10');
note.textContent = 'Coupon applied: 10% off.';
note.className = 'text-green-700';
} else {
localStorage.removeItem('coupon');
note.textContent = 'Invalid coupon.';
note.className = 'text-red-700';
}
e.target.appendChild(note);
setTimeout(()=> note.remove(), 1800);
render();
});
document.getElementById('checkout-form').addEventListener('submit',onCheckoutSubmit);
document.getElementById('btn-finalize').addEventListener('click',finalizeOrder);
}
function normalizeCart(){
cart = cart
.filter(x=>x && typeof x.id!=='undefined')
.map(x=>({id:x.id, qty: Math.max(1, parseInt(x.qty||1,10))}));
localStorage.setItem('cart', JSON.stringify(cart));
}
function render(){
const body = document.getElementById('cart-body');
body.innerHTML = '';
const items = cart.map(ci=>{
const c = catalog.find(x=>String(x.id)===String(ci.id));
return c ? { ...c, qty: ci.qty||1 } : null;
}).filter(Boolean);
if (items.length===0){
document.getElementById('cart-empty').classList.remove('hidden');
} else {
document.getElementById('cart-empty').classList.add('hidden');
}
items.forEach(it=>{
const tr = document.createElement('tr');
tr.innerHTML = `
${escapeHTML(it.title||'Untitled')}
${escapeHTML(it.level||'')}
|
$${Number(it.priceUSD||0).toFixed(2)} |
|
$${(Number(it.priceUSD||0)*it.qty).toFixed(2)} |
|
`;
body.appendChild(tr);
});
body.querySelectorAll('.qty').forEach(inp=>{
inp.addEventListener('change', ()=>{
const id = inp.getAttribute('data-id'); const val = Math.max(1, parseInt(inp.value||'1',10));
const f = cart.find(x=>String(x.id)===String(id)); if (f) f.qty = val;
localStorage.setItem('cart', JSON.stringify(cart)); render();
const cartEl = document.getElementById('count-cart'); if (cartEl) cartEl.textContent = cart.reduce((a,c)=>a+(c.qty||1),0);
});
inp.addEventListener('input', ()=>{
if (inp.value === '') return;
if (parseInt(inp.value,10) < 1) inp.value = '1';
});
});
body.querySelectorAll('.remove').forEach(btn=> btn.addEventListener('click', ()=>{
const id = btn.getAttribute('data-id'); cart = cart.filter(x=>String(x.id)!==String(id)); localStorage.setItem('cart', JSON.stringify(cart)); render();
const cartEl = document.getElementById('count-cart'); if (cartEl) cartEl.textContent = cart.reduce((a,c)=>a+(c.qty||1),0);
}));
const coupon = localStorage.getItem('coupon');
let total = items.reduce((a,c)=> a + Number(c.priceUSD||0) * c.qty, 0);
if (coupon === 'SAVE10') total = total * 0.9;
document.getElementById('total').textContent = total.toFixed(2);
const badge = document.getElementById('coupon-badge');
if (coupon === 'SAVE10') { badge.classList.remove('hidden'); } else { badge.classList.add('hidden'); }
const cartEl = document.getElementById('count-cart'); if (cartEl) cartEl.textContent = items.reduce((a,c)=>a+(c.qty||1),0);
}
function onCheckoutSubmit(e){
e.preventDefault();
if (!validateForm(e.target)) return;
const formData = new FormData(e.target);
const name = String(formData.get('name')||'').trim();
const email = String(formData.get('email')||'').trim();
const summaryNode = document.getElementById('confirm-summary');
const items = cart.map(ci=>{
const c = catalog.find(x=>String(x.id)===String(ci.id));
return c ? { ...c, qty: ci.qty||1 } : null;
}).filter(Boolean);
const coupon = localStorage.getItem('coupon');
let subtotal = items.reduce((a,c)=> a + Number(c.priceUSD||0) * c.qty, 0);
let discount = coupon==='SAVE10' ? subtotal * 0.10 : 0;
let total = subtotal - discount;
const orderId = 'SSA-' + Math.random().toString(36).slice(2,7).toUpperCase() + '-' + Date.now().toString().slice(-4);
summaryNode.innerHTML = `
Customer
${escapeHTML(name)} • ${escapeHTML(email)}
Items
${items.map(it=>`-
${escapeHTML(it.title)} × ${it.qty}
$${(Number(it.priceUSD||0)*it.qty).toFixed(2)}
`).join('')}
Subtotal$${subtotal.toFixed(2)}
Discount ${coupon? '('+coupon+')':''}-${discount.toFixed(2)}
Total$${total.toFixed(2)}
Order ID: ${orderId}
`;
const dlg = document.getElementById('modal-confirm');
if (!dlg.open) dlg.showModal();
dlg.dataset.orderId = orderId;
}
function finalizeOrder(){
const dlg = document.getElementById('modal-confirm');
dlg.close();
localStorage.removeItem('cart');
localStorage.removeItem('coupon');
cart = [];
render();
const t = document.getElementById('toast');
t.textContent = 'Order '+ (dlg.dataset.orderId||'') +' placed successfully';
t.classList.remove('hidden');
setTimeout(()=> t.classList.add('hidden'), 2400);
const form = document.getElementById('checkout-form');
form.reset();
}
function validateForm(form){
const name = form.querySelector('input[name="name"]');
const email = form.querySelector('input[name="email"]');
let ok = true;
clearFieldError(name); clearFieldError(email);
if (!/^[A-Za-z][A-Za-z\s'.-]{1,}$/.test(name.value.trim())){
setFieldError(name,'Please enter your full name.');
ok = false;
}
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.value.trim())){
setFieldError(email,'Please enter a valid email.');
ok = false;
}
return ok;
}
function setFieldError(input, msg){
input.classList.add('border-red-500');
let hint = input.nextElementSibling;
if (!hint || !hint.classList || !hint.classList.contains('uj4sw')){
hint = document.createElement('div');
hint.className = 'text-red-600 text-sm uj4sw';
input.insertAdjacentElement('afterend', hint);
}
hint.textContent = msg;
}
function clearFieldError(input){
input.classList.remove('border-red-500');
let hint = input.nextElementSibling;
if (hint && hint.classList && hint.classList.contains('uj4sw')) hint.remove();
}
function escapeHTML(s){
return String(s).replace(/[&<>"']/g, m=>({ '&':'&','<':'<','>':'>','"':'"',"'":'''}[m]));
}
document.addEventListener('keydown',(e)=>{
if (e.key==='Escape'){
document.getElementById('modal-confirm')?.close();
document.getElementById('modal-cookies')?.close();
}
});
init();