$(document).ready(function() { var currentAdmin = null; var users = []; var folders = []; var assignedFolders = []; var selectedUserId = ""; $.get('/api/auth/me') .done(function(user) { if (user.role !== 'admin') { alert('Kein Zugriff!'); window.location.href = '/'; return; } currentAdmin = user; $('#admin-user-label').text(user.username || 'Admin'); refreshDashboard(); $('body').removeClass('is-preload'); }) .fail(function() { window.location.href = '/'; }); function showView(name) { $('.admin-view').removeClass('active').filter('[data-view="' + name + '"]').addClass('active'); $('.nav-item').removeClass('active').filter('[data-view="' + name + '"]').addClass('active'); window.scrollTo({ top: 0, behavior: 'smooth' }); } $('.admin-nav').on('click', '.nav-item', function() { showView($(this).data('view')); }); $('#logout-btn').click(function(e) { e.preventDefault(); $.post('/api/auth/logout').done(function() { window.location.href = '/'; }); }); $('#refresh-dashboard').click(refreshDashboard); function refreshDashboard() { $.when(loadUsers(), loadFolders()).done(function() { renderStats(); renderUsers(); renderUserSelect(); renderFolders(); if (selectedUserId) { loadAssignments(selectedUserId); } else { renderSelectedUserSummary(null); } }); } function loadUsers() { return $.get('/api/admin/users').done(function(data) { users = data || []; }).fail(function(xhr) { $('#users-tbody').html( 'Fehler beim Laden: HTTP ' + xhr.status + '' ); }); } function loadFolders() { return $.get('/api/admin/folders').done(function(data) { folders = data || []; }).fail(function() { $('#folders-overview').html('

Ordner konnten nicht geladen werden.

'); }); } function roleCounts() { return users.reduce(function(acc, user) { var role = user.role || 'client'; acc[role] = (acc[role] || 0) + 1; return acc; }, { admin: 0, user: 0, client: 0 }); } function totalAssignments() { return users.reduce(function(total, user) { return total + ((user.folders && user.folders.length) || 0); }, 0); } function totalImages() { return folders.reduce(function(total, folder) { return total + (folder.image_count || 0); }, 0); } function renderStats() { var roles = roleCounts(); var expiredClients = users.filter(function(user) { return user.expired; }).length; var unassignedUsers = users.filter(function(user) { return (user.role || 'client') !== 'admin' && (!user.folders || !user.folders.length); }).length; var unsharedFolders = folders.filter(function(folder) { return !folder.assigned_users || !folder.assigned_users.length; }).length; $('#stat-users').text(users.length); $('#stat-users-sub').text(roles.admin + ' Admins, ' + roles.user + ' User'); $('#stat-folders').text(folders.length); $('#stat-folders-sub').text(totalImages() + ' Bilder in privaten Ordnern'); $('#stat-access').text(totalAssignments()); $('#stat-access-sub').text(unassignedUsers + ' Nutzer ohne Ordner'); $('#stat-clients').text(roles.client); $('#stat-clients-sub').text(expiredClients + ' abgelaufen'); renderAttentionList(unassignedUsers, unsharedFolders, expiredClients); renderRoleList(roles); } function appendSignal($target, label, value, muted) { var $item = $('
').addClass('signal-item'); $('').text(label).appendTo($item); $('').toggleClass('muted', !!muted).text(value).appendTo($item); $target.append($item); } function renderAttentionList(unassignedUsers, unsharedFolders, expiredClients) { var $list = $('#attention-list').empty(); appendSignal($list, 'Nutzer ohne Ordner', unassignedUsers, unassignedUsers === 0); appendSignal($list, 'Ordner ohne Freigabe', unsharedFolders, unsharedFolders === 0); appendSignal($list, 'Abgelaufene Clients', expiredClients, expiredClients === 0); appendSignal($list, 'Gesamtbilder privat', totalImages(), totalImages() === 0); } function renderRoleList(roles) { var $list = $('#role-list').empty(); appendSignal($list, 'Admins', roles.admin || 0); appendSignal($list, 'Permanente User', roles.user || 0); appendSignal($list, 'Clients', roles.client || 0); appendSignal($list, 'Freigaben', totalAssignments()); } function getRoleBadge(role, expired) { if (expired) return $('').addClass('badge badge-expired').text('abgelaufen'); return $('').addClass('badge badge-' + (role || 'client')).text(role || 'client'); } function renderUsers() { var $tbody = $('#users-tbody').empty(); if (!users.length) { $tbody.html('Keine Nutzer gefunden'); return; } users.forEach(function(user) { var role = user.role || 'client'; var created = user.created_at ? new Date(user.created_at).toLocaleDateString('de-DE') : '-'; var status = created; if (role === 'client' && user.expires_at) { status = (user.expired ? 'Abgelaufen: ' : 'Bis: ') + new Date(user.expires_at).toLocaleDateString('de-DE'); } var $row = $(''); var $userCell = $('').text(user.username + ' ').append(getRoleBadge(role, user.expired)); var $foldersCell = $(''); if (user.folders && user.folders.length) { user.folders.forEach(function(folder) { $('').addClass('badge folder-badge').text(folder).appendTo($foldersCell); }); } else { $foldersCell.append($('').addClass('muted').text('Keine privaten Ordner')); } var $actions = $(''); $('') .data('user-id', user.id) .appendTo($actions); $('') .data('user-id', user.id) .data('username', user.username) .appendTo($actions); $('') .data('user-id', user.id) .data('username', user.username) .appendTo($actions); $row.append($('').text(user.id)); $row.append($userCell); $row.append($('').text(role)); $row.append($('').text(status)); $row.append($foldersCell); $row.append($actions); $tbody.append($row); }); } function renderUserSelect() { var $select = $('#user-select').html(''); users.forEach(function(user) { if ((user.role || 'client') === 'admin') return; $('') .val(user.id) .text(user.username + ' (' + (user.role || 'client') + ')') .appendTo($select); }); if (selectedUserId) $select.val(selectedUserId); } function renderFolders() { var $wrap = $('#folders-overview').empty(); if (!folders.length) { $wrap.html('

Keine Unterordner im Bilderpfad gefunden.

'); return; } folders.forEach(function(folder) { var $card = $('
').addClass('folder-card'); $('

').text(folder.name).appendTo($card); var $meta = $('
').addClass('folder-meta').appendTo($card); $('').addClass('folder-pill').text((folder.image_count || 0) + ' Bilder').appendTo($meta); $('').addClass('folder-pill').text('privat').appendTo($meta); $('').addClass('folder-pill').text((folder.assigned_users || []).length + ' Freigaben').appendTo($meta); var $assigned = $('
').appendTo($card); if (folder.assigned_users && folder.assigned_users.length) { folder.assigned_users.forEach(function(user) { $('').addClass('badge folder-badge').text(user.username).appendTo($assigned); }); } else { $assigned.append($('').addClass('muted').text('Noch niemandem freigegeben')); } var $actions = $('
').addClass('folder-actions').appendTo($card); $('') .data('folder', folder.name) .appendTo($actions); $('') .data('folder', folder.name) .appendTo($actions); $wrap.append($card); }); } function renderAccessList() { var $list = $('#folder-access-list').empty(); if (!folders.length) { $list.html('

Keine Unterordner gefunden.

'); return; } folders.forEach(function(folder) { var checked = assignedFolders.indexOf(folder.name) !== -1; var $label = $('').addClass('access-item'); $('') .val(folder.name) .prop('checked', checked) .appendTo($label); $('').text(folder.name + ' (' + (folder.image_count || 0) + ')').appendTo($label); $list.append($label); }); } function findUser(userId) { var id = String(userId || ''); return users.find(function(user) { return String(user.id) === id; }) || null; } function renderSelectedUserSummary(user) { var $summary = $('#selected-user-summary').empty(); if (!user) { appendSignal($summary, 'Auswahl', 'Kein Nutzer', true); return; } appendSignal($summary, 'Rolle', user.role || 'client'); appendSignal($summary, 'Ordner', (assignedFolders.length || 0) + ' freigegeben'); if (user.expires_at) { appendSignal($summary, 'Client bis', new Date(user.expires_at).toLocaleDateString('de-DE'), !!user.expired); } } function loadAssignments(userId) { selectedUserId = String(userId || ""); if (!selectedUserId) { $('#folder-table-wrap').hide(); $('#folder-access-empty').show(); assignedFolders = []; renderSelectedUserSummary(null); return; } $('#user-select').val(selectedUserId); $.get('/api/admin/assign?user_id=' + encodeURIComponent(selectedUserId)).done(function(data) { assignedFolders = data || []; renderAccessList(); renderSelectedUserSummary(findUser(selectedUserId)); $('#folder-access-empty').hide(); $('#folder-table-wrap').show(); }).fail(function() { assignedFolders = []; renderAccessList(); renderSelectedUserSummary(findUser(selectedUserId)); $('#folder-access-empty').hide(); $('#folder-table-wrap').show(); }); } function saveAssignments() { if (!selectedUserId) return; assignedFolders = []; $('#folder-access-list input:checked').each(function() { assignedFolders.push($(this).val()); }); $.ajax({ url: '/api/admin/assign', type: 'POST', contentType: 'application/json', data: JSON.stringify({ user_id: selectedUserId, folders: assignedFolders }) }).done(function() { $('#assign-status').text('Gespeichert').show().delay(1200).fadeOut(); refreshDashboard(); }).fail(function(xhr) { alert((xhr.responseJSON && xhr.responseJSON.error) || 'Speichern fehlgeschlagen'); }); } $('#users-tbody').on('click', '.user-access', function() { loadAssignments($(this).data('user-id')); showView('access'); }); $('#user-select').change(function() { loadAssignments($(this).val()); }); $('#folder-access-list').on('change', 'input[type="checkbox"]', saveAssignments); $('#folders-overview').on('click', '.folder-assign-btn', function() { var folder = $(this).data('folder'); showView('access'); if (!selectedUserId) { alert('Bitte zuerst einen Nutzer in den Freigaben waehlen.'); return; } var $checkbox = $('#folder-access-list input').filter(function() { return $(this).val() === folder; }); if ($checkbox.length) { $checkbox.prop('checked', !$checkbox.prop('checked')).trigger('change'); } }); $('#folders-overview').on('click', '.folder-preview-btn', function() { var folder = $(this).data('folder'); showView('preview'); $('#folder-preview-title').text('Ordnerinhalt: ' + folder); $('#folder-preview-note').text('Lade Bilder...'); $('#folder-preview').empty(); $.get('/api/admin/folders/' + encodeURIComponent(folder) + '/images').done(function(images) { $('#folder-preview-note').text((images || []).length + ' Bilder im Ordner'); var $preview = $('#folder-preview').empty(); if (!images || !images.length) { $preview.html('

Dieser Ordner ist leer.

'); return; } images.forEach(function(img) { var $link = $('').attr({ href: img.full_url, target: '_blank', rel: 'noopener' }); $('').attr({ src: img.thumb_url, alt: img.name, title: img.name }).appendTo($link); $preview.append($link); }); }).fail(function(xhr) { $('#folder-preview-note').text((xhr.responseJSON && xhr.responseJSON.error) || 'Ordner konnte nicht geladen werden.'); }); }); $('#create-user-form').submit(function(e) { e.preventDefault(); var username = $('#new-username').val().trim(); var password = $('#new-password').val(); var role = $('#new-role').val(); if (!username || !password) { alert('Bitte Username und Passwort eingeben.'); return; } $.ajax({ url: '/api/admin/users', type: 'POST', contentType: 'application/json', data: JSON.stringify({ username: username, password: password, role: role }) }).done(function() { $('#new-username, #new-password').val(''); refreshDashboard(); }).fail(function(xhr) { var msg = (xhr.responseJSON && xhr.responseJSON.error) || 'Unbekannter Fehler'; alert('Fehler beim Erstellen: ' + msg); }); }); $('#users-tbody').on('click', '.user-delete', function() { var id = $(this).data('user-id'); var name = $(this).data('username'); if (!confirm('Nutzer "' + name + '" wirklich loeschen?')) return; $.ajax({ url: '/api/admin/users/' + id, type: 'DELETE' }) .done(function() { if (String(selectedUserId) === String(id)) { selectedUserId = ""; assignedFolders = []; } refreshDashboard(); }) .fail(function(err) { alert(err.responseJSON ? err.responseJSON.error : 'Fehler'); }); }); $('#users-tbody').on('click', '.user-password', function() { var id = $(this).data('user-id'); var name = $(this).data('username'); var newPw = prompt('Neues Passwort fuer "' + name + '":'); if (!newPw) return; $.ajax({ url: '/api/admin/users/' + id + '/password', type: 'PUT', contentType: 'application/json', data: JSON.stringify({ new_password: newPw }) }).done(function() { alert('Passwort geaendert.'); }).fail(function(err) { alert(err.responseJSON ? err.responseJSON.error : 'Fehler'); }); }); $('#admin-pw-form').submit(function(e) { e.preventDefault(); $.post('/api/auth/change-password', { old_password: $('#admin-pw-old').val(), new_password: $('#admin-pw-new').val() }).done(function() { alert('Passwort geaendert.'); $('#admin-pw-old, #admin-pw-new').val(''); }).fail(function(xhr) { alert(xhr.responseJSON ? xhr.responseJSON.error : 'Fehler'); }); }); });