Warenkorb: Artikel einzeln zum Kauf auswählen, andere bleiben unverbindlich liegen

Jeder Artikel im Warenkorb hat jetzt eine Checkbox "Jetzt kaufen". Nur
angehakte Artikel zählen zur Zwischensumme, zum Versand, zum Gutschein
und zum Checkout — abgewählte Artikel bleiben sichtbar (leicht
abgedunkelt, mit "Bleibt im Warenkorb, wird noch nicht gekauft"-Hinweis)
im Warenkorb liegen, ohne mitgekauft zu werden. Die Auswahl wird pro
Artikel gespeichert (localStorage) und bleibt bis zur nächsten Änderung
bestehen. Der Checkout übernimmt automatisch nur die ausgewählten
Artikel.

Verifiziert per Puppeteer: Abwählen eines Artikels reduziert Summe/
Rechnungsliste sofort korrekt, Checkout zeigt nur den ausgewählten
Artikel, keine Konsolenfehler.
This commit is contained in:
qcigano
2026-08-02 20:21:30 +02:00
parent 993c4f9265
commit 677104d4a6
11 changed files with 168 additions and 41 deletions
+4 -2
View File
@@ -147,7 +147,7 @@ const t = useTranslations(lang);
window.__checkoutVars = { freeLabel, totalLabel, shippingLabel, emptyCartAlert, thankYouPath, checkoutLang, discountLabel, couponInvalid, couponAppliedTemplate, couponRemoveLabel, klarnaConnectLabel, klarnaConnectingLabel, klarnaConnectedLabel, bankHolderLabel, bankIbanLabel, bankBicLabel, bankBankNameLabel, bankPlaceholderLabel };
</script>
<script type="module">
import { getCart, cartTotal, appliedCoupon, couponDiscount, setCouponCode, clearCoupon } from "../../../scripts/cart";
import { getCart, cartTotal, appliedCoupon, couponDiscount, setCouponCode, clearCoupon, istAusgewaehlt } from "../../../scripts/cart";
import { formatPrice } from "../../../i18n/format";
import { products } from "../../../data/products";
import { berechneVersandkosten } from "../../../data/versand";
@@ -158,7 +158,9 @@ import { bankverbindung, bankverbindungVollstaendig } from "../../../data/bankve
const summary = document.getElementById("checkout-summary");
const landSelect = document.getElementById("land");
const cart = getCart();
// Nur die im Warenkorb ausgewählten Artikel werden hier bestellt — abgewählte bleiben im
// Warenkorb liegen und tauchen im Checkout gar nicht erst auf.
const cart = getCart().filter(istAusgewaehlt);
const subtotal = cartTotal();
let currentTotal = 0;
+22 -7
View File
@@ -57,18 +57,18 @@ const t = useTranslations(lang);
</section>
</Layout>
<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, mengenrabattTemplate: t.cart.mengenrabatt("__P__"), couponInvalid: t.cart.couponInvalid, couponAppliedTemplate: t.cart.couponApplied("__C__"), couponRemoveLabel: t.cart.couponRemove }}>
<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, mengenrabattTemplate: t.cart.mengenrabatt("__P__"), couponInvalid: t.cart.couponInvalid, couponAppliedTemplate: t.cart.couponApplied("__C__"), couponRemoveLabel: t.cart.couponRemove, buyNowLabel: t.cart.buyNow, keptInCartLabel: t.cart.keptInCart }}>
// WICHTIG: define:vars-Skripte laufen als IIFE, keine "import"-Anweisungen möglich.
window.__cartVars = { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, freeLabel, cartLang, mengenrabattTemplate, couponInvalid, couponAppliedTemplate, couponRemoveLabel };
window.__cartVars = { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, freeLabel, cartLang, mengenrabattTemplate, couponInvalid, couponAppliedTemplate, couponRemoveLabel, buyNowLabel, keptInCartLabel };
</script>
<script type="module">
import { getCart, updateQuantity, removeFromCart, cartTotal, appliedCoupon, couponDiscount, setCouponCode, clearCoupon } from "../../../scripts/cart";
import { getCart, updateQuantity, removeFromCart, cartTotal, appliedCoupon, couponDiscount, setCouponCode, clearCoupon, istAusgewaehlt, setAusgewaehlt } from "../../../scripts/cart";
import { formatPrice } from "../../../i18n/format";
import { getProduct, products } from "../../../data/products";
import { berechneVersandkosten } from "../../../data/versand";
import { effektiverZeilenpreis, mengenrabattProzent } from "../../../data/rabatt";
const { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, freeLabel, cartLang, mengenrabattTemplate, couponInvalid, couponAppliedTemplate, couponRemoveLabel } = window.__cartVars;
const { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, freeLabel, cartLang, mengenrabattTemplate, couponInvalid, couponAppliedTemplate, couponRemoveLabel, buyNowLabel, keptInCartLabel } = window.__cartVars;
const landSelect = document.getElementById("cart-land");
function render() {
@@ -98,13 +98,17 @@ import { effektiverZeilenpreis, mengenrabattProzent } from "../../../data/rabatt
? `<img class="thumb product-photo" src="${foto}" alt="${i.name}" loading="lazy" />`
: `<div class="img-placeholder thumb" role="img" aria-label="${i.name}"></div>`;
const mengenBadge = mengenProzent > 0 ? `<div class="small mengenrabatt-badge">${mengenrabattTemplate.replace("__P__", mengenProzent)}</div>` : "";
const ausgewaehlt = istAusgewaehlt(i);
const keptBadge = !ausgewaehlt ? `<div class="small kept-in-cart-badge">🕓 ${keptInCartLabel}</div>` : "";
return `
<div class="cart-item">
<div class="cart-item ${ausgewaehlt ? "" : "deselected"}">
<input type="checkbox" class="buy-toggle" data-slug="${i.slug}" ${ausgewaehlt ? "checked" : ""} aria-label="${buyNowLabel}" title="${buyNowLabel}" />
${thumb}
<div class="info">
<strong>${i.name}</strong>
<div class="small">${formatPrice(zeilenpreis / i.menge, cartLang)} ${perItemLabel}</div>
${mengenBadge}
${keptBadge}
</div>
<div class="qty-controls">
<button data-action="dec" data-slug="${i.slug}" aria-label=""></button>
@@ -117,15 +121,19 @@ import { effektiverZeilenpreis, mengenrabattProzent } from "../../../data/rabatt
`;
}).join("");
// Nur ausgewählte Artikel fließen in Rechnung/Zwischensumme/Versand/Checkout ein — abgewählte
// bleiben sichtbar im Warenkorb liegen (leicht abgedunkelt), ohne mitgekauft zu werden.
const ausgewaehlteZeilen = zeilen.filter(({ item: i }) => istAusgewaehlt(i));
// Rechnungs-Übersicht in der Zusammenfassung — dieselbe Positionsliste wie im Warenkorb
// links, nur kompakt als "Beleg" (Menge × Name → Zeilenpreis), passend zum Checkout.
document.getElementById("summary-items").innerHTML = zeilen.map(({ item: i, zeilenpreis }) =>
document.getElementById("summary-items").innerHTML = ausgewaehlteZeilen.map(({ item: i, zeilenpreis }) =>
`<div class="row"><span>${i.menge}× ${i.name}</span><span>${formatPrice(zeilenpreis, cartLang)}</span></div>`
).join("");
const subtotal = cartTotal();
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);
const shipping = berechneVersandkosten(landSelect.value, ausgewaehlteZeilen.map(({ item: i }) => ({ slug: i.slug, preis: i.preis, menge: i.menge })), products);
const coupon = appliedCoupon();
const discount = couponDiscount(subtotal);
const total = Math.max(0, subtotal - discount) + shipping;
@@ -164,6 +172,13 @@ import { effektiverZeilenpreis, mengenrabattProzent } from "../../../data/rabatt
render();
});
});
itemsEl.querySelectorAll(".buy-toggle").forEach((el) => {
el.addEventListener("change", (e) => {
setAusgewaehlt(e.currentTarget.dataset.slug, e.currentTarget.checked);
render();
});
});
}
document.getElementById("coupon-apply").addEventListener("click", () => {
+4 -2
View File
@@ -150,7 +150,7 @@ const t = useTranslations(lang);
window.__checkoutVars = { freeLabel, totalLabel, shippingLabel, emptyCartAlert, thankYouPath, checkoutLang, discountLabel, couponInvalid, couponAppliedTemplate, couponRemoveLabel, klarnaConnectLabel, klarnaConnectingLabel, klarnaConnectedLabel, bankHolderLabel, bankIbanLabel, bankBicLabel, bankBankNameLabel, bankPlaceholderLabel };
</script>
<script type="module">
import { getCart, cartTotal, appliedCoupon, couponDiscount, setCouponCode, clearCoupon } from "../../scripts/cart";
import { getCart, cartTotal, appliedCoupon, couponDiscount, setCouponCode, clearCoupon, istAusgewaehlt } from "../../scripts/cart";
import { formatPrice } from "../../i18n/format";
import { products } from "../../data/products";
import { berechneVersandkosten } from "../../data/versand";
@@ -161,7 +161,9 @@ import { bankverbindung, bankverbindungVollstaendig } from "../../data/bankverbi
const summary = document.getElementById("checkout-summary");
const landSelect = document.getElementById("land");
const cart = getCart();
// Nur die im Warenkorb ausgewählten Artikel werden hier bestellt — abgewählte bleiben im
// Warenkorb liegen und tauchen im Checkout gar nicht erst auf.
const cart = getCart().filter(istAusgewaehlt);
const subtotal = cartTotal();
let currentTotal = 0;
+4 -2
View File
@@ -147,7 +147,7 @@ const t = useTranslations(lang);
window.__checkoutVars = { freeLabel, totalLabel, shippingLabel, emptyCartAlert, thankYouPath, checkoutLang, discountLabel, couponInvalid, couponAppliedTemplate, couponRemoveLabel, klarnaConnectLabel, klarnaConnectingLabel, klarnaConnectedLabel, bankHolderLabel, bankIbanLabel, bankBicLabel, bankBankNameLabel, bankPlaceholderLabel };
</script>
<script type="module">
import { getCart, cartTotal, appliedCoupon, couponDiscount, setCouponCode, clearCoupon } from "../../../scripts/cart";
import { getCart, cartTotal, appliedCoupon, couponDiscount, setCouponCode, clearCoupon, istAusgewaehlt } from "../../../scripts/cart";
import { formatPrice } from "../../../i18n/format";
import { products } from "../../../data/products";
import { berechneVersandkosten } from "../../../data/versand";
@@ -158,7 +158,9 @@ import { bankverbindung, bankverbindungVollstaendig } from "../../../data/bankve
const summary = document.getElementById("checkout-summary");
const landSelect = document.getElementById("land");
const cart = getCart();
// Nur die im Warenkorb ausgewählten Artikel werden hier bestellt — abgewählte bleiben im
// Warenkorb liegen und tauchen im Checkout gar nicht erst auf.
const cart = getCart().filter(istAusgewaehlt);
const subtotal = cartTotal();
let currentTotal = 0;
+22 -7
View File
@@ -57,18 +57,18 @@ const t = useTranslations(lang);
</section>
</Layout>
<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, mengenrabattTemplate: t.cart.mengenrabatt("__P__"), couponInvalid: t.cart.couponInvalid, couponAppliedTemplate: t.cart.couponApplied("__C__"), couponRemoveLabel: t.cart.couponRemove }}>
<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, mengenrabattTemplate: t.cart.mengenrabatt("__P__"), couponInvalid: t.cart.couponInvalid, couponAppliedTemplate: t.cart.couponApplied("__C__"), couponRemoveLabel: t.cart.couponRemove, buyNowLabel: t.cart.buyNow, keptInCartLabel: t.cart.keptInCart }}>
// WICHTIG: define:vars-Skripte laufen als IIFE, keine "import"-Anweisungen möglich.
window.__cartVars = { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, freeLabel, cartLang, mengenrabattTemplate, couponInvalid, couponAppliedTemplate, couponRemoveLabel };
window.__cartVars = { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, freeLabel, cartLang, mengenrabattTemplate, couponInvalid, couponAppliedTemplate, couponRemoveLabel, buyNowLabel, keptInCartLabel };
</script>
<script type="module">
import { getCart, updateQuantity, removeFromCart, cartTotal, appliedCoupon, couponDiscount, setCouponCode, clearCoupon } from "../../../scripts/cart";
import { getCart, updateQuantity, removeFromCart, cartTotal, appliedCoupon, couponDiscount, setCouponCode, clearCoupon, istAusgewaehlt, setAusgewaehlt } from "../../../scripts/cart";
import { formatPrice } from "../../../i18n/format";
import { getProduct, products } from "../../../data/products";
import { berechneVersandkosten } from "../../../data/versand";
import { effektiverZeilenpreis, mengenrabattProzent } from "../../../data/rabatt";
const { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, freeLabel, cartLang, mengenrabattTemplate, couponInvalid, couponAppliedTemplate, couponRemoveLabel } = window.__cartVars;
const { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, freeLabel, cartLang, mengenrabattTemplate, couponInvalid, couponAppliedTemplate, couponRemoveLabel, buyNowLabel, keptInCartLabel } = window.__cartVars;
const landSelect = document.getElementById("cart-land");
function render() {
@@ -98,13 +98,17 @@ import { effektiverZeilenpreis, mengenrabattProzent } from "../../../data/rabatt
? `<img class="thumb product-photo" src="${foto}" alt="${i.name}" loading="lazy" />`
: `<div class="img-placeholder thumb" role="img" aria-label="${i.name}"></div>`;
const mengenBadge = mengenProzent > 0 ? `<div class="small mengenrabatt-badge">${mengenrabattTemplate.replace("__P__", mengenProzent)}</div>` : "";
const ausgewaehlt = istAusgewaehlt(i);
const keptBadge = !ausgewaehlt ? `<div class="small kept-in-cart-badge">🕓 ${keptInCartLabel}</div>` : "";
return `
<div class="cart-item">
<div class="cart-item ${ausgewaehlt ? "" : "deselected"}">
<input type="checkbox" class="buy-toggle" data-slug="${i.slug}" ${ausgewaehlt ? "checked" : ""} aria-label="${buyNowLabel}" title="${buyNowLabel}" />
${thumb}
<div class="info">
<strong>${i.name}</strong>
<div class="small">${formatPrice(zeilenpreis / i.menge, cartLang)} ${perItemLabel}</div>
${mengenBadge}
${keptBadge}
</div>
<div class="qty-controls">
<button data-action="dec" data-slug="${i.slug}" aria-label=""></button>
@@ -117,15 +121,19 @@ import { effektiverZeilenpreis, mengenrabattProzent } from "../../../data/rabatt
`;
}).join("");
// Nur ausgewählte Artikel fließen in Rechnung/Zwischensumme/Versand/Checkout ein — abgewählte
// bleiben sichtbar im Warenkorb liegen (leicht abgedunkelt), ohne mitgekauft zu werden.
const ausgewaehlteZeilen = zeilen.filter(({ item: i }) => istAusgewaehlt(i));
// Rechnungs-Übersicht in der Zusammenfassung — dieselbe Positionsliste wie im Warenkorb
// links, nur kompakt als "Beleg" (Menge × Name → Zeilenpreis), passend zum Checkout.
document.getElementById("summary-items").innerHTML = zeilen.map(({ item: i, zeilenpreis }) =>
document.getElementById("summary-items").innerHTML = ausgewaehlteZeilen.map(({ item: i, zeilenpreis }) =>
`<div class="row"><span>${i.menge}× ${i.name}</span><span>${formatPrice(zeilenpreis, cartLang)}</span></div>`
).join("");
const subtotal = cartTotal();
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);
const shipping = berechneVersandkosten(landSelect.value, ausgewaehlteZeilen.map(({ item: i }) => ({ slug: i.slug, preis: i.preis, menge: i.menge })), products);
const coupon = appliedCoupon();
const discount = couponDiscount(subtotal);
const total = Math.max(0, subtotal - discount) + shipping;
@@ -164,6 +172,13 @@ import { effektiverZeilenpreis, mengenrabattProzent } from "../../../data/rabatt
render();
});
});
itemsEl.querySelectorAll(".buy-toggle").forEach((el) => {
el.addEventListener("change", (e) => {
setAusgewaehlt(e.currentTarget.dataset.slug, e.currentTarget.checked);
render();
});
});
}
document.getElementById("coupon-apply").addEventListener("click", () => {
+4 -2
View File
@@ -147,7 +147,7 @@ const t = useTranslations(lang);
window.__checkoutVars = { freeLabel, totalLabel, shippingLabel, emptyCartAlert, thankYouPath, checkoutLang, discountLabel, couponInvalid, couponAppliedTemplate, couponRemoveLabel, klarnaConnectLabel, klarnaConnectingLabel, klarnaConnectedLabel, bankHolderLabel, bankIbanLabel, bankBicLabel, bankBankNameLabel, bankPlaceholderLabel };
</script>
<script type="module">
import { getCart, cartTotal, appliedCoupon, couponDiscount, setCouponCode, clearCoupon } from "../../../scripts/cart";
import { getCart, cartTotal, appliedCoupon, couponDiscount, setCouponCode, clearCoupon, istAusgewaehlt } from "../../../scripts/cart";
import { formatPrice } from "../../../i18n/format";
import { products } from "../../../data/products";
import { berechneVersandkosten } from "../../../data/versand";
@@ -158,7 +158,9 @@ import { bankverbindung, bankverbindungVollstaendig } from "../../../data/bankve
const summary = document.getElementById("checkout-summary");
const landSelect = document.getElementById("land");
const cart = getCart();
// Nur die im Warenkorb ausgewählten Artikel werden hier bestellt — abgewählte bleiben im
// Warenkorb liegen und tauchen im Checkout gar nicht erst auf.
const cart = getCart().filter(istAusgewaehlt);
const subtotal = cartTotal();
let currentTotal = 0;
+22 -7
View File
@@ -57,18 +57,18 @@ const t = useTranslations(lang);
</section>
</Layout>
<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, mengenrabattTemplate: t.cart.mengenrabatt("__P__"), couponInvalid: t.cart.couponInvalid, couponAppliedTemplate: t.cart.couponApplied("__C__"), couponRemoveLabel: t.cart.couponRemove }}>
<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, mengenrabattTemplate: t.cart.mengenrabatt("__P__"), couponInvalid: t.cart.couponInvalid, couponAppliedTemplate: t.cart.couponApplied("__C__"), couponRemoveLabel: t.cart.couponRemove, buyNowLabel: t.cart.buyNow, keptInCartLabel: t.cart.keptInCart }}>
// WICHTIG: define:vars-Skripte laufen als IIFE, keine "import"-Anweisungen möglich.
window.__cartVars = { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, freeLabel, cartLang, mengenrabattTemplate, couponInvalid, couponAppliedTemplate, couponRemoveLabel };
window.__cartVars = { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, freeLabel, cartLang, mengenrabattTemplate, couponInvalid, couponAppliedTemplate, couponRemoveLabel, buyNowLabel, keptInCartLabel };
</script>
<script type="module">
import { getCart, updateQuantity, removeFromCart, cartTotal, appliedCoupon, couponDiscount, setCouponCode, clearCoupon } from "../../../scripts/cart";
import { getCart, updateQuantity, removeFromCart, cartTotal, appliedCoupon, couponDiscount, setCouponCode, clearCoupon, istAusgewaehlt, setAusgewaehlt } from "../../../scripts/cart";
import { formatPrice } from "../../../i18n/format";
import { getProduct, products } from "../../../data/products";
import { berechneVersandkosten } from "../../../data/versand";
import { effektiverZeilenpreis, mengenrabattProzent } from "../../../data/rabatt";
const { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, freeLabel, cartLang, mengenrabattTemplate, couponInvalid, couponAppliedTemplate, couponRemoveLabel } = window.__cartVars;
const { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, freeLabel, cartLang, mengenrabattTemplate, couponInvalid, couponAppliedTemplate, couponRemoveLabel, buyNowLabel, keptInCartLabel } = window.__cartVars;
const landSelect = document.getElementById("cart-land");
function render() {
@@ -98,13 +98,17 @@ import { effektiverZeilenpreis, mengenrabattProzent } from "../../../data/rabatt
? `<img class="thumb product-photo" src="${foto}" alt="${i.name}" loading="lazy" />`
: `<div class="img-placeholder thumb" role="img" aria-label="${i.name}"></div>`;
const mengenBadge = mengenProzent > 0 ? `<div class="small mengenrabatt-badge">${mengenrabattTemplate.replace("__P__", mengenProzent)}</div>` : "";
const ausgewaehlt = istAusgewaehlt(i);
const keptBadge = !ausgewaehlt ? `<div class="small kept-in-cart-badge">🕓 ${keptInCartLabel}</div>` : "";
return `
<div class="cart-item">
<div class="cart-item ${ausgewaehlt ? "" : "deselected"}">
<input type="checkbox" class="buy-toggle" data-slug="${i.slug}" ${ausgewaehlt ? "checked" : ""} aria-label="${buyNowLabel}" title="${buyNowLabel}" />
${thumb}
<div class="info">
<strong>${i.name}</strong>
<div class="small">${formatPrice(zeilenpreis / i.menge, cartLang)} ${perItemLabel}</div>
${mengenBadge}
${keptBadge}
</div>
<div class="qty-controls">
<button data-action="dec" data-slug="${i.slug}" aria-label=""></button>
@@ -117,15 +121,19 @@ import { effektiverZeilenpreis, mengenrabattProzent } from "../../../data/rabatt
`;
}).join("");
// Nur ausgewählte Artikel fließen in Rechnung/Zwischensumme/Versand/Checkout ein — abgewählte
// bleiben sichtbar im Warenkorb liegen (leicht abgedunkelt), ohne mitgekauft zu werden.
const ausgewaehlteZeilen = zeilen.filter(({ item: i }) => istAusgewaehlt(i));
// Rechnungs-Übersicht in der Zusammenfassung — dieselbe Positionsliste wie im Warenkorb
// links, nur kompakt als "Beleg" (Menge × Name → Zeilenpreis), passend zum Checkout.
document.getElementById("summary-items").innerHTML = zeilen.map(({ item: i, zeilenpreis }) =>
document.getElementById("summary-items").innerHTML = ausgewaehlteZeilen.map(({ item: i, zeilenpreis }) =>
`<div class="row"><span>${i.menge}× ${i.name}</span><span>${formatPrice(zeilenpreis, cartLang)}</span></div>`
).join("");
const subtotal = cartTotal();
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);
const shipping = berechneVersandkosten(landSelect.value, ausgewaehlteZeilen.map(({ item: i }) => ({ slug: i.slug, preis: i.preis, menge: i.menge })), products);
const coupon = appliedCoupon();
const discount = couponDiscount(subtotal);
const total = Math.max(0, subtotal - discount) + shipping;
@@ -164,6 +172,13 @@ import { effektiverZeilenpreis, mengenrabattProzent } from "../../../data/rabatt
render();
});
});
itemsEl.querySelectorAll(".buy-toggle").forEach((el) => {
el.addEventListener("change", (e) => {
setAusgewaehlt(e.currentTarget.dataset.slug, e.currentTarget.checked);
render();
});
});
}
document.getElementById("coupon-apply").addEventListener("click", () => {
+22 -7
View File
@@ -57,18 +57,18 @@ const t = useTranslations(lang);
</section>
</Layout>
<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, mengenrabattTemplate: t.cart.mengenrabatt("__P__"), couponInvalid: t.cart.couponInvalid, couponAppliedTemplate: t.cart.couponApplied("__C__"), couponRemoveLabel: t.cart.couponRemove }}>
<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, mengenrabattTemplate: t.cart.mengenrabatt("__P__"), couponInvalid: t.cart.couponInvalid, couponAppliedTemplate: t.cart.couponApplied("__C__"), couponRemoveLabel: t.cart.couponRemove, buyNowLabel: t.cart.buyNow, keptInCartLabel: t.cart.keptInCart }}>
// WICHTIG: define:vars-Skripte laufen als IIFE, keine "import"-Anweisungen möglich.
window.__cartVars = { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, freeLabel, cartLang, mengenrabattTemplate, couponInvalid, couponAppliedTemplate, couponRemoveLabel };
window.__cartVars = { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, freeLabel, cartLang, mengenrabattTemplate, couponInvalid, couponAppliedTemplate, couponRemoveLabel, buyNowLabel, keptInCartLabel };
</script>
<script type="module">
import { getCart, updateQuantity, removeFromCart, cartTotal, appliedCoupon, couponDiscount, setCouponCode, clearCoupon } from "../../scripts/cart";
import { getCart, updateQuantity, removeFromCart, cartTotal, appliedCoupon, couponDiscount, setCouponCode, clearCoupon, istAusgewaehlt, setAusgewaehlt } from "../../scripts/cart";
import { formatPrice } from "../../i18n/format";
import { getProduct, products } from "../../data/products";
import { berechneVersandkosten } from "../../data/versand";
import { effektiverZeilenpreis, mengenrabattProzent } from "../../data/rabatt";
const { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, freeLabel, cartLang, mengenrabattTemplate, couponInvalid, couponAppliedTemplate, couponRemoveLabel } = window.__cartVars;
const { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, freeLabel, cartLang, mengenrabattTemplate, couponInvalid, couponAppliedTemplate, couponRemoveLabel, buyNowLabel, keptInCartLabel } = window.__cartVars;
const landSelect = document.getElementById("cart-land");
function render() {
@@ -98,13 +98,17 @@ import { effektiverZeilenpreis, mengenrabattProzent } from "../../data/rabatt";
? `<img class="thumb product-photo" src="${foto}" alt="${i.name}" loading="lazy" />`
: `<div class="img-placeholder thumb" role="img" aria-label="${i.name}"></div>`;
const mengenBadge = mengenProzent > 0 ? `<div class="small mengenrabatt-badge">${mengenrabattTemplate.replace("__P__", mengenProzent)}</div>` : "";
const ausgewaehlt = istAusgewaehlt(i);
const keptBadge = !ausgewaehlt ? `<div class="small kept-in-cart-badge">🕓 ${keptInCartLabel}</div>` : "";
return `
<div class="cart-item">
<div class="cart-item ${ausgewaehlt ? "" : "deselected"}">
<input type="checkbox" class="buy-toggle" data-slug="${i.slug}" ${ausgewaehlt ? "checked" : ""} aria-label="${buyNowLabel}" title="${buyNowLabel}" />
${thumb}
<div class="info">
<strong>${i.name}</strong>
<div class="small">${formatPrice(zeilenpreis / i.menge, cartLang)} ${perItemLabel}</div>
${mengenBadge}
${keptBadge}
</div>
<div class="qty-controls">
<button data-action="dec" data-slug="${i.slug}" aria-label=""></button>
@@ -117,15 +121,19 @@ import { effektiverZeilenpreis, mengenrabattProzent } from "../../data/rabatt";
`;
}).join("");
// Nur ausgewählte Artikel fließen in Rechnung/Zwischensumme/Versand/Checkout ein — abgewählte
// bleiben sichtbar im Warenkorb liegen (leicht abgedunkelt), ohne mitgekauft zu werden.
const ausgewaehlteZeilen = zeilen.filter(({ item: i }) => istAusgewaehlt(i));
// Rechnungs-Übersicht in der Zusammenfassung — dieselbe Positionsliste wie im Warenkorb
// links, nur kompakt als "Beleg" (Menge × Name → Zeilenpreis), passend zum Checkout.
document.getElementById("summary-items").innerHTML = zeilen.map(({ item: i, zeilenpreis }) =>
document.getElementById("summary-items").innerHTML = ausgewaehlteZeilen.map(({ item: i, zeilenpreis }) =>
`<div class="row"><span>${i.menge}× ${i.name}</span><span>${formatPrice(zeilenpreis, cartLang)}</span></div>`
).join("");
const subtotal = cartTotal();
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);
const shipping = berechneVersandkosten(landSelect.value, ausgewaehlteZeilen.map(({ item: i }) => ({ slug: i.slug, preis: i.preis, menge: i.menge })), products);
const coupon = appliedCoupon();
const discount = couponDiscount(subtotal);
const total = Math.max(0, subtotal - discount) + shipping;
@@ -164,6 +172,13 @@ import { effektiverZeilenpreis, mengenrabattProzent } from "../../data/rabatt";
render();
});
});
itemsEl.querySelectorAll(".buy-toggle").forEach((el) => {
el.addEventListener("change", (e) => {
setAusgewaehlt(e.currentTarget.dataset.slug, e.currentTarget.checked);
render();
});
});
}
document.getElementById("coupon-apply").addEventListener("click", () => {