"use client"; import { memo, useCallback, useState } from "react"; import { Separator } from "@/components/ui/separator"; import { UINode } from "lib/ai/workflow/workflow.interface"; import { Loader, PlayIcon, AlignHorizontalSpaceAround } from "lucide-react"; import { Button } from "ui/button"; import equal from "lib/equal"; import { Avatar, AvatarFallback, AvatarImage } from "ui/avatar"; import { Tooltip, TooltipContent, TooltipTrigger } from "ui/tooltip"; import { ShareableActions } from "@/components/shareable-actions"; import { DBWorkflow } from "app-types/workflow"; import { SelectedNodeConfigTab } from "./selected-node-config-tab"; import { ExecuteTab } from "./node-config/execute-tab"; import { useReactFlow } from "@xyflow/react"; import { safe } from "ts-safe"; import { handleErrorWithToast } from "ui/shared-toast"; import { mutate } from "swr"; import { allNodeValidate } from "lib/ai/workflow/node-validate"; import { toast } from "sonner"; import { useTranslations } from "next-intl"; import { arrangeNodes } from "lib/ai/workflow/arrange-nodes"; import { EditWorkflowPopup } from "./edit-workflow-popup"; export const WorkflowPanel = memo( function WorkflowPanel({ selectedNode, isProcessing, onSave, workflow, addProcess, hasEditAccess, }: { selectedNode?: UINode; onSave: () => Promise; isProcessing: boolean; workflow: DBWorkflow; addProcess: () => () => void; hasEditAccess?: boolean; }) { const { setNodes, getNodes, getEdges } = useReactFlow(); const [showExecutePanel, setShowExecutePanel] = useState(false); const [isEditing, setIsEditing] = useState(false); const [isSaving, setIsSaving] = useState(false); const t = useTranslations(); const handleArrangeNodes = useCallback(() => { const nodes = getNodes() as UINode[]; const edges = getEdges(); const { nodes: arrangedNodes } = arrangeNodes(nodes, edges); setNodes(arrangedNodes); toast.success(t("Workflow.nodesArranged")); }, [getNodes, getEdges, setNodes, t]); const updateVisibility = useCallback( (visibility: DBWorkflow["visibility"]) => { setIsSaving(true); const close = addProcess(); safe(() => fetch(`/api/workflow/${workflow.id}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ visibility, }), }).then((res) => { if (res.status != 200) throw new Error(res.statusText); }), ) .ifOk(() => mutate(`/api/workflow/${workflow.id}`)) .ifFail((e) => handleErrorWithToast(e)) .watch(() => { setIsSaving(false); close(); }); }, [workflow], ); const updatePublished = useCallback( (isPublished: boolean) => { if (isPublished) { const validateResult = allNodeValidate({ nodes: getNodes() as UINode[], edges: getEdges(), }); if (validateResult !== true) { if (validateResult.node) { setNodes((nds) => { return nds.map((node) => { if (node.id === validateResult.node?.id) { return { ...node, selected: true }; } if (node.selected) { return { ...node, selected: false }; } return node; }); }); } return toast.warning(validateResult.errorMessage); } } const close = addProcess(); safe(() => onSave()) .ifOk(() => fetch(`/api/workflow/${workflow.id}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ isPublished, }), }).then((res) => { if (res.status != 200) throw new Error(res.statusText); }), ) .ifOk(() => mutate(`/api/workflow/${workflow.id}`)) .ifFail((e) => handleErrorWithToast(e)) .watch(close); }, [workflow], ); const handleWorkflowMasterSave = useCallback((workflow: DBWorkflow) => { mutate(`/api/workflow/${workflow.id}`); setIsEditing(false); }, []); return (
setIsEditing(true)} className="border transition-colors hover:bg-secondary! group items-center justify-center flex w-8 h-8 rounded-md ring ring-background hover:ring-ring" >

{workflow?.name}

{t("Workflow.arrangeNodes")}

{!workflow.isPublished && ( {t("Workflow.autoSaveDescription")} )}

{workflow.isPublished ? t("Workflow.publishedDescription") : t("Workflow.draftDescription")}

{selectedNode && } {showExecutePanel && ( { if (isProcessing) return; setShowExecutePanel(false); }} onSave={onSave} /> )}
); }, (prev, next) => { if (prev.isProcessing !== next.isProcessing) { return false; } if (Boolean(prev.selectedNode) !== Boolean(next.selectedNode)) { return false; } if (prev.hasEditAccess !== next.hasEditAccess) { return false; } if (!equal(prev.selectedNode?.data, next.selectedNode?.data)) { return false; } if (!equal(prev.workflow, next.workflow)) return false; return true; }, );