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:
@@ -152,6 +152,7 @@ export const ui = {
|
|||||||
discount: "Rabatt", mengenrabatt: (p: string) => `Mengenrabatt -${p}%`,
|
discount: "Rabatt", mengenrabatt: (p: string) => `Mengenrabatt -${p}%`,
|
||||||
mengenrabattTier: (ab: string, p: string) => `ab ${ab} Stück -${p}%`,
|
mengenrabattTier: (ab: string, p: string) => `ab ${ab} Stück -${p}%`,
|
||||||
couponLabel: "🎟️ Gutscheincode", couponPlaceholder: "Code eingeben", couponApply: "Einlösen", couponRemove: "Entfernen",
|
couponLabel: "🎟️ Gutscheincode", couponPlaceholder: "Code eingeben", couponApply: "Einlösen", couponRemove: "Entfernen",
|
||||||
|
buyNow: "Jetzt kaufen", keptInCart: "Bleibt im Warenkorb, wird noch nicht gekauft",
|
||||||
couponInvalid: "Code ungültig oder abgelaufen.", couponApplied: (code: string) => `Gutschein „${code}" eingelöst`,
|
couponInvalid: "Code ungültig oder abgelaufen.", couponApplied: (code: string) => `Gutschein „${code}" eingelöst`,
|
||||||
},
|
},
|
||||||
checkout: {
|
checkout: {
|
||||||
@@ -333,6 +334,7 @@ export const ui = {
|
|||||||
discount: "Discount", mengenrabatt: (p: string) => `Bulk discount -${p}%`,
|
discount: "Discount", mengenrabatt: (p: string) => `Bulk discount -${p}%`,
|
||||||
mengenrabattTier: (ab: string, p: string) => `from ${ab} pcs -${p}%`,
|
mengenrabattTier: (ab: string, p: string) => `from ${ab} pcs -${p}%`,
|
||||||
couponLabel: "🎟️ Coupon code", couponPlaceholder: "Enter code", couponApply: "Apply", couponRemove: "Remove",
|
couponLabel: "🎟️ Coupon code", couponPlaceholder: "Enter code", couponApply: "Apply", couponRemove: "Remove",
|
||||||
|
buyNow: "Buy now", keptInCart: "Stays in cart, not purchased yet",
|
||||||
couponInvalid: "Code invalid or expired.", couponApplied: (code: string) => `Coupon "${code}" applied`,
|
couponInvalid: "Code invalid or expired.", couponApplied: (code: string) => `Coupon "${code}" applied`,
|
||||||
},
|
},
|
||||||
checkout: {
|
checkout: {
|
||||||
@@ -514,6 +516,7 @@ export const ui = {
|
|||||||
discount: "Rabatt", mengenrabatt: (p: string) => `Mengerabatt -${p}%`,
|
discount: "Rabatt", mengenrabatt: (p: string) => `Mengerabatt -${p}%`,
|
||||||
mengenrabattTier: (ab: string, p: string) => `ab ${ab} Stuck -${p}%`,
|
mengenrabattTier: (ab: string, p: string) => `ab ${ab} Stuck -${p}%`,
|
||||||
couponLabel: "🎟️ Gutschy-Code", couponPlaceholder: "Code igäh", couponApply: "Ilöse", couponRemove: "Ewägmache",
|
couponLabel: "🎟️ Gutschy-Code", couponPlaceholder: "Code igäh", couponApply: "Ilöse", couponRemove: "Ewägmache",
|
||||||
|
buyNow: "Jetzt chaufe", keptInCart: "Blibt im Warenchorb, wird no nid chauft",
|
||||||
couponInvalid: "Code ungültig oder abgloffe.", couponApplied: (code: string) => `Gutschy „${code}" iglöst`,
|
couponInvalid: "Code ungültig oder abgloffe.", couponApplied: (code: string) => `Gutschy „${code}" iglöst`,
|
||||||
},
|
},
|
||||||
checkout: {
|
checkout: {
|
||||||
@@ -695,6 +698,7 @@ export const ui = {
|
|||||||
discount: "Remise", mengenrabatt: (p: string) => `Remise de quantité -${p}%`,
|
discount: "Remise", mengenrabatt: (p: string) => `Remise de quantité -${p}%`,
|
||||||
mengenrabattTier: (ab: string, p: string) => `dès ${ab} pièces -${p}%`,
|
mengenrabattTier: (ab: string, p: string) => `dès ${ab} pièces -${p}%`,
|
||||||
couponLabel: "🎟️ Code promo", couponPlaceholder: "Entrer le code", couponApply: "Valider", couponRemove: "Retirer",
|
couponLabel: "🎟️ Code promo", couponPlaceholder: "Entrer le code", couponApply: "Valider", couponRemove: "Retirer",
|
||||||
|
buyNow: "Acheter maintenant", keptInCart: "Reste dans le panier, pas encore acheté",
|
||||||
couponInvalid: "Code invalide ou expiré.", couponApplied: (code: string) => `Code promo « ${code} » appliqué`,
|
couponInvalid: "Code invalide ou expiré.", couponApplied: (code: string) => `Code promo « ${code} » appliqué`,
|
||||||
},
|
},
|
||||||
checkout: {
|
checkout: {
|
||||||
|
|||||||
@@ -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 };
|
window.__checkoutVars = { freeLabel, totalLabel, shippingLabel, emptyCartAlert, thankYouPath, checkoutLang, discountLabel, couponInvalid, couponAppliedTemplate, couponRemoveLabel, klarnaConnectLabel, klarnaConnectingLabel, klarnaConnectedLabel, bankHolderLabel, bankIbanLabel, bankBicLabel, bankBankNameLabel, bankPlaceholderLabel };
|
||||||
</script>
|
</script>
|
||||||
<script type="module">
|
<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 { formatPrice } from "../../../i18n/format";
|
||||||
import { products } from "../../../data/products";
|
import { products } from "../../../data/products";
|
||||||
import { berechneVersandkosten } from "../../../data/versand";
|
import { berechneVersandkosten } from "../../../data/versand";
|
||||||
@@ -158,7 +158,9 @@ import { bankverbindung, bankverbindungVollstaendig } from "../../../data/bankve
|
|||||||
|
|
||||||
const summary = document.getElementById("checkout-summary");
|
const summary = document.getElementById("checkout-summary");
|
||||||
const landSelect = document.getElementById("land");
|
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();
|
const subtotal = cartTotal();
|
||||||
let currentTotal = 0;
|
let currentTotal = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -57,18 +57,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, 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.
|
// 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>
|
||||||
<script type="module">
|
<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 { formatPrice } from "../../../i18n/format";
|
||||||
import { getProduct, products } from "../../../data/products";
|
import { getProduct, products } from "../../../data/products";
|
||||||
import { berechneVersandkosten } from "../../../data/versand";
|
import { berechneVersandkosten } from "../../../data/versand";
|
||||||
import { effektiverZeilenpreis, mengenrabattProzent } from "../../../data/rabatt";
|
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");
|
const landSelect = document.getElementById("cart-land");
|
||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
@@ -98,13 +98,17 @@ import { effektiverZeilenpreis, mengenrabattProzent } from "../../../data/rabatt
|
|||||||
? `<img class="thumb product-photo" src="${foto}" alt="${i.name}" loading="lazy" />`
|
? `<img class="thumb product-photo" src="${foto}" alt="${i.name}" loading="lazy" />`
|
||||||
: `<div class="img-placeholder thumb" role="img" aria-label="${i.name}"></div>`;
|
: `<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 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 `
|
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}
|
${thumb}
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<strong>${i.name}</strong>
|
<strong>${i.name}</strong>
|
||||||
<div class="small">${formatPrice(zeilenpreis / i.menge, cartLang)} ${perItemLabel}</div>
|
<div class="small">${formatPrice(zeilenpreis / i.menge, cartLang)} ${perItemLabel}</div>
|
||||||
${mengenBadge}
|
${mengenBadge}
|
||||||
|
${keptBadge}
|
||||||
</div>
|
</div>
|
||||||
<div class="qty-controls">
|
<div class="qty-controls">
|
||||||
<button data-action="dec" data-slug="${i.slug}" aria-label="−">−</button>
|
<button data-action="dec" data-slug="${i.slug}" aria-label="−">−</button>
|
||||||
@@ -117,15 +121,19 @@ import { effektiverZeilenpreis, mengenrabattProzent } from "../../../data/rabatt
|
|||||||
`;
|
`;
|
||||||
}).join("");
|
}).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
|
// Rechnungs-Übersicht in der Zusammenfassung — dieselbe Positionsliste wie im Warenkorb
|
||||||
// links, nur kompakt als "Beleg" (Menge × Name → Zeilenpreis), passend zum Checkout.
|
// 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>`
|
`<div class="row"><span>${i.menge}× ${i.name}</span><span>${formatPrice(zeilenpreis, cartLang)}</span></div>`
|
||||||
).join("");
|
).join("");
|
||||||
|
|
||||||
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);
|
const shipping = berechneVersandkosten(landSelect.value, ausgewaehlteZeilen.map(({ item: i }) => ({ slug: i.slug, preis: i.preis, menge: i.menge })), products);
|
||||||
const coupon = appliedCoupon();
|
const coupon = appliedCoupon();
|
||||||
const discount = couponDiscount(subtotal);
|
const discount = couponDiscount(subtotal);
|
||||||
const total = Math.max(0, subtotal - discount) + shipping;
|
const total = Math.max(0, subtotal - discount) + shipping;
|
||||||
@@ -164,6 +172,13 @@ import { effektiverZeilenpreis, mengenrabattProzent } from "../../../data/rabatt
|
|||||||
render();
|
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", () => {
|
document.getElementById("coupon-apply").addEventListener("click", () => {
|
||||||
|
|||||||
@@ -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 };
|
window.__checkoutVars = { freeLabel, totalLabel, shippingLabel, emptyCartAlert, thankYouPath, checkoutLang, discountLabel, couponInvalid, couponAppliedTemplate, couponRemoveLabel, klarnaConnectLabel, klarnaConnectingLabel, klarnaConnectedLabel, bankHolderLabel, bankIbanLabel, bankBicLabel, bankBankNameLabel, bankPlaceholderLabel };
|
||||||
</script>
|
</script>
|
||||||
<script type="module">
|
<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 { formatPrice } from "../../i18n/format";
|
||||||
import { products } from "../../data/products";
|
import { products } from "../../data/products";
|
||||||
import { berechneVersandkosten } from "../../data/versand";
|
import { berechneVersandkosten } from "../../data/versand";
|
||||||
@@ -161,7 +161,9 @@ import { bankverbindung, bankverbindungVollstaendig } from "../../data/bankverbi
|
|||||||
|
|
||||||
const summary = document.getElementById("checkout-summary");
|
const summary = document.getElementById("checkout-summary");
|
||||||
const landSelect = document.getElementById("land");
|
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();
|
const subtotal = cartTotal();
|
||||||
let currentTotal = 0;
|
let currentTotal = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -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 };
|
window.__checkoutVars = { freeLabel, totalLabel, shippingLabel, emptyCartAlert, thankYouPath, checkoutLang, discountLabel, couponInvalid, couponAppliedTemplate, couponRemoveLabel, klarnaConnectLabel, klarnaConnectingLabel, klarnaConnectedLabel, bankHolderLabel, bankIbanLabel, bankBicLabel, bankBankNameLabel, bankPlaceholderLabel };
|
||||||
</script>
|
</script>
|
||||||
<script type="module">
|
<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 { formatPrice } from "../../../i18n/format";
|
||||||
import { products } from "../../../data/products";
|
import { products } from "../../../data/products";
|
||||||
import { berechneVersandkosten } from "../../../data/versand";
|
import { berechneVersandkosten } from "../../../data/versand";
|
||||||
@@ -158,7 +158,9 @@ import { bankverbindung, bankverbindungVollstaendig } from "../../../data/bankve
|
|||||||
|
|
||||||
const summary = document.getElementById("checkout-summary");
|
const summary = document.getElementById("checkout-summary");
|
||||||
const landSelect = document.getElementById("land");
|
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();
|
const subtotal = cartTotal();
|
||||||
let currentTotal = 0;
|
let currentTotal = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -57,18 +57,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, 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.
|
// 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>
|
||||||
<script type="module">
|
<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 { formatPrice } from "../../../i18n/format";
|
||||||
import { getProduct, products } from "../../../data/products";
|
import { getProduct, products } from "../../../data/products";
|
||||||
import { berechneVersandkosten } from "../../../data/versand";
|
import { berechneVersandkosten } from "../../../data/versand";
|
||||||
import { effektiverZeilenpreis, mengenrabattProzent } from "../../../data/rabatt";
|
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");
|
const landSelect = document.getElementById("cart-land");
|
||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
@@ -98,13 +98,17 @@ import { effektiverZeilenpreis, mengenrabattProzent } from "../../../data/rabatt
|
|||||||
? `<img class="thumb product-photo" src="${foto}" alt="${i.name}" loading="lazy" />`
|
? `<img class="thumb product-photo" src="${foto}" alt="${i.name}" loading="lazy" />`
|
||||||
: `<div class="img-placeholder thumb" role="img" aria-label="${i.name}"></div>`;
|
: `<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 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 `
|
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}
|
${thumb}
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<strong>${i.name}</strong>
|
<strong>${i.name}</strong>
|
||||||
<div class="small">${formatPrice(zeilenpreis / i.menge, cartLang)} ${perItemLabel}</div>
|
<div class="small">${formatPrice(zeilenpreis / i.menge, cartLang)} ${perItemLabel}</div>
|
||||||
${mengenBadge}
|
${mengenBadge}
|
||||||
|
${keptBadge}
|
||||||
</div>
|
</div>
|
||||||
<div class="qty-controls">
|
<div class="qty-controls">
|
||||||
<button data-action="dec" data-slug="${i.slug}" aria-label="−">−</button>
|
<button data-action="dec" data-slug="${i.slug}" aria-label="−">−</button>
|
||||||
@@ -117,15 +121,19 @@ import { effektiverZeilenpreis, mengenrabattProzent } from "../../../data/rabatt
|
|||||||
`;
|
`;
|
||||||
}).join("");
|
}).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
|
// Rechnungs-Übersicht in der Zusammenfassung — dieselbe Positionsliste wie im Warenkorb
|
||||||
// links, nur kompakt als "Beleg" (Menge × Name → Zeilenpreis), passend zum Checkout.
|
// 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>`
|
`<div class="row"><span>${i.menge}× ${i.name}</span><span>${formatPrice(zeilenpreis, cartLang)}</span></div>`
|
||||||
).join("");
|
).join("");
|
||||||
|
|
||||||
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);
|
const shipping = berechneVersandkosten(landSelect.value, ausgewaehlteZeilen.map(({ item: i }) => ({ slug: i.slug, preis: i.preis, menge: i.menge })), products);
|
||||||
const coupon = appliedCoupon();
|
const coupon = appliedCoupon();
|
||||||
const discount = couponDiscount(subtotal);
|
const discount = couponDiscount(subtotal);
|
||||||
const total = Math.max(0, subtotal - discount) + shipping;
|
const total = Math.max(0, subtotal - discount) + shipping;
|
||||||
@@ -164,6 +172,13 @@ import { effektiverZeilenpreis, mengenrabattProzent } from "../../../data/rabatt
|
|||||||
render();
|
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", () => {
|
document.getElementById("coupon-apply").addEventListener("click", () => {
|
||||||
|
|||||||
@@ -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 };
|
window.__checkoutVars = { freeLabel, totalLabel, shippingLabel, emptyCartAlert, thankYouPath, checkoutLang, discountLabel, couponInvalid, couponAppliedTemplate, couponRemoveLabel, klarnaConnectLabel, klarnaConnectingLabel, klarnaConnectedLabel, bankHolderLabel, bankIbanLabel, bankBicLabel, bankBankNameLabel, bankPlaceholderLabel };
|
||||||
</script>
|
</script>
|
||||||
<script type="module">
|
<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 { formatPrice } from "../../../i18n/format";
|
||||||
import { products } from "../../../data/products";
|
import { products } from "../../../data/products";
|
||||||
import { berechneVersandkosten } from "../../../data/versand";
|
import { berechneVersandkosten } from "../../../data/versand";
|
||||||
@@ -158,7 +158,9 @@ import { bankverbindung, bankverbindungVollstaendig } from "../../../data/bankve
|
|||||||
|
|
||||||
const summary = document.getElementById("checkout-summary");
|
const summary = document.getElementById("checkout-summary");
|
||||||
const landSelect = document.getElementById("land");
|
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();
|
const subtotal = cartTotal();
|
||||||
let currentTotal = 0;
|
let currentTotal = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -57,18 +57,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, 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.
|
// 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>
|
||||||
<script type="module">
|
<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 { formatPrice } from "../../../i18n/format";
|
||||||
import { getProduct, products } from "../../../data/products";
|
import { getProduct, products } from "../../../data/products";
|
||||||
import { berechneVersandkosten } from "../../../data/versand";
|
import { berechneVersandkosten } from "../../../data/versand";
|
||||||
import { effektiverZeilenpreis, mengenrabattProzent } from "../../../data/rabatt";
|
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");
|
const landSelect = document.getElementById("cart-land");
|
||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
@@ -98,13 +98,17 @@ import { effektiverZeilenpreis, mengenrabattProzent } from "../../../data/rabatt
|
|||||||
? `<img class="thumb product-photo" src="${foto}" alt="${i.name}" loading="lazy" />`
|
? `<img class="thumb product-photo" src="${foto}" alt="${i.name}" loading="lazy" />`
|
||||||
: `<div class="img-placeholder thumb" role="img" aria-label="${i.name}"></div>`;
|
: `<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 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 `
|
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}
|
${thumb}
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<strong>${i.name}</strong>
|
<strong>${i.name}</strong>
|
||||||
<div class="small">${formatPrice(zeilenpreis / i.menge, cartLang)} ${perItemLabel}</div>
|
<div class="small">${formatPrice(zeilenpreis / i.menge, cartLang)} ${perItemLabel}</div>
|
||||||
${mengenBadge}
|
${mengenBadge}
|
||||||
|
${keptBadge}
|
||||||
</div>
|
</div>
|
||||||
<div class="qty-controls">
|
<div class="qty-controls">
|
||||||
<button data-action="dec" data-slug="${i.slug}" aria-label="−">−</button>
|
<button data-action="dec" data-slug="${i.slug}" aria-label="−">−</button>
|
||||||
@@ -117,15 +121,19 @@ import { effektiverZeilenpreis, mengenrabattProzent } from "../../../data/rabatt
|
|||||||
`;
|
`;
|
||||||
}).join("");
|
}).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
|
// Rechnungs-Übersicht in der Zusammenfassung — dieselbe Positionsliste wie im Warenkorb
|
||||||
// links, nur kompakt als "Beleg" (Menge × Name → Zeilenpreis), passend zum Checkout.
|
// 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>`
|
`<div class="row"><span>${i.menge}× ${i.name}</span><span>${formatPrice(zeilenpreis, cartLang)}</span></div>`
|
||||||
).join("");
|
).join("");
|
||||||
|
|
||||||
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);
|
const shipping = berechneVersandkosten(landSelect.value, ausgewaehlteZeilen.map(({ item: i }) => ({ slug: i.slug, preis: i.preis, menge: i.menge })), products);
|
||||||
const coupon = appliedCoupon();
|
const coupon = appliedCoupon();
|
||||||
const discount = couponDiscount(subtotal);
|
const discount = couponDiscount(subtotal);
|
||||||
const total = Math.max(0, subtotal - discount) + shipping;
|
const total = Math.max(0, subtotal - discount) + shipping;
|
||||||
@@ -164,6 +172,13 @@ import { effektiverZeilenpreis, mengenrabattProzent } from "../../../data/rabatt
|
|||||||
render();
|
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", () => {
|
document.getElementById("coupon-apply").addEventListener("click", () => {
|
||||||
|
|||||||
@@ -57,18 +57,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, 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.
|
// 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>
|
||||||
<script type="module">
|
<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 { formatPrice } from "../../i18n/format";
|
||||||
import { getProduct, products } from "../../data/products";
|
import { getProduct, products } from "../../data/products";
|
||||||
import { berechneVersandkosten } from "../../data/versand";
|
import { berechneVersandkosten } from "../../data/versand";
|
||||||
import { effektiverZeilenpreis, mengenrabattProzent } from "../../data/rabatt";
|
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");
|
const landSelect = document.getElementById("cart-land");
|
||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
@@ -98,13 +98,17 @@ import { effektiverZeilenpreis, mengenrabattProzent } from "../../data/rabatt";
|
|||||||
? `<img class="thumb product-photo" src="${foto}" alt="${i.name}" loading="lazy" />`
|
? `<img class="thumb product-photo" src="${foto}" alt="${i.name}" loading="lazy" />`
|
||||||
: `<div class="img-placeholder thumb" role="img" aria-label="${i.name}"></div>`;
|
: `<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 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 `
|
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}
|
${thumb}
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<strong>${i.name}</strong>
|
<strong>${i.name}</strong>
|
||||||
<div class="small">${formatPrice(zeilenpreis / i.menge, cartLang)} ${perItemLabel}</div>
|
<div class="small">${formatPrice(zeilenpreis / i.menge, cartLang)} ${perItemLabel}</div>
|
||||||
${mengenBadge}
|
${mengenBadge}
|
||||||
|
${keptBadge}
|
||||||
</div>
|
</div>
|
||||||
<div class="qty-controls">
|
<div class="qty-controls">
|
||||||
<button data-action="dec" data-slug="${i.slug}" aria-label="−">−</button>
|
<button data-action="dec" data-slug="${i.slug}" aria-label="−">−</button>
|
||||||
@@ -117,15 +121,19 @@ import { effektiverZeilenpreis, mengenrabattProzent } from "../../data/rabatt";
|
|||||||
`;
|
`;
|
||||||
}).join("");
|
}).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
|
// Rechnungs-Übersicht in der Zusammenfassung — dieselbe Positionsliste wie im Warenkorb
|
||||||
// links, nur kompakt als "Beleg" (Menge × Name → Zeilenpreis), passend zum Checkout.
|
// 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>`
|
`<div class="row"><span>${i.menge}× ${i.name}</span><span>${formatPrice(zeilenpreis, cartLang)}</span></div>`
|
||||||
).join("");
|
).join("");
|
||||||
|
|
||||||
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);
|
const shipping = berechneVersandkosten(landSelect.value, ausgewaehlteZeilen.map(({ item: i }) => ({ slug: i.slug, preis: i.preis, menge: i.menge })), products);
|
||||||
const coupon = appliedCoupon();
|
const coupon = appliedCoupon();
|
||||||
const discount = couponDiscount(subtotal);
|
const discount = couponDiscount(subtotal);
|
||||||
const total = Math.max(0, subtotal - discount) + shipping;
|
const total = Math.max(0, subtotal - discount) + shipping;
|
||||||
@@ -164,6 +172,13 @@ import { effektiverZeilenpreis, mengenrabattProzent } from "../../data/rabatt";
|
|||||||
render();
|
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", () => {
|
document.getElementById("coupon-apply").addEventListener("click", () => {
|
||||||
|
|||||||
+25
-2
@@ -10,6 +10,16 @@ export interface CartItem {
|
|||||||
name: string;
|
name: string;
|
||||||
preis: number;
|
preis: number;
|
||||||
menge: number;
|
menge: number;
|
||||||
|
// Ob dieser Artikel gerade gekauft werden soll oder nur "im Warenkorb liegen bleibt" für
|
||||||
|
// später. Fehlt das Feld (ältere, bereits gespeicherte Warenkörbe), gilt es als ausgewählt —
|
||||||
|
// siehe istAusgewaehlt().
|
||||||
|
ausgewaehlt?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** true, sofern nicht explizit auf false gesetzt — hält ältere localStorage-Warenkörbe ohne
|
||||||
|
* dieses Feld weiterhin vollständig "ausgewählt" (kein Verhaltensbruch für Bestandsbesucher). */
|
||||||
|
export function istAusgewaehlt(item: CartItem): boolean {
|
||||||
|
return item.ausgewaehlt !== false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const KEY = "vandiy_cart_v1";
|
const KEY = "vandiy_cart_v1";
|
||||||
@@ -54,15 +64,28 @@ export function removeFromCart(slug: string) {
|
|||||||
saveCart(getCart().filter((i) => i.slug !== slug));
|
saveCart(getCart().filter((i) => i.slug !== slug));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Legt fest, ob ein Artikel jetzt gekauft werden soll (true) oder nur im Warenkorb liegen
|
||||||
|
* bleibt, ohne mitgekauft zu werden (false) — z.B. wenn man sich noch nicht sicher ist. */
|
||||||
|
export function setAusgewaehlt(slug: string, ausgewaehlt: boolean) {
|
||||||
|
const cart = getCart();
|
||||||
|
const item = cart.find((i) => i.slug === slug);
|
||||||
|
if (item) item.ausgewaehlt = ausgewaehlt;
|
||||||
|
saveCart(cart);
|
||||||
|
}
|
||||||
|
|
||||||
export function cartCount(): number {
|
export function cartCount(): number {
|
||||||
return getCart().reduce((sum, i) => sum + i.menge, 0);
|
return getCart().reduce((sum, i) => sum + i.menge, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zwischensumme inkl. Produkt-Rabatt (Prozent/Betrag) UND Mengenrabatt-Staffel — schaut sich
|
// Zwischensumme inkl. Produkt-Rabatt (Prozent/Betrag) UND Mengenrabatt-Staffel — schaut sich
|
||||||
// dafür live die aktuellen Produktdaten an (nicht den bei addToCart gespeicherten Preis), damit
|
// dafür live die aktuellen Produktdaten an (nicht den bei addToCart gespeicherten Preis), damit
|
||||||
// ein nachträglich geänderter/abgelaufener Rabatt sich sofort korrekt auswirkt.
|
// ein nachträglich geänderter/abgelaufener Rabatt sich sofort korrekt auswirkt. Zählt NUR
|
||||||
|
// Artikel, die gerade zum Kauf ausgewählt sind — abgewählte Artikel bleiben im Warenkorb liegen,
|
||||||
|
// fließen aber nicht in Summe/Checkout ein.
|
||||||
export function cartTotal(): number {
|
export function cartTotal(): number {
|
||||||
return getCart().reduce((sum, i) => {
|
return getCart()
|
||||||
|
.filter(istAusgewaehlt)
|
||||||
|
.reduce((sum, i) => {
|
||||||
const produkt = getProduct(i.slug);
|
const produkt = getProduct(i.slug);
|
||||||
return sum + (produkt ? effektiverZeilenpreis(produkt, i.menge) : i.menge * i.preis);
|
return sum + (produkt ? effektiverZeilenpreis(produkt, i.menge) : i.menge * i.preis);
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|||||||
@@ -872,6 +872,38 @@ a:focus-visible, button:focus-visible {
|
|||||||
}
|
}
|
||||||
.cart-item:hover { background: rgba(244, 241, 247, 0.02); }
|
.cart-item:hover { background: rgba(244, 241, 247, 0.02); }
|
||||||
.cart-item:last-child { border-bottom: none; }
|
.cart-item:last-child { border-bottom: none; }
|
||||||
|
|
||||||
|
/* Checkbox "jetzt kaufen" — abgewählte Artikel bleiben sichtbar im Warenkorb liegen, zählen aber
|
||||||
|
nicht mehr zur Summe/zum Checkout, bis sie wieder angehakt werden. */
|
||||||
|
.cart-item .buy-toggle {
|
||||||
|
appearance: none;
|
||||||
|
width: 21px;
|
||||||
|
height: 21px;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 2px solid rgba(244, 241, 247, 0.3);
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: grid;
|
||||||
|
place-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.2s var(--ease), background 0.2s var(--ease);
|
||||||
|
}
|
||||||
|
.cart-item .buy-toggle::before {
|
||||||
|
content: "";
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
clip-path: polygon(14% 44%, 0 65%, 45% 100%, 100% 16%, 80% 0%, 43% 68%);
|
||||||
|
transform: scale(0);
|
||||||
|
transition: transform 0.15s var(--ease);
|
||||||
|
background: #08181c;
|
||||||
|
}
|
||||||
|
.cart-item .buy-toggle:checked {
|
||||||
|
border-color: var(--c-accent);
|
||||||
|
background: linear-gradient(135deg, var(--c-accent), var(--c-accent-hover));
|
||||||
|
}
|
||||||
|
.cart-item .buy-toggle:checked::before { transform: scale(1); }
|
||||||
|
.cart-item.deselected { opacity: 0.55; }
|
||||||
|
.cart-item.deselected:hover { opacity: 0.8; }
|
||||||
|
.kept-in-cart-badge { color: var(--c-text-muted); margin-top: 0.15rem; }
|
||||||
.cart-item .thumb {
|
.cart-item .thumb {
|
||||||
width: 76px;
|
width: 76px;
|
||||||
height: 76px;
|
height: 76px;
|
||||||
|
|||||||
Reference in New Issue
Block a user