// UI primitives — shared across modules
// Icons inline (Lucide-style stroke), Stat, Card, Table, Badge, Button, Tabs
const { useState, useEffect, useRef, useMemo } = React;
// ============ ICONS ============
const Ic = ({ name, size = 16, ...rest }) => {
const s = size;
const common = { width: s, height: s, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.75, strokeLinecap: "round", strokeLinejoin: "round", ...rest };
const paths = {
home: <>>,
live: <>>,
store: <>>,
site: <>>,
box: <>>,
cart: <>>,
users: <>>,
chart: <>>,
megaphone: <>>,
settings: <>>,
search: <>>,
bell: <>>,
plus: <>>,
arrowRight: <>>,
chevronDown: <>>,
chevronRight: <>>,
x: <>>,
check: <>>,
download: <>>,
filter: <>>,
play: <>>,
pause: <>>,
edit: <>>,
trash: <>>,
package: <>>,
tag: <>>,
truck: <>>,
whatsapp: <>>,
instagram: <>>,
pix: <>>,
qr: <>>,
barcode: <>>,
gift: <>>,
sparkles: <>>,
dollar: <>>,
file: <>>,
folder: <>>,
map: <>>,
sliders: <>>,
layers: <>>,
flame: <>>,
refresh: <>>,
eye: <>>,
send: <>>,
user: <>>,
phone: <>>,
grid: <>>,
list: <>>,
print: <>>,
cake: <>>,
bag: <>>,
spinner: <>>,
moneyIn: <>>,
devices: <>>,
};
return ;
};
// ============ Currency helpers ============
const brl = (v) => "R$ " + Number(v).toLocaleString("pt-BR", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
const brlNoCents = (v) => "R$ " + Number(v).toLocaleString("pt-BR", { maximumFractionDigits: 0 });
const numFmt = (v) => Number(v).toLocaleString("pt-BR");
const pct = (v) => (v >= 0 ? "+" : "") + v.toFixed(1) + "%";
// ============ Stat card ============
const Stat = ({ label, value, valueSuffix, delta, glow, hint, footer }) => (
{label}
{value}{valueSuffix && {valueSuffix}}
{delta !== undefined && delta !== null && (
= 0 ? "up" : "down")}>{pct(delta)} vs. período ant.
)}
{hint &&
{hint}
}
{footer}
);
// ============ Badge ============
const Badge = ({ children, kind, dot }) => (
{dot && }
{children}
);
// ============ Toggle ============
const Toggle = ({ on, onChange }) => (
onChange && onChange(!on)} role="switch" aria-checked={on}/>
);
// ============ Tabs ============
const Tabs = ({ tabs, active, onChange }) => (
{tabs.map(t => (
))}
);
// ============ Status helpers ============
const statusKind = (s) => {
const m = (s || "").toLowerCase();
if (m.includes("pago") || m.includes("aprovad") || m.includes("entregue") || m.includes("conclu") || m.includes("recupera")) return "green";
if (m.includes("cancel") || m.includes("falha") || m.includes("vencido")) return "red";
if (m.includes("aguard") || m.includes("pendente") || m.includes("rascunho")) return "muted";
if (m.includes("envia") || m.includes("separa") || m.includes("vence")) return "cyan";
if (m.includes("agend") || m.includes("sacola")) return "purple";
if (m.includes("em dia")) return "green";
return "";
};
const StatusBadge = ({ status }) =>
{status};
// ============ Avatar ============
const ClientAvatar = ({ name, size = "" }) => {
const initials = name.split(" ").map(p => p[0]).slice(0, 2).join("").toUpperCase();
return
{initials}
;
};
// ============ Level ============
const Level = ({ n }) => {
const kind = n >= 8 ? "l-high" : n >= 5 ? "l-mid" : "l-low";
return
N{n};
};
// ============ Spark / line / bars ============
const LineChart = ({ data, height = 180, accent = "var(--accent)" }) => {
// data: [{d, v}]
const max = Math.max(...data.map(d => d.v));
const min = 0;
const w = 100;
const h = 100;
const pts = data.map((d, i) => {
const x = (i / (data.length - 1)) * w;
const y = h - ((d.v - min) / (max - min || 1)) * (h - 8) - 4;
return [x, y];
});
const path = pts.map((p, i) => (i === 0 ? "M" : "L") + p[0].toFixed(2) + " " + p[1].toFixed(2)).join(" ");
const area = path + ` L ${w} ${h} L 0 ${h} Z`;
return (
{data.filter((_, i) => i % Math.max(1, Math.floor(data.length / 6)) === 0).map((d, i) => {d.d})}
);
};
const BarH = ({ rows, max }) => {
const m = max || Math.max(...rows.map(r => r.value));
return (
{rows.map((r, i) => (
{r.label}
{r.display || r.value}
))}
);
};
const Donut = ({ segments, center, label }) => {
// segments: [{value, color, label}]
const total = segments.reduce((s, x) => s + x.value, 0);
let acc = 0;
const r = 36, c = 2 * Math.PI * r;
return (
);
};
// ============ Empty state ============
const Empty = ({ icon = "box", title, hint, action }) => (
{title}
{hint &&
{hint}
}
{action &&
{action}
}
);
Object.assign(window, {
Ic, Stat, Badge, Toggle, Tabs, StatusBadge, ClientAvatar, Level,
LineChart, BarH, Donut, Empty,
brl, brlNoCents, numFmt, pct, statusKind,
});