import { useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { RefreshCw, Search, Server, GitBranch } from 'lucide-react'; import apiClient from '../../api/client'; interface EventLog { id: number; trace_id: string; event_type: string; level: string; node_name: string | null; message: string; metadata: Record | null; created_at: string | null; } interface WorkflowSummary { trace_id: string; title: string; status: string; created_at: string; } interface WorkflowStep { name: string; step: number; node: string; action: string; status: string; agent_id: string | null; } const LEVEL_STYLES: Record = { error: { bg: 'bg-[rgba(196,145,122,0.12)]', text: 'text-[#a0705a]', label: 'ERROR' }, warn: { bg: 'bg-[rgba(196,168,130,0.15)]', text: 'text-[#9a7d5e]', label: 'WARN' }, info: { bg: 'bg-[rgba(156,175,136,0.12)]', text: 'text-[#7a8e6a]', label: 'INFO' }, }; const STATUS_STYLES: Record = { completed: { bg: 'bg-success-bg', text: 'text-success' }, failed: { bg: 'bg-[rgba(196,145,122,0.12)]', text: 'text-[#a0705a]' }, working: { bg: 'bg-[rgba(156,175,136,0.12)]', text: 'text-[#7a8e6a]' }, pending: { bg: 'bg-bg-secondary', text: 'text-text-muted' }, }; export function SystemLogsView() { const { t } = useTranslation(); const [tab, setTab] = useState<'system' | 'workflow'>('system'); // System logs state const [logs, setLogs] = useState([]); const [loading, setLoading] = useState(false); const [traceFilter, setTraceFilter] = useState(''); const [typeFilter, setTypeFilter] = useState(''); const [levelFilter, setLevelFilter] = useState(''); // Workflow logs state const [workflows, setWorkflows] = useState([]); const [selectedTrace, setSelectedTrace] = useState(null); const [workflowSteps, setWorkflowSteps] = useState([]); const [wfLoading, setWfLoading] = useState(false); const fetchLogs = async () => { setLoading(true); try { const params = new URLSearchParams(); if (traceFilter) params.set('trace_id', traceFilter); if (typeFilter) params.set('event_type', typeFilter); if (levelFilter) params.set('level', levelFilter); params.set('limit', '200'); const resp = await apiClient.get(`/api/v1/system/logs?${params.toString()}`); setLogs(resp.data.logs || []); } catch (err) { console.error(err); } finally { setLoading(false); } }; const fetchWorkflows = async () => { try { const resp = await apiClient.get('/api/v1/workflow/list'); setWorkflows(resp.data.workflows || []); } catch (err) { console.error(err); } }; const fetchWorkflowDetail = async (traceId: string) => { setWfLoading(true); try { const resp = await apiClient.get(`/api/v1/workflow/${traceId}`); setWorkflowSteps(resp.data.steps || []); } catch (err) { console.error(err); setWorkflowSteps([]); } finally { setWfLoading(false); } }; useEffect(() => { if (tab === 'system') fetchLogs(); else fetchWorkflows(); }, [tab]); useEffect(() => { if (selectedTrace) fetchWorkflowDetail(selectedTrace); }, [selectedTrace]); const handleSearch = (e: React.FormEvent) => { e.preventDefault(); fetchLogs(); }; return (
{/* Tab Switcher */}
{tab === 'system' ? ( <>
setTraceFilter(e.target.value)} placeholder={t('agent.logFilterTraceId')} className="px-3 py-2 bg-bg-card border border-border-primary rounded-lg text-sm text-text-primary placeholder:text-text-muted/50 focus:outline-none focus:ring-2 focus:ring-accent/15" />
{logs.length === 0 ? ( ) : ( logs.map((log) => { const style = LEVEL_STYLES[log.level] || LEVEL_STYLES.info; return ( ); }) )}
{t('agent.logLevel')} {t('agent.logType')} Trace ID {t('agent.logNode')} {t('agent.logMessage')} {t('agent.logTime')}
{t('agent.noLogs')}
{style.label} {log.event_type} {log.trace_id.slice(-8)} {log.node_name || '-'} {log.message} {log.created_at ? new Date(log.created_at).toLocaleString() : '-'}
) : (
{/* Workflow List */}
{t('agent.workflowLogList')}
{workflows.length === 0 ? (
{t('workflow.noWorkflows')}
) : ( workflows.map((wf) => ( )) )}
{/* Workflow Steps Detail */}
{!selectedTrace ? (
{t('agent.selectWorkflowToView')}
) : wfLoading ? (
{t('common.loading')}
) : (
{workflowSteps.length === 0 ? ( ) : ( workflowSteps.map((step, idx) => { const ss = STATUS_STYLES[step.status] || STATUS_STYLES.pending; return ( ); }) )}
# {t('agent.name')} {t('agent.logNode')} {t('common.status')} {t('agent.workflowAction')}
{t('workflow.noStepsYet')}
{step.step || idx + 1} {step.name} {step.node} {step.status} {step.action}
)}
)}
); }