"use client" import { useCallback, useEffect, useState } from "react" import { PlusIcon } from "lucide-react" import { toast } from "sonner" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Textarea } from "@/components/ui/textarea" import { createAIWorkflow, fetchAIWorkflow, fetchAIWorkflowNodeSpecs, fetchAIWorkflows, publishAIWorkflow, updateAIWorkflow, validateAIWorkflow, type AIWorkflow, type AIWorkflowDefinition, type AIWorkflowNodeSpec, } from "@/lib/api/admin" import { WorkflowEditor } from "./_components/workflow-editor" const emptyDefinition: AIWorkflowDefinition = { schemaVersion: 2, nodes: [ { id: "start_1", type: "start", meta: { position: { x: 0, y: 80 } }, data: { title: "开始", config: {}, inputsValues: {} } }, { id: "end_1", type: "end", meta: { position: { x: 260, y: 80 } }, data: { title: "结束", config: {}, inputsValues: {} } }, ], edges: [{ sourceNodeID: "start_1", targetNodeID: "end_1", sourcePortID: "edge_start_end" }], } export default function DashboardAIWorkflowsPage() { const [items, setItems] = useState([]) const [nodeSpecs, setNodeSpecs] = useState([]) const [active, setActive] = useState(null) const [name, setName] = useState("") const [description, setDescription] = useState("") const [definition, setDefinition] = useState(emptyDefinition) const [saving, setSaving] = useState(false) const select = useCallback(async (id: number) => { const item = await fetchAIWorkflow(id) setActive(item) setName(item.name) setDescription(item.description) setDefinition(item.draftDefinition) }, []) const reload = useCallback(async () => { const [page, specs] = await Promise.all([fetchAIWorkflows({ limit: 100 }), fetchAIWorkflowNodeSpecs()]) setItems(page.results) setNodeSpecs(specs) if (!active && page.results[0]) await select(page.results[0].id) }, [active, select]) useEffect(() => { void reload().catch((error) => toast.error(error instanceof Error ? error.message : "加载工作流失败")) }, [reload]) async function save() { if (!name.trim()) { toast.error("请填写工作流名称"); return } setSaving(true) try { if (active) { await updateAIWorkflow({ id: active.id, name: name.trim(), description: description.trim(), definition }) await select(active.id) } else { const created = await createAIWorkflow({ name: name.trim(), description: description.trim(), definition }) await select(created.id) } await reload() toast.success("工作流草稿已保存") } catch (error) { toast.error(error instanceof Error ? error.message : "保存工作流失败") } finally { setSaving(false) } } async function publish() { if (!active) { toast.error("请先保存工作流草稿"); return } setSaving(true) try { const version = await publishAIWorkflow(active.id, definition) await select(active.id) await reload() toast.success(`已发布工作流 v${version.version}`) } catch (error) { toast.error(error instanceof Error ? error.message : "发布工作流失败") } finally { setSaving(false) } } function create() { setActive(null); setName(""); setDescription(""); setDefinition(emptyDefinition) } return
setName(event.target.value)} placeholder="工作流名称" className="max-w-sm" />