// ── Abhol-Slots generieren ─────────────────────────────────────────────────── function buildSlots() { const open = 17 * 60; // 17:00 const close = 22 * 60 + 30; // 22:30 const now = new Date(); const nowMin = now.getHours() * 60 + now.getMinutes(); const earliest = Math.max(open, Math.ceil((nowMin + 20) / 15) * 15); const slots = []; for (let m = open; m <= close; m += 15) { slots.push({ value: pad(m), disabled: m < earliest }); } return { slots, earliest: pad(earliest <= close ? earliest : open) }; } function pad(min) { const h = Math.floor(min / 60), m = min % 60; return String(h).padStart(2, "0") + ":" + String(m).padStart(2, "0"); } // ── Warenkorb-Zeile ────────────────────────────────────────────────────────── function CartLine({ item, onQty, onRemove }) { return (

{item.name}

{CHF(item.unit * item.qty)}
{item.options.filter((o) => !/^(Ohne|Normal)/.test(o.value)).map((o) => (
{o.value}
))} {item.note &&
„{item.note}"
}
{item.qty}
); } // ── Cart-Panel mit Flow ────────────────────────────────────────────────────── function CartPanel({ open, cart, onClose, onQty, onRemove, onClear, onBrowse }) { const [step, setStep] = React.useState("cart"); // cart | time | checkout | done const [slot, setSlot] = React.useState(null); // null = asap const [form, setForm] = React.useState({ name: "", phone: "" }); const [pay, setPay] = React.useState("twint"); const [touched, setTouched] = React.useState(false); const [order, setOrder] = React.useState(null); const { slots, earliest } = React.useMemo(buildSlots, [open]); React.useEffect(() => { if (open) { setStep("cart"); setTouched(false); } }, [open]); React.useEffect(() => { document.body.style.overflow = open ? "hidden" : ""; return () => { document.body.style.overflow = ""; }; }, [open]); const count = cart.reduce((s, i) => s + i.qty, 0); const subtotal = cart.reduce((s, i) => s + i.unit * i.qty, 0); const pickupAt = slot || earliest; const nameOk = form.name.trim().length >= 2; const phoneOk = form.phone.replace(/\D/g, "").length >= 9; const formOk = nameOk && phoneOk; const place = () => { setTouched(true); if (!formOk) return; const num = "OL-" + String(Math.floor(1000 + Math.random() * 9000)); setOrder({ num, at: pickupAt, total: subtotal, items: count, name: form.name.trim(), pay }); setStep("done"); onClear(); }; const PAYMENTS = [ { id: "twint", label: "TWINT", hint: "QR beim Abholen" }, { id: "card", label: "Karte", hint: "Vor Ort kontaktlos" }, { id: "cash", label: "Bei Abholung", hint: "Bar oder Karte" }, ]; if (!open) return null; return (
{/* Header */}
{step !== "cart" && step !== "done" && ( )}

{step === "cart" && "Dein Warenkorb"} {step === "time" && "Abholzeit"} {step === "checkout" && "Deine Angaben"} {step === "done" && "Bestätigt"}

{/* Stepper */} {step !== "done" && (
{["cart", "time", "checkout"].map((s, i) => { const order = { cart: 0, time: 1, checkout: 2 }; const cur = order[step]; const me = i; return ( ); })}
)} {/* Body */}
{/* STEP CART */} {step === "cart" && ( cart.length === 0 ? (

Noch nichts drin

Stöbere durch die Speisekarte und leg deine Favoriten hier ab.

) : (
{cart.map((i) => )}
) )} {/* STEP TIME */} {step === "time" && (

Wann möchtest du abholen? {RESTAURANT.address.street}, {RESTAURANT.address.city}

Oder Zeit wählen — heute
{slots.map((s) => ( ))}
)} {/* STEP CHECKOUT */} {step === "checkout" && (
setForm((f) => ({ ...f, name: e.target.value }))} placeholder="Vor- und Nachname" autoComplete="name" className={cx("w-full h-13 py-3.5 px-4 rounded-xl border bg-card text-[15px] text-ink placeholder:text-muted/60 outline-none transition-colors", touched && !nameOk ? "border-terra" : "border-line focus:border-terra")} /> {touched && !nameOk &&

Bitte gib deinen Namen an.

}
setForm((f) => ({ ...f, phone: e.target.value }))} placeholder="079 123 45 67" inputMode="tel" autoComplete="tel" className={cx("w-full h-13 py-3.5 px-4 rounded-xl border bg-card text-[15px] text-ink placeholder:text-muted/60 outline-none transition-colors", touched && !phoneOk ? "border-terra" : "border-line focus:border-terra")} /> {touched && !phoneOk &&

Für Rückfragen zur Bestellung.

}
Zahlung
{PAYMENTS.map((p) => { const on = pay === p.id; return ( ); })}

Die Bezahlung erfolgt vor Ort bei der Abholung — hier wird nichts belastet.

)} {/* STEP DONE */} {step === "done" && order && (

Grazie, {order.name.split(" ")[0]}!

Deine Bestellung ist aufgegeben.

Bestellnummer {order.num}
Bereit zur Abholung um
{order.at} Uhr
{order.items} Artikel · {PAYMENTS.find((p) => p.id === order.pay).label} {CHF(order.total)}

{RESTAURANT.address.street}, {RESTAURANT.address.city}

)}
{/* Footer-Aktionen */} {step !== "done" && cart.length > 0 && (
{step === "cart" ? "Zwischensumme" : "Total"} · {count} Artikel {CHF(subtotal)}
{step === "cart" && ( )} {step === "time" && ( )} {step === "checkout" && ( )}
)} {step === "done" && (
)}
); } Object.assign(window, { CartPanel, buildSlots });