55e6ff87a8
Nuevos componentes React/TS en frontend/functions/ui/: - data_table: tabla de datos con columnas tipadas, sorting, paginación y formato personalizable - pie_chart: gráfico circular Recharts con tooltips, leyenda y paleta configurable Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
90 lines
2.3 KiB
TypeScript
90 lines
2.3 KiB
TypeScript
import {
|
|
PieChart as RechartsPieChart, Pie, Cell, Tooltip, Legend, ResponsiveContainer,
|
|
} from 'recharts'
|
|
import { cn } from '../core/cn'
|
|
|
|
const DEFAULT_COLORS = [
|
|
'#3b82f6', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6',
|
|
'#ec4899', '#06b6d4', '#f97316',
|
|
]
|
|
|
|
interface PieChartProps {
|
|
data: Record<string, unknown>[]
|
|
nameKey: string
|
|
valueKey: string
|
|
colors?: string[]
|
|
donut?: boolean
|
|
innerRadius?: number
|
|
outerRadius?: number
|
|
showLegend?: boolean
|
|
showLabels?: boolean
|
|
height?: number | string
|
|
className?: string
|
|
valueFormatter?: (value: number) => string
|
|
}
|
|
|
|
function PieChartComponent({
|
|
data,
|
|
nameKey,
|
|
valueKey,
|
|
colors = DEFAULT_COLORS,
|
|
donut = false,
|
|
innerRadius,
|
|
outerRadius = 100,
|
|
showLegend = true,
|
|
showLabels = true,
|
|
height = 300,
|
|
className,
|
|
valueFormatter = (v) => v.toLocaleString(),
|
|
}: PieChartProps) {
|
|
// Ensure numeric values for Recharts Pie
|
|
const pieData = data.map(row => ({
|
|
...row,
|
|
[valueKey]: Number(row[valueKey]) || 0,
|
|
}))
|
|
|
|
const resolvedInnerRadius = donut ? (innerRadius ?? 50) : (innerRadius ?? 0)
|
|
|
|
const labelRenderer = showLabels
|
|
? ({ name, percent }: Record<string, unknown>) =>
|
|
`${name ?? ''} ${(((percent as number) ?? 0) * 100).toFixed(0)}%`
|
|
: undefined
|
|
|
|
return (
|
|
<ResponsiveContainer width="100%" height={height} className={cn(className)}>
|
|
<RechartsPieChart>
|
|
<Pie
|
|
data={pieData}
|
|
dataKey={valueKey}
|
|
nameKey={nameKey}
|
|
cx="50%"
|
|
cy="50%"
|
|
outerRadius={outerRadius}
|
|
innerRadius={resolvedInnerRadius}
|
|
strokeWidth={0}
|
|
fontSize={11}
|
|
label={labelRenderer}
|
|
labelLine={showLabels}
|
|
>
|
|
{pieData.map((_, i) => (
|
|
<Cell key={i} fill={colors[i % colors.length]} />
|
|
))}
|
|
</Pie>
|
|
<Tooltip
|
|
contentStyle={{
|
|
backgroundColor: 'var(--card)',
|
|
border: '1px solid var(--border)',
|
|
borderRadius: 8,
|
|
fontSize: 12,
|
|
}}
|
|
formatter={(value: unknown) => [valueFormatter(value as number)]}
|
|
/>
|
|
{showLegend && <Legend wrapperStyle={{ fontSize: 11 }} />}
|
|
</RechartsPieChart>
|
|
</ResponsiveContainer>
|
|
)
|
|
}
|
|
|
|
export const PieChart = PieChartComponent
|
|
export type { PieChartProps }
|