chore: Änderungen commited

This commit is contained in:
2025-05-10 23:10:31 +02:00
parent e7b3374c53
commit d1352286b7
2 changed files with 411 additions and 0 deletions

View File

@@ -435,6 +435,19 @@
<div id="public-cy" style="width: 100%; height: 250px;"></div>
</div>
<!-- Suchergebnisse Container -->
<div id="search-results-container" class="search-results-container p-4">
<div class="flex justify-between items-center mb-3">
<h3 class="text-lg font-semibold">Suchergebnisse</h3>
<button id="close-search" class="text-sm opacity-70 hover:opacity-100">
<i class="fas fa-times"></i>
</button>
</div>
<div id="search-results-list" class="space-y-1">
<!-- Suchergebnisse werden hier dynamisch hinzugefügt -->
</div>
</div>
<!-- Informationsanzeige für ausgewählten Knoten -->
<div id="node-info-panel" class="node-info-panel p-4">
<h3 class="text-xl font-bold gradient-text mb-2">Knotendetails</h3>
@@ -1051,6 +1064,272 @@
});
}
// Suchfunktionalität implementieren
const searchInput = document.getElementById('mindmap-search');
const searchBtn = document.getElementById('search-btn');
const searchResultsContainer = document.getElementById('search-results-container');
const searchResultsList = document.getElementById('search-results-list');
const closeSearchBtn = document.getElementById('close-search');
// Funktion zum Schließen der Suchergebnisse
function closeSearchResults() {
searchResultsContainer.classList.remove('visible');
searchInput.value = '';
}
// Event-Listener für die Suche
searchBtn.addEventListener('click', performSearch);
searchInput.addEventListener('keydown', function(e) {
if (e.key === 'Enter') {
performSearch();
}
});
closeSearchBtn.addEventListener('click', closeSearchResults);
// Funktion für die Suche
function performSearch() {
const query = searchInput.value.trim();
if (!query) {
showUINotification('Bitte geben Sie einen Suchbegriff ein.', 'info');
return;
}
// Lade-Animation im Suchergebnisbereich anzeigen
searchResultsList.innerHTML = '<div class="p-4 text-center"><i class="fas fa-spinner fa-spin mr-2"></i> Suche läuft...</div>';
searchResultsContainer.classList.add('visible');
// API-Anfrage für die Suche
fetch(`/api/search/mindmap?q=${encodeURIComponent(query)}`)
.then(response => response.json())
.then(data => {
if (data.success) {
displaySearchResults(data.results, query);
} else {
searchResultsList.innerHTML = `<div class="p-4 text-center text-red-500">Fehler: ${data.message}</div>`;
}
})
.catch(error => {
console.error('Fehler bei der Suche:', error);
searchResultsList.innerHTML = '<div class="p-4 text-center text-red-500">Ein Fehler ist aufgetreten.</div>';
});
}
// Funktion zum Anzeigen der Suchergebnisse
function displaySearchResults(results, query) {
searchResultsList.innerHTML = '';
if (results.length === 0) {
searchResultsList.innerHTML = '<div class="p-4 text-center">Keine Ergebnisse gefunden.</div>';
return;
}
// Hervorheben-Funktion für den Suchbegriff
function highlightText(text, query) {
if (!text) return '';
const regex = new RegExp(`(${query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
return text.replace(regex, '<span class="bg-yellow-200 dark:bg-yellow-800">$1</span>');
}
// Ergebnisse anzeigen
results.forEach(result => {
const item = document.createElement('div');
item.className = 'search-result-item';
// Inhalte mit hervorgehobenem Suchbegriff
const highlightedName = highlightText(result.name, query);
const highlightedDesc = highlightText(result.description, query);
item.innerHTML = `
<div class="search-result-title">
<span class="search-result-color" style="background-color: ${result.color_code}"></span>
<span>${highlightedName}</span>
</div>
<div class="search-result-desc">${highlightedDesc}</div>
<div class="search-result-source">
<i class="fas fa-map-marked-alt mr-1"></i> ${result.mindmap_name}
</div>
`;
// Event-Listener zum Springen zum Knoten
item.addEventListener('click', () => {
if (result.source === 'user_mindmap') {
// Knoten ist bereits in der aktuellen Mindmap
const node = cy.$id(result.id);
if (node.length > 0) {
// Knoten auswählen und zentrieren
node.select();
cy.animate({
center: { eles: node },
zoom: 1.5,
duration: 500
});
// Aktualisiere das Info-Panel
nodeDescription.textContent = result.description || 'Keine Beschreibung für diesen Knoten.';
nodeInfoPanel.classList.add('visible');
// Suchergebnisse schließen
closeSearchResults();
} else {
// Knoten ist in einer anderen Mindmap des Benutzers
if (result.mindmap_id && result.mindmap_id !== parseInt("{{ mindmap.id }}")) {
if (confirm(`Dieser Knoten befindet sich in der Mindmap "${result.mindmap_name}". Möchten Sie zu dieser Mindmap wechseln?`)) {
window.location.href = `/my-mindmap/${result.mindmap_id}`;
}
} else {
showUINotification('Knoten konnte nicht gefunden werden.', 'error');
}
}
} else if (result.source === 'public') {
// Knoten ist in der öffentlichen Mindmap, frage ob er hinzugefügt werden soll
if (confirm(`Möchten Sie den Knoten "${result.name}" aus der öffentlichen Mindmap zu Ihrer Mindmap hinzufügen?`)) {
// Dialog zum Hinzufügen des Knotens anzeigen
showAddNodeDialog(result.id, result.name, result.description);
closeSearchResults();
}
}
});
searchResultsList.appendChild(item);
});
}
// Export-Funktionalität implementieren
document.getElementById('export-btn').addEventListener('click', function() {
const mindmapId = parseInt("{{ mindmap.id }}");
// Dialog für Export-Format anzeigen
const overlay = document.createElement('div');
overlay.className = 'fixed inset-0 bg-black/50 flex items-center justify-center z-50';
overlay.id = 'export-dialog-overlay';
overlay.innerHTML = `
<div class="bg-white dark:bg-gray-800 rounded-lg p-6 max-w-md w-full mx-4">
<h3 class="text-xl font-bold mb-4">Mindmap exportieren</h3>
<div class="mb-4">
<label class="block mb-2">Format auswählen:</label>
<select id="export-format" class="w-full p-2 border rounded-lg dark:bg-gray-700 dark:border-gray-600 dark:text-white">
<option value="json">JSON</option>
<option value="xml">XML</option>
<option value="csv">CSV</option>
</select>
</div>
<div class="flex justify-end gap-2">
<button id="cancel-export" class="px-4 py-2 bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600 rounded-lg">Abbrechen</button>
<button id="confirm-export" class="px-4 py-2 bg-blue-500 hover:bg-blue-600 text-white rounded-lg">Exportieren</button>
</div>
</div>
`;
document.body.appendChild(overlay);
// Event-Listener für Export-Buttons
document.getElementById('cancel-export').addEventListener('click', function() {
document.getElementById('export-dialog-overlay').remove();
});
document.getElementById('confirm-export').addEventListener('click', function() {
const format = document.getElementById('export-format').value;
// API-Anfrage für den Export
fetch(`/api/mindmap/${mindmapId}/export?format=${format}`)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.blob();
})
.then(blob => {
// Download der Export-Datei
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `mindmap_${mindmapId}.${format}`;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.getElementById('export-dialog-overlay').remove();
showUINotification(`Mindmap wurde erfolgreich als ${format.toUpperCase()} exportiert.`, 'success');
})
.catch(error => {
console.error('Fehler beim Exportieren der Mindmap:', error);
showUINotification('Fehler beim Exportieren der Mindmap.', 'error');
document.getElementById('export-dialog-overlay').remove();
});
});
});
// Import-Funktionalität implementieren
document.getElementById('import-btn').addEventListener('click', function() {
const mindmapId = parseInt("{{ mindmap.id }}");
// Dialog für Import-Format anzeigen
const overlay = document.createElement('div');
overlay.className = 'fixed inset-0 bg-black/50 flex items-center justify-center z-50';
overlay.id = 'import-dialog-overlay';
overlay.innerHTML = `
<div class="bg-white dark:bg-gray-800 rounded-lg p-6 max-w-md w-full mx-4">
<h3 class="text-xl font-bold mb-4">Mindmap importieren</h3>
<div class="mb-4">
<label class="block mb-2">Datei auswählen:</label>
<input type="file" id="import-file" class="w-full p-2 border rounded-lg dark:bg-gray-700 dark:border-gray-600 dark:text-white"
accept=".json,.xml,.csv">
</div>
<div class="flex justify-end gap-2">
<button id="cancel-import" class="px-4 py-2 bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600 rounded-lg">Abbrechen</button>
<button id="confirm-import" class="px-4 py-2 bg-blue-500 hover:bg-blue-600 text-white rounded-lg">Importieren</button>
</div>
</div>
`;
document.body.appendChild(overlay);
// Event-Listener für Import-Buttons
document.getElementById('cancel-import').addEventListener('click', function() {
document.getElementById('import-dialog-overlay').remove();
});
document.getElementById('confirm-import').addEventListener('click', function() {
const fileInput = document.getElementById('import-file');
if (!fileInput.files || fileInput.files.length === 0) {
showUINotification('Bitte wählen Sie eine Datei aus.', 'error');
return;
}
const file = fileInput.files[0];
const formData = new FormData();
formData.append('file', file);
// API-Anfrage für den Import
fetch(`/api/mindmap/${mindmapId}/import`, {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
document.getElementById('import-dialog-overlay').remove();
showUINotification('Mindmap wurde erfolgreich importiert.', 'success');
// Seite nach kurzer Verzögerung neu laden, um die importierten Daten anzuzeigen
setTimeout(() => {
window.location.reload();
}, 1500);
} else {
showUINotification(`Fehler beim Importieren: ${data.message}`, 'error');
}
})
.catch(error => {
console.error('Fehler beim Importieren der Mindmap:', error);
showUINotification('Fehler beim Importieren der Mindmap.', 'error');
});
});
});
} else {
// Fallback, falls mindmapData null ist
if(mindmapNameH1) mindmapNameH1.textContent = "Mindmap nicht gefunden";