/* 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 (
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()}>
{children}
{footer &&
{footer}
}
);
}
function Modal({ title, onClose, children, footer, width }) {
return (
e.stopPropagation()}>
{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,
});