Warenkorb: Versandkosten werden jetzt sofort berechnet und angezeigt (Länderauswahl direkt im Warenkorb, reagiert live auf Wechsel), statt erst im Checkout

This commit is contained in:
qcigano
2026-08-02 18:19:56 +02:00
parent 559cdfc727
commit 4a12eab876
5 changed files with 88 additions and 20 deletions
+19 -5
View File
@@ -26,6 +26,15 @@ const t = useTranslations(lang);
<aside class="card cart-summary"> <aside class="card cart-summary">
<h3>🧾 {t.cart.subtotal}</h3> <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.subtotal}</span><span id="sum-subtotal">0,00 €</span></div>
<div class="ship-country-row">
<label for="cart-land">📦 {t.checkout.country}</label>
<select id="cart-land">
<option value="ch" selected>Schwiz</option>
<option value="de">Dütschland</option>
<option value="at">Öschterrych</option>
<option value="lu">Luxemburg</option>
</select>
</div>
<div class="summary-row"><span>{t.cart.shipping}</span><span id="sum-shipping">{t.cart.shippingCalculated}</span></div> <div class="summary-row"><span>{t.cart.shipping}</span><span id="sum-shipping">{t.cart.shippingCalculated}</span></div>
<p class="free-shipping-badge" id="shipping-hint"></p> <p class="free-shipping-badge" id="shipping-hint"></p>
<hr class="divider" /> <hr class="divider" />
@@ -38,16 +47,18 @@ const t = useTranslations(lang);
</section> </section>
</Layout> </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 }}> <script define:vars={{ freeShipFrom: 75, removeLabel: t.cart.remove, perItemLabel: t.cart.perItem, remainingTemplate: t.cart.remaining("__V__"), freeShippingText: t.cart.freeShipping, freeLabel: t.checkout.free, cartLang: lang }}>
// WICHTIG: define:vars-Skripte laufen als IIFE, keine "import"-Anweisungen möglich. // WICHTIG: define:vars-Skripte laufen als IIFE, keine "import"-Anweisungen möglich.
window.__cartVars = { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, cartLang }; window.__cartVars = { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, freeLabel, cartLang };
</script> </script>
<script type="module"> <script type="module">
import { getCart, updateQuantity, removeFromCart, cartTotal } from "../../../scripts/cart"; import { getCart, updateQuantity, removeFromCart, cartTotal } from "../../../scripts/cart";
import { formatPrice } from "../../../i18n/format"; import { formatPrice } from "../../../i18n/format";
import { getProduct } from "../../../data/products"; import { getProduct, products } from "../../../data/products";
import { berechneVersandkosten } from "../../../data/versand";
const { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, cartLang } = window.__cartVars; const { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, freeLabel, cartLang } = window.__cartVars;
const landSelect = document.getElementById("cart-land");
function render() { function render() {
const cart = getCart(); const cart = getCart();
@@ -89,8 +100,10 @@ const t = useTranslations(lang);
const subtotal = cartTotal(); const subtotal = cartTotal();
const remaining = Math.max(0, freeShipFrom - subtotal); const remaining = Math.max(0, freeShipFrom - subtotal);
const shipping = berechneVersandkosten(landSelect.value, cart.map((i) => ({ slug: i.slug, preis: i.preis, menge: i.menge })), products);
document.getElementById("sum-subtotal").textContent = formatPrice(subtotal, cartLang); document.getElementById("sum-subtotal").textContent = formatPrice(subtotal, cartLang);
document.getElementById("sum-total").textContent = formatPrice(subtotal, cartLang); document.getElementById("sum-shipping").textContent = shipping === 0 ? freeLabel : formatPrice(shipping, cartLang);
document.getElementById("sum-total").textContent = formatPrice(subtotal + shipping, cartLang);
document.getElementById("shipping-hint").textContent = document.getElementById("shipping-hint").textContent =
remaining > 0 ? remainingTemplate.replace("__V__", formatPrice(remaining, cartLang)) : `🩵 ${freeShippingText}`; remaining > 0 ? remainingTemplate.replace("__V__", formatPrice(remaining, cartLang)) : `🩵 ${freeShippingText}`;
@@ -112,4 +125,5 @@ const t = useTranslations(lang);
render(); render();
window.addEventListener("cart:changed", render); window.addEventListener("cart:changed", render);
landSelect.addEventListener("change", render);
</script> </script>
+19 -5
View File
@@ -26,6 +26,15 @@ const t = useTranslations(lang);
<aside class="card cart-summary"> <aside class="card cart-summary">
<h3>🧾 {t.cart.subtotal}</h3> <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.subtotal}</span><span id="sum-subtotal">0,00 €</span></div>
<div class="ship-country-row">
<label for="cart-land">📦 {t.checkout.country}</label>
<select id="cart-land">
<option value="de">Germany</option>
<option value="at">Austria</option>
<option value="ch">Switzerland</option>
<option value="lu">Luxembourg</option>
</select>
</div>
<div class="summary-row"><span>{t.cart.shipping}</span><span id="sum-shipping">{t.cart.shippingCalculated}</span></div> <div class="summary-row"><span>{t.cart.shipping}</span><span id="sum-shipping">{t.cart.shippingCalculated}</span></div>
<p class="free-shipping-badge" id="shipping-hint"></p> <p class="free-shipping-badge" id="shipping-hint"></p>
<hr class="divider" /> <hr class="divider" />
@@ -38,16 +47,18 @@ const t = useTranslations(lang);
</section> </section>
</Layout> </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 }}> <script define:vars={{ freeShipFrom: 75, removeLabel: t.cart.remove, perItemLabel: t.cart.perItem, remainingTemplate: t.cart.remaining("__V__"), freeShippingText: t.cart.freeShipping, freeLabel: t.checkout.free, cartLang: lang }}>
// WICHTIG: define:vars-Skripte laufen als IIFE, keine "import"-Anweisungen möglich. // WICHTIG: define:vars-Skripte laufen als IIFE, keine "import"-Anweisungen möglich.
window.__cartVars = { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, cartLang }; window.__cartVars = { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, freeLabel, cartLang };
</script> </script>
<script type="module"> <script type="module">
import { getCart, updateQuantity, removeFromCart, cartTotal } from "../../../scripts/cart"; import { getCart, updateQuantity, removeFromCart, cartTotal } from "../../../scripts/cart";
import { formatPrice } from "../../../i18n/format"; import { formatPrice } from "../../../i18n/format";
import { getProduct } from "../../../data/products"; import { getProduct, products } from "../../../data/products";
import { berechneVersandkosten } from "../../../data/versand";
const { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, cartLang } = window.__cartVars; const { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, freeLabel, cartLang } = window.__cartVars;
const landSelect = document.getElementById("cart-land");
function render() { function render() {
const cart = getCart(); const cart = getCart();
@@ -89,8 +100,10 @@ const t = useTranslations(lang);
const subtotal = cartTotal(); const subtotal = cartTotal();
const remaining = Math.max(0, freeShipFrom - subtotal); const remaining = Math.max(0, freeShipFrom - subtotal);
const shipping = berechneVersandkosten(landSelect.value, cart.map((i) => ({ slug: i.slug, preis: i.preis, menge: i.menge })), products);
document.getElementById("sum-subtotal").textContent = formatPrice(subtotal, cartLang); document.getElementById("sum-subtotal").textContent = formatPrice(subtotal, cartLang);
document.getElementById("sum-total").textContent = formatPrice(subtotal, cartLang); document.getElementById("sum-shipping").textContent = shipping === 0 ? freeLabel : formatPrice(shipping, cartLang);
document.getElementById("sum-total").textContent = formatPrice(subtotal + shipping, cartLang);
document.getElementById("shipping-hint").textContent = document.getElementById("shipping-hint").textContent =
remaining > 0 ? remainingTemplate.replace("__V__", formatPrice(remaining, cartLang)) : `🩵 ${freeShippingText}`; remaining > 0 ? remainingTemplate.replace("__V__", formatPrice(remaining, cartLang)) : `🩵 ${freeShippingText}`;
@@ -112,4 +125,5 @@ const t = useTranslations(lang);
render(); render();
window.addEventListener("cart:changed", render); window.addEventListener("cart:changed", render);
landSelect.addEventListener("change", render);
</script> </script>
+19 -5
View File
@@ -26,6 +26,15 @@ const t = useTranslations(lang);
<aside class="card cart-summary"> <aside class="card cart-summary">
<h3>🧾 {t.cart.subtotal}</h3> <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.subtotal}</span><span id="sum-subtotal">0,00 €</span></div>
<div class="ship-country-row">
<label for="cart-land">📦 {t.checkout.country}</label>
<select id="cart-land">
<option value="de">Allemagne</option>
<option value="at">Autriche</option>
<option value="ch">Suisse</option>
<option value="lu">Luxembourg</option>
</select>
</div>
<div class="summary-row"><span>{t.cart.shipping}</span><span id="sum-shipping">{t.cart.shippingCalculated}</span></div> <div class="summary-row"><span>{t.cart.shipping}</span><span id="sum-shipping">{t.cart.shippingCalculated}</span></div>
<p class="free-shipping-badge" id="shipping-hint"></p> <p class="free-shipping-badge" id="shipping-hint"></p>
<hr class="divider" /> <hr class="divider" />
@@ -38,16 +47,18 @@ const t = useTranslations(lang);
</section> </section>
</Layout> </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 }}> <script define:vars={{ freeShipFrom: 75, removeLabel: t.cart.remove, perItemLabel: t.cart.perItem, remainingTemplate: t.cart.remaining("__V__"), freeShippingText: t.cart.freeShipping, freeLabel: t.checkout.free, cartLang: lang }}>
// WICHTIG: define:vars-Skripte laufen als IIFE, keine "import"-Anweisungen möglich. // WICHTIG: define:vars-Skripte laufen als IIFE, keine "import"-Anweisungen möglich.
window.__cartVars = { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, cartLang }; window.__cartVars = { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, freeLabel, cartLang };
</script> </script>
<script type="module"> <script type="module">
import { getCart, updateQuantity, removeFromCart, cartTotal } from "../../../scripts/cart"; import { getCart, updateQuantity, removeFromCart, cartTotal } from "../../../scripts/cart";
import { formatPrice } from "../../../i18n/format"; import { formatPrice } from "../../../i18n/format";
import { getProduct } from "../../../data/products"; import { getProduct, products } from "../../../data/products";
import { berechneVersandkosten } from "../../../data/versand";
const { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, cartLang } = window.__cartVars; const { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, freeLabel, cartLang } = window.__cartVars;
const landSelect = document.getElementById("cart-land");
function render() { function render() {
const cart = getCart(); const cart = getCart();
@@ -89,8 +100,10 @@ const t = useTranslations(lang);
const subtotal = cartTotal(); const subtotal = cartTotal();
const remaining = Math.max(0, freeShipFrom - subtotal); const remaining = Math.max(0, freeShipFrom - subtotal);
const shipping = berechneVersandkosten(landSelect.value, cart.map((i) => ({ slug: i.slug, preis: i.preis, menge: i.menge })), products);
document.getElementById("sum-subtotal").textContent = formatPrice(subtotal, cartLang); document.getElementById("sum-subtotal").textContent = formatPrice(subtotal, cartLang);
document.getElementById("sum-total").textContent = formatPrice(subtotal, cartLang); document.getElementById("sum-shipping").textContent = shipping === 0 ? freeLabel : formatPrice(shipping, cartLang);
document.getElementById("sum-total").textContent = formatPrice(subtotal + shipping, cartLang);
document.getElementById("shipping-hint").textContent = document.getElementById("shipping-hint").textContent =
remaining > 0 ? remainingTemplate.replace("__V__", formatPrice(remaining, cartLang)) : `🩵 ${freeShippingText}`; remaining > 0 ? remainingTemplate.replace("__V__", formatPrice(remaining, cartLang)) : `🩵 ${freeShippingText}`;
@@ -112,4 +125,5 @@ const t = useTranslations(lang);
render(); render();
window.addEventListener("cart:changed", render); window.addEventListener("cart:changed", render);
landSelect.addEventListener("change", render);
</script> </script>
+19 -5
View File
@@ -26,6 +26,15 @@ const t = useTranslations(lang);
<aside class="card cart-summary"> <aside class="card cart-summary">
<h3>🧾 {t.cart.subtotal}</h3> <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.subtotal}</span><span id="sum-subtotal">0,00 €</span></div>
<div class="ship-country-row">
<label for="cart-land">📦 {t.checkout.country}</label>
<select id="cart-land">
<option value="de">Deutschland</option>
<option value="at">Österreich</option>
<option value="ch">Schweiz</option>
<option value="lu">Luxemburg</option>
</select>
</div>
<div class="summary-row"><span>{t.cart.shipping}</span><span id="sum-shipping">{t.cart.shippingCalculated}</span></div> <div class="summary-row"><span>{t.cart.shipping}</span><span id="sum-shipping">{t.cart.shippingCalculated}</span></div>
<p class="free-shipping-badge" id="shipping-hint"></p> <p class="free-shipping-badge" id="shipping-hint"></p>
<hr class="divider" /> <hr class="divider" />
@@ -38,16 +47,18 @@ const t = useTranslations(lang);
</section> </section>
</Layout> </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 }}> <script define:vars={{ freeShipFrom: 75, removeLabel: t.cart.remove, perItemLabel: t.cart.perItem, remainingTemplate: t.cart.remaining("__V__"), freeShippingText: t.cart.freeShipping, freeLabel: t.checkout.free, cartLang: lang }}>
// WICHTIG: define:vars-Skripte laufen als IIFE, keine "import"-Anweisungen möglich. // WICHTIG: define:vars-Skripte laufen als IIFE, keine "import"-Anweisungen möglich.
window.__cartVars = { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, cartLang }; window.__cartVars = { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, freeLabel, cartLang };
</script> </script>
<script type="module"> <script type="module">
import { getCart, updateQuantity, removeFromCart, cartTotal } from "../../scripts/cart"; import { getCart, updateQuantity, removeFromCart, cartTotal } from "../../scripts/cart";
import { formatPrice } from "../../i18n/format"; import { formatPrice } from "../../i18n/format";
import { getProduct } from "../../data/products"; import { getProduct, products } from "../../data/products";
import { berechneVersandkosten } from "../../data/versand";
const { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, cartLang } = window.__cartVars; const { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, freeLabel, cartLang } = window.__cartVars;
const landSelect = document.getElementById("cart-land");
function render() { function render() {
const cart = getCart(); const cart = getCart();
@@ -89,8 +100,10 @@ const t = useTranslations(lang);
const subtotal = cartTotal(); const subtotal = cartTotal();
const remaining = Math.max(0, freeShipFrom - subtotal); const remaining = Math.max(0, freeShipFrom - subtotal);
const shipping = berechneVersandkosten(landSelect.value, cart.map((i) => ({ slug: i.slug, preis: i.preis, menge: i.menge })), products);
document.getElementById("sum-subtotal").textContent = formatPrice(subtotal, cartLang); document.getElementById("sum-subtotal").textContent = formatPrice(subtotal, cartLang);
document.getElementById("sum-total").textContent = formatPrice(subtotal, cartLang); document.getElementById("sum-shipping").textContent = shipping === 0 ? freeLabel : formatPrice(shipping, cartLang);
document.getElementById("sum-total").textContent = formatPrice(subtotal + shipping, cartLang);
document.getElementById("shipping-hint").textContent = document.getElementById("shipping-hint").textContent =
remaining > 0 ? remainingTemplate.replace("__V__", formatPrice(remaining, cartLang)) : `🩵 ${freeShippingText}`; remaining > 0 ? remainingTemplate.replace("__V__", formatPrice(remaining, cartLang)) : `🩵 ${freeShippingText}`;
@@ -112,4 +125,5 @@ const t = useTranslations(lang);
render(); render();
window.addEventListener("cart:changed", render); window.addEventListener("cart:changed", render);
landSelect.addEventListener("change", render);
</script> </script>
+12
View File
@@ -826,6 +826,18 @@ a:focus-visible, button:focus-visible {
.summary-row { display: flex; justify-content: space-between; margin: 0.55rem 0; font-size: 0.92rem; color: var(--c-text-muted); } .summary-row { display: flex; justify-content: space-between; margin: 0.55rem 0; font-size: 0.92rem; color: var(--c-text-muted); }
.summary-row.total { color: var(--c-text); font-weight: 700; font-size: 1.2rem; margin-top: 0.3rem; } .summary-row.total { color: var(--c-text); font-weight: 700; font-size: 1.2rem; margin-top: 0.3rem; }
.cart-summary h3 { display: flex; align-items: center; gap: 0.5em; } .cart-summary h3 { display: flex; align-items: center; gap: 0.5em; }
.ship-country-row { display: flex; align-items: center; justify-content: space-between; gap: 0.6em; margin: 0.6rem 0; font-size: 0.85rem; color: var(--c-text-muted); }
.ship-country-row label { display: flex; align-items: center; gap: 0.4em; white-space: nowrap; }
.ship-country-row select {
background: var(--c-anthracite);
color: var(--c-text);
border: 1px solid rgba(244, 241, 247, 0.16);
border-radius: var(--radius-s);
padding: 0.35em 0.6em;
font-size: 0.85rem;
cursor: pointer;
}
.ship-country-row select:focus { outline: none; border-color: var(--c-accent); }
.free-shipping-badge { .free-shipping-badge {
display: flex; display: flex;
align-items: center; align-items: center;