'use client'; import { useState, useEffect } from 'react'; import { useParams } from 'next/navigation'; import Link from 'next/link'; import { ArrowLeft, CheckCircle, XCircle, Clock, PlayCircle, Loader, RefreshCw, AlertCircle, } from 'lucide-react'; import type { ExecutionStatus, PipelineDetail } from '@/lib/pipeline-types'; import { getPipeline, listExecutions } from '@/lib/pipeline-api'; // Status badge component function StatusBadge({ status }: { status: ExecutionStatus['status'] }) { const config = { pending: { icon: Clock, color: 'bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-400', }, running: { icon: Loader, color: 'bg-blue-100 text-blue-600 dark:bg-blue-900/30 dark:text-blue-400', }, completed: { icon: CheckCircle, color: 'bg-green-100 text-green-600 dark:bg-green-900/30 dark:text-green-400', }, failed: { icon: XCircle, color: 'bg-red-100 text-red-600 dark:bg-red-900/30 dark:text-red-400', }, cancelled: { icon: AlertCircle, color: 'bg-yellow-100 text-yellow-600 dark:bg-yellow-900/30 dark:text-yellow-400', }, }; const { icon: Icon, color } = config[status] || config.pending; return ( {status.charAt(0).toUpperCase() + status.slice(1)} ); } // Format date string function formatDate(dateStr: string | undefined): string { if (!dateStr) return '-'; const date = new Date(dateStr); return date.toLocaleString(); } // Calculate duration function formatDuration(start?: string, end?: string): string { if (!start) return '-'; const startDate = new Date(start); const endDate = end ? new Date(end) : new Date(); const ms = endDate.getTime() - startDate.getTime(); if (ms < 1000) return `${ms}ms`; if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`; return `${(ms / 60000).toFixed(1)}m`; } /** * Execution history page for a pipeline. */ export default function ExecutionsPage() { const params = useParams(); const pipelineId = params.pipelineId as string; const [pipeline, setPipeline] = useState(null); const [executions, setExecutions] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [statusFilter, setStatusFilter] = useState(''); const fetchData = async () => { setLoading(true); setError(null); try { const [pipelineData, executionsData] = await Promise.all([ getPipeline(pipelineId), listExecutions(pipelineId, { status: statusFilter || undefined, limit: 50, }), ]); setPipeline(pipelineData); setExecutions(executionsData); } catch (e) { setError(e instanceof Error ? e.message : 'Failed to load data'); } finally { setLoading(false); } }; useEffect(() => { if (pipelineId) { fetchData(); } }, [pipelineId, statusFilter]); return (
{/* Navigation */}
Back to Dashboard
{/* Header */}

Execution History

{pipeline?.name || pipelineId}

{/* Filters */}
{/* Content */} {error ? (

{error}

) : loading ? (
{[1, 2, 3, 4, 5].map((i) => (
))}
) : executions.length === 0 ? (

No executions found

) : (
{executions.map((execution) => ( ))}
Status Execution ID Job / Business Stages Started Duration
{execution.id.slice(0, 8)}... {execution.job_id ? ( {execution.job_id.slice(0, 8)}... ) : execution.business_id ? ( {execution.business_id} ) : ( - )} {execution.stages_completed.length} / {execution.stages_requested.length} {execution.error_message && ( (error) )} {formatDate(execution.started_at)} {formatDuration(execution.started_at, execution.completed_at)}
)}
); }