Files
geograsim/App/.LocalDeveloperTools/test-checklist-2026-08-19.html
Adminator fecd8bb9c9 Textsystem: Bis-Strich mit Leerzeichen (Zahl – Zahl) per Agent-Sweep
562 Ersetzungen in 50 Dateien: Halbgeviertstrich zwischen zwei Ziffern (=
"bis"-Bereich) bekommt Leerzeichen davor/danach (3–4 -> 3 – 4). Regex
/([0-9])\s*–\s*(?=[0-9])/ auf Rohtext (format-erhaltend, CRLF/Einrueckung
unveraendert), Bindestriche in Woertern und Minuszeichen unangetastet, alle
JSON weiter gueltig. Energiemanager-Zeitbloecke sind Display-Labels (kein
Logik-Key) -> safe.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-22 12:38:19 +02:00

273 lines
18 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GeoGraSim — Test-Checkliste (19.08.2026)</title>
<style>
:root{
--bg:#f4f2ec; --card:#fff; --ink:#242018; --muted:#7a7266; --line:rgba(0,0,0,.08);
--accent:#4a7c8a; --accent-d:#356070; --ok:#4a8a5a; --warn:#c58a2a; --sim:#7a5ca8;
}
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:'Inter',system-ui,-apple-system,sans-serif;background:var(--bg);color:var(--ink);line-height:1.5;padding-bottom:4rem}
header{position:sticky;top:0;z-index:10;background:#fff;border-bottom:1px solid var(--line);
padding:.9rem 1.2rem;display:flex;align-items:center;gap:1rem;flex-wrap:wrap;box-shadow:0 1px 6px rgba(0,0,0,.04)}
header h1{font-size:1.15rem;font-weight:900;color:var(--accent-d);letter-spacing:-.02em}
header .sub{font-size:.72rem;color:var(--muted)}
.progress{margin-left:auto;display:flex;align-items:center;gap:.6rem;font-size:.8rem;font-weight:700;color:var(--accent-d)}
.bar{width:160px;height:9px;border-radius:99px;background:#e4e0d6;overflow:hidden}
.bar > div{height:100%;background:var(--ok);width:0;transition:width .3s}
.reset{font-size:.66rem;color:var(--muted);border:1px solid var(--line);background:#fff;border-radius:6px;padding:.25rem .5rem;cursor:pointer}
.wrap{max-width:920px;margin:1.4rem auto;padding:0 1.1rem}
.intro{font-size:.86rem;color:var(--muted);margin-bottom:1.4rem;background:#fff;border:1px solid var(--line);border-radius:10px;padding:.8rem 1rem}
.intro code{background:#eee9df;padding:.05rem .35rem;border-radius:4px;font-size:.85em}
section{margin-bottom:1.8rem}
.sec-h{font-size:.7rem;font-weight:800;text-transform:uppercase;letter-spacing:.1em;color:var(--accent);
margin:0 0 .6rem .2rem;display:flex;align-items:center;gap:.5rem}
.sec-h .cnt{font-size:.62rem;color:var(--muted);font-weight:600;letter-spacing:.02em}
.item{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:.85rem 1rem;margin-bottom:.7rem;
box-shadow:0 1px 3px rgba(0,0,0,.03);transition:opacity .2s}
.item.done{opacity:.55}
.item-top{display:flex;gap:.7rem;align-items:flex-start}
.item-top input[type=checkbox]{width:22px;height:22px;margin-top:.1rem;accent-color:var(--ok);cursor:pointer;flex-shrink:0}
.item-title{font-weight:800;font-size:.98rem}
.item.done .item-title{text-decoration:line-through;text-decoration-color:rgba(0,0,0,.3)}
.tag{display:inline-block;font-size:.58rem;font-weight:800;text-transform:uppercase;letter-spacing:.05em;
padding:.12rem .5rem;border-radius:99px;vertical-align:middle;margin-left:.5rem}
.tag.live{background:#dceadd;color:#2f6b3a}
.tag.pending{background:#fdeecd;color:#8a6510}
.body{padding-left:calc(22px + .7rem);margin-top:.4rem;font-size:.86rem;color:#4a463d}
.body .row{margin:.28rem 0}
.body b{color:var(--ink)}
.k{display:inline-block;min-width:74px;font-weight:700;color:var(--muted);font-size:.72rem;text-transform:uppercase;letter-spacing:.03em}
a.url{color:var(--accent-d);font-family:ui-monospace,monospace;font-size:.82em;text-decoration:none;border-bottom:1px dotted var(--accent)}
a.url:hover{background:#eaf2f4}
.exp{background:#f0f6f1;border-left:3px solid var(--ok);border-radius:0 6px 6px 0;padding:.35rem .6rem;margin-top:.35rem;font-size:.82rem;color:#33613f}
.note{width:100%;margin-top:.5rem;border:1px solid var(--line);border-radius:7px;padding:.4rem .55rem;font-family:inherit;
font-size:.82rem;resize:vertical;min-height:0;height:2rem;background:#fcfbf8;color:var(--ink)}
.note::placeholder{color:#b3ac9e}
footer{text-align:center;color:var(--muted);font-size:.72rem;margin:2rem 0}
</style>
</head>
<body>
<header>
<div>
<h1>🧪 GeoGraSim — Test-Checkliste</h1>
<div class="sub">Alle Erledigungen seit den Weltküche-Lebensmitteln · Stand 19.08.2026 · lokal testen</div>
</div>
<div class="progress">
<span id="pcount">0 / 0</span>
<div class="bar"><div id="pbar"></div></div>
<button class="reset" onclick="if(confirm('Alle Haken + Notizen zurücksetzen?')){localStorage.clear();location.reload();}">reset</button>
</div>
</header>
<div class="wrap">
<div class="intro">
Basis-URL lokal: <code>http://localhost/geograsim/App/</code> · <b>Haken &amp; Notizen</b> werden im Browser gespeichert.
<span class="tag live">live</span> = auf Prod, <span class="tag pending">bereit</span> = committed, kommt mit dem nächsten Deploy.
Für Admin-Seiten zuerst über <a class="url" href="http://localhost/geograsim/App/admin.html" target="_blank">admin.html</a> anmelden (Username <b>Thomas</b> → PIN ist lokal vorausgefüllt).
</div>
<!-- ================= WELTKÜCHE (SIM) ================= -->
<section data-sec="Weltküche Simulation"></section>
<!-- ================= WELTKÜCHE (LEHRPLAN/ADMIN) ================= -->
<section data-sec="Weltküche Lehrplan &amp; Admin"></section>
<!-- ================= LEHRER-COCKPIT ================= -->
<section data-sec="Lehrer-Cockpit (teacher.html)"></section>
<!-- ================= TOURISMUSREGION ================= -->
<section data-sec="Tourismusregion"></section>
<!-- ================= LOGISTIK & PLATTFORM ================= -->
<section data-sec="Logistik &amp; Plattform"></section>
<!-- ================= ADMIN & AUTH ================= -->
<section data-sec="Admin &amp; Auth-Fundament"></section>
</div>
<footer>GeoGraSim · Test-Checkliste · lokal generiert 19.08.2026</footer>
<script>
// Alle Test-Items als Daten — daraus wird die Liste gebaut.
const BASE = 'http://localhost/geograsim/App/';
function u(path){ return BASE + path; }
const ITEMS = {
'Weltküche Simulation': [
{ id:'wk-artikel', t:'Korrekte Artikel für alle 70 Zutaten', tag:'live',
w:'der/die/das je Zutat („die Kartoffel", „der Pfeffer", „das Olivenöl") statt Platzhalter.',
url:'sims/weltkueche/game.html', steps:'Ein Gericht spielen, Zutat lösen/falsch klicken im Feedback-Satz steht der richtige Artikel.',
exp:'Nie mehr „der/die/das Kartoffel" immer „die Kartoffel" usw.' },
{ id:'wk-ukraine', t:'Verdrehter Herkunfts-Satz korrigiert (Ukraine-Fall)', tag:'live',
w:'Bei fernem Großproduzenten (z. B. Ukraine-Kartoffel im AT-Gericht) sachlich korrektes Feedback.',
url:'sims/weltkueche/game.html', steps:'Tiroler Gröstl → Kartoffel → ein fernes Land wie Ukraine anklicken.',
exp:'„…in unseren Handel kommt davon kaum etwas… nimmt man die Kartoffel aus Österreich oder einem Nachbarland." (nicht mehr „…stammt die Kartoffel von dort").' },
{ id:'wk-heimat', t:'Heimat-Banner neu formuliert', tag:'live',
w:'Erklärt „bevorzugt aus AT, aber AT deckt den Bedarf nicht allein".',
url:'sims/weltkueche/game.html', steps:'AT-Gericht, eine Heimat-Zutat lösen → grünes Banner lesen.',
exp:'„Zutaten werden zwar bevorzugt aus Österreich verwendet, aber Österreich allein deckt den Bedarf nicht ab darum zählt auch ein Nachbarland."' },
{ id:'wk-gravitation', t:'Herkunft per Handels-Gravitation (Nähe × Menge)', tag:'pending',
w:'Zweit-Lieferant = nächster großer Nachbar, nicht ferner Großproduzent.',
url:'sims/weltkueche/game.html', steps:'Ein AT-Gericht mit Eiern/Kartoffel spielen richtige Länder prüfen.',
exp:'AT-Eier → Österreich + Deutschland (NICHT Frankreich), obwohl FR mehr Eier erzeugt.' },
{ id:'wk-punkte', t:'Punktefix: „alle Falschen zuerst = 0"', tag:'pending',
w:'Strafe pro Fehlklick = 100 ÷ Anzahl Distraktoren.',
url:'sims/weltkueche/game.html', steps:'Bei einer Zutat ALLE falschen Länder zuerst anklicken, dann die richtigen.',
exp:'Ergebnis 0 Punkte. Banner zeigt die tatsächliche Strafe pro Fehlklick.' },
{ id:'wk-phase', t:'Live-State zeigt echten Fortschritt statt „forschen"', tag:'live',
w:'GGS_LIVE_STATE-Phase = „Gericht X · Zutat Y/Z".',
url:'teacher.html', steps:'Als Lehrer im Cockpit „Aktuell" ein Weltküche-Kind ansehen (oder Live-State im Sim prüfen).',
exp:'Phase/Info zeigt „Gericht 2 · Zutat 3/5" nicht dauerhaft „forschen".' },
{ id:'wk-submit', t:'Backend-Submit + Persistenz geschaffter Gerichte', tag:'live',
w:'Pro fertigem Gericht ein assessments-Eintrag; geschaffte Gerichte bleiben nach Reload.',
url:'sims/weltkueche/game.html', steps:'Ein Gericht abschließen, Seite neu laden.',
exp:'Bereits geschaffte Gerichte bleiben erkennbar; im Lehrer-Cockpit taucht das Ergebnis auf.' },
],
'Weltküche Lehrplan &amp; Admin': [
{ id:'wk-lehrplan', t:'Lehrplan-Bezug auf der Weltküche-Modulseite', tag:'live',
w:'Kompetenz „Welternährung, Konsum und globale Disparitäten" + Anker.',
url:'modul-weltkueche', steps:'Seite öffnen, zu „Lehrplan-Bezug" scrollen.',
exp:'Kompetenzen + Anker sichtbar (nicht mehr „Noch keine Kompetenzen zugeordnet").' },
{ id:'wk-filter', t:'Länder-Filter im Lehrplan-Bezug wirkt', tag:'live',
w:'Filter-Chips (Alle/AT/DE/CH/FL) blenden Fach-Anker wirklich um.',
url:'modul-weltkueche', steps:'Bei „Lehrplan-Bezug" auf Deutschland, dann Österreich klicken.',
exp:'Sichtbare Fach-Anker ändern sich je Land (vorher passierte nichts).' },
{ id:'wk-admin', t:'Admin-QA: Weltküche-Herkunftsübersicht (NEU)', tag:'pending',
w:'Pro Gericht × Zutat richtige Länder + Distraktoren; „nur Auffällige"-Filter.',
url:'admin-weltkueche.html', steps:'Als Admin einloggen, Seite öffnen. Suche/Kategorie testen, „nur Auffällige" aktivieren.',
exp:'30 Gerichte; Kartoffel/Eier → 🟢 AT+DE. „Nur Auffällige" zeigt ~5 Kandidaten zum Prüfen.' },
],
'Lehrer-Cockpit (teacher.html)': [
{ id:'tc-menu', t:'Menü „Live" → „Ergebnisse" mit Tag · Aktuell · Vergleich · Gesamt', tag:'pending',
w:'Umbenannt + 4. Unteransicht „Gesamt" (alte Ergebnis-Ansicht).',
url:'teacher.html', steps:'Als Lehrer einloggen, Klasse wählen, Tab „Ergebnisse".',
exp:'Vier Unteransichten; „Gesamt" zeigt die frühere Ergebnis-/Auswertungsansicht.' },
{ id:'tc-quali', t:'Qualitäts-Kennzahl (richtig ↔ falsch als Balken)', tag:'pending',
w:'Grafischer Balken (grün/rot) je Kind in Aktuell + Vergleich.',
url:'teacher.html', steps:'Ergebnisse → Aktuell/Vergleich bei aktivem Weltküche-Kind.',
exp:'Zweifarbiger Balken + %-Wert („blödeln vs. gut" sichtbar).' },
{ id:'tc-labels', t:'Klartext-Labels statt Jargon', tag:'pending',
w:'„Stark/Solide/Viele Fehler" statt „Kritisch/OK"; „Gerade dabei" statt „Phase".',
url:'teacher.html', steps:'Aktuell/Vergleich ansehen.',
exp:'Keine „Kritisch"-Bezeichnung mehr.' },
{ id:'tc-simlabels', t:'Sim-Bezeichner: Wort + 3-Buchstaben-Kürzel', tag:'pending',
w:'Kein „Log L1" mehr — enger Balken zeigt Kürzel (LOG), breiter das Wort.',
url:'teacher.html', steps:'Ergebnisse → Tag, Session-Balken ansehen.',
exp:'Balken tragen LOG/Logistik statt „Log L1".' },
{ id:'tc-logistik', t:'Logistik hat kuratierte Spalten (statt Roh-Dump)', tag:'pending',
w:'Stufe · Budget · Geliefert · Unterwegs · Offen · Verspätet · Fortschritt.',
url:'teacher.html', steps:'Ergebnisse → Aktuell mit aktivem Logistik-Kind.',
exp:'Sinnvolle Spalten keine simId/__activeMs/timestamp-Spalten mehr.' },
{ id:'tc-streifen', t:'Aktivitätsstreifen: Farbe je Sim, Höhe=Erfolg, Rahmen=Zustand', tag:'pending',
w:'Fertig = geschlossener Rahmen · läuft = rechts offen · Abbruch = gestrichelt + ✖.',
url:'teacher.html', steps:'Ergebnisse → Tag, mit Sessions eines Tages.',
exp:'Balken je Sim gefärbt, höher = besser; Rahmen/✖ zeigen den Zustand; Legende erklärt es.' },
{ id:'tc-assess', t:'assessment-Skip-Fix (keine leeren Ping-Zeilen mehr)', tag:'pending',
w:'„started/running"-Pings landen nicht mehr als leere Zeilen in Ergebnisse/Tag.',
url:'teacher.html', steps:'Nach etwas Spielzeit Ergebnisse/Tag ansehen.',
exp:'Nur echte Abschlüsse als Balken kein leeres Rauschen.' },
],
'Tourismusregion': [
{ id:'tr-start', t:'Startseite: keine Name/Klasscode-Felder, Klick auf Route startet', tag:'live',
w:'Felder entfernt; Klick auf eine Wegkarte startet sofort (kein „Partie starten").',
url:'sims/tourismusregion/game.html', steps:'Sim öffnen, eine Wegkarte anklicken.',
exp:'Keine Eingabefelder; Partie startet direkt bei Kartenklick.' },
{ id:'tr-ipad', t:'iPad-Responsive (Panels decken die Karte nicht mehr zu)', tag:'live',
w:'Panels mit Höhen-Deckel + eigene iPad-Landscape-Regeln. AM IPAD TESTEN!',
url:'sims/tourismusregion/game.html', steps:'Am echten iPad (Landscape) öffnen und ein Gebäude antippen.',
exp:'KPIs/Baumenü/Info-Panel überdecken die Karte nicht; Info-Panel scrollt statt zu überziehen.' },
{ id:'tr-lift', t:'Seilbahn hat eine Talstation (nicht mehr „transparent")', tag:'live',
w:'Talstation mit Dach + Umlenkrad unten am Lift.',
url:'sims/tourismusregion/game.html', steps:'Seilbahn bauen (Berg erschließen), auf die Talstation schauen.',
exp:'Solider Baukörper unten kein durchsichtiger Lift-Anfang.' },
{ id:'tr-save', t:'Abschluss: kein „Ergebnis speichern"-Button (Auto-Save)', tag:'live',
w:'Automatisches Speichern am Partie-Ende; Button entfernt.',
url:'sims/tourismusregion/game.html', steps:'Eine Partie zu Ende bringen.',
exp:'Hinweis „automatisch gespeichert", kein Speichern-Button.' },
{ id:'tr-pass', t:'Neue Karte „Passstraße"', tag:'live',
w:'Route bergauf (136 Punkte) + 41 Bauslots + Hintergrundbild.',
url:'sims/tourismusregion/game.html', steps:'Im Startbildschirm „Passstraße" wählen.',
exp:'Karte lädt, Route + Bauslots passen aufs Bild (optisch prüfen).' },
{ id:'tr-balance', t:'Balance: Umwelt beißt jetzt (Übertourismus)', tag:'pending',
w:'Zu viel Bauen + Verkehr senken die Umwelt spürbar; Umwelt-Malus wirkt.',
url:'sims/tourismusregion/game.html', steps:'Viel/dicht bauen und laufen lassen; Umwelt-KPI beobachten.',
exp:'Umwelt fällt deutlich (Richtung <45), umweltbewusste Gäste werden unzufriedener.' },
{ id:'tr-erklaer', t:'Schüler-Erklärung: Umwelt & Naturschutzgebiet', tag:'pending',
w:'Willkommenskarte + Warnkarte bei Umwelt<50 + klarere Naturschutz-Beschreibung.',
url:'sims/tourismusregion/game.html', steps:'Neu starten → Willkommenskarte lesen; Umwelt absacken lassen → Warnkarte.',
exp:'Karte erklärt Umwelt-Mechanik + Naturschutzgebiet; Warnkarte erscheint bei <50.' },
],
'Logistik &amp; Plattform': [
{ id:'lg-pause', t:'Logistik-Pause friert die Aktivzeit ein', tag:'pending',
w:'Bei Pause zählt die „Seit"-Zeit im Cockpit nicht weiter.',
url:'sims/logistik/game.html', steps:'Logistik spielen, ⏸ drücken, im Lehrer-Cockpit die Aktivzeit beobachten.',
exp:'Aktivzeit steht still, solange pausiert; läuft nach dem Fortsetzen weiter.' },
{ id:'pf-scripts', t:'Wrapper-Bug behoben (/teacher lud kein Script mehr)', tag:'live',
w:'Favicon wurde ins letzte </head> injiziert zerschoss ein JS-Popup-Script.',
url:'teacher', steps:'Die /teacher-Route (Wrapper) öffnen.',
exp:'Seite lädt sauber, keine „Invalid token"-Konsolenfehler. (teacher.html direkt lief immer.)' },
],
'Admin &amp; Auth-Fundament': [
{ id:'ad-login', t:'Lokaler Admin-Login ohne Mailversand (Dev-PIN)', tag:'pending',
w:'PIN wird lokal vorausgefüllt; Mailer-Fehler sperrt nicht mehr aus. Auf Prod bleibt 2FA-Mail Pflicht.',
url:'admin.html', steps:'Username Thomas + Passwort → „Anmelden".',
exp:'PIN-Feld ist vorausgefüllt („Dev: … vorausgefüllt"), „Bestätigen" → eingeloggt.' },
{ id:'auth-found', t:'Auth-Fundament JWT + Redis (schlafend, non-breaking)', tag:'pending',
w:'firebase/php-jwt + predis + Jwt/SessionStore + Session-Kanalisierung. AUTH_BACKEND=session.',
url:'', steps:'Kein direkter Test nötig — nur sicherstellen, dass Login/Admin normal funktionieren.',
exp:'Alles läuft wie bisher (das Fundament ist inaktiv, bis der JWT-Cutover kommt).' },
],
};
const listWrap = document.querySelector('.wrap');
document.querySelectorAll('section[data-sec]').forEach(sec => {
const name = sec.getAttribute('data-sec');
const items = ITEMS[name] || [];
let html = '<div class="sec-h">' + name + ' <span class="cnt">' + items.length + ' Punkte</span></div>';
items.forEach(it => {
const urlHtml = it.url ? '<a class="url" href="' + u(it.url) + '" target="_blank" rel="noopener">' + u(it.url) + '</a>' : '<span style="color:#b3ac9e">—</span>';
html += ''
+ '<div class="item" data-id="' + it.id + '">'
+ '<div class="item-top">'
+ '<input type="checkbox" data-check="' + it.id + '">'
+ '<div style="flex:1"><span class="item-title">' + it.t + '</span>'
+ '<span class="tag ' + it.tag + '">' + (it.tag==='live'?'live':'bereit') + '</span></div>'
+ '</div>'
+ '<div class="body">'
+ '<div class="row"><span class="k">Was</span> ' + it.w + '</div>'
+ '<div class="row"><span class="k">Öffnen</span> ' + urlHtml + '</div>'
+ '<div class="row"><span class="k">Testen</span> ' + it.steps + '</div>'
+ '<div class="exp">✓ Erwartet: ' + it.exp + '</div>'
+ '<textarea class="note" data-note="' + it.id + '" placeholder="Notiz / gefundene Abweichung…"></textarea>'
+ '</div>'
+ '</div>';
});
sec.innerHTML = html;
});
// Persistenz + Fortschritt
const checks = document.querySelectorAll('input[data-check]');
function refresh(){
let done = 0;
checks.forEach(c => { if(c.checked){ done++; c.closest('.item').classList.add('done'); } else c.closest('.item').classList.remove('done'); });
document.getElementById('pcount').textContent = done + ' / ' + checks.length;
document.getElementById('pbar').style.width = (checks.length? (done/checks.length*100):0) + '%';
}
checks.forEach(c => {
const key = 'chk_' + c.getAttribute('data-check');
c.checked = localStorage.getItem(key) === '1';
c.addEventListener('change', () => { localStorage.setItem(key, c.checked?'1':'0'); refresh(); });
});
document.querySelectorAll('textarea[data-note]').forEach(t => {
const key = 'note_' + t.getAttribute('data-note');
t.value = localStorage.getItem(key) || '';
t.addEventListener('input', () => localStorage.setItem(key, t.value));
});
refresh();
</script>
</body>
</html>