/* ========================================================================== PoE Manager — shared UI behaviour (sidebar, theme, modals, toasts) ========================================================================== */ (function () { "use strict"; /* ---------------- Theme ---------------- */ const THEME_KEY = "poe-theme"; function applyTheme(theme) { document.documentElement.setAttribute("data-theme", theme); document.querySelectorAll("[data-theme-icon]").forEach((el) => { el.innerHTML = theme === "light" ? ICONS.moon : ICONS.sun; }); } function initTheme() { const saved = localStorage.getItem(THEME_KEY) || (window.matchMedia && window.matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark"); applyTheme(saved); } function toggleTheme() { const current = document.documentElement.getAttribute("data-theme") === "light" ? "light" : "dark"; const next = current === "light" ? "dark" : "light"; localStorage.setItem(THEME_KEY, next); applyTheme(next); } const ICONS = { sun: '', moon: '', }; /* ---------------- Sidebar (mobile) ---------------- */ function initSidebar() { const sidebar = document.querySelector(".sidebar"); const backdrop = document.querySelector(".sidebar-backdrop"); const main = document.querySelector(".main"); const toggles = document.querySelectorAll("[data-sidebar-toggle]"); if (!sidebar) return; const DESKTOP_BREAKPOINT = 900; const COLLAPSE_KEY = "poe-sidebar-collapsed"; // Mobile: temporäres Überlagern per Hamburger + Backdrop. function open() { sidebar.classList.add("open"); backdrop && backdrop.classList.add("open"); } function close() { sidebar.classList.remove("open"); backdrop && backdrop.classList.remove("open"); } // Desktop: dauerhaftes Ein-/Ausklappen, über Neuladen hinweg gemerkt. function setCollapsed(collapsed) { sidebar.classList.toggle("collapsed", collapsed); main && main.classList.toggle("sidebar-collapsed", collapsed); localStorage.setItem(COLLAPSE_KEY, collapsed ? "1" : "0"); } if (localStorage.getItem(COLLAPSE_KEY) === "1") setCollapsed(true); toggles.forEach((btn) => btn.addEventListener("click", () => { if (window.innerWidth <= DESKTOP_BREAKPOINT) { sidebar.classList.contains("open") ? close() : open(); } else { setCollapsed(!sidebar.classList.contains("collapsed")); } })); backdrop && backdrop.addEventListener("click", close); document.querySelectorAll(".nav-item").forEach((a) => a.addEventListener("click", close)); } /* ---------------- Modals ---------------- */ function openModal(id) { const el = document.getElementById(id); if (!el) return; el.classList.add("open"); document.body.style.overflow = "hidden"; } function closeModal(el) { const overlay = el.closest ? el.closest(".modal-overlay") : el; if (!overlay) return; overlay.classList.remove("open"); document.body.style.overflow = ""; } function initModals() { document.querySelectorAll("[data-open-modal]").forEach((trigger) => { trigger.addEventListener("click", () => openModal(trigger.getAttribute("data-open-modal"))); }); document.querySelectorAll("[data-close-modal]").forEach((btn) => { btn.addEventListener("click", () => closeModal(btn)); }); document.querySelectorAll(".modal-overlay").forEach((overlay) => { overlay.addEventListener("click", (e) => { if (e.target === overlay) closeModal(overlay); }); }); document.addEventListener("keydown", (e) => { if (e.key === "Escape") { document.querySelectorAll(".modal-overlay.open").forEach((o) => closeModal(o)); } }); } /* ---------------- Confirm dialog (replaces window.confirm) ---------------- */ function ensureConfirmModal() { if (document.getElementById("confirm-modal")) return; const html = `
`; document.body.insertAdjacentHTML("beforeend", html); document.querySelectorAll("#confirm-modal [data-close-modal]").forEach((btn) => { btn.addEventListener("click", () => closeModal(btn)); }); document.getElementById("confirm-modal").addEventListener("click", (e) => { if (e.target.id === "confirm-modal") closeModal(e.target); }); } window.confirmAction = function (message, onConfirm, title) { ensureConfirmModal(); document.getElementById("confirm-title").innerText = title || "Bist du sicher?"; document.getElementById("confirm-message").innerText = message; const okBtn = document.getElementById("confirm-ok"); const freshBtn = okBtn.cloneNode(true); okBtn.parentNode.replaceChild(freshBtn, okBtn); freshBtn.addEventListener("click", () => { closeModal(freshBtn); onConfirm(); }); openModal("confirm-modal"); }; /* Intercept forms/buttons marked with data-confirm="message" */ function initConfirmables() { document.querySelectorAll("form[data-confirm]").forEach((form) => { form.addEventListener("submit", function (e) { if (form.dataset.confirmed === "1") return; e.preventDefault(); window.confirmAction(form.getAttribute("data-confirm"), () => { form.dataset.confirmed = "1"; form.requestSubmit ? form.requestSubmit() : form.submit(); }, form.getAttribute("data-confirm-title")); }); }); } /* ---------------- Toasts ---------------- */ function ensureToastStack() { let stack = document.querySelector(".toast-stack"); if (!stack) { stack = document.createElement("div"); stack.className = "toast-stack"; document.body.appendChild(stack); } return stack; } window.showToast = function (message, type) { const stack = ensureToastStack(); const toast = document.createElement("div"); toast.className = "toast " + (type || "info"); toast.innerHTML = `${message}×`; stack.appendChild(toast); const remove = () => { toast.classList.add("hide"); setTimeout(() => toast.remove(), 180); }; toast.querySelector(".toast-close").addEventListener("click", remove); setTimeout(remove, 5000); }; function initFlashedMessages() { const data = document.getElementById("flashed-data"); if (!data) return; try { const messages = JSON.parse(data.textContent); messages.forEach(([category, msg]) => { const type = category === "danger" ? "danger" : category === "success" ? "success" : "info"; window.showToast(msg, type); }); } catch (e) { /* noop */ } } /* Nav-Gruppen mit Untermenü (z.B. "Logs") auf-/zuklappen */ function initNavGroups() { document.querySelectorAll("[data-nav-group-toggle]").forEach((btn) => { btn.addEventListener("click", () => { btn.closest("[data-nav-group]").classList.toggle("expanded"); }); }); } /* ---------------- Globaler "Nächste Prüfung"-Timer (Topbar, jede Seite) ---------------- */ function initCheckTimer() { const pill = document.getElementById("global-timer-pill"); const timerEl = document.getElementById("global-timer"); if (!pill || !timerEl) return; const lastRunMs = parseInt(pill.dataset.lastRunMs, 10); const intervalMs = parseInt(pill.dataset.intervalMs, 10); if (!intervalMs) { timerEl.textContent = "--"; return; } function update() { let remainingMs = intervalMs; if (!isNaN(lastRunMs) && lastRunMs > 0) { const elapsed = Date.now() - lastRunMs; remainingMs = intervalMs - (((elapsed % intervalMs) + intervalMs) % intervalMs); } timerEl.textContent = Math.max(0, Math.ceil(remainingMs / 1000)); } update(); setInterval(update, 1000); } /* ---------------- Sortierbare Tabellen ---------------- */ /* * Klick auf