Lehrer-Aktivitätsstreifen: Farbe je Sim, Höhe=Erfolg, Rahmen=Zustand

Tages-Balken neu kodiert (nach Thomas' Spec):
- Eine Farbe pro Simulation (simColor, stabil per Hash).
- Balkenhöhe = Maß für den Erfolg (mehr Sterne → höher) → Leistungen auf einen
  Blick vergleichbar.
- Rahmen kodiert den Zustand: fertig gespielt = geschlossener dünner Rahmen ·
  noch dran = rechts offener Rahmen · Abbruch = gestrichelt + ✖ am Ende.
- Beschriftung: 3-Buchstaben-Kürzel (eng) → Wort → Wort+Lektion (Platz).
- Legende entsprechend erneuert.

Datenbasis vorhanden: live.php summary liefert success (inkl. 'aborted'),
stars, completed pro Session. Offen für v2: „läuft noch"-Balken aus aktiven
live_sessions einblenden (Tag-View ist bisher assessments-basiert = Abschlüsse).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-19 18:14:56 +02:00
parent 5f603cb193
commit 0555a2d245
+37 -25
View File
@@ -1487,6 +1487,13 @@ var SIM_LABELS = {
};
function simWord(id){ return (SIM_LABELS[id] && SIM_LABELS[id].w) || id; }
function simCode(id){ return (SIM_LABELS[id] && SIM_LABELS[id].c) || String(id||'').slice(0,3).toUpperCase(); }
// Stabile, unterscheidbare Farbe je Sim (eine Farbe pro Simulation) — Hash→Farbton.
function simColor(id){
if (SIM_LABELS[id] && SIM_LABELS[id].col) return SIM_LABELS[id].col;
var s = String(id||''), h = 0;
for (var i=0;i<s.length;i++) h = (h*33 + s.charCodeAt(i)) % 360;
return 'hsl('+h+',48%,42%)';
}
// Rückwärtskompatibel: LIVE_SIM_SHORT[id] = Wort je Sim.
var LIVE_SIM_SHORT = Object.keys(SIM_LABELS).reduce(function(o,k){ o[k]=SIM_LABELS[k].w; return o; }, {});
function _liveFmtMin(sec) {
@@ -2049,44 +2056,49 @@ async function renderLiveSummary() {
var pctL = _msToPct(sess.startMs);
var pctR = _msToPct(endMs);
var pctW = Math.max(0.8, pctR - pctL); // mindestens 0.8 % sichtbar
var simW = simWord(sess.simId);
var simC = simCode(sess.simId);
var lbl = simW + (sess.lessonLabel ? ' '+sess.lessonLabel : '');
var color = _liveSuccessColor(sess.success);
var simW = simWord(sess.simId), simC = simCode(sess.simId);
var col = simColor(sess.simId); // eine Farbe je Sim
var stars = (sess.stars != null) ? Math.max(0, Math.min(5, sess.stars)) : null;
var aborted = sess.success === 'aborted';
var completed = !aborted && stars != null; // fertig gespielt
var inProgress = !aborted && stars == null; // noch dran / kein Ergebnis
// Balkenhöhe = Maß für den Erfolg (mehr Sterne → höher). Abbruch/läuft: flach.
var barH = completed ? (9 + Math.round(stars / 5 * 15)) : (aborted ? 9 : 12);
// Rahmen kodiert den Zustand: fertig = geschlossen · noch dran = rechts offen ·
// Abbruch = gestrichelt (+ ✖ am Ende).
var border = completed ? 'border:1.5px solid rgba(20,20,20,.45);'
: inProgress ? 'border:1.5px solid rgba(20,20,20,.4);border-right:0;'
: 'border:1.5px dashed rgba(20,20,20,.45);';
var stateWord = completed ? 'fertig' : aborted ? 'abgebrochen' : 'läuft noch';
var tip = (sess.simTitle||sess.simId) + (sess.lessonLabel?' · '+sess.lessonLabel:'')
+ ' · ' + _fmtHM(sess.startMs) + '' + _fmtHM(endMs)
+ ' · ' + _liveFmtMin(realDurSec)
+ (sess.stars!=null?' · '+sess.stars+'/5★':'');
// enger Balken → 3-Buchstaben-Kürzel, mittel → Wort, breit → Wort + Lektion
var inner = (pctW < 2.5) ? '' : (pctW < 6) ? simC : (pctW < 11) ? simW : lbl;
+ ' · ' + _liveFmtMin(realDurSec) + ' · ' + stateWord
+ (stars!=null?' · '+stars+'/5★':'');
// enger Balken → Kürzel, mittel → Wort, breit → Wort + Lektion
var inner = (pctW < 2.5) ? '' : (pctW < 6) ? simC : (pctW < 11) ? simW : (simW + (sess.lessonLabel ? ' '+sess.lessonLabel : ''));
var sessKey = s.studentId + ':' + sess.simId + ':' + (sess.lessonLabel||'_') + ':' + sess.startMs;
html += '<div onclick="openLiveSessionDetail(\''+sessKey+'\')" '
+ 'title="'+esc(tip)+'" '
+ 'style="position:absolute;left:'+pctL+'%;width:'+pctW+'%;top:2px;height:24px;'
+ 'background:'+color+';color:#fff;border-radius:3px;'
+ 'padding:0 .35rem;font-size:.62rem;font-weight:700;line-height:24px;'
+ 'style="position:absolute;left:'+pctL+'%;width:'+pctW+'%;bottom:2px;height:'+barH+'px;'
+ 'background:'+col+';color:#fff;border-radius:3px;'+border
+ 'padding:0 .3rem;font-size:.6rem;font-weight:700;line-height:'+Math.max(9,barH-3)+'px;'
+ 'white-space:nowrap;overflow:hidden;text-overflow:ellipsis;cursor:pointer;'
+ 'box-shadow:0 1px 2px rgba(0,0,0,.1)">'
+ 'text-shadow:0 1px 1px rgba(0,0,0,.35);box-shadow:0 1px 2px rgba(0,0,0,.12)">'
+ esc(inner) + '</div>';
// Nach Abschluss immer die Sterne (gold) am Balkenende zeigen
if (sess.stars != null) {
var _sc = Math.max(0, Math.min(5, sess.stars));
var _starTxt = '★'.repeat(_sc) + '<span style="color:#d9d2be">' + '★'.repeat(5-_sc) + '</span>';
var _rightEdge = pctR > 82;
html += '<span style="position:absolute;'
+ (_rightEdge ? 'right:'+(100-pctR)+'%;padding-right:4px;' : 'left:'+pctR+'%;padding-left:3px;')
+ 'top:0;height:28px;line-height:28px;font-size:.62rem;color:#e0a838;font-weight:700;'
+ 'letter-spacing:.5px;white-space:nowrap;pointer-events:none">'+_starTxt+'</span>';
if (aborted) {
html += '<span style="position:absolute;left:'+pctR+'%;bottom:-1px;height:28px;line-height:28px;'
+ 'font-size:.74rem;color:#b03a2a;font-weight:800;padding-left:1px;pointer-events:none">✖</span>';
}
});
html += '</div>';
});
html += '</div>';
html += '<div style="margin-top:.8rem;display:flex;gap:.6rem;flex-wrap:wrap;font-size:.65rem;color:#5a5a5a">';
[['good','★ ≥ 4 — gut'],['mid','★ 2-3 — mittel'],['bad','★ ≤ 1 — schwach'],['aborted','abgebrochen'],['running','läuft noch']].forEach(function(p){
html += '<span><span style="display:inline-block;width:10px;height:10px;border-radius:2px;background:'+_liveSuccessColor(p[0])+';vertical-align:middle;margin-right:.25rem"></span>'+p[1]+'</span>';
});
html += '<div style="margin-top:.8rem;display:flex;gap:1rem;flex-wrap:wrap;font-size:.66rem;color:#5a5a5a;align-items:center">';
html += '<span><b>Farbe</b> = Simulation · <b>Höhe</b> = Erfolg (höher = besser)</span>';
html += '<span><span style="display:inline-block;width:16px;height:11px;background:#8a8a8a;border:1.5px solid rgba(20,20,20,.45);vertical-align:middle;margin-right:.25rem"></span>fertig</span>';
html += '<span><span style="display:inline-block;width:16px;height:11px;background:#8a8a8a;border:1.5px solid rgba(20,20,20,.4);border-right:0;vertical-align:middle;margin-right:.25rem"></span>läuft noch</span>';
html += '<span><span style="display:inline-block;width:16px;height:11px;background:#8a8a8a;border:1.5px dashed rgba(20,20,20,.45);vertical-align:middle;margin-right:.15rem"></span><span style="color:#b03a2a;font-weight:800"></span> Abbruch</span>';
html += '</div>';
html += '</div>';