diff --git a/systades.db b/systades.db deleted file mode 100644 index e69de29..0000000 diff --git a/templates/base.html b/templates/base.html index 2bb3639..7bfc29d 100644 --- a/templates/base.html +++ b/templates/base.html @@ -241,6 +241,78 @@ background: linear-gradient(135deg, #8b5cf6, #6366f1); box-shadow: 0 4px 12px rgba(79, 70, 229, 0.3); } + + /* Style improvements for the theme toggle button */ + .theme-toggle { + position: relative; + width: 48px; + height: 24px; + border-radius: 24px; + padding: 2px; + cursor: pointer; + transition: all 0.3s ease; + overflow: hidden; + } + + body.dark .theme-toggle { + background: linear-gradient(to right, #7c3aed, #3b82f6); + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3), 0 0 10px rgba(124, 58, 237, 0.3); + border: 1px solid rgba(255, 255, 255, 0.1); + } + + body:not(.dark) .theme-toggle { + background: linear-gradient(to right, #8b5cf6, #60a5fa); + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1), 0 0 10px rgba(124, 58, 237, 0.15); + border: 1px solid rgba(0, 0, 0, 0.05); + } + + .theme-toggle::after { + content: ''; + position: absolute; + width: 20px; + height: 20px; + border-radius: 50%; + top: 2px; + transition: all 0.3s ease; + z-index: 2; + } + + body.dark .theme-toggle::after { + background: #f1f5f9 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%237c3aed' width='14' height='14'%3E%3Cpath d='M21.752 15.002A9.718 9.718 0 0118 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 003 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 009.002-5.998z'%3E%3C/path%3E%3C/svg%3E") no-repeat center center; + transform: translateX(24px); + box-shadow: 0 0 5px rgba(0, 0, 0, 0.2); + } + + body:not(.dark) .theme-toggle::after { + background: #fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23f59e0b' width='14' height='14'%3E%3Cpath d='M12 2.25a.75.75 0 01.75.75v2.25a.75.75 0 01-1.5 0V3a.75.75 0 01.75-.75zM7.5 12a4.5 4.5 0 119 0 4.5 4.5 0 01-9 0zM18.894 6.166a.75.75 0 00-1.06-1.06l-1.591 1.59a.75.75 0 101.06 1.061l1.591-1.59zM21.75 12a.75.75 0 01-.75.75h-2.25a.75.75 0 010-1.5H21a.75.75 0 01.75.75zM17.834 18.894a.75.75 0 001.06-1.06l-1.59-1.591a.75.75 0 10-1.061 1.06l1.59 1.591zM12 18a.75.75 0 01.75.75V21a.75.75 0 01-1.5 0v-2.25A.75.75 0 0112 18zM7.758 17.303a.75.75 0 00-1.061-1.06l-1.591 1.59a.75.75 0 001.06 1.061l1.591-1.59zM6 12a.75.75 0 01-.75.75H3a.75.75 0 010-1.5h2.25A.75.75 0 016 12zM6.697 7.757a.75.75 0 001.06-1.06l-1.59-1.591a.75.75 0 00-1.061 1.06l1.59 1.591z'%3E%3C/path%3E%3C/svg%3E") no-repeat center center; + transform: translateX(2px); + box-shadow: 0 0 8px rgba(124, 58, 237, 0.2); + } + + .theme-toggle:hover::after { + box-shadow: 0 0 12px rgba(124, 58, 237, 0.4); + } + + /* Fixes for light mode button text colors */ + body:not(.dark) .btn-primary { + color: white !important; + } + + /* Fix for KI-Chat container */ + #chatgpt-assistant { + position: fixed; + bottom: 1.5rem; + right: 1.5rem; + z-index: 100; + } + + .chat-assistant { + max-height: 80vh !important; + } + + .chat-assistant .chat-messages { + max-height: calc(80vh - 160px) !important; + }
- @@ -703,6 +770,11 @@ if (appEl && appEl.__x) { appEl.__x.$data.darkMode = isDarkMode; } + + // Event für andere Komponenten auslösen + document.dispatchEvent(new CustomEvent('darkModeToggled', { + detail: { isDark: isDarkMode } + })); } window.MindMap.toggleDarkMode = function() { @@ -712,27 +784,12 @@ // DOM aktualisieren applyDarkModeClasses(newIsDark); - // Alpine.js Status aktualisieren (falls verfügbar) - const appEl = document.querySelector('body'); - if (appEl && appEl.__x) { - appEl.__x.$data.darkMode = newIsDark; - } - // Server aktualisieren fetch('/api/set_dark_mode', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ darkMode: newIsDark }) }) - .then(response => response.json()) - .then(data => { - if (data.success) { - // Event für andere Komponenten auslösen - document.dispatchEvent(new CustomEvent('darkModeToggled', { - detail: { isDark: newIsDark } - })); - } - }) .catch(console.error); }; @@ -754,8 +811,10 @@ fetch('/api/get_dark_mode') .then(response => response.json()) .then(data => { - const serverDarkMode = data.darkMode === true || data.darkMode === 'true'; - applyDarkModeClasses(serverDarkMode); + if (data.success) { + const serverDarkMode = data.darkMode === true || data.darkMode === 'true'; + applyDarkModeClasses(serverDarkMode); + } }) .catch(error => console.error('Fehler beim Abrufen des Dark Mode Status:', error)); diff --git a/templates/profile.html b/templates/profile.html index 83cfada..5d4ea51 100644 --- a/templates/profile.html +++ b/templates/profile.html @@ -985,52 +985,173 @@ }); } - // Einstellungen-Formular-Handling - const settingsForm = document.querySelector('.settings-card form'); - const saveSettingsBtn = document.querySelector('.settings-card .profile-action-btn.primary'); - - if (saveSettingsBtn && !settingsForm) { - saveSettingsBtn.addEventListener('click', function() { - // Sammle Daten aus den Eingabefeldern - const formData = new FormData(); - formData.append('action', 'update_profile'); - formData.append('bio', document.getElementById('bio').value); - formData.append('location', document.getElementById('location').value); - formData.append('website', document.getElementById('website').value || ''); + // Avatar-Bearbeitung + const avatarEditBtn = document.querySelector('.avatar-edit'); + if (avatarEditBtn) { + avatarEditBtn.addEventListener('click', function() { + // Dateiauwahl öffnen + const fileInput = document.createElement('input'); + fileInput.type = 'file'; + fileInput.accept = 'image/*'; + fileInput.style.display = 'none'; + document.body.appendChild(fileInput); - // AJAX-Anfrage senden - fetch('{{ url_for("settings") }}', { - method: 'POST', - body: formData, - credentials: 'same-origin' - }) - .then(response => response.json()) - .catch(error => { - console.error('Fehler beim Speichern der Profileinstellungen:', error); - }) - .then(data => { - // Erfolgsanimation - const originalText = this.innerHTML; - this.innerHTML = ' Gespeichert'; + fileInput.click(); + + fileInput.addEventListener('change', function() { + if (this.files && this.files[0]) { + // Anzeigen des gewählten Bildes + const avatarImg = document.querySelector('.avatar'); + + // FileReader zum Einlesen des Bildes + const reader = new FileReader(); + reader.onload = function(e) { + // Vorschau anzeigen + avatarImg.src = e.target.result; + + // Avatar-URL im Einstellungsbereich speichern + const avatarUrlInput = document.createElement('input'); + avatarUrlInput.type = 'hidden'; + avatarUrlInput.name = 'avatar_url'; + avatarUrlInput.id = 'avatar_url'; + avatarUrlInput.value = e.target.result; + + // Entferne vorhandenes Input, falls vorhanden + const existingInput = document.getElementById('avatar_url'); + if (existingInput) { + existingInput.remove(); + } + + // Zum Formular hinzufügen + const settingsForm = document.querySelector('.settings-card'); + if (settingsForm) { + settingsForm.appendChild(avatarUrlInput); + } + + // Erfolgsmeldung anzeigen + showNotification('Avatar wurde aktualisiert! Bitte speichere deine Änderungen.', 'success'); + }; + + reader.readAsDataURL(this.files[0]); + } - setTimeout(() => { - this.innerHTML = originalText; - }, 2000); + // Input entfernen + document.body.removeChild(fileInput); }); }); } - // Mindmap-Karten mit Hover-Effekten - const mindmapItems = document.querySelectorAll('.mindmap-item'); - mindmapItems.forEach(item => { - item.addEventListener('mouseenter', () => { - item.style.transform = 'translateY(-5px)'; - item.style.boxShadow = '0 12px 30px rgba(0, 0, 0, 0.15)'; - }); - - item.addEventListener('mouseleave', () => { - item.style.transform = 'translateY(0)'; - item.style.boxShadow = 'none'; + // Einstellungen-Formular-Handling + const saveSettingsBtn = document.querySelectorAll('.settings-card .profile-action-btn.primary'); + + saveSettingsBtn.forEach(btn => { + btn.addEventListener('click', function() { + const isPasswordUpdate = this.textContent.includes('Passwort'); + + // Passwort-Update + if (isPasswordUpdate) { + const currentPassword = document.getElementById('password').value; + const newPassword = document.getElementById('password_confirm').value; + + if (!currentPassword || !newPassword) { + showNotification('Bitte fülle alle Passwortfelder aus', 'error'); + return; + } + + // AJAX-Anfrage senden + const formData = new FormData(); + formData.append('action', 'update_password'); + formData.append('current_password', currentPassword); + formData.append('new_password', newPassword); + formData.append('confirm_password', newPassword); + + // Visuelle Rückmeldung + const originalText = this.innerHTML; + this.innerHTML = ' Speichern...'; + this.disabled = true; + + fetch('{{ url_for("settings") }}', { + method: 'POST', + body: formData + }) + .then(response => response.json()) + .catch(error => { + console.error('Fehler beim Aktualisieren des Passworts:', error); + return { success: false, message: 'Netzwerkfehler. Bitte versuche es erneut.' }; + }) + .then(data => { + this.innerHTML = originalText; + this.disabled = false; + + if (data && data.success) { + showNotification('Passwort erfolgreich aktualisiert!', 'success'); + document.getElementById('password').value = ''; + document.getElementById('password_confirm').value = ''; + } else { + showNotification(data?.message || 'Fehler beim Aktualisieren des Passworts', 'error'); + } + }); + } + // Profil-Update + else { + // Sammle Daten aus den Eingabefeldern + const formData = new FormData(); + formData.append('action', 'update_profile'); + formData.append('bio', document.getElementById('bio').value || ''); + formData.append('location', document.getElementById('location').value || ''); + formData.append('website', document.getElementById('website').value || ''); + + // Avatar hinzufügen, falls vorhanden + const avatarUrlInput = document.getElementById('avatar_url'); + if (avatarUrlInput) { + formData.append('avatar_url', avatarUrlInput.value); + } + + // Visuelle Rückmeldung + const originalText = this.innerHTML; + this.innerHTML = ' Speichern...'; + this.disabled = true; + + // AJAX-Anfrage senden + fetch('{{ url_for("settings") }}', { + method: 'POST', + body: formData + }) + .then(response => { + if (!response.ok) { + throw new Error('Fehler beim Speichern'); + } + return response.json(); + }) + .catch(error => { + console.error('Fehler beim Speichern der Profileinstellungen:', error); + return { success: false, message: 'Netzwerkfehler. Bitte versuche es erneut.' }; + }) + .then(data => { + this.innerHTML = originalText; + this.disabled = false; + + if (data && data.success) { + // Erfolgsanimation + showNotification('Profil erfolgreich aktualisiert!', 'success'); + + // UI aktualisieren ohne Neuladen + const bioElement = document.querySelector('.user-bio'); + const locationElement = document.querySelector('.user-meta span:first-child'); + + if (bioElement) { + bioElement.textContent = document.getElementById('bio').value || 'Keine Bio vorhanden. Klicke auf bearbeiten, um eine hinzuzufügen.'; + } + + if (locationElement) { + const location = document.getElementById('location').value; + locationElement.innerHTML = ` ${location || 'Kein Standort angegeben'}`; + } + } else { + showNotification(data?.message || 'Fehler beim Aktualisieren des Profils', 'error'); + } + }); + } }); }); @@ -1054,6 +1175,76 @@ borderElem.style.borderLeftColor = borderElem.dataset.color; } }); + + // Mindmap-Karten mit Hover-Effekten + const mindmapItems = document.querySelectorAll('.mindmap-item'); + mindmapItems.forEach(item => { + item.addEventListener('mouseenter', () => { + item.style.transform = 'translateY(-5px)'; + item.style.boxShadow = '0 12px 30px rgba(0, 0, 0, 0.15)'; + }); + + item.addEventListener('mouseleave', () => { + item.style.transform = 'translateY(0)'; + item.style.boxShadow = 'none'; + }); + }); + + // Benachrichtigungsfunktion + function showNotification(message, type = 'info') { + // Bestehende Benachrichtigung entfernen + const existingNotification = document.getElementById('notification'); + if (existingNotification) { + existingNotification.remove(); + } + + // Neue Benachrichtigung erstellen + const notification = document.createElement('div'); + notification.id = 'notification'; + notification.className = `fixed top-4 right-4 p-4 rounded-lg shadow-lg z-50 flex items-center transform transition-all duration-300 translate-y-0 opacity-0`; + + // Typ-basierte Stile + if (type === 'success') { + notification.classList.add('bg-green-500', 'text-white'); + notification.innerHTML = ` ${message}`; + } else if (type === 'error') { + notification.classList.add('bg-red-500', 'text-white'); + notification.innerHTML = ` ${message}`; + } else { + notification.classList.add('bg-blue-500', 'text-white'); + notification.innerHTML = ` ${message}`; + } + + // Close-Button + const closeBtn = document.createElement('button'); + closeBtn.className = 'ml-4 text-white opacity-75 hover:opacity-100'; + closeBtn.innerHTML = ''; + closeBtn.addEventListener('click', () => { + notification.classList.add('opacity-0', 'translate-y-[-10px]'); + setTimeout(() => notification.remove(), 300); + }); + + notification.appendChild(closeBtn); + document.body.appendChild(notification); + + // Animation starten + setTimeout(() => { + notification.classList.remove('opacity-0'); + notification.classList.add('opacity-100'); + }, 10); + + // Automatisch ausblenden nach 5 Sekunden + setTimeout(() => { + if (document.body.contains(notification)) { + notification.classList.add('opacity-0', 'translate-y-[-10px]'); + setTimeout(() => { + if (document.body.contains(notification)) { + notification.remove(); + } + }, 300); + } + }, 5000); + } }); {% endblock %} \ No newline at end of file