/** * RunTracingPanel * * Shows AutoStepLog entries for a workflow run with live-update capability. * Displays per-node status (running/completed/failed/skipped) with timing info. */ import React, { useState, useEffect, useCallback } from 'react'; import { useApiRequest } from '../../../hooks/useApi'; import type { AutoStepLog } from '../../../api/workflowApi'; interface RunTracingPanelProps { instanceId: string; runId: string | null; onNodeSelect?: (nodeId: string) => void; } const STATUS_COLORS: Record = { pending: '#999', running: '#f0ad4e', completed: '#28a745', failed: '#dc3545', skipped: '#6c757d', }; const STATUS_ICONS: Record = { pending: '○', running: '◉', completed: '✓', failed: '✗', skipped: '—', }; export const RunTracingPanel: React.FC = ({ instanceId, runId, onNodeSelect, }) => { const [steps, setSteps] = useState([]); const [loading, setLoading] = useState(false); const { request } = useApiRequest(); const loadSteps = useCallback(async () => { if (!runId || !instanceId) return; setLoading(true); try { const data = await request({ url: `/api/workflows/${instanceId}/runs/${runId}/steps`, method: 'get', }); setSteps(data?.steps || []); } catch (e) { console.error('[RunTracing] Failed to load steps:', e); } finally { setLoading(false); } }, [runId, instanceId, request]); useEffect(() => { loadSteps(); const interval = setInterval(loadSteps, 3000); return () => clearInterval(interval); }, [loadSteps]); if (!runId) { return (
Select a run to see tracing details.
); } return (
Run Steps {loading && (loading...)}
{steps.length === 0 && !loading && (
No steps recorded yet.
)} {steps.map((step) => (
onNodeSelect?.(step.nodeId)} style={{ padding: '8px 12px', marginBottom: '6px', borderRadius: '6px', border: `1px solid ${STATUS_COLORS[step.status] || '#ddd'}`, background: 'var(--bg-primary, #fff)', cursor: 'pointer', fontSize: '13px', }} >
{STATUS_ICONS[step.status] || '?'} {step.nodeType} ({step.nodeId}) {step.durationMs != null && ( {step.durationMs}ms )}
{step.error && (
{step.error}
)} {step.tokensUsed > 0 && (
{step.tokensUsed} tokens
)}
))}
); };