import { NodeProps, NodeToolbar, Position } from "@xyflow/react"; import { Check, Copy, Edit, Trash2Icon, Webhook } from "lucide-react"; import Link from "next/link"; import { memo, useEffect, useMemo, useState } from "react"; import { useWorkflow } from "@/app/workflow/[workflowId]/contexts/WorkflowContext"; import { FlowNodeData } from "@/components/flow/types"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { NodeContent } from "./common/NodeContent"; import { NodeEditDialog } from "./common/NodeEditDialog"; import { useNodeHandlers } from "./common/useNodeHandlers"; interface TriggerNodeEditFormProps { name: string; setName: (value: string) => void; endpoint: string; } interface TriggerNodeProps extends NodeProps { data: FlowNodeData; } export const TriggerNode = memo(({ data, selected, id }: TriggerNodeProps) => { const { open, setOpen, handleSaveNodeData, handleDeleteNode } = useNodeHandlers({ id }); const { saveWorkflow } = useWorkflow(); // Form state const [name, setName] = useState(data.name || "API Trigger"); // Generate trigger_path if not present (should be done on node creation) const [triggerPath] = useState(() => data.trigger_path ?? crypto.randomUUID()); // Get backend URL from environment const backendUrl = process.env.NEXT_PUBLIC_BACKEND_URL || "http://localhost:8000"; const endpoint = `${backendUrl}/api/v1/public/agent/${triggerPath}`; // Copy state for button feedback const [copied, setCopied] = useState(false); // Compute if form has unsaved changes (simplified: only check name) const isDirty = useMemo(() => { return name !== (data.name || "API Trigger"); }, [name, data.name]); const handleCopy = async () => { await navigator.clipboard.writeText(endpoint); setCopied(true); setTimeout(() => setCopied(false), 2000); }; const handleSave = async () => { handleSaveNodeData({ ...data, name, trigger_path: triggerPath, }); setOpen(false); // Save the workflow after updating node data setTimeout(async () => { await saveWorkflow(); }, 100); }; // Reset form state when dialog opens const handleOpenChange = (newOpen: boolean) => { if (newOpen) { setName(data.name || "API Trigger"); } setOpen(newOpen); }; // Update form state when data changes (e.g., from undo/redo) useEffect(() => { if (open) { setName(data.name || "API Trigger"); } }, [data, open]); // Ensure trigger_path is saved on initial render if it was generated useEffect(() => { if (!data.trigger_path && triggerPath) { handleSaveNodeData({ ...data, trigger_path: triggerPath, name: data.name || "API Trigger", }); } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return ( <> } nodeType="trigger" onDoubleClick={() => setOpen(true)} nodeId={id} >

API Endpoint:

{endpoint}
{open && ( )} ); }); const TriggerNodeEditForm = ({ name, setName, endpoint, }: TriggerNodeEditFormProps) => { const [copied, setCopied] = useState(false); const [curlCopied, setCurlCopied] = useState(false); const handleCopyEndpoint = async () => { await navigator.clipboard.writeText(endpoint); setCopied(true); setTimeout(() => setCopied(false), 2000); }; const curlExample = `curl -X POST "${endpoint}" \\ -H "X-API-Key: YOUR_API_KEY" \\ -H "Content-Type: application/json" \\ -d '{"phone_number": "+1234567890", "initial_context": {}}'`; const handleCopyCurl = async () => { await navigator.clipboard.writeText(curlExample); setCurlCopied(true); setTimeout(() => setCurlCopied(false), 2000); }; return (
setName(e.target.value)} />
{endpoint}
                        {curlExample}
                    
); }; TriggerNode.displayName = "TriggerNode";