Remove deprecated files and templates: Delete unused files including deployment scripts, environment configurations, and various HTML templates to streamline the project structure. This cleanup enhances maintainability and reduces clutter in the codebase.
This commit is contained in:
719
static/js/modules/mindmap-page.js
Normal file
719
static/js/modules/mindmap-page.js
Normal file
@@ -0,0 +1,719 @@
|
||||
/**
|
||||
* Mindmap-Seite JavaScript
|
||||
* Spezifische Funktionen für die Mindmap-Seite
|
||||
*/
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Registriere den Initialisierer im MindMap-Objekt
|
||||
if (window.MindMap) {
|
||||
window.MindMap.pageInitializers.mindmap = initMindmapPage;
|
||||
}
|
||||
|
||||
// Prüfe, ob wir auf der Mindmap-Seite sind und initialisiere
|
||||
if (document.body.dataset.page === 'mindmap') {
|
||||
initMindmapPage();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Initialisiert die Mindmap-Seite
|
||||
*/
|
||||
function initMindmapPage() {
|
||||
const mindmapContainer = document.getElementById('mindmap-container');
|
||||
const thoughtsContainer = document.getElementById('thoughts-container');
|
||||
|
||||
if (!mindmapContainer) {
|
||||
console.error('Mindmap-Container nicht gefunden!');
|
||||
return;
|
||||
}
|
||||
|
||||
// Prüfe, ob D3.js geladen ist
|
||||
if (typeof d3 === 'undefined') {
|
||||
console.error('D3.js ist nicht geladen!');
|
||||
mindmapContainer.innerHTML = `
|
||||
<div class="glass-effect p-6 text-center">
|
||||
<div class="text-red-500 mb-4">
|
||||
<i class="fa-solid fa-triangle-exclamation text-4xl"></i>
|
||||
</div>
|
||||
<p class="text-xl">D3.js konnte nicht geladen werden. Bitte laden Sie die Seite neu.</p>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
// Erstelle die Mindmap-Visualisierung
|
||||
const mindmap = new MindMapVisualization('#mindmap-container', {
|
||||
height: 600,
|
||||
onNodeClick: handleNodeClick
|
||||
});
|
||||
|
||||
// Globale Referenz für die Zoom-Buttons erstellen
|
||||
window.mindmapInstance = mindmap;
|
||||
|
||||
// Lade die Mindmap-Daten
|
||||
mindmap.loadData();
|
||||
|
||||
// Suchfunktion für die Mindmap
|
||||
const searchInput = document.getElementById('mindmap-search');
|
||||
if (searchInput) {
|
||||
searchInput.addEventListener('input', function(e) {
|
||||
mindmap.filterBySearchTerm(e.target.value);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Behandelt Klicks auf Mindmap-Knoten
|
||||
*/
|
||||
async function handleNodeClick(node) {
|
||||
if (!thoughtsContainer) return;
|
||||
|
||||
// Zeige Lade-Animation
|
||||
thoughtsContainer.innerHTML = `
|
||||
<div class="flex justify-center items-center p-12">
|
||||
<div class="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-primary-400"></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
try {
|
||||
// Lade Gedanken für den ausgewählten Knoten
|
||||
const response = await fetch(`/api/nodes/${node.id}/thoughts`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const thoughts = await response.json();
|
||||
|
||||
// Gedanken anzeigen
|
||||
renderThoughts(thoughts, node.name);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Gedanken:', error);
|
||||
thoughtsContainer.innerHTML = `
|
||||
<div class="glass-effect p-6 text-center">
|
||||
<div class="text-red-500 mb-4">
|
||||
<i class="fa-solid fa-triangle-exclamation text-4xl"></i>
|
||||
</div>
|
||||
<p class="text-xl">Fehler beim Laden der Gedanken.</p>
|
||||
<p class="text-gray-300">Bitte versuchen Sie es später erneut.</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rendert die Gedanken in den Container
|
||||
*/
|
||||
function renderThoughts(thoughts, nodeName) {
|
||||
// Wenn keine Gedanken vorhanden sind
|
||||
if (thoughts.length === 0) {
|
||||
thoughtsContainer.innerHTML = `
|
||||
<div class="glass-effect p-6 text-center">
|
||||
<div class="text-blue-400 mb-4">
|
||||
<i class="fa-solid fa-info-circle text-4xl"></i>
|
||||
</div>
|
||||
<p class="text-xl">Keine Gedanken für "${nodeName}" vorhanden.</p>
|
||||
<button id="add-thought-btn" class="btn-primary mt-4">
|
||||
<i class="fa-solid fa-plus mr-2"></i> Gedanke hinzufügen
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Event-Listener für den Button
|
||||
document.getElementById('add-thought-btn').addEventListener('click', () => {
|
||||
openAddThoughtModal(nodeName);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Gedanken anzeigen
|
||||
thoughtsContainer.innerHTML = `
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h2 class="text-xl font-bold text-white">Gedanken zu "${nodeName}"</h2>
|
||||
<button id="add-thought-btn" class="btn-primary">
|
||||
<i class="fa-solid fa-plus mr-2"></i> Neuer Gedanke
|
||||
</button>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-4" id="thoughts-grid"></div>
|
||||
`;
|
||||
|
||||
// Button-Event-Listener
|
||||
document.getElementById('add-thought-btn').addEventListener('click', () => {
|
||||
openAddThoughtModal(nodeName);
|
||||
});
|
||||
|
||||
// Gedanken-Karten rendern
|
||||
const thoughtsGrid = document.getElementById('thoughts-grid');
|
||||
thoughts.forEach((thought, index) => {
|
||||
const card = createThoughtCard(thought);
|
||||
|
||||
// Animation verzögern für gestaffeltes Erscheinen
|
||||
setTimeout(() => {
|
||||
card.classList.add('opacity-100');
|
||||
card.classList.remove('opacity-0', 'translate-y-4');
|
||||
}, index * 100);
|
||||
|
||||
thoughtsGrid.appendChild(card);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt eine Gedanken-Karte
|
||||
*/
|
||||
function createThoughtCard(thought) {
|
||||
const card = document.createElement('div');
|
||||
card.className = 'card transition-all duration-300 opacity-0 translate-y-4 transform hover:shadow-lg border-l-4';
|
||||
card.style.borderLeftColor = thought.color_code || '#4080ff';
|
||||
|
||||
// Karten-Inhalt
|
||||
card.innerHTML = `
|
||||
<div class="p-4">
|
||||
<div class="flex justify-between items-start">
|
||||
<h3 class="text-lg font-bold text-white">${thought.title}</h3>
|
||||
<div class="text-sm text-gray-400">${thought.timestamp}</div>
|
||||
</div>
|
||||
<div class="prose dark:prose-invert mt-2">
|
||||
<p>${thought.content}</p>
|
||||
</div>
|
||||
${thought.keywords ? `
|
||||
<div class="flex flex-wrap gap-1 mt-3">
|
||||
${thought.keywords.split(',').map(keyword =>
|
||||
`<span class="px-2 py-1 text-xs rounded-full bg-secondary-700 text-white">${keyword.trim()}</span>`
|
||||
).join('')}
|
||||
</div>
|
||||
` : ''}
|
||||
<div class="mt-4 flex justify-between items-center">
|
||||
<div class="text-sm text-gray-400">
|
||||
<i class="fa-solid fa-user mr-1"></i> ${thought.author}
|
||||
</div>
|
||||
<div class="flex space-x-2">
|
||||
<button class="text-sm px-2 py-1 rounded hover:bg-white/10 transition-colors"
|
||||
onclick="showComments(${thought.id})">
|
||||
<i class="fa-solid fa-comments mr-1"></i> Kommentare
|
||||
</button>
|
||||
<button class="text-sm px-2 py-1 rounded hover:bg-white/10 transition-colors"
|
||||
onclick="showRelations(${thought.id})">
|
||||
<i class="fa-solid fa-diagram-project mr-1"></i> Beziehungen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
/**
|
||||
* Öffnet das Modal zum Hinzufügen eines neuen Gedankens
|
||||
*/
|
||||
function openAddThoughtModal(nodeName) {
|
||||
// Node-Information extrahieren
|
||||
let nodeId, nodeTitle;
|
||||
|
||||
if (typeof nodeName === 'string') {
|
||||
// Wenn nur ein String übergeben wurde
|
||||
nodeTitle = nodeName;
|
||||
// Versuche nodeId aus der Mindmap zu finden
|
||||
const nodeElement = d3.selectAll('.node-group').filter(d => d.name === nodeName);
|
||||
if (nodeElement.size() > 0) {
|
||||
nodeId = nodeElement.datum().id;
|
||||
}
|
||||
} else if (typeof nodeName === 'object') {
|
||||
// Wenn ein Node-Objekt übergeben wurde
|
||||
nodeId = nodeName.id;
|
||||
nodeTitle = nodeName.name;
|
||||
} else {
|
||||
console.error('Ungültiger Node-Parameter', nodeName);
|
||||
return;
|
||||
}
|
||||
|
||||
// Modal-Struktur erstellen
|
||||
const modal = document.createElement('div');
|
||||
modal.className = 'fixed inset-0 z-50 flex items-center justify-center p-4';
|
||||
modal.innerHTML = `
|
||||
<div class="absolute inset-0 bg-black/50 backdrop-blur-sm" id="modal-backdrop"></div>
|
||||
<div class="glass-effect relative rounded-lg shadow-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto z-10 transform transition-all">
|
||||
<div class="p-6">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-xl font-bold text-white flex items-center">
|
||||
<span class="w-3 h-3 rounded-full bg-primary-400 mr-2"></span>
|
||||
Neuer Gedanke zu "${nodeTitle}"
|
||||
</h3>
|
||||
<button id="close-modal-btn" class="text-gray-400 hover:text-white transition-colors">
|
||||
<i class="fa-solid fa-xmark text-xl"></i>
|
||||
</button>
|
||||
</div>
|
||||
<form id="add-thought-form" class="space-y-4">
|
||||
<input type="hidden" id="node_id" name="node_id" value="${nodeId || ''}">
|
||||
<div>
|
||||
<label for="title" class="block text-sm font-medium text-gray-300">Titel</label>
|
||||
<input type="text" id="title" name="title" required
|
||||
class="mt-1 block w-full rounded-md bg-dark-700 border border-dark-500 text-white p-2.5 focus:ring-2 focus:ring-primary-500 focus:border-transparent">
|
||||
</div>
|
||||
<div>
|
||||
<label for="content" class="block text-sm font-medium text-gray-300">Inhalt</label>
|
||||
<textarea id="content" name="content" rows="5" required
|
||||
class="mt-1 block w-full rounded-md bg-dark-700 border border-dark-500 text-white p-2.5 focus:ring-2 focus:ring-primary-500 focus:border-transparent"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label for="keywords" class="block text-sm font-medium text-gray-300">Schlüsselwörter (kommagetrennt)</label>
|
||||
<input type="text" id="keywords" name="keywords"
|
||||
class="mt-1 block w-full rounded-md bg-dark-700 border border-dark-500 text-white p-2.5 focus:ring-2 focus:ring-primary-500 focus:border-transparent">
|
||||
</div>
|
||||
<div>
|
||||
<label for="abstract" class="block text-sm font-medium text-gray-300">Zusammenfassung (optional)</label>
|
||||
<textarea id="abstract" name="abstract" rows="2"
|
||||
class="mt-1 block w-full rounded-md bg-dark-700 border border-dark-500 text-white p-2.5 focus:ring-2 focus:ring-primary-500 focus:border-transparent"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label for="color_code" class="block text-sm font-medium text-gray-300">Farbcode</label>
|
||||
<div class="flex space-x-2 mt-1">
|
||||
<input type="color" id="color_code" name="color_code" value="#4080ff"
|
||||
class="h-10 w-10 rounded bg-dark-700 border border-dark-500">
|
||||
<select id="predefined_colors"
|
||||
class="block flex-grow rounded-md bg-dark-700 border border-dark-500 text-white p-2.5">
|
||||
<option value="#4080ff">Blau</option>
|
||||
<option value="#a040ff">Lila</option>
|
||||
<option value="#40bf80">Grün</option>
|
||||
<option value="#ff4080">Rot</option>
|
||||
<option value="#ffaa00">Orange</option>
|
||||
<option value="#00ccff">Türkis</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-between pt-4">
|
||||
<div class="flex items-center">
|
||||
<div class="relative">
|
||||
<button type="button" id="open-relation-btn" class="btn-outline text-sm pl-3 pr-9">
|
||||
<i class="fa-solid fa-diagram-project mr-2"></i> Verbindung
|
||||
<i class="fa-solid fa-chevron-down absolute right-3 top-1/2 transform -translate-y-1/2"></i>
|
||||
</button>
|
||||
<div id="relation-menu" class="absolute left-0 mt-2 w-60 rounded-md shadow-lg bg-dark-800 ring-1 ring-black ring-opacity-5 z-10 hidden">
|
||||
<div class="py-1">
|
||||
<div class="px-3 py-2 text-xs font-semibold text-gray-400 border-b border-dark-600">BEZIEHUNGSTYPEN</div>
|
||||
<div class="max-h-48 overflow-y-auto">
|
||||
<button type="button" class="relation-type-btn w-full text-left px-4 py-2 text-sm text-white hover:bg-dark-600" data-type="supports">
|
||||
<i class="fa-solid fa-circle-arrow-up text-green-400 mr-2"></i> Stützt
|
||||
</button>
|
||||
<button type="button" class="relation-type-btn w-full text-left px-4 py-2 text-sm text-white hover:bg-dark-600" data-type="contradicts">
|
||||
<i class="fa-solid fa-circle-arrow-down text-red-400 mr-2"></i> Widerspricht
|
||||
</button>
|
||||
<button type="button" class="relation-type-btn w-full text-left px-4 py-2 text-sm text-white hover:bg-dark-600" data-type="builds_upon">
|
||||
<i class="fa-solid fa-arrow-right text-blue-400 mr-2"></i> Baut auf auf
|
||||
</button>
|
||||
<button type="button" class="relation-type-btn w-full text-left px-4 py-2 text-sm text-white hover:bg-dark-600" data-type="generalizes">
|
||||
<i class="fa-solid fa-arrow-up-wide-short text-purple-400 mr-2"></i> Verallgemeinert
|
||||
</button>
|
||||
<button type="button" class="relation-type-btn w-full text-left px-4 py-2 text-sm text-white hover:bg-dark-600" data-type="specifies">
|
||||
<i class="fa-solid fa-arrow-down-wide-short text-yellow-400 mr-2"></i> Spezifiziert
|
||||
</button>
|
||||
<button type="button" class="relation-type-btn w-full text-left px-4 py-2 text-sm text-white hover:bg-dark-600" data-type="inspires">
|
||||
<i class="fa-solid fa-lightbulb text-amber-400 mr-2"></i> Inspiriert
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" id="relation_type" name="relation_type" value="">
|
||||
<input type="hidden" id="relation_target" name="relation_target" value="">
|
||||
</div>
|
||||
<div class="flex space-x-3">
|
||||
<button type="button" id="cancel-btn" class="btn-outline">Abbrechen</button>
|
||||
<button type="submit" class="btn-primary">
|
||||
<i class="fa-solid fa-save mr-2"></i> Speichern
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(modal);
|
||||
|
||||
// Focus auf das erste Feld setzen
|
||||
setTimeout(() => {
|
||||
modal.querySelector('#title').focus();
|
||||
}, 100);
|
||||
|
||||
// Event-Listener hinzufügen
|
||||
modal.querySelector('#modal-backdrop').addEventListener('click', closeModal);
|
||||
modal.querySelector('#close-modal-btn').addEventListener('click', closeModal);
|
||||
modal.querySelector('#cancel-btn').addEventListener('click', closeModal);
|
||||
|
||||
// Farbauswahl-Event-Listener
|
||||
const colorInput = modal.querySelector('#color_code');
|
||||
const predefinedColors = modal.querySelector('#predefined_colors');
|
||||
|
||||
predefinedColors.addEventListener('change', function() {
|
||||
colorInput.value = this.value;
|
||||
});
|
||||
|
||||
// Beziehungsmenü-Funktionalität
|
||||
const relationBtn = modal.querySelector('#open-relation-btn');
|
||||
const relationMenu = modal.querySelector('#relation-menu');
|
||||
|
||||
relationBtn.addEventListener('click', function() {
|
||||
relationMenu.classList.toggle('hidden');
|
||||
});
|
||||
|
||||
// Klick außerhalb des Menüs schließt es
|
||||
document.addEventListener('click', function(event) {
|
||||
if (!relationBtn.contains(event.target) && !relationMenu.contains(event.target)) {
|
||||
relationMenu.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
// Beziehungstyp-Auswahl
|
||||
const relationTypeBtns = modal.querySelectorAll('.relation-type-btn');
|
||||
const relationTypeInput = modal.querySelector('#relation_type');
|
||||
|
||||
relationTypeBtns.forEach(btn => {
|
||||
btn.addEventListener('click', function() {
|
||||
const relationType = this.dataset.type;
|
||||
relationTypeInput.value = relationType;
|
||||
|
||||
// Sichtbare Anzeige aktualisieren
|
||||
relationBtn.innerHTML = `
|
||||
<i class="fa-solid fa-diagram-project mr-2"></i>
|
||||
${this.innerText.trim()}
|
||||
<i class="fa-solid fa-chevron-down absolute right-3 top-1/2 transform -translate-y-1/2"></i>
|
||||
`;
|
||||
|
||||
// Menü schließen
|
||||
relationMenu.classList.add('hidden');
|
||||
});
|
||||
});
|
||||
|
||||
// Form-Submit-Handler
|
||||
const form = modal.querySelector('#add-thought-form');
|
||||
form.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData(form);
|
||||
const thoughtData = {
|
||||
node_id: formData.get('node_id'),
|
||||
title: formData.get('title'),
|
||||
content: formData.get('content'),
|
||||
keywords: formData.get('keywords'),
|
||||
abstract: formData.get('abstract'),
|
||||
color_code: formData.get('color_code'),
|
||||
relation_type: formData.get('relation_type'),
|
||||
relation_target: formData.get('relation_target')
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/thoughts', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(thoughtData)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Fehler beim Speichern des Gedankens.');
|
||||
}
|
||||
|
||||
// Modal schließen
|
||||
closeModal();
|
||||
|
||||
// Gedanken neu laden
|
||||
if (nodeId) {
|
||||
handleNodeClick({ id: nodeId, name: nodeTitle });
|
||||
}
|
||||
|
||||
// Erfolgsbenachrichtigung
|
||||
if (window.MindMap && window.MindMap.showNotification) {
|
||||
MindMap.showNotification('Gedanke erfolgreich gespeichert.', 'success');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Speichern:', error);
|
||||
if (window.MindMap && window.MindMap.showNotification) {
|
||||
MindMap.showNotification('Fehler beim Speichern des Gedankens.', 'error');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Modal schließen
|
||||
function closeModal() {
|
||||
modal.classList.add('opacity-0');
|
||||
setTimeout(() => {
|
||||
modal.remove();
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Zeigt die Kommentare zu einem Gedanken an
|
||||
*/
|
||||
window.showComments = async function(thoughtId) {
|
||||
try {
|
||||
// Lade-Animation erstellen
|
||||
const modal = createModalWithLoading('Kommentare werden geladen...');
|
||||
document.body.appendChild(modal);
|
||||
|
||||
// Kommentare laden
|
||||
const response = await fetch(`/api/comments/${thoughtId}`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const comments = await response.json();
|
||||
|
||||
// Modal mit Kommentaren aktualisieren
|
||||
updateModalWithComments(modal, comments, thoughtId);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Kommentare:', error);
|
||||
MindMap.showNotification('Fehler beim Laden der Kommentare.', 'error');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Zeigt die Beziehungen eines Gedankens an
|
||||
*/
|
||||
window.showRelations = async function(thoughtId) {
|
||||
try {
|
||||
// Lade-Animation erstellen
|
||||
const modal = createModalWithLoading('Beziehungen werden geladen...');
|
||||
document.body.appendChild(modal);
|
||||
|
||||
// Beziehungen laden
|
||||
const response = await fetch(`/api/thoughts/${thoughtId}/relations`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const relations = await response.json();
|
||||
|
||||
// Modal mit Beziehungen aktualisieren
|
||||
updateModalWithRelations(modal, relations, thoughtId);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Beziehungen:', error);
|
||||
MindMap.showNotification('Fehler beim Laden der Beziehungen.', 'error');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Erstellt ein Modal mit Lade-Animation
|
||||
*/
|
||||
function createModalWithLoading(loadingText) {
|
||||
const modal = document.createElement('div');
|
||||
modal.className = 'fixed inset-0 z-50 flex items-center justify-center p-4';
|
||||
modal.innerHTML = `
|
||||
<div class="absolute inset-0 bg-black/50" id="modal-backdrop"></div>
|
||||
<div class="glass-effect relative rounded-lg shadow-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto z-10">
|
||||
<div class="p-6 text-center">
|
||||
<div class="flex justify-center mb-4">
|
||||
<div class="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-primary-400"></div>
|
||||
</div>
|
||||
<p class="text-lg text-white">${loadingText}</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Event-Listener zum Schließen
|
||||
modal.querySelector('#modal-backdrop').addEventListener('click', () => {
|
||||
modal.remove();
|
||||
});
|
||||
|
||||
return modal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktualisiert das Modal mit Kommentaren
|
||||
*/
|
||||
function updateModalWithComments(modal, comments, thoughtId) {
|
||||
const modalContent = modal.querySelector('.glass-effect');
|
||||
|
||||
modalContent.innerHTML = `
|
||||
<div class="p-6">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-xl font-bold text-white">Kommentare</h3>
|
||||
<button id="close-modal-btn" class="text-gray-400 hover:text-white">
|
||||
<i class="fa-solid fa-xmark text-xl"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="comments-list mb-6 space-y-4">
|
||||
${comments.length === 0 ?
|
||||
'<div class="text-center text-gray-400 py-4">Keine Kommentare vorhanden.</div>' :
|
||||
comments.map(comment => `
|
||||
<div class="glass-effect p-3 rounded">
|
||||
<div class="flex justify-between items-start">
|
||||
<div class="font-medium text-white">${comment.author}</div>
|
||||
<div class="text-xs text-gray-400">${comment.timestamp}</div>
|
||||
</div>
|
||||
<p class="mt-2 text-gray-200">${comment.content}</p>
|
||||
</div>
|
||||
`).join('')
|
||||
}
|
||||
</div>
|
||||
|
||||
<form id="comment-form" class="space-y-3">
|
||||
<div>
|
||||
<label for="comment-content" class="block text-sm font-medium text-gray-300">Neuer Kommentar</label>
|
||||
<textarea id="comment-content" name="content" rows="3" required
|
||||
class="mt-1 block w-full rounded-md bg-dark-700 border-dark-500 text-white"></textarea>
|
||||
</div>
|
||||
<div class="flex justify-end pt-2">
|
||||
<button type="submit" class="btn-primary">
|
||||
<i class="fa-solid fa-paper-plane mr-2"></i> Senden
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Event-Listener hinzufügen
|
||||
modalContent.querySelector('#close-modal-btn').addEventListener('click', () => {
|
||||
modal.remove();
|
||||
});
|
||||
|
||||
// Kommentar-Formular
|
||||
const form = modalContent.querySelector('#comment-form');
|
||||
form.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const content = form.elements.content.value;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/comments', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
thought_id: thoughtId,
|
||||
content: content
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Fehler beim Speichern des Kommentars.');
|
||||
}
|
||||
|
||||
// Modal schließen
|
||||
modal.remove();
|
||||
|
||||
// Erfolgsbenachrichtigung
|
||||
MindMap.showNotification('Kommentar erfolgreich gespeichert.', 'success');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Speichern des Kommentars:', error);
|
||||
MindMap.showNotification('Fehler beim Speichern des Kommentars.', 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktualisiert das Modal mit Beziehungen
|
||||
*/
|
||||
function updateModalWithRelations(modal, relations, thoughtId) {
|
||||
const modalContent = modal.querySelector('.glass-effect');
|
||||
|
||||
modalContent.innerHTML = `
|
||||
<div class="p-6">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h3 class="text-xl font-bold text-white">Beziehungen</h3>
|
||||
<button id="close-modal-btn" class="text-gray-400 hover:text-white">
|
||||
<i class="fa-solid fa-xmark text-xl"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="relations-list mb-6 space-y-4">
|
||||
${relations.length === 0 ?
|
||||
'<div class="text-center text-gray-400 py-4">Keine Beziehungen vorhanden.</div>' :
|
||||
relations.map(relation => `
|
||||
<div class="glass-effect p-3 rounded">
|
||||
<div class="flex items-center">
|
||||
<span class="inline-block px-2 py-1 rounded-full text-xs font-medium bg-primary-600 text-white">
|
||||
${relation.relation_type}
|
||||
</span>
|
||||
<div class="ml-3">
|
||||
<div class="text-white">Ziel: Gedanke #${relation.target_id}</div>
|
||||
<div class="text-xs text-gray-400">Erstellt von ${relation.created_by} am ${relation.created_at}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`).join('')
|
||||
}
|
||||
</div>
|
||||
|
||||
<form id="relation-form" class="space-y-3">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label for="target_id" class="block text-sm font-medium text-gray-300">Ziel-Gedanke ID</label>
|
||||
<input type="number" id="target_id" name="target_id" required
|
||||
class="mt-1 block w-full rounded-md bg-dark-700 border-dark-500 text-white">
|
||||
</div>
|
||||
<div>
|
||||
<label for="relation_type" class="block text-sm font-medium text-gray-300">Beziehungstyp</label>
|
||||
<select id="relation_type" name="relation_type" required
|
||||
class="mt-1 block w-full rounded-md bg-dark-700 border-dark-500 text-white">
|
||||
<option value="SUPPORTS">Stützt</option>
|
||||
<option value="CONTRADICTS">Widerspricht</option>
|
||||
<option value="BUILDS_UPON">Baut auf auf</option>
|
||||
<option value="GENERALIZES">Verallgemeinert</option>
|
||||
<option value="SPECIFIES">Spezifiziert</option>
|
||||
<option value="INSPIRES">Inspiriert</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-end pt-2">
|
||||
<button type="submit" class="btn-primary">
|
||||
<i class="fa-solid fa-plus mr-2"></i> Beziehung erstellen
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Event-Listener hinzufügen
|
||||
modalContent.querySelector('#close-modal-btn').addEventListener('click', () => {
|
||||
modal.remove();
|
||||
});
|
||||
|
||||
// Beziehungs-Formular
|
||||
const form = modalContent.querySelector('#relation-form');
|
||||
form.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = {
|
||||
source_id: thoughtId,
|
||||
target_id: parseInt(form.elements.target_id.value),
|
||||
relation_type: form.elements.relation_type.value
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/relations', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(formData)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Fehler beim Erstellen der Beziehung.');
|
||||
}
|
||||
|
||||
// Modal schließen
|
||||
modal.remove();
|
||||
|
||||
// Erfolgsbenachrichtigung
|
||||
MindMap.showNotification('Beziehung erfolgreich erstellt.', 'success');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Erstellen der Beziehung:', error);
|
||||
MindMap.showNotification('Fehler beim Erstellen der Beziehung.', 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user