/** * Heatmap Component * Displays data intensity across a grid */ interface HeatmapCell { x: string; y: string; value: number; } interface HeatmapProps { data: HeatmapCell[]; title?: string; xLabels: string[]; yLabels: string[]; colorRange?: { min: string; max: string }; height?: number; showValues?: boolean; } function interpolateColor(color1: string, color2: string, factor: number): string { const hex = (c: string) => parseInt(c, 16); const r1 = hex(color1.slice(1, 3)); const g1 = hex(color1.slice(3, 5)); const b1 = hex(color1.slice(5, 7)); const r2 = hex(color2.slice(1, 3)); const g2 = hex(color2.slice(3, 5)); const b2 = hex(color2.slice(5, 7)); const r = Math.round(r1 + (r2 - r1) * factor); const g = Math.round(g1 + (g2 - g1) * factor); const b = Math.round(b1 + (b2 - b1) * factor); return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`; } export default function Heatmap({ data, title, xLabels, yLabels, colorRange = { min: '#fee2e2', max: '#10b981' }, height = 300, showValues = true, }: HeatmapProps) { const maxValue = Math.max(...data.map((d) => d.value)); const minValue = Math.min(...data.map((d) => d.value)); const range = maxValue - minValue || 1; const getColor = (value: number): string => { const factor = (value - minValue) / range; return interpolateColor(colorRange.min, colorRange.max, factor); }; const getValue = (x: string, y: string): number | undefined => { const cell = data.find((d) => d.x === x && d.y === y); return cell?.value; }; const cellWidth = `${100 / xLabels.length}%`; const cellHeight = (height - 40) / yLabels.length; return (