286 lines
9.9 KiB
JavaScript
286 lines
9.9 KiB
JavaScript
(function () {
|
|
const dialog = document.getElementById("completeDialog");
|
|
const dialogForm = document.getElementById("completeDialogForm");
|
|
const dialogChoice = document.getElementById("completeDialogChoice");
|
|
const dialogText = document.getElementById("completeDialogText");
|
|
const dialogChoices = document.getElementById("completeDialogChoices");
|
|
const closeButton = document.getElementById("completeDialogClose");
|
|
const quickTaskDialog = document.getElementById("quickTaskDialog");
|
|
const quickTaskOpen = document.getElementById("quickTaskOpen");
|
|
const quickTaskClose = document.getElementById("quickTaskClose");
|
|
const quickWinsSubmit = document.getElementById("quickWinsSubmit");
|
|
const quickWinInputs = document.querySelectorAll("[data-quick-win-input]");
|
|
const quickWinCustomToggle = document.querySelector("[data-quick-win-custom-toggle]");
|
|
const quickWinCustomFields = document.getElementById("quickWinCustomFields");
|
|
const quickWinTitle = document.getElementById("quick-title");
|
|
const quickWinEffort = document.getElementById("quick-effort");
|
|
const currentUserId = document.body.dataset.currentUserId;
|
|
const currentUserName = document.body.dataset.currentUserName;
|
|
const quickWinSortList = document.querySelector("[data-quick-win-sort-list]");
|
|
const quickWinSortIds = document.getElementById("quickWinSortIds");
|
|
const quickWinSortSave = document.getElementById("quickWinSortSave");
|
|
const quickWinToggleButtons = document.querySelectorAll("[data-quick-win-toggle]");
|
|
let draggedQuickWin = null;
|
|
let quickWinSortDirty = false;
|
|
|
|
function buildCompletionOptions(button) {
|
|
const options = [];
|
|
const assignedPairs = [
|
|
[button.dataset.assignedPrimaryId, button.dataset.assignedPrimaryName],
|
|
[button.dataset.assignedSecondaryId, button.dataset.assignedSecondaryName],
|
|
];
|
|
|
|
assignedPairs.forEach(([id, label]) => {
|
|
if (id && label && !options.some((option) => option.value === id)) {
|
|
options.push({ value: id, label });
|
|
}
|
|
});
|
|
|
|
if (currentUserId && currentUserName && !options.some((option) => option.value === currentUserId)) {
|
|
options.push({ value: currentUserId, label: "Ich" });
|
|
}
|
|
|
|
return options;
|
|
}
|
|
|
|
document.querySelectorAll("[data-complete-action]").forEach((button) => {
|
|
button.addEventListener("click", () => {
|
|
if (!dialog || !dialogForm || !dialogChoice || !dialogText || !dialogChoices) {
|
|
return;
|
|
}
|
|
dialogForm.action = button.dataset.completeAction;
|
|
dialogText.textContent = `Die Aufgabe "${button.dataset.completeTitle}" war ${button.dataset.completeAssigned} zugewiesen. Wer hat sie erledigt?`;
|
|
dialogChoices.innerHTML = "";
|
|
|
|
buildCompletionOptions(button).forEach((option, index) => {
|
|
const choiceButton = document.createElement("button");
|
|
choiceButton.type = "button";
|
|
choiceButton.className = index === 0 ? "button button--secondary" : "button";
|
|
choiceButton.dataset.completeChoice = option.value;
|
|
choiceButton.textContent = option.label;
|
|
choiceButton.addEventListener("click", () => {
|
|
dialogChoice.value = option.value;
|
|
dialog.close();
|
|
dialogForm.submit();
|
|
});
|
|
dialogChoices.appendChild(choiceButton);
|
|
});
|
|
dialog.showModal();
|
|
});
|
|
});
|
|
|
|
if (closeButton && dialog) {
|
|
closeButton.addEventListener("click", () => dialog.close());
|
|
}
|
|
|
|
if (quickTaskOpen && quickTaskDialog) {
|
|
quickTaskOpen.addEventListener("click", () => quickTaskDialog.showModal());
|
|
}
|
|
|
|
if (quickTaskClose && quickTaskDialog) {
|
|
quickTaskClose.addEventListener("click", () => quickTaskDialog.close());
|
|
}
|
|
|
|
function updateQuickWinsState() {
|
|
const selectedPresetCount = [...quickWinInputs].filter((input) => input.checked).length;
|
|
const customSelected = quickWinCustomToggle?.checked === true;
|
|
const totalCount = selectedPresetCount + (customSelected ? 1 : 0);
|
|
|
|
if (quickWinCustomFields) {
|
|
quickWinCustomFields.hidden = !customSelected;
|
|
}
|
|
|
|
if (quickWinTitle) {
|
|
quickWinTitle.disabled = !customSelected;
|
|
quickWinTitle.required = customSelected;
|
|
}
|
|
|
|
if (quickWinEffort) {
|
|
quickWinEffort.disabled = !customSelected;
|
|
quickWinEffort.required = customSelected;
|
|
}
|
|
|
|
if (quickWinsSubmit) {
|
|
quickWinsSubmit.disabled = totalCount === 0;
|
|
quickWinsSubmit.textContent = totalCount <= 1 ? "Quick-Win sichern" : "Quick Wins sichern";
|
|
}
|
|
}
|
|
|
|
quickWinInputs.forEach((input) => input.addEventListener("change", updateQuickWinsState));
|
|
if (quickWinCustomToggle) {
|
|
quickWinCustomToggle.addEventListener("change", updateQuickWinsState);
|
|
}
|
|
updateQuickWinsState();
|
|
|
|
if (quickTaskDialog) {
|
|
quickTaskDialog.addEventListener("close", () => {
|
|
const quickWinsForm = document.getElementById("quickWinsForm");
|
|
if (!quickWinsForm) {
|
|
return;
|
|
}
|
|
quickWinsForm.reset();
|
|
updateQuickWinsState();
|
|
});
|
|
}
|
|
|
|
function syncQuickWinSortIds() {
|
|
if (!quickWinSortList || !quickWinSortIds) {
|
|
return;
|
|
}
|
|
const ids = [...quickWinSortList.querySelectorAll("[data-quick-win-sort-item]")]
|
|
.map((item) => item.dataset.quickWinSortItem)
|
|
.filter(Boolean);
|
|
quickWinSortIds.value = ids.join(",");
|
|
}
|
|
|
|
function setQuickWinSortDirty(isDirty) {
|
|
quickWinSortDirty = isDirty;
|
|
if (quickWinSortSave) {
|
|
quickWinSortSave.disabled = !isDirty;
|
|
}
|
|
}
|
|
|
|
if (quickWinSortList) {
|
|
syncQuickWinSortIds();
|
|
setQuickWinSortDirty(false);
|
|
|
|
quickWinSortList.querySelectorAll("[data-quick-win-sort-item]").forEach((item) => {
|
|
item.addEventListener("dragstart", () => {
|
|
draggedQuickWin = item;
|
|
item.classList.add("is-dragging");
|
|
});
|
|
|
|
item.addEventListener("dragend", () => {
|
|
item.classList.remove("is-dragging");
|
|
draggedQuickWin = null;
|
|
});
|
|
|
|
item.addEventListener("dragover", (event) => {
|
|
event.preventDefault();
|
|
});
|
|
|
|
item.addEventListener("drop", async (event) => {
|
|
event.preventDefault();
|
|
if (!draggedQuickWin || draggedQuickWin === item) {
|
|
return;
|
|
}
|
|
|
|
const items = [...quickWinSortList.querySelectorAll("[data-quick-win-sort-item]")];
|
|
const draggedIndex = items.indexOf(draggedQuickWin);
|
|
const targetIndex = items.indexOf(item);
|
|
if (draggedIndex < targetIndex) {
|
|
item.after(draggedQuickWin);
|
|
} else {
|
|
item.before(draggedQuickWin);
|
|
}
|
|
syncQuickWinSortIds();
|
|
setQuickWinSortDirty(true);
|
|
});
|
|
});
|
|
}
|
|
|
|
quickWinToggleButtons.forEach((button) => {
|
|
button.addEventListener("click", () => {
|
|
const targetId = button.dataset.target;
|
|
if (!targetId) {
|
|
return;
|
|
}
|
|
|
|
const target = document.getElementById(targetId);
|
|
if (!target) {
|
|
return;
|
|
}
|
|
|
|
const willOpen = target.hidden;
|
|
|
|
document.querySelectorAll("[data-quick-win-edit]").forEach((editForm) => {
|
|
editForm.hidden = true;
|
|
});
|
|
quickWinToggleButtons.forEach((toggle) => {
|
|
toggle.setAttribute("aria-expanded", "false");
|
|
toggle.textContent = "Bearbeiten";
|
|
});
|
|
|
|
if (willOpen) {
|
|
target.hidden = false;
|
|
button.setAttribute("aria-expanded", "true");
|
|
button.textContent = "Schließen";
|
|
}
|
|
});
|
|
});
|
|
|
|
const pushButton = document.getElementById("pushToggle");
|
|
const pushHint = document.getElementById("pushHint");
|
|
const vapidKey = document.body.dataset.pushKey;
|
|
const isIos = /iphone|ipad|ipod/i.test(window.navigator.userAgent);
|
|
const isStandalone =
|
|
window.matchMedia("(display-mode: standalone)").matches ||
|
|
window.navigator.standalone === true;
|
|
|
|
function urlBase64ToUint8Array(base64String) {
|
|
const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
|
|
const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/");
|
|
const raw = atob(base64);
|
|
return Uint8Array.from([...raw].map((char) => char.charCodeAt(0)));
|
|
}
|
|
|
|
async function postJSON(url, payload) {
|
|
const response = await fetch(url, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(payload),
|
|
});
|
|
return response.json();
|
|
}
|
|
|
|
async function togglePush() {
|
|
if (!("serviceWorker" in navigator) || !("PushManager" in window) || !pushButton) {
|
|
return;
|
|
}
|
|
|
|
const registration = await navigator.serviceWorker.register("/service-worker.js");
|
|
const existing = await registration.pushManager.getSubscription();
|
|
|
|
if (existing) {
|
|
await postJSON("/settings/push/unsubscribe", { endpoint: existing.endpoint });
|
|
await existing.unsubscribe();
|
|
pushButton.dataset.subscribed = "0";
|
|
pushButton.textContent = "Push aktivieren";
|
|
return;
|
|
}
|
|
|
|
const permission = await Notification.requestPermission();
|
|
if (permission !== "granted") {
|
|
return;
|
|
}
|
|
|
|
const subscription = await registration.pushManager.subscribe({
|
|
userVisibleOnly: true,
|
|
applicationServerKey: urlBase64ToUint8Array(vapidKey),
|
|
});
|
|
await postJSON("/settings/push/subscribe", subscription.toJSON());
|
|
pushButton.dataset.subscribed = "1";
|
|
pushButton.textContent = "Push deaktivieren";
|
|
}
|
|
|
|
if ("serviceWorker" in navigator) {
|
|
navigator.serviceWorker.register("/service-worker.js").catch(() => {});
|
|
}
|
|
|
|
if (pushButton && (!("serviceWorker" in navigator) || !("PushManager" in window))) {
|
|
pushButton.disabled = true;
|
|
if (pushHint) {
|
|
pushHint.textContent = "Dieser Browser unterstützt Web-Push hier aktuell nicht.";
|
|
}
|
|
} else if (pushButton && isIos && !isStandalone) {
|
|
pushButton.disabled = true;
|
|
if (pushHint) {
|
|
pushHint.textContent = "Auf iPhone/iPad funktioniert Web-Push erst nach „Zum Home-Bildschirm“ und Öffnen als Web-App.";
|
|
}
|
|
} else if (pushButton && vapidKey) {
|
|
pushButton.addEventListener("click", () => {
|
|
togglePush().catch((error) => console.error("Push toggle failed", error));
|
|
});
|
|
}
|
|
})();
|