'use client' import { useState, useMemo } from 'react' import type { ModelData } from '@/lib/types' import { fmtPct, fmtNum, creatorColor } from '@/lib/utils' import { LabLogo } from '@/components/LabLogo' type Metric = 'luc' | 'rag' | 'fairness' const TABS: { key: Metric; label: string; yLabel: string }[] = [ { key: 'luc', label: 'Refusal Rate', yLabel: 'Score (Higher is Better)' }, { key: 'rag', label: 'RAG Score', yLabel: 'Score (Higher is Better)' }, { key: 'fairness', label: 'Fairness', yLabel: 'Wasserstein Distance (Lower is Better)' }, ] function getValue(m: ModelData, metric: Metric): number | null { if (metric === 'luc') return m.luc.avg if (metric === 'rag') return m.rag.avg return m.fairness.avg } // SVG layout (coordinate units) const ML = 80 // left margin const MR = 80 // right margin const MT = 14 // top margin const BAR_H = 300 // bar area height const LBL_H = 150 // rotated label area const VW = 1100 const VH = MT + BAR_H + LBL_H const CHART_W = VW - ML - MR const GAP = 4 const GRID = [0, 0.25, 0.5, 0.75, 1.0] export default function BarChartSection({ models, maxFairness, }: { models: ModelData[] maxFairness: number }) { const [metric, setMetric] = useState('luc') const [activeCreators, setCreators] = useState>(new Set()) const allCreators = useMemo( () => [...new Set(models.filter(m => !m.archived).map(m => m.creator))].sort(), [models], ) const toggleCreator = (c: string) => setCreators(prev => { const s = new Set(prev); s.has(c) ? s.delete(c) : s.add(c); return s }) const sorted = useMemo(() => { let list = models.filter(m => !m.archived) if (activeCreators.size) list = list.filter(m => activeCreators.has(m.creator)) list = list.filter(m => getValue(m, metric) !== null) return [...list].sort((a, b) => { const va = getValue(a, metric)! const vb = getValue(b, metric)! return metric === 'fairness' ? va - vb : vb - va }) }, [models, metric, activeCreators]) const maxVal = metric === 'fairness' ? maxFairness : 1 const n = sorted.length const bw = (CHART_W - (n - 1) * GAP) / n const legendCreators = useMemo( () => [...new Set(sorted.map(m => m.creator))].sort(), [sorted], ) const tab = TABS.find(t => t.key === metric)! return (
{/* Filters row */}
{/* Creator chips */}
{allCreators.map(c => { const cc = creatorColor(c) const on = activeCreators.has(c) return ( ) })}
{/* Metric tabs */}
{TABS.map((t, i) => ( ))}
{/* Chart container */}
{/* Grid lines + y-axis labels */} {GRID.map(lvl => { const y = MT + BAR_H - lvl * BAR_H const display = metric === 'fairness' ? (lvl * maxVal).toFixed(2) : `${Math.round(lvl * 100)}` return ( {display} ) })} {/* Y-axis title */} {tab.yLabel} {/* Bars + labels */} {sorted.map((m, i) => { const val = getValue(m, metric)! const pct = Math.min(1, val / maxVal) const barHeight = pct * BAR_H const x = ML + i * (bw + GAP) const y = MT + BAR_H - barHeight const cc = creatorColor(m.creator) const valLabel = metric === 'fairness' ? val.toFixed(3) : `${(val * 100).toFixed(0)}%` return ( {/* Bar */} {/* Value above bar */} {barHeight > 18 && ( {valLabel} )} {/* Model name */} {m.model} ) })} {/* Creator legend */}
{legendCreators.map(c => (
{c}
))}
) }