Cockpit-Triage + Sterne->%+Balken durchgaengig (teacher.html)
Cockpit (Uebersicht): - Farben auf kanonische Palette (--ivg..--ivr = _pctColor-Hex); ivAmp- Schwellen auf 50/60/80/90. - ivTriage neu: >=90 Fordern (kann alles) / 50-89 Laeuft gut / <50 & fleissig -> Begleiten / <50 sonst -> Foerdern. Bestaetigen entfaellt. - Fleiss im Klassenvergleich (_fleissig = 0.5*Zeit + 0.5*Durchgaenge >= Schnitt), fuer HEUTE- und insgesamt-Triage. IV_META-Texte angepasst. Sterne raus -> %+Balken (colorblind-freundlich): - _pctBadge (Run-Karten) neu. Alle 7 Run-Karten + generische Sonnensystem- Karte auf %+Balken (Busfahrt/Flug rechnen /3). Heli Start/Landung/Gesamt- Spalten in Aktuell auf _pctBar. Spectator, Drilldown (ivStars, Top-Performers), fmtMetric, Heli-Detail: Balken bzw. %. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+68
-48
@@ -667,6 +667,16 @@ function _pctBar(pct) {
|
|||||||
+ '<div style="margin-top:2px;height:4px;border-radius:99px;background:rgba(0,0,0,.07);overflow:hidden"><div style="height:100%;width:'+pct+'%;background:'+c+'"></div></div>'
|
+ '<div style="margin-top:2px;height:4px;border-radius:99px;background:rgba(0,0,0,.07);overflow:hidden"><div style="height:100%;width:'+pct+'%;background:'+c+'"></div></div>'
|
||||||
+ '</div>';
|
+ '</div>';
|
||||||
}
|
}
|
||||||
|
// Größere %+Balken-Plakette für Run-Karten (Ersatz für Sterne) — Balken statt
|
||||||
|
// Sterne, weil auch für Farbenblinde am Füllstand unterscheidbar.
|
||||||
|
function _pctBadge(pct) {
|
||||||
|
if (pct == null || isNaN(pct)) return '<span style="color:#9a9a9a">—</span>';
|
||||||
|
pct = Math.max(0, Math.min(100, Math.round(pct)));
|
||||||
|
var c = _pctColor(pct);
|
||||||
|
return '<span style="display:inline-flex;align-items:center;gap:.4rem">'
|
||||||
|
+ '<span style="width:64px;height:8px;border-radius:99px;background:rgba(0,0,0,.09);overflow:hidden;display:inline-block"><span style="display:block;height:100%;width:'+pct+'%;background:'+c+'"></span></span>'
|
||||||
|
+ '<b style="color:'+c+';font-size:.85rem;font-variant-numeric:tabular-nums">'+pct+' %</b></span>';
|
||||||
|
}
|
||||||
// Das EINE Leistungs-% (0–100) je Sim aus dem Live-State. Sims mit eigenem
|
// Das EINE Leistungs-% (0–100) je Sim aus dem Live-State. Sims mit eigenem
|
||||||
// 0–100-Score nutzen ihn direkt; sonst aus den vorhandenen Kennzahlen abgeleitet.
|
// 0–100-Score nutzen ihn direkt; sonst aus den vorhandenen Kennzahlen abgeleitet.
|
||||||
function _leistungPct(simId, s) {
|
function _leistungPct(simId, s) {
|
||||||
@@ -944,19 +954,13 @@ var LIVE_PRIMARY_FIELDS = {
|
|||||||
: '<span style="color:'+c+';font-weight:700">'+n+'</span>';
|
: '<span style="color:'+c+';font-weight:700">'+n+'</span>';
|
||||||
}},
|
}},
|
||||||
{k:'startScore', label:'🛫 Start', fmt:function(v){
|
{k:'startScore', label:'🛫 Start', fmt:function(v){
|
||||||
var n = Math.max(0, Math.min(5, v||0));
|
return v==null ? '<span style="color:#9a9a9a">—</span>' : _pctBar(Math.round(Math.max(0,Math.min(5,v))/5*100));
|
||||||
var c = n>=4 ? '#5a8a5e' : n>=2 ? '#c9913a' : '#c07a6b';
|
|
||||||
return v==null ? '—' : '<span style="color:'+c+';font-weight:700">'+'★'.repeat(n)+'<span style="color:#ccc">'+'★'.repeat(5-n)+'</span></span>';
|
|
||||||
}},
|
}},
|
||||||
{k:'landingScore', label:'🛬 Landung', fmt:function(v){
|
{k:'landingScore', label:'🛬 Landung', fmt:function(v){
|
||||||
var n = Math.max(0, Math.min(5, v||0));
|
return v==null ? '<span style="color:#9a9a9a">—</span>' : _pctBar(Math.round(Math.max(0,Math.min(5,v))/5*100));
|
||||||
var c = n>=4 ? '#5a8a5e' : n>=2 ? '#c9913a' : '#c07a6b';
|
|
||||||
return v==null ? '—' : '<span style="color:'+c+';font-weight:700">'+'★'.repeat(n)+'<span style="color:#ccc">'+'★'.repeat(5-n)+'</span></span>';
|
|
||||||
}},
|
}},
|
||||||
{k:'totalStars', label:'⭐ Gesamt', fmt:function(v){
|
{k:'totalStars', label:'⭐ Gesamt', fmt:function(v){
|
||||||
var n = Math.max(0, Math.min(5, v||0));
|
return v==null ? '<span style="color:#9a9a9a">—</span>' : _pctBar(Math.round(Math.max(0,Math.min(5,v))/5*100));
|
||||||
var c = n>=4 ? '#5a8a5e' : n>=2 ? '#c9913a' : '#c07a6b';
|
|
||||||
return v==null ? '—' : '<span style="color:'+c+';font-weight:800;font-size:.82rem">'+'★'.repeat(n)+'<span style="color:#ccc">'+'★'.repeat(5-n)+'</span></span>';
|
|
||||||
}},
|
}},
|
||||||
{k:'failed', label:'Status', align:'center', fmt:function(v, row){
|
{k:'failed', label:'Status', align:'center', fmt:function(v, row){
|
||||||
var s = row.state||{};
|
var s = row.state||{};
|
||||||
@@ -2536,7 +2540,7 @@ async function openLiveSessionDetail(sessionKey) {
|
|||||||
var dur = _liveFmtMin(sess.durationSec||0);
|
var dur = _liveFmtMin(sess.durationSec||0);
|
||||||
var stars = sess.stars;
|
var stars = sess.stars;
|
||||||
var starsHtml = stars != null
|
var starsHtml = stars != null
|
||||||
? '<span style="color:#e8c547;font-size:1.1rem">'+'★'.repeat(stars)+'</span><span style="color:#ccc;font-size:1.1rem">'+'★'.repeat(5-stars)+'</span>'
|
? _pctBadge(Math.round(Math.max(0,Math.min(5,stars))/5*100))
|
||||||
: '<span style="color:#aaa;font-size:.74rem">noch keine Wertung</span>';
|
: '<span style="color:#aaa;font-size:.74rem">noch keine Wertung</span>';
|
||||||
var statusLabels = {good:'<span style="color:#5a8a5e;font-weight:700">✓ gut</span>',
|
var statusLabels = {good:'<span style="color:#5a8a5e;font-weight:700">✓ gut</span>',
|
||||||
mid:'<span style="color:#c9913a;font-weight:700">⚠ mittel</span>',
|
mid:'<span style="color:#c9913a;font-weight:700">⚠ mittel</span>',
|
||||||
@@ -2578,8 +2582,8 @@ async function openLiveSessionDetail(sessionKey) {
|
|||||||
if (nested.missionTitle) rows.push(['Auftrag', nested.missionTitle]);
|
if (nested.missionTitle) rows.push(['Auftrag', nested.missionTitle]);
|
||||||
if (nested.planCorrect != null) rows.push(['🗺️ Treffer', nested.planCorrect + ' / ' + (nested.planTotal||'?')]);
|
if (nested.planCorrect != null) rows.push(['🗺️ Treffer', nested.planCorrect + ' / ' + (nested.planTotal||'?')]);
|
||||||
if (nested.planPct != null) rows.push(['🎯 Genauigkeit', nested.planPct + ' %']);
|
if (nested.planPct != null) rows.push(['🎯 Genauigkeit', nested.planPct + ' %']);
|
||||||
if (nested.startScore != null) rows.push(['🛫 Start', '★'.repeat(nested.startScore||0) + '☆'.repeat(5-(nested.startScore||0))]);
|
if (nested.startScore != null) rows.push(['🛫 Start', Math.round((nested.startScore||0)/5*100) + ' %']);
|
||||||
if (nested.landingScore != null)rows.push(['🛬 Landung', '★'.repeat(nested.landingScore||0) + '☆'.repeat(5-(nested.landingScore||0))]);
|
if (nested.landingScore != null)rows.push(['🛬 Landung', Math.round((nested.landingScore||0)/5*100) + ' %']);
|
||||||
if (nested.planTakeover) rows.push(['Routenplanung', '<span style="color:#c07a6b">Chefpilot übernahm</span>']);
|
if (nested.planTakeover) rows.push(['Routenplanung', '<span style="color:#c07a6b">Chefpilot übernahm</span>']);
|
||||||
if (nested.failed) rows.push(['Mission', '<span style="color:#c07a6b">gescheitert</span>'+(nested.failReason?': '+esc(nested.failReason):'')]);
|
if (nested.failed) rows.push(['Mission', '<span style="color:#c07a6b">gescheitert</span>'+(nested.failReason?': '+esc(nested.failReason):'')]);
|
||||||
}
|
}
|
||||||
@@ -2844,8 +2848,8 @@ function startLiveStatusPoll() {
|
|||||||
|
|
||||||
// === INTERVENTIONS-ÜBERSICHT (neu, oben im Übersicht-Tab) ===
|
// === INTERVENTIONS-ÜBERSICHT (neu, oben im Übersicht-Tab) ===
|
||||||
var IV_CSS = ''
|
var IV_CSS = ''
|
||||||
+ '.iv-wrap{--ivg:#3e9b63;--ivli:#7aa03a;--ivy:#dcae35;--ivo:#db8a33;--ivr:#cc4a37;'
|
+ '.iv-wrap{--ivg:#1f7a3d;--ivli:#7cb342;--ivy:#f9c33b;--ivo:#ef8b2c;--ivr:#d8453a;'
|
||||||
+ '--ivns:#5b7fa6;--ivnm:#3f8f8f;--ivnw:#8f6f9c;--ivngd:#6a72b0;--ivni:#8c857c;'
|
+ '--ivns:#b04a3e;--ivnm:#155e2a;--ivnw:#d98b2c;--ivngd:#6a72b0;--ivni:#8c857c;'
|
||||||
+ '--ivtrack:rgba(41,52,46,.13);--ivline:rgba(0,0,0,.08);--ivsoft:#f5f3ec;--ivmut:#8a8a8a;font-size:14px}'
|
+ '--ivtrack:rgba(41,52,46,.13);--ivline:rgba(0,0,0,.08);--ivsoft:#f5f3ec;--ivmut:#8a8a8a;font-size:14px}'
|
||||||
+ '.iv-wrap *{box-sizing:border-box}'
|
+ '.iv-wrap *{box-sizing:border-box}'
|
||||||
+ '.iv-h1{font-size:1.15rem;font-weight:800;margin:0 0 14px;color:#3d4a44}'
|
+ '.iv-h1{font-size:1.15rem;font-weight:800;margin:0 0 14px;color:#3d4a44}'
|
||||||
@@ -2907,7 +2911,7 @@ var IV_CSS = ''
|
|||||||
+ '@media (prefers-reduced-motion:reduce){.iv-dotA{animation:none!important}}';
|
+ '@media (prefers-reduced-motion:reduce){.iv-dotA{animation:none!important}}';
|
||||||
|
|
||||||
function ivInjectCss(){ if(!document.getElementById('iv-css')){ var s=document.createElement('style'); s.id='iv-css'; s.textContent=IV_CSS; document.head.appendChild(s); } }
|
function ivInjectCss(){ if(!document.getElementById('iv-css')){ var s=document.createElement('style'); s.id='iv-css'; s.textContent=IV_CSS; document.head.appendChild(s); } }
|
||||||
function ivAmp(v){ return v<38?'iv-r':v<50?'iv-o':v<62?'iv-y':v<75?'iv-li':'iv-g'; }
|
function ivAmp(v){ return v<50?'iv-r':v<60?'iv-o':v<80?'iv-y':v<90?'iv-li':'iv-g'; } // = _pctColor-Grenzen
|
||||||
function ivErf(v){
|
function ivErf(v){
|
||||||
if (v==null||isNaN(v)) return '<span class="iv-erf"><span class="iv-ebar"><i style="width:0"></i></span><b style="color:var(--ivmut)">–</b></span>';
|
if (v==null||isNaN(v)) return '<span class="iv-erf"><span class="iv-ebar"><i style="width:0"></i></span><b style="color:var(--ivmut)">–</b></span>';
|
||||||
v=Math.round(v);
|
v=Math.round(v);
|
||||||
@@ -2937,16 +2941,18 @@ var IV_ICON={
|
|||||||
good:'<path d="m4 15 5-5 4 4 7-8"/><path d="M17 6h3v3"/>',
|
good:'<path d="m4 15 5-5 4 4 7-8"/><path d="M17 6h3v3"/>',
|
||||||
idle:'<circle cx="12" cy="12" r="9" stroke-dasharray="3 3"/><path d="M12 8v4l2 2"/>',
|
idle:'<circle cx="12" cy="12" r="9" stroke-dasharray="3 3"/><path d="M12 8v4l2 2"/>',
|
||||||
calm:'<circle cx="12" cy="12" r="9"/><path d="m8.5 12.5 2.5 2.5 4.5-5"/>'};
|
calm:'<circle cx="12" cy="12" r="9"/><path d="m8.5 12.5 2.5 2.5 4.5-5"/>'};
|
||||||
var IV_META={support:['Fördern','zuletzt schwächere Läufe','--ivns'],more:['Fordern','zuletzt sicher, kaum gefordert','--ivnm'],
|
var IV_META={support:['Fördern','schwach (< 50 %) — braucht Hilfe','--ivns'],more:['Fordern','kann alles (≥ 90 %) — mehr herausfordern','--ivnm'],
|
||||||
work:['Begleiten','zuletzt viel gearbeitet, wenig Ertrag','--ivnw'],good:['Bestätigen','zuletzt besser geworden','--ivngd'],
|
work:['Begleiten','schwach, aber fleißiger als der Klassenschnitt','--ivnw'],good:['Bestätigen','zuletzt besser geworden','--ivngd'],
|
||||||
calm:['Läuft gut','solide unterwegs, keine Auffälligkeiten','--ivg'],idle:['Nicht aktiv','länger nicht mehr dran (oder noch nie)','--ivni']};
|
calm:['Läuft gut','solide unterwegs (50–89 %)','--ivg'],idle:['Nicht aktiv','länger nicht mehr dran (oder noch nie)','--ivni']};
|
||||||
var IV_WEEK_CAP = 10800; // eine Woche zählt max. 3 h aktive Zeit (Ausreißer-Schutz)
|
var IV_WEEK_CAP = 10800; // eine Woche zählt max. 3 h aktive Zeit (Ausreißer-Schutz)
|
||||||
|
|
||||||
// Rendert die 5 Triage-Karten (support/more/work/good/idle) aus fertigen Gruppen.
|
// Rendert die 5 Triage-Karten (support/more/work/good/idle) aus fertigen Gruppen.
|
||||||
// Wiederverwendet für „HEUTE" und „insgesamt". todayMode passt Untertitel/Tooltips an.
|
// Wiederverwendet für „HEUTE" und „insgesamt". todayMode passt Untertitel/Tooltips an.
|
||||||
function _ivTriageCards(groups, maxSec, todayMode){
|
function _ivTriageCards(groups, maxSec, todayMode){
|
||||||
var h='<div class="iv-grid">';
|
var h='<div class="iv-grid">';
|
||||||
['support','more','work','good','calm','idle'].forEach(function(key){
|
// 4 Handlungs-Felder (aus dem Leistungs-% + Fleiß) + „Nicht aktiv". „Bestätigen"
|
||||||
|
// entfällt — geht in „Läuft gut" auf.
|
||||||
|
['support','work','calm','more','idle'].forEach(function(key){
|
||||||
var rows=groups[key], m=IV_META[key];
|
var rows=groups[key], m=IV_META[key];
|
||||||
var sub=(todayMode && key==='idle')?'heute noch nicht dran':m[1];
|
var sub=(todayMode && key==='idle')?'heute noch nicht dran':m[1];
|
||||||
h+='<div class="iv-card" style="--iva:var('+m[2]+')"><div class="iv-chead"><span class="iv-ic"><svg viewBox="0 0 24 24">'+IV_ICON[key]+'</svg></span><div class="iv-ct"><h4>'+m[0]+'</h4><p>'+sub+'</p></div><span class="iv-cn num">'+rows.length+'</span></div>';
|
h+='<div class="iv-card" style="--iva:var('+m[2]+')"><div class="iv-chead"><span class="iv-ic"><svg viewBox="0 0 24 24">'+IV_ICON[key]+'</svg></span><div class="iv-ct"><h4>'+m[0]+'</h4><p>'+sub+'</p></div><span class="iv-cn num">'+rows.length+'</span></div>';
|
||||||
@@ -2968,14 +2974,15 @@ function _ivTriageCards(groups, maxSec, todayMode){
|
|||||||
// Gesehene, kein Allzeit-Rückblick): recentErfolg = Ø letzte 3 Läufe, daysSince =
|
// Gesehene, kein Allzeit-Rückblick): recentErfolg = Ø letzte 3 Läufe, daysSince =
|
||||||
// Tage seit letztem Lauf, recentPlays = Läufe der letzten 14 Tage, rising =
|
// Tage seit letztem Lauf, recentPlays = Läufe der letzten 14 Tage, rising =
|
||||||
// zuletzt besser als der bisherige Schnitt.
|
// zuletzt besser als der bisherige Schnitt.
|
||||||
function ivTriage(recErf, daysSince, recentPlays, rising){
|
// Triage aus dem Leistungs-% (0–100) × Fleiß-im-Klassenvergleich.
|
||||||
if (daysSince == null || daysSince > 14) return 'idle'; // länger nicht dran / nie
|
// ≥90 → Fordern (kann alles) · 50–89 → Läuft gut · <50 & fleißig → Begleiten ·
|
||||||
if (recErf == null) return 'idle';
|
// <50 & nicht fleißig → Fördern. Länger als 14 Tage weg / kein Wert → Nicht aktiv.
|
||||||
if (recErf < 48 && recentPlays >= 5) return 'work'; // zuletzt viel, wenig Ertrag
|
function ivTriage(erf, daysSince, fleissig){
|
||||||
if (recErf < 48) return 'support'; // zuletzt schwach
|
if (daysSince == null || daysSince > 14) return 'idle';
|
||||||
if (recErf >= 65 && recentPlays <= 2) return 'more'; // zuletzt sicher, kaum gefordert
|
if (erf == null) return 'idle';
|
||||||
if (rising) return 'good'; // zuletzt besser geworden
|
if (erf >= 90) return 'more'; // Fordern
|
||||||
return null; // läuft rund
|
if (erf >= 50) return 'calm'; // Läuft gut
|
||||||
|
return fleissig ? 'work' : 'support'; // Begleiten : Fördern
|
||||||
}
|
}
|
||||||
function ivZuletzt(days){
|
function ivZuletzt(days){
|
||||||
if (days == null) return '<span class="iv-last iv-last-old">nie</span>';
|
if (days == null) return '<span class="iv-last iv-last-old">nie</span>';
|
||||||
@@ -3012,6 +3019,22 @@ function ivBuildOverview(cockpit, weeks, studentsCount, matrix, todaySummary, to
|
|||||||
var playsById={};
|
var playsById={};
|
||||||
((matrix&&matrix.students)||[]).forEach(function(s){ playsById[s.id]=(matrix.totals[s.id]||{}).plays||0; });
|
((matrix&&matrix.students)||[]).forEach(function(s){ playsById[s.id]=(matrix.totals[s.id]||{}).plays||0; });
|
||||||
|
|
||||||
|
// Fleiß-Referenz (Klassenschnitt): aktive Zeit (Wochen-Buckets) + Durchgänge.
|
||||||
|
var _secTot={}, _secSum=0, _secCnt=0, _playSum=0, _playCnt=0;
|
||||||
|
cs.forEach(function(s){
|
||||||
|
var sid=s.studentId||s.id;
|
||||||
|
var w=wById[sid]; var tsec=w?w.buckets.reduce(function(a,b){return a+Math.min(IV_WEEK_CAP,b.sum||0);},0):0;
|
||||||
|
_secTot[sid]=tsec; if(tsec>0){_secSum+=tsec;_secCnt++;}
|
||||||
|
var pl=playsById[sid]||0; if(pl>0){_playSum+=pl;_playCnt++;}
|
||||||
|
});
|
||||||
|
var _avgSec=_secCnt?_secSum/_secCnt:1, _avgPlay=_playCnt?_playSum/_playCnt:1;
|
||||||
|
// „fleißig" = kombiniert (Zeit + Durchgänge) mindestens Klassenschnitt.
|
||||||
|
function _fleissig(sid, plays){
|
||||||
|
var t=(_secTot[sid]||0)/Math.max(1,_avgSec);
|
||||||
|
var p=(plays!=null?plays:(playsById[sid]||0))/Math.max(1,_avgPlay);
|
||||||
|
return (0.5*t + 0.5*p) >= 1;
|
||||||
|
}
|
||||||
|
|
||||||
var groups={support:[],more:[],work:[],good:[],calm:[],idle:[]}, erfSum=0,erfN=0, weekTotals=[];
|
var groups={support:[],more:[],work:[],good:[],calm:[],idle:[]}, erfSum=0,erfN=0, weekTotals=[];
|
||||||
cs.forEach(function(s){
|
cs.forEach(function(s){
|
||||||
var sid=s.studentId||s.id;
|
var sid=s.studentId||s.id;
|
||||||
@@ -3025,7 +3048,7 @@ function ivBuildOverview(cockpit, weeks, studentsCount, matrix, todaySummary, to
|
|||||||
var rising=(recErf!=null && s.earlyErfolg!=null && recErf > s.earlyErfolg+10); // letzte 3 besser als frühere
|
var rising=(recErf!=null && s.earlyErfolg!=null && recErf > s.earlyErfolg+10); // letzte 3 besser als frühere
|
||||||
if(recErf!=null){ erfSum+=recErf; erfN++; }
|
if(recErf!=null){ erfSum+=recErf; erfN++; }
|
||||||
weekTotals.push(secs);
|
weekTotals.push(secs);
|
||||||
var g=ivTriage(recErf, days, rplays, rising);
|
var g=ivTriage(recErf, days, _fleissig(sid, playsById[sid]));
|
||||||
var row={sid:sid, name:s.displayName, erf:recErf, status:s.status, secs:secs, days:days};
|
var row={sid:sid, name:s.displayName, erf:recErf, status:s.status, secs:secs, days:days};
|
||||||
if(g) groups[g].push(row); else groups.calm.push(row);
|
if(g) groups[g].push(row); else groups.calm.push(row);
|
||||||
});
|
});
|
||||||
@@ -3100,15 +3123,12 @@ function ivBuildOverview(cockpit, weeks, studentsCount, matrix, todaySummary, to
|
|||||||
var tErf=todayErfById[sid];
|
var tErf=todayErfById[sid];
|
||||||
var w2=wById[sid], secs2=w2?w2.buckets.map(function(b){return Math.min(IV_WEEK_CAP, b.sum||0);}):[];
|
var w2=wById[sid], secs2=w2?w2.buckets.map(function(b){return Math.min(IV_WEEK_CAP, b.sum||0);}):[];
|
||||||
while(secs2.length<10) secs2.unshift(0);
|
while(secs2.length<10) secs2.unshift(0);
|
||||||
// Totalversagen heute (ein Lauf < 40) → immer Fördern, egal wie der Schnitt ist.
|
var g2 = !played ? 'idle' : ivTriage(tErf, 0, _fleissig(sid, todayPlaysById[sid]));
|
||||||
var g2 = !played ? 'idle'
|
|
||||||
: (todayMinById[sid]!=null && todayMinById[sid] < 40) ? 'support'
|
|
||||||
: ivTriage(tErf, 0, todayPlaysById[sid], false);
|
|
||||||
var row2={sid:sid, name:s.displayName, erf:played?tErf:null, status:s.status, secs:secs2, days:played?0:(s.daysSince!=null?s.daysSince:null)};
|
var row2={sid:sid, name:s.displayName, erf:played?tErf:null, status:s.status, secs:secs2, days:played?0:(s.daysSince!=null?s.daysSince:null)};
|
||||||
if(g2) heuteGroups[g2].push(row2); else heuteGroups.calm.push(row2);
|
if(g2) heuteGroups[g2].push(row2); else heuteGroups.calm.push(row2);
|
||||||
});
|
});
|
||||||
var heutePlayed=Object.keys(todayPlaysById).length;
|
var heutePlayed=Object.keys(todayPlaysById).length;
|
||||||
var need=groups.support.length+groups.more.length+groups.work.length+groups.good.length+groups.idle.length;
|
var need=groups.support.length+groups.work.length+groups.more.length+groups.idle.length;
|
||||||
|
|
||||||
// 1) HEUTE (oben) — genau dieselbe Darstellung, nur auf heute gefiltert
|
// 1) HEUTE (oben) — genau dieselbe Darstellung, nur auf heute gefiltert
|
||||||
h+='<div class="iv-sec">Empfohlene Aktivität für dich — <b style="color:#2a5a2e">HEUTE</b><span>'+heutePlayed+' heute aktiv</span></div>';
|
h+='<div class="iv-sec">Empfohlene Aktivität für dich — <b style="color:#2a5a2e">HEUTE</b><span>'+heutePlayed+' heute aktiv</span></div>';
|
||||||
@@ -3170,7 +3190,7 @@ function ivAvatar(emoji){
|
|||||||
}
|
}
|
||||||
return '<span class="em" style="font-size:1.6rem">'+(emoji||'🧑🎓')+'</span>';
|
return '<span class="em" style="font-size:1.6rem">'+(emoji||'🧑🎓')+'</span>';
|
||||||
}
|
}
|
||||||
function ivStars(n){ return (typeof ggsStarsHtml==='function') ? ggsStarsHtml(n) : ('★'.repeat(Math.max(0,Math.min(5,Math.round(n)))) ); }
|
function ivStars(n){ return _pctBar(Math.round(Math.max(0,Math.min(5,n||0))/5*100)); }
|
||||||
async function ivOpenStudent(sid){
|
async function ivOpenStudent(sid){
|
||||||
ivInjectCss();
|
ivInjectCss();
|
||||||
var d = await api('live?student_runs='+sid);
|
var d = await api('live?student_runs='+sid);
|
||||||
@@ -3381,7 +3401,7 @@ function ivTodayAvatar(emoji, px) {
|
|||||||
function ivBuildTodaySection(today, modById) {
|
function ivBuildTodaySection(today, modById) {
|
||||||
var tt = (today && today.topToday) || [];
|
var tt = (today && today.topToday) || [];
|
||||||
var prog = (today && today.progress) || [];
|
var prog = (today && today.progress) || [];
|
||||||
var starsHtml = (typeof ggsStarsHtml === 'function') ? ggsStarsHtml : function(n){ return '★'.repeat(Math.max(0,Math.min(5,n))); };
|
var starsHtml = function(n){ return _pctBadge(Math.round(Math.max(0,Math.min(5,n||0))/5*100)); };
|
||||||
|
|
||||||
// Heute neu erreichte Abzeichen: Bedingung jetzt erfüllt, vor heute nicht.
|
// Heute neu erreichte Abzeichen: Bedingung jetzt erfüllt, vor heute nicht.
|
||||||
var newBadges = [];
|
var newBadges = [];
|
||||||
@@ -4689,7 +4709,7 @@ function renderModDetail() {
|
|||||||
|
|
||||||
stu.forEach(function(s) {
|
stu.forEach(function(s) {
|
||||||
var stars = s.best_stars || 0;
|
var stars = s.best_stars || 0;
|
||||||
var starStr = '★'.repeat(stars) + '<span style="opacity:.3">'+'★'.repeat(5-stars)+'</span>';
|
var starStr = _pctBar(Math.round(Math.max(0,Math.min(5,stars))/5*100));
|
||||||
var lastDate = s.last_played ? new Date(s.last_played.replace(' ','T')+'Z').toLocaleDateString('de-AT', {day:'2-digit',month:'2-digit'}) : '—';
|
var lastDate = s.last_played ? new Date(s.last_played.replace(' ','T')+'Z').toLocaleDateString('de-AT', {day:'2-digit',month:'2-digit'}) : '—';
|
||||||
var dur = s.total_duration_ms ? Math.round(s.total_duration_ms/60000)+' min' : '—';
|
var dur = s.total_duration_ms ? Math.round(s.total_duration_ms/60000)+' min' : '—';
|
||||||
var avatar = (typeof s.emoji === 'string' && s.emoji.indexOf('avatar:') === 0)
|
var avatar = (typeof s.emoji === 'string' && s.emoji.indexOf('avatar:') === 0)
|
||||||
@@ -4948,7 +4968,7 @@ function renderSonnensystemSessionTeacher(sess) {
|
|||||||
}
|
}
|
||||||
var quote = total > 0 ? Math.round(done/total*100) : 0;
|
var quote = total > 0 ? Math.round(done/total*100) : 0;
|
||||||
var starsHtml = sess.complete
|
var starsHtml = sess.complete
|
||||||
? '<span class="run-stars">'+'★'.repeat(stars)+'<span class="run-stars-empty">'+'★'.repeat(5-stars)+'</span></span>'
|
? _pctBadge(quote)
|
||||||
: '<span style="font-size:.7rem;color:#c9913a;font-weight:700">⏸ läuft / abgebrochen</span>';
|
: '<span style="font-size:.7rem;color:#c9913a;font-weight:700">⏸ läuft / abgebrochen</span>';
|
||||||
var statusEmoji = sess.complete ? '🪐' : '⏳';
|
var statusEmoji = sess.complete ? '🪐' : '⏳';
|
||||||
var statusLabel = sess.complete ? 'Aufgaben-Runde abgeschlossen' : 'Teil-Durchlauf (nicht abgeschlossen)';
|
var statusLabel = sess.complete ? 'Aufgaben-Runde abgeschlossen' : 'Teil-Durchlauf (nicht abgeschlossen)';
|
||||||
@@ -5033,7 +5053,7 @@ function renderRunsLineChart(simId, runs) {
|
|||||||
}
|
}
|
||||||
function fmtMetric(m, v){
|
function fmtMetric(m, v){
|
||||||
if (v === null) return '–';
|
if (v === null) return '–';
|
||||||
if (m.type === 'stars' || m.type === 'starsSmall') return Math.round(v) + '★';
|
if (m.type === 'stars' || m.type === 'starsSmall') return _pctBar(Math.round(Math.max(0,Math.min(5,v))/5*100));
|
||||||
if (m.type === 'pct' || m.type === 'pctInv' || m.type === 'ratio') return Math.round(v) + ' %';
|
if (m.type === 'pct' || m.type === 'pctInv' || m.type === 'ratio') return Math.round(v) + ' %';
|
||||||
if (m.type === 'temp') return (Math.round(v*10)/10).toLocaleString('de-AT') + ' °C';
|
if (m.type === 'temp') return (Math.round(v*10)/10).toLocaleString('de-AT') + ' °C';
|
||||||
var n = Math.abs(v) >= 100 ? Math.round(v) : Math.round(v * 10) / 10;
|
var n = Math.abs(v) >= 100 ? Math.round(v) : Math.round(v * 10) / 10;
|
||||||
@@ -5136,7 +5156,7 @@ function renderRunDetailCard(simId, run, idx) {
|
|||||||
|
|
||||||
function renderSonnensystemRun(dt, dur, r, d) {
|
function renderSonnensystemRun(dt, dur, r, d) {
|
||||||
var stars = Math.max(0, Math.min(5, r.stars || (d.stars||0)));
|
var stars = Math.max(0, Math.min(5, r.stars || (d.stars||0)));
|
||||||
var starsHtml = '<span class="run-stars">'+'★'.repeat(stars)+'<span class="run-stars-empty">'+'★'.repeat(5-stars)+'</span></span>';
|
var starsHtml = _pctBadge(Math.round(stars/5*100));
|
||||||
var done = d.completed || 0;
|
var done = d.completed || 0;
|
||||||
var total = d.total || 0;
|
var total = d.total || 0;
|
||||||
var quote = d.scoreQuote != null ? d.scoreQuote : (total > 0 ? Math.round(done/total*100) : 0);
|
var quote = d.scoreQuote != null ? d.scoreQuote : (total > 0 ? Math.round(done/total*100) : 0);
|
||||||
@@ -5196,7 +5216,7 @@ function renderEnergiemanagerRun(dt, dur, r, d) {
|
|||||||
+ '</div>';
|
+ '</div>';
|
||||||
}
|
}
|
||||||
var stars = Math.max(0, Math.min(5, r.stars || 0));
|
var stars = Math.max(0, Math.min(5, r.stars || 0));
|
||||||
var starsHtml = '<span class="run-stars">'+'★'.repeat(stars)+'<span class="run-stars-empty">'+'★'.repeat(5-stars)+'</span></span>';
|
var starsHtml = _pctBadge(Math.round(stars/5*100));
|
||||||
var setIcon = d.setIcon || '⚡';
|
var setIcon = d.setIcon || '⚡';
|
||||||
var setTitle = d.setTitle || d.setId || '?';
|
var setTitle = d.setTitle || d.setId || '?';
|
||||||
var blocksB = d.blocksBalanced || 0;
|
var blocksB = d.blocksBalanced || 0;
|
||||||
@@ -5238,7 +5258,7 @@ function renderEnergiemanagerRun(dt, dur, r, d) {
|
|||||||
|
|
||||||
function renderEuWerkstattRun(dt, dur, r, d) {
|
function renderEuWerkstattRun(dt, dur, r, d) {
|
||||||
var stars = Math.max(0, Math.min(5, r.stars || 0));
|
var stars = Math.max(0, Math.min(5, r.stars || 0));
|
||||||
var starsHtml = '<span class="run-stars">'+'★'.repeat(stars)+'<span class="run-stars-empty">'+'★'.repeat(5-stars)+'</span></span>';
|
var starsHtml = _pctBadge(Math.round(stars/5*100));
|
||||||
var endTypeLabel = ({success:'🏆 Gewonnen', mixed:'⚖️ Teilweise', rejected:'❌ Verloren'})[d.endType] || '—';
|
var endTypeLabel = ({success:'🏆 Gewonnen', mixed:'⚖️ Teilweise', rejected:'❌ Verloren'})[d.endType] || '—';
|
||||||
var endTypeColor = ({success:'#5a8a5e', mixed:'#c9913a', rejected:'#c07a6b'})[d.endType] || '#8a8a8a';
|
var endTypeColor = ({success:'#5a8a5e', mixed:'#c9913a', rejected:'#c07a6b'})[d.endType] || '#8a8a8a';
|
||||||
var diffLabel = d.difficulty === 'expert' ? '🦉 Profi' : '🌱 Einfach';
|
var diffLabel = d.difficulty === 'expert' ? '🦉 Profi' : '🌱 Einfach';
|
||||||
@@ -5285,8 +5305,8 @@ function renderEuWerkstattRun(dt, dur, r, d) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderBusfahrtRun(dt, dur, r, d) {
|
function renderBusfahrtRun(dt, dur, r, d) {
|
||||||
var stars = Math.max(0, Math.min(5, r.stars || 0));
|
var stars = Math.max(0, Math.min(3, r.stars || 0)); // Busfahrt: 0–3-Skala
|
||||||
var starsHtml = '<span class="run-stars">'+'★'.repeat(stars)+'<span class="run-stars-empty">'+'★'.repeat(5-stars)+'</span></span>';
|
var starsHtml = _pctBadge(Math.round(stars/3*100));
|
||||||
var lvl = (d.level || 'l1').toString().toUpperCase();
|
var lvl = (d.level || 'l1').toString().toUpperCase();
|
||||||
var budget = d.results && d.results.budget != null ? d.results.budget : (d.budget != null ? d.budget : 0);
|
var budget = d.results && d.results.budget != null ? d.results.budget : (d.budget != null ? d.budget : 0);
|
||||||
var earnings = d.results && d.results.earnings != null ? d.results.earnings : (d.earnings != null ? d.earnings : 0);
|
var earnings = d.results && d.results.earnings != null ? d.results.earnings : (d.earnings != null ? d.earnings : 0);
|
||||||
@@ -5314,7 +5334,7 @@ function renderHeliRun(dt, dur, r, d) {
|
|||||||
// Heli liefert results direkt im d (siehe submitHeliMission in game.html)
|
// Heli liefert results direkt im d (siehe submitHeliMission in game.html)
|
||||||
var res = d.results || d;
|
var res = d.results || d;
|
||||||
var stars = Math.max(0, Math.min(5, r.stars || (d.stars || 0)));
|
var stars = Math.max(0, Math.min(5, r.stars || (d.stars || 0)));
|
||||||
var starsHtml = '<span class="run-stars">'+'★'.repeat(stars)+'<span class="run-stars-empty">'+'★'.repeat(5-stars)+'</span></span>';
|
var starsHtml = _pctBadge(Math.round(stars/5*100));
|
||||||
var failed = !!res.failed;
|
var failed = !!res.failed;
|
||||||
var statusIcon = failed ? '🧑✈️' : '🚁';
|
var statusIcon = failed ? '🧑✈️' : '🚁';
|
||||||
var statusLbl = failed ? 'Chefpilot übernahm' : 'Einsatz erfolgreich';
|
var statusLbl = failed ? 'Chefpilot übernahm' : 'Einsatz erfolgreich';
|
||||||
@@ -5344,8 +5364,8 @@ function renderHeliRun(dt, dur, r, d) {
|
|||||||
+ '</div>'
|
+ '</div>'
|
||||||
+ '<div class="run-stats">'
|
+ '<div class="run-stats">'
|
||||||
+ '<span>🗺️ Navigation: <b>'+planCorrect+'</b>/'+planTotal+' ('+planPct+'%)</span>'
|
+ '<span>🗺️ Navigation: <b>'+planCorrect+'</b>/'+planTotal+' ('+planPct+'%)</span>'
|
||||||
+ '<span>🛫 Start: <b>'+'★'.repeat(startScore)+'</b></span>'
|
+ '<span>🛫 Start: <b style="color:'+_pctColor((startScore||0)/5*100)+'">'+Math.round((startScore||0)/5*100)+' %</b></span>'
|
||||||
+ '<span>🛬 Landung: <b>'+'★'.repeat(landingScore)+'</b></span>'
|
+ '<span>🛬 Landung: <b style="color:'+_pctColor((landingScore||0)/5*100)+'">'+Math.round((landingScore||0)/5*100)+' %</b></span>'
|
||||||
+ (heliName ? '<span>'+esc(heliName)+'</span>' : '')
|
+ (heliName ? '<span>'+esc(heliName)+'</span>' : '')
|
||||||
+ '</div>'
|
+ '</div>'
|
||||||
+ (takeoverNotes.length ? '<div style="margin-top:.4rem;color:#c9913a;font-size:.78rem">⚠ '+takeoverNotes.join(' · ')+'</div>' : '')
|
+ (takeoverNotes.length ? '<div style="margin-top:.4rem;color:#c9913a;font-size:.78rem">⚠ '+takeoverNotes.join(' · ')+'</div>' : '')
|
||||||
@@ -5354,8 +5374,8 @@ function renderHeliRun(dt, dur, r, d) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderFluggesellschaftRun(dt, dur, r, d) {
|
function renderFluggesellschaftRun(dt, dur, r, d) {
|
||||||
var stars = Math.max(0, Math.min(5, r.stars || 0));
|
var stars = Math.max(0, Math.min(3, r.stars || 0)); // Fluggesellschaft: 0–3-Skala
|
||||||
var starsHtml = '<span class="run-stars">'+'★'.repeat(stars)+'<span class="run-stars-empty">'+'★'.repeat(5-stars)+'</span></span>';
|
var starsHtml = _pctBadge(Math.round(stars/3*100));
|
||||||
var lvl = (d.level || 'l1').toString().toUpperCase();
|
var lvl = (d.level || 'l1').toString().toUpperCase();
|
||||||
var res = d.results || d;
|
var res = d.results || d;
|
||||||
var budget = res.budget != null ? res.budget : 0;
|
var budget = res.budget != null ? res.budget : 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user