This commit is contained in:
breitenbach76 2026-06-07 12:52:01 +02:00
parent 02dd93db80
commit 6d09b07dec

View file

@ -144,6 +144,13 @@
details.det>summary::before{content:"▸ ";color:var(--muted)} details.det>summary::before{content:"▸ ";color:var(--muted)}
details.det[open]>summary::before{content:"▾ "} details.det[open]>summary::before{content:"▾ "}
details.det>div,details.det>p,details.det>ul{margin-top:10px} details.det>div,details.det>p,details.det>ul{margin-top:10px}
/* Schrittweise Aktivitaet: Aufloesung */
.aufBox{background:#f1faf4;border:1px solid #cde6d6;border-left:3px solid var(--ok);border-radius:10px;padding:14px 16px;margin:14px 0 4px}
.aufBox .aufH{margin:0 0 6px;font-size:12px;text-transform:uppercase;letter-spacing:.5px;color:var(--muted)}
.aufBox ul{margin:6px 0 0;padding-left:20px}
.aufBox ul li{margin:3px 0}
.roleChips{display:flex;flex-wrap:wrap;gap:6px}
.roleChip{background:#eef0f3;border-radius:999px;padding:3px 11px;font-size:13px}
.actions { display:flex; gap:10px; align-items:center; margin-top:24px; flex-wrap:wrap; } .actions { display:flex; gap:10px; align-items:center; margin-top:24px; flex-wrap:wrap; }
.actions .spacer { flex:1; } .actions .spacer { flex:1; }
@ -196,6 +203,7 @@
.classifyTop{display:grid;grid-template-columns:minmax(220px,320px) 1fr;gap:26px;align-items:start;margin:18px 0 6px} .classifyTop{display:grid;grid-template-columns:minmax(220px,320px) 1fr;gap:26px;align-items:start;margin:18px 0 6px}
.classifyCard{display:block;width:100%;border-radius:12px;box-shadow:0 3px 16px rgba(0,0,0,.16)} .classifyCard{display:block;width:100%;border-radius:12px;box-shadow:0 3px 16px rgba(0,0,0,.16)}
.classifyMain{min-width:0} .classifyMain{min-width:0}
.classifyMain .phaseRow{grid-template-columns:repeat(2,1fr);margin-top:12px}
@media(max-width:680px){.classifyTop{grid-template-columns:1fr}.classifyCard{max-width:300px;margin:0 auto}.classifyMain{margin-top:6px}} @media(max-width:680px){.classifyTop{grid-template-columns:1fr}.classifyCard{max-width:300px;margin:0 auto}.classifyMain{margin-top:6px}}
.choice{text-align:left;padding:12px 14px;border:1px solid var(--line);border-radius:10px;background:#fff;cursor:pointer;font-size:15px;font-weight:600} .choice{text-align:left;padding:12px 14px;border:1px solid var(--line);border-radius:10px;background:#fff;cursor:pointer;font-size:15px;font-weight:600}
.choice:hover{border-color:var(--ink)} .choice:hover{border-color:var(--ink)}
@ -863,6 +871,7 @@ function defaultState(){
classifyDone:false, classifyWrong:null, classifyDone:false, classifyWrong:null,
entryDone:false, entryWrong:null, entryDone:false, entryWrong:null,
index:0, stage:"discuss", quizIndex:0, index:0, stage:"discuss", quizIndex:0,
actStep:0, actReveal:false,
picks:{}, done:{}, unclear:{}, picks:{}, done:{}, unclear:{},
loopback:null, revisit:{}, endReason:null, endGate:null }; loopback:null, revisit:{}, endReason:null, endGate:null };
} }
@ -961,7 +970,7 @@ function renderDeck(){
function renderClassify(){ function renderClassify(){
const correct = S.change; const correct = S.change;
const card = acard(S.service,S.change); const card = acard(S.service,S.change);
const thumb = `<img class="cardThumb" src="cards/s${S.service}-c${S.change}.png" alt="${card.titel}">`; const cardBig = `<img class="classifyCard" src="cards/s${S.service}-c${S.change}.png" alt="${card.titel}">`;
if(!S.classifyDone){ if(!S.classifyDone){
const choices = CHANGE_TYPES.map((t,i)=> const choices = CHANGE_TYPES.map((t,i)=>
`<button class="choice ${S.classifyWrong===i?'bad':''}" data-i="${i}">${t}</button>`).join(""); `<button class="choice ${S.classifyWrong===i?'bad':''}" data-i="${i}">${t}</button>`).join("");
@ -972,7 +981,7 @@ function renderClassify(){
$("#panel").innerHTML = ` $("#panel").innerHTML = `
<div class="setupHead">Schritt 2 · Change-Art bestimmen</div> <div class="setupHead">Schritt 2 · Change-Art bestimmen</div>
<div class="classifyTop"> <div class="classifyTop">
<img class="classifyCard" src="cards/s${S.service}-c${S.change}.png" alt="${card.titel}"> ${cardBig}
<div class="classifyMain"> <div class="classifyMain">
<h2 class="setupTitle" style="margin-top:0">Welche Art von Change ist das?</h2> <h2 class="setupTitle" style="margin-top:0">Welche Art von Change ist das?</h2>
<p class="muted">Überlegt gemeinsam und wählt die passende Change-Art. Die Legende hilft beim Einordnen.</p> <p class="muted">Überlegt gemeinsam und wählt die passende Change-Art. Die Legende hilft beim Einordnen.</p>
@ -991,10 +1000,14 @@ function renderClassify(){
} else { } else {
$("#panel").innerHTML = ` $("#panel").innerHTML = `
<div class="setupHead">Schritt 3 · Erfolgreiche Kategorisierung</div> <div class="setupHead">Schritt 3 · Erfolgreiche Kategorisierung</div>
${thumb} <div class="classifyTop">
<div class="hint ok">✓ Richtig: ${CHANGE_TYPES[correct]}</div> ${cardBig}
<div class="recBox"><h4>Warum?</h4> <div class="classifyMain">
<p style="margin:0;color:var(--muted)">${CHANGE_LEGEND[correct]}</p></div> <div class="hint ok">✓ Richtig: ${CHANGE_TYPES[correct]}</div>
<div class="recBox"><h4>Warum?</h4>
<p style="margin:0;color:var(--muted)">${CHANGE_LEGEND[correct]}</p></div>
</div>
</div>
<div class="actions"> <div class="actions">
<button class="ghost" id="backDeck">← Andere Karte</button> <button class="ghost" id="backDeck">← Andere Karte</button>
<div class="spacer"></div> <div class="spacer"></div>
@ -1011,6 +1024,7 @@ function renderEntry(){
const recIndex = STATIONEN.findIndex(s=>s.id===rec.id); const recIndex = STATIONEN.findIndex(s=>s.id===rec.id);
const correctPhase = STATIONEN[recIndex].phase; const correctPhase = STATIONEN[recIndex].phase;
const order = ["design","transition","operation","support","review"]; const order = ["design","transition","operation","support","review"];
const cardBig = `<img class="classifyCard" src="cards/s${S.service}-c${S.change}.png" alt="${acard(S.service,S.change).titel}">`;
if(!S.entryDone){ if(!S.entryDone){
const zones = order.map(ph=> const zones = order.map(ph=>
`<button class="phaseZone ${S.entryWrong===ph?'bad':''}" data-ph="${ph}" `<button class="phaseZone ${S.entryWrong===ph?'bad':''}" data-ph="${ph}"
@ -1019,11 +1033,16 @@ function renderEntry(){
? `<div class="hint bad">Diese Phase passt nicht zur Change-Art — denkt an die Definition und probiert es erneut.</div>` : ``; ? `<div class="hint bad">Diese Phase passt nicht zur Change-Art — denkt an die Definition und probiert es erneut.</div>` : ``;
$("#panel").innerHTML = ` $("#panel").innerHTML = `
<div class="setupHead">Schritt 4 · Einstieg finden</div> <div class="setupHead">Schritt 4 · Einstieg finden</div>
<div class="hint ok">Change-Art: ${CHANGE_TYPES[S.change]}</div> <div class="classifyTop">
<h2 class="setupTitle">In welcher Phase startet dieser Change?</h2> ${cardBig}
<p class="muted">Klickt auf die Lebenszyklus-Phase, in der dieser Change einsteigt.</p> <div class="classifyMain">
${hint} <div class="hint ok">Change-Art: ${CHANGE_TYPES[S.change]}</div>
<div class="phaseRow">${zones}</div> <h2 class="setupTitle" style="margin-top:8px">In welcher Phase startet dieser Change?</h2>
<p class="muted">Klickt auf die Lebenszyklus-Phase, in der dieser Change einsteigt.</p>
${hint}
<div class="phaseRow">${zones}</div>
</div>
</div>
<div class="actions"><button class="ghost" id="backClassify">← zurück</button></div>`; <div class="actions"><button class="ghost" id="backClassify">← zurück</button></div>`;
$("#panel").querySelectorAll(".phaseZone").forEach(el=>{ $("#panel").querySelectorAll(".phaseZone").forEach(el=>{
el.onclick=()=>{ const ph=el.dataset.ph; el.onclick=()=>{ const ph=el.dataset.ph;
@ -1034,10 +1053,15 @@ function renderEntry(){
} else { } else {
$("#panel").innerHTML = ` $("#panel").innerHTML = `
<div class="setupHead">Schritt 5 · Los geht's</div> <div class="setupHead">Schritt 5 · Los geht's</div>
<div class="hint ok">✓ Einstieg: ${PHASEN[correctPhase].label}</div> <div class="classifyTop">
<div class="recBox"><h4>Start-Station</h4> ${cardBig}
<p style="margin:0 0 6px"><b>${rec.id} — ${STATIONEN[recIndex].name}</b></p> <div class="classifyMain">
<p style="margin:0;color:var(--muted)">${rec.grund}</p></div> <div class="hint ok">✓ Einstieg: ${PHASEN[correctPhase].label}</div>
<div class="recBox"><h4>Start-Station</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>
</div>
</div>
<div class="actions"> <div class="actions">
<button class="ghost" id="backClassify">← zurück</button> <button class="ghost" id="backClassify">← zurück</button>
<div class="spacer"></div> <div class="spacer"></div>
@ -1064,6 +1088,7 @@ function enterStation(idx){
S.index = idx; S.index = idx;
S.stage = STATIONEN[idx].typ==="gate" ? "gate" : "act"; S.stage = STATIONEN[idx].typ==="gate" ? "gate" : "act";
S.gatePick = null; S.quizIndex = 0; S.gatePick = null; S.quizIndex = 0;
S.actStep = 0; S.actReveal = false;
} }
function gateGoto(st, i){ function gateGoto(st, i){
S.done[st.id] = true; S.done[st.id] = true;
@ -1095,53 +1120,62 @@ function renderRun(){
const loopBanner = (S.loopback && S.index < S.loopback.untilIdx) const loopBanner = (S.loopback && S.index < S.loopback.untilIdx)
? `<div class="tourBanner"><b>Nacharbeit nach Gate ${S.loopback.gateNr}</b> — überarbeitet die folgenden Stationen; danach wird das Gate erneut vorgelegt und entscheidet neu.</div>` ? `<div class="tourBanner"><b>Nacharbeit nach Gate ${S.loopback.gateNr}</b> — überarbeitet die folgenden Stationen; danach wird das Gate erneut vorgelegt und entscheidet neu.</div>`
: ``; : ``;
const cardBig = `<img class="classifyCard" src="cards/s${S.service}-c${S.change}.png" alt="${acard(S.service,S.change).titel}">`;
const stepBody = st.typ==="gate"
? (S.stage==="gateDone" ? renderGateDone(st) : renderGate(st))
: renderActivity(st);
let body = ` let body = `
${loopBanner} ${loopBanner}
<div class="sHead">${chip}<span class="sId">${st.id}</span></div> <div class="classifyTop">
<h2 class="sTitle">${st.name}</h2> ${cardBig}
<div class="caseLine">Fall: «${acard(S.service,S.change).titel}»</div>`; <div class="classifyMain">
if(st.typ==="gate") body += (S.stage==="gateDone") ? renderGateDone(st) : renderGate(st); <div class="sHead">${chip}<span class="sId">${st.id}</span></div>
else body += (S.stage==="reveal") ? renderReveal(st) : renderAct(st); <h2 class="sTitle" style="margin-top:8px">${st.name}</h2>
${stepBody}
</div>
</div>`;
$("#panel").innerHTML = body; $("#panel").innerHTML = body;
wire(st); wire(st);
} }
/* Aktivitaet — Takt 1: Handeln am Brett (mit "Zeig mir") */ /* Aktivitaet — schrittweiser Mikro-Ablauf: 4 Fragen, je einzeln + Aufloesung.
function renderAct(st){ 1) Was steckt hinter der Ueberschrift? 2) Beteiligte Rollen 3) RACI 4) Artefakt */
return ` function activitySteps(st){
<p class="lead">Handeln am Brett — besprecht und legt ab:</p> return [
<ol class="todo"> { label:"Diskussion",
<li>Beteiligte Rollen → Figuren auf die <b>Mulden des Station-Pucks</b></li> frage:`<p class="lead">Diskutiert gemeinsam: Was fällt alles unter <b>„${st.name}"</b>? Was stellt ihr euch darunter vor? Nennt Beispiele.</p>`,
<li>RACI klären → dieselben Figuren ins <b>Aktiv-Feld (R·A·C·I)</b></li> auf:`<p style="margin:0 0 8px">${st.beschreibung}</p><h4 class="aufH">Das fällt darunter</h4><ul>${st.umfasst.map(u=>`<li>${u}</li>`).join("")}</ul>` },
<li>Artefaktkarte → in die <b>Service-Akte</b></li> { label:"Beteiligte Rollen",
</ol> frage:`<p class="lead">Welche Rollen sind an dieser Aktivität beteiligt? Stellt ihre Figuren auf die <b>Mulden des Station-Pucks</b>.</p>`,
<details class="det"> auf:`<h4 class="aufH">Beteiligte Rollen</h4><div class="roleChips">${st.raci.map(([r])=>`<span class="roleChip">${roleLabel(r)}</span>`).join("")}</div>` },
<summary>Zeig mir</summary> { label:"RACI",
<div>${raciTable(st)}<p class="muted" style="margin:8px 0 0"><b>Artefakt:</b> ${st.artefakt}</p></div> frage:`<p class="lead">Klärt die <b>RACI</b>: Wer ist R, A, C, I? Sortiert die Figuren ins <b>Aktiv-Feld (R·A·C·I)</b>.</p>`,
</details> auf:`<h4 class="aufH">RACI</h4>${raciTable(st)}` },
<div class="actions"> { label:"Artefakt",
<div class="spacer"></div> frage:`<p class="lead">Welche <b>Artefaktkarte</b> entsteht hier und gehört in die <b>Service-Akte</b>?</p>`,
<button class="primary" id="toReveal">Auflösen →</button> auf:`<h4 class="aufH">Artefakt</h4><p style="margin:0"><b>${st.artefakt}</b></p>` }
</div>`; ];
} }
function renderActivity(st){
const steps = activitySteps(st);
const i = Math.min(S.actStep||0, steps.length-1);
const step = steps[i];
const isLast = i === steps.length-1;
let html = `<div class="tourProg">Schritt ${i+1}/${steps.length} · ${step.label}</div>${step.frage}`;
if(S.actReveal) html += `<div class="aufBox">${step.auf}</div>`;
/* Aktivitaet — Takt 2: Aufloesung & Abgleich */ let actions = `<div class="actions">`;
function renderReveal(st){ if(S.actReveal || i>0) actions += `<button class="ghost" id="actBack">← zurück</button>`;
return ` if(S.actReveal && isLast)
<p class="lead">Auflösung — gleicht euer Brett ab:</p> actions += `<label class="unclear"><input type="checkbox" id="unclear" ${S.unclear[st.id]?'checked':''}/> War unklar</label>`;
${raciTable(st)} actions += `<div class="spacer"></div>`;
<p class="muted" style="margin-top:10px"><b>Artefakt:</b> ${st.artefakt}</p> if(!S.actReveal) actions += `<button class="primary" id="actReveal">Auflösen →</button>`;
<details class="det"> else if(!isLast) actions += `<button class="primary" id="actNext">Weiter →</button>`;
<summary>Worum geht's & was umfasst die Station</summary> else actions += S.index < STATIONEN.length-1
<div><p>${st.beschreibung}</p><ul>${st.umfasst.map(u=>`<li>${u}</li>`).join("")}</ul></div> ? `<button class="primary" id="nextStation">Nächste Station →</button>`
</details> : `<button class="primary" id="finish">Durchlauf abschließen</button>`;
<div class="actions"> actions += `</div>`;
<label class="unclear"><input type="checkbox" id="unclear" ${S.unclear[st.id]?'checked':''}/> War unklar</label> return html + actions;
<div class="spacer"></div>
${S.index < STATIONEN.length-1
? `<button class="primary" id="nextStation">Nächste Station →</button>`
: `<button class="primary" id="finish">Durchlauf abschließen</button>`}
</div>`;
} }
/* Gate — Entscheidung nach Kriterien */ /* Gate — Entscheidung nach Kriterien */
@ -1190,7 +1224,9 @@ function renderGateDone(st){
/* ====================== WIRING ====================== */ /* ====================== WIRING ====================== */
function wire(st){ function wire(st){
const b = id => $("#"+id); const b = id => $("#"+id);
if(b("toReveal")) b("toReveal").onclick = ()=>{ S.stage="reveal"; save(); draw(); }; if(b("actReveal")) b("actReveal").onclick = ()=>{ S.actReveal=true; save(); draw(); };
if(b("actNext")) b("actNext").onclick = ()=>{ S.actStep=(S.actStep||0)+1; S.actReveal=false; save(); draw(); };
if(b("actBack")) b("actBack").onclick = ()=>{ if(S.actReveal){ S.actReveal=false; } else if((S.actStep||0)>0){ S.actStep--; S.actReveal=true; } save(); draw(); };
if(b("unclear")) b("unclear").onchange = e=>{ if(e.target.checked) S.unclear[st.id]=true; else delete S.unclear[st.id]; save(); renderList(); }; if(b("unclear")) b("unclear").onchange = e=>{ if(e.target.checked) S.unclear[st.id]=true; else delete S.unclear[st.id]; save(); renderList(); };
if(b("nextStation")) b("nextStation").onclick = ()=>{ S.done[st.id]=true; enterStation(S.index+1); save(); draw(); }; if(b("nextStation")) b("nextStation").onclick = ()=>{ S.done[st.id]=true; enterStation(S.index+1); save(); draw(); };
if(b("finish")) b("finish").onclick = ()=>{ S.done[st.id]=true; S.view="end"; S.endReason="done"; save(); draw(); }; if(b("finish")) b("finish").onclick = ()=>{ S.done[st.id]=true; S.view="end"; S.endReason="done"; save(); draw(); };