(() => {
const { Icon, Badge } = window.TaskFlowDesignSystem_dd53c2;
/* Chart primitives for the dashboards. Same shapes as the product's
analytics reference — capsule bars, a time heat grid, a segmented
category bar and KPI tiles with an open-arrow. */
function KpiTile({ label, value, icon, delta, tone = "teal", onOpen }) {
const [hover, setHover] = React.useState(false);
const tint = {
teal: ["var(--teal-100)", "var(--teal-600)"], mint: ["var(--mint-100)", "var(--mint-600)"],
gold: ["var(--gold-100)", "var(--gold-600)"], coral: ["var(--coral-100)", "var(--coral-600)"],
leaf: ["var(--leaf-100)", "var(--leaf-600)"],
}[tone];
const up = (delta || "").trim().startsWith("+");
return (
setHover(true)} onMouseLeave={() => setHover(false)}
style={{
display: "flex", flexDirection: "column", gap: 12, padding: 16, borderRadius: "var(--radius-xl)",
background: "var(--surface-glass)", backdropFilter: "var(--blur-glass)", WebkitBackdropFilter: "var(--blur-glass)",
border: "1px solid var(--border-glass)", boxShadow: hover ? "var(--glow-teal)" : "var(--shadow-glass)",
transform: hover ? "var(--lift-hover)" : "none", transition: "var(--transition-float)", minWidth: 0,
}}>
{label}
{value}
{delta && (
{delta}
vs bulan lalu
)}
);
}
/* Capsule bar chart. series: [{ label, value, secondary }] */
function CapsuleBars({ series = [], max, height = 210, unit = "", legend = [] }) {
const phone = useBreakpoint() === "mobile";
const [active, setActive] = React.useState(null);
const raw = max || Math.max(1, ...series.map((s) => s.value));
const top = Math.ceil(raw / 4) * 4; // keeps the four axis steps even
const peak = series.reduce((a, b) => (b.value > (a ? a.value : -1) ? b : a), null);
const shown = active != null ? series[active] : peak;
return (
{legend.length > 0 && (
{legend.map(([label, color]) => (
{label}
))}
)}
{[4, 3, 2, 1, 0].map((i) => (
{Math.round((top / 4) * i)}{unit}
))}
{shown && !phone && (
{shown.label}
{shown.value}{unit}
{shown.note && {shown.note} }
)}
{series.map((s, i) => {
const h = Math.max(4, Math.round(((s.value / top) * (height - 34))));
const on = active === i || (active == null && shown === s);
return (
setActive(i)} onMouseLeave={() => setActive(null)}
style={{ flex: 1, minWidth: 0, display: "flex", flexDirection: "column", alignItems: "center", gap: 8, cursor: "default" }}>
{on && }
{s.label}
);
})}
);
}
/* Time-of-day × weekday heat grid. cells: { [row]: number[] } */
function HeatGrid({ rows = [], cols = [], values = [], max }) {
const top = max || Math.max(1, ...values.flat());
const shade = (v) => {
if (!v) return ["var(--neutral-100)", "transparent"];
const r = v / top;
if (r > 0.75) return ["var(--teal-600)", "0 4px 12px -4px rgba(19,168,140,.6)"];
if (r > 0.5) return ["var(--teal-500)", "none"];
if (r > 0.25) return ["var(--leaf-400)", "none"];
return ["var(--teal-200)", "none"];
};
return (
{rows.map((label, r) => (
{label}
{cols.map((c, i) => {
const v = (values[r] || [])[i] || 0;
const [bg, sh] = shade(v);
return ;
})}
))}
{cols.map((c, i) => (
{c}
))}
);
}
/* Segmented share bar with a legend underneath. parts: [{label,value,color}] */
function ShareBar({ parts = [], total }) {
const sum = total || parts.reduce((a, p) => a + p.value, 0) || 1;
return (
{parts.map((p) => (
))}
{parts.map((p) => (
{p.label} {Math.round((p.value / sum) * 100)}%
))}
);
}
/* Two-endpoint route strip (pipeline hand-off). */
function RouteStrip({ fromLabel, fromMeta, toLabel, toMeta, progress = 50 }) {
return (
{fromLabel}
{fromMeta}
{toLabel}
{toMeta}
);
}
Object.assign(window, { KpiTile, CapsuleBars, HeatGrid, ShareBar, RouteStrip });
})();