import { useState, useEffect } from 'react'; import { Search, AlertCircle, CheckCircle2, Clock, ArrowLeft } from 'lucide-react'; import { useClient } from 'hooks/useClient'; import { Card } from '@/components/Card'; import { MessageBubble, type ChatMessage } from 'apps/Chat'; type LogMetadata = { filename: string; taskName: string; taskDirName: string; entryName: string; entryType: 'file' | 'directory'; provider: string; model: string; startedAt: string; completedAt: string | null; isError: boolean; }; type FullLog = LogMetadata & { messages: ChatMessage[]; }; const formatDate = (iso: string) => { const d = new Date(iso); return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); }; const ProviderBadge = ({ provider }: { provider: string }) => ( {provider} ); export const TaskLogs = () => { const client = useClient(); const [logs, setLogs] = useState([]); const [selectedFilename, setSelectedFilename] = useState(null); const [showDetail, setShowDetail] = useState(false); const [selectedLog, setSelectedLog] = useState(null); const [search, setSearch] = useState(''); const [isLoading, setIsLoading] = useState(true); useEffect(() => { client .get('/task-logs') .then((data) => { setLogs(data); setIsLoading(false); }) .catch(() => setIsLoading(false)); }, []); useEffect(() => { if (!selectedFilename) { setSelectedLog(null); return; } client .get(`/task-logs/${selectedFilename}`) .then(setSelectedLog) .catch(() => setSelectedLog(null)); }, [selectedFilename]); const filtered = search ? logs.filter((l) => { const q = search.toLowerCase(); return ( l.taskName.toLowerCase().includes(q) || l.entryName.toLowerCase().includes(q) || l.provider.toLowerCase().includes(q) ); }) : logs; return ( {/* Left panel: list */} setSearch(ev.target.value)} className="w-full pl-8 pr-3 py-1.5 text-sm rounded-md border border-duck-dark/15 bg-background/60 text-duck-dark placeholder:text-duck-dark/30 focus:outline-none focus:ring-1 focus:ring-duck-teal/40" /> {isLoading && ( Loading... )} {!isLoading && filtered.length === 0 && ( No logs found )} {filtered.map((log) => ( { setSelectedFilename(log.filename); setShowDetail(true); }} className={`w-full text-left px-3 py-2.5 border-b border-duck-dark/5 hover:bg-duck-dark/5 transition-colors cursor-pointer ${selectedFilename === log.filename ? 'bg-duck-teal/10' : ''}`} > {log.isError ? ( ) : log.completedAt ? ( ) : ( )} {log.taskName} {log.entryName} {formatDate(log.startedAt)} ))} {/* Right panel: log viewer */} {!selectedLog && ( Select a log to view setShowDetail(false)} className="md:hidden text-duck-teal text-xs cursor-pointer"> Back to list )} {selectedLog && ( <> setShowDetail(false)} className="md:hidden p-1 -ml-1 rounded hover:bg-duck-dark/10 cursor-pointer" > {selectedLog.taskName} {selectedLog.entryName} · {selectedLog.model} · {formatDate(selectedLog.startedAt)} {selectedLog.completedAt && ` — ${formatDate(selectedLog.completedAt)}`} {selectedLog.isError && ( Error )} {selectedLog.messages.map((msg, i) => ( ))} > )} ); };