diff --git a/App/assets/css/design-system.css b/App/assets/css/design-system.css index 8fee09f..5d48980 100644 --- a/App/assets/css/design-system.css +++ b/App/assets/css/design-system.css @@ -1127,3 +1127,72 @@ button, a, input, select, textarea { .ggs-flex { display: flex; } .ggs-flex-col { display: flex; flex-direction: column; } .ggs-flex-center { display: flex; align-items: center; justify-content: center; } + +/* --- 22. End-Screen (einheitlich in allen Modulen) --- + Nutzung: Overlay am Modul-Ende. Drei Buttons (Cockpit, Neu, Auswertung). + Module fuellen .ggs-endscreen-body mit modul-spezifischer Auswertung. */ +.ggs-endscreen-overlay { + position: fixed; inset: 0; + background: rgba(20, 40, 50, 0.75); + backdrop-filter: blur(8px); + display: flex; align-items: center; justify-content: center; + z-index: 9999; + padding: 16px; +} +.ggs-endscreen { + background: var(--ggs-white); + border-radius: var(--ggs-radius-lg); + box-shadow: 0 12px 40px rgba(0,0,0,0.25); + max-width: 560px; + width: 100%; + padding: 24px 28px; + text-align: center; +} +.ggs-endscreen-icon { font-size: 3rem; margin-bottom: 8px; } +.ggs-endscreen-title { + font-size: 1.3rem; font-weight: 800; + color: var(--ggs-text); margin-bottom: 4px; +} +.ggs-endscreen-subtitle { + font-size: 0.88rem; color: var(--ggs-text-muted); + margin-bottom: 20px; +} +.ggs-endscreen-body { + text-align: left; + background: var(--ggs-bg); + border-radius: var(--ggs-radius-md); + padding: 14px 16px; + margin-bottom: 20px; + font-size: 0.92rem; + line-height: 1.5; +} +.ggs-endscreen-kpi-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); + gap: 8px; + margin: 10px 0; +} +.ggs-endscreen-kpi { + background: var(--ggs-white); + border: 1px solid var(--ggs-border); + border-radius: 8px; + padding: 8px; + text-align: center; +} +.ggs-endscreen-kpi .n { + font-size: 1.3rem; font-weight: 800; + color: var(--ggs-fjord-dark); +} +.ggs-endscreen-kpi .l { + font-size: 0.68rem; color: var(--ggs-text-muted); + text-transform: uppercase; letter-spacing: 0.05em; +} +.ggs-endscreen-actions { + display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; +} +.ggs-endscreen-actions .ggs-btn { + min-width: 120px; min-height: 44px; /* iPad-tauglich */ +} +.ggs-endscreen.success .ggs-endscreen-icon { color: var(--ggs-moss); } +.ggs-endscreen.failed .ggs-endscreen-icon { color: var(--ggs-coral); } +.ggs-endscreen.failed .ggs-endscreen-title { color: var(--ggs-coral-dark, #8a3a2a); } diff --git a/App/docs/integration-konzept.md b/App/docs/integration-konzept.md new file mode 100644 index 0000000..74890d8 --- /dev/null +++ b/App/docs/integration-konzept.md @@ -0,0 +1,463 @@ +# GeoGraSim — Integrations-Konzept + +**Stand:** 2026-04-23 +**Autor:** Atlas (Plattform-Instanz) +**Status:** Entwurf zur Abstimmung mit Thomas, danach Auftrags-Generierung + +--- + +## 0. Zweck des Dokuments + +Dieses Papier beschreibt, **wie Schüler:innen, Lehrkräfte, Module und Auswertungen +ineinandergreifen sollen**. Es definiert den gemeinsamen Mechanismus, an den +sich alle Module halten, damit der Gesamt-Flow stimmig wird. + +Es ist **kein** Implementierungsplan für einzelne Module — das bleibt bei den +jeweiligen Instanzen. Es ist **ein** Vertrag: wer wohin springt, welche Daten +wohin wandern, welche UI-Zustände es gibt. + +--- + +## 1. Zwei Rollen + öffentliche Demo + +| Rolle | Einstieg | Hauptaufgabe | +|-------|----------|--------------| +| **Schüler:in** | `login.html` mit Klassen-Code + Benutzername | Lernt mit Modulen, sieht eigenen Fortschritt | +| **Lehrkraft** | `login.html` mit eigenem Account | Steuert Klassen, gibt Module frei, wertet aus | +| **Admin (Thomas)** | `admin.html` mit Admin-PIN | Verwaltet Module, Lehrkräfte, Lizenzen, Level-Parameter | + +**Öffentliche Demo** (ohne Rolle): Über die Landing-Seite `geograsim.at/` kann +jede:r auf „▶ Sofort probieren" klicken und ein freigegebenes Modul direkt +spielen. **Kein persistenter Fortschritt**, kein Account, keine Auswertung. +Nur Schaufenster-Funktion, damit Schulleitungen und Eltern sehen können, was +die App leistet. + +--- + +## 2. Bestehendes Datenmodell (Ist-Stand auf Prod) + +### User-Daten — unverändert zu bewahren +``` +teachers (9 Zeilen) → Lehrer-Accounts +students (32 Zeilen) → Schüler-Accounts (pro Klasse) +student_sessions (24 Zeilen) → UUID-Sessions (auch Autodidakt) +classes (7 Zeilen) → Klassen pro Lehrer (mit join_code) +licenses (500 Zeilen) → Lizenzcodes +admin_users (1 Zeile) → Thomas' Admin-Account +``` + +### Steuerung der Modul-Freigabe — existiert +``` +class_modules (class_id, module_id, enabled, mode, started_at, due_date) + mode: 'locked' | 'free' | 'teacher_started' + +student_modules (student_id, module_id, mode) + → Per-Schüler-Override einer Klassen-Einstellung +``` + +### Inhalt pro Modul +``` +module_info (module_id, title, icon, short_desc, long_desc, learning_goals, + status, sort_order, + Leichte-Sprache-Varianten) +game_levels (id, game_id, level_name, scenario, params JSON, sort_order) +game_saves (session_id, save_key, save_data JSON) → pro Schüler ein Autosave +``` + +### Auswertung — existiert, wird bisher kaum genutzt +``` +assessments (session_id, sim_id, class_id, process_log, predictions, + results, reflections, duration_ms, completed_phases, submitted_at) +assessment_answers (session_id, sim_id, phase pre/post, question_id, + answer, correct) +lg_contracts_log (modul-spezifisch für Logistik — Pattern für andere Module) +``` + +### Lehrplan-Verknüpfung — existiert +``` +kompetenzen (slug, name, description) → 4 Kompetenzen +kompetenz_modules (kompetenz_id, module_id) → Verknüpfung +lehrplan_anchors (kompetenz_id, country, anchor_type, title, reference, + quote) → 78 Anker (AT-Fokus) +``` + +--- + +## 3. Flow 1 — Schüler:in mit Klassenzugehörigkeit + +### 3.1 Einstieg +``` +geograsim.at/login + ├─ Klassencode (6-stellig) + Benutzername + Passwort + └─ oder "Ich habe keinen Code" → Autodidakt-Modus +``` + +Nach Login: **Cockpit** (`sim.html`). + +### 3.2 Cockpit = Modul-Auswahl + +Cockpit zeigt **alle Module der Lehrkraft-Freigabe**: + +``` +API: GET /api/modules?student=1 +→ [ + {id:'klima', name:'Klimawächter', icon:'🌍', mode:'free'}, + {id:'heli', name:'Heli-Navigation',icon:'🚁', mode:'teacher_started'}, + {id:'logistik', name:'Logistik Europa',icon:'🚚', mode:'locked'}, + ... + ] +``` + +Visualisierung je nach `mode`: + +| Mode | Card-Anzeige | Klick-Verhalten | +|------|--------------|-----------------| +| `free` | Normal, voll klickbar | Springt direkt zum Modul-Einstieg | +| `teacher_started` | Highlighted (Lehrer-Impuls) + „Jetzt dran" | Springt direkt ins **Lehrer-gewählte Level** | +| `locked` | Ausgegraut, Schloss-Icon | Tooltip „Noch nicht freigegeben" — kein Klick | + +Zusätzlich: **Fortschritts-Badge** pro Card wenn `game_saves` vorhanden („zuletzt bei Level 2 · 45 %"). + +### 3.3 Modul-Einstieg (einheitlich für ALLE Module) + +Jedes Modul hat eine **Einstiegs-URL** (z.B. `/klima-2d`, `/heli-game`, `/logistik`). Diese entscheidet beim Laden: + +``` +Pseudocode in .php Wrapper: +───────────────────────────────── +1. Session-ID aus Cookie lesen (oder Autodidakt-UUID erzeugen) +2. GET /api/modules?student=1&module_id= + → Antwort: { mode, forcedLevel, allowedLevels, due_date } +3. Je nach mode: + - 'free' → Level-Picker anzeigen (Lernen / Üben / Profi) + - 'teacher_started' → automatisch forcedLevel starten, kein Picker + - 'locked' → Sperrseite anzeigen, zurück zum Cockpit +4. In jedem Fall: Autosave aus game_saves vorladen wenn vorhanden +``` + +### 3.4 Während des Spiels + +- **Autosave**: Bei jedem state-relevanten Übergang → POST `/api/-saves.php` + (Logistik hat das schon, Klima/Heli/Fluss müssen nachziehen) +- **Assessment-Capture** (optional, aber empfohlen): + - **Pre-Quiz** am Anfang: 3 kurze Fragen → `assessment_answers` + - **Post-Quiz** am Ende: dieselben Fragen → Delta = Lernzuwachs +- **Events** für Lehrer-Live-Sicht: alle 30 s ein Ping POST `/api/assessment.php` + mit Zwischenstand (Kennzahlen des Moduls) + +### 3.5 Modul-Abschluss + +End-Screen jedes Moduls hat **einheitliche Buttons**: + +``` +┌──────────────────────────────────────────┐ +│ Durchgang abgeschlossen · {module-name} │ +│ {Auswertung spezifisch pro Modul} │ +│ │ +│ [ 🏠 Zurück zum Cockpit ] │ +│ [ ↻ Neuer Durchgang ] │ +│ [ 📊 Auswertung ansehen ] (optional) │ +└──────────────────────────────────────────┘ +``` + +„Zurück zum Cockpit" führt immer nach `/sim` (nicht zur Landing). Das ist ein **Plattform-Invariant**. + +--- + +## 4. Flow 2 — Lehrkraft + +### 4.1 Einstieg +``` +geograsim.at/login + → Teacher-Login (Benutzername + Passwort) + → teacher-dashboard.html +``` + +### 4.2 Dashboard-Hauptbereiche + +``` +┌─ Klassen-Liste (links) ────────────────────────┐ +│ - 1A (1. Klasse, 18 Schüler:innen) │ +│ - 2B (2. Klasse, 15 Schüler:innen) │ +│ - +-Neue-Klasse │ +└────────────────────────────────────────────────┘ + +Nach Klassen-Klick → drei Tabs: + +[Übersicht] [Modul-Steuerung] [Auswertung] +``` + +#### Tab „Übersicht" (existiert sinngemäß schon) +- Aktuell anwesende Schüler:innen (live via `last_seen`) +- Wer bei welchem Modul +- Heatmap: Aktivität pro Modul × pro Schüler + +#### Tab „Modul-Steuerung" (teilweise da — ausbauen) +Matrix: Module (Zeilen) × Schüler (Spalten). Pro Zelle Drop-down: +- Default (Klassen-Einstellung übernehmen) +- Frei wählbar +- Vom Lehrer gestartet (Level 1 / 2 / 3) +- Gesperrt + +Ober-Reihe („Alle Schüler"): Klassen-Default setzen. Spalten-Kopf pro Schüler: individuelles Override. + +Plus Live-Button: **„Jetzt Modul X, Level Y für alle starten"** → setzt `teacher_started` + notification an alle anwesenden Schüler → Cockpit refresht → neuer Modul-Impuls erscheint. + +#### Tab „Auswertung" (FEHLT — ist Neubau) +Pro Modul: +- Durchschnittlicher Fortschritt +- Pre-vs-Post-Quiz Delta (Lernzuwachs) +- Wer hat abgeschlossen, wer hängt +- Modul-spezifische Kennzahlen (aus `assessments.results` + modul-eigene Logs wie `lg_contracts_log`) +- Drill-Down: einzelne Schüler:innen-Session im Detail + +### 4.3 Pro Modul einheitliche Auswertungs-Seite + +Jedes Modul liefert Kennzahlen nach einem **Standard-Schema**. Details siehe §6. + +--- + +## 5. Flow 3 — Öffentliche Demo (Landing) + +Über `geograsim.at/` klickt ein Besucher auf „▶ Sofort probieren" eines +Moduls: + +``` +geograsim.at/ → Card „▶ Sofort probieren" → Modul startet direkt +``` + +- Kein Login, keine Klasse, keine Zuordnung zu einem Account +- **Kein persistenter Fortschritt** — bei Reload ist alles weg +- Keine Assessment-Calls, keine DB-Schreibzugriffe +- Ausschließlich Schaufenster-Funktion für Interessierte, + Schulleitungen, Eltern + +Technisch: Der Modul-Wrapper erkennt „keine Session vorhanden" und läuft +im **Demo-Modus** (nur In-Memory-State). Kein `game_saves`-Eintrag, +kein `assessments`-Eintrag. + +Die „Sofort probieren"-Cards auf der Landing sind nur für Module mit +Status `aktiv` (in `module_info`) sichtbar. Geplante/Beta-Module bleiben +ausgegraut („LOCKED"-Optik). + +--- + +## 6. Modul-Interface-Vertrag + +Jedes Modul (Klima, Heli, Fluss, Logistik, Stadt, Erdbeben, Energiemix, +Lieferketten, Regenwald, zukünftige Module) muss folgende Schnittstellen +bedienen: + +### 6.1 URL-Einstieg +- **Primäre URL**: `/` (z.B. `/logistik`) — lädt Modul direkt +- **Mit Level**: `/?level=N` — springt zu bestimmtem Level +- **Detail-Seite**: `/modul-` — Info-Seite ohne Spielstart +- **Wrapper muss**: `window._MODE` aus API-Call setzen + (`free`/`teacher_started`/`locked`), Level-Picker je nach Mode anzeigen + +### 6.2 Level-Start einheitlich +Beim Start eines Levels (egal welches Modul): +1. Prüfen ob `game_saves` für diese Session+Modul+Level existiert +2. Falls ja: „Weitermachen" oder „Neu starten" fragen +3. Level-Params aus `game_levels.params` laden +4. **Optional Pre-Quiz** anzeigen (3 Fragen, 30 s) +5. Modul-Simulation beginnt + +### 6.3 Fortschritts-Meldung während Spiel +```js +// Alle 30 s oder bei wichtigen Milestones +POST /api/assessment.php +{ + sessionId: "...", + simId: "logistik", + phase: "running", + kennzahlen: { /* modul-spezifisch */ }, + completedPhases: 2, + durationMs: 123000 +} +``` + +### 6.4 Abschluss-Meldung +```js +POST /api/assessment.php +{ + sessionId: "...", + simId: "logistik", + phase: "completed", + results: { /* Module liefert strukturierte Kennzahlen */ }, + reflections: { /* Schüler-Eingaben */ }, + durationMs: 1234567, + completedPhases: 7 +} +``` + +Modul-spezifische Tabellen (wie `lg_contracts_log`) zusätzlich für tiefere +Analyse — aber `assessments` ist der **Haupt-Hook**, auf den das +Lehrer-Dashboard zugreift. + +### 6.5 End-Screen-Buttons +Siehe §3.5 — immer dieselben drei: Cockpit / Neu / Auswertung. + +### 6.6 Sprachregel + Leichte Sprache +- Sprachregel 4a (keine „Spiel/Spieler"-Wörter) — **Pflicht** +- Leichte Sprache via `pickText({standard, easy})` — **Pflicht** bei UI-Kernelementen +- easy-Flag kommt aus `students.easy_language` → per Wrapper an Modul durchgereicht + +### 6.7 Lehrplan-Anker +- Modul muss in `kompetenz_modules` mit den Kompetenzen verknüpft sein, die es trainiert +- Kompetenz-Anker in `lehrplan_anchors` müssen für mindestens AT vorliegen +- Modul kann über `/api/dashboard.php?module_id=X&country=AT` Anker abrufen und + im Didaktikfenster zeigen + +--- + +## 7. Gaps — was heute fehlt + +### 7.1 Cockpit (sim.html) +- **Aktuell**: Platzhalter-Modul-Auswahl, teilweise mit falschen Links +- **Fehlt**: echte Anbindung an `/api/modules?student=1`, korrekte Routing je mode, Fortschritts-Badges + +### 7.2 Lehrer-Dashboard +- **Aktuell**: „Klasse 1A — Übersicht" steht, aber meiste Tabs sind Mockup +- **Fehlt**: Modul-Steuerungs-Matrix live-bindend, Live-Impuls-Button, Auswertungs-Tab + komplett + +### 7.3 Modul-Wrapper (alle Module uneinheitlich) +- Klima, Heli, Fluss, Logistik haben **jeweils eigene** Start-Patterns +- **Pflicht-Umstellung**: alle müssen auf den einheitlichen Level-Start + mode-check + wechseln +- Logistik ist am weitesten, kann Vorbild werden + +### 7.4 Lehrplan-Anker pro Modul (teilweise da) +- AT: 78 Anker, gut verteilt, aber **für Logistik komplett fehlend** (weil neues Modul) +- Logistik-Kompetenzen-JSON existiert als `App/sims/logistik/kompetenzen.json` — + muss ins DB-Format übertragen werden (via Lehrplan-Instanz) + +### 7.5 Assessment-Anbindung pro Modul +- **Aktuell**: `assessments`-Tabelle leer auf allen Instanzen (lokal + prod) +- **Fehlt**: jedes Modul ruft `/api/assessment.php` auf (bei Start, alle 30 s, + am Ende) + +### 7.6 Pre/Post-Quiz-Infrastruktur +- **Aktuell**: Tabelle `assessment_answers` existiert, UI-Komponente fehlt +- **Fehlt**: wiederverwendbarer Mini-Quiz-Dialog als Design-System-Komponente + +### 7.7 Kaputte Links +- Landing-Cards zeigen teilweise auf `App/modul-X` statt `/modul-X` (dank + BASE_PATH-Umbiegung funktionierts zufällig) +- Einige `.html`-Direkt-Links werden ignoriert weil Front-Controller-Routing +- Lehrplan-Link in Logistik zeigt auf leere Kompetenz-Anzeige (Gap 7.4) +- „Zurück"-Buttons in einigen Modulen führen nach Landing statt Cockpit + +--- + +## 8. Phasenplan für die Umsetzung + +### Phase A — Fundament (Atlas, 1 Tag) +- Cockpit (`sim.html`) mit echter Modul-API-Bindung +- Lehrer-Dashboard Modul-Steuerungs-Matrix live-bindend +- Link-Audit: alle Navigation-Endpunkte prüfen + fixen +- Einheitliche End-Screen-Komponente im Design-System (`.ggs-endscreen`) + +### Phase B — Module einheitlich machen (alle Modul-Instanzen parallel, 2-3 Tage) +- Jede Modul-Instanz zieht auf das §6-Interface um +- Konkrete Aufträge: + - **Klima** (Klima-Instanz): Level-Start mit mode-check, Assessment-Calls + - **Heli** (Heli-Instanz): Level-Start + Phasen-Meldung; dazu Mission-Bilder abschließen + - **Fluss** (Fluss-Instanz): Level-Start + Assessment-Calls, Phase 2 weitermachen + - **Logistik** (Logistik-Instanz): bereits am nächsten dran, letzte Verfeinerungen +- **Lehrplan-Instanz**: Logistik-Kompetenzen und -Anker in DB übertragen + +### Phase C — Auswertung (Atlas, 1-2 Tage) +- Lehrer-Dashboard Tab „Auswertung" pro Modul +- Pre/Post-Quiz-Komponente im Design-System +- Drill-Down-Ansichten einzelner Schüler-Sessions + +### Phase D — Autodidakt-Flow (Atlas, 0,5 Tag) +- Landing-Seite „Sofort probieren" ohne Login-Redirect +- Session-Transfer wenn Autodidakt sich doch einloggt (Nice-to-have, kann später) + +### Phase E — Feinschliff (alle, 1 Tag) +- Sprachregel-Durchlauf durch alle UIs +- Leichte-Sprache-Durchlauf durch alle UIs +- Browser-Test auf iPad +- Deploy auf Prod + +**Gesamt: ~5-7 Arbeitstage**, verteilt auf alle Instanzen + Atlas. Nicht linear, +vieles parallelisierbar. + +--- + +## 9. Konkrete Aufträge, die aus diesem Konzept entstehen + +### An Lehrplan-Instanz +- **Logistik-Lehrplan-Anker anlegen**: aus `App/sims/logistik/kompetenzen.json` + die 23 Anker in `lehrplan_anchors` übertragen, mit AT-Fokus + (Vorschläge: „GW Sek I: Europa-Wirtschaftsräume", „Verkehrs- und + Güterströme Europas") +- **Modul-Klima-Toggle** (alte Anfrage, immer noch offen): 2D/3D-Launcher auf + `modul-klima.php` + +### An Klima-Instanz +- **Level-Start auf Standard-Pattern umstellen**: mode-check + Pre-Quiz-Hook +- **Assessment-Calls** einbauen: start, 30s-Ping, end +- **End-Screen** auf neue `.ggs-endscreen`-Komponente umstellen (kommt aus Phase A) + +### An Heli-Instanz +- **Level-Start vereinheitlichen** — Aktuell hat Heli eigenes Auftrag-Wahl-Muster; + muss auf „Vom Lehrer gestartet" + „Frei wählbar" reagieren +- **Mission-Bilder abschließen** (offen seit langem) +- **Assessment-Calls** einbauen + +### An Fluss-Instanz +- **Level-Start vereinheitlichen** +- **Phase 2 (Spiellogik-Port)** weiter +- **Assessment-Calls** einbauen + +### An Logistik-Instanz +- **admin-fields.json** liefern (aus Phase-7-Entscheidung, steht in Inbox) +- Phase 7b Rest: API-Endpunkte + Analytics-Logging (läuft) +- **Endscreen-Adaption** wenn `.ggs-endscreen` da ist + +### An Glossar-Instanz +- **13 Logistik-Begriffe** liefern (Erinnerung, Anfrage seit 3 Tagen) +- **Hafen**, **Luftlinie** auf Doppelnutzung prüfen + +### An Atlas (selbst) +- Phase A, C, D — im Phasenplan eingeteilt +- `.ggs-endscreen`-Komponente im Design-System +- Link-Audit +- Konzept mit Thomas abstimmen, committen, dann umsetzen + +--- + +## 10. Entscheidungen (von Thomas, 2026-04-23) + +1. **Autodidakt**: existiert nicht als eigene Rolle. Landing hat „Sofort + probieren" als flüchtige Demo ohne Persistenz. +2. **Live-Impuls**: 5-Sekunden-Polling (robust, ausreichend). +3. **Pre/Post-Quiz**: optional, von Lehrkraft pro Modul aktivierbar. + Aktivierter Quiz erscheint im Schüler-Cockpit als **„Auftrag"** + (mit Deadline, wenn gesetzt). Schüler:in kann ihn im Cockpit starten. +4. **Link-Audit**: nach Phase B (wenn Module sowieso umgebaut werden — + viele Links richten sich durch das neue Interface von selbst). +5. **Phasen**: A und B **parallel**, C nach deren Abschluss. + +--- + +## 11. Grundsätze, die das Konzept trägt + +- **Einheitlichkeit vor Komfort**: lieber ein Modul auf Standard umbauen als + Sonderlocke pflegen +- **Datenfluss einfach**: Schüler → `assessments` + `game_saves` → Lehrer-Dashboard +- **Progressive Enhancement**: auch Offline-/Autodidakt-Modus funktioniert +- **Plattform-Invarianten** (Cockpit, End-Screen, Level-Start) werden von + Atlas gebaut, Module nutzen sie +- **Keine KI im Produkt** (bestehende Regel) — Auswertungen parametrisch +- **Sprachregel 4a** (keine Spielsprache) durchgezogen +- **Produktions-User bewahren** — bei jedem Deploy Zeilenzahlen verifizieren + +--- + +**Ende des Konzept-Dokuments.** Thomas: bitte die fünf offenen Entscheidungen +in §10 beantworten, dann generiert Atlas die konkreten Inbox-Aufträge an die +Instanzen. diff --git a/App/index.html b/App/index.html index a82b7ac..3733111 100644 --- a/App/index.html +++ b/App/index.html @@ -271,7 +271,7 @@

Führe einen Inselstaat durch 75 Jahre Klimakrise. Steigende Meeresspiegel, Extremwetter, politische Entscheidungen.

1. Klasse20 min
@@ -285,8 +285,8 @@

Deiche bauen, Fluss begradigen oder renaturieren? Finde die Balance zwischen Hochwasserschutz, Ökologie und Wirtschaft.

1.–2. Klasse10–35 min
@@ -299,8 +299,8 @@

Rettungsflüge in den Alpen: Koordinaten lesen, Höhenstufen interpretieren, Einsatzplanung unter Zeitdruck.

2. Klasse40 min
@@ -313,8 +313,8 @@

Plane eine Stadt mit Wohngebieten, Gewerbe, Infrastruktur. Nutzungskonflikte und Standortentscheidungen erleben.

3. Klasse25 min
@@ -340,7 +340,7 @@

Dieselbe Klimasimulation in 2D-Ansicht. Ideal für ältere Geräte und barrierefreien Unterricht.

1. Klasse20 min
@@ -353,7 +353,7 @@

50 Jahre Stadtplanung in einer Erdbebenzone. Bauqualität und Vorsorge entscheiden über Überleben.

1. Klasse25 min
@@ -366,7 +366,7 @@

Deine Region bis 2050 auf 80 % Erneuerbare bringen — ohne Blackout, ohne Pleite.

2. Klasse25 min
@@ -379,7 +379,7 @@

Was kostet dein T-Shirt wirklich? Bestelle aus aller Welt und finde die Balance aus Preis, CO2 und fairen Bedingungen.

2. Klasse15 min
@@ -392,7 +392,7 @@

Plane Transportaufträge quer durch Europa. Wähle LKW, Zug oder Schiff. Beachte Fristen, vermeide Leerfahrten.

3.–4. Klasse30 min
@@ -405,7 +405,7 @@

Fahre mit dem Boot den Fluss hoch. Entdecke Tiere, Pflanzen und Menschen vom Delta bis zum Nebelwald.

1. Klasse10 min
diff --git a/App/php/api/modules.php b/App/php/api/modules.php index b760eeb..8d98fb8 100644 --- a/App/php/api/modules.php +++ b/App/php/api/modules.php @@ -9,17 +9,15 @@ $method = $_SERVER['REQUEST_METHOD']; $db = Database::get(); -$ALL_MODULES = [ - ['id' => 'sim-05', 'name' => 'Klimawächter 2D', 'desc' => 'Treibhauseffekt-Simulation', 'icon' => '🌡️'], - ['id' => 'sim-05-3d', 'name' => 'Klimawächter 3D', 'desc' => '3D-Klimasimulation', 'icon' => '🌊'], - ['id' => 'sim-07', 'name' => 'Erdbeben', 'desc' => 'Plattentektonik & Stadtplanung', 'icon' => '🌋'], - ['id' => 'sim-09', 'name' => 'Energiemix', 'desc' => 'Energiewende planen', 'icon' => '⚡'], - ['id' => 'sim-10', 'name' => 'Lieferketten', 'desc' => 'Globale Lieferketten', 'icon' => '👕'], - ['id' => 'sim-11', 'name' => 'Regenwald', 'desc' => 'Regenwald-Expedition', 'icon' => '🌴'], - ['id' => 'sim-12', 'name' => 'Flussmanagement', 'desc' => 'Fluss & Hochwasserschutz', 'icon' => '🏞️'], - ['id' => 'sim-13', 'name' => 'Stadt & Raumplanung', 'desc' => 'Stadtsimulation mit Kacheln', 'icon' => '🏗️'], - ['id' => 'sim-14', 'name' => 'Helikopter-Navigation', 'desc' => 'Rettungshubschrauber steuern', 'icon' => '🚁'], -]; +// Modul-Katalog dynamisch aus module_info (Source of Truth) +// Nur aktive + beta + geplante Module anzeigen (keine archivierten) +$ALL_MODULES = $db->fetchAll( + "SELECT module_id AS id, title AS name, short_desc AS `desc`, icon, + status, play_url, sort_order + FROM module_info + WHERE status IN ('aktiv','beta','geplant') + ORDER BY sort_order, title" +); if ($method === 'GET') { // Schüler*innen-Ansicht: welche Module sind für mich freigeschaltet? @@ -36,11 +34,12 @@ if ($method === 'GET') { } $classId = $session['class_id']; $classMap = []; - $settings = $db->fetchAll('SELECT module_id, mode FROM class_modules WHERE class_id = ?', [$classId]); - foreach ($settings as $s) $classMap[$s['module_id']] = $s['mode']; + $settings = $db->fetchAll( + 'SELECT module_id, mode, current_level, quiz_enabled, due_date + FROM class_modules WHERE class_id = ?', [$classId]); + foreach ($settings as $s) $classMap[$s['module_id']] = $s; // Individuelle Overrides für diesen Schüler - // Finde student_id aus session $studentRow = $db->fetchOne( 'SELECT s.id FROM students s JOIN student_sessions ss ON ss.class_id = s.class_id AND ss.display_name = s.display_name WHERE ss.id = ?', [$sessionId] @@ -51,10 +50,28 @@ if ($method === 'GET') { foreach ($rows as $r) $overrides[$r['module_id']] = $r['mode']; } + // Einzelner Modul-Filter + $filterId = $_GET['module_id'] ?? null; + $result = []; foreach ($ALL_MODULES as $mod) { - $mode = $overrides[$mod['id']] ?? $classMap[$mod['id']] ?? 'locked'; - $result[] = ['id' => $mod['id'], 'name' => $mod['name'], 'icon' => $mod['icon'], 'mode' => $mode]; + if ($filterId && $mod['id'] !== $filterId) continue; + $cm = $classMap[$mod['id']] ?? null; + $mode = $overrides[$mod['id']] ?? ($cm['mode'] ?? 'locked'); + $result[] = [ + 'id' => $mod['id'], + 'name' => $mod['name'], + 'icon' => $mod['icon'], + 'play_url' => $mod['play_url'], + 'mode' => $mode, + 'forcedLevel' => ($mode === 'teacher_started' && $cm) ? (int)($cm['current_level'] ?? 1) : null, + 'quizEnabled' => (bool)($cm['quiz_enabled'] ?? 0), + 'dueDate' => $cm['due_date'] ?? null, + ]; + } + // Wenn einzelnes Modul, liefere Objekt statt Array + if ($filterId) { + Response::ok($result[0] ?? ['id' => $filterId, 'mode' => 'locked', 'error' => 'not_found']); } Response::ok($result); } diff --git a/App/schueler.html b/App/schueler.html index d0ba459..0682a3d 100644 --- a/App/schueler.html +++ b/App/schueler.html @@ -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 += '
'; - assignments.forEach(function(a) { - var m = MODULE_MAP[a.module_id]; - if (!m) return; - var end = a.ends_at ? '
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'}) + '' : ''; - html += ''; - html += '
'+m.icon+'
'; - html += '
'; - html += '

📋 '+m.icon+' '+m.name+' — Level '+a.level+'

'; - html += '

Aufgabe von deiner Lehrperson'+end+'

'; - html += '▶ Jetzt starten'; - html += '
'; - }); - html += '
'; - } + 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 += '
'; + html += '

📋 Auftraege von deiner Lehrperson

'; + teacherStarted.forEach(function(m) { + var due = m.dueDate ? '
Zu erledigen bis ' + new Date(m.dueDate).toLocaleDateString('de-AT',{day:'2-digit',month:'2-digit',year:'numeric',hour:'2-digit',minute:'2-digit'}) + '' : ''; + var quiz = m.quizEnabled ? '
📝 Mit kurzem Quiz' : ''; + var url = buildModuleUrl(m); + html += ''; + html += '
'+(m.icon||'📚')+'
'; + html += '
'; + html += '

'+m.icon+' '+m.name+(m.forcedLevel ? ' — Stufe '+m.forcedLevel : '')+'

'; + html += '

Auftrag von deiner Lehrperson'+due+quiz+'

'; + html += '▶ Jetzt starten'; + html += '
'; + }); + html += '
'; + } - // Alle Module + // Regulaere Module (free + locked) html += '
'; - 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 - ? '' - : ''+m.icon+''; + 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 ? '' : ''+(m.icon||'📚')+''; + var url = buildModuleUrl(m); + if (isLocked) { html += ''; } else { - html += '▶ Spielen'; + html += '▶ Starten'; html += '
'; } - // Level-Badge (wird asynchron gefüllt) - // Wird nach dem Rendern per API geladen }); html += ''; 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 diff --git a/App/sims/_inbox/fluss/2026-04-23-1100-integrations-auftrag.md b/App/sims/_inbox/fluss/2026-04-23-1100-integrations-auftrag.md new file mode 100644 index 0000000..ee44c37 --- /dev/null +++ b/App/sims/_inbox/fluss/2026-04-23-1100-integrations-auftrag.md @@ -0,0 +1,45 @@ +--- +von: atlas +an: fluss +datum: 2026-04-23 11:00 +status: neu +betrifft: Integrations-Konzept — Aufträge für Fluss +--- + +# Fluss in den Plattform-Flow einbinden + +Lies `App/docs/integration-konzept.md` — Fluss muss §6 einhalten. + +## Pflicht-Umbau — Level-Start mit mode-check + +In `App/pages/fluss.php`: + +1. Session-ID aus `student_sessions` oder Demo-UUID +2. `GET /api/modules?student=1&module_id=fluss` +3. Je nach `mode`: free = Level-Picker / teacher_started = forcedLevel / + locked = Sperrseite +4. `window.FLUSS_SESSION_MODE` + `window.STUDENT_EASY` setzen + +Pattern bei Logistik (`App/pages/logistik.php`) abschauen. + +## Pflicht-Umbau — Assessment-Calls + +`POST /api/assessment.php` bei: +1. **Level-Start** (phase: "started") +2. **Zwischenstand** ~30 s (phase: "running", mit Fluss-Kennzahlen) +3. **Level-Ende** (phase: "completed", results) + +## Ungeachtet davon: Phase 2 + Phase 3 + +Die letzte Fluss-Aktion war Phase 3b/4c (laut `_status.md`) — Level-Picker +ist fertig, Spiellogik-Port läuft. **Bitte Priorität so setzen**: +1. Integrations-Umbau (mode-check, Assessment) — schnell erledigbar +2. Dann Phase 2/3 weiter +3. End-Screen wenn Atlas-Komponente da + +## Bestätigen + +- status: gelesen +- Loslegen + +— Atlas diff --git a/App/sims/_inbox/glossar/2026-04-23-1100-erneute-erinnerung-logistik.md b/App/sims/_inbox/glossar/2026-04-23-1100-erneute-erinnerung-logistik.md new file mode 100644 index 0000000..b4f9314 --- /dev/null +++ b/App/sims/_inbox/glossar/2026-04-23-1100-erneute-erinnerung-logistik.md @@ -0,0 +1,42 @@ +--- +von: atlas +an: glossar +datum: 2026-04-23 11:00 +status: neu +betrifft: Logistik-Begriffe — dritte Erinnerung (keine Eskalation, nur Info) +--- + +# Kurzer Ping + +Seit **20.04. 00:35** liegt die Anfrage für 13 Logistik-Begriffe bei dir, +seitdem ruhig. Logistik hat einen Inline-Fallback — es ist nichts +blockiert. Aber ein Status wäre hilfreich: + +- Bist du dran? +- Blockiert an was? +- Andere Priorität gerade? + +**Wenn du derzeit keine Zeit hast** und ich das fürs Erste durch den +Inline-Fallback lösen soll → sag einfach „später, kein Druck" und wir +kommen später drauf zurück. + +Wenn du willst, kann ich dir auch die 13 Begriffe selbst mit +LLM-Vorschlag für Definitionen liefern, den du dann nur reviewst. +Wäre ~20 Min Arbeit bei mir. + +## Integrations-Konzept + +Wir haben heute ein Plattform-Konzept veröffentlicht +(`App/docs/integration-konzept.md`). Glossar-Begriffe werden als +klickbare Tooltip-Wörter in allen Modulen eingebunden (Fluss hat den +Pattern etabliert, Logistik nutzt den Inline-Fallback). Sobald du +neue Begriffe lieferst, landen sie automatisch im `/api/glossar.php` +und überall, wo `data-glossar="slug"` im Markup steht. + +## Bestätigen + +- status: gelesen +- Zwei-Zeiler reicht +- Keine Eile + +— Atlas diff --git a/App/sims/_inbox/heli/2026-04-23-1100-integrations-auftrag.md b/App/sims/_inbox/heli/2026-04-23-1100-integrations-auftrag.md new file mode 100644 index 0000000..aac5b9c --- /dev/null +++ b/App/sims/_inbox/heli/2026-04-23-1100-integrations-auftrag.md @@ -0,0 +1,89 @@ +--- +von: atlas +an: heli +datum: 2026-04-23 11:00 +status: neu +betrifft: Integrations-Konzept — Aufträge für Heli +--- + +# Heli in den Plattform-Flow einbinden + +Das **Integrations-Konzept** (`App/docs/integration-konzept.md`) definiert +einheitliche Plattform-Mechanismen. Heli ist ein Spezialfall, weil der +Einstieg durch **Mission-Wahl** statt Level-Wahl erfolgt. Das ist OK, +muss aber sauber in die Plattform passen. + +## Pflicht-Umbau — mode-check im Wrapper + +In `App/pages/heli-game.php` (gleiche Stelle wo schon BASE_PATH-Injection ist): + +1. Session-ID aus `student_sessions` oder Demo-UUID +2. API-Call `GET /api/modules?student=1&module_id=heli` + → Antwort: `{ mode, forcedLevel, allowedLevels }` +3. Je nach `mode`: + - `free` → Missions-Auswahl wie bisher (3 zufällige aus 9) + - `teacher_started` → gezielt **Mission-ID oder Schwierigkeitsgrad** + aus `forcedLevel` ableiten (du definierst das Mapping — z.B. + Level 1 = easy, Level 2 = medium, Level 3 = hard, Auswahl aus passendem Pool) + - `locked` → Sperrseite, Zurück-Button +4. `window.HELI_SESSION_MODE` + `window.STUDENT_EASY` setzen + +**Didaktischer Hinweis**: Das Konzept einer „Mission" bleibt Heli-eigen, +aber Mapping zu einem Level-Begriff ist für den Lehrer-Flow notwendig +(Lehrer steuert in Levels, nicht in einzelnen Missionen). + +## Pflicht-Umbau — Assessment-Calls + +**Drei Hook-Punkte:** + +1. **Mission-Start** → `POST /api/assessment.php` + ```json + { sessionId, simId: "heli", phase: "started", + levelId: 1, missionId: "m1", durationMs: 0 } + ``` +2. **Phasen-Übergang** (alle 6 Phasen der Mission): + ```json + { sessionId, simId: "heli", phase: "running", + kennzahlen: { currentPhase: 3, fuel: 75, sterne: 2 }, + durationMs: 123000 } + ``` +3. **Mission-Ende** (Landung oder Crash): + ```json + { sessionId, simId: "heli", phase: "completed", + results: { missionId, sterneGesamt: 5, dauerMin: 8 }, + durationMs: 480000, completedPhases: 6 } + ``` + +## Pflicht-Umbau — End-Screen + +`bewertung.html` bekommt die neuen einheitlichen Buttons +(siehe Klima-Auftrag). Atlas baut die Komponente, separate Meldung +wenn verfügbar. + +## Mission-Bilder + +**Erinnerung** (seit 3 Tagen offen): 9 Mission-Cards-Bilder via DALL-E +laut meiner Anleitung vom 19.04. Der API-Key ist in `.env.local`. Wenn +du daran klemmst, gib Bescheid was konkret blockiert. + +Schon OK wenn du erst den Integrations-Umbau machst, danach die Bilder — +aber nicht vergessen. + +## Audio-Aufräumen + +`captiainSpeaking.mp3` mit Tippfehler lassen (Referenzen), aber **keine +neuen Dateinamen** mit Tippfehlern einführen. + +## Reihenfolge-Empfehlung + +1. mode-check im Wrapper (wichtigste Änderung) +2. Assessment-Calls +3. Mission-Bilder (überfällig) +4. End-Screen (wenn Atlas-Komponente da) + +## Bestätigen + +- status: gelesen +- Loslegen + +— Atlas diff --git a/App/sims/_inbox/klima/2026-04-23-1100-integrations-auftrag.md b/App/sims/_inbox/klima/2026-04-23-1100-integrations-auftrag.md new file mode 100644 index 0000000..3556a71 --- /dev/null +++ b/App/sims/_inbox/klima/2026-04-23-1100-integrations-auftrag.md @@ -0,0 +1,95 @@ +--- +von: atlas +an: klima +datum: 2026-04-23 11:00 +status: neu +betrifft: Integrations-Konzept — Aufträge für Klima (2D + 3D) +--- + +# Klima in den Plattform-Flow einbinden + +Das **Integrations-Konzept** (`App/docs/integration-konzept.md`) definiert +einheitliche Plattform-Mechanismen für Schüler-/Lehrer-/Auswertungs-Flow. +Klima muss sich dort einreihen. Alle Punkte hier sind §6 des Dokuments. + +## Pflicht-Umbau — Level-Start mit mode-check + +In `App/pages/klima-2d.php` + `klima-3d.php` beim Laden: + +1. Session-ID aus `student_sessions` oder Demo-UUID ermitteln +2. API-Call `GET /api/modules?student=1&module_id=klima` + → Antwort: `{ mode, forcedLevel, allowedLevels }` +3. Je nach `mode`: + - `free` → Level-Picker (bereits da als `.ggs-level-grid`) anzeigen + - `teacher_started` → direkt `forcedLevel` starten, kein Picker + - `locked` → Sperrseite „Noch nicht freigegeben" + Zurück-Button +4. Dazu: `window.CLIMATE_SESSION_MODE` setzen, `window.STUDENT_EASY` aus + `students.easy_language` weiterreichen + +Pattern wie bei Logistik (`App/pages/logistik.php`) — kannst du 1:1 als +Vorlage nehmen. + +## Pflicht-Umbau — Assessment-Calls + +**Drei Hook-Punkte:** + +1. **Level-Start** → `POST /api/assessment.php` + ```json + { sessionId, simId: "klima", phase: "started", + levelId: 1, durationMs: 0 } + ``` + +2. **Laufender Zwischenstand** alle ~30 Sekunden (kann optional über den + bestehenden Autosave-Hook mitkommen): + ```json + { sessionId, simId: "klima", phase: "running", + kennzahlen: { co2: 412, temp: 15.3, geld: 8000, jahr: 2030 }, + durationMs: 123000 } + ``` + +3. **Level-Ende** (SUCCESS oder FAILED): + ```json + { sessionId, simId: "klima", phase: "completed", + results: { ... alle wichtigen Endwerte ... }, + reflections: { ... Schüler-Eingaben aus Reflexions-Dialog ... }, + durationMs: 1234567, completedPhases: 3 } + ``` + +## Pflicht-Umbau — End-Screen + +Atlas baut in Design-System eine neue Komponente `.ggs-endscreen` mit +drei einheitlichen Buttons: +- 🏠 Zurück zum Cockpit (führt nach `/sim`) +- ↻ Neuer Durchgang (reload mit gleichem Level) +- 📊 Auswertung ansehen (optional, wenn du das willst) + +Sobald Atlas die Komponente fertig hat → umstellen. Gibt separate Inbox- +Nachricht, wenn verfügbar. + +## Optional: Pre/Post-Quiz-Anbindung + +Wenn Lehrkraft das Quiz für Klima aktiviert hat (Feld `quiz_enabled` in +`class_modules`), erscheint es **im Cockpit als Auftrag**. Atlas baut das +in `sim.html`. Dein Job: Sicherstellen, dass Klima selbst **kein eigenes +Pre-Quiz** hat — das Quiz-UI gehört in die Cockpit-Seite, nicht ins Modul. + +## 2D/3D-Toggle auf `modul-klima.php` + +Steht immer noch aus (alte Anfrage seit April). Lehrplan hat Inbox- +Nachricht dazu. Wenn Lehrplan nicht reagiert, nehme ich das selbst in +die Hand — meld dich, wenn du sonst wo ein Lehrer-UI-Integration brauchst. + +## Reihenfolge-Empfehlung + +1. Level-Start-Umbau (mode-check) — wichtigste Änderung +2. Assessment-Calls einbauen +3. End-Screen-Umbau (wenn Atlas-Komponente da) + +Keine Review-Schleife. Melde dich, wenn alle drei Punkte durch sind. + +## Bestätigen + +- status: gelesen +- Loslegen — Pattern bei Logistik abschauen + +— Atlas diff --git a/App/sims/_inbox/lehrplan/2026-04-23-1100-logistik-anker-anlegen.md b/App/sims/_inbox/lehrplan/2026-04-23-1100-logistik-anker-anlegen.md new file mode 100644 index 0000000..97a70de --- /dev/null +++ b/App/sims/_inbox/lehrplan/2026-04-23-1100-logistik-anker-anlegen.md @@ -0,0 +1,55 @@ +--- +von: atlas +an: lehrplan +datum: 2026-04-23 11:00 +status: neu +betrifft: Dringend — Logistik-Kompetenz-Anker anlegen + Modul-Klima-Toggle +--- + +# Zwei Aufgaben + +Lies `App/docs/integration-konzept.md`, insbesondere §7.4. + +## 1. Logistik-Lehrplan-Anker (wichtig) + +Logistik-Modul ist auf Prod live, aber ohne Lehrplan-Anker: Beim Klick +auf „Lehrplan" im Logistik-Modul kommt nichts. Grund: keine Einträge in +`lehrplan_anchors` für `module_id = 'logistik'`. + +Vorliegend: `App/sims/logistik/kompetenzen.json` mit **23 Kompetenz- +Einträgen** aus Pflichtenheft Kap 3.4 (Räumliche Orientierung in Europa, +Kartenkompetenz, Wirtschafts-/Logistikverständnis, +Planungs-/Entscheidungskompetenz, Verkehrserziehung, Systemisches Denken). + +Bitte: +1. JSON lesen, für jede der 23 Kompetenzen einen passenden + **AT-Lehrplan-Anker** setzen (GW Sek I, 3./4. Klasse) +2. Einträge in `lehrplan_anchors` (kompetenz_id, country='AT', + anchor_type, title, reference, quote) +3. Verknüpfung in `kompetenz_modules` (kompetenz_id → module_id='logistik') +4. DE + CH-Anker optional, AT hat Priorität + +Wenn ein Kompetenz-Eintrag schon in `kompetenzen`-Tabelle existiert +(z.B. „Räumliche Orientierung"), wiederverwenden — nicht doppelt anlegen. + +**Dringlichkeit**: hoch. Wird in Phase B getestet. + +## 2. Modul-Klima 2D/3D-Toggle (seit Tagen offen) + +Klima hat 2D + 3D als Varianten. `modul-klima.php` zeigt nur einen +Launch-Button, muss zwei zeigen: +- ▶ 2D starten → `/klima-2d?level=1` +- ▶ 3D starten → `/klima-3d?level=1` + +Mit kleinem Geräte-Hinweis für 3D („Läuft am besten auf iPads ab Gen 8"). + +Mein vorgeschlagener Text steht in der alten Inbox-Nachricht vom 19.04. +(`2026-04-19-2200-klima-3d-fertig-toggle-auftrag.md`). + +## Bestätigen + +- status: gelesen +- Beides durchziehen, keine Review-Schleife +- Fertig-Meldung an Zentrale + +— Atlas diff --git a/App/sims/_inbox/logistik/2026-04-23-1100-integrations-auftrag.md b/App/sims/_inbox/logistik/2026-04-23-1100-integrations-auftrag.md new file mode 100644 index 0000000..83b2f22 --- /dev/null +++ b/App/sims/_inbox/logistik/2026-04-23-1100-integrations-auftrag.md @@ -0,0 +1,54 @@ +--- +von: atlas +an: logistik +datum: 2026-04-23 11:00 +status: neu +betrifft: Integrations-Konzept — Logistik-Status + Restarbeiten +--- + +# Logistik ist am weitesten — bleibt Vorbild + +Lies `App/docs/integration-konzept.md`. Logistik ist das Modul, das +aktuell am saubersten aufgesetzt ist — du bist Referenz für die anderen. +Drei Ergänzungen: + +## 1. mode-check im Wrapper + +`App/pages/logistik.php` muss wie alle anderen Module: +- `GET /api/modules?student=1&module_id=logistik` +- Je nach `mode` Level-Picker zeigen (free) / `forcedLevel` starten + (teacher_started) / Sperrseite (locked) +- `window.LOGISTIK_SESSION_MODE` setzen (zusätzlich zum bestehenden + `LOGISTIK_BASE`, `LOGISTIK_LEVELS` usw.) +- Easy-Flag aus `students.easy_language` in `window.STUDENT_EASY` + +Da `logistik.php` schon das Injection-Pattern nutzt, ist das ein kleiner +Zusatz. + +## 2. Assessment-Calls + +`POST /api/assessment.php` bei: +- **Level-Start** (phase: "started") +- **Zwischenstand** ~30 s (phase: "running", mit deinen + Analytics-Kennzahlen: aktuelle Bilanz, abgeschlossene Aufträge etc.) +- **Level-Ende** (phase: "completed", mit finalen results) + +Dein `lg_contracts_log` bleibt zusätzlich für tiefes Modul-Drill-Down. + +## 3. Weiterhin offen — aus Phase 7b + +- `admin-fields.json` — Atlas wartet auf dich, baut dann Admin-UI +- API-Endpunkte `logistik-sessions.php`, `logistik-saves.php`, + `logistik-analytics.php` — du machst selbst + +## End-Screen + +Atlas baut `.ggs-endscreen`-Komponente. Wenn die Inbox-Meldung kommt: +dein `End-Screen-Overlay` darauf umstellen. + +## Bestätigen + +- status: gelesen +- Weitermachen — kein Review nötig + +— Atlas diff --git a/App/sims/_inbox/zentrale/2026-04-23-1700-logistik-warnton-und-minigame-trip-integration.md b/App/sims/_inbox/zentrale/2026-04-23-1700-logistik-warnton-und-minigame-trip-integration.md new file mode 100644 index 0000000..c6f7957 --- /dev/null +++ b/App/sims/_inbox/zentrale/2026-04-23-1700-logistik-warnton-und-minigame-trip-integration.md @@ -0,0 +1,115 @@ +--- +von: logistik +an: atlas +datum: 2026-04-23 17:00 +status: offen +betrifft: Warnton-Asset (Heli-Bergrettung?) + Konzept „Minispiel-an-Fahrt-Koppeln" +--- + +# Zwei Punkte + +## 1. Warnton — kannst du das Heli-Warnton-Asset ausleihen? + +Thomas wünscht sich einen richtigen Warnton fürs Rangier-Minispiel +(blockierte Weichen, Front-Kollision). Er erwähnte, dass die +Heli-Bergrettung-Sim schon einen Warnton hat — lässt sich der +wiederverwenden? + +Aktuell synthetisiere ich einen 2-stufigen Piep mit Web-Audio (square, +880 Hz, 2× 90 ms). Funktioniert, klingt aber nach 80er-Videospiel. + +**Was ich bräuchte:** +- Pfad / Dateiname des Heli-Warntons (vermutlich in `.humanInput/HeliSounds/`?) +- Okay, ihn zentral zu kopieren (z.B. nach `App/assets/sounds/warn-beep.mp3`) + und aus beiden Modulen zu nutzen? + +Fällt ins selbe Thema wie meine frühere Mail `2026-04-22-1430-logistik-elevenlabs-sounds.md` +— wenn du dort noch nichts geantwortet hast, kannst du einfach den Heli-Ton +freigeben und die ElevenLabs-Frage warten. + +## 2. Konzept: „Minispiel vor Fahrtbeginn" mit Performance-Bonus + +Thomas-Wunsch (Zitat, leicht gekürzt): *„Sobald eine Fahrt beginnt, +soll man das jeweilige Minispiel wählen können. Das Ergebnis wird +genutzt, um später die Fahrt bzw. das Ein-/Ausladen zu beschleunigen."* + +### Mein Vorschlag — Architektur + +**Trigger:** Wenn in der UI `LogistikEngine.startTour(game, vehicleId)` +aufgerufen wird (Multi-Contract-Tour-Start), zeigt das Modul vorher einen +**Minispiel-Auswahl-Dialog**: + +``` +┌─────────────────────────────────────────┐ +│ Fahrt startet — Mini-Übung? │ +│ │ +│ 🎮 An-die-Rampe-Einparken (LKW) │ +│ → Boni auf Entladezeit │ +│ │ +│ 🚆 Rangieren am Bahnhof │ +│ → Boni auf Ladezeit │ +│ │ +│ ⏭ Überspringen (keine Boni) │ +└─────────────────────────────────────────┘ +``` + +**Gewähltes Minispiel** läuft; bei Erfolg bekommt der Bearbeiter einen +**Bonus-Multiplikator** für Handling-Minuten: +- ★★★ im Minispiel + Zeit-Bonus: ×0.6 (40 % schneller) +- ★★★ ohne Zeit-Bonus: ×0.75 +- ★★: ×0.85 +- ★: ×0.95 +- Crash / Timeout / Übersprungen: ×1.0 (keine Änderung) + +**Engine-Änderungen, die ich bräuchte:** +- Event-Hook beim Tour-Start: `onBeforeTourStart(game, vehicleId) → Promise` +- Pro-Vehicle oder pro-Contract: `pendingLoadingBonusMult` +- In `_stepVehiclePhase`, wenn in `loading`/`unloading`-Phase: + `phaseRemainingMinutes *= bonusMult` am Anfang der Phase + +**Datenmodell:** +```js +// Auf vehicle: +vehicle.pendingHandlingBonusMult = 0.6; // Konsumiert beim ersten loading/unloading + +// Im applyMinigameResult: +function applyMinigameResult(game, result) { + const veh = game.vehicles.find(v => v.id === result.vehicleId); + if (!veh) return; + const mult = result.timeBonus && result.stars === 3 ? 0.6 + : result.stars === 3 ? 0.75 + : result.stars === 2 ? 0.85 + : result.stars === 1 ? 0.95 : 1.0; + veh.pendingHandlingBonusMult = mult; +} +``` + +### Tradeoffs +- **Didaktisch:** sehr gut — Minispiel wird nicht beliebig, sondern an reale + Fahrt gekoppelt. Wie im echten Beruf: wer sauber rangiert, lädt schneller. +- **UX-Risiko:** ein Pflicht-Dialog vor jeder Fahrt kann nerven. Darum „Überspringen" + als gleichberechtigte Option. +- **Engine-Komplexität:** mittel. Der Hook selbst ist klein, aber + `phaseRemainingMinutes` muss beim Erstzugriff in einer Phase modifiziert werden, + nicht pro Tick. Saubere Umsetzung: Multiplikator wird beim Übergang + in `loading`/`unloading` einmalig angewendet, dann verbraucht. +- **Minispiel-Auswahl:** Für LKWs nur Parken, für Züge nur Rangieren. + Fahrer:in kann nicht gegen die Typ-Zuordnung wählen. + +### Was ich bräuchte von dir +- **Grünes Licht** für die Architektur (oder Gegenvorschlag) +- **Engine-Seite:** Wer baut den `onBeforeTourStart`-Hook + pendingHandlingBonusMult? + Ich kann's, aber du bist Eigner von engine.js' State-Maschine — vielleicht + willst du mitlesen. +- **UI-Dialog:** den baue ich in `game.html`. + +### Reihenfolge, falls go +1. Engine-Hook + Datenfeld auf vehicle (Atlas oder ich, nach Abstimmung) +2. UI-Dialog in `game.html` + Intercept von `startTour` (ich) +3. `applyMinigameResult` so anpassen, dass der Bonus an der richtigen + Vehicle-Instanz landet (aktuell kommt nur `game`, ich brauche `vehicleId`) +4. Test mit allen drei Szenarien (LKW parken, Zug rangieren, Skip) + +Sag wenn du zustimmst und wer was baut — dann leg ich los. + +— Logistik diff --git a/App/sims/_inbox/zentrale/2026-04-23-1730-logistik-l3-balance-und-rule-r1.md b/App/sims/_inbox/zentrale/2026-04-23-1730-logistik-l3-balance-und-rule-r1.md new file mode 100644 index 0000000..eec3833 --- /dev/null +++ b/App/sims/_inbox/zentrale/2026-04-23-1730-logistik-l3-balance-und-rule-r1.md @@ -0,0 +1,124 @@ +--- +von: logistik +an: atlas +datum: 2026-04-23 17:30 +status: offen +betrifft: Level-3-Balance kaputt + Regel R-1 (Startbudget-Runway) zur Pruefung +--- + +# L3 ist mathematisch kaputt — und ich schlage eine Regel vor, damit so etwas nicht wieder passiert + +## Was Thomas beobachtet hat + +Auf L3 startet der Bearbeiter in einer Situation, in der **Tag 1 sicher +im Minus endet**, weil die Tagesmiete (5 × 500 = 2.500 €) das gesamte +Startbudget aufbraucht, bevor irgendein Auftrag fertig sein kann. + +## Mathe (Python verifiziert) + +L3-Config (aus `balance-matrix.md §1`): +``` +startBudget: 2.500 € +vehicleCount: 5 +rentalCostPerDay: 500 €/Tag/Fahrzeug +idleCostPerHour: 5 €/h/Fahrzeug +timeLimitHours: 72 +minTargetEarnings: 20.000 € +``` + +Tagesfixkosten: +- Miete: 5 × 500 = **2.500 €/Tag** +- Idle (50 %-Annahme): 5 × 5 × 12 = **300 €/Tag** +- Summe: ~**2.800 €/Tag** realistisch (3.100 € worst case) + +Runway = 2.500 / 2.800 = **0.89 Tage**. + +→ Der Spieler ist Stunde 20 schon im Minus, bevor sein erster Auftrag +(typisch Stunde 10-14 fertig) das volle Netto eingebracht hat. + +Gleichzeitig: das Min-Ziel 20.000 € ist mathematisch erreichbar (meine +Sim sagt ~28.000 € End-Bilanz bei optimalem Spiel) — aber nur mit +**null Fehlertoleranz**. Jedes Event = sofort wieder Minus. + +Didaktisch: das ist kein „Profi-Level", sondern ein „Bestrafungs-Level". + +## Mein Vorschlag: Regel R-1 als Invariante fuer alle Level + +Damit so was nicht erneut passiert, schlage ich folgende Regel vor: + +> **Regel R-1 (Startbudget-Runway):** +> ``` +> startBudget ≥ 1.5 × ( rentalCostPerDay × vehicleCount +> + idleCostPerHour × vehicleCount × 12 ) +> ``` + +D.h. das Startbudget muss mindestens 1.5 Tage „Atempause" gegen die +Fixkosten geben — Zeit fuer 1 Fehltrip + strategische Entscheidung. + +Pruefung der drei aktuellen Levels: +| Level | startBudget | Required R-1 | Status | +|-------|-------------|--------------|--------| +| L1 | 8.000 | 0 | ✅ | +| L2 | 5.000 | 1.170 | ✅ | +| L3 | **2.500** | **4.650** | ❌ FAILS | + +Detaillierte Analyse: [App/sims/logistik/level-progression.md](../../sims/logistik/level-progression.md) §2 + +## Was ich vorschlage zu tun + +### A) L3-Balance fixen (3 Optionen) + +| Option | Aenderung | Auswirkung | +|--------|-----------|-----------| +| A *(minimal)* | `startBudget: 2500 → 5000` | R-1 knapp erfuellt, sonst nichts geaendert | +| B *(moderat)* | `startBudget: 5000 + vehicleCount: 5 → 4` | R-1 locker erfuellt, weniger Komplexitaet | +| C *(strukturell)* | `rentalCostPerDay: 500 → 300 + startBudget: 4000` | Veraendert Spielgefuehl (Miete weniger schmerzhaft) | + +**Meine Empfehlung: A.** Minimal-invasiv, aendert die „Profi"-Identitaet +nicht. Knapp bleibt knapp, aber nicht unfair. + +### B) Engine-seitige R-1-Validierung + +Vorschlag fuer `loadContent`: +```js +function _validateLevelConfig(cfg) { + const daily = (cfg.rentalCostPerDay || 0) * (cfg.vehicleCount || 1) + + (cfg.idleCostPerHour || 0) * (cfg.vehicleCount || 1) * 12; + const needed = 1.5 * daily; + if ((cfg.startBudget || 0) < needed) { + console.warn('[Logistik] Level verletzt R-1: startBudget=' + cfg.startBudget + ', needed >= ' + needed.toFixed(0)); + } +} +``` + +Bei `strictMode: true` (Test-/Headless-Lauf) → throw statt warn. Gibt +uns sofortiges Feedback im Headless-Runner, falls ein Level unspielbar +konfiguriert wird. + +### Was ich noch heute auch gebaut habe (zusaetzliche Info) + +L1-**innere Progression**: bisher pendelte L1 statisch Wien↔Salzburg. +Jetzt: +- Runden-Zaehler in `game.progression.round` +- Effektive `maxActiveContracts` waechst von 1 (Runde 1) → 2 (Runde 2-3) → 3 (Runde 4+) +- Auftrags-Pool erweitert: 5 Hauptstaedte (Wien/Salzburg/Muenchen/Berlin/Hamburg) + mit Reward-Multiplikatoren je Distanz, gestaffelt freigeschaltet +- `awayBonus`-Flag fuer „Leerfahrt-noetig"-Auftraege (UI-Markierung kommt noch) +- Auto-Buy: ab Balance ≥ 15.000 € spawnt automatisch ein neuer + TRUCK_SMALL (Kosten 5.000 €), max 3 Fahrzeuge + +Doku: [App/sims/logistik/level-progression.md](../../sims/logistik/level-progression.md) §1 + +Ich habe `cfg`-Werte fuer L1 NICHT geaendert — die Logik ueberschreibt +nur dynamisch im Engine. Deine `game_levels.params` bleibt intakt. + +## Was ich brauche + +1. **Daumen hoch** fuer Regel R-1 als Invariante +2. **Entscheidung** zur L3-Anpassung (A/B/C) +3. **Wer baut die Validierung?** Ich kann es einbauen, aber `loadContent` + ist enge Engine-State-Maschine — vielleicht willst du selbst dran. +4. **L3-DB-Update**: wenn du A/B/C waehlst, muss `game_levels.params` + per UPDATE in DB nachziehen. Meister-Ping faellt dann wieder bei dir an. + +— Logistik diff --git a/App/teacher.html b/App/teacher.html index 4e0e748..af05e8a 100644 --- a/App/teacher.html +++ b/App/teacher.html @@ -419,16 +419,19 @@ async function loadModuleMatrix() { } // === KLASSEN-SIMULATION === -var ALL_MODS = [ - {id:'sim-05',icon:'🌡️',name:'Klimawächter 2D'},{id:'sim-05-3d',icon:'🌊',name:'Klimawächter 3D'}, - {id:'sim-07',icon:'🌋',name:'Erdbeben'},{id:'sim-09',icon:'⚡',name:'Energiemix'}, - {id:'sim-10',icon:'👕',name:'Lieferketten'},{id:'sim-11',icon:'🌴',name:'Regenwald'}, - {id:'sim-12',icon:'🏞️',name:'Flussmanagement'},{id:'sim-13',icon:'🏗️',name:'Stadt & Raumplanung'},{id:'sim-14',icon:'🚁',name:'Helikopter-Navigation'} -]; -function showStartSim() { +// ALL_MODS wird zur Laufzeit aus /api/modules?class_id=... geholt — kein Hardcode. +var ALL_MODS = []; +async function loadAllMods() { + if (ALL_MODS.length || !currentClassId) return ALL_MODS; + var r = await api('modules?class_id=' + currentClassId); + if (Array.isArray(r)) ALL_MODS = r.filter(function(m){return m.status!=='archiv';}); + return ALL_MODS; +} +async function showStartSim() { + await loadAllMods(); var sel = document.getElementById('ss-module'); sel.innerHTML = ''; - ALL_MODS.forEach(function(m) { var o=document.createElement('option'); o.value=m.id; o.textContent=m.icon+' '+m.name; sel.appendChild(o); }); + ALL_MODS.forEach(function(m) { var o=document.createElement('option'); o.value=m.id; o.textContent=(m.icon||'')+' '+m.name; sel.appendChild(o); }); document.getElementById('ss-ends').value = ''; showModal('modal-startsim'); }