// Studio scene — design a new FlowMaster process and publish it to EA2. import { useState } from "react"; import { useApp } from "../state/store"; import { api } from "../lib/api"; import { Branch, Play, Check, Close, Bot, User, Cog } from "../components/icons"; type NodeKind = "start" | "human_task" | "agent_task" | "service_task" | "end"; interface DraftNode { id: string; type: NodeKind; label: string; } interface DraftEdge { id: string; source: string; target: string; } const DEFAULT_NODES: DraftNode[] = [ { id: "start", type: "start", label: "Request received" }, { id: "review", type: "human_task", label: "Manager review" }, { id: "approved", type: "end", label: "Approved" }, ]; const DEFAULT_EDGES: DraftEdge[] = [ { id: "e1", source: "start", target: "review" }, { id: "e2", source: "review", target: "approved" }, ]; const KIND_ICON: Record React.ReactElement> = { start: Branch, end: Branch, human_task: User, agent_task: Bot, service_task: Cog, }; const ORG_ID = "a0000000-0000-0000-0000-000000000010"; export default function Studio() { const mode = useApp((s) => s.mode); const pushToast = useApp((s) => s.pushToast); const refreshLive = useApp((s) => s.refreshLive); const actor = useApp((s) => s.actor); const setScene = useApp((s) => s.setScene); const [name, setName] = useState("my-process-" + Math.random().toString(36).slice(2, 8)); const [displayName, setDisplayName] = useState("My Process"); const [hub, setHub] = useState("procurement"); const [description, setDescription] = useState("Demo process designed in the Studio."); const [nodes, setNodes] = useState(DEFAULT_NODES); const [edges, setEdges] = useState(DEFAULT_EDGES); const [publishing, setPublishing] = useState(false); const [lastResult, setLastResult] = useState<{ ok: boolean; key?: string; err?: string } | null>(null); const canPublish = mode === "live" && !!actor?.user_id; const addNode = () => { const idx = nodes.length; setNodes([...nodes, { id: `n${idx}`, type: "human_task", label: `New step ${idx}` }]); }; const removeNode = (id: string) => { setNodes(nodes.filter((n) => n.id !== id)); setEdges(edges.filter((e) => e.source !== id && e.target !== id)); }; const updateNode = (id: string, patch: Partial) => { setNodes(nodes.map((n) => (n.id === id ? { ...n, ...patch } : n))); }; const addEdge = () => { if (nodes.length < 2) return; const idx = edges.length; setEdges([...edges, { id: `e${idx + 1}`, source: nodes[0].id, target: nodes[nodes.length - 1].id }]); }; const removeEdge = (id: string) => setEdges(edges.filter((e) => e.id !== id)); const updateEdge = (id: string, patch: Partial) => { setEdges(edges.map((e) => (e.id === id ? { ...e, ...patch } : e))); }; const publish = async () => { if (!canPublish) { pushToast("warn", "Switch to LIVE mode + sign in to publish a process."); return; } setPublishing(true); setLastResult(null); try { const r = await api.createProcess({ name, display_name: displayName, label: displayName, description, hub, config: { org_id: ORG_ID, executable: true, nodes: nodes.map((n) => ({ id: n.id, type: n.type, label: n.label })), edges: edges.map((e) => ({ id: e.id, source: e.source, target: e.target })), }, }); pushToast("ok", `Published ${r.name} → ${r._key.slice(0, 8)}`); setLastResult({ ok: true, key: r._key }); await refreshLive(); } catch (e) { pushToast("err", `Publish failed: ${(e as Error).message.slice(0, 140)}`); setLastResult({ ok: false, err: (e as Error).message }); } finally { setPublishing(false); } }; const valid = nodes.length >= 2 && edges.length >= 1 && nodes.every((n) => n.label.trim().length > 0); return (
Process Studio

Design a new process

Hand-craft a typed FlowMaster process and publish it straight to EA2 on demo.flow-master.ai.

Definition