PageSourceSearch

https://aegnord.de/js/views/admin/profile-management.js

js aegnord.de collected 2026-10-05 05:22:05 UTC 11,331 bytes, 296 lines download raw bytes

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.