diff --git a/backend/version.py b/backend/version.py
index abefb7c..14cd65c 100644
--- a/backend/version.py
+++ b/backend/version.py
@@ -7,8 +7,8 @@ Semantic Versioning: MAJOR.MINOR.PATCH
- PATCH: Bugfix, kleine Γnderung, Refactor
"""
-APP_VERSION = "0.9n"
-BUILD_DATE = "2026-04-05"
+APP_VERSION = "0.9o"
+BUILD_DATE = "2026-04-09"
DB_SCHEMA_VERSION = "20260406e" # Migration 041
MODULE_VERSIONS = {
diff --git a/frontend/src/components/workflow/panels/WorkflowExecutePanel.jsx b/frontend/src/components/workflow/panels/WorkflowExecutePanel.jsx
new file mode 100644
index 0000000..24096a4
--- /dev/null
+++ b/frontend/src/components/workflow/panels/WorkflowExecutePanel.jsx
@@ -0,0 +1,139 @@
+import { useState } from 'react'
+import { api } from '../../../utils/api'
+
+/**
+ * WorkflowExecutePanel - Execution Controls fΓΌr Workflow Editor
+ *
+ * Features:
+ * - Execute Button mit Loading State
+ * - Error Handling
+ * - Success State
+ * - Debug Mode Toggle
+ *
+ * Part 2: Frontend Execute Integration
+ */
+export function WorkflowExecutePanel({
+ currentPrompt,
+ onExecutionComplete,
+ disabled = false
+}) {
+ const [executing, setExecuting] = useState(false)
+ const [error, setError] = useState(null)
+ const [debugMode, setDebugMode] = useState(true)
+
+ const handleExecute = async () => {
+ if (!currentPrompt || !currentPrompt.slug) {
+ setError('Workflow muss zuerst gespeichert werden (benΓΆtigt slug)')
+ return
+ }
+
+ try {
+ setExecuting(true)
+ setError(null)
+
+ console.log('π Executing workflow:', currentPrompt.slug)
+
+ const result = await api.executeWorkflow(
+ currentPrompt.slug,
+ null, // variables (spΓ€ter erweiterbar)
+ debugMode,
+ false // save (nicht in ai_insights speichern)
+ )
+
+ console.log('β
Workflow execution completed:', result)
+
+ // Success - kurze BestΓ€tigung
+ if (result.status === 'completed') {
+ // Callback mit result
+ if (onExecutionComplete) {
+ onExecutionComplete(result)
+ }
+ } else if (result.status === 'failed') {
+ setError(result.error || 'Workflow-AusfΓΌhrung fehlgeschlagen')
+ }
+
+ } catch (e) {
+ console.error('β Workflow execution error:', e)
+ setError(e.message)
+ } finally {
+ setExecuting(false)
+ }
+ }
+
+ return (
+
+ {/* Debug Mode Toggle */}
+
+
+ {/* Execute Button */}
+
+
+ {/* Error Display */}
+ {error && (
+
+ β {error}
+
+ )}
+
+ )
+}
diff --git a/frontend/src/components/workflow/panels/WorkflowResultViewer.jsx b/frontend/src/components/workflow/panels/WorkflowResultViewer.jsx
new file mode 100644
index 0000000..add6aa5
--- /dev/null
+++ b/frontend/src/components/workflow/panels/WorkflowResultViewer.jsx
@@ -0,0 +1,303 @@
+import { useState } from 'react'
+
+/**
+ * WorkflowResultViewer - Zeigt Execution-Ergebnisse eines Workflows
+ *
+ * Features:
+ * - Aggregated Result (Final Output)
+ * - Node States (wenn Debug Mode aktiv)
+ * - Collapsible Sections
+ * - Copy to Clipboard
+ *
+ * Part 2: Frontend Execute Integration
+ */
+export function WorkflowResultViewer({ result, onClose }) {
+ const [expandedNodes, setExpandedNodes] = useState({})
+
+ if (!result) {
+ return null
+ }
+
+ const toggleNode = (nodeId) => {
+ setExpandedNodes((prev) => ({ ...prev, [nodeId]: !prev[nodeId] }))
+ }
+
+ const copyToClipboard = (text) => {
+ navigator.clipboard.writeText(text)
+ alert('In Zwischenablage kopiert')
+ }
+
+ const aggregated = result.aggregated_result || {}
+ const nodeStates = result.node_states || []
+
+ return (
+
+ {/* Header */}
+
+
+ Workflow-Ergebnis
+ {result.status === 'completed' && (
+ β
+ )}
+ {result.status === 'failed' && (
+ β
+ )}
+
+
+
+
+ {/* Content */}
+
+ {/* Execution Info */}
+
+
Execution ID: {result.execution_id}
+
Status: {result.status}
+ {aggregated.total_nodes && (
+
Nodes: {aggregated.executed_nodes}/{aggregated.total_nodes}
+ )}
+ {aggregated.failed_nodes > 0 && (
+
+ Failed Nodes: {aggregated.failed_nodes}
+
+ )}
+
+
+ {/* Final Output */}
+
+
+
+ Final Output
+
+ {aggregated.analysis_core && (
+
+ )}
+
+
+ {aggregated.analysis_core || aggregated.combined_analysis || '(Kein Output)'}
+
+
+
+ {/* All Signals */}
+ {aggregated.all_signals && aggregated.all_signals.length > 0 && (
+
+
+ All Signals ({aggregated.all_signals.length})
+
+
+ {aggregated.all_signals.map((signal, idx) => (
+
+ β’ {signal}
+
+ ))}
+
+
+ )}
+
+ {/* Node States (Debug Info) */}
+ {nodeStates.length > 0 && (
+
+
+ Node States (Debug)
+
+
+ {nodeStates.map((node) => (
+
+ {/* Node Header */}
+
toggleNode(node.node_id)}
+ style={{
+ padding: '10px 12px',
+ background: 'var(--surface2)',
+ cursor: 'pointer',
+ display: 'flex',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ fontSize: '13px',
+ fontWeight: 500
+ }}
+ >
+
+ {node.node_type === 'start' && 'π'}
+ {node.node_type === 'analysis' && 'π€'}
+ {node.node_type === 'logic' && 'β‘'}
+ {node.node_type === 'join' && 'π'}
+ {node.node_type === 'end' && 'π'}
+ {' '}
+ {node.node_label || node.node_id}
+ {node.status === 'skipped' && (
+
+ (skipped)
+
+ )}
+
+
+ {expandedNodes[node.node_id] ? 'βΌ' : 'βΆ'}
+
+
+
+ {/* Node Details */}
+ {expandedNodes[node.node_id] && (
+
+ {node.output && (
+
+
Output:
+
+ {typeof node.output === 'string'
+ ? node.output
+ : JSON.stringify(node.output, null, 2)}
+
+
+ )}
+ {node.error && (
+
+ Error: {node.error}
+
+ )}
+ {node.metadata && (
+
+
Metadata:
+
+ {JSON.stringify(node.metadata, null, 2)}
+
+
+ )}
+
+ )}
+
+ ))}
+
+
+ )}
+
+ {/* Error (if failed) */}
+ {result.error && (
+
+ Error: {result.error}
+
+ )}
+
+
+ )
+}
diff --git a/frontend/src/pages/WorkflowEditorPage.jsx b/frontend/src/pages/WorkflowEditorPage.jsx
index 036f876..a761be2 100644
--- a/frontend/src/pages/WorkflowEditorPage.jsx
+++ b/frontend/src/pages/WorkflowEditorPage.jsx
@@ -14,6 +14,8 @@ import { QuestionAugmentationPanel } from '../components/workflow/panels/Questio
import { LogicExpressionEditor } from '../components/workflow/panels/LogicExpressionEditor'
import { FallbackConfig } from '../components/workflow/panels/FallbackConfig'
import { JoinConfig } from '../components/workflow/panels/JoinConfig'
+import { WorkflowExecutePanel } from '../components/workflow/panels/WorkflowExecutePanel'
+import { WorkflowResultViewer } from '../components/workflow/panels/WorkflowResultViewer'
import '../styles/workflowEditor.css'
// Node-Type Mapping
@@ -44,6 +46,7 @@ export default function WorkflowEditorPage() {
const [loading, setLoading] = useState(false)
const [error, setError] = useState(null)
const [availablePrompts, setAvailablePrompts] = useState([])
+ const [executionResult, setExecutionResult] = useState(null)
// Load available basis prompts for Analysis nodes
useEffect(() => {
@@ -156,7 +159,7 @@ export default function WorkflowEditorPage() {
graph_data
})
console.log('β
Workflow created:', result)
- setCurrentPrompt({ id: result.id, name: workflowName })
+ setCurrentPrompt({ id: result.id, slug: result.slug, name: workflowName })
alert('Workflow erstellt!')
console.log('π Navigating to:', `/workflow-editor/${result.id}`)
navigate(`/workflow-editor/${result.id}`)
@@ -249,6 +252,11 @@ export default function WorkflowEditorPage() {
}
}
+ const handleExecutionComplete = (result) => {
+ console.log('π― Execution completed:', result)
+ setExecutionResult(result)
+ }
+
// ββ Render ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
return (
@@ -273,6 +281,14 @@ export default function WorkflowEditorPage() {
+
+ {/* Execute Panel */}
+ 0}
+ />
+