/* 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."; });
});
}
}
})();