Lehrer-Live-Zeiten fest auf Europe/Vienna (Server laeuft UTC) -> immer oesterreichische Zeit
_hhmm/_vdt/_fmtHM formatieren mit timeZone Europe/Vienna; _asMs normalisiert Epoch UND MySQL-UTC-Strings. Betrifft Tag-Achse+Balken, Session-Detail, "Heute erledigt", Cockpit "zuletzt aktiv", Reflexionen, letztes Lebenszeichen. Vorher zeigte new Date().getHours() je nach Geraet evtl. UTC. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+19
-9
@@ -1082,7 +1082,7 @@ function _cockpitStudentRow(s) {
|
||||
sub += ' <span style="color:#8a7a4a;font-size:.7rem;font-weight:600;margin-left:.4rem">📵 idle seit '+idleStr+' — Zeit pausiert</span>';
|
||||
}
|
||||
} else if (s.status === 'heute') {
|
||||
var when = s.lastSeen ? new Date(s.lastSeen).toLocaleTimeString('de-AT',{hour:'2-digit',minute:'2-digit'}) : '';
|
||||
var when = s.lastSeen ? _hhmm(s.lastSeen) : '';
|
||||
sub = '<span style="color:#6a6a6a;font-size:.74rem">heute aktiv · zuletzt um '+when+'</span>';
|
||||
} else if (s.status === 'kalt') {
|
||||
sub = '<span style="color:#8a8a8a;font-size:.74rem;font-style:italic">heute noch nicht eingeloggt'+(s.lastSeen?' · zuletzt '+_cockpitFmtAgo(Math.floor((Date.now()-new Date(s.lastSeen).getTime())/1000)):'')+'</span>';
|
||||
@@ -1227,7 +1227,7 @@ async function _loadCockpitReflections() {
|
||||
(data.modules || []).forEach(function(m){ simIcons[m.id] = m.icon || ''; });
|
||||
var html = '<div style="display:flex;flex-direction:column;gap:.55rem">';
|
||||
items.forEach(function(it) {
|
||||
var when = it.recordedAt ? new Date(it.recordedAt).toLocaleString('de-AT', {day:'2-digit',month:'2-digit',hour:'2-digit',minute:'2-digit'}) : '';
|
||||
var when = it.recordedAt ? _vdt(it.recordedAt) : '';
|
||||
html += '<div style="background:#fbfaf6;border:1px solid rgba(0,0,0,.06);border-left:4px solid #c9913a;border-radius:8px;padding:.55rem .8rem">'
|
||||
+ '<div style="display:flex;justify-content:space-between;font-size:.7rem;color:#6a6a6a;margin-bottom:.2rem">'
|
||||
+ '<span><b style="color:#3a3a3a">' + _avatarHtml(it.avatarSlug, it.emoji, 1.2) + ' ' + esc(it.displayName||'—') + '</b>'
|
||||
@@ -1368,8 +1368,19 @@ function _liveIsActive(ls) {
|
||||
return ls.last_seen_ms && (Date.now() - ls.last_seen_ms) <= _LIVE_PAUSE_SEC * 1000;
|
||||
}
|
||||
|
||||
// Uhrzeit HH:MM aus ms-Epoch.
|
||||
function _hhmm(ms){ if(!ms) return '—'; var d=new Date(ms); return ('0'+d.getHours()).slice(-2)+':'+('0'+d.getMinutes()).slice(-2); }
|
||||
// Uhrzeit/Datum IMMER in österreichischer Zeit (Europe/Vienna) — unabhängig von der
|
||||
// Zeitzone des Servers (läuft in UTC) ODER des Anzeigegeräts.
|
||||
// Eingabe: ms-Epoch (Zahl) ODER MySQL-Datetime-String (wird als UTC interpretiert).
|
||||
function _asMs(t){
|
||||
if (t == null || t === '') return 0;
|
||||
if (typeof t === 'number') return t;
|
||||
var s = String(t).trim();
|
||||
if (/^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}/.test(s) && !/[zZ]|[+-]\d{2}:?\d{2}$/.test(s))
|
||||
return new Date(s.replace(' ', 'T') + 'Z').getTime(); // DB-Zeit ist UTC
|
||||
return new Date(s).getTime();
|
||||
}
|
||||
function _hhmm(t){ var ms=_asMs(t); if(!ms) return '—'; return new Date(ms).toLocaleTimeString('de-AT',{timeZone:'Europe/Vienna',hour:'2-digit',minute:'2-digit'}); }
|
||||
function _vdt(t){ var ms=_asMs(t); if(!ms) return '—'; return new Date(ms).toLocaleString('de-AT',{timeZone:'Europe/Vienna',day:'2-digit',month:'2-digit',hour:'2-digit',minute:'2-digit'}); }
|
||||
|
||||
// Avatar als Bild rendern — egal ob der Slug in avatar_slug ODER als
|
||||
// „avatar:<slug>" im emoji_avatar-Feld steckt (Daten sind uneinheitlich).
|
||||
@@ -2160,8 +2171,7 @@ async function renderLiveSummary() {
|
||||
var winSpanMs = winEnd - winStart;
|
||||
function _msToPct(ms) { return ((ms - winStart) / winSpanMs) * 100; }
|
||||
function _fmtHM(ms) {
|
||||
var d = new Date(ms);
|
||||
return ('0'+d.getHours()).slice(-2) + ':' + ('0'+d.getMinutes()).slice(-2);
|
||||
return new Date(ms).toLocaleTimeString('de-AT', {timeZone:'Europe/Vienna', hour:'2-digit', minute:'2-digit'});
|
||||
}
|
||||
|
||||
var activeCount = students.filter(function(s){return (s.sessions||[]).length>0;}).length;
|
||||
@@ -2292,8 +2302,8 @@ async function openLiveSessionDetail(sessionKey) {
|
||||
(sess.simIcon||'📚') + ' ' + (sess.simTitle||sess.simId) + (sess.lessonLabel?' · '+sess.lessonLabel:'');
|
||||
var body = document.getElementById('sessdet-body');
|
||||
var avatarHtml = _avatarHtml(sess.studentAvatarSlug, sess.studentEmoji, 2);
|
||||
var startTime = new Date(sess.startMs).toLocaleString('de-AT',{hour:'2-digit',minute:'2-digit',day:'2-digit',month:'2-digit'});
|
||||
var endTime = new Date(sess.endMs).toLocaleString('de-AT',{hour:'2-digit',minute:'2-digit'});
|
||||
var startTime = _vdt(sess.startMs);
|
||||
var endTime = _hhmm(sess.endMs);
|
||||
var dur = _liveFmtMin(sess.durationSec||0);
|
||||
var stars = sess.stars;
|
||||
var starsHtml = stars != null
|
||||
@@ -2433,7 +2443,7 @@ async function renderSpectator(studentId) {
|
||||
html += '<div style="margin-top:.6rem;padding-top:.5rem;border-top:1px dashed rgba(0,0,0,.1);font-size:.78rem"><span style="color:#8a8a8a">Erfolgreich abgeschlossen: </span><strong>'+wonVal+'</strong></div>';
|
||||
}
|
||||
}
|
||||
html += '<div style="margin-top:.8rem;font-size:.62rem;color:#8a8a8a">Aktualisiert alle 3 Sekunden · letztes Lebenszeichen: '+(data.last_seen_ms?new Date(data.last_seen_ms).toLocaleTimeString('de-AT'):'?')+'</div>';
|
||||
html += '<div style="margin-top:.8rem;font-size:.62rem;color:#8a8a8a">Aktualisiert alle 3 Sekunden · letztes Lebenszeichen: '+(data.last_seen_ms?_hhmm(data.last_seen_ms):'?')+'</div>';
|
||||
host.innerHTML = html;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user