Profil
Übersicht
Modulfreigabe
Klasse
Ergebnisse
Lizenzen
'; w.document.write(html); w.document.close(); } function editStudent(id, username, displayName) { document.getElementById('me-id').value = id; document.getElementById('me-user').value = username; document.getElementById('me-name').value = displayName; document.getElementById('me-pass').value = ''; showModal('modal-edit'); } async function updateStudent() { var id = parseInt(document.getElementById('me-id').value); var name = document.getElementById('me-name').value.trim(); var pass = document.getElementById('me-pass').value; var data = {action:'update', studentId:id}; if (name) data.displayName = name; if (pass) data.newPassword = pass; var r = await api('students', data); if (r.error) { alert(r.error); return; } hideModal('modal-edit'); loadStudents(); } async function deleteStudent(id) { if (!confirm('Schüler*in wirklich löschen?')) return; await api('students', {action:'delete', studentId:id}); loadStudents(); } async function toggleEasyLanguage(studentId, enabled) { await api('students', {action:'update', studentId:studentId, easyLanguage: enabled}); // Kein Reload nötig — Checkbox-Zustand wird direkt am Element reflektiert } // === ERGEBNISSE === async function loadResults() { var data = await api('dashboard?class_id=' + currentClassId); if (data.error) { document.getElementById('tab-results').innerHTML = '

'+esc(data.error)+'

'; return; } var assessments = data.assessments || []; var assignments = await api('assignments?class_id=' + currentClassId); if (assignments.error) assignments = []; var html = ''; // Aktive Aufgaben mit Fortschritt var active = assignments.filter(function(a){return a.active;}); if (active.length > 0) { html += '
Aktive Aufgaben — Fortschritt
'; active.forEach(function(a) { var mod = ALL_MODS.find(function(m){return m.id===a.module_id;}); var completed = assessments.filter(function(r){return r.sim_id===a.module_id && new Date(r.submitted_at) >= new Date(a.starts_at);}); var students = data.allStudents || []; var pct = students.length > 0 ? Math.round(completed.length / students.length * 100) : 0; html += '
'; html += '
'; html += ''+(mod?.icon||'📋')+''; html += ''+(mod?.name||a.module_id)+''; html += 'Level '+a.level+' · '+completed.length+'/'+students.length+' abgeschlossen'; html += '
'; html += '
'; html += '
'; }); html += '
'; } // Alle Ergebnisse html += '
Letzte Ergebnisse
'; if (assessments.length > 0) { html += ''; assessments.forEach(function(a) { var mod = ALL_MODS.find(function(m){return m.id===a.sim_id;}); var dur = a.duration_ms ? Math.round(a.duration_ms/60000) + ' min' : '—'; var date = new Date(a.submitted_at).toLocaleDateString('de-AT',{day:'2-digit',month:'2-digit',hour:'2-digit',minute:'2-digit'}); html += ''; html += ''; html += ''; }); html += '
Schüler*inModulDauerDatum
'+(a.display_name||'—')+''+(mod ? mod.icon+' '+mod.name : a.sim_id)+''+dur+''+date+'
'; } else { html += '

Noch keine Ergebnisse. Starten Sie eine Klassen-Simulation im Tab „Modulfreigabe".

'; } html += '
'; document.getElementById('tab-results').innerHTML = html; } // === LIZENZEN === async function loadLicenses() { var r = await api('licenses?class_id=' + currentClassId); if (r.error) r = {assigned:[], free:[]}; var html = '
'; html += '
'+(r.free?.length||0)+'
Freie Lizenzen
'; html += '
'+(r.assigned?.length||0)+'
Zugewiesen
'; html += '
'; html += ''; html += '
'; // Zugewiesene Lizenzen html += '
Zugewiesene Lizenzen
'; if (r.assigned && r.assigned.length > 0) { html += ''; r.assigned.forEach(function(l) { html += ''; html += ''; html += ''; }); html += '
Schüler*inLizenzcodeSchuljahr
'+esc(l.student_display || l.student_name)+''+l.code+''+l.school_year+'
'; } else { html += '

Noch keine Lizenzen zugewiesen.

'; } html += '
'; // Freie Lizenzen html += '
Freie Lizenzen (nicht zugewiesen)
'; if (r.free && r.free.length > 0) { html += '
'; r.free.forEach(function(l) { html += ''; html += ''; }); html += '
LizenzcodeSchuljahr
'+l.code+''+l.school_year+'
'; } else { html += '

Keine freien Lizenzen. Neue Codes über das Textfeld oben einlösen.

'; } html += '
'; document.getElementById('tab-licenses').innerHTML = html; } // === BULK SELECTION === function bulkUpdate() { var checked = document.querySelectorAll('.bulk-cb:checked'); var bar = document.getElementById('bulk-bar'); if (checked.length > 0) { bar.classList.remove('hidden'); bar.style.display = 'flex'; document.getElementById('bulk-count').textContent = checked.length; } else { bar.classList.add('hidden'); bar.style.display = 'none'; } } function bulkToggleAll(masterCb) { document.querySelectorAll('.bulk-cb:not(:disabled)').forEach(function(cb) { cb.checked = masterCb.checked; }); bulkUpdate(); } function bulkSelectNone() { document.querySelectorAll('.bulk-cb').forEach(function(cb) { cb.checked = false; }); var master = document.querySelector('th input[type=checkbox]'); if (master) master.checked = false; bulkUpdate(); } function bulkSelectWithout() { document.querySelectorAll('.bulk-cb:not(:disabled)').forEach(function(cb) { cb.checked = true; }); bulkUpdate(); } async function bulkAssignLicense() { var ids = []; document.querySelectorAll('.bulk-cb:checked').forEach(function(cb) { ids.push(parseInt(cb.value)); }); if (ids.length === 0) return; if (!confirm(ids.length + ' Schüler*innen eine Lizenz zuweisen?')) return; var ok = 0, fail = 0; for (var i = 0; i < ids.length; i++) { var r = await api('licenses', {action:'auto_assign', studentId:ids[i]}); if (r.error) fail++; else ok++; } var msg = ok + ' Lizenzen zugewiesen.'; if (fail) msg += ' ' + fail + ' fehlgeschlagen (keine freien Lizenzen?).'; alert(msg); loadStudents(); loadLicenses(); } async function assignLicense(studentId) { var r = await api('licenses', {action:'auto_assign', studentId:studentId}); if (r.error) { alert(r.error); return; } loadStudents(); loadLicenses(); } async function redeemLicenses() { var text = document.getElementById('ml-codes').value.trim(); if (!text) return; var codes = text.split(/[\n,;]+/).map(function(c){return c.trim();}).filter(Boolean); var r = codes.length === 1 ? await api('licenses', {action:'redeem', code:codes[0]}) : await api('licenses', {action:'redeem_batch', codes:codes}); var el = document.getElementById('license-result'); if (r.error) { el.innerHTML = ''+esc(r.error)+''; return; } var msg = r.redeemed ? r.redeemed + ' Lizenzen eingelöst.' : 'Lizenz eingelöst!'; el.innerHTML = ''+msg+''; if (r.errors?.length) el.innerHTML += '
'+r.errors.join('
')+'
'; document.getElementById('ml-codes').value = ''; loadLicenses(); }