import { useState, useCallback } from 'react' import { databases, DATABASE_ID, COLLECTIONS, ID, isDemoMode } from '../lib/appwrite' import { fetchRoadmapNodes, logNodeStatusWorksheet, allGoalsDone, NODE_KIND, NODE_STATUS, } from '../lib/roadmaps' export function useRoadmapNodes() { const [nodes, setNodes] = useState([]) const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const fetchNodes = useCallback(async (roadmapId) => { if (!roadmapId || isDemoMode) return [] setLoading(true) setError(null) try { const docs = await fetchRoadmapNodes(roadmapId) setNodes(docs) return docs } catch (err) { setError(err.message) return [] } finally { setLoading(false) } }, []) const createNode = async (roadmapId, { title, column, row, kind }) => { if (isDemoMode) return { success: false, error: 'Demo-Modus' } try { const now = new Date().toISOString() const doc = await databases.createDocument(DATABASE_ID, COLLECTIONS.ROADMAP_NODES, ID.unique(), { roadmapId, title: title || 'Neuer Schritt', kind: kind || NODE_KIND.STEP, column, row, status: NODE_STATUS.OPEN, createdAt: now, updatedAt: now, }) setNodes((prev) => [...prev, doc]) return { success: true, data: doc } } catch (err) { return { success: false, error: err.message } } } const updateNode = async (nodeId, data) => { if (isDemoMode) return { success: false, error: 'Demo-Modus' } try { const doc = await databases.updateDocument(DATABASE_ID, COLLECTIONS.ROADMAP_NODES, nodeId, { ...data, updatedAt: new Date().toISOString(), }) setNodes((prev) => prev.map((n) => (n.$id === nodeId ? doc : n))) return { success: true, data: doc } } catch (err) { return { success: false, error: err.message } } } /** Knoten loeschen inkl. Bereinigung eingehender Kanten bei den Nachfolgern. */ const deleteNode = async (node) => { if (isDemoMode) return { success: false, error: 'Demo-Modus' } if (node.kind === NODE_KIND.START) { return { success: false, error: 'Der Start-Knoten kann nicht geloescht werden.' } } if (node.kind === NODE_KIND.GOAL && nodes.filter((n) => n.kind === NODE_KIND.GOAL).length <= 1) { return { success: false, error: 'Ein Plan braucht mindestens ein Ziel.' } } try { const successors = nodes.filter((n) => (n.sourceIds || []).includes(node.$id)) await Promise.all(successors.map((n) => databases.updateDocument(DATABASE_ID, COLLECTIONS.ROADMAP_NODES, n.$id, { sourceIds: (n.sourceIds || []).filter((id) => id !== node.$id), updatedAt: new Date().toISOString(), }) )) await databases.deleteDocument(DATABASE_ID, COLLECTIONS.ROADMAP_NODES, node.$id) setNodes((prev) => prev .filter((n) => n.$id !== node.$id) .map((n) => ((n.sourceIds || []).includes(node.$id) ? { ...n, sourceIds: n.sourceIds.filter((id) => id !== node.$id) } : n)) ) return { success: true } } catch (err) { return { success: false, error: err.message } } } /** Kante anlegen: immer vorwaerts (Zielspalte > Quellspalte), keine Duplikate. */ const connectNodes = async (source, target) => { if (isDemoMode) return { success: false, error: 'Demo-Modus' } if (source.$id === target.$id) return { success: false, error: 'Knoten kann nicht mit sich selbst verbunden werden.' } if (target.column <= source.column) { return { success: false, error: 'Verbindungen laufen immer vorwaerts (nach rechts).' } } if ((target.sourceIds || []).includes(source.$id)) { return { success: false, error: 'Diese Verbindung existiert bereits.' } } return updateNode(target.$id, { sourceIds: [...(target.sourceIds || []), source.$id] }) } const disconnectNodes = async (sourceId, target) => { return updateNode(target.$id, { sourceIds: (target.sourceIds || []).filter((id) => id !== sourceId) }) } /** Statuswechsel inkl. Worksheet-Log am Projektticket; meldet, ob alle Ziele fertig sind. */ const setNodeStatus = async (roadmap, node, newStatus, user) => { if (isDemoMode) return { success: false, error: 'Demo-Modus' } const oldStatus = node.status || NODE_STATUS.OPEN if (oldStatus === newStatus) return { success: true, allGoalsDone: false } const result = await updateNode(node.$id, { status: newStatus }) if (!result.success) return result try { await logNodeStatusWorksheet(roadmap, node, oldStatus, newStatus, user) } catch { /* Verlaufslog ist optional - Statuswechsel bleibt gueltig */ } const updatedNodes = nodes.map((n) => (n.$id === node.$id ? { ...n, status: newStatus } : n)) return { success: true, data: result.data, allGoalsDone: allGoalsDone(updatedNodes) } } return { nodes, loading, error, setNodes, fetchNodes, createNode, updateNode, deleteNode, connectNodes, disconnectNodes, setNodeStatus, } }