release: dev -> main (P1 conception v0.2 + front P5 + admin shell) #1

Merged
Corentin merged 34 commits from dev into main 2026-06-04 17:44:30 +02:00
2 changed files with 237 additions and 0 deletions
Showing only changes of commit c517b16569 - Show all commits

View file

@ -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 = `
<img
class="cart-line__image"
src="${item.image}"
alt="${item.libelle}"
onerror="this.src='assets/images/ui/logo.png'; this.alt='Image non disponible';"
>
<div class="cart-line__info">
<span class="cart-line__name">${item.libelle}</span>
<span class="cart-line__unit-price">${formatPrice(item.prix_cents)} / unite</span>
</div>
<div class="cart-line__qty" role="group" aria-label="Quantite de ${item.libelle}">
<button
class="qty-btn qty-btn--minus"
data-index="${index}"
aria-label="Diminuer la quantite de ${item.libelle}"
type="button"
>-</button>
<span class="qty-value" aria-live="polite">${item.quantite}</span>
<button
class="qty-btn qty-btn--plus"
data-index="${index}"
aria-label="Augmenter la quantite de ${item.libelle}"
type="button"
>+</button>
</div>
<span class="cart-line__total">${formatPrice(lineTotalCents)}</span>
<button
class="cart-line__remove"
data-index="${index}"
aria-label="Supprimer ${item.libelle} du panier"
type="button"
>
<img src="assets/images/ui/trash.png" alt="" aria-hidden="true" width="24" height="24">
</button>
`;
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);

View file

@ -0,0 +1,99 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="robots" content="noindex, nofollow">
<meta name="description" content="Wakdo - Votre panier">
<title>Wakdo - Panier</title>
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body class="cart-page">
<!--
cart.html — Shopping cart.
page-cart.js renders all cart lines, handles qty controls and removal.
TVA: 10% (restauration France 2024 — simplified rate).
TODO: verify exact rate with accountant in P3 — actual rate depends
on sur-place vs a-emporter and product type (alcohol, etc.).
The stored prices are TTC. HT is back-calculated at display time only.
-->
<header class="site-header">
<a
class="site-header__back"
href="categories.html"
aria-label="Continuer mes achats"
>
&#8592; Continuer
</a>
<img
class="site-header__logo"
src="assets/images/ui/logo.png"
alt="Wakdo"
>
<span class="mode-badge site-header__mode" data-mode-badge aria-label="Mode de consommation">Sur place</span>
</header>
<main class="cart-main" aria-label="Votre panier">
<h1 class="cart-main__heading">Votre panier</h1>
<!-- Empty cart state -->
<div id="cart-empty" class="cart-empty" hidden>
<p class="cart-empty__message">Votre panier est vide.</p>
<a class="btn btn--secondary" href="categories.html">Decouvrir nos produits</a>
</div>
<!-- Cart lines -->
<ul id="cart-list" class="cart-list" aria-label="Lignes du panier">
<!-- Filled by page-cart.js -->
</ul>
<!-- Order summary -->
<aside id="cart-summary" class="cart-summary" hidden aria-label="Recapitulatif de commande">
<div class="cart-summary__line">
<span>Total HT</span>
<span id="total-ht"></span>
</div>
<div class="cart-summary__line">
<!-- TVA 10% — taux restauration FR 2024 (simplifie, voir commentaire ci-dessus) -->
<span>TVA (10%)</span>
<span id="total-tva"></span>
</div>
<div class="cart-summary__line cart-summary__line--total">
<span>Total TTC</span>
<strong id="total-ttc"></strong>
</div>
</aside>
<!-- Actions -->
<div class="cart-actions">
<button
id="abandon-btn"
class="btn btn--secondary"
type="button"
aria-label="Abandonner la commande et retourner aux categories"
>
Abandonner
</button>
<a
id="pay-btn"
class="btn btn--primary"
href="payment.html"
role="button"
aria-label="Passer au paiement"
aria-disabled="true"
>
Valider ma commande
</a>
</div>
</main>
<script type="module" src="assets/js/nav.js"></script>
<script type="module" src="assets/js/page-cart.js"></script>
</body>
</html>