import React, { useMemo } from "react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { AlertTriangle, BarChart3 } from "lucide-react"; import { PieChart, Pie, Cell, ResponsiveContainer } from "recharts"; interface RiskTypeStats { [riskType: string]: { count: number; percentage: number; affectedEntities: Set; avgSeverity: number; }; } interface RiskDistributionPieChartProps { riskTypeStats: RiskTypeStats; totalFailures: number; onRiskTypeClick: (riskType: string, stats: any) => void; } interface PieChartData { name: string; value: number; percentage: number; color: string; severity: number; affectedEntities: number; } export function RiskDistributionPieChart({ riskTypeStats, totalFailures, onRiskTypeClick, }: RiskDistributionPieChartProps) { const [hoveredIndex, setHoveredIndex] = React.useState(null); // Add rotation animation styles React.useEffect(() => { const style = document.createElement("style"); style.textContent = ` .rotating-chart { animation: rotate 20s linear infinite; transform-origin: center; } .rotating-chart:hover { animation-play-state: paused; } @keyframes rotate { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } `; document.head.appendChild(style); return () => { document.head.removeChild(style); }; }, []); // Transform data for pie chart const chartData = useMemo(() => { const colors = [ "#ef4444", // red-500 - High severity "#f97316", // orange-500 - Medium-high severity "#eab308", // yellow-500 - Medium severity "#3b82f6", // blue-500 - Medium-low severity "#8b5cf6", // purple-500 - Low severity "#10b981", // emerald-500 - Very low severity ]; // Guard against undefined or empty riskTypeStats if (!riskTypeStats || typeof riskTypeStats !== "object") { return []; } return Object.entries(riskTypeStats) .filter( ([riskType, stats]) => riskType && stats && typeof stats === "object" ) .map(([riskType, stats], index) => ({ name: riskType.replace(/_/g, " "), value: stats.count || 0, percentage: stats.percentage || 0, color: colors[index % colors.length] || "#6b7280", severity: stats.avgSeverity || 0, affectedEntities: stats.affectedEntities?.size || 0, })); }, [riskTypeStats]); // Handle pie chart segment click const handleSegmentClick = (data: PieChartData) => { // Guard against undefined data if (!data || !data.name) { return; } const originalRiskType = Object.keys(riskTypeStats).find( (key) => key.replace(/_/g, " ") === data.name ); if (originalRiskType) { onRiskTypeClick(originalRiskType, riskTypeStats[originalRiskType]); } }; if (totalFailures === 0) { return ( Risk Distribution

No risk data available

Upload traces with failures to see distribution

); } return (
Risk Distribution

Click segments for detailed analysis

{/* Pie Chart with legend */}
{/* Rotating Pie Chart */}
{/* Glass overlay effect */}
{chartData.map((entry, index) => ( setHoveredIndex(index)} onMouseLeave={() => setHoveredIndex(null)} /> ))}
{/* Legend Table */}
{chartData.map((entry, index) => (
handleSegmentClick(entry)} onMouseEnter={() => setHoveredIndex(index)} onMouseLeave={() => setHoveredIndex(null)} >
{entry.name}
{entry.value} failures {entry.percentage}%
))}
); }