"Refactor database schema for user profiles and profile templates updated to remove obsolete db table 'systades (feat): systades.db) system data)"
This commit is contained in:
@@ -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;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body data-page="{{ request.endpoint }}" class="relative overflow-x-hidden dark bg-gray-900 text-white" x-data="{
|
||||
@@ -369,14 +441,9 @@
|
||||
<!-- Dark/Light Mode Schalter -->
|
||||
<button
|
||||
@click="toggleDarkMode()"
|
||||
class="theme-toggle relative w-12 h-6 rounded-full bg-gradient-to-r transition-all duration-300 flex items-center"
|
||||
:class="darkMode ? 'from-purple-700 to-indigo-800' : 'from-purple-400 to-indigo-500'"
|
||||
class="theme-toggle relative w-12 h-6 rounded-full transition-all duration-300 flex items-center overflow-hidden"
|
||||
aria-label="Dark Mode umschalten"
|
||||
>
|
||||
<span
|
||||
class="absolute w-5 h-5 rounded-full bg-white shadow-md transform transition-transform duration-300"
|
||||
:class="darkMode ? 'translate-x-7' : 'translate-x-1'"
|
||||
></span>
|
||||
<span class="sr-only" x-text="darkMode ? 'Zum Light Mode wechseln' : 'Zum Dark Mode wechseln'"></span>
|
||||
</button>
|
||||
<!-- Profil-Link oder Login -->
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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 = '<i class="fas fa-check mr-1"></i> 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 = '<i class="fas fa-circle-notch fa-spin mr-1"></i> 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 = '<i class="fas fa-circle-notch fa-spin mr-1"></i> 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 = `<i class="fas fa-map-marker-alt"></i> ${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 = `<i class="fas fa-check-circle mr-2"></i> ${message}`;
|
||||
} else if (type === 'error') {
|
||||
notification.classList.add('bg-red-500', 'text-white');
|
||||
notification.innerHTML = `<i class="fas fa-exclamation-circle mr-2"></i> ${message}`;
|
||||
} else {
|
||||
notification.classList.add('bg-blue-500', 'text-white');
|
||||
notification.innerHTML = `<i class="fas fa-info-circle mr-2"></i> ${message}`;
|
||||
}
|
||||
|
||||
// Close-Button
|
||||
const closeBtn = document.createElement('button');
|
||||
closeBtn.className = 'ml-4 text-white opacity-75 hover:opacity-100';
|
||||
closeBtn.innerHTML = '<i class="fas fa-times"></i>';
|
||||
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);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user