// Lab tab: Process, Extrusion Deep Dive, Sourcing & Raw Materials function GLLab({ app, go }) { const [sub, setSub] = React.useState('process'); // process | extrusion | sourcing return (
GUSTAI LAB
}/> {/* Cook Timer banner */} {/* Sub tabs */}
{[['process','The Process'],['extrusion','Extrusion'],['sourcing','Sourcing']].map(([id, label]) => ( ))}
{sub === 'process' && } {sub === 'extrusion' && } {sub === 'sourcing' && }
); } function LabProcess({ go }) { const { heading, subheading, steps } = GL_LAB_CONTENT.process; return (

The Process

{subheading}
{steps.map((s, i) => (
{s.n}
{s.label}

{s.title}

{s.body}

))}
go('home')} icon={}> Taste the Result
); } function LabExtrusion() { const { heroImg, kicker, heading, cards, ctaLabel } = GL_LAB_CONTENT.extrusion; const mapped = cards.map((c, i) => ({ ...c, big: c.bigStat, accent: c.accent ? '#F5C64F' : undefined, closeable: !!c.accent, shapeTag: c.shapeTag ? 'SHAPE' : undefined, })); return (
{kicker}

{heading.split('\n').map((line, i) => {line}{i < heading.split('\n').length - 1 &&
}
)}

{mapped.map((s, i) => (
{s.closeable && ( )}
{s.n}

{s.title}

{s.big && ( <>
{s.big}
{s.label}
)} {s.label && !s.big && (
{s.label}
)}

{s.body}

{s.img && } {s.chips && (
{s.chips.map((c, j) => ( {c} {j < s.chips.length - 1 && //} ))}
)} {s.shapeTag && (
SHAPE
MF-04
)}
))}
Ready to cook?
); } function LabSourcing() { const { heading, subheading, items } = GL_LAB_CONTENT.sourcing; return (

Sourcing &
Raw Materials

{subheading}

{items.map((it, i) => (
{it.n}
{it.img && } {it.origin && (
{it.origin}
)}

{it.title}

{it.quote && (

{it.quote}

)} {it.chips && (
{it.chips.map(c => {c})}
)}

{it.body}

{it.stats && (
{it.stats.map(([k, v]) => (
{k} {v}
))}
)} {it.callout && (
{it.callout[0]}
{it.callout[1]}
)}
))}
); } // Profile (My Account summary) function GLProfile({ app, go }) { const rows = [ { id: 'subs', icon: 'M3 12a9 9 0 0 1 15-6.7l3 3M21 12a9 9 0 0 1-15 6.7l-3-3M15 3h6v6M9 21H3v-6', label: 'Subscriptions', sub: 'Active · Next: Mon, Oct 12', live: true, }, { id: 'track', icon: 'M3 12h18M3 6h18M3 18h18', label: 'Orders & Tracking', sub: `Order #GUSTAI-${app.orderNo}`, live: true, }, { id: 'notify', icon: 'M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9M13.7 21a2 2 0 0 1-3.4 0', label: 'Notifications', sub: 'Manage alerts & channel', live: true, }, { id: 'favorites', icon: 'M20.8 4.6a5.5 5.5 0 0 0-7.8 0L12 5.7l-1-1.1a5.5 5.5 0 0 0-7.8 7.8L12 21.2l8.8-8.8a5.5 5.5 0 0 0 0-7.8z', label: 'Favorites', get sub() { return app.favorites?.length ? `${app.favorites.length} saved item${app.favorites.length === 1 ? '' : 's'}` : 'No items saved yet'; }, live: true, }, { id: 'addresses', icon: 'M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z|12|10|3', label: 'Addresses', get sub() { const def = (app.addresses || []).find(a => a.isDefault); return def ? def.label : 'No addresses saved'; }, live: true, }, ]; function handleRow(id) { if (id === 'subs') return go('subs'); if (id === 'track') return go('track'); if (id === 'notify') return go('notifications'); if (id === 'favorites') return go('favorites'); if (id === 'addresses') return go('addresses'); } return (
PROFILE
}/>
{/* Identity card */}
{(app.userName || 'G')[0].toUpperCase()}
{app.userName || 'Lab Member'}
{app.phone || '+66 ··· ····'}

My Account

{/* Admin panel — only shown to verified owner */} {app.isAdmin && ( )} {/* Cook timer feature card */}
{rows.map(r => ( ))}
{/* Sign out */}
); } // Notifications settings screen function GLNotifications({ app, go }) { const [marketing, setMarketing] = React.useState(app.marketingConsent ?? false); const [orderAlerts, setOrderAlerts] = React.useState(true); const channel = app.channel || 'whatsapp'; const channelLabel = channel === 'line' ? 'LINE' : 'WhatsApp'; const channelIcon = channel === 'line' ? '💬' : '🟢'; function ToggleRow({ label, sub, value, onChange }) { return (
{label}
{sub}
); } return (
go('me')} center={
NOTIFICATIONS
}/>

Your
Alerts

{/* Channel badge */}
{channelIcon}
Messages sent via
{channelLabel}
Change in
account settings
{/* Toggles */}
{ setMarketing(v); if (app.setMarketingConsent) app.setMarketingConsent(v); }} />
{/* PDPA note */}
Your Rights

Under Thailand's PDPA you may withdraw marketing consent at any time. Order updates are required to fulfil your orders and cannot be disabled while you have an active account.

); } // Subscriptions dashboard (v3 refined) function GLSubscriptions({ app, go }) { const [paused, setPaused] = React.useState(false); const [schedule, setSchedule] = React.useState([true, true, false]); const current = app.subscription; return (
go('me')} center={
GUSTAI LAB
}/>

My
Subscriptions

Current Drop
{paused ? 'Paused' : 'Active'}
NEXT: MON, OCT 12
PASTA + SAUCE SET

{current.name}

Hand-cut ribbon pasta infused with black truffle shavings. Rich, earthy, and perfectly textured.

go('swap')} icon={}/> setPaused(!paused)} icon={paused ? :
}/> }/>
Upcoming Schedule
{[ { mo: 'OCT', d: '19', title: 'Campanelle', sub: 'Semolina & Water', on: schedule[0] }, { mo: 'OCT', d: '26', title: 'Rigatoni', sub: 'Whole Wheat', on: schedule[1] }, { mo: 'NOV', d: '02', title: 'Orecchiette', sub: 'Durum Wheat', on: schedule[2] }, ].map((d, i) => (
setSchedule(s => s.map((v, j) => j === i ? !v : v))} style={{ display: 'flex', alignItems: 'center', gap: 14, padding: '16px 0', borderBottom: `1px solid ${GL.line}`, cursor: 'pointer', opacity: d.on ? 1 : 0.5 }}>
{d.mo}
{d.d}
{d.title}
{d.sub}
{d.on && }
))}
); } function ActionRow({ label, onClick, icon }) { return ( ); } // Swap Complete Set (dark variant) function GLSwapSet({ app, go }) { const sets = [ { id: 'truffle', name: 'Truffle Mafaldine Set', img: 'https://images.unsplash.com/photo-1622973536968-3ead9e780960?w=800&q=80', includes: ['Fresh Mafaldine (200g)', 'Black Truffle Butter', 'Grana Padano DOP'], badge: 'BEST SELLER' }, { id: 'wagyu', name: 'Wagyu Bolognese Set', img: 'https://images.unsplash.com/photo-1587740908075-9e245311e158?w=800&q=80', includes: ['Tagliatelle (200g)', 'Slow-Cooked Wagyu Ragu', '24 Month Parmesan'] }, { id: 'vodka', name: 'Spicy Vodka Rigatoni', img: 'https://images.unsplash.com/photo-1551183053-bf91a1d81141?w=800&q=80', includes: ['Hand-rolled Rigatoni', 'Calabrian Chili Vodka Sauce', 'Basil Oil'], badge: 'NEW' }, { id: 'cacio', name: 'Cacio e Pepe Set', img: null, includes: ['Fresh Tonnarelli (200g)', 'Pecorino Romano Cream', 'Toasted Black Pepper'], soldOut: true }, ]; const [selected, setSelected] = React.useState(app.subscription.id); return (
go('subs')} center={
SWAP SET
} right={
DROP #42
FRI 24 NOV
}/>

Swap Your
Complete Set

{sets.map(s => { const isSel = selected === s.id; return (
{s.badge && (
{s.badge}
)} {s.soldOut && (
SOLD OUT
)}

{s.name}

Set Includes:
{s.includes.map(it => (
{it}
))}
); })}
{ app.setSubscription(sets.find(s => s.id === selected)); go('subs'); }}> Confirm Swap
); } // ── Favorites ────────────────────────────────────────────────────────────────── function GLFavorites({ app, go }) { const savedIds = app.favorites || []; const items = (window.GL_PASTA || []).filter(p => savedIds.includes(p.id)); return (
go('me')} center={
FAVORITES
}/>

Your
Favorites

{items.length === 0 ? (
Nothing saved yet

Tap the heart on any product
to save it here.

go('home')} variant="primary" icon={}> Browse Products
) : (
{items.map(p => (
{/* Thumbnail */}
{p.name}
{/* Info */}
{p.name}
{p.pair}
฿{p.price}
{/* Actions */}
))}
)}
); } // ── Addresses ────────────────────────────────────────────────────────────────── function GLAddresses({ app, go }) { const addresses = app.addresses || []; const [adding, setAdding] = React.useState(false); const [editing, setEditing] = React.useState(null); // address object being edited const [form, setForm] = React.useState({ label: '', text: '' }); const [confirmDelete, setConfirmDelete] = React.useState(null); function openAdd() { setForm({ label: '', text: '' }); setEditing(null); setAdding(true); } function openEdit(addr) { setForm({ label: addr.label, text: addr.text }); setEditing(addr); setAdding(true); } function handleSave() { if (!form.label.trim() || !form.text.trim()) return; const isFirst = addresses.length === 0 && !editing; app.saveAddress({ ...(editing || {}), label: form.label.trim(), text: form.text.trim(), isDefault: editing ? editing.isDefault : isFirst, }); setAdding(false); setEditing(null); } function handleDelete(id) { app.deleteAddress(id); setConfirmDelete(null); } return (
go('me')} center={
ADDRESSES
}/>

Delivery
Addresses

{/* Address list */} {addresses.length === 0 && !adding && (
No addresses saved

Add a delivery address to speed up checkout.

)}
{addresses.map(addr => (
{/* Default badge */} {addr.isDefault && (
Default
)}
{addr.label}
{addr.text}
{!addr.isDefault && ( )} {addresses.length > 1 && ( )}
))}
{/* Add / Edit form */} {adding && (
{editing ? 'Edit Address' : 'New Address'}
{[ { label: 'Label', key: 'label', ph: 'e.g. Home, Office, Villa 5' }, { label: 'Full Address', key: 'text', ph: 'Street, subdistrict, district, province' }, ].map(f => (
setForm(prev => ({ ...prev, [f.key]: e.target.value }))} placeholder={f.ph} style={{ width: '100%', border: `1.5px solid ${GL.ink}`, padding: '12px 14px', fontSize: 14, outline: 'none', boxSizing: 'border-box' }} />
))}
)} {/* Add new button */} {!adding && ( )}
{/* Delete confirmation overlay */} {confirmDelete && (
Remove Address?

This address will be permanently removed from your saved list.

)}
); } Object.assign(window, { GLLab, GLProfile, GLNotifications, GLFavorites, GLAddresses, GLSubscriptions, GLSwapSet });