'use client'; import { useState } from 'react'; import { ChevronDown } from 'lucide-react'; import type { PredictionState, ModelInfoResponse, PredictionSummary } from '@/types/predictions'; import ModelSelector from '@/components/ModelSelector'; interface PredictionPanelProps { predictionState: PredictionState; onToggleVisibility: () => void; onFetchPredictions: () => void; onFetchBatchPredictions: () => void; onConfidenceChange: (threshold: number) => void; onToggleLabelSelection: (label: string) => void; predictionSummary: PredictionSummary | null; isModelOnline: boolean; showOnlyDisagreements?: boolean; onToggleShowOnlyDisagreements?: () => void; onModelLoaded?: () => void; onModelLoadError?: (msg: string) => void; onModelLoadStart?: () => void; } export default function PredictionPanel({ predictionState, onToggleVisibility, onFetchPredictions, onFetchBatchPredictions, onConfidenceChange, onToggleLabelSelection, predictionSummary, isModelOnline, showOnlyDisagreements = false, onToggleShowOnlyDisagreements, onModelLoaded, onModelLoadError, onModelLoadStart, }: PredictionPanelProps) { const [expanded, setExpanded] = useState(true); const [modelLoadError, setModelLoadError] = useState(null); const { visible, isLoading, error, modelInfo, confidenceThreshold, selectedLabels, spans, } = predictionState; return (
{/* Collapsible header */} {expanded && (
{!isModelOnline ? (

Prediction service unavailable.

) : ( <> {/* Model Info */} {modelInfo && (
Model: {modelInfo.model_name}
Version: {modelInfo.model_version || 'N/A'}
Type: {modelInfo.model_type}
)} {/* Model Selector */} {onModelLoaded && (
{ setModelLoadError(null); onModelLoaded(); }} onLoadError={(msg) => { setModelLoadError(msg); onModelLoadError?.(msg); }} onLoadStart={() => onModelLoadStart?.()} disabled={isLoading} /> {modelLoadError && (

{modelLoadError}

)}
)} {/* Action Buttons */}
{/* Error */} {error && (

{error}

)} {/* Confidence Slider */}
{(confidenceThreshold * 100).toFixed(0)}%
onConfidenceChange(Number(e.target.value) / 100)} className="w-full h-1 bg-secondary rounded-lg appearance-none cursor-pointer accent-primary" />
{/* Show on chart toggle */}
{/* Label Filter */} {modelInfo && modelInfo.labels.length > 0 && (
{modelInfo.labels.map((label) => { const metrics = modelInfo.per_class_metrics.find((m) => m.label === label); return ( ); })}
)} {/* Disagreement Filter */} {predictionSummary && predictionSummary.disagreements.length > 0 && onToggleShowOnlyDisagreements && ( )} {/* Summary */} {visible && spans.length > 0 && predictionSummary ? (
Predictions: {predictionSummary.total_predictions}
Agreements: {predictionSummary.agreements}
Disagreements: {predictionSummary.disagreements.length}
) : (

No predictions loaded.

)} )}
)}
); }