1- import React from "react" ;
2- import {
3- PieChart ,
4- Pie ,
5- Cell ,
6- ResponsiveContainer ,
7- Tooltip ,
8- Legend
9- } from "recharts" ;
1+ import React , { useMemo } from "react" ;
2+ import { PieChart , Pie , Cell } from "recharts" ;
103import { PanelResult } from "../../../types" ;
114import { COLOR_PALETTE , getSeverityOfText , severityToHex } from "./utils" ;
5+ import {
6+ ChartConfig ,
7+ ChartContainer ,
8+ ChartLegend ,
9+ ChartLegendContent ,
10+ ChartTooltip ,
11+ ChartTooltipContent
12+ } from "@flanksource-ui/components/ui/chart" ;
1213
1314interface PieChartPanelProps {
1415 summary : PanelResult ;
@@ -19,22 +20,17 @@ interface PieChartPanelProps {
1920 */
2021export const generatePieChartData = (
2122 rows : Record < string , any > [ ] ,
22- customColors ?: Record < string , string >
23+ customColors ?: Record < string , string > ,
24+ maxSlices = 6
2325) => {
24- return rows . map ( ( row , index ) => {
26+ const entries = rows . map ( ( row , index ) => {
2527 const { count, ...rest } = row ;
2628 const labelKey = Object . keys ( rest ) [ 0 ] ;
2729 const labelValue = rest [ labelKey ] ;
28-
29- if ( ! labelValue ) {
30- return {
31- name : "Unknown" ,
32- value : count ,
33- fill : COLOR_PALETTE [ index % COLOR_PALETTE . length ]
34- } ;
35- }
36-
37- const customColor = customColors ?. [ labelValue ] ;
30+ const parsedValue = Number ( count ) ;
31+ const value = Number . isFinite ( parsedValue ) ? parsedValue : 0 ;
32+ const name = labelValue ? String ( labelValue ) : "Unknown" ;
33+ const customColor = customColors ?. [ name ] ;
3834 let fill : string ;
3935
4036 if ( customColor ) {
@@ -49,34 +45,32 @@ export const generatePieChartData = (
4945 }
5046
5147 return {
52- name : labelValue ,
53- value : count ,
48+ name,
49+ value,
5450 fill
5551 } ;
5652 } ) ;
57- } ;
5853
59- /**
60- * Custom legend renderer for the pie chart displaying color indicators with labels.
61- * @param props - Recharts legend props containing payload array
62- * @returns JSX element rendering the legend
63- */
64- const renderLegend = ( props : any ) => {
65- const { payload } = props ;
54+ if ( entries . length <= maxSlices ) {
55+ return entries ;
56+ }
6657
67- return (
68- < ul className = "mt-1 flex flex-wrap justify-center gap-1 text-xs text-gray-600" >
69- { payload . map ( ( entry : any , index : number ) => (
70- < li key = { `item-${ index } ` } className = "flex items-center" >
71- < span
72- className = "mr-1 h-1.5 w-1.5 rounded-full"
73- style = { { backgroundColor : entry . color } }
74- />
75- < span > { entry . value } </ span >
76- </ li >
77- ) ) }
78- </ ul >
79- ) ;
58+ const sorted = [ ...entries ] . sort ( ( a , b ) => b . value - a . value ) ;
59+ const keep = sorted . slice ( 0 , Math . max ( maxSlices - 1 , 1 ) ) ;
60+ const overflow = sorted . slice ( keep . length ) ;
61+ const overflowValue = overflow . reduce ( ( sum , item ) => sum + item . value , 0 ) ;
62+
63+ const othersFill =
64+ customColors ?. others || COLOR_PALETTE [ keep . length % COLOR_PALETTE . length ] ;
65+
66+ return [
67+ ...keep ,
68+ {
69+ name : "others" ,
70+ value : overflowValue ,
71+ fill : othersFill
72+ }
73+ ] ;
8074} ;
8175
8276/**
@@ -92,10 +86,16 @@ const renderLegend = (props: any) => {
9286 * @param props.summary - Panel data containing rows, title, description, and chart config
9387 */
9488const PieChartPanel : React . FC < PieChartPanelProps > = ( { summary } ) => {
95- const chartData = generatePieChartData (
96- summary . rows || [ ] ,
97- summary . piechart ?. colors
98- ) ;
89+ const chartData = useMemo ( ( ) => {
90+ return generatePieChartData ( summary . rows || [ ] , summary . piechart ?. colors ) ;
91+ } , [ summary . rows , summary . piechart ?. colors ] ) ;
92+
93+ const chartConfig = useMemo < ChartConfig > ( ( ) => {
94+ return chartData . reduce < ChartConfig > ( ( acc , item ) => {
95+ acc [ item . name ] = { label : item . name , color : item . fill } ;
96+ return acc ;
97+ } , { } ) ;
98+ } , [ chartData ] ) ;
9999
100100 return (
101101 < div className = "flex h-full min-h-[300px] w-full flex-col overflow-hidden rounded-lg border border-gray-200 bg-white p-4" >
@@ -104,8 +104,12 @@ const PieChartPanel: React.FC<PieChartPanelProps> = ({ summary }) => {
104104 < p className = "mb-3 text-xs text-gray-500" > { summary . description } </ p >
105105 ) }
106106 < div className = "flex flex-1 items-center justify-center" >
107- < ResponsiveContainer width = "100%" height = "100%" >
107+ < ChartContainer
108+ config = { chartConfig }
109+ className = "flex h-full min-h-[240px] w-full flex-1 items-center justify-center"
110+ >
108111 < PieChart >
112+ < ChartTooltip content = { < ChartTooltipContent nameKey = "name" /> } />
109113 < Pie
110114 data = { chartData }
111115 dataKey = "value"
@@ -121,10 +125,9 @@ const PieChartPanel: React.FC<PieChartPanelProps> = ({ summary }) => {
121125 < Cell key = { `cell-${ entryIndex } ` } fill = { entry . fill } />
122126 ) ) }
123127 </ Pie >
124- < Tooltip allowEscapeViewBox = { { x : true , y : true } } />
125- < Legend content = { renderLegend } />
128+ < ChartLegend content = { < ChartLegendContent nameKey = "name" /> } />
126129 </ PieChart >
127- </ ResponsiveContainer >
130+ </ ChartContainer >
128131 </ div >
129132 </ div >
130133 ) ;
0 commit comments