Files
dogfather-universe/assets/js/postfach.js
T

217 lines
8.9 KiB
JavaScript

/* =====================================================================
postfach.js — Logik für postfach.html
===================================================================== */
const API_BASE_URL = "https://dogfather-universe-postfach.dogfather1608.workers.dev";
const { getSession, sessionHasPermission, apiCall, doLogin, doLogout } = window.AdminAuth;
const STATUS_OPTIONS = ["neu", "in_bearbeitung", "beantwortet"];
function $(id) { return document.getElementById(id); }
function showApp(session) {
$("gate-container").style.display = "none";
$("session-bar").style.display = "block";
$("inbox-section").style.display = "block";
$("role-badge").textContent = session.isOwner ? "Eingeloggt als: Dogi (Owner)" : `Eingeloggt als: ${session.name} (${session.roleName})`;
const canSeeTeam = session.isOwner || sessionHasPermission(session, "TEAM_VIEW");
$("team-link").style.display = canSeeTeam ? "inline-flex" : "none";
if (session.isOwner) {
$("live-toggle-section").style.display = "block";
loadLiveStatus();
}
loadInbox();
}
function showGate() {
$("gate-container").style.display = "block";
$("session-bar").style.display = "none";
$("live-toggle-section").style.display = "none";
$("inbox-section").style.display = "none";
}
/* ---------------- Bewerbungen ---------------- */
async function loadInbox() {
const session = getSession();
const includeArchived = $("show-archived").checked;
const res = await apiCall(API_BASE_URL, "/admin/applications/list", { includeArchived });
if (!res.ok) {
if (!getSession()) showGate();
return;
}
renderInbox(res.applications, session);
}
function renderInbox(applications, session) {
const list = $("inbox-list");
if (!applications.length) {
list.innerHTML = `<p class="muted">Keine Bewerbungen in dieser Ansicht.</p>`;
return;
}
list.innerHTML = applications
.map((a) => {
const fields = a.data
? Object.entries(a.data).map(([k, v]) => `<p class="small"><strong>${k}:</strong> ${v}</p>`).join("")
: `<p class="small muted">Keine Berechtigung, den vollen Inhalt zu öffnen.</p>`;
const date = new Date(a.createdAt).toLocaleString("de-DE");
const actions = [];
if (sessionHasPermission(session, "APPLICATIONS_TAKE_OVER")) {
actions.push(`<button class="btn btn-outline take-btn" data-id="${a.id}">Übernehmen</button>`);
}
if (sessionHasPermission(session, "APPLICATIONS_MARK_ANSWERED")) {
actions.push(`<button class="btn btn-outline answered-btn" data-id="${a.id}">Als beantwortet markieren</button>`);
}
if (sessionHasPermission(session, "APPLICATIONS_ARCHIVE") && !a.archived) {
actions.push(`<button class="btn btn-outline archive-btn" data-id="${a.id}">Archivieren</button>`);
}
if (sessionHasPermission(session, "APPLICATIONS_RESTORE") && a.archived) {
actions.push(`<button class="btn btn-outline restore-btn" data-id="${a.id}">Wiederherstellen</button>`);
}
if (sessionHasPermission(session, "APPLICATIONS_DELETE")) {
actions.push(`<button class="btn btn-outline delete-btn" data-id="${a.id}" style="color:var(--accent);">Löschen</button>`);
}
if (sessionHasPermission(session, "NOTES_READ")) {
actions.push(`<button class="btn btn-outline notes-toggle-btn" data-id="${a.id}">Notizen</button>`);
}
const statusSelect = sessionHasPermission(session, "APPLICATIONS_CHANGE_STATUS")
? `<select class="status-select" data-id="${a.id}">
${STATUS_OPTIONS.map((s) => `<option value="${s}" ${s === a.status ? "selected" : ""}>${s}</option>`).join("")}
</select>`
: `<span class="tag">${a.status}</span>`;
return `
<div class="card app-card">
<div style="display:flex;justify-content:space-between;align-items:center;">
<span class="tag">${a.type}${a.archived ? " · archiviert" : ""}</span>
${statusSelect}
</div>
<p class="small muted" style="margin:0;">${date}</p>
<div class="fields">${fields}</div>
<div class="actions">${actions.join("")}</div>
<div class="notes-box" id="notes-${a.id}">
<div class="notes-list" id="notes-list-${a.id}"><p class="small muted">Lädt…</p></div>
${
sessionHasPermission(session, "NOTES_WRITE")
? `<div class="field" style="margin-top:.6rem;">
<textarea class="note-input" data-id="${a.id}" placeholder="Neue Notiz…" style="min-height:60px;"></textarea>
<button class="btn btn-outline add-note-btn" data-id="${a.id}" style="margin-top:.4rem;">Notiz speichern</button>
</div>`
: ""
}
</div>
</div>`;
})
.join("");
list.querySelectorAll(".take-btn").forEach((b) => b.addEventListener("click", () => appAction("/admin/applications/take-over", b.dataset.id)));
list.querySelectorAll(".answered-btn").forEach((b) => b.addEventListener("click", () => appAction("/admin/applications/mark-answered", b.dataset.id)));
list.querySelectorAll(".archive-btn").forEach((b) => b.addEventListener("click", () => appAction("/admin/applications/archive", b.dataset.id)));
list.querySelectorAll(".restore-btn").forEach((b) => b.addEventListener("click", () => appAction("/admin/applications/restore", b.dataset.id)));
list.querySelectorAll(".delete-btn").forEach((b) =>
b.addEventListener("click", () => {
if (confirm("Diese Bewerbung wirklich endgültig löschen?")) appAction("/admin/applications/delete", b.dataset.id);
})
);
list.querySelectorAll(".status-select").forEach((sel) =>
sel.addEventListener("change", () => appAction("/admin/applications/status", sel.dataset.id, { status: sel.value }, false))
);
list.querySelectorAll(".notes-toggle-btn").forEach((b) =>
b.addEventListener("click", () => {
const box = $(`notes-${b.dataset.id}`);
box.classList.toggle("open");
if (box.classList.contains("open")) loadNotes(b.dataset.id);
})
);
list.querySelectorAll(".add-note-btn").forEach((b) =>
b.addEventListener("click", async () => {
const textarea = document.querySelector(`.note-input[data-id="${b.dataset.id}"]`);
const text = textarea.value.trim();
if (!text) return;
const res = await apiCall(API_BASE_URL, "/admin/notes/add", { applicationId: b.dataset.id, text });
if (res.ok) {
textarea.value = "";
loadNotes(b.dataset.id);
}
})
);
}
async function appAction(path, id, extra, reload = true) {
const res = await apiCall(API_BASE_URL, path, { id, ...(extra || {}) });
if (res.ok && reload) loadInbox();
else if (!res.ok) alert(res.error || "Fehler.");
}
async function loadNotes(applicationId) {
const box = $(`notes-list-${applicationId}`);
const res = await apiCall(API_BASE_URL, "/admin/notes/list", { applicationId });
if (!res.ok) {
box.innerHTML = `<p class="small muted">Konnte Notizen nicht laden.</p>`;
return;
}
box.innerHTML =
res.notes
.map((n) => `<div class="note-item"><strong>${n.author}</strong>: ${n.text}<br><span class="small muted">${new Date(n.created_at).toLocaleString("de-DE")}</span></div>`)
.join("") || `<p class="small muted">Noch keine Notizen.</p>`;
}
/* ---------------- Live-Status ---------------- */
function renderLiveStatus(live) {
$("admin-live-dot").classList.toggle("is-live", !!live);
$("admin-live-text").textContent = live ? "Live jetzt!" : "Aktuell offline";
}
async function loadLiveStatus() {
try {
const res = await fetch(`${API_BASE_URL}/live-status`);
const data = await res.json();
if (res.ok && data.ok) renderLiveStatus(data.live);
} catch {
/* keine Blockade */
}
}
async function setLiveStatus(live) {
const res = await apiCall(API_BASE_URL, "/live-status/set", { live });
if (res.ok) renderLiveStatus(res.live);
}
/* ---------------- Login / Logout ---------------- */
document.addEventListener("DOMContentLoaded", () => {
const existing = getSession();
if (existing) showApp(existing);
else showGate();
$("code-gate").addEventListener("submit", async (e) => {
e.preventDefault();
const status = $("gate-status");
status.className = "form-status show";
status.textContent = "Prüfe Code …";
const res = await doLogin(API_BASE_URL, $("code-input").value);
if (res.ok) {
status.className = "form-status";
showApp(getSession());
} else {
status.textContent = res.error || "Falscher Code.";
status.className = "form-status show error";
}
});
$("logout-btn").addEventListener("click", async () => {
await doLogout(API_BASE_URL);
showGate();
});
$("refresh-btn").addEventListener("click", loadInbox);
$("show-archived").addEventListener("change", loadInbox);
$("go-live-btn").addEventListener("click", () => setLiveStatus(true));
$("go-offline-btn").addEventListener("click", () => setLiveStatus(false));
});