diff --git a/website/__pycache__/app.cpython-311.pyc b/website/__pycache__/app.cpython-311.pyc index 87c2fde..c43e69d 100644 Binary files a/website/__pycache__/app.cpython-311.pyc and b/website/__pycache__/app.cpython-311.pyc differ diff --git a/website/app.py b/website/app.py index 84c35f4..8cd9d24 100755 --- a/website/app.py +++ b/website/app.py @@ -207,6 +207,26 @@ def settings(): return render_template('settings.html') +# API-Endpunkt für Flash-Nachrichten +@app.route('/api/get_flash_messages') +def get_flash_messages(): + """Liefert aktuelle Flash-Nachrichten für API/JS-Clients.""" + # Hole alle gespeicherten Flash-Nachrichten + messages = [] + flashed_messages = session.get('_flashes', []) + + # Formatierung der Nachrichten für die API-Antwort + for category, message in flashed_messages: + messages.append({ + 'category': category, + 'message': message + }) + + # Lösche die Nachrichten aus der Session, nachdem sie abgerufen wurden + session.pop('_flashes', None) + + return jsonify(messages) + # Routes für rechtliche Seiten @app.route('/impressum/') def impressum(): @@ -1099,6 +1119,13 @@ def my_account(): return render_template('my_account.html', bookmarked_thoughts=bookmarked_thoughts) +# Dummy-Route, um 404-Fehler für fehlende Netzwerk-Hintergrundbilder zu vermeiden +@app.route('/static/network-bg.jpg') +@app.route('/static/network-bg.svg') +def dummy_network_bg(): + """Leere Antwort für die nicht mehr verwendeten Netzwerk-Hintergrundbilder.""" + return '', 200 + # Flask starten if __name__ == '__main__': with app.app_context(): diff --git a/website/static/mindmap.js b/website/static/mindmap.js index 489c923..e601499 100644 --- a/website/static/mindmap.js +++ b/website/static/mindmap.js @@ -32,6 +32,9 @@ class MindMapVisualization { this.tooltipDiv = null; this.isLoading = true; + // Flash-Nachrichten-Container + this.flashContainer = null; + // Erweiterte Farbpalette für Knotentypen this.colorPalette = { 'default': '#b38fff', @@ -57,6 +60,7 @@ class MindMapVisualization { if (this.container.node()) { this.init(); this.setupDefaultNodes(); + this.setupFlashMessages(); // Sofortige Datenladung window.setTimeout(() => { @@ -67,6 +71,183 @@ class MindMapVisualization { } } + // Flash-Nachrichten-System einrichten + setupFlashMessages() { + // Flash-Container erstellen, falls er noch nicht existiert + if (!document.getElementById('mindmap-flash-container')) { + this.flashContainer = document.createElement('div'); + this.flashContainer.id = 'mindmap-flash-container'; + this.flashContainer.className = 'mindmap-flash-container'; + this.flashContainer.style.position = 'fixed'; + this.flashContainer.style.top = '20px'; + this.flashContainer.style.right = '20px'; + this.flashContainer.style.zIndex = '1000'; + this.flashContainer.style.maxWidth = '350px'; + this.flashContainer.style.display = 'flex'; + this.flashContainer.style.flexDirection = 'column'; + this.flashContainer.style.gap = '10px'; + document.body.appendChild(this.flashContainer); + } else { + this.flashContainer = document.getElementById('mindmap-flash-container'); + } + + // Prüfen, ob Server-seitige Flash-Nachrichten existieren und anzeigen + this.checkForServerFlashMessages(); + } + + // Prüft auf Server-seitige Flash-Nachrichten + async checkForServerFlashMessages() { + try { + const response = await fetch('/api/get_flash_messages'); + if (response.ok) { + const messages = await response.json(); + messages.forEach(message => { + this.showFlash(message.message, message.category); + }); + } + } catch (err) { + console.error('Fehler beim Abrufen der Flash-Nachrichten:', err); + } + } + + // Zeigt eine Flash-Nachricht an + showFlash(message, type = 'info', duration = 5000) { + if (!this.flashContainer) return; + + const flashElement = document.createElement('div'); + flashElement.className = `mindmap-flash flash-${type}`; + flashElement.style.padding = '12px 18px'; + flashElement.style.borderRadius = '8px'; + flashElement.style.boxShadow = '0 4px 12px rgba(0, 0, 0, 0.15)'; + flashElement.style.display = 'flex'; + flashElement.style.alignItems = 'center'; + flashElement.style.justifyContent = 'space-between'; + flashElement.style.fontSize = '14px'; + flashElement.style.fontWeight = '500'; + flashElement.style.backdropFilter = 'blur(10px)'; + flashElement.style.opacity = '0'; + flashElement.style.transform = 'translateY(-20px)'; + flashElement.style.transition = 'all 0.3s ease'; + + // Spezifische Stile je nach Nachrichtentyp + switch(type) { + case 'success': + flashElement.style.backgroundColor = 'rgba(34, 197, 94, 0.9)'; + flashElement.style.borderLeft = '5px solid #16a34a'; + flashElement.style.color = 'white'; + break; + case 'error': + flashElement.style.backgroundColor = 'rgba(239, 68, 68, 0.9)'; + flashElement.style.borderLeft = '5px solid #dc2626'; + flashElement.style.color = 'white'; + break; + case 'warning': + flashElement.style.backgroundColor = 'rgba(245, 158, 11, 0.9)'; + flashElement.style.borderLeft = '5px solid #d97706'; + flashElement.style.color = 'white'; + break; + default: // info + flashElement.style.backgroundColor = 'rgba(59, 130, 246, 0.9)'; + flashElement.style.borderLeft = '5px solid #2563eb'; + flashElement.style.color = 'white'; + } + + // Icon je nach Nachrichtentyp + let icon = ''; + switch(type) { + case 'success': + icon = ''; + break; + case 'error': + icon = ''; + break; + case 'warning': + icon = ''; + break; + default: + icon = ''; + } + + // Inhalt der Nachricht mit Icon + const contentWrapper = document.createElement('div'); + contentWrapper.style.display = 'flex'; + contentWrapper.style.alignItems = 'center'; + contentWrapper.style.gap = '12px'; + + const iconElement = document.createElement('div'); + iconElement.className = 'flash-icon'; + iconElement.innerHTML = icon; + + const textElement = document.createElement('div'); + textElement.className = 'flash-text'; + textElement.textContent = message; + + contentWrapper.appendChild(iconElement); + contentWrapper.appendChild(textElement); + + // Schließen-Button + const closeButton = document.createElement('button'); + closeButton.className = 'flash-close'; + closeButton.innerHTML = ''; + closeButton.style.background = 'none'; + closeButton.style.border = 'none'; + closeButton.style.color = 'currentColor'; + closeButton.style.cursor = 'pointer'; + closeButton.style.marginLeft = '15px'; + closeButton.style.padding = '3px'; + closeButton.style.fontSize = '14px'; + closeButton.style.opacity = '0.7'; + closeButton.style.transition = 'opacity 0.2s'; + + closeButton.addEventListener('mouseover', () => { + closeButton.style.opacity = '1'; + }); + + closeButton.addEventListener('mouseout', () => { + closeButton.style.opacity = '0.7'; + }); + + closeButton.addEventListener('click', () => { + this.removeFlash(flashElement); + }); + + // Zusammenfügen + flashElement.appendChild(contentWrapper); + flashElement.appendChild(closeButton); + + // Zum Container hinzufügen + this.flashContainer.appendChild(flashElement); + + // Animation einblenden + setTimeout(() => { + flashElement.style.opacity = '1'; + flashElement.style.transform = 'translateY(0)'; + }, 10); + + // Automatisches Ausblenden nach der angegebenen Zeit + if (duration > 0) { + setTimeout(() => { + this.removeFlash(flashElement); + }, duration); + } + + return flashElement; + } + + // Entfernt eine Flash-Nachricht mit Animation + removeFlash(flashElement) { + if (!flashElement) return; + + flashElement.style.opacity = '0'; + flashElement.style.transform = 'translateY(-20px)'; + + setTimeout(() => { + if (flashElement.parentNode) { + flashElement.parentNode.removeChild(flashElement); + } + }, 300); + } + // Standardknoten als Fallback einrichten, falls die API nicht reagiert setupDefaultNodes() { // Basis-Mindmap mit Hauptthemen @@ -301,6 +482,9 @@ class MindMapVisualization { // Lade-Animation ausblenden this.hideLoading(); + + // Erfolgreiche Ladung melden + this.showFlash('Mindmap-Daten erfolgreich geladen', 'success'); } catch (error) { console.error('Fehler beim Laden der Mindmap-Daten:', error); @@ -310,6 +494,7 @@ class MindMapVisualization { // Fehler anzeigen this.showError('Mindmap-Daten konnten nicht geladen werden. Verwende Standarddaten.'); + this.showFlash('Fehler beim Laden der Mindmap-Daten. Standarddaten werden angezeigt.', 'error'); // Visualisierung auch im Fehlerfall aktualisieren this.updateVisualization(); @@ -992,6 +1177,9 @@ class MindMapVisualization { window.onNodeDeselected(); } + // Flash-Nachricht für abgewählten Knoten + this.showFlash('Knotenauswahl aufgehoben', 'info', 2000); + return; } @@ -1083,6 +1271,7 @@ class MindMapVisualization { if (!thoughtContainer || !thoughtsList) { console.error('Gedanken-Container nicht gefunden'); + this.showFlash('Fehler: Gedanken-Container nicht gefunden', 'error'); return; } @@ -1118,6 +1307,9 @@ class MindMapVisualization { thoughtsList.innerHTML = ''; } + // Flash-Nachricht über ausgewählten Knoten + this.showFlash(`Knoten "${node.name}" ausgewählt`, 'info'); + // Verzögerung für Animation setTimeout(() => { // API-Aufruf für echte Daten aus der Datenbank @@ -1133,6 +1325,7 @@ class MindMapVisualization { this.renderThoughts(thoughts, thoughtsList); } else { this.renderEmptyThoughts(thoughtsList, node); + this.showFlash(`Keine Gedanken zu "${node.name}" gefunden`, 'warning'); } }) .catch(error => { @@ -1141,6 +1334,7 @@ class MindMapVisualization { loadingIndicator.style.display = 'none'; } this.renderErrorState(thoughtsList); + this.showFlash('Fehler beim Laden der Gedanken. Bitte versuche es später erneut.', 'error'); }); }, 600); // Verzögerung für bessere UX } @@ -1152,6 +1346,7 @@ class MindMapVisualization { const id = nodeId.toString().split('_')[1]; if (!id) { console.warn('Ungültige Node-ID: ', nodeId); + this.showFlash('Ungültige Knoten-ID: ' + nodeId, 'warning'); return []; } @@ -1164,9 +1359,17 @@ class MindMapVisualization { const thoughts = await response.json(); console.log('Geladene Gedanken für Knoten:', thoughts); + + if (thoughts.length > 0) { + this.showFlash(`${thoughts.length} Gedanken zum Thema geladen`, 'info'); + } else { + this.showFlash('Keine Gedanken für diesen Knoten gefunden', 'info'); + } + return thoughts; } catch (error) { console.error('Fehler beim Laden der Gedanken für Knoten:', error); + this.showFlash('Fehler beim Laden der Gedanken', 'error'); return []; } } @@ -1282,10 +1485,16 @@ class MindMapVisualization { d3.zoom().transform, d3.zoomIdentity.translate(x, y).scale(scale) ); + + // Flash-Nachricht für Zentrierung + if (node && node.name) { + this.showFlash(`Ansicht auf "${node.name}" zentriert`, 'info', 2000); + } } // Fehlermeldung anzeigen showError(message) { + // Standard-Fehlermeldung als Banner const errorBanner = d3.select('body').selectAll('.error-banner').data([0]); const errorEnter = errorBanner.enter() @@ -1314,12 +1523,18 @@ class MindMapVisualization { .delay(5000) .duration(500) .style('bottom', '-100px'); + + // Auch als Flash-Nachricht anzeigen + this.showFlash(message, 'error'); } // Fokussieren auf einen bestimmten Knoten per ID focusNode(nodeId) { const targetNode = this.nodes.find(n => n.id === nodeId); - if (!targetNode) return; + if (!targetNode) { + this.showFlash(`Knoten mit ID "${nodeId}" nicht gefunden`, 'error'); + return; + } // Ausgewählten Zustand zurücksetzen this.selectedNode = null; @@ -1339,6 +1554,8 @@ class MindMapVisualization { d3.zoom().transform, transform ); + + this.showFlash(`Fokus auf Knoten "${targetNode.name}" gesetzt`, 'success'); } } @@ -1358,6 +1575,8 @@ class MindMapVisualization { .style('display', 'block') .style('stroke-opacity', 0.5); + this.showFlash('Suchfilter zurückgesetzt', 'info', 2000); + return; } @@ -1393,6 +1612,9 @@ class MindMapVisualization { // Wenn mehr als ein Knoten gefunden wurde, Simulation mit reduzierter Stärke neu starten if (matchingNodes.length > 1) { this.simulation.alpha(0.3).restart(); + this.showFlash(`${matchingNodes.length} Knoten für "${searchTerm}" gefunden`, 'success'); + } else if (matchingNodes.length === 0) { + this.showFlash(`Keine Knoten für "${searchTerm}" gefunden`, 'warning'); } } } diff --git a/website/static/network-animation.js b/website/static/network-animation.js index f8c5779..aab0172 100644 --- a/website/static/network-animation.js +++ b/website/static/network-animation.js @@ -75,7 +75,7 @@ document.addEventListener('DOMContentLoaded', function() { left: 0; width: 100%; height: 100%; - background-image: url('/static/network-bg.jpg'); + background: rgba(179, 143, 255, 0.05); background-size: cover; background-position: center; opacity: 0.15; diff --git a/website/static/network-background.js b/website/static/network-background.js index 2274539..5ab35e2 100644 --- a/website/static/network-background.js +++ b/website/static/network-background.js @@ -42,34 +42,10 @@ function initNetworkBackground() { networkImage = new Image(); networkImage.crossOrigin = "anonymous"; // Vermeidet CORS-Probleme - // Event-Handler für Fehler - Fallback auf Standard-Hintergrund - networkImage.onerror = function() { - loadAttempts++; - if (loadAttempts < MAX_LOAD_ATTEMPTS) { - // Wenn SVG fehlschlägt, versuche JPG - if (networkImage.src.endsWith('svg')) { - networkImage.src = '/static/network-bg.jpg'; - } else { - // Wenn beide fehlschlagen, starte einfach Animation ohne Hintergrund - console.log("Konnte kein Hintergrundbild laden, verwende einfachen Hintergrund"); - isImageLoaded = true; // Trotzdem Animation starten - startAnimation(); - } - } else { - // Zu viele Versuche, verwende einfachen Hintergrund - console.log("Konnte kein Hintergrundbild laden, verwende einfachen Hintergrund"); - isImageLoaded = true; // Trotzdem Animation starten - startAnimation(); - } - }; - - // Versuche zuerst die SVG-Version zu laden - networkImage.src = '/static/network-bg.svg'; - - networkImage.onload = function() { - isImageLoaded = true; - startAnimation(); - }; + // Keine Bilder laden, direkt Fallback-Hintergrund verwenden + console.log("Verwende einfachen Hintergrund ohne Bilddateien"); + isImageLoaded = true; // Animation ohne Hintergrundbild starten + startAnimation(); // Handle window resize window.addEventListener('resize', debounce(resizeCanvas, 250)); diff --git a/website/templates/base.html b/website/templates/base.html index b20d577..445af5f 100644 --- a/website/templates/base.html +++ b/website/templates/base.html @@ -540,7 +540,7 @@
-
+
diff --git a/website/templates/index.html b/website/templates/index.html index c2bacf1..599814a 100644 --- a/website/templates/index.html +++ b/website/templates/index.html @@ -100,17 +100,23 @@ in einem interaktiven Wissensnetzwerk.

- - - - Mindmap erkunden + + + + + Mindmap erkunden + + {% if not current_user.is_authenticated %} - - - - Konto erstellen + + + + + Konto erstellen + + {% endif %} @@ -267,21 +273,23 @@ -
+
-
-
+
+
-

Bereit, Wissen neu zu entdecken?

-

+

+ Bereit, Wissen neu zu entdecken? +

+

Starte jetzt deine Reise durch das Wissensnetzwerk und erschließe neue Perspektiven.

@@ -291,94 +299,101 @@
-
+
-
+
-
+

-
- +
+
- Themen-Übersicht + Themen-Übersicht

- - -
-

-
- + +
+

+
+
- KI-Assistent + KI-Assistent

-

+

Stelle Fragen, lasse dir Themen erklären oder finde neue Verbindungen mit Hilfe unseres KI-Assistenten.

- -
-
+ +
+
+
-
+
- - +
diff --git a/website/templates/mindmap.html b/website/templates/mindmap.html index e449133..b1c3982 100644 --- a/website/templates/mindmap.html +++ b/website/templates/mindmap.html @@ -516,9 +516,7 @@ left: 0; width: 100%; height: 100%; - background-image: url('/static/network-bg.svg'); - background-size: cover; - background-position: center; + background-image: none; opacity: 0.2; z-index: -1; animation: pulse 10s ease-in-out infinite alternate; diff --git a/website/templates/search.html b/website/templates/search.html index a109fd9..54507aa 100644 --- a/website/templates/search.html +++ b/website/templates/search.html @@ -3,39 +3,39 @@ {% block title %}Suche{% endblock %} {% block content %} -
+
-
-
-

Erweiterte Suche

+
+
+

Erweiterte Suche

-
- -
- +
+ +
+ - +
-
- -
- +
+ +
+ - +
-
- -
- +
+ +
+ - @@ -45,13 +45,13 @@
-
- -
- +
+ +
+ - @@ -60,13 +60,13 @@
-
- -
- +
+ +
+ - @@ -78,26 +78,26 @@
-
-
-
-

Suchergebnisse

-

Nutze die Filter links, um deine Suche zu präzisieren.

+
+
+

Suchergebnisse

+

Nutze die Filter links, um deine Suche zu präzisieren.

-
+
-
- -
Wissen entdecken
-

Gib einen Suchbegriff ein, um in der wissenschaftlichen Wissensdatenbank zu suchen.

+
+ +
Wissen entdecken
+

Gib einen Suchbegriff ein, um in der wissenschaftlichen Wissensdatenbank zu suchen.