'use client'; import { useState, useEffect } from 'react'; import { useParams, useRouter } from 'next/navigation'; import Link from 'next/link'; import { ArrowLeft, Play, CheckCircle, Search, Loader, AlertCircle, ChevronRight, } from 'lucide-react'; import type { PipelineDetail } from '@/lib/pipeline-types'; import { getPipeline, executePipeline } from '@/lib/pipeline-api'; interface Job { job_id: string; business_name: string; place_id: string; status: string; total_reviews: number; created_at: string; completed_at?: string; } /** * Run Pipeline page - select a job to process through the pipeline. */ export default function RunPipelinePage() { const params = useParams(); const router = useRouter(); const pipelineId = params.pipelineId as string; const [pipeline, setPipeline] = useState(null); const [jobs, setJobs] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [searchQuery, setSearchQuery] = useState(''); const [selectedJob, setSelectedJob] = useState(null); const [executing, setExecuting] = useState(false); useEffect(() => { const fetchData = async () => { setLoading(true); setError(null); try { // Fetch pipeline details const pipelineData = await getPipeline(pipelineId); setPipeline(pipelineData); // Fetch completed jobs const response = await fetch('/api/jobs/?status=completed&limit=100'); if (!response.ok) throw new Error('Failed to fetch jobs'); const jobsData = await response.json(); // API returns { jobs: [...] } setJobs(jobsData.jobs || []); } catch (e) { setError(e instanceof Error ? e.message : 'Failed to load data'); } finally { setLoading(false); } }; fetchData(); }, [pipelineId]); const handleExecute = async () => { if (!selectedJob || !pipeline) return; setExecuting(true); setError(null); try { const execution = await executePipeline(pipelineId, { job_id: selectedJob.job_id, // Always run all stages - they're sequential and depend on each other stages: pipeline.stages, }); // Navigate to execution detail page router.push(`/pipelines/${pipelineId}/executions/${execution.execution_id}`); } catch (e) { setError(e instanceof Error ? e.message : 'Failed to execute pipeline'); setExecuting(false); } }; // Filter jobs by search query const filteredJobs = jobs.filter((job) => job.business_name.toLowerCase().includes(searchQuery.toLowerCase()) || job.place_id.toLowerCase().includes(searchQuery.toLowerCase()) ); if (loading) { return (
); } if (error && !pipeline) { return (
Back to Pipeline

{error}

); } return (
{/* Navigation */} Back to Pipeline {/* Header */}

Run Pipeline

{pipeline?.name} - Select a completed job to process

{error && (

{error}

)}
{/* Job Selection */}

Select Job

{/* Search */}
setSearchQuery(e.target.value)} placeholder="Search jobs by business name..." className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500" />
{/* Jobs List */}
{filteredJobs.length === 0 ? (

No completed jobs found

) : ( filteredJobs.map((job, index) => (
setSelectedJob(job)} className={`p-4 rounded-lg border-2 cursor-pointer transition-all ${ selectedJob?.job_id === job.job_id ? 'border-blue-500 bg-blue-50' : 'border-gray-200 hover:border-gray-300 hover:bg-gray-50' }`} >

{job.business_name}

{job.place_id}

{job.total_reviews} reviews {new Date(job.completed_at || job.created_at).toLocaleDateString()}
{selectedJob?.job_id === job.job_id ? ( ) : ( )}
)) )}
{/* Pipeline Info & Execute */}

Pipeline Stages

{/* Stages (read-only, informational) */}
{pipeline?.stages.map((stage, index) => (
{index + 1}
{stage}
))}

All stages will run sequentially. Each stage depends on the output of the previous stage.

{/* Summary */}

Summary

Selected Job: {selectedJob?.business_name || 'None'}
Reviews: {selectedJob?.total_reviews || '-'}
Stages: {pipeline?.stages.length || 0}
{/* Execute Button */}
); }