1/** 2 * Profile Management - Dashboard-Modul 3 * Profil-Verwaltung innerhalb des Admin-Dashboards 4 */ 5 6async function loadProfileManagement(container, token) { 7 const userDataStr = localStorage.getItem('aegnord_user'); 8 9 if (!userDataStr) { 10 window.router.navigate('/login'); 11 return; 12 } 13 14 const userData = JSON.parse(userDataStr); 15 16 container.innerHTML = ` 17 <div class="max-w-4xl"> 18 <h1 class="text-2xl font-bold text-gray-900 mb-2">Profil bearbeiten</h1> 19 <p class="text-gray-600 mb-6">Verwalten Sie Ihre Account-Einstellungen</p> 20 21 <div class="grid grid-cols-1 md:grid-cols-2 gap-6"> 22 <!-- User Info Card --> 23 <div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6"> 24 <h2 class="text-lg font-bold mb-4 text-gray-900">Account-Informationen</h2> 25 <div class="space-y-3"> 26 <div> 27 <span class="text-sm font-semibold text-gray-700">Benutzername:</span> 28 <p class="text-gray-900">${userData.username}</p> 29 <p class="text-xs text-gray-500 mt-1">Benutzername kann nicht geändert werden</p> 30 </div> 31 <div> 32 <span class="text-sm font-semibold text-gray-700">E-Mail:</span> 33 <p class="text-gray-900">${userData.email}</p> 34 </div> 35 <div> 36 <span class="text-sm font-semibold text-gray-700">Rollen:</span> 37 <p class="text-gray-900">${userData.roles ? userData.roles.join(', ') : 'Keine Rollen'}</p> 38 </div> 39 <div> 40 <span class="text-sm font-semibold text-gray-700">2FA Status:</span> 41 <p class="text-gray-900"> 42 ${ 43 userData['2fa_enabled'] 44 ? '<span class="text-green-600 font-semibold">Aktiviert</span>' 45 : '<span class="text-gray-500">Nicht aktiviert</span>' 46 } 47 </p> 48 </div> 49 </div> 50 </div> 51 52 <!-- Security Card --> 53 <div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6"> 54 <h2 class="text-lg font-bold mb-4 text-gray-900">Sicherheit</h2> 55 <div class="space-y-3"> 56 ${ 57 !userData['2fa_enabled'] 58 ? ` 59 <a href="/profile/2fa-setup" data-link class="block px-4 py-3 bg-green-50 hover:bg-green-100 rounded-lg transition-colors border border-green-200"> 60 <span class="font-semibold text-green-700">2FA aktivieren</span> 61 <p class="text-xs text-green-600 mt-1">Empfohlen für zusätzliche Sicherheit</p> 62 </a> 63 ` 64 : ` 65 <a href="/profile/2fa-manage" data-link class="block px-4 py-3 bg-gray-50 hover:bg-gray-100 rounded-lg transition-colors border border-gray-200"> 66 <span class="font-semibold text-gray-700">2FA verwalten</span> 67 </a> 68 ` 69 } 70 </div> 71 </div> 72 </div> 73 74 <!-- E-Mail ändern --> 75 <div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6 mt-6"> 76 <h2 class="text-lg font-bold mb-4 text-gray-900">E-Mail ändern</h2> 77 <form id="profile-email-form" class="space-y-4"> 78 <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> 79 <div> 80 <label for="profile-new-email" class="block text-sm font-semibold text-gray-700 mb-1"> 81 Neue E-Mail-Adresse 82 </label> 83 <input 84 id="profile-new-email" 85 type="email" 86 required 87 class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#2D4B9B] focus:border-transparent" 88 placeholder="${userData.email}"> 89 </div> 90 91 <div> 92 <label for="profile-email-password" class="block text-sm font-semibold text-gray-700 mb-1"> 93 Aktuelles Passwort bestätigen 94 </label> 95 <input 96 id="profile-email-password" 97 type="password" 98 required 99 class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#2D4B9B] focus:border-transparent" 100 placeholder="Ihr aktuelles Passwort"> 101 </div> 102 </div> 103
104 <div id="profile-email-error" class="hidden p-4 bg-red-50 border border-red-200 text-red-800 rounded-lg text-sm"></div> 105 <div id="profile-email-success" class="hidden p-4 bg-green-50 border border-green-200 text-green-800 rounded-lg text-sm"></div> 106 107 <button 108 type="submit" 109 class="px-6 py-2 bg-[#2D4B9B] text-white rounded-lg hover:bg-[#1a2f5a] font-semibold transition-colors"> 110 E-Mail ändern 111 </button> 112 </form> 113 </div> 114 115 <!-- Passwort ändern --> 116 <div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6 mt-6"> 117 <h2 class="text-lg font-bold mb-4 text-gray-900">Passwort ändern</h2> 118 <form id="profile-password-form" class="space-y-4"> 119 <div class="grid grid-cols-1 md:grid-cols-3 gap-4"> 120 <div> 121 <label for="profile-current-password" class="block text-sm font-semibold text-gray-700 mb-1"> 122 Aktuelles Passwort 123 </label> 124 <input 125 id="profile-current-password" 126 type="password" 127 required 128 class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#2D4B9B] focus:border-transparent" 129 placeholder="Aktuelles Passwort"> 130 </div> 131 132 <div> 133 <label for="profile-new-password" class="block text-sm font-semibold text-gray-700 mb-1"> 134 Neues Passwort 135 </label> 136 <input 137 id="profile-new-password" 138 type="password" 139 required 140 minlength="8" 141 class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#2D4B9B] focus:border-transparent" 142 placeholder="Mindestens 8 Zeichen"> 143 </div> 144 145 <div> 146 <label for="profile-new-password-confirm" class="block text-sm font-semibold text-gray-700 mb-1"> 147 Neues Passwort bestätigen 148 </label> 149 <input 150 id="profile-new-password-confirm" 151 type="password" 152 required 153 minlength="8" 154 class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#2D4B9B] focus:border-transparent" 155 placeholder="Passwort wiederholen"> 156 </div> 157 </div> 158 159 <div id="profile-password-error" class="hidden p-4 bg-red-50 border border-red-200 text-red-800 rounded-lg text-sm"></div> 160 <div id="profile-password-success" class="hidden p-4 bg-green-50 border border-green-200 text-green-800 rounded-lg text-sm"></div> 161 162 <button 163 type="submit" 164 class="px-6 py-2 bg-[#2D4B9B] text-white rounded-lg hover:bg-[#1a2f5a] font-semibold transition-colors"> 165 Passwort ändern 166 </button> 167 </form> 168 </div> 169 </div> 170 `; 171 172 // E-Mail Form Handler 173 document 174 .getElementById('profile-email-form') 175 ?.addEventListener('submit', async (e) => { 176 e.preventDefault(); 177 178 const newEmail = document.getElementById('profile-new-email').value; 179 const password = document.getElementById('profile-email-password').value; 180 const errorDiv = document.getElementById('profile-email-error'); 181 const successDiv = document.getElementById('profile-email-success'); 182 183 if (!token) { 184 window.router.navigate('/login'); 185 return; 186 } 187 188 errorDiv.classList.add('hidden'); 189 successDiv.classList.add('hidden'); 190 191 try { 192 const response = await apiFetch(`${CONFIG.API_URL}/auth/update-email`, { 193 method: 'POST', 194 headers: { 195 'Content-Type': 'application/json', 196 }, 197 body: JSON.stringify({ email: newEmail, password }), 198 }); 199 200 const data = await response.json(); 201 202 if (data.error) { 203 errorDiv.textContent = data.error; 204 errorDiv.classList.remove('hidden'); 205 return; 206 } 207 208 // Update localStorage 209 localStorage.setItem('aegnord_user', JSON.stringify(data.user)); 210 211 // Show success 212 successDiv.textContent = data.message; 213 successDiv.classList.remove('hidden'); 214 215 // Reset form 216 document.getElementById('profile-email-form').reset(); 217 218 // Reload view after 2 seconds 219 setTimeout(() => { 220 loadProfileManagement(container, token); 221 }, 2000); 222 } catch (err) { 223 errorDiv.textContent = 'Ein Fehler ist aufgetreten: ' + err.message; 224 errorDiv.classList.remove('hidden'); 225 } 226 }); 227 228 // Password Form Handler 229 document 230 .getElementById('profile-password-form') 231 ?.addEventListener('submit', async (e) => { 232 e.preventDefault(); 233 234 const currentPassword = document.getElementById('profile-current-password').value; 235 const newPassword = document.getElementById('profile-new-password').value; 236 const newPasswordConfirm = document.getElementById('profile-new-password-confirm').value; 237 const errorDiv = document.getElementById('profile-password-error'); 238 const successDiv = document.getElementById('profile-password-success'); 239 240 if (!token) { 241 window.router.navigate('/login'); 242 return; 243 } 244 245 errorDiv.classList.add('hidden'); 246 successDiv.classList.add('hidden'); 247 248 // Client-side validation 249 if (newPassword !== newPasswordConfirm) { 250 errorDiv.textContent = 'Neue Passwörter stimmen nicht überein'; 251 errorDiv.classList.remove('hidden'); 252 return; 253 } 254 255 if (newPassword.length < 8) { 256 errorDiv.textContent = 'Passwort muss mindestens 8 Zeichen lang sein'; 257 errorDiv.classList.remove('hidden'); 258 return; 259 } 260
261 try { 262 const response = await apiFetch(`${CONFIG.API_URL}/auth/update-password`, { 263 method: 'POST', 264 headers: { 265 'Content-Type': 'application/json', 266 }, 267 body: JSON.stringify({ 268 current_password: currentPassword, 269 new_password: newPassword, 270 new_password_confirm: newPasswordConfirm, 271 }), 272 }); 273 274 const data = await response.json(); 275 276 if (data.error) { 277 errorDiv.textContent = data.error; 278 errorDiv.classList.remove('hidden'); 279 return; 280 } 281 282 // Show success 283 successDiv.textContent = data.message; 284 successDiv.classList.remove('hidden'); 285 286 // Reset form 287 document.getElementById('profile-password-form').reset(); 288 } catch (err) { 289 errorDiv.textContent = 'Ein Fehler ist aufgetreten: ' + err.message; 290 errorDiv.classList.remove('hidden'); 291 } 292 }); 293} 294 295// Global verfügbar machen 296window.loadProfileManagement = loadProfileManagement;
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.