feat: quiz, cheat sheets, admin modules - MC quiz, search, user mgmt, content import

Dieser Commit ist enthalten in:
hafroese 2026-04-02 23:41:19 +02:00
Ursprung 03778e4bed
Commit 3cd865ccbc
6 geänderte Dateien mit 425 neuen und 0 gelöschten Zeilen

86
edu/js/admin.js Normale Datei
Datei anzeigen

@ -0,0 +1,86 @@
'use strict';
var Admin = {
render: function() {
var el = document.getElementById('content');
if (!App.currentUser || !App.currentUser.is_admin) {
el.textContent = 'Kein Zugriff';
return;
}
el.textContent = 'Lade Admin...';
var self = this;
API.get('/admin/users').then(function(data) { self.renderPanel(el, data.users); })
.catch(function(e) { el.textContent = e.message; });
},
renderPanel: function(el, users) {
var h = '<div class="page-header"><h1>Administration</h1></div>';
h += '<div class="admin-section"><h2>Benutzer</h2>';
h += '<table class="user-table"><thead><tr><th>ID</th><th>Benutzer</th><th>Name</th><th>Admin</th><th>Erstellt</th><th></th></tr></thead><tbody>';
users.forEach(function(u) {
h += '<tr><td>' + u.id + '</td><td>' + Markdown.esc(u.username) + '</td><td>' + Markdown.esc(u.display_name) + '</td>';
h += '<td>' + (u.is_admin ? 'Ja' : 'Nein') + '</td><td>' + new Date(u.created_at).toLocaleDateString('de-DE') + '</td>';
h += '<td>' + (u.id !== App.currentUser.id ? '<button class="btn btn-small" data-del="' + u.id + '" data-name="' + Markdown.esc(u.username) + '">Entfernen</button>' : '') + '</td></tr>';
});
h += '</tbody></table>';
h += '<h3 style="margin-top:24px;">Neuer Benutzer</h3>';
h += '<form id="add-user-form" style="display:flex;gap:8px;margin-top:8px;flex-wrap:wrap;align-items:end;">';
h += '<div><label style="font-size:12px;color:var(--text-secondary)">Benutzername</label><br><input type="text" id="new-username" required style="width:140px"></div>';
h += '<div><label style="font-size:12px;color:var(--text-secondary)">Anzeigename</label><br><input type="text" id="new-displayname" required style="width:140px"></div>';
h += '<div><label style="font-size:12px;color:var(--text-secondary)">Passwort</label><br><input type="password" id="new-password" required style="width:140px"></div>';
h += '<div><label style="font-size:12px;color:var(--text-secondary)"><input type="checkbox" id="new-is-admin"> Admin</label></div>';
h += '<button type="submit" class="btn btn-primary">Erstellen</button></form>';
h += '<div id="add-user-msg" style="margin-top:8px;"></div></div>';
h += '<div class="admin-section"><h2>Content</h2>';
h += '<p style="color:var(--text-secondary);margin-bottom:12px;">Markdown-Dateien neu einlesen und Datenbank aktualisieren.</p>';
h += '<button class="btn btn-primary" id="import-btn">Content aktualisieren</button>';
h += '<div id="import-msg" style="margin-top:8px;"></div></div>';
el.innerHTML = h;
var self = this;
el.querySelectorAll('[data-del]').forEach(function(btn) {
btn.addEventListener('click', function() { self.deleteUser(parseInt(btn.dataset.del), btn.dataset.name); });
});
document.getElementById('add-user-form').addEventListener('submit', function(e) { e.preventDefault(); self.addUser(); });
document.getElementById('import-btn').addEventListener('click', function() { self.importContent(); });
},
addUser: function() {
var msg = document.getElementById('add-user-msg');
var self = this;
API.post('/admin/users', {
username: document.getElementById('new-username').value.trim(),
display_name: document.getElementById('new-displayname').value.trim(),
password: document.getElementById('new-password').value,
is_admin: document.getElementById('new-is-admin').checked
}).then(function() {
msg.textContent = 'Benutzer erstellt!';
msg.style.color = 'var(--green)';
self.render();
}).catch(function(e) {
msg.textContent = e.message;
msg.style.color = 'var(--red)';
});
},
deleteUser: function(id, name) {
if (!confirm('Benutzer "' + name + '" wirklich entfernen? Lernfortschritt geht verloren.')) return;
var self = this;
API.del('/admin/users/' + id).then(function() { self.render(); })
.catch(function(e) { alert('Fehler: ' + e.message); });
},
importContent: function() {
var msg = document.getElementById('import-msg');
msg.textContent = 'Importiere...';
msg.style.color = 'var(--text-muted)';
API.post('/admin/import').then(function(data) {
var s = data.stats;
msg.textContent = 'Fertig! ' + s.decks + ' Decks, ' + s.cards + ' Karten, ' + s.tutorials + ' Tutorials, ' + s.cheatsheets + ' Cheat Sheets importiert.';
msg.style.color = 'var(--green)';
}).catch(function(e) {
msg.textContent = e.message;
msg.style.color = 'var(--red)';
});
}
};