@open-mercato/core 0.7.1-develop.7193.1.910a5b0a1e → 0.7.1-develop.7195.1.b5efd1d51a
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/.turbo/turbo-build.log +2 -2
- package/AGENTS.md +4 -3
- package/agentic/standalone-guide.md +1 -1
- package/build.mjs +16 -0
- package/dist/bootstrap.js +7 -3
- package/dist/bootstrap.js.map +2 -2
- package/dist/generated/entities/action_log/index.js +2 -0
- package/dist/generated/entities/action_log/index.js.map +2 -2
- package/dist/generated/entities/user/index.js +2 -0
- package/dist/generated/entities/user/index.js.map +2 -2
- package/dist/generated/entities/user_task/index.js +14 -0
- package/dist/generated/entities/user_task/index.js.map +2 -2
- package/dist/generated/entities/workflow_definition/index.js +6 -0
- package/dist/generated/entities/workflow_definition/index.js.map +2 -2
- package/dist/generated/entities/workflow_definition_draft/index.js +25 -0
- package/dist/generated/entities/workflow_definition_draft/index.js.map +7 -0
- package/dist/generated/entities/workflow_definition_metric_rollup/index.js +23 -0
- package/dist/generated/entities/workflow_definition_metric_rollup/index.js.map +7 -0
- package/dist/generated/entities/workflow_instance/index.js +4 -0
- package/dist/generated/entities/workflow_instance/index.js.map +2 -2
- package/dist/generated/entities.ids.generated.js +4 -1
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +39 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/helpers/integration/workflowsFixtures.js +477 -2
- package/dist/helpers/integration/workflowsFixtures.js.map +2 -2
- package/dist/helpers/integration/workflowsUi.js +148 -0
- package/dist/helpers/integration/workflowsUi.js.map +7 -0
- package/dist/modules/attachments/lib/createFromBuffer.js +79 -0
- package/dist/modules/attachments/lib/createFromBuffer.js.map +7 -0
- package/dist/modules/audit_logs/data/entities.js +5 -0
- package/dist/modules/audit_logs/data/entities.js.map +2 -2
- package/dist/modules/audit_logs/data/validators.js +1 -0
- package/dist/modules/audit_logs/data/validators.js.map +2 -2
- package/dist/modules/audit_logs/lib/projections.js +2 -1
- package/dist/modules/audit_logs/lib/projections.js.map +2 -2
- package/dist/modules/audit_logs/migrations/Migration20260625120000.js +15 -0
- package/dist/modules/audit_logs/migrations/Migration20260625120000.js.map +7 -0
- package/dist/modules/audit_logs/services/actionLogService.js +3 -0
- package/dist/modules/audit_logs/services/actionLogService.js.map +2 -2
- package/dist/modules/auth/data/entities.js +6 -0
- package/dist/modules/auth/data/entities.js.map +2 -2
- package/dist/modules/auth/lib/executionPrincipal.js +140 -0
- package/dist/modules/auth/lib/executionPrincipal.js.map +7 -0
- package/dist/modules/auth/migrations/Migration20260625050000.js +13 -0
- package/dist/modules/auth/migrations/Migration20260625050000.js.map +7 -0
- package/dist/modules/business_rules/components/InlineRuleEditor.js +154 -0
- package/dist/modules/business_rules/components/InlineRuleEditor.js.map +7 -0
- package/dist/modules/business_rules/data/validators.js +6 -0
- package/dist/modules/business_rules/data/validators.js.map +2 -2
- package/dist/modules/business_rules/lib/rule-engine.js +5 -3
- package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
- package/dist/modules/catalog/commands/products.js +5 -0
- package/dist/modules/catalog/commands/products.js.map +2 -2
- package/dist/modules/catalog/workflows.js +19 -0
- package/dist/modules/catalog/workflows.js.map +7 -0
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js +3 -1
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js.map +2 -2
- package/dist/modules/customer_accounts/cli.js +80 -0
- package/dist/modules/customer_accounts/cli.js.map +7 -0
- package/dist/modules/customer_accounts/lib/customerRoleAclSync.js +59 -0
- package/dist/modules/customer_accounts/lib/customerRoleAclSync.js.map +7 -0
- package/dist/modules/customer_accounts/setup.js +4 -2
- package/dist/modules/customer_accounts/setup.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +4 -0
- package/dist/modules/customers/commands/deals.js.map +2 -2
- package/dist/modules/customers/components/detail/ChangelogEntryRow.js +1 -1
- package/dist/modules/customers/components/detail/ChangelogEntryRow.js.map +2 -2
- package/dist/modules/customers/components/detail/utils.js +2 -0
- package/dist/modules/customers/components/detail/utils.js.map +2 -2
- package/dist/modules/customers/events.js +13 -2
- package/dist/modules/customers/events.js.map +2 -2
- package/dist/modules/customers/lib/link-workflow-task-handler.js +64 -0
- package/dist/modules/customers/lib/link-workflow-task-handler.js.map +7 -0
- package/dist/modules/customers/lib/todoCompatibility.js +1 -1
- package/dist/modules/customers/lib/todoCompatibility.js.map +2 -2
- package/dist/modules/customers/lib/workflowTaskLink.js +9 -0
- package/dist/modules/customers/lib/workflowTaskLink.js.map +7 -0
- package/dist/modules/customers/subscribers/link-workflow-task.js +12 -0
- package/dist/modules/customers/subscribers/link-workflow-task.js.map +7 -0
- package/dist/modules/customers/workflows.js +29 -0
- package/dist/modules/customers/workflows.js.map +7 -0
- package/dist/modules/entities/api/records.js +13 -33
- package/dist/modules/entities/api/records.js.map +3 -3
- package/dist/modules/entities/lib/entityClassification.js +60 -0
- package/dist/modules/entities/lib/entityClassification.js.map +7 -0
- package/dist/modules/sales/commands/documents.js +31 -0
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/events.js +12 -1
- package/dist/modules/sales/events.js.map +2 -2
- package/dist/modules/sales/workflows.js +12 -1
- package/dist/modules/sales/workflows.js.map +2 -2
- package/dist/modules/seeds/cli.js +199 -0
- package/dist/modules/seeds/cli.js.map +7 -0
- package/dist/modules/seeds/examples/sample-seed.json +26 -0
- package/dist/modules/seeds/index.js +12 -0
- package/dist/modules/seeds/index.js.map +7 -0
- package/dist/modules/workflows/acl.js +96 -0
- package/dist/modules/workflows/acl.js.map +2 -2
- package/dist/modules/workflows/ai-agents.js +50 -0
- package/dist/modules/workflows/ai-agents.js.map +7 -0
- package/dist/modules/workflows/ai-tools/authoring-pack.js +582 -0
- package/dist/modules/workflows/ai-tools/authoring-pack.js.map +7 -0
- package/dist/modules/workflows/ai-tools/types.js +43 -0
- package/dist/modules/workflows/ai-tools/types.js.map +7 -0
- package/dist/modules/workflows/ai-tools.js +10 -0
- package/dist/modules/workflows/ai-tools.js.map +7 -0
- package/dist/modules/workflows/api/command-settings/route.js +137 -0
- package/dist/modules/workflows/api/command-settings/route.js.map +7 -0
- package/dist/modules/workflows/api/commands/route.js +72 -0
- package/dist/modules/workflows/api/commands/route.js.map +7 -0
- package/dist/modules/workflows/api/definitions/[id]/callers/route.js +76 -0
- package/dist/modules/workflows/api/definitions/[id]/callers/route.js.map +7 -0
- package/dist/modules/workflows/api/definitions/[id]/context-schema/route.js +134 -0
- package/dist/modules/workflows/api/definitions/[id]/context-schema/route.js.map +7 -0
- package/dist/modules/workflows/api/definitions/[id]/customize/route.js +5 -1
- package/dist/modules/workflows/api/definitions/[id]/customize/route.js.map +2 -2
- package/dist/modules/workflows/api/definitions/[id]/draft/route.js +286 -0
- package/dist/modules/workflows/api/definitions/[id]/draft/route.js.map +7 -0
- package/dist/modules/workflows/api/definitions/[id]/publish/route.js +207 -0
- package/dist/modules/workflows/api/definitions/[id]/publish/route.js.map +7 -0
- package/dist/modules/workflows/api/definitions/[id]/reset-to-code/route.js +5 -0
- package/dist/modules/workflows/api/definitions/[id]/reset-to-code/route.js.map +2 -2
- package/dist/modules/workflows/api/definitions/[id]/route.js +93 -9
- package/dist/modules/workflows/api/definitions/[id]/route.js.map +3 -3
- package/dist/modules/workflows/api/definitions/[id]/test-step/route.js +210 -0
- package/dist/modules/workflows/api/definitions/[id]/test-step/route.js.map +7 -0
- package/dist/modules/workflows/api/definitions/generate/route.js +233 -0
- package/dist/modules/workflows/api/definitions/generate/route.js.map +7 -0
- package/dist/modules/workflows/api/definitions/route.js +61 -113
- package/dist/modules/workflows/api/definitions/route.js.map +2 -2
- package/dist/modules/workflows/api/definitions/serialize.js +9 -0
- package/dist/modules/workflows/api/definitions/serialize.js.map +2 -2
- package/dist/modules/workflows/api/endpoints/route.js +69 -0
- package/dist/modules/workflows/api/endpoints/route.js.map +7 -0
- package/dist/modules/workflows/api/functions/route.js +73 -0
- package/dist/modules/workflows/api/functions/route.js.map +7 -0
- package/dist/modules/workflows/api/grantable-features/route.js +75 -0
- package/dist/modules/workflows/api/grantable-features/route.js.map +7 -0
- package/dist/modules/workflows/api/instances/[id]/context/route.js +180 -0
- package/dist/modules/workflows/api/instances/[id]/context/route.js.map +7 -0
- package/dist/modules/workflows/api/instances/[id]/rerun-step/route.js +184 -0
- package/dist/modules/workflows/api/instances/[id]/rerun-step/route.js.map +7 -0
- package/dist/modules/workflows/api/instances/[id]/retry/route.js +12 -2
- package/dist/modules/workflows/api/instances/[id]/retry/route.js.map +2 -2
- package/dist/modules/workflows/api/instances/[id]/step-through/route.js +148 -0
- package/dist/modules/workflows/api/instances/[id]/step-through/route.js.map +7 -0
- package/dist/modules/workflows/api/instances/[id]/steps/route.js +123 -0
- package/dist/modules/workflows/api/instances/[id]/steps/route.js.map +7 -0
- package/dist/modules/workflows/api/instances/[id]/would-do/route.js +172 -0
- package/dist/modules/workflows/api/instances/[id]/would-do/route.js.map +7 -0
- package/dist/modules/workflows/api/instances/bulk-replay/route.js +117 -0
- package/dist/modules/workflows/api/instances/bulk-replay/route.js.map +7 -0
- package/dist/modules/workflows/api/instances/failure-queue/route.js +165 -0
- package/dist/modules/workflows/api/instances/failure-queue/route.js.map +7 -0
- package/dist/modules/workflows/api/instances/route.js +94 -3
- package/dist/modules/workflows/api/instances/route.js.map +2 -2
- package/dist/modules/workflows/api/metrics/definitions/route.js +188 -0
- package/dist/modules/workflows/api/metrics/definitions/route.js.map +7 -0
- package/dist/modules/workflows/api/openapi.js +415 -4
- package/dist/modules/workflows/api/openapi.js.map +2 -2
- package/dist/modules/workflows/api/portal/tasks/[id]/complete/route.js +132 -0
- package/dist/modules/workflows/api/portal/tasks/[id]/complete/route.js.map +7 -0
- package/dist/modules/workflows/api/portal/tasks/[id]/route.js +97 -0
- package/dist/modules/workflows/api/portal/tasks/[id]/route.js.map +7 -0
- package/dist/modules/workflows/api/portal/tasks/route.js +126 -0
- package/dist/modules/workflows/api/portal/tasks/route.js.map +7 -0
- package/dist/modules/workflows/api/rule-usage/route.js +80 -0
- package/dist/modules/workflows/api/rule-usage/route.js.map +7 -0
- package/dist/modules/workflows/api/task-settings/route.js +107 -0
- package/dist/modules/workflows/api/task-settings/route.js.map +7 -0
- package/dist/modules/workflows/api/tasks/[id]/claim/route.js +27 -16
- package/dist/modules/workflows/api/tasks/[id]/claim/route.js.map +2 -2
- package/dist/modules/workflows/api/tasks/[id]/complete/route.js +40 -16
- package/dist/modules/workflows/api/tasks/[id]/complete/route.js.map +2 -2
- package/dist/modules/workflows/api/tasks/[id]/reassign/route.js +150 -0
- package/dist/modules/workflows/api/tasks/[id]/reassign/route.js.map +7 -0
- package/dist/modules/workflows/api/tasks/[id]/route.js +39 -6
- package/dist/modules/workflows/api/tasks/[id]/route.js.map +2 -2
- package/dist/modules/workflows/api/tasks/[id]/unclaim/route.js +100 -0
- package/dist/modules/workflows/api/tasks/[id]/unclaim/route.js.map +7 -0
- package/dist/modules/workflows/api/tasks/route.js +37 -6
- package/dist/modules/workflows/api/tasks/route.js.map +2 -2
- package/dist/modules/workflows/api/tasks/serialize.js +83 -0
- package/dist/modules/workflows/api/tasks/serialize.js.map +7 -0
- package/dist/modules/workflows/api/templates/route.js +69 -0
- package/dist/modules/workflows/api/templates/route.js.map +7 -0
- package/dist/modules/workflows/api/work-inbox/next/route.js +129 -0
- package/dist/modules/workflows/api/work-inbox/next/route.js.map +7 -0
- package/dist/modules/workflows/api/work-inbox/route.js +119 -0
- package/dist/modules/workflows/api/work-inbox/route.js.map +7 -0
- package/dist/modules/workflows/backend/config/workflows/page.js +10 -0
- package/dist/modules/workflows/backend/config/workflows/page.js.map +7 -0
- package/dist/modules/workflows/backend/config/workflows/page.meta.js +35 -0
- package/dist/modules/workflows/backend/config/workflows/page.meta.js.map +7 -0
- package/dist/modules/workflows/backend/definitions/[id]/page.js +6 -271
- package/dist/modules/workflows/backend/definitions/[id]/page.js.map +3 -3
- package/dist/modules/workflows/backend/definitions/create/page.js +7 -56
- package/dist/modules/workflows/backend/definitions/create/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/create/page.meta.js +1 -1
- package/dist/modules/workflows/backend/definitions/create/page.meta.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/page.js +131 -51
- package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js +2606 -677
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +3 -3
- package/dist/modules/workflows/backend/instances/[id]/page.js +532 -421
- package/dist/modules/workflows/backend/instances/[id]/page.js.map +2 -2
- package/dist/modules/workflows/backend/instances/[id]/page.meta.js +1 -1
- package/dist/modules/workflows/backend/instances/[id]/page.meta.js.map +1 -1
- package/dist/modules/workflows/backend/instances/failure-queue/page.js +211 -0
- package/dist/modules/workflows/backend/instances/failure-queue/page.js.map +7 -0
- package/dist/modules/workflows/backend/instances/failure-queue/page.meta.js +20 -0
- package/dist/modules/workflows/backend/instances/failure-queue/page.meta.js.map +7 -0
- package/dist/modules/workflows/backend/instances/page.js +159 -22
- package/dist/modules/workflows/backend/instances/page.js.map +2 -2
- package/dist/modules/workflows/backend/instances/page.meta.js +1 -1
- package/dist/modules/workflows/backend/instances/page.meta.js.map +1 -1
- package/dist/modules/workflows/backend/tasks/[id]/page.js +553 -392
- package/dist/modules/workflows/backend/tasks/[id]/page.js.map +3 -3
- package/dist/modules/workflows/backend/tasks/page.js +8 -250
- package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
- package/dist/modules/workflows/backend/tasks/page.meta.js +1 -0
- package/dist/modules/workflows/backend/tasks/page.meta.js.map +2 -2
- package/dist/modules/workflows/backend/work-inbox/page.js +399 -0
- package/dist/modules/workflows/backend/work-inbox/page.js.map +7 -0
- package/dist/modules/workflows/backend/work-inbox/page.meta.js +19 -0
- package/dist/modules/workflows/backend/work-inbox/page.meta.js.map +7 -0
- package/dist/modules/workflows/cli.js +1 -1
- package/dist/modules/workflows/cli.js.map +2 -2
- package/dist/modules/workflows/commands/tasks.js +81 -0
- package/dist/modules/workflows/commands/tasks.js.map +7 -0
- package/dist/modules/workflows/components/ActivitiesEditor.js +281 -287
- package/dist/modules/workflows/components/ActivitiesEditor.js.map +2 -2
- package/dist/modules/workflows/components/ActivityEditDialog.js +220 -0
- package/dist/modules/workflows/components/ActivityEditDialog.js.map +7 -0
- package/dist/modules/workflows/components/AgentSelector.js +115 -0
- package/dist/modules/workflows/components/AgentSelector.js.map +7 -0
- package/dist/modules/workflows/components/AnnotationEditDialog.js +110 -0
- package/dist/modules/workflows/components/AnnotationEditDialog.js.map +7 -0
- package/dist/modules/workflows/components/BusinessRuleUsagePanel.js +71 -0
- package/dist/modules/workflows/components/BusinessRuleUsagePanel.js.map +7 -0
- package/dist/modules/workflows/components/BusinessRulesSelector.js +151 -93
- package/dist/modules/workflows/components/BusinessRulesSelector.js.map +2 -2
- package/dist/modules/workflows/components/ConfigJsonTextarea.js +85 -0
- package/dist/modules/workflows/components/ConfigJsonTextarea.js.map +7 -0
- package/dist/modules/workflows/components/ContextSchemaEditor.js +150 -0
- package/dist/modules/workflows/components/ContextSchemaEditor.js.map +7 -0
- package/dist/modules/workflows/components/DefinitionErrorHandlerField.js +102 -0
- package/dist/modules/workflows/components/DefinitionErrorHandlerField.js.map +7 -0
- package/dist/modules/workflows/components/DefinitionMetadataDrawer.js +377 -0
- package/dist/modules/workflows/components/DefinitionMetadataDrawer.js.map +7 -0
- package/dist/modules/workflows/components/DefinitionMetricsCells.js +132 -0
- package/dist/modules/workflows/components/DefinitionMetricsCells.js.map +7 -0
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js +307 -216
- package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +2 -2
- package/dist/modules/workflows/components/EdgeEditDialog.js +11 -18
- package/dist/modules/workflows/components/EdgeEditDialog.js.map +2 -2
- package/dist/modules/workflows/components/EdgeEditDialogCrudForm.js +112 -72
- package/dist/modules/workflows/components/EdgeEditDialogCrudForm.js.map +2 -2
- package/dist/modules/workflows/components/InputDataPanel.js +128 -0
- package/dist/modules/workflows/components/InputDataPanel.js.map +7 -0
- package/dist/modules/workflows/components/InspectorPanel.js +161 -0
- package/dist/modules/workflows/components/InspectorPanel.js.map +7 -0
- package/dist/modules/workflows/components/NodeEditDialog.js +363 -41
- package/dist/modules/workflows/components/NodeEditDialog.js.map +2 -2
- package/dist/modules/workflows/components/NodeEditDialogCrudForm.js +714 -159
- package/dist/modules/workflows/components/NodeEditDialogCrudForm.js.map +2 -2
- package/dist/modules/workflows/components/StepsEditor.js.map +2 -2
- package/dist/modules/workflows/components/TemplateGalleryDialog.js +148 -0
- package/dist/modules/workflows/components/TemplateGalleryDialog.js.map +7 -0
- package/dist/modules/workflows/components/TransitionsEditor.js +80 -74
- package/dist/modules/workflows/components/TransitionsEditor.js.map +2 -2
- package/dist/modules/workflows/components/TriggersDialog.js +53 -0
- package/dist/modules/workflows/components/TriggersDialog.js.map +7 -0
- package/dist/modules/workflows/components/TriggersEditor.js +331 -0
- package/dist/modules/workflows/components/TriggersEditor.js.map +7 -0
- package/dist/modules/workflows/components/WorkflowAiDraftDialog.js +278 -0
- package/dist/modules/workflows/components/WorkflowAiDraftDialog.js.map +7 -0
- package/dist/modules/workflows/components/WorkflowCodeView.js +173 -0
- package/dist/modules/workflows/components/WorkflowCodeView.js.map +7 -0
- package/dist/modules/workflows/components/WorkflowCommandPalette.js +62 -0
- package/dist/modules/workflows/components/WorkflowCommandPalette.js.map +7 -0
- package/dist/modules/workflows/components/WorkflowCompensationGhostEdge.js +70 -0
- package/dist/modules/workflows/components/WorkflowCompensationGhostEdge.js.map +7 -0
- package/dist/modules/workflows/components/WorkflowDataMappingEdge.js +36 -0
- package/dist/modules/workflows/components/WorkflowDataMappingEdge.js.map +7 -0
- package/dist/modules/workflows/components/WorkflowGraph.js +4 -2
- package/dist/modules/workflows/components/WorkflowGraph.js.map +2 -2
- package/dist/modules/workflows/components/WorkflowGraphImpl.js +324 -86
- package/dist/modules/workflows/components/WorkflowGraphImpl.js.map +3 -3
- package/dist/modules/workflows/components/WorkflowIconPicker.js +109 -0
- package/dist/modules/workflows/components/WorkflowIconPicker.js.map +7 -0
- package/dist/modules/workflows/components/WorkflowLegend.js +45 -29
- package/dist/modules/workflows/components/WorkflowLegend.js.map +2 -2
- package/dist/modules/workflows/components/WorkflowNodeCard.js +221 -22
- package/dist/modules/workflows/components/WorkflowNodeCard.js.map +2 -2
- package/dist/modules/workflows/components/WorkflowRouteChips.js +111 -0
- package/dist/modules/workflows/components/WorkflowRouteChips.js.map +7 -0
- package/dist/modules/workflows/components/WorkflowSelector.js +108 -88
- package/dist/modules/workflows/components/WorkflowSelector.js.map +2 -2
- package/dist/modules/workflows/components/WorkflowTransitionEdge.js +61 -11
- package/dist/modules/workflows/components/WorkflowTransitionEdge.js.map +2 -2
- package/dist/modules/workflows/components/WorkflowTransitionLabel.js +15 -5
- package/dist/modules/workflows/components/WorkflowTransitionLabel.js.map +2 -2
- package/dist/modules/workflows/components/fields/ActivityArrayEditor.js +88 -21
- package/dist/modules/workflows/components/fields/ActivityArrayEditor.js.map +2 -2
- package/dist/modules/workflows/components/fields/ActivityConfigFields.js +615 -0
- package/dist/modules/workflows/components/fields/ActivityConfigFields.js.map +7 -0
- package/dist/modules/workflows/components/fields/ActivityTestPanel.js +191 -0
- package/dist/modules/workflows/components/fields/ActivityTestPanel.js.map +7 -0
- package/dist/modules/workflows/components/fields/AgentInvokeConfigField.js +488 -0
- package/dist/modules/workflows/components/fields/AgentInvokeConfigField.js.map +7 -0
- package/dist/modules/workflows/components/fields/AgentReviewOnBreachField.js +61 -0
- package/dist/modules/workflows/components/fields/AgentReviewOnBreachField.js.map +7 -0
- package/dist/modules/workflows/components/fields/BranchingRoutesEditor.js +227 -0
- package/dist/modules/workflows/components/fields/BranchingRoutesEditor.js.map +7 -0
- package/dist/modules/workflows/components/fields/BusinessRuleConditionsEditor.js +37 -4
- package/dist/modules/workflows/components/fields/BusinessRuleConditionsEditor.js.map +2 -2
- package/dist/modules/workflows/components/fields/CommandPicker.js +111 -0
- package/dist/modules/workflows/components/fields/CommandPicker.js.map +7 -0
- package/dist/modules/workflows/components/fields/EndpointPicker.js +235 -0
- package/dist/modules/workflows/components/fields/EndpointPicker.js.map +7 -0
- package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js +2 -2
- package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js.map +2 -2
- package/dist/modules/workflows/components/fields/FunctionPicker.js +55 -0
- package/dist/modules/workflows/components/fields/FunctionPicker.js.map +7 -0
- package/dist/modules/workflows/components/fields/MappingArrayEditor.js +38 -15
- package/dist/modules/workflows/components/fields/MappingArrayEditor.js.map +2 -2
- package/dist/modules/workflows/components/fields/PortFieldArrayEditor.js +206 -0
- package/dist/modules/workflows/components/fields/PortFieldArrayEditor.js.map +7 -0
- package/dist/modules/workflows/components/fields/RolesMultiSelect.js +53 -0
- package/dist/modules/workflows/components/fields/RolesMultiSelect.js.map +7 -0
- package/dist/modules/workflows/components/fields/RouteOrderEditor.js +130 -0
- package/dist/modules/workflows/components/fields/RouteOrderEditor.js.map +7 -0
- package/dist/modules/workflows/components/fields/SchemaBuilderDialog.js +104 -0
- package/dist/modules/workflows/components/fields/SchemaBuilderDialog.js.map +7 -0
- package/dist/modules/workflows/components/fields/StartPreConditionsEditor.js +2 -2
- package/dist/modules/workflows/components/fields/StartPreConditionsEditor.js.map +2 -2
- package/dist/modules/workflows/components/fields/TaskAssignmentField.js +210 -0
- package/dist/modules/workflows/components/fields/TaskAssignmentField.js.map +7 -0
- package/dist/modules/workflows/components/fields/TaskDeadlineFields.js +126 -0
- package/dist/modules/workflows/components/fields/TaskDeadlineFields.js.map +7 -0
- package/dist/modules/workflows/components/fields/TaskDecisionsField.js +218 -0
- package/dist/modules/workflows/components/fields/TaskDecisionsField.js.map +7 -0
- package/dist/modules/workflows/components/fields/TaskEntityBindingsField.js +114 -0
- package/dist/modules/workflows/components/fields/TaskEntityBindingsField.js.map +7 -0
- package/dist/modules/workflows/components/fields/TemplateTextControl.js +64 -0
- package/dist/modules/workflows/components/fields/TemplateTextControl.js.map +7 -0
- package/dist/modules/workflows/components/fields/VariablePickerButton.js +121 -0
- package/dist/modules/workflows/components/fields/VariablePickerButton.js.map +7 -0
- package/dist/modules/workflows/components/fields/ledgerDropTarget.js +35 -0
- package/dist/modules/workflows/components/fields/ledgerDropTarget.js.map +7 -0
- package/dist/modules/workflows/components/fields/useActivityTypeOptions.js +16 -0
- package/dist/modules/workflows/components/fields/useActivityTypeOptions.js.map +7 -0
- package/dist/modules/workflows/components/formConfig.js +39 -10
- package/dist/modules/workflows/components/formConfig.js.map +2 -2
- package/dist/modules/workflows/components/mobile/MobileDefinitionDetail.js.map +2 -2
- package/dist/modules/workflows/components/mobile/MobileMetadataSheet.js.map +2 -2
- package/dist/modules/workflows/components/mobile/MobileTaskForm.js +29 -2
- package/dist/modules/workflows/components/mobile/MobileTaskForm.js.map +2 -2
- package/dist/modules/workflows/components/mobile/MobileVisualEditor.js +64 -65
- package/dist/modules/workflows/components/mobile/MobileVisualEditor.js.map +2 -2
- package/dist/modules/workflows/components/nodes/AnnotationGroupNode.js +64 -0
- package/dist/modules/workflows/components/nodes/AnnotationGroupNode.js.map +7 -0
- package/dist/modules/workflows/components/nodes/AutomatedNode.js +24 -16
- package/dist/modules/workflows/components/nodes/AutomatedNode.js.map +2 -2
- package/dist/modules/workflows/components/nodes/EndNode.js +13 -12
- package/dist/modules/workflows/components/nodes/EndNode.js.map +2 -2
- package/dist/modules/workflows/components/nodes/ErrorOutputHandle.js +29 -0
- package/dist/modules/workflows/components/nodes/ErrorOutputHandle.js.map +7 -0
- package/dist/modules/workflows/components/nodes/IfElseNode.js +51 -0
- package/dist/modules/workflows/components/nodes/IfElseNode.js.map +7 -0
- package/dist/modules/workflows/components/nodes/InvokeAgentNode.js +122 -0
- package/dist/modules/workflows/components/nodes/InvokeAgentNode.js.map +7 -0
- package/dist/modules/workflows/components/nodes/NodeOutcomeRows.js +100 -0
- package/dist/modules/workflows/components/nodes/NodeOutcomeRows.js.map +7 -0
- package/dist/modules/workflows/components/nodes/ParallelForkNode.js +18 -14
- package/dist/modules/workflows/components/nodes/ParallelForkNode.js.map +2 -2
- package/dist/modules/workflows/components/nodes/ParallelJoinNode.js +18 -14
- package/dist/modules/workflows/components/nodes/ParallelJoinNode.js.map +2 -2
- package/dist/modules/workflows/components/nodes/StartNode.js +66 -15
- package/dist/modules/workflows/components/nodes/StartNode.js.map +2 -2
- package/dist/modules/workflows/components/nodes/StickyNoteNode.js +54 -0
- package/dist/modules/workflows/components/nodes/StickyNoteNode.js.map +7 -0
- package/dist/modules/workflows/components/nodes/SubWorkflowNode.js +107 -41
- package/dist/modules/workflows/components/nodes/SubWorkflowNode.js.map +2 -2
- package/dist/modules/workflows/components/nodes/SwitchNode.js +51 -0
- package/dist/modules/workflows/components/nodes/SwitchNode.js.map +7 -0
- package/dist/modules/workflows/components/nodes/TriggerCap.js +65 -0
- package/dist/modules/workflows/components/nodes/TriggerCap.js.map +7 -0
- package/dist/modules/workflows/components/nodes/TriggerNode.js +111 -0
- package/dist/modules/workflows/components/nodes/TriggerNode.js.map +7 -0
- package/dist/modules/workflows/components/nodes/UserTaskNode.js +43 -17
- package/dist/modules/workflows/components/nodes/UserTaskNode.js.map +2 -2
- package/dist/modules/workflows/components/nodes/WaitForConditionNode.js +53 -0
- package/dist/modules/workflows/components/nodes/WaitForConditionNode.js.map +7 -0
- package/dist/modules/workflows/components/nodes/WaitForSignalNode.js +21 -15
- package/dist/modules/workflows/components/nodes/WaitForSignalNode.js.map +2 -2
- package/dist/modules/workflows/components/nodes/WaitForTimerNode.js +21 -15
- package/dist/modules/workflows/components/nodes/WaitForTimerNode.js.map +2 -2
- package/dist/modules/workflows/components/nodes/index.js +14 -0
- package/dist/modules/workflows/components/nodes/index.js.map +2 -2
- package/dist/modules/workflows/components/portal/PortalTaskForm.js +196 -0
- package/dist/modules/workflows/components/portal/PortalTaskForm.js.map +7 -0
- package/dist/modules/workflows/components/run/AgentActivityPanel.js +22 -0
- package/dist/modules/workflows/components/run/AgentActivityPanel.js.map +7 -0
- package/dist/modules/workflows/components/run/RerunStepDialog.js +106 -0
- package/dist/modules/workflows/components/run/RerunStepDialog.js.map +7 -0
- package/dist/modules/workflows/components/run/RunEventBadge.js +30 -0
- package/dist/modules/workflows/components/run/RunEventBadge.js.map +7 -0
- package/dist/modules/workflows/components/run/RunGantt.js +130 -0
- package/dist/modules/workflows/components/run/RunGantt.js.map +7 -0
- package/dist/modules/workflows/components/run/RunStatusBadge.js +82 -0
- package/dist/modules/workflows/components/run/RunStatusBadge.js.map +7 -0
- package/dist/modules/workflows/components/run/RunStepInspector.js +116 -0
- package/dist/modules/workflows/components/run/RunStepInspector.js.map +7 -0
- package/dist/modules/workflows/components/run/WouldDoReport.js +52 -0
- package/dist/modules/workflows/components/run/WouldDoReport.js.map +7 -0
- package/dist/modules/workflows/components/run/useAgentLabels.js +28 -0
- package/dist/modules/workflows/components/run/useAgentLabels.js.map +7 -0
- package/dist/modules/workflows/components/run/useLastRunOverlay.js +89 -0
- package/dist/modules/workflows/components/run/useLastRunOverlay.js.map +7 -0
- package/dist/modules/workflows/components/run/useLiveAgentActions.js +41 -0
- package/dist/modules/workflows/components/run/useLiveAgentActions.js.map +7 -0
- package/dist/modules/workflows/components/run/useLiveRunUpdates.js +57 -0
- package/dist/modules/workflows/components/run/useLiveRunUpdates.js.map +7 -0
- package/dist/modules/workflows/components/settings/CommandSettingsPageClient.js +156 -0
- package/dist/modules/workflows/components/settings/CommandSettingsPageClient.js.map +7 -0
- package/dist/modules/workflows/components/work-inbox/EntityContextPanel.js +72 -0
- package/dist/modules/workflows/components/work-inbox/EntityContextPanel.js.map +7 -0
- package/dist/modules/workflows/components/work-inbox/ReassignTaskDialog.js +156 -0
- package/dist/modules/workflows/components/work-inbox/ReassignTaskDialog.js.map +7 -0
- package/dist/modules/workflows/components/work-inbox/StatusChip.js +46 -0
- package/dist/modules/workflows/components/work-inbox/StatusChip.js.map +7 -0
- package/dist/modules/workflows/components/work-inbox/TaskActionNotice.js +53 -0
- package/dist/modules/workflows/components/work-inbox/TaskActionNotice.js.map +7 -0
- package/dist/modules/workflows/components/work-inbox/TaskFormFields.js +261 -0
- package/dist/modules/workflows/components/work-inbox/TaskFormFields.js.map +7 -0
- package/dist/modules/workflows/components/work-inbox/WorkInboxEmptyState.js +27 -0
- package/dist/modules/workflows/components/work-inbox/WorkInboxEmptyState.js.map +7 -0
- package/dist/modules/workflows/data/activity-config-schemas.js +191 -0
- package/dist/modules/workflows/data/activity-config-schemas.js.map +7 -0
- package/dist/modules/workflows/data/activity-config-warnings.js +42 -0
- package/dist/modules/workflows/data/activity-config-warnings.js.map +7 -0
- package/dist/modules/workflows/data/branching-route-warnings.js +65 -0
- package/dist/modules/workflows/data/branching-route-warnings.js.map +7 -0
- package/dist/modules/workflows/data/entities.js +144 -4
- package/dist/modules/workflows/data/entities.js.map +2 -2
- package/dist/modules/workflows/data/metrics-validators.js +35 -0
- package/dist/modules/workflows/data/metrics-validators.js.map +7 -0
- package/dist/modules/workflows/data/task-primitives.js +14 -0
- package/dist/modules/workflows/data/task-primitives.js.map +7 -0
- package/dist/modules/workflows/data/validators.js +504 -61
- package/dist/modules/workflows/data/validators.js.map +3 -3
- package/dist/modules/workflows/di.js +16 -1
- package/dist/modules/workflows/di.js.map +2 -2
- package/dist/modules/workflows/events.js +66 -8
- package/dist/modules/workflows/events.js.map +2 -2
- package/dist/modules/workflows/examples/oze-residential-install-60-nodes.json +1545 -0
- package/dist/modules/workflows/examples/sales-pipeline-definition.json +35 -35
- package/dist/modules/workflows/examples/templates/lead-to-install.json +160 -0
- package/dist/modules/workflows/examples/templates/order-approval.json +230 -0
- package/dist/modules/workflows/examples/templates/task-escalation.json +110 -0
- package/dist/modules/workflows/examples/templates/webhook-integration.json +113 -0
- package/dist/modules/workflows/extension-points.js +1 -1
- package/dist/modules/workflows/extension-points.js.map +2 -2
- package/dist/modules/workflows/frontend/[orgSlug]/portal/tasks/[id]/page.js +144 -0
- package/dist/modules/workflows/frontend/[orgSlug]/portal/tasks/[id]/page.js.map +7 -0
- package/dist/modules/workflows/frontend/[orgSlug]/portal/tasks/[id]/page.meta.js +12 -0
- package/dist/modules/workflows/frontend/[orgSlug]/portal/tasks/[id]/page.meta.js.map +7 -0
- package/dist/modules/workflows/frontend/[orgSlug]/portal/tasks/page.js +114 -0
- package/dist/modules/workflows/frontend/[orgSlug]/portal/tasks/page.js.map +7 -0
- package/dist/modules/workflows/frontend/[orgSlug]/portal/tasks/page.meta.js +18 -0
- package/dist/modules/workflows/frontend/[orgSlug]/portal/tasks/page.meta.js.map +7 -0
- package/dist/modules/workflows/lib/activity-executor.js +505 -137
- package/dist/modules/workflows/lib/activity-executor.js.map +3 -3
- package/dist/modules/workflows/lib/activity-queue-types.js +3 -1
- package/dist/modules/workflows/lib/activity-queue-types.js.map +2 -2
- package/dist/modules/workflows/lib/activity-registry-bootstrap.js +2 -0
- package/dist/modules/workflows/lib/activity-registry-bootstrap.js.map +7 -0
- package/dist/modules/workflows/lib/activity-registry.js +27 -0
- package/dist/modules/workflows/lib/activity-registry.js.map +7 -0
- package/dist/modules/workflows/lib/activity-types.js +291 -0
- package/dist/modules/workflows/lib/activity-types.js.map +7 -0
- package/dist/modules/workflows/lib/activity-worker-handler.js +456 -32
- package/dist/modules/workflows/lib/activity-worker-handler.js.map +3 -3
- package/dist/modules/workflows/lib/advanced-config.js +31 -0
- package/dist/modules/workflows/lib/advanced-config.js.map +7 -0
- package/dist/modules/workflows/lib/agent-disposition-task.js +198 -0
- package/dist/modules/workflows/lib/agent-disposition-task.js.map +7 -0
- package/dist/modules/workflows/lib/agent-outcome-paths.js +82 -0
- package/dist/modules/workflows/lib/agent-outcome-paths.js.map +7 -0
- package/dist/modules/workflows/lib/agent-result-mapping.js +54 -0
- package/dist/modules/workflows/lib/agent-result-mapping.js.map +7 -0
- package/dist/modules/workflows/lib/agent-review-inspector.js +61 -0
- package/dist/modules/workflows/lib/agent-review-inspector.js.map +7 -0
- package/dist/modules/workflows/lib/agent-review.js +66 -0
- package/dist/modules/workflows/lib/agent-review.js.map +7 -0
- package/dist/modules/workflows/lib/ai-authoring.js +155 -0
- package/dist/modules/workflows/lib/ai-authoring.js.map +7 -0
- package/dist/modules/workflows/lib/ai-draft-runner.js +43 -0
- package/dist/modules/workflows/lib/ai-draft-runner.js.map +7 -0
- package/dist/modules/workflows/lib/annotation-events.js +10 -0
- package/dist/modules/workflows/lib/annotation-events.js.map +7 -0
- package/dist/modules/workflows/lib/branching-routes.js +182 -0
- package/dist/modules/workflows/lib/branching-routes.js.map +7 -0
- package/dist/modules/workflows/lib/breach-routing.js +50 -0
- package/dist/modules/workflows/lib/breach-routing.js.map +7 -0
- package/dist/modules/workflows/lib/bulk-replay.js +101 -0
- package/dist/modules/workflows/lib/bulk-replay.js.map +7 -0
- package/dist/modules/workflows/lib/business-rule-usage.js +91 -0
- package/dist/modules/workflows/lib/business-rule-usage.js.map +7 -0
- package/dist/modules/workflows/lib/caller-graph.js +53 -0
- package/dist/modules/workflows/lib/caller-graph.js.map +7 -0
- package/dist/modules/workflows/lib/code-view-apply.js +86 -0
- package/dist/modules/workflows/lib/code-view-apply.js.map +7 -0
- package/dist/modules/workflows/lib/collect-validation-issues.js +164 -0
- package/dist/modules/workflows/lib/collect-validation-issues.js.map +7 -0
- package/dist/modules/workflows/lib/compensation-ghosts.js +72 -0
- package/dist/modules/workflows/lib/compensation-ghosts.js.map +7 -0
- package/dist/modules/workflows/lib/component-guard.js +7 -0
- package/dist/modules/workflows/lib/component-guard.js.map +7 -0
- package/dist/modules/workflows/lib/condition-handler.js +443 -0
- package/dist/modules/workflows/lib/condition-handler.js.map +7 -0
- package/dist/modules/workflows/lib/context-ledger.js +448 -0
- package/dist/modules/workflows/lib/context-ledger.js.map +7 -0
- package/dist/modules/workflows/lib/crud-form-dialogs-flag.js +8 -0
- package/dist/modules/workflows/lib/crud-form-dialogs-flag.js.map +7 -0
- package/dist/modules/workflows/lib/data-edge-mapping.js +63 -0
- package/dist/modules/workflows/lib/data-edge-mapping.js.map +7 -0
- package/dist/modules/workflows/lib/definition-context-schema.js +56 -0
- package/dist/modules/workflows/lib/definition-context-schema.js.map +7 -0
- package/dist/modules/workflows/lib/definition-edit-safety.js +123 -0
- package/dist/modules/workflows/lib/definition-edit-safety.js.map +7 -0
- package/dist/modules/workflows/lib/definition-error-body.js +33 -0
- package/dist/modules/workflows/lib/definition-error-body.js.map +7 -0
- package/dist/modules/workflows/lib/definition-evaluation.js +79 -0
- package/dist/modules/workflows/lib/definition-evaluation.js.map +7 -0
- package/dist/modules/workflows/lib/definition-grant.js +90 -0
- package/dist/modules/workflows/lib/definition-grant.js.map +7 -0
- package/dist/modules/workflows/lib/definition-json-locations.js +139 -0
- package/dist/modules/workflows/lib/definition-json-locations.js.map +7 -0
- package/dist/modules/workflows/lib/definition-payload.js +31 -0
- package/dist/modules/workflows/lib/definition-payload.js.map +7 -0
- package/dist/modules/workflows/lib/draft-restore.js +49 -0
- package/dist/modules/workflows/lib/draft-restore.js.map +7 -0
- package/dist/modules/workflows/lib/dry-run.js +106 -0
- package/dist/modules/workflows/lib/dry-run.js.map +7 -0
- package/dist/modules/workflows/lib/duration.js +66 -17
- package/dist/modules/workflows/lib/duration.js.map +2 -2
- package/dist/modules/workflows/lib/edge-reattachment.js +79 -0
- package/dist/modules/workflows/lib/edge-reattachment.js.map +7 -0
- package/dist/modules/workflows/lib/edgeFormTransforms.js +7 -11
- package/dist/modules/workflows/lib/edgeFormTransforms.js.map +2 -2
- package/dist/modules/workflows/lib/editor-annotations.js +195 -0
- package/dist/modules/workflows/lib/editor-annotations.js.map +7 -0
- package/dist/modules/workflows/lib/editor-commands.js +103 -0
- package/dist/modules/workflows/lib/editor-commands.js.map +7 -0
- package/dist/modules/workflows/lib/editor-history.js +51 -0
- package/dist/modules/workflows/lib/editor-history.js.map +7 -0
- package/dist/modules/workflows/lib/endpoint-catalog.js +181 -0
- package/dist/modules/workflows/lib/endpoint-catalog.js.map +7 -0
- package/dist/modules/workflows/lib/endpoint-path.js +90 -0
- package/dist/modules/workflows/lib/endpoint-path.js.map +7 -0
- package/dist/modules/workflows/lib/engine-version.js +42 -0
- package/dist/modules/workflows/lib/engine-version.js.map +7 -0
- package/dist/modules/workflows/lib/error-handler.js +132 -0
- package/dist/modules/workflows/lib/error-handler.js.map +7 -0
- package/dist/modules/workflows/lib/error-routing.js +104 -0
- package/dist/modules/workflows/lib/error-routing.js.map +7 -0
- package/dist/modules/workflows/lib/event-logger.js +23 -0
- package/dist/modules/workflows/lib/event-logger.js.map +2 -2
- package/dist/modules/workflows/lib/expression-refs.js +168 -0
- package/dist/modules/workflows/lib/expression-refs.js.map +7 -0
- package/dist/modules/workflows/lib/failure-grouping.js +62 -0
- package/dist/modules/workflows/lib/failure-grouping.js.map +7 -0
- package/dist/modules/workflows/lib/find-definition.js +1 -0
- package/dist/modules/workflows/lib/find-definition.js.map +2 -2
- package/dist/modules/workflows/lib/flow-logic-warnings.js +338 -0
- package/dist/modules/workflows/lib/flow-logic-warnings.js.map +7 -0
- package/dist/modules/workflows/lib/graph-utils.js +328 -77
- package/dist/modules/workflows/lib/graph-utils.js.map +2 -2
- package/dist/modules/workflows/lib/icon-search.js +32 -0
- package/dist/modules/workflows/lib/icon-search.js.map +7 -0
- package/dist/modules/workflows/lib/insert-at-cursor.js +26 -0
- package/dist/modules/workflows/lib/insert-at-cursor.js.map +7 -0
- package/dist/modules/workflows/lib/instance-date-filter.js +28 -0
- package/dist/modules/workflows/lib/instance-date-filter.js.map +7 -0
- package/dist/modules/workflows/lib/interpolation-pipeline.js +271 -0
- package/dist/modules/workflows/lib/interpolation-pipeline.js.map +7 -0
- package/dist/modules/workflows/lib/ledger-drag.js +28 -0
- package/dist/modules/workflows/lib/ledger-drag.js.map +7 -0
- package/dist/modules/workflows/lib/ledger-entry-display.js +66 -0
- package/dist/modules/workflows/lib/ledger-entry-display.js.map +7 -0
- package/dist/modules/workflows/lib/ledger-schema-flatten.js +82 -0
- package/dist/modules/workflows/lib/ledger-schema-flatten.js.map +7 -0
- package/dist/modules/workflows/lib/metrics/definition-metrics.js +110 -0
- package/dist/modules/workflows/lib/metrics/definition-metrics.js.map +7 -0
- package/dist/modules/workflows/lib/metrics/metrics-display.js +26 -0
- package/dist/modules/workflows/lib/metrics/metrics-display.js.map +7 -0
- package/dist/modules/workflows/lib/metrics/rollup-schedule.js +7 -0
- package/dist/modules/workflows/lib/metrics/rollup-schedule.js.map +7 -0
- package/dist/modules/workflows/lib/metrics/rollup-service.js +160 -0
- package/dist/modules/workflows/lib/metrics/rollup-service.js.map +7 -0
- package/dist/modules/workflows/lib/node-config-summary.js +128 -0
- package/dist/modules/workflows/lib/node-config-summary.js.map +7 -0
- package/dist/modules/workflows/lib/node-geometry.js +53 -0
- package/dist/modules/workflows/lib/node-geometry.js.map +7 -0
- package/dist/modules/workflows/lib/node-nudge.js +44 -0
- package/dist/modules/workflows/lib/node-nudge.js.map +7 -0
- package/dist/modules/workflows/lib/node-outcome-rows.js +88 -0
- package/dist/modules/workflows/lib/node-outcome-rows.js.map +7 -0
- package/dist/modules/workflows/lib/node-placement.js +55 -0
- package/dist/modules/workflows/lib/node-placement.js.map +7 -0
- package/dist/modules/workflows/lib/node-type-icons.js +54 -13
- package/dist/modules/workflows/lib/node-type-icons.js.map +2 -2
- package/dist/modules/workflows/lib/nodeFormTransforms.js +329 -19
- package/dist/modules/workflows/lib/nodeFormTransforms.js.map +2 -2
- package/dist/modules/workflows/lib/outcome-routing.js +158 -0
- package/dist/modules/workflows/lib/outcome-routing.js.map +7 -0
- package/dist/modules/workflows/lib/owned-definition.js +107 -0
- package/dist/modules/workflows/lib/owned-definition.js.map +7 -0
- package/dist/modules/workflows/lib/owned-node-changes.js +10 -0
- package/dist/modules/workflows/lib/owned-node-changes.js.map +7 -0
- package/dist/modules/workflows/lib/palette-drag.js +36 -0
- package/dist/modules/workflows/lib/palette-drag.js.map +7 -0
- package/dist/modules/workflows/lib/palette-drop.js +45 -0
- package/dist/modules/workflows/lib/palette-drop.js.map +7 -0
- package/dist/modules/workflows/lib/parallel-handler.js +57 -2
- package/dist/modules/workflows/lib/parallel-handler.js.map +2 -2
- package/dist/modules/workflows/lib/port-contract.js +54 -0
- package/dist/modules/workflows/lib/port-contract.js.map +7 -0
- package/dist/modules/workflows/lib/portal-task-access.js +89 -0
- package/dist/modules/workflows/lib/portal-task-access.js.map +7 -0
- package/dist/modules/workflows/lib/rerun-from-step.js +65 -0
- package/dist/modules/workflows/lib/rerun-from-step.js.map +7 -0
- package/dist/modules/workflows/lib/route-chip-events.js +23 -0
- package/dist/modules/workflows/lib/route-chip-events.js.map +7 -0
- package/dist/modules/workflows/lib/route-chips.js +91 -0
- package/dist/modules/workflows/lib/route-chips.js.map +7 -0
- package/dist/modules/workflows/lib/route-edge.js +17 -0
- package/dist/modules/workflows/lib/route-edge.js.map +7 -0
- package/dist/modules/workflows/lib/route-kinds.js +67 -0
- package/dist/modules/workflows/lib/route-kinds.js.map +7 -0
- package/dist/modules/workflows/lib/route-priority.js +104 -0
- package/dist/modules/workflows/lib/route-priority.js.map +7 -0
- package/dist/modules/workflows/lib/run-duration.js +44 -0
- package/dist/modules/workflows/lib/run-duration.js.map +7 -0
- package/dist/modules/workflows/lib/run-event-tone.js +25 -0
- package/dist/modules/workflows/lib/run-event-tone.js.map +7 -0
- package/dist/modules/workflows/lib/run-execution.js +305 -0
- package/dist/modules/workflows/lib/run-execution.js.map +7 -0
- package/dist/modules/workflows/lib/run-gantt.js +180 -0
- package/dist/modules/workflows/lib/run-gantt.js.map +7 -0
- package/dist/modules/workflows/lib/run-outcome-evidence.js +33 -0
- package/dist/modules/workflows/lib/run-outcome-evidence.js.map +7 -0
- package/dist/modules/workflows/lib/run-outcome.js +48 -0
- package/dist/modules/workflows/lib/run-outcome.js.map +7 -0
- package/dist/modules/workflows/lib/sample-resolver.js +71 -0
- package/dist/modules/workflows/lib/sample-resolver.js.map +7 -0
- package/dist/modules/workflows/lib/seeds.js +1 -1
- package/dist/modules/workflows/lib/seeds.js.map +2 -2
- package/dist/modules/workflows/lib/server-output-contract.js +65 -0
- package/dist/modules/workflows/lib/server-output-contract.js.map +7 -0
- package/dist/modules/workflows/lib/set-variable.js +47 -0
- package/dist/modules/workflows/lib/set-variable.js.map +7 -0
- package/dist/modules/workflows/lib/signal-handler.js +63 -4
- package/dist/modules/workflows/lib/signal-handler.js.map +2 -2
- package/dist/modules/workflows/lib/start-fixtures.js +42 -0
- package/dist/modules/workflows/lib/start-fixtures.js.map +7 -0
- package/dist/modules/workflows/lib/status-colors.js +153 -22
- package/dist/modules/workflows/lib/status-colors.js.map +2 -2
- package/dist/modules/workflows/lib/step-handler.js +529 -53
- package/dist/modules/workflows/lib/step-handler.js.map +3 -3
- package/dist/modules/workflows/lib/step-presentation.js +147 -0
- package/dist/modules/workflows/lib/step-presentation.js.map +7 -0
- package/dist/modules/workflows/lib/step-through.js +26 -0
- package/dist/modules/workflows/lib/step-through.js.map +7 -0
- package/dist/modules/workflows/lib/step-type-conversion.js +167 -0
- package/dist/modules/workflows/lib/step-type-conversion.js.map +7 -0
- package/dist/modules/workflows/lib/subgraph-clipboard.js +114 -0
- package/dist/modules/workflows/lib/subgraph-clipboard.js.map +7 -0
- package/dist/modules/workflows/lib/task-decision-context.js +38 -0
- package/dist/modules/workflows/lib/task-decision-context.js.map +7 -0
- package/dist/modules/workflows/lib/task-decisions.js +49 -0
- package/dist/modules/workflows/lib/task-decisions.js.map +7 -0
- package/dist/modules/workflows/lib/task-entity-access.js +62 -0
- package/dist/modules/workflows/lib/task-entity-access.js.map +7 -0
- package/dist/modules/workflows/lib/task-entity-aliases.js +39 -0
- package/dist/modules/workflows/lib/task-entity-aliases.js.map +7 -0
- package/dist/modules/workflows/lib/task-entity-types.js +36 -0
- package/dist/modules/workflows/lib/task-entity-types.js.map +7 -0
- package/dist/modules/workflows/lib/task-form-registry.js +33 -0
- package/dist/modules/workflows/lib/task-form-registry.js.map +7 -0
- package/dist/modules/workflows/lib/task-form-schema.js +75 -0
- package/dist/modules/workflows/lib/task-form-schema.js.map +7 -0
- package/dist/modules/workflows/lib/task-handler.js +251 -32
- package/dist/modules/workflows/lib/task-handler.js.map +2 -2
- package/dist/modules/workflows/lib/task-inspector-config.js +182 -0
- package/dist/modules/workflows/lib/task-inspector-config.js.map +7 -0
- package/dist/modules/workflows/lib/task-list-query.js +19 -0
- package/dist/modules/workflows/lib/task-list-query.js.map +7 -0
- package/dist/modules/workflows/lib/task-notifications.js +119 -0
- package/dist/modules/workflows/lib/task-notifications.js.map +7 -0
- package/dist/modules/workflows/lib/task-permission-settings.js +43 -0
- package/dist/modules/workflows/lib/task-permission-settings.js.map +7 -0
- package/dist/modules/workflows/lib/task-presets.js +35 -0
- package/dist/modules/workflows/lib/task-presets.js.map +7 -0
- package/dist/modules/workflows/lib/task-quick-action.js +45 -0
- package/dist/modules/workflows/lib/task-quick-action.js.map +7 -0
- package/dist/modules/workflows/lib/task-resolution.js +120 -0
- package/dist/modules/workflows/lib/task-resolution.js.map +7 -0
- package/dist/modules/workflows/lib/task-sla.js +357 -0
- package/dist/modules/workflows/lib/task-sla.js.map +7 -0
- package/dist/modules/workflows/lib/task-visibility-request.js +266 -0
- package/dist/modules/workflows/lib/task-visibility-request.js.map +7 -0
- package/dist/modules/workflows/lib/task-visibility.js +194 -0
- package/dist/modules/workflows/lib/task-visibility.js.map +7 -0
- package/dist/modules/workflows/lib/timer-handler.js +3 -1
- package/dist/modules/workflows/lib/timer-handler.js.map +2 -2
- package/dist/modules/workflows/lib/transition-handler.js +148 -20
- package/dist/modules/workflows/lib/transition-handler.js.map +2 -2
- package/dist/modules/workflows/lib/trigger-node.js +121 -0
- package/dist/modules/workflows/lib/trigger-node.js.map +7 -0
- package/dist/modules/workflows/lib/visual-editor-navigation.js +14 -0
- package/dist/modules/workflows/lib/visual-editor-navigation.js.map +7 -0
- package/dist/modules/workflows/lib/work-inbox/empty-state.js +52 -0
- package/dist/modules/workflows/lib/work-inbox/empty-state.js.map +7 -0
- package/dist/modules/workflows/lib/work-inbox/entity-links.js +63 -0
- package/dist/modules/workflows/lib/work-inbox/entity-links.js.map +7 -0
- package/dist/modules/workflows/lib/work-inbox/navigation.js +11 -0
- package/dist/modules/workflows/lib/work-inbox/navigation.js.map +7 -0
- package/dist/modules/workflows/lib/work-inbox/presentation.js +90 -0
- package/dist/modules/workflows/lib/work-inbox/presentation.js.map +7 -0
- package/dist/modules/workflows/lib/work-inbox/provider.js +118 -0
- package/dist/modules/workflows/lib/work-inbox/provider.js.map +7 -0
- package/dist/modules/workflows/lib/work-inbox/query.js +70 -0
- package/dist/modules/workflows/lib/work-inbox/query.js.map +7 -0
- package/dist/modules/workflows/lib/work-inbox/service.js +56 -0
- package/dist/modules/workflows/lib/work-inbox/service.js.map +7 -0
- package/dist/modules/workflows/lib/work-inbox/user-task-source.js +149 -0
- package/dist/modules/workflows/lib/work-inbox/user-task-source.js.map +7 -0
- package/dist/modules/workflows/lib/workflow-command-enablement.js +57 -0
- package/dist/modules/workflows/lib/workflow-command-enablement.js.map +7 -0
- package/dist/modules/workflows/lib/workflow-command-settings.js +54 -0
- package/dist/modules/workflows/lib/workflow-command-settings.js.map +7 -0
- package/dist/modules/workflows/lib/workflow-executor.js +658 -6
- package/dist/modules/workflows/lib/workflow-executor.js.map +2 -2
- package/dist/modules/workflows/lib/workflow-function-registry.js +40 -0
- package/dist/modules/workflows/lib/workflow-function-registry.js.map +7 -0
- package/dist/modules/workflows/lib/workflow-safe-commands.js +11 -1
- package/dist/modules/workflows/lib/workflow-safe-commands.js.map +2 -2
- package/dist/modules/workflows/lib/workflow-templates.js +69 -0
- package/dist/modules/workflows/lib/workflow-templates.js.map +7 -0
- package/dist/modules/workflows/migrations/Migration20260626214855_workflows.js +15 -0
- package/dist/modules/workflows/migrations/Migration20260626214855_workflows.js.map +7 -0
- package/dist/modules/workflows/migrations/Migration20260626222424_workflows.js +17 -0
- package/dist/modules/workflows/migrations/Migration20260626222424_workflows.js.map +7 -0
- package/dist/modules/workflows/migrations/Migration20260727074335_workflows.js +15 -0
- package/dist/modules/workflows/migrations/Migration20260727074335_workflows.js.map +7 -0
- package/dist/modules/workflows/migrations/Migration20260728104038_workflows.js +13 -0
- package/dist/modules/workflows/migrations/Migration20260728104038_workflows.js.map +7 -0
- package/dist/modules/workflows/migrations/Migration20260728163001_workflows.js +15 -0
- package/dist/modules/workflows/migrations/Migration20260728163001_workflows.js.map +7 -0
- package/dist/modules/workflows/migrations/Migration20260729061736_workflows.js +13 -0
- package/dist/modules/workflows/migrations/Migration20260729061736_workflows.js.map +7 -0
- package/dist/modules/workflows/migrations/Migration20260729192652_workflows.js +12 -0
- package/dist/modules/workflows/migrations/Migration20260729192652_workflows.js.map +7 -0
- package/dist/modules/workflows/migrations/Migration20260730085000_workflows.js +13 -0
- package/dist/modules/workflows/migrations/Migration20260730085000_workflows.js.map +7 -0
- package/dist/modules/workflows/migrations/Migration20260730092706_workflows.js +13 -0
- package/dist/modules/workflows/migrations/Migration20260730092706_workflows.js.map +7 -0
- package/dist/modules/workflows/notifications.js +38 -10
- package/dist/modules/workflows/notifications.js.map +2 -2
- package/dist/modules/workflows/setup.js +81 -1
- package/dist/modules/workflows/setup.js.map +2 -2
- package/dist/modules/workflows/subscribers/task-assigned-notification.js +19 -27
- package/dist/modules/workflows/subscribers/task-assigned-notification.js.map +2 -2
- package/dist/modules/workflows/subscribers/task-deadline-breached-notification.js +24 -0
- package/dist/modules/workflows/subscribers/task-deadline-breached-notification.js.map +7 -0
- package/dist/modules/workflows/subscribers/task-reminder-notification.js +24 -0
- package/dist/modules/workflows/subscribers/task-reminder-notification.js.map +7 -0
- package/dist/modules/workflows/widgets/injection/pending-work/widget.client.js +73 -0
- package/dist/modules/workflows/widgets/injection/pending-work/widget.client.js.map +7 -0
- package/dist/modules/workflows/widgets/injection/pending-work/widget.js +17 -0
- package/dist/modules/workflows/widgets/injection/pending-work/widget.js.map +7 -0
- package/dist/modules/workflows/widgets/injection-table.js +26 -0
- package/dist/modules/workflows/widgets/injection-table.js.map +2 -2
- package/dist/modules/workflows/workers/workflow-activities.worker.js +61 -39
- package/dist/modules/workflows/workers/workflow-activities.worker.js.map +2 -2
- package/dist/modules/workflows/workers/workflow-definition-metric-rollup.worker.js +40 -0
- package/dist/modules/workflows/workers/workflow-definition-metric-rollup.worker.js.map +7 -0
- package/dist/modules/workflows/workers/workflow-instance-bulk-replay.js +43 -0
- package/dist/modules/workflows/workers/workflow-instance-bulk-replay.js.map +7 -0
- package/dist/modules/workflows/workers/workflow-invoke-agent.worker.js +30 -0
- package/dist/modules/workflows/workers/workflow-invoke-agent.worker.js.map +7 -0
- package/generated/entities/action_log/index.ts +1 -0
- package/generated/entities/user/index.ts +1 -0
- package/generated/entities/user_task/index.ts +7 -0
- package/generated/entities/workflow_definition/index.ts +3 -0
- package/generated/entities/workflow_definition_draft/index.ts +11 -0
- package/generated/entities/workflow_definition_metric_rollup/index.ts +10 -0
- package/generated/entities/workflow_instance/index.ts +2 -0
- package/generated/entities.ids.generated.ts +4 -1
- package/generated/entity-fields-registry.ts +39 -0
- package/jest.setup.ts +13 -0
- package/package.json +8 -7
- package/src/bootstrap.ts +13 -3
- package/src/helpers/integration/workflowsFixtures.ts +765 -5
- package/src/helpers/integration/workflowsUi.ts +318 -0
- package/src/modules/attachments/lib/createFromBuffer.ts +120 -0
- package/src/modules/audit_logs/data/entities.ts +12 -0
- package/src/modules/audit_logs/data/validators.ts +1 -0
- package/src/modules/audit_logs/lib/projections.ts +5 -1
- package/src/modules/audit_logs/migrations/.snapshot-open-mercato.json +26 -0
- package/src/modules/audit_logs/migrations/Migration20260625120000.ts +15 -0
- package/src/modules/audit_logs/services/actionLogService.ts +3 -0
- package/src/modules/auth/data/entities.ts +21 -0
- package/src/modules/auth/i18n/de.json +31 -0
- package/src/modules/auth/i18n/en.json +31 -0
- package/src/modules/auth/i18n/es.json +31 -0
- package/src/modules/auth/i18n/ko.json +31 -0
- package/src/modules/auth/i18n/pl.json +31 -0
- package/src/modules/auth/lib/executionPrincipal.ts +235 -0
- package/src/modules/auth/migrations/.snapshot-open-mercato.json +16 -0
- package/src/modules/auth/migrations/Migration20260625050000.ts +21 -0
- package/src/modules/business_rules/components/InlineRuleEditor.tsx +231 -0
- package/src/modules/business_rules/data/validators.ts +6 -0
- package/src/modules/business_rules/i18n/de.json +4 -0
- package/src/modules/business_rules/i18n/en.json +4 -0
- package/src/modules/business_rules/i18n/es.json +4 -0
- package/src/modules/business_rules/i18n/ko.json +4 -0
- package/src/modules/business_rules/i18n/pl.json +4 -0
- package/src/modules/business_rules/lib/rule-engine.ts +15 -1
- package/src/modules/catalog/commands/products.ts +12 -0
- package/src/modules/catalog/i18n/de.json +1 -0
- package/src/modules/catalog/i18n/en.json +1 -0
- package/src/modules/catalog/i18n/es.json +1 -0
- package/src/modules/catalog/i18n/ko.json +1 -0
- package/src/modules/catalog/i18n/pl.json +1 -0
- package/src/modules/catalog/workflows.ts +35 -0
- package/src/modules/customer_accounts/AGENTS.md +11 -0
- package/src/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.tsx +2 -0
- package/src/modules/customer_accounts/cli.ts +96 -0
- package/src/modules/customer_accounts/i18n/de.json +5 -0
- package/src/modules/customer_accounts/i18n/en.json +5 -0
- package/src/modules/customer_accounts/i18n/es.json +5 -0
- package/src/modules/customer_accounts/i18n/ko.json +5 -0
- package/src/modules/customer_accounts/i18n/pl.json +5 -0
- package/src/modules/customer_accounts/lib/customerRoleAclSync.ts +92 -0
- package/src/modules/customer_accounts/setup.ts +15 -3
- package/src/modules/customers/commands/deals.ts +5 -0
- package/src/modules/customers/components/detail/ChangelogEntryRow.tsx +4 -2
- package/src/modules/customers/components/detail/utils.ts +2 -0
- package/src/modules/customers/events.ts +21 -3
- package/src/modules/customers/i18n/de.json +4 -0
- package/src/modules/customers/i18n/en.json +4 -0
- package/src/modules/customers/i18n/es.json +4 -0
- package/src/modules/customers/i18n/ko.json +4 -0
- package/src/modules/customers/i18n/pl.json +4 -0
- package/src/modules/customers/lib/link-workflow-task-handler.ts +136 -0
- package/src/modules/customers/lib/todoCompatibility.ts +4 -1
- package/src/modules/customers/lib/workflowTaskLink.ts +23 -0
- package/src/modules/customers/subscribers/link-workflow-task.ts +19 -0
- package/src/modules/customers/workflows.ts +57 -0
- package/src/modules/entities/api/records.ts +18 -62
- package/src/modules/entities/lib/entityClassification.ts +102 -0
- package/src/modules/sales/commands/documents.ts +46 -0
- package/src/modules/sales/events.ts +20 -2
- package/src/modules/sales/i18n/de.json +2 -1
- package/src/modules/sales/i18n/en.json +2 -1
- package/src/modules/sales/i18n/es.json +2 -1
- package/src/modules/sales/i18n/ko.json +2 -1
- package/src/modules/sales/i18n/pl.json +2 -1
- package/src/modules/sales/workflows.ts +12 -1
- package/src/modules/seeds/README.md +86 -0
- package/src/modules/seeds/cli.ts +220 -0
- package/src/modules/seeds/examples/sample-seed.json +26 -0
- package/src/modules/seeds/index.ts +11 -0
- package/src/modules/workflows/AGENTS.md +851 -10
- package/src/modules/workflows/acl.ts +96 -0
- package/src/modules/workflows/ai-agents.ts +80 -0
- package/src/modules/workflows/ai-tools/authoring-pack.ts +798 -0
- package/src/modules/workflows/ai-tools/types.ts +117 -0
- package/src/modules/workflows/ai-tools.ts +20 -0
- package/src/modules/workflows/api/command-settings/route.ts +171 -0
- package/src/modules/workflows/api/commands/route.ts +109 -0
- package/src/modules/workflows/api/definitions/[id]/callers/route.ts +95 -0
- package/src/modules/workflows/api/definitions/[id]/context-schema/route.ts +187 -0
- package/src/modules/workflows/api/definitions/[id]/customize/route.ts +6 -1
- package/src/modules/workflows/api/definitions/[id]/draft/route.ts +395 -0
- package/src/modules/workflows/api/definitions/[id]/publish/route.ts +250 -0
- package/src/modules/workflows/api/definitions/[id]/reset-to-code/route.ts +5 -0
- package/src/modules/workflows/api/definitions/[id]/route.ts +107 -7
- package/src/modules/workflows/api/definitions/[id]/test-step/route.ts +288 -0
- package/src/modules/workflows/api/definitions/generate/route.ts +335 -0
- package/src/modules/workflows/api/definitions/route.ts +88 -113
- package/src/modules/workflows/api/definitions/serialize.ts +9 -0
- package/src/modules/workflows/api/endpoints/route.ts +88 -0
- package/src/modules/workflows/api/functions/route.ts +91 -0
- package/src/modules/workflows/api/grantable-features/route.ts +102 -0
- package/src/modules/workflows/api/instances/[id]/context/route.ts +214 -0
- package/src/modules/workflows/api/instances/[id]/rerun-step/route.ts +226 -0
- package/src/modules/workflows/api/instances/[id]/retry/route.ts +18 -2
- package/src/modules/workflows/api/instances/[id]/step-through/route.ts +195 -0
- package/src/modules/workflows/api/instances/[id]/steps/route.ts +160 -0
- package/src/modules/workflows/api/instances/[id]/would-do/route.ts +216 -0
- package/src/modules/workflows/api/instances/bulk-replay/route.ts +141 -0
- package/src/modules/workflows/api/instances/failure-queue/route.ts +197 -0
- package/src/modules/workflows/api/instances/route.ts +135 -3
- package/src/modules/workflows/api/metrics/definitions/route.ts +262 -0
- package/src/modules/workflows/api/openapi.ts +549 -2
- package/src/modules/workflows/api/portal/tasks/[id]/complete/route.ts +160 -0
- package/src/modules/workflows/api/portal/tasks/[id]/route.ts +124 -0
- package/src/modules/workflows/api/portal/tasks/route.ts +157 -0
- package/src/modules/workflows/api/rule-usage/route.ts +94 -0
- package/src/modules/workflows/api/task-settings/route.ts +138 -0
- package/src/modules/workflows/api/tasks/[id]/claim/route.ts +42 -17
- package/src/modules/workflows/api/tasks/[id]/complete/route.ts +47 -15
- package/src/modules/workflows/api/tasks/[id]/reassign/route.ts +197 -0
- package/src/modules/workflows/api/tasks/[id]/route.ts +57 -6
- package/src/modules/workflows/api/tasks/[id]/unclaim/route.ts +132 -0
- package/src/modules/workflows/api/tasks/route.ts +60 -5
- package/src/modules/workflows/api/tasks/serialize.ts +119 -0
- package/src/modules/workflows/api/templates/route.ts +87 -0
- package/src/modules/workflows/api/work-inbox/next/route.ts +170 -0
- package/src/modules/workflows/api/work-inbox/route.ts +153 -0
- package/src/modules/workflows/backend/config/workflows/page.meta.ts +33 -0
- package/src/modules/workflows/backend/config/workflows/page.tsx +12 -0
- package/src/modules/workflows/backend/definitions/[id]/page.tsx +19 -343
- package/src/modules/workflows/backend/definitions/create/page.meta.ts +1 -1
- package/src/modules/workflows/backend/definitions/create/page.tsx +19 -59
- package/src/modules/workflows/backend/definitions/page.tsx +156 -76
- package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +3129 -584
- package/src/modules/workflows/backend/instances/[id]/page.meta.ts +1 -1
- package/src/modules/workflows/backend/instances/[id]/page.tsx +711 -539
- package/src/modules/workflows/backend/instances/failure-queue/page.meta.ts +16 -0
- package/src/modules/workflows/backend/instances/failure-queue/page.tsx +311 -0
- package/src/modules/workflows/backend/instances/page.meta.ts +1 -1
- package/src/modules/workflows/backend/instances/page.tsx +218 -38
- package/src/modules/workflows/backend/tasks/[id]/page.tsx +639 -400
- package/src/modules/workflows/backend/tasks/page.meta.ts +7 -0
- package/src/modules/workflows/backend/tasks/page.tsx +20 -333
- package/src/modules/workflows/backend/work-inbox/page.meta.ts +15 -0
- package/src/modules/workflows/backend/work-inbox/page.tsx +506 -0
- package/src/modules/workflows/cli.ts +1 -1
- package/src/modules/workflows/commands/tasks.ts +146 -0
- package/src/modules/workflows/components/ActivitiesEditor.tsx +91 -114
- package/src/modules/workflows/components/ActivityEditDialog.tsx +244 -0
- package/src/modules/workflows/components/AgentSelector.tsx +200 -0
- package/src/modules/workflows/components/AnnotationEditDialog.tsx +157 -0
- package/src/modules/workflows/components/BusinessRuleUsagePanel.tsx +106 -0
- package/src/modules/workflows/components/BusinessRulesSelector.tsx +71 -17
- package/src/modules/workflows/components/ConfigJsonTextarea.tsx +111 -0
- package/src/modules/workflows/components/ContextSchemaEditor.tsx +165 -0
- package/src/modules/workflows/components/DefinitionErrorHandlerField.tsx +133 -0
- package/src/modules/workflows/components/DefinitionMetadataDrawer.tsx +456 -0
- package/src/modules/workflows/components/DefinitionMetricsCells.tsx +212 -0
- package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +196 -95
- package/src/modules/workflows/components/EdgeEditDialog.tsx +15 -16
- package/src/modules/workflows/components/EdgeEditDialogCrudForm.tsx +130 -80
- package/src/modules/workflows/components/InputDataPanel.tsx +177 -0
- package/src/modules/workflows/components/InspectorPanel.tsx +250 -0
- package/src/modules/workflows/components/NodeEditDialog.tsx +444 -38
- package/src/modules/workflows/components/NodeEditDialogCrudForm.tsx +848 -178
- package/src/modules/workflows/components/StepsEditor.tsx +9 -2
- package/src/modules/workflows/components/TemplateGalleryDialog.tsx +205 -0
- package/src/modules/workflows/components/TransitionsEditor.tsx +98 -89
- package/src/modules/workflows/components/TriggersDialog.tsx +83 -0
- package/src/modules/workflows/components/TriggersEditor.tsx +363 -0
- package/src/modules/workflows/components/WorkflowAiDraftDialog.tsx +383 -0
- package/src/modules/workflows/components/WorkflowCodeView.tsx +257 -0
- package/src/modules/workflows/components/WorkflowCommandPalette.tsx +82 -0
- package/src/modules/workflows/components/WorkflowCompensationGhostEdge.tsx +78 -0
- package/src/modules/workflows/components/WorkflowDataMappingEdge.tsx +46 -0
- package/src/modules/workflows/components/WorkflowGraph.tsx +54 -1
- package/src/modules/workflows/components/WorkflowGraphImpl.tsx +463 -37
- package/src/modules/workflows/components/WorkflowIconPicker.tsx +144 -0
- package/src/modules/workflows/components/WorkflowLegend.tsx +59 -33
- package/src/modules/workflows/components/WorkflowNodeCard.tsx +355 -28
- package/src/modules/workflows/components/WorkflowRouteChips.tsx +145 -0
- package/src/modules/workflows/components/WorkflowSelector.tsx +33 -14
- package/src/modules/workflows/components/WorkflowTransitionEdge.tsx +83 -10
- package/src/modules/workflows/components/WorkflowTransitionLabel.tsx +13 -2
- package/src/modules/workflows/components/fields/ActivityArrayEditor.tsx +120 -30
- package/src/modules/workflows/components/fields/ActivityConfigFields.tsx +675 -0
- package/src/modules/workflows/components/fields/ActivityTestPanel.tsx +250 -0
- package/src/modules/workflows/components/fields/AgentInvokeConfigField.tsx +580 -0
- package/src/modules/workflows/components/fields/AgentReviewOnBreachField.tsx +88 -0
- package/src/modules/workflows/components/fields/BranchingRoutesEditor.tsx +323 -0
- package/src/modules/workflows/components/fields/BusinessRuleConditionsEditor.tsx +36 -4
- package/src/modules/workflows/components/fields/CommandPicker.tsx +158 -0
- package/src/modules/workflows/components/fields/EndpointPicker.tsx +330 -0
- package/src/modules/workflows/components/fields/FormFieldArrayEditor.tsx +2 -2
- package/src/modules/workflows/components/fields/FunctionPicker.tsx +68 -0
- package/src/modules/workflows/components/fields/MappingArrayEditor.tsx +46 -12
- package/src/modules/workflows/components/fields/PortFieldArrayEditor.tsx +255 -0
- package/src/modules/workflows/components/fields/RolesMultiSelect.tsx +68 -0
- package/src/modules/workflows/components/fields/RouteOrderEditor.tsx +169 -0
- package/src/modules/workflows/components/fields/SchemaBuilderDialog.tsx +139 -0
- package/src/modules/workflows/components/fields/StartPreConditionsEditor.tsx +2 -2
- package/src/modules/workflows/components/fields/TaskAssignmentField.tsx +355 -0
- package/src/modules/workflows/components/fields/TaskDeadlineFields.tsx +172 -0
- package/src/modules/workflows/components/fields/TaskDecisionsField.tsx +294 -0
- package/src/modules/workflows/components/fields/TaskEntityBindingsField.tsx +156 -0
- package/src/modules/workflows/components/fields/TemplateTextControl.tsx +85 -0
- package/src/modules/workflows/components/fields/VariablePickerButton.tsx +177 -0
- package/src/modules/workflows/components/fields/ledgerDropTarget.ts +55 -0
- package/src/modules/workflows/components/fields/useActivityTypeOptions.ts +19 -0
- package/src/modules/workflows/components/formConfig.tsx +84 -10
- package/src/modules/workflows/components/mobile/MobileDefinitionDetail.tsx +5 -0
- package/src/modules/workflows/components/mobile/MobileMetadataSheet.tsx +10 -0
- package/src/modules/workflows/components/mobile/MobileTaskForm.tsx +55 -19
- package/src/modules/workflows/components/mobile/MobileVisualEditor.tsx +49 -40
- package/src/modules/workflows/components/nodes/AnnotationGroupNode.tsx +81 -0
- package/src/modules/workflows/components/nodes/AutomatedNode.tsx +34 -17
- package/src/modules/workflows/components/nodes/EndNode.tsx +15 -14
- package/src/modules/workflows/components/nodes/ErrorOutputHandle.tsx +32 -0
- package/src/modules/workflows/components/nodes/IfElseNode.tsx +64 -0
- package/src/modules/workflows/components/nodes/InvokeAgentNode.tsx +180 -0
- package/src/modules/workflows/components/nodes/NodeOutcomeRows.tsx +143 -0
- package/src/modules/workflows/components/nodes/ParallelForkNode.tsx +28 -15
- package/src/modules/workflows/components/nodes/ParallelJoinNode.tsx +28 -15
- package/src/modules/workflows/components/nodes/StartNode.tsx +78 -18
- package/src/modules/workflows/components/nodes/StickyNoteNode.tsx +65 -0
- package/src/modules/workflows/components/nodes/SubWorkflowNode.tsx +124 -23
- package/src/modules/workflows/components/nodes/SwitchNode.tsx +64 -0
- package/src/modules/workflows/components/nodes/TriggerCap.tsx +103 -0
- package/src/modules/workflows/components/nodes/TriggerNode.tsx +151 -0
- package/src/modules/workflows/components/nodes/UserTaskNode.tsx +67 -22
- package/src/modules/workflows/components/nodes/WaitForConditionNode.tsx +74 -0
- package/src/modules/workflows/components/nodes/WaitForSignalNode.tsx +31 -17
- package/src/modules/workflows/components/nodes/WaitForTimerNode.tsx +31 -16
- package/src/modules/workflows/components/nodes/index.ts +17 -0
- package/src/modules/workflows/components/portal/PortalTaskForm.tsx +280 -0
- package/src/modules/workflows/components/run/AgentActivityPanel.tsx +48 -0
- package/src/modules/workflows/components/run/RerunStepDialog.tsx +133 -0
- package/src/modules/workflows/components/run/RunEventBadge.tsx +37 -0
- package/src/modules/workflows/components/run/RunGantt.tsx +180 -0
- package/src/modules/workflows/components/run/RunStatusBadge.tsx +112 -0
- package/src/modules/workflows/components/run/RunStepInspector.tsx +176 -0
- package/src/modules/workflows/components/run/WouldDoReport.tsx +93 -0
- package/src/modules/workflows/components/run/useAgentLabels.ts +42 -0
- package/src/modules/workflows/components/run/useLastRunOverlay.ts +150 -0
- package/src/modules/workflows/components/run/useLiveAgentActions.ts +70 -0
- package/src/modules/workflows/components/run/useLiveRunUpdates.ts +89 -0
- package/src/modules/workflows/components/settings/CommandSettingsPageClient.tsx +228 -0
- package/src/modules/workflows/components/work-inbox/EntityContextPanel.tsx +179 -0
- package/src/modules/workflows/components/work-inbox/ReassignTaskDialog.tsx +203 -0
- package/src/modules/workflows/components/work-inbox/StatusChip.tsx +61 -0
- package/src/modules/workflows/components/work-inbox/TaskActionNotice.tsx +77 -0
- package/src/modules/workflows/components/work-inbox/TaskFormFields.tsx +322 -0
- package/src/modules/workflows/components/work-inbox/WorkInboxEmptyState.tsx +43 -0
- package/src/modules/workflows/data/activity-config-schemas.ts +249 -0
- package/src/modules/workflows/data/activity-config-warnings.ts +71 -0
- package/src/modules/workflows/data/branching-route-warnings.ts +129 -0
- package/src/modules/workflows/data/entities.ts +397 -4
- package/src/modules/workflows/data/metrics-validators.ts +51 -0
- package/src/modules/workflows/data/task-primitives.ts +36 -0
- package/src/modules/workflows/data/types.ts +81 -2
- package/src/modules/workflows/data/validators.ts +729 -60
- package/src/modules/workflows/di.ts +22 -0
- package/src/modules/workflows/events.ts +68 -8
- package/src/modules/workflows/examples/oze-residential-install-60-nodes.json +1545 -0
- package/src/modules/workflows/examples/sales-pipeline-definition.json +35 -35
- package/src/modules/workflows/examples/templates/lead-to-install.json +160 -0
- package/src/modules/workflows/examples/templates/order-approval.json +230 -0
- package/src/modules/workflows/examples/templates/task-escalation.json +110 -0
- package/src/modules/workflows/examples/templates/webhook-integration.json +113 -0
- package/src/modules/workflows/extension-points.ts +1 -1
- package/src/modules/workflows/frontend/[orgSlug]/portal/tasks/[id]/page.meta.ts +21 -0
- package/src/modules/workflows/frontend/[orgSlug]/portal/tasks/[id]/page.tsx +212 -0
- package/src/modules/workflows/frontend/[orgSlug]/portal/tasks/page.meta.ts +25 -0
- package/src/modules/workflows/frontend/[orgSlug]/portal/tasks/page.tsx +185 -0
- package/src/modules/workflows/i18n/de.json +1083 -24
- package/src/modules/workflows/i18n/en.json +1081 -22
- package/src/modules/workflows/i18n/es.json +1083 -24
- package/src/modules/workflows/i18n/ko.json +1072 -13
- package/src/modules/workflows/i18n/pl.json +1082 -23
- package/src/modules/workflows/lib/activity-executor.ts +855 -186
- package/src/modules/workflows/lib/activity-queue-types.ts +120 -1
- package/src/modules/workflows/lib/activity-registry-bootstrap.ts +7 -0
- package/src/modules/workflows/lib/activity-registry.ts +89 -0
- package/src/modules/workflows/lib/activity-types.ts +415 -0
- package/src/modules/workflows/lib/activity-worker-handler.ts +744 -35
- package/src/modules/workflows/lib/advanced-config.ts +41 -0
- package/src/modules/workflows/lib/agent-disposition-task.ts +343 -0
- package/src/modules/workflows/lib/agent-outcome-paths.ts +153 -0
- package/src/modules/workflows/lib/agent-result-mapping.ts +88 -0
- package/src/modules/workflows/lib/agent-review-inspector.ts +125 -0
- package/src/modules/workflows/lib/agent-review.ts +171 -0
- package/src/modules/workflows/lib/ai-authoring.ts +402 -0
- package/src/modules/workflows/lib/ai-draft-runner.ts +105 -0
- package/src/modules/workflows/lib/annotation-events.ts +15 -0
- package/src/modules/workflows/lib/branching-routes.ts +280 -0
- package/src/modules/workflows/lib/breach-routing.ts +142 -0
- package/src/modules/workflows/lib/bulk-replay.ts +185 -0
- package/src/modules/workflows/lib/business-rule-usage.ts +154 -0
- package/src/modules/workflows/lib/caller-graph.ts +104 -0
- package/src/modules/workflows/lib/code-view-apply.ts +178 -0
- package/src/modules/workflows/lib/collect-validation-issues.ts +239 -0
- package/src/modules/workflows/lib/compensation-ghosts.ts +134 -0
- package/src/modules/workflows/lib/component-guard.ts +13 -0
- package/src/modules/workflows/lib/condition-handler.ts +658 -0
- package/src/modules/workflows/lib/context-ledger.ts +814 -0
- package/src/modules/workflows/lib/crud-form-dialogs-flag.ts +5 -0
- package/src/modules/workflows/lib/data-edge-mapping.ts +112 -0
- package/src/modules/workflows/lib/definition-context-schema.ts +103 -0
- package/src/modules/workflows/lib/definition-edit-safety.ts +228 -0
- package/src/modules/workflows/lib/definition-error-body.ts +43 -0
- package/src/modules/workflows/lib/definition-evaluation.ts +156 -0
- package/src/modules/workflows/lib/definition-grant.ts +211 -0
- package/src/modules/workflows/lib/definition-json-locations.ts +226 -0
- package/src/modules/workflows/lib/definition-payload.ts +81 -0
- package/src/modules/workflows/lib/draft-restore.ts +72 -0
- package/src/modules/workflows/lib/dry-run.ts +203 -0
- package/src/modules/workflows/lib/duration.ts +138 -24
- package/src/modules/workflows/lib/edge-reattachment.ts +186 -0
- package/src/modules/workflows/lib/edgeFormTransforms.ts +15 -14
- package/src/modules/workflows/lib/editor-annotations.ts +301 -0
- package/src/modules/workflows/lib/editor-commands.ts +205 -0
- package/src/modules/workflows/lib/editor-history.ts +151 -0
- package/src/modules/workflows/lib/endpoint-catalog.ts +283 -0
- package/src/modules/workflows/lib/endpoint-path.ts +134 -0
- package/src/modules/workflows/lib/engine-version.ts +67 -0
- package/src/modules/workflows/lib/error-handler.ts +192 -0
- package/src/modules/workflows/lib/error-routing.ts +236 -0
- package/src/modules/workflows/lib/event-logger.ts +27 -0
- package/src/modules/workflows/lib/expression-refs.ts +291 -0
- package/src/modules/workflows/lib/failure-grouping.ts +134 -0
- package/src/modules/workflows/lib/find-definition.ts +4 -0
- package/src/modules/workflows/lib/flow-logic-warnings.ts +591 -0
- package/src/modules/workflows/lib/graph-utils.ts +620 -110
- package/src/modules/workflows/lib/icon-search.ts +65 -0
- package/src/modules/workflows/lib/insert-at-cursor.ts +53 -0
- package/src/modules/workflows/lib/instance-date-filter.ts +56 -0
- package/src/modules/workflows/lib/interpolation-pipeline.ts +351 -0
- package/src/modules/workflows/lib/ledger-drag.ts +61 -0
- package/src/modules/workflows/lib/ledger-entry-display.ts +89 -0
- package/src/modules/workflows/lib/ledger-schema-flatten.ts +137 -0
- package/src/modules/workflows/lib/metrics/definition-metrics.ts +268 -0
- package/src/modules/workflows/lib/metrics/metrics-display.ts +57 -0
- package/src/modules/workflows/lib/metrics/rollup-schedule.ts +26 -0
- package/src/modules/workflows/lib/metrics/rollup-service.ts +292 -0
- package/src/modules/workflows/lib/node-config-summary.ts +235 -0
- package/src/modules/workflows/lib/node-geometry.ts +168 -0
- package/src/modules/workflows/lib/node-nudge.ts +63 -0
- package/src/modules/workflows/lib/node-outcome-rows.ts +195 -0
- package/src/modules/workflows/lib/node-placement.ts +104 -0
- package/src/modules/workflows/lib/node-type-icons.ts +71 -10
- package/src/modules/workflows/lib/nodeFormTransforms.ts +519 -28
- package/src/modules/workflows/lib/outcome-routing.ts +346 -0
- package/src/modules/workflows/lib/owned-definition.ts +188 -0
- package/src/modules/workflows/lib/owned-node-changes.ts +29 -0
- package/src/modules/workflows/lib/palette-drag.ts +68 -0
- package/src/modules/workflows/lib/palette-drop.ts +114 -0
- package/src/modules/workflows/lib/parallel-handler.ts +79 -2
- package/src/modules/workflows/lib/port-contract.ts +107 -0
- package/src/modules/workflows/lib/portal-task-access.ts +220 -0
- package/src/modules/workflows/lib/rerun-from-step.ts +140 -0
- package/src/modules/workflows/lib/route-chip-events.ts +45 -0
- package/src/modules/workflows/lib/route-chips.ts +177 -0
- package/src/modules/workflows/lib/route-edge.ts +57 -0
- package/src/modules/workflows/lib/route-kinds.ts +166 -0
- package/src/modules/workflows/lib/route-priority.ts +197 -0
- package/src/modules/workflows/lib/run-duration.ts +70 -0
- package/src/modules/workflows/lib/run-event-tone.ts +46 -0
- package/src/modules/workflows/lib/run-execution.ts +598 -0
- package/src/modules/workflows/lib/run-gantt.ts +311 -0
- package/src/modules/workflows/lib/run-outcome-evidence.ts +60 -0
- package/src/modules/workflows/lib/run-outcome.ts +142 -0
- package/src/modules/workflows/lib/sample-resolver.ts +113 -0
- package/src/modules/workflows/lib/seeds.ts +3 -1
- package/src/modules/workflows/lib/server-output-contract.ts +120 -0
- package/src/modules/workflows/lib/set-variable.ts +94 -0
- package/src/modules/workflows/lib/signal-handler.ts +134 -4
- package/src/modules/workflows/lib/start-fixtures.ts +93 -0
- package/src/modules/workflows/lib/status-colors.ts +220 -21
- package/src/modules/workflows/lib/step-handler.ts +744 -68
- package/src/modules/workflows/lib/step-presentation.ts +295 -0
- package/src/modules/workflows/lib/step-through.ts +71 -0
- package/src/modules/workflows/lib/step-type-conversion.ts +263 -0
- package/src/modules/workflows/lib/subgraph-clipboard.ts +214 -0
- package/src/modules/workflows/lib/task-decision-context.ts +76 -0
- package/src/modules/workflows/lib/task-decisions.ts +117 -0
- package/src/modules/workflows/lib/task-entity-access.ts +181 -0
- package/src/modules/workflows/lib/task-entity-aliases.ts +77 -0
- package/src/modules/workflows/lib/task-entity-types.ts +66 -0
- package/src/modules/workflows/lib/task-form-registry.ts +123 -0
- package/src/modules/workflows/lib/task-form-schema.ts +129 -0
- package/src/modules/workflows/lib/task-handler.ts +492 -51
- package/src/modules/workflows/lib/task-inspector-config.ts +348 -0
- package/src/modules/workflows/lib/task-list-query.ts +46 -0
- package/src/modules/workflows/lib/task-notifications.ts +218 -0
- package/src/modules/workflows/lib/task-permission-settings.ts +117 -0
- package/src/modules/workflows/lib/task-presets.ts +95 -0
- package/src/modules/workflows/lib/task-quick-action.ts +111 -0
- package/src/modules/workflows/lib/task-resolution.ts +270 -0
- package/src/modules/workflows/lib/task-sla.ts +628 -0
- package/src/modules/workflows/lib/task-visibility-request.ts +668 -0
- package/src/modules/workflows/lib/task-visibility.ts +627 -0
- package/src/modules/workflows/lib/timer-handler.ts +8 -1
- package/src/modules/workflows/lib/transition-handler.ts +228 -20
- package/src/modules/workflows/lib/trigger-node.ts +272 -0
- package/src/modules/workflows/lib/visual-editor-navigation.ts +29 -0
- package/src/modules/workflows/lib/work-inbox/empty-state.ts +150 -0
- package/src/modules/workflows/lib/work-inbox/entity-links.ts +99 -0
- package/src/modules/workflows/lib/work-inbox/navigation.ts +28 -0
- package/src/modules/workflows/lib/work-inbox/presentation.ts +163 -0
- package/src/modules/workflows/lib/work-inbox/provider.ts +390 -0
- package/src/modules/workflows/lib/work-inbox/query.ts +103 -0
- package/src/modules/workflows/lib/work-inbox/service.ts +122 -0
- package/src/modules/workflows/lib/work-inbox/user-task-source.ts +240 -0
- package/src/modules/workflows/lib/workflow-command-enablement.ts +188 -0
- package/src/modules/workflows/lib/workflow-command-settings.ts +124 -0
- package/src/modules/workflows/lib/workflow-executor.ts +1036 -10
- package/src/modules/workflows/lib/workflow-function-registry.ts +44 -0
- package/src/modules/workflows/lib/workflow-safe-commands.ts +42 -1
- package/src/modules/workflows/lib/workflow-templates.ts +91 -0
- package/src/modules/workflows/migrations/.snapshot-open-mercato.json +2321 -698
- package/src/modules/workflows/migrations/Migration20260626214855_workflows.ts +15 -0
- package/src/modules/workflows/migrations/Migration20260626222424_workflows.ts +17 -0
- package/src/modules/workflows/migrations/Migration20260727074335_workflows.ts +15 -0
- package/src/modules/workflows/migrations/Migration20260728104038_workflows.ts +13 -0
- package/src/modules/workflows/migrations/Migration20260728163001_workflows.ts +36 -0
- package/src/modules/workflows/migrations/Migration20260729061736_workflows.ts +13 -0
- package/src/modules/workflows/migrations/Migration20260729192652_workflows.ts +11 -0
- package/src/modules/workflows/migrations/Migration20260730085000_workflows.ts +13 -0
- package/src/modules/workflows/migrations/Migration20260730092706_workflows.ts +13 -0
- package/src/modules/workflows/notifications.ts +40 -10
- package/src/modules/workflows/setup.ts +128 -0
- package/src/modules/workflows/subscribers/task-assigned-notification.ts +36 -45
- package/src/modules/workflows/subscribers/task-deadline-breached-notification.ts +31 -0
- package/src/modules/workflows/subscribers/task-reminder-notification.ts +31 -0
- package/src/modules/workflows/widgets/injection/pending-work/widget.client.tsx +119 -0
- package/src/modules/workflows/widgets/injection/pending-work/widget.ts +27 -0
- package/src/modules/workflows/widgets/injection-table.ts +52 -2
- package/src/modules/workflows/workers/workflow-activities.worker.ts +95 -42
- package/src/modules/workflows/workers/workflow-definition-metric-rollup.worker.ts +71 -0
- package/src/modules/workflows/workers/workflow-instance-bulk-replay.ts +51 -0
- package/src/modules/workflows/workers/workflow-invoke-agent.worker.ts +72 -0
|
@@ -1,16 +1,93 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { WorkflowGraph } from "../../../components/WorkflowGraph.js";
|
|
4
|
+
import { useLastRunOverlay } from "../../../components/run/useLastRunOverlay.js";
|
|
4
5
|
import { NodeEditDialog } from "../../../components/NodeEditDialog.js";
|
|
5
6
|
import { EdgeEditDialog } from "../../../components/EdgeEditDialog.js";
|
|
6
7
|
import { NodeEditDialogCrudForm } from "../../../components/NodeEditDialogCrudForm.js";
|
|
7
8
|
import { EdgeEditDialogCrudForm } from "../../../components/EdgeEditDialogCrudForm.js";
|
|
8
|
-
import { useState, useCallback, useEffect } from "react";
|
|
9
|
+
import { useState, useCallback, useEffect, useMemo, useRef } from "react";
|
|
9
10
|
import { useRouter, useSearchParams, usePathname } from "next/navigation";
|
|
10
|
-
import { graphToDefinition, definitionToGraph, validateWorkflowGraph, generateStepId, generateTransitionId, appendWorkflowEdge } from "../../../lib/graph-utils.js";
|
|
11
|
+
import { graphToDefinition, definitionToGraph, applyAutoLayout, validateWorkflowGraph, generateStepId, generateTransitionId, appendWorkflowEdge, getBadgeForNodeType } from "../../../lib/graph-utils.js";
|
|
12
|
+
import { convertStepType } from "../../../lib/step-type-conversion.js";
|
|
13
|
+
import { collectValidationIssues, countIssuesBySeverity } from "../../../lib/collect-validation-issues.js";
|
|
14
|
+
import { formatWorkflowValidationError } from "../../../lib/format-validation-error.js";
|
|
15
|
+
import { resolveNewNodePlacement } from "../../../lib/node-placement.js";
|
|
16
|
+
import {
|
|
17
|
+
commitEditorHistory,
|
|
18
|
+
createEditorHistory,
|
|
19
|
+
redoEditorHistory,
|
|
20
|
+
undoEditorHistory
|
|
21
|
+
} from "../../../lib/editor-history.js";
|
|
22
|
+
import {
|
|
23
|
+
parseWorkflowSubgraph,
|
|
24
|
+
pasteWorkflowSubgraph,
|
|
25
|
+
serializeWorkflowSubgraph,
|
|
26
|
+
stringifyWorkflowSubgraph
|
|
27
|
+
} from "../../../lib/subgraph-clipboard.js";
|
|
28
|
+
import {
|
|
29
|
+
annotationsFromNodes,
|
|
30
|
+
annotationsToNodes,
|
|
31
|
+
createGroupNode,
|
|
32
|
+
createNoteNode,
|
|
33
|
+
isAnnotationNode,
|
|
34
|
+
readEditorAnnotations,
|
|
35
|
+
removeAnnotationNode,
|
|
36
|
+
updateAnnotationNode
|
|
37
|
+
} from "../../../lib/editor-annotations.js";
|
|
38
|
+
import { WORKFLOW_GROUP_TOGGLE_EVENT } from "../../../lib/annotation-events.js";
|
|
39
|
+
import { AnnotationEditDialog } from "../../../components/AnnotationEditDialog.js";
|
|
40
|
+
import { WorkflowCodeView } from "../../../components/WorkflowCodeView.js";
|
|
41
|
+
import { DefinitionMetadataDrawer } from "../../../components/DefinitionMetadataDrawer.js";
|
|
42
|
+
import { TriggersDialog } from "../../../components/TriggersDialog.js";
|
|
43
|
+
import { describeCodeViewDraft, evaluateCodeViewDraft } from "../../../lib/code-view-apply.js";
|
|
44
|
+
import {
|
|
45
|
+
WorkflowAiDraftDialog
|
|
46
|
+
} from "../../../components/WorkflowAiDraftDialog.js";
|
|
47
|
+
import { buildAiCheckpointLabel } from "../../../lib/ai-authoring.js";
|
|
48
|
+
import {
|
|
49
|
+
locateDefinitionJsonEntities,
|
|
50
|
+
locateIssues,
|
|
51
|
+
severityByLine
|
|
52
|
+
} from "../../../lib/definition-json-locations.js";
|
|
53
|
+
import { WorkflowCommandPalette } from "../../../components/WorkflowCommandPalette.js";
|
|
54
|
+
import { buildWorkflowEditorCommands } from "../../../lib/editor-commands.js";
|
|
55
|
+
import { NUDGE_COMMIT_DELAY_MS, nudgeOffset, nudgeSelectedNodes, resolveNudgeDirection, selectedNodeIds } from "../../../lib/node-nudge.js";
|
|
56
|
+
import { canRedoEditorHistory, canUndoEditorHistory } from "../../../lib/editor-history.js";
|
|
57
|
+
import { readPaletteDragItem, writePaletteDragPayload } from "../../../lib/palette-drag.js";
|
|
58
|
+
import { appendActivityToRoute, insertStepOnRoute } from "../../../lib/palette-drop.js";
|
|
59
|
+
import { useActivityTypeOptions } from "../../../components/fields/useActivityTypeOptions.js";
|
|
60
|
+
import { resolveActivityIcon } from "../../../components/WorkflowRouteChips.js";
|
|
11
61
|
import { performDeleteEdgeFlow, performDeleteNodeFlow } from "../../../lib/visual-editor-delete-flow.js";
|
|
12
|
-
import {
|
|
62
|
+
import { resolveCrudFormDialogsEnabled } from "../../../lib/crud-form-dialogs-flag.js";
|
|
63
|
+
import { decideDraftRestore, isServerDraftEligible, stableSerializeDefinition } from "../../../lib/draft-restore.js";
|
|
64
|
+
import { buildDefinitionPayload, buildMetadataPayload } from "../../../lib/definition-payload.js";
|
|
65
|
+
import { resolveDefinitionInterpolationMode } from "../../../lib/interpolation-pipeline.js";
|
|
66
|
+
import { findRouteKindDescriptorForHandle } from "../../../lib/route-kinds.js";
|
|
67
|
+
import { buildWorkflowRouteEdge } from "../../../lib/route-edge.js";
|
|
68
|
+
import { isDecisionSourceHandle } from "../../../lib/node-outcome-rows.js";
|
|
69
|
+
import { STRUCTURAL_EDIT_CONFLICT_CODE } from "../../../lib/definition-edit-safety.js";
|
|
70
|
+
import { WORKFLOW_NODE_DELETE_EVENT } from "../../../components/WorkflowNodeCard.js";
|
|
71
|
+
import { WORKFLOW_ROUTE_CHIP_EVENT, WORKFLOW_ROUTE_ACTIVITY_EVENT } from "../../../lib/route-chip-events.js";
|
|
72
|
+
import { ActivityEditDialog } from "../../../components/ActivityEditDialog.js";
|
|
73
|
+
import { applyRouteOrder, canNormalizeRoutePriorities, normalizeRoutePriorities, readRouteOrder } from "../../../lib/route-priority.js";
|
|
74
|
+
import { classifyConnection, applyInputMappingToNodes, buildDataMappingEdge } from "../../../lib/data-edge-mapping.js";
|
|
75
|
+
import { reattachWorkflowEdge } from "../../../lib/edge-reattachment.js";
|
|
13
76
|
import { workflowDefinitionDataSchema } from "../../../data/validators.js";
|
|
77
|
+
import { collectActivityConfigWarnings } from "../../../data/activity-config-warnings.js";
|
|
78
|
+
import {
|
|
79
|
+
applyIfElseRoutes,
|
|
80
|
+
applySwitchRoutes,
|
|
81
|
+
isBranchingNodeType,
|
|
82
|
+
readBranchingRoutes,
|
|
83
|
+
readSwitchField
|
|
84
|
+
} from "../../../lib/branching-routes.js";
|
|
85
|
+
import {
|
|
86
|
+
buildTriggerPayloadContracts,
|
|
87
|
+
computeContextLedger
|
|
88
|
+
} from "../../../lib/context-ledger.js";
|
|
89
|
+
import { useAvailableEvents } from "@open-mercato/ui/backend/inputs/EventSelect";
|
|
90
|
+
import { toUnresolvedContextRefIssues } from "../../../lib/definition-evaluation.js";
|
|
14
91
|
import { Page } from "@open-mercato/ui/backend/Page";
|
|
15
92
|
import { Button } from "@open-mercato/ui/primitives/button";
|
|
16
93
|
import { Input } from "@open-mercato/ui/primitives/input";
|
|
@@ -18,37 +95,92 @@ import { Textarea } from "@open-mercato/ui/primitives/textarea";
|
|
|
18
95
|
import { Label } from "@open-mercato/ui/primitives/label";
|
|
19
96
|
import { Switch } from "@open-mercato/ui/primitives/switch";
|
|
20
97
|
import {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
98
|
+
Drawer,
|
|
99
|
+
DrawerBody,
|
|
100
|
+
DrawerContent,
|
|
101
|
+
DrawerDescription,
|
|
102
|
+
DrawerFooter,
|
|
103
|
+
DrawerHeader,
|
|
104
|
+
DrawerTitle
|
|
105
|
+
} from "@open-mercato/ui/primitives/drawer";
|
|
29
106
|
import { LoadingMessage } from "@open-mercato/ui/backend/detail";
|
|
30
|
-
import { Alert, AlertTitle } from "@open-mercato/ui/primitives/alert";
|
|
31
|
-
import { useConfirmDialog } from "@open-mercato/ui/backend/confirm-dialog";
|
|
107
|
+
import { Alert, AlertDescription, AlertTitle } from "@open-mercato/ui/primitives/alert";
|
|
108
|
+
import { ConfirmDialog, useConfirmDialog } from "@open-mercato/ui/backend/confirm-dialog";
|
|
32
109
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
33
|
-
import {
|
|
110
|
+
import { formatRelativeTime } from "@open-mercato/shared/lib/time";
|
|
111
|
+
import { FormHeader, ActionsDropdown } from "@open-mercato/ui/backend/forms";
|
|
34
112
|
import { apiCall, withScopedApiRequestHeaders } from "@open-mercato/ui/backend/utils/apiCall";
|
|
113
|
+
import { apiFetch } from "@open-mercato/ui/backend/utils/api";
|
|
35
114
|
import { buildOptimisticLockHeader } from "@open-mercato/ui/backend/utils/optimisticLock";
|
|
36
115
|
import { surfaceRecordConflict } from "@open-mercato/ui/backend/conflicts";
|
|
37
116
|
import { buildRecordInjectionContext, useSetCurrentRecordInjectionContext } from "@open-mercato/ui/backend/injection/recordContext";
|
|
117
|
+
import { readJsonSafe } from "@open-mercato/ui/backend/utils/serverErrors";
|
|
118
|
+
import { useGuardedMutation } from "@open-mercato/ui/backend/injection/useGuardedMutation";
|
|
119
|
+
import { Spinner } from "@open-mercato/ui/primitives/spinner";
|
|
120
|
+
import {
|
|
121
|
+
listStartFixtures,
|
|
122
|
+
removeStartFixture,
|
|
123
|
+
upsertStartFixture
|
|
124
|
+
} from "../../../lib/start-fixtures.js";
|
|
38
125
|
import { flash } from "@open-mercato/ui/backend/FlashMessages";
|
|
39
|
-
import { CircleQuestionMark,
|
|
126
|
+
import { ChevronDown, ChevronRight, CircleAlert, CircleQuestionMark, Code, Command, Group, History, Maximize2, Minimize2, Network, PanelLeftClose, PanelLeftOpen, PanelRightOpen, Play, Save, ShieldMinus, Sparkles, StickyNote, Trash2, TriangleAlert, X } from "lucide-react";
|
|
127
|
+
import { IconButton } from "@open-mercato/ui/primitives/icon-button";
|
|
128
|
+
import { usePersistedBooleanFlag } from "@open-mercato/ui/backend/crud/usePersistedBooleanFlag";
|
|
129
|
+
import { useSidebarCollapse } from "@open-mercato/ui/backend/AppShell";
|
|
40
130
|
import { NODE_TYPE_ICONS, NODE_TYPE_COLORS, NODE_TYPE_LABELS } from "../../../lib/node-type-icons.js";
|
|
41
|
-
import {
|
|
131
|
+
import { TemplateGalleryDialog } from "../../../components/TemplateGalleryDialog.js";
|
|
42
132
|
import { MobileVisualEditor } from "../../../components/mobile/MobileVisualEditor.js";
|
|
43
133
|
import { useIsMobile } from "@open-mercato/ui/hooks/useIsMobile";
|
|
134
|
+
import * as React from "react";
|
|
44
135
|
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
45
136
|
const logger = createLogger("workflows");
|
|
137
|
+
const DRAFT_AUTOSAVE_DEBOUNCE_MS = 2e3;
|
|
138
|
+
const DRAFT_SAVED_LABEL_REFRESH_MS = 3e4;
|
|
139
|
+
function computeClientContextLedger(definitionData, triggerPayloadContracts) {
|
|
140
|
+
return computeContextLedger(definitionData, {
|
|
141
|
+
resolveOutputContract: () => "unknown",
|
|
142
|
+
triggerPayloadContracts
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
function collectContextRefWarnings(definitionData, translate, triggerPayloadContracts) {
|
|
146
|
+
const ledger = computeClientContextLedger(definitionData, triggerPayloadContracts);
|
|
147
|
+
return toUnresolvedContextRefIssues(definitionData, ledger, translate);
|
|
148
|
+
}
|
|
149
|
+
async function loadSubWorkflowContracts(definition) {
|
|
150
|
+
const contracts = /* @__PURE__ */ new Map();
|
|
151
|
+
const subWorkflowIds = Array.from(
|
|
152
|
+
new Set(
|
|
153
|
+
(definition?.steps || []).filter((step) => step?.stepType === "SUB_WORKFLOW" && step?.config?.subWorkflowId).map((step) => step.config.subWorkflowId)
|
|
154
|
+
)
|
|
155
|
+
);
|
|
156
|
+
await Promise.all(
|
|
157
|
+
subWorkflowIds.map(async (workflowId) => {
|
|
158
|
+
try {
|
|
159
|
+
const res = await apiCall(
|
|
160
|
+
`/api/workflows/definitions?workflowId=${encodeURIComponent(workflowId)}&limit=1`
|
|
161
|
+
);
|
|
162
|
+
const io = res.ok ? res.result?.data?.[0]?.definition?.io : void 0;
|
|
163
|
+
if (io) contracts.set(workflowId, io);
|
|
164
|
+
} catch {
|
|
165
|
+
}
|
|
166
|
+
})
|
|
167
|
+
);
|
|
168
|
+
return contracts;
|
|
169
|
+
}
|
|
170
|
+
const PALETTE_NODE_TYPES = ["start", "userTask", "automated", "invokeAgent", "ifElse", "switch", "waitForSignal", "waitForTimer", "waitForCondition", "subWorkflow", "end"];
|
|
171
|
+
function readStructuralEditConflictCount(status, body) {
|
|
172
|
+
if (status !== 409) return null;
|
|
173
|
+
const record = body && typeof body === "object" ? body : null;
|
|
174
|
+
if (!record || record.code !== STRUCTURAL_EDIT_CONFLICT_CODE) return null;
|
|
175
|
+
return typeof record.activeInstanceCount === "number" ? record.activeInstanceCount : 0;
|
|
176
|
+
}
|
|
46
177
|
function VisualEditorPage() {
|
|
47
178
|
const t = useT();
|
|
48
179
|
const router = useRouter();
|
|
49
180
|
const searchParams = useSearchParams();
|
|
50
181
|
const pathname = usePathname();
|
|
51
182
|
const definitionId = searchParams.get("id");
|
|
183
|
+
const templateId = searchParams.get("template");
|
|
52
184
|
const isMobile = useIsMobile();
|
|
53
185
|
const { confirm, ConfirmDialogElement } = useConfirmDialog();
|
|
54
186
|
const [isLoading, setIsLoading] = useState(!!definitionId);
|
|
@@ -57,15 +189,56 @@ function VisualEditorPage() {
|
|
|
57
189
|
const [edges, setEdges] = useState([]);
|
|
58
190
|
const [selectedNode, setSelectedNode] = useState(null);
|
|
59
191
|
const [selectedEdge, setSelectedEdge] = useState(null);
|
|
60
|
-
const [
|
|
192
|
+
const [edgeDialogFocusFieldId, setEdgeDialogFocusFieldId] = useState(null);
|
|
193
|
+
const edgesRef = React.useRef([]);
|
|
194
|
+
edgesRef.current = edges;
|
|
195
|
+
const nodesRef = React.useRef([]);
|
|
196
|
+
nodesRef.current = nodes;
|
|
197
|
+
const [metadataOpen, setMetadataOpen] = useState(false);
|
|
198
|
+
const [metadataFocusSection, setMetadataFocusSection] = useState(null);
|
|
199
|
+
const [triggersDialogOpen, setTriggersDialogOpen] = useState(false);
|
|
200
|
+
const [activityEdit, setActivityEdit] = useState(null);
|
|
201
|
+
const handleOpenTriggers = useCallback(() => {
|
|
202
|
+
setTriggersDialogOpen(true);
|
|
203
|
+
}, []);
|
|
204
|
+
const handleMetadataOpenChange = useCallback((open) => {
|
|
205
|
+
if (!open) setMetadataFocusSection(null);
|
|
206
|
+
setMetadataOpen(open);
|
|
207
|
+
}, []);
|
|
61
208
|
const [isCompactViewport, setIsCompactViewport] = useState(false);
|
|
209
|
+
const [autosaveState, setAutosaveState] = useState("idle");
|
|
210
|
+
const [structuralConflict, setStructuralConflict] = useState(null);
|
|
211
|
+
const [isCreatingVersion, setIsCreatingVersion] = useState(false);
|
|
212
|
+
const autosaveTimerRef = React.useRef(null);
|
|
213
|
+
const performAutosaveRef = React.useRef(async () => {
|
|
214
|
+
});
|
|
215
|
+
const lastAutosavedBodyRef = React.useRef(null);
|
|
216
|
+
const { value: paletteCollapsed, toggle: togglePaletteCollapsed, setValue: setPaletteCollapsed } = usePersistedBooleanFlag("om:wf-editor-palette", false);
|
|
217
|
+
const [showPaletteHowTo, setShowPaletteHowTo] = useState(false);
|
|
218
|
+
const { value: focusMode, setValue: setFocusMode, toggle: toggleFocus } = usePersistedBooleanFlag("om:wf-editor-focus", false);
|
|
219
|
+
const { value: showCompensation, toggle: toggleCompensation } = usePersistedBooleanFlag("om:wf-editor-compensation", false);
|
|
220
|
+
const { value: showLastRun, toggle: toggleLastRun } = usePersistedBooleanFlag("om:wf-editor-last-run", false);
|
|
221
|
+
const { requestCollapse, releaseRequest } = useSidebarCollapse();
|
|
222
|
+
const priorPaletteCollapsedRef = React.useRef(null);
|
|
223
|
+
useEffect(() => {
|
|
224
|
+
if (focusMode) {
|
|
225
|
+
if (priorPaletteCollapsedRef.current === null) priorPaletteCollapsedRef.current = paletteCollapsed;
|
|
226
|
+
requestCollapse(true);
|
|
227
|
+
setPaletteCollapsed(true);
|
|
228
|
+
} else {
|
|
229
|
+
releaseRequest();
|
|
230
|
+
if (priorPaletteCollapsedRef.current !== null) {
|
|
231
|
+
setPaletteCollapsed(priorPaletteCollapsedRef.current);
|
|
232
|
+
priorPaletteCollapsedRef.current = null;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}, [focusMode]);
|
|
236
|
+
useEffect(() => () => releaseRequest(), [releaseRequest]);
|
|
62
237
|
useEffect(() => {
|
|
63
238
|
if (typeof window === "undefined") return;
|
|
64
239
|
const mediaQuery = window.matchMedia("(max-width: 1279px)");
|
|
65
240
|
const applyViewportMode = () => {
|
|
66
|
-
|
|
67
|
-
setIsCompactViewport(compact);
|
|
68
|
-
setShowMetadata(!compact);
|
|
241
|
+
setIsCompactViewport(mediaQuery.matches);
|
|
69
242
|
};
|
|
70
243
|
applyViewportMode();
|
|
71
244
|
mediaQuery.addEventListener("change", applyViewportMode);
|
|
@@ -75,8 +248,29 @@ function VisualEditorPage() {
|
|
|
75
248
|
}, []);
|
|
76
249
|
const [showNodeDialog, setShowNodeDialog] = useState(false);
|
|
77
250
|
const [showEdgeDialog, setShowEdgeDialog] = useState(false);
|
|
251
|
+
const crudFormDialogsEnabled = resolveCrudFormDialogsEnabled(process.env.NEXT_PUBLIC_WORKFLOW_CRUDFORM_ENABLED);
|
|
252
|
+
const inspectorsDocked = crudFormDialogsEnabled && !isMobile && !isCompactViewport;
|
|
253
|
+
const [selectedAnnotation, setSelectedAnnotation] = useState(null);
|
|
254
|
+
const [showAnnotationDialog, setShowAnnotationDialog] = useState(false);
|
|
255
|
+
const [showCommandPalette, setShowCommandPalette] = useState(false);
|
|
256
|
+
const [showCodeView, setShowCodeView] = useState(false);
|
|
78
257
|
const [showClearConfirm, setShowClearConfirm] = useState(false);
|
|
258
|
+
const [showTemplateGallery, setShowTemplateGallery] = useState(false);
|
|
259
|
+
const [problems, setProblems] = useState([]);
|
|
260
|
+
const [showProblems, setShowProblems] = useState(false);
|
|
261
|
+
const [problemsCollapsed, setProblemsCollapsed] = useState(false);
|
|
262
|
+
const [focusTarget, setFocusTarget] = useState(null);
|
|
263
|
+
const focusRequestRef = React.useRef(0);
|
|
264
|
+
const nodeErrorCounts = React.useMemo(() => {
|
|
265
|
+
const counts = {};
|
|
266
|
+
for (const issue of problems) {
|
|
267
|
+
if (issue.severity !== "error" || !issue.nodeId) continue;
|
|
268
|
+
counts[issue.nodeId] = (counts[issue.nodeId] ?? 0) + 1;
|
|
269
|
+
}
|
|
270
|
+
return counts;
|
|
271
|
+
}, [problems]);
|
|
79
272
|
const [workflowId, setWorkflowId] = useState("");
|
|
273
|
+
const lastRunOverlay = useLastRunOverlay({ workflowId, enabled: showLastRun && !!definitionId });
|
|
80
274
|
const [workflowName, setWorkflowName] = useState("");
|
|
81
275
|
const [description, setDescription] = useState("");
|
|
82
276
|
const [version, setVersion] = useState(1);
|
|
@@ -87,13 +281,101 @@ function VisualEditorPage() {
|
|
|
87
281
|
const [effectiveFrom, setEffectiveFrom] = useState("");
|
|
88
282
|
const [effectiveTo, setEffectiveTo] = useState("");
|
|
89
283
|
const [triggers, setTriggers] = useState([]);
|
|
284
|
+
const [contextSchema, setContextSchema] = useState(void 0);
|
|
285
|
+
const [definitionIo, setDefinitionIo] = useState(void 0);
|
|
286
|
+
const [interpolation, setInterpolation] = useState(void 0);
|
|
287
|
+
const [errorHandler, setErrorHandler] = useState(void 0);
|
|
288
|
+
const [loadedMetadata, setLoadedMetadata] = useState(null);
|
|
90
289
|
const [source, setSource] = useState(null);
|
|
91
290
|
const [updatedAt, setUpdatedAt] = useState(null);
|
|
291
|
+
const [startOpen, setStartOpen] = useState(false);
|
|
292
|
+
const [startContext, setStartContext] = useState("{}");
|
|
293
|
+
const [starting, setStarting] = useState(false);
|
|
294
|
+
const [startDryRun, setStartDryRun] = useState(false);
|
|
295
|
+
const [startStepThrough, setStartStepThrough] = useState(false);
|
|
296
|
+
const [fixtureName, setFixtureName] = useState("");
|
|
297
|
+
const [history, setHistory] = useState(createEditorHistory);
|
|
298
|
+
const historyRef = React.useRef(history);
|
|
299
|
+
historyRef.current = history;
|
|
300
|
+
const loadedMetadataRef = React.useRef(null);
|
|
301
|
+
loadedMetadataRef.current = loadedMetadata;
|
|
302
|
+
const historyLabels = useMemo(() => ({
|
|
303
|
+
addStep: t("workflows.visualEditor.history.addStep", "Add step"),
|
|
304
|
+
editStep: t("workflows.visualEditor.history.editStep", "Edit step"),
|
|
305
|
+
editRoute: t("workflows.visualEditor.history.editRoute", "Edit route"),
|
|
306
|
+
deleteStep: t("workflows.visualEditor.history.deleteStep", "Delete step"),
|
|
307
|
+
deleteRoute: t("workflows.visualEditor.history.deleteRoute", "Delete route"),
|
|
308
|
+
connect: t("workflows.visualEditor.history.connect", "Connect steps"),
|
|
309
|
+
reattach: t("workflows.visualEditor.history.reattach", "Re-target route"),
|
|
310
|
+
move: t("workflows.visualEditor.history.move", "Move steps"),
|
|
311
|
+
convert: t("workflows.visualEditor.history.convert", "Change step type"),
|
|
312
|
+
branchRoutes: t("workflows.visualEditor.history.branchRoutes", "Edit branch routes"),
|
|
313
|
+
routeOrder: t("workflows.visualEditor.history.routeOrder", "Reorder routes"),
|
|
314
|
+
pinSample: t("workflows.visualEditor.history.pinSample", "Pin sample"),
|
|
315
|
+
unpinSample: t("workflows.visualEditor.history.unpinSample", "Unpin sample"),
|
|
316
|
+
tidy: t("workflows.visualEditor.history.tidy", "Tidy layout"),
|
|
317
|
+
paste: t("workflows.visualEditor.history.paste", "Paste"),
|
|
318
|
+
duplicate: t("workflows.visualEditor.history.duplicate", "Duplicate"),
|
|
319
|
+
insertOnRoute: t("workflows.visualEditor.history.insertOnRoute", "Insert step on route"),
|
|
320
|
+
addActivity: t("workflows.visualEditor.history.addActivity", "Add action to route"),
|
|
321
|
+
addNote: t("workflows.visualEditor.history.addNote", "Add note"),
|
|
322
|
+
addGroup: t("workflows.visualEditor.history.addGroup", "Add group"),
|
|
323
|
+
editAnnotation: t("workflows.visualEditor.history.editAnnotation", "Edit annotation"),
|
|
324
|
+
deleteAnnotation: t("workflows.visualEditor.history.deleteAnnotation", "Delete annotation"),
|
|
325
|
+
toggleGroup: t("workflows.visualEditor.history.toggleGroup", "Collapse group"),
|
|
326
|
+
saveFixture: t("workflows.visualEditor.history.saveFixture", "Save start fixture"),
|
|
327
|
+
deleteFixture: t("workflows.visualEditor.history.deleteFixture", "Delete start fixture"),
|
|
328
|
+
applyCode: t("workflows.visualEditor.history.applyCode", "Apply JSON from Code view")
|
|
329
|
+
}), [t]);
|
|
330
|
+
const panelStateRef = React.useRef({
|
|
331
|
+
triggers: [],
|
|
332
|
+
contextSchema: void 0,
|
|
333
|
+
io: void 0,
|
|
334
|
+
interpolation: void 0,
|
|
335
|
+
errorHandler: void 0
|
|
336
|
+
});
|
|
337
|
+
useEffect(() => {
|
|
338
|
+
panelStateRef.current = {
|
|
339
|
+
triggers,
|
|
340
|
+
contextSchema,
|
|
341
|
+
io: definitionIo,
|
|
342
|
+
interpolation,
|
|
343
|
+
errorHandler
|
|
344
|
+
};
|
|
345
|
+
}, [triggers, contextSchema, definitionIo, interpolation, errorHandler]);
|
|
346
|
+
const captureDocument = useCallback(() => ({
|
|
347
|
+
nodes: nodesRef.current,
|
|
348
|
+
edges: edgesRef.current,
|
|
349
|
+
metadata: loadedMetadataRef.current,
|
|
350
|
+
panel: panelStateRef.current
|
|
351
|
+
}), []);
|
|
352
|
+
const commitCapturedDocument = useCallback((document2, label) => {
|
|
353
|
+
setHistory((current) => commitEditorHistory(current, document2, label));
|
|
354
|
+
}, []);
|
|
355
|
+
const commitHistory = useCallback((label) => {
|
|
356
|
+
commitCapturedDocument(captureDocument(), label);
|
|
357
|
+
}, [captureDocument, commitCapturedDocument]);
|
|
358
|
+
const resetHistory = useCallback(() => {
|
|
359
|
+
setHistory(createEditorHistory());
|
|
360
|
+
}, []);
|
|
361
|
+
const mutationContextId = `workflows.definitions.visual-editor:${definitionId ?? "unknown"}`;
|
|
362
|
+
const { runMutation, retryLastMutation } = useGuardedMutation({
|
|
363
|
+
contextId: mutationContextId
|
|
364
|
+
});
|
|
92
365
|
const isCodeOnly = source === "code";
|
|
93
366
|
const isCodeOverride = source === "code_override";
|
|
367
|
+
const draftEligible = isServerDraftEligible(definitionId) && !isCodeOnly;
|
|
368
|
+
const [pendingDraft, setPendingDraft] = useState(null);
|
|
369
|
+
const [draftAutosaveReady, setDraftAutosaveReady] = useState(false);
|
|
370
|
+
const [draftSavedAt, setDraftSavedAt] = useState(null);
|
|
371
|
+
const [draftSaveFailed, setDraftSaveFailed] = useState(false);
|
|
372
|
+
const [draftClock, setDraftClock] = useState(0);
|
|
373
|
+
const lastPersistedDraftRef = useRef(null);
|
|
374
|
+
const draftSuspendedRef = useRef(false);
|
|
94
375
|
useEffect(() => {
|
|
95
376
|
const loadDefinition = async () => {
|
|
96
377
|
if (!definitionId) {
|
|
378
|
+
setInterpolation("strict");
|
|
97
379
|
setIsLoading(false);
|
|
98
380
|
return;
|
|
99
381
|
}
|
|
@@ -115,12 +397,64 @@ function VisualEditorPage() {
|
|
|
115
397
|
setIcon(definition.metadata?.icon || "");
|
|
116
398
|
setEffectiveFrom(definition.effectiveFrom || "");
|
|
117
399
|
setEffectiveTo(definition.effectiveTo || "");
|
|
118
|
-
const
|
|
119
|
-
|
|
400
|
+
const childContracts = await loadSubWorkflowContracts(definition.definition);
|
|
401
|
+
const graph = definitionToGraph(definition.definition, { childContracts });
|
|
402
|
+
const loadedMetadataBag = definition.metadata && typeof definition.metadata === "object" ? definition.metadata : null;
|
|
403
|
+
const annotationNodes = annotationsToNodes(readEditorAnnotations(loadedMetadataBag));
|
|
404
|
+
setNodes([...graph.nodes, ...annotationNodes]);
|
|
120
405
|
setEdges(graph.edges);
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
406
|
+
const loadedTriggers = definition.definition?.triggers || [];
|
|
407
|
+
setTriggers(loadedTriggers);
|
|
408
|
+
const loadedContextSchema = definition.definition?.contextSchema ?? void 0;
|
|
409
|
+
setContextSchema(loadedContextSchema);
|
|
410
|
+
const loadedIo = definition.definition?.io ?? void 0;
|
|
411
|
+
setDefinitionIo(loadedIo);
|
|
412
|
+
const loadedInterpolation = resolveDefinitionInterpolationMode(definition.definition);
|
|
413
|
+
setInterpolation(loadedInterpolation);
|
|
414
|
+
const loadedErrorHandler = definition.definition?.errorHandler ?? void 0;
|
|
415
|
+
setErrorHandler(loadedErrorHandler);
|
|
416
|
+
const loadedMetadataObject = loadedMetadataBag ? { ...loadedMetadataBag } : null;
|
|
417
|
+
setLoadedMetadata(loadedMetadataObject);
|
|
418
|
+
const loadedSource = definition.source ?? null;
|
|
419
|
+
setSource(loadedSource);
|
|
420
|
+
const loadedUpdatedAt = typeof definition.updatedAt === "string" ? definition.updatedAt : null;
|
|
421
|
+
setUpdatedAt(loadedUpdatedAt);
|
|
422
|
+
const comparableDefinition = buildDefinitionPayload({
|
|
423
|
+
graphDefinition: graphToDefinition(graph.nodes, graph.edges, { includePositions: true }),
|
|
424
|
+
triggers: loadedTriggers,
|
|
425
|
+
contextSchema: loadedContextSchema,
|
|
426
|
+
io: loadedIo,
|
|
427
|
+
interpolation: loadedInterpolation,
|
|
428
|
+
errorHandler: loadedErrorHandler
|
|
429
|
+
});
|
|
430
|
+
const loadedDraftMetadata = buildMetadataPayload({
|
|
431
|
+
loadedMetadata: loadedMetadataObject,
|
|
432
|
+
category: definition.metadata?.category || "",
|
|
433
|
+
tags: definition.metadata?.tags || [],
|
|
434
|
+
icon: definition.metadata?.icon || "",
|
|
435
|
+
annotations: annotationsFromNodes(annotationNodes)
|
|
436
|
+
});
|
|
437
|
+
lastPersistedDraftRef.current = stableSerializeDefinition({
|
|
438
|
+
definition: comparableDefinition,
|
|
439
|
+
metadata: loadedDraftMetadata
|
|
440
|
+
});
|
|
441
|
+
if (loadedSource !== "code" && isServerDraftEligible(definitionId)) {
|
|
442
|
+
const draftResult = await apiCall(
|
|
443
|
+
`/api/workflows/definitions/${definitionId}/draft`
|
|
444
|
+
);
|
|
445
|
+
const draft = draftResult.ok ? draftResult.result?.data : void 0;
|
|
446
|
+
const decision = draft ? decideDraftRestore({
|
|
447
|
+
draftDefinition: draft.definition,
|
|
448
|
+
draftBaseUpdatedAt: draft.baseUpdatedAt,
|
|
449
|
+
loadedDefinition: comparableDefinition,
|
|
450
|
+
definitionUpdatedAt: loadedUpdatedAt
|
|
451
|
+
}) : { offerRestore: false };
|
|
452
|
+
if (draft && decision.offerRestore) {
|
|
453
|
+
setPendingDraft({ draft, baseMismatch: decision.baseMismatch });
|
|
454
|
+
} else {
|
|
455
|
+
setDraftAutosaveReady(true);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
124
458
|
} catch (error) {
|
|
125
459
|
logger.error("Error loading workflow definition", { err: error });
|
|
126
460
|
flash("Failed to load workflow definition", "error");
|
|
@@ -130,62 +464,475 @@ function VisualEditorPage() {
|
|
|
130
464
|
};
|
|
131
465
|
loadDefinition();
|
|
132
466
|
}, [definitionId]);
|
|
133
|
-
const
|
|
467
|
+
const annotations = useMemo(() => annotationsFromNodes(nodes), [nodes]);
|
|
468
|
+
const draftMetadata = useMemo(
|
|
469
|
+
() => buildMetadataPayload({ loadedMetadata, tags, category, icon, annotations }),
|
|
470
|
+
[loadedMetadata, tags, category, icon, annotations]
|
|
471
|
+
);
|
|
472
|
+
useEffect(() => {
|
|
473
|
+
if (!draftAutosaveReady || !draftEligible || !definitionId) return;
|
|
474
|
+
if (draftSuspendedRef.current) return;
|
|
475
|
+
let payload;
|
|
476
|
+
try {
|
|
477
|
+
payload = {
|
|
478
|
+
definition: buildDefinitionPayload({
|
|
479
|
+
graphDefinition: graphToDefinition(nodes, edges, { includePositions: true }),
|
|
480
|
+
triggers,
|
|
481
|
+
contextSchema,
|
|
482
|
+
io: definitionIo,
|
|
483
|
+
interpolation,
|
|
484
|
+
errorHandler
|
|
485
|
+
}),
|
|
486
|
+
metadata: draftMetadata,
|
|
487
|
+
baseUpdatedAt: updatedAt
|
|
488
|
+
};
|
|
489
|
+
} catch {
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
const serialized = stableSerializeDefinition({ definition: payload.definition, metadata: payload.metadata });
|
|
493
|
+
if (serialized === lastPersistedDraftRef.current) return;
|
|
494
|
+
const timer = window.setTimeout(async () => {
|
|
495
|
+
if (draftSuspendedRef.current) return;
|
|
496
|
+
try {
|
|
497
|
+
const result = await apiCall(
|
|
498
|
+
`/api/workflows/definitions/${definitionId}/draft`,
|
|
499
|
+
{
|
|
500
|
+
method: "PUT",
|
|
501
|
+
headers: { "Content-Type": "application/json" },
|
|
502
|
+
body: JSON.stringify(payload)
|
|
503
|
+
}
|
|
504
|
+
);
|
|
505
|
+
if (result.ok) {
|
|
506
|
+
lastPersistedDraftRef.current = serialized;
|
|
507
|
+
setDraftSavedAt((/* @__PURE__ */ new Date()).toISOString());
|
|
508
|
+
setDraftSaveFailed(false);
|
|
509
|
+
} else {
|
|
510
|
+
setDraftSaveFailed(true);
|
|
511
|
+
}
|
|
512
|
+
} catch {
|
|
513
|
+
setDraftSaveFailed(true);
|
|
514
|
+
}
|
|
515
|
+
}, DRAFT_AUTOSAVE_DEBOUNCE_MS);
|
|
516
|
+
return () => window.clearTimeout(timer);
|
|
517
|
+
}, [draftAutosaveReady, draftEligible, definitionId, nodes, edges, triggers, contextSchema, definitionIo, interpolation, errorHandler, draftMetadata, updatedAt, workflowName, description, version, enabled, effectiveFrom, effectiveTo]);
|
|
518
|
+
useEffect(() => {
|
|
519
|
+
if (!draftSavedAt) return;
|
|
520
|
+
const interval = window.setInterval(() => setDraftClock((tick) => tick + 1), DRAFT_SAVED_LABEL_REFRESH_MS);
|
|
521
|
+
return () => window.clearInterval(interval);
|
|
522
|
+
}, [draftSavedAt]);
|
|
523
|
+
const draftSavedLabel = useMemo(() => {
|
|
524
|
+
if (!draftSavedAt) return null;
|
|
525
|
+
return formatRelativeTime(draftSavedAt, { translate: t });
|
|
526
|
+
}, [draftSavedAt, draftClock, t]);
|
|
527
|
+
const handleRestoreDraft = useCallback(() => {
|
|
528
|
+
if (!pendingDraft) return;
|
|
529
|
+
try {
|
|
530
|
+
const draftDefinition = pendingDraft.draft.definition;
|
|
531
|
+
const graph = definitionToGraph(draftDefinition);
|
|
532
|
+
setNodes([...graph.nodes, ...annotationsToNodes(readEditorAnnotations(pendingDraft.draft.metadata ?? null))]);
|
|
533
|
+
setEdges(graph.edges);
|
|
534
|
+
const draftTriggers = pendingDraft.draft.definition.triggers;
|
|
535
|
+
setTriggers(Array.isArray(draftTriggers) ? draftTriggers : []);
|
|
536
|
+
const draftContextSchema = pendingDraft.draft.definition.contextSchema;
|
|
537
|
+
setContextSchema(draftContextSchema ? draftContextSchema : void 0);
|
|
538
|
+
const draftIo = pendingDraft.draft.definition.io;
|
|
539
|
+
setDefinitionIo(draftIo ?? void 0);
|
|
540
|
+
setInterpolation(resolveDefinitionInterpolationMode(pendingDraft.draft.definition));
|
|
541
|
+
const draftErrorHandler = pendingDraft.draft.definition.errorHandler;
|
|
542
|
+
setErrorHandler(draftErrorHandler ?? void 0);
|
|
543
|
+
const restoredMetadata = pendingDraft.draft.metadata ?? null;
|
|
544
|
+
setLoadedMetadata(restoredMetadata);
|
|
545
|
+
if (restoredMetadata) {
|
|
546
|
+
setCategory(typeof restoredMetadata.category === "string" ? restoredMetadata.category : "");
|
|
547
|
+
setTags(Array.isArray(restoredMetadata.tags) ? restoredMetadata.tags.filter((tag) => typeof tag === "string") : []);
|
|
548
|
+
setIcon(typeof restoredMetadata.icon === "string" ? restoredMetadata.icon : "");
|
|
549
|
+
}
|
|
550
|
+
lastPersistedDraftRef.current = stableSerializeDefinition({
|
|
551
|
+
definition: pendingDraft.draft.definition,
|
|
552
|
+
metadata: pendingDraft.draft.metadata ?? null
|
|
553
|
+
});
|
|
554
|
+
resetHistory();
|
|
555
|
+
flash(t("workflows.visualEditor.draft.restored", "Draft restored"), "success");
|
|
556
|
+
} catch (error) {
|
|
557
|
+
logger.error("Error restoring workflow definition draft", { err: error });
|
|
558
|
+
flash(t("workflows.visualEditor.draft.restoreFailed", "Failed to restore draft"), "error");
|
|
559
|
+
} finally {
|
|
560
|
+
setPendingDraft(null);
|
|
561
|
+
setDraftAutosaveReady(true);
|
|
562
|
+
}
|
|
563
|
+
}, [pendingDraft, resetHistory, t]);
|
|
564
|
+
const handleDiscardDraft = useCallback(async () => {
|
|
565
|
+
setPendingDraft(null);
|
|
566
|
+
setDraftAutosaveReady(true);
|
|
567
|
+
if (!definitionId) return;
|
|
568
|
+
try {
|
|
569
|
+
await apiCall(`/api/workflows/definitions/${definitionId}/draft`, { method: "DELETE" });
|
|
570
|
+
} catch (error) {
|
|
571
|
+
logger.error("Error discarding workflow definition draft", { err: error });
|
|
572
|
+
}
|
|
573
|
+
}, [definitionId]);
|
|
574
|
+
const scheduleAutosave = useCallback(() => {
|
|
575
|
+
if (!definitionId || isCodeOnly) return;
|
|
576
|
+
if (autosaveTimerRef.current) clearTimeout(autosaveTimerRef.current);
|
|
577
|
+
autosaveTimerRef.current = setTimeout(() => {
|
|
578
|
+
autosaveTimerRef.current = null;
|
|
579
|
+
void performAutosaveRef.current();
|
|
580
|
+
}, 900);
|
|
581
|
+
}, [definitionId, isCodeOnly]);
|
|
582
|
+
useEffect(() => () => {
|
|
583
|
+
if (autosaveTimerRef.current) clearTimeout(autosaveTimerRef.current);
|
|
584
|
+
}, []);
|
|
585
|
+
const applyHistoryDocument = useCallback((document2) => {
|
|
586
|
+
setNodes(document2.nodes);
|
|
587
|
+
setEdges(document2.edges);
|
|
588
|
+
setLoadedMetadata(document2.metadata);
|
|
589
|
+
if (document2.panel) {
|
|
590
|
+
setTriggers(document2.panel.triggers);
|
|
591
|
+
setContextSchema(document2.panel.contextSchema);
|
|
592
|
+
setDefinitionIo(document2.panel.io);
|
|
593
|
+
setInterpolation(document2.panel.interpolation);
|
|
594
|
+
setErrorHandler(document2.panel.errorHandler);
|
|
595
|
+
}
|
|
596
|
+
scheduleAutosave();
|
|
597
|
+
}, [scheduleAutosave]);
|
|
598
|
+
const handleUndo = useCallback(() => {
|
|
134
599
|
if (isCodeOnly) return;
|
|
600
|
+
const step = undoEditorHistory(historyRef.current, captureDocument());
|
|
601
|
+
if (!step) {
|
|
602
|
+
flash(t("workflows.visualEditor.history.nothingToUndo", "Nothing to undo"), "info");
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
setHistory(step.state);
|
|
606
|
+
applyHistoryDocument(step.document);
|
|
607
|
+
flash(t("workflows.visualEditor.history.undone", "Undone: {label}", { label: step.label }), "success");
|
|
608
|
+
}, [isCodeOnly, captureDocument, applyHistoryDocument, t]);
|
|
609
|
+
const handleRedo = useCallback(() => {
|
|
610
|
+
if (isCodeOnly) return;
|
|
611
|
+
const step = redoEditorHistory(historyRef.current, captureDocument());
|
|
612
|
+
if (!step) {
|
|
613
|
+
flash(t("workflows.visualEditor.history.nothingToRedo", "Nothing to redo"), "info");
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
setHistory(step.state);
|
|
617
|
+
applyHistoryDocument(step.document);
|
|
618
|
+
flash(t("workflows.visualEditor.history.redone", "Redone: {label}", { label: step.label }), "success");
|
|
619
|
+
}, [isCodeOnly, captureDocument, applyHistoryDocument, t]);
|
|
620
|
+
const localClipboardRef = React.useRef(null);
|
|
621
|
+
const selectedSubgraph = useCallback(() => {
|
|
622
|
+
const selectedIds = nodesRef.current.filter((node) => node.selected).map((node) => node.id);
|
|
623
|
+
return serializeWorkflowSubgraph(nodesRef.current, edgesRef.current, selectedIds);
|
|
624
|
+
}, []);
|
|
625
|
+
const handleCopySelection = useCallback(async () => {
|
|
626
|
+
const payload = selectedSubgraph();
|
|
627
|
+
if (!payload) {
|
|
628
|
+
flash(t("workflows.visualEditor.clipboard.nothingSelected", "Select one or more steps first"), "info");
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
const serialized = stringifyWorkflowSubgraph(payload);
|
|
632
|
+
localClipboardRef.current = serialized;
|
|
633
|
+
try {
|
|
634
|
+
await navigator.clipboard.writeText(serialized);
|
|
635
|
+
flash(
|
|
636
|
+
t("workflows.visualEditor.clipboard.copied", "Copied {count} step(s)", { count: String(payload.steps.length) }),
|
|
637
|
+
"success"
|
|
638
|
+
);
|
|
639
|
+
} catch (error) {
|
|
640
|
+
logger.warn("Workflow subgraph copy fell back to the in-page buffer", { err: error });
|
|
641
|
+
flash(
|
|
642
|
+
t(
|
|
643
|
+
"workflows.visualEditor.clipboard.copyUnavailable",
|
|
644
|
+
"The browser blocked clipboard access, so the selection was kept in this editor only \u2014 pasting into another tab will not work."
|
|
645
|
+
),
|
|
646
|
+
"warning"
|
|
647
|
+
);
|
|
648
|
+
}
|
|
649
|
+
}, [selectedSubgraph, t]);
|
|
650
|
+
const describeClipboardFailure = useCallback((code) => {
|
|
651
|
+
switch (code) {
|
|
652
|
+
case "invalidJson":
|
|
653
|
+
return t("workflows.visualEditor.clipboard.invalidJson", "The clipboard does not contain valid JSON.");
|
|
654
|
+
case "empty":
|
|
655
|
+
return t("workflows.visualEditor.clipboard.empty", "The copied selection contains no steps.");
|
|
656
|
+
default:
|
|
657
|
+
return t("workflows.visualEditor.clipboard.unsupportedFormat", "The clipboard does not contain a copied workflow selection.");
|
|
658
|
+
}
|
|
659
|
+
}, [t]);
|
|
660
|
+
const spliceSubgraph = useCallback((payload, label) => {
|
|
661
|
+
const result = pasteWorkflowSubgraph(nodesRef.current, edgesRef.current, payload);
|
|
662
|
+
commitHistory(label);
|
|
663
|
+
setNodes(result.nodes);
|
|
664
|
+
setEdges(result.edges);
|
|
665
|
+
scheduleAutosave();
|
|
666
|
+
flash(
|
|
667
|
+
t("workflows.visualEditor.clipboard.pasted", "Pasted {count} step(s)", { count: String(result.pastedNodeIds.length) }),
|
|
668
|
+
"success"
|
|
669
|
+
);
|
|
670
|
+
}, [commitHistory, scheduleAutosave, t]);
|
|
671
|
+
const handlePaste = useCallback(async () => {
|
|
672
|
+
if (isCodeOnly) return;
|
|
673
|
+
let text = null;
|
|
674
|
+
try {
|
|
675
|
+
text = await navigator.clipboard.readText();
|
|
676
|
+
} catch (error) {
|
|
677
|
+
logger.warn("Workflow subgraph paste fell back to the in-page buffer", { err: error });
|
|
678
|
+
text = localClipboardRef.current;
|
|
679
|
+
if (!text) {
|
|
680
|
+
flash(
|
|
681
|
+
t(
|
|
682
|
+
"workflows.visualEditor.clipboard.pasteUnavailable",
|
|
683
|
+
"The browser blocked clipboard access, so there is nothing to paste. Copy a selection in this editor first."
|
|
684
|
+
),
|
|
685
|
+
"error"
|
|
686
|
+
);
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
const parsed = parseWorkflowSubgraph(text ?? "");
|
|
691
|
+
if (!parsed.ok) {
|
|
692
|
+
flash(describeClipboardFailure(parsed.code), "error");
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
spliceSubgraph(parsed.payload, historyLabels.paste);
|
|
696
|
+
}, [isCodeOnly, describeClipboardFailure, spliceSubgraph, historyLabels.paste, t]);
|
|
697
|
+
const handleDuplicateSelection = useCallback(() => {
|
|
698
|
+
if (isCodeOnly) return;
|
|
699
|
+
const payload = selectedSubgraph();
|
|
700
|
+
if (!payload) {
|
|
701
|
+
flash(t("workflows.visualEditor.clipboard.nothingSelected", "Select one or more steps first"), "info");
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
spliceSubgraph(payload, historyLabels.duplicate);
|
|
705
|
+
}, [isCodeOnly, selectedSubgraph, spliceSubgraph, historyLabels.duplicate, t]);
|
|
706
|
+
const dragBaselineRef = React.useRef(null);
|
|
707
|
+
const handleNodesChange = useCallback((nextNodes, meta) => {
|
|
708
|
+
if (isCodeOnly) return;
|
|
709
|
+
if ((meta?.dragging || meta?.resizing) && !dragBaselineRef.current) dragBaselineRef.current = captureDocument();
|
|
710
|
+
const baseline = dragBaselineRef.current ?? captureDocument();
|
|
135
711
|
setNodes(nextNodes);
|
|
136
|
-
|
|
712
|
+
if (meta && !meta.persistable) return;
|
|
713
|
+
dragBaselineRef.current = null;
|
|
714
|
+
commitCapturedDocument(baseline, historyLabels.move);
|
|
715
|
+
scheduleAutosave();
|
|
716
|
+
}, [isCodeOnly, captureDocument, commitCapturedDocument, historyLabels.move, scheduleAutosave]);
|
|
717
|
+
const handleAutoArrange = useCallback(() => {
|
|
718
|
+
if (isCodeOnly) return;
|
|
719
|
+
commitHistory(historyLabels.tidy);
|
|
720
|
+
setNodes((nds) => applyAutoLayout(nds, edges));
|
|
721
|
+
scheduleAutosave();
|
|
722
|
+
}, [isCodeOnly, edges, commitHistory, historyLabels.tidy, scheduleAutosave]);
|
|
137
723
|
const handleEdgesChange = useCallback((nextEdges) => {
|
|
138
724
|
if (isCodeOnly) return;
|
|
139
725
|
setEdges(nextEdges);
|
|
140
726
|
}, [isCodeOnly]);
|
|
141
|
-
const
|
|
727
|
+
const buildPaletteNode = useCallback((nodeType, dropPosition) => ({
|
|
728
|
+
id: generateStepId(nodeType),
|
|
729
|
+
type: nodeType,
|
|
730
|
+
position: resolveNewNodePlacement(nodesRef.current, { dropPosition }),
|
|
731
|
+
data: {
|
|
732
|
+
label: getDefaultLabel(nodeType),
|
|
733
|
+
description: "",
|
|
734
|
+
badge: getDefaultBadge(nodeType),
|
|
735
|
+
status: "pending"
|
|
736
|
+
}
|
|
737
|
+
}), []);
|
|
738
|
+
const handleAddNode = useCallback((nodeType, dropPosition) => {
|
|
142
739
|
if (isCodeOnly) return;
|
|
143
|
-
const newNode =
|
|
144
|
-
|
|
145
|
-
type: nodeType,
|
|
146
|
-
position: {
|
|
147
|
-
x: 250 + nodes.length * 50,
|
|
148
|
-
y: 100 + nodes.length * 150
|
|
149
|
-
},
|
|
150
|
-
data: {
|
|
151
|
-
label: getDefaultLabel(nodeType),
|
|
152
|
-
description: "",
|
|
153
|
-
badge: getDefaultBadge(nodeType),
|
|
154
|
-
status: "pending"
|
|
155
|
-
}
|
|
156
|
-
};
|
|
740
|
+
const newNode = buildPaletteNode(nodeType, dropPosition);
|
|
741
|
+
commitHistory(historyLabels.addStep);
|
|
157
742
|
setNodes((nds) => [...nds, newNode]);
|
|
158
|
-
|
|
743
|
+
scheduleAutosave();
|
|
744
|
+
}, [isCodeOnly, buildPaletteNode, commitHistory, historyLabels.addStep, scheduleAutosave]);
|
|
745
|
+
const handleAddAnnotation = useCallback((kind) => {
|
|
746
|
+
if (isCodeOnly) return;
|
|
747
|
+
const position = resolveNewNodePlacement(nodesRef.current, {});
|
|
748
|
+
const node = kind === "note" ? createNoteNode(position, "") : createGroupNode(position, t("workflows.annotations.group.untitled", "Untitled group"));
|
|
749
|
+
commitHistory(kind === "note" ? historyLabels.addNote : historyLabels.addGroup);
|
|
750
|
+
setNodes((nds) => [...nds, node]);
|
|
751
|
+
setSelectedAnnotation(node);
|
|
752
|
+
setShowAnnotationDialog(true);
|
|
753
|
+
scheduleAutosave();
|
|
754
|
+
}, [isCodeOnly, commitHistory, historyLabels.addNote, historyLabels.addGroup, scheduleAutosave, t]);
|
|
755
|
+
const activityTypeOptions = useActivityTypeOptions();
|
|
756
|
+
const describePaletteDropRejection = useCallback((code) => {
|
|
757
|
+
switch (code) {
|
|
758
|
+
case "dataMappingRoute":
|
|
759
|
+
return t("workflows.visualEditor.palette.rejected.dataMappingRoute", "Data mapping links are edited in the step configuration, not on the canvas.");
|
|
760
|
+
default:
|
|
761
|
+
return t("workflows.visualEditor.palette.rejected.unknownRoute", "That route no longer exists.");
|
|
762
|
+
}
|
|
763
|
+
}, [t]);
|
|
764
|
+
const handleCanvasDrop = useCallback((event) => {
|
|
765
|
+
if (isCodeOnly) return;
|
|
766
|
+
const item = readPaletteDragItem(event.dataTransfer);
|
|
767
|
+
if (!item) return;
|
|
768
|
+
if (item.kind === "step") {
|
|
769
|
+
if (!event.edgeId) {
|
|
770
|
+
handleAddNode(item.nodeType, event.position);
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
const result2 = insertStepOnRoute(
|
|
774
|
+
nodesRef.current,
|
|
775
|
+
edgesRef.current,
|
|
776
|
+
event.edgeId,
|
|
777
|
+
buildPaletteNode(item.nodeType, event.position)
|
|
778
|
+
);
|
|
779
|
+
if (!result2.ok) {
|
|
780
|
+
flash(describePaletteDropRejection(result2.code), "error");
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
commitHistory(historyLabels.insertOnRoute);
|
|
784
|
+
setNodes(result2.nodes);
|
|
785
|
+
setEdges(result2.edges);
|
|
786
|
+
scheduleAutosave();
|
|
787
|
+
flash(t("workflows.visualEditor.palette.insertedOnRoute", "Step inserted on the route"), "success");
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
if (!event.edgeId) {
|
|
791
|
+
flash(t("workflows.visualEditor.palette.dragActionHint", "Drop an action onto a route to add it there"), "info");
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
const activityLabel = activityTypeOptions.find((option) => option.value === item.activityType)?.label ?? item.activityType;
|
|
795
|
+
const result = appendActivityToRoute(edgesRef.current, event.edgeId, {
|
|
796
|
+
activityId: `activity_${Date.now()}`,
|
|
797
|
+
activityName: activityLabel,
|
|
798
|
+
activityType: item.activityType,
|
|
799
|
+
config: {}
|
|
800
|
+
});
|
|
801
|
+
if (!result.ok) {
|
|
802
|
+
flash(describePaletteDropRejection(result.code), "error");
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
commitHistory(historyLabels.addActivity);
|
|
806
|
+
setEdges(result.edges);
|
|
807
|
+
scheduleAutosave();
|
|
808
|
+
flash(t("workflows.visualEditor.palette.activityAdded", "{activity} added to the route", { activity: activityLabel }), "success");
|
|
809
|
+
}, [
|
|
810
|
+
isCodeOnly,
|
|
811
|
+
handleAddNode,
|
|
812
|
+
buildPaletteNode,
|
|
813
|
+
activityTypeOptions,
|
|
814
|
+
describePaletteDropRejection,
|
|
815
|
+
commitHistory,
|
|
816
|
+
historyLabels.insertOnRoute,
|
|
817
|
+
historyLabels.addActivity,
|
|
818
|
+
scheduleAutosave,
|
|
819
|
+
t
|
|
820
|
+
]);
|
|
821
|
+
const handlePaletteStepDragStart = useCallback((event, nodeType) => {
|
|
822
|
+
writePaletteDragPayload(event.dataTransfer, { kind: "step", nodeType }, NODE_TYPE_LABELS[nodeType]?.title ?? nodeType);
|
|
823
|
+
}, []);
|
|
824
|
+
const handlePaletteActivityDragStart = useCallback((event, activityType, label) => {
|
|
825
|
+
writePaletteDragPayload(event.dataTransfer, { kind: "activity", activityType }, label);
|
|
826
|
+
}, []);
|
|
159
827
|
const handleNodeClick = useCallback((_event, node) => {
|
|
160
828
|
if (isCodeOnly) return;
|
|
829
|
+
if (isAnnotationNode(node)) {
|
|
830
|
+
setSelectedAnnotation(node);
|
|
831
|
+
setSelectedNode(null);
|
|
832
|
+
setSelectedEdge(null);
|
|
833
|
+
setShowAnnotationDialog(true);
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
161
836
|
setSelectedNode(node);
|
|
162
837
|
setSelectedEdge(null);
|
|
838
|
+
setShowEdgeDialog(false);
|
|
163
839
|
setShowNodeDialog(true);
|
|
164
840
|
}, [isCodeOnly]);
|
|
165
841
|
const handleEdgeClick = useCallback((_event, edge) => {
|
|
166
842
|
if (isCodeOnly) return;
|
|
167
843
|
setSelectedEdge(edge);
|
|
168
844
|
setSelectedNode(null);
|
|
845
|
+
setEdgeDialogFocusFieldId(null);
|
|
846
|
+
setShowNodeDialog(false);
|
|
847
|
+
setShowEdgeDialog(true);
|
|
848
|
+
}, [isCodeOnly]);
|
|
849
|
+
const handleRouteChipOpen = useCallback((edgeId, section) => {
|
|
850
|
+
if (isCodeOnly) return;
|
|
851
|
+
const edge = edgesRef.current.find((candidate) => candidate.id === edgeId);
|
|
852
|
+
if (!edge) return;
|
|
853
|
+
setSelectedEdge(edge);
|
|
854
|
+
setSelectedNode(null);
|
|
855
|
+
setEdgeDialogFocusFieldId(section === "condition" ? "condition" : "activities");
|
|
169
856
|
setShowEdgeDialog(true);
|
|
170
857
|
}, [isCodeOnly]);
|
|
171
858
|
const handleSaveNode = useCallback((nodeId, updates) => {
|
|
859
|
+
commitHistory(historyLabels.editStep);
|
|
860
|
+
if (Object.prototype.hasOwnProperty.call(updates, "decisions")) {
|
|
861
|
+
const previousNode = nodesRef.current.find((node) => node.id === nodeId);
|
|
862
|
+
const previousDecisions = previousNode?.data.decisions;
|
|
863
|
+
const nextDecisions = updates.decisions;
|
|
864
|
+
const previousIds = new Set(
|
|
865
|
+
(previousDecisions ?? []).flatMap(
|
|
866
|
+
(decision) => typeof decision.transitionId === "string" ? [decision.transitionId] : []
|
|
867
|
+
)
|
|
868
|
+
);
|
|
869
|
+
const nextIds = new Set(
|
|
870
|
+
(nextDecisions ?? []).flatMap(
|
|
871
|
+
(decision) => typeof decision.transitionId === "string" ? [decision.transitionId] : []
|
|
872
|
+
)
|
|
873
|
+
);
|
|
874
|
+
setEdges(
|
|
875
|
+
(currentEdges) => currentEdges.map((edge) => {
|
|
876
|
+
if (edge.source !== nodeId) return edge;
|
|
877
|
+
if (nextIds.has(edge.id)) return { ...edge, sourceHandle: edge.id };
|
|
878
|
+
if (previousIds.has(edge.id) && edge.sourceHandle === edge.id) {
|
|
879
|
+
const { sourceHandle: _removed, ...remainingEdge } = edge;
|
|
880
|
+
return remainingEdge;
|
|
881
|
+
}
|
|
882
|
+
return edge;
|
|
883
|
+
})
|
|
884
|
+
);
|
|
885
|
+
}
|
|
172
886
|
setNodes(
|
|
173
887
|
(nds) => nds.map(
|
|
174
888
|
(node) => node.id === nodeId ? { ...node, data: { ...node.data, ...updates } } : node
|
|
175
889
|
)
|
|
176
890
|
);
|
|
177
891
|
flash("Node updated successfully", "success");
|
|
178
|
-
}, []);
|
|
892
|
+
}, [commitHistory, historyLabels.editStep]);
|
|
893
|
+
const branchingRoutesValue = useMemo(() => {
|
|
894
|
+
if (!selectedNode || !isBranchingNodeType(selectedNode.type)) return void 0;
|
|
895
|
+
return {
|
|
896
|
+
field: readSwitchField(edges, selectedNode.id),
|
|
897
|
+
routes: readBranchingRoutes(edges, selectedNode.id)
|
|
898
|
+
};
|
|
899
|
+
}, [selectedNode, edges]);
|
|
900
|
+
const handleSaveBranchingRoutes = useCallback((nodeId, value) => {
|
|
901
|
+
const nodeType = nodes.find((node) => node.id === nodeId)?.type;
|
|
902
|
+
commitHistory(historyLabels.branchRoutes);
|
|
903
|
+
setEdges((eds) => nodeType === "switch" ? applySwitchRoutes(eds, nodeId, value) : applyIfElseRoutes(eds, nodeId, value.routes));
|
|
904
|
+
scheduleAutosave();
|
|
905
|
+
}, [nodes, commitHistory, historyLabels.branchRoutes, scheduleAutosave]);
|
|
906
|
+
const routeOrderValue = useMemo(() => {
|
|
907
|
+
if (!selectedNode || isBranchingNodeType(selectedNode.type)) return void 0;
|
|
908
|
+
return readRouteOrder(edges, selectedNode.id);
|
|
909
|
+
}, [selectedNode, edges]);
|
|
910
|
+
const normalizedRouteNodesRef = React.useRef(/* @__PURE__ */ new Set());
|
|
911
|
+
const normalizeRoutesOnFirstEdit = useCallback((nodeId) => {
|
|
912
|
+
if (!canNormalizeRoutePriorities(source)) return;
|
|
913
|
+
if (normalizedRouteNodesRef.current.has(nodeId)) return;
|
|
914
|
+
normalizedRouteNodesRef.current.add(nodeId);
|
|
915
|
+
setEdges((eds) => normalizeRoutePriorities(eds, nodeId));
|
|
916
|
+
}, [source]);
|
|
917
|
+
const handleSaveRouteOrder = useCallback((nodeId, entries) => {
|
|
918
|
+
if (isCodeOnly) return;
|
|
919
|
+
commitHistory(historyLabels.routeOrder);
|
|
920
|
+
normalizeRoutesOnFirstEdit(nodeId);
|
|
921
|
+
setEdges((eds) => applyRouteOrder(eds, nodeId, entries));
|
|
922
|
+
scheduleAutosave();
|
|
923
|
+
}, [isCodeOnly, normalizeRoutesOnFirstEdit, commitHistory, historyLabels.routeOrder, scheduleAutosave]);
|
|
179
924
|
const handleSaveEdge = useCallback((edgeId, updates) => {
|
|
925
|
+
commitHistory(historyLabels.editRoute);
|
|
180
926
|
setEdges(
|
|
181
927
|
(eds) => eds.map(
|
|
182
928
|
(edge) => edge.id === edgeId ? { ...edge, data: { ...edge.data, ...updates } } : edge
|
|
183
929
|
)
|
|
184
930
|
);
|
|
185
931
|
flash("Transition updated successfully", "success");
|
|
186
|
-
}, []);
|
|
932
|
+
}, [commitHistory, historyLabels.editRoute]);
|
|
187
933
|
const handleDeleteEdge = useCallback(async (edgeId) => {
|
|
188
|
-
|
|
934
|
+
const before = captureDocument();
|
|
935
|
+
const deleted = await performDeleteEdgeFlow(edgeId, {
|
|
189
936
|
confirm,
|
|
190
937
|
t,
|
|
191
938
|
setShowEdgeDialog,
|
|
@@ -193,9 +940,54 @@ function VisualEditorPage() {
|
|
|
193
940
|
setEdges,
|
|
194
941
|
notifyDeleted: () => flash("Transition deleted successfully", "success")
|
|
195
942
|
});
|
|
196
|
-
|
|
943
|
+
if (deleted) commitCapturedDocument(before, historyLabels.deleteRoute);
|
|
944
|
+
}, [confirm, t, captureDocument, commitCapturedDocument, historyLabels.deleteRoute]);
|
|
945
|
+
const handleSaveAnnotation = useCallback((nodeId, updates) => {
|
|
946
|
+
if (isCodeOnly) return;
|
|
947
|
+
commitHistory(historyLabels.editAnnotation);
|
|
948
|
+
setNodes((nds) => updateAnnotationNode(nds, nodeId, updates));
|
|
949
|
+
scheduleAutosave();
|
|
950
|
+
}, [isCodeOnly, commitHistory, historyLabels.editAnnotation, scheduleAutosave]);
|
|
951
|
+
const handleDeleteAnnotation = useCallback(async (nodeId) => {
|
|
952
|
+
if (isCodeOnly) return;
|
|
953
|
+
const before = captureDocument();
|
|
954
|
+
setShowAnnotationDialog(false);
|
|
955
|
+
setSelectedAnnotation(null);
|
|
956
|
+
const confirmed = await confirm({
|
|
957
|
+
title: t("workflows.annotations.confirmDeleteTitle", "Delete annotation?"),
|
|
958
|
+
text: t("workflows.annotations.confirmDeleteText", "The note or group is removed from the canvas. Nothing the workflow executes changes."),
|
|
959
|
+
variant: "destructive"
|
|
960
|
+
});
|
|
961
|
+
if (!confirmed) return;
|
|
962
|
+
commitCapturedDocument(before, historyLabels.deleteAnnotation);
|
|
963
|
+
setNodes((nds) => removeAnnotationNode(nds, nodeId));
|
|
964
|
+
scheduleAutosave();
|
|
965
|
+
}, [isCodeOnly, captureDocument, commitCapturedDocument, historyLabels.deleteAnnotation, confirm, scheduleAutosave, t]);
|
|
966
|
+
const handleToggleGroupCollapsed = useCallback((nodeId) => {
|
|
967
|
+
if (isCodeOnly) return;
|
|
968
|
+
const node = nodesRef.current.find((candidate) => candidate.id === nodeId);
|
|
969
|
+
if (!node) return;
|
|
970
|
+
const collapsed = node.data?.collapsed === true;
|
|
971
|
+
commitHistory(historyLabels.toggleGroup);
|
|
972
|
+
setNodes((nds) => updateAnnotationNode(nds, nodeId, { collapsed: !collapsed }));
|
|
973
|
+
scheduleAutosave();
|
|
974
|
+
}, [isCodeOnly, commitHistory, historyLabels.toggleGroup, scheduleAutosave]);
|
|
975
|
+
useEffect(() => {
|
|
976
|
+
const onToggle = (event) => {
|
|
977
|
+
const nodeId = event.detail?.nodeId;
|
|
978
|
+
if (typeof nodeId === "string") handleToggleGroupCollapsed(nodeId);
|
|
979
|
+
};
|
|
980
|
+
window.addEventListener(WORKFLOW_GROUP_TOGGLE_EVENT, onToggle);
|
|
981
|
+
return () => window.removeEventListener(WORKFLOW_GROUP_TOGGLE_EVENT, onToggle);
|
|
982
|
+
}, [handleToggleGroupCollapsed]);
|
|
197
983
|
const handleDeleteNode = useCallback(async (nodeId) => {
|
|
198
|
-
|
|
984
|
+
const annotation = nodesRef.current.find((candidate) => candidate.id === nodeId && isAnnotationNode(candidate));
|
|
985
|
+
if (annotation) {
|
|
986
|
+
await handleDeleteAnnotation(nodeId);
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
const before = captureDocument();
|
|
990
|
+
const deleted = await performDeleteNodeFlow(nodeId, {
|
|
199
991
|
nodes,
|
|
200
992
|
confirm,
|
|
201
993
|
t,
|
|
@@ -205,100 +997,569 @@ function VisualEditorPage() {
|
|
|
205
997
|
setEdges,
|
|
206
998
|
notifyDeleted: () => flash("Step deleted successfully", "success")
|
|
207
999
|
});
|
|
208
|
-
|
|
1000
|
+
if (deleted) commitCapturedDocument(before, historyLabels.deleteStep);
|
|
1001
|
+
}, [confirm, nodes, t, captureDocument, commitCapturedDocument, historyLabels.deleteStep, handleDeleteAnnotation]);
|
|
1002
|
+
const handleConvertNodeType = useCallback(async (nodeId, targetType) => {
|
|
1003
|
+
if (isCodeOnly) return;
|
|
1004
|
+
const node = nodesRef.current.find((candidate) => candidate.id === nodeId);
|
|
1005
|
+
if (!node) return;
|
|
1006
|
+
const before = captureDocument();
|
|
1007
|
+
const result = convertStepType({ type: node.type, data: node.data }, targetType);
|
|
1008
|
+
if (!result.ok) {
|
|
1009
|
+
flash(t("workflows.stepConversion.unavailable", "This step type cannot be changed."), "error");
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
setShowNodeDialog(false);
|
|
1013
|
+
setSelectedNode(null);
|
|
1014
|
+
const confirmed = await confirm({
|
|
1015
|
+
title: t("workflows.stepConversion.confirmTitle", "Change step type?"),
|
|
1016
|
+
text: result.quarantined.length > 0 ? t(
|
|
1017
|
+
"workflows.stepConversion.confirmWithQuarantine",
|
|
1018
|
+
"The step keeps its name, position and routes. {count} setting(s) the new type cannot execute move to Unmapped configuration: {keys}",
|
|
1019
|
+
{ count: String(result.quarantined.length), keys: result.quarantined.join(", ") }
|
|
1020
|
+
) : t("workflows.stepConversion.confirm", "The step keeps its name, position and routes.")
|
|
1021
|
+
});
|
|
1022
|
+
if (!confirmed) return;
|
|
1023
|
+
commitCapturedDocument(before, historyLabels.convert);
|
|
1024
|
+
setNodes(
|
|
1025
|
+
(nds) => nds.map(
|
|
1026
|
+
(candidate) => candidate.id === nodeId ? { ...candidate, type: result.type, data: { ...result.data, badge: getBadgeForNodeType(result.type) } } : candidate
|
|
1027
|
+
)
|
|
1028
|
+
);
|
|
1029
|
+
scheduleAutosave();
|
|
1030
|
+
flash(
|
|
1031
|
+
result.quarantined.length > 0 ? t("workflows.stepConversion.convertedWithQuarantine", "Step type changed; {count} setting(s) parked as unmapped configuration", { count: String(result.quarantined.length) }) : t("workflows.stepConversion.converted", "Step type changed"),
|
|
1032
|
+
result.quarantined.length > 0 ? "warning" : "success"
|
|
1033
|
+
);
|
|
1034
|
+
}, [isCodeOnly, confirm, captureDocument, commitCapturedDocument, historyLabels.convert, scheduleAutosave, t]);
|
|
1035
|
+
useEffect(() => {
|
|
1036
|
+
if (isCodeOnly) return;
|
|
1037
|
+
const onNodeDelete = (event) => {
|
|
1038
|
+
const nodeId = event.detail?.nodeId;
|
|
1039
|
+
if (typeof nodeId === "string") void handleDeleteNode(nodeId);
|
|
1040
|
+
};
|
|
1041
|
+
window.addEventListener(WORKFLOW_NODE_DELETE_EVENT, onNodeDelete);
|
|
1042
|
+
return () => window.removeEventListener(WORKFLOW_NODE_DELETE_EVENT, onNodeDelete);
|
|
1043
|
+
}, [isCodeOnly, handleDeleteNode]);
|
|
1044
|
+
useEffect(() => {
|
|
1045
|
+
const onChipOpen = (event) => {
|
|
1046
|
+
const detail = event.detail;
|
|
1047
|
+
if (detail?.edgeId) handleRouteChipOpen(detail.edgeId, detail.section);
|
|
1048
|
+
};
|
|
1049
|
+
window.addEventListener(WORKFLOW_ROUTE_CHIP_EVENT, onChipOpen);
|
|
1050
|
+
return () => window.removeEventListener(WORKFLOW_ROUTE_CHIP_EVENT, onChipOpen);
|
|
1051
|
+
}, [handleRouteChipOpen]);
|
|
1052
|
+
useEffect(() => {
|
|
1053
|
+
const onActivityOpen = (event) => {
|
|
1054
|
+
if (isCodeOnly) return;
|
|
1055
|
+
const detail = event.detail;
|
|
1056
|
+
if (detail?.edgeId && detail?.activityId) {
|
|
1057
|
+
setActivityEdit({ edgeId: detail.edgeId, activityId: detail.activityId });
|
|
1058
|
+
}
|
|
1059
|
+
};
|
|
1060
|
+
window.addEventListener(WORKFLOW_ROUTE_ACTIVITY_EVENT, onActivityOpen);
|
|
1061
|
+
return () => window.removeEventListener(WORKFLOW_ROUTE_ACTIVITY_EVENT, onActivityOpen);
|
|
1062
|
+
}, [isCodeOnly]);
|
|
209
1063
|
const handleConnect = useCallback((connection) => {
|
|
210
|
-
const
|
|
211
|
-
|
|
1064
|
+
const classification = classifyConnection(connection);
|
|
1065
|
+
if (classification.kind === "data-ignored") {
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
1068
|
+
if (classification.kind === "data-mapping") {
|
|
1069
|
+
const { targetNodeId, childPortKey, parentPath } = classification;
|
|
1070
|
+
commitHistory(historyLabels.connect);
|
|
1071
|
+
setNodes((nds) => applyInputMappingToNodes(nds, targetNodeId, childPortKey, parentPath));
|
|
1072
|
+
const dataEdge = buildDataMappingEdge(connection, childPortKey);
|
|
1073
|
+
setEdges((eds) => appendWorkflowEdge(eds.filter((e) => e.id !== dataEdge.id), dataEdge));
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1076
|
+
const routeKind = findRouteKindDescriptorForHandle(connection.sourceHandle);
|
|
1077
|
+
const sourceNode = nodesRef.current.find((node) => node.id === connection.source);
|
|
1078
|
+
const decisions = sourceNode?.data.decisions;
|
|
1079
|
+
const decisionTransitionId = isDecisionSourceHandle(decisions, connection.sourceHandle) ? connection.sourceHandle : null;
|
|
1080
|
+
if (decisionTransitionId && edgesRef.current.some(
|
|
1081
|
+
(edge) => edge.source === connection.source && (edge.id === decisionTransitionId || edge.sourceHandle === decisionTransitionId)
|
|
1082
|
+
)) {
|
|
1083
|
+
return;
|
|
1084
|
+
}
|
|
1085
|
+
const routeData = routeKind ? {
|
|
1086
|
+
kind: routeKind.kind,
|
|
1087
|
+
...routeKind.discriminatorFields({
|
|
1088
|
+
sourceHandle: connection.sourceHandle
|
|
1089
|
+
})
|
|
1090
|
+
} : {};
|
|
1091
|
+
const newEdge = buildWorkflowRouteEdge({
|
|
1092
|
+
id: decisionTransitionId ?? generateTransitionId(),
|
|
212
1093
|
source: connection.source,
|
|
213
1094
|
target: connection.target,
|
|
214
|
-
|
|
1095
|
+
sourceHandle: routeKind ? connection.sourceHandle : decisionTransitionId,
|
|
215
1096
|
data: {
|
|
216
1097
|
trigger: "auto",
|
|
217
1098
|
preConditions: [],
|
|
218
1099
|
postConditions: [],
|
|
219
1100
|
activities: [],
|
|
220
|
-
label: ""
|
|
1101
|
+
label: "",
|
|
1102
|
+
...routeData
|
|
221
1103
|
}
|
|
222
|
-
};
|
|
1104
|
+
});
|
|
1105
|
+
commitHistory(historyLabels.connect);
|
|
223
1106
|
setEdges((eds) => appendWorkflowEdge(eds, newEdge));
|
|
224
|
-
}, []);
|
|
225
|
-
const
|
|
1107
|
+
}, [commitHistory, historyLabels.connect]);
|
|
1108
|
+
const describeReattachRejection = useCallback((rejection) => {
|
|
1109
|
+
switch (rejection.code) {
|
|
1110
|
+
case "selfLoop":
|
|
1111
|
+
return t("workflows.reattach.rejected.selfLoop", "A route cannot start and end on the same step.");
|
|
1112
|
+
case "duplicateRoute":
|
|
1113
|
+
return t("workflows.reattach.rejected.duplicateRoute", "A route already connects those two steps.");
|
|
1114
|
+
case "dataMappingRoute":
|
|
1115
|
+
return t("workflows.reattach.rejected.dataMappingRoute", "Data mapping links are edited in the step configuration, not on the canvas.");
|
|
1116
|
+
case "errorHandleUnsupported":
|
|
1117
|
+
return t("workflows.reattach.rejected.errorHandleUnsupported", "An error route can only start at a step that can fail.");
|
|
1118
|
+
case "kindHandleUnsupported":
|
|
1119
|
+
return t("workflows.reattach.rejected.kindHandleUnsupported", "This route cannot start at that type of step.");
|
|
1120
|
+
case "graphInvalid":
|
|
1121
|
+
return t("workflows.reattach.rejected.graphInvalid", "That target would break the workflow: {reason}", { reason: rejection.detail ?? "" });
|
|
1122
|
+
case "forkJoinInvalid":
|
|
1123
|
+
return t("workflows.reattach.rejected.forkJoinInvalid", "That target would break the parallel branch structure: {reason}", { reason: rejection.detail ?? "" });
|
|
1124
|
+
default:
|
|
1125
|
+
return t("workflows.reattach.rejected.unsupported", "That route cannot be re-targeted.");
|
|
1126
|
+
}
|
|
1127
|
+
}, [t]);
|
|
1128
|
+
const handleReconnect = useCallback((oldEdge, connection) => {
|
|
1129
|
+
if (isCodeOnly) return;
|
|
1130
|
+
const result = reattachWorkflowEdge(nodesRef.current, edgesRef.current, oldEdge.id, connection);
|
|
1131
|
+
if (!result.ok) {
|
|
1132
|
+
flash(describeReattachRejection(result), "error");
|
|
1133
|
+
return;
|
|
1134
|
+
}
|
|
1135
|
+
commitHistory(historyLabels.reattach);
|
|
1136
|
+
setEdges(result.edges);
|
|
1137
|
+
scheduleAutosave();
|
|
1138
|
+
flash(t("workflows.reattach.retargeted", "Route re-targeted"), "success");
|
|
1139
|
+
}, [isCodeOnly, describeReattachRejection, commitHistory, historyLabels.reattach, scheduleAutosave, t]);
|
|
1140
|
+
const errorHandlerStepOptions = useMemo(
|
|
1141
|
+
() => nodes.filter((node) => node.type !== "start" && node.type !== "end").map((node) => ({
|
|
1142
|
+
stepId: node.id,
|
|
1143
|
+
label: typeof node.data?.label === "string" && node.data.label ? node.data.label : node.id
|
|
1144
|
+
})),
|
|
1145
|
+
[nodes]
|
|
1146
|
+
);
|
|
1147
|
+
const { events: availableEvents } = useAvailableEvents();
|
|
1148
|
+
const triggerPayloadContracts = useMemo(
|
|
1149
|
+
() => buildTriggerPayloadContracts(triggers, availableEvents),
|
|
1150
|
+
[triggers, availableEvents]
|
|
1151
|
+
);
|
|
1152
|
+
const dialogLedger = useMemo(() => {
|
|
1153
|
+
if (!showNodeDialog && !showEdgeDialog) return null;
|
|
1154
|
+
try {
|
|
1155
|
+
const definitionData = buildDefinitionPayload({
|
|
1156
|
+
graphDefinition: graphToDefinition(nodes, edges),
|
|
1157
|
+
triggers,
|
|
1158
|
+
contextSchema,
|
|
1159
|
+
io: definitionIo
|
|
1160
|
+
});
|
|
1161
|
+
return computeClientContextLedger(definitionData, triggerPayloadContracts);
|
|
1162
|
+
} catch {
|
|
1163
|
+
return null;
|
|
1164
|
+
}
|
|
1165
|
+
}, [showNodeDialog, showEdgeDialog, nodes, edges, triggers, contextSchema, definitionIo, triggerPayloadContracts]);
|
|
1166
|
+
const nodeDialogLedgerEntries = useMemo(() => {
|
|
1167
|
+
if (!dialogLedger || !selectedNode) return void 0;
|
|
1168
|
+
const stepEntries = dialogLedger.steps[selectedNode.id]?.entries;
|
|
1169
|
+
if (stepEntries && stepEntries.length > 0) return stepEntries;
|
|
1170
|
+
const startNode = nodes.find((candidate) => candidate.type === "start");
|
|
1171
|
+
const startEntries = startNode ? dialogLedger.steps[startNode.id]?.entries : void 0;
|
|
1172
|
+
return startEntries && startEntries.length > 0 ? startEntries : stepEntries;
|
|
1173
|
+
}, [dialogLedger, nodes, selectedNode]);
|
|
1174
|
+
const editorSamples = useMemo(() => {
|
|
1175
|
+
const editorValue = loadedMetadata?.editor;
|
|
1176
|
+
if (!editorValue || typeof editorValue !== "object" || Array.isArray(editorValue)) return void 0;
|
|
1177
|
+
const samplesValue = editorValue.samples;
|
|
1178
|
+
if (!samplesValue || typeof samplesValue !== "object" || Array.isArray(samplesValue)) return void 0;
|
|
1179
|
+
return samplesValue;
|
|
1180
|
+
}, [loadedMetadata]);
|
|
1181
|
+
const handlePinSample = useCallback((stepId, data) => {
|
|
1182
|
+
commitHistory(historyLabels.pinSample);
|
|
1183
|
+
setLoadedMetadata((previous) => {
|
|
1184
|
+
const base = { ...previous ?? {} };
|
|
1185
|
+
const editorValue = base.editor;
|
|
1186
|
+
const editor = editorValue && typeof editorValue === "object" && !Array.isArray(editorValue) ? { ...editorValue } : {};
|
|
1187
|
+
const samplesValue = editor.samples;
|
|
1188
|
+
const samples = samplesValue && typeof samplesValue === "object" && !Array.isArray(samplesValue) ? { ...samplesValue } : {};
|
|
1189
|
+
samples[stepId] = { pinnedAt: (/* @__PURE__ */ new Date()).toISOString(), source: "test", data };
|
|
1190
|
+
editor.samples = samples;
|
|
1191
|
+
base.editor = editor;
|
|
1192
|
+
return base;
|
|
1193
|
+
});
|
|
1194
|
+
flash(t("workflows.testStep.pinnedFlash", "Sample pinned \u2014 it is stored with the definition on save"), "success");
|
|
1195
|
+
}, [commitHistory, historyLabels.pinSample, t]);
|
|
1196
|
+
const handleUnpinSample = useCallback((stepId) => {
|
|
1197
|
+
commitHistory(historyLabels.unpinSample);
|
|
1198
|
+
setLoadedMetadata((previous) => {
|
|
1199
|
+
if (!previous) return previous;
|
|
1200
|
+
const editorValue = previous.editor;
|
|
1201
|
+
if (!editorValue || typeof editorValue !== "object" || Array.isArray(editorValue)) return previous;
|
|
1202
|
+
const editor = { ...editorValue };
|
|
1203
|
+
const samplesValue = editor.samples;
|
|
1204
|
+
if (!samplesValue || typeof samplesValue !== "object" || Array.isArray(samplesValue)) return previous;
|
|
1205
|
+
const samples = { ...samplesValue };
|
|
1206
|
+
if (!(stepId in samples)) return previous;
|
|
1207
|
+
delete samples[stepId];
|
|
1208
|
+
if (Object.keys(samples).length > 0) editor.samples = samples;
|
|
1209
|
+
else delete editor.samples;
|
|
1210
|
+
const base = { ...previous };
|
|
1211
|
+
if (Object.keys(editor).length > 0) base.editor = editor;
|
|
1212
|
+
else delete base.editor;
|
|
1213
|
+
return Object.keys(base).length > 0 ? base : null;
|
|
1214
|
+
});
|
|
1215
|
+
}, [commitHistory, historyLabels.unpinSample]);
|
|
1216
|
+
const startFixtures = useMemo(() => {
|
|
1217
|
+
const editorValue = loadedMetadata?.editor;
|
|
1218
|
+
if (!editorValue || typeof editorValue !== "object" || Array.isArray(editorValue)) return void 0;
|
|
1219
|
+
const fixturesValue = editorValue.fixtures;
|
|
1220
|
+
if (!fixturesValue || typeof fixturesValue !== "object" || Array.isArray(fixturesValue)) return void 0;
|
|
1221
|
+
return fixturesValue;
|
|
1222
|
+
}, [loadedMetadata]);
|
|
1223
|
+
const startFixtureList = useMemo(() => listStartFixtures(startFixtures), [startFixtures]);
|
|
1224
|
+
const handleSaveFixture = useCallback(() => {
|
|
1225
|
+
let context;
|
|
1226
|
+
try {
|
|
1227
|
+
const parsed = startContext.trim() ? JSON.parse(startContext) : {};
|
|
1228
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
1229
|
+
throw new Error("[internal] context must be a JSON object");
|
|
1230
|
+
}
|
|
1231
|
+
context = parsed;
|
|
1232
|
+
} catch {
|
|
1233
|
+
flash(t("workflows.startInstance.invalidJson"), "error");
|
|
1234
|
+
return;
|
|
1235
|
+
}
|
|
1236
|
+
const result = upsertStartFixture(startFixtures, fixtureName, context, (/* @__PURE__ */ new Date()).toISOString());
|
|
1237
|
+
if (!result.ok) {
|
|
1238
|
+
flash(t(`workflows.fixtures.errors.${result.reason}`), "error");
|
|
1239
|
+
return;
|
|
1240
|
+
}
|
|
1241
|
+
commitHistory(historyLabels.saveFixture);
|
|
1242
|
+
setLoadedMetadata((previous) => {
|
|
1243
|
+
const base = { ...previous ?? {} };
|
|
1244
|
+
const editorValue = base.editor;
|
|
1245
|
+
const editor = editorValue && typeof editorValue === "object" && !Array.isArray(editorValue) ? { ...editorValue } : {};
|
|
1246
|
+
editor.fixtures = result.fixtures;
|
|
1247
|
+
base.editor = editor;
|
|
1248
|
+
return base;
|
|
1249
|
+
});
|
|
1250
|
+
setFixtureName("");
|
|
1251
|
+
flash(t("workflows.fixtures.saved"), "success");
|
|
1252
|
+
}, [startContext, startFixtures, fixtureName, commitHistory, historyLabels.saveFixture, t]);
|
|
1253
|
+
const handleApplyFixture = useCallback((name) => {
|
|
1254
|
+
const fixture = startFixtures?.[name];
|
|
1255
|
+
if (!fixture) return;
|
|
1256
|
+
setStartContext(JSON.stringify(fixture.context, null, 2));
|
|
1257
|
+
setFixtureName(fixture.name);
|
|
1258
|
+
}, [startFixtures]);
|
|
1259
|
+
const handleDeleteFixture = useCallback((name) => {
|
|
1260
|
+
commitHistory(historyLabels.deleteFixture);
|
|
1261
|
+
setLoadedMetadata((previous) => {
|
|
1262
|
+
if (!previous) return previous;
|
|
1263
|
+
const editorValue = previous.editor;
|
|
1264
|
+
if (!editorValue || typeof editorValue !== "object" || Array.isArray(editorValue)) return previous;
|
|
1265
|
+
const editor = { ...editorValue };
|
|
1266
|
+
const next = removeStartFixture(editor.fixtures, name);
|
|
1267
|
+
if (Object.keys(next).length > 0) editor.fixtures = next;
|
|
1268
|
+
else delete editor.fixtures;
|
|
1269
|
+
const base = { ...previous };
|
|
1270
|
+
if (Object.keys(editor).length > 0) base.editor = editor;
|
|
1271
|
+
else delete base.editor;
|
|
1272
|
+
return Object.keys(base).length > 0 ? base : null;
|
|
1273
|
+
});
|
|
1274
|
+
}, [commitHistory, historyLabels.deleteFixture]);
|
|
1275
|
+
const edgeDialogLedgerEntries = useMemo(
|
|
1276
|
+
() => dialogLedger && selectedEdge ? dialogLedger.steps[selectedEdge.target]?.entries : void 0,
|
|
1277
|
+
[dialogLedger, selectedEdge]
|
|
1278
|
+
);
|
|
1279
|
+
const translateIssue = useCallback(
|
|
1280
|
+
(key, fallback, params) => t(key, fallback, params),
|
|
1281
|
+
[t]
|
|
1282
|
+
);
|
|
1283
|
+
const evaluateWorkflowIssues = useCallback(() => {
|
|
226
1284
|
const graphErrors = validateWorkflowGraph(nodes, edges);
|
|
227
|
-
|
|
1285
|
+
let zodIssues = [];
|
|
1286
|
+
let configWarnings = [];
|
|
1287
|
+
let schemaFailureMessage = null;
|
|
1288
|
+
let flowLogicDefinition = null;
|
|
1289
|
+
let flowLogicLedger;
|
|
228
1290
|
try {
|
|
229
1291
|
const definitionData = graphToDefinition(nodes, edges, { includePositions: true });
|
|
230
1292
|
const result = workflowDefinitionDataSchema.safeParse(definitionData);
|
|
231
1293
|
if (!result.success) {
|
|
232
|
-
result.error.issues
|
|
233
|
-
allErrors.push({
|
|
234
|
-
type: "error",
|
|
235
|
-
message: `Schema validation: ${issue.path.join(".")} - ${issue.message}`
|
|
236
|
-
});
|
|
237
|
-
});
|
|
1294
|
+
zodIssues = result.error.issues;
|
|
238
1295
|
}
|
|
1296
|
+
const ledgerDefinition = buildDefinitionPayload({ graphDefinition: definitionData, triggers, contextSchema, io: definitionIo, interpolation, errorHandler });
|
|
1297
|
+
flowLogicDefinition = ledgerDefinition;
|
|
1298
|
+
flowLogicLedger = computeClientContextLedger(ledgerDefinition, triggerPayloadContracts);
|
|
1299
|
+
configWarnings = [
|
|
1300
|
+
...collectActivityConfigWarnings(definitionData),
|
|
1301
|
+
...collectContextRefWarnings(ledgerDefinition, t, triggerPayloadContracts)
|
|
1302
|
+
];
|
|
239
1303
|
} catch (error) {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
1304
|
+
schemaFailureMessage = error instanceof Error ? error.message : String(error);
|
|
1305
|
+
}
|
|
1306
|
+
const issues = collectValidationIssues({
|
|
1307
|
+
graphErrors,
|
|
1308
|
+
zodIssues,
|
|
1309
|
+
configWarnings,
|
|
1310
|
+
nodes,
|
|
1311
|
+
edges,
|
|
1312
|
+
definition: flowLogicDefinition,
|
|
1313
|
+
ledger: flowLogicLedger,
|
|
1314
|
+
translate: translateIssue
|
|
1315
|
+
});
|
|
1316
|
+
if (schemaFailureMessage) {
|
|
1317
|
+
issues.unshift({
|
|
1318
|
+
id: "schema-exception",
|
|
1319
|
+
severity: "error",
|
|
1320
|
+
message: t("workflows.visualEditor.problems.schemaValidationFailed", "Schema validation failed: {message}", { message: schemaFailureMessage })
|
|
243
1321
|
});
|
|
244
1322
|
}
|
|
245
|
-
|
|
246
|
-
|
|
1323
|
+
return issues;
|
|
1324
|
+
}, [nodes, edges, triggers, contextSchema, definitionIo, interpolation, errorHandler, triggerPayloadContracts, translateIssue, t]);
|
|
1325
|
+
const runProblemsPass = useCallback(() => {
|
|
1326
|
+
const issues = evaluateWorkflowIssues();
|
|
1327
|
+
setProblems(issues);
|
|
1328
|
+
if (issues.length === 0) {
|
|
1329
|
+
setShowProblems(false);
|
|
1330
|
+
flash(t("workflows.visualEditor.problems.validationPassed", "Validation passed! Your workflow is valid and ready to save."), "success");
|
|
247
1331
|
} else {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
const
|
|
251
|
-
flash(
|
|
1332
|
+
setShowProblems(true);
|
|
1333
|
+
setProblemsCollapsed(false);
|
|
1334
|
+
const { errors, warnings } = countIssuesBySeverity(issues);
|
|
1335
|
+
flash(
|
|
1336
|
+
t("workflows.visualEditor.problems.summary", "Validation found {errors} error(s) and {warnings} warning(s).", { errors, warnings }),
|
|
1337
|
+
errors > 0 ? "error" : "warning"
|
|
1338
|
+
);
|
|
252
1339
|
}
|
|
253
|
-
}, [
|
|
1340
|
+
}, [evaluateWorkflowIssues, t]);
|
|
1341
|
+
const handleValidate = runProblemsPass;
|
|
1342
|
+
const codeViewJson = useMemo(() => {
|
|
1343
|
+
if (!showCodeView) return "";
|
|
1344
|
+
try {
|
|
1345
|
+
const definitionData = buildDefinitionPayload({
|
|
1346
|
+
graphDefinition: graphToDefinition(nodes, edges, { includePositions: true }),
|
|
1347
|
+
triggers,
|
|
1348
|
+
contextSchema,
|
|
1349
|
+
io: definitionIo,
|
|
1350
|
+
interpolation,
|
|
1351
|
+
errorHandler
|
|
1352
|
+
});
|
|
1353
|
+
return JSON.stringify(definitionData, null, 2);
|
|
1354
|
+
} catch (error) {
|
|
1355
|
+
return t(
|
|
1356
|
+
"workflows.visualEditor.codeView.assemblyFailed",
|
|
1357
|
+
"The definition could not be assembled: {message}",
|
|
1358
|
+
{ message: error instanceof Error ? error.message : String(error) }
|
|
1359
|
+
);
|
|
1360
|
+
}
|
|
1361
|
+
}, [showCodeView, nodes, edges, triggers, contextSchema, definitionIo, interpolation, errorHandler, t]);
|
|
1362
|
+
const codeViewIssues = useMemo(
|
|
1363
|
+
() => showCodeView ? evaluateWorkflowIssues() : [],
|
|
1364
|
+
[showCodeView, evaluateWorkflowIssues]
|
|
1365
|
+
);
|
|
1366
|
+
const [codeDraft, setCodeDraft] = useState(null);
|
|
1367
|
+
const codeDraftText = codeDraft ?? codeViewJson;
|
|
1368
|
+
const definitionApplyGate = useMemo(() => ({
|
|
1369
|
+
parseDefinition: (parsed) => {
|
|
1370
|
+
const result = workflowDefinitionDataSchema.safeParse(parsed);
|
|
1371
|
+
if (result.success) return { ok: true, definition: result.data };
|
|
1372
|
+
return {
|
|
1373
|
+
ok: false,
|
|
1374
|
+
messages: result.error.issues.map((issue) => `${issue.path.join(".") || "(root)"}: ${issue.message}`)
|
|
1375
|
+
};
|
|
1376
|
+
},
|
|
1377
|
+
// Warnings never block, exactly as they never block a Save; only graph
|
|
1378
|
+
// ERRORS do, because a canvas holding a graph the engine rejects is worse
|
|
1379
|
+
// than no apply — the author has lost the text they typed.
|
|
1380
|
+
// `autoLayout: false` on purpose: whether a definition is VALID cannot
|
|
1381
|
+
// depend on running a layout engine over it, and skipping dagre keeps this
|
|
1382
|
+
// memo cheap enough to run on every keystroke.
|
|
1383
|
+
validateGraph: (definition) => {
|
|
1384
|
+
const graph = definitionToGraph(definition, { autoLayout: false });
|
|
1385
|
+
return validateWorkflowGraph(graph.nodes, graph.edges).filter((issue) => issue.type === "error").map((issue) => issue.message);
|
|
1386
|
+
}
|
|
1387
|
+
}), []);
|
|
1388
|
+
const codeApplyDecision = useMemo(
|
|
1389
|
+
() => evaluateCodeViewDraft({
|
|
1390
|
+
draftText: codeDraftText,
|
|
1391
|
+
canvasText: codeViewJson,
|
|
1392
|
+
...definitionApplyGate
|
|
1393
|
+
}),
|
|
1394
|
+
[codeDraftText, codeViewJson, definitionApplyGate]
|
|
1395
|
+
);
|
|
1396
|
+
const codeDraftStatus = useMemo(() => describeCodeViewDraft(codeApplyDecision), [codeApplyDecision]);
|
|
1397
|
+
const codeIssueLocations = useMemo(() => {
|
|
1398
|
+
if (!showCodeView) return { severityByLine: /* @__PURE__ */ new Map(), lineByIssueId: /* @__PURE__ */ new Map() };
|
|
1399
|
+
let parsed;
|
|
1400
|
+
try {
|
|
1401
|
+
parsed = JSON.parse(codeDraftText);
|
|
1402
|
+
} catch {
|
|
1403
|
+
return { severityByLine: /* @__PURE__ */ new Map(), lineByIssueId: /* @__PURE__ */ new Map() };
|
|
1404
|
+
}
|
|
1405
|
+
const locations = locateDefinitionJsonEntities(codeDraftText, parsed);
|
|
1406
|
+
const located = locateIssues(codeViewIssues, locations);
|
|
1407
|
+
return {
|
|
1408
|
+
severityByLine: severityByLine(located),
|
|
1409
|
+
lineByIssueId: new Map(located.map((issue) => [issue.issueId, issue.line]))
|
|
1410
|
+
};
|
|
1411
|
+
}, [showCodeView, codeDraftText, codeViewIssues]);
|
|
1412
|
+
const applyDefinitionDocument = useCallback((definition, label) => {
|
|
1413
|
+
commitHistory(label);
|
|
1414
|
+
const graph = definitionToGraph(definition, { autoLayout: true });
|
|
1415
|
+
setNodes(graph.nodes);
|
|
1416
|
+
setEdges(graph.edges);
|
|
1417
|
+
setTriggers(definition.triggers ?? []);
|
|
1418
|
+
setContextSchema(definition.contextSchema);
|
|
1419
|
+
setDefinitionIo(definition.io);
|
|
1420
|
+
setInterpolation(definition.interpolation);
|
|
1421
|
+
setErrorHandler(definition.errorHandler);
|
|
1422
|
+
scheduleAutosave();
|
|
1423
|
+
}, [commitHistory, scheduleAutosave]);
|
|
1424
|
+
const handleApplyCodeDraft = useCallback(() => {
|
|
1425
|
+
if (!codeApplyDecision.ok) return;
|
|
1426
|
+
applyDefinitionDocument(codeApplyDecision.definition, historyLabels.applyCode);
|
|
1427
|
+
setCodeDraft(null);
|
|
1428
|
+
flash(t("workflows.visualEditor.codeView.applied"), "success");
|
|
1429
|
+
}, [codeApplyDecision, applyDefinitionDocument, historyLabels.applyCode, t]);
|
|
1430
|
+
const [showAiDraft, setShowAiDraft] = useState(false);
|
|
1431
|
+
const [problemsPassToken, setProblemsPassToken] = useState(0);
|
|
1432
|
+
const runProblemsPassRef = React.useRef(runProblemsPass);
|
|
1433
|
+
runProblemsPassRef.current = runProblemsPass;
|
|
1434
|
+
useEffect(() => {
|
|
1435
|
+
if (problemsPassToken === 0) return;
|
|
1436
|
+
runProblemsPassRef.current();
|
|
1437
|
+
}, [problemsPassToken]);
|
|
1438
|
+
const handleApplyAiDraft = useCallback((input) => {
|
|
1439
|
+
const decision = evaluateCodeViewDraft({
|
|
1440
|
+
// Serialized so the AI path and the Code view path are literally the same
|
|
1441
|
+
// call. It also proves the draft survives the JSON round trip the Code
|
|
1442
|
+
// view and every save will put it through.
|
|
1443
|
+
draftText: JSON.stringify(input.definition, null, 2),
|
|
1444
|
+
...definitionApplyGate
|
|
1445
|
+
});
|
|
1446
|
+
if (!decision.ok) {
|
|
1447
|
+
switch (decision.reason) {
|
|
1448
|
+
case "parseError":
|
|
1449
|
+
return { reason: "parseError", messages: [decision.message] };
|
|
1450
|
+
case "schemaError":
|
|
1451
|
+
case "graphError":
|
|
1452
|
+
return { reason: decision.reason, messages: decision.messages };
|
|
1453
|
+
default:
|
|
1454
|
+
return {
|
|
1455
|
+
reason: "schemaError",
|
|
1456
|
+
messages: [t("workflows.aiDraft.refusedUnknown", "The generated definition was refused.")]
|
|
1457
|
+
};
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
applyDefinitionDocument(
|
|
1461
|
+
decision.definition,
|
|
1462
|
+
buildAiCheckpointLabel(input.prompt, {
|
|
1463
|
+
prefix: t("workflows.visualEditor.history.aiDraft", "AI draft")
|
|
1464
|
+
})
|
|
1465
|
+
);
|
|
1466
|
+
setProblemsPassToken((token) => token + 1);
|
|
1467
|
+
flash(
|
|
1468
|
+
t("workflows.aiDraft.applied", "AI draft applied. Review the problems, then save when you are happy with it."),
|
|
1469
|
+
"success"
|
|
1470
|
+
);
|
|
1471
|
+
return null;
|
|
1472
|
+
}, [definitionApplyGate, applyDefinitionDocument, t]);
|
|
1473
|
+
const handleRevertCodeDraft = useCallback(() => {
|
|
1474
|
+
setCodeDraft(null);
|
|
1475
|
+
}, []);
|
|
1476
|
+
const handleCloseCodeView = useCallback(() => {
|
|
1477
|
+
setCodeDraft(null);
|
|
1478
|
+
setShowCodeView(false);
|
|
1479
|
+
}, []);
|
|
1480
|
+
const handleCopyDefinitionJson = useCallback(async () => {
|
|
1481
|
+
try {
|
|
1482
|
+
await navigator.clipboard.writeText(codeViewJson);
|
|
1483
|
+
flash(t("workflows.visualEditor.codeView.copied", "Definition JSON copied"), "success");
|
|
1484
|
+
} catch (error) {
|
|
1485
|
+
logger.warn("Workflow definition JSON copy was blocked by the browser", { err: error });
|
|
1486
|
+
flash(
|
|
1487
|
+
t(
|
|
1488
|
+
"workflows.visualEditor.codeView.copyUnavailable",
|
|
1489
|
+
"The browser blocked clipboard access, so nothing was copied \u2014 select the JSON and copy it manually."
|
|
1490
|
+
),
|
|
1491
|
+
"warning"
|
|
1492
|
+
);
|
|
1493
|
+
}
|
|
1494
|
+
}, [codeViewJson, t]);
|
|
1495
|
+
const handleProblemClick = useCallback((issue) => {
|
|
1496
|
+
if (!issue.nodeId && !issue.edgeId) return;
|
|
1497
|
+
focusRequestRef.current += 1;
|
|
1498
|
+
setFocusTarget({
|
|
1499
|
+
...issue.nodeId ? { nodeId: issue.nodeId } : { edgeId: issue.edgeId },
|
|
1500
|
+
requestId: focusRequestRef.current
|
|
1501
|
+
});
|
|
1502
|
+
}, []);
|
|
254
1503
|
const handleSave = useCallback(async () => {
|
|
255
1504
|
if (!workflowId || !workflowName) {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
const errors = validateWorkflowGraph(nodes, edges);
|
|
260
|
-
const criticalErrors = errors.filter((e) => e.type === "error");
|
|
261
|
-
if (criticalErrors.length > 0) {
|
|
262
|
-
flash(`Cannot save: ${criticalErrors.length} validation error(s) found. Please fix them first.`, "error");
|
|
1505
|
+
setMetadataOpen(true);
|
|
1506
|
+
flash(t("workflows.visualEditor.metadata.requiredFields", "Workflow ID and name are required."), "error");
|
|
263
1507
|
return;
|
|
264
1508
|
}
|
|
265
|
-
const
|
|
266
|
-
const definitionData = {
|
|
267
|
-
|
|
268
|
-
triggers
|
|
269
|
-
|
|
1509
|
+
const graphErrors = validateWorkflowGraph(nodes, edges);
|
|
1510
|
+
const definitionData = buildDefinitionPayload({
|
|
1511
|
+
graphDefinition: graphToDefinition(nodes, edges, { includePositions: true }),
|
|
1512
|
+
triggers,
|
|
1513
|
+
contextSchema,
|
|
1514
|
+
io: definitionIo,
|
|
1515
|
+
interpolation,
|
|
1516
|
+
errorHandler
|
|
1517
|
+
});
|
|
270
1518
|
const schemaResult = workflowDefinitionDataSchema.safeParse(definitionData);
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
1519
|
+
const issues = collectValidationIssues({
|
|
1520
|
+
graphErrors,
|
|
1521
|
+
zodIssues: schemaResult.success ? [] : schemaResult.error.issues,
|
|
1522
|
+
configWarnings: [
|
|
1523
|
+
...collectActivityConfigWarnings(definitionData),
|
|
1524
|
+
...collectContextRefWarnings(definitionData, t, triggerPayloadContracts)
|
|
1525
|
+
],
|
|
1526
|
+
nodes,
|
|
1527
|
+
edges,
|
|
1528
|
+
definition: definitionData,
|
|
1529
|
+
ledger: computeClientContextLedger(definitionData, triggerPayloadContracts),
|
|
1530
|
+
translate: translateIssue
|
|
1531
|
+
});
|
|
1532
|
+
setProblems(issues);
|
|
1533
|
+
const { errors } = countIssuesBySeverity(issues);
|
|
1534
|
+
if (errors > 0) {
|
|
1535
|
+
setShowProblems(true);
|
|
1536
|
+
setProblemsCollapsed(false);
|
|
1537
|
+
flash(t("workflows.visualEditor.problems.saveBlocked", "Cannot save: {count} validation error(s) found. Please fix them first.", { count: errors }), "error");
|
|
276
1538
|
return;
|
|
277
1539
|
}
|
|
278
1540
|
setIsSaving(true);
|
|
279
1541
|
try {
|
|
280
|
-
const
|
|
281
|
-
if (category) metadata2.category = category;
|
|
282
|
-
if (tags.length > 0) metadata2.tags = tags;
|
|
283
|
-
if (icon) metadata2.icon = icon;
|
|
1542
|
+
const metadataPayload = buildMetadataPayload({ loadedMetadata, tags, category, icon, definition: definitionData, annotations });
|
|
284
1543
|
const isUpdate = !!definitionId;
|
|
285
1544
|
let result;
|
|
1545
|
+
let updateBody = null;
|
|
286
1546
|
if (isUpdate) {
|
|
1547
|
+
updateBody = {
|
|
1548
|
+
workflowName,
|
|
1549
|
+
description: description || null,
|
|
1550
|
+
version,
|
|
1551
|
+
definition: definitionData,
|
|
1552
|
+
metadata: metadataPayload,
|
|
1553
|
+
enabled,
|
|
1554
|
+
effectiveFrom: effectiveFrom || null,
|
|
1555
|
+
effectiveTo: effectiveTo || null
|
|
1556
|
+
};
|
|
287
1557
|
result = await withScopedApiRequestHeaders(
|
|
288
1558
|
buildOptimisticLockHeader(updatedAt),
|
|
289
1559
|
() => apiCall(`/api/workflows/definitions/${definitionId}`, {
|
|
290
1560
|
method: "PUT",
|
|
291
1561
|
headers: { "Content-Type": "application/json" },
|
|
292
|
-
body: JSON.stringify(
|
|
293
|
-
workflowName,
|
|
294
|
-
description: description || null,
|
|
295
|
-
version,
|
|
296
|
-
definition: definitionData,
|
|
297
|
-
metadata: Object.keys(metadata2).length > 0 ? metadata2 : null,
|
|
298
|
-
enabled,
|
|
299
|
-
effectiveFrom: effectiveFrom || null,
|
|
300
|
-
effectiveTo: effectiveTo || null
|
|
301
|
-
})
|
|
1562
|
+
body: JSON.stringify(updateBody)
|
|
302
1563
|
})
|
|
303
1564
|
);
|
|
304
1565
|
} else {
|
|
@@ -311,7 +1572,7 @@ function VisualEditorPage() {
|
|
|
311
1572
|
description: description || null,
|
|
312
1573
|
version,
|
|
313
1574
|
definition: definitionData,
|
|
314
|
-
metadata:
|
|
1575
|
+
metadata: metadataPayload,
|
|
315
1576
|
enabled,
|
|
316
1577
|
effectiveFrom: effectiveFrom || null,
|
|
317
1578
|
effectiveTo: effectiveTo || null
|
|
@@ -319,27 +1580,424 @@ function VisualEditorPage() {
|
|
|
319
1580
|
});
|
|
320
1581
|
}
|
|
321
1582
|
if (!result.ok) {
|
|
1583
|
+
const activeInstanceCount = readStructuralEditConflictCount(result.status, result.result);
|
|
1584
|
+
if (activeInstanceCount !== null && updateBody) {
|
|
1585
|
+
setStructuralConflict({ activeInstanceCount, payload: updateBody });
|
|
1586
|
+
return;
|
|
1587
|
+
}
|
|
322
1588
|
const conflictError = Object.assign(new Error(t("workflows.messages.saveFailed", "Failed to save")), {
|
|
323
1589
|
status: result.status,
|
|
324
1590
|
...result.result && typeof result.result === "object" ? result.result : {}
|
|
325
1591
|
});
|
|
326
1592
|
if (!surfaceRecordConflict(conflictError, t)) {
|
|
327
|
-
flash(
|
|
1593
|
+
flash(formatWorkflowValidationError(result.result, t("workflows.messages.saveFailed", "Failed to save")), "error");
|
|
328
1594
|
}
|
|
329
1595
|
return;
|
|
330
1596
|
}
|
|
1597
|
+
setStructuralConflict(null);
|
|
1598
|
+
if (updateBody) lastAutosavedBodyRef.current = stableSerializeDefinition(updateBody);
|
|
331
1599
|
const savedDefinition = result.result?.data;
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
1600
|
+
draftSuspendedRef.current = true;
|
|
1601
|
+
setPendingDraft(null);
|
|
1602
|
+
setDraftSavedAt(null);
|
|
1603
|
+
setDraftSaveFailed(false);
|
|
1604
|
+
if (isUpdate && isServerDraftEligible(definitionId)) {
|
|
1605
|
+
void apiCall(`/api/workflows/definitions/${definitionId}/draft`, { method: "DELETE" }).catch(() => void 0);
|
|
1606
|
+
}
|
|
1607
|
+
flash(
|
|
1608
|
+
isUpdate ? t("workflows.messages.workflowUpdated", "Workflow updated successfully") : t("workflows.messages.workflowCreated", "Workflow created successfully"),
|
|
1609
|
+
"success"
|
|
1610
|
+
);
|
|
1611
|
+
if (isUpdate) {
|
|
1612
|
+
if (typeof savedDefinition?.updatedAt === "string") {
|
|
1613
|
+
setUpdatedAt(savedDefinition.updatedAt);
|
|
1614
|
+
}
|
|
1615
|
+
} else if (savedDefinition?.id) {
|
|
1616
|
+
router.replace(`/backend/definitions/visual-editor?id=${encodeURIComponent(savedDefinition.id)}`);
|
|
1617
|
+
}
|
|
336
1618
|
} catch (error) {
|
|
337
1619
|
logger.error("Error saving workflow definition", { err: error });
|
|
338
|
-
flash("Failed to save
|
|
1620
|
+
flash(t("workflows.messages.saveFailed", "Failed to save"), "error");
|
|
339
1621
|
} finally {
|
|
340
1622
|
setIsSaving(false);
|
|
341
1623
|
}
|
|
342
|
-
}, [nodes, edges, workflowId, workflowName, description, version, enabled, category, tags, icon, effectiveFrom, effectiveTo, triggers, definitionId, updatedAt, router]);
|
|
1624
|
+
}, [nodes, edges, workflowId, workflowName, description, version, enabled, category, tags, icon, effectiveFrom, effectiveTo, triggers, contextSchema, definitionIo, interpolation, errorHandler, loadedMetadata, annotations, definitionId, updatedAt, router, t]);
|
|
1625
|
+
const focusNode = useCallback((nodeId) => {
|
|
1626
|
+
focusRequestRef.current += 1;
|
|
1627
|
+
setFocusTarget({ nodeId, requestId: focusRequestRef.current });
|
|
1628
|
+
}, []);
|
|
1629
|
+
const openSelectedInspector = useCallback(() => {
|
|
1630
|
+
const selected = nodesRef.current.find((node) => node.selected);
|
|
1631
|
+
if (!selected) {
|
|
1632
|
+
const selectedEdgeNode = edgesRef.current.find((edge) => edge.selected);
|
|
1633
|
+
if (!selectedEdgeNode || isCodeOnly) return;
|
|
1634
|
+
setSelectedEdge(selectedEdgeNode);
|
|
1635
|
+
setSelectedNode(null);
|
|
1636
|
+
setEdgeDialogFocusFieldId(null);
|
|
1637
|
+
setShowEdgeDialog(true);
|
|
1638
|
+
return;
|
|
1639
|
+
}
|
|
1640
|
+
handleNodeClick({}, selected);
|
|
1641
|
+
}, [isCodeOnly, handleNodeClick]);
|
|
1642
|
+
const handleDeleteSelection = useCallback(async () => {
|
|
1643
|
+
if (isCodeOnly) return;
|
|
1644
|
+
const nodeIds = selectedNodeIds(nodesRef.current);
|
|
1645
|
+
const edgeIds = edgesRef.current.filter((edge) => edge.selected).map((edge) => edge.id);
|
|
1646
|
+
if (nodeIds.length === 0 && edgeIds.length === 0) {
|
|
1647
|
+
flash(t("workflows.visualEditor.keyboard.nothingSelected", "Select a step or a route first"), "info");
|
|
1648
|
+
return;
|
|
1649
|
+
}
|
|
1650
|
+
for (const edgeId of edgeIds) await handleDeleteEdge(edgeId);
|
|
1651
|
+
for (const nodeId of nodeIds) await handleDeleteNode(nodeId);
|
|
1652
|
+
}, [isCodeOnly, handleDeleteEdge, handleDeleteNode, t]);
|
|
1653
|
+
const nudgeBaselineRef = React.useRef(null);
|
|
1654
|
+
const nudgeCommitTimerRef = React.useRef(null);
|
|
1655
|
+
const handleNudge = useCallback((key, coarse) => {
|
|
1656
|
+
if (isCodeOnly) return false;
|
|
1657
|
+
const direction = resolveNudgeDirection(key);
|
|
1658
|
+
if (!direction) return false;
|
|
1659
|
+
if (selectedNodeIds(nodesRef.current).length === 0) return false;
|
|
1660
|
+
if (!nudgeBaselineRef.current) nudgeBaselineRef.current = captureDocument();
|
|
1661
|
+
setNodes((nds) => nudgeSelectedNodes(nds, nudgeOffset(direction, coarse)));
|
|
1662
|
+
if (nudgeCommitTimerRef.current) clearTimeout(nudgeCommitTimerRef.current);
|
|
1663
|
+
nudgeCommitTimerRef.current = setTimeout(() => {
|
|
1664
|
+
nudgeCommitTimerRef.current = null;
|
|
1665
|
+
const baseline = nudgeBaselineRef.current;
|
|
1666
|
+
nudgeBaselineRef.current = null;
|
|
1667
|
+
if (baseline) commitCapturedDocument(baseline, historyLabels.move);
|
|
1668
|
+
scheduleAutosave();
|
|
1669
|
+
}, NUDGE_COMMIT_DELAY_MS);
|
|
1670
|
+
return true;
|
|
1671
|
+
}, [isCodeOnly, captureDocument, commitCapturedDocument, historyLabels.move, scheduleAutosave]);
|
|
1672
|
+
useEffect(() => () => {
|
|
1673
|
+
if (nudgeCommitTimerRef.current) clearTimeout(nudgeCommitTimerRef.current);
|
|
1674
|
+
}, []);
|
|
1675
|
+
const commandPaletteCommands = useMemo(() => buildWorkflowEditorCommands({
|
|
1676
|
+
readOnly: isCodeOnly,
|
|
1677
|
+
canUndo: canUndoEditorHistory(history),
|
|
1678
|
+
canRedo: canRedoEditorHistory(history),
|
|
1679
|
+
hasNodes: nodes.length > 0,
|
|
1680
|
+
hasSelection: nodes.some((node) => node.selected) || edges.some((edge) => edge.selected),
|
|
1681
|
+
canRunTest: !!definitionId,
|
|
1682
|
+
stepTypes: PALETTE_NODE_TYPES.map((nodeType) => ({
|
|
1683
|
+
nodeType,
|
|
1684
|
+
typeLabel: NODE_TYPE_LABELS[nodeType]?.title ?? nodeType
|
|
1685
|
+
})),
|
|
1686
|
+
steps: nodes.filter((node) => !isAnnotationNode(node)).map((node) => ({
|
|
1687
|
+
id: node.id,
|
|
1688
|
+
label: typeof node.data?.label === "string" && node.data.label ? node.data.label : node.id,
|
|
1689
|
+
typeLabel: NODE_TYPE_LABELS[node.type ?? ""]?.title ?? node.type ?? ""
|
|
1690
|
+
})),
|
|
1691
|
+
labels: {
|
|
1692
|
+
undo: t("workflows.commandPalette.undo", "Undo"),
|
|
1693
|
+
redo: t("workflows.commandPalette.redo", "Redo"),
|
|
1694
|
+
deleteSelection: t("workflows.commandPalette.delete", "Delete selection"),
|
|
1695
|
+
copy: t("workflows.commandPalette.copy", "Copy selection"),
|
|
1696
|
+
paste: t("workflows.commandPalette.paste", "Paste"),
|
|
1697
|
+
duplicate: t("workflows.commandPalette.duplicate", "Duplicate selection"),
|
|
1698
|
+
addStep: (typeLabel) => t("workflows.commandPalette.addStep", "Add step: {type}", { type: typeLabel }),
|
|
1699
|
+
addNote: t("workflows.commandPalette.addNote", "Add note"),
|
|
1700
|
+
addGroup: t("workflows.commandPalette.addGroup", "Add group"),
|
|
1701
|
+
goToStep: t("workflows.commandPalette.goToStep", "Go to step"),
|
|
1702
|
+
tidy: t("workflows.visualEditor.autoArrange"),
|
|
1703
|
+
togglePalette: t("workflows.commandPalette.togglePalette", "Toggle the step palette"),
|
|
1704
|
+
toggleMetadata: t("workflows.commandPalette.toggleMetadata", "Toggle the workflow details panel"),
|
|
1705
|
+
toggleFocus: t("workflows.commandPalette.toggleFocus", "Toggle focus mode"),
|
|
1706
|
+
toggleProblems: t("workflows.commandPalette.toggleProblems", "Toggle the Problems panel"),
|
|
1707
|
+
toggleCodeView: t("workflows.commandPalette.toggleCodeView", "Toggle the Code view"),
|
|
1708
|
+
toggleCompensation: t("workflows.commandPalette.toggleCompensation", "Toggle compensation paths"),
|
|
1709
|
+
validate: t("workflows.visualEditor.validate"),
|
|
1710
|
+
runTest: t("workflows.actions.startInstance"),
|
|
1711
|
+
save: t("workflows.common.save"),
|
|
1712
|
+
aiDraft: t("workflows.aiDraft.open", "Draft this workflow with AI")
|
|
1713
|
+
},
|
|
1714
|
+
actions: {
|
|
1715
|
+
undo: handleUndo,
|
|
1716
|
+
redo: handleRedo,
|
|
1717
|
+
deleteSelection: () => {
|
|
1718
|
+
void handleDeleteSelection();
|
|
1719
|
+
},
|
|
1720
|
+
copy: () => {
|
|
1721
|
+
void handleCopySelection();
|
|
1722
|
+
},
|
|
1723
|
+
paste: () => {
|
|
1724
|
+
void handlePaste();
|
|
1725
|
+
},
|
|
1726
|
+
duplicate: handleDuplicateSelection,
|
|
1727
|
+
addStep: (nodeType) => handleAddNode(nodeType),
|
|
1728
|
+
addNote: () => handleAddAnnotation("note"),
|
|
1729
|
+
addGroup: () => handleAddAnnotation("group"),
|
|
1730
|
+
goToStep: focusNode,
|
|
1731
|
+
tidy: handleAutoArrange,
|
|
1732
|
+
togglePalette: togglePaletteCollapsed,
|
|
1733
|
+
toggleMetadata: () => setMetadataOpen((open) => !open),
|
|
1734
|
+
toggleFocus,
|
|
1735
|
+
toggleProblems: () => setShowProblems((visible) => !visible),
|
|
1736
|
+
toggleCodeView: () => setShowCodeView((visible) => !visible),
|
|
1737
|
+
toggleCompensation,
|
|
1738
|
+
validate: handleValidate,
|
|
1739
|
+
runTest: () => setStartOpen(true),
|
|
1740
|
+
save: () => {
|
|
1741
|
+
void handleSave();
|
|
1742
|
+
},
|
|
1743
|
+
aiDraft: () => setShowAiDraft(true)
|
|
1744
|
+
}
|
|
1745
|
+
}), [
|
|
1746
|
+
isCodeOnly,
|
|
1747
|
+
history,
|
|
1748
|
+
nodes,
|
|
1749
|
+
edges,
|
|
1750
|
+
definitionId,
|
|
1751
|
+
t,
|
|
1752
|
+
handleUndo,
|
|
1753
|
+
handleRedo,
|
|
1754
|
+
handleDeleteSelection,
|
|
1755
|
+
handleCopySelection,
|
|
1756
|
+
handlePaste,
|
|
1757
|
+
handleDuplicateSelection,
|
|
1758
|
+
handleAddNode,
|
|
1759
|
+
handleAddAnnotation,
|
|
1760
|
+
focusNode,
|
|
1761
|
+
handleAutoArrange,
|
|
1762
|
+
togglePaletteCollapsed,
|
|
1763
|
+
toggleFocus,
|
|
1764
|
+
toggleCompensation,
|
|
1765
|
+
handleValidate,
|
|
1766
|
+
handleSave
|
|
1767
|
+
]);
|
|
1768
|
+
useEffect(() => {
|
|
1769
|
+
if (isMobile) return;
|
|
1770
|
+
const onKeyDown = (event) => {
|
|
1771
|
+
const active = document.activeElement;
|
|
1772
|
+
const tag = (active?.tagName || "").toLowerCase();
|
|
1773
|
+
const isEditing = tag === "input" || tag === "textarea" || tag === "select" || !!active?.isContentEditable;
|
|
1774
|
+
const isInspectorOpen = showNodeDialog || showEdgeDialog;
|
|
1775
|
+
const isDialogOpen = isInspectorOpen || showAnnotationDialog || showClearConfirm || startOpen || showCodeView;
|
|
1776
|
+
const isOverlayOpen = isDialogOpen || metadataOpen || showAiDraft;
|
|
1777
|
+
const isCommandKey = (event.metaKey || event.ctrlKey) && !event.altKey;
|
|
1778
|
+
if (isCommandKey && (event.key === "k" || event.key === "K")) {
|
|
1779
|
+
if (isOverlayOpen) return;
|
|
1780
|
+
event.preventDefault();
|
|
1781
|
+
setShowCommandPalette((open) => !open);
|
|
1782
|
+
return;
|
|
1783
|
+
}
|
|
1784
|
+
if (isCommandKey && !event.shiftKey && (event.key === "s" || event.key === "S")) {
|
|
1785
|
+
if (isDialogOpen || isCodeOnly) return;
|
|
1786
|
+
event.preventDefault();
|
|
1787
|
+
void handleSave();
|
|
1788
|
+
return;
|
|
1789
|
+
}
|
|
1790
|
+
if (isEditing || showCommandPalette) return;
|
|
1791
|
+
if (isCommandKey && (event.key === "z" || event.key === "Z")) {
|
|
1792
|
+
if (isOverlayOpen) return;
|
|
1793
|
+
event.preventDefault();
|
|
1794
|
+
if (event.shiftKey) handleRedo();
|
|
1795
|
+
else handleUndo();
|
|
1796
|
+
return;
|
|
1797
|
+
}
|
|
1798
|
+
if (isCommandKey && !event.shiftKey && !isOverlayOpen) {
|
|
1799
|
+
if (event.key === "c" || event.key === "C") {
|
|
1800
|
+
event.preventDefault();
|
|
1801
|
+
void handleCopySelection();
|
|
1802
|
+
return;
|
|
1803
|
+
}
|
|
1804
|
+
if (event.key === "v" || event.key === "V") {
|
|
1805
|
+
event.preventDefault();
|
|
1806
|
+
void handlePaste();
|
|
1807
|
+
return;
|
|
1808
|
+
}
|
|
1809
|
+
if (event.key === "d" || event.key === "D") {
|
|
1810
|
+
event.preventDefault();
|
|
1811
|
+
handleDuplicateSelection();
|
|
1812
|
+
return;
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
if (event.metaKey || event.ctrlKey || event.altKey) return;
|
|
1816
|
+
if (event.key === "Escape") {
|
|
1817
|
+
if (inspectorsDocked && isInspectorOpen) {
|
|
1818
|
+
event.preventDefault();
|
|
1819
|
+
setShowNodeDialog(false);
|
|
1820
|
+
setShowEdgeDialog(false);
|
|
1821
|
+
return;
|
|
1822
|
+
}
|
|
1823
|
+
if (focusMode && !isOverlayOpen) {
|
|
1824
|
+
event.preventDefault();
|
|
1825
|
+
setFocusMode(false);
|
|
1826
|
+
}
|
|
1827
|
+
return;
|
|
1828
|
+
}
|
|
1829
|
+
if (isOverlayOpen) return;
|
|
1830
|
+
if (event.key === "Enter") {
|
|
1831
|
+
event.preventDefault();
|
|
1832
|
+
openSelectedInspector();
|
|
1833
|
+
return;
|
|
1834
|
+
}
|
|
1835
|
+
if (event.key === "Delete" || event.key === "Backspace") {
|
|
1836
|
+
event.preventDefault();
|
|
1837
|
+
void handleDeleteSelection();
|
|
1838
|
+
return;
|
|
1839
|
+
}
|
|
1840
|
+
if (resolveNudgeDirection(event.key)) {
|
|
1841
|
+
if (handleNudge(event.key, event.shiftKey)) event.preventDefault();
|
|
1842
|
+
return;
|
|
1843
|
+
}
|
|
1844
|
+
if (event.key === "f" || event.key === "F") {
|
|
1845
|
+
event.preventDefault();
|
|
1846
|
+
toggleFocus();
|
|
1847
|
+
}
|
|
1848
|
+
};
|
|
1849
|
+
window.addEventListener("keydown", onKeyDown);
|
|
1850
|
+
return () => window.removeEventListener("keydown", onKeyDown);
|
|
1851
|
+
}, [
|
|
1852
|
+
isMobile,
|
|
1853
|
+
focusMode,
|
|
1854
|
+
inspectorsDocked,
|
|
1855
|
+
showNodeDialog,
|
|
1856
|
+
showEdgeDialog,
|
|
1857
|
+
showAnnotationDialog,
|
|
1858
|
+
showClearConfirm,
|
|
1859
|
+
startOpen,
|
|
1860
|
+
showCodeView,
|
|
1861
|
+
metadataOpen,
|
|
1862
|
+
showAiDraft,
|
|
1863
|
+
showCommandPalette,
|
|
1864
|
+
toggleFocus,
|
|
1865
|
+
setFocusMode,
|
|
1866
|
+
handleUndo,
|
|
1867
|
+
handleRedo,
|
|
1868
|
+
handleCopySelection,
|
|
1869
|
+
handlePaste,
|
|
1870
|
+
handleDuplicateSelection,
|
|
1871
|
+
openSelectedInspector,
|
|
1872
|
+
handleDeleteSelection,
|
|
1873
|
+
handleNudge,
|
|
1874
|
+
handleSave,
|
|
1875
|
+
isCodeOnly
|
|
1876
|
+
]);
|
|
1877
|
+
performAutosaveRef.current = async () => {
|
|
1878
|
+
if (!definitionId || isCodeOnly) return;
|
|
1879
|
+
if (!workflowId || !workflowName) return;
|
|
1880
|
+
const criticalErrors = validateWorkflowGraph(nodes, edges).filter((e) => e.type === "error");
|
|
1881
|
+
if (criticalErrors.length > 0) return;
|
|
1882
|
+
const definitionData = buildDefinitionPayload({
|
|
1883
|
+
graphDefinition: graphToDefinition(nodes, edges, { includePositions: true }),
|
|
1884
|
+
triggers,
|
|
1885
|
+
contextSchema,
|
|
1886
|
+
io: definitionIo,
|
|
1887
|
+
interpolation,
|
|
1888
|
+
errorHandler
|
|
1889
|
+
});
|
|
1890
|
+
if (!workflowDefinitionDataSchema.safeParse(definitionData).success) return;
|
|
1891
|
+
const metadataPayload = buildMetadataPayload({ loadedMetadata, tags, category, icon, definition: definitionData, annotations });
|
|
1892
|
+
const updateBody = {
|
|
1893
|
+
workflowName,
|
|
1894
|
+
description: description || null,
|
|
1895
|
+
version,
|
|
1896
|
+
definition: definitionData,
|
|
1897
|
+
metadata: metadataPayload,
|
|
1898
|
+
enabled,
|
|
1899
|
+
effectiveFrom: effectiveFrom || null,
|
|
1900
|
+
effectiveTo: effectiveTo || null
|
|
1901
|
+
};
|
|
1902
|
+
const serializedBody = stableSerializeDefinition(updateBody);
|
|
1903
|
+
if (serializedBody === lastAutosavedBodyRef.current) return;
|
|
1904
|
+
lastAutosavedBodyRef.current = serializedBody;
|
|
1905
|
+
setAutosaveState("saving");
|
|
1906
|
+
try {
|
|
1907
|
+
const result = await withScopedApiRequestHeaders(
|
|
1908
|
+
buildOptimisticLockHeader(updatedAt),
|
|
1909
|
+
() => apiCall(`/api/workflows/definitions/${definitionId}`, {
|
|
1910
|
+
method: "PUT",
|
|
1911
|
+
headers: { "Content-Type": "application/json" },
|
|
1912
|
+
body: JSON.stringify(updateBody)
|
|
1913
|
+
})
|
|
1914
|
+
);
|
|
1915
|
+
if (!result.ok) {
|
|
1916
|
+
setAutosaveState("idle");
|
|
1917
|
+
lastAutosavedBodyRef.current = null;
|
|
1918
|
+
const activeInstanceCount = readStructuralEditConflictCount(result.status, result.result);
|
|
1919
|
+
if (activeInstanceCount !== null) {
|
|
1920
|
+
setStructuralConflict({ activeInstanceCount, payload: updateBody });
|
|
1921
|
+
return;
|
|
1922
|
+
}
|
|
1923
|
+
const conflictError = Object.assign(new Error("[internal] workflow autosave failed"), {
|
|
1924
|
+
status: result.status,
|
|
1925
|
+
...result.result && typeof result.result === "object" ? result.result : {}
|
|
1926
|
+
});
|
|
1927
|
+
surfaceRecordConflict(conflictError, t);
|
|
1928
|
+
return;
|
|
1929
|
+
}
|
|
1930
|
+
setStructuralConflict(null);
|
|
1931
|
+
const savedDefinition = result.result?.data;
|
|
1932
|
+
if (typeof savedDefinition?.updatedAt === "string") {
|
|
1933
|
+
setUpdatedAt(savedDefinition.updatedAt);
|
|
1934
|
+
}
|
|
1935
|
+
setAutosaveState("saved");
|
|
1936
|
+
} catch (error) {
|
|
1937
|
+
logger.error("workflow autosave failed", {
|
|
1938
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1939
|
+
});
|
|
1940
|
+
lastAutosavedBodyRef.current = null;
|
|
1941
|
+
setAutosaveState("idle");
|
|
1942
|
+
}
|
|
1943
|
+
};
|
|
1944
|
+
useEffect(() => {
|
|
1945
|
+
if (autosaveState !== "saved") return;
|
|
1946
|
+
const timer = setTimeout(() => setAutosaveState("idle"), 2e3);
|
|
1947
|
+
return () => clearTimeout(timer);
|
|
1948
|
+
}, [autosaveState]);
|
|
1949
|
+
const handleCreateVersion = useCallback(async () => {
|
|
1950
|
+
if (!definitionId || !structuralConflict) return;
|
|
1951
|
+
setIsCreatingVersion(true);
|
|
1952
|
+
try {
|
|
1953
|
+
const publishResult = await apiCall(
|
|
1954
|
+
`/api/workflows/definitions/${definitionId}/publish`,
|
|
1955
|
+
{ method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({}) }
|
|
1956
|
+
);
|
|
1957
|
+
const mintedVersion = publishResult.result?.data;
|
|
1958
|
+
if (!publishResult.ok || !mintedVersion?.id) {
|
|
1959
|
+
flash(
|
|
1960
|
+
publishResult.result?.error || t("workflows.visualEditor.editSafety.createVersionFailed", "Could not create a new version."),
|
|
1961
|
+
"error"
|
|
1962
|
+
);
|
|
1963
|
+
return;
|
|
1964
|
+
}
|
|
1965
|
+
const nextVersion = mintedVersion.version ?? version;
|
|
1966
|
+
const saveResult = await withScopedApiRequestHeaders(
|
|
1967
|
+
buildOptimisticLockHeader(mintedVersion.updatedAt ?? null),
|
|
1968
|
+
() => apiCall(
|
|
1969
|
+
`/api/workflows/definitions/${mintedVersion.id}`,
|
|
1970
|
+
{
|
|
1971
|
+
method: "PUT",
|
|
1972
|
+
headers: { "Content-Type": "application/json" },
|
|
1973
|
+
body: JSON.stringify({ ...structuralConflict.payload, version: nextVersion })
|
|
1974
|
+
}
|
|
1975
|
+
)
|
|
1976
|
+
);
|
|
1977
|
+
if (!saveResult.ok) {
|
|
1978
|
+
flash(
|
|
1979
|
+
formatWorkflowValidationError(
|
|
1980
|
+
saveResult.result,
|
|
1981
|
+
t("workflows.visualEditor.editSafety.createVersionFailed", "Could not create a new version.")
|
|
1982
|
+
),
|
|
1983
|
+
"error"
|
|
1984
|
+
);
|
|
1985
|
+
return;
|
|
1986
|
+
}
|
|
1987
|
+
setStructuralConflict(null);
|
|
1988
|
+
setVersion(nextVersion);
|
|
1989
|
+
setUpdatedAt(saveResult.result?.data?.updatedAt ?? null);
|
|
1990
|
+
flash(
|
|
1991
|
+
t("workflows.visualEditor.editSafety.createVersionSucceeded", "Version {version} created. Your changes were saved there.", {
|
|
1992
|
+
version: String(nextVersion)
|
|
1993
|
+
}),
|
|
1994
|
+
"success"
|
|
1995
|
+
);
|
|
1996
|
+
router.replace(`/backend/definitions/visual-editor?id=${encodeURIComponent(mintedVersion.id)}`);
|
|
1997
|
+
} finally {
|
|
1998
|
+
setIsCreatingVersion(false);
|
|
1999
|
+
}
|
|
2000
|
+
}, [definitionId, structuralConflict, version, router, t]);
|
|
343
2001
|
const handleCustomize = useCallback(async () => {
|
|
344
2002
|
if (!definitionId) return;
|
|
345
2003
|
setIsSaving(true);
|
|
@@ -387,90 +2045,106 @@ function VisualEditorPage() {
|
|
|
387
2045
|
setIsSaving(false);
|
|
388
2046
|
}
|
|
389
2047
|
}, [definitionId, workflowId, router, confirm, t]);
|
|
390
|
-
const
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
2048
|
+
const handleStartInstance = useCallback(async () => {
|
|
2049
|
+
let initialContext;
|
|
2050
|
+
try {
|
|
2051
|
+
const parsed = startContext.trim() ? JSON.parse(startContext) : {};
|
|
2052
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
2053
|
+
throw new Error("[internal] context must be a JSON object");
|
|
2054
|
+
}
|
|
2055
|
+
initialContext = parsed;
|
|
2056
|
+
} catch {
|
|
2057
|
+
flash(t("workflows.startInstance.invalidJson"), "error");
|
|
395
2058
|
return;
|
|
396
2059
|
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
description: "User reviews the incoming request",
|
|
426
|
-
status: "pending",
|
|
427
|
-
stepNumber: 1,
|
|
428
|
-
badge: "User Task",
|
|
429
|
-
assignedToRoles: ["Reviewer"]
|
|
430
|
-
}
|
|
431
|
-
},
|
|
432
|
-
{
|
|
433
|
-
id: "end",
|
|
434
|
-
type: "end",
|
|
435
|
-
position: { x: 250, y: 450 },
|
|
436
|
-
data: {
|
|
437
|
-
label: "Complete",
|
|
438
|
-
description: "Workflow ends",
|
|
439
|
-
status: "pending",
|
|
440
|
-
badge: "End"
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
];
|
|
444
|
-
const exampleEdges = [
|
|
445
|
-
{
|
|
446
|
-
id: "e-start-step1",
|
|
447
|
-
source: "start",
|
|
448
|
-
target: "step1",
|
|
449
|
-
type: "smoothstep",
|
|
450
|
-
data: {
|
|
451
|
-
trigger: "auto",
|
|
452
|
-
preConditions: [],
|
|
453
|
-
postConditions: [],
|
|
454
|
-
activities: []
|
|
455
|
-
}
|
|
456
|
-
},
|
|
457
|
-
{
|
|
458
|
-
id: "e-step1-end",
|
|
459
|
-
source: "step1",
|
|
460
|
-
target: "end",
|
|
461
|
-
type: "smoothstep",
|
|
462
|
-
data: {
|
|
463
|
-
trigger: "auto",
|
|
464
|
-
preConditions: [],
|
|
465
|
-
postConditions: [],
|
|
466
|
-
activities: []
|
|
2060
|
+
setStarting(true);
|
|
2061
|
+
try {
|
|
2062
|
+
const result = await runMutation({
|
|
2063
|
+
operation: async () => {
|
|
2064
|
+
const response = await apiFetch("/api/workflows/instances", {
|
|
2065
|
+
method: "POST",
|
|
2066
|
+
headers: { "Content-Type": "application/json" },
|
|
2067
|
+
body: JSON.stringify({
|
|
2068
|
+
workflowId,
|
|
2069
|
+
version,
|
|
2070
|
+
initialContext,
|
|
2071
|
+
...startDryRun ? { dryRun: true } : {},
|
|
2072
|
+
...startStepThrough ? { stepThrough: true } : {}
|
|
2073
|
+
})
|
|
2074
|
+
});
|
|
2075
|
+
if (!response.ok) {
|
|
2076
|
+
const errorBody = await readJsonSafe(response, null);
|
|
2077
|
+
throw new Error(errorBody?.error || t("workflows.startInstance.failed"));
|
|
2078
|
+
}
|
|
2079
|
+
return readJsonSafe(response, null);
|
|
2080
|
+
},
|
|
2081
|
+
mutationPayload: { resourceId: definitionId, operation: "start-instance" },
|
|
2082
|
+
context: {
|
|
2083
|
+
formId: mutationContextId,
|
|
2084
|
+
resourceKind: "workflows.instance",
|
|
2085
|
+
resourceId: definitionId,
|
|
2086
|
+
operation: "start-instance",
|
|
2087
|
+
retryLastMutation
|
|
467
2088
|
}
|
|
2089
|
+
});
|
|
2090
|
+
flash(t("workflows.startInstance.started"), "success");
|
|
2091
|
+
setStartOpen(false);
|
|
2092
|
+
const instanceId = result?.data?.instance?.id;
|
|
2093
|
+
if (instanceId) {
|
|
2094
|
+
router.push(`/backend/instances/${instanceId}`);
|
|
2095
|
+
router.refresh();
|
|
468
2096
|
}
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
2097
|
+
} catch (error) {
|
|
2098
|
+
flash(error instanceof Error ? error.message : t("workflows.startInstance.failed"), "error");
|
|
2099
|
+
} finally {
|
|
2100
|
+
setStarting(false);
|
|
2101
|
+
}
|
|
2102
|
+
}, [startContext, startDryRun, startStepThrough, workflowId, version, definitionId, mutationContextId, retryLastMutation, router, t]);
|
|
2103
|
+
const applyTemplate = useCallback((template) => {
|
|
2104
|
+
setWorkflowId(template.id.replace(/-/g, "_"));
|
|
2105
|
+
setWorkflowName(t(template.nameKey));
|
|
2106
|
+
setDescription(t(template.descriptionKey));
|
|
2107
|
+
setVersion(1);
|
|
2108
|
+
setEnabled(true);
|
|
2109
|
+
setCategory(template.category);
|
|
2110
|
+
setTags([]);
|
|
2111
|
+
setIcon(template.icon);
|
|
2112
|
+
const graph = definitionToGraph(template.definition);
|
|
2113
|
+
setNodes(graph.nodes);
|
|
2114
|
+
setEdges(graph.edges);
|
|
2115
|
+
setTriggers(template.definition.triggers || []);
|
|
2116
|
+
setContextSchema(template.definition.contextSchema ?? void 0);
|
|
2117
|
+
setDefinitionIo(template.definition.io ?? void 0);
|
|
2118
|
+
setInterpolation(resolveDefinitionInterpolationMode(template.definition) ?? "strict");
|
|
2119
|
+
setErrorHandler(template.definition.errorHandler ?? void 0);
|
|
2120
|
+
setLoadedMetadata(null);
|
|
2121
|
+
resetHistory();
|
|
2122
|
+
flash(t("workflows.visualEditor.templateLoaded", "Template loaded"), "success");
|
|
2123
|
+
}, [resetHistory, t]);
|
|
2124
|
+
const handleOpenTemplateGallery = useCallback(() => {
|
|
2125
|
+
setShowTemplateGallery(true);
|
|
473
2126
|
}, []);
|
|
2127
|
+
const handleTemplateSelect = useCallback((template) => {
|
|
2128
|
+
if (template) applyTemplate(template);
|
|
2129
|
+
}, [applyTemplate]);
|
|
2130
|
+
useEffect(() => {
|
|
2131
|
+
if (definitionId || !templateId) return;
|
|
2132
|
+
let cancelled = false;
|
|
2133
|
+
const loadTemplate = async () => {
|
|
2134
|
+
const result = await apiCall("/api/workflows/templates");
|
|
2135
|
+
if (cancelled) return;
|
|
2136
|
+
const template = result.ok ? (result.result?.items || []).find((item) => item.id === templateId) : void 0;
|
|
2137
|
+
if (template) {
|
|
2138
|
+
applyTemplate(template);
|
|
2139
|
+
} else {
|
|
2140
|
+
flash(t("workflows.templates.gallery.notFound", "Template not found"), "error");
|
|
2141
|
+
}
|
|
2142
|
+
};
|
|
2143
|
+
void loadTemplate();
|
|
2144
|
+
return () => {
|
|
2145
|
+
cancelled = true;
|
|
2146
|
+
};
|
|
2147
|
+
}, [definitionId, templateId, applyTemplate, t]);
|
|
474
2148
|
const handleClear = useCallback(() => {
|
|
475
2149
|
if (nodes.length > 0 || edges.length > 0 || workflowId || workflowName) {
|
|
476
2150
|
setShowClearConfirm(true);
|
|
@@ -490,9 +2164,13 @@ function VisualEditorPage() {
|
|
|
490
2164
|
setEffectiveFrom("");
|
|
491
2165
|
setEffectiveTo("");
|
|
492
2166
|
setTriggers([]);
|
|
2167
|
+
setContextSchema(void 0);
|
|
2168
|
+
setDefinitionIo(void 0);
|
|
2169
|
+
setLoadedMetadata(null);
|
|
493
2170
|
setShowClearConfirm(false);
|
|
2171
|
+
resetHistory();
|
|
494
2172
|
flash("Canvas cleared", "success");
|
|
495
|
-
}, []);
|
|
2173
|
+
}, [resetHistory]);
|
|
496
2174
|
useSetCurrentRecordInjectionContext(
|
|
497
2175
|
buildRecordInjectionContext({
|
|
498
2176
|
resourceKind: "workflows.definition",
|
|
@@ -515,7 +2193,10 @@ function VisualEditorPage() {
|
|
|
515
2193
|
icon,
|
|
516
2194
|
effectiveFrom,
|
|
517
2195
|
effectiveTo,
|
|
518
|
-
triggers
|
|
2196
|
+
triggers,
|
|
2197
|
+
contextSchema,
|
|
2198
|
+
interpolation,
|
|
2199
|
+
errorHandler
|
|
519
2200
|
};
|
|
520
2201
|
const metadataHandlers = {
|
|
521
2202
|
setWorkflowId,
|
|
@@ -528,24 +2209,345 @@ function VisualEditorPage() {
|
|
|
528
2209
|
setIcon,
|
|
529
2210
|
setEffectiveFrom,
|
|
530
2211
|
setEffectiveTo,
|
|
531
|
-
setTriggers
|
|
2212
|
+
setTriggers,
|
|
2213
|
+
setContextSchema,
|
|
2214
|
+
setInterpolation,
|
|
2215
|
+
setErrorHandler: (next) => setErrorHandler(next ?? void 0)
|
|
532
2216
|
};
|
|
2217
|
+
const metadataIncomplete = !isCodeOnly && (!workflowId || !workflowName);
|
|
2218
|
+
const inspectorPanels = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2219
|
+
crudFormDialogsEnabled ? (
|
|
2220
|
+
// Both forms are dense (Invoke-Agent, timeout, sub-editors on the step;
|
|
2221
|
+
// condition, mapping and activities on the route) and the 384px docked
|
|
2222
|
+
// rail made them unusable, so each opens as the wide overlay Drawer that
|
|
2223
|
+
// mirrors the definition metadata drawer, with its ledger in a sticky
|
|
2224
|
+
// side column.
|
|
2225
|
+
/* @__PURE__ */ jsx(NodeEditDialogCrudForm, { node: selectedNode, isOpen: showNodeDialog, onClose: () => setShowNodeDialog(false), onSave: handleSaveNode, onDelete: handleDeleteNode, ledgerEntries: nodeDialogLedgerEntries, definitionId, samples: editorSamples, onPinSample: handlePinSample, onUnpinSample: handleUnpinSample, branchingRoutes: branchingRoutesValue, onSaveBranchingRoutes: handleSaveBranchingRoutes, routeOrder: routeOrderValue, onSaveRouteOrder: handleSaveRouteOrder, onConvertType: handleConvertNodeType, variant: "overlay", wide: true })
|
|
2226
|
+
) : /* @__PURE__ */ jsx(NodeEditDialog, { node: selectedNode, isOpen: showNodeDialog, onClose: () => setShowNodeDialog(false), onSave: handleSaveNode, onDelete: handleDeleteNode }),
|
|
2227
|
+
crudFormDialogsEnabled ? /* @__PURE__ */ jsx(EdgeEditDialogCrudForm, { edge: selectedEdge, isOpen: showEdgeDialog, onClose: () => setShowEdgeDialog(false), onSave: handleSaveEdge, onDelete: handleDeleteEdge, ledgerEntries: edgeDialogLedgerEntries, focusFieldId: edgeDialogFocusFieldId, variant: "overlay", wide: true }) : /* @__PURE__ */ jsx(EdgeEditDialog, { edge: selectedEdge, isOpen: showEdgeDialog, onClose: () => setShowEdgeDialog(false), onSave: handleSaveEdge, onDelete: handleDeleteEdge })
|
|
2228
|
+
] });
|
|
533
2229
|
const sharedDialogs = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
2230
|
+
inspectorsDocked ? null : inspectorPanels,
|
|
2231
|
+
/* @__PURE__ */ jsx(
|
|
2232
|
+
WorkflowCommandPalette,
|
|
2233
|
+
{
|
|
2234
|
+
open: showCommandPalette,
|
|
2235
|
+
onOpenChange: setShowCommandPalette,
|
|
2236
|
+
commands: commandPaletteCommands
|
|
2237
|
+
}
|
|
2238
|
+
),
|
|
2239
|
+
/* @__PURE__ */ jsx(
|
|
2240
|
+
DefinitionMetadataDrawer,
|
|
2241
|
+
{
|
|
2242
|
+
open: metadataOpen,
|
|
2243
|
+
onOpenChange: handleMetadataOpenChange,
|
|
2244
|
+
focusSection: metadataFocusSection,
|
|
2245
|
+
definitionId,
|
|
2246
|
+
readOnly: isCodeOnly,
|
|
2247
|
+
metadata,
|
|
2248
|
+
handlers: metadataHandlers,
|
|
2249
|
+
errorHandlerStepOptions,
|
|
2250
|
+
onSave: () => {
|
|
2251
|
+
void handleSave();
|
|
2252
|
+
},
|
|
2253
|
+
isSaving
|
|
2254
|
+
}
|
|
2255
|
+
),
|
|
2256
|
+
/* @__PURE__ */ jsx(
|
|
2257
|
+
TriggersDialog,
|
|
2258
|
+
{
|
|
2259
|
+
open: triggersDialogOpen,
|
|
2260
|
+
onOpenChange: setTriggersDialogOpen,
|
|
2261
|
+
triggers,
|
|
2262
|
+
onSave: setTriggers
|
|
2263
|
+
}
|
|
2264
|
+
),
|
|
2265
|
+
(() => {
|
|
2266
|
+
const edge = activityEdit ? edges.find((candidate) => candidate.id === activityEdit.edgeId) ?? null : null;
|
|
2267
|
+
const activities = edge?.data?.activities ?? [];
|
|
2268
|
+
const activity = activityEdit ? activities.find((item) => item.activityId === activityEdit.activityId) ?? null : null;
|
|
2269
|
+
const labelFor = (nodeId) => nodes.find((node) => node.id === nodeId)?.data?.label ?? nodeId;
|
|
2270
|
+
const routeLabel = edge ? `${labelFor(edge.source)} \u2192 ${labelFor(edge.target)}` : void 0;
|
|
2271
|
+
return /* @__PURE__ */ jsx(
|
|
2272
|
+
ActivityEditDialog,
|
|
2273
|
+
{
|
|
2274
|
+
open: !!activity,
|
|
2275
|
+
onOpenChange: (open) => {
|
|
2276
|
+
if (!open) setActivityEdit(null);
|
|
2277
|
+
},
|
|
2278
|
+
activity,
|
|
2279
|
+
routeLabel,
|
|
2280
|
+
onSave: (updated) => {
|
|
2281
|
+
if (!activityEdit) return;
|
|
2282
|
+
handleSaveEdge(activityEdit.edgeId, {
|
|
2283
|
+
activities: activities.map((item) => item.activityId === updated.activityId ? updated : item)
|
|
2284
|
+
});
|
|
2285
|
+
},
|
|
2286
|
+
onRemove: () => {
|
|
2287
|
+
if (!activityEdit) return;
|
|
2288
|
+
handleSaveEdge(activityEdit.edgeId, {
|
|
2289
|
+
activities: activities.filter((item) => item.activityId !== activityEdit.activityId)
|
|
2290
|
+
});
|
|
2291
|
+
},
|
|
2292
|
+
onOpenFullTransition: () => {
|
|
2293
|
+
if (activityEdit) handleRouteChipOpen(activityEdit.edgeId, "activities");
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
);
|
|
2297
|
+
})(),
|
|
2298
|
+
/* @__PURE__ */ jsx(
|
|
2299
|
+
WorkflowCodeView,
|
|
2300
|
+
{
|
|
2301
|
+
isOpen: showCodeView,
|
|
2302
|
+
onClose: handleCloseCodeView,
|
|
2303
|
+
definitionJson: codeViewJson,
|
|
2304
|
+
issues: codeViewIssues,
|
|
2305
|
+
onCopy: () => {
|
|
2306
|
+
void handleCopyDefinitionJson();
|
|
2307
|
+
},
|
|
2308
|
+
onPasteSubgraph: () => {
|
|
2309
|
+
void handlePaste();
|
|
2310
|
+
},
|
|
2311
|
+
canPaste: !isCodeOnly,
|
|
2312
|
+
onIssueClick: handleProblemClick,
|
|
2313
|
+
draftText: codeDraftText,
|
|
2314
|
+
onDraftChange: setCodeDraft,
|
|
2315
|
+
onApply: handleApplyCodeDraft,
|
|
2316
|
+
onRevert: handleRevertCodeDraft,
|
|
2317
|
+
draftStatus: codeDraftStatus,
|
|
2318
|
+
severityByLine: codeIssueLocations.severityByLine,
|
|
2319
|
+
lineByIssueId: codeIssueLocations.lineByIssueId
|
|
2320
|
+
}
|
|
2321
|
+
),
|
|
2322
|
+
/* @__PURE__ */ jsx(
|
|
2323
|
+
WorkflowAiDraftDialog,
|
|
2324
|
+
{
|
|
2325
|
+
open: showAiDraft,
|
|
2326
|
+
onOpenChange: setShowAiDraft,
|
|
2327
|
+
onApply: handleApplyAiDraft,
|
|
2328
|
+
canvasHasContent: nodes.length > 0
|
|
2329
|
+
}
|
|
2330
|
+
),
|
|
2331
|
+
/* @__PURE__ */ jsx(
|
|
2332
|
+
AnnotationEditDialog,
|
|
2333
|
+
{
|
|
2334
|
+
node: selectedAnnotation,
|
|
2335
|
+
isOpen: showAnnotationDialog,
|
|
2336
|
+
onClose: () => setShowAnnotationDialog(false),
|
|
2337
|
+
onSave: handleSaveAnnotation,
|
|
2338
|
+
onDelete: handleDeleteAnnotation
|
|
2339
|
+
}
|
|
2340
|
+
),
|
|
2341
|
+
/* @__PURE__ */ jsx(
|
|
2342
|
+
TemplateGalleryDialog,
|
|
2343
|
+
{
|
|
2344
|
+
open: showTemplateGallery,
|
|
2345
|
+
onOpenChange: setShowTemplateGallery,
|
|
2346
|
+
onSelect: handleTemplateSelect
|
|
2347
|
+
}
|
|
2348
|
+
),
|
|
2349
|
+
/* @__PURE__ */ jsx(
|
|
2350
|
+
ConfirmDialog,
|
|
2351
|
+
{
|
|
2352
|
+
open: showClearConfirm,
|
|
2353
|
+
onOpenChange: setShowClearConfirm,
|
|
2354
|
+
title: t("workflows.visualEditor.clearTitle"),
|
|
2355
|
+
text: t("workflows.visualEditor.clearDescription"),
|
|
2356
|
+
confirmText: t("common.clear", "Clear"),
|
|
2357
|
+
cancelText: t("common.cancel", "Cancel"),
|
|
2358
|
+
variant: "destructive",
|
|
2359
|
+
onConfirm: confirmClear
|
|
2360
|
+
}
|
|
2361
|
+
),
|
|
2362
|
+
/* @__PURE__ */ jsx(Drawer, { open: startOpen, onOpenChange: setStartOpen, children: /* @__PURE__ */ jsxs(
|
|
2363
|
+
DrawerContent,
|
|
2364
|
+
{
|
|
2365
|
+
"data-testid": "workflow-start-instance-drawer",
|
|
2366
|
+
closeAriaLabel: t("workflows.startInstance.close"),
|
|
2367
|
+
onKeyDown: (event) => {
|
|
2368
|
+
if (!(event.metaKey || event.ctrlKey) || event.key !== "Enter") return;
|
|
2369
|
+
event.preventDefault();
|
|
2370
|
+
event.stopPropagation();
|
|
2371
|
+
if (starting) return;
|
|
2372
|
+
void handleStartInstance();
|
|
2373
|
+
},
|
|
2374
|
+
children: [
|
|
2375
|
+
/* @__PURE__ */ jsxs(DrawerHeader, { children: [
|
|
2376
|
+
/* @__PURE__ */ jsx(DrawerTitle, { children: t("workflows.startInstance.title") }),
|
|
2377
|
+
/* @__PURE__ */ jsx(DrawerDescription, { children: t("workflows.startInstance.description") })
|
|
2378
|
+
] }),
|
|
2379
|
+
/* @__PURE__ */ jsxs(DrawerBody, { className: "space-y-3 py-2", children: [
|
|
2380
|
+
startFixtureList.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
2381
|
+
/* @__PURE__ */ jsx("span", { className: "text-overline text-muted-foreground", children: t("workflows.fixtures.label") }),
|
|
2382
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: startFixtureList.map((fixture) => /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 rounded-md border border-border bg-muted px-2 py-1", children: [
|
|
2383
|
+
/* @__PURE__ */ jsx(
|
|
2384
|
+
"button",
|
|
2385
|
+
{
|
|
2386
|
+
type: "button",
|
|
2387
|
+
className: "text-xs font-medium hover:underline",
|
|
2388
|
+
onClick: () => handleApplyFixture(fixture.name),
|
|
2389
|
+
children: fixture.name
|
|
2390
|
+
}
|
|
2391
|
+
),
|
|
2392
|
+
/* @__PURE__ */ jsx(
|
|
2393
|
+
"button",
|
|
2394
|
+
{
|
|
2395
|
+
type: "button",
|
|
2396
|
+
className: "text-xs text-muted-foreground hover:text-foreground",
|
|
2397
|
+
"aria-label": t("workflows.fixtures.delete", { name: fixture.name }),
|
|
2398
|
+
onClick: () => handleDeleteFixture(fixture.name),
|
|
2399
|
+
children: "\xD7"
|
|
2400
|
+
}
|
|
2401
|
+
)
|
|
2402
|
+
] }, fixture.name)) })
|
|
2403
|
+
] }) : null,
|
|
2404
|
+
/* @__PURE__ */ jsx("label", { className: "text-sm font-medium", htmlFor: "workflow-start-context", children: t("workflows.startInstance.contextLabel") }),
|
|
2405
|
+
/* @__PURE__ */ jsx(
|
|
2406
|
+
Textarea,
|
|
2407
|
+
{
|
|
2408
|
+
id: "workflow-start-context",
|
|
2409
|
+
value: startContext,
|
|
2410
|
+
onChange: (e) => setStartContext(e.target.value),
|
|
2411
|
+
rows: 9,
|
|
2412
|
+
spellCheck: false,
|
|
2413
|
+
className: "font-mono text-sm"
|
|
2414
|
+
}
|
|
2415
|
+
),
|
|
2416
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-end gap-2", children: [
|
|
2417
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-1", children: [
|
|
2418
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "workflow-fixture-name", className: "text-xs", children: t("workflows.fixtures.nameLabel") }),
|
|
2419
|
+
/* @__PURE__ */ jsx(
|
|
2420
|
+
Input,
|
|
2421
|
+
{
|
|
2422
|
+
id: "workflow-fixture-name",
|
|
2423
|
+
value: fixtureName,
|
|
2424
|
+
onChange: (e) => setFixtureName(e.target.value),
|
|
2425
|
+
placeholder: t("workflows.fixtures.namePlaceholder")
|
|
2426
|
+
}
|
|
2427
|
+
)
|
|
2428
|
+
] }),
|
|
2429
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", onClick: handleSaveFixture, disabled: !fixtureName.trim(), children: t("workflows.fixtures.save") })
|
|
2430
|
+
] }),
|
|
2431
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("workflows.fixtures.storageWarning") }),
|
|
2432
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2 rounded-md border border-border p-2", children: [
|
|
2433
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
2434
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "workflow-start-dry-run", className: "text-sm font-medium", children: t("workflows.startInstance.dryRunLabel") }),
|
|
2435
|
+
/* @__PURE__ */ jsx(
|
|
2436
|
+
Switch,
|
|
2437
|
+
{
|
|
2438
|
+
id: "workflow-start-dry-run",
|
|
2439
|
+
checked: startDryRun,
|
|
2440
|
+
onCheckedChange: (checked) => setStartDryRun(checked === true)
|
|
2441
|
+
}
|
|
2442
|
+
)
|
|
2443
|
+
] }),
|
|
2444
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("workflows.startInstance.dryRunHint") }),
|
|
2445
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
|
|
2446
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "workflow-start-step-through", className: "text-sm font-medium", children: t("workflows.startInstance.stepThroughLabel") }),
|
|
2447
|
+
/* @__PURE__ */ jsx(
|
|
2448
|
+
Switch,
|
|
2449
|
+
{
|
|
2450
|
+
id: "workflow-start-step-through",
|
|
2451
|
+
checked: startStepThrough,
|
|
2452
|
+
onCheckedChange: (checked) => setStartStepThrough(checked === true)
|
|
2453
|
+
}
|
|
2454
|
+
)
|
|
2455
|
+
] }),
|
|
2456
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("workflows.startInstance.stepThroughHint") })
|
|
2457
|
+
] })
|
|
2458
|
+
] }),
|
|
2459
|
+
/* @__PURE__ */ jsxs(DrawerFooter, { children: [
|
|
2460
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", onClick: () => setStartOpen(false), disabled: starting, children: t("workflows.startInstance.cancel") }),
|
|
2461
|
+
/* @__PURE__ */ jsx(Button, { onClick: () => void handleStartInstance(), disabled: starting, children: starting ? /* @__PURE__ */ jsx(Spinner, { className: "h-4 w-4" }) : t("workflows.startInstance.start") })
|
|
2462
|
+
] })
|
|
2463
|
+
]
|
|
2464
|
+
}
|
|
2465
|
+
) })
|
|
546
2466
|
] });
|
|
2467
|
+
const structuralConflictBanner = structuralConflict ? /* @__PURE__ */ jsx("div", { className: "shrink-0 border-b border-border bg-background px-3 py-2 md:px-6 md:py-3", children: /* @__PURE__ */ jsxs(Alert, { variant: "warning", children: [
|
|
2468
|
+
/* @__PURE__ */ jsx(AlertTitle, { children: t("workflows.visualEditor.editSafety.bannerTitle", "This change needs a new version") }),
|
|
2469
|
+
/* @__PURE__ */ jsx(AlertDescription, { children: t(
|
|
2470
|
+
"workflows.visualEditor.editSafety.bannerDescription",
|
|
2471
|
+
"{count} instance(s) are still running on this version. Changing the steps or routes would re-point them mid-flight, so the change is saved to a new version instead.",
|
|
2472
|
+
{ count: String(structuralConflict.activeInstanceCount) }
|
|
2473
|
+
) }),
|
|
2474
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-2 flex flex-wrap gap-2", children: [
|
|
2475
|
+
/* @__PURE__ */ jsx(Button, { size: "sm", onClick: handleCreateVersion, disabled: isCreatingVersion, className: "h-8 text-xs", children: t("workflows.visualEditor.editSafety.createVersion", "Create version") }),
|
|
2476
|
+
/* @__PURE__ */ jsx(
|
|
2477
|
+
Button,
|
|
2478
|
+
{
|
|
2479
|
+
size: "sm",
|
|
2480
|
+
variant: "outline",
|
|
2481
|
+
onClick: () => setStructuralConflict(null),
|
|
2482
|
+
disabled: isCreatingVersion,
|
|
2483
|
+
className: "h-8 text-xs",
|
|
2484
|
+
children: t("workflows.visualEditor.editSafety.keepEditing", "Keep editing")
|
|
2485
|
+
}
|
|
2486
|
+
)
|
|
2487
|
+
] })
|
|
2488
|
+
] }) }) : null;
|
|
2489
|
+
const draftRestoreBanner = pendingDraft ? /* @__PURE__ */ jsx("div", { className: "shrink-0 border-b border-border bg-background px-3 py-2 md:px-6 md:py-3", children: /* @__PURE__ */ jsxs(Alert, { variant: "info", children: [
|
|
2490
|
+
/* @__PURE__ */ jsx(AlertTitle, { children: t("workflows.visualEditor.draft.bannerTitle", "You have an unsaved draft from {time}", {
|
|
2491
|
+
time: formatRelativeTime(pendingDraft.draft.updatedAt, { translate: t }) ?? ""
|
|
2492
|
+
}) }),
|
|
2493
|
+
pendingDraft.baseMismatch && /* @__PURE__ */ jsx(AlertDescription, { children: t("workflows.visualEditor.draft.bannerConflict", "The workflow definition has changed since this draft was made. Restoring will apply your draft over the newer version.") }),
|
|
2494
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-2 flex flex-wrap gap-2", children: [
|
|
2495
|
+
/* @__PURE__ */ jsx(Button, { size: "sm", onClick: handleRestoreDraft, className: "h-8 text-xs", children: t("workflows.visualEditor.draft.restore", "Restore draft") }),
|
|
2496
|
+
/* @__PURE__ */ jsx(Button, { size: "sm", variant: "outline", onClick: handleDiscardDraft, className: "h-8 text-xs", children: t("workflows.visualEditor.draft.discard", "Discard") })
|
|
2497
|
+
] })
|
|
2498
|
+
] }) }) : null;
|
|
2499
|
+
const { errors: problemErrorCount, warnings: problemWarningCount } = countIssuesBySeverity(problems);
|
|
2500
|
+
const problemsPanel = showProblems && problems.length > 0 ? /* @__PURE__ */ jsx("div", { className: "shrink-0 border-t border-border bg-background px-3 py-2 md:px-6 md:py-3", children: /* @__PURE__ */ jsxs("div", { className: `rounded-lg border bg-card ${problemErrorCount > 0 ? "border-status-error-border" : "border-status-warning-border"}`, children: [
|
|
2501
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 px-3 py-2", children: [
|
|
2502
|
+
/* @__PURE__ */ jsxs(
|
|
2503
|
+
Button,
|
|
2504
|
+
{
|
|
2505
|
+
variant: "ghost",
|
|
2506
|
+
size: "sm",
|
|
2507
|
+
onClick: () => setProblemsCollapsed((collapsed) => !collapsed),
|
|
2508
|
+
"aria-expanded": !problemsCollapsed,
|
|
2509
|
+
className: "h-auto gap-2 p-0 text-sm font-semibold text-foreground hover:bg-transparent",
|
|
2510
|
+
children: [
|
|
2511
|
+
problemsCollapsed ? /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4", "aria-hidden": "true" }),
|
|
2512
|
+
t("workflows.visualEditor.problems.title", "Problems"),
|
|
2513
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-normal text-muted-foreground", children: t("workflows.visualEditor.problems.counts", "{errors} error(s) \xB7 {warnings} warning(s)", { errors: problemErrorCount, warnings: problemWarningCount }) })
|
|
2514
|
+
]
|
|
2515
|
+
}
|
|
2516
|
+
),
|
|
2517
|
+
/* @__PURE__ */ jsx(
|
|
2518
|
+
Button,
|
|
2519
|
+
{
|
|
2520
|
+
variant: "ghost",
|
|
2521
|
+
size: "sm",
|
|
2522
|
+
onClick: () => setShowProblems(false),
|
|
2523
|
+
"aria-label": t("workflows.visualEditor.problems.dismiss", "Dismiss problems"),
|
|
2524
|
+
className: "h-7 w-7 p-0",
|
|
2525
|
+
children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4", "aria-hidden": "true" })
|
|
2526
|
+
}
|
|
2527
|
+
)
|
|
2528
|
+
] }),
|
|
2529
|
+
!problemsCollapsed && /* @__PURE__ */ jsx("ul", { className: "max-h-56 overflow-y-auto border-t border-border", children: problems.map((issue) => {
|
|
2530
|
+
const isNavigable = Boolean(issue.nodeId || issue.edgeId);
|
|
2531
|
+
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
|
|
2532
|
+
Button,
|
|
2533
|
+
{
|
|
2534
|
+
variant: "ghost",
|
|
2535
|
+
onClick: () => handleProblemClick(issue),
|
|
2536
|
+
disabled: !isNavigable,
|
|
2537
|
+
className: `flex h-auto w-full items-start justify-start gap-2 rounded-none px-3 py-1.5 text-left text-sm font-normal ${isNavigable ? "hover:bg-muted" : "cursor-default hover:bg-transparent"}`,
|
|
2538
|
+
children: [
|
|
2539
|
+
issue.severity === "error" ? /* @__PURE__ */ jsx(CircleAlert, { className: "mt-0.5 h-4 w-4 shrink-0 text-status-error-text", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(TriangleAlert, { className: "mt-0.5 h-4 w-4 shrink-0 text-status-warning-text", "aria-hidden": "true" }),
|
|
2540
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 text-foreground", children: issue.message }),
|
|
2541
|
+
issue.nodeLabel && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: issue.nodeLabel })
|
|
2542
|
+
]
|
|
2543
|
+
}
|
|
2544
|
+
) }, issue.id);
|
|
2545
|
+
}) })
|
|
2546
|
+
] }) }) : null;
|
|
547
2547
|
if (isMobile) {
|
|
548
2548
|
return /* @__PURE__ */ jsxs(Page, { className: "flex h-[100svh] flex-col space-y-0 overflow-hidden", children: [
|
|
2549
|
+
structuralConflictBanner,
|
|
2550
|
+
draftRestoreBanner,
|
|
549
2551
|
/* @__PURE__ */ jsx(
|
|
550
2552
|
MobileVisualEditor,
|
|
551
2553
|
{
|
|
@@ -558,500 +2560,419 @@ function VisualEditorPage() {
|
|
|
558
2560
|
onNodeClick: handleNodeClick,
|
|
559
2561
|
onEdgeClick: handleEdgeClick,
|
|
560
2562
|
onConnect: handleConnect,
|
|
2563
|
+
onReconnect: handleReconnect,
|
|
561
2564
|
onAddNode: handleAddNode,
|
|
562
2565
|
onSave: handleSave,
|
|
563
2566
|
onValidate: handleValidate,
|
|
564
|
-
|
|
565
|
-
onLoadExample:
|
|
2567
|
+
onStartInstance: () => setStartOpen(true),
|
|
2568
|
+
onLoadExample: handleOpenTemplateGallery,
|
|
566
2569
|
onClear: handleClear,
|
|
567
2570
|
metadata,
|
|
568
|
-
|
|
2571
|
+
onMetadataOpenChange: handleMetadataOpenChange
|
|
569
2572
|
}
|
|
570
2573
|
),
|
|
571
2574
|
sharedDialogs,
|
|
572
2575
|
ConfirmDialogElement
|
|
573
2576
|
] });
|
|
574
2577
|
}
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
2578
|
+
const fullBleedSideMargin = "calc(-1 * (max(0px, (100vw - var(--app-content-offset, 0px) - 1536px) / 2) + 24px))";
|
|
2579
|
+
const fullBleedStyle = !isCompactViewport ? {
|
|
2580
|
+
width: "calc(100vw - var(--app-content-offset, 0px))",
|
|
2581
|
+
marginLeft: fullBleedSideMargin,
|
|
2582
|
+
marginRight: fullBleedSideMargin
|
|
2583
|
+
} : {};
|
|
2584
|
+
return /* @__PURE__ */ jsxs(
|
|
2585
|
+
Page,
|
|
2586
|
+
{
|
|
2587
|
+
className: "flex min-h-[calc(100svh-7rem)] flex-col space-y-0 overflow-x-hidden",
|
|
2588
|
+
style: fullBleedStyle,
|
|
2589
|
+
children: [
|
|
2590
|
+
/* @__PURE__ */ jsx("div", { className: `shrink-0 border-b border-border bg-background ${focusMode ? "px-3 py-1.5 md:px-4" : "px-3 py-2 md:px-6 md:py-3"}`, children: /* @__PURE__ */ jsx(
|
|
2591
|
+
FormHeader,
|
|
2592
|
+
{
|
|
2593
|
+
mode: "detail",
|
|
2594
|
+
backHref: "/backend/definitions",
|
|
2595
|
+
backLabel: t("workflows.definitions.backToList", "Back to definitions"),
|
|
2596
|
+
title: definitionId ? workflowName || t("workflows.definitions.singular") : t("workflows.backend.definitions.visual_editor.title"),
|
|
2597
|
+
subtitle: focusMode ? void 0 : definitionId ? t("workflows.definitions.detail.summary", "Editing workflow definition") : t("workflows.definitions.create.summary", "Create and edit workflow definitions visually with a drag-and-drop interface"),
|
|
2598
|
+
actionsContent: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-end gap-1 md:gap-2", children: [
|
|
2599
|
+
draftEligible && (draftSaveFailed || draftSavedLabel) && /* @__PURE__ */ jsx("span", { role: "status", className: "text-xs text-muted-foreground", children: draftSaveFailed ? t("workflows.visualEditor.draft.saveFailed", "Draft not saved") : t("workflows.visualEditor.draft.saved", "Draft saved {time}", { time: draftSavedLabel ?? "" }) }),
|
|
2600
|
+
!isCodeOnly && definitionId && autosaveState !== "idle" && /* @__PURE__ */ jsx("span", { className: "mr-1 text-xs text-muted-foreground", "aria-live": "polite", children: autosaveState === "saving" ? t("workflows.visualEditor.autosaving") : t("workflows.visualEditor.autosaved") }),
|
|
2601
|
+
focusMode ? (
|
|
2602
|
+
// Focus mode collapses the toolbar to the essentials; the toggle
|
|
2603
|
+
// stays reachable so the author can leave, and Cmd+K still opens
|
|
2604
|
+
// every command.
|
|
2605
|
+
/* @__PURE__ */ jsxs(
|
|
2606
|
+
Button,
|
|
2607
|
+
{
|
|
2608
|
+
variant: "outline",
|
|
2609
|
+
size: "sm",
|
|
2610
|
+
onClick: toggleFocus,
|
|
2611
|
+
disabled: isSaving,
|
|
2612
|
+
className: "h-8 px-2 text-xs",
|
|
2613
|
+
"aria-label": t("workflows.visualEditor.exitFocusMode"),
|
|
2614
|
+
children: [
|
|
2615
|
+
/* @__PURE__ */ jsx(Minimize2, { className: "mr-1.5 h-4 w-4" }),
|
|
2616
|
+
t("workflows.visualEditor.exitFocusMode")
|
|
2617
|
+
]
|
|
2618
|
+
}
|
|
2619
|
+
)
|
|
2620
|
+
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2621
|
+
/* @__PURE__ */ jsxs(
|
|
2622
|
+
Button,
|
|
2623
|
+
{
|
|
2624
|
+
variant: "outline",
|
|
2625
|
+
size: "sm",
|
|
2626
|
+
onClick: handleValidate,
|
|
2627
|
+
disabled: isSaving,
|
|
2628
|
+
className: "h-8 px-2 text-xs",
|
|
2629
|
+
"aria-label": problemErrorCount > 0 ? t("workflows.visualEditor.validateWithErrors", "{count} validation errors", { count: problemErrorCount }) : t("workflows.visualEditor.validate"),
|
|
2630
|
+
children: [
|
|
2631
|
+
problemErrorCount > 0 ? /* @__PURE__ */ jsx(CircleAlert, { className: "mr-1.5 h-4 w-4 text-destructive", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(CircleQuestionMark, { className: "mr-1.5 h-4 w-4", "aria-hidden": "true" }),
|
|
2632
|
+
t("workflows.visualEditor.validate"),
|
|
2633
|
+
problemErrorCount > 0 && /* @__PURE__ */ jsx("span", { className: "ml-1.5 inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-destructive px-1 text-xs font-semibold text-white", children: problemErrorCount })
|
|
2634
|
+
]
|
|
2635
|
+
}
|
|
2636
|
+
),
|
|
2637
|
+
(() => {
|
|
2638
|
+
const moreActions = [];
|
|
2639
|
+
moreActions.push({ id: "group-tools", header: t("workflows.visualEditor.menuGroups.tools", "Tools") });
|
|
2640
|
+
moreActions.push({ id: "commands", label: t("workflows.commandPalette.buttonLabel", "Commands"), icon: Command, onSelect: () => setShowCommandPalette(true) });
|
|
2641
|
+
if (!isCodeOnly) moreActions.push({ id: "ai-draft", label: t("workflows.aiDraft.toggleShort", "AI draft"), icon: Sparkles, onSelect: () => setShowAiDraft(true) });
|
|
2642
|
+
moreActions.push({ id: "group-view", header: t("workflows.visualEditor.menuGroups.view", "View") });
|
|
2643
|
+
moreActions.push({ id: "focus", label: t("workflows.visualEditor.enterFocusMode"), icon: Maximize2, onSelect: toggleFocus, disabled: isSaving });
|
|
2644
|
+
moreActions.push({ id: "settings", label: t("workflows.visualEditor.metadata.buttonLabel", "Settings"), icon: metadataIncomplete ? TriangleAlert : PanelRightOpen, onSelect: () => setMetadataOpen(true), disabled: isSaving });
|
|
2645
|
+
moreActions.push({ id: "code", label: t("workflows.visualEditor.codeView.title", "Code"), icon: Code, onSelect: () => setShowCodeView(true) });
|
|
2646
|
+
moreActions.push({ id: "group-canvas", header: t("workflows.visualEditor.menuGroups.canvas", "Canvas") });
|
|
2647
|
+
if (!isCodeOnly) moreActions.push({ id: "load-example", label: t("workflows.visualEditor.loadExample"), onSelect: handleOpenTemplateGallery, disabled: isSaving });
|
|
2648
|
+
if (!isCodeOnly) moreActions.push({ id: "auto-arrange", label: t("workflows.visualEditor.autoArrange"), icon: Network, onSelect: handleAutoArrange, disabled: isSaving || nodes.length === 0 });
|
|
2649
|
+
moreActions.push({ id: "compensation", label: t("workflows.compensation.toggleShort", "Compensation"), icon: ShieldMinus, onSelect: toggleCompensation });
|
|
2650
|
+
if (isCodeOverride) moreActions.push({ id: "reset-to-code", label: t("workflows.actions.resetToCode"), onSelect: handleResetToCode, disabled: isSaving });
|
|
2651
|
+
if (!isCodeOnly) {
|
|
2652
|
+
moreActions.push({ separator: true, id: "sep-destructive" });
|
|
2653
|
+
moreActions.push({ id: "clear", label: t("workflows.visualEditor.clear"), icon: Trash2, onSelect: handleClear, disabled: isSaving, destructive: true });
|
|
2654
|
+
}
|
|
2655
|
+
return /* @__PURE__ */ jsx(
|
|
2656
|
+
ActionsDropdown,
|
|
2657
|
+
{
|
|
2658
|
+
items: moreActions,
|
|
2659
|
+
label: t("workflows.visualEditor.more", "More"),
|
|
2660
|
+
ariaLabel: t("workflows.visualEditor.more", "More"),
|
|
2661
|
+
triggerClassName: "h-8 px-2 text-xs",
|
|
2662
|
+
triggerIcon: false,
|
|
2663
|
+
attention: metadataIncomplete
|
|
2664
|
+
}
|
|
2665
|
+
);
|
|
2666
|
+
})(),
|
|
2667
|
+
definitionId && /* @__PURE__ */ jsxs(
|
|
2668
|
+
Button,
|
|
2669
|
+
{
|
|
2670
|
+
variant: "outline",
|
|
2671
|
+
size: "sm",
|
|
2672
|
+
onClick: toggleLastRun,
|
|
2673
|
+
"aria-pressed": showLastRun,
|
|
2674
|
+
className: "h-8 px-2 text-xs",
|
|
2675
|
+
"aria-label": t("workflows.lastRun.toggle", "Show the last run on the canvas"),
|
|
2676
|
+
children: [
|
|
2677
|
+
/* @__PURE__ */ jsx(History, { className: "mr-1.5 h-4 w-4", "aria-hidden": "true" }),
|
|
2678
|
+
t("workflows.lastRun.toggleShort", "Last run"),
|
|
2679
|
+
showLastRun && lastRunOverlay.isUnavailable ? /* @__PURE__ */ jsx("span", { className: "ml-1.5 text-muted-foreground", children: t("workflows.lastRun.never", "(never run)") }) : null
|
|
2680
|
+
]
|
|
2681
|
+
}
|
|
2682
|
+
),
|
|
2683
|
+
definitionId && /* @__PURE__ */ jsxs(
|
|
2684
|
+
Button,
|
|
2685
|
+
{
|
|
2686
|
+
variant: "outline",
|
|
2687
|
+
size: "sm",
|
|
2688
|
+
onClick: () => setStartOpen(true),
|
|
2689
|
+
disabled: isSaving,
|
|
2690
|
+
className: "h-8 text-xs",
|
|
2691
|
+
children: [
|
|
2692
|
+
/* @__PURE__ */ jsx(Play, { className: "mr-1.5 h-4 w-4" }),
|
|
2693
|
+
t("workflows.actions.startInstance")
|
|
2694
|
+
]
|
|
2695
|
+
}
|
|
2696
|
+
)
|
|
2697
|
+
] }),
|
|
2698
|
+
isCodeOnly ? /* @__PURE__ */ jsx(
|
|
2699
|
+
Button,
|
|
2700
|
+
{
|
|
2701
|
+
size: "sm",
|
|
2702
|
+
onClick: handleCustomize,
|
|
2703
|
+
disabled: isSaving,
|
|
2704
|
+
className: "h-8 px-2 text-xs md:px-3",
|
|
2705
|
+
children: t("workflows.actions.customize")
|
|
2706
|
+
}
|
|
2707
|
+
) : /* @__PURE__ */ jsxs(
|
|
2708
|
+
Button,
|
|
2709
|
+
{
|
|
2710
|
+
size: "sm",
|
|
2711
|
+
onClick: handleSave,
|
|
2712
|
+
disabled: isSaving,
|
|
2713
|
+
className: "h-8 px-2 text-xs md:px-3",
|
|
2714
|
+
"aria-label": isSaving ? t("workflows.mobile.saving") : definitionId ? t("workflows.common.update") : t("workflows.common.save"),
|
|
2715
|
+
children: [
|
|
2716
|
+
/* @__PURE__ */ jsx(Save, { className: "mr-1.5 h-4 w-4" }),
|
|
2717
|
+
isSaving ? t("workflows.mobile.saving") : definitionId ? t("workflows.common.update") : t("workflows.common.save")
|
|
2718
|
+
]
|
|
2719
|
+
}
|
|
2720
|
+
)
|
|
2721
|
+
] })
|
|
2722
|
+
}
|
|
2723
|
+
) }),
|
|
2724
|
+
(isCodeOnly || isCodeOverride) && /* @__PURE__ */ jsxs("div", { className: "shrink-0 border-b border-border bg-background px-3 py-2 md:px-6 md:py-3", children: [
|
|
2725
|
+
isCodeOnly && /* @__PURE__ */ jsx(Alert, { status: "information", children: /* @__PURE__ */ jsx(AlertTitle, { children: t("workflows.source.code.readonlyBanner") }) }),
|
|
2726
|
+
isCodeOverride && /* @__PURE__ */ jsx(Alert, { status: "warning", children: /* @__PURE__ */ jsx(AlertTitle, { children: t("workflows.source.code_override.banner") }) })
|
|
2727
|
+
] }),
|
|
2728
|
+
structuralConflictBanner,
|
|
2729
|
+
draftRestoreBanner,
|
|
2730
|
+
isCompactViewport ? /* @__PURE__ */ jsxs("div", { className: "px-3 py-3 md:px-6 md:py-4", children: [
|
|
2731
|
+
/* @__PURE__ */ jsxs("div", { className: "relative min-w-0", children: [
|
|
2732
|
+
/* @__PURE__ */ jsx("div", { className: "h-[64svh] min-h-[360px] rounded-lg border bg-card", children: /* @__PURE__ */ jsx(
|
|
2733
|
+
WorkflowGraph,
|
|
707
2734
|
{
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
2735
|
+
initialNodes: nodes,
|
|
2736
|
+
initialEdges: edges,
|
|
2737
|
+
onNodesChange: handleNodesChange,
|
|
2738
|
+
onEdgesChange: handleEdgesChange,
|
|
2739
|
+
onNodeClick: handleNodeClick,
|
|
2740
|
+
onEdgeClick: handleEdgeClick,
|
|
2741
|
+
onConnect: handleConnect,
|
|
2742
|
+
onReconnect: handleReconnect,
|
|
2743
|
+
onCanvasDrop: handleCanvasDrop,
|
|
2744
|
+
editable: !isCodeOnly,
|
|
2745
|
+
height: "100%",
|
|
2746
|
+
focusTarget,
|
|
2747
|
+
nodeErrorCounts,
|
|
2748
|
+
showCompensation,
|
|
2749
|
+
runOverlay: lastRunOverlay.execution,
|
|
2750
|
+
runAgentLabels: lastRunOverlay.agentLabels,
|
|
2751
|
+
triggers,
|
|
2752
|
+
definitionEnabled: enabled,
|
|
2753
|
+
onOpenTriggers: handleOpenTriggers
|
|
714
2754
|
}
|
|
715
|
-
),
|
|
716
|
-
|
|
2755
|
+
) }),
|
|
2756
|
+
nodes.length === 0 && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-0 flex items-center justify-center px-4", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
2757
|
+
/* @__PURE__ */ jsx("h2", { className: "mb-2 text-lg font-semibold text-foreground", children: t("workflows.visualEditor.startBuilding") }),
|
|
2758
|
+
/* @__PURE__ */ jsx("p", { className: "mb-4 text-sm text-muted-foreground", children: t("workflows.visualEditor.tapToAddBelow") }),
|
|
2759
|
+
/* @__PURE__ */ jsx(
|
|
2760
|
+
"button",
|
|
2761
|
+
{
|
|
2762
|
+
onClick: handleOpenTemplateGallery,
|
|
2763
|
+
className: "pointer-events-auto text-sm text-primary hover:underline",
|
|
2764
|
+
children: t("workflows.visualEditor.loadExampleWorkflow")
|
|
2765
|
+
}
|
|
2766
|
+
)
|
|
2767
|
+
] }) })
|
|
717
2768
|
] }),
|
|
718
|
-
/* @__PURE__ */ jsxs("div", { className: "
|
|
719
|
-
/* @__PURE__ */
|
|
720
|
-
|
|
721
|
-
|
|
2769
|
+
!isCodeOnly && /* @__PURE__ */ jsxs("div", { className: "mt-3 rounded-lg border bg-card p-3", children: [
|
|
2770
|
+
/* @__PURE__ */ jsx("h2", { className: "mb-2 text-xs font-semibold uppercase text-muted-foreground", children: t("workflows.visualEditor.stepPalette") }),
|
|
2771
|
+
/* @__PURE__ */ jsx("p", { className: "mb-3 text-xs text-muted-foreground", children: t("workflows.visualEditor.tapToAdd") }),
|
|
2772
|
+
/* @__PURE__ */ jsx("div", { className: "flex gap-2 overflow-x-auto pb-1", children: ["start", "userTask", "automated", "invokeAgent", "waitForSignal", "waitForTimer", "subWorkflow", "end"].map((nodeType) => {
|
|
2773
|
+
const Icon = NODE_TYPE_ICONS[nodeType];
|
|
2774
|
+
return /* @__PURE__ */ jsxs(
|
|
2775
|
+
"button",
|
|
2776
|
+
{
|
|
2777
|
+
draggable: true,
|
|
2778
|
+
onDragStart: (event) => handlePaletteStepDragStart(event, nodeType),
|
|
2779
|
+
onClick: () => handleAddNode(nodeType),
|
|
2780
|
+
className: "flex shrink-0 items-center gap-1 rounded-md border bg-background px-2 py-1 text-xs hover:bg-muted active:bg-muted/50",
|
|
2781
|
+
children: [
|
|
2782
|
+
/* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5" }),
|
|
2783
|
+
/* @__PURE__ */ jsx("span", { children: NODE_TYPE_LABELS[nodeType].title })
|
|
2784
|
+
]
|
|
2785
|
+
},
|
|
2786
|
+
nodeType
|
|
2787
|
+
);
|
|
2788
|
+
}) })
|
|
2789
|
+
] })
|
|
2790
|
+
] }) : /* @__PURE__ */ jsxs("div", { "data-testid": "workflow-editor-row", className: "flex min-h-0 min-w-0 flex-1 border-t border-border pl-3 md:pl-6", children: [
|
|
2791
|
+
!isCodeOnly && /* @__PURE__ */ jsxs("div", { className: `${paletteCollapsed ? "w-14" : "w-48"} shrink-0 overflow-y-auto border-r border-border bg-background p-2`, children: [
|
|
2792
|
+
/* @__PURE__ */ jsxs("div", { className: `mb-2 flex items-center ${paletteCollapsed ? "justify-center" : "justify-between"}`, children: [
|
|
2793
|
+
!paletteCollapsed && /* @__PURE__ */ jsx("h2", { className: "px-1 text-xs font-semibold uppercase text-muted-foreground", children: t("workflows.visualEditor.stepPalette") }),
|
|
2794
|
+
/* @__PURE__ */ jsx(
|
|
2795
|
+
IconButton,
|
|
2796
|
+
{
|
|
2797
|
+
type: "button",
|
|
2798
|
+
variant: "ghost",
|
|
2799
|
+
size: "sm",
|
|
2800
|
+
onClick: togglePaletteCollapsed,
|
|
2801
|
+
title: paletteCollapsed ? t("workflows.visualEditor.expandPalette") : t("workflows.visualEditor.collapsePalette"),
|
|
2802
|
+
"aria-label": paletteCollapsed ? t("workflows.visualEditor.expandPalette") : t("workflows.visualEditor.collapsePalette"),
|
|
2803
|
+
children: paletteCollapsed ? /* @__PURE__ */ jsx(PanelLeftOpen, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(PanelLeftClose, { className: "h-4 w-4" })
|
|
2804
|
+
}
|
|
2805
|
+
)
|
|
722
2806
|
] }),
|
|
723
|
-
/* @__PURE__ */ jsx(
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
className: "h-8 text-sm"
|
|
744
|
-
}
|
|
745
|
-
)
|
|
746
|
-
] }),
|
|
747
|
-
/* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-1 sm:col-span-2 lg:col-span-3", children: [
|
|
748
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "description", className: "text-xs", children: t("workflows.form.description") }),
|
|
749
|
-
/* @__PURE__ */ jsx(
|
|
750
|
-
Textarea,
|
|
751
|
-
{
|
|
752
|
-
id: "description",
|
|
753
|
-
value: description,
|
|
754
|
-
onChange: (e) => setDescription(e.target.value),
|
|
755
|
-
placeholder: t("workflows.form.placeholders.description"),
|
|
756
|
-
rows: 2,
|
|
757
|
-
className: "min-h-[60px] text-sm"
|
|
2807
|
+
!paletteCollapsed && /* @__PURE__ */ jsx("p", { className: "mb-2 px-1 text-xs text-muted-foreground", children: t("workflows.visualEditor.clickToAdd") }),
|
|
2808
|
+
/* @__PURE__ */ jsx("div", { className: `flex flex-col gap-1 ${paletteCollapsed ? "items-center" : ""}`, children: PALETTE_NODE_TYPES.map((nodeType) => {
|
|
2809
|
+
const Icon = NODE_TYPE_ICONS[nodeType];
|
|
2810
|
+
const label = NODE_TYPE_LABELS[nodeType];
|
|
2811
|
+
const tooltip = `${label.title} \u2014 ${label.description}`;
|
|
2812
|
+
if (paletteCollapsed) {
|
|
2813
|
+
return /* @__PURE__ */ jsx(
|
|
2814
|
+
"button",
|
|
2815
|
+
{
|
|
2816
|
+
type: "button",
|
|
2817
|
+
draggable: true,
|
|
2818
|
+
onDragStart: (event) => handlePaletteStepDragStart(event, nodeType),
|
|
2819
|
+
onClick: () => handleAddNode(nodeType),
|
|
2820
|
+
title: tooltip,
|
|
2821
|
+
"aria-label": tooltip,
|
|
2822
|
+
className: "flex h-9 w-9 items-center justify-center rounded-md border hover:bg-muted",
|
|
2823
|
+
children: /* @__PURE__ */ jsx(Icon, { className: `h-4 w-4 ${NODE_TYPE_COLORS[nodeType]}` })
|
|
2824
|
+
},
|
|
2825
|
+
nodeType
|
|
2826
|
+
);
|
|
758
2827
|
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
2828
|
+
return /* @__PURE__ */ jsxs(
|
|
2829
|
+
"button",
|
|
2830
|
+
{
|
|
2831
|
+
type: "button",
|
|
2832
|
+
draggable: true,
|
|
2833
|
+
onDragStart: (event) => handlePaletteStepDragStart(event, nodeType),
|
|
2834
|
+
onClick: () => handleAddNode(nodeType),
|
|
2835
|
+
title: label.description,
|
|
2836
|
+
className: "flex w-full items-center gap-2 rounded-md border px-2 py-1.5 text-left text-xs hover:bg-muted",
|
|
2837
|
+
children: [
|
|
2838
|
+
/* @__PURE__ */ jsx(Icon, { className: `h-4 w-4 shrink-0 ${NODE_TYPE_COLORS[nodeType]}` }),
|
|
2839
|
+
/* @__PURE__ */ jsx("span", { className: "truncate font-medium text-foreground", children: label.title })
|
|
2840
|
+
]
|
|
2841
|
+
},
|
|
2842
|
+
nodeType
|
|
2843
|
+
);
|
|
2844
|
+
}) }),
|
|
2845
|
+
!paletteCollapsed && /* @__PURE__ */ jsxs("div", { className: "mt-4", children: [
|
|
2846
|
+
/* @__PURE__ */ jsx("h2", { className: "mb-1 px-1 text-xs font-semibold uppercase text-muted-foreground", children: t("workflows.visualEditor.palette.actionsTitle", "Actions") }),
|
|
2847
|
+
/* @__PURE__ */ jsx("p", { className: "mb-2 px-1 text-xs text-muted-foreground", children: t("workflows.visualEditor.palette.dragActionHint", "Drop an action onto a route to add it there") }),
|
|
2848
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1", children: activityTypeOptions.map((option) => {
|
|
2849
|
+
const ActionIcon = resolveActivityIcon(option.value);
|
|
2850
|
+
return /* @__PURE__ */ jsxs(
|
|
2851
|
+
"button",
|
|
2852
|
+
{
|
|
2853
|
+
type: "button",
|
|
2854
|
+
draggable: true,
|
|
2855
|
+
onDragStart: (event) => handlePaletteActivityDragStart(event, option.value, option.label),
|
|
2856
|
+
onClick: () => flash(t("workflows.visualEditor.palette.dragActionHint", "Drop an action onto a route to add it there"), "info"),
|
|
2857
|
+
title: t("workflows.visualEditor.palette.dragActionHint", "Drop an action onto a route to add it there"),
|
|
2858
|
+
className: "flex w-full items-center gap-2 rounded-md border px-2 py-1.5 text-left text-xs hover:bg-muted",
|
|
2859
|
+
children: [
|
|
2860
|
+
/* @__PURE__ */ jsx(ActionIcon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
|
|
2861
|
+
/* @__PURE__ */ jsx("span", { className: "truncate font-medium text-foreground", children: option.label })
|
|
2862
|
+
]
|
|
2863
|
+
},
|
|
2864
|
+
option.value
|
|
2865
|
+
);
|
|
2866
|
+
}) })
|
|
765
2867
|
] }),
|
|
766
|
-
/* @__PURE__ */
|
|
767
|
-
|
|
768
|
-
{
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
2868
|
+
!paletteCollapsed && /* @__PURE__ */ jsxs("div", { className: "mt-4", children: [
|
|
2869
|
+
/* @__PURE__ */ jsx("h2", { className: "mb-1 px-1 text-xs font-semibold uppercase text-muted-foreground", children: t("workflows.annotations.paletteTitle", "Annotations") }),
|
|
2870
|
+
/* @__PURE__ */ jsx("p", { className: "mb-2 px-1 text-xs text-muted-foreground", children: t("workflows.annotations.paletteHint", "Documentation only \u2014 never executed") }),
|
|
2871
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
2872
|
+
/* @__PURE__ */ jsxs(
|
|
2873
|
+
"button",
|
|
2874
|
+
{
|
|
2875
|
+
type: "button",
|
|
2876
|
+
onClick: () => handleAddAnnotation("note"),
|
|
2877
|
+
title: t("workflows.annotations.note.addHint", "Add a markdown sticky note to the canvas"),
|
|
2878
|
+
className: "flex w-full items-center gap-2 rounded-md border px-2 py-1.5 text-left text-xs hover:bg-muted",
|
|
2879
|
+
children: [
|
|
2880
|
+
/* @__PURE__ */ jsx(StickyNote, { className: "h-4 w-4 shrink-0 text-status-warning-text", "aria-hidden": "true" }),
|
|
2881
|
+
/* @__PURE__ */ jsx("span", { className: "truncate font-medium text-foreground", children: t("workflows.annotations.note.add", "Note") })
|
|
2882
|
+
]
|
|
2883
|
+
}
|
|
2884
|
+
),
|
|
2885
|
+
/* @__PURE__ */ jsxs(
|
|
2886
|
+
"button",
|
|
2887
|
+
{
|
|
2888
|
+
type: "button",
|
|
2889
|
+
onClick: () => handleAddAnnotation("group"),
|
|
2890
|
+
title: t("workflows.annotations.group.addHint", "Add a named region around part of the canvas"),
|
|
2891
|
+
className: "flex w-full items-center gap-2 rounded-md border px-2 py-1.5 text-left text-xs hover:bg-muted",
|
|
2892
|
+
children: [
|
|
2893
|
+
/* @__PURE__ */ jsx(Group, { className: "h-4 w-4 shrink-0 text-muted-foreground", "aria-hidden": "true" }),
|
|
2894
|
+
/* @__PURE__ */ jsx("span", { className: "truncate font-medium text-foreground", children: t("workflows.annotations.group.add", "Group") })
|
|
2895
|
+
]
|
|
2896
|
+
}
|
|
2897
|
+
)
|
|
2898
|
+
] })
|
|
2899
|
+
] }),
|
|
2900
|
+
!paletteCollapsed && /* @__PURE__ */ jsxs("div", { className: "mt-3", children: [
|
|
2901
|
+
/* @__PURE__ */ jsxs(
|
|
2902
|
+
"button",
|
|
784
2903
|
{
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
2904
|
+
type: "button",
|
|
2905
|
+
onClick: () => setShowPaletteHowTo((prev) => !prev),
|
|
2906
|
+
"aria-expanded": showPaletteHowTo,
|
|
2907
|
+
className: "flex w-full items-center gap-1.5 rounded-md px-1 py-1 text-xs text-muted-foreground hover:text-foreground",
|
|
2908
|
+
children: [
|
|
2909
|
+
/* @__PURE__ */ jsx(CircleQuestionMark, { className: "h-3.5 w-3.5" }),
|
|
2910
|
+
/* @__PURE__ */ jsx("span", { children: t("workflows.visualEditor.howToUse", "How to use:") })
|
|
2911
|
+
]
|
|
788
2912
|
}
|
|
789
2913
|
),
|
|
790
|
-
/* @__PURE__ */
|
|
2914
|
+
showPaletteHowTo && /* @__PURE__ */ jsxs(Alert, { variant: "info", className: "mt-2", children: [
|
|
2915
|
+
/* @__PURE__ */ jsx(AlertTitle, { className: "text-xs", children: t("workflows.visualEditor.howToUse", "How to use:") }),
|
|
2916
|
+
/* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsxs("ul", { className: "list-inside list-disc space-y-1 text-xs", children: [
|
|
2917
|
+
/* @__PURE__ */ jsx("li", { children: t("workflows.visualEditor.hint.addSteps", "Click step types to add them") }),
|
|
2918
|
+
/* @__PURE__ */ jsx("li", { children: t("workflows.visualEditor.hint.dragFromPalette", "Drag a step onto the canvas, or onto a route to insert it there") }),
|
|
2919
|
+
/* @__PURE__ */ jsx("li", { children: t("workflows.visualEditor.hint.dragSteps", "Drag steps to position them") }),
|
|
2920
|
+
/* @__PURE__ */ jsx("li", { children: t("workflows.visualEditor.hint.connectSteps", "Connect steps by dragging from handles") }),
|
|
2921
|
+
/* @__PURE__ */ jsx("li", { children: t("workflows.visualEditor.hint.editSteps", "Click steps and transitions to edit them") }),
|
|
2922
|
+
/* @__PURE__ */ jsx("li", { children: t("workflows.visualEditor.hint.copyPaste", "Select steps and copy, paste or duplicate them with Cmd/Ctrl+C, +V and +D") }),
|
|
2923
|
+
/* @__PURE__ */ jsx("li", { children: t("workflows.visualEditor.hint.commandPalette", "Press Cmd/Ctrl+K for every command without the mouse") }),
|
|
2924
|
+
/* @__PURE__ */ jsx("li", { children: t("workflows.visualEditor.hint.keyboardCanvas", "With a step selected: Enter opens it, Del removes it, arrows nudge it (hold Shift for bigger steps)") }),
|
|
2925
|
+
/* @__PURE__ */ jsx("li", { children: t("workflows.visualEditor.hint.validate", "Validate before saving") })
|
|
2926
|
+
] }) })
|
|
2927
|
+
] })
|
|
791
2928
|
] })
|
|
792
2929
|
] }),
|
|
793
|
-
/* @__PURE__ */
|
|
794
|
-
/* @__PURE__ */ jsx(
|
|
795
|
-
|
|
796
|
-
TagsInput,
|
|
2930
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1 p-6", children: /* @__PURE__ */ jsxs("div", { className: "relative h-full min-h-[480px]", children: [
|
|
2931
|
+
/* @__PURE__ */ jsx("div", { className: "h-full rounded-lg border bg-card", children: /* @__PURE__ */ jsx(
|
|
2932
|
+
WorkflowGraph,
|
|
797
2933
|
{
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
2934
|
+
initialNodes: nodes,
|
|
2935
|
+
initialEdges: edges,
|
|
2936
|
+
onNodesChange: handleNodesChange,
|
|
2937
|
+
onEdgesChange: handleEdgesChange,
|
|
2938
|
+
onNodeClick: handleNodeClick,
|
|
2939
|
+
onEdgeClick: handleEdgeClick,
|
|
2940
|
+
onConnect: handleConnect,
|
|
2941
|
+
onReconnect: handleReconnect,
|
|
2942
|
+
onCanvasDrop: handleCanvasDrop,
|
|
2943
|
+
editable: !isCodeOnly,
|
|
2944
|
+
height: "100%",
|
|
2945
|
+
focusTarget,
|
|
2946
|
+
nodeErrorCounts,
|
|
2947
|
+
showCompensation,
|
|
2948
|
+
runOverlay: lastRunOverlay.execution,
|
|
2949
|
+
runAgentLabels: lastRunOverlay.agentLabels,
|
|
2950
|
+
triggers,
|
|
2951
|
+
definitionEnabled: enabled,
|
|
2952
|
+
onOpenTriggers: handleOpenTriggers
|
|
801
2953
|
}
|
|
802
|
-
)
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
/* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-1", children: [
|
|
818
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "effectiveFrom", className: "text-xs", children: t("workflows.form.effectiveFrom") }),
|
|
819
|
-
/* @__PURE__ */ jsx(
|
|
820
|
-
Input,
|
|
821
|
-
{
|
|
822
|
-
id: "effectiveFrom",
|
|
823
|
-
type: "date",
|
|
824
|
-
value: effectiveFrom,
|
|
825
|
-
onChange: (e) => setEffectiveFrom(e.target.value),
|
|
826
|
-
className: "h-8 text-sm"
|
|
827
|
-
}
|
|
828
|
-
)
|
|
829
|
-
] }),
|
|
830
|
-
/* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-1", children: [
|
|
831
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "effectiveTo", className: "text-xs", children: t("workflows.form.effectiveTo") }),
|
|
832
|
-
/* @__PURE__ */ jsx(
|
|
833
|
-
Input,
|
|
834
|
-
{
|
|
835
|
-
id: "effectiveTo",
|
|
836
|
-
type: "date",
|
|
837
|
-
value: effectiveTo,
|
|
838
|
-
onChange: (e) => setEffectiveTo(e.target.value),
|
|
839
|
-
className: "h-8 text-sm"
|
|
840
|
-
}
|
|
841
|
-
)
|
|
842
|
-
] })
|
|
843
|
-
] })
|
|
844
|
-
] }),
|
|
845
|
-
/* @__PURE__ */ jsx("fieldset", { disabled: isCodeOnly, className: "mt-3 disabled:opacity-70", children: /* @__PURE__ */ jsx(
|
|
846
|
-
DefinitionTriggersEditor,
|
|
847
|
-
{
|
|
848
|
-
value: triggers,
|
|
849
|
-
onChange: setTriggers
|
|
850
|
-
}
|
|
851
|
-
) })
|
|
852
|
-
] }),
|
|
853
|
-
isCompactViewport ? /* @__PURE__ */ jsxs("div", { className: "px-3 py-3 md:px-6 md:py-4", children: [
|
|
854
|
-
/* @__PURE__ */ jsxs("div", { className: "relative min-w-0", children: [
|
|
855
|
-
/* @__PURE__ */ jsx("div", { className: "h-[64svh] min-h-[360px] rounded-lg border bg-card", children: /* @__PURE__ */ jsx(
|
|
856
|
-
WorkflowGraph,
|
|
857
|
-
{
|
|
858
|
-
initialNodes: nodes,
|
|
859
|
-
initialEdges: edges,
|
|
860
|
-
onNodesChange: handleNodesChange,
|
|
861
|
-
onEdgesChange: handleEdgesChange,
|
|
862
|
-
onNodeClick: handleNodeClick,
|
|
863
|
-
onEdgeClick: handleEdgeClick,
|
|
864
|
-
onConnect: handleConnect,
|
|
865
|
-
editable: !isCodeOnly,
|
|
866
|
-
height: "100%"
|
|
867
|
-
}
|
|
868
|
-
) }),
|
|
869
|
-
nodes.length === 0 && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-0 flex items-center justify-center px-4", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
870
|
-
/* @__PURE__ */ jsx("h2", { className: "mb-2 text-lg font-semibold text-foreground", children: t("workflows.visualEditor.startBuilding") }),
|
|
871
|
-
/* @__PURE__ */ jsx("p", { className: "mb-4 text-sm text-muted-foreground", children: t("workflows.visualEditor.tapToAddBelow") }),
|
|
872
|
-
/* @__PURE__ */ jsx(
|
|
873
|
-
"button",
|
|
874
|
-
{
|
|
875
|
-
onClick: handleLoadExample,
|
|
876
|
-
className: "pointer-events-auto text-sm text-primary hover:underline",
|
|
877
|
-
children: t("workflows.visualEditor.loadExampleWorkflow")
|
|
878
|
-
}
|
|
879
|
-
)
|
|
880
|
-
] }) })
|
|
881
|
-
] }),
|
|
882
|
-
!isCodeOnly && /* @__PURE__ */ jsxs("div", { className: "mt-3 rounded-lg border bg-card p-3", children: [
|
|
883
|
-
/* @__PURE__ */ jsx("h2", { className: "mb-2 text-xs font-semibold uppercase text-muted-foreground", children: t("workflows.visualEditor.stepPalette") }),
|
|
884
|
-
/* @__PURE__ */ jsx("p", { className: "mb-3 text-xs text-muted-foreground", children: t("workflows.visualEditor.tapToAdd") }),
|
|
885
|
-
/* @__PURE__ */ jsx("div", { className: "flex gap-2 overflow-x-auto pb-1", children: ["start", "userTask", "automated", "waitForSignal", "waitForTimer", "subWorkflow", "end"].map((nodeType) => {
|
|
886
|
-
const Icon = NODE_TYPE_ICONS[nodeType];
|
|
887
|
-
return /* @__PURE__ */ jsxs(
|
|
888
|
-
"button",
|
|
889
|
-
{
|
|
890
|
-
onClick: () => handleAddNode(nodeType),
|
|
891
|
-
className: "flex shrink-0 items-center gap-1 rounded-md border bg-background px-2 py-1 text-xs hover:bg-muted active:bg-muted/50",
|
|
892
|
-
children: [
|
|
893
|
-
/* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5" }),
|
|
894
|
-
/* @__PURE__ */ jsx("span", { children: NODE_TYPE_LABELS[nodeType].title })
|
|
895
|
-
]
|
|
896
|
-
},
|
|
897
|
-
nodeType
|
|
898
|
-
);
|
|
899
|
-
}) })
|
|
900
|
-
] })
|
|
901
|
-
] }) : /* @__PURE__ */ jsxs("div", { className: "flex min-h-[72svh] min-w-0 flex-1 border-t border-border", children: [
|
|
902
|
-
!isCodeOnly && /* @__PURE__ */ jsx("div", { className: "w-[24rem] shrink-0 overflow-y-auto border-r border-border bg-background p-6", children: /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card p-4", children: [
|
|
903
|
-
/* @__PURE__ */ jsx("h2", { className: "mb-2 text-sm font-semibold uppercase text-muted-foreground", children: t("workflows.visualEditor.stepPalette") }),
|
|
904
|
-
/* @__PURE__ */ jsx("p", { className: "mb-4 text-xs text-muted-foreground", children: t("workflows.visualEditor.clickToAdd") }),
|
|
905
|
-
/* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
906
|
-
/* @__PURE__ */ jsxs(
|
|
907
|
-
"button",
|
|
908
|
-
{
|
|
909
|
-
onClick: () => handleAddNode("start"),
|
|
910
|
-
className: "group relative w-full cursor-pointer rounded-xl border-2 border-border bg-background px-4 py-3 text-left transition-all hover:border-muted-foreground/30 hover:shadow-md",
|
|
911
|
-
children: [
|
|
912
|
-
/* @__PURE__ */ jsx("div", { className: `absolute right-2 top-2 ${NODE_TYPE_COLORS.start} opacity-60 transition-opacity group-hover:opacity-100`, children: (() => {
|
|
913
|
-
const Icon = NODE_TYPE_ICONS.start;
|
|
914
|
-
return /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" });
|
|
915
|
-
})() }),
|
|
916
|
-
/* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-foreground", children: NODE_TYPE_LABELS.start.title }),
|
|
917
|
-
/* @__PURE__ */ jsx("div", { className: "mt-0.5 text-xs text-muted-foreground", children: NODE_TYPE_LABELS.start.description })
|
|
918
|
-
]
|
|
919
|
-
}
|
|
920
|
-
),
|
|
921
|
-
/* @__PURE__ */ jsxs(
|
|
922
|
-
"button",
|
|
923
|
-
{
|
|
924
|
-
onClick: () => handleAddNode("userTask"),
|
|
925
|
-
className: "group relative w-full cursor-pointer rounded-xl border-2 border-border bg-background px-4 py-3 text-left transition-all hover:border-muted-foreground/30 hover:shadow-md",
|
|
926
|
-
children: [
|
|
927
|
-
/* @__PURE__ */ jsx("div", { className: `absolute right-2 top-2 ${NODE_TYPE_COLORS.userTask} opacity-60 transition-opacity group-hover:opacity-100`, children: (() => {
|
|
928
|
-
const Icon = NODE_TYPE_ICONS.userTask;
|
|
929
|
-
return /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" });
|
|
930
|
-
})() }),
|
|
931
|
-
/* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-foreground", children: NODE_TYPE_LABELS.userTask.title }),
|
|
932
|
-
/* @__PURE__ */ jsx("div", { className: "mt-0.5 text-xs text-muted-foreground", children: NODE_TYPE_LABELS.userTask.description })
|
|
933
|
-
]
|
|
934
|
-
}
|
|
935
|
-
),
|
|
936
|
-
/* @__PURE__ */ jsxs(
|
|
937
|
-
"button",
|
|
938
|
-
{
|
|
939
|
-
onClick: () => handleAddNode("automated"),
|
|
940
|
-
className: "group relative w-full cursor-pointer rounded-xl border-2 border-border bg-background px-4 py-3 text-left transition-all hover:border-muted-foreground/30 hover:shadow-md",
|
|
941
|
-
children: [
|
|
942
|
-
/* @__PURE__ */ jsx("div", { className: `absolute right-2 top-2 ${NODE_TYPE_COLORS.automated} opacity-60 transition-opacity group-hover:opacity-100`, children: (() => {
|
|
943
|
-
const Icon = NODE_TYPE_ICONS.automated;
|
|
944
|
-
return /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" });
|
|
945
|
-
})() }),
|
|
946
|
-
/* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-foreground", children: NODE_TYPE_LABELS.automated.title }),
|
|
947
|
-
/* @__PURE__ */ jsx("div", { className: "mt-0.5 text-xs text-muted-foreground", children: NODE_TYPE_LABELS.automated.description })
|
|
948
|
-
]
|
|
949
|
-
}
|
|
950
|
-
),
|
|
951
|
-
/* @__PURE__ */ jsxs(
|
|
952
|
-
"button",
|
|
953
|
-
{
|
|
954
|
-
onClick: () => handleAddNode("waitForSignal"),
|
|
955
|
-
className: "group relative w-full cursor-pointer rounded-xl border-2 border-border bg-background px-4 py-3 text-left transition-all hover:border-muted-foreground/30 hover:shadow-md",
|
|
956
|
-
children: [
|
|
957
|
-
/* @__PURE__ */ jsx("div", { className: `absolute right-2 top-2 ${NODE_TYPE_COLORS.waitForSignal} opacity-60 transition-opacity group-hover:opacity-100`, children: (() => {
|
|
958
|
-
const Icon = NODE_TYPE_ICONS.waitForSignal;
|
|
959
|
-
return /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" });
|
|
960
|
-
})() }),
|
|
961
|
-
/* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-foreground", children: NODE_TYPE_LABELS.waitForSignal.title }),
|
|
962
|
-
/* @__PURE__ */ jsx("div", { className: "mt-0.5 text-xs text-muted-foreground", children: NODE_TYPE_LABELS.waitForSignal.description })
|
|
963
|
-
]
|
|
964
|
-
}
|
|
965
|
-
),
|
|
966
|
-
/* @__PURE__ */ jsxs(
|
|
967
|
-
"button",
|
|
968
|
-
{
|
|
969
|
-
onClick: () => handleAddNode("waitForTimer"),
|
|
970
|
-
className: "group relative w-full cursor-pointer rounded-xl border-2 border-border bg-background px-4 py-3 text-left transition-all hover:border-muted-foreground/30 hover:shadow-md",
|
|
971
|
-
children: [
|
|
972
|
-
/* @__PURE__ */ jsx("div", { className: `absolute right-2 top-2 ${NODE_TYPE_COLORS.waitForTimer} opacity-60 transition-opacity group-hover:opacity-100`, children: (() => {
|
|
973
|
-
const Icon = NODE_TYPE_ICONS.waitForTimer;
|
|
974
|
-
return /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" });
|
|
975
|
-
})() }),
|
|
976
|
-
/* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-foreground", children: NODE_TYPE_LABELS.waitForTimer.title }),
|
|
977
|
-
/* @__PURE__ */ jsx("div", { className: "mt-0.5 text-xs text-muted-foreground", children: NODE_TYPE_LABELS.waitForTimer.description })
|
|
978
|
-
]
|
|
979
|
-
}
|
|
980
|
-
),
|
|
981
|
-
/* @__PURE__ */ jsxs(
|
|
982
|
-
"button",
|
|
983
|
-
{
|
|
984
|
-
onClick: () => handleAddNode("subWorkflow"),
|
|
985
|
-
className: "group relative w-full cursor-pointer rounded-xl border-2 border-border bg-background px-4 py-3 text-left transition-all hover:border-muted-foreground/30 hover:shadow-md",
|
|
986
|
-
children: [
|
|
987
|
-
/* @__PURE__ */ jsx("div", { className: `absolute right-2 top-2 ${NODE_TYPE_COLORS.subWorkflow} opacity-60 transition-opacity group-hover:opacity-100`, children: (() => {
|
|
988
|
-
const Icon = NODE_TYPE_ICONS.subWorkflow;
|
|
989
|
-
return /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" });
|
|
990
|
-
})() }),
|
|
991
|
-
/* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-foreground", children: NODE_TYPE_LABELS.subWorkflow.title }),
|
|
992
|
-
/* @__PURE__ */ jsx("div", { className: "mt-0.5 text-xs text-muted-foreground", children: NODE_TYPE_LABELS.subWorkflow.description })
|
|
993
|
-
]
|
|
994
|
-
}
|
|
995
|
-
),
|
|
996
|
-
/* @__PURE__ */ jsxs(
|
|
997
|
-
"button",
|
|
998
|
-
{
|
|
999
|
-
onClick: () => handleAddNode("end"),
|
|
1000
|
-
className: "group relative w-full cursor-pointer rounded-xl border-2 border-border bg-background px-4 py-3 text-left transition-all hover:border-muted-foreground/30 hover:shadow-md",
|
|
1001
|
-
children: [
|
|
1002
|
-
/* @__PURE__ */ jsx("div", { className: `absolute right-2 top-2 ${NODE_TYPE_COLORS.end} opacity-60 transition-opacity group-hover:opacity-100`, children: (() => {
|
|
1003
|
-
const Icon = NODE_TYPE_ICONS.end;
|
|
1004
|
-
return /* @__PURE__ */ jsx(Icon, { className: "h-4 w-4" });
|
|
1005
|
-
})() }),
|
|
1006
|
-
/* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-foreground", children: NODE_TYPE_LABELS.end.title }),
|
|
1007
|
-
/* @__PURE__ */ jsx("div", { className: "mt-0.5 text-xs text-muted-foreground", children: NODE_TYPE_LABELS.end.description })
|
|
1008
|
-
]
|
|
1009
|
-
}
|
|
1010
|
-
)
|
|
2954
|
+
) }),
|
|
2955
|
+
nodes.length === 0 && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-0 flex items-center justify-center px-4", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
2956
|
+
/* @__PURE__ */ jsx("h2", { className: "mb-2 text-xl font-semibold text-foreground", children: t("workflows.visualEditor.startBuilding") }),
|
|
2957
|
+
/* @__PURE__ */ jsx("p", { className: "mb-4 text-muted-foreground", children: t("workflows.visualEditor.clickToAddFromPalette") }),
|
|
2958
|
+
/* @__PURE__ */ jsx(
|
|
2959
|
+
"button",
|
|
2960
|
+
{
|
|
2961
|
+
onClick: handleOpenTemplateGallery,
|
|
2962
|
+
className: "pointer-events-auto text-sm text-primary hover:underline",
|
|
2963
|
+
children: t("workflows.visualEditor.loadExampleWorkflow")
|
|
2964
|
+
}
|
|
2965
|
+
)
|
|
2966
|
+
] }) })
|
|
2967
|
+
] }) }),
|
|
2968
|
+
inspectorsDocked ? inspectorPanels : null
|
|
1011
2969
|
] }),
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
/* @__PURE__ */ jsx("li", { children: t("workflows.visualEditor.hint.editSteps", "Click steps and transitions to edit them") }),
|
|
1019
|
-
/* @__PURE__ */ jsx("li", { children: t("workflows.visualEditor.hint.validate", "Validate before saving") })
|
|
1020
|
-
] }) })
|
|
1021
|
-
] })
|
|
1022
|
-
] }) }),
|
|
1023
|
-
/* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1 p-6", children: /* @__PURE__ */ jsxs("div", { className: "relative h-[72svh] min-h-[640px]", children: [
|
|
1024
|
-
/* @__PURE__ */ jsx("div", { className: "h-full rounded-lg border bg-card", children: /* @__PURE__ */ jsx(
|
|
1025
|
-
WorkflowGraph,
|
|
1026
|
-
{
|
|
1027
|
-
initialNodes: nodes,
|
|
1028
|
-
initialEdges: edges,
|
|
1029
|
-
onNodesChange: handleNodesChange,
|
|
1030
|
-
onEdgesChange: handleEdgesChange,
|
|
1031
|
-
onNodeClick: handleNodeClick,
|
|
1032
|
-
onEdgeClick: handleEdgeClick,
|
|
1033
|
-
onConnect: handleConnect,
|
|
1034
|
-
editable: !isCodeOnly,
|
|
1035
|
-
height: "100%"
|
|
1036
|
-
}
|
|
1037
|
-
) }),
|
|
1038
|
-
nodes.length === 0 && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-0 flex items-center justify-center px-4", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
1039
|
-
/* @__PURE__ */ jsx("h2", { className: "mb-2 text-xl font-semibold text-foreground", children: t("workflows.visualEditor.startBuilding") }),
|
|
1040
|
-
/* @__PURE__ */ jsx("p", { className: "mb-4 text-muted-foreground", children: t("workflows.visualEditor.clickToAddFromPalette") }),
|
|
1041
|
-
/* @__PURE__ */ jsx(
|
|
1042
|
-
"button",
|
|
1043
|
-
{
|
|
1044
|
-
onClick: handleLoadExample,
|
|
1045
|
-
className: "pointer-events-auto text-sm text-primary hover:underline",
|
|
1046
|
-
children: t("workflows.visualEditor.loadExampleWorkflow")
|
|
1047
|
-
}
|
|
1048
|
-
)
|
|
1049
|
-
] }) })
|
|
1050
|
-
] }) })
|
|
1051
|
-
] }),
|
|
1052
|
-
sharedDialogs,
|
|
1053
|
-
ConfirmDialogElement
|
|
1054
|
-
] });
|
|
2970
|
+
problemsPanel,
|
|
2971
|
+
sharedDialogs,
|
|
2972
|
+
ConfirmDialogElement
|
|
2973
|
+
]
|
|
2974
|
+
}
|
|
2975
|
+
);
|
|
1055
2976
|
}
|
|
1056
2977
|
function getDefaultLabel(nodeType) {
|
|
1057
2978
|
const labels = {
|
|
@@ -1061,7 +2982,11 @@ function getDefaultLabel(nodeType) {
|
|
|
1061
2982
|
automated: "New Automated Task",
|
|
1062
2983
|
decision: "Decision Point",
|
|
1063
2984
|
waitForSignal: "Wait for Signal",
|
|
1064
|
-
waitForTimer: "Wait for Timer"
|
|
2985
|
+
waitForTimer: "Wait for Timer",
|
|
2986
|
+
waitForCondition: "Wait for Condition",
|
|
2987
|
+
invokeAgent: "Invoke Agent",
|
|
2988
|
+
ifElse: "If / Else",
|
|
2989
|
+
switch: "Switch"
|
|
1065
2990
|
};
|
|
1066
2991
|
return labels[nodeType] || "New Step";
|
|
1067
2992
|
}
|
|
@@ -1073,7 +2998,11 @@ function getDefaultBadge(nodeType) {
|
|
|
1073
2998
|
automated: "Automated",
|
|
1074
2999
|
decision: "Decision",
|
|
1075
3000
|
waitForSignal: "Wait for Signal",
|
|
1076
|
-
waitForTimer: "Wait for Timer"
|
|
3001
|
+
waitForTimer: "Wait for Timer",
|
|
3002
|
+
waitForCondition: "Wait for Condition",
|
|
3003
|
+
invokeAgent: "Invoke Agent",
|
|
3004
|
+
ifElse: "If / Else",
|
|
3005
|
+
switch: "Switch"
|
|
1077
3006
|
};
|
|
1078
3007
|
return badges[nodeType] || "Task";
|
|
1079
3008
|
}
|