import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/src/components/ui/card"; import { Loader2 } from "lucide-react"; import { useScoreAnalytics } from "../ScoreAnalyticsProvider"; import { Heatmap } from "../charts/Heatmap"; import { HeatmapLegend } from "../charts/HeatmapLegend"; import { HeatmapSkeleton } from "../charts/HeatmapSkeleton"; import { getHeatmapCellColor } from "@/src/features/score-analytics/lib/color-scales"; import { type HeatmapCell } from "@/src/features/score-analytics/lib/heatmap-utils"; import { useCallback } from "react"; import { SamplingDetailsHoverCard } from "../SamplingDetailsHoverCard"; import { type ScoreDataTypeType } from "@langfuse/shared"; interface HeatmapTooltipContentProps { cell: HeatmapCell; dataType: ScoreDataTypeType; score1: { name: string; source: string }; score2: { name: string; source: string } | undefined; score1Color: string; score2Color: string; totalMatchedPairs: number; } /** * HeatmapTooltipContent - Renders tooltip content for heatmap cells * * Displays different information based on data type: * - Numeric: Shows bin ranges and value distributions * - Categorical: Shows category pairs and agreement metrics */ function HeatmapTooltipContent({ cell, dataType, score1, score2, score1Color, score2Color, totalMatchedPairs, }: HeatmapTooltipContentProps) { const percentage = (cell.metadata?.percentage as number) ?? 0; return (
{/* Header Section */}

{dataType === "NUMERIC" ? `Bin ${cell.row}×${cell.col}` : `${cell.metadata?.rowCategory as string} → ${cell.metadata?.colCategory as string}`}

{/* Primary Metrics Section */}

{cell.value.toLocaleString()} observations

{percentage.toFixed(1)}% of {totalMatchedPairs.toLocaleString()}{" "} matched pairs

{/* Secondary Info Section */}
{dataType === "NUMERIC" ? ( <>
{score1.name} ({score1.source}) {(cell.metadata?.yRange as [number, number])?.[0]?.toFixed(2)} -{" "} {(cell.metadata?.yRange as [number, number])?.[1]?.toFixed(2)}
{score2?.name} ({score2?.source}) {(cell.metadata?.xRange as [number, number])?.[0]?.toFixed(2)} -{" "} {(cell.metadata?.xRange as [number, number])?.[1]?.toFixed(2)}
) : ( <>
{score1.name} ({score1.source})
{score2?.name} ({score2?.source})
)}
); } /** * HeatmapCard - Smart card component for displaying score comparison heatmaps * * Consumes ScoreAnalyticsProvider context and displays: * - Numeric scores: 10x10 bin heatmap showing correlation patterns * - Categorical/Boolean: Confusion matrix showing agreement * - Placeholder in single-score mode * * Handles: * - Loading states * - Empty states * - Single vs two-score modes (only shows in two-score mode) * - Numeric vs categorical data types */ export function HeatmapCard() { const { data, isLoading, params, getColorForScore } = useScoreAnalytics(); // Compute max value for color scaling (must be before early returns) const maxValue = data?.heatmap && data.heatmap.cells && data.heatmap.cells.length > 0 ? "maxValue" in data.heatmap && typeof data.heatmap.maxValue === "number" ? data.heatmap.maxValue : Math.max(...data.heatmap.cells.map((c: HeatmapCell) => c.value)) : 0; // Create color function using score1's color (must be before early returns) const getColor = useCallback( (cell: HeatmapCell) => { return getHeatmapCellColor(1, cell.value, 0, maxValue); }, [maxValue], ); // Loading state if (isLoading) { return ( Score Comparison Loading heatmap... ); } // No data state if (!data) { return ( Score Comparison No data available Select a score to view comparison ); } const { heatmap, metadata, statistics } = data; const { mode, dataType } = metadata; const { score1, score2 } = params; // Get total matched pairs for tooltip context const totalMatchedPairs = statistics.comparison?.matchedCount ?? 0; const title = dataType === "NUMERIC" ? "Score Comparison Heatmap" : "Confusion Matrix"; const description = mode === "single" ? dataType === "NUMERIC" ? "Distribution of matched score pairs showing correlation patterns" : "Agreement matrix between categorical scores" : dataType === "NUMERIC" ? `${totalMatchedPairs.toLocaleString()} matched pairs showing correlation patterns` : `${totalMatchedPairs.toLocaleString()} matched pairs showing agreement`; // Single score mode - show placeholder if (mode === "single") { return ( {title} {description}

Select a second score to view comparison heatmap

); } // Two score mode - show heatmap or empty state const hasData = heatmap && heatmap.cells.length > 0; // Calculate dynamic cell height based on available space // Magic number 230px represents approximate available height for grid // (card height minus header, labels, legend, gaps) const numRows = dataType === "NUMERIC" ? 10 : heatmap && "rows" in heatmap ? heatmap.rows : 10; const calculatedCellHeight = Math.floor(200 / numRows); return (
{title} {data.samplingMetadata.isSampled && ( )} {description}
{hasData && ( )}
{/* Placeholder to align with tabs in other cards */}
{hasData ? ( ( )} /> ) : ( )} ); }