diff --git a/App/admin-licenses.html b/App/admin-licenses.html index d0f08d4..f866ceb 100644 --- a/App/admin-licenses.html +++ b/App/admin-licenses.html @@ -145,16 +145,12 @@
Geht in die Mail (für die Rechnung des Kunden).
- - + +
- - -
-
- - + +
@@ -166,7 +162,6 @@
Standard: 1. Okt. Folgejahr (merkt sich die letzte Wahl).
-
'; return s; } @@ -293,7 +296,7 @@ async function renderModules() { s += '

'+(m.icon||'📚')+' '+m.name+'

'; s += '

'+(m.desc||'')+'

'; s += '
'; - if (isSpielbar) s += '▶ Starten'; + if (isSpielbar) s += licensed ? '▶ Starten' : '🔑 Lizenz nötig'; s += '📖 Infos'; if (!isComing) s += '📚 Glossar'; if (buchHref) s += '📘 Schulbuch'; @@ -323,6 +326,13 @@ async function renderModules() { html += '
'; } + if (!licensed) { + html = '
' + + '
🔑
' + + '
Noch keine Lizenz
' + + '
Deine Lehrperson muss dir noch eine gültige Lizenz zuweisen. Sobald das erledigt ist, kannst du die Simulationen starten.
' + + '
' + html; + } document.getElementById('tab-modules').innerHTML = html; } diff --git a/App/teacher.html b/App/teacher.html index 289ddc2..999dbbd 100644 --- a/App/teacher.html +++ b/App/teacher.html @@ -209,6 +209,9 @@ + + +
Übersicht
@@ -2801,6 +2804,7 @@ async function loadClasses() { if (validTabs[initialTab]) currentTab = initialTab; loadTab(currentTab); if (validTabs[initialTab]) switchTab(initialTab); + checkLicenseWarning(); window.addEventListener('hashchange', function() { var t = (location.hash || '').replace('#',''); if (validTabs[t]) switchTab(t); @@ -2815,6 +2819,26 @@ function onClassChange() { document.getElementById('class-code').textContent = currentClass?.join_code || '—'; loadTab(currentTab); startLiveStatusPoll(); + checkLicenseWarning(); +} + +// Rote Cockpit-Warnung: Schüler:innen der Klasse ohne gültige Lizenz. +async function checkLicenseWarning() { + var el = document.getElementById('license-warning'); + if (!el) return; + if (!currentClassId) { el.style.display='none'; return; } + try { + var r = await api('licenses?class_id=' + currentClassId); + var n = (r && r.unlicensed) ? r.unlicensed.length : 0; + if (n > 0) { + el.innerHTML = '
' + + '🔑' + + ''+n+' '+(n===1?'Schüler:in hat':'Schüler:innen haben')+' noch keine gültige Lizenz — ohne Lizenz können sie keine Simulationen nutzen.' + + '' + + '
'; + el.style.display = 'block'; + } else { el.style.display='none'; el.innerHTML=''; } + } catch(e) { el.style.display='none'; } } async function saveClass() { @@ -5428,6 +5452,25 @@ async function loadLicenses() { html += ''; html += '
'; + // Ohne gültige Lizenz — direkt zuweisbar + var unlic = r.unlicensed || []; + html += '
Ohne gültige Lizenz ('+unlic.length+')
'; + if (unlic.length > 0) { + var freeN = r.free ? r.free.length : 0; + html += '

Diese Schüler:innen können noch keine Simulationen nutzen.'+(freeN < unlic.length ? ' Nur '+freeN+' freie Lizenz(en) verfügbar.' : '')+'

'; + if (freeN > 0) html += ''; + html += ''; + unlic.forEach(function(s) { + html += ''; + }); + html += '
Schüler*in
'+esc(s.display_name||s.username)+'' + + (freeN>0 ? '' : 'keine freie Lizenz') + + '
'; + } else { + html += '

Alle Schüler:innen haben eine gültige Lizenz. ✓

'; + } + html += '
'; + // Zugewiesene Lizenzen html += '
Zugewiesene Lizenzen
'; if (r.assigned && r.assigned.length > 0) { @@ -5458,6 +5501,24 @@ async function loadLicenses() { html += '
'; document.getElementById('tab-licenses').innerHTML = html; + checkLicenseWarning(); +} + +async function assignLicenseToStudent(studentId) { + var r = await api('licenses', {action:'auto_assign', studentId:studentId}); + if (r.error) { alert(r.error); return; } + loadLicenses(); +} +async function assignAllUnlicensed(ids) { + if (!ids || !ids.length) return; + if (!confirm(ids.length + ' Schüler:innen je eine freie Lizenz zuweisen?')) return; + var failed = null; + for (var i = 0; i < ids.length; i++) { + var r = await api('licenses', {action:'auto_assign', studentId:ids[i]}); + if (r.error) { failed = r.error; break; } + } + if (failed) alert('Nicht alle konnten zugewiesen werden: ' + failed); + loadLicenses(); } // === BULK SELECTION ===