import { BaseEdge, type Edge, EdgeLabelRenderer, type EdgeProps, getSmoothStepPath, useReactFlow } from '@xyflow/react'; import { AlertCircle, Pencil } from 'lucide-react'; import { useCallback, useState } from 'react'; import { useWorkflow } from "@/app/workflow/[workflowId]/contexts/WorkflowContext"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from '@/components/ui/textarea'; import { cn } from "@/lib/utils"; import { FlowEdge, FlowEdgeData, FlowNode } from '../types'; type CustomEdge = Edge<{ value: number }, 'custom'>; interface EdgeDetailsDialogProps { open: boolean; onOpenChange: (open: boolean) => void; data?: FlowEdgeData; onSave: (value: FlowEdgeData) => void; } const EdgeDetailsDialog = ({ open, onOpenChange, data, onSave }: EdgeDetailsDialogProps) => { const [condition, setCondition] = useState(data?.condition ?? ''); const [label, setLabel] = useState(data?.label ?? ''); const handleSave = () => { onSave({ condition: condition, label: label }); onOpenChange(false); }; return ( Edit Condition {data?.invalid && data.validationMessage && ( {data.validationMessage} )} Condition Label Enter a short label which helps identify this pathway in logs setLabel(e.target.value)} /> {label.length}/64 characters Condition Describe a condition that will be evaluated to determine if this pathway should be taken setCondition(e.target.value)} /> onOpenChange(false)}>Cancel Save ); }; interface CustomEdgeProps extends EdgeProps { data: FlowEdgeData; } export default function CustomEdge(props: CustomEdgeProps) { const { id, source, target, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, data } = props; const { getEdges, setEdges } = useReactFlow(); const { saveWorkflow } = useWorkflow(); const parallel = getEdges().filter( (e) => (e.source === source && e.target === target) || (e.source === target && e.target === source) ); // 2) if there are two, sort by id and pick an index let offsetX = 0; let offsetY = 0; if (parallel.length > 1) { const sorted = parallel.slice().sort((a, b) => a.id.localeCompare(b.id)); const idx = sorted.findIndex((e) => e.id === id); // first edge (idx 0) moves right & down; // second edge (idx 1) moves left & up if (idx === 0) { offsetX = 100; offsetY = 0; } else { offsetX = 0; offsetY = -50; } } // 3) draw the straight path + get label coords const [edgePath, labelX, labelY] = getSmoothStepPath({ sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition, }); const [open, setOpen] = useState(false); const handleSaveEdgeData = useCallback(async (updatedData: FlowEdgeData) => { // Update the node data in the ReactFlow nodes state setEdges((edges) => { const updatedEdges = edges.map((edge) => edge.id === id ? { ...edge, data: updatedData } : edge ) return updatedEdges; } ); // Save the workflow after updating edge data with a small delay to ensure state is updated setTimeout(async () => { await saveWorkflow(); }, 100); }, [id, setEdges, saveWorkflow]); return ( <> {data?.label || data?.condition || 'Set Condition'} setOpen(true)} > > ); }