feat: implement Section 5 - Span Tool State & Integration

- Add SpanAnnotation and SpanLabelType interfaces to page.tsx
- Add span-related state: spanAnnotations, selectedSpanId, spanLabelTypes
- Add fetchSpanAnnotations() and fetchSpanLabelTypes() data fetching functions
- Load span annotations and label types when chart changes
- Add "Span" tool button to Toolbox component with RectangleHorizontal icon
- Mark Section 5 tasks (5.1-5.5) as complete in tasks.md

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Marko Djordjevic 2026-02-14 06:42:24 +01:00
parent 5ea63a613e
commit c9d2cbfc4b
3 changed files with 79 additions and 10 deletions

View file

@ -21,8 +21,33 @@ interface Annotation {
created_at: number;
}
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;
}
export default function Home() {
const [activeTool, setActiveTool] = useState<Tool>(null);
const [activeTool, setActiveTool] = useState<Tool | 'span'>(null);
const [selectedColor, setSelectedColor] = useState('#3b82f6');
const [selectedLabelId, setSelectedLabelId] = useState<number | null>(null);
const [annotations, setAnnotations] = useState<Annotation[]>([]);
@ -30,6 +55,11 @@ export default function Home() {
const [activeChartId, setActiveChartId] = useState<number | null>(null);
const chartRef = useRef<CandleChartHandle>(null);
// Span annotation state
const [spanAnnotations, setSpanAnnotations] = useState<SpanAnnotation[]>([]);
const [selectedSpanId, setSelectedSpanId] = useState<number | null>(null);
const [spanLabelTypes, setSpanLabelTypes] = useState<SpanLabelType[]>([]);
// Fetch charts list
const fetchCharts = useCallback(async () => {
try {
@ -58,25 +88,54 @@ export default function Home() {
}
}, []);
// Fetch charts on mount, auto-select the most recent
// Fetch span annotations for active chart
const fetchSpanAnnotations = useCallback(async (chartId: number | null) => {
if (!chartId) {
setSpanAnnotations([]);
return;
}
try {
const response = await fetch(`/api/span-annotations?chartId=${chartId}`);
const data = await response.json();
setSpanAnnotations(data);
} catch (error) {
console.error('Failed to fetch span annotations:', error);
}
}, []);
// Fetch span label types
const fetchSpanLabelTypes = useCallback(async () => {
try {
const response = await fetch('/api/span-label-types');
const data = await response.json();
setSpanLabelTypes(data);
} catch (error) {
console.error('Failed to fetch span label types:', error);
}
}, []);
// Fetch charts and span label types on mount, auto-select the most recent chart
useEffect(() => {
const init = async () => {
const chartList = await fetchCharts();
await fetchSpanLabelTypes();
if (chartList.length > 0) {
setActiveChartId(chartList[0].id); // sorted by created_at desc
}
};
init();
}, [fetchCharts]);
}, [fetchCharts, fetchSpanLabelTypes]);
// When activeChartId changes, refetch data
useEffect(() => {
if (activeChartId !== null) {
chartRef.current?.refreshData();
fetchAnnotations(activeChartId);
fetchSpanAnnotations(activeChartId);
setSelectedLabelId(null);
setSelectedSpanId(null);
}
}, [activeChartId, fetchAnnotations]);
}, [activeChartId, fetchAnnotations, fetchSpanAnnotations]);
const handleExport = () => {
if (activeChartId) {