import { useCallback, useEffect, useState } from 'react'; import { api } from '@renderer/api'; import { MemberExecutionLog } from '@renderer/components/team/members/MemberExecutionLog'; import { formatDuration } from '@renderer/utils/formatters'; import { AlertCircle, ChevronDown, ChevronRight, Clock, FileText, Loader2, MessageSquare, } from 'lucide-react'; import type { EnhancedChunk } from '@renderer/types/data'; import type { MemberLogSummary } from '@shared/types'; interface MemberLogsTabProps { teamName: string; memberName?: string; taskId?: string; } export const MemberLogsTab = ({ teamName, memberName, taskId, }: MemberLogsTabProps): React.JSX.Element => { const [logs, setLogs] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [expandedId, setExpandedId] = useState(null); const [detailChunks, setDetailChunks] = useState(null); const [detailLoading, setDetailLoading] = useState(false); useEffect(() => { let cancelled = false; setLoading(true); setError(null); void (async () => { try { if (taskId == null && !memberName) { if (!cancelled) setLogs([]); return; } const result = taskId != null ? await api.teams.getLogsForTask(teamName, taskId) : await api.teams.getMemberLogs(teamName, memberName!); if (!cancelled) { setLogs(result); } } catch (e) { if (!cancelled) { setError(e instanceof Error ? e.message : 'Unknown error'); } } finally { if (!cancelled) { setLoading(false); } } })(); return () => { cancelled = true; }; }, [teamName, memberName, taskId]); const handleExpand = useCallback( async (log: MemberLogSummary) => { const rowId = log.kind === 'subagent' ? `subagent:${log.sessionId}:${log.subagentId}` : `lead:${log.sessionId}`; if (expandedId === rowId) { setExpandedId(null); setDetailChunks(null); return; } setExpandedId(rowId); setDetailChunks(null); setDetailLoading(true); try { if (log.kind === 'subagent') { const d = await api.getSubagentDetail(log.projectId, log.sessionId, log.subagentId); setDetailChunks(d?.chunks ?? null); } else { const d = await api.getSessionDetail(log.projectId, log.sessionId); setDetailChunks((d?.chunks ?? null) as unknown as EnhancedChunk[] | null); } } catch { setDetailChunks(null); } finally { setDetailLoading(false); } }, [expandedId] ); if (loading) { return (
Searching logs...
); } if (error) { return (
{error}
); } if (logs.length === 0) { return (
No logs found

{taskId != null ? 'No session activity for this task yet' : 'This member has no recorded session activity yet'}

); } return (
{logs.map((log) => ( void handleExpand(log)} /> ))}
); }; interface LogCardProps { log: MemberLogSummary; expanded: boolean; detailChunks: EnhancedChunk[] | null; detailLoading: boolean; onToggle: () => void; } const LogCard = ({ log, expanded, detailChunks, detailLoading, onToggle, }: LogCardProps): React.JSX.Element => { const timeAgo = formatRelativeTime(log.startTime); return (
{expanded && (
{detailLoading && (
Loading details...
)} {!detailLoading && !detailChunks && (
Failed to load details
)} {!detailLoading && detailChunks && (
)}
)}
); }; function formatRelativeTime(isoString: string): string { const date = new Date(isoString); const now = Date.now(); const diffMs = now - date.getTime(); const diffMin = Math.floor(diffMs / 60_000); const diffHours = Math.floor(diffMin / 60); const diffDays = Math.floor(diffHours / 24); if (diffMin < 1) return 'just now'; if (diffMin < 60) return `${diffMin}m ago`; if (diffHours < 24) return `${diffHours}h ago`; if (diffDays < 7) return `${diffDays}d ago`; return date.toLocaleDateString(); }