import React from 'react'; import { GenerationStep } from '../types'; import { Database, Palette, FileText, Layers, Clapperboard, Check } from 'lucide-react'; interface PipelineVisualizerProps { currentStep: GenerationStep; onNavigate: (step: GenerationStep) => void; isProcessing: boolean; } const PipelineVisualizer: React.FC = ({ currentStep, onNavigate, isProcessing }) => { const nodes = [ { id: 'source', label: '來源選擇', icon: Database, steps: [GenerationStep.PROJECT_SELECTION], color: '#ec4899' }, { id: 'style', label: '風格定義', icon: Palette, steps: [GenerationStep.STYLE_SELECTION], color: '#8b5cf6' }, { id: 'blueprint', label: '結構藍圖', icon: FileText, steps: [GenerationStep.INPUT_PHASE, GenerationStep.STRUCTURE_PLANNING], color: '#6366f1' }, { id: 'assets', label: '資產合成', icon: Layers, steps: [GenerationStep.ASSET_MANAGEMENT], color: '#06b6d4' }, { id: 'prod', label: '分鏡製作', icon: Clapperboard, steps: [GenerationStep.PRODUCTION_DASHBOARD], color: '#10b981' }, ]; const getNodeStatus = (nodeSteps: GenerationStep[]) => { const stepOrder = Object.values(GenerationStep); const firstNodeStepIndex = stepOrder.indexOf(nodeSteps[0]); const currentStepIndex = stepOrder.indexOf(currentStep); if (nodeSteps.includes(currentStep)) return 'active'; if (currentStepIndex > firstNodeStepIndex) return 'completed'; return 'pending'; }; const getTargetStep = (nodeSteps: GenerationStep[]) => { return nodeSteps[0]; } const nodeWidth = 40; const gap = 120; const startX = 50; const y = 40; // Centered vertically in the available space of Header (excluding text) return (
{/* Edges */} {nodes.map((node, i) => { if (i === nodes.length - 1) return null; const status = getNodeStatus(node.steps); const nextStatus = getNodeStatus(nodes[i+1].steps); const isActiveFlow = status === 'active' && isProcessing; const isCompletedFlow = nextStatus === 'active' || nextStatus === 'completed'; return ( {(isActiveFlow || isCompletedFlow) && ( )} ); })} {/* Nodes */} {nodes.map((node, i) => { const status = getNodeStatus(node.steps); const cx = startX + (i * gap); const isClickable = status === 'completed' || status === 'active'; return ( isClickable && onNavigate(getTargetStep(node.steps))} className={`transition-all duration-300 ${isClickable ? 'cursor-pointer hover:opacity-80' : 'opacity-40 cursor-not-allowed'}`} > {status === 'active' && ( )}
{status === 'completed' ? : }
{node.label}
) })}
); }; export default PipelineVisualizer;