// aerOS Guest v2 — Menu screen: hero + featured + sticky scroll-spy nav + live search + filters + sections. const { useState: _mn2S, useRef: _mn2R, useMemo: _mn2M, useEffect: _mn2E } = React; function G2Menu({ C, t, lang, tw, onSelect, onQuickAdd, onCart, onWaiter, cartCount, cartTotal, onLang, onCurrency, brand, brandSub, onBack, onSettings }) { const D = window.G2DATA; const animate = tw.animate; const [activeCat, setActiveCat] = _mn2S(D.CATS[0].id); const [q, setQ] = _mn2S(''); const [showSearch, setShowSearch] = _mn2S(false); const [diet, setDiet] = _mn2S([]); const MAXP = 750, MAXC = 900; const [priceMax, setPriceMax] = _mn2S(MAXP); const [calMax, setCalMax] = _mn2S(MAXC); const scroller = _mn2R(null), navRef = _mn2R(null), pillBar = _mn2R(null); const secRefs = _mn2R({}), pillRefs = _mn2R({}); const clickScroll = _mn2R(false); const staggerCls = animate ? 'g2-stagger' : ''; const name = p => lang === 'en' ? p.en : p.name; const filterCount = diet.length + (priceMax < MAXP ? 1 : 0) + (calMax < MAXC ? 1 : 0); const searching = q.trim() || filterCount > 0; const matches = p => { if (diet.length && !diet.every(d => p.diet.includes(d))) return false; if (p.price > priceMax) return false; if (p.cal > calMax) return false; if (q.trim()) { const ql = q.toLowerCase(); return (p.name + ' ' + p.en + ' ' + p.desc + ' ' + p.en_desc).toLowerCase().includes(ql); } return true; }; const flat = _mn2M(() => D.PRODUCTS.filter(matches), [q, diet, priceMax, calMax]); const featured = _mn2M(() => D.PRODUCTS.filter(p => p.badge === 'chef' || p.badge === 'signature' || p.badge === 'popular').slice(0, 6), []); const dietCounts = _mn2M(() => { const c = {}; ['veg', 'vegan', 'gf', 'spicy'].forEach(d => c[d] = D.PRODUCTS.filter(p => p.diet.includes(d)).length); return c; }, []); // scroll spy const onScroll = () => { if (clickScroll.current || searching || !scroller.current || !navRef.current) return; const navBottom = navRef.current.getBoundingClientRect().bottom; let cur = D.CATS[0].id; for (const c of D.CATS) { const el = secRefs.current[c.id]; if (el && el.getBoundingClientRect().top - navBottom < 16) cur = c.id; } setActiveCat(cur); }; _mn2E(() => { // keep active pill visible const pill = pillRefs.current[activeCat], bar = pillBar.current; if (pill && bar) { const want = pill.offsetLeft - bar.offsetWidth / 2 + pill.offsetWidth / 2; bar.scrollTo({ left: Math.max(0, want), behavior: 'smooth' }); } }, [activeCat]); const goCat = (id) => { setActiveCat(id); const el = secRefs.current[id]; if (!el || !scroller.current || !navRef.current) return; const navH = navRef.current.offsetHeight; const delta = el.getBoundingClientRect().top - scroller.current.getBoundingClientRect().top - navH - 6; clickScroll.current = true; scroller.current.scrollTo({ top: scroller.current.scrollTop + delta, behavior: 'smooth' }); setTimeout(() => { clickScroll.current = false; }, 520); }; const layout = tw.layout; const gridStyle = layout === 'gallery' ? { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 } : layout === 'text' ? { display: 'block' } : { display: 'flex', flexDirection: 'column', gap: layout === 'magazine' ? 16 : 11 }; const toggleDiet = d => setDiet(s => s.includes(d) ? s.filter(x => x !== d) : [...s, d]); const _ctx = window.G2CTX || {}; const modeLabel = tw.mode === 'room' ? (t.roomLabel + (_ctx.room ? ' ' + _ctx.room : '')) : tw.mode === 'view' ? t.viewOnly : (t.tableLabel + (_ctx.table ? ' ' + _ctx.table : '')); // ── HERO ── const langSwitch = (onDark) => ( ); const iconBtn = (icon, onClick, onDark, label) => ( ); const rightCluster = (onDark) => (
{langSwitch(onDark)} {onSettings && iconBtn(Icon.settings, onSettings, onDark, t.settings)}
); let hero; if (tw.heroStyle === 'minimal') { hero = (
{onBack ? iconBtn(Icon.chevLeft, onBack, false, t.back) : } {rightCluster(false)}
{modeLabel}

{brand}

{brandSub}
); } else if (tw.heroStyle === 'gradient') { hero = (
{onBack && iconBtn(Icon.chevLeft, onBack, true, t.back)} {Icon.utensils({ size: 13 })}{modeLabel}
{rightCluster(true)}

{brand}

{brandSub}
); } else { // photo hero = (
{onBack && iconBtn(Icon.chevLeft, onBack, true, t.back)} {Icon.utensils({ size: 13 })}{modeLabel}
{rightCluster(true)}
{t.welcome}

{brand}

{brandSub}
); } return (
{hero} {/* featured carousel */} {tw.featured && !searching && (
{Icon.sparkles({ size: 17 })} {t.featured}
{featured.map(p => ( ))}
)} {/* sticky bar: search + filters + category nav */}
{Icon.search({ size: 18 })} setQ(e.target.value)} placeholder={t.search} style={{ flex: 1, border: 'none', background: 'transparent', outline: 'none', fontSize: 14.5, fontFamily: G2THEME.BODY_FONT, color: C.ink }} /> {q && }
{!searching && ( tw.catNav === 'tabs' ? (
{D.CATS.map(c => { const on = activeCat === c.id; return ( ); })}
) : (
{D.CATS.map(c => pillRefs.current[c.id] = el}> goCat(c.id)} C={C}>{lang === 'en' ? c.en : c.name})}
) )}
{/* body */}
0 ? 150 : 96 }}> {searching ? ( flat.length === 0 ? (
{Icon.search({ size: 28 })}
{t.noResults}
) : ( <>
{flat.length} {t.items}
{flat.map((p, i) => )}
) ) : ( D.CATS.map(c => { const items = D.PRODUCTS.filter(p => p.cat === c.id); if (!items.length) return null; return (
secRefs.current[c.id] = el} style={{ marginBottom: 26, scrollMarginTop: 120 }}>

{lang === 'en' ? c.en : c.name}

{items.length} {t.items}
{items.map((p, i) => )}
); }) )}
{/* filter sheet */} setShowSearch(false)} C={C} t={t} tw={tw} diet={diet} toggleDiet={toggleDiet} counts={dietCounts} priceMax={priceMax} setPriceMax={setPriceMax} calMax={calMax} setCalMax={setCalMax} maxP={MAXP} maxC={MAXC} resultCount={flat.length} onClear={() => { setDiet([]); setPriceMax(MAXP); setCalMax(MAXC); }} animate={animate} /> {/* floating actions */}
{cartCount > 0 && ( )}
{tw.mode !== 'view' && ( )}
); } window.G2Menu = G2Menu;