import { useState } from "react"; import type { DatasetInfo, Preset } from "../types"; // Consistent group colors for visual distinction const GROUP_COLORS = [ { bg: "bg-blue-500", border: "border-blue-500", text: "text-blue-400", label: "text-blue-300" }, { bg: "bg-emerald-500", border: "border-emerald-500", text: "text-emerald-400", label: "text-emerald-300" }, { bg: "bg-amber-500", border: "border-amber-500", text: "text-amber-400", label: "text-amber-300" }, { bg: "bg-purple-500", border: "border-purple-500", text: "text-purple-400", label: "text-purple-300" }, { bg: "bg-rose-500", border: "border-rose-500", text: "text-rose-400", label: "text-rose-300" }, { bg: "bg-cyan-500", border: "border-cyan-500", text: "text-cyan-400", label: "text-cyan-300" }, ]; interface SidebarProps { datasets: DatasetInfo[]; presets: Preset[]; loading: Record; groups: Record; groupIds: string[]; currentGroupId: string | null; onAddDataset: (repo: string, column?: string, split?: string, promptColumn?: string) => void; onRemoveDataset: (id: string) => void; onToggleDataset: (id: string) => void; onSetCurrentGroup: (groupId: string) => void; onLoadPreset: (preset: Preset) => void; onSavePreset: (name: string, repo: string, column: string, split?: string) => void; onDeletePreset: (id: string, datasetId?: string) => void; onUpdatePreset: (presetId: string, datasetId: string, updates: { name?: string }) => void; } export default function Sidebar({ datasets, presets, loading, groups, groupIds, currentGroupId, onAddDataset, onRemoveDataset, onToggleDataset, onSetCurrentGroup, onLoadPreset, onSavePreset, onDeletePreset, onUpdatePreset, }: SidebarProps) { const [showAddModal, setShowAddModal] = useState(false); const [repoInput, setRepoInput] = useState(""); const [columnInput, setColumnInput] = useState("model_responses"); const [splitInput, setSplitInput] = useState("train"); const [promptColumnInput, setPromptColumnInput] = useState("formatted_prompt"); const [presetSearch, setPresetSearch] = useState(""); // Track which dataset is currently being saved as a preset (by dataset id) const [savingPresetForId, setSavingPresetForId] = useState(null); const [presetName, setPresetName] = useState(""); // Track which dataset is selected for preset editing const [editingDatasetId, setEditingDatasetId] = useState(null); const [editPresetName, setEditPresetName] = useState(""); const handleAdd = () => { if (!repoInput.trim()) return; onAddDataset( repoInput.trim(), columnInput.trim() || undefined, splitInput.trim() || undefined, promptColumnInput.trim() || undefined, ); setRepoInput(""); setShowAddModal(false); }; const handleSavePresetForRepo = (ds: DatasetInfo) => { if (!presetName.trim()) return; onSavePreset(presetName.trim(), ds.repo, ds.column, ds.split); setPresetName(""); setSavingPresetForId(null); }; const getGroupColor = (groupId: string) => { const idx = groupIds.indexOf(groupId); return GROUP_COLORS[idx % GROUP_COLORS.length]; }; return (
{/* Presets section */}

Presets

{presets.length === 0 ? (

No presets saved

) : ( <> {presets.length > 6 && ( setPresetSearch(e.target.value)} placeholder="Search presets..." className="w-full px-2 py-1 mb-2 text-xs bg-gray-800 border border-gray-600 rounded text-gray-200 placeholder-gray-500 focus:border-blue-500 focus:outline-none" /> )}
{presets .filter((p) => !presetSearch || p.name.toLowerCase().includes(presetSearch.toLowerCase()) || p.repo.toLowerCase().includes(presetSearch.toLowerCase())) .map((p) => (
))}
)}
{/* Datasets section — grouped by question fingerprint */}

Loaded Repos

{datasets.length === 0 ? (

No repos loaded. Add one below.

) : (
{groupIds.map((gid) => { const color = getGroupColor(gid); const groupDatasets = groups[gid]; const isCurrentGroup = gid === currentGroupId; return (
{/* Group header — clickable to switch group */} {/* Repos in this group */}
{groupDatasets.map((ds) => (
{ if (ds.presetId) { setEditingDatasetId(editingDatasetId === ds.id ? null : ds.id); setEditPresetName(ds.presetName || ""); setShowAddModal(false); } }} className={`flex items-center gap-2 px-2 py-1.5 rounded text-sm transition-colors ${ ds.active ? "bg-gray-800" : "bg-gray-900 opacity-60" } ${editingDatasetId === ds.id ? "ring-1 ring-blue-500" : ""} ${ds.presetId ? "cursor-pointer" : ""}`} > onToggleDataset(ds.id)} onClick={(e) => e.stopPropagation()} className="rounded border-gray-600 bg-gray-800 text-blue-500 focus:ring-blue-500 focus:ring-offset-0" />
{ds.presetName || ds.name}
{ds.column} | {ds.n_rows} rows | {ds.n_samples} samples
{/* Save as preset */} {/* Remove */}
{/* Inline preset name input */} {savingPresetForId === ds.id && (
setPresetName(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") handleSavePresetForRepo(ds); if (e.key === "Escape") setSavingPresetForId(null); }} placeholder="Preset name..." className="flex-1 px-2 py-1 text-xs bg-gray-800 border border-gray-600 rounded text-gray-200 placeholder-gray-500 focus:border-blue-500 focus:outline-none" autoFocus />
)}
))}
); })}
)}
{/* Preset edit panel */} {editingDatasetId && (() => { const editDs = datasets.find(d => d.id === editingDatasetId); if (!editDs?.presetId) return null; return (
Edit Preset
setEditPresetName(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter" && editPresetName.trim()) { onUpdatePreset(editDs.presetId!, editDs.id, { name: editPresetName.trim() }); setEditingDatasetId(null); } if (e.key === "Escape") setEditingDatasetId(null); }} placeholder="Preset name..." className="w-full px-2 py-1 text-xs bg-gray-800 border border-gray-600 rounded text-gray-200 placeholder-gray-500 focus:border-blue-500 focus:outline-none" autoFocus />
); })()} {/* Add repo section */}
{!showAddModal ? ( ) : (
setRepoInput(e.target.value)} onKeyDown={(e) => e.key === "Enter" && handleAdd()} placeholder="org/dataset-name" className="w-full px-2 py-1.5 text-sm bg-gray-800 border border-gray-600 rounded text-gray-200 placeholder-gray-500 focus:border-blue-500 focus:outline-none" autoFocus />
setColumnInput(e.target.value)} placeholder="Column" className="flex-1 px-2 py-1 text-xs bg-gray-800 border border-gray-600 rounded text-gray-200 placeholder-gray-500 focus:border-blue-500 focus:outline-none" /> setSplitInput(e.target.value)} placeholder="Split" className="w-16 px-2 py-1 text-xs bg-gray-800 border border-gray-600 rounded text-gray-200 placeholder-gray-500 focus:border-blue-500 focus:outline-none" />
setPromptColumnInput(e.target.value)} placeholder="Prompt col" className="flex-1 px-2 py-1 text-xs bg-gray-800 border border-gray-600 rounded text-gray-200 placeholder-gray-500 focus:border-blue-500 focus:outline-none" />
)}
); }