(function () { const loginView = document.getElementById("loginView"); const adminView = document.getElementById("adminView"); const loginForm = document.getElementById("loginForm"); const passwordInput = document.getElementById("passwordInput"); const loginMessage = document.getElementById("loginMessage"); const addDateForm = document.getElementById("addDateForm"); const dateInput = document.getElementById("dateInput"); const addDateMessage = document.getElementById("addDateMessage"); const datesList = document.getElementById("datesList"); const dateCount = document.getElementById("dateCount"); const emptyDates = document.getElementById("emptyDates"); const logoutBtn = document.getElementById("logoutBtn"); let dates = []; function setMessage(element, message, success) { element.textContent = message; element.classList.toggle("is-success", Boolean(success)); } function todayKey() { const today = new Date(); const year = today.getFullYear(); const month = String(today.getMonth() + 1).padStart(2, "0"); const day = String(today.getDate()).padStart(2, "0"); return `${year}-${month}-${day}`; } function formatDate(date) { return new Date(`${date}T00:00:00`).toLocaleDateString("de-DE", { weekday: "short", day: "2-digit", month: "2-digit", year: "numeric", }); } async function request(path, options) { const response = await fetch(path, { ...options, headers: { "Content-Type": "application/json", ...(options && options.headers), }, }); const data = await response.json().catch(() => ({})); if (!response.ok) { const error = new Error(data.message || "Die Anfrage ist fehlgeschlagen."); error.status = response.status; throw error; } return data; } function showLogin(message) { adminView.classList.add("hidden"); loginView.classList.remove("hidden"); if (message) { setMessage(loginMessage, message, false); } passwordInput.focus(); } function renderDates() { datesList.innerHTML = ""; dateCount.textContent = `${dates.length} ${dates.length === 1 ? "Termin" : "Termine"}`; emptyDates.classList.toggle("hidden", dates.length !== 0); dates.forEach((date) => { const row = document.createElement("li"); row.className = "date-row"; const label = document.createElement("span"); label.className = "date-label"; label.textContent = formatDate(date.date); const typeSelect = document.createElement("select"); typeSelect.className = "date-type-select"; typeSelect.setAttribute("aria-label", `Tagestyp für ${formatDate(date.date)}`); [["short", "Kurz"], ["long", "Lang"]].forEach(([value, text]) => { const option = document.createElement("option"); option.value = value; option.textContent = text; option.selected = date.type === value; typeSelect.appendChild(option); }); typeSelect.addEventListener("change", async () => { typeSelect.disabled = true; try { await request("/api/admin/dates", { method: "PUT", body: JSON.stringify({ date: date.date, type: typeSelect.value }), }); date.type = typeSelect.value; setMessage(addDateMessage, "Tagestyp aktualisiert.", true); } catch (error) { if (error.status === 401) { showLogin("Deine Anmeldung ist abgelaufen."); return; } typeSelect.value = date.type; setMessage(addDateMessage, error.message, false); } finally { typeSelect.disabled = false; } }); const deleteButton = document.createElement("button"); deleteButton.className = "delete-date-btn"; deleteButton.type = "button"; deleteButton.textContent = "Löschen"; deleteButton.addEventListener("click", async () => { if (!window.confirm(`${formatDate(date.date)} wirklich löschen?`)) { return; } deleteButton.disabled = true; try { await request("/api/admin/dates", { method: "DELETE", body: JSON.stringify({ date: date.date }), }); dates = dates.filter((item) => item.date !== date.date); renderDates(); setMessage(addDateMessage, "Datum gelöscht.", true); } catch (error) { if (error.status === 401) { showLogin("Deine Anmeldung ist abgelaufen."); return; } setMessage(addDateMessage, error.message, false); deleteButton.disabled = false; } }); row.append(label, typeSelect, deleteButton); datesList.appendChild(row); }); } async function loadDates() { try { dates = await request("/api/admin/dates", { method: "GET" }); dates.sort((first, second) => first.date.localeCompare(second.date)); loginView.classList.add("hidden"); adminView.classList.remove("hidden"); renderDates(); } catch (error) { if (error.status === 401) { showLogin(); return; } showLogin("Die Termine konnten nicht geladen werden."); } } loginForm.addEventListener("submit", async (event) => { event.preventDefault(); setMessage(loginMessage, "", false); try { await request("/api/admin/login", { method: "POST", body: JSON.stringify({ password: passwordInput.value }), }); passwordInput.value = ""; await loadDates(); } catch (error) { setMessage(loginMessage, error.message, false); } }); addDateForm.addEventListener("submit", async (event) => { event.preventDefault(); const type = addDateForm.elements.dateType.value; setMessage(addDateMessage, "", false); try { await request("/api/admin/dates", { method: "POST", body: JSON.stringify({ date: dateInput.value, type }), }); dates.push({ date: dateInput.value, type }); dates.sort((first, second) => first.date.localeCompare(second.date)); renderDates(); setMessage(addDateMessage, "Datum hinzugefügt.", true); addDateForm.reset(); dateInput.min = todayKey(); } catch (error) { if (error.status === 401) { showLogin("Deine Anmeldung ist abgelaufen."); return; } setMessage(addDateMessage, error.message, false); } }); logoutBtn.addEventListener("click", async () => { try { await request("/api/admin/logout", { method: "POST" }); } finally { showLogin(); } }); dateInput.min = todayKey(); loadDates(); })();