// account.js document.addEventListener('DOMContentLoaded', () => { const toast = document.getElementById('account-toast'); function showToast(msg, isError = false) { toast.textContent = msg; toast.classList.toggle('account-toast--error', isError); toast.classList.add('account-toast--visible'); setTimeout(() => toast.classList.remove('account-toast--visible'), 3000); } async function patchJson(url, data) { const res = await fetch(url, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data), }); const json = await res.json(); if (!res.ok) throw new Error(json.error ?? 'Fehler'); return json; } // ── Farbfeld: Picker ↔ Hex-Input synchron + Live-Kontrast ──────────────── const colorPicker = document.getElementById('account-color-picker'); const colorHex = document.getElementById('account-color'); function applyHeaderContrast(hex) { const r = parseInt(hex.slice(1, 3), 16); const g = parseInt(hex.slice(3, 5), 16); const b = parseInt(hex.slice(5, 7), 16); const brightness = (r * 299 + g * 587 + b * 114) / 1000; const isLight = brightness > 128; const root = document.documentElement; root.style.setProperty('--header-text', isLight ? '#1a2a3a' : '#ffffff'); root.style.setProperty('--header-text-muted', isLight ? 'rgba(26, 42, 58, 0.65)' : 'rgba(255, 255, 255, 0.75)'); root.style.setProperty('--header-overlay', isLight ? 'rgba(0, 0, 0, 0.08)' : 'rgba(255, 255, 255, 0.18)'); } if (colorPicker && colorHex) { colorPicker.addEventListener('input', () => { colorHex.value = colorPicker.value; applyHeaderContrast(colorPicker.value); }); colorHex.addEventListener('input', () => { if (/^#[0-9a-fA-F]{6}$/.test(colorHex.value)) { colorPicker.value = colorHex.value; applyHeaderContrast(colorHex.value); } }); } // ── Account-Formular ────────────────────────────────────────────────────── const btnAccountSave = document.getElementById('btn-account-save'); if (btnAccountSave) { btnAccountSave.addEventListener('click', async () => { const payload = { name: document.getElementById('account-name').value.trim(), trackingInterval: parseInt(document.getElementById('account-interval').value, 10), }; if (colorHex) { const hex = colorHex.value.trim(); if (hex && !/^#[0-9a-fA-F]{6}$/.test(hex)) { showToast('Ungültiger Hex-Wert. Beispiel: #3a7bbf', true); return; } payload.primaryColor = hex || ''; } try { await patchJson('/api/account', payload); showToast('Gespeichert. Seite wird neu geladen…'); setTimeout(() => window.location.reload(), 1200); } catch (e) { showToast(e.message, true); } }); } // ── Besitzer des Accounts ───────────────────────────────────────────────── const superadminSelect = document.getElementById('superadmin-select'); if (superadminSelect && !superadminSelect.disabled) { superadminSelect.addEventListener('change', async () => { const selectedName = superadminSelect.options[superadminSelect.selectedIndex].text; if (!confirm(`${selectedName} zum neuen Kontoinhaber machen?`)) { // Auswahl zurücksetzen superadminSelect.value = superadminSelect.dataset.original; return; } try { await patchJson('/api/account/superadmin', { userId: parseInt(superadminSelect.value, 10), }); showToast('Kontoinhaber geändert. Seite wird neu geladen…'); setTimeout(() => window.location.reload(), 1500); } catch (e) { showToast(e.message, true); superadminSelect.value = superadminSelect.dataset.original; } }); // Original-Wert merken für Rollback superadminSelect.dataset.original = superadminSelect.value; } // ── Passwort-Toggle ─────────────────────────────────────────────────────── const btnPwToggle = document.getElementById('btn-pw-toggle'); const pwSection = document.getElementById('pw-section'); if (btnPwToggle && pwSection) { btnPwToggle.addEventListener('click', (e) => { e.preventDefault(); const open = !pwSection.hidden; pwSection.hidden = open; btnPwToggle.textContent = open ? 'ändern' : 'abbrechen'; }); } // ── Theme-Picker ────────────────────────────────────────────────────────── const themePicker = document.getElementById('theme-picker'); if (themePicker) { themePicker.querySelectorAll('input[name="theme"]').forEach(radio => { radio.addEventListener('change', async () => { const theme = radio.value; try { await patchJson('/api/account/user', { theme }); // Optionen visuell aktualisieren themePicker.querySelectorAll('.theme-option').forEach(opt => { opt.classList.toggle('theme-option--active', opt.dataset.theme === theme); }); document.body.dataset.theme = theme; showToast('Darstellung geändert.'); } catch (e) { showToast(e.message, true); } }); }); } // ── Benutzer-Formular ───────────────────────────────────────────────────── const btnUserSave = document.getElementById('btn-user-save'); if (btnUserSave) { btnUserSave.addEventListener('click', async () => { const data = { firstName: document.getElementById('user-firstname').value.trim(), lastName: document.getElementById('user-lastname').value.trim(), email: document.getElementById('user-email').value.trim(), }; if (pwSection && !pwSection.hidden) { const pwNew = document.getElementById('user-pw-new').value; const pwRepeat = document.getElementById('user-pw-repeat').value; if (pwNew !== pwRepeat) { showToast('Die Passwörter stimmen nicht überein.', true); return; } data.currentPassword = document.getElementById('user-pw-current').value; data.newPassword = pwNew; } try { await patchJson('/api/account/user', data); showToast('Gespeichert.'); if (pwSection) { pwSection.hidden = true; document.getElementById('btn-pw-toggle').textContent = 'ändern'; ['user-pw-current', 'user-pw-new', 'user-pw-repeat'].forEach(id => { document.getElementById(id).value = ''; }); } } catch (e) { showToast(e.message, true); } }); } });