diff --git a/src/public/borne/assets/js/page-cart.js b/src/public/borne/assets/js/page-cart.js new file mode 100644 index 0000000..a4a7b79 --- /dev/null +++ b/src/public/borne/assets/js/page-cart.js @@ -0,0 +1,138 @@ +/* + * page-cart.js — Shopping cart screen. + * + * Displays all cart lines with quantity controls and totals. + * + * TVA: 10% (taux normal restauration, France 2024 — simplification MVPp). + * TODO: verify exact applicable TVA rate with an accountant in P3. + * The real rate depends on sur-place vs a-emporter, alcohol content, etc. + * + * The total displayed is TTC (tax inclusive) because French consumer law + * requires prices shown to end-consumers to include all taxes. + */ + +import { getCart, removeFromCart, updateQuantity, getTotalCents, clearCart, formatPrice } from './state.js'; +import { refreshCartBadge } from './nav.js'; + +/* TVA rate used for display breakdown only — stored prices are already TTC */ +const TVA_RATE = 0.10; + +const cartList = document.getElementById('cart-list'); +const emptyBlock = document.getElementById('cart-empty'); +const summaryBlock= document.getElementById('cart-summary'); +const totalTTC = document.getElementById('total-ttc'); +const totalHT = document.getElementById('total-ht'); +const totalTVA = document.getElementById('total-tva'); +const payBtn = document.getElementById('pay-btn'); +const abandonBtn = document.getElementById('abandon-btn'); + +function renderCart() { + const items = getCart(); + refreshCartBadge(); + + if (!items.length) { + cartList.innerHTML = ''; + emptyBlock.hidden = false; + summaryBlock.hidden = true; + if (payBtn) payBtn.disabled = true; + return; + } + + emptyBlock.hidden = false; /* keep structure; toggle via class */ + emptyBlock.hidden = true; + summaryBlock.hidden = false; + if (payBtn) payBtn.disabled = false; + + cartList.innerHTML = ''; + items.forEach((item, index) => { + const lineTotalCents = item.prix_cents * item.quantite; + + const row = document.createElement('li'); + row.className = 'cart-line'; + row.setAttribute('aria-label', `${item.libelle}, quantite ${item.quantite}`); + + row.innerHTML = ` + ${item.libelle} +
+ ${item.libelle} + ${formatPrice(item.prix_cents)} / unite +
+
+ + ${item.quantite} + +
+ ${formatPrice(lineTotalCents)} + + `; + cartList.appendChild(row); + }); + + /* Attach event listeners after render */ + cartList.querySelectorAll('.qty-btn--minus').forEach(btn => { + btn.addEventListener('click', () => { + const idx = parseInt(btn.dataset.index, 10); + const cart = getCart(); + updateQuantity(idx, cart[idx].quantite - 1); + renderCart(); + }); + }); + + cartList.querySelectorAll('.qty-btn--plus').forEach(btn => { + btn.addEventListener('click', () => { + const idx = parseInt(btn.dataset.index, 10); + const cart = getCart(); + updateQuantity(idx, cart[idx].quantite + 1); + renderCart(); + }); + }); + + cartList.querySelectorAll('.cart-line__remove').forEach(btn => { + btn.addEventListener('click', () => { + const idx = parseInt(btn.dataset.index, 10); + removeFromCart(idx); + renderCart(); + }); + }); + + /* Update totals */ + const ttcCents = getTotalCents(); + /* Back-calculate HT from TTC (prices assumed to be TTC already) */ + const htCents = Math.round(ttcCents / (1 + TVA_RATE)); + const tvaCents = ttcCents - htCents; + + if (totalTTC) totalTTC.textContent = formatPrice(ttcCents); + if (totalHT) totalHT.textContent = formatPrice(htCents); + if (totalTVA) totalTVA.textContent = formatPrice(tvaCents); +} + +if (abandonBtn) { + abandonBtn.addEventListener('click', () => { + clearCart(); + window.location.href = 'categories.html'; + }); +} + +document.addEventListener('DOMContentLoaded', renderCart); diff --git a/src/public/borne/cart.html b/src/public/borne/cart.html new file mode 100644 index 0000000..6c49f84 --- /dev/null +++ b/src/public/borne/cart.html @@ -0,0 +1,99 @@ + + + + + + + + Wakdo - Panier + + + + + + + + +
+ +

Votre panier

+ + + + + + + + + + + +
+ + + Valider ma commande + +
+ +
+ + + + +