'use client'; import React, { useState, useMemo } from 'react'; import { Plus } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { ConditionalStep } from '@/components/agents/workflows/conditional-workflow-builder'; import { StepCard } from './step-card'; import { ConditionalGroup } from './conditional-group'; import { AnimatedFlowLine } from '../animated-flow-line'; import { DndContext, closestCenter, KeyboardSensor, PointerSensor, useSensor, useSensors, DragEndEvent, DragOverlay, DragStartEvent, } from '@dnd-kit/core'; import { arrayMove, SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy, } from '@dnd-kit/sortable'; interface WorkflowStepsProps { steps: ConditionalStep[]; onAddStep: (index: number, parentStepId?: string) => void; onEditStep: (step: ConditionalStep) => void; onUpdateStep: (updates: Partial) => void; onDeleteStep: (stepId: string) => void; onAddElseIf: (afterStepId: string) => void; onAddElse: (afterStepId: string) => void; onStepsChange: (steps: ConditionalStep[]) => void; agentTools?: any; isLoadingTools?: boolean; } export function WorkflowSteps({ steps, onAddStep, onEditStep, onUpdateStep, onDeleteStep, onAddElseIf, onAddElse, onStepsChange, agentTools, isLoadingTools }: WorkflowStepsProps) { const [activeId, setActiveId] = useState(null); const sensors = useSensors( useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates, }) ); // Group conditional steps and generate sortable IDs const { groupedSteps, sortableIds } = useMemo(() => { const result: Array = []; const ids: string[] = []; let currentConditionGroup: ConditionalStep[] = []; steps.forEach(step => { if (step.type === 'condition') { currentConditionGroup.push(step); } else { if (currentConditionGroup.length > 0) { const group = [...currentConditionGroup]; result.push(group); ids.push(`condition-group-${group[0]?.id}`); currentConditionGroup = []; } result.push(step); ids.push(step.id); } }); if (currentConditionGroup.length > 0) { const group = [...currentConditionGroup]; result.push(group); ids.push(`condition-group-${group[0]?.id}`); } return { groupedSteps: result, sortableIds: ids }; }, [steps]); const handleDragStart = (event: DragStartEvent) => { setActiveId(event.active.id as string); }; const handleDragEnd = (event: DragEndEvent) => { const { active, over } = event; setActiveId(null); if (active.id !== over?.id) { const activeId = active.id as string; const overId = over?.id as string; // Find indices in the sortableIds array const oldIndex = sortableIds.findIndex(id => id === activeId); const newIndex = sortableIds.findIndex(id => id === overId); if (oldIndex !== -1 && newIndex !== -1) { // Reorder the groupedSteps array const newGroupedSteps = arrayMove(groupedSteps, oldIndex, newIndex); // Flatten back to steps array const newSteps: ConditionalStep[] = []; newGroupedSteps.forEach(item => { if (Array.isArray(item)) { newSteps.push(...item); } else { newSteps.push(item); } }); // Update order values newSteps.forEach((step, idx) => { step.order = idx; }); // Call the parent's onStepsChange onStepsChange(newSteps); } } }; return (
{groupedSteps.map((item, index) => ( {Array.isArray(item) ? ( // Conditional flow ) : ( // Regular step )} {/* Flow line between steps with hover add button */} {index < groupedSteps.length - 1 && (
{/* Ghost add button that appears on hover */}
)}
))}
{/* Add step button at the bottom */}
{activeId ? (
{(() => { const activeStep = steps.find(s => s.id === activeId); if (!activeStep) return null; return ( { }} onUpdateStep={() => { }} /> ); })()}
) : null}
); }