613 lines
16 KiB
HTML
613 lines
16 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Mindmap{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
/* Full page background */
|
|
html, body {
|
|
min-height: 100vh;
|
|
width: 100%;
|
|
margin: 0;
|
|
padding: 0;
|
|
overflow-x: hidden;
|
|
}
|
|
|
|
/* Mindmap Container */
|
|
#mindmap-container {
|
|
position: relative;
|
|
min-height: calc(100vh - 160px);
|
|
z-index: 1;
|
|
}
|
|
|
|
/* Control Panel */
|
|
.control-panel {
|
|
position: fixed;
|
|
top: 100px;
|
|
left: 20px;
|
|
z-index: 10;
|
|
transition: all 0.3s ease;
|
|
border-radius: 1rem;
|
|
overflow: hidden;
|
|
border: 1px solid;
|
|
}
|
|
|
|
.dark .control-panel {
|
|
background-color: rgba(17, 24, 39, 0.8);
|
|
border-color: rgba(109, 40, 217, 0.3);
|
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
|
|
}
|
|
|
|
.control-panel {
|
|
background-color: rgba(255, 255, 255, 0.85);
|
|
border-color: rgba(139, 92, 246, 0.2);
|
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
/* Control Panel Toggle */
|
|
.panel-toggle {
|
|
position: absolute;
|
|
top: 8px;
|
|
right: 8px;
|
|
z-index: 2;
|
|
width: 30px;
|
|
height: 30px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 50%;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.dark .panel-toggle {
|
|
background-color: rgba(109, 40, 217, 0.2);
|
|
color: rgba(255, 255, 255, 0.8);
|
|
}
|
|
|
|
.panel-toggle {
|
|
background-color: rgba(139, 92, 246, 0.1);
|
|
color: rgba(30, 41, 59, 0.8);
|
|
}
|
|
|
|
.panel-toggle:hover {
|
|
transform: rotate(180deg);
|
|
}
|
|
|
|
/* Category Tree */
|
|
.category-tree {
|
|
max-height: 70vh;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.category-item {
|
|
transition: all 0.3s ease;
|
|
border-left: 2px solid transparent;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.dark .category-item:hover {
|
|
background-color: rgba(109, 40, 217, 0.1);
|
|
border-left-color: rgba(139, 92, 246, 0.5);
|
|
}
|
|
|
|
.category-item:hover {
|
|
background-color: rgba(139, 92, 246, 0.05);
|
|
border-left-color: rgba(139, 92, 246, 0.5);
|
|
}
|
|
|
|
/* Node List */
|
|
.node-list {
|
|
max-height: 300px;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.node-item {
|
|
transition: all 0.3s ease;
|
|
border-radius: 0.5rem;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.dark .node-item {
|
|
background-color: rgba(31, 41, 55, 0.5);
|
|
border: 1px solid rgba(55, 65, 81, 0.5);
|
|
}
|
|
|
|
.node-item {
|
|
background-color: rgba(255, 255, 255, 0.5);
|
|
border: 1px solid rgba(226, 232, 240, 0.5);
|
|
}
|
|
|
|
.dark .node-item:hover {
|
|
background-color: rgba(55, 65, 81, 0.7);
|
|
border-color: rgba(139, 92, 246, 0.5);
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
|
|
}
|
|
|
|
.node-item:hover {
|
|
background-color: rgba(255, 255, 255, 0.8);
|
|
border-color: rgba(139, 92, 246, 0.3);
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.05);
|
|
}
|
|
|
|
/* Node Counter Badge */
|
|
.node-counter {
|
|
min-width: 20px;
|
|
height: 20px;
|
|
border-radius: 10px;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 0.75rem;
|
|
font-weight: 500;
|
|
padding: 0 6px;
|
|
}
|
|
|
|
.dark .node-counter {
|
|
background-color: rgba(109, 40, 217, 0.3);
|
|
color: rgba(255, 255, 255, 0.9);
|
|
}
|
|
|
|
.node-counter {
|
|
background-color: rgba(139, 92, 246, 0.1);
|
|
color: rgba(109, 40, 217, 0.9);
|
|
}
|
|
|
|
/* Canvas area */
|
|
#mindmap-canvas {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
overflow: hidden;
|
|
z-index: 1;
|
|
}
|
|
|
|
/* Tooltip */
|
|
.tooltip-container {
|
|
position: absolute;
|
|
pointer-events: none;
|
|
z-index: 1000;
|
|
max-width: 300px;
|
|
opacity: 0;
|
|
transition: opacity 0.3s ease;
|
|
}
|
|
|
|
.dark .tooltip-container {
|
|
background-color: rgba(17, 24, 39, 0.9);
|
|
border: 1px solid rgba(109, 40, 217, 0.3);
|
|
color: rgba(255, 255, 255, 0.9);
|
|
}
|
|
|
|
.tooltip-container {
|
|
background-color: rgba(255, 255, 255, 0.95);
|
|
border: 1px solid rgba(139, 92, 246, 0.2);
|
|
color: rgba(30, 41, 59, 0.9);
|
|
}
|
|
|
|
/* Search input */
|
|
.search-input {
|
|
transition: all 0.3s ease;
|
|
width: 100%;
|
|
border-radius: 0.5rem;
|
|
padding: 0.5rem 0.75rem;
|
|
outline: none;
|
|
}
|
|
|
|
.dark .search-input {
|
|
background-color: rgba(31, 41, 55, 0.7);
|
|
border: 1px solid rgba(55, 65, 81, 0.5);
|
|
color: rgba(255, 255, 255, 0.9);
|
|
}
|
|
|
|
.search-input {
|
|
background-color: rgba(255, 255, 255, 0.8);
|
|
border: 1px solid rgba(226, 232, 240, 0.8);
|
|
color: rgba(30, 41, 59, 0.9);
|
|
}
|
|
|
|
.dark .search-input:focus {
|
|
border-color: rgba(139, 92, 246, 0.5);
|
|
box-shadow: 0 0 0 2px rgba(139, 92, 246, 0.2);
|
|
}
|
|
|
|
.search-input:focus {
|
|
border-color: rgba(139, 92, 246, 0.3);
|
|
box-shadow: 0 0 0 2px rgba(139, 92, 246, 0.1);
|
|
}
|
|
|
|
/* Mode toggle */
|
|
.mode-toggle {
|
|
padding: 0.5rem 1rem;
|
|
border-radius: 0.5rem;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.dark .mode-toggle {
|
|
background-color: rgba(31, 41, 55, 0.5);
|
|
border: 1px solid rgba(55, 65, 81, 0.5);
|
|
color: rgba(255, 255, 255, 0.9);
|
|
}
|
|
|
|
.mode-toggle {
|
|
background-color: rgba(255, 255, 255, 0.7);
|
|
border: 1px solid rgba(226, 232, 240, 0.8);
|
|
color: rgba(30, 41, 59, 0.9);
|
|
}
|
|
|
|
.dark .mode-toggle.active {
|
|
background-color: rgba(109, 40, 217, 0.2);
|
|
border-color: rgba(139, 92, 246, 0.4);
|
|
color: rgba(255, 255, 255, 1);
|
|
}
|
|
|
|
.mode-toggle.active {
|
|
background-color: rgba(139, 92, 246, 0.1);
|
|
border-color: rgba(139, 92, 246, 0.3);
|
|
color: rgba(109, 40, 217, 1);
|
|
}
|
|
|
|
/* User Mindmaps */
|
|
.user-mindmap-section {
|
|
position: fixed;
|
|
bottom: 20px;
|
|
right: 20px;
|
|
z-index: 10;
|
|
transition: all 0.3s ease;
|
|
border-radius: 1rem;
|
|
overflow: hidden;
|
|
max-width: 350px;
|
|
}
|
|
|
|
.dark .user-mindmap-section {
|
|
background-color: rgba(17, 24, 39, 0.85);
|
|
border: 1px solid rgba(109, 40, 217, 0.3);
|
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
|
|
}
|
|
|
|
.user-mindmap-section {
|
|
background-color: rgba(255, 255, 255, 0.9);
|
|
border: 1px solid rgba(139, 92, 246, 0.2);
|
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
/* User Mindmap List */
|
|
.user-mindmap-item {
|
|
transition: all 0.3s ease;
|
|
border-radius: 0.5rem;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.dark .user-mindmap-item {
|
|
background-color: rgba(31, 41, 55, 0.5);
|
|
border: 1px solid rgba(55, 65, 81, 0.5);
|
|
}
|
|
|
|
.user-mindmap-item {
|
|
background-color: rgba(255, 255, 255, 0.7);
|
|
border: 1px solid rgba(226, 232, 240, 0.7);
|
|
}
|
|
|
|
.dark .user-mindmap-item:hover {
|
|
background-color: rgba(55, 65, 81, 0.7);
|
|
border-color: rgba(139, 92, 246, 0.4);
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.user-mindmap-item:hover {
|
|
background-color: rgba(255, 255, 255, 0.9);
|
|
border-color: rgba(139, 92, 246, 0.3);
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
/* Zoom Controls */
|
|
.zoom-controls {
|
|
position: fixed;
|
|
bottom: 20px;
|
|
left: 20px;
|
|
z-index: 10;
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
border-radius: 2rem;
|
|
padding: 0.5rem;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.dark .zoom-controls {
|
|
background-color: rgba(17, 24, 39, 0.7);
|
|
border: 1px solid rgba(55, 65, 81, 0.5);
|
|
}
|
|
|
|
.zoom-controls {
|
|
background-color: rgba(255, 255, 255, 0.8);
|
|
border: 1px solid rgba(226, 232, 240, 0.7);
|
|
}
|
|
|
|
.zoom-btn {
|
|
width: 36px;
|
|
height: 36px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 50%;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.dark .zoom-btn {
|
|
background-color: rgba(31, 41, 55, 0.7);
|
|
color: rgba(255, 255, 255, 0.9);
|
|
}
|
|
|
|
.zoom-btn {
|
|
background-color: rgba(255, 255, 255, 0.9);
|
|
color: rgba(30, 41, 59, 0.9);
|
|
}
|
|
|
|
.dark .zoom-btn:hover {
|
|
background-color: rgba(139, 92, 246, 0.3);
|
|
color: rgba(255, 255, 255, 1);
|
|
}
|
|
|
|
.zoom-btn:hover {
|
|
background-color: rgba(139, 92, 246, 0.1);
|
|
color: rgba(109, 40, 217, 1);
|
|
}
|
|
|
|
/* Loading spinner */
|
|
.spinner {
|
|
border: 3px solid rgba(255, 255, 255, 0.3);
|
|
border-radius: 50%;
|
|
border-top: 3px solid;
|
|
width: 24px;
|
|
height: 24px;
|
|
animation: spin 1s linear infinite;
|
|
}
|
|
|
|
.dark .spinner {
|
|
border-top-color: rgba(139, 92, 246, 0.7);
|
|
}
|
|
|
|
.spinner {
|
|
border-top-color: rgba(109, 40, 217, 0.7);
|
|
}
|
|
|
|
@keyframes spin {
|
|
0% { transform: rotate(0deg); }
|
|
100% { transform: rotate(360deg); }
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<!-- Mindmap Container -->
|
|
<div id="mindmap-container">
|
|
<!-- Main Canvas -->
|
|
<div id="mindmap-canvas"></div>
|
|
|
|
<!-- Control Panel -->
|
|
<div class="control-panel p-4 w-64" id="control-panel" x-data="{ isExpanded: true }">
|
|
<div class="panel-toggle" @click="isExpanded = !isExpanded">
|
|
<i class="fa-solid" :class="isExpanded ? 'fa-chevron-left' : 'fa-chevron-right'"></i>
|
|
</div>
|
|
|
|
<div x-show="isExpanded">
|
|
<h2 class="text-xl font-semibold mb-4 text-gray-900 dark:text-white">Wissensbereiche</h2>
|
|
|
|
<!-- Search Box -->
|
|
<div class="mb-4">
|
|
<input type="text" id="category-search" class="search-input" placeholder="Bereich suchen...">
|
|
</div>
|
|
|
|
<!-- Category Tree -->
|
|
<div class="category-tree">
|
|
<!-- Recursive template for categories -->
|
|
<script type="text/x-template" id="category-template">
|
|
<div class="category-item pl-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-700"
|
|
:class="[level > 0 ? 'ml-' + (level * 2) : '']"
|
|
@click="toggleCategory(category.id)">
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center">
|
|
<i class="fa-solid" :class="[isExpanded ? 'fa-chevron-down' : 'fa-chevron-right', 'mr-2 text-sm transition-transform']"></i>
|
|
<span :class="{'font-medium': isActive}">
|
|
<i class="fa-solid mr-2" :class="category.icon || 'fa-folder'"></i>
|
|
${category.name}
|
|
</span>
|
|
</div>
|
|
<span class="node-counter">${category.nodes.length}</span>
|
|
</div>
|
|
<!-- Nodes for this category -->
|
|
<div x-show="isExpanded && isActive" class="mt-2 node-list pl-4">
|
|
<div v-for="node in category.nodes" class="node-item p-2 mb-2"
|
|
:style="{ borderLeftColor: node.color_code }"
|
|
@click.stop="addNodeToCanvas(node)">
|
|
<div class="flex items-center justify-between">
|
|
<div>${node.name}</div>
|
|
<span class="node-counter">${node.thought_count}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Subcategories recursive -->
|
|
<div x-show="isExpanded" class="mt-2">
|
|
<template v-for="child in category.children">
|
|
<category-item :category="child" :level="level + 1"></category-item>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</script>
|
|
|
|
<!-- Root categories rendered here -->
|
|
<div id="categories-container"></div>
|
|
</div>
|
|
|
|
<!-- View Mode Toggle -->
|
|
<div class="mt-4">
|
|
<h3 class="text-sm font-medium mb-2 text-gray-700 dark:text-gray-300">Ansicht</h3>
|
|
<div class="flex justify-between gap-2">
|
|
<button id="view-all" class="mode-toggle text-sm flex-1 active">
|
|
<i class="fa-solid fa-diagram-project mr-1"></i> Alles
|
|
</button>
|
|
<button id="view-focused" class="mode-toggle text-sm flex-1">
|
|
<i class="fa-solid fa-bullseye mr-1"></i> Fokus
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- User Mindmaps Section -->
|
|
{% if current_user.is_authenticated %}
|
|
<div class="user-mindmap-section p-4 w-64" x-data="{ isExpanded: true }">
|
|
<div class="flex items-center justify-between mb-4">
|
|
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">Meine Mindmaps</h2>
|
|
<button @click="isExpanded = !isExpanded" class="text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200">
|
|
<i class="fa-solid" :class="isExpanded ? 'fa-chevron-down' : 'fa-chevron-up'"></i>
|
|
</button>
|
|
</div>
|
|
|
|
<div x-show="isExpanded">
|
|
<!-- User Mindmap List -->
|
|
<div class="space-y-2 max-h-60 overflow-y-auto mb-3">
|
|
<!-- Will be populated by JS -->
|
|
<div id="user-mindmaps-list" class="space-y-2"></div>
|
|
</div>
|
|
|
|
<!-- Add New Mindmap Button -->
|
|
<a href="{{ url_for('create_mindmap') }}" class="mystical-button mystical-button-primary w-full text-center text-sm">
|
|
<i class="fa-solid fa-plus mr-1"></i> Neue Mindmap erstellen
|
|
</a>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Zoom Controls -->
|
|
<div class="zoom-controls">
|
|
<button id="zoom-in" class="zoom-btn">
|
|
<i class="fa-solid fa-plus"></i>
|
|
</button>
|
|
<button id="zoom-out" class="zoom-btn">
|
|
<i class="fa-solid fa-minus"></i>
|
|
</button>
|
|
<button id="reset-view" class="zoom-btn">
|
|
<i class="fa-solid fa-home"></i>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Node Tooltip -->
|
|
<div id="node-tooltip" class="tooltip-container rounded-lg p-4 shadow-lg"></div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<!-- D3.js for Mindmap Visualization -->
|
|
<script src="https://d3js.org/d3.v7.min.js"></script>
|
|
|
|
<!-- Custom D3 Extensions -->
|
|
<script src="{{ url_for('static', filename='d3-extensions.js') }}"></script>
|
|
|
|
<!-- Mindmap Script -->
|
|
<script src="{{ url_for('static', filename='mindmap.js') }}"></script>
|
|
|
|
<script>
|
|
// Initialize the public mindmap
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Set up for dark/light mode changes
|
|
const isDarkMode = document.documentElement.classList.contains('dark');
|
|
|
|
// Initialize the node tooltip
|
|
const tooltip = document.getElementById('node-tooltip');
|
|
|
|
// Initialize mindmap
|
|
const mindmap = new MindMap({
|
|
container: '#mindmap-canvas',
|
|
apiEndpoint: '/api/mindmap/public',
|
|
isDarkMode: isDarkMode,
|
|
tooltip: tooltip
|
|
});
|
|
|
|
// Load public mindmap data
|
|
mindmap.loadData().then(() => {
|
|
console.log('Mindmap data loaded');
|
|
});
|
|
|
|
// View mode toggle
|
|
document.getElementById('view-all').addEventListener('click', function() {
|
|
this.classList.add('active');
|
|
document.getElementById('view-focused').classList.remove('active');
|
|
mindmap.setViewMode('all');
|
|
});
|
|
|
|
document.getElementById('view-focused').addEventListener('click', function() {
|
|
this.classList.add('active');
|
|
document.getElementById('view-all').classList.remove('active');
|
|
mindmap.setViewMode('focus');
|
|
});
|
|
|
|
// Zoom controls
|
|
document.getElementById('zoom-in').addEventListener('click', function() {
|
|
mindmap.zoomIn();
|
|
});
|
|
|
|
document.getElementById('zoom-out').addEventListener('click', function() {
|
|
mindmap.zoomOut();
|
|
});
|
|
|
|
document.getElementById('reset-view').addEventListener('click', function() {
|
|
mindmap.resetView();
|
|
});
|
|
|
|
// Handle dark mode toggle
|
|
document.addEventListener('darkModeToggled', function(event) {
|
|
const isDark = event.detail.isDark;
|
|
mindmap.updateTheme(isDark);
|
|
});
|
|
|
|
// Search functionality
|
|
const searchInput = document.getElementById('category-search');
|
|
searchInput.addEventListener('input', function() {
|
|
const searchTerm = this.value.toLowerCase();
|
|
mindmap.searchCategories(searchTerm);
|
|
});
|
|
|
|
{% if current_user.is_authenticated %}
|
|
// Load user mindmaps
|
|
fetch('/api/mindmap/user')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
const mindmapsList = document.getElementById('user-mindmaps-list');
|
|
mindmapsList.innerHTML = '';
|
|
|
|
if (data.length === 0) {
|
|
mindmapsList.innerHTML = `
|
|
<div class="text-center text-gray-500 dark:text-gray-400 py-2">
|
|
Keine Mindmaps gefunden
|
|
</div>
|
|
`;
|
|
return;
|
|
}
|
|
|
|
data.forEach(mindmap => {
|
|
const item = document.createElement('div');
|
|
item.className = 'user-mindmap-item p-3';
|
|
item.innerHTML = `
|
|
<div class="font-medium text-gray-800 dark:text-gray-200">${mindmap.name}</div>
|
|
<div class="text-xs text-gray-500 dark:text-gray-400 mb-2">${mindmap.description}</div>
|
|
<a href="/my-mindmap/${mindmap.id}" class="text-purple-600 dark:text-purple-400 text-xs hover:underline">
|
|
<i class="fa-solid fa-arrow-right mr-1"></i> Öffnen
|
|
</a>
|
|
`;
|
|
mindmapsList.appendChild(item);
|
|
});
|
|
})
|
|
.catch(error => {
|
|
console.error('Error loading user mindmaps:', error);
|
|
});
|
|
{% endif %}
|
|
});
|
|
</script>
|
|
{% endblock %} |