import React, { useState } from 'react' import { ArrowDownTrayIcon } from '@heroicons/react/24/outline' interface Row { metric: string [key: string]: string | number } interface QualityMetricsTableProps { qualityMetrics: string[] tableHeader: string[] selectedModels: Set tableRows: Row[] } const QualityMetricsTable: React.FC = ({ qualityMetrics, tableHeader, selectedModels, tableRows, }) => { // Sorting state const [rowSort, setRowSort] = useState<{ metric: string; direction: 'asc' | 'desc' } | null>(null) const [columnSort, setColumnSort] = useState<{ model: string; direction: 'asc' | 'desc' } | null>( null ) // Handle row sort (sort columns by this metric) const handleRowSort = (metric: string) => { setColumnSort(null) // Only one sort active at a time setRowSort((prev) => { if (!prev || prev.metric !== metric) return { metric, direction: 'asc' } if (prev.direction === 'asc') return { metric, direction: 'desc' } return null // Remove sort }) } // Handle column sort (sort rows by this model) const handleColumnSort = (model: string) => { setRowSort(null) // Only one sort active at a time setColumnSort((prev) => { if (!prev || prev.model !== model) return { model, direction: 'asc' } if (prev.direction === 'asc') return { model, direction: 'desc' } return null // Remove sort }) } // Sort models (columns) let sortedModels = tableHeader.filter((model) => selectedModels.has(model)) if (rowSort) { // Sort columns by the value in the selected metric row sortedModels = [...sortedModels].sort((a, b) => { const row = tableRows.find((r) => r.metric === rowSort.metric) if (!row) return 0 const valA = Number(row[a]) const valB = Number(row[b]) if (isNaN(valA) && isNaN(valB)) return 0 if (isNaN(valA)) return 1 if (isNaN(valB)) return -1 return rowSort.direction === 'asc' ? valA - valB : valB - valA }) } // Sort metrics (rows) let sortedMetrics = [...qualityMetrics] if (columnSort) { // Sort rows by the value in the selected model column sortedMetrics = [...sortedMetrics].sort((a, b) => { const rowA = tableRows.find((r) => r.metric === a) const rowB = tableRows.find((r) => r.metric === b) if (!rowA || !rowB) return 0 const valA = Number(rowA[columnSort.model]) const valB = Number(rowB[columnSort.model]) if (isNaN(valA) && isNaN(valB)) return 0 if (isNaN(valA)) return 1 if (isNaN(valB)) return -1 return columnSort.direction === 'asc' ? valA - valB : valB - valA }) } // CSV export logic function exportToCSV() { // Build header row const header = ['Metric', ...sortedModels] // Build data rows const rows = sortedMetrics .map((metric) => { const row = tableRows.find((r) => r.metric === metric) if (!row) return null return [ metric, ...sortedModels.map((col) => { const cell = row[col] // Format as displayed return !isNaN(Number(cell)) ? Number(Number(cell).toFixed(3)) : cell }), ] }) .filter((row): row is (string | number)[] => !!row) // Combine const csv = [header, ...rows] .map((row) => row.map((cell) => `"${String(cell).replace(/"/g, '""')}"`).join(',')) .join('\n') // Download const blob = new Blob([csv], { type: 'text/csv' }) const url = URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = 'quality_metrics.csv' document.body.appendChild(a) a.click() document.body.removeChild(a) URL.revokeObjectURL(url) } if (qualityMetrics.length === 0) return null return (
{sortedModels.map((model) => { const isSorted = columnSort && columnSort.model === model const direction = isSorted ? columnSort.direction : undefined return ( ) })} {sortedMetrics.map((metric) => { const row = tableRows.find((r) => r.metric === metric) if (!row) return null const isSorted = rowSort && rowSort.metric === metric const direction = isSorted ? rowSort.direction : undefined return ( {sortedModels.map((col) => { const cell = row[col] return ( ) })} ) })}
Metric handleColumnSort(model)} title={ isSorted ? direction === 'asc' ? 'Sort descending' : 'Clear sort' : 'Sort by this column' } > {model} {isSorted ? (direction === 'asc' ? '↑' : '↓') : '⇅'}
handleRowSort(metric)} title={ isSorted ? direction === 'asc' ? 'Sort descending' : 'Clear sort' : 'Sort by this row (sorts columns)' } > {metric} {isSorted ? (direction === 'asc' ? '↑' : '↓') : '⇅'} {!isNaN(Number(cell)) ? Number(Number(cell).toFixed(3)) : cell}
) } export default QualityMetricsTable