// Admin JavaScript document.addEventListener('DOMContentLoaded', function() { // Benutzer-Formular const form = document.getElementById('addUserForm'); form.addEventListener('submit', async function(e) { e.preventDefault(); // Rollen aus Checkboxen sammeln const roleCheckboxes = document.querySelectorAll('input[name="roles"]:checked'); const roles = Array.from(roleCheckboxes).map(cb => cb.value); // Validierung: Mindestens eine Rolle muss ausgewählt sein if (roles.length === 0) { alert('Bitte wählen Sie mindestens eine Rolle aus.'); return; } const defaultBreakInput = document.getElementById('defaultBreakMinutes'); const defaultBreakVal = defaultBreakInput && defaultBreakInput.value !== '' ? parseInt(defaultBreakInput.value, 10) : 30; const default_break_minutes = (!isNaN(defaultBreakVal) && defaultBreakVal >= 0) ? defaultBreakVal : 30; const formData = { username: document.getElementById('username').value, password: document.getElementById('password').value, firstname: document.getElementById('firstname').value, lastname: document.getElementById('lastname').value, roles: roles, personalnummer: document.getElementById('personalnummer').value, wochenstunden: document.getElementById('wochenstunden').value, arbeitstage: document.getElementById('arbeitstage').value, urlaubstage: document.getElementById('urlaubstage').value, default_break_minutes: default_break_minutes }; try { const response = await fetch('/admin/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) }); const result = await response.json(); if (result.success) { alert('Benutzer wurde erfolgreich angelegt!'); location.reload(); } else { alert('Fehler: ' + (result.error || 'Benutzer konnte nicht angelegt werden')); } } catch (error) { console.error('Fehler:', error); alert('Fehler beim Anlegen des Benutzers'); } }); // LDAP-Konfiguration laden loadLDAPConfig(); // Optionen laden loadOptions(); // MSSQL-Konfiguration laden loadMssqlConfig(); // Timesheet-Duplikate Button const loadTimesheetDuplicatesBtn = document.getElementById('loadTimesheetDuplicatesBtn'); if (loadTimesheetDuplicatesBtn) { loadTimesheetDuplicatesBtn.addEventListener('click', function() { loadTimesheetDuplicates(); }); } // Optionen-Formular const optionsForm = document.getElementById('optionsForm'); if (optionsForm) { optionsForm.addEventListener('submit', async function(e) { e.preventDefault(); const formData = { saturday_percentage: document.getElementById('saturdayPercentage').value, sunday_percentage: document.getElementById('sundayPercentage').value, checkin_root_url: document.getElementById('checkinRootUrl') ? document.getElementById('checkinRootUrl').value : null }; try { const response = await fetch('/admin/options', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) }); const result = await response.json(); if (result.success) { alert('Optionen wurden erfolgreich gespeichert!'); } else { alert('Fehler: ' + (result.error || 'Optionen konnten nicht gespeichert werden')); } } catch (error) { console.error('Fehler:', error); alert('Fehler beim Speichern der Optionen'); } }); } // LDAP-Konfigurationsformular const ldapConfigForm = document.getElementById('ldapConfigForm'); if (ldapConfigForm) { ldapConfigForm.addEventListener('submit', async function(e) { e.preventDefault(); const enabled = document.getElementById('ldapEnabled').checked; const url = document.getElementById('ldapUrl').value; const baseDn = document.getElementById('ldapBaseDn').value; // Validierung: URL und Base DN sind erforderlich wenn aktiviert if (enabled && (!url || !baseDn)) { alert('Bitte füllen Sie URL und Base DN aus, wenn LDAP aktiviert ist.'); return; } const formData = { enabled: enabled, url: url, bind_dn: document.getElementById('ldapBindDn').value, bind_password: document.getElementById('ldapBindPassword').value, base_dn: baseDn, user_search_filter: document.getElementById('ldapSearchFilter').value, username_attribute: document.getElementById('ldapUsernameAttr').value, firstname_attribute: document.getElementById('ldapFirstnameAttr').value, lastname_attribute: document.getElementById('ldapLastnameAttr').value, sync_interval: parseInt(document.getElementById('ldapSyncInterval').value) || 0 }; try { const response = await fetch('/admin/ldap/config', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) }); const result = await response.json(); if (result.success) { alert('LDAP-Konfiguration wurde erfolgreich gespeichert!'); location.reload(); } else { alert('Fehler: ' + (result.error || 'Konfiguration konnte nicht gespeichert werden')); } } catch (error) { console.error('Fehler:', error); alert('Fehler beim Speichern der Konfiguration'); } }); } // MSSQL-Konfigurationsformular const mssqlConfigForm = document.getElementById('mssqlConfigForm'); if (mssqlConfigForm) { mssqlConfigForm.addEventListener('submit', async function(e) { e.preventDefault(); const server = document.getElementById('mssqlServer').value; const database = document.getElementById('mssqlDatabase').value; const username = document.getElementById('mssqlUsername').value; const password = document.getElementById('mssqlPassword').value; if (!server || !database || !username) { alert('Bitte Server, Datenbankname und Benutzername ausfüllen.'); return; } const formData = { server, database, username, password }; try { const response = await fetch('/admin/mssql-config', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) }); const result = await response.json(); if (result.success) { alert('MSSQL-Konfiguration wurde erfolgreich gespeichert!'); // Passwort-Feld leeren nach dem Speichern document.getElementById('mssqlPassword').value = ''; } else { alert('Fehler: ' + (result.error || 'MSSQL-Konfiguration konnte nicht gespeichert werden')); } } catch (error) { console.error('Fehler:', error); alert('Fehler beim Speichern der MSSQL-Konfiguration'); } }); } // MSSQL Test-Verbindung const mssqlTestBtn = document.getElementById('mssqlTestConnectionBtn'); if (mssqlTestBtn) { mssqlTestBtn.addEventListener('click', async function() { const statusEl = document.getElementById('mssqlTestStatus'); mssqlTestBtn.disabled = true; if (statusEl) { statusEl.textContent = 'Verbindung wird getestet...'; statusEl.style.color = 'blue'; } try { const response = await fetch('/admin/mssql-test-connection', { method: 'POST', headers: { 'Content-Type': 'application/json' } }); const result = await response.json(); if (response.ok && result && result.success) { if (statusEl) { statusEl.textContent = 'Verbindung erfolgreich.'; statusEl.style.color = 'green'; } } else { const msg = (result && result.error) ? result.error : 'Testverbindung fehlgeschlagen.'; if (statusEl) { statusEl.textContent = msg; statusEl.style.color = 'red'; } alert(msg); } } catch (error) { console.error('Fehler bei MSSQL-Testverbindung:', error); if (statusEl) { statusEl.textContent = 'Fehler bei der Testverbindung.'; statusEl.style.color = 'red'; } alert('Fehler bei der Testverbindung zur MSSQL-Datenbank.'); } finally { mssqlTestBtn.disabled = false; } }); } // Sync-Button const syncNowBtn = document.getElementById('syncNowBtn'); if (syncNowBtn) { syncNowBtn.addEventListener('click', async function() { if (!confirm('Möchten Sie die LDAP-Synchronisation jetzt starten?')) { return; } const statusEl = document.getElementById('syncStatus'); syncNowBtn.disabled = true; statusEl.textContent = 'Synchronisation läuft...'; statusEl.style.color = 'blue'; try { const response = await fetch('/admin/ldap/sync', { method: 'POST', headers: { 'Content-Type': 'application/json' } }); const result = await response.json(); if (result.success) { statusEl.textContent = `Erfolgreich: ${result.synced} Benutzer synchronisiert`; statusEl.style.color = 'green'; if (result.errors && result.errors.length > 0) { alert('Synchronisation abgeschlossen mit Warnungen:\n' + result.errors.join('\n')); } else { alert(`Synchronisation erfolgreich abgeschlossen: ${result.synced} Benutzer synchronisiert`); } // Seite neu laden nach kurzer Verzögerung setTimeout(() => { location.reload(); }, 2000); } else { statusEl.textContent = 'Fehler: ' + (result.error || 'Synchronisation fehlgeschlagen'); statusEl.style.color = 'red'; alert('Fehler: ' + (result.error || 'Synchronisation fehlgeschlagen')); syncNowBtn.disabled = false; } } catch (error) { console.error('Fehler:', error); statusEl.textContent = 'Fehler bei der Synchronisation'; statusEl.style.color = 'red'; alert('Fehler bei der Synchronisation'); syncNowBtn.disabled = false; } }); } // PDF-Archiv (Timesheets) const pdfYearSelect = document.getElementById('pdfYearSelect'); const pdfUserSelect = document.getElementById('pdfUserSelect'); const pdfFilesContainer = document.getElementById('pdfFilesContainer'); const pdfRefreshBtn = document.getElementById('pdfRefreshBtn'); if (pdfYearSelect && pdfUserSelect && pdfFilesContainer) { if (pdfRefreshBtn) { pdfRefreshBtn.addEventListener('click', function() { clearPdfPreview(); loadPdfYears(); }); } pdfYearSelect.addEventListener('change', function() { clearPdfPreview(); const year = this.value; if (year) loadPdfUsers(year); }); pdfUserSelect.addEventListener('change', function() { clearPdfPreview(); const year = pdfYearSelect.value; const userId = this.value; if (year && userId) loadPdfFiles(year, userId); }); loadPdfYears(); } }); function setPdfStatus(text, color) { const el = document.getElementById('pdfArchiveStatus'); if (!el) return; el.textContent = text || ''; el.style.color = color || ''; } function clearPdfPreview() { const container = document.getElementById('pdfPreviewContainer'); const iframe = document.getElementById('pdfPreviewIframe'); if (container) container.style.display = 'none'; if (iframe) iframe.src = ''; } function formatBytes(bytes) { if (bytes === null || bytes === undefined || !Number.isFinite(Number(bytes))) return '-'; const num = Number(bytes); const units = ['B', 'KB', 'MB', 'GB', 'TB']; let i = 0; let n = num; while (n >= 1024 && i < units.length - 1) { n /= 1024; i += 1; } const fixed = i === 0 ? 0 : 1; return `${n.toFixed(fixed)} ${units[i]}`; } async function loadPdfYears() { const pdfYearSelect = document.getElementById('pdfYearSelect'); const pdfUserSelect = document.getElementById('pdfUserSelect'); const pdfFilesContainer = document.getElementById('pdfFilesContainer'); if (!pdfYearSelect || !pdfUserSelect || !pdfFilesContainer) return; setPdfStatus('Lade PDF-Jahre...', 'blue'); pdfYearSelect.innerHTML = ''; pdfUserSelect.innerHTML = ''; pdfUserSelect.disabled = true; pdfFilesContainer.innerHTML = ''; clearPdfPreview(); try { const response = await fetch('/admin/api/pdfs/years'); const result = await response.json(); if (!response.ok) { throw new Error(result && result.error ? result.error : 'Fehler beim Laden der PDF-Jahre'); } const years = Array.isArray(result.years) ? result.years : []; if (years.length === 0) { pdfYearSelect.innerHTML = ''; setPdfStatus('Keine PDF-Dateien im Archiv gefunden.', ''); return; } pdfYearSelect.innerHTML = ''; years.forEach(year => { const opt = document.createElement('option'); opt.value = year; opt.textContent = year; pdfYearSelect.appendChild(opt); }); const firstYear = years[0]; pdfYearSelect.value = firstYear; setPdfStatus('Jahre geladen. Lade User...', ''); await loadPdfUsers(firstYear); } catch (error) { console.error('Fehler beim Laden der PDF-Jahre:', error); setPdfStatus('Fehler beim Laden der PDF-Jahre', 'red'); pdfYearSelect.innerHTML = ''; pdfUserSelect.disabled = true; } } async function loadPdfUsers(year) { const pdfYearSelect = document.getElementById('pdfYearSelect'); const pdfUserSelect = document.getElementById('pdfUserSelect'); const pdfFilesContainer = document.getElementById('pdfFilesContainer'); if (!pdfYearSelect || !pdfUserSelect || !pdfFilesContainer) return; setPdfStatus(`Lade PDFs für ${year}...`, 'blue'); pdfUserSelect.disabled = true; pdfUserSelect.innerHTML = ''; pdfFilesContainer.innerHTML = ''; clearPdfPreview(); try { const response = await fetch(`/admin/api/pdfs/users?year=${encodeURIComponent(year)}`); const result = await response.json(); if (!response.ok) { throw new Error(result && result.error ? result.error : 'Fehler beim Laden der User-Ordner'); } const users = Array.isArray(result.users) ? result.users : (Array.isArray(result.userIds) ? result.userIds.map(id => ({ id, name: id })) : []); const userIds = users.map(u => u.id); if (users.length === 0) { pdfUserSelect.innerHTML = ''; pdfUserSelect.disabled = true; setPdfStatus(`Keine PDF-Dateien für das Jahr ${year} gefunden.`, ''); return; } pdfUserSelect.disabled = false; pdfUserSelect.innerHTML = ''; users.forEach(user => { const userId = user.id; const opt = document.createElement('option'); opt.value = userId; opt.textContent = user.name || userId; pdfUserSelect.appendChild(opt); }); const firstUserId = userIds[0]; pdfUserSelect.value = firstUserId; setPdfStatus('User geladen. Lade Dateien...', ''); await loadPdfFiles(year, firstUserId); } catch (error) { console.error('Fehler beim Laden der PDF-User:', error); setPdfStatus('Fehler beim Laden der PDF-User', 'red'); pdfUserSelect.innerHTML = ''; pdfUserSelect.disabled = true; } } async function loadPdfFiles(year, userId) { const pdfFilesContainer = document.getElementById('pdfFilesContainer'); if (!pdfFilesContainer) return; setPdfStatus('Lade PDF-Dateien...', 'blue'); pdfFilesContainer.innerHTML = ''; clearPdfPreview(); try { const response = await fetch(`/admin/api/pdfs/files?year=${encodeURIComponent(year)}&userId=${encodeURIComponent(userId)}`); const result = await response.json(); if (!response.ok) { throw new Error(result && result.error ? result.error : 'Fehler beim Laden der PDF-Dateien'); } const files = Array.isArray(result.files) ? result.files : []; if (files.length === 0) { pdfFilesContainer.innerHTML = '

Keine PDF-Dateien gefunden.

'; setPdfStatus(`Keine PDFs für User ${userId} (${year}).`, ''); return; } const rowsHtml = files .map(file => { const mtimeLabel = file.mtime ? new Date(file.mtime).toLocaleString('de-DE') : '-'; const sizeLabel = formatBytes(file.size); // name ist serverseitig streng gefiltert; hier daher direkt in JS-String verwenden. return ` ${file.name} ${sizeLabel} ${mtimeLabel} `; }) .join(''); pdfFilesContainer.innerHTML = ` ${rowsHtml}
Datei Größe Änderung Aktionen
`; setPdfStatus(`${files.length} PDF(s) geladen.`, 'green'); } catch (error) { console.error('Fehler beim Laden der PDF-Dateien:', error); pdfFilesContainer.innerHTML = '

Fehler beim Laden der PDF-Dateien.

'; setPdfStatus('Fehler beim Laden der PDF-Dateien', 'red'); } } function previewPdf(year, userId, name) { const iframe = document.getElementById('pdfPreviewIframe'); const container = document.getElementById('pdfPreviewContainer'); if (!iframe || !container) return; setPdfStatus(`Vorschau: ${name}`, 'blue'); container.style.display = 'block'; iframe.src = `/admin/api/pdfs/file?year=${encodeURIComponent(year)}&userId=${encodeURIComponent(userId)}&name=${encodeURIComponent(name)}&inline=true`; } function downloadPdf(year, userId, name) { const url = `/admin/api/pdfs/file?year=${encodeURIComponent(year)}&userId=${encodeURIComponent(userId)}&name=${encodeURIComponent(name)}&inline=false`; setPdfStatus('Download gestartet...', ''); const link = document.createElement('a'); link.href = url; link.target = '_blank'; link.rel = 'noopener'; link.click(); } async function loadTimesheetDuplicates() { const container = document.getElementById('timesheetDuplicatesContainer'); if (!container) return; container.innerHTML = '

Lade Timesheet-Duplikate...

'; try { const response = await fetch('/admin/api/timesheet-duplicates'); const result = await response.json(); if (!response.ok) { const msg = result && result.error ? result.error : 'Fehler beim Laden der Timesheet-Duplikate.'; container.innerHTML = `

${msg}

`; return; } const groups = Array.isArray(result.groups) ? result.groups : []; if (groups.length === 0) { container.innerHTML = '

Es wurden keine doppelten Timesheet-Einträge gefunden. Alles sauber.

'; return; } let html = ''; groups.forEach((group, index) => { const headerLabel = `${group.user_name || group.username || ('User #' + group.user_id)} – ${group.date} (Anzahl Einträge: ${group.entry_count})`; html += `
Gruppe ${index + 1}: ${headerLabel}
`; (group.entries || []).forEach(entry => { const created = entry.created_at ? new Date(entry.created_at).toLocaleString('de-DE') : '-'; const updated = entry.updated_at ? new Date(entry.updated_at).toLocaleString('de-DE') : '-'; const totalHours = entry.total_hours != null ? entry.total_hours : '-'; const status = entry.status || '-'; const breakMinutes = entry.break_minutes != null ? entry.break_minutes : 0; html += ` `; }); html += `
ID Start Ende Pause (Min) Stunden (total_hours) Status Erstellt Aktualisiert Aktionen
${entry.id} ${entry.start_time || '-'} ${entry.end_time || '-'} ${breakMinutes} ${totalHours} ${status} ${created} ${updated}
`; }); container.innerHTML = html; } catch (error) { console.error('Fehler beim Laden der Timesheet-Duplikate:', error); container.innerHTML = '

Fehler beim Laden der Timesheet-Duplikate.

'; } } async function deleteTimesheetEntry(entryId) { try { const response = await fetch(`/admin/api/timesheet-entry/${entryId}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json' } }); const result = await response.json(); if (response.ok && result && result.success) { // Liste neu laden, um den aktuellen Stand anzuzeigen loadTimesheetDuplicates(); } else { const msg = (result && result.error) ? result.error : 'Timesheet-Eintrag konnte nicht gelöscht werden.'; alert('Fehler: ' + msg); } } catch (error) { console.error('Fehler beim Löschen des Timesheet-Eintrags:', error); alert('Fehler beim Löschen des Timesheet-Eintrags.'); } } // Optionen laden und Formular ausfüllen async function loadOptions() { try { const response = await fetch('/admin/options'); const result = await response.json(); if (result.config) { const config = result.config; if (document.getElementById('saturdayPercentage')) { document.getElementById('saturdayPercentage').value = config.saturday_percentage || 0; } if (document.getElementById('sundayPercentage')) { document.getElementById('sundayPercentage').value = config.sunday_percentage || 0; } if (document.getElementById('checkinRootUrl')) { document.getElementById('checkinRootUrl').value = config.checkin_root_url || ''; } } } catch (error) { console.error('Fehler beim Laden der Optionen:', error); } } // LDAP-Konfiguration laden und Formular ausfüllen async function loadLDAPConfig() { try { const response = await fetch('/admin/ldap/config'); const result = await response.json(); if (result.config) { const config = result.config; if (document.getElementById('ldapEnabled')) { document.getElementById('ldapEnabled').checked = config.enabled === 1; } if (document.getElementById('ldapUrl')) { document.getElementById('ldapUrl').value = config.url || ''; } if (document.getElementById('ldapBaseDn')) { document.getElementById('ldapBaseDn').value = config.base_dn || ''; } if (document.getElementById('ldapBindDn')) { document.getElementById('ldapBindDn').value = config.bind_dn || ''; } if (document.getElementById('ldapSearchFilter')) { document.getElementById('ldapSearchFilter').value = config.user_search_filter || '(objectClass=person)'; } if (document.getElementById('ldapUsernameAttr')) { document.getElementById('ldapUsernameAttr').value = config.username_attribute || 'sAMAccountName'; } if (document.getElementById('ldapFirstnameAttr')) { document.getElementById('ldapFirstnameAttr').value = config.firstname_attribute || 'givenName'; } if (document.getElementById('ldapLastnameAttr')) { document.getElementById('ldapLastnameAttr').value = config.lastname_attribute || 'sn'; } if (document.getElementById('ldapSyncInterval')) { document.getElementById('ldapSyncInterval').value = config.sync_interval || 0; } } } catch (error) { console.error('Fehler beim Laden der LDAP-Konfiguration:', error); } } // MSSQL-Konfiguration laden und Formular ausfüllen async function loadMssqlConfig() { try { const response = await fetch('/admin/mssql-config'); const result = await response.json(); if (result.config) { const config = result.config; if (document.getElementById('mssqlServer')) { document.getElementById('mssqlServer').value = config.server || ''; } if (document.getElementById('mssqlDatabase')) { document.getElementById('mssqlDatabase').value = config.database || ''; } if (document.getElementById('mssqlUsername')) { document.getElementById('mssqlUsername').value = config.username || ''; } // Passwort wird aus Sicherheitsgründen nie vorausgefüllt } } catch (error) { console.error('Fehler beim Laden der MSSQL-Konfiguration:', error); } } async function deleteUser(userId, username) { const confirmed = confirm(`Möchten Sie den Benutzer "${username}" wirklich löschen?`); if (!confirmed) return; try { const response = await fetch(`/admin/users/${userId}`, { method: 'DELETE' }); const result = await response.json(); if (result.success) { alert('Benutzer wurde erfolgreich gelöscht!'); location.reload(); } else { alert('Fehler: ' + (result.error || 'Benutzer konnte nicht gelöscht werden')); } } catch (error) { console.error('Fehler:', error); alert('Fehler beim Löschen des Benutzers'); } } // User bearbeiten function editUser(userId) { const row = document.querySelector(`tr[data-user-id="${userId}"]`); if (!row) return; // Alle Display-Felder ausblenden und Edit-Felder einblenden row.querySelectorAll('.user-field-display').forEach(display => { display.style.display = 'none'; }); row.querySelectorAll('.user-field-edit').forEach(edit => { edit.style.display = 'inline-block'; }); // Buttons umschalten row.querySelector('.edit-user-btn').style.display = 'none'; row.querySelector('.save-user-btn').style.display = 'inline-block'; row.querySelector('.cancel-user-btn').style.display = 'inline-block'; } // User speichern async function saveUser(userId) { const row = document.querySelector(`tr[data-user-id="${userId}"]`); if (!row) return; const personalnummer = row.querySelector('input[data-field="personalnummer"]').value; const wochenstunden = row.querySelector('input[data-field="wochenstunden"]').value; const arbeitstage = row.querySelector('input[data-field="arbeitstage"]').value; const urlaubstage = row.querySelector('input[data-field="urlaubstage"]').value; const defaultBreakInput = row.querySelector('input[data-field="default_break_minutes"]'); const default_break_minutes = defaultBreakInput && defaultBreakInput.value !== '' ? parseInt(defaultBreakInput.value, 10) : 30; const normalizedDefaultBreak = (!isNaN(default_break_minutes) && default_break_minutes >= 0) ? default_break_minutes : 30; // Rollen aus Checkboxen sammeln const roleCheckboxes = row.querySelectorAll('.role-checkbox:checked'); const roles = Array.from(roleCheckboxes).map(cb => cb.value); // Validierung: Mindestens eine Rolle erforderlich if (roles.length === 0) { alert('Mindestens eine Rolle muss ausgewählt sein.'); return; } try { const response = await fetch(`/admin/users/${userId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ personalnummer: personalnummer || null, wochenstunden: wochenstunden || null, arbeitstage: arbeitstage || 5, urlaubstage: urlaubstage || null, default_break_minutes: normalizedDefaultBreak, roles: roles }) }); const result = await response.json(); if (result.success) { // Werte in Display-Felder übernehmen row.querySelector('span[data-field="personalnummer"]').textContent = personalnummer || '-'; row.querySelector('span[data-field="wochenstunden"]').textContent = wochenstunden || '-'; row.querySelector('span[data-field="urlaubstage"]').textContent = urlaubstage || '-'; const defaultBreakDisplay = row.querySelector('span[data-field="default_break_minutes"]'); if (defaultBreakDisplay) defaultBreakDisplay.textContent = normalizedDefaultBreak; // Rollen-Display aktualisieren const rolesDisplay = row.querySelector('div[data-field="roles"]'); if (rolesDisplay) { const roleLabels = { 'mitarbeiter': 'Mitarbeiter', 'verwaltung': 'Verwaltung', 'admin': 'Admin' }; rolesDisplay.innerHTML = roles.map(role => `${roleLabels[role] || role}` ).join(''); } // Bearbeitung beenden cancelEditUser(userId); alert('Benutzerdaten wurden erfolgreich gespeichert!'); // Seite neu laden um sicherzustellen dass alles korrekt ist location.reload(); } else { alert('Fehler: ' + (result.error || 'Daten konnten nicht gespeichert werden')); } } catch (error) { console.error('Fehler:', error); alert('Fehler beim Speichern der Benutzerdaten'); } } // Bearbeitung abbrechen function cancelEditUser(userId) { const row = document.querySelector(`tr[data-user-id="${userId}"]`); if (!row) return; // Alle Edit-Felder ausblenden und Display-Felder einblenden row.querySelectorAll('.user-field-edit').forEach(edit => { edit.style.display = 'none'; // Wert zurücksetzen (nur für Input-Felder, nicht für Rollen) const field = edit.dataset.field; if (field !== 'roles') { const display = row.querySelector(`span[data-field="${field}"]`); if (display && edit.tagName === 'INPUT') { edit.value = display.textContent === '-' ? '' : display.textContent; } } }); row.querySelectorAll('.user-field-display').forEach(display => { if (display.tagName === 'DIV' || display.tagName === 'SPAN') { display.style.display = 'block'; } else { display.style.display = 'inline'; } }); // Buttons umschalten row.querySelector('.edit-user-btn').style.display = 'inline-block'; row.querySelector('.save-user-btn').style.display = 'none'; row.querySelector('.cancel-user-btn').style.display = 'none'; }