// Pantry / Meal Plan / Shopping List screens — with backup code, email menu, currency const Pantry = ({ mobile = false }) => { const [staples, setStaples] = React.useState(['雞蛋','米飯','醬油','蔥','蒜','薑','鹽','糖','米酒']); const [today, setToday] = React.useState(['番茄','豆腐']); const [v, setV] = React.useState(''); const [showCode, setShowCode] = React.useState(false); const [showRestore, setShowRestore] = React.useState(false); const [restoreV, setRestoreV] = React.useState(''); // Pseudo-deterministic backup code const code = React.useMemo(() => { const s = (staples.length + today.length) * 7 + 235; const letters = ['BBQ','SOY','WOK','TOFU','MISO','CHILI','BASIL','MINT']; return `${letters[(s * 3) % letters.length]}-${String(s % 999).padStart(3,'0')}`; }, [staples, today]); const W = mobile ? 'm' : 'd'; return (
Pantry Mode · 我的冰箱

記住你的常備食材,
每次搜索少打 5 個字。

把家裡常備的調味料與主食記在這裡。每次搜索時自動帶入,你只需輸入今天買的特殊食材即可。資料儲存在你的瀏覽器,不需要登入。

常備食材 {staples.length} 樣

這些每天都有 — 醬油、蛋、米、蔥薑蒜…

{staples.map(s => ( {s} ))}
{e.preventDefault(); if(v){setStaples([...staples, v]); setV('');}}}> setV(e.target.value)} placeholder="加入常備食材…"/>

今日特殊食材 {today.length} 樣

今天剛買的、快過期的、想用掉的…

{today.map(s => ( {s} ))}
{/* Backup code section — the surprise/delight feature */}
備份 · 跨裝置同步

不想註冊?用一組代碼帶走你的冰箱。

產生一組短代碼,在另一台手機/電腦輸入即可恢復你的常備食材、收藏食譜、購物清單。零帳號、零密碼、零追蹤。

你的備份代碼 {code} {staples.length + today.length} 樣食材 · {(staples.length+today.length)*4} 道相關食譜 · 15 個收藏
setShowCode(false)} title="掃描 · 在手機上恢復">
{/* Stylized QR placeholder */} {Array.from({length: 100}).map((_, i) => { const x = i % 10, y = Math.floor(i / 10); const seed = (x * 7 + y * 13 + code.length * 3) % 100; if (seed < 45) return ; return null; })} {/* QR finder squares */}
代碼 {code}

用另一台裝置打開 airecipelive.com,到「我的冰箱」→「輸入代碼恢復」貼上即可。

代碼有效期 · 30 天
setShowRestore(false)} title="輸入備份代碼">

在另一台裝置產生的 6–8 位代碼,輸入後即恢復常備食材、收藏與購物清單。

setRestoreV(e.target.value.toUpperCase())} placeholder="例:BBQ-742" /> 🔒 端對端 · 我們無法讀取你的資料內容
); }; const MealPlan = ({ mobile = false }) => { const W = mobile ? 'm' : 'd'; const days = ['週一','週二','週三','週四','週五','週六','週日']; const meals = ['早','午','晚']; const recipes = window.RECIPES; const money = useMoney(); const [showEmail, setShowEmail] = React.useState(false); const [emailV, setEmailV] = React.useState(''); const [sent, setSent] = React.useState(false); // Mock TWD prices const total = money(1840); const perPerson = money(460); return (
每週膳食計劃 · Phase 2

這週 七天
讓我們替你規劃菜單。

告訴我們你的飲食偏好、家庭人數、想避開的食材,系統會生成一週菜單與一張統合採購清單。

偏好 人數
{!mobile ? (
{days.map(d =>
{d}
)} {meals.map((m, mi) => (
{m}餐
{days.map((d, di) => { const r = recipes[(mi*7+di) % recipes.length]; return (
{r.title} {r.time}分 · {r.cal}卡
); })}
))}
) : (
{days.map((d, di) => (

{d}

{meals.map((m, mi) => { const r = recipes[(mi*7+di) % recipes.length]; return (
{m}餐{r.title}{r.time}分 · {r.cal}卡
); })}
))}
)}
21 餐本週菜單
{total.sym} {total.num} 預估採購 · {total.code}
{perPerson.sym} {perPerson.num}每人每週
43 樣食材清單
菜單已自動儲存到此瀏覽器 · 不需要註冊。換裝置?到 {}}>我的冰箱 → 備份代碼 即可同步。
{setShowEmail(false); setSent(false);}} title="把這份菜單寄給你"> {!sent ? (

輸入信箱,我們會把這週的菜單與採購清單寄給你。不訂閱、不發其他信,承諾就是這一封。

setEmailV(e.target.value)} placeholder="your@email.com" />
) : (

已寄出 ✓

請查看 {emailV} 的收件匣。沒收到?看看垃圾信。

)}
); }; const Shopping = ({ mobile = false }) => { const W = mobile ? 'm' : 'd'; const [checked, setChecked] = React.useState(new Set()); const money = useMoney(); const list = [ { c: '蔬菜', items: [ { n: '中型番茄', q: '6 顆', from: '番茄炒蛋 · 紅酒燉牛肉', p: 90 }, { n: '洋蔥', q: '3 顆', from: '蛋包飯 · 紅酒燉牛肉', p: 60 }, { n: '馬鈴薯', q: '4 顆', from: '紅酒燉牛肉 · 烤雞', p: 80 }, { n: '蒜', q: '1 整顆', from: '蒜香義麵', p: 30 }, ]}, { c: '肉品 · 海鮮', items: [ { n: '雞腿肉', q: '500g', from: '蛋包飯', p: 180 }, { n: '牛肋條', q: '800g', from: '紅酒燉牛肉', p: 480 }, { n: '蝦仁', q: '200g', from: '蝦仁炒飯', p: 220 }, ]}, { c: '其他', items: [ { n: '雞蛋', q: '1 盒', from: '番茄炒蛋 · 蛋包飯 · 蝦仁炒飯', p: 95 }, { n: '紅酒', q: '1 瓶', from: '紅酒燉牛肉', p: 380 }, { n: '迷迭香', q: '1 小束', from: '香草烤全雞', p: 50 }, ]}, ]; const total = list.reduce((s,c)=>s+c.items.length, 0); const done = checked.size; // Cost calculation const allItems = list.flatMap((cat, ci) => cat.items.map((it, i) => ({...it, key: cat.c+i}))); const totalTwd = allItems.reduce((s, it) => s + it.p, 0); const remainingTwd = allItems.filter(it => !checked.has(it.key)).reduce((s, it) => s + it.p, 0); const totalM = money(totalTwd); const remainM = money(remainingTwd); return (
購物清單

本週要買的,
都在這張單上。

統合自 5 道食譜 的食材清單,已自動去重並按超市動線分類。

{done} / {total} 項
還需要 {remainM.sym} {remainM.num} / 共 {totalM.sym} {totalM.num}
{list.map(cat => (

{cat.c}

{cat.items.map((it, i) => { const key = cat.c + i; const m = money(it.p); return ( ); })}
))}
一鍵下單到 Shopee 聯盟連結 · 系統會自動帶入清單 · 約省 15 分鐘
當前匯率 · 1 TWD ≈ {(CURRENCIES[money(1).code].rate).toFixed(3)} {money(1).code} · 每日自動更新(European Central Bank)
); }; Object.assign(window, { Pantry, MealPlan, Shopping });