Script in eigene File verschoben, Startscripte geändert
This commit is contained in:
+1
-166
@@ -49,171 +49,6 @@
|
||||
<footer>Der Kundenbezug wird serverseitig gegen die Stammdaten geprüft, bevor das Ticket erstellt wird.</footer>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
const searchInput = document.getElementById("customerSearch");
|
||||
const searchWrap = document.getElementById("searchWrap");
|
||||
const suggestionsBox = document.getElementById("suggestions");
|
||||
const customerChip = document.getElementById("customerChip");
|
||||
const chipNumber = document.getElementById("chipNumber");
|
||||
const chipName = document.getElementById("chipName");
|
||||
const chipReset = document.getElementById("chipReset");
|
||||
const stepIssue = document.getElementById("stepIssue");
|
||||
const subjectInput = document.getElementById("subject");
|
||||
const descriptionInput = document.getElementById("description");
|
||||
const submitButton = document.getElementById("submitButton");
|
||||
const feedback = document.getElementById("feedback");
|
||||
|
||||
let selectedCustomer = null;
|
||||
let currentResults = [];
|
||||
let activeIndex = -1;
|
||||
let debounceTimer = null;
|
||||
|
||||
function updateSubmitState() {
|
||||
const subjectOk = subjectInput.value.trim().length >= 5;
|
||||
submitButton.disabled = !(selectedCustomer && subjectOk);
|
||||
}
|
||||
|
||||
function closeSuggestions() {
|
||||
suggestionsBox.classList.remove("open");
|
||||
searchInput.setAttribute("aria-expanded", "false");
|
||||
activeIndex = -1;
|
||||
}
|
||||
|
||||
function renderSuggestions() {
|
||||
suggestionsBox.innerHTML = "";
|
||||
if (currentResults.length === 0) {
|
||||
const empty = document.createElement("div");
|
||||
empty.className = "suggestion-empty";
|
||||
empty.textContent = "Kein Kunde gefunden. Nummer und Schreibweise prüfen.";
|
||||
suggestionsBox.appendChild(empty);
|
||||
} else {
|
||||
currentResults.forEach((customer, index) => {
|
||||
const item = document.createElement("button");
|
||||
item.type = "button";
|
||||
item.className = index === activeIndex ? "suggestion active" : "suggestion";
|
||||
item.setAttribute("role", "option");
|
||||
item.innerHTML = "";
|
||||
const numberSpan = document.createElement("span");
|
||||
numberSpan.className = "cnum";
|
||||
numberSpan.textContent = customer.number;
|
||||
const nameSpan = document.createElement("span");
|
||||
nameSpan.textContent = customer.name;
|
||||
item.appendChild(numberSpan);
|
||||
item.appendChild(nameSpan);
|
||||
item.addEventListener("click", () => selectCustomer(customer));
|
||||
suggestionsBox.appendChild(item);
|
||||
});
|
||||
}
|
||||
suggestionsBox.classList.add("open");
|
||||
searchInput.setAttribute("aria-expanded", "true");
|
||||
}
|
||||
|
||||
async function runSearch(query) {
|
||||
try {
|
||||
const response = await fetch("/api/customers?search=" + encodeURIComponent(query));
|
||||
const payload = await response.json();
|
||||
currentResults = payload.results;
|
||||
activeIndex = currentResults.length > 0 ? 0 : -1;
|
||||
renderSuggestions();
|
||||
} catch (error) {
|
||||
currentResults = [];
|
||||
renderSuggestions();
|
||||
}
|
||||
}
|
||||
|
||||
function selectCustomer(customer) {
|
||||
selectedCustomer = customer;
|
||||
chipNumber.textContent = customer.number;
|
||||
chipName.textContent = customer.name;
|
||||
customerChip.classList.add("visible");
|
||||
searchWrap.style.display = "none";
|
||||
closeSuggestions();
|
||||
stepIssue.dataset.disabled = "false";
|
||||
updateSubmitState();
|
||||
subjectInput.focus();
|
||||
}
|
||||
|
||||
function resetCustomer() {
|
||||
selectedCustomer = null;
|
||||
customerChip.classList.remove("visible");
|
||||
searchWrap.style.display = "";
|
||||
searchInput.value = "";
|
||||
stepIssue.dataset.disabled = "true";
|
||||
updateSubmitState();
|
||||
searchInput.focus();
|
||||
}
|
||||
|
||||
searchInput.addEventListener("input", () => {
|
||||
const query = searchInput.value.trim();
|
||||
clearTimeout(debounceTimer);
|
||||
if (query.length < 2) { closeSuggestions(); return; }
|
||||
debounceTimer = setTimeout(() => runSearch(query), 200);
|
||||
});
|
||||
|
||||
searchInput.addEventListener("keydown", (event) => {
|
||||
const isOpen = suggestionsBox.classList.contains("open");
|
||||
if (event.key === "ArrowDown" && isOpen) {
|
||||
event.preventDefault();
|
||||
activeIndex = Math.min(activeIndex + 1, currentResults.length - 1);
|
||||
renderSuggestions();
|
||||
} else if (event.key === "ArrowUp" && isOpen) {
|
||||
event.preventDefault();
|
||||
activeIndex = Math.max(activeIndex - 1, 0);
|
||||
renderSuggestions();
|
||||
} else if (event.key === "Enter" && isOpen && activeIndex >= 0) {
|
||||
event.preventDefault();
|
||||
selectCustomer(currentResults[activeIndex]);
|
||||
} else if (event.key === "Escape") {
|
||||
closeSuggestions();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("click", (event) => {
|
||||
if (!searchWrap.contains(event.target)) { closeSuggestions(); }
|
||||
});
|
||||
|
||||
chipReset.addEventListener("click", resetCustomer);
|
||||
subjectInput.addEventListener("input", updateSubmitState);
|
||||
|
||||
submitButton.addEventListener("click", async () => {
|
||||
feedback.className = "feedback";
|
||||
submitButton.disabled = true;
|
||||
submitButton.textContent = "Wird angelegt ...";
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/tickets", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
customerNumber: selectedCustomer.number,
|
||||
subject: subjectInput.value,
|
||||
description: descriptionInput.value,
|
||||
}),
|
||||
});
|
||||
const payload = await response.json();
|
||||
|
||||
if (response.status === 201) {
|
||||
const dryRunHint = payload.dryRun ? " (Trockenlauf, nicht wirklich in Redmine angelegt)" : "";
|
||||
feedback.className = "feedback success";
|
||||
feedback.innerHTML = "Ticket <a class=\"mono\" href=\"" + payload.issueUrl + "\" target=\"_blank\" rel=\"noopener\">#" + payload.issueId + "</a> fuer <span class=\"mono\">" + payload.customer.number + "</span> angelegt." + dryRunHint;
|
||||
subjectInput.value = "";
|
||||
descriptionInput.value = "";
|
||||
} else {
|
||||
feedback.className = "feedback error";
|
||||
feedback.textContent = payload.error || "Unbekannter Fehler beim Anlegen des Tickets.";
|
||||
}
|
||||
} catch (error) {
|
||||
feedback.className = "feedback error";
|
||||
feedback.textContent = "Server nicht erreichbar. Bitte später erneut versuchen.";
|
||||
} finally {
|
||||
submitButton.textContent = "Ticket anlegen";
|
||||
updateSubmitState();
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<script src="index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
const searchInput = document.getElementById("customerSearch");
|
||||
const searchWrap = document.getElementById("searchWrap");
|
||||
const suggestionsBox = document.getElementById("suggestions");
|
||||
const customerChip = document.getElementById("customerChip");
|
||||
const chipNumber = document.getElementById("chipNumber");
|
||||
const chipName = document.getElementById("chipName");
|
||||
const chipReset = document.getElementById("chipReset");
|
||||
const stepIssue = document.getElementById("stepIssue");
|
||||
const subjectInput = document.getElementById("subject");
|
||||
const descriptionInput = document.getElementById("description");
|
||||
const submitButton = document.getElementById("submitButton");
|
||||
const feedback = document.getElementById("feedback");
|
||||
|
||||
let selectedCustomer = null;
|
||||
let currentResults = [];
|
||||
let activeIndex = -1;
|
||||
let debounceTimer = null;
|
||||
|
||||
function updateSubmitState() {
|
||||
const subjectOk = subjectInput.value.trim().length >= 5;
|
||||
submitButton.disabled = !(selectedCustomer && subjectOk);
|
||||
}
|
||||
|
||||
function closeSuggestions() {
|
||||
suggestionsBox.classList.remove("open");
|
||||
searchInput.setAttribute("aria-expanded", "false");
|
||||
activeIndex = -1;
|
||||
}
|
||||
|
||||
function renderSuggestions() {
|
||||
suggestionsBox.innerHTML = "";
|
||||
if (currentResults.length === 0) {
|
||||
const empty = document.createElement("div");
|
||||
empty.className = "suggestion-empty";
|
||||
empty.textContent = "Kein Kunde gefunden. Nummer und Schreibweise prüfen.";
|
||||
suggestionsBox.appendChild(empty);
|
||||
} else {
|
||||
currentResults.forEach((customer, index) => {
|
||||
const item = document.createElement("button");
|
||||
item.type = "button";
|
||||
item.className = index === activeIndex ? "suggestion active" : "suggestion";
|
||||
item.setAttribute("role", "option");
|
||||
item.innerHTML = "";
|
||||
const numberSpan = document.createElement("span");
|
||||
numberSpan.className = "cnum";
|
||||
numberSpan.textContent = customer.number;
|
||||
const nameSpan = document.createElement("span");
|
||||
nameSpan.textContent = customer.name;
|
||||
item.appendChild(numberSpan);
|
||||
item.appendChild(nameSpan);
|
||||
item.addEventListener("click", () => selectCustomer(customer));
|
||||
suggestionsBox.appendChild(item);
|
||||
});
|
||||
}
|
||||
suggestionsBox.classList.add("open");
|
||||
searchInput.setAttribute("aria-expanded", "true");
|
||||
}
|
||||
|
||||
async function runSearch(query) {
|
||||
try {
|
||||
const response = await fetch("/api/customers?search=" + encodeURIComponent(query));
|
||||
const payload = await response.json();
|
||||
currentResults = payload.results;
|
||||
activeIndex = currentResults.length > 0 ? 0 : -1;
|
||||
renderSuggestions();
|
||||
} catch (error) {
|
||||
currentResults = [];
|
||||
renderSuggestions();
|
||||
}
|
||||
}
|
||||
|
||||
function selectCustomer(customer) {
|
||||
selectedCustomer = customer;
|
||||
chipNumber.textContent = customer.number;
|
||||
chipName.textContent = customer.name;
|
||||
customerChip.classList.add("visible");
|
||||
searchWrap.style.display = "none";
|
||||
closeSuggestions();
|
||||
stepIssue.dataset.disabled = "false";
|
||||
updateSubmitState();
|
||||
subjectInput.focus();
|
||||
}
|
||||
|
||||
function resetCustomer() {
|
||||
selectedCustomer = null;
|
||||
customerChip.classList.remove("visible");
|
||||
searchWrap.style.display = "";
|
||||
searchInput.value = "";
|
||||
stepIssue.dataset.disabled = "true";
|
||||
updateSubmitState();
|
||||
searchInput.focus();
|
||||
}
|
||||
|
||||
searchInput.addEventListener("input", () => {
|
||||
const query = searchInput.value.trim();
|
||||
clearTimeout(debounceTimer);
|
||||
if (query.length < 2) { closeSuggestions(); return; }
|
||||
debounceTimer = setTimeout(() => runSearch(query), 200);
|
||||
});
|
||||
|
||||
searchInput.addEventListener("keydown", (event) => {
|
||||
const isOpen = suggestionsBox.classList.contains("open");
|
||||
if (event.key === "ArrowDown" && isOpen) {
|
||||
event.preventDefault();
|
||||
activeIndex = Math.min(activeIndex + 1, currentResults.length - 1);
|
||||
renderSuggestions();
|
||||
} else if (event.key === "ArrowUp" && isOpen) {
|
||||
event.preventDefault();
|
||||
activeIndex = Math.max(activeIndex - 1, 0);
|
||||
renderSuggestions();
|
||||
} else if (event.key === "Enter" && isOpen && activeIndex >= 0) {
|
||||
event.preventDefault();
|
||||
selectCustomer(currentResults[activeIndex]);
|
||||
} else if (event.key === "Escape") {
|
||||
closeSuggestions();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("click", (event) => {
|
||||
if (!searchWrap.contains(event.target)) { closeSuggestions(); }
|
||||
});
|
||||
|
||||
chipReset.addEventListener("click", resetCustomer);
|
||||
subjectInput.addEventListener("input", updateSubmitState);
|
||||
|
||||
submitButton.addEventListener("click", async () => {
|
||||
feedback.className = "feedback";
|
||||
submitButton.disabled = true;
|
||||
submitButton.textContent = "Wird angelegt ...";
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/tickets", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
customerNumber: selectedCustomer.number,
|
||||
subject: subjectInput.value,
|
||||
description: descriptionInput.value,
|
||||
}),
|
||||
});
|
||||
const payload = await response.json();
|
||||
|
||||
if (response.status === 201) {
|
||||
const dryRunHint = payload.dryRun ? " (Trockenlauf, nicht wirklich in Redmine angelegt)" : "";
|
||||
feedback.className = "feedback success";
|
||||
feedback.innerHTML = "Ticket <a class=\"mono\" href=\"" + payload.issueUrl + "\" target=\"_blank\" rel=\"noopener\">#" + payload.issueId + "</a> fuer <span class=\"mono\">" + payload.customer.number + "</span> angelegt." + dryRunHint;
|
||||
subjectInput.value = "";
|
||||
descriptionInput.value = "";
|
||||
} else {
|
||||
feedback.className = "feedback error";
|
||||
feedback.textContent = payload.error || "Unbekannter Fehler beim Anlegen des Tickets.";
|
||||
}
|
||||
} catch (error) {
|
||||
feedback.className = "feedback error";
|
||||
feedback.textContent = "Server nicht erreichbar. Bitte später erneut versuchen.";
|
||||
} finally {
|
||||
submitButton.textContent = "Ticket anlegen";
|
||||
updateSubmitState();
|
||||
}
|
||||
});
|
||||
})();
|
||||
Reference in New Issue
Block a user