fix: Phase 5 - Workflow Editor UX Fixes (Round 3)
All checks were successful
Deploy Development / deploy (push) Successful in 53s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 14s

Frontend Fixes:
- AdminPromptsPage: Edit button navigates to workflow-editor for workflow type prompts
- WorkflowEditorPage: Fixed save navigation (alert before navigate)
- WorkflowEditorPage: selectedNode derived from selectedNodeId (eliminates stale state)
- FallbackConfig: Show node labels instead of IDs in fallback edge dropdown
- WorkflowCanvas: Enable edge deletion with deletable: true
- WorkflowEditorPage: Hide sidebar when config panel is open

Bugs Fixed:
- C1: Save error "Method Not Allowed" after success
- C2: Edit button in admin doesn't open workflow editor
- H1: Prompt selection not displayed when re-editing node
- H2: Fallback edge dropdown shows node_1/node_2 instead of names
- H3: Cannot delete edges
- M1: Sidebar takes space when config panel open

Technical Changes:
- Replaced useState(selectedNode) with useState(selectedNodeId) + derived selectedNode
- Removed sync useEffect (no longer needed with derived state)
- Added nodes prop to FallbackConfig for label lookup
- Swapped alert/navigate order to prevent navigation errors

Testing: Manual testing required (see manual test cases)
This commit is contained in:
Lars 2026-04-04 21:16:15 +02:00
parent 7d22b052dd
commit 2f70a39052
4 changed files with 30 additions and 27 deletions

View File

@ -41,7 +41,8 @@ export function WorkflowCanvas({
maxZoom={2}
defaultEdgeOptions={{
animated: false,
style: { strokeWidth: 2 }
style: { strokeWidth: 2 },
deletable: true
}}
>
<Background

View File

@ -4,15 +4,22 @@
* Props:
* - node: React Flow Node object
* - edges: Array of React Flow edges
* - nodes: Array of React Flow nodes (for label lookup)
* - onChange: (nodeId, updates) => void
*/
export function FallbackConfig({ node, edges, onChange }) {
export function FallbackConfig({ node, edges, nodes, onChange }) {
const fallbackStrategy = node.data.fallback_strategy || 'conservative_skip'
const fallbackEdge = node.data.fallback_edge || null
// Outgoing Edges von diesem Node
const outgoingEdges = edges.filter(e => e.source === node.id)
// Helper: Get node label by ID
const getNodeLabel = (nodeId) => {
const targetNode = nodes.find(n => n.id === nodeId)
return targetNode?.data?.label || nodeId
}
const handleStrategyChange = (e) => {
const strategy = e.target.value
onChange(node.id, { fallback_strategy: strategy })
@ -53,7 +60,7 @@ export function FallbackConfig({ node, edges, onChange }) {
<option value="">-- Kante wählen --</option>
{outgoingEdges.map(e => (
<option key={e.id} value={e.id}>
{e.data?.label || `Edge → ${e.target}`}
{e.data?.label || `Edge → ${getNodeLabel(e.target)}`}
</option>
))}
</select>

View File

@ -530,7 +530,13 @@ export default function AdminPromptsPage() {
justifyContent: 'flex-end'
}}>
<button
onClick={() => setEditingPrompt(prompt)}
onClick={() => {
if (prompt.type === 'workflow') {
navigate(`/workflow-editor/${prompt.id}`)
} else {
setEditingPrompt(prompt)
}
}}
style={{
background: 'none',
border: 'none',

View File

@ -34,7 +34,8 @@ export default function WorkflowEditorPage() {
// State
const [nodes, setNodes, onNodesChange] = useNodesState([])
const [edges, setEdges, onEdgesChange] = useEdgesState([])
const [selectedNode, setSelectedNode] = useState(null)
const [selectedNodeId, setSelectedNodeId] = useState(null)
const selectedNode = selectedNodeId ? nodes.find(n => n.id === selectedNodeId) : null
const [currentPrompt, setCurrentPrompt] = useState(null)
const [workflowName, setWorkflowName] = useState('Neuer Workflow')
const [workflowDescription, setWorkflowDescription] = useState('')
@ -73,16 +74,6 @@ export default function WorkflowEditorPage() {
setValidationWarnings(warnings)
}, [nodes, edges])
// Keep selectedNode in sync with nodes array (wichtig für Config Panel!)
useEffect(() => {
if (selectedNode) {
const updatedNode = nodes.find(n => n.id === selectedNode.id)
if (updatedNode && updatedNode !== selectedNode) {
setSelectedNode(updatedNode)
}
}
}, [nodes])
// Handlers
const onConnect = useCallback(
@ -91,7 +82,7 @@ export default function WorkflowEditorPage() {
)
const onNodeClick = useCallback((event, node) => {
setSelectedNode(node)
setSelectedNodeId(node.id)
}, [])
const handleAddNode = (nodeType) => {
@ -118,7 +109,7 @@ export default function WorkflowEditorPage() {
setNodes((nds) => nds.filter((n) => n.id !== selectedNode.id))
setEdges((eds) => eds.filter((e) => e.source !== selectedNode.id && e.target !== selectedNode.id))
setSelectedNode(null)
setSelectedNodeId(null)
}
const handleSave = async () => {
@ -157,8 +148,8 @@ export default function WorkflowEditorPage() {
graph_data
})
setCurrentPrompt({ id: result.id, name: workflowName })
navigate(`/workflow-editor/${result.id}`)
alert('Workflow erstellt!')
navigate(`/workflow-editor/${result.id}`)
}
} catch (e) {
setError(e.message)
@ -219,7 +210,7 @@ export default function WorkflowEditorPage() {
setCurrentPrompt(null)
setWorkflowName('Neuer Workflow')
setWorkflowDescription('')
setSelectedNode(null)
setSelectedNodeId(null)
navigate('/workflow-editor/new')
}
}
@ -296,7 +287,7 @@ export default function WorkflowEditorPage() {
{/* Main Content */}
<div className="workflow-content">
{/* Sidebar */}
<div className="workflow-sidebar">
<div className="workflow-sidebar" style={{ display: selectedNode ? 'none' : 'block' }}>
<div className="sidebar-section">
<h3>Workflow-Knoten</h3>
<div className="node-palette">
@ -357,7 +348,7 @@ export default function WorkflowEditorPage() {
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
<h2 style={{ margin: 0 }}>Node-Konfiguration</h2>
<button
onClick={() => setSelectedNode(null)}
onClick={() => setSelectedNodeId(null)}
style={{
background: 'none',
border: 'none',
@ -435,7 +426,7 @@ export default function WorkflowEditorPage() {
</div>
<QuestionAugmentationPanel node={selectedNode} onChange={handleNodeUpdate} />
<FallbackConfig node={selectedNode} edges={edges} onChange={handleNodeUpdate} />
<FallbackConfig node={selectedNode} edges={edges} nodes={nodes} onChange={handleNodeUpdate} />
</>
)}
@ -447,7 +438,7 @@ export default function WorkflowEditorPage() {
edges={edges}
onChange={handleNodeUpdate}
/>
<FallbackConfig node={selectedNode} edges={edges} onChange={handleNodeUpdate} />
<FallbackConfig node={selectedNode} edges={edges} nodes={nodes} onChange={handleNodeUpdate} />
</>
)}
@ -464,8 +455,7 @@ export default function WorkflowEditorPage() {
{validationErrors.map((err, i) => (
<div key={i} className="validation-error" onClick={() => {
if (err.nodeId) {
const node = nodes.find(n => n.id === err.nodeId)
if (node) setSelectedNode(node)
setSelectedNodeId(err.nodeId)
}
}}>
{err.message}
@ -475,8 +465,7 @@ export default function WorkflowEditorPage() {
{validationWarnings.map((warn, i) => (
<div key={i} className="validation-warning" onClick={() => {
if (warn.nodeId) {
const node = nodes.find(n => n.id === warn.nodeId)
if (node) setSelectedNode(node)
setSelectedNodeId(warn.nodeId)
}
}}>
{warn.message}