From fcd82eb5c9273c76e8be50537b918419fb85f69b Mon Sep 17 00:00:00 2001 From: Till Tomczak Date: Sat, 10 May 2025 22:56:49 +0200 Subject: [PATCH] =?UTF-8?q?chore:=20=C3=84nderungen=20commited?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.py | 18 ++ templates/user_mindmap.html | 420 +++++++++++++++++++++++++++++++++++- 2 files changed, 431 insertions(+), 7 deletions(-) diff --git a/app.py b/app.py index b978401..f2ebf3e 100644 --- a/app.py +++ b/app.py @@ -1154,6 +1154,24 @@ def remove_node_from_mindmap(mindmap_id, node_id): return jsonify({'success': True}) +@app.route('/api/mindmap/node/', methods=['GET']) +@login_required +def get_node_info(node_id): + """Liefert Detailinformationen zu einem einzelnen Knoten""" + try: + # Knoten abrufen + node = MindMapNode.query.get_or_404(node_id) + + return jsonify({ + 'id': node.id, + 'name': node.name, + 'description': node.description or '', + 'color_code': node.color_code or '#9F7AEA' + }) + except Exception as e: + print(f"Fehler in get_node_info: {str(e)}") + return jsonify({'success': False, 'message': str(e)}), 500 + @app.route('/api/mindmap//update_node_position', methods=['POST']) @login_required def update_node_position(mindmap_id): diff --git a/templates/user_mindmap.html b/templates/user_mindmap.html index 07c8438..b392fd7 100644 --- a/templates/user_mindmap.html +++ b/templates/user_mindmap.html @@ -319,6 +319,10 @@ Layout speichern + @@ -538,13 +542,415 @@ cy.style().selector('node').style({'text-opacity': labelsVisible ? 1 : 0}).update(); }); - // TODO: add-note-btn und save-layout-btn Funktionalität (benötigt /edit_mindmap Seite oder API Endpunkte) - document.getElementById('add-note-btn').addEventListener('click', function() { - showUINotification('Notizfunktion wird auf der Bearbeitungsseite implementiert.', 'info'); - }); - document.getElementById('save-layout-btn').addEventListener('click', function() { - showUINotification('Layout-Speicherung wird auf der Bearbeitungsseite implementiert.', 'info'); - }); + // Notizfunktion implementieren + document.getElementById('add-note-btn').addEventListener('click', function() { + const selectedNodes = cy.$('node:selected'); + if (selectedNodes.length === 0) { + showUINotification('Bitte wählen Sie einen Knoten aus, um eine Notiz hinzuzufügen.', 'info'); + return; + } + + const nodeId = selectedNodes[0].id(); + + // Notizen für den ausgewählten Knoten laden + fetch(`/api/mindmap/node/${nodeId}/notes`) + .then(response => response.json()) + .then(data => { + if (data.success) { + // Notiz-Dialog anzeigen + showNoteDialog(nodeId, data.notes || '', data.color_code || '#FFF59D'); + } else { + showUINotification('Fehler beim Laden der Notizen: ' + data.message, 'error'); + } + }) + .catch(error => { + console.error('Fehler beim Laden der Notizen:', error); + showUINotification('Fehler beim Laden der Notizen', 'error'); + }); + }); + + // Layout speichern implementieren + document.getElementById('save-layout-btn').addEventListener('click', function() { + const mindmapId = parseInt("{{ mindmap.id }}"); + const nodes = []; + + // Sammle die Positionen aller Knoten + cy.nodes().forEach(node => { + nodes.push({ + id: node.id(), + x: node.position().x, + y: node.position().y + }); + }); + + // Layout speichern + fetch(`/api/mindmap/${mindmapId}/layout`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': '{{ csrf_token() }}' + }, + body: JSON.stringify({ nodes: nodes }) + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + showUINotification('Layout erfolgreich gespeichert', 'success'); + } else { + showUINotification('Fehler beim Speichern des Layouts: ' + data.message, 'error'); + } + }) + .catch(error => { + console.error('Fehler beim Speichern des Layouts:', error); + showUINotification('Fehler beim Speichern des Layouts', 'error'); + }); + }); + + // Öffentliche Mindmap anzeigen/verbergen + document.getElementById('toggle-public-mindmap-btn').addEventListener('click', function() { + const container = document.getElementById('public-mindmap-container'); + if (container.classList.contains('hidden')) { + container.classList.remove('hidden'); + this.querySelector('span').textContent = 'Öffentliche Mindmap verbergen'; + loadPublicMindmap(); + } else { + container.classList.add('hidden'); + this.querySelector('span').textContent = 'Öffentliche Mindmap'; + } + }); + + // Funktion zum Anzeigen des Notiz-Dialogs + function showNoteDialog(nodeId, noteContent, colorCode) { + // Dialog erstellen + const overlay = document.createElement('div'); + overlay.className = 'fixed inset-0 bg-black/50 flex items-center justify-center z-50'; + overlay.id = 'note-dialog-overlay'; + + overlay.innerHTML = ` +
+

Notizen bearbeiten

+ +
+ + +
+
+ + +
+
+ `; + + document.body.appendChild(overlay); + + // Event-Listener für Buttons + document.getElementById('cancel-note').addEventListener('click', function() { + document.getElementById('note-dialog-overlay').remove(); + }); + + document.getElementById('save-note').addEventListener('click', function() { + const noteContent = document.getElementById('note-content').value; + const colorCode = document.getElementById('note-color').value; + + // Notizen speichern + fetch(`/api/mindmap/node/${nodeId}/notes`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': '{{ csrf_token() }}' + }, + body: JSON.stringify({ + notes: noteContent, + color_code: colorCode + }) + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + showUINotification('Notizen erfolgreich gespeichert', 'success'); + document.getElementById('note-dialog-overlay').remove(); + + // Knoten visuell markieren, der eine Notiz hat + if (noteContent.trim() !== '') { + cy.$id(nodeId).addClass('has-note'); + + // Stil für Knoten mit Notizen hinzufügen, falls noch nicht vorhanden + if (!cy.style().selector('node.has-note').style('border-color')) { + cy.style() + .selector('node.has-note') + .style({ + 'border-color': colorCode, + 'border-width': 3 + }) + .update(); + } + } else { + cy.$id(nodeId).removeClass('has-note'); + } + } else { + showUINotification('Fehler beim Speichern der Notizen: ' + data.message, 'error'); + } + }) + .catch(error => { + console.error('Fehler beim Speichern der Notizen:', error); + showUINotification('Fehler beim Speichern der Notizen', 'error'); + }); + }); + } + + // Funktion zum Laden der öffentlichen Mindmap + function loadPublicMindmap() { + const container = document.getElementById('public-cy'); + + // Lade-Anzeige + container.innerHTML = '

Lade öffentliche Mindmap...

'; + + // Daten abrufen + fetch('/api/public-mindmap') + .then(response => response.json()) + .then(data => { + if (data.success) { + container.innerHTML = ''; + + // Initialisiere Cytoscape für die öffentliche Mindmap + const publicCy = cytoscape({ + container: container, + elements: [], + style: [ + { + selector: 'node', + style: { + 'background-color': 'data(color_code)', + 'label': 'data(name)', + 'color': '#FFFFFF', + 'text-valign': 'center', + 'text-halign': 'center', + 'width': '30px', + 'height': '30px', + 'text-wrap': 'wrap', + 'text-max-width': '80px', + 'font-size': '10px', + 'border-width': '2px', + 'border-color': '#FFFFFF', + 'text-outline-color': '#555', + 'text-outline-width': 1 + } + }, + { + selector: 'edge', + style: { + 'width': 1, + 'line-color': '#9CA3AF', + 'target-arrow-color': '#9CA3AF', + 'target-arrow-shape': 'triangle', + 'curve-style': 'bezier' + } + }, + { + selector: 'node:selected', + style: { + 'border-color': '#F59E0B', + 'border-width': '3px' + } + } + ], + layout: { + name: 'cose', + padding: 30 + } + }); + + // Elemente zur öffentlichen Mindmap hinzufügen + const elements = []; + + // Knoten hinzufügen + data.nodes.forEach(node => { + elements.push({ + group: 'nodes', + data: { + id: 'pub_' + node.id, + originalId: node.id, + name: node.name, + description: node.description, + color_code: node.color_code + } + }); + }); + + // Kanten hinzufügen + data.edges.forEach(edge => { + elements.push({ + group: 'edges', + data: { + source: 'pub_' + edge.source, + target: 'pub_' + edge.target + } + }); + }); + + publicCy.add(elements); + publicCy.layout({ name: 'cose' }).run(); + + // Event-Listener für Knoten-Klicks in der öffentlichen Mindmap + publicCy.on('tap', 'node', function(evt) { + const node = evt.target; + const nodeData = node.data(); + + // Dialog zum Hinzufügen des Knotens anzeigen + showAddNodeDialog(nodeData.originalId, nodeData.name, nodeData.description); + }); + + } else { + container.innerHTML = '

Fehler beim Laden der öffentlichen Mindmap

'; + showUINotification('Fehler beim Laden der öffentlichen Mindmap: ' + data.message, 'error'); + } + }) + .catch(error => { + console.error('Fehler beim Laden der öffentlichen Mindmap:', error); + container.innerHTML = '

Fehler beim Laden der öffentlichen Mindmap

'; + showUINotification('Fehler beim Laden der öffentlichen Mindmap', 'error'); + }); + } + + // Funktion zum Anzeigen des Dialogs zum Hinzufügen eines Knotens + function showAddNodeDialog(nodeId, nodeName, nodeDescription) { + // Dialog erstellen + const overlay = document.createElement('div'); + overlay.className = 'fixed inset-0 bg-black/50 flex items-center justify-center z-50'; + overlay.id = 'add-node-dialog-overlay'; + + overlay.innerHTML = ` +
+

Knoten zu Ihrer Mindmap hinzufügen

+
+

${nodeName}

+

${nodeDescription || 'Keine Beschreibung'}

+
+
+ + +
+
+ + +
+
+ `; + + document.body.appendChild(overlay); + + // Fülle den Parent-Node-Selector mit vorhandenen Knoten + const parentSelect = document.getElementById('parent-node-select'); + cy.nodes().forEach(node => { + const option = document.createElement('option'); + option.value = node.id(); + option.textContent = node.data('label'); + parentSelect.appendChild(option); + }); + + // Event-Listener für Buttons + document.getElementById('cancel-add-node').addEventListener('click', function() { + document.getElementById('add-node-dialog-overlay').remove(); + }); + + document.getElementById('confirm-add-node').addEventListener('click', function() { + const parentNodeId = document.getElementById('parent-node-select').value; + const mindmapId = parseInt("{{ mindmap.id }}"); + + // Generiere Startposition basierend auf Parent oder Zufallsposition + let position = { x: 0, y: 0 }; + + if (parentNodeId) { + const parentNode = cy.$id(parentNodeId); + if (parentNode) { + // Positioniere in der Nähe des Elternknotens + position = { + x: parentNode.position('x') + Math.random() * 100 - 50, + y: parentNode.position('y') + Math.random() * 100 - 50 + }; + } + } else { + // Zufällige Position im sichtbaren Bereich + const extent = cy.extent(); + position = { + x: extent.x1 + Math.random() * (extent.x2 - extent.x1), + y: extent.y1 + Math.random() * (extent.y2 - extent.y1) + }; + } + + // Knoten zur Mindmap hinzufügen + fetch(`/api/mindmap/${mindmapId}/add_node`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': '{{ csrf_token() }}' + }, + body: JSON.stringify({ + node_id: nodeId, + parent_id: parentNodeId || null, + x: position.x, + y: position.y + }) + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + showUINotification('Knoten erfolgreich hinzugefügt', 'success'); + document.getElementById('add-node-dialog-overlay').remove(); + + // Aktualisiere die Mindmap (neu laden oder Knoten hinzufügen) + // Option 1: Seite neu laden + // window.location.reload(); + + // Option 2: Knoten dynamisch hinzufügen (eleganter) + fetch(`/api/mindmap/node/${nodeId}`) + .then(response => response.json()) + .then(nodeData => { + // Füge den neuen Knoten zur Anzeige hinzu + cy.add({ + group: 'nodes', + data: { + id: nodeId, + label: nodeData.name, + description: nodeData.description, + color: nodeData.color_code, + fontColor: '#FFFFFF' + }, + position: position + }); + + // Füge auch die Kante hinzu, falls ein Elternknoten ausgewählt wurde + if (parentNodeId) { + cy.add({ + group: 'edges', + data: { + source: parentNodeId, + target: nodeId + } + }); + } + + // Wähle den neuen Knoten aus + cy.$id(nodeId).select(); + }) + .catch(error => { + console.error('Fehler beim Laden der Knotendaten:', error); + // Fallback: Seite neu laden + window.location.reload(); + }); + + } else { + showUINotification('Fehler beim Hinzufügen des Knotens: ' + (data.message || data.error), 'error'); + } + }) + .catch(error => { + console.error('Fehler beim Hinzufügen des Knotens:', error); + showUINotification('Fehler beim Hinzufügen des Knotens', 'error'); + }); + }); + } } else { // Fallback, falls mindmapData null ist