Versandkosten-System (Pflichtenheft Punkt 9) + kritischen Warenkorb/Checkout-Bug behoben
Neu: - Produkte können eigene Versandkosten je Land (DE/AT/CH/LU) bekommen, gelten nur bei Einzelkauf (genau 1 Stück von 1 Produkt) - Versandkosten-Staffel nach Warenwert je Land, von VanVan im Adminbereich bearbeitbar (neuer Bereich "Versandkosten-Staffel"), greift bei mehreren Produkten/Stück - Versandkostenfrei ab 75 € gilt immer, unabhängig von Einzelkauf/Staffel - Checkout berechnet und zeigt jetzt die echten Versandkosten live beim Länderwechsel an Kritischer Bugfix (unabhängig vom neuen Feature entdeckt): - Warenkorb- und Checkout-Skripte haben "import" innerhalb von define:vars-Skripten benutzt, was Astro als IIFE rendert (kein ES-Modul) — das hat in der echten Live-Version die ganze Zeit zu "Cannot use import statement outside a module" geführt, wodurch Warenkorb und Checkout praktisch nie funktioniert haben. Behoben durch Trennung: define:vars-Skript setzt nur Werte auf window, separates <script type="module"> mit den echten Imports liest sie aus. Mit echtem End-to-End-Test verifiziert (Artikel in Warenkorb legen, Land wechseln, Summe prüfen) statt nur Screenshot.
This commit is contained in:
@@ -92,6 +92,16 @@ collections:
|
|||||||
- { label: Bestseller, value: bestseller }
|
- { label: Bestseller, value: bestseller }
|
||||||
- { label: "Im Sale", value: sale }
|
- { label: "Im Sale", value: sale }
|
||||||
- { label: Handgemacht, value: handgemacht }
|
- { label: Handgemacht, value: handgemacht }
|
||||||
|
- label: "🚚 Eigene Versandkosten (nur für Einzelkauf dieses Produkts)"
|
||||||
|
name: versand
|
||||||
|
widget: object
|
||||||
|
required: false
|
||||||
|
hint: "Gilt NUR, wenn jemand genau 1 Stück von genau diesem Produkt kauft. Bei mehreren Produkten/Stück im Warenkorb gilt automatisch die allgemeine Versandkosten-Staffel (siehe eigener Bereich 'Versandkosten-Staffel' im Menü links). Leer lassen = Staffel gilt auch beim Einzelkauf."
|
||||||
|
fields:
|
||||||
|
- { label: "Deutschland (€)", name: de, widget: number, value_type: float, step: 0.01, required: false }
|
||||||
|
- { label: "Österreich (€)", name: at, widget: number, value_type: float, step: 0.01, required: false }
|
||||||
|
- { label: "Schweiz (€)", name: ch, widget: number, value_type: float, step: 0.01, required: false }
|
||||||
|
- { label: "Luxemburg (€)", name: lu, widget: number, value_type: float, step: 0.01, required: false }
|
||||||
|
|
||||||
- name: categories
|
- name: categories
|
||||||
label: Kategorien
|
label: Kategorien
|
||||||
@@ -147,3 +157,44 @@ collections:
|
|||||||
- { label: Englisch, name: en, widget: string }
|
- { label: Englisch, name: en, widget: string }
|
||||||
- { label: "Schweizerdeutsch", name: ch, widget: string }
|
- { label: "Schweizerdeutsch", name: ch, widget: string }
|
||||||
- { label: Französisch, name: fr, widget: string }
|
- { label: Französisch, name: fr, widget: string }
|
||||||
|
|
||||||
|
- name: versandkosten
|
||||||
|
label: "🚚 Versandkosten-Staffel"
|
||||||
|
files:
|
||||||
|
- name: staffel
|
||||||
|
label: "Versandkosten-Staffel nach Warenwert"
|
||||||
|
file: src/content/versandstaffel.json
|
||||||
|
description: >
|
||||||
|
Gilt automatisch im Warenkorb/an der Kasse, sobald jemand mehrere Produkte oder
|
||||||
|
mehrere Stück kauft (bei genau 1 Stück von 1 Produkt gilt stattdessen der eigene
|
||||||
|
Versandpreis am Produkt, falls dort einer eingetragen ist). Ab 75 € Warenwert ist der
|
||||||
|
Versand immer kostenlos, unabhängig von dieser Tabelle.
|
||||||
|
fields:
|
||||||
|
- label: Deutschland
|
||||||
|
name: de
|
||||||
|
widget: list
|
||||||
|
summary: "ab {{fields.abWarenwert}} € → {{fields.kosten}} €"
|
||||||
|
fields:
|
||||||
|
- { label: "Ab Warenwert (€)", name: abWarenwert, widget: number, value_type: float, step: 0.01 }
|
||||||
|
- { label: "Versandkosten (€)", name: kosten, widget: number, value_type: float, step: 0.01 }
|
||||||
|
- label: Österreich
|
||||||
|
name: at
|
||||||
|
widget: list
|
||||||
|
summary: "ab {{fields.abWarenwert}} € → {{fields.kosten}} €"
|
||||||
|
fields:
|
||||||
|
- { label: "Ab Warenwert (€)", name: abWarenwert, widget: number, value_type: float, step: 0.01 }
|
||||||
|
- { label: "Versandkosten (€)", name: kosten, widget: number, value_type: float, step: 0.01 }
|
||||||
|
- label: Schweiz
|
||||||
|
name: ch
|
||||||
|
widget: list
|
||||||
|
summary: "ab {{fields.abWarenwert}} € → {{fields.kosten}} €"
|
||||||
|
fields:
|
||||||
|
- { label: "Ab Warenwert (€)", name: abWarenwert, widget: number, value_type: float, step: 0.01 }
|
||||||
|
- { label: "Versandkosten (€)", name: kosten, widget: number, value_type: float, step: 0.01 }
|
||||||
|
- label: Luxemburg
|
||||||
|
name: lu
|
||||||
|
widget: list
|
||||||
|
summary: "ab {{fields.abWarenwert}} € → {{fields.kosten}} €"
|
||||||
|
fields:
|
||||||
|
- { label: "Ab Warenwert (€)", name: abWarenwert, widget: number, value_type: float, step: 0.01 }
|
||||||
|
- { label: "Versandkosten (€)", name: kosten, widget: number, value_type: float, step: 0.01 }
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"de": [
|
||||||
|
{ "abWarenwert": 0, "kosten": 4.95 },
|
||||||
|
{ "abWarenwert": 25, "kosten": 3.95 },
|
||||||
|
{ "abWarenwert": 50, "kosten": 2.95 }
|
||||||
|
],
|
||||||
|
"at": [
|
||||||
|
{ "abWarenwert": 0, "kosten": 6.95 },
|
||||||
|
{ "abWarenwert": 25, "kosten": 5.95 },
|
||||||
|
{ "abWarenwert": 50, "kosten": 4.95 }
|
||||||
|
],
|
||||||
|
"ch": [
|
||||||
|
{ "abWarenwert": 0, "kosten": 9.95 },
|
||||||
|
{ "abWarenwert": 25, "kosten": 8.95 },
|
||||||
|
{ "abWarenwert": 50, "kosten": 7.95 }
|
||||||
|
],
|
||||||
|
"lu": [
|
||||||
|
{ "abWarenwert": 0, "kosten": 6.95 },
|
||||||
|
{ "abWarenwert": 25, "kosten": 5.95 },
|
||||||
|
{ "abWarenwert": 50, "kosten": 4.95 }
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -29,6 +29,11 @@ export interface Product {
|
|||||||
lieferzeit?: string;
|
lieferzeit?: string;
|
||||||
badges: Badge[];
|
badges: Badge[];
|
||||||
bilder?: string[]; // Pfade zu Produktfotos, von VanVan im Admin-Bereich hochgeladen
|
bilder?: string[]; // Pfade zu Produktfotos, von VanVan im Admin-Bereich hochgeladen
|
||||||
|
// Eigene Versandkosten je Land — gelten NUR, wenn genau 1 Stück dieses einen Produkts
|
||||||
|
// gekauft wird (Einzelkauf). Bei mehreren Produkten/Stückzahlen greift stattdessen die
|
||||||
|
// Versandkosten-Staffel (siehe src/data/versand.ts). Optional, damit Bestandsprodukte ohne
|
||||||
|
// diese Angabe nicht kaputtgehen — dann greift automatisch die Staffel als Rückfalllösung.
|
||||||
|
versand?: { de?: number; at?: number; ch?: number; lu?: number };
|
||||||
}
|
}
|
||||||
|
|
||||||
const modules = import.meta.glob("/src/content/products/*.json", { eager: true }) as Record<
|
const modules = import.meta.glob("/src/content/products/*.json", { eager: true }) as Record<
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
// Versandkosten-Logik. Regeln (mit dem Nutzer am 02.08.2026 abgestimmt):
|
||||||
|
// 1. Warenkorb-Gesamtwert >= 75 € → Versand immer kostenlos, egal wie viele Produkte/Stück.
|
||||||
|
// 2. Genau 1 Stück von genau 1 Produkt im Warenkorb → der am Produkt selbst hinterlegte
|
||||||
|
// Versandpreis für das Zielland gilt (falls hinterlegt, sonst Fallback auf die Staffel).
|
||||||
|
// 3. Alles andere (mehrere Produkte und/oder mehrere Stück) → Versandkosten-Staffel nach
|
||||||
|
// Gesamt-Warenwert für das Zielland (src/content/versandstaffel.json), von VanVan im
|
||||||
|
// Adminbereich bearbeitbar.
|
||||||
|
|
||||||
|
import type { Product } from "./products";
|
||||||
|
|
||||||
|
export type CountryCode = "de" | "at" | "ch" | "lu";
|
||||||
|
|
||||||
|
export interface VersandStufe {
|
||||||
|
abWarenwert: number;
|
||||||
|
kosten: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Versandstaffel {
|
||||||
|
de: VersandStufe[];
|
||||||
|
at: VersandStufe[];
|
||||||
|
ch: VersandStufe[];
|
||||||
|
lu: VersandStufe[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// @ts-ignore -- JSON-Import, zur Build-Zeit von Astro/Vite aufgelöst
|
||||||
|
import versandstaffelRaw from "../content/versandstaffel.json";
|
||||||
|
export const versandstaffel = versandstaffelRaw as Versandstaffel;
|
||||||
|
|
||||||
|
export const FREE_SHIPPING_FROM = 75;
|
||||||
|
|
||||||
|
function tierLookup(land: CountryCode, warenwert: number): number {
|
||||||
|
const stufen = versandstaffel[land] ?? [];
|
||||||
|
const sortiert = [...stufen].sort((a, b) => a.abWarenwert - b.abWarenwert);
|
||||||
|
let kosten = sortiert[0]?.kosten ?? 0;
|
||||||
|
for (const s of sortiert) {
|
||||||
|
if (warenwert >= s.abWarenwert) kosten = s.kosten;
|
||||||
|
}
|
||||||
|
return kosten;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MinimalCartItem {
|
||||||
|
slug: string;
|
||||||
|
preis: number;
|
||||||
|
menge: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Berechnet die Versandkosten für einen Warenkorb in ein bestimmtes Land.
|
||||||
|
* `alleProdukte` wird gebraucht, um bei Einzelkäufen den produktspezifischen Versandpreis
|
||||||
|
* nachzuschlagen (die Cart-Items selbst kennen nur slug/preis/menge, keine Versanddaten).
|
||||||
|
*/
|
||||||
|
export function berechneVersandkosten(
|
||||||
|
land: CountryCode,
|
||||||
|
items: MinimalCartItem[],
|
||||||
|
alleProdukte: Product[]
|
||||||
|
): number {
|
||||||
|
if (items.length === 0) return 0;
|
||||||
|
|
||||||
|
const warenwert = items.reduce((sum, i) => sum + i.preis * i.menge, 0);
|
||||||
|
if (warenwert >= FREE_SHIPPING_FROM) return 0;
|
||||||
|
|
||||||
|
const gesamtmenge = items.reduce((sum, i) => sum + i.menge, 0);
|
||||||
|
const istEinzelkauf = items.length === 1 && gesamtmenge === 1;
|
||||||
|
|
||||||
|
if (istEinzelkauf) {
|
||||||
|
const produkt = alleProdukte.find((p) => p.slug === items[0].slug);
|
||||||
|
const eigenerPreis = produkt?.versand?.[land];
|
||||||
|
if (typeof eigenerPreis === "number") return eigenerPreis;
|
||||||
|
// Kein eigener Versandpreis hinterlegt -> Staffel als Rückfalllösung
|
||||||
|
}
|
||||||
|
|
||||||
|
return tierLookup(land, warenwert);
|
||||||
|
}
|
||||||
@@ -32,10 +32,10 @@ const t = useTranslations(lang);
|
|||||||
<div>
|
<div>
|
||||||
<label for="land">{t.checkout.country}</label>
|
<label for="land">{t.checkout.country}</label>
|
||||||
<select id="land">
|
<select id="land">
|
||||||
<option selected>Schwiz</option>
|
<option value="ch" selected>Schwiz</option>
|
||||||
<option>Dütschland</option>
|
<option value="de">Dütschland</option>
|
||||||
<option>Öschterrych</option>
|
<option value="at">Öschterrych</option>
|
||||||
<option>Luxemburg</option>
|
<option value="lu">Luxemburg</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -74,19 +74,35 @@ const t = useTranslations(lang);
|
|||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<script define:vars={{ freeLabel: t.checkout.free, totalLabel: t.checkout.total, shippingLabel: t.cart.shipping, emptyCartAlert: t.checkout.emptyCartAlert, thankYouPath: "/ch/checkout/danke/", checkoutLang: lang }}>
|
<script define:vars={{ freeLabel: t.checkout.free, totalLabel: t.checkout.total, shippingLabel: t.cart.shipping, emptyCartAlert: t.checkout.emptyCartAlert, thankYouPath: "/ch/checkout/danke/", checkoutLang: lang }}>
|
||||||
|
// WICHTIG: define:vars-Skripte laufen als IIFE, keine "import"-Anweisungen möglich.
|
||||||
|
window.__checkoutVars = { freeLabel, totalLabel, shippingLabel, emptyCartAlert, thankYouPath, checkoutLang };
|
||||||
|
</script>
|
||||||
|
<script type="module">
|
||||||
import { getCart, cartTotal } from "../../../scripts/cart";
|
import { getCart, cartTotal } from "../../../scripts/cart";
|
||||||
import { formatPrice } from "../../../i18n/format";
|
import { formatPrice } from "../../../i18n/format";
|
||||||
|
import { products } from "../../../data/products";
|
||||||
|
import { berechneVersandkosten } from "../../../data/versand";
|
||||||
|
|
||||||
|
const { freeLabel, totalLabel, shippingLabel, emptyCartAlert, thankYouPath, checkoutLang } = window.__checkoutVars;
|
||||||
|
|
||||||
const summary = document.getElementById("checkout-summary");
|
const summary = document.getElementById("checkout-summary");
|
||||||
|
const landSelect = document.getElementById("land");
|
||||||
const cart = getCart();
|
const cart = getCart();
|
||||||
const subtotal = cartTotal();
|
const subtotal = cartTotal();
|
||||||
const shipping = cart.length === 0 ? 0 : subtotal >= 75 ? 0 : 4.95;
|
|
||||||
|
function renderSummary() {
|
||||||
|
const land = landSelect.value;
|
||||||
|
const shipping = cart.length === 0 ? 0 : berechneVersandkosten(land, cart.map((i) => ({ slug: i.slug, preis: i.preis, menge: i.menge })), products);
|
||||||
|
|
||||||
summary.innerHTML = `
|
summary.innerHTML = `
|
||||||
${cart.map((i) => `<div class="row"><span>${i.menge}× ${i.name}</span><span>${formatPrice(i.preis * i.menge, checkoutLang)}</span></div>`).join("")}
|
${cart.map((i) => `<div class="row"><span>${i.menge}× ${i.name}</span><span>${formatPrice(i.preis * i.menge, checkoutLang)}</span></div>`).join("")}
|
||||||
<div class="row"><span>${shippingLabel}</span><span>${shipping === 0 ? freeLabel : formatPrice(shipping, checkoutLang)}</span></div>
|
<div class="row"><span>${shippingLabel}</span><span>${shipping === 0 ? freeLabel : formatPrice(shipping, checkoutLang)}</span></div>
|
||||||
<div class="row total"><span>${totalLabel}</span><span>${formatPrice(subtotal + shipping, checkoutLang)}</span></div>
|
<div class="row total"><span>${totalLabel}</span><span>${formatPrice(subtotal + shipping, checkoutLang)}</span></div>
|
||||||
`;
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSummary();
|
||||||
|
landSelect.addEventListener("change", renderSummary);
|
||||||
|
|
||||||
document.getElementById("checkout-form").addEventListener("submit", (e) => {
|
document.getElementById("checkout-form").addEventListener("submit", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|||||||
@@ -39,9 +39,15 @@ const t = useTranslations(lang);
|
|||||||
</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, cartLang: lang }}>
|
||||||
|
// WICHTIG: define:vars-Skripte laufen als IIFE, keine "import"-Anweisungen möglich.
|
||||||
|
window.__cartVars = { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, cartLang };
|
||||||
|
</script>
|
||||||
|
<script type="module">
|
||||||
import { getCart, updateQuantity, removeFromCart, cartTotal } from "../../../scripts/cart";
|
import { getCart, updateQuantity, removeFromCart, cartTotal } from "../../../scripts/cart";
|
||||||
import { formatPrice } from "../../../i18n/format";
|
import { formatPrice } from "../../../i18n/format";
|
||||||
|
|
||||||
|
const { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, cartLang } = window.__cartVars;
|
||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
const cart = getCart();
|
const cart = getCart();
|
||||||
const empty = document.getElementById("cart-empty");
|
const empty = document.getElementById("cart-empty");
|
||||||
|
|||||||
@@ -32,10 +32,10 @@ const t = useTranslations(lang);
|
|||||||
<div>
|
<div>
|
||||||
<label for="land">{t.checkout.country}</label>
|
<label for="land">{t.checkout.country}</label>
|
||||||
<select id="land">
|
<select id="land">
|
||||||
<option>Deutschland</option>
|
<option value="de">Deutschland</option>
|
||||||
<option>Österreich</option>
|
<option value="at">Österreich</option>
|
||||||
<option>Schweiz</option>
|
<option value="ch">Schweiz</option>
|
||||||
<option>Luxemburg</option>
|
<option value="lu">Luxemburg</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -74,19 +74,38 @@ const t = useTranslations(lang);
|
|||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<script define:vars={{ freeLabel: t.checkout.free, totalLabel: t.checkout.total, shippingLabel: t.cart.shipping, emptyCartAlert: t.checkout.emptyCartAlert, thankYouPath: "/checkout/danke/", checkoutLang: lang }}>
|
<script define:vars={{ freeLabel: t.checkout.free, totalLabel: t.checkout.total, shippingLabel: t.cart.shipping, emptyCartAlert: t.checkout.emptyCartAlert, thankYouPath: "/checkout/danke/", checkoutLang: lang }}>
|
||||||
|
// WICHTIG: define:vars-Skripte werden von Astro in ein IIFE gepackt (kein ES-Modul) — echte
|
||||||
|
// `import`-Anweisungen würden hier zur Laufzeit mit "Cannot use import statement outside a
|
||||||
|
// module" fehlschlagen. Deshalb hier nur die vom Server gerenderten Werte auf window ablegen,
|
||||||
|
// die eigentliche Logik läuft im separaten <script type="module"> darunter.
|
||||||
|
window.__checkoutVars = { freeLabel, totalLabel, shippingLabel, emptyCartAlert, thankYouPath, checkoutLang };
|
||||||
|
</script>
|
||||||
|
<script type="module">
|
||||||
import { getCart, cartTotal } from "../../scripts/cart";
|
import { getCart, cartTotal } from "../../scripts/cart";
|
||||||
import { formatPrice } from "../../i18n/format";
|
import { formatPrice } from "../../i18n/format";
|
||||||
|
import { products } from "../../data/products";
|
||||||
|
import { berechneVersandkosten } from "../../data/versand";
|
||||||
|
|
||||||
|
const { freeLabel, totalLabel, shippingLabel, emptyCartAlert, thankYouPath, checkoutLang } = window.__checkoutVars;
|
||||||
|
|
||||||
const summary = document.getElementById("checkout-summary");
|
const summary = document.getElementById("checkout-summary");
|
||||||
|
const landSelect = document.getElementById("land");
|
||||||
const cart = getCart();
|
const cart = getCart();
|
||||||
const subtotal = cartTotal();
|
const subtotal = cartTotal();
|
||||||
const shipping = cart.length === 0 ? 0 : subtotal >= 75 ? 0 : 4.95;
|
|
||||||
|
function renderSummary() {
|
||||||
|
const land = landSelect.value;
|
||||||
|
const shipping = cart.length === 0 ? 0 : berechneVersandkosten(land, cart.map((i) => ({ slug: i.slug, preis: i.preis, menge: i.menge })), products);
|
||||||
|
|
||||||
summary.innerHTML = `
|
summary.innerHTML = `
|
||||||
${cart.map((i) => `<div class="row"><span>${i.menge}× ${i.name}</span><span>${formatPrice(i.preis * i.menge, checkoutLang)}</span></div>`).join("")}
|
${cart.map((i) => `<div class="row"><span>${i.menge}× ${i.name}</span><span>${formatPrice(i.preis * i.menge, checkoutLang)}</span></div>`).join("")}
|
||||||
<div class="row"><span>${shippingLabel}</span><span>${shipping === 0 ? freeLabel : formatPrice(shipping, checkoutLang)}</span></div>
|
<div class="row"><span>${shippingLabel}</span><span>${shipping === 0 ? freeLabel : formatPrice(shipping, checkoutLang)}</span></div>
|
||||||
<div class="row total"><span>${totalLabel}</span><span>${formatPrice(subtotal + shipping, checkoutLang)}</span></div>
|
<div class="row total"><span>${totalLabel}</span><span>${formatPrice(subtotal + shipping, checkoutLang)}</span></div>
|
||||||
`;
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSummary();
|
||||||
|
landSelect.addEventListener("change", renderSummary);
|
||||||
|
|
||||||
document.getElementById("checkout-form").addEventListener("submit", (e) => {
|
document.getElementById("checkout-form").addEventListener("submit", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|||||||
@@ -32,10 +32,10 @@ const t = useTranslations(lang);
|
|||||||
<div>
|
<div>
|
||||||
<label for="land">{t.checkout.country}</label>
|
<label for="land">{t.checkout.country}</label>
|
||||||
<select id="land">
|
<select id="land">
|
||||||
<option>Germany</option>
|
<option value="de">Germany</option>
|
||||||
<option>Austria</option>
|
<option value="at">Austria</option>
|
||||||
<option>Switzerland</option>
|
<option value="ch">Switzerland</option>
|
||||||
<option>Luxembourg</option>
|
<option value="lu">Luxembourg</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -74,19 +74,35 @@ const t = useTranslations(lang);
|
|||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<script define:vars={{ freeLabel: t.checkout.free, totalLabel: t.checkout.total, shippingLabel: t.cart.shipping, emptyCartAlert: t.checkout.emptyCartAlert, thankYouPath: "/en/checkout/danke/", checkoutLang: lang }}>
|
<script define:vars={{ freeLabel: t.checkout.free, totalLabel: t.checkout.total, shippingLabel: t.cart.shipping, emptyCartAlert: t.checkout.emptyCartAlert, thankYouPath: "/en/checkout/danke/", checkoutLang: lang }}>
|
||||||
|
// WICHTIG: define:vars-Skripte laufen als IIFE, keine "import"-Anweisungen möglich.
|
||||||
|
window.__checkoutVars = { freeLabel, totalLabel, shippingLabel, emptyCartAlert, thankYouPath, checkoutLang };
|
||||||
|
</script>
|
||||||
|
<script type="module">
|
||||||
import { getCart, cartTotal } from "../../../scripts/cart";
|
import { getCart, cartTotal } from "../../../scripts/cart";
|
||||||
import { formatPrice } from "../../../i18n/format";
|
import { formatPrice } from "../../../i18n/format";
|
||||||
|
import { products } from "../../../data/products";
|
||||||
|
import { berechneVersandkosten } from "../../../data/versand";
|
||||||
|
|
||||||
|
const { freeLabel, totalLabel, shippingLabel, emptyCartAlert, thankYouPath, checkoutLang } = window.__checkoutVars;
|
||||||
|
|
||||||
const summary = document.getElementById("checkout-summary");
|
const summary = document.getElementById("checkout-summary");
|
||||||
|
const landSelect = document.getElementById("land");
|
||||||
const cart = getCart();
|
const cart = getCart();
|
||||||
const subtotal = cartTotal();
|
const subtotal = cartTotal();
|
||||||
const shipping = cart.length === 0 ? 0 : subtotal >= 75 ? 0 : 4.95;
|
|
||||||
|
function renderSummary() {
|
||||||
|
const land = landSelect.value;
|
||||||
|
const shipping = cart.length === 0 ? 0 : berechneVersandkosten(land, cart.map((i) => ({ slug: i.slug, preis: i.preis, menge: i.menge })), products);
|
||||||
|
|
||||||
summary.innerHTML = `
|
summary.innerHTML = `
|
||||||
${cart.map((i) => `<div class="row"><span>${i.menge}× ${i.name}</span><span>${formatPrice(i.preis * i.menge, checkoutLang)}</span></div>`).join("")}
|
${cart.map((i) => `<div class="row"><span>${i.menge}× ${i.name}</span><span>${formatPrice(i.preis * i.menge, checkoutLang)}</span></div>`).join("")}
|
||||||
<div class="row"><span>${shippingLabel}</span><span>${shipping === 0 ? freeLabel : formatPrice(shipping, checkoutLang)}</span></div>
|
<div class="row"><span>${shippingLabel}</span><span>${shipping === 0 ? freeLabel : formatPrice(shipping, checkoutLang)}</span></div>
|
||||||
<div class="row total"><span>${totalLabel}</span><span>${formatPrice(subtotal + shipping, checkoutLang)}</span></div>
|
<div class="row total"><span>${totalLabel}</span><span>${formatPrice(subtotal + shipping, checkoutLang)}</span></div>
|
||||||
`;
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSummary();
|
||||||
|
landSelect.addEventListener("change", renderSummary);
|
||||||
|
|
||||||
document.getElementById("checkout-form").addEventListener("submit", (e) => {
|
document.getElementById("checkout-form").addEventListener("submit", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|||||||
@@ -39,9 +39,15 @@ const t = useTranslations(lang);
|
|||||||
</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, cartLang: lang }}>
|
||||||
|
// WICHTIG: define:vars-Skripte laufen als IIFE, keine "import"-Anweisungen möglich.
|
||||||
|
window.__cartVars = { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, cartLang };
|
||||||
|
</script>
|
||||||
|
<script type="module">
|
||||||
import { getCart, updateQuantity, removeFromCart, cartTotal } from "../../../scripts/cart";
|
import { getCart, updateQuantity, removeFromCart, cartTotal } from "../../../scripts/cart";
|
||||||
import { formatPrice } from "../../../i18n/format";
|
import { formatPrice } from "../../../i18n/format";
|
||||||
|
|
||||||
|
const { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, cartLang } = window.__cartVars;
|
||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
const cart = getCart();
|
const cart = getCart();
|
||||||
const empty = document.getElementById("cart-empty");
|
const empty = document.getElementById("cart-empty");
|
||||||
|
|||||||
@@ -32,10 +32,10 @@ const t = useTranslations(lang);
|
|||||||
<div>
|
<div>
|
||||||
<label for="land">{t.checkout.country}</label>
|
<label for="land">{t.checkout.country}</label>
|
||||||
<select id="land">
|
<select id="land">
|
||||||
<option>Allemagne</option>
|
<option value="de">Allemagne</option>
|
||||||
<option>Autriche</option>
|
<option value="at">Autriche</option>
|
||||||
<option>Suisse</option>
|
<option value="ch">Suisse</option>
|
||||||
<option>Luxembourg</option>
|
<option value="lu">Luxembourg</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -74,19 +74,35 @@ const t = useTranslations(lang);
|
|||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<script define:vars={{ freeLabel: t.checkout.free, totalLabel: t.checkout.total, shippingLabel: t.cart.shipping, emptyCartAlert: t.checkout.emptyCartAlert, thankYouPath: "/fr/checkout/danke/", checkoutLang: lang }}>
|
<script define:vars={{ freeLabel: t.checkout.free, totalLabel: t.checkout.total, shippingLabel: t.cart.shipping, emptyCartAlert: t.checkout.emptyCartAlert, thankYouPath: "/fr/checkout/danke/", checkoutLang: lang }}>
|
||||||
|
// WICHTIG: define:vars-Skripte laufen als IIFE, keine "import"-Anweisungen möglich.
|
||||||
|
window.__checkoutVars = { freeLabel, totalLabel, shippingLabel, emptyCartAlert, thankYouPath, checkoutLang };
|
||||||
|
</script>
|
||||||
|
<script type="module">
|
||||||
import { getCart, cartTotal } from "../../../scripts/cart";
|
import { getCart, cartTotal } from "../../../scripts/cart";
|
||||||
import { formatPrice } from "../../../i18n/format";
|
import { formatPrice } from "../../../i18n/format";
|
||||||
|
import { products } from "../../../data/products";
|
||||||
|
import { berechneVersandkosten } from "../../../data/versand";
|
||||||
|
|
||||||
|
const { freeLabel, totalLabel, shippingLabel, emptyCartAlert, thankYouPath, checkoutLang } = window.__checkoutVars;
|
||||||
|
|
||||||
const summary = document.getElementById("checkout-summary");
|
const summary = document.getElementById("checkout-summary");
|
||||||
|
const landSelect = document.getElementById("land");
|
||||||
const cart = getCart();
|
const cart = getCart();
|
||||||
const subtotal = cartTotal();
|
const subtotal = cartTotal();
|
||||||
const shipping = cart.length === 0 ? 0 : subtotal >= 75 ? 0 : 4.95;
|
|
||||||
|
function renderSummary() {
|
||||||
|
const land = landSelect.value;
|
||||||
|
const shipping = cart.length === 0 ? 0 : berechneVersandkosten(land, cart.map((i) => ({ slug: i.slug, preis: i.preis, menge: i.menge })), products);
|
||||||
|
|
||||||
summary.innerHTML = `
|
summary.innerHTML = `
|
||||||
${cart.map((i) => `<div class="row"><span>${i.menge}× ${i.name}</span><span>${formatPrice(i.preis * i.menge, checkoutLang)}</span></div>`).join("")}
|
${cart.map((i) => `<div class="row"><span>${i.menge}× ${i.name}</span><span>${formatPrice(i.preis * i.menge, checkoutLang)}</span></div>`).join("")}
|
||||||
<div class="row"><span>${shippingLabel}</span><span>${shipping === 0 ? freeLabel : formatPrice(shipping, checkoutLang)}</span></div>
|
<div class="row"><span>${shippingLabel}</span><span>${shipping === 0 ? freeLabel : formatPrice(shipping, checkoutLang)}</span></div>
|
||||||
<div class="row total"><span>${totalLabel}</span><span>${formatPrice(subtotal + shipping, checkoutLang)}</span></div>
|
<div class="row total"><span>${totalLabel}</span><span>${formatPrice(subtotal + shipping, checkoutLang)}</span></div>
|
||||||
`;
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSummary();
|
||||||
|
landSelect.addEventListener("change", renderSummary);
|
||||||
|
|
||||||
document.getElementById("checkout-form").addEventListener("submit", (e) => {
|
document.getElementById("checkout-form").addEventListener("submit", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|||||||
@@ -39,9 +39,15 @@ const t = useTranslations(lang);
|
|||||||
</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, cartLang: lang }}>
|
||||||
|
// WICHTIG: define:vars-Skripte laufen als IIFE, keine "import"-Anweisungen möglich.
|
||||||
|
window.__cartVars = { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, cartLang };
|
||||||
|
</script>
|
||||||
|
<script type="module">
|
||||||
import { getCart, updateQuantity, removeFromCart, cartTotal } from "../../../scripts/cart";
|
import { getCart, updateQuantity, removeFromCart, cartTotal } from "../../../scripts/cart";
|
||||||
import { formatPrice } from "../../../i18n/format";
|
import { formatPrice } from "../../../i18n/format";
|
||||||
|
|
||||||
|
const { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, cartLang } = window.__cartVars;
|
||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
const cart = getCart();
|
const cart = getCart();
|
||||||
const empty = document.getElementById("cart-empty");
|
const empty = document.getElementById("cart-empty");
|
||||||
|
|||||||
@@ -39,9 +39,15 @@ const t = useTranslations(lang);
|
|||||||
</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, cartLang: lang }}>
|
||||||
|
// WICHTIG: define:vars-Skripte laufen als IIFE, keine "import"-Anweisungen möglich.
|
||||||
|
window.__cartVars = { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, cartLang };
|
||||||
|
</script>
|
||||||
|
<script type="module">
|
||||||
import { getCart, updateQuantity, removeFromCart, cartTotal } from "../../scripts/cart";
|
import { getCart, updateQuantity, removeFromCart, cartTotal } from "../../scripts/cart";
|
||||||
import { formatPrice } from "../../i18n/format";
|
import { formatPrice } from "../../i18n/format";
|
||||||
|
|
||||||
|
const { freeShipFrom, removeLabel, perItemLabel, remainingTemplate, freeShippingText, cartLang } = window.__cartVars;
|
||||||
|
|
||||||
function render() {
|
function render() {
|
||||||
const cart = getCart();
|
const cart = getCart();
|
||||||
const empty = document.getElementById("cart-empty");
|
const empty = document.getElementById("cart-empty");
|
||||||
|
|||||||
Reference in New Issue
Block a user