document.addEventListener("DOMContentLoaded", function () {
// === KONFIGURATION ===
const JSON_URL = "/assets/baumdiagramm.json"; // Pfad zu deiner Excel->JSON
const MOUNT_ID = "qa-tool";
// === STYLES (zentriert, max 800px) ===
const css = `
.qa-wrap{width:100%;max-width:800px;margin:0 auto;padding:16px;box-sizing:border-box;font-family:system-ui,-apple-system,Segoe UI,Roboto,Arial,sans-serif}
.qa-card{border:0;border-radius:16px;box-shadow:0 4px 12px rgba(0,0,0,0.06);padding:16px;background:#fff}
.qa-title{font-size:20px;font-weight:600;margin:0 0 6px}
.qa-sub{font-size:13px;color:#666;margin:0 0 14px}
.qa-breadcrumb{font-size:13px;color:#666;margin:0 0 10px}
.qa-breadcrumb a{color:inherit;text-decoration:underline;text-underline-offset:2px;cursor:pointer}
.qa-list{display:grid;gap:8px;margin:0;padding:0;list-style:none}
.qa-btn{display:flex;justify-content:space-between;align-items:center;width:100%;text-align:left;padding:12px 14px;border-radius:12px;border:1px solid #e5e7eb;background:#fff;cursor:pointer}
.qa-btn:hover{background:#f8fafc}
.qa-answer h3{margin:10px 0 8px;font-size:16px}
.qa-answer p{margin:0 0 8px;line-height:1.5}
.qa-actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}
.qa-secondary,.qa-ghost{border-radius:10px;padding:10px 12px;font-size:14px;cursor:pointer}
.qa-secondary{background:#f1f5f9;border:1px solid #e2e8f0}
.qa-ghost{background:transparent;border:1px solid transparent}
.qa-search{display:flex;gap:8px;margin:0 0 12px}
.qa-input{flex:1;padding:10px 12px;border-radius:10px;border:1px solid #e5e7eb}
.qa-copy{font-size:12px;color:#666;margin-top:6px}
`;
const style = document.createElement("style");
style.textContent = css;
document.head.appendChild(style);
// === Hilfsfunktionen ===
function buildTreeFromSheets(json) {
const sheets = json && json.sheets ? json.sheets : {};
const firstName = Object.keys(sheets)[0];
const rows = (firstName && Array.isArray(sheets[firstName])) ? sheets[firstName] : [];
const matrix = rows.map(obj => {
const vals = Object.values(obj || {});
return vals.map(v => v == null ? "" : String(v).trim()).filter(s => s !== "");
}).filter(arr => arr.length > 0);
return buildTreeFromPaths(matrix);
}
function buildTreeFromPaths(rows) {
const root = { id: "root", label: "Start", children: [] };
const indexByPath = new Map(); indexByPath.set("root", root);
const keyOf = (arr) => arr.join(" > ");
rows.forEach((labels, rIdx) => {
let parent = root; let pathParts = ["root"];
for (let i = 0; i < labels.length; i++) {
const lab = labels[i]; const isLast = i === labels.length - 1;
const key = keyOf([...pathParts, lab]);
let node = indexByPath.get(key);
if (!node) {
node = { id: key + "#" + rIdx + "_" + i, label: lab };
(parent.children || (parent.children = [])).push(node);
indexByPath.set(key, node);
}
if (isLast) {
const looksLikeAnswer = /[.!?]/.test(lab) || lab.split(" ").length > 8;
if (looksLikeAnswer) node.answer = lab;
}
parent = node; pathParts.push(lab);
}
});
return root;
}
function isLeaf(n){ return !!n.answer && !n.children; }
function childrenOf(n){ return Array.isArray(n.children) ? n.children : []; }
// === DOM aufbauen ===
const mount = document.getElementById(MOUNT_ID);
if (!mount) return;
const wrap = document.createElement("div"); wrap.className = "qa-wrap"; mount.appendChild(wrap);
const card = document.createElement("div"); card.className = "qa-card"; wrap.appendChild(card);
const title = document.createElement("h2"); title.className = "qa-title"; title.textContent = "Fragen & Antworten"; card.appendChild(title);
const sub = document.createElement("p"); sub.className = "qa-sub"; sub.textContent = "Wählen Sie Schritt für Schritt aus oder nutzen Sie die Suche."; card.appendChild(sub);
// Suche
const searchForm = document.createElement("form"); searchForm.className = "qa-search";
const input = document.createElement("input"); input.className = "qa-input"; input.placeholder = "Suchen…";
const searchBtn = document.createElement("button"); searchBtn.type = "submit"; searchBtn.className = "qa-secondary"; searchBtn.textContent = "Suchen";
searchForm.appendChild(input); searchForm.appendChild(searchBtn); card.appendChild(searchForm);
const breadcrumb = document.createElement("div"); breadcrumb.className = "qa-breadcrumb"; card.appendChild(breadcrumb);
const list = document.createElement("ul"); list.className = "qa-list"; card.appendChild(list);
const answerBox = document.createElement("div"); answerBox.className = "qa-answer"; card.appendChild(answerBox);
const actions = document.createElement("div"); actions.className = "qa-actions"; card.appendChild(actions);
const backBtn = document.createElement("button"); backBtn.className = "qa-secondary"; backBtn.textContent = "Zurück"; backBtn.disabled = true;
const restartBtn = document.createElement("button"); restartBtn.className = "qa-ghost"; restartBtn.textContent = "Neu starten";
actions.appendChild(backBtn); actions.appendChild(restartBtn);
// Kontakt-iframe programmatisch erzeugen (nicht über innerHTML – hilft gegen Sanitizer)
function addContactIframe(target) {
const outer = document.createElement("div");
outer.style.width = "100%";
outer.style.display = "flex";
outer.style.justifyContent = "center";
outer.style.alignItems = "center";
outer.style.margin = "0 auto";
const iframe = document.createElement("iframe");
iframe.src = "https://analytics-eu.clickdimensions.com/wwwdnhkorg-aj4kw/pages/c9n27oy3eeubugbqvoa6bw.html?PageId=ec76d30b37e6eb11815200505686ba6f";
iframe.allowTransparency = true;
iframe.width = "100%";
iframe.height = "1100px";
iframe.frameBorder = "0";
iframe.style.border = "0";
iframe.style.maxWidth = "800px";
outer.appendChild(iframe);
target.appendChild(outer);
}
// State
let tree = null, path = [];
function renderBreadcrumb() {
breadcrumb.innerHTML = "";
const span = document.createElement("span"); span.textContent = "Pfad: "; breadcrumb.appendChild(span);
path.forEach((n, i) => {
if (i > 0) breadcrumb.appendChild(document.createTextNode(" / "));
const a = document.createElement("a"); a.textContent = n.label || "Start";
a.onclick = () => { path = path.slice(0, i + 1); render(); };
breadcrumb.appendChild(a);
});
}
function setContactAuto(n) {
const label = (n && n.label ? String(n.label) : "").toLowerCase();
if (label.includes("registrierungspflicht")) addContactIframe(answerBox);
}
function renderNode(n) {
list.innerHTML = ""; answerBox.innerHTML = "";
if (!isLeaf(n)) {
childrenOf(n).forEach(c => {
const li = document.createElement("li");
const btn = document.createElement("button"); btn.className = "qa-btn"; btn.textContent = c.label;
btn.onclick = () => { path.push(c); render(); };
li.appendChild(btn); list.appendChild(li);
});
setContactAuto(n);
} else {
const h3 = document.createElement("h3"); h3.textContent = "Antwort";
const p = document.createElement("p"); p.textContent = n.answer || "";
answerBox.appendChild(h3); answerBox.appendChild(p);
const copyNote = document.createElement("div"); copyNote.className = "qa-copy";
const copyBtn = document.createElement("button"); copyBtn.className = "qa-secondary"; copyBtn.textContent = "Antwort kopieren";
copyBtn.onclick = () => { navigator.clipboard.writeText(n.answer || ""); copyNote.textContent = "Antwort kopiert."; };
actions.insertBefore(copyBtn, actions.firstChild);
setTimeout(() => { try { actions.removeChild(copyBtn); } catch(e){} }, 0); // einmalig
answerBox.appendChild(copyNote);
setContactAuto(n);
}
}
function render(){ backBtn.disabled = path.length <= 1; renderBreadcrumb(); renderNode(path[path.length - 1]); }
backBtn.onclick = () => { if (path.length > 1) { path.pop(); render(); } };
restartBtn.onclick = () => { path = [tree]; render(); };
searchForm.onsubmit = (e) => {
e.preventDefault();
const t = (input.value || "").trim().toLowerCase();
if (!t) return;
const results = [];
(function dfs(n, p){
const inLabel = (n.label || "").toLowerCase().includes(t);
const inAns = (n.answer || "").toLowerCase().includes(t);
if (inLabel || inAns) results.push([...p, n]);
(n.children || []).forEach(c => dfs(c, [...p, n]));
})(tree, []);
if (results.length) { path = results[0]; render(); }
else { alert("Kein Treffer."); }
};
// Daten laden & starten
fetch(JSON_URL, { cache: "no-store" })
.then(r => r.json())
.then(json => { tree = buildTreeFromSheets(json); path = [tree]; render(); })
.catch(err => {
console.error("Fehler beim Laden der JSON:", err);
card.innerHTML = "
Die Inhalte konnten nicht geladen werden. Prüfe bitte den JSON-Pfad.
"; }); });