@xtr-dev/payload-automation 0.0.37 → 0.0.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/dist/collections/Workflow.js +2 -4
- package/dist/collections/Workflow.js.map +1 -1
- package/dist/components/WorkflowBuilder/StepConfigurationForm.js +316 -0
- package/dist/components/WorkflowBuilder/StepConfigurationForm.js.map +1 -0
- package/dist/components/WorkflowBuilder/WorkflowBuilder.js +211 -0
- package/dist/components/WorkflowBuilder/WorkflowBuilder.js.map +1 -0
- package/dist/components/WorkflowBuilder/WorkflowToolbar.js +107 -0
- package/dist/components/WorkflowBuilder/WorkflowToolbar.js.map +1 -0
- package/dist/components/WorkflowBuilder/index.js +6 -0
- package/dist/components/WorkflowBuilder/index.js.map +1 -0
- package/dist/components/WorkflowBuilder/nodes/StepNode.js +139 -0
- package/dist/components/WorkflowBuilder/nodes/StepNode.js.map +1 -0
- package/dist/core/workflow-executor.js +150 -116
- package/dist/core/workflow-executor.js.map +1 -1
- package/dist/fields/WorkflowBuilderField.js +119 -0
- package/dist/fields/WorkflowBuilderField.js.map +1 -0
- package/dist/fields/parameter.js +1 -1
- package/dist/fields/parameter.js.map +1 -1
- package/dist/plugin/collection-hook.js +34 -0
- package/dist/plugin/collection-hook.js.map +1 -1
- package/package.json +4 -3
- package/dist/collections/Workflow.d.ts +0 -3
- package/dist/collections/WorkflowRuns.d.ts +0 -2
- package/dist/components/ErrorDisplay.d.ts +0 -9
- package/dist/components/StatusCell.d.ts +0 -6
- package/dist/core/trigger-custom-workflow.d.ts +0 -52
- package/dist/core/workflow-executor.d.ts +0 -90
- package/dist/exports/client.d.ts +0 -2
- package/dist/exports/fields.d.ts +0 -1
- package/dist/exports/rsc.d.ts +0 -1
- package/dist/exports/server.d.ts +0 -5
- package/dist/exports/views.d.ts +0 -1
- package/dist/fields/parameter.d.ts +0 -4
- package/dist/index.d.ts +0 -2
- package/dist/plugin/collection-hook.d.ts +0 -1
- package/dist/plugin/config-types.d.ts +0 -18
- package/dist/plugin/global-hook.d.ts +0 -1
- package/dist/plugin/index.d.ts +0 -4
- package/dist/plugin/logger.d.ts +0 -20
- package/dist/steps/create-document-handler.d.ts +0 -2
- package/dist/steps/create-document.d.ts +0 -46
- package/dist/steps/delete-document-handler.d.ts +0 -2
- package/dist/steps/delete-document.d.ts +0 -39
- package/dist/steps/http-request-handler.d.ts +0 -2
- package/dist/steps/http-request.d.ts +0 -155
- package/dist/steps/index.d.ts +0 -12
- package/dist/steps/read-document-handler.d.ts +0 -2
- package/dist/steps/read-document.d.ts +0 -46
- package/dist/steps/send-email-handler.d.ts +0 -2
- package/dist/steps/send-email.d.ts +0 -44
- package/dist/steps/update-document-handler.d.ts +0 -2
- package/dist/steps/update-document.d.ts +0 -46
- package/dist/test/basic.test.js +0 -14
- package/dist/test/basic.test.js.map +0 -1
- package/dist/test/create-document-step.test.js +0 -378
- package/dist/test/create-document-step.test.js.map +0 -1
- package/dist/test/http-request-step.test.js +0 -361
- package/dist/test/http-request-step.test.js.map +0 -1
- package/dist/test/workflow-executor.test.js +0 -530
- package/dist/test/workflow-executor.test.js.map +0 -1
- package/dist/triggers/collection-trigger.d.ts +0 -2
- package/dist/triggers/global-trigger.d.ts +0 -2
- package/dist/triggers/index.d.ts +0 -2
- package/dist/triggers/types.d.ts +0 -5
- package/dist/types/index.d.ts +0 -31
package/README.md
CHANGED
|
@@ -56,7 +56,10 @@ The plugin uses separate exports to avoid bundling server-side code in client bu
|
|
|
56
56
|
import { workflowsPlugin } from '@xtr-dev/payload-automation/server'
|
|
57
57
|
|
|
58
58
|
// Client-side components
|
|
59
|
-
import {
|
|
59
|
+
import { StatusCell, ErrorDisplay } from '@xtr-dev/payload-automation/client'
|
|
60
|
+
|
|
61
|
+
// Helper utilities
|
|
62
|
+
import { /* helpers */ } from '@xtr-dev/payload-automation/helpers'
|
|
60
63
|
|
|
61
64
|
// Types only (safe for both server and client)
|
|
62
65
|
import type { WorkflowsPluginConfig } from '@xtr-dev/payload-automation'
|
|
@@ -216,6 +219,8 @@ export default async function handler(req, res) {
|
|
|
216
219
|
|
|
217
220
|
**Benefits**: Better reliability, proper process isolation, easier debugging, and leverages existing infrastructure.
|
|
218
221
|
|
|
222
|
+
**Note**: Built-in cron triggers have been removed in v0.0.37+ to focus on webhook-based scheduling which provides better reliability and debugging capabilities.
|
|
223
|
+
|
|
219
224
|
## Documentation
|
|
220
225
|
|
|
221
226
|
Full documentation coming soon. For now, explore the development environment in the repository for examples and patterns.
|
|
@@ -82,15 +82,13 @@ export const createWorkflowCollection = (options)=>{
|
|
|
82
82
|
options: steps.map((t)=>t.slug)
|
|
83
83
|
},
|
|
84
84
|
{
|
|
85
|
-
name: '
|
|
85
|
+
name: 'input',
|
|
86
86
|
type: 'json',
|
|
87
87
|
admin: {
|
|
88
|
-
|
|
88
|
+
description: 'Step input configuration. Use JSONPath expressions to reference dynamic data (e.g., {"url": "$.trigger.doc.webhookUrl", "data": "$.steps.previousStep.output.result"})'
|
|
89
89
|
},
|
|
90
90
|
defaultValue: {}
|
|
91
91
|
},
|
|
92
|
-
// Virtual fields for custom triggers
|
|
93
|
-
...steps.flatMap((step)=>(step.inputSchema || []).map((s)=>parameter(step.slug, s))),
|
|
94
92
|
{
|
|
95
93
|
name: 'dependencies',
|
|
96
94
|
type: 'text',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/collections/Workflow.ts"],"sourcesContent":["import type {CollectionConfig} from 'payload'\n\nimport type {WorkflowsPluginConfig} from \"../plugin/config-types.js\"\n\nimport {parameter} from \"../fields/parameter.js\"\nimport {collectionTrigger, globalTrigger} from \"../triggers/index.js\"\n\nexport const createWorkflowCollection: <T extends string>(options: WorkflowsPluginConfig<T>) => CollectionConfig = (options) => {\n const steps = options.steps || []\n const triggers = (options.triggers || []).map(t => t(options)).concat(collectionTrigger(options), globalTrigger(options))\n return {\n slug: 'workflows',\n access: {\n create: () => true,\n delete: () => true,\n read: () => true,\n update: () => true,\n },\n admin: {\n defaultColumns: ['name', 'updatedAt'],\n description: 'Create and manage automated workflows.',\n group: 'Automation',\n useAsTitle: 'name',\n },\n fields: [\n {\n name: 'name',\n type: 'text',\n admin: {\n description: 'Human-readable name for the workflow',\n },\n required: true,\n },\n {\n name: 'description',\n type: 'textarea',\n admin: {\n description: 'Optional description of what this workflow does',\n },\n },\n {\n name: 'triggers',\n type: 'array',\n fields: [\n {\n name: 'type',\n type: 'select',\n options: [\n ...triggers.map(t => t.slug)\n ]\n },\n {\n name: 'parameters',\n type: 'json',\n admin: {\n hidden: true,\n },\n defaultValue: {}\n },\n // Virtual fields for custom triggers\n ...triggers.flatMap(t => (t.parameters || []).map(p => parameter(t.slug, p as any))),\n {\n name: 'condition',\n type: 'text',\n admin: {\n description: 'JSONPath expression that must evaluate to true for this trigger to execute the workflow (e.g., \"$.trigger.doc.status == \\'published\\'\")'\n },\n required: false\n },\n ]\n },\n {\n name: 'steps',\n type: 'array',\n fields: [\n {\n name: 'name',\n type: 'text',\n defaultValue: 'Unnamed Step'\n },\n {\n name: 'type',\n type: 'select',\n options: steps.map(t => t.slug)\n },\n {\n name: '
|
|
1
|
+
{"version":3,"sources":["../../src/collections/Workflow.ts"],"sourcesContent":["import type {CollectionConfig} from 'payload'\n\nimport type {WorkflowsPluginConfig} from \"../plugin/config-types.js\"\n\nimport {parameter} from \"../fields/parameter.js\"\nimport {collectionTrigger, globalTrigger} from \"../triggers/index.js\"\n\nexport const createWorkflowCollection: <T extends string>(options: WorkflowsPluginConfig<T>) => CollectionConfig = (options) => {\n const steps = options.steps || []\n const triggers = (options.triggers || []).map(t => t(options)).concat(collectionTrigger(options), globalTrigger(options))\n return {\n slug: 'workflows',\n access: {\n create: () => true,\n delete: () => true,\n read: () => true,\n update: () => true,\n },\n admin: {\n defaultColumns: ['name', 'updatedAt'],\n description: 'Create and manage automated workflows.',\n group: 'Automation',\n useAsTitle: 'name',\n },\n fields: [\n {\n name: 'name',\n type: 'text',\n admin: {\n description: 'Human-readable name for the workflow',\n },\n required: true,\n },\n {\n name: 'description',\n type: 'textarea',\n admin: {\n description: 'Optional description of what this workflow does',\n },\n },\n {\n name: 'triggers',\n type: 'array',\n fields: [\n {\n name: 'type',\n type: 'select',\n options: [\n ...triggers.map(t => t.slug)\n ]\n },\n {\n name: 'parameters',\n type: 'json',\n admin: {\n hidden: true,\n },\n defaultValue: {}\n },\n // Virtual fields for custom triggers\n ...triggers.flatMap(t => (t.parameters || []).map(p => parameter(t.slug, p as any))),\n {\n name: 'condition',\n type: 'text',\n admin: {\n description: 'JSONPath expression that must evaluate to true for this trigger to execute the workflow (e.g., \"$.trigger.doc.status == \\'published\\'\")'\n },\n required: false\n },\n ]\n },\n {\n name: 'steps',\n type: 'array',\n fields: [\n {\n name: 'name',\n type: 'text',\n defaultValue: 'Unnamed Step'\n },\n {\n name: 'type',\n type: 'select',\n options: steps.map(t => t.slug)\n },\n {\n name: 'input',\n type: 'json',\n admin: {\n description: 'Step input configuration. Use JSONPath expressions to reference dynamic data (e.g., {\"url\": \"$.trigger.doc.webhookUrl\", \"data\": \"$.steps.previousStep.output.result\"})'\n },\n defaultValue: {}\n },\n {\n name: 'dependencies',\n type: 'text',\n admin: {\n description: 'Step names that must complete before this step can run'\n },\n hasMany: true,\n required: false\n },\n {\n name: 'condition',\n type: 'text',\n admin: {\n description: 'JSONPath expression that must evaluate to true for this step to execute (e.g., \"$.trigger.doc.status == \\'published\\'\")'\n },\n required: false\n },\n ],\n }\n ],\n versions: {\n drafts: {\n autosave: false,\n },\n maxPerDoc: 10,\n },\n }\n}\n"],"names":["parameter","collectionTrigger","globalTrigger","createWorkflowCollection","options","steps","triggers","map","t","concat","slug","access","create","delete","read","update","admin","defaultColumns","description","group","useAsTitle","fields","name","type","required","hidden","defaultValue","flatMap","parameters","p","hasMany","versions","drafts","autosave","maxPerDoc"],"mappings":"AAIA,SAAQA,SAAS,QAAO,yBAAwB;AAChD,SAAQC,iBAAiB,EAAEC,aAAa,QAAO,uBAAsB;AAErE,OAAO,MAAMC,2BAAsG,CAACC;IAClH,MAAMC,QAAQD,QAAQC,KAAK,IAAI,EAAE;IACjC,MAAMC,WAAW,AAACF,CAAAA,QAAQE,QAAQ,IAAI,EAAE,AAAD,EAAGC,GAAG,CAACC,CAAAA,IAAKA,EAAEJ,UAAUK,MAAM,CAACR,kBAAkBG,UAAUF,cAAcE;IAChH,OAAO;QACLM,MAAM;QACNC,QAAQ;YACNC,QAAQ,IAAM;YACdC,QAAQ,IAAM;YACdC,MAAM,IAAM;YACZC,QAAQ,IAAM;QAChB;QACAC,OAAO;YACLC,gBAAgB;gBAAC;gBAAQ;aAAY;YACrCC,aAAa;YACbC,OAAO;YACPC,YAAY;QACd;QACAC,QAAQ;YACN;gBACEC,MAAM;gBACNC,MAAM;gBACNP,OAAO;oBACLE,aAAa;gBACf;gBACAM,UAAU;YACZ;YACA;gBACEF,MAAM;gBACNC,MAAM;gBACNP,OAAO;oBACLE,aAAa;gBACf;YACF;YACA;gBACEI,MAAM;gBACNC,MAAM;gBACNF,QAAQ;oBACN;wBACEC,MAAM;wBACNC,MAAM;wBACNnB,SAAS;+BACJE,SAASC,GAAG,CAACC,CAAAA,IAAKA,EAAEE,IAAI;yBAC5B;oBACH;oBACA;wBACEY,MAAM;wBACNC,MAAM;wBACNP,OAAO;4BACLS,QAAQ;wBACV;wBACAC,cAAc,CAAC;oBACjB;oBACA,qCAAqC;uBAClCpB,SAASqB,OAAO,CAACnB,CAAAA,IAAK,AAACA,CAAAA,EAAEoB,UAAU,IAAI,EAAE,AAAD,EAAGrB,GAAG,CAACsB,CAAAA,IAAK7B,UAAUQ,EAAEE,IAAI,EAAEmB;oBACzE;wBACEP,MAAM;wBACNC,MAAM;wBACNP,OAAO;4BACLE,aAAa;wBACf;wBACAM,UAAU;oBACZ;iBACD;YACH;YACA;gBACEF,MAAM;gBACNC,MAAM;gBACNF,QAAQ;oBACN;wBACEC,MAAM;wBACNC,MAAM;wBACNG,cAAc;oBAChB;oBACA;wBACEJ,MAAM;wBACNC,MAAM;wBACNnB,SAASC,MAAME,GAAG,CAACC,CAAAA,IAAKA,EAAEE,IAAI;oBAChC;oBACA;wBACEY,MAAM;wBACNC,MAAM;wBACNP,OAAO;4BACLE,aAAa;wBACf;wBACAQ,cAAc,CAAC;oBACjB;oBACA;wBACEJ,MAAM;wBACNC,MAAM;wBACNP,OAAO;4BACLE,aAAa;wBACf;wBACAY,SAAS;wBACTN,UAAU;oBACZ;oBACA;wBACEF,MAAM;wBACNC,MAAM;wBACNP,OAAO;4BACLE,aAAa;wBACf;wBACAM,UAAU;oBACZ;iBACD;YACH;SACD;QACDO,UAAU;YACRC,QAAQ;gBACNC,UAAU;YACZ;YACAC,WAAW;QACb;IACF;AACF,EAAC"}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import React, { useState, useCallback, useEffect } from 'react';
|
|
4
|
+
import { Button } from '@payloadcms/ui';
|
|
5
|
+
export const StepConfigurationForm = ({ selectedNode, availableStepTypes, availableSteps, onNodeUpdate, onClose })=>{
|
|
6
|
+
const [formData, setFormData] = useState(selectedNode?.data.configuration || {});
|
|
7
|
+
const [jsonText, setJsonText] = useState(()=>JSON.stringify(selectedNode?.data.configuration || {}, null, 2));
|
|
8
|
+
if (!selectedNode) return null;
|
|
9
|
+
const stepType = availableStepTypes.find((type)=>type.slug === selectedNode.data.stepType);
|
|
10
|
+
const inputSchema = stepType?.inputSchema || [];
|
|
11
|
+
// Update form data when selected node changes
|
|
12
|
+
useEffect(()=>{
|
|
13
|
+
const config = selectedNode?.data.configuration || {};
|
|
14
|
+
setFormData(config);
|
|
15
|
+
setJsonText(JSON.stringify(config, null, 2));
|
|
16
|
+
}, [
|
|
17
|
+
selectedNode
|
|
18
|
+
]);
|
|
19
|
+
const handleSave = useCallback(()=>{
|
|
20
|
+
// Update the node with form data
|
|
21
|
+
onNodeUpdate(selectedNode.id, {
|
|
22
|
+
...selectedNode.data,
|
|
23
|
+
configuration: formData
|
|
24
|
+
});
|
|
25
|
+
onClose();
|
|
26
|
+
}, [
|
|
27
|
+
selectedNode,
|
|
28
|
+
formData,
|
|
29
|
+
onNodeUpdate,
|
|
30
|
+
onClose
|
|
31
|
+
]);
|
|
32
|
+
const renderStepConfiguration = ()=>{
|
|
33
|
+
if (!inputSchema.length) {
|
|
34
|
+
return /*#__PURE__*/ _jsx("div", {
|
|
35
|
+
style: {
|
|
36
|
+
padding: '20px',
|
|
37
|
+
textAlign: 'center',
|
|
38
|
+
color: 'var(--theme-text-400)',
|
|
39
|
+
fontStyle: 'italic'
|
|
40
|
+
},
|
|
41
|
+
children: "This step type has no configuration parameters."
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
45
|
+
style: {
|
|
46
|
+
marginBottom: '16px'
|
|
47
|
+
},
|
|
48
|
+
children: [
|
|
49
|
+
/*#__PURE__*/ _jsx("label", {
|
|
50
|
+
style: {
|
|
51
|
+
display: 'block',
|
|
52
|
+
marginBottom: '4px',
|
|
53
|
+
fontSize: '12px',
|
|
54
|
+
fontWeight: '500',
|
|
55
|
+
color: 'var(--theme-text)'
|
|
56
|
+
},
|
|
57
|
+
children: "Step Configuration"
|
|
58
|
+
}),
|
|
59
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
60
|
+
style: {
|
|
61
|
+
fontSize: '11px',
|
|
62
|
+
color: 'var(--theme-text-400)',
|
|
63
|
+
marginBottom: '8px'
|
|
64
|
+
},
|
|
65
|
+
children: [
|
|
66
|
+
"Configure this step's parameters in JSON format. Use JSONPath expressions like ",
|
|
67
|
+
/*#__PURE__*/ _jsx("code", {
|
|
68
|
+
children: "$.trigger.doc.id"
|
|
69
|
+
}),
|
|
70
|
+
" to reference dynamic data."
|
|
71
|
+
]
|
|
72
|
+
}),
|
|
73
|
+
/*#__PURE__*/ _jsxs("details", {
|
|
74
|
+
style: {
|
|
75
|
+
marginBottom: '12px'
|
|
76
|
+
},
|
|
77
|
+
children: [
|
|
78
|
+
/*#__PURE__*/ _jsx("summary", {
|
|
79
|
+
style: {
|
|
80
|
+
fontSize: '11px',
|
|
81
|
+
color: 'var(--theme-text-400)',
|
|
82
|
+
cursor: 'pointer',
|
|
83
|
+
marginBottom: '8px'
|
|
84
|
+
},
|
|
85
|
+
children: "📖 Available Fields (click to expand)"
|
|
86
|
+
}),
|
|
87
|
+
/*#__PURE__*/ _jsx("div", {
|
|
88
|
+
style: {
|
|
89
|
+
background: 'var(--theme-elevation-50)',
|
|
90
|
+
border: '1px solid var(--theme-elevation-100)',
|
|
91
|
+
borderRadius: '4px',
|
|
92
|
+
padding: '12px',
|
|
93
|
+
fontSize: '11px',
|
|
94
|
+
fontFamily: 'monospace'
|
|
95
|
+
},
|
|
96
|
+
children: inputSchema.map((field, index)=>/*#__PURE__*/ _jsxs("div", {
|
|
97
|
+
style: {
|
|
98
|
+
marginBottom: index < inputSchema.length - 1 ? '8px' : '0'
|
|
99
|
+
},
|
|
100
|
+
children: [
|
|
101
|
+
/*#__PURE__*/ _jsx("strong", {
|
|
102
|
+
children: field.name
|
|
103
|
+
}),
|
|
104
|
+
" (",
|
|
105
|
+
field.type,
|
|
106
|
+
")",
|
|
107
|
+
field.required && /*#__PURE__*/ _jsx("span", {
|
|
108
|
+
style: {
|
|
109
|
+
color: 'var(--theme-error-500)'
|
|
110
|
+
},
|
|
111
|
+
children: " *required"
|
|
112
|
+
}),
|
|
113
|
+
field.admin?.description && /*#__PURE__*/ _jsx("div", {
|
|
114
|
+
style: {
|
|
115
|
+
color: 'var(--theme-text-400)',
|
|
116
|
+
marginTop: '2px'
|
|
117
|
+
},
|
|
118
|
+
children: field.admin.description
|
|
119
|
+
})
|
|
120
|
+
]
|
|
121
|
+
}, field.name))
|
|
122
|
+
})
|
|
123
|
+
]
|
|
124
|
+
}),
|
|
125
|
+
/*#__PURE__*/ _jsx("textarea", {
|
|
126
|
+
value: jsonText,
|
|
127
|
+
onChange: (e)=>{
|
|
128
|
+
const text = e.target.value;
|
|
129
|
+
setJsonText(text);
|
|
130
|
+
try {
|
|
131
|
+
const parsed = JSON.parse(text);
|
|
132
|
+
setFormData(parsed);
|
|
133
|
+
} catch {
|
|
134
|
+
// Keep invalid JSON, user is still typing
|
|
135
|
+
// Don't update formData until JSON is valid
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
rows: Math.min(Math.max(inputSchema.length * 2, 6), 15),
|
|
139
|
+
style: {
|
|
140
|
+
width: '100%',
|
|
141
|
+
padding: '12px',
|
|
142
|
+
border: '1px solid var(--theme-elevation-100)',
|
|
143
|
+
borderRadius: '4px',
|
|
144
|
+
fontSize: '13px',
|
|
145
|
+
fontFamily: 'monospace',
|
|
146
|
+
lineHeight: '1.4',
|
|
147
|
+
background: 'var(--theme-input-bg)',
|
|
148
|
+
color: 'var(--theme-text)',
|
|
149
|
+
resize: 'vertical'
|
|
150
|
+
},
|
|
151
|
+
placeholder: '{\\n "field1": "value1",\\n "field2": "$.trigger.doc.id"\\n}'
|
|
152
|
+
})
|
|
153
|
+
]
|
|
154
|
+
});
|
|
155
|
+
};
|
|
156
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
157
|
+
style: {
|
|
158
|
+
height: '100%',
|
|
159
|
+
width: '100%',
|
|
160
|
+
display: 'flex',
|
|
161
|
+
flexDirection: 'column',
|
|
162
|
+
overflow: 'hidden'
|
|
163
|
+
},
|
|
164
|
+
children: [
|
|
165
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
166
|
+
style: {
|
|
167
|
+
padding: '16px',
|
|
168
|
+
borderBottom: '1px solid var(--theme-elevation-100)',
|
|
169
|
+
background: 'var(--theme-elevation-50)'
|
|
170
|
+
},
|
|
171
|
+
children: [
|
|
172
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
173
|
+
style: {
|
|
174
|
+
display: 'flex',
|
|
175
|
+
justifyContent: 'space-between',
|
|
176
|
+
alignItems: 'center'
|
|
177
|
+
},
|
|
178
|
+
children: [
|
|
179
|
+
/*#__PURE__*/ _jsx("h4", {
|
|
180
|
+
style: {
|
|
181
|
+
margin: 0,
|
|
182
|
+
fontSize: '16px',
|
|
183
|
+
fontWeight: '600',
|
|
184
|
+
color: 'var(--theme-text)'
|
|
185
|
+
},
|
|
186
|
+
children: "Configure Step"
|
|
187
|
+
}),
|
|
188
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
189
|
+
buttonStyle: "none",
|
|
190
|
+
onClick: onClose,
|
|
191
|
+
size: "small",
|
|
192
|
+
children: "×"
|
|
193
|
+
})
|
|
194
|
+
]
|
|
195
|
+
}),
|
|
196
|
+
/*#__PURE__*/ _jsx("div", {
|
|
197
|
+
style: {
|
|
198
|
+
fontSize: '12px',
|
|
199
|
+
color: 'var(--theme-text-400)',
|
|
200
|
+
marginTop: '4px'
|
|
201
|
+
},
|
|
202
|
+
children: stepType?.label || selectedNode.data.stepType
|
|
203
|
+
})
|
|
204
|
+
]
|
|
205
|
+
}),
|
|
206
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
207
|
+
style: {
|
|
208
|
+
flex: 1,
|
|
209
|
+
overflow: 'auto',
|
|
210
|
+
padding: '16px'
|
|
211
|
+
},
|
|
212
|
+
children: [
|
|
213
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
214
|
+
style: {
|
|
215
|
+
marginBottom: '16px'
|
|
216
|
+
},
|
|
217
|
+
children: [
|
|
218
|
+
/*#__PURE__*/ _jsx("label", {
|
|
219
|
+
style: {
|
|
220
|
+
display: 'block',
|
|
221
|
+
marginBottom: '4px',
|
|
222
|
+
fontSize: '12px',
|
|
223
|
+
fontWeight: '500'
|
|
224
|
+
},
|
|
225
|
+
children: "Step Name *"
|
|
226
|
+
}),
|
|
227
|
+
/*#__PURE__*/ _jsx("input", {
|
|
228
|
+
type: "text",
|
|
229
|
+
value: selectedNode.data.label || '',
|
|
230
|
+
onChange: (e)=>onNodeUpdate(selectedNode.id, {
|
|
231
|
+
...selectedNode.data,
|
|
232
|
+
label: e.target.value
|
|
233
|
+
}),
|
|
234
|
+
style: {
|
|
235
|
+
width: '100%',
|
|
236
|
+
padding: '8px',
|
|
237
|
+
border: '1px solid var(--theme-elevation-100)',
|
|
238
|
+
borderRadius: '4px',
|
|
239
|
+
fontSize: '14px'
|
|
240
|
+
},
|
|
241
|
+
required: true
|
|
242
|
+
})
|
|
243
|
+
]
|
|
244
|
+
}),
|
|
245
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
246
|
+
style: {
|
|
247
|
+
marginBottom: '16px'
|
|
248
|
+
},
|
|
249
|
+
children: [
|
|
250
|
+
/*#__PURE__*/ _jsx("label", {
|
|
251
|
+
style: {
|
|
252
|
+
display: 'block',
|
|
253
|
+
marginBottom: '4px',
|
|
254
|
+
fontSize: '12px',
|
|
255
|
+
fontWeight: '500'
|
|
256
|
+
},
|
|
257
|
+
children: "Dependencies"
|
|
258
|
+
}),
|
|
259
|
+
/*#__PURE__*/ _jsx("div", {
|
|
260
|
+
style: {
|
|
261
|
+
fontSize: '11px',
|
|
262
|
+
color: 'var(--theme-text-400)',
|
|
263
|
+
marginBottom: '8px'
|
|
264
|
+
},
|
|
265
|
+
children: "Steps that must complete before this step can run"
|
|
266
|
+
}),
|
|
267
|
+
availableSteps.filter((step)=>step !== selectedNode.id).map((stepId)=>/*#__PURE__*/ _jsxs("label", {
|
|
268
|
+
style: {
|
|
269
|
+
display: 'block',
|
|
270
|
+
fontSize: '12px',
|
|
271
|
+
marginBottom: '4px'
|
|
272
|
+
},
|
|
273
|
+
children: [
|
|
274
|
+
/*#__PURE__*/ _jsx("input", {
|
|
275
|
+
type: "checkbox",
|
|
276
|
+
checked: (selectedNode.data.dependencies || []).includes(stepId),
|
|
277
|
+
onChange: (e)=>{
|
|
278
|
+
const currentDeps = selectedNode.data.dependencies || [];
|
|
279
|
+
const newDeps = e.target.checked ? [
|
|
280
|
+
...currentDeps,
|
|
281
|
+
stepId
|
|
282
|
+
] : currentDeps.filter((dep)=>dep !== stepId);
|
|
283
|
+
onNodeUpdate(selectedNode.id, {
|
|
284
|
+
...selectedNode.data,
|
|
285
|
+
dependencies: newDeps
|
|
286
|
+
});
|
|
287
|
+
},
|
|
288
|
+
style: {
|
|
289
|
+
marginRight: '8px'
|
|
290
|
+
}
|
|
291
|
+
}),
|
|
292
|
+
stepId
|
|
293
|
+
]
|
|
294
|
+
}, stepId))
|
|
295
|
+
]
|
|
296
|
+
}),
|
|
297
|
+
renderStepConfiguration(),
|
|
298
|
+
/*#__PURE__*/ _jsx("div", {
|
|
299
|
+
style: {
|
|
300
|
+
borderTop: '1px solid var(--theme-elevation-100)',
|
|
301
|
+
paddingTop: '16px',
|
|
302
|
+
marginTop: '16px'
|
|
303
|
+
},
|
|
304
|
+
children: /*#__PURE__*/ _jsx(Button, {
|
|
305
|
+
buttonStyle: "primary",
|
|
306
|
+
onClick: handleSave,
|
|
307
|
+
children: "Save Configuration"
|
|
308
|
+
})
|
|
309
|
+
})
|
|
310
|
+
]
|
|
311
|
+
})
|
|
312
|
+
]
|
|
313
|
+
});
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
//# sourceMappingURL=StepConfigurationForm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/WorkflowBuilder/StepConfigurationForm.tsx"],"sourcesContent":["'use client'\n\nimport React, { useState, useCallback, useEffect } from 'react'\nimport type { Node } from '@xyflow/react'\nimport { Button } from '@payloadcms/ui'\n\ninterface StepField {\n name: string\n type: string\n label?: string\n admin?: {\n description?: string\n condition?: (data: any, siblingData: any) => boolean\n }\n options?: Array<{ label: string; value: string }>\n defaultValue?: any\n required?: boolean\n hasMany?: boolean\n fields?: StepField[] // For group fields\n}\n\ninterface StepType {\n slug: string\n label?: string\n inputSchema?: StepField[]\n outputSchema?: StepField[]\n}\n\ninterface StepConfigurationFormProps {\n selectedNode: Node | null\n availableStepTypes: StepType[]\n availableSteps: string[] // For dependency selection\n onNodeUpdate: (nodeId: string, data: Partial<Node['data']>) => void\n onClose: () => void\n}\n\nexport const StepConfigurationForm: React.FC<StepConfigurationFormProps> = ({\n selectedNode,\n availableStepTypes,\n availableSteps,\n onNodeUpdate,\n onClose\n}) => {\n const [formData, setFormData] = useState<Record<string, any>>(\n selectedNode?.data.configuration || {}\n )\n const [jsonText, setJsonText] = useState<string>(() => \n JSON.stringify(selectedNode?.data.configuration || {}, null, 2)\n )\n\n if (!selectedNode) return null\n\n const stepType = availableStepTypes.find(type => type.slug === selectedNode.data.stepType)\n const inputSchema = stepType?.inputSchema || []\n\n // Update form data when selected node changes\n useEffect(() => {\n const config = selectedNode?.data.configuration || {}\n setFormData(config)\n setJsonText(JSON.stringify(config, null, 2))\n }, [selectedNode])\n\n\n const handleSave = useCallback(() => {\n // Update the node with form data\n onNodeUpdate(selectedNode.id, {\n ...selectedNode.data,\n configuration: formData\n })\n \n onClose()\n }, [selectedNode, formData, onNodeUpdate, onClose])\n\n const renderStepConfiguration = () => {\n if (!inputSchema.length) {\n return (\n <div style={{ \n padding: '20px',\n textAlign: 'center',\n color: 'var(--theme-text-400)',\n fontStyle: 'italic'\n }}>\n This step type has no configuration parameters.\n </div>\n )\n }\n\n return (\n <div style={{ marginBottom: '16px' }}>\n <label style={{ \n display: 'block', \n marginBottom: '4px', \n fontSize: '12px', \n fontWeight: '500',\n color: 'var(--theme-text)' \n }}>\n Step Configuration\n </label>\n <div style={{ fontSize: '11px', color: 'var(--theme-text-400)', marginBottom: '8px' }}>\n Configure this step's parameters in JSON format. Use JSONPath expressions like <code>$.trigger.doc.id</code> to reference dynamic data.\n </div>\n \n {/* Schema Reference */}\n <details style={{ marginBottom: '12px' }}>\n <summary style={{ \n fontSize: '11px', \n color: 'var(--theme-text-400)', \n cursor: 'pointer',\n marginBottom: '8px'\n }}>\n 📖 Available Fields (click to expand)\n </summary>\n <div style={{ \n background: 'var(--theme-elevation-50)',\n border: '1px solid var(--theme-elevation-100)',\n borderRadius: '4px',\n padding: '12px',\n fontSize: '11px',\n fontFamily: 'monospace'\n }}>\n {inputSchema.map((field, index) => (\n <div key={field.name} style={{ marginBottom: index < inputSchema.length - 1 ? '8px' : '0' }}>\n <strong>{field.name}</strong> ({field.type})\n {field.required && <span style={{ color: 'var(--theme-error-500)' }}> *required</span>}\n {field.admin?.description && (\n <div style={{ color: 'var(--theme-text-400)', marginTop: '2px' }}>\n {field.admin.description}\n </div>\n )}\n </div>\n ))}\n </div>\n </details>\n\n <textarea\n value={jsonText}\n onChange={(e) => {\n const text = e.target.value\n setJsonText(text)\n try {\n const parsed = JSON.parse(text)\n setFormData(parsed)\n } catch {\n // Keep invalid JSON, user is still typing\n // Don't update formData until JSON is valid\n }\n }}\n rows={Math.min(Math.max(inputSchema.length * 2, 6), 15)}\n style={{\n width: '100%',\n padding: '12px',\n border: '1px solid var(--theme-elevation-100)',\n borderRadius: '4px',\n fontSize: '13px',\n fontFamily: 'monospace',\n lineHeight: '1.4',\n background: 'var(--theme-input-bg)',\n color: 'var(--theme-text)',\n resize: 'vertical'\n }}\n placeholder='{\\n \"field1\": \"value1\",\\n \"field2\": \"$.trigger.doc.id\"\\n}'\n />\n </div>\n )\n }\n\n return (\n <div style={{\n height: '100%',\n width: '100%',\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden'\n }}>\n {/* Header */}\n <div style={{\n padding: '16px',\n borderBottom: '1px solid var(--theme-elevation-100)',\n background: 'var(--theme-elevation-50)'\n }}>\n <div style={{ \n display: 'flex', \n justifyContent: 'space-between', \n alignItems: 'center' \n }}>\n <h4 style={{ margin: 0, fontSize: '16px', fontWeight: '600', color: 'var(--theme-text)' }}>\n Configure Step\n </h4>\n <Button\n buttonStyle=\"none\"\n onClick={onClose}\n size=\"small\"\n >\n ×\n </Button>\n </div>\n <div style={{ fontSize: '12px', color: 'var(--theme-text-400)', marginTop: '4px' }}>\n {stepType?.label || (selectedNode.data.stepType as string)}\n </div>\n </div>\n\n {/* Form */}\n <div style={{ \n flex: 1, \n overflow: 'auto', \n padding: '16px' \n }}>\n {/* Basic step info */}\n <div style={{ marginBottom: '16px' }}>\n <label style={{ \n display: 'block', \n marginBottom: '4px', \n fontSize: '12px', \n fontWeight: '500' \n }}>\n Step Name *\n </label>\n <input\n type=\"text\"\n value={(selectedNode.data.label as string) || ''}\n onChange={(e) => onNodeUpdate(selectedNode.id, { \n ...selectedNode.data, \n label: e.target.value \n })}\n style={{\n width: '100%',\n padding: '8px',\n border: '1px solid var(--theme-elevation-100)',\n borderRadius: '4px',\n fontSize: '14px'\n }}\n required\n />\n </div>\n\n {/* Dependencies */}\n <div style={{ marginBottom: '16px' }}>\n <label style={{ \n display: 'block', \n marginBottom: '4px', \n fontSize: '12px', \n fontWeight: '500' \n }}>\n Dependencies\n </label>\n <div style={{ fontSize: '11px', color: 'var(--theme-text-400)', marginBottom: '8px' }}>\n Steps that must complete before this step can run\n </div>\n {availableSteps\n .filter(step => step !== selectedNode.id)\n .map(stepId => (\n <label key={stepId} style={{ \n display: 'block', \n fontSize: '12px', \n marginBottom: '4px' \n }}>\n <input\n type=\"checkbox\"\n checked={((selectedNode.data.dependencies as string[]) || []).includes(stepId)}\n onChange={(e) => {\n const currentDeps = (selectedNode.data.dependencies as string[]) || []\n const newDeps = e.target.checked\n ? [...currentDeps, stepId]\n : currentDeps.filter((dep: string) => dep !== stepId)\n \n onNodeUpdate(selectedNode.id, {\n ...selectedNode.data,\n dependencies: newDeps\n })\n }}\n style={{ marginRight: '8px' }}\n />\n {stepId}\n </label>\n ))}\n </div>\n\n {/* Step-specific configuration */}\n {renderStepConfiguration()}\n\n {/* Submit button */}\n <div style={{ \n borderTop: '1px solid var(--theme-elevation-100)', \n paddingTop: '16px', \n marginTop: '16px' \n }}>\n <Button\n buttonStyle=\"primary\"\n onClick={handleSave}\n >\n Save Configuration\n </Button>\n </div>\n </div>\n </div>\n )\n}"],"names":["React","useState","useCallback","useEffect","Button","StepConfigurationForm","selectedNode","availableStepTypes","availableSteps","onNodeUpdate","onClose","formData","setFormData","data","configuration","jsonText","setJsonText","JSON","stringify","stepType","find","type","slug","inputSchema","config","handleSave","id","renderStepConfiguration","length","div","style","padding","textAlign","color","fontStyle","marginBottom","label","display","fontSize","fontWeight","code","details","summary","cursor","background","border","borderRadius","fontFamily","map","field","index","strong","name","required","span","admin","description","marginTop","textarea","value","onChange","e","text","target","parsed","parse","rows","Math","min","max","width","lineHeight","resize","placeholder","height","flexDirection","overflow","borderBottom","justifyContent","alignItems","h4","margin","buttonStyle","onClick","size","flex","input","filter","step","stepId","checked","dependencies","includes","currentDeps","newDeps","dep","marginRight","borderTop","paddingTop"],"mappings":"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"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import React, { useCallback, useMemo, useState } from 'react';
|
|
4
|
+
import { ReactFlow, addEdge, useNodesState, useEdgesState, Controls, Background, BackgroundVariant, MiniMap, Panel } from '@xyflow/react';
|
|
5
|
+
import '@xyflow/react/dist/style.css';
|
|
6
|
+
// Import custom node types
|
|
7
|
+
import { StepNode } from './nodes/StepNode.js';
|
|
8
|
+
import { WorkflowToolbar } from './WorkflowToolbar.js';
|
|
9
|
+
import { StepConfigurationForm } from './StepConfigurationForm.js';
|
|
10
|
+
// Define node types for React Flow
|
|
11
|
+
const nodeTypes = {
|
|
12
|
+
stepNode: StepNode
|
|
13
|
+
};
|
|
14
|
+
export const WorkflowBuilder = ({ workflow, availableStepTypes = [], onSave, readonly = false })=>{
|
|
15
|
+
const [selectedNode, setSelectedNode] = useState(null);
|
|
16
|
+
// Convert workflow steps to React Flow nodes
|
|
17
|
+
const initialNodes = useMemo(()=>{
|
|
18
|
+
if (!workflow?.steps) return [];
|
|
19
|
+
return workflow.steps.map((step, index)=>({
|
|
20
|
+
id: step.name || `step-${index}`,
|
|
21
|
+
type: 'stepNode',
|
|
22
|
+
position: step.position || {
|
|
23
|
+
x: 100 + index * 200,
|
|
24
|
+
y: 100
|
|
25
|
+
},
|
|
26
|
+
data: {
|
|
27
|
+
label: step.name || 'Unnamed Step',
|
|
28
|
+
stepType: step.type,
|
|
29
|
+
color: step.visual?.color || '#3b82f6',
|
|
30
|
+
icon: step.visual?.icon,
|
|
31
|
+
dependencies: step.dependencies || []
|
|
32
|
+
}
|
|
33
|
+
}));
|
|
34
|
+
}, [
|
|
35
|
+
workflow?.steps
|
|
36
|
+
]);
|
|
37
|
+
// Convert dependencies to React Flow edges
|
|
38
|
+
const initialEdges = useMemo(()=>{
|
|
39
|
+
if (!workflow?.steps) return [];
|
|
40
|
+
const edges = [];
|
|
41
|
+
workflow.steps.forEach((step, index)=>{
|
|
42
|
+
const targetId = step.name || `step-${index}`;
|
|
43
|
+
if (step.dependencies) {
|
|
44
|
+
step.dependencies.forEach((depName)=>{
|
|
45
|
+
// Find the source step
|
|
46
|
+
const sourceStep = workflow.steps?.find((s)=>s.name === depName);
|
|
47
|
+
if (sourceStep) {
|
|
48
|
+
const sourceId = sourceStep.name || `step-${workflow.steps?.indexOf(sourceStep)}`;
|
|
49
|
+
edges.push({
|
|
50
|
+
id: `${sourceId}-${targetId}`,
|
|
51
|
+
source: sourceId,
|
|
52
|
+
target: targetId,
|
|
53
|
+
type: 'smoothstep'
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
return edges;
|
|
60
|
+
}, [
|
|
61
|
+
workflow?.steps
|
|
62
|
+
]);
|
|
63
|
+
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
|
|
64
|
+
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
|
65
|
+
// Handle new connections
|
|
66
|
+
const onConnect = useCallback((params)=>{
|
|
67
|
+
if (readonly) return;
|
|
68
|
+
setEdges((eds)=>addEdge({
|
|
69
|
+
...params,
|
|
70
|
+
type: 'smoothstep'
|
|
71
|
+
}, eds));
|
|
72
|
+
}, [
|
|
73
|
+
setEdges,
|
|
74
|
+
readonly
|
|
75
|
+
]);
|
|
76
|
+
// Handle node selection
|
|
77
|
+
const onNodeClick = useCallback((_event, node)=>{
|
|
78
|
+
console.log('Node clicked:', node.id, node.data.label);
|
|
79
|
+
setSelectedNode(node);
|
|
80
|
+
}, []);
|
|
81
|
+
// Handle adding new step
|
|
82
|
+
const onAddStep = useCallback((stepType)=>{
|
|
83
|
+
if (readonly) return;
|
|
84
|
+
const newStep = {
|
|
85
|
+
id: `step-${Date.now()}`,
|
|
86
|
+
type: 'stepNode',
|
|
87
|
+
position: {
|
|
88
|
+
x: 100,
|
|
89
|
+
y: 100
|
|
90
|
+
},
|
|
91
|
+
data: {
|
|
92
|
+
label: 'New Step',
|
|
93
|
+
stepType,
|
|
94
|
+
color: '#3b82f6',
|
|
95
|
+
dependencies: []
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
setNodes((nds)=>[
|
|
99
|
+
...nds,
|
|
100
|
+
newStep
|
|
101
|
+
]);
|
|
102
|
+
}, [
|
|
103
|
+
setNodes,
|
|
104
|
+
readonly
|
|
105
|
+
]);
|
|
106
|
+
// Handle updating a node's data
|
|
107
|
+
const handleNodeUpdate = useCallback((nodeId, newData)=>{
|
|
108
|
+
setNodes((nds)=>nds.map((node)=>node.id === nodeId ? {
|
|
109
|
+
...node,
|
|
110
|
+
data: {
|
|
111
|
+
...node.data,
|
|
112
|
+
...newData
|
|
113
|
+
}
|
|
114
|
+
} : node));
|
|
115
|
+
}, [
|
|
116
|
+
setNodes
|
|
117
|
+
]);
|
|
118
|
+
// Handle saving workflow
|
|
119
|
+
const handleSave = useCallback(()=>{
|
|
120
|
+
if (!workflow || !onSave) return;
|
|
121
|
+
// Convert nodes and edges back to workflow format
|
|
122
|
+
const updatedSteps = nodes.map((node)=>{
|
|
123
|
+
// Find dependencies from edges
|
|
124
|
+
const dependencies = edges.filter((edge)=>edge.target === node.id).map((edge)=>edge.source);
|
|
125
|
+
return {
|
|
126
|
+
name: node.id,
|
|
127
|
+
type: node.data.stepType,
|
|
128
|
+
position: node.position,
|
|
129
|
+
visual: {
|
|
130
|
+
color: node.data.color,
|
|
131
|
+
icon: node.data.icon
|
|
132
|
+
},
|
|
133
|
+
dependencies: dependencies.length > 0 ? dependencies : undefined
|
|
134
|
+
};
|
|
135
|
+
});
|
|
136
|
+
const updatedWorkflow = {
|
|
137
|
+
...workflow,
|
|
138
|
+
steps: updatedSteps
|
|
139
|
+
};
|
|
140
|
+
onSave(updatedWorkflow);
|
|
141
|
+
}, [
|
|
142
|
+
workflow,
|
|
143
|
+
nodes,
|
|
144
|
+
edges,
|
|
145
|
+
onSave
|
|
146
|
+
]);
|
|
147
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
148
|
+
style: {
|
|
149
|
+
width: '100%',
|
|
150
|
+
height: '600px',
|
|
151
|
+
display: 'flex',
|
|
152
|
+
background: 'var(--theme-bg)',
|
|
153
|
+
borderRadius: '4px',
|
|
154
|
+
border: '1px solid var(--theme-elevation-100)'
|
|
155
|
+
},
|
|
156
|
+
children: [
|
|
157
|
+
/*#__PURE__*/ _jsx("div", {
|
|
158
|
+
style: {
|
|
159
|
+
flex: selectedNode ? '1 1 70%' : '1 1 100%',
|
|
160
|
+
transition: 'flex 0.3s ease'
|
|
161
|
+
},
|
|
162
|
+
children: /*#__PURE__*/ _jsxs(ReactFlow, {
|
|
163
|
+
nodes: nodes,
|
|
164
|
+
edges: edges,
|
|
165
|
+
onNodesChange: onNodesChange,
|
|
166
|
+
onEdgesChange: onEdgesChange,
|
|
167
|
+
onConnect: onConnect,
|
|
168
|
+
onNodeClick: onNodeClick,
|
|
169
|
+
nodeTypes: nodeTypes,
|
|
170
|
+
fitView: true,
|
|
171
|
+
attributionPosition: "top-right",
|
|
172
|
+
children: [
|
|
173
|
+
/*#__PURE__*/ _jsx(Controls, {}),
|
|
174
|
+
/*#__PURE__*/ _jsx(MiniMap, {}),
|
|
175
|
+
/*#__PURE__*/ _jsx(Background, {
|
|
176
|
+
variant: BackgroundVariant.Dots,
|
|
177
|
+
gap: 12,
|
|
178
|
+
size: 1
|
|
179
|
+
}),
|
|
180
|
+
!readonly && /*#__PURE__*/ _jsx(Panel, {
|
|
181
|
+
position: "top-left",
|
|
182
|
+
children: /*#__PURE__*/ _jsx(WorkflowToolbar, {
|
|
183
|
+
availableStepTypes: availableStepTypes,
|
|
184
|
+
onAddStep: onAddStep,
|
|
185
|
+
onSave: handleSave
|
|
186
|
+
})
|
|
187
|
+
})
|
|
188
|
+
]
|
|
189
|
+
})
|
|
190
|
+
}),
|
|
191
|
+
selectedNode && !readonly && /*#__PURE__*/ _jsx("div", {
|
|
192
|
+
style: {
|
|
193
|
+
flex: '0 0 30%',
|
|
194
|
+
borderLeft: '1px solid var(--theme-elevation-100)',
|
|
195
|
+
background: 'var(--theme-elevation-0)',
|
|
196
|
+
display: 'flex',
|
|
197
|
+
flexDirection: 'column'
|
|
198
|
+
},
|
|
199
|
+
children: /*#__PURE__*/ _jsx(StepConfigurationForm, {
|
|
200
|
+
selectedNode: selectedNode,
|
|
201
|
+
availableStepTypes: availableStepTypes,
|
|
202
|
+
availableSteps: nodes.map((node)=>node.id),
|
|
203
|
+
onNodeUpdate: handleNodeUpdate,
|
|
204
|
+
onClose: ()=>setSelectedNode(null)
|
|
205
|
+
})
|
|
206
|
+
})
|
|
207
|
+
]
|
|
208
|
+
});
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
//# sourceMappingURL=WorkflowBuilder.js.map
|