m
This commit is contained in:
parent
246aa3b7b9
commit
5043e9ac7b
2 changed files with 207 additions and 20 deletions
|
|
@ -93,9 +93,53 @@ Plättchenrückseite stand.
|
|||
- **Inhalt:** Aktivitäten/Gates/Rollen = Brett-Elemente (`../00_Konzept/`).
|
||||
- **Ausgang:** Debrief-Daten → Workshop-Dokumentation (`../05_Workshop-Dokumentation/`).
|
||||
|
||||
## 8. Offene Punkte
|
||||
## 8. Companion-Chatbot (optional · Nachschlage-Begleiter)
|
||||
|
||||
Idee: Teilnehmende stellen spontane Verständnisfragen, die das feste Quiz nicht
|
||||
abdeckt — z. B. „Was ist das Service Design Document?" oder „Wer ist an Gate 2
|
||||
accountable?". Der Bot beantwortet sie aus dem Blueprint.
|
||||
|
||||
### Rolle & Grenzen (didaktisch)
|
||||
- **Nachschlagen, nicht führen.** Der Bot ersetzt **nicht** die Stationsführung.
|
||||
Das Lernprinzip (erst Gruppendiskussion → Tipp → App-Auflösung) bleibt das
|
||||
Rückgrat; ein frei führender Bot würde das „produktive Ringen" aushebeln.
|
||||
- **Freischaltung gated:** pro Station erst **nach dem „Auflösen"** verfügbar
|
||||
(oder als klar getrennter „Frag nach"-Knopf), damit er das Diskutieren-zuerst
|
||||
nicht kurzschließt.
|
||||
- **Scope-begrenzt:** antwortet nur aus dem Blueprint, keine freie Beratung;
|
||||
bei Unklarheit Verweis auf den „Unklar"-Marker → Debrief.
|
||||
|
||||
### Datengrundlage
|
||||
- Der Lifecycle-Blueprint ist klein (~2.000 Zeilen YAML). **Kein RAG/Vektor-Setup
|
||||
nötig** — alle `service-lifecycle_*.yaml` + `spm_rollen.yaml` passen in einen
|
||||
einzigen System-Prompt-Kontext.
|
||||
- Single Source of Truth bleibt der Blueprint (keine Doppelpflege).
|
||||
|
||||
### Wiederverwendung statt Neubau
|
||||
- Im Repo existiert bereits ein Bot: `#099_tools/digitom-bot/` mit Knowledge-Base
|
||||
(`SPM_service-lifecycle_*.md`) und Dify-Upload. **Vor Neubau prüfen**, ob dieser
|
||||
als Begleiter angedockt werden kann.
|
||||
|
||||
### Offene Architektur-Entscheidung: Offline vs. Cloud
|
||||
Ein Laufzeit-LLM braucht einen API-Aufruf → Internet/Endpoint/Key. Das steht im
|
||||
**Konflikt zur Konzept-Bedingung** (offline, Kiosk, keine Cloud, kein Login;
|
||||
vgl. Abschnitt 6). Optionen:
|
||||
|
||||
| Variante | Pro | Contra |
|
||||
|----------|-----|--------|
|
||||
| **(a) Cloud-LLM** (Dify / Anthropic / Azure-OpenAI) | geringster Aufwand, beste Qualität | Online-Pflicht, Datenschutz-/Beschaffungsfreigabe |
|
||||
| **(b) Lokales LLM** (z. B. Ollama auf dem Gerät) | offline-konform | Hardware-/Setup-Aufwand, schwächere Qualität |
|
||||
| **(c) Statische FAQ** (vorab generierte Q&A aus Blueprint) | voll offline, keine Kosten | keine Freitextfragen, begrenzte Abdeckung |
|
||||
|
||||
Empfehlung für den Pilot: mit **(a)** als separater, optionaler Begleiter starten
|
||||
und im Pilot evaluieren, ob der Mehrwert die Online-Pflicht rechtfertigt.
|
||||
|
||||
## 9. Offene Punkte
|
||||
|
||||
- [ ] Format `questions.json` spezifizieren.
|
||||
- [ ] Entscheidung Framework vs. Vanilla.
|
||||
- [ ] Wer pflegt/baut? (intern DIGIT vs. extern)
|
||||
- [ ] Datenschutz: rein lokal, keine personenbezogenen Daten — bestätigen.
|
||||
- [ ] Companion-Chatbot: Entscheidung Offline vs. Cloud (Abschnitt 8).
|
||||
- [ ] Prüfen, ob `digitom-bot`/Dify als Begleiter wiederverwendbar ist.
|
||||
- [ ] Freischalt-Logik des Bots festlegen (nach „Auflösen" vs. jederzeit).
|
||||
|
|
|
|||
|
|
@ -145,6 +145,16 @@
|
|||
.pickerItem .dot { width:10px; height:10px; border-radius:50%; flex:none; }
|
||||
.pickerItem .id { color:var(--muted); font-size:12px; font-variant-numeric:tabular-nums; }
|
||||
.pickerItem .gate { margin-left:auto; font-size:11px; font-weight:700; color:#fff; background:var(--ink); padding:1px 8px; border-radius:999px; }
|
||||
.pickerItem.correct { border-color:var(--ok); background:#f1faf4; }
|
||||
.pickerItem.wrongPick { border-color:var(--bad); background:#fdf3f3; }
|
||||
.recBox { border:1px solid var(--line); border-left:4px solid var(--ok); border-radius:10px; padding:14px 16px; margin:14px 0; }
|
||||
.recBox h4 { margin:0 0 6px; font-size:13px; text-transform:uppercase; letter-spacing:.5px; color:var(--muted); }
|
||||
/* Geführte Tour */
|
||||
.tourBanner { background:#fff7f7; border:1px solid var(--line); border-left:4px solid var(--accent); border-radius:10px; padding:10px 14px; margin-bottom:16px; font-size:13px; line-height:1.45; }
|
||||
.tourBanner b { color:var(--ink); }
|
||||
.tourProg { font-size:12px; color:var(--muted); margin-bottom:8px; font-variant-numeric:tabular-nums; }
|
||||
.tourNarr { background:#eef4fb; border-radius:12px; padding:16px 18px; margin:14px 0; font-size:16px; line-height:1.55; }
|
||||
.tourNarr h4 { margin:0 0 8px; font-size:12px; text-transform:uppercase; letter-spacing:.6px; color:var(--design); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
|
@ -178,6 +188,65 @@
|
|||
</dialog>
|
||||
|
||||
<script>
|
||||
/* Empfohlener Einstiegspunkt je Change-Typ (didaktische Auflösung in Schritt 2).
|
||||
Reihenfolge entspricht CHANGE_TYPES. */
|
||||
const START_EMPFEHLUNG = [
|
||||
{ id:"ds_01", grund:"Ein Major Change auf Top-Level ist strategisch getrieben und betrifft den Service grundlegend – er durchläuft den vollen Lebenszyklus ab dem Design (Service-Definition)." },
|
||||
{ id:"ds_01", grund:"Auch ein Major Change auf Low-Level bringt neue Komponenten/Funktionen und braucht ein echtes Design – Einstieg im Design. Unterschied zum Top-Level ist v.a. Tragweite und Freigabe-Ebene, nicht der Einstiegspunkt." },
|
||||
{ id:"tr_01", grund:"Ein Normal Change ist geplant und dokumentiert, aber nicht strategisch. Er steigt an Gate 1 ein, wo Build-oder-Konfiguration entschieden wird – meist der Konfigurationspfad (tr_05)." },
|
||||
{ id:"op_03", grund:"Ein Standard Change ist vorab genehmigt und im Katalog hinterlegt. Er braucht keine Gates und kein Design, sondern wird im laufenden Betrieb umgesetzt (op_03: Umsetzung freigegebener Standard-Changes)." },
|
||||
{ id:"tr_10", grund:"Ein Emergency Change muss die Störung sofort beheben. Der Fix wird beschleunigt ausgerollt (tr_10); die formale Freigabe (Gate 3) erfolgt nachgelagert. Ausgelöst wird er typischerweise durch einen Incident aus Operation/Support." }
|
||||
];
|
||||
|
||||
/* Geführtes Beispiel ("for dummies"-Tour): EIN konkreter Fall durch den ganzen
|
||||
Lifecycle. Online-Bürgerportal · Major Change Top-Level (neues Landesgesetz zur
|
||||
digitalen Bürgerbeteiligung → Erweiterung um Beteiligungs-Module). */
|
||||
const TOUR = {
|
||||
service: 2, change: 0,
|
||||
text: {
|
||||
ds_01: "Das neue Landesgesetz verlangt Online-Beteiligung. Zuerst klären Fachamt und (designierter) Service Owner, was das Beteiligungs-Modul leisten muss: Wer nutzt es (Bürger:innen, Planungsamt)? Welchen Nutzen bringt es? Welche Fristen und Verfügbarkeiten sind gesetzlich nötig? Ergebnis ist ein erster Entwurf der Service-Definition.",
|
||||
ds_02: "Jetzt entsteht das Lösungsdesign: welche Module (Online-Konsultation, Kommentar-Workflows, Veröffentlichung von Plänen), welche Schnittstellen zum bestehenden Portal und zum DMS, welche Datenschutz- und Barrierefreiheits-Vorgaben. Das ist das Service Design Document.",
|
||||
ds_03: "Hier wird geplant, WIE das Modul organisatorisch eingeführt wird – noch nicht technisch: Wer schult die Sachbearbeiter:innen im Planungsamt? Welche Prozesse und Rollen ändern sich? Wann kann der Betrieb übernehmen? Ergebnis: Implementation Blueprint.",
|
||||
ds_04: "Letzte Vorbereitung vor der Transition: Betrieb und Support werden abgestimmt, Tools und Strukturen vorbereitet, ein Early-Life-Support-Konzept skizziert, und der Service wird vollständig im Portfolio erfasst.",
|
||||
tr_01: "Erstes Tor – die SOR entscheidet: bauen oder nur konfigurieren? Da völlig neue Beteiligungs-Module gebraucht werden, fällt die Entscheidung auf ENTWICKLUNG → weiter zu tr_02. (Bei einer reinen Einstellung wäre es Konfiguration, und tr_02–tr_04 würden übersprungen.)",
|
||||
tr_02: "Die Projektleitung steuert die Umsetzung: Abstimmung mit dem Dienstleister, der die Module baut, dazu Termine, Budget und Arbeitspakete.",
|
||||
tr_03: "Projektteam bzw. Dienstleister entwickeln die Beteiligungs-Module: Formulare, Kommentar-Workflows, Veröffentlichungsfunktion und die Schnittstellen.",
|
||||
tr_04: "Die fertigen Komponenten werden geprüft und angenommen (Vollständigkeit, Qualität, Dokumentation) und ans Testmanagement übergeben.",
|
||||
tr_05: "Parameter, Rollen, Zugänge und die Anbindung an Portal und Service-Katalog werden eingerichtet. (Genau hier wäre der Einstieg gewesen, hätte Gate 1 'Konfiguration' entschieden.)",
|
||||
tr_06: "Die Betriebs- und Supportunterlagen entstehen: Betriebshandbuch, Supportanleitungen, Monitoring-Vorgaben und Eskalationswege für das neue Modul.",
|
||||
tr_07: "Das Testmanagement prüft Funktion, Integration und Abnahme – etwa ob eine Online-Konsultation fristgerecht startet und endet und Eingaben korrekt gespeichert werden.",
|
||||
tr_08: "Der Build ist fertig. Alle Unterlagen und Testprotokolle werden für die Prüfung an Gate 2 zusammengestellt.",
|
||||
tr_09: "Zweites Tor – der Service Owner entscheidet allein: Ist alles übergabefähig? Doku vollständig, Abnahme liegt vor, Betrieb und Support vorbereitet → FREIGABE → weiter zu tr_10.",
|
||||
tr_10: "Das Betriebsteam rollt die Module in die produktive Umgebung aus: Systeme konfigurieren, Daten migrieren, Monitoring aktivieren, Zugänge schalten.",
|
||||
tr_11: "Letzter Check vor dem Go-Live: Betriebsdoku, Überwachung und Eskalationswege werden geprüft, die Go-Live-Kommunikation an die Ämter vorbereitet und der Gate-3-Antrag erstellt.",
|
||||
tr_12: "Drittes Tor – die SOR entscheidet im Konsent: Go-Live? Portfolio passt, Betrieb und Support sind bereit, SLAs vereinbart → GO-LIVE. Der Service wird formal ins Portfolio aufgenommen und geht in die Operation.",
|
||||
op_01: "Direkt nach dem Start läuft das Modul unter erhöhter Aufmerksamkeit (Early Life Support): enge Beobachtung, schnelle Hilfe, erste Bürger-Rückmeldungen werden aufgearbeitet. Der Service Owner entscheidet, wann der Normalbetrieb beginnt.",
|
||||
op_02: "Betriebshandbuch und Betriebsprozesse sind bereitgestellt – klare Regeln und Zuständigkeiten für den täglichen Betrieb des Beteiligungs-Moduls.",
|
||||
op_03: "Tagesgeschäft: Benutzer und Berechtigungen, Backups, Routinepflege und die Umsetzung freigegebener Standard-Changes (z. B. ein neues Beteiligungsverfahren anlegen).",
|
||||
op_04: "Es wird sichergestellt, dass genug Personal, Technik und Budget vorhanden sind – inklusive Steuerung des Dienstleisters.",
|
||||
op_05: "Verfügbarkeit und Performance werden überwacht – besonders wichtig, wenn zum Ende einer Beteiligungsfrist viele Bürger:innen gleichzeitig zugreifen.",
|
||||
op_06: "Regelmäßiger Qualitätsbericht: Wurden SLAs/SLOs eingehalten? Wie war die Verfügbarkeit während der Konsultationen? Das ist die Grundlage fürs Review.",
|
||||
op_07: "Über das reine Monitoring hinaus: Trends erkennen – etwa wiederkehrende Lastspitzen oder ein Formular, das auffällig oft abgebrochen wird.",
|
||||
sp_01: "Der Support bekommt seinen Rahmen: Incident- und Request-Prozesse, Reaktionszeiten und Ticketkategorien für das Beteiligungs-Modul.",
|
||||
sp_02: "Lösungen, Workarounds und FAQ werden gepflegt – damit der 1st Level häufige Bürgerfragen schnell beantworten kann.",
|
||||
sp_03: "Eingehende Tickets von Bürger:innen und Ämtern werden gesichtet, priorisiert und an die richtige Stelle geroutet.",
|
||||
sp_04: "Standardanfragen werden erledigt – z. B. ein Zugang für eine neue Sachbearbeiterin im Planungsamt.",
|
||||
sp_05: "Typische Störungen löst der 1st Level direkt – etwa wenn ein Nutzer den Kommentar-Button nicht findet (Anleitung aus der Wissensdatenbank).",
|
||||
sp_06: "Knifflige Fälle gehen an den 2nd Level – z. B. Kommentare werden unter hoher Last nicht gespeichert. Bleibt es ungelöst, entsteht daraus ein Problem Record.",
|
||||
sp_07: "Der Fall wird sauber abgeschlossen: Lösung validiert, Nutzer informiert, Dokumentation und FAQ aktualisiert.",
|
||||
sp_08: "Endgültiges Schließen inklusive Klassifizierung für die Statistik und Erkennen wiederkehrender Fälle.",
|
||||
sp_09: "Ein Incident bleibt ungelöst – eine strukturelle Ursache wird vermutet. Der Problem Manager legt einen Problem Record an.",
|
||||
sp_10: "Mehrere ähnliche Speicher-Fehler häufen sich → sie werden geclustert und gebündelt als Problem Record erfasst.",
|
||||
sp_11: "Ursachensuche: Das Speicherproblem entsteht durch einen Timeout unter Last. Es gibt einen Workaround (Eintrag in die Wissensdatenbank) und die Entscheidung, dass ein Change nötig ist.",
|
||||
rv_01: "Im Review werden Support-KPIs und wiederkehrende Fälle ausgewertet – die Speicher-Probleme tauchen klar als Muster auf.",
|
||||
rv_02: "Der Service wird über vier Dimensionen per Ampel bewertet (Leistung, Stabilität, Nutzerzufriedenheit, Zukunftsfähigkeit). Empfehlung in unserem Fall: IMPROVEMENT.",
|
||||
rv_03: "Die SOR sichtet das Review und entscheidet. In unserem Beispiel: VERBESSERUNG NÖTIG (klein) → rv_04. (Alternativen wären: weiter wie bisher, Redesign rv_05 oder Außerbetriebnahme rv_06.)",
|
||||
rv_04: "Die kleine Verbesserung wird geplant und umgesetzt – z. B. die Last- und Timeout-Optimierung beim Speichern. Sie fließt zurück in den laufenden Betrieb (Operation). Damit schließt sich der Kreis.",
|
||||
rv_05: "In unserem Beispiel NICHT gewählt. Wäre die Änderung grundlegend (z. B. eine komplett neue Beteiligungsplattform), würde sie hier als neuer Demand an den Demand-Lifecycle übergeben – der Service liefe quasi neu durch Design und Transition.",
|
||||
rv_06: "Ebenfalls NICHT gewählt. Würde das Modul z. B. wegen eines Nachfolgesystems abgeschaltet, plant die SOR hier die kontrollierte Stilllegung (Retirement-Plan)."
|
||||
}
|
||||
};
|
||||
|
||||
/* =========================================================================
|
||||
STATIONEN — vollständiger Lifecycle, abgeleitet aus den Blueprint-YAMLs
|
||||
(service-lifecycle_design/transition/operation/support/review.yaml v3.2 +
|
||||
|
|
@ -763,8 +832,8 @@ const STATIONEN = [
|
|||
/* ====================== STATE ====================== */
|
||||
const LS_KEY = "slc-companion-proto";
|
||||
function defaultState(){
|
||||
return { view:"card", service:0, change:0, startIndex:null,
|
||||
index:0, stage:"discuss", quizIndex:0,
|
||||
return { view:"card", service:0, change:0, startIndex:null, startRevealed:false,
|
||||
tourIndex:0, index:0, stage:"discuss", quizIndex:0,
|
||||
picks:{}, done:{}, unclear:{} };
|
||||
}
|
||||
let S = load();
|
||||
|
|
@ -808,13 +877,14 @@ function draw(){
|
|||
document.body.classList.toggle("runMode", S.view==="run");
|
||||
renderCardBadge();
|
||||
if(S.view==="card") return renderCardScreen();
|
||||
if(S.view==="tour") return renderTour();
|
||||
if(S.view==="start") return renderStartScreen();
|
||||
renderRun();
|
||||
}
|
||||
|
||||
function renderCardBadge(){
|
||||
const el = $("#cardBadge");
|
||||
if(S.view==="card"){ el.style.display="none"; el.innerHTML=""; return; }
|
||||
if(S.view==="card" || S.view==="tour"){ el.style.display="none"; el.innerHTML=""; return; }
|
||||
el.style.display="flex";
|
||||
el.innerHTML = `<span class="cb-svc">${USE_CASES[S.service].service}</span><span class="ctChip">${CHANGE_TYPES[S.change]}</span>`;
|
||||
}
|
||||
|
|
@ -832,9 +902,11 @@ function renderCardScreen(){
|
|||
<div class="ctText" id="cardTrigger">${USE_CASES[S.service].changes[S.change]}</div>
|
||||
<div class="actions">
|
||||
<button class="ghost" id="randomCard">🎲 Zufällig ziehen</button>
|
||||
<button class="ghost" id="tourBtn">📘 Geführtes Beispiel</button>
|
||||
<div class="spacer"></div>
|
||||
<button class="primary" id="toStart">Weiter → Startpunkt wählen</button>
|
||||
</div>`;
|
||||
</div>
|
||||
<p class="note" style="text-align:left">Neu hier? Das „Geführte Beispiel" spielt einen kompletten Fall (Bürgerportal) Station für Station durch – zum Verstehen, ohne Quiz.</p>`;
|
||||
const svc=$("#serviceSel"), ch=$("#changeSel");
|
||||
svc.innerHTML = USE_CASES.map((u,i)=>`<option value="${i}">${u.service}</option>`).join("");
|
||||
ch.innerHTML = CHANGE_TYPES.map((c,i)=>`<option value="${i}">${c}</option>`).join("");
|
||||
|
|
@ -843,11 +915,58 @@ function renderCardScreen(){
|
|||
svc.onchange=()=>{ S.service=+svc.value; save(); refresh(); };
|
||||
ch.onchange=()=>{ S.change=+ch.value; save(); refresh(); };
|
||||
$("#randomCard").onclick=()=>{ S.service=Math.floor(Math.random()*USE_CASES.length); S.change=Math.floor(Math.random()*CHANGE_TYPES.length); save(); draw(); };
|
||||
$("#toStart").onclick=()=>{ S.view="start"; save(); draw(); };
|
||||
$("#tourBtn").onclick=()=>{ S.view="tour"; S.tourIndex=0; save(); draw(); };
|
||||
$("#toStart").onclick=()=>{ S.view="start"; S.startRevealed=false; save(); draw(); };
|
||||
}
|
||||
|
||||
/* ---------- Screen 2: Startpunkt bestimmen ---------- */
|
||||
/* ---------- Geführte Tour (ein Beispiel durch alle Stationen) ---------- */
|
||||
function renderTour(){
|
||||
const st = STATIONEN[S.tourIndex];
|
||||
const ph = PHASEN[st.phase];
|
||||
const chip = st.typ==="gate"
|
||||
? `<span class="phaseChip gateChip">⛩ Gate ${st.gateNr}</span>`
|
||||
: `<span class="phaseChip" style="background:${ph.color}">${ph.label}</span>`;
|
||||
const raci = st.raci.map(([r,c])=>`<tr><td>${roleLabel(r)}</td><td><span class="raciBadge raci-${c}">${c}</span></td></tr>`).join("");
|
||||
let extra="";
|
||||
if(st.pfade){
|
||||
extra += `<h4>Entscheidungspfade</h4><div class="pfade">` +
|
||||
st.pfade.map(([n,d])=>`<div class="pfad"><b>${n}</b><span style="color:var(--muted)">${d}</span></div>`).join("") + `</div>`;
|
||||
}
|
||||
const narr = TOUR.text[st.id] || "—";
|
||||
const last = S.tourIndex === STATIONEN.length-1;
|
||||
$("#panel").innerHTML = `
|
||||
<div class="tourBanner">📘 Geführtes Beispiel · <b>${USE_CASES[TOUR.service].service}</b> — ${CHANGE_TYPES[TOUR.change]}<br>
|
||||
<span style="color:var(--muted)">${USE_CASES[TOUR.service].changes[TOUR.change]}</span></div>
|
||||
<div class="tourProg">Station ${S.tourIndex+1} von ${STATIONEN.length}</div>
|
||||
${chip}
|
||||
<div class="stationName">${st.name}</div>
|
||||
<div class="stationId">${st.id}</div>
|
||||
<div class="tourNarr"><h4>In diesem Beispiel</h4>${narr}</div>
|
||||
<div class="reveal">
|
||||
<h4>Fachlich (aus dem Blueprint)</h4>
|
||||
<p>${st.beschreibung}</p>
|
||||
<h4>Umfasst</h4><ul>${st.umfasst.map(u=>`<li>${u}</li>`).join("")}</ul>
|
||||
<h4>Rollen / RACI</h4>
|
||||
<table class="raci"><thead><tr><th>Rolle</th><th>RACI</th></tr></thead><tbody>${raci}</tbody></table>
|
||||
<h4>Artefakt</h4><p>${st.artefakt}</p>
|
||||
${extra}
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button class="ghost" id="tourExit">Tour beenden</button>
|
||||
<button class="ghost" id="tourBack" ${S.tourIndex===0?'disabled':''}>← Zurück</button>
|
||||
<div class="spacer"></div>
|
||||
<button class="primary" id="tourNext">${last?'Fertig — zur Startseite':'Weiter →'}</button>
|
||||
</div>`;
|
||||
$("#tourExit").onclick=()=>{ S.view="card"; save(); draw(); };
|
||||
$("#tourBack").onclick=()=>{ if(S.tourIndex>0){ S.tourIndex--; save(); renderTour(); } };
|
||||
$("#tourNext").onclick=()=>{ if(last){ S.view="card"; save(); draw(); } else { S.tourIndex++; save(); renderTour(); } };
|
||||
}
|
||||
|
||||
/* ---------- Screen 2: Startpunkt bestimmen (Tipp → Auflösen) ---------- */
|
||||
function renderStartScreen(){
|
||||
const rec = START_EMPFEHLUNG[S.change];
|
||||
const recIndex = STATIONEN.findIndex(s=>s.id===rec.id);
|
||||
const revealed = S.startRevealed;
|
||||
const groups={};
|
||||
STATIONEN.forEach((st,i)=>{ (groups[st.phase]=groups[st.phase]||[]).push({st,i}); });
|
||||
let list="";
|
||||
|
|
@ -855,7 +974,12 @@ function renderStartScreen(){
|
|||
if(!groups[ph]) continue;
|
||||
list += `<div class="pickerPhase">${PHASEN[ph].label}</div>`;
|
||||
groups[ph].forEach(({st,i})=>{
|
||||
list += `<div class="pickerItem ${i===S.startIndex?'sel':''}" data-i="${i}">
|
||||
let cls="pickerItem";
|
||||
if(revealed){
|
||||
if(i===recIndex) cls+=" correct";
|
||||
else if(i===S.startIndex) cls+=" wrongPick";
|
||||
} else if(i===S.startIndex) cls+=" sel";
|
||||
list += `<div class="${cls}" data-i="${i}">
|
||||
<span class="dot" style="background:${PHASEN[ph].color}"></span>
|
||||
<span class="id">${st.id}</span>
|
||||
<span class="nm">${st.name}</span>
|
||||
|
|
@ -863,22 +987,41 @@ function renderStartScreen(){
|
|||
</div>`;
|
||||
});
|
||||
}
|
||||
let head, actions;
|
||||
if(!revealed){
|
||||
head = `<p class="muted">Welches Tile ist der sinnvolle Einstieg für diese Action Card? Diskutiert in der Gruppe, wählt ein Tile und löst dann auf.</p>`;
|
||||
actions = `<button class="ghost" id="backToCard">← Action Card</button>
|
||||
<div class="spacer"></div>
|
||||
<button class="primary" id="revealStart" ${S.startIndex==null?'disabled':''}>Auflösen</button>`;
|
||||
} else {
|
||||
const tip = S.startIndex==null ? `` :
|
||||
(S.startIndex===recIndex
|
||||
? `<p style="color:var(--ok);font-weight:600;margin:0 0 8px">Euer Tipp ${STATIONEN[S.startIndex].id} passt zum empfohlenen Einstieg. ✓</p>`
|
||||
: `<p style="color:var(--bad);font-weight:600;margin:0 0 8px">Euer Tipp war ${STATIONEN[S.startIndex].id} — nicht der empfohlene Einstieg.</p>`);
|
||||
head = `${tip}<div class="recBox"><h4>Empfohlener Einstieg · ${CHANGE_TYPES[S.change]}</h4>
|
||||
<p style="margin:0 0 6px"><b>${rec.id} — ${STATIONEN[recIndex].name}</b></p>
|
||||
<p style="margin:0;color:var(--muted)">${rec.grund}</p></div>`;
|
||||
const ownDiff = (S.startIndex!=null && S.startIndex!==recIndex);
|
||||
actions = `<button class="ghost" id="backToCard">← Action Card</button>
|
||||
<div class="spacer"></div>
|
||||
${ownDiff?`<button class="ghost" id="startOwn">Auf eigener Wahl starten</button>`:``}
|
||||
<button class="primary" id="startRec">Auf ${rec.id} starten →</button>`;
|
||||
}
|
||||
$("#panel").innerHTML = `
|
||||
<div class="setupHead">Schritt 2 · Startpunkt</div>
|
||||
<h2 class="setupTitle">Wo startet ihr sinnvollerweise?</h2>
|
||||
<p class="muted">Das hängt vom Change-Typ ab – ein großer (Major) Change beginnt eher im Design, ein Standard- oder Emergency-Change steigt oft später ein. Diskutiert in der Gruppe und wählt das Tile, auf das der Action-Stein zuerst gestellt wird. Es gibt nicht die eine richtige Antwort.</p>
|
||||
${head}
|
||||
<div class="pickerList">${list}</div>
|
||||
<div class="actions">
|
||||
<button class="ghost" id="backToCard">← Action Card</button>
|
||||
<div class="spacer"></div>
|
||||
<button class="primary" id="confirmStart" ${S.startIndex==null?'disabled':''}>Hier starten →</button>
|
||||
</div>`;
|
||||
$("#panel").querySelectorAll(".pickerItem").forEach(el=>{
|
||||
el.onclick=()=>{ S.startIndex=+el.dataset.i; save(); renderStartScreen(); };
|
||||
});
|
||||
$("#backToCard").onclick=()=>{ S.view="card"; save(); draw(); };
|
||||
const cs=$("#confirmStart");
|
||||
if(cs) cs.onclick=()=>{ if(S.startIndex==null) return; S.index=S.startIndex; S.view="run"; S.stage="discuss"; S.quizIndex=0; save(); draw(); };
|
||||
<div class="actions">${actions}</div>`;
|
||||
if(!revealed){
|
||||
$("#panel").querySelectorAll(".pickerItem").forEach(el=>{
|
||||
el.onclick=()=>{ S.startIndex=+el.dataset.i; save(); renderStartScreen(); };
|
||||
});
|
||||
}
|
||||
$("#backToCard").onclick=()=>{ S.view="card"; S.startRevealed=false; save(); draw(); };
|
||||
if($("#revealStart")) $("#revealStart").onclick=()=>{ if(S.startIndex==null) return; S.startRevealed=true; save(); renderStartScreen(); };
|
||||
if($("#startRec")) $("#startRec").onclick=()=>{ S.index=recIndex; S.view="run"; S.stage="discuss"; S.quizIndex=0; save(); draw(); };
|
||||
if($("#startOwn")) $("#startOwn").onclick=()=>{ S.index=S.startIndex; S.view="run"; S.stage="discuss"; S.quizIndex=0; save(); draw(); };
|
||||
}
|
||||
|
||||
/* ====================== RENDER: RUN (Station) ====================== */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue