Rabatt-System, größerer Mengen-Stepper, Zahlungsart zuerst, echte HasiDog-Inhalte

- Neues Rabatt-System: Prozent-/Betrag-Rabatt mit optionalem Ablaufdatum,
  Mengenrabatt-Staffel pro Produkt, sowie site-weite Gutscheincodes.
  Wird live im Warenkorb/Checkout berechnet (src/data/rabatt.ts,
  src/data/gutscheine.ts, cart.ts) und ist im Adminbereich direkt unter dem
  Preis bearbeitbar (config.yml: "Rabatte" + neue Collection
  "Gutscheincodes"). Effektiver Preis + Rabatt-Badge auf Produktkarten und
  Produktseite sichtbar.
- Mengen-Stepper auf der Produktseite vergrößert (bereits vorbereitet,
  jetzt mit Build/Test verifiziert).
- Zahlungsart im Checkout auf Platz 1, mit markenfarbigen, fertig
  gestalteten Buttons (PayPal/Klarna/Banküberweisung).
- Unterkategorie-Seiten zeigen jetzt VanVans eigene Produktbeschreibung
  und das echte Fotos des Produkts statt Platzhaltertext, mit kleinem
  Link zur HasiDog-Universum-Seite statt großem Button.

Verifiziert per Puppeteer-Funktionstest gegen den echten Production-Build
(Rabatt-Berechnung, Mengenrabatt, Gutschein-Einlösung, Checkout-Summary,
keine Konsolenfehler).
This commit is contained in:
qcigano
2026-08-02 18:59:11 +02:00
parent 0f93ab73c3
commit 6f850181f1
25 changed files with 1098 additions and 194 deletions
+41 -1
View File
@@ -1,6 +1,10 @@
// Rein clientseitiger Warenkorb (Phase 1, kein Backend). Speichert im localStorage.
// TODO Phase 2: durch echten Cloudflare-Worker-Warenkorb/Checkout ersetzen.
import { getProduct } from "../data/products";
import { effektiverZeilenpreis } from "../data/rabatt";
import { findeGutschein, gutscheinRabatt, type Gutschein } from "../data/gutscheine";
export interface CartItem {
slug: string;
name: string;
@@ -54,6 +58,42 @@ export function cartCount(): number {
return getCart().reduce((sum, i) => sum + i.menge, 0);
}
// 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
// ein nachträglich geänderter/abgelaufener Rabatt sich sofort korrekt auswirkt.
export function cartTotal(): number {
return getCart().reduce((sum, i) => sum + i.menge * i.preis, 0);
return getCart().reduce((sum, i) => {
const produkt = getProduct(i.slug);
return sum + (produkt ? effektiverZeilenpreis(produkt, i.menge) : i.menge * i.preis);
}, 0);
}
const COUPON_KEY = "vandiy_coupon_v1";
export function getCouponCode(): string {
if (typeof localStorage === "undefined") return "";
return localStorage.getItem(COUPON_KEY) || "";
}
export function setCouponCode(code: string) {
localStorage.setItem(COUPON_KEY, code.trim());
window.dispatchEvent(new CustomEvent("cart:changed", { detail: getCart() }));
}
export function clearCoupon() {
localStorage.removeItem(COUPON_KEY);
window.dispatchEvent(new CustomEvent("cart:changed", { detail: getCart() }));
}
/** Der aktuell eingelöste Gutschein, falls der gespeicherte Code (noch) gültig ist. */
export function appliedCoupon(): Gutschein | undefined {
const code = getCouponCode();
if (!code) return undefined;
return findeGutschein(code);
}
/** Rabatt-Betrag in Euro, den der eingelöste Gutschein auf die gegebene Zwischensumme gibt. */
export function couponDiscount(subtotal: number): number {
const g = appliedCoupon();
return g ? gutscheinRabatt(g, subtotal) : 0;
}