/* cart.jsx — slide-out cart drawer + checkout flow */ const DELIVERY_FEE = 6.0; const FREE_OVER = 60.0; function CartDrawer({ open, items, soupsById, onClose, onInc, onDec, onRemove, onCheckout }) { const subtotal = items.reduce((s, it) => s + (soupsById[it.id]?.price || it.price || 0) * it.qty, 0); const count = items.reduce((s, it) => s + it.qty, 0); return (
); } /* ---------------- CHECKOUT ---------------- */ function Checkout({ items, soupsById, onClose, onDone }) { const [step, setStep] = React.useState("details"); // details | pay | confirm const [method, setMethod] = React.useState("delivery"); const [form, setForm] = React.useState({ name: "", email: "", phone: "", street: "", zip: "", city: "Zürich", notes: "" }); const [pay, setPay] = React.useState({ card: "", exp: "", cvc: "", holder: "" }); const [errors, setErrors] = React.useState({}); const [orderNo] = React.useState(() => "SK-" + Math.floor(1000 + Math.random() * 9000)); const [processing, setProcessing] = React.useState(false); const subtotal = items.reduce((s, it) => s + (soupsById[it.id]?.price || it.price || 0) * it.qty, 0); const fee = method === "pickup" ? 0 : subtotal >= FREE_OVER ? 0 : DELIVERY_FEE; const total = subtotal + fee; const set = (k, v) => { setForm((f) => ({ ...f, [k]: v })); setErrors((e) => ({ ...e, [k]: null })); }; const validateDetails = () => { const e = {}; if (!form.name.trim()) e.name = "Bitte Name angeben"; if (!/\S+@\S+\.\S+/.test(form.email)) e.email = "Gültige E-Mail angeben"; if (!form.phone.trim()) e.phone = "Telefon für Rückfragen"; if (method === "delivery") { if (!form.street.trim()) e.street = "Strasse & Nr. angeben"; if (!/^\d{4}$/.test(form.zip.trim())) e.zip = "PLZ"; } setErrors(e); return Object.keys(e).length === 0; }; const goPay = () => { if (validateDetails()) { setStep("pay"); window.scrollTo(0, 0); } }; const doPay = (e) => { e.preventDefault(); setProcessing(true); setTimeout(() => { setProcessing(false); setStep("confirm"); onDone(); }, 1400); }; const steps = [ { id: "details", n: 1, lbl: "Angaben" }, { id: "pay", n: 2, lbl: "Zahlung" }, { id: "confirm", n: 3, lbl: "Bestätigung" }, ]; const stepIdx = steps.findIndex((s) => s.id === step); // formatting helpers for the mock card form const fmtCard = (v) => v.replace(/\D/g, "").slice(0, 16).replace(/(.{4})/g, "$1 ").trim(); const fmtExp = (v) => { const d = v.replace(/\D/g, "").slice(0, 4); return d.length > 2 ? d.slice(0, 2) + "/" + d.slice(2) : d; }; const Summary = () => ( ); return (
{steps.map((s, i) => ( {i > 0 && } {i < stepIdx ? : s.n} {s.lbl} ))}
{step === "details" && (

Lieferung & Kontakt

Kein Konto nötig – Christine bestätigt deine Bestellung per Nachricht.

Wie möchtest du erhalten?
setMethod("delivery")}>
LieferungZürich & Zollikon · Di & Fr
setMethod("pickup")}>
AbholungWollishofen · gratis
Deine Angaben
set("name", e.target.value)} placeholder="Vor- und Nachname" /> {errors.name &&
{errors.name}
}
set("email", e.target.value)} placeholder="du@email.ch" /> {errors.email &&
{errors.email}
}
set("phone", e.target.value)} placeholder="079 123 45 67" /> {errors.phone &&
{errors.phone}
}
{method === "delivery" && (
Lieferadresse
set("street", e.target.value)} placeholder="Seestrasse 12" /> {errors.street &&
{errors.street}
}
set("zip", e.target.value)} placeholder="8038" inputMode="numeric" /> {errors.zip &&
{errors.zip}
}
set("city", e.target.value)} />
)}
)} {step === "pay" && (

Zahlung

Sicher bezahlen – deine Suppen werden danach frisch für dich gekocht.

setPay({ ...pay, holder: e.target.value })} placeholder="Name auf der Karte" />
setPay({ ...pay, card: fmtCard(e.target.value) })} placeholder="4242 4242 4242 4242" inputMode="numeric" /> VISA
setPay({ ...pay, exp: fmtExp(e.target.value) })} placeholder="MM/JJ" inputMode="numeric" />
setPay({ ...pay, cvc: e.target.value.replace(/\D/g, "").slice(0, 4) })} placeholder="123" inputMode="numeric" />
Verschlüsselte Zahlung · powered by Stripe
Demo-Modus: Es wird keine echte Zahlung ausgeführt. Beliebige Testdaten funktionieren.
)} {step === "confirm" && (
Bestellung {orderNo}

Danke, {form.name.split(" ")[0] || "und bis bald"}!

Deine Bestellung ist bei Christine eingegangen. Du erhältst gleich eine Bestätigung an {form.email}.

{method === "pickup" ? "Abholung" : "Lieferung an"}{method === "pickup" ? "Wollishofen, nach Vereinbarung" : `${form.street}, ${form.zip} ${form.city}`}
Suppen{items.reduce((s, it) => s + it.qty, 0)} Portionen
Voraussichtlich{method === "pickup" ? "ab morgen abholbereit" : "nächster Liefertag (Di/Fr)"}
Bezahlt{CHF(total)}
)}
); } Object.assign(window, { CartDrawer, Checkout });