Update index.html

This commit is contained in:
breitenbach76 2026-05-30 14:17:07 +02:00
parent 38c4a948ca
commit 246aa3b7b9

View file

@ -124,18 +124,36 @@
.stat div b { display:block; font-size:24px; } .stat div b { display:block; font-size:24px; }
.stat div span { color:var(--muted); font-size:12px; } .stat div span { color:var(--muted); font-size:12px; }
.note { font-size:12px; color:var(--muted); margin-top:18px; text-align:center; } .note { font-size:12px; color:var(--muted); margin-top:18px; text-align:center; }
/* Setup-Screens (Action Card / Startpunkt) */
body:not(.runMode) aside { display:none; }
body:not(.runMode) .layout { grid-template-columns: 1fr; }
body:not(.runMode) .progress { display:none; }
.cardBadge { display:none; align-items:center; gap:8px; font-size:13px; }
.cardBadge .cb-svc { font-weight:600; }
.setupHead { font-size:12px; text-transform:uppercase; letter-spacing:.8px; color:var(--muted); }
.setupTitle { font-size:26px; margin:6px 0 4px; line-height:1.2; }
.muted { color:var(--muted); }
.cardForm { display:flex; gap:16px; flex-wrap:wrap; margin:18px 0; }
.cardForm label { display:flex; flex-direction:column; gap:6px; font-size:12px; text-transform:uppercase; letter-spacing:.5px; color:var(--muted); }
.cardForm select { font:inherit; padding:10px 12px; border-radius:10px; border:1px solid var(--line); min-width:280px; background:#fff; color:var(--ink); }
.pickerList { margin:14px 0 4px; }
.pickerPhase { font-size:11px; text-transform:uppercase; letter-spacing:.8px; color:var(--muted); margin:16px 0 6px; }
.pickerItem { display:flex; align-items:center; gap:10px; padding:9px 12px; border:1px solid var(--line); border-radius:10px; margin-bottom:6px; cursor:pointer; }
.pickerItem:hover { border-color:#c9d2dd; background:#f7f9fb; }
.pickerItem.sel { border-color:var(--accent); box-shadow:0 0 0 2px rgba(226,0,26,.12); }
.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; }
</style> </style>
</head> </head>
<body> <body>
<header> <header>
<div class="brand">SLC&nbsp;<b>Companion</b></div> <div class="brand">SLC&nbsp;<b>Companion</b></div>
<span class="tag">Prototyp · v0.4</span> <span class="tag">Prototyp · v0.5</span>
<div class="scenario"> <div id="cardBadge" class="cardBadge"></div>
<select id="serviceSel" title="Service (Action Card)"></select>
<select id="changeSel" title="Change-Typ"></select>
</div>
<div class="spacer"></div> <div class="spacer"></div>
<button class="ghost" id="resetBtn" title="Durchlauf zurücksetzen">Zurücksetzen</button> <button class="ghost" id="resetBtn" title="Neue Action Card / Durchlauf zurücksetzen">Neu starten</button>
<button class="primary" id="debriefBtn">Debrief</button> <button class="primary" id="debriefBtn">Debrief</button>
</header> </header>
<div class="progress"><div id="progressBar" style="width:0%"></div></div> <div class="progress"><div id="progressBar" style="width:0%"></div></div>
@ -745,7 +763,8 @@ const STATIONEN = [
/* ====================== STATE ====================== */ /* ====================== STATE ====================== */
const LS_KEY = "slc-companion-proto"; const LS_KEY = "slc-companion-proto";
function defaultState(){ function defaultState(){
return { service:0, change:0, index:0, stage:"discuss", quizIndex:0, return { view:"card", service:0, change:0, startIndex:null,
index:0, stage:"discuss", quizIndex:0,
picks:{}, done:{}, unclear:{} }; picks:{}, done:{}, unclear:{} };
} }
let S = load(); let S = load();
@ -778,14 +797,92 @@ function renderList(){
} }
$("#stationList").innerHTML = html; $("#stationList").innerHTML = html;
$("#stationList").querySelectorAll(".stationItem").forEach(el=>{ $("#stationList").querySelectorAll(".stationItem").forEach(el=>{
el.onclick = ()=>{ S.index = +el.dataset.i; S.stage="discuss"; S.quizIndex=0; save(); render(); }; el.onclick = ()=>{ S.index = +el.dataset.i; S.stage="discuss"; S.quizIndex=0; save(); draw(); };
}); });
const pct = Math.round(Object.keys(S.done).length / STATIONEN.length * 100); const pct = Math.round(Object.keys(S.done).length / STATIONEN.length * 100);
$("#progressBar").style.width = pct+"%"; $("#progressBar").style.width = pct+"%";
} }
/* ====================== RENDER: PANEL ====================== */ /* ====================== VIEW DISPATCH ====================== */
function render(){ function draw(){
document.body.classList.toggle("runMode", S.view==="run");
renderCardBadge();
if(S.view==="card") return renderCardScreen();
if(S.view==="start") return renderStartScreen();
renderRun();
}
function renderCardBadge(){
const el = $("#cardBadge");
if(S.view==="card"){ 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>`;
}
/* ---------- Screen 1: Action Card ziehen ---------- */
function renderCardScreen(){
$("#panel").innerHTML = `
<div class="setupHead">Schritt 1 · Action Card</div>
<h2 class="setupTitle">Welches Szenario zieht ihr?</h2>
<p class="muted">Wählt Service und Change-Typ der gezogenen Action Card oder zieht zufällig. Diese Karte steckt im Action-Stein und wandert durch alle Stationen.</p>
<div class="cardForm">
<label>Service<select id="serviceSel"></select></label>
<label>Change-Typ<select id="changeSel"></select></label>
</div>
<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>
<div class="spacer"></div>
<button class="primary" id="toStart">Weiter → Startpunkt wählen</button>
</div>`;
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("");
svc.value=S.service; ch.value=S.change;
const refresh=()=>{ $("#cardTrigger").textContent=USE_CASES[S.service].changes[S.change]; };
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(); };
}
/* ---------- Screen 2: Startpunkt bestimmen ---------- */
function renderStartScreen(){
const groups={};
STATIONEN.forEach((st,i)=>{ (groups[st.phase]=groups[st.phase]||[]).push({st,i}); });
let list="";
for(const ph in PHASEN){
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}">
<span class="dot" style="background:${PHASEN[ph].color}"></span>
<span class="id">${st.id}</span>
<span class="nm">${st.name}</span>
${st.typ==="gate"?`<span class="gate">Gate ${st.gateNr}</span>`:''}
</div>`;
});
}
$("#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>
<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(); };
}
/* ====================== RENDER: RUN (Station) ====================== */
function renderRun(){
renderList(); renderList();
const st = cur(); const st = cur();
const ph = PHASEN[st.phase]; const ph = PHASEN[st.phase];
@ -896,15 +993,15 @@ function renderReveal(st){
/* ====================== WIRING ====================== */ /* ====================== WIRING ====================== */
function wire(st){ function wire(st){
const b = id => $("#"+id); const b = id => $("#"+id);
if(b("toQuiz")) b("toQuiz").onclick = ()=>{ S.stage="quiz"; S.quizIndex=0; save(); render(); }; if(b("toQuiz")) b("toQuiz").onclick = ()=>{ S.stage="quiz"; S.quizIndex=0; save(); draw(); };
if(b("backDiscuss")) b("backDiscuss").onclick = ()=>{ S.stage="discuss"; save(); render(); }; if(b("backDiscuss")) b("backDiscuss").onclick = ()=>{ S.stage="discuss"; save(); draw(); };
$("#panel").querySelectorAll(".opt[data-opt]").forEach(el=>{ $("#panel").querySelectorAll(".opt[data-opt]").forEach(el=>{
el.onclick = ()=>{ S.picks[pkey(st.id,S.quizIndex)] = +el.dataset.opt; save(); render(); }; el.onclick = ()=>{ S.picks[pkey(st.id,S.quizIndex)] = +el.dataset.opt; save(); draw(); };
}); });
if(b("nextQ")) b("nextQ").onclick = ()=>{ S.quizIndex++; save(); render(); }; if(b("nextQ")) b("nextQ").onclick = ()=>{ S.quizIndex++; save(); draw(); };
if(b("toReveal")) b("toReveal").onclick = ()=>{ S.stage="reveal"; save(); render(); }; if(b("toReveal")) b("toReveal").onclick = ()=>{ S.stage="reveal"; 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; S.index++; S.stage="discuss"; S.quizIndex=0; save(); render(); }; if(b("nextStation")) b("nextStation").onclick = ()=>{ S.done[st.id]=true; S.index++; S.stage="discuss"; S.quizIndex=0; save(); draw(); };
if(b("finish")) b("finish").onclick = ()=>{ S.done[st.id]=true; save(); openDebrief(); }; if(b("finish")) b("finish").onclick = ()=>{ S.done[st.id]=true; save(); openDebrief(); };
} }
@ -945,18 +1042,11 @@ function openDebrief(){
/* ====================== INIT ====================== */ /* ====================== INIT ====================== */
(function init(){ (function init(){
const svcSel = $("#serviceSel"), chSel = $("#changeSel");
svcSel.innerHTML = USE_CASES.map((u,i)=>`<option value="${i}">${u.service}</option>`).join("");
chSel.innerHTML = CHANGE_TYPES.map((c,i)=>`<option value="${i}">${c}</option>`).join("");
svcSel.value = S.service;
chSel.value = S.change;
svcSel.onchange = ()=>{ S.service = +svcSel.value; save(); render(); };
chSel.onchange = ()=>{ S.change = +chSel.value; save(); render(); };
$("#debriefBtn").onclick = openDebrief; $("#debriefBtn").onclick = openDebrief;
$("#closeDebrief").onclick = ()=> $("#debriefDlg").close(); $("#closeDebrief").onclick = ()=> $("#debriefDlg").close();
$("#copyBtn").onclick = ()=> navigator.clipboard?.writeText($("#debriefText").textContent); $("#copyBtn").onclick = ()=> navigator.clipboard?.writeText($("#debriefText").textContent);
$("#resetBtn").onclick = ()=>{ if(confirm("Durchlauf zurücksetzen?")){ S=defaultState(); save(); render(); } }; $("#resetBtn").onclick = ()=>{ if(confirm("Neue Action Card ziehen und Durchlauf zurücksetzen?")){ S=defaultState(); save(); draw(); } };
render(); draw();
})(); })();
</script> </script>
</body> </body>