/* AAU CRM — Polaris UI primitives. Exposes components on window. */ const { useState, useEffect, useRef, useMemo } = React; function Card({ title, subtitle, actions, children, pad, className = '', style }) { return (
{(title || actions) && (
{title &&
{title}
{subtitle &&
{subtitle}
}
} {actions &&
{actions}
}
)}
{children}
); } function Badge({ tone = 'neutral', dot, children }) { return {dot && }{children}; } function Tag({ children, onRemove }) { return {children}{onRemove && }; } function Avatar({ name, color, size = 32, src }) { const init = (name || '?').split(' ').slice(-2).map(w => w[0]).join('').toUpperCase().slice(0, 2); return
{src ? : init}
; } function Button({ variant = 'secondary', size, icon, iconRight, children, onClick, disabled, style, title }) { const cls = ['btn', 'btn-' + variant, size === 'sm' ? 'btn-sm' : '', !children ? 'btn-icon' : ''].join(' '); return ( ); } function IconButton({ name, onClick, title, size = 18 }) { return ; } function Field({ label, children }) { return
{label && }{children}
; } function Input({ icon, value, onChange, placeholder, type = 'text', style, onKeyDown, autoFocus }) { const el = onChange && onChange(e.target.value)} placeholder={placeholder} type={type} style={style} onKeyDown={onKeyDown} autoFocus={autoFocus} />; if (icon) return
{el}
; return el; } function Select({ value, onChange, options, style }) { return ( ); } function Seg({ value, onChange, options }) { return
{options.map(o => { const v = o.value ?? o, l = o.label ?? o; return ; })}
; } function PillTabs({ value, onChange, options }) { return
{options.map(o => { const v = o.value ?? o, l = o.label ?? o; return ; })}
; } function Switch({ on, onChange }) { return
onChange(!on)} />; } function Checkbox({ on, onChange }) { return
onChange(!on)}>{on && }
; } function Tabs({ tabs, value, onChange }) { return
{tabs.map(t => { const v = t.value ?? t, l = t.label ?? t; return ; })}
; } function KPI({ label, value, delta, deltaDir, sub, icon }) { const dir = deltaDir || (delta > 0 ? 'up' : delta < 0 ? 'down' : 'flat'); return (
{icon && }{label}
{value}
{(delta != null || sub) && (
{delta != null && {typeof delta === 'number' ? Math.abs(delta) + '%' : delta}} {sub && {sub}}
)}
); } function Progress({ value, max = 100, color, height = 7 }) { const pct = Math.min(100, Math.round((value / max) * 100)); return
; } function Drawer({ title, onClose, children, footer, width }) { return (
e.stopPropagation()}>
{title}
{children}
{footer &&
{footer}
}
); } function Modal({ title, onClose, children, footer, width }) { return (
e.stopPropagation()}>
{title}
{children}
{footer &&
{footer}
}
); } function EmptyState({ icon = 'inbox', title, desc, action }) { return
{title}
{desc &&
{desc}
}{action &&
{action}
}
; } function PageHead({ title, subtitle, actions, breadcrumb }) { return (
{breadcrumb &&
{breadcrumb.map((b, i) => {i > 0 && }{b.href ? {b.label} : {b.label}})}
}
{title}
{subtitle &&
{subtitle}
}
{actions &&
{actions}
}
); } // ---- domain helpers ---- function StageChip({ stageId }) { const s = AAU.stageById(stageId); if (!s) return null; return {s.code} {s.name}; } function GradeBadge({ grade }) { const map = { A: 'success', B: 'info', C: 'warning', D: 'neutral' }; return {grade}; } function SLATag({ sla, left }) { const map = { ok: { t: 'success', i: 'clock' }, warn: { t: 'warning', i: 'clock' }, bad: { t: 'critical', i: 'alert' } }; const m = map[sla] || map.ok; return {left}; } function ChannelDot({ ch, size = 20, showLabel }) { const c = AAU.channels[ch]; if (!c) return null; return {c.short[0]}{showLabel && {c.label}}; } function HotIcon() { return ; } // ---- playbook helpers ---- function SignalBadge({ signal, compact }) { if (!signal) return null; const map = { HIGH: { bg: '#fdecea', fg: '#c4320a', dot: '#e8590c' }, MED: { bg: '#fff4e0', fg: '#9a6700', dot: '#f59e0b' }, LOW: { bg: '#eef1f4', fg: '#5a6472', dot: '#94a3b8' } }; const m = map[signal.level] || map.LOW; return ( {signal.level}{!compact && signal.text ? ' · ' + signal.text : ''} ); } function ScriptTag({ type }) { const meta = (window.PLAYBOOK && PLAYBOOK.scriptTypeMeta[type]) || { label: type, color: '#6b7280' }; return {meta.label}; } function BranchTag({ branch }) { if (!branch) return null; const m = branch === 'A' ? { c: '#2563eb', t: 'Nánh A' } : { c: '#0d9488', t: 'Nhánh B' }; return {branch === 'A' ? 'A' : 'B'}; } function LaneTag({ lane }) { if (!lane) return null; const m = lane === 'hot' ? { c: '#ea580c', t: 'Hot lane' } : { c: '#6366f1', t: 'Nurture' }; return {m.t}; } function pbGuide(code) { return (window.PLAYBOOK && PLAYBOOK.guide[code]) || null; } function Money({ v, m }) { return {m ? AAU.fmtVNDm(v) : AAU.fmtVND(v)}; } // relative time function relTime(iso) { const diff = (Date.now() - new Date(iso).getTime()) / 60000; if (diff < 1) return 'vừa xong'; if (diff < 60) return Math.round(diff) + ' phút'; if (diff < 1440) return Math.round(diff / 60) + ' giờ'; return Math.round(diff / 1440) + ' ngày'; } Object.assign(window, { Card, Badge, Tag, Avatar, Button, IconButton, Field, Input, Select, Seg, PillTabs, Switch, Checkbox, Tabs, KPI, Progress, Drawer, Modal, EmptyState, PageHead, StageChip, GradeBadge, SLATag, ChannelDot, HotIcon, Money, relTime, SignalBadge, ScriptTag, BranchTag, LaneTag, pbGuide, });