Workflow 1.1 bug fixes #74

Merged
Lars merged 8 commits from develop into main 2026-04-11 15:39:23 +02:00
Showing only changes of commit 3fa01dd686 - Show all commits

View File

@ -77,11 +77,23 @@ export default function WorkflowEditorPage() {
const [placeholderPickerTarget, setPlaceholderPickerTarget] = useState('end') // 'end' | 'inline'
const endNodeTextareaRef = useRef(null)
const inlineTemplateTextareaRef = useRef(null)
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false)
// Toast & Confirm Dialog
const [toast, setToast] = useState(null)
const [confirmDialog, setConfirmDialog] = useState(null)
// Wrapped handlers to track unsaved changes
const handleNodesChange = useCallback((changes) => {
onNodesChange(changes)
setHasUnsavedChanges(true)
}, [onNodesChange])
const handleEdgesChange = useCallback((changes) => {
onEdgesChange(changes)
setHasUnsavedChanges(true)
}, [onEdgesChange])
// Load available basis prompts for Analysis nodes
useEffect(() => {
async function loadPrompts() {
@ -112,10 +124,26 @@ export default function WorkflowEditorPage() {
setValidationWarnings(warnings)
}, [nodes, edges])
// Warn on browser back/refresh if unsaved changes
useEffect(() => {
const handleBeforeUnload = (e) => {
if (hasUnsavedChanges) {
e.preventDefault()
e.returnValue = '' // Chrome requires returnValue to be set
}
}
window.addEventListener('beforeunload', handleBeforeUnload)
return () => window.removeEventListener('beforeunload', handleBeforeUnload)
}, [hasUnsavedChanges])
// Handlers
const onConnect = useCallback(
(params) => setEdges((eds) => addEdge(params, eds)),
(params) => {
setEdges((eds) => addEdge(params, eds))
setHasUnsavedChanges(true)
},
[setEdges]
)
@ -144,6 +172,7 @@ export default function WorkflowEditorPage() {
}
}
setNodes((nds) => [...nds, base])
setHasUnsavedChanges(true)
}
const handleNodeUpdate = (nodeId, updates) => {
@ -153,6 +182,7 @@ export default function WorkflowEditorPage() {
console.log('📝 Nodes after update:', updated.find(n => n.id === nodeId))
return updated
})
setHasUnsavedChanges(true)
}
const handleDeleteNode = () => {
@ -161,6 +191,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))
setSelectedNodeId(null)
setHasUnsavedChanges(true)
}
const handleSave = async () => {
@ -217,6 +248,9 @@ export default function WorkflowEditorPage() {
console.log('🚀 Navigating to:', `/workflow-editor/${result.id}`)
navigate(`/workflow-editor/${result.id}`)
}
// Clear unsaved changes flag after successful save
setHasUnsavedChanges(false)
} catch (e) {
console.error('❌ handleSave error:', e)
setError(e.message)
@ -275,6 +309,9 @@ export default function WorkflowEditorPage() {
)
nodeIdCounter = maxId + 1
console.log('✅ Workflow loaded successfully, nodes:', loadedNodes.length, 'edges:', loadedEdges.length)
// Clear unsaved changes flag after successful load
setHasUnsavedChanges(false)
} catch (e) {
console.error('❌ loadWorkflow error:', e)
setError(e.message)
@ -296,14 +333,18 @@ export default function WorkflowEditorPage() {
}
const handleNew = () => {
if (confirm('Neuen Workflow erstellen? Ungespeicherte Änderungen gehen verloren.')) {
setNodes([])
setEdges([])
setCurrentPrompt(null)
setWorkflowName('Neuer Workflow')
setSelectedNodeId(null)
navigate('/workflow-editor/new')
if (hasUnsavedChanges) {
if (!confirm('Neuen Workflow erstellen? Ungespeicherte Änderungen gehen verloren.')) {
return
}
}
setNodes([])
setEdges([])
setCurrentPrompt(null)
setWorkflowName('Neuer Workflow')
setSelectedNodeId(null)
setHasUnsavedChanges(false)
navigate('/workflow-editor/new')
}
const handleDelete = async () => {
@ -328,6 +369,17 @@ export default function WorkflowEditorPage() {
setExecutionResult(result)
}
const handleBack = () => {
if (hasUnsavedChanges) {
const confirm = window.confirm(
'Du hast ungespeicherte Änderungen.\n\n' +
'Möchtest du wirklich zurück gehen? Alle Änderungen gehen verloren.'
)
if (!confirm) return
}
navigate('/admin/prompts')
}
const handlePlaceholderSelect = (placeholderString) => {
if (!selectedNode) return
@ -392,14 +444,17 @@ export default function WorkflowEditorPage() {
<div className="workflow-editor">
{/* Toolbar */}
<div className="workflow-toolbar">
<button className="btn-secondary" onClick={() => navigate('/admin/prompts')}>
<button className="btn-secondary" onClick={handleBack}>
Zurück
</button>
<input
type="text"
value={workflowName}
onChange={(e) => setWorkflowName(e.target.value)}
onChange={(e) => {
setWorkflowName(e.target.value)
setHasUnsavedChanges(true)
}}
placeholder="Interner Workflow-Name (Slug-Basis)"
title="Technischer Name in der Datenbank. Den sichtbaren Titel für die KI-Analyse setzt du in der Start-Node."
style={{ flex: 1, padding: '8px', borderRadius: '4px', border: '1px solid var(--border)' }}
@ -498,8 +553,8 @@ export default function WorkflowEditorPage() {
nodes={nodes}
edges={edges}
nodeTypes={nodeTypes}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onNodesChange={handleNodesChange}
onEdgesChange={handleEdgesChange}
onConnect={onConnect}
onNodeClick={onNodeClick}
/>