"use client"; import { useState } from "react"; import { Cell, Label, Pie, PieChart, Tooltip } from "recharts"; import { ChartConfig, ChartContainer } from "@/components/ui/chart/Chart"; import { ChartLegend } from "./shared/chart-legend"; import { DonutDataPoint } from "./types"; const CHART_COLORS = { emptyState: "var(--border-neutral-tertiary)", }; interface TooltipPayloadData { percentage?: number; change?: number; color?: string; } interface TooltipPayloadEntry { name: string; color?: string; payload?: TooltipPayloadData; } interface CustomTooltipProps { active?: boolean; payload?: TooltipPayloadEntry[]; } interface LegendPayloadData { percentage?: number; } interface LegendPayloadEntry { value: string; color: string; payload: LegendPayloadData; } interface CustomLegendProps { payload: LegendPayloadEntry[]; } interface CenterLabel { value: string | number; label: string; } interface DonutChartProps { data: DonutDataPoint[]; height?: number; innerRadius?: number; outerRadius?: number; showLegend?: boolean; centerLabel?: CenterLabel; onSegmentClick?: (dataPoint: DonutDataPoint, index: number) => void; } const CustomTooltip = ({ active, payload }: CustomTooltipProps) => { if (!active || !payload || !payload.length) return null; const entry = payload[0]; const name = entry.name; const percentage = entry.payload?.percentage; const color = entry.color || entry.payload?.color; const change = entry.payload?.change; return (
{/* Title with color chip */}

{percentage}% {name}

{/* Change percentage row */} {change !== undefined && (

{change > 0 ? "+" : ""} {change}% Since last scan

)}
); }; const CustomLegend = ({ payload }: CustomLegendProps) => { const items = payload.map((entry: LegendPayloadEntry) => ({ label: `${entry.value} (${entry.payload.percentage ?? 0}%)`, color: entry.color, })); return ; }; export function DonutChart({ data, innerRadius = 68, outerRadius = 86, showLegend = true, centerLabel, onSegmentClick, }: DonutChartProps) { const [hoveredIndex, setHoveredIndex] = useState(null); const chartConfig = data.reduce( (config, item) => ({ ...config, [item.name]: { label: item.name, color: item.color, }, }), {} as ChartConfig, ); const chartData = data.map((item) => ({ name: item.name, value: item.value, fill: item.color, color: item.color, percentage: item.percentage, change: item.change, })); const total = chartData.reduce((sum, d) => sum + (Number(d.value) || 0), 0); const isEmpty = total <= 0; const emptyData = [ { name: "No data", value: 1, fill: CHART_COLORS.emptyState, color: CHART_COLORS.emptyState, percentage: 0, change: undefined, }, ]; const legendPayload = (isEmpty ? emptyData : chartData).map((entry) => ({ value: isEmpty ? "No data" : entry.name, color: entry.color, payload: { percentage: isEmpty ? 0 : entry.percentage, }, })); return ( <> {!isEmpty && } />} {(isEmpty ? emptyData : chartData).map((entry, index) => { const opacity = hoveredIndex === null ? 1 : hoveredIndex === index ? 1 : 0.5; const isClickable = !isEmpty && onSegmentClick; return ( setHoveredIndex(index)} onMouseLeave={() => setHoveredIndex(null)} onClick={() => { if (isClickable) { onSegmentClick(data[index], index); } }} /> ); })} {(centerLabel || isEmpty) && ( {showLegend && } ); }