import { useState, useCallback, useEffect } from "react"; import { useParams, useNavigate } from "react-router"; import { Masthead } from "../components/Masthead"; import { TimelineList } from "../components/history/TimelineList"; import { ConversationView } from "../components/history/ConversationView"; import { CheckpointList } from "../components/history/CheckpointList"; import { HistoryFilters } from "../components/history/HistoryFilters"; import { ResumeControls } from "../components/history/ResumeControls"; import { useAuth } from "../contexts/AuthContext"; import type { HistoryEvent, TaskConversationResponse, SupervisorConversationResponse, TaskCheckpoint, ContractSummary, } from "../lib/api"; import { getTimeline, getTaskConversation, getSupervisorConversation, getTaskCheckpoints, listContracts, } from "../lib/api"; // Detail view modes type DetailMode = "conversation" | "checkpoints"; export default function HistoryPage() { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const { isAuthenticated, isAuthConfigured, isLoading: authLoading } = useAuth(); // Timeline state const [events, setEvents] = useState([]); const [totalCount, setTotalCount] = useState(0); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); // Filters const [contracts, setContracts] = useState([]); const [selectedContractId, setSelectedContractId] = useState(null); const [selectedTaskId, setSelectedTaskId] = useState(null); const [dateFrom, setDateFrom] = useState(""); const [dateTo, setDateTo] = useState(""); // Selected event and detail const [selectedEvent, setSelectedEvent] = useState(null); const [detailMode, setDetailMode] = useState("conversation"); const [conversation, setConversation] = useState< TaskConversationResponse | SupervisorConversationResponse | null >(null); const [checkpoints, setCheckpoints] = useState([]); const [detailLoading, setDetailLoading] = useState(false); // Redirect to login if not authenticated useEffect(() => { if (!authLoading && isAuthConfigured && !isAuthenticated) { navigate("/login"); } }, [authLoading, isAuthConfigured, isAuthenticated, navigate]); // Load contracts for filter dropdown useEffect(() => { async function loadContracts() { try { const response = await listContracts(); setContracts(response.contracts); } catch (e) { console.error("Failed to load contracts:", e); } } if (isAuthenticated || !isAuthConfigured) { loadContracts(); } }, [isAuthenticated, isAuthConfigured]); // Load timeline const loadTimeline = useCallback(async () => { setLoading(true); setError(null); try { const response = await getTimeline({ contractId: selectedContractId || undefined, taskId: selectedTaskId || undefined, from: dateFrom || undefined, to: dateTo || undefined, limit: 100, }); setEvents(response.entries); setTotalCount(response.totalCount); } catch (e) { console.error("Failed to load timeline:", e); setError(e instanceof Error ? e.message : "Failed to load timeline"); } finally { setLoading(false); } }, [selectedContractId, selectedTaskId, dateFrom, dateTo]); // Load timeline on mount and filter change useEffect(() => { if (isAuthenticated || !isAuthConfigured) { loadTimeline(); } }, [loadTimeline, isAuthenticated, isAuthConfigured]); // Load detail when event selected const handleSelectEvent = useCallback(async (event: HistoryEvent) => { setSelectedEvent(event); setDetailLoading(true); try { // Determine if this is a task or supervisor event if (event.taskId) { // Load task conversation and checkpoints const [conv, cps] = await Promise.all([ getTaskConversation(event.taskId, { includeToolCalls: true, includeToolResults: true, }), getTaskCheckpoints(event.taskId).catch(() => []), ]); setConversation(conv); setCheckpoints(cps); } else if (event.contractId) { // Load supervisor conversation const conv = await getSupervisorConversation(event.contractId); setConversation(conv); setCheckpoints([]); } } catch (e) { console.error("Failed to load event details:", e); } finally { setDetailLoading(false); } }, []); // Handle URL param for direct navigation useEffect(() => { if (id && events.length > 0) { const event = events.find((e) => e.taskId === id || e.contractId === id); if (event && event !== selectedEvent) { handleSelectEvent(event); } } }, [id, events, selectedEvent, handleSelectEvent]); // Clear selection const handleClearSelection = useCallback(() => { setSelectedEvent(null); setConversation(null); setCheckpoints([]); navigate("/history"); }, [navigate]); // Handle filter changes const handleContractChange = useCallback((contractId: string | null) => { setSelectedContractId(contractId); setSelectedTaskId(null); // Reset task filter when contract changes }, []); // Handle actions completed const handleActionComplete = useCallback(() => { // Refresh timeline and detail after action loadTimeline(); if (selectedEvent?.taskId) { handleSelectEvent(selectedEvent); } }, [loadTimeline, selectedEvent, handleSelectEvent]); if (authLoading) { return (
Loading...
); } return (
{/* Filters */} {/* Main content area */}
{/* Timeline list */}
{/* Detail panel */}
{selectedEvent ? ( <> {/* Detail header */}
{selectedEvent.eventType} {selectedEvent.eventSubtype && ` / ${selectedEvent.eventSubtype}`}
{new Date(selectedEvent.createdAt).toLocaleString()}
{/* Mode toggle (only if task has checkpoints) */} {checkpoints.length > 0 && (
)}
{/* Detail content */}
{detailLoading ? (
Loading...
) : detailMode === "conversation" && conversation ? ( ) : detailMode === "checkpoints" && checkpoints.length > 0 ? ( ) : (
No {detailMode} data available
)}
{/* Resume controls */} {selectedEvent.taskId && ( )} ) : (
Select an event to view details
View conversation history, checkpoints, and more
)}
); }