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:
2025-04-27 07:18:32 +02:00
parent 11ab15127c
commit 5372fe220e
9 changed files with 363 additions and 125 deletions

View File

@@ -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():

View File

@@ -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');
}
}
}

View File

@@ -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;

View File

@@ -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));

View File

@@ -540,7 +540,7 @@
<!-- Globaler Hintergrund -->
<div class="full-page-bg"></div>
<!-- Statischer Fallback-Hintergrund (wird nur angezeigt, wenn JavaScript deaktiviert ist) -->
<div class="fixed inset-0 z-[-9] bg-cover bg-center opacity-50" style="background-image: url('{{ url_for('static', filename='network-bg.jpg') }}');"></div>
<div class="fixed inset-0 z-[-9] bg-cover bg-center opacity-50"></div>
<!-- App-Container -->
<div id="app-container" class="flex flex-col min-h-screen" x-data="layout">

View File

@@ -100,17 +100,23 @@
in einem interaktiven Wissensnetzwerk.
</p>
<div class="flex flex-col sm:flex-row gap-5 justify-center">
<a href="{{ url_for('mindmap') }}" class="btn-primary text-lg px-8 py-4 rounded-2xl shadow-lg hover:shadow-xl hover:shadow-purple-500/10">
<span class="flex items-center">
<i class="fa-solid fa-diagram-project mr-3 animate-pulse"></i>
Mindmap erkunden
<a href="{{ url_for('mindmap') }}" class="group transition-all duration-300 bg-gradient-to-r from-purple-600 to-indigo-600 hover:from-purple-700 hover:to-indigo-700 text-white font-medium text-lg px-8 py-4 rounded-2xl shadow-lg hover:shadow-xl hover:shadow-purple-500/20 transform hover:-translate-y-1">
<span class="flex items-center justify-center">
<i class="fa-solid fa-diagram-project mr-3 text-purple-200 group-hover:text-white transition-all duration-300 animate-pulse"></i>
<span class="relative">
Mindmap erkunden
<span class="absolute -bottom-1 left-0 w-0 h-0.5 bg-white group-hover:w-full transition-all duration-300"></span>
</span>
</span>
</a>
{% if not current_user.is_authenticated %}
<a href="{{ url_for('register') }}" class="btn-secondary text-lg px-8 py-4 rounded-2xl shadow-lg hover:shadow-xl hover:shadow-blue-500/10">
<span class="flex items-center">
<i class="fa-solid fa-user-plus mr-3 icon-pulse"></i>
Konto erstellen
<a href="{{ url_for('register') }}" class="group transition-all duration-300 bg-gradient-to-r from-blue-500 to-cyan-500 hover:from-blue-600 hover:to-cyan-600 text-white font-medium text-lg px-8 py-4 rounded-2xl shadow-lg hover:shadow-xl hover:shadow-blue-500/20 transform hover:-translate-y-1">
<span class="flex items-center justify-center">
<i class="fa-solid fa-user-plus mr-3 text-blue-200 group-hover:text-white transition-all duration-300 icon-pulse"></i>
<span class="relative">
Konto erstellen
<span class="absolute -bottom-1 left-0 w-0 h-0.5 bg-white group-hover:w-full transition-all duration-300"></span>
</span>
</span>
</a>
{% endif %}
@@ -267,21 +273,23 @@
</section>
<!-- Call to Action Section -->
<section class="py-20 relative overflow-hidden">
<section class="py-16 sm:py-20 md:py-24 relative overflow-hidden">
<div class="max-w-screen-xl mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
<div class="glass-effect p-8 md:p-12 rounded-3xl transform transition-all duration-500 hover:-translate-y-2 hover:shadow-xl bg-gradient-to-br from-purple-500/10 to-blue-500/10 backdrop-blur-xl border border-white/10">
<div class="md:flex md:items-center md:justify-between">
<div class="glass-effect p-6 sm:p-8 md:p-12 rounded-3xl transform transition-all duration-500 hover:-translate-y-2 hover:shadow-2xl bg-gradient-to-br from-purple-500/15 to-blue-500/15 backdrop-blur-xl border border-white/10 shadow-lg">
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-6">
<div class="md:w-2/3">
<h2 class="text-3xl font-bold mb-4 text-gray-900 dark:text-white">Bereit, <span class="gradient-text">Wissen</span> neu zu entdecken?</h2>
<p class="text-gray-700 dark:text-gray-300 text-lg mb-6 md:mb-0">
<h2 class="text-2xl sm:text-3xl lg:text-4xl font-bold mb-3 text-gray-900 dark:text-white leading-tight">
Bereit, <span class="gradient-text bg-clip-text text-transparent bg-gradient-to-r from-purple-500 to-blue-500">Wissen</span> neu zu entdecken?
</h2>
<p class="text-gray-700 dark:text-gray-300 text-base sm:text-lg mb-6 md:mb-0 max-w-2xl">
Starte jetzt deine Reise durch das Wissensnetzwerk und erschließe neue Perspektiven.
</p>
</div>
<div class="md:w-1/3 text-center md:text-right">
<a href="{{ url_for('mindmap') }}" class="inline-block btn-primary font-bold py-3.5 px-8 rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1 hover:scale-105">
<a href="{{ url_for('mindmap') }}" class="inline-flex items-center justify-center w-full md:w-auto btn-primary font-bold py-3 sm:py-3.5 px-6 sm:px-8 rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 transform hover:-translate-y-1 hover:scale-105 bg-gradient-to-r from-purple-600 to-blue-600 text-white">
<span class="flex items-center justify-center">
<i class="fa-solid fa-arrow-right mr-2"></i>
Zur Mindmap
<span>Zur Mindmap</span>
</span>
</a>
</div>
@@ -291,94 +299,101 @@
</section>
<!-- Quick Access Section -->
<section class="py-16">
<section class="py-16 sm:py-20">
<div class="max-w-screen-xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 lg:gap-8">
<!-- Themen-Übersicht -->
<div class="glass-morphism p-8 rounded-3xl transition-all duration-500 hover:-translate-y-3 hover:shadow-xl">
<div class="glass-morphism p-6 sm:p-8 rounded-3xl transition-all duration-500 hover:-translate-y-3 hover:shadow-xl border border-white/10 backdrop-blur-md">
<h3 class="text-xl font-bold mb-4 flex items-center text-gray-800 dark:text-white">
<div class="w-12 h-12 rounded-2xl bg-gradient-to-r from-violet-500 to-fuchsia-500 flex items-center justify-center mr-4 shadow-md">
<i class="fa-solid fa-fire text-white text-lg"></i>
<div class="w-10 h-10 sm:w-12 sm:h-12 rounded-2xl bg-gradient-to-r from-violet-500 to-fuchsia-500 flex items-center justify-center mr-3 sm:mr-4 shadow-md transform transition-transform duration-300 hover:scale-110">
<i class="fa-solid fa-fire text-white text-base sm:text-lg"></i>
</div>
Themen-Übersicht
<span class="text-lg sm:text-xl md:text-2xl">Themen-Übersicht</span>
</h3>
<div class="space-y-4 mb-6">
<a href="{{ url_for('mindmap') }}" class="flex items-center p-3.5 rounded-xl hover:bg-gray-100/50 dark:hover:bg-white/5 transition-all duration-200 group">
<div class="space-y-3 sm:space-y-4 mb-6">
<a href="{{ url_for('mindmap') }}" class="flex items-center p-3 sm:p-3.5 rounded-xl hover:bg-gray-100/50 dark:hover:bg-white/5 transition-all duration-200 group">
<div class="w-3 h-3 rounded-full bg-purple-400 mr-3 group-hover:scale-125 transition-transform"></div>
<div class="flex-grow">
<p class="font-medium text-gray-800 dark:text-gray-200">Wissensbereiche <span class="text-xs text-gray-500">(12)</span></p>
<p class="text-sm text-gray-500 dark:text-gray-400">Überblick über Themenbereiche</p>
<p class="text-xs sm:text-sm text-gray-500 dark:text-gray-400">Überblick über Themenbereiche</p>
</div>
<i class="fa-solid fa-chevron-right text-gray-500 group-hover:translate-x-1 transition-transform"></i>
</a>
<a href="{{ url_for('search_thoughts_page') }}" class="flex items-center p-3.5 rounded-xl hover:bg-gray-100/50 dark:hover:bg-white/5 transition-all duration-200 group">
<a href="{{ url_for('search_thoughts_page') }}" class="flex items-center p-3 sm:p-3.5 rounded-xl hover:bg-gray-100/50 dark:hover:bg-white/5 transition-all duration-200 group">
<div class="w-3 h-3 rounded-full bg-blue-400 mr-3 group-hover:scale-125 transition-transform"></div>
<div class="flex-grow">
<p class="font-medium text-gray-800 dark:text-gray-200">Gedanken <span class="text-xs text-gray-500">(87)</span></p>
<p class="text-sm text-gray-500 dark:text-gray-400">Konkrete Einträge durchsuchen</p>
<p class="text-xs sm:text-sm text-gray-500 dark:text-gray-400">Konkrete Einträge durchsuchen</p>
</div>
<i class="fa-solid fa-chevron-right text-gray-500 group-hover:translate-x-1 transition-transform"></i>
</a>
<a href="#" class="flex items-center p-3.5 rounded-xl hover:bg-gray-100/50 dark:hover:bg-white/5 transition-all duration-200 group">
<a href="#" class="flex items-center p-3 sm:p-3.5 rounded-xl hover:bg-gray-100/50 dark:hover:bg-white/5 transition-all duration-200 group">
<div class="w-3 h-3 rounded-full bg-green-400 mr-3 group-hover:scale-125 transition-transform"></div>
<div class="flex-grow">
<p class="font-medium text-gray-800 dark:text-gray-200">Verbindungen <span class="text-xs text-gray-500">(34)</span></p>
<p class="text-sm text-gray-500 dark:text-gray-400">Beziehungen zwischen Gedanken</p>
<p class="text-xs sm:text-sm text-gray-500 dark:text-gray-400">Beziehungen zwischen Gedanken</p>
</div>
<i class="fa-solid fa-chevron-right text-gray-500 group-hover:translate-x-1 transition-transform"></i>
</a>
</div>
<a href="{{ url_for('search_thoughts_page') }}" class="btn-primary w-full text-center rounded-xl py-3.5">Alle Themen entdecken</a>
<a href="{{ url_for('search_thoughts_page') }}" class="btn-primary w-full text-center rounded-xl py-3 sm:py-3.5 transform transition-all duration-300 hover:-translate-y-1 hover:shadow-lg flex items-center justify-center">
<span>Alle Themen entdecken</span>
<i class="fa-solid fa-arrow-right ml-2"></i>
</a>
</div>
<!-- Google-like KI-Assistent Search Bar -->
<div class="glass-morphism p-8 rounded-3xl transition-all duration-500 hover:-translate-y-3 hover:shadow-xl">
<h3 class="text-xl font-bold mb-4 flex items-center text-gray-800 dark:text-white">
<div class="w-12 h-12 rounded-2xl bg-gradient-to-r from-purple-500 to-blue-500 flex items-center justify-center mr-4 shadow-md">
<i class="fa-solid fa-robot text-white text-lg"></i>
<!-- KI-Assistent mit verbessertem Design -->
<div class="glass-morphism p-6 sm:p-8 rounded-3xl transition-all duration-500 hover:-translate-y-3 hover:shadow-xl backdrop-blur-md border border-white/10">
<h3 class="text-xl md:text-2xl font-bold mb-4 flex flex-wrap sm:flex-nowrap items-center text-gray-800 dark:text-white">
<div class="w-10 h-10 sm:w-12 sm:h-12 rounded-2xl bg-gradient-to-r from-purple-600 to-blue-600 flex items-center justify-center mr-3 sm:mr-4 shadow-lg transform transition-transform duration-300 hover:scale-110">
<i class="fa-solid fa-robot text-white text-base sm:text-lg"></i>
</div>
KI-Assistent
<span class="mt-1 sm:mt-0">KI-Assistent</span>
</h3>
<p class="text-gray-600 dark:text-gray-300 mb-6">
<p class="text-gray-600 dark:text-gray-300 mb-5 sm:mb-6 text-sm md:text-base leading-relaxed">
Stelle Fragen, lasse dir Themen erklären oder finde neue Verbindungen mit Hilfe
unseres KI-Assistenten.
</p>
<!-- Google-like Search Bar -->
<div class="mb-6">
<div class="relative">
<!-- Verbesserte Suchleiste -->
<div class="mb-5 sm:mb-6">
<div class="relative group">
<div class="absolute inset-0 bg-gradient-to-r from-purple-500/20 to-blue-500/20 rounded-xl blur-sm group-hover:blur-md transition-all duration-300 opacity-70 group-hover:opacity-100"></div>
<input type="text" placeholder="Frage den KI-Assistenten"
class="w-full px-4 py-3.5 rounded-xl border bg-white/50 dark:bg-gray-800/50 border-gray-300 dark:border-gray-700 shadow-sm focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-purple-500 transition-all duration-200"
class="relative w-full px-4 sm:px-5 py-3 sm:py-4 rounded-xl border bg-white/70 dark:bg-gray-800/70 border-gray-300 dark:border-gray-700 shadow-sm focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent transition-all duration-200 placeholder-gray-400 dark:placeholder-gray-500 text-gray-700 dark:text-gray-200"
onkeypress="if(event.keyCode==13) window.MindMap.assistant.toggleAssistant(true);">
<button onclick="window.MindMap.assistant.toggleAssistant(true)"
class="absolute right-2 top-1/2 transform -translate-y-1/2 text-gray-500 dark:text-gray-400 hover:text-purple-500 dark:hover:text-purple-400 focus:outline-none transition-all duration-200">
<i class="fa-solid fa-search"></i>
class="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-500 dark:text-gray-400 hover:text-purple-500 dark:hover:text-purple-400 focus:outline-none transition-all duration-200 w-8 h-8 sm:w-10 sm:h-10 flex items-center justify-center bg-white/50 dark:bg-gray-700/50 rounded-full hover:bg-purple-100 dark:hover:bg-purple-900/30">
<i class="fa-solid fa-search text-sm sm:text-base"></i>
</button>
</div>
</div>
<div class="glass-morphism p-5 rounded-2xl mb-6 transition-all duration-300 hover:shadow-md bg-gradient-to-br from-purple-500/5 to-blue-500/5 border border-white/10">
<div class="glass-morphism p-4 sm:p-5 rounded-2xl mb-5 sm:mb-6 transition-all duration-300 hover:shadow-md bg-gradient-to-br from-purple-500/5 to-blue-500/5 border border-white/10">
<div class="flex items-start">
<div class="w-10 h-10 rounded-xl bg-gradient-to-r from-purple-500 to-blue-500 flex items-center justify-center flex-shrink-0 mr-3 shadow-md">
<i class="fa-solid fa-robot text-white"></i>
<div class="w-8 h-8 sm:w-10 sm:h-10 rounded-xl bg-gradient-to-r from-purple-500 to-blue-500 flex items-center justify-center flex-shrink-0 mr-3 shadow-md">
<i class="fa-solid fa-robot text-white text-xs sm:text-sm"></i>
</div>
<div>
<p class="text-sm text-gray-700 dark:text-gray-300">Frage den KI-Assistenten</p>
<div class="mt-4 flex flex-wrap gap-2">
<a href="{{ url_for('mindmap') }}" class="px-3 py-1.5 bg-gray-100 hover:bg-gray-200 dark:bg-gray-800/70 dark:hover:bg-gray-700/80 rounded-xl text-xs text-gray-700 dark:text-gray-300 transition-all duration-200 hover:-translate-y-0.5 shadow-sm hover:shadow">
<p class="text-xs sm:text-sm text-gray-700 dark:text-gray-300">Frage den KI-Assistenten</p>
<div class="mt-3 sm:mt-4 flex flex-wrap gap-2">
<a href="{{ url_for('mindmap') }}" class="px-2 sm:px-3 py-1 sm:py-1.5 bg-gray-100 hover:bg-gray-200 dark:bg-gray-800/70 dark:hover:bg-gray-700/80 rounded-lg sm:rounded-xl text-xs text-gray-700 dark:text-gray-300 transition-all duration-200 hover:-translate-y-0.5 shadow-sm hover:shadow">
Erkunde die Mindmap
</a>
<a href="{{ url_for('search_thoughts_page') }}" class="px-3 py-1.5 bg-gray-100 hover:bg-gray-200 dark:bg-gray-800/70 dark:hover:bg-gray-700/80 rounded-xl text-xs text-gray-700 dark:text-gray-300 transition-all duration-200 hover:-translate-y-0.5 shadow-sm hover:shadow">
<a href="{{ url_for('search_thoughts_page') }}" class="px-2 sm:px-3 py-1 sm:py-1.5 bg-gray-100 hover:bg-gray-200 dark:bg-gray-800/70 dark:hover:bg-gray-700/80 rounded-lg sm:rounded-xl text-xs text-gray-700 dark:text-gray-300 transition-all duration-200 hover:-translate-y-0.5 shadow-sm hover:shadow">
Themen durchsuchen
</a>
<a href="#" class="px-3 py-1.5 bg-gray-100 hover:bg-gray-200 dark:bg-gray-800/70 dark:hover:bg-gray-700/80 rounded-xl text-xs text-gray-700 dark:text-gray-300 transition-all duration-200 hover:-translate-y-0.5 shadow-sm hover:shadow">
<a href="#" class="px-2 sm:px-3 py-1 sm:py-1.5 bg-gray-100 hover:bg-gray-200 dark:bg-gray-800/70 dark:hover:bg-gray-700/80 rounded-lg sm:rounded-xl text-xs text-gray-700 dark:text-gray-300 transition-all duration-200 hover:-translate-y-0.5 shadow-sm hover:shadow">
Beziehungen erforschen
</a>
</div>
</div>
</div>
</div>
<button onclick="window.MindMap.assistant.toggleAssistant(true)" class="btn-primary w-full text-center rounded-xl py-3.5 shadow-md hover:shadow-lg transition-all duration-300 hover:-translate-y-1">KI-Chat starten</button>
<button onclick="window.MindMap.assistant.toggleAssistant(true)" class="btn-primary w-full text-center rounded-xl py-3 sm:py-3.5 shadow-md hover:shadow-lg transition-all duration-300 hover:-translate-y-1 flex items-center justify-center">
<i class="fa-solid fa-comments mr-2"></i>
<span>KI-Chat starten</span>
</button>
</div>
</div>
</div>

View File

@@ -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;

View File

@@ -3,39 +3,39 @@
{% block title %}Suche{% endblock %}
{% block content %}
<div class="row">
<div class="flex flex-col md:flex-row gap-6">
<!-- Filter-Sidebar -->
<div class="col-lg-3 col-md-4 mb-4">
<div class="glass">
<h4 class="mb-4">Erweiterte Suche</h4>
<div class="w-full md:w-1/3 lg:w-1/4 mb-6">
<div class="bg-white/10 backdrop-blur-md rounded-xl p-6 shadow-lg">
<h4 class="text-xl font-semibold mb-4">Erweiterte Suche</h4>
<form id="search-form">
<div class="mb-3">
<label class="form-label">Suchbegriff</label>
<div class="input-group">
<span class="input-group-text">
<div class="mb-4">
<label class="block text-sm font-medium mb-1">Suchbegriff</label>
<div class="flex items-center border rounded-lg overflow-hidden bg-white/5">
<span class="px-3 py-2 text-gray-400">
<i class="fas fa-search"></i>
</span>
<input type="text" class="form-control" name="q" placeholder="Suche...">
<input type="text" class="w-full bg-transparent border-0 focus:ring-0 py-2 px-1" name="q" placeholder="Suche...">
</div>
</div>
<div class="mb-3">
<label class="form-label">Schlagworte</label>
<div class="input-group">
<span class="input-group-text">
<div class="mb-4">
<label class="block text-sm font-medium mb-1">Schlagworte</label>
<div class="flex items-center border rounded-lg overflow-hidden bg-white/5">
<span class="px-3 py-2 text-gray-400">
<i class="fas fa-tags"></i>
</span>
<input type="text" class="form-control" name="keywords" placeholder="Schlagworte (kommagetrennt)">
<input type="text" class="w-full bg-transparent border-0 focus:ring-0 py-2 px-1" name="keywords" placeholder="Schlagworte (kommagetrennt)">
</div>
</div>
<div class="mb-3">
<label class="form-label">Minimale Bewertung</label>
<div class="input-group">
<span class="input-group-text">
<div class="mb-4">
<label class="block text-sm font-medium mb-1">Minimale Bewertung</label>
<div class="flex items-center border rounded-lg overflow-hidden bg-white/5">
<span class="px-3 py-2 text-gray-400">
<i class="fas fa-star"></i>
</span>
<select class="form-select" name="min_rating">
<select class="w-full bg-transparent border-0 focus:ring-0 py-2 px-1" name="min_rating">
<option value="">Alle</option>
<option value="4">4+ Sterne</option>
<option value="3">3+ Sterne</option>
@@ -45,13 +45,13 @@
</div>
</div>
<div class="mb-3">
<label class="form-label">Quellentyp</label>
<div class="input-group">
<span class="input-group-text">
<div class="mb-4">
<label class="block text-sm font-medium mb-1">Quellentyp</label>
<div class="flex items-center border rounded-lg overflow-hidden bg-white/5">
<span class="px-3 py-2 text-gray-400">
<i class="fas fa-file-alt"></i>
</span>
<select class="form-select" name="source_type">
<select class="w-full bg-transparent border-0 focus:ring-0 py-2 px-1" name="source_type">
<option value="">Alle</option>
<option value="PDF">PDF</option>
<option value="Markdown">Markdown</option>
@@ -60,13 +60,13 @@
</div>
</div>
<div class="mb-4">
<label class="form-label">Beziehungstyp</label>
<div class="input-group">
<span class="input-group-text">
<div class="mb-5">
<label class="block text-sm font-medium mb-1">Beziehungstyp</label>
<div class="flex items-center border rounded-lg overflow-hidden bg-white/5">
<span class="px-3 py-2 text-gray-400">
<i class="fas fa-project-diagram"></i>
</span>
<select class="form-select" name="relation_type">
<select class="w-full bg-transparent border-0 focus:ring-0 py-2 px-1" name="relation_type">
<option value="">Alle</option>
<option value="SUPPORTS">Stützt</option>
<option value="CONTRADICTS">Widerspricht</option>
@@ -78,26 +78,26 @@
</div>
</div>
<button type="submit" class="btn btn-primary w-100">
<i class="fas fa-search me-2"></i> Suchen
<button type="submit" class="w-full bg-gradient-to-r from-blue-500 to-indigo-600 text-white py-2 px-4 rounded-lg hover:opacity-90 transition-all flex items-center justify-center">
<i class="fas fa-search mr-2"></i> Suchen
</button>
</form>
</div>
</div>
<!-- Suchergebnisse -->
<div class="col-lg-9 col-md-8">
<div class="glass mb-4">
<h3 class="mb-3">Suchergebnisse</h3>
<p class="opacity-75">Nutze die Filter links, um deine Suche zu präzisieren.</p>
<div class="w-full md:w-2/3 lg:w-3/4">
<div class="bg-white/10 backdrop-blur-md rounded-xl p-6 shadow-lg mb-6">
<h3 class="text-2xl font-semibold mb-2">Suchergebnisse</h3>
<p class="text-gray-300 text-sm">Nutze die Filter links, um deine Suche zu präzisieren.</p>
</div>
<div id="search-results" class="mb-4">
<div id="search-results" class="mb-6">
<!-- Suchergebnisse werden hier dynamisch eingefügt -->
<div class="glass text-center py-5">
<i class="fas fa-search fa-3x mb-4" style="color: var(--primary-color);"></i>
<h5 class="mb-3">Wissen entdecken</h5>
<p class="opacity-75">Gib einen Suchbegriff ein, um in der wissenschaftlichen Wissensdatenbank zu suchen.</p>
<div class="bg-white/10 backdrop-blur-md rounded-xl p-8 shadow-lg text-center">
<i class="fas fa-search text-5xl mb-6 text-blue-400"></i>
<h5 class="text-xl font-medium mb-3">Wissen entdecken</h5>
<p class="text-gray-300">Gib einen Suchbegriff ein, um in der wissenschaftlichen Wissensdatenbank zu suchen.</p>
</div>
</div>
</div>