/** * WorkflowHistory - ZCLAW Workflow Execution History Component * * Displays the execution history of a specific workflow, * showing run details, status, and results. * * Design based on ZCLAW Dashboard v0.4.0 */ import { useState, useEffect, useCallback } from 'react'; import { useWorkflowStore, type Workflow, type WorkflowRun } from '../store/workflowStore'; import { ArrowLeft, Clock, CheckCircle, XCircle, AlertCircle, Loader2, ChevronRight, RefreshCw, History, } from 'lucide-react'; interface WorkflowHistoryProps { workflow: Workflow; isOpen?: boolean; onClose?: () => void; onBack?: () => void; } // Status configuration const STATUS_CONFIG: Record }> = { pending: { label: '等待中', className: 'text-gray-500 bg-gray-100', icon: Clock }, running: { label: '运行中', className: 'text-blue-600 bg-blue-100', icon: Loader2 }, completed: { label: '已完成', className: 'text-green-600 bg-green-100', icon: CheckCircle }, success: { label: '成功', className: 'text-green-600 bg-green-100', icon: CheckCircle }, failed: { label: '失败', className: 'text-red-600 bg-red-100', icon: XCircle }, error: { label: '错误', className: 'text-red-600 bg-red-100', icon: XCircle }, cancelled: { label: '已取消', className: 'text-gray-500 bg-gray-100', icon: XCircle }, paused: { label: '已暂停', className: 'text-yellow-600 bg-yellow-100', icon: AlertCircle }, }; // Run Card Component interface RunCardProps { run: WorkflowRun; index: number; } function RunCard({ run, index }: RunCardProps) { const [isExpanded, setIsExpanded] = useState(false); const config = STATUS_CONFIG[run.status] || STATUS_CONFIG.pending; const StatusIcon = config.icon; // Format result for display const resultText = run.result ? (typeof run.result === 'string' ? run.result : JSON.stringify(run.result, null, 2)) : undefined; return (
setIsExpanded(!isExpanded)} >
{index + 1} 运行 #{run.runId.slice(0, 8)} {run.step && ( 步骤: {run.step} )}
{config.label}
{/* Expanded Details */} {isExpanded && (
运行 ID {run.runId}
{resultText && (
结果:
{resultText}
)} {run.status === 'failed' && !resultText && (
执行失败,请检查日志获取详细信息。
)}
)}
); } export function WorkflowHistory({ workflow, onBack }: WorkflowHistoryProps) { const loadWorkflowRuns = useWorkflowStore((s) => s.loadWorkflowRuns); const cancelWorkflow = useWorkflowStore((s) => s.cancelWorkflow); const isLoading = useWorkflowStore((s) => s.isLoading); const [runs, setRuns] = useState([]); const [isRefreshing, setIsRefreshing] = useState(false); const [cancellingRunId, setCancellingRunId] = useState(null); // Load workflow runs const loadRuns = useCallback(async () => { try { const result = await loadWorkflowRuns(workflow.id, { limit: 50 }); setRuns(result || []); } catch { setRuns([]); } }, [workflow.id, loadWorkflowRuns]); useEffect(() => { loadRuns(); }, [loadRuns]); // Refresh runs const handleRefresh = useCallback(async () => { setIsRefreshing(true); try { await loadRuns(); } finally { setIsRefreshing(false); } }, [loadRuns]); // Cancel running workflow const handleCancel = useCallback(async (runId: string) => { if (!confirm('确定要取消这个正在运行的工作流吗?')) return; setCancellingRunId(runId); try { await cancelWorkflow(workflow.id, runId); await loadRuns(); } catch (error) { console.error('Failed to cancel workflow:', error); alert(`取消失败: ${error instanceof Error ? error.message : '未知错误'}`); } finally { setCancellingRunId(null); } }, [workflow.id, cancelWorkflow, loadRuns]); // Categorize runs const runningRuns = runs.filter(r => r.status === 'running'); const completedRuns = runs.filter(r => ['completed', 'success', 'failed', 'error', 'cancelled'].includes(r.status)); const pendingRuns = runs.filter(r => ['pending', 'paused'].includes(r.status)); return (
{/* Header */}

{workflow.name}

执行历史 ({runs.length} 次运行)

{/* Content */}
{/* Loading State */} {isLoading && runs.length === 0 && (

加载执行历史中...

)} {/* Running Runs */} {runningRuns.length > 0 && (

运行中 ({runningRuns.length})

{runningRuns.map((run, index) => (
))}
)} {/* Pending Runs */} {pendingRuns.length > 0 && (

等待中 ({pendingRuns.length})

{pendingRuns.map((run, index) => ( ))}
)} {/* Completed Runs */} {completedRuns.length > 0 && (

历史记录 ({completedRuns.length})

{completedRuns.map((run, index) => ( ))}
)} {/* Empty State */} {!isLoading && runs.length === 0 && (

暂无执行记录

运行此工作流后将在此显示历史记录

)}
); } export default WorkflowHistory;