/* 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 (
{items.length === 0 ? (
Noch ist der Topf leer
Stöbere durch die Speisekarte und füge deine Lieblingssuppen hinzu.
) : (
{items.map((it) => {
const s = soupsById[it.id] || it;
return (
{s.name}
{s.size} · {CHF(s.price)}
onDec(it.id)} onInc={() => onInc(it.id)} />
{CHF(s.price * it.qty)}
);
})}
Zwischensumme{CHF(subtotal)}
Lieferung
{subtotal >= FREE_OVER ? "gratis ab CHF 60" : "ab " + CHF(DELIVERY_FEE)}
Total{CHF(subtotal)}
Lieferung in Zürich & Zollikon · Versand kantonweit
)}
);
}
/* ---------------- 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}
}
)}
)}
{step === "pay" && (
Zahlung
Sicher bezahlen – deine Suppen werden danach frisch für dich gekocht.
)}
{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 });