Atlas: Phase A Integrations-Konzept + Cockpit + End-Screen
- Integrations-Konzept dokumentiert (App/docs/integration-konzept.md) - Module-IDs vereinheitlicht: sim-XX -> semantische IDs (klima/heli/fluss/logistik/...) - class_modules um current_level + quiz_enabled Spalten erweitert - modules.php liefert dynamisch aus module_info, inkl. forcedLevel, quizEnabled, play_url, dueDate; neuer Filter ?module_id=X fuer einzeln - schueler.html Cockpit: komplett API-first, teacher_started-Sektion als Auftrags-Block, neue IDs, Sprachregel 4a (Starten statt Spielen) - teacher.html: Hardcoded ALL_MODS entfernt, laedt dynamisch - Design-System: .ggs-endscreen-Komponente fuer einheitliche Modul-Abschluesse - index.html Landing-Links gefixt: App/modul-X -> modul-X, V1-HTMLs auf Front-Controller-Routen (fluss, heli-game, stadt) - 6 Inbox-Auftraege an Instanzen (Klima/Heli/Fluss/Logistik/Lehrplan/Glossar) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+58
-64
@@ -94,16 +94,19 @@ if (!BASE) { var p = location.pathname, i = p.indexOf('/geograsim'); if (i !== -
|
||||
var profile = null;
|
||||
var modules = [];
|
||||
|
||||
var MODULE_MAP = {
|
||||
'sim-05': {name:'Klimasimulator 2D', href:'game.html', img:'assets/img/card-climate.png', icon:'🌡️'},
|
||||
'sim-05-3d': {name:'Klimasimulator 3D', href:'game-3d.html', img:'assets/img/card-climate.png', icon:'🌊'},
|
||||
'sim-07': {name:'Erdbeben & Plattentektonik', href:'erdbeben.html', img:'assets/img/card-tectonic.png', icon:'🌋'},
|
||||
'sim-09': {name:'Energiewende-Planer', href:'energiemix.html', img:'assets/img/card-energy.png', icon:'⚡'},
|
||||
'sim-10': {name:'Lieferketten-Planer', href:'lieferketten.html', img:'assets/img/card-globe.png', icon:'👕'},
|
||||
'sim-11': {name:'Regenwald-Expedition', href:'regenwald.html', img:'assets/img/card-climate.png', icon:'🌴'},
|
||||
'sim-12': {name:'Flussmanagement', href:'fluss.html', img:'assets/img/card-fluss.png', icon:'🏞️'},
|
||||
'sim-13': {name:'Stadt & Raumplanung', href:'stadt.html', img:'assets/img/card-city.png', icon:'🏗️'},
|
||||
'sim-14': {name:'Helikopter-Navigation', href:'heli-game', img:'assets/img/card-heli.png', icon:'🚁'},
|
||||
// Module kommen aus /api/modules?student=1 (inkl. play_url, icon, name, mode).
|
||||
// card_image + Fallback-Beschreibung ergaenzen wir clientseitig.
|
||||
var CARD_IMAGES = {
|
||||
'klima': 'assets/img/card-climate.png',
|
||||
'klima-3d': 'assets/img/card-climate.png',
|
||||
'fluss': 'assets/img/card-fluss.png',
|
||||
'heli': 'assets/img/card-heli.png',
|
||||
'stadt': 'assets/img/card-city.png',
|
||||
'erdbeben': 'assets/img/card-tectonic.png',
|
||||
'energiemix': 'assets/img/card-energy.png',
|
||||
'lieferketten': 'assets/img/card-globe.png',
|
||||
'regenwald': 'assets/img/card-climate.png',
|
||||
'logistik': 'assets/img/card-globe.png'
|
||||
};
|
||||
|
||||
var BADGES = [
|
||||
@@ -153,85 +156,76 @@ async function init() {
|
||||
renderProfile();
|
||||
}
|
||||
|
||||
function buildModuleUrl(mod) {
|
||||
// Bevorzugt play_url aus module_info; Fallback: module_id
|
||||
var base = mod.play_url || mod.id;
|
||||
// Relativ zum aktuellen Front-Controller-Pfad — Apache-Rewrite macht den Rest
|
||||
var url = base.indexOf('/') === 0 ? base : base;
|
||||
if (mod.mode === 'teacher_started' && mod.forcedLevel) {
|
||||
url += (url.indexOf('?') === -1 ? '?' : '&') + 'level=' + mod.forcedLevel;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
async function renderModules() {
|
||||
var html = '';
|
||||
|
||||
// Aktive Aufgaben der Klasse laden
|
||||
var modList = [];
|
||||
try {
|
||||
var assignments = await api('assignments?student=1');
|
||||
if (!assignments.error && assignments.length > 0) {
|
||||
html += '<div style="margin-bottom:1rem">';
|
||||
assignments.forEach(function(a) {
|
||||
var m = MODULE_MAP[a.module_id];
|
||||
if (!m) return;
|
||||
var end = a.ends_at ? '<br><span style="font-size:.6rem;color:#8a8a8a">Zu erledigen bis ' + new Date(a.ends_at).toLocaleDateString('de-AT',{day:'2-digit',month:'2-digit',year:'numeric',hour:'2-digit',minute:'2-digit'}) + '</span>' : '';
|
||||
html += '<a href="'+m.href+'" class="mod-card" style="border-color:#e8833a;border-width:2px;margin-bottom:.5rem">';
|
||||
html += '<div class="mod-img" style="height:80px;background:linear-gradient(135deg,#e8833a,#f4a261)"><span style="font-size:2.5rem">'+m.icon+'</span></div>';
|
||||
html += '<div class="mod-body">';
|
||||
html += '<h3 style="color:#e8833a">📋 '+m.icon+' '+m.name+' — Level '+a.level+'</h3>';
|
||||
html += '<p>Aufgabe von deiner Lehrperson'+end+'</p>';
|
||||
html += '<span class="mod-status" style="background:#fde8d0;color:#e8833a">▶ Jetzt starten</span>';
|
||||
html += '</div></a>';
|
||||
});
|
||||
html += '</div>';
|
||||
}
|
||||
modList = await api('modules?student=1');
|
||||
if (modList.error) modList = [];
|
||||
} catch(e) {}
|
||||
|
||||
// Modul-Status laden
|
||||
var modStatus = {};
|
||||
try {
|
||||
var mods = await api('modules?student=1');
|
||||
if (!mods.error) mods.forEach(function(m) { modStatus[m.id] = m.mode; });
|
||||
} catch(e) {}
|
||||
// Lehrer-gesteuerte Auftraege zuerst
|
||||
var teacherStarted = modList.filter(function(m) { return m.mode === 'teacher_started'; });
|
||||
if (teacherStarted.length > 0) {
|
||||
html += '<div style="margin-bottom:1rem">';
|
||||
html += '<p style="font-size:.78rem;font-weight:700;color:#e8833a;margin-bottom:.4rem">📋 Auftraege von deiner Lehrperson</p>';
|
||||
teacherStarted.forEach(function(m) {
|
||||
var due = m.dueDate ? '<br><span style="font-size:.6rem;color:#8a8a8a">Zu erledigen bis ' + new Date(m.dueDate).toLocaleDateString('de-AT',{day:'2-digit',month:'2-digit',year:'numeric',hour:'2-digit',minute:'2-digit'}) + '</span>' : '';
|
||||
var quiz = m.quizEnabled ? '<br><span style="font-size:.6rem;color:#4a7c8a">📝 Mit kurzem Quiz</span>' : '';
|
||||
var url = buildModuleUrl(m);
|
||||
html += '<a href="'+url+'" class="mod-card" style="border-color:#e8833a;border-width:2px;margin-bottom:.5rem">';
|
||||
html += '<div class="mod-img" style="height:80px;background:linear-gradient(135deg,#e8833a,#f4a261)"><span style="font-size:2.5rem">'+(m.icon||'📚')+'</span></div>';
|
||||
html += '<div class="mod-body">';
|
||||
html += '<h3 style="color:#e8833a">'+m.icon+' '+m.name+(m.forcedLevel ? ' — Stufe '+m.forcedLevel : '')+'</h3>';
|
||||
html += '<p>Auftrag von deiner Lehrperson'+due+quiz+'</p>';
|
||||
html += '<span class="mod-status" style="background:#fde8d0;color:#e8833a">▶ Jetzt starten</span>';
|
||||
html += '</div></a>';
|
||||
});
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
// Alle Module
|
||||
// Regulaere Module (free + locked)
|
||||
html += '<div class="mod-grid">';
|
||||
var entries = Object.entries(MODULE_MAP);
|
||||
entries.forEach(function(e) {
|
||||
var id = e[0], m = e[1];
|
||||
var mode = modStatus[id] || 'free'; // Default: frei wenn keine Klasse
|
||||
var isLocked = mode === 'locked';
|
||||
var imgHtml = m.img
|
||||
? '<img src="'+m.img+'" alt="">'
|
||||
: '<span style="font-size:3rem">'+m.icon+'</span>';
|
||||
modList.filter(function(m) { return m.mode !== 'teacher_started'; }).forEach(function(m) {
|
||||
var isLocked = m.mode === 'locked';
|
||||
var cardImg = CARD_IMAGES[m.id];
|
||||
var imgHtml = cardImg ? '<img src="'+cardImg+'" alt="">' : '<span style="font-size:3rem">'+(m.icon||'📚')+'</span>';
|
||||
var url = buildModuleUrl(m);
|
||||
|
||||
if (isLocked) {
|
||||
html += '<div class="mod-card locked">';
|
||||
html += '<div class="mod-img" style="position:relative">'+imgHtml+'<div class="lock-icon">🔒</div></div>';
|
||||
} else {
|
||||
html += '<a href="'+m.href+'" class="mod-card">';
|
||||
html += '<a href="'+url+'" class="mod-card">';
|
||||
html += '<div class="mod-img">'+imgHtml+'</div>';
|
||||
}
|
||||
html += '<div class="mod-body">';
|
||||
html += '<h3>'+m.icon+' '+m.name+'</h3>';
|
||||
html += '<p>'+getModuleDesc(id)+'</p>';
|
||||
html += '<h3>'+(m.icon||'📚')+' '+m.name+'</h3>';
|
||||
html += '<p>'+(m.desc||'')+'</p>';
|
||||
if (isLocked) {
|
||||
html += '<span class="mod-status locked-s">🔒 Gesperrt</span>';
|
||||
html += '</div></div>';
|
||||
} else {
|
||||
html += '<span class="mod-status available">▶ Spielen</span>';
|
||||
html += '<span class="mod-status available">▶ Starten</span>';
|
||||
html += '</div></a>';
|
||||
}
|
||||
// Level-Badge (wird asynchron gefüllt)
|
||||
// Wird nach dem Rendern per API geladen
|
||||
});
|
||||
html += '</div>';
|
||||
document.getElementById('tab-modules').innerHTML = html;
|
||||
}
|
||||
|
||||
function getModuleDesc(id) {
|
||||
var descs = {
|
||||
'sim-05': 'Inselstaat durch die Klimakrise führen (2D-Ansicht)',
|
||||
'sim-05-3d': 'Inselstaat durch die Klimakrise führen (3D)',
|
||||
'sim-07': '50 Jahre Stadtplanung in einer Erdbebenzone',
|
||||
'sim-09': 'Deine Region bis 2050 auf Erneuerbare umstellen',
|
||||
'sim-10': 'Was kostet dein T-Shirt wirklich?',
|
||||
'sim-11': 'Mit dem Boot den Fluss hoch — Tiere und Pflanzen entdecken',
|
||||
'sim-12': 'Deiche, Renaturierung und Bewässerung managen',
|
||||
'sim-13': 'Plane eine Stadt mit Wohngebieten und Infrastruktur',
|
||||
'sim-14': 'Steuere einen Rettungshubschrauber durch Österreich',
|
||||
};
|
||||
return descs[id] || '';
|
||||
}
|
||||
// Modul-Beschreibungen kommen jetzt aus module_info.short_desc via API.
|
||||
|
||||
async function renderAchievements() {
|
||||
// Lade echte Fortschrittsdaten
|
||||
|
||||
Reference in New Issue
Block a user