import { useState } from 'react'; import styles from './DonutChart.module.scss'; import type { Bucket } from '../../../types/analytics.types'; const PALETTE = ['#6366f1', '#8b5cf6', '#06b6d4', '#f59e0b', '#10b981', '#ef4444', '#ec4899', '#3b82f6', '#14b8a6', '#f97316', '#a855f7', '#0ea5e9']; const SIZE = 200, R = 78, STROKE = 20, C = 2 * Math.PI * R; export default function DonutChart({ title, data, topN = 11 }: { title: string; data: Bucket[]; topN?: number }) { const [pct, setPct] = useState(false); const sorted = [...data].sort((a, b) => b.count - a.count); const head = sorted.slice(0, topN); const rest = sorted.slice(topN); const restTotal = rest.reduce((s, b) => s + b.count, 0); const slices = restTotal > 0 ? [...head, { key: '__others', label: `Others (${rest.length})`, count: restTotal }] : head; const total = slices.reduce((s, b) => s + b.count, 0) || 1; const color = (i: number) => (i === slices.length - 1 && restTotal > 0 ? 'var(--text-dim)' : PALETTE[i % PALETTE.length]); let offset = 0; const arcs = slices.map((b, i) => { const frac = b.count / total; const dash = frac * C; const arc = { b, i, dash, gap: C - dash, rot: (offset / C) * 360, color: color(i) }; offset += dash; return arc; }); return (
{title}
{arcs.map(a => ( {`${a.b.label}: ${a.b.count} (${Math.round((a.b.count / total) * 100)}%)`} ))} {total} total
); }