// Dashboard Kit — shared React components // Loaded via // REQUIRES: window.DK.theme + window.DK.utils + window.React (already loaded) // Exposes: window.DK.components (function () { const ns = (window.DK = window.DK || {}); const T = ns.theme; // ── BackToHub ───────────────────────────────────────────────────────── // Floating back-link to / (the hub). Mount once near the top of any // dashboard; positioned absolute so it doesn't disturb existing layout. function BackToHub() { return ( Hub ); } // ── MetricCard ──────────────────────────────────────────────────────── // KPI tile: label (uppercase mono) + big value + optional sub-line. // `accent` overrides value color (use theme.GREEN/RED/GOLD for status). function MetricCard({ label, value, sub, accent }) { return (
{label}
{value}
{sub &&
{sub}
}
); } // ── SectionTitle ────────────────────────────────────────────────────── // H2 with gold underline. Used to group sections within a dashboard. function SectionTitle({ children, color }) { const c = color || T.GOLD; return (

{children}

); } // ── CustomTooltip ───────────────────────────────────────────────────── // Recharts tooltip with the dashboard theme. Drop in: }/> // `valueFormat`: "auto" (default) shows raw, "percent" appends %, "fixed1" uses .toFixed(1). function CustomTooltip({ active, payload, label, valueFormat = "auto" }) { if (!active || !payload || !payload.length) return null; function fmtVal(v) { if (typeof v !== "number") return v; if (valueFormat === "percent") return v.toFixed(1) + "%"; if (valueFormat === "fixed1") return v.toFixed(1); return v; } return (
{label &&
{label}
} {payload.map((p, i) => (
{p.name}: {fmtVal(p.value)}
))}
); } // ── PieLabel ────────────────────────────────────────────────────────── // Recharts . Hides slices < 4 % to avoid clutter. function PieLabel({ cx, cy, midAngle, outerRadius, percent, name }) { if (percent < 0.04) return null; const RADIAN = Math.PI / 180; const radius = outerRadius + 20; const x = cx + radius * Math.cos(-midAngle * RADIAN); const y = cy + radius * Math.sin(-midAngle * RADIAN); return ( cx ? "start" : "end"} dominantBaseline="central" fontSize={11} fontFamily={T.FONT_MONO} > {name} {(percent * 100).toFixed(0)}% ); } // ── useIsNarrow ─────────────────────────────────────────────────────── // Viewport hook for the responsive table→cards switch ("mobile karty"). function useIsNarrow(bp = 600) { const [narrow, setNarrow] = React.useState(window.innerWidth < bp); React.useEffect(() => { const h = () => setNarrow(window.innerWidth < bp); window.addEventListener("resize", h); return () => window.removeEventListener("resize", h); }, [bp]); return narrow; } // ── MobileCards ─────────────────────────────────────────────────────── // Mobile replacement for wide tables: one card per row — title + main value // on the first line, the remaining columns folded into a mono sub-line. // items: array; title/value/sub: (item, i) => node; valueColor: (item, i) => color. function MobileCards({ items, title, value, sub, valueColor, onItem, keyFn }) { return (
{items.map((it, i) => (
onItem(it, i) : undefined} style={{ background: T.CARD, border: `1px solid ${T.BORDER}`, borderRadius: 8, padding: "9px 12px", cursor: onItem ? "pointer" : "default", }}>
{title(it, i)} {value && {value(it, i)}}
{sub &&
{sub(it, i)}
}
))}
); } ns.components = { BackToHub, MetricCard, SectionTitle, CustomTooltip, PieLabel, useIsNarrow, MobileCards }; })();