// Table cards at 5 densities. All share the same data model — each card type // just renders a subset, sized for fast reading at-a-glance. const { TABLE_STATUS, TABLE_BADGES } = window; // ---------- shared bits ---------------------------------------------------- function fmtAmount(n) { if (n == null || n === 0) return '0.00'; return n.toFixed(2); } // Splits "12.34" into ["12", ".34"] so we can typeset cents smaller function splitAmount(n) { const s = fmtAmount(n); const [whole, cents] = s.split('.'); return [whole, '.' + cents]; } function avatarHash(name) { const palette = ['#3758c9', '#7a44c9', '#2f9e5e', '#d94b26', '#8a6d2b', '#0d7a8a', '#c93775', '#1d6f3a']; let h = 0; for (let i = 0; i < name.length; i++) h = (h * 31 + name.charCodeAt(i)) >>> 0; return palette[h % palette.length]; } function WaiterDot({ name, size = 22, ring }) { const initials = name.split(' ').map(p => p[0]).slice(0, 2).join('').toUpperCase(); return (
{initials}
); } function StackedAvatars({ waiters, size = 22, ring }) { if (!waiters?.length) return null; if (waiters.length >= 3) { return (
{waiters.slice(0, 2).map((w, i) => (
))}
+{waiters.length - 2}
); } return (
{waiters.map((w, i) => (
))}
); } function StatusPill({ status, size = 'md' }) { const s = TABLE_STATUS[status]; const sizes = { sm: { h: 18, px: 7, fs: 10 }, md: { h: 22, px: 9, fs: 11 }, lg: { h: 26, px: 11, fs: 12 }, }; const z = sizes[size]; return ( {s.label} ); } function BadgeChip({ kind, size = 'md' }) { const b = TABLE_BADGES[kind]; if (!b) return null; const sizes = { sm: { h: 20, fs: 11, ic: 12 }, md: { h: 24, fs: 12, ic: 14 }, lg: { h: 28, fs: 13, ic: 16 }, }; const z = sizes[size]; return ( {b.icon} {b.label} ); } function BadgeDot({ kind, size = 16 }) { const b = TABLE_BADGES[kind]; if (!b) return null; return (
{b.icon}
); } function Amount({ value, size = 22, color }) { const [w, c] = splitAmount(value); return (
{w} {c}€
); } // ---------- card shell ----------------------------------------------------- // All densities share this shell — just different content + dimensions. function CardShell({ status, w, h, children, padding }) { const s = TABLE_STATUS[status]; return (
{children}
); } // =========================================================================== // 1×1 — tiniest. Just NAME. Status is purely the card color. // =========================================================================== function Card1x1({ table, w, h }) { const t = table; // Show one badge dot if present (very subtle, top-right) const badge = t.badges[0]; return (
{t.name}
{badge && (
)}
); } // =========================================================================== // 2×1 — wider. NAME + status PILL + maybe one badge dot. // =========================================================================== function Card2x1({ table, w, h }) { const t = table; return (
{t.name}
{t.badges.length > 0 && (
{t.badges.slice(0, 2).map(b => )}
)}
); } // =========================================================================== // 2×2 — square. NAME big + status pill + amount + waiter dots + badges // =========================================================================== function Card2x2({ table, w, h }) { const t = table; const showAmount = t.amount > 0 || t.status === 'paid' || t.status === 'partial'; return (
{/* left column: name + pill (top), amount (bottom) */}
{t.name}
{showAmount && }
{/* right column: badges stacked vertically, bottom-aligned */} {t.badges.length > 0 && (
{t.badges.slice(0, 3).map(b => )}
)}
); } // =========================================================================== // 4×1 — wide horizontal. NAME · AMOUNT · status pill + waiter dots // =========================================================================== function Card4x1({ table, w, h }) { const t = table; const showAmount = t.amount > 0 || t.status === 'paid' || t.status === 'partial'; return (
{/* name */}
{t.name}
{/* amount (or spacer) */}
{showAmount && }
{/* badges */} {t.badges.length > 0 && (
{t.badges.slice(0, 2).map(b => )}
)} {/* status pill */}
); } // =========================================================================== // 4×2 — full detail. Name + section + status pill + amount + badges + waiters with names // =========================================================================== function Card4x2({ table, w, h }) { const t = table; const s = TABLE_STATUS[t.status]; const showAmount = t.amount > 0 || t.status === 'paid' || t.status === 'partial'; // First waiter name (or "Multiple") const waiterCaption = t.waiters.length === 0 ? 'Unassigned' : t.waiters.length >= 3 ? `${t.waiters.length} waiters` : t.waiters.map(w => w.split(' ')[0]).join(', '); return ( {/* top row: name + section + status pill | amount */}
{t.name}
{t.section}
{showAmount && }
{/* badges block — right-aligned, up to 4 in 2×2 grid, sits above waiter line */}
{t.badges.length > 0 && (
{t.badges.slice(0, 4).map(b => (
))}
)}
{/* bottom: waiters with names */}
{t.waiters.length === 0 ? ( Unassigned ) : ( <> {waiterCaption} )}
); } window.TableCards = { Card1x1, Card2x1, Card2x2, Card4x1, Card4x2 };