'use client'; import { useState } from 'react'; import { Trash2, ChevronDown, ChevronUp } from 'lucide-react'; import { Button } from '@/components/ui/button'; interface SpanAnnotation { id: number; chart_id: number; start_time: number; end_time: number; label: string; confidence: number | null; outcome: string | null; notes: string | null; sub_spans: any; color: string; created_at: number; } interface SpanLabelType { id: number; name: string; display_name: string; color: string; hotkey: string | null; is_active: number; sort_order: number; created_at: number; } interface SpanAnnotationListProps { spanAnnotations: SpanAnnotation[]; spanLabelTypes: SpanLabelType[]; selectedSpanId: number | null; onSelectSpan: (spanId: number) => void; onDeleteSpan: (spanId: number) => void; } export default function SpanAnnotationList({ spanAnnotations, spanLabelTypes, selectedSpanId, onSelectSpan, onDeleteSpan, }: SpanAnnotationListProps) { const [expanded, setExpanded] = useState(true); // Format timestamp to readable date/time const formatTime = (timestamp: number) => { return new Date(timestamp * 1000).toLocaleString('en-US', { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', }); }; // Get display name and color for label const getLabelInfo = (labelName: string) => { const labelType = spanLabelTypes.find((t) => t.name === labelName); return { displayName: labelType?.display_name || labelName.replace(/_/g, ' ').toUpperCase(), color: labelType?.color || '#2196F3', }; }; // Calculate count per label type const labelCounts = spanAnnotations.reduce((acc, span) => { acc[span.label] = (acc[span.label] || 0) + 1; return acc; }, {} as Record); // Sort by start_time descending (most recent first) const sortedSpans = [...spanAnnotations].sort((a, b) => b.start_time - a.start_time); return (
{/* Header with collapse toggle */} {expanded && ( <> {/* Count summary */} {spanAnnotations.length > 0 && (
{Object.entries(labelCounts).map(([labelName, count], idx) => { const { displayName } = getLabelInfo(labelName); return ( {idx > 0 && ' | '} {displayName}: {count} ); })}
)} {/* Empty state */} {spanAnnotations.length === 0 ? (
No span annotations yet. Use the Span tool to select candle ranges.
) : ( /* Span list */
{sortedSpans.map((span) => { const { displayName, color } = getLabelInfo(span.label); const isSelected = span.id === selectedSpanId; return (
onSelectSpan(span.id)} >
{/* Time range */}
{formatTime(span.start_time)} → {formatTime(span.end_time)}
{/* Label badge */}
{displayName}
{/* Additional info */} {(span.confidence || span.outcome) && (
{span.confidence && Confidence: {span.confidence}} {span.outcome && Outcome: {span.outcome}}
)} {/* Notes preview */} {span.notes && (
{span.notes}
)}
{/* Delete button */}
); })}
)} )}
); }