ultisuite-client/lib/mail-automation/workflow-flow.ts
2026-05-25 13:52:40 +02:00

121 lines
3.2 KiB
TypeScript

import type { Edge, Node } from '@xyflow/react'
import type {
AutomationWorkflow,
WorkflowEdgeDef,
WorkflowNodeData,
WorkflowNodeDef,
WorkflowNodeType,
} from './types'
import { nextNodeId } from './defaults'
import type { ActionsNodeData, ConditionNodeData, LabelCheckNodeData, LLMCheckNodeData, SetVarNodeData, SwitchNodeData } from './types'
export function workflowNodesToFlow(nodes: WorkflowNodeDef[]): Node[] {
return nodes.map((n) => ({
id: n.id,
type: n.type,
position: n.position,
data: n.data as Record<string, unknown>,
}))
}
export function workflowEdgesToFlow(edges: WorkflowEdgeDef[]): Edge[] {
return edges.map((e) => ({
id: e.id,
source: e.source,
target: e.target,
sourceHandle: e.sourceHandle ?? null,
animated: true,
}))
}
export function flowToWorkflow(
kind: AutomationWorkflow['kind'],
triggers: AutomationWorkflow['triggers'],
variables: AutomationWorkflow['variables'],
nodes: Node[],
edges: Edge[]
): AutomationWorkflow {
return {
version: 1,
kind,
triggers,
variables,
nodes: nodes.map((n) => ({
id: n.id,
type: n.type as WorkflowNodeType,
position: n.position,
data: (n.data ?? {}) as WorkflowNodeData,
})),
edges: edges.map((e) => ({
id: e.id,
source: e.source,
target: e.target,
sourceHandle: e.sourceHandle ?? undefined,
})),
}
}
export function defaultDataForNodeType(type: WorkflowNodeType): WorkflowNodeData {
switch (type) {
case 'condition':
return { field: 'subject', operator: 'contains', value: '' } satisfies ConditionNodeData
case 'label_check':
return { label: '', operator: 'has' } satisfies LabelCheckNodeData
case 'switch':
return {
field: 'subject',
cases: [
{ value: 'facture', label: 'Factures' },
{ value: 'newsletter', label: 'Newsletters' },
],
} satisfies SwitchNodeData
case 'llm_check':
return {
prompt: 'Ce message est-il du spam ou une arnaque ?',
provider: '',
model: '',
} satisfies LLMCheckNodeData
case 'actions':
return { actions: [{ type: 'label', value: '' }] } satisfies ActionsNodeData
case 'set_var':
return { name: 'result', value: '{{subject}}' } satisfies SetVarNodeData
case 'call_function':
case 'call_rule':
return { rule_id: '' }
default:
return {}
}
}
export function createFlowNode(
type: WorkflowNodeType,
position: { x: number; y: number }
): Node {
return {
id: nextNodeId(type),
type,
position,
data: defaultDataForNodeType(type) as Record<string, unknown>,
}
}
export function parseWorkflowFromRule(
workflow: unknown,
ruleKind: 'rule' | 'function'
): AutomationWorkflow | null {
if (!workflow || typeof workflow !== 'object') return null
const w = workflow as AutomationWorkflow
if (!Array.isArray(w.nodes) || w.nodes.length === 0) return null
return {
version: 1,
kind: w.kind ?? ruleKind,
triggers: w.triggers ?? {
operator: 'or',
groups: [{ operator: 'and', items: [{ type: 'message_received' }] }],
},
variables: w.variables ?? [],
nodes: w.nodes,
edges: w.edges ?? [],
}
}