/** * Automation2 Flow Editor - Data flow context for Data Picker and DynamicValueField. * Extended with portTypeCatalog and systemVariables for the Typed Port System. */ import React, { createContext, useContext, useMemo } from 'react'; import type { CanvasNode, CanvasConnection } from '../editor/FlowCanvas'; import { getAvailableSources } from '../nodes/shared/dataFlowGraph'; import type { ApiRequestFunction, NodeType, PortField, PortSchema, SystemVariable } from '../../../api/workflowApi'; export interface Automation2DataFlowContextValue { currentNodeId: string; nodes: CanvasNode[]; connections: CanvasConnection[]; nodeOutputsPreview: Record; nodeTypes: NodeType[]; language: string; portTypeCatalog: Record; systemVariables: Record; getNodeLabel: (node: { id: string; title?: string; label?: string; type?: string }) => string; getAvailableSourceIds: () => string[]; /** Present when rendered inside the flow editor (ConnectionPicker / tools). */ instanceId?: string; request?: ApiRequestFunction; /** Build FormPayload-like schema from ``parameters[parameterKey]`` (fieldBuilder JSON). */ parseGraphDefinedSchema: (parameterKey: string) => PortSchema | null; } const Automation2DataFlowContext = createContext(null); export function useAutomation2DataFlow(): Automation2DataFlowContextValue | null { return useContext(Automation2DataFlowContext); } interface Automation2DataFlowProviderProps { node: CanvasNode | null; nodes: CanvasNode[]; connections: CanvasConnection[]; nodeOutputsPreview: Record; nodeTypes: NodeType[]; language: string; portTypeCatalog?: Record; systemVariables?: Record; instanceId?: string; request?: ApiRequestFunction; children: React.ReactNode; } export const Automation2DataFlowProvider: React.FC = ({ node, nodes, connections, nodeOutputsPreview, nodeTypes, language, portTypeCatalog = {}, systemVariables = {}, instanceId, request, children, }) => { const value = useMemo((): Automation2DataFlowContextValue | null => { if (!node) return null; const parseGraphDefinedSchema = (parameterKey: string): PortSchema | null => { const raw = node.parameters?.[parameterKey]; if (!Array.isArray(raw)) return null; const fields: PortField[] = []; for (const item of raw) { if (typeof item !== 'object' || item === null) continue; const rec = item as Record; if (typeof rec.name !== 'string') continue; const lab = rec.label; const desc = typeof lab === 'string' ? lab : typeof lab === 'object' && lab !== null ? String((lab as Record).de ?? '') : ''; const ftype = typeof rec.type === 'string' ? rec.type : 'str'; if (ftype === 'group' && Array.isArray(rec.fields)) { for (const sub of rec.fields as Record[]) { if (!sub || typeof sub.name !== 'string') continue; const sl = sub.label; const sdesc = typeof sl === 'string' ? sl : typeof sl === 'object' && sl !== null ? String((sl as Record).de ?? '') : ''; fields.push({ name: `${rec.name}.${sub.name}`, type: typeof sub.type === 'string' ? sub.type : 'str', description: (sdesc && sdesc.trim()) || `${rec.name}.${sub.name}`, required: Boolean(sub.required), }); } continue; } fields.push({ name: rec.name, type: ftype, description: (desc && desc.trim()) || rec.name, required: Boolean(rec.required), }); } return fields.length ? { name: 'FormPayload_dynamic', fields } : null; }; return { currentNodeId: node.id, nodes, connections, nodeOutputsPreview, nodeTypes, language, portTypeCatalog, systemVariables, getNodeLabel: (n: { id: string; title?: string; label?: string; type?: string }) => n.title ?? n.label ?? n.type ?? n.id, getAvailableSourceIds: () => getAvailableSources(node.id, nodes, connections), instanceId, request, parseGraphDefinedSchema, }; }, [node, nodes, connections, nodeOutputsPreview, nodeTypes, language, portTypeCatalog, systemVariables, instanceId, request]); return ( {children} ); };