// Shared UI primitives — Metal Padrão
const fmtBRL = (n) =>
(n || 0).toLocaleString("pt-BR", { style: "currency", currency: "BRL" });
const fmtNumber = (n, d = 0) =>
(n || 0).toLocaleString("pt-BR", { minimumFractionDigits: d, maximumFractionDigits: d });
const fmtDate = (iso) => {
if (!iso) return "—";
const [y, m, d] = iso.split("-");
return `${d}/${m}/${y}`;
};
const fmtDateShort = (iso) => {
if (!iso) return "—";
const [, m, d] = iso.split("-");
const meses = ["jan","fev","mar","abr","mai","jun","jul","ago","set","out","nov","dez"];
return `${d} ${meses[parseInt(m,10)-1]}`;
};
// Button
const Button = ({ children, variant = "primary", size = "md", icon, onClick, type = "button", disabled, full, ...rest }) => {
const cls = ["btn", `btn--${variant}`, `btn--${size}`, full && "btn--full", disabled && "btn--disabled"].filter(Boolean).join(" ");
return (
);
};
// Field wrapper
const Field = ({ label, required, hint, error, children, span = 12 }) => (
);
// Input
const Input = React.forwardRef(({ prefix, suffix, ...rest }, ref) => (
{prefix && {prefix}}
{suffix && {suffix}}
));
// Select
const Select = ({ options, value, onChange, placeholder, ...rest }) => (
);
// Currency input
const InputMoney = ({ value, onChange, ...rest }) => {
const handle = (e) => {
const raw = e.target.value.replace(/[^\d]/g, "");
onChange?.(raw ? parseInt(raw, 10) / 100 : 0);
};
return (
R$
);
};
// Badge / status
const Badge = ({ children, color, bg, dot, size = "md" }) => (
{dot && }
{children}
);
const StatusBadge = ({ statusId }) => {
const s = window.MP_DATA.statusList.find(x => x.id === statusId);
if (!s) return null;
return {s.nome};
};
const CnpjPill = ({ cnpjId }) => {
const c = window.MP_DATA.cnpjs.find(x => x.id === cnpjId);
if (!c) return null;
return {c.nome};
};
// Avatar (vendedor)
const Avatar = ({ vendedorId, size = 28 }) => {
const v = window.MP_DATA.vendedores.find(x => x.id === vendedorId);
if (!v) return null;
return (
{v.iniciais}
);
};
// Card
const Card = ({ children, title, action, padded = true, className = "" }) => (
{(title || action) && (
)}
{children}
);
// KPI tile
const KPI = ({ label, value, sub, accent, icon }) => (
{icon && }
{label}
{value}
{sub &&
{sub}
}
);
// Empty state
const Empty = ({ icon = "sparkle", title, hint, action }) => (
{title}
{hint &&
{hint}
}
{action &&
{action}
}
);
// Modal
const Modal = ({ open, onClose, title, children, footer, size = "md" }) => {
if (!open) return null;
return (
e.stopPropagation()}>
{title}
{children}
{footer &&
{footer}
}
);
};
// Drawer (slide from right)
const Drawer = ({ open, onClose, title, subtitle, children, footer }) => {
if (!open) return null;
return (
e.stopPropagation()}>
{title}
{subtitle &&
{subtitle}
}
{children}
{footer &&
{footer}
}
);
};
Object.assign(window, {
Button, Field, Input, InputMoney, Select,
Badge, StatusBadge, CnpjPill, Avatar,
Card, KPI, Empty, Modal, Drawer,
fmtBRL, fmtNumber, fmtDate, fmtDateShort,
});