/* ITSM Frontend-Logik -- CSP-konform: kein Inline-JS, keine onclick-Attribute. Alle zustandsaendernden Requests senden das CSRF-Token als Header. */ (function () { "use strict"; var CSRF = (document.querySelector('meta[name="csrf"]') || {}).content || ""; function esc(s) { return String(s == null ? "" : s) .replace(/&/g, "&").replace(//g, ">") .replace(/"/g, """).replace(/'/g, "'"); } function post(url, params) { var body = new URLSearchParams(params || {}).toString(); return fetch(url, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", "X-CSRF-Token": CSRF }, body: body }).then(function (r) { return r.json().catch(function () { return { ok: r.ok }; }); }); } // ── Zeilen-Navigation + Ticket-Detail ──────────────────────────────────────── document.addEventListener("click", function (ev) { var row = ev.target.closest("tr.row"); if (!row) return; if (ev.target.closest("a, button, form, select, input")) return; if (row.dataset.ticketId) openDetail(parseInt(row.dataset.ticketId, 10)); else if (row.dataset.href) location.href = row.dataset.href; }); // Formulare mit Bestaetigung (z. B. Loeschen) und Auto-Submit-Selects. document.addEventListener("submit", function (ev) { var f = ev.target; if (f.dataset && f.dataset.confirm && !window.confirm(f.dataset.confirm)) { ev.preventDefault(); } }); document.addEventListener("change", function (ev) { if (ev.target.matches("select[data-autosubmit]")) ev.target.form.submit(); if (ev.target.id === "new-kategorie") { var row = document.getElementById("change-typ-row"); if (row) row.hidden = ev.target.value !== "Change"; } }); var overlay = document.getElementById("detail-overlay"); var panel = document.getElementById("detail-panel"); if (overlay) overlay.addEventListener("click", closeDetail); function openDetail(id) { fetch("/api/tickets/" + id, { headers: { "Accept": "application/json" } }) .then(function (r) { return r.json(); }) .then(function (t) { if (!t || t.ok === false) return; panel.innerHTML = renderDetail(t); bindDetail(t); panel.classList.add("open"); overlay.classList.add("open"); }); } function closeDetail() { panel.classList.remove("open"); overlay.classList.remove("open"); } function pill(text) { return '' + esc(text) + " "; } function renderDetail(t) { var h = ""; h += "

" + esc(t.ticket_nr) + "

"; h += '
' + esc(t.titel) + "
"; h += "
" + pill(t.status) + pill(t.prioritaet) + pill(t.kategorie); h += pill("Impact: " + t.impact) + pill("Urgency: " + t.urgency); if (t.kategorie === "Change" && t.change_typ) h += pill(t.change_typ) + pill("Freigabe: " + (t.approval_status || "-")); if (t.known_error) h += pill("Known Error"); h += "
"; h += '
Beschreibung
' + esc(t.beschreibung || "") + "
"; h += '
Service
' + esc(t.service_name || "(kein Service)") + "
"; if (t.sla) { h += '
SLA
'; h += "Antwort bis: " + esc(t.sla.response_due || "--"); h += " · Loesung bis: " + esc(t.sla.resolve_due || "--"); if (t.sla.overdue) h += ' · Ueberfaellig'; h += "
"; } if (t.problem_nr) { h += '
Problem
Verknuepft mit ' + esc(t.problem_nr) + "
"; } if (t.operative && t.allowed_next && t.allowed_next.length) { h += '
Status aendern (erlaubte ITIL-Uebergaenge)
'; t.allowed_next.forEach(function (s) { h += '"; }); h += "
"; } if (t.can_approve) { h += '
Change-Freigabe (CAB)
'; h += ''; h += '
'; } if (t.operative && t.kategorie === "Problem") { h += '
Problem Management
'; h += '
"; } if (t.operative && t.kategorie !== "Problem" && t.problems && t.problems.length) { h += '
Mit Problem verknuepfen
'; h += '
'; } if (t.operative) { h += '
Configuration Items (CMDB)
'; (t.cis || []).forEach(function (c) { h += '' + esc(c.name) + ' '; }); if (t.cis_available && t.cis_available.length) { h += '
'; } h += "
"; } if (t.can_repo_edit) { h += '
Repo-Datei bearbeiten (Forge, dokumentationspflichtig)
'; h += '
'; h += '
'; h += '
'; h += '
'; h += ''; h += '
'; h += ''; h += ''; h += ''; h += ''; h += "
"; } else if (t.operative && t.kategorie === "Change") { h += '
Repo-Bearbeitung: erfordert Rolle Change Manager/Administrator und einen freigegebenen Change.
'; } h += '
Kommentar (Worklog)
'; h += ''; h += '
'; h += '
Zeitleiste
'; var tl = (t.timeline || []).map(function (e) { return '
' + esc(e.akteur || "") + '
' + esc(e.zeit) + "
" + esc(e.text || "") + "
"; }).join("") || '
Keine Ereignisse.
'; h += tl; h += '
'; return h; } function bindDetail(t) { var id = t.id; panel.querySelectorAll("[data-status]").forEach(function (b) { b.addEventListener("click", function () { post("/tickets/" + id + "/status", { status: b.dataset.status }).then(function (d) { if (d.ok) openDetail(id); else alert(d.error || "Fehler"); }); }); }); panel.querySelectorAll("[data-approve]").forEach(function (b) { b.addEventListener("click", function () { post("/tickets/" + id + "/approval", { decision: b.dataset.approve }).then(function (d) { if (d.ok) openDetail(id); else alert(d.error || "Fehler"); }); }); }); panel.querySelectorAll("[data-known-error]").forEach(function (b) { b.addEventListener("click", function () { post("/tickets/" + id + "/known-error", { known_error: b.dataset.knownError }).then(function (d) { if (d.ok) openDetail(id); else alert(d.error || "Fehler"); }); }); }); panel.querySelectorAll("[data-ci-unlink]").forEach(function (b) { b.addEventListener("click", function () { post("/tickets/" + id + "/ci-unlink", { ci_id: b.dataset.ciUnlink }).then(function () { openDetail(id); }); }); }); var ciAdd = panel.querySelector("#dt-ci-add"); if (ciAdd) ciAdd.addEventListener("click", function () { post("/tickets/" + id + "/ci-link", { ci_id: panel.querySelector("#dt-ci").value }) .then(function () { openDetail(id); }); }); var probSave = panel.querySelector("#dt-problem-save"); if (probSave) probSave.addEventListener("click", function () { post("/tickets/" + id + "/problem-link", { problem_id: panel.querySelector("#dt-problem").value }) .then(function (d) { if (d.ok) openDetail(id); else alert(d.error || "Fehler"); }); }); var commentSave = panel.querySelector("#dt-comment-save"); if (commentSave) commentSave.addEventListener("click", function () { var text = panel.querySelector("#dt-comment").value.trim(); if (!text) return; post("/tickets/" + id + "/comment", { text: text }).then(function (d) { if (d.ok) openDetail(id); else alert(d.error || "Fehler"); }); }); var close = panel.querySelector("#dt-close"); if (close) close.addEventListener("click", closeDetail); // Repo-Bearbeitung (nur wenn gerendert) var repoLoad = panel.querySelector("#repo-load"); if (repoLoad) { repoLoad.addEventListener("click", function () { var repo = panel.querySelector("#repo-name").value.trim(); var path = panel.querySelector("#repo-path").value.trim(); var branch = panel.querySelector("#repo-branch").value.trim() || "main"; var status = panel.querySelector("#repo-status"); if (!repo || !path) { status.textContent = "Repo und Dateipfad angeben."; return; } status.textContent = "Laedt..."; fetch("/api/tickets/" + id + "/repo-file?repo=" + encodeURIComponent(repo) + "&path=" + encodeURIComponent(path) + "&branch=" + encodeURIComponent(branch)) .then(function (r) { return r.json(); }) .then(function (d) { if (!d.ok) { status.textContent = "Fehler: " + d.error; return; } status.textContent = "Geladen (sha " + d.sha.slice(0, 10) + ")."; var ta = panel.querySelector("#repo-content"); ta.value = d.content; ta.hidden = false; panel.querySelector("#repo-sha").value = d.sha; panel.querySelector("#repo-msg-row").hidden = false; panel.querySelector("#repo-save").hidden = false; }) .catch(function () { status.textContent = "Fehler beim Laden."; }); }); panel.querySelector("#repo-save").addEventListener("click", function () { var status = panel.querySelector("#repo-status"); var message = panel.querySelector("#repo-msg").value.trim(); if (!message) { status.textContent = "Bitte eine Commit-Nachricht angeben."; return; } status.textContent = "Speichert..."; post("/tickets/" + id + "/repo-edit", { repo: panel.querySelector("#repo-name").value.trim(), path: panel.querySelector("#repo-path").value.trim(), branch: panel.querySelector("#repo-branch").value.trim() || "main", sha: panel.querySelector("#repo-sha").value, content: panel.querySelector("#repo-content").value, message: message }).then(function (d) { if (!d.ok) { status.textContent = "Fehler: " + d.error; return; } status.textContent = "Gespeichert -- Commit " + d.commit.slice(0, 10) + ", im Worklog dokumentiert."; openDetail(id); }).catch(function () { status.textContent = "Fehler beim Speichern."; }); }); } } })();