/* ============================================================ KeS Metas — Primitivos de UI (compartilhados via window) ============================================================ */ const { useState, useEffect, useRef, useMemo } = React; /* ---------- Ícones (estilo Lucide, traço arredondado) ---------- */ const ICON_PATHS = { dashboard: '', calendar: '', calendarCheck: '', users: '', user: '', target: '', sliders: '', logout: '', menu: '', plus: '', trash: '', pencil: '', check: '', checkCircle: '', x: '', arrowRight: '', chevronLeft: '', chevronRight: '', chevronDown: '', phone: '', link: '', handshake: '', trophy: '', banknote: '', lock: '', clock: '', trendingUp: '', filter: '', search: '', alert: '', mail: '', mapPin: '', eye: '', eyeOff: '', cloudCheck: '', refresh: '', sparkles: '', download: '', upload: '', externalLink: '', grip: '', message: '', tag: '', spreadsheet: '', video: '', userPlus: '', funnel: '', copy: '', }; function Icon({ name, size = 20, strokeWidth = 2, style, className }) { return React.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth, strokeLinecap: "round", strokeLinejoin: "round", style, className, dangerouslySetInnerHTML: { __html: ICON_PATHS[name] || "" }, }); } function GoogleG({ size = 18 }) { return ( ); } /* ---------- Logo ---------- */ function Logo({ size = 38 }) { return KeS; } /* ---------- Avatar ---------- */ function initials(nome) { const p = nome.trim().split(/\s+/); return (p[0][0] + (p[1] ? p[1][0] : "")).toUpperCase(); } function Avatar({ user, size = 38 }) { return (
{initials(user.nome)}
); } /* ---------- Botão ---------- */ function Btn({ variant = "primary", size, icon, iconRight, disc, children, ...rest }) { const cls = ["btn", `btn-${variant}`, size === "sm" ? "btn-sm" : "", rest.className || ""].join(" ").trim(); const { className, ...r } = rest; return ( ); } /* ---------- Progresso + tom de cor ---------- */ function tonePct(pct) { if (pct >= 100) return "ok"; if (pct >= 70) return "warn"; return "bad"; } function Progress({ pct }) { const t = tonePct(pct); return (
); } /* ---------- Badge ---------- */ function Badge({ tone = "muted", icon, children }) { return ( {icon && } {children} ); } /* ---------- Campos ---------- */ function Field({ label, error, children }) { return (
{label && } {children} {error &&
{error}
}
); } /* ---------- Modal ---------- */ function Modal({ title, onClose, children, footer, wide }) { useEffect(() => { const h = (e) => { if (e.key === "Escape") onClose(); }; window.addEventListener("keydown", h); return () => window.removeEventListener("keydown", h); }, [onClose]); return (
e.stopPropagation()}>

{title}

{children}
{footer &&
{footer}
}
); } /* ---------- Toast ---------- */ function Toast({ toast }) { if (!toast) return null; return (
{toast}
); } /* ============================================================ FILTRO DE PERÍODO ============================================================ */ const K = window.KES; function startOfWeek(d) { const x = new Date(d); const day = (x.getDay() + 6) % 7; x.setDate(x.getDate() - day); return x; } function buildPreset(name) { const today = new Date(K.SIM_TODAY); if (name === "hoje") return { preset: "hoje", start: K.iso(today), end: K.iso(today), label: "Hoje" }; if (name === "semana") { const s = startOfWeek(today); return { preset: "semana", start: K.iso(s), end: K.iso(today), label: "Esta semana" }; } if (name === "mes") { const s = new Date(today.getFullYear(), today.getMonth(), 1); return { preset: "mes", start: K.iso(s), end: K.iso(today), label: "Este mês" }; } if (name === "mesPassado") { const s = new Date(today.getFullYear(), today.getMonth() - 1, 1); const e = new Date(today.getFullYear(), today.getMonth(), 0); return { preset: "mesPassado", start: K.iso(s), end: K.iso(e), label: "Mês passado" }; } return buildPreset("mes"); } function PeriodFilter({ value, onChange }) { const [open, setOpen] = useState(false); const ref = useRef(null); useEffect(() => { const h = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); }; document.addEventListener("mousedown", h); return () => document.removeEventListener("mousedown", h); }, []); const shortcuts = [ ["hoje", "Hoje"], ["semana", "Esta semana"], ["mes", "Este mês"], ["mesPassado", "Mês passado"], ]; const labelText = value.preset === "custom" ? `${K.fmtBR(value.start)} – ${K.fmtBR(value.end)}` : value.label; return (
{open && (
{shortcuts.map(([k, lbl]) => ( ))}
onChange({ ...value, preset: "custom", start: e.target.value, label: "Personalizado" })} /> onChange({ ...value, preset: "custom", end: e.target.value, label: "Personalizado" })} />
)}
); } /* ============================================================ GRÁFICO DE BARRAS (evolução no período) ============================================================ */ function BarChart({ series, target, color = "var(--kes-red)", height = 150 }) { // series: [{ label, value }] const max = Math.max(target || 0, ...series.map((s) => s.value), 1); const W = Math.max(series.length * 26, 300), H = height, pad = 8; const bw = (W - pad * 2) / series.length; const y = (v) => H - pad - (v / max) * (H - pad * 2); const ty = target ? y(target) : null; const step = Math.max(1, Math.ceil(series.length / 12)); return ( {target ? ( meta {target} ) : null} {series.map((s, i) => { const x = pad + i * bw; const h = H - pad - y(s.value); const reached = target ? s.value >= target : true; return ( {i % step === 0 && ( {s.label} )} ); })} ); } /* ---------- Export global ---------- */ Object.assign(window, { Icon, GoogleG, Logo, Avatar, initials, Btn, tonePct, Progress, Badge, Field, Modal, Toast, PeriodFilter, buildPreset, BarChart, });