- Create ExecutionsView component with TanStack Table - Add status filter buttons with count badges - Add action buttons: Analytics, Metrics, Debug - Add debug modal with AI copy-paste button for failed executions - Generate detailed debug report with stage metrics and error context - Update executions page to use new component Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
111 lines
3.5 KiB
TypeScript
111 lines
3.5 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect } from 'react';
|
|
import { useParams } from 'next/navigation';
|
|
import Link from 'next/link';
|
|
import { ArrowLeft, PlayCircle, AlertCircle } from 'lucide-react';
|
|
import type { ExecutionStatus, PipelineDetail } from '@/lib/pipeline-types';
|
|
import { getPipeline, listExecutions } from '@/lib/pipeline-api';
|
|
import ExecutionsView from '@/components/ExecutionsView';
|
|
|
|
/**
|
|
* Execution history page for a pipeline.
|
|
* Uses TanStack Table for filtering, sorting, and pagination.
|
|
*/
|
|
export default function ExecutionsPage() {
|
|
const params = useParams();
|
|
const pipelineId = params.pipelineId as string;
|
|
|
|
const [pipeline, setPipeline] = useState<PipelineDetail | null>(null);
|
|
const [executions, setExecutions] = useState<ExecutionStatus[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const fetchData = async () => {
|
|
setLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
const [pipelineData, executionsData] = await Promise.all([
|
|
getPipeline(pipelineId),
|
|
listExecutions(pipelineId, { limit: 100 }),
|
|
]);
|
|
setPipeline(pipelineData);
|
|
setExecutions(executionsData);
|
|
} catch (e) {
|
|
setError(e instanceof Error ? e.message : 'Failed to load data');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (pipelineId) {
|
|
fetchData();
|
|
}
|
|
}, [pipelineId]);
|
|
|
|
return (
|
|
<div className="h-full overflow-y-auto p-6">
|
|
{/* Navigation */}
|
|
<div className="flex items-center justify-between mb-6">
|
|
<Link
|
|
href={`/pipelines/${pipelineId}`}
|
|
className="inline-flex items-center text-sm text-gray-500 hover:text-gray-700"
|
|
>
|
|
<ArrowLeft className="w-4 h-4 mr-1" />
|
|
Back to Pipeline
|
|
</Link>
|
|
|
|
<Link
|
|
href={`/pipelines/${pipelineId}/run`}
|
|
className="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 text-sm font-medium"
|
|
>
|
|
<PlayCircle className="w-4 h-4 mr-2" />
|
|
Run Pipeline
|
|
</Link>
|
|
</div>
|
|
|
|
{/* Header */}
|
|
<div className="mb-6">
|
|
<h1 className="text-2xl font-bold text-gray-900">
|
|
Execution History
|
|
</h1>
|
|
<p className="text-gray-500 mt-1">
|
|
{pipeline?.name || pipelineId} - View and analyze pipeline executions
|
|
</p>
|
|
</div>
|
|
|
|
{/* Content */}
|
|
{error ? (
|
|
<div className="text-center py-12">
|
|
<AlertCircle className="w-12 h-12 text-red-500 mx-auto mb-4" />
|
|
<p className="text-red-600">{error}</p>
|
|
<button
|
|
onClick={fetchData}
|
|
className="mt-4 px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700"
|
|
>
|
|
Retry
|
|
</button>
|
|
</div>
|
|
) : loading ? (
|
|
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-8">
|
|
<div className="animate-pulse space-y-4">
|
|
<div className="h-10 bg-gray-200 rounded w-full" />
|
|
<div className="h-12 bg-gray-100 rounded w-full" />
|
|
<div className="h-12 bg-gray-100 rounded w-full" />
|
|
<div className="h-12 bg-gray-100 rounded w-full" />
|
|
<div className="h-12 bg-gray-100 rounded w-full" />
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<ExecutionsView
|
|
executions={executions}
|
|
pipelineId={pipelineId}
|
|
onRefresh={fetchData}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|