Neu: - Produkte können eigene Versandkosten je Land (DE/AT/CH/LU) bekommen, gelten nur bei Einzelkauf (genau 1 Stück von 1 Produkt) - Versandkosten-Staffel nach Warenwert je Land, von VanVan im Adminbereich bearbeitbar (neuer Bereich "Versandkosten-Staffel"), greift bei mehreren Produkten/Stück - Versandkostenfrei ab 75 € gilt immer, unabhängig von Einzelkauf/Staffel - Checkout berechnet und zeigt jetzt die echten Versandkosten live beim Länderwechsel an Kritischer Bugfix (unabhängig vom neuen Feature entdeckt): - Warenkorb- und Checkout-Skripte haben "import" innerhalb von define:vars-Skripten benutzt, was Astro als IIFE rendert (kein ES-Modul) — das hat in der echten Live-Version die ganze Zeit zu "Cannot use import statement outside a module" geführt, wodurch Warenkorb und Checkout praktisch nie funktioniert haben. Behoben durch Trennung: define:vars-Skript setzt nur Werte auf window, separates <script type="module"> mit den echten Imports liest sie aus. Mit echtem End-to-End-Test verifiziert (Artikel in Warenkorb legen, Land wechseln, Summe prüfen) statt nur Screenshot.
108 lines
4.5 KiB
Plaintext
108 lines
4.5 KiB
Plaintext
---
|
||
import Layout from "../../../layouts/Layout.astro";
|
||
import type { Locale } from "../../../i18n/config";
|
||
import { useTranslations } from "../../../i18n/ui";
|
||
|
||
const lang: Locale = "fr";
|
||
const t = useTranslations(lang);
|
||
---
|
||
<Layout title="Panier" description="Votre panier chez Van's DIY & Bastelbedarf." lang={lang} path="/warenkorb/">
|
||
<section class="section-tight">
|
||
<div class="container">
|
||
<span class="eyebrow">{t.cart.eyebrow}</span>
|
||
<h1>{t.cart.title}</h1>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="section-tight">
|
||
<div class="container">
|
||
<div id="cart-empty" class="card" style="display:none;">
|
||
<p>{t.cart.empty}</p>
|
||
<a class="btn btn-primary" href="/fr/shop/">{t.common.browseShop}</a>
|
||
</div>
|
||
|
||
<div id="cart-content" class="cart-layout" style="display:none;">
|
||
<div class="card cart-items" id="cart-items"></div>
|
||
<aside class="card cart-summary">
|
||
<h3>{t.cart.subtotal}</h3>
|
||
<div class="summary-row"><span>{t.cart.subtotal}</span><span id="sum-subtotal">0,00 €</span></div>
|
||
<div class="summary-row"><span>{t.cart.shipping}</span><span id="sum-shipping">{t.cart.shippingCalculated}</span></div>
|
||
<p class="small" id="shipping-hint"></p>
|
||
<hr class="divider" />
|
||
<div class="summary-row total"><span>{t.cart.total}</span><span id="sum-total">0,00 €</span></div>
|
||
<p class="small">{t.common.vatNote}</p>
|
||
<a class="btn btn-primary btn-block" href="/fr/checkout/">{t.cart.toCheckout}</a>
|
||
</aside>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</Layout>
|
||
|
||
<script define:vars={{ freeShipFrom: 75, removeLabel: t.cart.remove, perItemLabel: t.cart.perItem, remainingTemplate: t.cart.remaining("__V__"), freeShippingText: t.cart.freeShipping, cartLang: lang }}>
|
||
// WICHTIG: define:vars-Skripte laufen als IIFE, keine "import"-Anweisungen möglich.
|
||
window.__cartVars = { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, cartLang };
|
||
</script>
|
||
<script type="module">
|
||
import { getCart, updateQuantity, removeFromCart, cartTotal } from "../../../scripts/cart";
|
||
import { formatPrice } from "../../../i18n/format";
|
||
|
||
const { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, cartLang } = window.__cartVars;
|
||
|
||
function render() {
|
||
const cart = getCart();
|
||
const empty = document.getElementById("cart-empty");
|
||
const content = document.getElementById("cart-content");
|
||
const itemsEl = document.getElementById("cart-items");
|
||
|
||
if (cart.length === 0) {
|
||
empty.style.display = "block";
|
||
content.style.display = "none";
|
||
return;
|
||
}
|
||
empty.style.display = "none";
|
||
content.style.display = "grid";
|
||
|
||
itemsEl.innerHTML = cart.map((i) => `
|
||
<div class="cart-item">
|
||
<div class="img-placeholder thumb"></div>
|
||
<div class="info">
|
||
<strong>${i.name}</strong>
|
||
<div class="small">${formatPrice(i.preis, cartLang)} ${perItemLabel}</div>
|
||
</div>
|
||
<div class="qty-controls">
|
||
<button data-action="dec" data-slug="${i.slug}">−</button>
|
||
<span>${i.menge}</span>
|
||
<button data-action="inc" data-slug="${i.slug}">+</button>
|
||
</div>
|
||
<div><strong>${formatPrice(i.preis * i.menge, cartLang)}</strong></div>
|
||
<a href="#" class="remove" data-action="remove" data-slug="${i.slug}">${removeLabel}</a>
|
||
</div>
|
||
`).join("");
|
||
|
||
const subtotal = cartTotal();
|
||
const remaining = Math.max(0, freeShipFrom - subtotal);
|
||
document.getElementById("sum-subtotal").textContent = formatPrice(subtotal, cartLang);
|
||
document.getElementById("sum-total").textContent = formatPrice(subtotal, cartLang);
|
||
document.getElementById("shipping-hint").textContent =
|
||
remaining > 0 ? remainingTemplate.replace("__V__", formatPrice(remaining, cartLang)) : freeShippingText;
|
||
|
||
itemsEl.querySelectorAll("button, a.remove").forEach((el) => {
|
||
el.addEventListener("click", (e) => {
|
||
e.preventDefault();
|
||
const target = e.currentTarget;
|
||
const slug = target.dataset.slug;
|
||
const action = target.dataset.action;
|
||
const item = getCart().find((i) => i.slug === slug);
|
||
if (!item) return;
|
||
if (action === "inc") updateQuantity(slug, item.menge + 1);
|
||
if (action === "dec") updateQuantity(slug, item.menge - 1);
|
||
if (action === "remove") removeFromCart(slug);
|
||
render();
|
||
});
|
||
});
|
||
}
|
||
|
||
render();
|
||
window.addEventListener("cart:changed", render);
|
||
</script>
|