diff --git a/dev/payload.config.ts b/dev/payload.config.ts index 2d392df..b40691e 100644 --- a/dev/payload.config.ts +++ b/dev/payload.config.ts @@ -1,16 +1,13 @@ -import type {CollectionSlug, TypedJobs} from 'payload'; +import type {CollectionSlug} from 'payload'; -import {sqliteAdapter} from "@payloadcms/db-sqlite" import { lexicalEditor } from '@payloadcms/richtext-lexical' -import { MongoMemoryReplSet } from 'mongodb-memory-server' import path from 'path' import {buildConfig} from 'payload' import sharp from 'sharp' import { fileURLToPath } from 'url' import {workflowsPlugin} from "../src/plugin/index.js" -import {HttpRequestStepTask} from "../src/steps/http-request.js" -import {CreateDocumentStepTask} from "../src/steps/index.js" +import {CreateDocumentStepTask,HttpRequestStepTask} from "../src/steps/index.js" import { testEmailAdapter } from './helpers/testEmailAdapter.js' import { seed } from './seed.js' diff --git a/package.json b/package.json index 52725f7..117f4b9 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "@payloadcms/ui": "3.45.0", "@playwright/test": "^1.52.0", "@swc/cli": "0.6.0", + "@types/handlebars": "^4.1.0", "@types/nock": "^11.1.0", "@types/node": "^22.5.4", "@types/node-cron": "^3.0.11", @@ -135,7 +136,7 @@ "registry": "https://registry.npmjs.org/", "packageManager": "pnpm@10.12.4+sha512.5ea8b0deed94ed68691c9bad4c955492705c5eeb8a87ef86bc62c74a26b037b08ff9570f108b2e4dbd1dd1a9186fea925e527f141c648e85af45631074680184", "dependencies": { - "jsonpath-plus": "^10.3.0", + "handlebars": "^4.7.8", "node-cron": "^4.2.1", "pino": "^9.9.0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b9c1d83..8b164ec 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,9 +8,9 @@ importers: .: dependencies: - jsonpath-plus: - specifier: ^10.3.0 - version: 10.3.0 + handlebars: + specifier: ^4.7.8 + version: 4.7.8 node-cron: specifier: ^4.2.1 version: 4.2.1 @@ -45,6 +45,9 @@ importers: '@swc/cli': specifier: 0.6.0 version: 0.6.0(@swc/core@1.13.4) + '@types/handlebars': + specifier: ^4.1.0 + version: 4.1.0 '@types/nock': specifier: ^11.1.0 version: 11.1.0 @@ -962,18 +965,6 @@ packages: '@jsdevtools/ono@7.1.3': resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} - '@jsep-plugin/assignment@1.3.0': - resolution: {integrity: sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==} - engines: {node: '>= 10.16.0'} - peerDependencies: - jsep: ^0.4.0||^1.0.0 - - '@jsep-plugin/regex@1.0.4': - resolution: {integrity: sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==} - engines: {node: '>= 10.16.0'} - peerDependencies: - jsep: ^0.4.0||^1.0.0 - '@lexical/clipboard@0.28.0': resolution: {integrity: sha512-LYqion+kAwFQJStA37JAEMxTL/m1WlZbotDfM/2WuONmlO0yWxiyRDI18oeCwhBD6LQQd9c3Ccxp9HFwUG1AVw==} @@ -1600,6 +1591,10 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/handlebars@4.1.0': + resolution: {integrity: sha512-gq9YweFKNNB1uFK71eRqsd4niVkXrxHugqWFQkeLRJvGjnxsLr16bYtcsG4tOFwmYi0Bax+wCkbf1reUfdl4kA==} + deprecated: This is a stub types definition. handlebars provides its own type definitions, so you do not need this installed. + '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} @@ -2886,6 +2881,11 @@ packages: resolution: {integrity: sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + handlebars@4.7.8: + resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} + engines: {node: '>=0.4.7'} + hasBin: true + has-bigints@1.1.0: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} engines: {node: '>= 0.4'} @@ -3172,10 +3172,6 @@ packages: resolution: {integrity: sha512-iZ8Bdb84lWRuGHamRXFyML07r21pcwBrLkHEuHgEY5UbCouBwv7ECknDRKzsQIXMiqpPymqtIf8TC/shYKB5rw==} engines: {node: '>=12.0.0'} - jsep@1.4.0: - resolution: {integrity: sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==} - engines: {node: '>= 10.16.0'} - jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -3204,11 +3200,6 @@ packages: json-stringify-safe@5.0.1: resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} - jsonpath-plus@10.3.0: - resolution: {integrity: sha512-8TNmfeTCk2Le33A3vRRwtuworG/L5RrgMvdjhKZxvyShO+mBu2fP50OWUjRLNtvw344DdDarFh9buFAZs5ujeA==} - engines: {node: '>=18.0.0'} - hasBin: true - jsox@1.2.121: resolution: {integrity: sha512-9Ag50tKhpTwS6r5wh3MJSAvpSof0UBr39Pto8OnzFT32Z/pAbxAsKHzyvsyMEHVslELvHyO/4/jaQELHk8wDcw==} hasBin: true @@ -3534,6 +3525,9 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + new-find-package-json@2.0.0: resolution: {integrity: sha512-lDcBsjBSMlj3LXH2v/FW3txlh2pYTjmbOXPYJD93HI5EwuLzI11tdHSIpUMmfq/IOsldj4Ps8M8flhm+pCK4Ew==} engines: {node: '>=12.22.0'} @@ -4472,6 +4466,11 @@ packages: engines: {node: '>=14.17'} hasBin: true + uglify-js@3.19.3: + resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} + engines: {node: '>=0.8.0'} + hasBin: true + uint8array-extras@1.5.0: resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} engines: {node: '>=18'} @@ -4663,6 +4662,9 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wordwrap@1.0.0: + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -5447,14 +5449,6 @@ snapshots: '@jsdevtools/ono@7.1.3': {} - '@jsep-plugin/assignment@1.3.0(jsep@1.4.0)': - dependencies: - jsep: 1.4.0 - - '@jsep-plugin/regex@1.0.4(jsep@1.4.0)': - dependencies: - jsep: 1.4.0 - '@lexical/clipboard@0.28.0': dependencies: '@lexical/html': 0.28.0 @@ -6316,6 +6310,10 @@ snapshots: '@types/estree@1.0.8': {} + '@types/handlebars@4.1.0': + dependencies: + handlebars: 4.7.8 + '@types/hast@3.0.4': dependencies: '@types/unist': 3.0.3 @@ -7966,6 +7964,15 @@ snapshots: graphql@16.11.0: {} + handlebars@4.7.8: + dependencies: + minimist: 1.2.8 + neo-async: 2.6.2 + source-map: 0.6.1 + wordwrap: 1.0.0 + optionalDependencies: + uglify-js: 3.19.3 + has-bigints@1.1.0: {} has-flag@4.0.0: {} @@ -8224,8 +8231,6 @@ snapshots: jsdoc-type-pratt-parser@4.8.0: {} - jsep@1.4.0: {} - jsesc@3.1.0: {} json-buffer@3.0.1: {} @@ -8252,12 +8257,6 @@ snapshots: json-stringify-safe@5.0.1: {} - jsonpath-plus@10.3.0: - dependencies: - '@jsep-plugin/assignment': 1.3.0(jsep@1.4.0) - '@jsep-plugin/regex': 1.0.4(jsep@1.4.0) - jsep: 1.4.0 - jsox@1.2.121: {} jsx-ast-utils@3.3.5: @@ -8698,6 +8697,8 @@ snapshots: natural-compare@1.4.0: {} + neo-async@2.6.2: {} + new-find-package-json@2.0.0: dependencies: debug: 4.4.1 @@ -9761,6 +9762,9 @@ snapshots: typescript@5.7.3: {} + uglify-js@3.19.3: + optional: true + uint8array-extras@1.5.0: {} unbox-primitive@1.1.0: @@ -9973,6 +9977,8 @@ snapshots: word-wrap@1.2.5: {} + wordwrap@1.0.0: {} + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 diff --git a/src/collections/Workflow.ts b/src/collections/Workflow.ts index 023e927..dcf7c1e 100644 --- a/src/collections/Workflow.ts +++ b/src/collections/Workflow.ts @@ -84,15 +84,13 @@ export const createWorkflowCollection: (options: WorkflowsPlug options: steps.map(t => t.slug) }, { - name: 'parameters', + name: 'input', type: 'json', admin: { - hidden: true, + description: 'Step input configuration. Use JSONPath expressions to reference dynamic data (e.g., {"url": "$.trigger.doc.webhookUrl", "data": "$.steps.previousStep.output.result"})' }, defaultValue: {} }, - // Virtual fields for custom triggers - ...steps.flatMap(step => (step.inputSchema || []).map(s => parameter(step.slug, s as any))), { name: 'dependencies', type: 'text', diff --git a/src/components/WorkflowBuilder/StepConfigurationForm.tsx b/src/components/WorkflowBuilder/StepConfigurationForm.tsx new file mode 100644 index 0000000..9d2caeb --- /dev/null +++ b/src/components/WorkflowBuilder/StepConfigurationForm.tsx @@ -0,0 +1,297 @@ +'use client' + +import React, { useState, useCallback, useEffect } from 'react' +import type { Node } from '@xyflow/react' +import { Button } from '@payloadcms/ui' + +interface StepField { + name: string + type: string + label?: string + admin?: { + description?: string + condition?: (data: any, siblingData: any) => boolean + } + options?: Array<{ label: string; value: string }> + defaultValue?: any + required?: boolean + hasMany?: boolean + fields?: StepField[] // For group fields +} + +interface StepType { + slug: string + label?: string + inputSchema?: StepField[] + outputSchema?: StepField[] +} + +interface StepConfigurationFormProps { + selectedNode: Node | null + availableStepTypes: StepType[] + availableSteps: string[] // For dependency selection + onNodeUpdate: (nodeId: string, data: Partial) => void + onClose: () => void +} + +export const StepConfigurationForm: React.FC = ({ + selectedNode, + availableStepTypes, + availableSteps, + onNodeUpdate, + onClose +}) => { + const [formData, setFormData] = useState>( + selectedNode?.data.configuration || {} + ) + const [jsonText, setJsonText] = useState(() => + JSON.stringify(selectedNode?.data.configuration || {}, null, 2) + ) + + if (!selectedNode) return null + + const stepType = availableStepTypes.find(type => type.slug === selectedNode.data.stepType) + const inputSchema = stepType?.inputSchema || [] + + // Update form data when selected node changes + useEffect(() => { + const config = selectedNode?.data.configuration || {} + setFormData(config) + setJsonText(JSON.stringify(config, null, 2)) + }, [selectedNode]) + + + const handleSave = useCallback(() => { + // Update the node with form data + onNodeUpdate(selectedNode.id, { + ...selectedNode.data, + configuration: formData + }) + + onClose() + }, [selectedNode, formData, onNodeUpdate, onClose]) + + const renderStepConfiguration = () => { + if (!inputSchema.length) { + return ( +
+ This step type has no configuration parameters. +
+ ) + } + + return ( +
+ +
+ Configure this step's parameters in JSON format. Use JSONPath expressions like $.trigger.doc.id to reference dynamic data. +
+ + {/* Schema Reference */} +
+ + 📖 Available Fields (click to expand) + +
+ {inputSchema.map((field, index) => ( +
+ {field.name} ({field.type}) + {field.required && *required} + {field.admin?.description && ( +
+ {field.admin.description} +
+ )} +
+ ))} +
+
+ +