"use strict"; // Data komt uit products.js (window.PRODUCTS / window.CATEGORIES). const PRODUCTS = window.PRODUCTS || []; const CATEGORIES = window.CATEGORIES || [{ key: "all", label: "Alles" }]; const TAX_RATE = 0.21; // Winkelmandje: Map van product-id -> aantal. const cart = new Map(); let activeFilter = "all"; const els = { grid: document.getElementById("grid"), filters: document.getElementById("filters"), productCount: document.getElementById("productCount"), overlay: document.getElementById("overlay"), drawer: document.getElementById("drawer"), cartItems: document.getElementById("cartItems"), cartCount: document.getElementById("cartCount"), cartSub: document.getElementById("cartSub"), cartTax: document.getElementById("cartTax"), cartTotal: document.getElementById("cartTotal"), checkoutBtn: document.getElementById("checkoutBtn"), modalWrap: document.getElementById("modalWrap"), modalContent: document.getElementById("modalContent"), toast: document.getElementById("toast"), openCart: document.getElementById("openCart"), closeCart: document.getElementById("closeCart"), }; // Hulpfuncties voor opmaak. const formatPrice = (value) => "\u20AC" + value.toFixed(2).replace(".", ","); const escapeHtml = (str) => String(str).replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c])); const stars = (rating) => "\u2605".repeat(rating) + "\u2606".repeat(5 - rating); const findProduct = (id) => PRODUCTS.find((p) => p.id === id); // Tegelinhoud: toon de foto als die er is, val anders terug op de connector-tegel. // onerror verwijdert een gebroken afbeelding en toont alsnog de connectors. function thumbInner(p) { const fallback = ` ${escapeHtml(p.from)} ${escapeHtml(p.to)} `; if (p.image) { return `${escapeHtml(p.name)}`; } return fallback; } // Rendering. function renderFilters() { els.filters.innerHTML = CATEGORIES.map((c) => `` ).join(""); } function visibleProducts() { return activeFilter === "all" ? PRODUCTS : PRODUCTS.filter((p) => p.category === activeFilter); } function renderCatalog() { const list = visibleProducts(); els.productCount.textContent = list.length + " producten" + (activeFilter === "all" ? "" : " in deze categorie"); els.grid.innerHTML = list.map((p) => { const fallback = `${escapeHtml(p.from)}${escapeHtml(p.to)}`; const thumb = p.image ? `${escapeHtml(p.name)}` : fallback; return `
${thumb}
${escapeHtml(p.id)} ${stars(p.rating)}(${p.reviews})

${escapeHtml(p.name)}

${escapeHtml(p.desc)}

${formatPrice(p.price)}${formatPrice(p.old)}
`; }).join(""); } function cartTotals() { let count = 0; let subtotal = 0; for (const [id, qty] of cart) { const product = findProduct(id); if (!product) continue; count += qty; subtotal += product.price * qty; } const tax = subtotal * TAX_RATE; return { count, subtotal, tax, total: subtotal + tax }; } function renderCart() { const { count, subtotal, tax, total } = cartTotals(); els.cartCount.textContent = String(count); els.cartSub.innerHTML = formatPrice(subtotal); els.cartTax.innerHTML = formatPrice(tax); els.cartTotal.innerHTML = formatPrice(total); els.checkoutBtn.disabled = count === 0; if (cart.size === 0) { els.cartItems.innerHTML = '
Uw winkelmandje is leeg.
'; return; } const rows = []; for (const [id, qty] of cart) { const p = findProduct(id); if (!p) continue; const tile = p.image ? `` : escapeHtml(p.from); rows.push(`
${escapeHtml(p.name)} ${formatPrice(p.price)} per stuk
${qty}
${formatPrice(p.price * qty)}
`); } els.cartItems.innerHTML = rows.join(""); } // Toast. let toastTimer; function showToast(message) { els.toast.textContent = message; els.toast.classList.add("show"); clearTimeout(toastTimer); toastTimer = setTimeout(() => els.toast.classList.remove("show"), 2200); } // Mandje-operaties. function addToCart(id) { const product = findProduct(id); if (!product) return; cart.set(id, (cart.get(id) || 0) + 1); renderCart(); showToast(product.name + " toegevoegd aan het mandje."); } function stepQuantity(id, step) { if (!cart.has(id)) return; const next = cart.get(id) + step; if (next <= 0) { cart.delete(id); } else { cart.set(id, next); } renderCart(); } function clearCart() { cart.clear(); renderCart(); } // Drawer. function openCart() { els.overlay.classList.add("open"); els.drawer.classList.add("open"); } function closeCart() { els.overlay.classList.remove("open"); els.drawer.classList.remove("open"); } // Modal. function openModal() { els.modalWrap.classList.add("open"); } function closeModal() { els.modalWrap.classList.remove("open"); } function renderCheckoutForm() { const { total } = cartTotals(); els.modalContent.innerHTML = `

Afrekenen

Dit formulier verwerkt geen gegevens. Ingevoerde waarden worden niet opgeslagen of verzonden.

Demonstratieformulier. Er wordt geen betaling uitgevoerd.

`; document.getElementById("payForm").addEventListener("submit", (event) => { event.preventDefault(); const form = event.currentTarget; // Gebruik native validatie in plaats van ongeldige invoer te negeren. if (!form.checkValidity()) { form.reportValidity(); return; } renderSuccess(); }); } function renderSuccess() { const { count } = cartTotals(); const orderId = "CL-" + Math.floor(100000 + Math.random() * 900000); els.modalContent.innerHTML = `

Bestelling geregistreerd

${count} fictief artikel(en) zijn vastgelegd in deze demonstratie.

Referentie: ${escapeHtml(orderId)}

Verwachte levering: niet van toepassing.

`; clearCart(); document.getElementById("doneBtn").addEventListener("click", () => { closeModal(); showToast("Winkelmandje geleegd."); }); } // Event-koppeling (delegatie waar lijsten betrokken zijn). els.filters.addEventListener("click", (event) => { const button = event.target.closest("[data-filter]"); if (!button) return; activeFilter = button.dataset.filter; renderFilters(); renderCatalog(); }); els.grid.addEventListener("click", (event) => { const button = event.target.closest("[data-add]"); if (button) addToCart(button.dataset.add); }); els.cartItems.addEventListener("click", (event) => { const button = event.target.closest("[data-step]"); if (button) stepQuantity(button.dataset.id, Number(button.dataset.step)); }); els.openCart.addEventListener("click", openCart); els.closeCart.addEventListener("click", closeCart); els.overlay.addEventListener("click", closeCart); els.checkoutBtn.addEventListener("click", () => { if (cart.size === 0) return; closeCart(); renderCheckoutForm(); openModal(); }); els.modalWrap.addEventListener("click", (event) => { if (event.target === els.modalWrap) closeModal(); }); document.addEventListener("keydown", (event) => { if (event.key === "Escape") { closeModal(); closeCart(); } }); // Eerste render. document.getElementById("year").textContent = String(new Date().getFullYear()); renderFilters(); renderCatalog(); renderCart();