// ── Gericht-Karte ──────────────────────────────────────────────────────────── function DishCard({ dish, onOpen }) { return ( ); } // ── Speisekarte ────────────────────────────────────────────────────────────── function Menu({ onOpen }) { const [active, setActive] = React.useState(CATEGORIES[0].id); const navRef = React.useRef(null); // Spy: welche Kategorie ist im Blick React.useEffect(() => { const secs = CATEGORIES.map((c) => document.getElementById("cat-" + c.id)).filter(Boolean); const io = new IntersectionObserver( (entries) => { entries.forEach((en) => { if (en.isIntersecting) setActive(en.target.id.replace("cat-", "")); }); }, { rootMargin: "-45% 0px -50% 0px", threshold: 0 } ); secs.forEach((s) => io.observe(s)); return () => io.disconnect(); }, []); const go = (id) => { const el = document.getElementById("cat-" + id); if (el) { const y = el.getBoundingClientRect().top + window.scrollY - 132; window.scrollTo({ top: y, behavior: "smooth" }); } }; return ( ); } // ── Gericht-Modal (Menge + Varianten) ──────────────────────────────────────── function DishModal({ dish, onClose, onAdd }) { const opts = dish.options || []; const [sel, setSel] = React.useState(() => { const init = {}; opts.forEach((o) => { init[o.id] = o.choices[0].id; }); return init; }); const [qty, setQty] = React.useState(1); const [note, setNote] = React.useState(""); const deltaSum = opts.reduce((s, o) => { const ch = o.choices.find((c) => c.id === sel[o.id]); return s + (ch ? ch.delta : 0); }, 0); const unit = dish.price + deltaSum; const total = unit * qty; // Esc schliesst React.useEffect(() => { const on = (e) => e.key === "Escape" && onClose(); document.addEventListener("keydown", on); document.body.style.overflow = "hidden"; return () => { document.removeEventListener("keydown", on); document.body.style.overflow = ""; }; }, []); const add = () => { const chosen = opts.map((o) => ({ label: o.label, value: o.choices.find((c) => c.id === sel[o.id]).label })); onAdd({ key: dish.id + "|" + Object.values(sel).join("-") + "|" + note.trim(), id: dish.id, name: dish.name, tone: dish.tone, unit, qty, options: chosen, note: note.trim(), }); onClose(); }; return (

{dish.name}

{CHF(dish.price)}

{dish.desc}

{dish.tags.map((t) => )} {dish.allergens.length > 0 && Allergene: }
{/* Optionen */} {opts.map((o) => (
{o.label}{o.required && · Pflicht}
{o.choices.map((c) => { const on = sel[o.id] === c.id; return ( ); })}
))} {/* Notiz */}
setNote(e.target.value)} maxLength={120} placeholder="z.B. ohne Zwiebeln, extra scharf …" className="w-full h-12 px-4 rounded-xl border border-line bg-card text-[15px] text-ink placeholder:text-muted/60 focus:border-terra outline-none" />
{/* Footer: Menge + Add */}
{qty}
); } Object.assign(window, { DishCard, Menu, DishModal });