Live: Cockpit entfernt; braucht-Hilfe + Reflexionen in Aktuell
- Live-Modi jetzt nur Tag / Aktuell / Vergleich (Cockpit-Toggle raus) - Aktuell: Live-"braucht Hilfe"-Banner + rote Zeilen-Hervorhebung (Heuristik gespiegelt aus live.php), Spectator per Zeilenklick bleibt - Reflexionen als Block am Ende von Aktuell, gecacht gegen 4s-Refresh-Flackern Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+50
-8
@@ -492,16 +492,15 @@ var _liveDate = new Date().toISOString().slice(0,10); // YYYY-MM-DD, default h
|
||||
async function loadLive() {
|
||||
await _renderLiveTab();
|
||||
if (_liveTimer) clearInterval(_liveTimer);
|
||||
// Auto-Refresh: cockpit + aktuell alle 4s, übersicht/vergleich nur wenn heute
|
||||
var refreshMs = (_liveMode === 'cockpit' || _liveMode === 'aktuell') ? 4000
|
||||
// Auto-Refresh: aktuell alle 4s, übersicht/vergleich nur wenn heute
|
||||
var refreshMs = (_liveMode === 'aktuell') ? 4000
|
||||
: (_liveDate === new Date().toISOString().slice(0,10)) ? 60000 : 0;
|
||||
if (refreshMs) _liveTimer = setInterval(_renderLiveTab, refreshMs);
|
||||
}
|
||||
async function _renderLiveTab() {
|
||||
if (_liveMode === 'cockpit') return renderLiveCockpit();
|
||||
if (_liveMode === 'uebersicht') return renderLiveSummary();
|
||||
if (_liveMode === 'vergleich') return renderLiveCompare();
|
||||
return renderLive();
|
||||
return renderLive(); // 'aktuell'
|
||||
}
|
||||
window.setCompareSim = function(simId){ _liveCompareSim = simId; loadLive(); };
|
||||
function setLiveMode(mode) {
|
||||
@@ -533,7 +532,6 @@ function _liveToggleHtml() {
|
||||
return '<div style="display:flex;gap:.4rem;align-items:center;padding:.4rem 0 .8rem;flex-wrap:wrap">'
|
||||
+ '<div style="display:inline-flex;border:1.5px solid rgba(0,0,0,.08);border-radius:6px;overflow:hidden">'
|
||||
+ ' <button class="btn-sm" onclick="setLiveMode(\'uebersicht\')" style="padding:.35rem .8rem;border:0;background:'+(_liveMode==='uebersicht'?'#4a7c8a':'transparent')+';color:'+(_liveMode==='uebersicht'?'#fff':'#3d5165')+';font-weight:700;font-size:.74rem">📅 Tag</button>'
|
||||
+ ' <button class="btn-sm" onclick="setLiveMode(\'cockpit\')" style="padding:.35rem .8rem;border:0;background:'+(_liveMode==='cockpit'?'#4a7c8a':'transparent')+';color:'+(_liveMode==='cockpit'?'#fff':'#3d5165')+';font-weight:700;font-size:.74rem">🎯 Cockpit</button>'
|
||||
+ ' <button class="btn-sm" onclick="setLiveMode(\'aktuell\')" style="padding:.35rem .8rem;border:0;background:'+(_liveMode==='aktuell'?'#4a7c8a':'transparent')+';color:'+(_liveMode==='aktuell'?'#fff':'#3d5165')+';font-weight:700;font-size:.74rem">🔴 Aktuell</button>'
|
||||
+ ' <button class="btn-sm" onclick="setLiveMode(\'vergleich\')" style="padding:.35rem .8rem;border:0;background:'+(_liveMode==='vergleich'?'#4a7c8a':'transparent')+';color:'+(_liveMode==='vergleich'?'#fff':'#3d5165')+';font-weight:700;font-size:.74rem">📊 Vergleich</button>'
|
||||
+ '</div>'
|
||||
@@ -1090,7 +1088,8 @@ async function _loadCockpitReflections() {
|
||||
var host = document.getElementById('cockpit-reflections-list');
|
||||
if (!host) return;
|
||||
if (!items.length) {
|
||||
host.innerHTML = '<p style="font-size:.78rem;color:#8a8a8a;font-style:italic">Noch keine Reflexionen aus dieser Klasse. Sobald Lernende einen Durchgang abschließen und die Reflexionsfrage beantworten, erscheint die Antwort hier.</p>';
|
||||
_liveReflCache = '<p style="font-size:.78rem;color:#8a8a8a;font-style:italic">Noch keine Reflexionen aus dieser Klasse. Sobald Lernende einen Durchgang abschließen und die Reflexionsfrage beantworten, erscheint die Antwort hier.</p>';
|
||||
host.innerHTML = _liveReflCache;
|
||||
return;
|
||||
}
|
||||
var simIcons = {};
|
||||
@@ -1113,6 +1112,7 @@ async function _loadCockpitReflections() {
|
||||
if (data.total > items.length) {
|
||||
html += '<p style="font-size:.7rem;color:#8a8a8a;margin-top:.5rem">Zeige ' + items.length + ' von ' + data.total + ' Reflexionen.</p>';
|
||||
}
|
||||
_liveReflCache = html;
|
||||
host.innerHTML = html;
|
||||
} catch (e) {
|
||||
var host = document.getElementById('cockpit-reflections-list');
|
||||
@@ -1120,6 +1120,23 @@ async function _loadCockpitReflections() {
|
||||
}
|
||||
}
|
||||
|
||||
// „Braucht Hilfe" live aus dem Sim-State (spiegelt die Heuristik in live.php)
|
||||
function _liveNeedsHelp(simId, s) {
|
||||
if (!s || typeof s !== 'object') return false;
|
||||
switch (simId) {
|
||||
case 'heli': return (s.planAttempts||0) >= 10;
|
||||
case 'klima': case 'klima-3d': return (s.budget||0) < 0;
|
||||
case 'weltkueche': return (s.mistakesInDish||0) >= 12;
|
||||
case 'farmer': return (s.cash==null?1000:s.cash) < 0;
|
||||
case 'logistik': return (s.balance==null?1000:s.balance) < 0;
|
||||
case 'eu-werkstatt': return (s.attemptsThisStep||0) >= 6;
|
||||
case 'tourismustal': return (s.money==null?1:s.money) < 0 || (s.lossStreak||0) >= 8;
|
||||
case 'tourismusregion': return (s.budget==null?1:s.budget) < -400 || (s.env==null?100:s.env) < 30;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
var _liveReflCache = '', _liveReflAt = 0;
|
||||
|
||||
async function renderLive() {
|
||||
var sessions = await api('live?class_id=' + currentClassId);
|
||||
if (sessions.error) { document.getElementById('tab-live').innerHTML = '<p style="color:#c07a6b">'+esc(sessions.error)+'</p>'; return; }
|
||||
@@ -1132,6 +1149,15 @@ async function renderLive() {
|
||||
return;
|
||||
}
|
||||
|
||||
// „Braucht Hilfe"-Banner (live)
|
||||
var helpList = sessions.filter(function(ls){ return _liveNeedsHelp(ls.module_id, ls.state); });
|
||||
if (helpList.length) {
|
||||
html += '<div style="background:#fdf3ee;border:1px solid #e6c3b4;border-left:4px solid #c0574a;border-radius:8px;padding:.5rem .8rem;margin-bottom:.7rem;font-size:.76rem;color:#8a3a2a">'
|
||||
+ '🆘 <b>'+helpList.length+' brauchen evtl. Hilfe</b> — '
|
||||
+ helpList.map(function(ls){ return esc(ls.displayName); }).join(' · ')
|
||||
+ '</div>';
|
||||
}
|
||||
|
||||
// Nach Modul gruppieren
|
||||
var byModule = {};
|
||||
sessions.forEach(function(ls) {
|
||||
@@ -1197,9 +1223,12 @@ async function renderLive() {
|
||||
var lastSeen = ls.last_seen_ms ? Math.max(0, Math.round((Date.now() - ls.last_seen_ms)/1000)) : 0;
|
||||
var dotColor = lastSeen < 8 ? '#5a8a5e' : lastSeen < 20 ? '#e8833a' : '#c07a6b';
|
||||
var s = ls.state || {};
|
||||
var help = _liveNeedsHelp(ls.module_id, s);
|
||||
var restBg = help ? '#fdf3ee' : '';
|
||||
var hoverBg = help ? '#f9e7e0' : '#f7f6f3';
|
||||
|
||||
html += '<tr style="border-bottom:1px solid rgba(0,0,0,.04);cursor:pointer" onclick="openLiveSpectator('+ls.student_id+',\''+ls.module_id+'\')" onmouseover="this.style.background=\'#f7f6f3\'" onmouseout="this.style.background=\'\'">';
|
||||
html += '<td style="padding:.4rem .5rem;white-space:nowrap"><span style="display:inline-flex;align-items:center;gap:.4rem">'+avatarHtml+'<strong>'+esc(ls.displayName)+'</strong></span></td>';
|
||||
html += '<tr style="border-bottom:1px solid rgba(0,0,0,.04);cursor:pointer;background:'+restBg+';box-shadow:'+(help?'inset 3px 0 0 #c0574a':'none')+'" onclick="openLiveSpectator('+ls.student_id+',\''+ls.module_id+'\')" onmouseover="this.style.background=\''+hoverBg+'\'" onmouseout="this.style.background=\''+restBg+'\'">';
|
||||
html += '<td style="padding:.4rem .5rem;white-space:nowrap"><span style="display:inline-flex;align-items:center;gap:.4rem">'+(help?'<span title="stockt · braucht evtl. Hilfe">🆘</span>':'')+avatarHtml+'<strong>'+esc(ls.displayName)+'</strong></span></td>';
|
||||
html += '<td style="padding:.4rem .5rem;white-space:nowrap;line-height:1.15" title="Aktuelle Mission/Lektion · Gesamte Sim-Session"><div style="font-size:.78rem;color:#3a3a3a;font-weight:600">'+missionStr+'</div><div style="font-size:.62rem;color:#8a8a8a">'+simStr+' ges.</div></td>';
|
||||
fields.forEach(function(f) {
|
||||
var align = f.align || 'right';
|
||||
@@ -1212,7 +1241,20 @@ async function renderLive() {
|
||||
});
|
||||
|
||||
html += '</div>';
|
||||
|
||||
// Reflexionen (letzte Antworten der Klasse) — gecacht, damit der 4s-Refresh nicht flackert
|
||||
html += '<div class="card" id="cockpit-reflections">'
|
||||
+ '<div class="card-h">💭 Reflexionen — letzte Antworten der Klasse</div>'
|
||||
+ '<p style="font-size:.7rem;color:#8a8a8a;margin-bottom:.6rem">Was die Bearbeiter:innen am Ende eines Durchgangs als wichtigsten Eindruck festgehalten haben.</p>'
|
||||
+ '<div id="cockpit-reflections-list">' + (_liveReflCache || 'Lade …') + '</div>'
|
||||
+ '</div>';
|
||||
|
||||
host.innerHTML = html;
|
||||
|
||||
if (!_liveReflCache || Date.now() - _liveReflAt > 30000) {
|
||||
_liveReflAt = Date.now();
|
||||
_loadCockpitReflections();
|
||||
}
|
||||
}
|
||||
|
||||
// === LIVE-ÜBERSICHT (Tages-Balkenchart pro Schüler*in) ===
|
||||
|
||||
Reference in New Issue
Block a user