type Dataset = { name: string; values: number[]; color?: string }; interface Props { type: 'Bar' | 'Pie' | 'Line' | string; labels: string[]; datasets: Dataset[]; height?: number; } const clamp = (n: number) => (Number.isFinite(n) ? Math.max(0, n) : 0); export default function SimpleChart({ type, labels, datasets, height = 220 }: Props) { if (!labels?.length || !datasets?.length) { return
No data
; } if (type.toLowerCase() === 'pie') { const values = datasets[0].values.map(clamp); const total = values.reduce((a, b) => a + b, 0) || 1; const radius = Math.min(100, height / 2 - 10); const cx = radius + 10; const cy = radius + 10; let cumulative = 0; const colors = datasets[0].values.map((_, i) => datasets[0].color || defaultColor(i)); return ( {values.map((v, i) => { const startAngle = (cumulative / total) * 2 * Math.PI; cumulative += v; const endAngle = (cumulative / total) * 2 * Math.PI; const largeArc = endAngle - startAngle > Math.PI ? 1 : 0; const x1 = cx + radius * Math.cos(startAngle); const y1 = cy + radius * Math.sin(startAngle); const x2 = cx + radius * Math.cos(endAngle); const y2 = cy + radius * Math.sin(endAngle); const d = `M ${cx} ${cy} L ${x1} ${y1} A ${radius} ${radius} 0 ${largeArc} 1 ${x2} ${y2} Z`; return ; })} ); } // Bar chart (stack if multiple datasets) const series = datasets; const max = Math.max(...series.flatMap(s => s.values.map(clamp)), 1); const width = Math.max(labels.length * 60, 300); const chartHeight = height - 40; const barWidth = Math.max(20, (width - 40) / labels.length - 10); return ( {/* Axis */} {labels.map((label, i) => { const x = 40 + i * (barWidth + 10); let yOffset = 0; return ( {series.map((s, si) => { const v = clamp(s.values[i] || 0); const h = (v / max) * (chartHeight - 20); const y = chartHeight - h - yOffset; const color = s.color || defaultColor(si); yOffset += h; return ; })} {truncate(label, 8)} ); })} ); } function defaultColor(i: number): string { const palette = ['#4F46E5', '#10B981', '#F59E0B', '#EF4444', '#6366F1', '#22C55E', '#E11D48']; return palette[i % palette.length]; } function truncate(s: string, n: number) { return s.length > n ? s.slice(0, n - 1) + '…' : s; }