'use client' import { useState, useEffect, useRef } from 'react' import { useParams } from 'next/navigation' import Link from 'next/link' import type { Route } from 'next' import { trpc } from '@/lib/trpc/client' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Card, CardContent, } from '@/components/ui/card' import { Skeleton } from '@/components/ui/skeleton' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' import { toast } from 'sonner' import { cn } from '@/lib/utils' import { ArrowLeft, MoreHorizontal, Rocket, Archive, Settings2, Layers, GitBranch, Loader2, ChevronDown, } from 'lucide-react' import { InlineEditableText } from '@/components/ui/inline-editable-text' import { PipelineFlowchart } from '@/components/admin/pipeline/pipeline-flowchart' import { StageConfigEditor } from '@/components/admin/pipeline/stage-config-editor' import { usePipelineInlineEdit } from '@/hooks/use-pipeline-inline-edit' import { IntakePanel } from '@/components/admin/pipeline/stage-panels/intake-panel' import { FilterPanel } from '@/components/admin/pipeline/stage-panels/filter-panel' import { EvaluationPanel } from '@/components/admin/pipeline/stage-panels/evaluation-panel' import { SelectionPanel } from '@/components/admin/pipeline/stage-panels/selection-panel' import { LiveFinalPanel } from '@/components/admin/pipeline/stage-panels/live-final-panel' import { ResultsPanel } from '@/components/admin/pipeline/stage-panels/results-panel' const statusColors: Record = { DRAFT: 'bg-gray-100 text-gray-700', ACTIVE: 'bg-emerald-100 text-emerald-700', ARCHIVED: 'bg-muted text-muted-foreground', CLOSED: 'bg-blue-100 text-blue-700', } function StagePanel({ stageId, stageType, configJson, }: { stageId: string stageType: string configJson: Record | null }) { switch (stageType) { case 'INTAKE': return case 'FILTER': return case 'EVALUATION': return case 'SELECTION': return case 'LIVE_FINAL': return case 'RESULTS': return default: return ( Unknown stage type: {stageType} ) } } export default function PipelineDetailPage() { const params = useParams() const pipelineId = params.id as string const [selectedTrackId, setSelectedTrackId] = useState(null) const [selectedStageId, setSelectedStageId] = useState(null) const stagePanelRef = useRef(null) const { data: pipeline, isLoading } = trpc.pipeline.getDraft.useQuery({ id: pipelineId, }) const { isUpdating, updatePipeline, updateStageConfig } = usePipelineInlineEdit(pipelineId) const publishMutation = trpc.pipeline.publish.useMutation({ onSuccess: () => toast.success('Pipeline published'), onError: (err) => toast.error(err.message), }) const updateMutation = trpc.pipeline.update.useMutation({ onSuccess: () => toast.success('Pipeline updated'), onError: (err) => toast.error(err.message), }) // Auto-select first track and stage on load useEffect(() => { if (pipeline && pipeline.tracks.length > 0 && !selectedTrackId) { const firstTrack = pipeline.tracks.sort((a, b) => a.sortOrder - b.sortOrder)[0] setSelectedTrackId(firstTrack.id) if (firstTrack.stages.length > 0) { const firstStage = firstTrack.stages.sort((a, b) => a.sortOrder - b.sortOrder)[0] setSelectedStageId(firstStage.id) } } }, [pipeline, selectedTrackId]) // Scroll to stage panel when a stage is selected useEffect(() => { if (selectedStageId && stagePanelRef.current) { stagePanelRef.current.scrollIntoView({ behavior: 'smooth', block: 'start' }) } }, [selectedStageId]) if (isLoading) { return (
) } if (!pipeline) { return (

Pipeline Not Found

The requested pipeline does not exist

) } const selectedTrack = pipeline.tracks.find((t) => t.id === selectedTrackId) const selectedStage = selectedTrack?.stages.find( (s) => s.id === selectedStageId ) const handleTrackChange = (trackId: string) => { setSelectedTrackId(trackId) const track = pipeline.tracks.find((t) => t.id === trackId) if (track && track.stages.length > 0) { const firstStage = track.stages.sort((a, b) => a.sortOrder - b.sortOrder)[0] setSelectedStageId(firstStage.id) } else { setSelectedStageId(null) } } const handleStageSelect = (stageId: string) => { setSelectedStageId(stageId) } const handleStatusChange = async (newStatus: 'DRAFT' | 'ACTIVE' | 'CLOSED' | 'ARCHIVED') => { await updateMutation.mutateAsync({ id: pipelineId, status: newStatus, }) } // Prepare flowchart data for the selected track const flowchartTracks = selectedTrack ? [selectedTrack] : [] return (
{/* Header */}
updatePipeline({ name: newName })} variant="h1" placeholder="Untitled Pipeline" disabled={isUpdating} /> handleStatusChange('DRAFT')} disabled={pipeline.status === 'DRAFT' || updateMutation.isPending} > Draft handleStatusChange('ACTIVE')} disabled={pipeline.status === 'ACTIVE' || updateMutation.isPending} > Active handleStatusChange('CLOSED')} disabled={pipeline.status === 'CLOSED' || updateMutation.isPending} > Closed handleStatusChange('ARCHIVED')} disabled={pipeline.status === 'ARCHIVED' || updateMutation.isPending} > Archived
slug: updatePipeline({ slug: newSlug })} variant="mono" placeholder="pipeline-slug" disabled={isUpdating} />
{pipeline.status === 'DRAFT' && ( publishMutation.mutate({ id: pipelineId })} > {publishMutation.isPending ? ( ) : ( )} Publish )} {pipeline.status === 'ACTIVE' && ( handleStatusChange('CLOSED')} > Close Pipeline )} handleStatusChange('ARCHIVED')} > Archive
{/* Pipeline Summary */}
Tracks

{pipeline.tracks.length}

{pipeline.tracks.filter((t) => t.kind === 'MAIN').length} main,{' '} {pipeline.tracks.filter((t) => t.kind === 'AWARD').length} award

Stages

{pipeline.tracks.reduce((sum, t) => sum + t.stages.length, 0)}

across all tracks

Transitions

{pipeline.tracks.reduce( (sum, t) => sum + t.stages.reduce( (s, stage) => s + stage.transitionsFrom.length, 0 ), 0 )}

stage connections

{/* Track Switcher (only if multiple tracks) */} {pipeline.tracks.length > 1 && (
{pipeline.tracks .sort((a, b) => a.sortOrder - b.sortOrder) .map((track) => ( ))}
)} {/* Pipeline Flowchart */} {flowchartTracks.length > 0 ? ( ) : ( No tracks configured for this pipeline )} {/* Selected Stage Detail */}
{selectedStage ? (

Selected Stage: {selectedStage.name}

{/* Stage Config Editor */} | null} onSave={updateStageConfig} isSaving={isUpdating} /> {/* Stage Activity Panel */} | null} />
) : (

Click a stage in the flowchart above to view its configuration and activity

)}
) }