Add flash message API and enhance mindmap visualization: Implement a new API endpoint for retrieving flash messages, integrate flash message display in the mindmap visualization, and improve user feedback with dynamic notifications. Update UI elements for better responsiveness and visual appeal, while removing obsolete background image references.
This commit is contained in:
@@ -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 = '<i class="fas fa-check-circle"></i>';
|
||||
break;
|
||||
case 'error':
|
||||
icon = '<i class="fas fa-exclamation-circle"></i>';
|
||||
break;
|
||||
case 'warning':
|
||||
icon = '<i class="fas fa-exclamation-triangle"></i>';
|
||||
break;
|
||||
default:
|
||||
icon = '<i class="fas fa-info-circle"></i>';
|
||||
}
|
||||
|
||||
// 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 = '<i class="fas fa-times"></i>';
|
||||
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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user