// components/MetricsBreakdown.jsx "use client"; import React, { useState, useEffect, useMemo } from "react"; import { RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis, Radar, Tooltip as RechartsTooltip, // Renamed to avoid conflict with local Tooltip Legend, ResponsiveContainer, } from "recharts"; import { getScoreColor, getMetricTooltip } from "../lib/utils"; import { Tooltip } from "./Tooltip"; // Your custom Tooltip component for headers etc. // Component receives processed metrics data, model metadata, and category radar data const MetricsBreakdown = ({ metricsData, modelsMeta, radarData: categoryRadarDataProp, // Already processed radar data for categories }) => { const [subTab, setSubTab] = useState("categories"); // 'categories' or 'metrics' const [selectedModels, setSelectedModels] = useState([]); // console.log("Metrics Data in Breakdown:", metricsData); // For debugging // console.log("Models Meta in Breakdown:", modelsMeta); // console.log("Category Radar Data Prop:", categoryRadarDataProp); // Extract data from props with defaults const { highLevelCategories, lowLevelMetrics } = metricsData || { highLevelCategories: {}, lowLevelMetrics: {}, }; // Use modelsMeta directly for clarity, aliasing if preferred const models = modelsMeta || []; // Get sorted lists of category and metric names const sortedCategoryNames = useMemo( () => Object.keys(highLevelCategories || {}).sort((a, b) => a.localeCompare(b)), [highLevelCategories] ); const sortedMetricNames = useMemo( () => Object.keys(lowLevelMetrics || {}).sort((a, b) => a.localeCompare(b)), [lowLevelMetrics] ); // Initialize selections useEffect(() => { if (selectedModels.length === 0 && models.length > 0) { setSelectedModels(models.map((m) => m.model)); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [models]); // Only depends on models changing/loading // --- Memoized data generation functions --- // Radar data for LL Metrics (used when subTab === 'metrics') - CORRECTED ACCESSORS const metricRadarData = useMemo(() => { if ( !lowLevelMetrics || models.length === 0 || sortedMetricNames.length === 0 ) return []; return sortedMetricNames.map((metricName) => { const entry = { category: metricName }; // Use metric name as the axis category const metricData = lowLevelMetrics[metricName]; if (metricData) { models .filter((m) => selectedModels.includes(m.model)) .forEach((model) => { // Use correct camelCase keys entry[model.model] = Number(metricData.modelScores?.[model.model]?.nationalScore) || 0; // Standard deviation per metric is NOT available, so we don't add it here }); } return entry; }); }, [lowLevelMetrics, models, selectedModels, sortedMetricNames]); // Custom tooltip (common for both radar charts) - CORRECTED (removed std dev logic) const CustomRadarTooltip = ({ active, payload, label }) => { if (active && payload && payload.length) { return (
{label}
{/* Get tooltip description for the category/metric itself */}{getMetricTooltip(label)}
Model | {sortedCategoryNames.map((catName) => ({catName} | ))}
---|---|
{" "}
{/* Keep sticky styles */}
{model.model}
|
{sortedCategoryNames.map((catName) => {
// Use correct camelCase keys
const scoreData =
highLevelCategories[catName]?.modelScores?.[
model.model
];
const score = scoreData?.nationalScore; // Access camelCase key
const displayScore =
score !== null && score !== undefined
? Number(score).toFixed(1)
: "N/A";
return (
{displayScore}
|
);
})}
No category data available.
)}Radar data not available.
)}This radar chart visualizes how each model performs across different high-level evaluation categories. The further out on each axis, the better the performance on that category.
Metric data not available for radar chart.
)}This radar chart visualizes how each model performs across different low-level metrics. The further out on each axis, the better the performance on that metric.