SLC_Game/04_Tablet-Quiz/app/index.html
breitenbach76 668367a241 Companion-App: 30 Action-Card-Texte (Titel + Szenario) eingebaut
- USE_CASES.changes von Strings auf {titel, text} umgestellt (6 Services x 5 =
  30 Action Cards, Stil wie das Freiburg-Action-Card-PDF).
- UI zeigt Titel (fett) + Text + "Was passiert an welchen Stellen?";
  Karten-Screen, Run-Token, Tour-Banner und Debrief (MD+JSON) nachgezogen.
- Helfer acard()/cardHtml(); JS-Syntax + Datenform verifiziert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 17:01:28 +02:00

1242 lines
84 KiB
HTML
Raw 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, viewport-fit=cover" />
<title>SLC-Workshop — Companion-App</title>
<meta name="description" content="Begleit-App zum SLC-Workshop-Tabletop: führt durch die Stationen, vermittelndes Quiz, Auflösung und Debrief. Offline lauffähig." />
<meta name="theme-color" content="#1d2430" />
<link rel="manifest" href="manifest.webmanifest" />
<link rel="apple-touch-icon" href="icon.svg" />
<link rel="icon" href="icon.svg" type="image/svg+xml" />
<style>
:root {
--bg: #f4f5f7;
--panel: #ffffff;
--ink: #1d2430;
--muted: #6b7686;
--line: #e2e6ec;
--accent: #e2001a; /* Freiburg-Rot */
--ok: #1f9d57;
--bad: #d23b3b;
--design: #2f80c9;
--transition: #e8862b;
--operation: #2f9e57;
--support: #18a9a0;
--review: #8358c6;
--radius: 14px;
--shadow: 0 1px 3px rgba(20,30,50,.08), 0 6px 24px rgba(20,30,50,.06);
}
* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; }
body {
font: 16px/1.5 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
color: var(--ink); background: var(--bg);
-webkit-font-smoothing: antialiased;
}
header {
display: flex; align-items: center; gap: 16px;
padding: 12px 20px; background: var(--panel);
border-bottom: 1px solid var(--line); position: sticky; top: 0; z-index: 5;
}
header .brand { font-weight: 700; letter-spacing: .2px; }
header .brand b { color: var(--accent); }
header .tag { font-size: 12px; color: var(--muted); border:1px solid var(--line); padding:2px 8px; border-radius:999px; }
header .spacer { flex: 1; }
.scenario { display:flex; gap:8px; align-items:center; flex-wrap:wrap; }
.scenario select, button {
font: inherit; border-radius: 10px; border: 1px solid var(--line);
background: #fff; color: var(--ink); padding: 8px 12px; cursor: pointer;
}
.scenario select { max-width: 260px; }
button.primary { background: var(--accent); color: #fff; border-color: var(--accent); font-weight: 600; }
button.ghost { background: #fff; }
button:disabled { opacity: .45; cursor: default; }
.progress { height: 6px; background: var(--line); }
.progress > div { height: 100%; background: var(--accent); transition: width .3s; }
.layout { display: grid; grid-template-columns: 300px 1fr; gap: 0; min-height: calc(100% - 55px); }
@media (max-width: 820px) { .layout { grid-template-columns: 1fr; } aside { display:none; } }
aside { border-right: 1px solid var(--line); background: var(--panel); padding: 14px; overflow:auto; }
aside h3 { font-size: 11px; text-transform: uppercase; letter-spacing: .8px; color: var(--muted); margin: 16px 0 6px; }
.stationItem {
display:flex; align-items:center; gap:8px; padding:8px 10px; border-radius:10px;
cursor:pointer; font-size: 14px;
}
.stationItem:hover { background: #f7f9fb; }
.stationItem.active { background: #eef4fb; font-weight:600; }
.stationItem .dot { width:10px; height:10px; border-radius:50%; flex:none; }
.stationItem .id { color: var(--muted); font-variant-numeric: tabular-nums; font-size:12px; }
.stationItem .flag { margin-left:auto; color: var(--accent); }
.stationItem.done .id::after { content:" ✓"; color: var(--ok); }
main { padding: 28px clamp(20px, 5vw, 64px); overflow:auto; }
.card { background: var(--panel); border:1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); padding: 26px; max-width: 860px; margin: 0 auto; }
.phaseChip { display:inline-flex; align-items:center; gap:8px; font-size:12px; font-weight:700; text-transform:uppercase; letter-spacing:.6px; color:#fff; padding:5px 12px; border-radius:999px; }
.gateChip { background: var(--ink); }
.stationName { font-size: 28px; font-weight: 700; margin: 14px 0 4px; line-height:1.2; }
.stationId { color: var(--muted); font-size: 14px; }
.token { font-size:13px; color:var(--muted); margin-top:10px; }
.token b { color: var(--ink); }
.ctChip { display:inline-block; margin-left:6px; font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:.4px; color:var(--accent); border:1px solid var(--accent); border-radius:999px; padding:2px 9px; vertical-align:middle; }
.ctText { margin-top:8px; font-size:14px; color:var(--ink); background:#fff7f7; border-left:3px solid var(--accent); border-radius:8px; padding:10px 14px; max-width:760px; }
.step { margin-top: 22px; }
.stepHead { display:flex; align-items:center; gap:10px; font-size:12px; text-transform:uppercase; letter-spacing:.8px; color:var(--muted); margin-bottom:10px; }
.stepHead .n { width:22px; height:22px; border-radius:50%; background:var(--ink); color:#fff; display:grid; place-items:center; font-size:12px; }
.discuss { background:#fbfdff; border:1px dashed var(--line); border-radius:12px; padding:18px 20px; }
.discuss ul { margin:8px 0 0; padding-left:20px; }
.discuss li { margin:4px 0; }
.q { border:1px solid var(--line); border-radius:12px; padding:18px 20px; margin-bottom:14px; }
.q .frage { font-weight:600; margin-bottom:12px; }
.opts { display:grid; gap:8px; }
.opt { text-align:left; padding:12px 14px; border-radius:10px; border:1px solid var(--line); background:#fff; cursor:pointer; }
.opt:hover { border-color:#c9d2dd; }
.opt.sel { border-color: var(--accent); box-shadow: 0 0 0 2px rgba(226,0,26,.12); }
.opt.correct { border-color: var(--ok); background:#f1faf4; }
.opt.wrong { border-color: var(--bad); background:#fdf3f3; }
.opt .mark { float:right; font-weight:700; }
.qExpl { margin-top:10px; font-size:14px; color:var(--muted); border-left:3px solid var(--line); padding-left:12px; }
.reveal h4 { margin: 18px 0 6px; font-size: 13px; text-transform:uppercase; letter-spacing:.6px; color:var(--muted); }
.reveal p { margin: 0 0 8px; }
.reveal ul { margin: 4px 0; padding-left: 20px; }
table.raci { width:100%; border-collapse: collapse; font-size:14px; }
table.raci th, table.raci td { text-align:left; padding:8px 10px; border-bottom:1px solid var(--line); }
table.raci th { color:var(--muted); font-weight:600; font-size:12px; text-transform:uppercase; letter-spacing:.5px; }
.raciBadge { display:inline-block; min-width:22px; text-align:center; font-weight:700; border-radius:6px; padding:2px 6px; font-size:12px; background:#eef0f3; color:#5a6675; }
.raci-A { background:#fbe3e3; color:#b5202a; }
.raci-R { background:#e3eefb; color:#1f5fae; }
.raci-C { background:#fff1dd; color:#b5701a; }
.raci-I { background:#eef0f3; color:#5a6675; }
.pfade { display:grid; gap:8px; }
.pfad { border:1px solid var(--line); border-left:4px solid var(--transition); border-radius:8px; padding:10px 12px; }
.pfad b { display:block; }
.actions { display:flex; gap:10px; align-items:center; margin-top:24px; flex-wrap:wrap; }
.actions .spacer { flex:1; }
.unclear { display:flex; align-items:center; gap:8px; color:var(--muted); font-size:14px; }
dialog { border:none; border-radius:16px; padding:0; box-shadow: var(--shadow); max-width: 640px; width: 92%; }
dialog::backdrop { background: rgba(15,22,35,.45); }
.modal { padding: 24px; }
.modal h2 { margin:0 0 6px; }
pre.export { background:#0f1623; color:#d6e2f0; padding:14px; border-radius:10px; overflow:auto; max-height:320px; font-size:13px; }
.stat { display:flex; gap:20px; margin:14px 0; }
.stat div b { display:block; font-size:24px; }
.stat div span { color:var(--muted); font-size:12px; }
.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; }
.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>
<header>
<div class="brand">SLC&nbsp;<b>Companion</b></div>
<span class="tag">v0.5</span>
<div id="cardBadge" class="cardBadge"></div>
<div class="spacer"></div>
<button class="ghost" id="resetBtn" title="Neue Action Card / Durchlauf zurücksetzen">Neu starten</button>
<button class="primary" id="debriefBtn">Debrief</button>
</header>
<div class="progress"><div id="progressBar" style="width:0%"></div></div>
<div class="layout">
<aside id="stationList"></aside>
<main><div class="card" id="panel"></div></main>
</div>
<dialog id="debriefDlg">
<div class="modal">
<h2>Debrief-Export</h2>
<p style="color:var(--muted);margin-top:0">Lokale Auswertung dieses Durchlaufs — nichts wird hochgeladen.</p>
<div class="stat" id="debriefStat"></div>
<pre class="export" id="debriefText"></pre>
<div class="actions">
<button class="ghost" id="copyBtn">In Zwischenablage</button>
<button class="ghost" id="dlMd">↓ Markdown</button>
<button class="ghost" id="dlJson">↓ JSON</button>
<div class="spacer"></div>
<button class="primary" id="closeDebrief">Schließen</button>
</div>
</div>
</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_02tr_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 +
spm_rollen.yaml). Reihenfolge: Design → Transition (inkl. Gate 13) →
Operation ⇄ Support → Review. Quiz-Fragen sind vermittelnd formuliert.
Im Echtbetrieb generiert ein Build-Skript questions.json aus den YAMLs.
========================================================================= */
const ROLLEN = {
spm:"Service-Portfolio-Manager", sor:"Service Operations Runde (SOR)",
service_owner:"Service Owner", al_basis_cloud:"AL Basis & Cloud",
al_applikationen:"AL Applikationen", support_manager:"Support Manager",
problem_manager:"Problem Manager", projektleitung:"Projektleitung",
betriebsteam:"Betriebsteam", service_support_team:"Service-Support Team",
projektteam:"Projektteam", queue_koordinator:"Queue Koordinator",
first_level_agent:"1st Level Agent", second_level_agent:"2nd Level Agent",
testmanagement:"Testmanagement", architektur:"Architektur",
lieferant:"Lieferant", operations_manager:"Betrieb (AL B&C / AL App)"
};
const PHASEN = {
design:{label:"Design", color:"var(--design)"},
transition:{label:"Transition", color:"var(--transition)"},
operation:{label:"Operation", color:"var(--operation)"},
support:{label:"Support", color:"var(--support)"},
review:{label:"Review", color:"var(--review)"}
};
/* Action Cards: 6 Services × 5 Change-Typen (aus „Use Cases mit möglichen Changes"). */
const CHANGE_TYPES = [
"Major Change Top-Level",
"Major Change Low Level",
"Normal Change",
"Standard Change",
"Emergency Change"
];
const USE_CASES = [
{ service:"Zentrale VDI (Virtual-Desktop-Infrastructure)",
desc:"Bereitstellung von virtuellen Windows-Desktops über das interne Rechenzentrum.",
changes:[
{titel:"Open Source von oben!", text:"Der OB gibt die Richtung vor: Die proprietäre VDI-Lösung soll auf eine Open-Source-Alternative (OpenStack + Thin-Client) umgestellt werden."},
{titel:"Bauamt will mehr!", text:"Das Bauamt fordert ein neues GIS-Fachverfahren, das künftig direkt auf dem virtuellen Desktop laufen soll."},
{titel:"Tapetenwechsel", text:"Die Stadt bekommt ein neues Logo — der Desktop-Hintergrund aller virtuellen Arbeitsplätze muss angepasst werden."},
{titel:"Quartalspflege", text:"Das turnusmäßige Firmware-Update der VDI-Host-Hypervisoren steht an — im Standard-Change-Katalog längst hinterlegt."},
{titel:"Blackout!", text:"Ein Stromausfall reißt ein ganzes VDI-Host-Cluster aus dem Betrieb — die Sitzungen müssen sofort auf ein Backup-Cluster migriert werden."}
]},
{ service:"Managed VPN-Access Service",
desc:"Zentral verwalteter VPN-Dienst, der Mitarbeitenden der Fachämter sicheren Remote-Zugriff auf interne Systeme (Intranet, Fachanwendungen, Datenbanken) ermöglicht.",
changes:[
{titel:"Brüssel ruft!", text:"Eine neue EU-weite IT-Sicherheitsverordnung zwingt dazu, die gesamte VPN-Architektur neu aufzustellen."},
{titel:"Schlüsselpflicht", text:"Das Hauptpersonalamt verlangt eine neue Authentifizierung: FIDO2-Security-Keys werden für alle verpflichtend."},
{titel:"Heimvorteil", text:"Ein neues Intranet-Portal soll in die Split-Tunnel-Liste, damit Mitarbeitende auch aus dem Homeoffice darauf zugreifen."},
{titel:"Monatsroutine", text:"Das monatliche Firmware-Update der VPN-Appliance steht an — als Standard-Change bereits freigegeben."},
{titel:"Gephisht!", text:"Ein erfolgreicher Phishing-Angriff hat eine VPN-Zertifikatskette kompromittiert — sofort sperren und neu ausstellen."}
]},
{ service:"Online-Bürgerportal für Meldungen & Anträge",
desc:"Zentrales Web-Portal, über das Bürger*innen Meldungen (z. B. Straßenreparatur, Lärm) und Anträge (z. B. Baugenehmigung, Personalausweis) digital einreichen und den Status verfolgen.",
changes:[
{titel:"Mitreden, Pflicht!", text:"Ein neues Landesgesetz schreibt digitale Bürgerbeteiligung vor — das Portal muss um komplette Beteiligungs-Module erweitert werden."},
{titel:"Ratsbeschluss!", text:"Der Gemeinderat will einen neuen Meldetyp „Klimaschutz“ — das Umweltamt braucht dafür eigene Formulare und Workflows."},
{titel:"Rotstift gefragt", text:"Der Bürgerservice meldet einen Rechtschreibfehler in einem statischen Hinweistext, der korrigiert werden muss."},
{titel:"Patchday", text:"Das monatliche Sicherheits-Patch des Webservers (Apache/Nginx) steht an — im Change-Katalog definiert."},
{titel:"Lücke im Formular!", text:"In einem Eingabe-Formular wird eine kritische XSS-Schwachstelle entdeckt — ein Hotfix muss sofort raus."}
]},
{ service:"Zentrales Dokumenten-Management-System (DMS)",
desc:"Elektronisches Archiv, das alle ein- und ausgehenden Dokumente (PDF, Scans, E-Mails) zentral speichert, versioniert und revisionssicher archiviert.",
changes:[
{titel:"Datendiät", text:"Eine DSGVO-Ergänzung zur Datenminimierung erzwingt die komplette Neugestaltung von Metadaten-Modell und Archivierungs-Policies."},
{titel:"Akten-Zuwachs", text:"Ein neues Fachverfahren zieht ein und braucht eigene Dokumentenklassen und Workflows im DMS."},
{titel:"Dropdown-Wunsch", text:"Das Finanzamt bittet um ein neues Metadaten-Feld „Kostenstelle“ als Auswahlliste."},
{titel:"Versionspflege", text:"Das quartalsweise Update der DMS-Software (Sicherheits- und Funktions-Patches) steht an."},
{titel:"Ransomware!", text:"Alarm: Dokumente werden verdächtig verschlüsselt — Storage sofort isolieren und aus den letzten Snapshots wiederherstellen."}
]},
{ service:"Kommunales GIS-Portal (Geoinformations-System)",
desc:"Web-basiertes Karten- und Analyse-Portal, das Fachämtern (Bau, Umwelt, Verkehr) räumliche Daten (Flurstücke, Infrastruktur, Umweltzonen) und bearbeitbare Layer bereitstellt.",
changes:[
{titel:"Norm-Zwang", text:"Eine bundesweite Vorgabe zu EU-Standards erzwingt die komplette Migration des GIS-Stacks auf konforme Services und Datenmodelle."},
{titel:"Klimarisiko im Blick", text:"Das Umweltamt will ein neues Analyse-Modul „Klimarisiko“ — mit neuen Daten-Layern und Rechen-Ressourcen."},
{titel:"Falsch beschriftet", text:"Das Bauamt meldet eine falsche Beschriftung eines Karten-Layers, die korrigiert werden muss."},
{titel:"GeoServer-Update", text:"Das monatliche Update der GIS-Software (GeoServer 2.23 → 2.24) steht an — im Standard-Change-Katalog."},
{titel:"Schnittstelle offen!", text:"An einer Schnittstelle wird eine kritische Schwachstelle entdeckt, die unautorisierten Datenzugriff erlaubt — Dienst sofort abschalten und patchen."}
]},
{ service:"Beschaffungs- und Vertrags-System für Fachämter",
desc:"Web-Applikation, über die Fachämter interne Beschaffungen (Material, Dienstleistungen) anlegen, prüfen und Verträge digital verwalten.",
changes:[
{titel:"Vergabe neu!", text:"Eine neue EU-Vergaberichtlinie zwingt zur Einführung von E-Invoicing und erweiterten Transparenz-Reports."},
{titel:"Portal für Partner", text:"Ein neues Lieferanten-Portal (z. B. für Badenova) soll andocken — mit neuen API-Schnittstellen und Authentifizierungs-Flows."},
{titel:"Vierstellig, bitte", text:"Das Finanzamt wünscht eine kleine Anpassung: aus dem Label „Kostenstelle“ wird „Kostenstelle (4-stellig)“."},
{titel:"Patch-Quartal", text:"Das quartalsweise Sicherheits-Patch des Anwendungsservers steht an — bereits im Change-Katalog."},
{titel:"Upload-Falle!", text:"Im Vertrags-Upload wird eine kritische Lücke entdeckt, über die sich Schadcode hochladen lässt — Endpoint sofort sperren, Hotfix einspielen."}
]}
];
const STATIONEN = [
{ id:"ds_01", phase:"design", typ:"aktivitaet",
name:"Definieren der erforderlichen Service-Eigenschaften",
beschreibung:"Grundlegende Definition des neuen oder geänderten Services aus fachlicher Perspektive. Startpunkt für die Service-Definition als zentrales Artefakt.",
umfasst:["Zweck, Nutzen, Zielgruppen","Utility & Warranty","SLA-/SLO-Anforderungen","Abhängigkeiten zu unterstützenden Services","Fachliche & technische Anforderungen"],
artefakt:"Service-Definition (Entwurf)",
raci:[["service_owner","A"],["projektleitung","R"],["betriebsteam","C"],["architektur","C"],["spm","C"]],
quiz:[
{frage:"Wer trägt die Ergebnisverantwortung (A) für diese Aktivität?",
optionen:["Service Owner","Projektleitung","SPM","Architektur"], richtig:0,
expl:"Der (designierte) Service Owner ist Accountable; die Projektleitung führt operativ aus (R)."},
{frage:"Welches zentrale Artefakt entsteht hier?",
optionen:["Betriebshandbuch","Service-Definition","Review-Bericht","Incident Record"], richtig:1,
expl:"Die Service-Definition ist das zentrale Artefakt der Design-Phase."}
]},
{ id:"ds_02", phase:"design", typ:"aktivitaet",
name:"Designen der erforderlichen Service- und Service-Management-Komponenten",
beschreibung:"Einarbeiten der Service-Eigenschaften in ein vollständiges Designmodell.",
umfasst:["Servicearchitektur (Komponenten, Schnittstellen)","Design der Betriebs- & Supportprozesse","Sicherheit/Compliance/Datenschutz","Monitoring & Reporting","Rollenmodell"],
artefakt:"Service Design Document",
raci:[["service_owner","A"],["architektur","R"],["projektteam","R"],["spm","I"]],
quiz:[
{frage:"Wer führt das Architektur-Design operativ aus (R)?",
optionen:["SPM","Service Owner","Architektur","Support Manager"], richtig:2,
expl:"Architektur und Projektteam sind Responsible; der Service Owner bleibt Accountable."}
]},
{ id:"ds_03", phase:"design", typ:"aktivitaet",
name:"Beschreiben des Vorgehens zur Implementierung",
beschreibung:"Planen, WIE der Service organisatorisch eingeführt wird (nicht technisch deployt wird).",
umfasst:["Organisatorische Integration","Rollenübergaben","Anpassung Richtlinien/Prozesse/Tools","Trainings & Kommunikation","Time-to-Operate"],
artefakt:"Implementation Blueprint",
raci:[["service_owner","A"],["projektleitung","R"],["betriebsteam","C"],["service_support_team","C"],["spm","I"]],
quiz:[
{frage:"Was wird hier geplant?",
optionen:["Das technische Deployment","Wie der Service organisatorisch eingeführt wird","Die Incident-Bearbeitung","Die Portfolio-Strategie"], richtig:1,
expl:"ds_03 plant die organisatorische Einführung — bewusst getrennt vom technischen Build."}
]},
{ id:"ds_04", phase:"design", typ:"aktivitaet",
name:"Vorbereiten der Service-Implementierung",
beschreibung:"Finalisieren aller organisatorischen Voraussetzungen für die spätere Übergabe in den Betrieb.",
umfasst:["Abstimmung mit Betrieb & Support","Prozesse/Tools/Strukturen vorbereiten","ELS-Konzept (Early Life Support)","Service vollständig im Portfolio erfasst"],
artefakt:"Übergabe-Readiness (organisatorisch)",
raci:[["service_owner","A"],["projektleitung","R"],["betriebsteam","C"],["service_support_team","C"],["spm","I"]],
quiz:[
{frage:"Wofür steht das ELS-Konzept?",
optionen:["Early Life Support","End of Life Service","Enterprise License System","Externer Lieferanten-Support"], richtig:0,
expl:"Early Life Support = erhöhte Betreuung direkt nach Go-Live (siehe op_01)."}
]},
{ id:"tr_01", phase:"transition", typ:"gate", gateNr:1,
name:"Gate 1: Entwicklung oder Konfiguration?",
beschreibung:"Entry-Gate der Transition: Entscheidung, ob die Service-Komponenten entwickelt oder nur konfiguriert werden. Erfordert SOR-Approval (Budget- und Ressourcenimplikationen).",
umfasst:["Aufwandsschätzung (Build vs. Configure)","Technische Risiken","Budget-Abgleich","ggf. Lieferanten-Einbindung","SOR-Vorlage für Freigabe"],
artefakt:"Gate-Entscheidung + SOR-Vorlage",
pfade:[
["Entwicklung","Neuentwicklung/wesentliche Anpassung → weiter zu tr_02"],
["Konfiguration","Bestehende Komponenten konfigurieren → springt zu tr_05"]
],
pruef:[
["Design-Vollständigkeit","Ist das Service Design Document vollständig?"],
["Budget-Verfügbarkeit","Ist Budget für die Strategie verfügbar?"],
["Projektressourcen","Können Ressourcen mobilisiert werden?"],
["Betriebs-/Support-Bereitschaft","Grundsätzliches Commitment vorhanden?"]
],
raci:[["sor","A"],["service_owner","R"],["projektleitung","R"],["architektur","R"],["spm","C"],["lieferant","I"]],
quiz:[
{frage:"Wer entscheidet an Gate 1 (Accountable)?",
optionen:["Service Owner","SOR","SPM","Projektleitung"], richtig:1,
expl:"Gate 1 ist eine SOR-Gremienentscheidung (Budget-/Ressourcenimplikationen)."},
{frage:"Welche zwei Pfade öffnet Gate 1?",
optionen:["Go oder Stop","Intern oder Extern","Entwicklung oder Konfiguration","Test oder Release"], richtig:2,
expl:"Entwicklung (tr_02) vs. Konfiguration (tr_05) — Konfiguration überspringt tr_02tr_04."}
]},
{ id:"tr_02", phase:"transition", typ:"aktivitaet",
name:"Koordinieren der Entwicklungs- und Beschaffungsaktivitäten",
beschreibung:"Steuern der Entwicklungsaktivitäten im Projekt.",
umfasst:["Abstimmung mit Lieferanten","Ressourcenplanung","Termin- und Budgetnachführung","Sicherstellung von Change-Kontrollen","Definition von Build- und Konfigurationspaketen"],
artefakt:"Projektsteuerung / Build- & Konfigurationspakete",
raci:[["projektleitung","A"],["architektur","C"],["service_owner","I"],["lieferant","C/I"]],
quiz:[
{frage:"Wer trägt die Ergebnisverantwortung (A) für die Koordination der Entwicklung?",
optionen:["Projektleitung","Architektur","Service Owner","Lieferant"], richtig:0,
expl:"Die Projektleitung ist Accountable; Architektur berät (C), der Service Owner ist informiert (I)."}
]},
{ id:"tr_03", phase:"transition", typ:"aktivitaet",
name:"Entwickeln von Anwendungen und Systemen",
beschreibung:"Realisierung der technischen Service-Komponenten.",
umfasst:["Softwareentwicklung","Customizing","Schnittstellenentwicklung","Infrastrukturaufbau","Versionierung & Dokumentation","Testdaten & Migrationspfade vorbereiten"],
artefakt:"Entwickelte Service-Komponenten",
raci:[["projektleitung","A"],["projektteam","R"],["lieferant","R"],["service_owner","C"],["architektur","C"]],
quiz:[
{frage:"Wer führt die Entwicklung operativ aus (R)?",
optionen:["SPM","Projektteam bzw. Lieferant","Service Owner","Testmanagement"], richtig:1,
expl:"Projektteam (intern) und Lieferant (extern) sind Responsible; die Projektleitung bleibt Accountable."}
]},
{ id:"tr_04", phase:"transition", typ:"aktivitaet",
name:"Entgegennehmen der Service-Komponenten",
beschreibung:"Qualitätssicherung beim Übergang von der Entwicklung zur Testphase.",
umfasst:["Eingangskontrolle","Prüfung von Vollständigkeit / Qualität","Sicherstellung der Dokumentationen","Übergabe an Testmanagement"],
artefakt:"Abgenommene Service-Komponenten",
raci:[["service_owner","A"],["projektleitung","R"],["testmanagement","R"],["lieferant","C"]],
quiz:[
{frage:"An wen werden die entgegengenommenen Komponenten übergeben?",
optionen:["An den Betrieb","An das Testmanagement","An die SOR","An das SPM"], richtig:1,
expl:"tr_04 ist die Qualitätssicherung beim Übergang in die Testphase — Übergabe an das Testmanagement."}
]},
{ id:"tr_05", phase:"transition", typ:"aktivitaet",
name:"Konfiguration der Service-Komponenten",
beschreibung:"Einrichtung von Konfigurationsparametern, Settings, Rollen und Zugängen. Einstiegspunkt bei Gate-1-Entscheidung Konfiguration (überspringt tr_02tr_04).",
umfasst:["Setup von Parametern, Policies, Templates","Toolkonfiguration (ITSM-Systeme, Monitoring)","Anpassung bestehender Komponenten","Verknüpfung mit Service-Katalog / Portfolio"],
artefakt:"Konfigurierte Service-Komponenten",
raci:[["service_owner","A"],["projektteam","R"],["betriebsteam","C"],["service_support_team","C"]],
quiz:[
{frage:"Bei welcher Gate-1-Entscheidung ist tr_05 der Einstiegspunkt?",
optionen:["Entwicklung","Konfiguration","Ablehnung","Go-Live"], richtig:1,
expl:"Bei der Entscheidung Konfiguration werden tr_02tr_04 übersprungen und direkt bei tr_05 eingestiegen."}
]},
{ id:"tr_06", phase:"transition", typ:"aktivitaet",
name:"Erstellen oder Aktualisieren der Betriebs-Dokumentation",
beschreibung:"Alle Betriebsunterlagen werden erstellt oder aktualisiert.",
umfasst:["Service Operation Manual","Supportanleitungen","Monitoring-Spezifikationen","Eskalationswege","Standard Changes","Konfigurations-/Betriebsrichtlinien"],
artefakt:"Betriebsdokumentation (Service Operation Manual)",
raci:[["service_owner","A"],["projektteam","R"],["betriebsteam","C"],["service_support_team","C"]],
quiz:[
{frage:"Welches Artefakt entsteht hier vor allem?",
optionen:["Service-Definition","Betriebsdokumentation (Service Operation Manual)","Gate-Vorlage","Review-Bericht"], richtig:1,
expl:"tr_06 erstellt/aktualisiert alle Betriebsunterlagen (u.a. Service Operation Manual, Supportanleitungen)."}
]},
{ id:"tr_07", phase:"transition", typ:"aktivitaet",
name:"Testen der Service-Komponenten",
beschreibung:"Verifizierung, dass alle Servicekomponenten funktionsfähig und betriebsbereit sind.",
umfasst:["Funktionstests","Integrationstests","Abnahmetests","Nachweis der Betriebsreife","Testprotokolle & Freigaben"],
artefakt:"Testprotokolle & Freigaben",
raci:[["projektleitung","A"],["testmanagement","R"],["service_owner","C"],["lieferant","C/I"]],
quiz:[
{frage:"Wer ist Responsible (R) für das Testen der Komponenten?",
optionen:["Projektleitung","Testmanagement","Service Owner","Betriebsteam"], richtig:1,
expl:"Das Testmanagement ist Responsible; die Projektleitung ist Accountable."}
]},
{ id:"tr_08", phase:"transition", typ:"aktivitaet",
name:"Formale Übergabe (Build abgeschlossen)",
beschreibung:"Der Build ist abgeschlossen. Alle Artefakte werden für die Entry-Prüfung (Gate 2) bereitgestellt.",
umfasst:["Bereitstellung aller Betriebsunterlagen","Bereitstellung der Testprotokolle","Dokumentierter Übergabe-Termin","Vorbereitung Gate-2-Antrag"],
artefakt:"Übergabepaket (Gate-2-Antrag)",
raci:[["projektleitung","A"],["service_owner","R"],["betriebsteam","C"],["service_support_team","C"],["spm","I"]],
quiz:[
{frage:"Worauf bereitet die formale Übergabe (Build abgeschlossen) vor?",
optionen:["Gate 1","Gate 2","Gate 3","ELS-Exit"], richtig:1,
expl:"tr_08 stellt alle Artefakte für die Entry-Prüfung an Gate 2 (tr_09) bereit."}
]},
{ id:"tr_09", phase:"transition", typ:"gate", gateNr:2,
name:"Gate 2: Entry-Prüfung nach Build",
beschreibung:"Validierung der Übergabefähigkeit nach Abschluss des Builds. Dies ist eine SO-Einzelentscheidung (keine Gremienentscheidung).",
umfasst:["Prüfung der Übergabe-Vollständigkeit","Prüfung des Abnahme-Status","Ressourcen-Outlook (Betrieb/Support vorbereitet)","Pilot-Entscheidung (falls erforderlich)","Dokumentation im Transition-Steckbrief"],
artefakt:"Gate-2-Entscheidung (Transition-Steckbrief)",
pfade:[
["Freigabe","Weiter zu den Deploy-Aktivitäten → tr_10"],
["Freigabe mit Auflagen","Weiter, definierte Punkte müssen nachgearbeitet werden"],
["Zurück an Build","Nachbesserung erforderlich → tr_02 (ggf. tr_05tr_07)"],
["Ablehnung","Endgültige Ablehnung — erfordert SOR-Eskalation"]
],
pruef:[
["Übergabe-Vollständigkeit","Sind alle Artefakte vorhanden?"],
["Abnahme-Status","Liegt die Auftraggeber-Abnahme vor?"],
["Ressourcen-Outlook","Sind Betrieb und Support prinzipiell vorbereitet?"],
["Pilot-Entscheidung","Ist ein Pilotbetrieb erforderlich?"]
],
raci:[["service_owner","A"],["projektleitung","R"],["betriebsteam","C"],["service_support_team","C"],["spm","I"]],
quiz:[
{frage:"Wer entscheidet an Gate 2 (Accountable)?",
optionen:["SOR-Gremium","Service Owner allein","SPM","Projektleitung"], richtig:1,
expl:"Gate 2 ist eine SO-Einzelentscheidung; bei Ablehnung erfolgt SOR-Eskalation."},
{frage:"Was prüft Gate 2 vor allem?",
optionen:["Die Budgetfreigabe für das Design","Die Übergabefähigkeit nach dem Build","Die Portfolio-Strategie","Die Incident-Quote"], richtig:1,
expl:"Gate 2 validiert die Übergabefähigkeit nach Abschluss des Builds (Vollständigkeit, Abnahme, Ressourcen)."}
]},
{ id:"tr_10", phase:"transition", typ:"aktivitaet",
name:"Ausrollen der Service-Komponenten",
beschreibung:"Technische Bereitstellung/Deployment des Services in die produktive Umgebung.",
umfasst:["Deployment von Anwendungen, Systemen, Komponenten","Konfiguration produktiver Systeme","Migration von Daten","Aktivierung von Monitoring","Zugänge, Rollen, Berechtigungen","Schnittstellen-Integration","Technische Abnahmetests"],
artefakt:"Ausgerollter Service (produktiv)",
raci:[["service_owner","A"],["betriebsteam","R"],["spm","C"],["lieferant","C/I"]],
quiz:[
{frage:"Wer rollt die Service-Komponenten aus (R)?",
optionen:["Betriebsteam","Projektteam","Testmanagement","SPM"], richtig:0,
expl:"Das Betriebsteam ist Responsible für das Deployment; der Service Owner ist Accountable."}
]},
{ id:"tr_11", phase:"transition", typ:"aktivitaet",
name:"Vorbereiten der Service-Aktivierung",
beschreibung:"Prüfung der Betriebsbereitschaft und Vorbereitung des Go-Live-Antrags für Gate 3.",
umfasst:["Review der Betriebsdokumentation","Prüfung der Überwachungsregeln","Prüfung der Rollen- und Eskalationswege","Zugriffe & Toolanbindung sicherstellen","Vorbereitung der Go-Live-Kommunikation","Validierung mit Supportteams","Vorbereitung Gate-3-Antrag (SOR-Vorlage)"],
artefakt:"Go-Live-Readiness (Gate-3-Antrag)",
raci:[["service_owner","A"],["betriebsteam","R"],["spm","C"]],
quiz:[
{frage:"Worauf bereitet tr_11 vor?",
optionen:["Den Build-Start","Den Go-Live-Antrag für Gate 3","Das ELS-Ende","Die Außerbetriebnahme"], richtig:1,
expl:"tr_11 prüft die Betriebsbereitschaft und bereitet den Gate-3-Antrag (SOR-Vorlage) vor."}
]},
{ id:"tr_12", phase:"transition", typ:"gate", gateNr:3,
name:"Gate 3: Go-Live-Freigabe",
beschreibung:"Portfolio-Freigabe und formale Aktivierung des Services. Exit-Gate der Transition — SOR-Entscheidung nach dem Konsent-Prinzip.",
umfasst:["Prüfung der Portfolio-Konsistenz","Prüfung der Betriebsreife (SO-Bestätigung)","Prüfung der Support-Bereitschaft","Prüfung der SLA/SLO-Vereinbarung","Prüfung der Auflagen-Erfüllung aus Gate 2","Bewertung der Geschäftskritikalität","Formale Aufnahme ins Service-Portfolio"],
artefakt:"Gate-3-Entscheidung + Portfolio-Aufnahme",
pfade:[
["Go-Live","Service wird aktiviert → Operation (op_01)"],
["Go-Live mit Auflagen","Aktivierung, definierte Punkte nacharbeiten → Operation"],
["Zurück an Transition","Nachbesserung erforderlich → tr_10 (ggf. tr_09)"],
["Ablehnung","Endgültige Ablehnung des Service-Vorhabens"]
],
raci:[["sor","A"],["service_owner","R"],["spm","C"],["projektleitung","C"],["operations_manager","C"],["support_manager","C"]],
quiz:[
{frage:"Wer entscheidet an Gate 3 (Accountable)?",
optionen:["Service Owner","SOR (Konsent)","Support Manager","Betrieb"], richtig:1,
expl:"Gate 3 ist eine SOR-Entscheidung nach dem Konsent-Prinzip — das Exit-Gate der Transition."},
{frage:"Was bewirkt die Go-Live-Freigabe zusätzlich?",
optionen:["Die Aufnahme ins Service-Portfolio","Den Start des Designs","Die Schließung aller Incidents","Den ELS-Exit"], richtig:0,
expl:"Mit der Freigabe wird der Service formal ins Portfolio aufgenommen und der Katalog-Eintrag aktiviert."}
]},
{ id:"op_01", phase:"operation", typ:"aktivitaet",
name:"Early Life Support (ELS)",
beschreibung:"Phase erhöhter Aufmerksamkeit direkt nach Go-Live: produktiv, aber intensiver beobachtet und betreut als im Normalbetrieb.",
umfasst:["Erhöhte Monitoring-Bereitschaft","Direkte Eskalation zum Projektteam","Schnelles Troubleshooting","Erste Incidents aufarbeiten","ELS-Exit-Entscheidung (SO)"],
artefakt:"Stabilisierter Service (ELS-Exit)",
raci:[["service_owner","A"],["support_manager","R"],["operations_manager","R"],["projektteam","C"],["spm","I"]],
quiz:[
{frage:"Wer entscheidet über den ELS-Exit?",
optionen:["SOR","Service Owner","Support Manager","Betrieb"], richtig:1,
expl:"Der Service Owner entscheidet eigenständig (Informationspflicht an SPM)."}
]},
{ id:"op_02", phase:"operation", typ:"aktivitaet",
name:"Bereitstellen von Leitlinien für den Service-Betrieb",
beschreibung:"Strukturelle Grundlage für den täglichen Servicebetrieb.",
umfasst:["Betriebshandbuch bereitstellen/pflegen","Betriebsprozesse & Arbeitsanweisungen","Rollen, Eskalation, Kommunikation klar","Standard Changes & Known Errors"],
artefakt:"Betriebshandbuch",
raci:[["service_owner","A"],["operations_manager","R"],["spm","C/I"]],
quiz:[
{frage:"Welches Artefakt ist hier zentral?",
optionen:["Service-Definition","Betriebshandbuch","Testbericht","Gate-Vorlage"], richtig:1,
expl:"Das Betriebshandbuch ist die strukturelle Grundlage des laufenden Betriebs."}
]},
{ id:"op_03", phase:"operation", typ:"aktivitaet",
name:"Durchführen laufender Betriebsaufgaben",
beschreibung:"Täglich wiederkehrende Betriebsaktivitäten zur Erbringung des Services.",
umfasst:["Benutzerverwaltung & Berechtigungen","Backup/Restore","Technische Routineaufgaben","Konfigurationspflege","Pflege von Logs, Diensten, Überwachungsobjekten","Security- & Compliance-Anforderungen","Umsetzung freigegebener Standard-Changes"],
artefakt:"Erbrachte Betriebsleistung",
raci:[["operations_manager","A"],["betriebsteam","R"],["service_owner","C"],["lieferant","C/I"],["spm","I"]],
quiz:[
{frage:"Wer ist Responsible (R) für die laufenden Betriebsaufgaben?",
optionen:["Betriebsteam","Service Owner","SPM","Support Manager"], richtig:0,
expl:"Das Betriebsteam führt aus (R); der Betrieb (AL B&C / AL App) ist Accountable."}
]},
{ id:"op_04", phase:"operation", typ:"aktivitaet",
name:"Ressourcen, Dienstleister und Budget managen",
beschreibung:"Sicherstellen, dass der Servicebetrieb über die nötigen Mittel verfügt — stabil und wirtschaftlich.",
umfasst:["Personelle und technische Ressourcen sicherstellen","Koordination mit externen Dienstleistern","Überwachung des Betriebsbudgets","Abstimmung mit Supplier-/Finanz-/Vertragsmanagement","Sicherstellen von Wartung & Lizenzen"],
artefakt:"Ressourcen- & Budget-Status",
raci:[["operations_manager","A"],["betriebsteam","R"],["service_owner","C"],["spm","I"]],
quiz:[
{frage:"Was ist das Ziel dieser Aktivität?",
optionen:["Incidents lösen","Ressourcen, Dienstleister und Budget steuern","Den Service designen","Das Review durchführen"], richtig:1,
expl:"op_04 sichert die nötigen personellen, technischen und finanziellen Mittel für einen stabilen, wirtschaftlichen Betrieb."}
]},
{ id:"op_05", phase:"operation", typ:"aktivitaet",
name:"Überwachen der Services",
beschreibung:"Strukturierte Überwachung des Services anhand definierter KPIs und Metriken.",
umfasst:["Verfügbarkeit, Performance, Auslastung überwachen","Schnittstellen und Konfigurationsobjekte überwachen","Trends / drohende Störungen erkennen","Verknüpfung mit Events, Alerts, Dashboards","Monitoring-Regeln im Betrieb erstellen"],
artefakt:"Monitoring-Daten / Alerts",
raci:[["operations_manager","A"],["betriebsteam","R"],["service_owner","C"],["service_support_team","I"],["spm","I"]],
quiz:[
{frage:"Welche Aktivität beschreibt op_05?",
optionen:["Strukturierte Überwachung anhand von KPIs/Metriken","Erstellung der Service-Definition","Lösen von Incidents","Gate-Entscheidung"], richtig:0,
expl:"op_05 überwacht Verfügbarkeit, Performance und Auslastung und verknüpft mit Events/Alerts/Dashboards."}
]},
{ id:"op_06", phase:"operation", typ:"aktivitaet",
name:"Erstellen des Service-Qualitätsbericht",
beschreibung:"Regelmäßige formale Berichte über die erreichte Servicequalität.",
umfasst:["SLA-/SLO-Auswertung","Auswertung technischer KPIs","Abgleich gegen Qualitätsziele","Identifikation von Verbesserungspotenzialen","Zuarbeit für Service Review & SPM"],
artefakt:"Service-Qualitätsbericht",
raci:[["service_owner","A"],["betriebsteam","R"],["spm","C"]],
quiz:[
{frage:"Welches Artefakt entsteht hier?",
optionen:["Betriebshandbuch","Service-Qualitätsbericht","Problem Record","Testbericht"], richtig:1,
expl:"op_06 erstellt den Service-Qualitätsbericht (SLA-/SLO-Auswertung, KPIs) als Zuarbeit für das Review."}
]},
{ id:"op_07", phase:"operation", typ:"aktivitaet",
name:"Proaktive Problem Identifikation",
beschreibung:"Geht über Monitoring hinaus: aktive Suche nach strukturellen Problemen.",
umfasst:["Trendanalysen aus Monitoringdaten","Analyse von Engpässen, Ressourcenproblemen","Technische Analyse von Systemmustern","Erkennen potenzieller SLA-Verletzungen"],
artefakt:"Identifizierte strukturelle Probleme",
raci:[["service_owner","A"],["betriebsteam","R"],["spm","I"]],
quiz:[
{frage:"Wodurch unterscheidet sich op_07 vom reinen Monitoring?",
optionen:["Es ist rein reaktiv","Es ist die aktive Suche nach strukturellen Problemen","Es schließt Tickets","Es entscheidet über Go-Live"], richtig:1,
expl:"Proaktive Problem-Identifikation nutzt Trend-/Musteranalyse zur Früherkennung struktureller Probleme."}
]},
{ id:"sp_01", phase:"support", typ:"aktivitaet",
name:"Bereitstellen von Leitlinien für den Service-Support",
beschreibung:"Strukturelle Rahmenbedingungen, damit der Service-Support effizient arbeiten kann.",
umfasst:["Unterstützungsprozesse (Incident-/Request-Modell)","Vorgaben zu Eskalationen, Reaktionszeiten, Rollen","Tool-Konfigurationen (Ticketklassen, Templates)","Zugriffsmöglichkeiten & Security-Policies","Konsistentes Wissensmanagement"],
artefakt:"Support-Leitlinien",
raci:[["service_owner","A"],["support_manager","R"],["spm","C/I"]],
quiz:[
{frage:"Wer ist Responsible (R) für die Support-Leitlinien?",
optionen:["Support Manager","Service Owner","SPM","1st Level Agent"], richtig:0,
expl:"Der Support Manager führt aus und pflegt die Leitlinien; der Service Owner verantwortet die fachlichen Vorgaben (A)."}
]},
{ id:"sp_02", phase:"support", typ:"aktivitaet",
name:"Wissensdatenbank",
beschreibung:"Strukturierter Wissensspeicher für Support-Lösungen, Betriebsinfos, Workarounds und Standardanfragen.",
umfasst:["Lösungen zu Incidents speichern","Workarounds dokumentieren","FAQ, Standard-Requests, Anleitungen","Pflege & Aktualisierung","Zentrales Arbeitsmittel für 1st & 2nd Level"],
artefakt:"Gepflegte Wissensdatenbank",
raci:[["service_owner","A"],["service_support_team","R"],["problem_manager","C"]],
quiz:[
{frage:"Wer liefert Workarounds und Known Errors für die Wissensdatenbank (C)?",
optionen:["Problem Manager","Queue Koordinator","SPM","Testmanagement"], richtig:0,
expl:"Der Problem Manager liefert Workarounds/Known Errors; das Service-Support-Team pflegt die Inhalte (R)."}
]},
{ id:"sp_03", phase:"support", typ:"aktivitaet",
name:"Überwachen / Verteilung von Incidents und Service Requests",
beschreibung:"Koordination eingehender Störungen/Anfragen: Priorisierung, Routing, korrekte Aufnahme und Verteilung (Ticket-Queue-Koordination).",
umfasst:["Sichtung neuer Tickets","Automatisches oder manuelles Routing","Prioritäts- und Impact-Bewertung","SLA-Konformität sicherstellen","Eskalation bei Bedarf"],
artefakt:"Priorisierte/geroutete Tickets",
raci:[["support_manager","A"],["queue_koordinator","R"]],
quiz:[
{frage:"Wer übernimmt Sichtung und Routing der Tickets (R)?",
optionen:["Queue Koordinator","Support Manager","2nd Level Agent","Service Owner"], richtig:0,
expl:"Der Queue Koordinator erledigt die initiale Sichtung und das Routing; der Support Manager ist Accountable."}
]},
{ id:"sp_04", phase:"support", typ:"aktivitaet",
name:"Bearbeiten von Requests",
beschreibung:"Behandlung aller Standard-Serviceanfragen von Nutzern (z.B. Passwort, Berechtigungen, Informationen).",
umfasst:["Klassifizierung gemäß Request-Katalog","Prüfung von Berechtigungen","Erfüllung standardisierter Anfragen","Dokumentation der Erledigung"],
artefakt:"Erledigter Service Request",
raci:[["support_manager","A"],["first_level_agent","R"]],
quiz:[
{frage:"Wer bearbeitet einfache Standard-Requests (R)?",
optionen:["1st Level Agent","2nd Level Agent","Problem Manager","Betriebsteam"], richtig:0,
expl:"Der 1st Level Agent bearbeitet Standard-Requests gemäß Katalog; der Support Manager ist Accountable."}
]},
{ id:"sp_05", phase:"support", typ:"aktivitaet",
name:"Lösen von Incidents im 1st Level Support",
beschreibung:"Schnelle Lösung typischer Störungen mit bekanntem Wissen.",
umfasst:["Erstdiagnose","Nutzung der Wissensdatenbank","Anwenderunterstützung","Workarounds anwenden","Lösung dokumentieren","Entscheidung über Weiterleitung an 2nd Level"],
artefakt:"Gelöster Incident (1st Level)",
raci:[["support_manager","A"],["first_level_agent","R"]],
quiz:[
{frage:"Wer löst typische Incidents im 1st Level (R)?",
optionen:["1st Level Agent","2nd Level Agent","Problem Manager","Lieferant"], richtig:0,
expl:"Der 1st Level Agent löst mit bekanntem Wissen; reicht es nicht, erfolgt Weiterleitung an den 2nd Level."}
]},
{ id:"sp_06", phase:"support", typ:"aktivitaet",
name:"Lösen von Incidents im 2nd Level Support",
beschreibung:"Bearbeitung von Incidents, die im 1st Level nicht lösbar sind — tiefergehende Diagnostik und technische Lösung.",
umfasst:["Komplexe technische Diagnosen","Zusammenarbeit mit Betrieb, Fachverfahren, Lieferanten","Ggf. Einbezug von Spezialisten/Herstellern","Rückmeldung an 1st Level / Anwender","Lösung dokumentieren","Ungelöst → Problem Record"],
artefakt:"Gelöster Incident (2nd Level)",
raci:[["support_manager","A"],["second_level_agent","R"],["lieferant","C"],["service_owner","I"]],
quiz:[
{frage:"Wer übernimmt Incidents, die der 1st Level nicht lösen kann (R)?",
optionen:["2nd Level Agent","Queue Koordinator","SPM","Service Owner"], richtig:0,
expl:"Der 2nd Level Agent macht die tiefergehende Diagnose; bleibt es ungelöst, entsteht ein Problem Record (sp_09)."}
]},
{ id:"sp_07", phase:"support", typ:"aktivitaet",
name:"Incident Record (Gelöst) / Request Record (Gelöst)",
beschreibung:"Abschluss der Ticketbearbeitung inkl. Dokumentation und Qualitätssicherung.",
umfasst:["Validierung, ob wirklich gelöst","Kommunikation an den Endnutzer","Dokumentationen / FAQs aktualisieren","Klassifikation für Trendanalysen","Übergabe an Schließen von Incidents"],
artefakt:"Incident/Request Record (gelöst)",
raci:[["support_manager","A"],["first_level_agent","R"],["second_level_agent","R"]],
quiz:[
{frage:"Was passiert in sp_07?",
optionen:["Eröffnen eines Problem Records","Abschluss der Bearbeitung inkl. Validierung und Doku","Routing der Tickets","Root-Cause-Analyse"], richtig:1,
expl:"sp_07 validiert die Lösung, kommuniziert an den Nutzer und bereitet den finalen Abschluss (sp_08) vor."}
]},
{ id:"sp_08", phase:"support", typ:"aktivitaet",
name:"Schließen von Incidents & Service Requests",
beschreibung:"Finaler Ticketabschluss inkl. Dokumentation, Reporting und Einordnung für spätere Verbesserungen.",
umfasst:["Ticket final schließen","Prüfung vollständiger Dokumentation","Ticketklassifizierung / KPI-Zuordnung","Rückmeldung an Monitoring/Reporting","Wiederkehrende Incidents identifizieren"],
artefakt:"Geschlossenes Ticket + KPI-Zuordnung",
raci:[["support_manager","A"],["first_level_agent","R"],["second_level_agent","R"]],
quiz:[
{frage:"Wer ist Accountable für das Schließen von Incidents und Requests?",
optionen:["Support Manager","1st Level Agent","Problem Manager","SOR"], richtig:0,
expl:"Der Support Manager ist Accountable; 1st und 2nd Level sind Responsible für den finalen Abschluss."}
]},
{ id:"sp_09", phase:"support", typ:"aktivitaet",
name:"Anlegen Problem Record für nicht lösbare Incidents",
beschreibung:"Ist ein Incident auch im 2nd Level nicht lösbar, wird eine strukturelle Ursache vermutet.",
umfasst:["Nicht-lösbar-Entscheidung","Dokumentation der Symptome","Eröffnen eines Problem Records","Übergabe an Root-Cause-Analyse"],
artefakt:"Problem Record",
raci:[["problem_manager","A"],["second_level_agent","R"],["service_owner","I"]],
quiz:[
{frage:"Welches Artefakt entsteht, wenn ein Incident nicht lösbar ist?",
optionen:["Workaround","Problem Record","Service-Definition","Testprotokoll"], richtig:1,
expl:"sp_09 eröffnet einen Problem Record; der Problem Manager ist Accountable, der 2nd Level Agent Responsible."}
]},
{ id:"sp_10", phase:"support", typ:"aktivitaet",
name:"Wiederkehrende Incidents analysieren & Problem Record anlegen",
beschreibung:"Reaktiver Weg zur strukturierten Problemerkennung, wenn dieselbe Störung mehrfach auftritt.",
umfasst:["Clustering wiederkehrender Incidents","Analyse gemeinsamer Ursachen","Entscheidung: Problem Record notwendig","Übergabe an Problem Management"],
artefakt:"Problem Record (aus wiederkehrenden Incidents)",
raci:[["problem_manager","A"],["support_manager","R"],["second_level_agent","C"],["service_owner","I"]],
quiz:[
{frage:"Wann wird in sp_10 ein Problem Record angelegt?",
optionen:["Bei jedem Incident","Wenn dieselbe Störung mehrfach auftritt","Nur an Gates","Bei Go-Live"], richtig:1,
expl:"sp_10 clustert wiederkehrende Incidents und legt bei gemeinsamer Ursache einen Problem Record an."}
]},
{ id:"sp_11", phase:"support", typ:"aktivitaet",
name:"Operative Root-Cause-Analyse durchführen & Workaround bereitstellen",
beschreibung:"Start der Problembehandlung zur Ermittlung der Ursache und Schaffung eines Workarounds.",
umfasst:["Root-Cause-Analyse","Erstellen eines Workarounds","Eintrag in die Wissensdatenbank","Entscheidung über Change-Bedarf"],
artefakt:"Workaround + aktualisierter Problem Record",
raci:[["service_owner","A"],["problem_manager","R"],["second_level_agent","C"],["lieferant","C/I"]],
quiz:[
{frage:"Was wird in der operativen Root-Cause-Analyse zusätzlich bereitgestellt?",
optionen:["Ein Workaround","Ein Betriebshandbuch","Eine Gate-Vorlage","Ein Review-Bericht"], richtig:0,
expl:"sp_11 ermittelt die Ursache, erstellt einen Workaround und entscheidet über Change-Bedarf (aktualisiert den Problem Record)."}
]},
{ id:"rv_01", phase:"review", typ:"aktivitaet",
name:"Taktische Root-Cause-Analyse + Auswertung Support-KPIs",
beschreibung:"Strukturelle Analyse wiederkehrender Supportfälle und KPIs, um mittelfristige Verbesserungen anzustoßen.",
umfasst:["KPI-Auswertung (Supportqualität, Trends)","Wiederkehrende Tickets und systemische Ursachen","Abgleich mit Problem Records","Ableitung taktischer Verbesserungsmaßnahmen"],
artefakt:"Taktische Verbesserungsmaßnahmen",
raci:[["service_owner","A"],["problem_manager","R"]],
quiz:[
{frage:"Wer führt die taktische Root-Cause-Analyse durch (R)?",
optionen:["Problem Manager","Service Owner","SOR","1st Level Agent"], richtig:0,
expl:"Der Problem Manager führt die taktische Analyse durch; der Service Owner priorisiert und bestätigt Maßnahmen (A)."}
]},
{ id:"rv_02", phase:"review", typ:"aktivitaet",
name:"Service Performance & Improvement Review",
beschreibung:"Operativer Review des Servicezustands anhand Qualitätsberichten, Supportdaten und Problem-Analysen. Methodik: 4-Dimensionen-Modell mit Ampelbewertung.",
umfasst:["Leistung gegen Ziele und SLAs abgleichen","Gesamterfüllungsgrad bewerten","Operative Risiken, Störungen, Engpässe diskutieren","Optimierungsmaßnahmen identifizieren","Vorbereitung für SOR-Review","Handlungsempfehlung: CONTINUE / IMPROVEMENT / REDESIGN / RETIRE"],
artefakt:"Service Performance & Improvement Review (Ampelbewertung)",
raci:[["service_owner","A/R"],["spm","I"]],
quiz:[
{frage:"Welche Bewertungsskala nutzt das Service Performance & Improvement Review?",
optionen:["110 Punkte","Grün / Gelb / Rot","Pass / Fail","A / B / C"], richtig:1,
expl:"Das 4-Dimensionen-Modell (Leistung, Stabilität, Zufriedenheit, Zukunftsfähigkeit) wird per Ampel (Grün/Gelb/Rot) bewertet."}
]},
{ id:"rv_03", phase:"review", typ:"aktivitaet",
name:"SOR: Periodischer Service Review",
beschreibung:"Die SOR bewertet regelmäßig die Serviceleistung und trifft operative Entscheidungen. Verzweigt in vier Wege.",
umfasst:["Sichtung des Service Performance & Improvement Reviews","Bewertung von Stabilität & Betriebsreife","Priorisierung operativer Verbesserungen","Freigabe kleinerer Maßnahmen","Schnittstelle zur Demand-/Portfolio-Steuerung"],
artefakt:"SOR-Entscheidung (Review)",
pfade:[
["Weiter wie bisher","Service stabil, keine Maßnahmen → Operation"],
["Verbesserung nötig (klein)","→ rv_04 Service Improvement"],
["Strukturelle Überarbeitung","→ rv_05 Redesign / Erweiterung"],
["Service End-of-Life","→ rv_06 Außerbetriebnahme"]
],
raci:[["sor","A"],["service_owner","R"]],
quiz:[
{frage:"Wer trifft im periodischen Service Review die Entscheidung (A)?",
optionen:["Service Owner","SOR","SPM","Problem Manager"], richtig:1,
expl:"Die SOR entscheidet; der Service Owner berichtet und präsentiert (R)."},
{frage:"Welche Wege kann rv_03 eröffnen?",
optionen:["Nur Weiter oder Stop","Weiter / Improvement / Redesign / Retirement","Go / No-Go","Intern / Extern"], richtig:1,
expl:"Weiter wie bisher, kleine Verbesserung (rv_04), Redesign (rv_05) oder Außerbetriebnahme (rv_06)."}
]},
{ id:"rv_04", phase:"review", typ:"aktivitaet",
name:"Service Improvement",
beschreibung:"SOR-Entscheidung: Planung und Steuerung kleinerer Verbesserungsmaßnahmen innerhalb des Service.",
umfasst:["Probleme in Verbesserungs-Initiativen umwandeln","Aufwandsschätzung und Priorisierung","Zielmetriken festlegen","Verantwortlichkeiten zuweisen","Sichtbarkeit im Service-Portfolio"],
artefakt:"Verbesserungs-Initiative (zurück in Operation)",
raci:[["service_owner","A/R"],["spm","C"],["problem_manager","C"]],
quiz:[
{frage:"Wohin fließen die Verbesserungsmaßnahmen aus rv_04 zurück?",
optionen:["In die Operation (laufender Betrieb)","In die Design-Phase","In den Demand-Lifecycle","An Gate 1"], richtig:0,
expl:"Service Improvement setzt kleinere Maßnahmen im laufenden Betrieb (Operation) um."}
]},
{ id:"rv_05", phase:"review", typ:"aktivitaet",
name:"Service Redesign / Erweiterung",
beschreibung:"SOR-Entscheidung: strukturelle Weiterentwicklung des Service. Übergabe an den Demand-Lifecycle.",
umfasst:["Überarbeitung der Service-Definitionen","Anpassung von Komponenten, Prozessen, Rollen","Anforderungen für Entwicklungsprozess identifizieren","Übergabe an Demand-Lifecycle-Prozess"],
artefakt:"Neuer Demand (Redesign) → DPM",
raci:[["service_owner","A"],["spm","C"]],
quiz:[
{frage:"Wohin übergibt rv_05 (Redesign/Erweiterung)?",
optionen:["An die Operation","An den Demand-Lifecycle (DPM) als neuer Demand","An den Support","An Gate 2"], richtig:1,
expl:"Strukturelle Änderungen werden als neuer Demand erfasst und durchlaufen den Demand-Lifecycle."}
]},
{ id:"rv_06", phase:"review", typ:"aktivitaet",
name:"Service außer Betrieb nehmen",
beschreibung:"Strukturierte Entscheidung und kontrollierte Abschaltung eines Services.",
umfasst:["Auslöser definieren (Nutzung, Kosten, Überalterung)","Bewertung durch SOR","Retirement-Plan erstellen (Kommunikation, Migration, Abschaltung)","Koordination mit Portfolio (SPM)","Übergabe an Projekt-/Transitionsthemen"],
artefakt:"Retirement-Plan / Decommissioning-Auftrag → DPM",
raci:[["sor","A"],["service_owner","R"],["spm","C"]],
quiz:[
{frage:"Wer entscheidet über die Außerbetriebnahme eines Services (A)?",
optionen:["Service Owner","SOR","SPM","Support Manager"], richtig:1,
expl:"Die SOR entscheidet über die Stilllegung; der Service Owner führt den Retirement-Plan aus (R)."}
]}
];
/* ====================== STATE ====================== */
const LS_KEY = "slc-companion-proto";
function defaultState(){
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();
function load(){ try{ return Object.assign(defaultState(), JSON.parse(localStorage.getItem(LS_KEY)||"{}")); }catch(e){ return defaultState(); } }
function save(){ localStorage.setItem(LS_KEY, JSON.stringify(S)); }
/* ====================== HELPERS ====================== */
const $ = s => document.querySelector(s);
const cur = () => STATIONEN[S.index];
function pkey(sid, qi){ return sid+"#"+qi; }
function roleLabel(id){ return ROLLEN[id] || id; }
function acard(si, ci){ return USE_CASES[si].changes[ci]; } // {titel, text}
function cardHtml(si, ci){ const c = acard(si, ci);
return `<div style="font-weight:800;font-size:18px;color:var(--ink);margin-bottom:6px">${c.titel}</div>`
+ `<div>${c.text}</div>`
+ `<div style="color:var(--muted);font-style:italic;margin-top:8px">Was passiert an welchen Stellen?</div>`; }
/* ====================== RENDER: SIDEBAR ====================== */
function renderList(){
const groups = {};
STATIONEN.forEach((st,i)=>{ (groups[st.phase]=groups[st.phase]||[]).push({st,i}); });
let html = "";
for(const ph in PHASEN){
if(!groups[ph]) continue;
html += `<h3>${PHASEN[ph].label}</h3>`;
groups[ph].forEach(({st,i})=>{
const cls = [ "stationItem", i===S.index?"active":"", S.done[st.id]?"done":"" ].join(" ");
html += `<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.typ==="gate"?"⛩ ":""}${st.name.length>34?st.name.slice(0,32)+"…":st.name}</span>
${S.unclear[st.id]?'<span class="flag">●</span>':''}
</div>`;
});
}
$("#stationList").innerHTML = html;
$("#stationList").querySelectorAll(".stationItem").forEach(el=>{
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);
$("#progressBar").style.width = pct+"%";
}
/* ====================== VIEW DISPATCH ====================== */
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" || 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>`;
}
/* ---------- 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 liegt an der aktuellen Station und wandert mit 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">${cardHtml(S.service,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>
<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("");
svc.value=S.service; ch.value=S.change;
const refresh=()=>{ $("#cardTrigger").innerHTML=cardHtml(S.service,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(); };
$("#tourBtn").onclick=()=>{ S.view="tour"; S.tourIndex=0; save(); draw(); };
$("#toStart").onclick=()=>{ S.view="start"; S.startRevealed=false; save(); draw(); };
}
/* ---------- 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)"><b>${acard(TOUR.service,TOUR.change).titel}</b> — ${acard(TOUR.service,TOUR.change).text}</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="";
for(const ph in PHASEN){
if(!groups[ph]) continue;
list += `<div class="pickerPhase">${PHASEN[ph].label}</div>`;
groups[ph].forEach(({st,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>
${st.typ==="gate"?`<span class="gate">Gate ${st.gateNr}</span>`:''}
</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>
${head}
<div class="pickerList">${list}</div>
<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) ====================== */
function renderRun(){
renderList();
const st = cur();
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>`;
let body = `
${chip}
<div class="stationName">${st.name}</div>
<div class="stationId">${st.id}</div>
<div class="token">Action Card: <b>${USE_CASES[S.service].service}</b>
<span class="ctChip">${CHANGE_TYPES[S.change]}</span>
<div class="ctText"><b>${acard(S.service,S.change).titel}</b> — ${acard(S.service,S.change).text}</div>
</div>
`;
if(S.stage==="discuss") body += renderDiscuss(st);
else if(S.stage==="quiz") body += renderQuiz(st);
else body += renderReveal(st);
$("#panel").innerHTML = body;
wire(st);
}
function renderDiscuss(st){
return `
<div class="step">
<div class="stepHead"><span class="n">1</span> Diskussion · App noch zu</div>
<div class="discuss">
<strong>Besprecht in der Gruppe — anhand der Kurzbezeichnung:</strong>
<ul>
<li>Was passiert hier konkret für euer Szenario?</li>
<li>Wer macht es? Steckt die Rollen-Figuren ins <b>Aktiv-Feld (R/A/C/I)</b>.</li>
<li>Welches Artefakt entsteht?</li>
</ul>
</div>
</div>
<div class="actions">
<div class="spacer"></div>
<button class="primary" id="toQuiz">Quiz starten →</button>
</div>`;
}
function renderQuiz(st){
const qi = S.quizIndex, q = st.quiz[qi];
const picked = S.picks[pkey(st.id,qi)];
const answered = picked !== undefined;
const opts = q.optionen.map((o,i)=>{
let cls = "opt";
if(answered){
if(i===q.richtig) cls+=" correct";
else if(i===picked) cls+=" wrong";
} else if(i===picked) cls+=" sel";
const mark = answered ? (i===q.richtig?'<span class="mark" style="color:var(--ok)">✓</span>': (i===picked?'<span class="mark" style="color:var(--bad)">✗</span>':'')) : '';
return `<button class="${cls}" data-opt="${i}" ${answered?'disabled':''}>${o}${mark}</button>`;
}).join("");
return `
<div class="step">
<div class="stepHead"><span class="n">2</span> Quiz · Frage ${qi+1} / ${st.quiz.length}</div>
<div class="q">
<div class="frage">${q.frage}</div>
<div class="opts">${opts}</div>
${answered ? `<div class="qExpl">${q.expl}</div>` : ``}
</div>
</div>
<div class="actions">
<button class="ghost" id="backDiscuss">← Diskussion</button>
<div class="spacer"></div>
${answered
? (qi < st.quiz.length-1
? `<button class="primary" id="nextQ">Nächste Frage →</button>`
: `<button class="primary" id="toReveal">Auflösung anzeigen →</button>`)
: `<button class="primary" disabled>Antwort wählen…</button>`}
</div>`;
}
function renderReveal(st){
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>`;
}
if(st.pruef){
extra += `<h4>Prüf-Dimensionen</h4><ul>` + st.pruef.map(([n,d])=>`<li><b>${n}</b> — ${d}</li>`).join("") + `</ul>`;
}
return `
<div class="step reveal">
<div class="stepHead"><span class="n">3</span> Auflösung & Reflexion</div>
<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">
<label class="unclear"><input type="checkbox" id="unclear" ${S.unclear[st.id]?'checked':''}/> War unklar</label>
<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>`;
}
/* ====================== WIRING ====================== */
function wire(st){
const b = id => $("#"+id);
if(b("toQuiz")) b("toQuiz").onclick = ()=>{ S.stage="quiz"; S.quizIndex=0; save(); draw(); };
if(b("backDiscuss")) b("backDiscuss").onclick = ()=>{ S.stage="discuss"; save(); draw(); };
$("#panel").querySelectorAll(".opt[data-opt]").forEach(el=>{
el.onclick = ()=>{ S.picks[pkey(st.id,S.quizIndex)] = +el.dataset.opt; save(); draw(); };
});
if(b("nextQ")) b("nextQ").onclick = ()=>{ S.quizIndex++; save(); draw(); };
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("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(); };
}
/* ====================== DEBRIEF ====================== */
function quizScore(){
let correct=0, total=0;
STATIONEN.forEach(st=> st.quiz.forEach((q,qi)=>{
const p = S.picks[pkey(st.id,qi)];
if(p!==undefined){ total++; if(p===q.richtig) correct++; }
}));
return {correct,total};
}
let DEBRIEF = { md:"", json:null };
function download(name, text, mime){
const blob = new Blob([text], {type:mime});
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url; a.download = name; document.body.appendChild(a); a.click();
a.remove(); setTimeout(()=>URL.revokeObjectURL(url), 1000);
}
function openDebrief(){
const {correct,total} = quizScore();
const doneN = Object.keys(S.done).length;
const unclearList = STATIONEN.filter(st=>S.unclear[st.id]);
$("#debriefStat").innerHTML = `
<div><b>${doneN}/${STATIONEN.length}</b><span>Stationen</span></div>
<div><b>${total?Math.round(correct/total*100):0}%</b><span>Quiz richtig (${correct}/${total})</span></div>
<div><b>${unclearList.length}</b><span>als unklar markiert</span></div>`;
let md = `# SLC-Workshop — Debrief\n\n`;
md += `**Service:** ${USE_CASES[S.service].service}\n`;
md += `**Change-Typ:** ${CHANGE_TYPES[S.change]}\n`;
md += `**Action Card:** „${acard(S.service,S.change).titel}“ — ${acard(S.service,S.change).text}\n`;
md += `**Stationen bearbeitet:** ${doneN}/${STATIONEN.length}\n`;
md += `**Quiz:** ${correct}/${total} richtig\n\n`;
md += `## Als unklar markiert\n`;
md += unclearList.length ? unclearList.map(st=>`- ${st.id}${st.name}`).join("\n") : "_keine_";
md += `\n\n## Quiz-Detail\n`;
STATIONEN.forEach(st=> st.quiz.forEach((q,qi)=>{
const p = S.picks[pkey(st.id,qi)];
if(p===undefined) return;
md += `- [${p===q.richtig?"✓":"✗"}] ${st.id}: ${q.frage} → „${q.optionen[p]}"\n`;
}));
const stamp = new Date().toISOString().slice(0,16).replace("T"," ");
const json = {
erzeugt: stamp,
service: USE_CASES[S.service].service,
changeTyp: CHANGE_TYPES[S.change],
actionCard: acard(S.service,S.change).titel,
ausloeser: acard(S.service,S.change).text,
stationenBearbeitet: doneN,
stationenGesamt: STATIONEN.length,
quiz: { richtig: correct, gesamt: total },
unklar: unclearList.map(st=>({id:st.id, name:st.name})),
quizDetail: []
};
STATIONEN.forEach(st=> st.quiz.forEach((q,qi)=>{
const p = S.picks[pkey(st.id,qi)];
if(p===undefined) return;
json.quizDetail.push({station:st.id, frage:q.frage, gewaehlt:q.optionen[p], richtig:p===q.richtig});
}));
DEBRIEF = { md, json };
$("#debriefText").textContent = md;
$("#debriefDlg").showModal();
}
/* ====================== INIT ====================== */
(function init(){
$("#debriefBtn").onclick = openDebrief;
$("#closeDebrief").onclick = ()=> $("#debriefDlg").close();
$("#copyBtn").onclick = ()=> navigator.clipboard?.writeText($("#debriefText").textContent);
$("#dlMd").onclick = ()=> download("slc-debrief.md", DEBRIEF.md, "text/markdown");
$("#dlJson").onclick = ()=> download("slc-debrief.json", JSON.stringify(DEBRIEF.json, null, 2), "application/json");
$("#resetBtn").onclick = ()=>{ if(confirm("Neue Action Card ziehen und Durchlauf zurücksetzen?")){ S=defaultState(); save(); draw(); } };
draw();
})();
// PWA: Service Worker fuer Offline-/Kiosk-Betrieb (nur ueber http/https aktiv)
if ("serviceWorker" in navigator) {
window.addEventListener("load", ()=> navigator.serviceWorker.register("sw.js").catch(()=>{}));
}
</script>
</body>
</html>