// ============================================================ // Shared UI primitives + icon set // ============================================================ (function () { const I = { dash: 'M3 3h7v7H3zM14 3h7v7h-7zM14 14h7v7h-7zM3 14h7v7H3z', box: 'M21 8l-9-5-9 5 9 5 9-5zM3 8v8l9 5M21 8v8l-9 5M12 13v8', print: 'M6 9V3h12v6M6 18H4a2 2 0 0 1-2-2v-4a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v4a2 2 0 0 1-2 2h-2M6 14h12v8H6z', history: 'M3 3v6h6M3.5 9a9 9 0 1 1-1 4M12 7v5l3 2', settings: 'M4 21v-7M4 10V3M12 21v-9M12 8V3M20 21v-5M20 12V3M1 14h6M9 8h6M17 16h6', search: 'M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16zM21 21l-4.3-4.3', plus: 'M12 5v14M5 12h14', edit: 'M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7M18.5 2.5a2.12 2.12 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z', trash: 'M3 6h18M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2M10 11v6M14 11v6', check: 'M20 6L9 17l-5-5', x: 'M18 6L6 18M6 6l12 12', chevronD: 'M6 9l6 6 6-6', chevronR: 'M9 6l6 6-6 6', chevronL: 'M15 6l-6 6 6 6', logout: 'M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4M16 17l5-5-5-5M21 12H9', user: 'M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2M12 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8z', filter: 'M22 3H2l8 9.46V19l4 2v-8.54L22 3z', barcode: 'M3 5v14M7 5v14M11 5v9M11 17v2M15 5v14M19 5v9M19 17v2', layers: 'M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5', download: 'M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3', send: 'M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z', signal: 'M5 12.55a11 11 0 0 1 14.08 0M1.42 9a16 16 0 0 1 21.16 0M8.53 16.11a6 6 0 0 1 6.95 0M12 20h.01', alert: 'M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0zM12 9v4M12 17h.01', minus: 'M5 12h14', copy: 'M20 9H11a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2-2v-9a2 2 0 0 0-2-2zM5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1', tag: 'M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82zM7 7h.01', grid: 'M3 3h7v7H3zM14 3h7v7h-7zM14 14h7v7h-7zM3 14h7v7H3z', list: 'M8 6h13M8 12h13M8 18h13M3 6h.01M3 12h.01M3 18h.01', zap: 'M13 2L3 14h9l-1 8 10-12h-9l1-8z', trending: 'M23 6l-9.5 9.5-5-5L1 18M17 6h6v6', eye: 'M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7zM12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6z', refresh: 'M23 4v6h-6M1 20v-6h6M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15', clock: 'M12 22a10 10 0 1 0 0-20 10 10 0 0 0 0 20zM12 6v6l4 2', package: 'M16.5 9.4l-9-5.19M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z', bolt: 'M13 2L3 14h9l-1 8 10-12h-9l1-8z', sliders: 'M4 21v-7M4 10V3M12 21v-9M12 8V3M20 21v-5M20 12V3M1 14h6M9 8h6M17 16h6' }; function Icon({ name, size = 18, stroke = 1.7, color = 'currentColor', fill = 'none', style }) { const d = I[name] || I.box; return React.createElement('svg', { width: size, height: size, viewBox: '0 0 24 24', fill: fill, stroke: color, strokeWidth: stroke, strokeLinecap: 'round', strokeLinejoin: 'round', style: { flexShrink: 0, ...style } }, d.split('M').filter(Boolean).map((seg, i) => React.createElement('path', { key: i, d: 'M' + seg }) )); } function Badge({ children, tone = 'neutral', style }) { return React.createElement('span', { className: 'badge badge-' + tone, style }, children); } function BrandDot({ brand, size = 8 }) { return React.createElement('span', { style: { width: size, height: size, borderRadius: '50%', flexShrink: 0, background: brand === 'LUXURIATE' ? 'var(--lux)' : 'var(--vmv)', display: 'inline-block' } }); } function Btn({ children, icon, variant = 'default', size = 'md', onClick, disabled, style, title, full }) { return React.createElement('button', { className: `btn btn-${variant} btn-${size}` + (full ? ' btn-full' : ''), onClick, disabled, title, style }, icon && React.createElement(Icon, { name: icon, size: size === 'sm' ? 15 : 17, stroke: 1.9 }), children && React.createElement('span', null, children) ); } function Field({ label, children, hint, style }) { return React.createElement('label', { className: 'field', style }, label && React.createElement('span', { className: 'field-label' }, label), children, hint && React.createElement('span', { className: 'field-hint' }, hint) ); } function Modal({ open, onClose, title, children, footer, width = 520 }) { if (!open) return null; return React.createElement('div', { className: 'modal-backdrop', onMouseDown: onClose }, React.createElement('div', { className: 'modal', style: { width }, onMouseDown: (e) => e.stopPropagation() }, React.createElement('div', { className: 'modal-head' }, React.createElement('h3', null, title), React.createElement('button', { className: 'icon-btn', onClick: onClose }, React.createElement(Icon, { name: 'x', size: 18 })) ), React.createElement('div', { className: 'modal-body' }, children), footer && React.createElement('div', { className: 'modal-foot' }, footer) ) ); } function KPI({ label, value, sub, icon, tone = 'amber', trend }) { return React.createElement('div', { className: 'kpi' }, React.createElement('div', { className: 'kpi-top' }, React.createElement('div', { className: 'kpi-ico kpi-ico-' + tone }, React.createElement(Icon, { name: icon, size: 18, stroke: 1.8 })), trend && React.createElement('span', { className: 'kpi-trend' }, React.createElement(Icon, { name: 'trending', size: 13, stroke: 2 }), trend) ), React.createElement('div', { className: 'kpi-val' }, value), React.createElement('div', { className: 'kpi-label' }, label), sub && React.createElement('div', { className: 'kpi-sub' }, sub) ); } // Toast system function useToasts() { const [toasts, setToasts] = React.useState([]); const push = React.useCallback((msg, tone = 'ok') => { const id = Math.random().toString(36).slice(2); setToasts(t => [...t, { id, msg, tone }]); setTimeout(() => setToasts(t => t.filter(x => x.id !== id)), 3200); }, []); const node = React.createElement('div', { className: 'toast-wrap' }, toasts.map(t => React.createElement('div', { key: t.id, className: 'toast toast-' + t.tone }, React.createElement(Icon, { name: t.tone === 'ok' ? 'check' : t.tone === 'err' ? 'alert' : 'send', size: 16, stroke: 2.2 }), React.createElement('span', null, t.msg) )) ); return { push, node }; } function fmtNum(n) { return n.toLocaleString('en-IN'); } Object.assign(window, { Icon, Badge, BrandDot, Btn, Field, Modal, KPI, useToasts, fmtNum }); })();