// VMAX POS app — PIN sign-in (multi-user), identity session (active user + idle/stale), // chrome, routing between order + queue, Tweaks dock. Landscape touchscreen. (function () { const DS = window.VMAX365DesignSystem_0f78b2; const { SegmentedControl } = DS; const { Icon, NetBadge } = window.VMAXUI; const { Avatar, PinDialog, Toaster, pushToast, NotifBadge } = window.VMAXW; const { useVMAX, notifications } = window.VMAXStore; const { OrderSurface } = window.VMAXORDER; const { MakerQueueSurface } = window.VMAXQUEUE; const { RequestStockSheet, CashierClose } = window.VMAXEXTRA; const { TweakDock } = window.VMAXTWEAKS; const IDLE_MS = 180000; const SURFACE_LABEL = { pos: 'Point of sale', queue: 'Order queue' }; function surfacesFor(p) { if (!p) return ['pos']; const r = p.roles, out = []; if (r.includes('cashier') || r.includes('owner') || r.includes('manager')) out.push('pos'); if (r.includes('maker') || r.includes('owner') || r.includes('manager')) out.push('queue'); return out.length ? out : ['pos']; } function useIdentity() { const [s, a] = useVMAX(); const [signedIds, setSignedIds] = React.useState([]); const [activeId, setActiveId] = React.useState(null); const [stale, setStale] = React.useState(false); const lastRef = React.useRef(Date.now()); const people = s.people; const signedIn = signedIds.map((id) => people.find((p) => p.id === id)).filter(Boolean); const active = people.find((p) => p.id === activeId) || signedIn[0] || null; React.useEffect(() => { const t = setInterval(() => { if (Date.now() - lastRef.current > IDLE_MS) setStale(true); }, 5000); return () => clearInterval(t); }, []); const markFresh = () => { lastRef.current = Date.now(); setStale(false); }; return { signedIn, active, stale, people, verify: (id, pin) => a.verifyPin(id, pin), markFresh, signIn: (p) => { setSignedIds((ids) => (ids.includes(p.id) ? ids : [...ids, p.id])); setActiveId(p.id); markFresh(); }, setActive: (p) => { setActiveId(p.id); markFresh(); }, signOut: (id) => { setSignedIds((ids) => ids.filter((x) => x !== id)); if (activeId === id) setActiveId(null); }, signOutAll: () => { setSignedIds([]); setActiveId(null); }, simulateIdle: () => { lastRef.current = Date.now() - IDLE_MS - 1; setStale(true); pushToast('Till marked idle — next print will re-verify', 'warn'); }, }; } // ---------- Sign in (PIN) ---------- function SignIn({ session }) { const [s, a] = useVMAX(); const [pinFor, setPinFor] = React.useState(null); const roster = s.people.filter((p) => p.roles.some((r) => ['cashier', 'maker', 'owner', 'manager'].includes(r))); const setMode = pinFor && (pinFor.pin == null || pinFor.resetPending); return (
VMAX 365POINT OF SALE

Heritage Community Park · enter your PIN to start a shift

Demo PINs — Clara 1111 · Joseph 2222 · Maria 3333 · Owner 2468 · Bright sets a new one. Or open Tweaks (⚙ lower-right) to jump in.

{roster.map((p) => ( ))}
session.verify(pinFor.id, pin)} onClose={() => setPinFor(null)} onSuccess={(pin) => { if (setMode) a.setPin(pinFor.id, pin); const p = { ...pinFor, pin }; session.signIn(p); pushToast('Signed in · ' + pinFor.name.split(' ')[0]); setPinFor(null); }} />
); } // ---------- Chrome user cluster ---------- function UserCluster({ session }) { const [open, setOpen] = React.useState(false); const [addOpen, setAddOpen] = React.useState(false); const [switchTo, setSwitchTo] = React.useState(null); const [reqOpen, setReqOpen] = React.useState(false); const [cashOpen, setCashOpen] = React.useState(false); const [s] = useVMAX(); const active = session.active; const others = s.people.filter((p) => p.roles.some((r) => ['cashier', 'maker', 'owner', 'manager'].includes(r)) && p.pin && !session.signedIn.find((x) => x.id === p.id)); return (
{open && (
setOpen(false)} style={{ position: 'fixed', inset: 0, zIndex: 79 }} />
Signed in ({session.signedIn.length})
{session.signedIn.map((p) => { const isA = active && p.id === active.id; return (
); })}
)} {/* switch active (PIN) */} session.verify(switchTo.id, pin)} onClose={() => setSwitchTo(null)} onSuccess={() => { session.setActive(switchTo); pushToast('Active user · ' + switchTo.name.split(' ')[0]); setSwitchTo(null); }} /> {/* add another (name -> PIN) */} {addOpen && setAddOpen(false)} />} setReqOpen(false)} who={active ? active.name : ''} /> setCashOpen(false)} who={active ? active.name : ''} />
); } const menuRow = { width: '100%', display: 'flex', alignItems: 'center', gap: 10, padding: '9px 10px', border: 'none', background: 'transparent', cursor: 'pointer', borderRadius: 'var(--radius-md)', color: 'var(--vmax-ink)', fontWeight: 600, fontSize: 13, fontFamily: 'var(--font-body)' }; function AddUserFlow({ session, others, onClose }) { const [pinFor, setPinFor] = React.useState(null); const { Sheet } = window.VMAXUI; if (pinFor) return session.verify(pinFor.id, pin)} onClose={onClose} onSuccess={() => { session.signIn(pinFor); pushToast(pinFor.name.split(' ')[0] + ' signed in'); onClose(); }} />; return (
Add another user
{others.map((p) => )} {others.length === 0 &&
Everyone's already signed in.
}
); } function App() { const [s, a] = useVMAX(); const session = useIdentity(); const notif = notifications(s); const surfaces = surfacesFor(session.active); const [surface, setSurface] = React.useState('pos'); React.useEffect(() => { if (!surfaces.includes(surface)) setSurface(surfaces[0]); }, [surfaces.join()]); const pick = (id) => s.people.find((p) => p.id === id); const roleJump = [ pick('u_clara') && { label: 'Cashier', person: pick('u_clara') }, pick('u_joseph') && { label: 'Maker', person: pick('u_joseph') }, pick('u_owner') && { label: 'Owner', person: pick('u_owner') }, ].filter(Boolean); const dock = session.signIn(p)} onSimulateIdle={session.active ? session.simulateIdle : null} />; if (!session.active) return {dock}; return (
VMAX 365POINT OF SALE
{surfaces.length > 1 &&
({ value: x, label: SURFACE_LABEL[x] }))} value={surface} onChange={setSurface} />{surfaces.includes('queue') && surface !== 'queue' && notif.unclaimed > 0 && }
}
a.setTweak('net', v)} />
{surface === 'pos' && } {surface === 'queue' && }
{dock}
); } window.VMAXPosApp = App; })();