import { useEffect, useRef } from "react";
import clsx from "clsx";
import { Loader2, X } from "lucide-react";
export const cx = clsx;
// ---------- formatting ----------
const nf = new Intl.NumberFormat("en-CA");
export const fmt = (n) => (n == null || Number.isNaN(n) ? "—" : nf.format(n));
export const fmtCompact = (n) =>
n == null ? "—" : n >= 10000 ? new Intl.NumberFormat("en-CA", { notation: "compact", maximumFractionDigits: 1 }).format(n) : nf.format(n);
export const fmtDate = (iso, opts = { month: "short", day: "numeric" }) => (iso ? new Date(iso).toLocaleDateString("en-CA", opts) : "—");
export const fmtDateTime = (iso) =>
iso ? new Date(iso).toLocaleString("en-CA", { month: "short", day: "numeric", hour: "numeric", minute: "2-digit" }) : "—";
export function timeAgo(iso) {
if (!iso) return "never";
const s = (Date.now() - new Date(iso)) / 1000;
if (s < 60) return "just now";
if (s < 3600) return `${Math.floor(s / 60)} min ago`;
if (s < 86400) return `${Math.floor(s / 3600)} h ago`;
const d = Math.floor(s / 86400);
return d === 1 ? "yesterday" : `${d} days ago`;
}
export const plural = (n, word) => `${fmt(n)} ${word}${n === 1 ? "" : "s"}`;
// ---------- primitives ----------
export function Button({ variant = "secondary", size = "md", loading, icon: Icon, className, children, ...props }) {
return (
);
}
export function Panel({ title, action, children, className, bodyClass, subtitle }) {
return (
{(title || action) && (
{title}
{subtitle &&
{subtitle}
}
{action}
)}
{children}
);
}
const TONES = {
neutral: "bg-canvas text-ink-2 border-line",
brand: "bg-brand-soft text-brand-deep border-[#cfe0f1]",
ok: "bg-ok-soft text-ok border-[#cfe6d7]",
warn: "bg-warn-soft text-warn border-[#f1dcb8]",
bad: "bg-bad-soft text-bad border-[#f2cdc8]",
};
export function Badge({ tone = "neutral", children, className, dot }) {
return (
{dot && }
{children}
);
}
/** Headline number with label and optional comparison/note. */
export function Stat({ label, value, note, tone, loading }) {
return (
{label}
{loading ? (
) : (
{value}
)}
{note &&
{note}
}
);
}
export function StatRow({ children, className }) {
return {children}
;
}
export const StatCell = ({ children }) => {children}
;
export function Spinner({ label }) {
return (
{label || "Loading…"}
);
}
export function Empty({ icon: Icon, title, children, action }) {
return (
{Icon &&
}
{title}
{children &&
{children}
}
{action &&
{action}
}
);
}
export function ErrorNote({ error, className }) {
if (!error) return null;
return {String(error.message || error)}
;
}
export function Tabs({ tabs, value, onChange }) {
return (
{tabs.map((t) => (
))}
);
}
/** Modal dialog on top of the native