Lehreransicht "Aktuell": Mini-Balken unter den Zahlen (Farmer-Spalten)
Kleine Balkengrafik unter jedem Zahlenwert: bounded Metriken (Jahr/Trefferquote/ Sorten absolut zur Skala) + offene Werte (Konto/Laender/Felder/Versuche/CO2) relativ zum groessten Wert der Spalte ueber alle Schueler:innen -> auf einen Blick "wer liegt vorn". Konto signiert (rot bei minus), CO2/Krankheiten invers (rot = mehr). Helfer _liveMiniBar/_liveBarColor, colMax pro Spalte. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+50
-10
@@ -634,25 +634,39 @@ function _liveBar(val, min, max, goodWhen) {
|
||||
+ '</span>';
|
||||
}
|
||||
|
||||
// Farbwahl nach „gut wenn" (aus _liveBar extrahiert) — für die Mini-Balken.
|
||||
function _liveBarColor(pct, goodWhen) {
|
||||
pct = Math.max(0, Math.min(100, pct||0));
|
||||
if (goodWhen === 'low') return pct <= 33 ? '#5a8a5e' : pct <= 66 ? '#c9913a' : '#c07a6b';
|
||||
if (goodWhen === 'mid') return '#9a9a9a';
|
||||
return pct >= 60 ? '#5a8a5e' : pct >= 30 ? '#c9913a' : '#c07a6b';
|
||||
}
|
||||
// Mini-Balken UNTER der Zahl (Cockpit „Aktuell"). pct 0–100.
|
||||
function _liveMiniBar(pct, color) {
|
||||
pct = Math.max(0, Math.min(100, pct||0));
|
||||
return '<div style="margin-top:3px;height:3px;border-radius:99px;background:rgba(0,0,0,.06);overflow:hidden">'
|
||||
+ '<div style="height:100%;width:'+pct.toFixed(0)+'%;background:'+color+'"></div></div>';
|
||||
}
|
||||
|
||||
var LIVE_PRIMARY_FIELDS = {
|
||||
farmer: [
|
||||
{k:'year', label:'Jahr', fmt:function(v){ return (v||0) + '<span style="color:#8a8a8a"> / 10</span>'; }},
|
||||
{k:'year', label:'Jahr', bar:{kind:'abs',max:10,good:'mid'}, fmt:function(v){ return (v||0) + '<span style="color:#8a8a8a"> / 10</span>'; }},
|
||||
{k:'market_de', label:'Markt', align:'left', fmt:function(v){ return v ? '🌍 '+esc(v) : '<span style="color:#8a8a8a">—</span>'; }},
|
||||
{k:'cash', label:'Konto', fmt:function(v){
|
||||
{k:'cash', label:'Konto', bar:{kind:'rel',good:'signed'}, fmt:function(v){
|
||||
if (v == null) return '<span style="color:#9a9a9a">—</span>';
|
||||
var color = v < 0 ? '#c0392b' : '#5a8a5e';
|
||||
return '<span style="color:'+color+';font-weight:700">'+Number(v).toLocaleString('de-AT')+' €</span>';
|
||||
}},
|
||||
{k:'hit_rate', label:'Trefferquote', fmt:function(v){ return _liveBar(v, 0, 100, 'high'); }},
|
||||
{k:'crops_used', label:'Sorten', fmt:function(v){ return (v||0) + '<span style="color:#8a8a8a"> / 10</span>'; }},
|
||||
{k:'countries', label:'Länder', fmt:function(v){ return (v!=null? v : 0); }},
|
||||
{k:'fields', label:'Felder', fmt:function(v){ return (v!=null? v : 0); }},
|
||||
{k:'attempts', label:'Versuche',fmt:function(v){ return (v!=null? v : 0); }},
|
||||
{k:'co2_kg', label:'CO₂', unit:'kg', fmt:function(v){
|
||||
{k:'hit_rate', label:'Trefferquote', unit:'%', bar:{kind:'abs',max:100,good:'high'}, fmt:function(v){ return (v!=null? v : 0); }},
|
||||
{k:'crops_used', label:'Sorten', bar:{kind:'abs',max:10,good:'high'}, fmt:function(v){ return (v||0) + '<span style="color:#8a8a8a"> / 10</span>'; }},
|
||||
{k:'countries', label:'Länder', bar:{kind:'rel',good:'high'}, fmt:function(v){ return (v!=null? v : 0); }},
|
||||
{k:'fields', label:'Felder', bar:{kind:'rel',good:'mid'}, fmt:function(v){ return (v!=null? v : 0); }},
|
||||
{k:'attempts', label:'Versuche',bar:{kind:'rel',good:'mid'}, fmt:function(v){ return (v!=null? v : 0); }},
|
||||
{k:'co2_kg', label:'CO₂', unit:'kg', bar:{kind:'rel',good:'low'}, fmt:function(v){
|
||||
if (v == null) return '<span style="color:#9a9a9a">—</span>';
|
||||
return '<span style="font-variant-numeric:tabular-nums">'+Number(Math.round(v)).toLocaleString('de-AT')+'</span>';
|
||||
}},
|
||||
{k:'diseases', label:'Krankheiten', align:'center', fmt:function(v){
|
||||
{k:'diseases', label:'Krankheiten', align:'center', bar:{kind:'abs',max:5,good:'low'}, fmt:function(v){
|
||||
if (v == null) return '<span style="color:#9a9a9a">—</span>';
|
||||
var color = (v > 0) ? '#c07a6b' : '#8a8a8a';
|
||||
return '<span style="color:'+color+';font-weight:'+(v>0?'700':'400')+'">'+v+'</span>';
|
||||
@@ -1419,6 +1433,16 @@ async function renderLive() {
|
||||
html += '<th style="width:1.2em"></th>';
|
||||
html += '</tr></thead><tbody>';
|
||||
|
||||
// Spalten-Maxima für relative Mini-Balken (über alle Zeilen dieser Sim).
|
||||
var colMax = {};
|
||||
fields.forEach(function(f) {
|
||||
if (f.bar && f.bar.kind === 'rel') {
|
||||
var mx = 0;
|
||||
grp.rows.forEach(function(r) { var val = (r.state||{})[f.k]; if (typeof val === 'number' && !isNaN(val)) mx = Math.max(mx, Math.abs(val)); });
|
||||
colMax[f.k] = mx;
|
||||
}
|
||||
});
|
||||
|
||||
grp.rows.forEach(function(ls) {
|
||||
var avatarHtml = (typeof ls.emoji === 'string' && ls.emoji.indexOf('avatar:') === 0)
|
||||
? '<img src="assets/img/avatars/thumbs/avatar-'+ls.emoji.substring(7)+'.webp" alt="" style="width:1.6em;height:1.6em;border-radius:50%;object-fit:cover;vertical-align:middle">'
|
||||
@@ -1450,7 +1474,23 @@ async function renderLive() {
|
||||
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';
|
||||
html += '<td style="text-align:'+align+';padding:.4rem .5rem;font-variant-numeric:tabular-nums">'+_liveFmt(s[f.k], f.unit, f, ls)+'</td>';
|
||||
var cell = _liveFmt(s[f.k], f.unit, f, ls);
|
||||
if (f.bar) {
|
||||
var raw = s[f.k];
|
||||
if (typeof raw === 'number' && !isNaN(raw)) {
|
||||
var pct = 0, bcol;
|
||||
if (f.bar.kind === 'abs') {
|
||||
pct = (raw / (f.bar.max || 100)) * 100;
|
||||
bcol = _liveBarColor(pct, f.bar.good);
|
||||
} else {
|
||||
var mx = colMax[f.k] || 0;
|
||||
pct = mx > 0 ? (Math.abs(raw) / mx) * 100 : 0;
|
||||
bcol = (f.bar.good === 'signed') ? (raw < 0 ? '#c0392b' : '#5a8a5e') : _liveBarColor(pct, f.bar.good);
|
||||
}
|
||||
cell = '<div style="line-height:1.1">'+cell+'</div>' + _liveMiniBar(pct, bcol);
|
||||
}
|
||||
}
|
||||
html += '<td style="text-align:'+align+';padding:.4rem .5rem;font-variant-numeric:tabular-nums;vertical-align:top">'+cell+'</td>';
|
||||
});
|
||||
html += '<td style="text-align:center">'+(paused
|
||||
? '<span style="font-size:.6rem;color:#9a9a9a;white-space:nowrap" title="seit '+_fmtSince(lastSeen)+' keine Aktivität — Session bleibt offen">⏸ '+_fmtSince(lastSeen)+'</span>'
|
||||
|
||||
Reference in New Issue
Block a user