fix: Phase 5 - Workflow Editor UX Fixes (Round 3)
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:
parent
7d22b052dd
commit
2f70a39052
|
|
@ -41,7 +41,8 @@ export function WorkflowCanvas({
|
|||
maxZoom={2}
|
||||
defaultEdgeOptions={{
|
||||
animated: false,
|
||||
style: { strokeWidth: 2 }
|
||||
style: { strokeWidth: 2 },
|
||||
deletable: true
|
||||
}}
|
||||
>
|
||||
<Background
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user