'use client'; import type { WidgetConfig, ChartData, HeatmapConfig } from '@/lib/pipeline-types'; import { WidgetWrapper } from './WidgetWrapper'; interface HeatmapWidgetProps { config: WidgetConfig; data: ChartData | null; loading: boolean; error?: string; onRefresh?: () => void; } /** * Heatmap widget for displaying 2D data grids. */ export function HeatmapWidget({ config, data, loading, error, onRefresh, }: HeatmapWidgetProps) { const heatmapConfig = config.config as HeatmapConfig; const rawData = data?.data || []; // Extract unique x and y values const xValues = [...new Set(rawData.map((d) => d[heatmapConfig.x_key] as string))]; const yValues = [...new Set(rawData.map((d) => d[heatmapConfig.y_key] as string))]; // Find min/max values for color scaling const values = rawData.map((d) => d[heatmapConfig.value_key] as number); const minValue = Math.min(...values, 0); const maxValue = Math.max(...values, 1); // Create lookup map const valueMap = new Map(); rawData.forEach((d) => { const key = `${d[heatmapConfig.y_key]}-${d[heatmapConfig.x_key]}`; valueMap.set(key, d[heatmapConfig.value_key] as number); }); // Get color for a value const getColor = (value: number): string => { const colors = heatmapConfig.color_scale || ['#f0fdf4', '#22c55e']; const ratio = maxValue === minValue ? 0.5 : (value - minValue) / (maxValue - minValue); // Simple interpolation between first and last color if (colors.length === 2) { return interpolateColor(colors[0], colors[1], ratio); } return colors[Math.min(Math.floor(ratio * colors.length), colors.length - 1)]; }; return ( {rawData.length === 0 ? (
No data available
) : (
))} {yValues.map((y) => ( {xValues.map((x) => { const key = `${y}-${x}`; const value = valueMap.get(key) || 0; return ( ); })} ))}
{xValues.map((x) => ( {x}
{y} {heatmapConfig.show_values && ( {value.toLocaleString()} )}
)}
); } /** * Interpolate between two hex colors. */ function interpolateColor(color1: string, color2: string, ratio: 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) * ratio); const g = Math.round(g1 + (g2 - g1) * ratio); const b = Math.round(b1 + (b2 - b1) * ratio); return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`; }