feat: persist and edit versioned workflow definitions

This commit is contained in:
2026-07-28 13:48:06 +02:00
parent 85eef00913
commit 6737b60c11
25 changed files with 3913 additions and 9 deletions

View File

@@ -0,0 +1,99 @@
import {
CalendarClock,
CheckCircle2,
CirclePlay,
CircleX,
ClipboardCheck,
GitFork,
PlugZap,
Radio,
Split,
SquareCheckBig,
Timer,
Waypoints,
type LucideIcon
} from "lucide-react";
import {
Handle,
Position,
type Node,
type NodeProps
} from "@xyflow/react";
import type { WorkflowNodeType } from "../../api/workflow";
export type WorkflowFlowNodeData = {
label: string;
workflowType: string;
definition: WorkflowNodeType;
hasError: boolean;
};
export type WorkflowFlowNode = Node<WorkflowFlowNodeData, "workflow">;
const iconByName: Record<string, LucideIcon> = {
"calendar-clock": CalendarClock,
"circle-check-big": CheckCircle2,
"circle-play": CirclePlay,
"circle-x": CircleX,
"clipboard-check": ClipboardCheck,
"plug-zap": PlugZap,
radio: Radio,
split: Split,
"square-check-big": SquareCheckBig,
timer: Timer,
waypoints: Waypoints
};
export default function WorkflowNode({
data,
selected,
isConnectable
}: NodeProps<WorkflowFlowNode>) {
const Icon = iconByName[data.definition.icon] ?? GitFork;
const inputPorts = data.definition.input_ports;
const outputPorts = data.definition.output_ports;
return (
<div
className={[
"workflow-node",
`workflow-node-${data.definition.category}`,
selected ? "is-selected" : "",
data.hasError ? "has-error" : ""
].filter(Boolean).join(" ")}
>
{inputPorts.map((port, index) => (
<Handle
key={port.id}
id={port.id}
type="target"
position={Position.Left}
className="workflow-node-handle"
style={{ top: portPosition(index, inputPorts.length) }}
isConnectable={isConnectable}
title={port.label}
/>
))}
<span className="workflow-node-icon"><Icon size={17} /></span>
<span className="workflow-node-copy">
<strong>{data.label}</strong>
<small>{data.definition.label}</small>
</span>
{outputPorts.map((port, index) => (
<Handle
key={port.id}
id={port.id}
type="source"
position={Position.Right}
className="workflow-node-handle"
style={{ top: portPosition(index, outputPorts.length) }}
isConnectable={isConnectable}
title={port.label}
/>
))}
</div>
);
}
function portPosition(index: number, count: number): string {
return `${((index + 1) / (count + 1)) * 100}%`;
}