53 lines
2.0 KiB
TypeScript
53 lines
2.0 KiB
TypeScript
interface Row {
|
|
dimension: unknown;
|
|
value: unknown;
|
|
}
|
|
|
|
interface Props {
|
|
title?: string;
|
|
rows: Row[];
|
|
loading?: boolean;
|
|
}
|
|
|
|
export default function BarChart({ title, rows, loading }: Props) {
|
|
const data = (rows ?? []).map(r => ({ label: String(r.dimension ?? "—"), value: Number(r.value ?? 0) }));
|
|
const max = Math.max(1, ...data.map(d => d.value));
|
|
|
|
return (
|
|
<div className="flex-1 min-w-[280px] bg-white dark:bg-zinc-900 rounded-2xl border border-stone-200/80 dark:border-zinc-700/60 px-5 py-4 shadow-sm">
|
|
{title && (
|
|
<div className="text-[11px] font-semibold uppercase tracking-widest text-stone-400 dark:text-zinc-500 mb-3">
|
|
{title}
|
|
</div>
|
|
)}
|
|
{loading ? (
|
|
<div className="space-y-2">
|
|
{[0,1,2,3].map(i => <div key={i} className="h-5 bg-stone-100 dark:bg-zinc-800 rounded animate-pulse" />)}
|
|
</div>
|
|
) : data.length === 0 ? (
|
|
<div className="text-[12px] text-stone-400 dark:text-zinc-500 py-4 text-center">No data</div>
|
|
) : (
|
|
<div className="space-y-2">
|
|
{data.map((d, i) => {
|
|
const pct = (d.value / max) * 100;
|
|
return (
|
|
<div key={i} className="grid grid-cols-[140px_1fr_48px] items-center gap-2 text-[12px]">
|
|
<span className="truncate text-stone-600 dark:text-zinc-400" title={d.label}>{d.label}</span>
|
|
<div className="h-3 bg-stone-100 dark:bg-zinc-800 rounded-full overflow-hidden">
|
|
<div
|
|
className="h-full rounded-full transition-all"
|
|
style={{ width: `${pct}%`, background: "linear-gradient(90deg, #C8102E, #E84258)" }}
|
|
/>
|
|
</div>
|
|
<span className="text-right font-semibold tabular-nums text-stone-700 dark:text-zinc-200">
|
|
{d.value.toLocaleString("en-IN")}
|
|
</span>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|