/* OPENBUREAU Dialog — flache Wortmeldungen pro Beitrag. Lesen öffentlich, Mitreden nach Login (eingeladene User). Kein Build nötig. */ (function () { const root = document.getElementById('ob-dialog'); if (!root) return; const thread = root.dataset.thread || new URLSearchParams(location.search).get('thread') || ''; if (!thread) { root.innerHTML = '

Kein Thema gewählt.

'; return; } const TKEY = 'ob_dialog_token', NKEY = 'ob_dialog_name'; let token = localStorage.getItem(TKEY); let myName = localStorage.getItem(NKEY); let replyTo = null; let textarea = null; root.innerHTML = ''; const title = document.createElement('h2'); title.className = 'dialog-title'; title.textContent = 'Dialog'; const list = document.createElement('div'); list.className = 'dialog-list'; const composer = document.createElement('div'); composer.className = 'dialog-composer'; root.append(title, list, composer); function fmt(ts) { const d = new Date(ts), s = (Date.now() - d) / 1000; if (s < 60) return 'gerade eben'; if (s < 3600) return Math.floor(s / 60) + ' Min.'; if (s < 86400) return Math.floor(s / 3600) + ' Std.'; return d.toLocaleDateString('de-CH'); } async function load() { let data = []; try { const res = await fetch('/api/comments?thread=' + encodeURIComponent(thread)); if (res.ok) data = await res.json(); } catch { /* offline */ } render(data); } function render(data) { list.innerHTML = ''; const names = {}; data.forEach((c) => { names[c.id] = c.author_name; }); if (!data.length) { const e = document.createElement('p'); e.className = 'dialog-empty'; e.textContent = 'Noch keine Wortmeldungen — beginne den Dialog.'; list.appendChild(e); return; } data.forEach((c) => { const card = document.createElement('article'); card.className = 'dialog-card'; const head = document.createElement('header'); head.className = 'dialog-card-head'; const av = document.createElement('span'); av.className = 'dialog-avatar'; if (c.author_avatar) av.style.backgroundImage = 'url(' + c.author_avatar + ')'; else av.textContent = (c.author_name || '?').slice(0, 1).toUpperCase(); const meta = document.createElement('div'); meta.className = 'dialog-meta'; const nm = document.createElement('span'); nm.className = 'dialog-name'; nm.textContent = c.author_name || 'Unbekannt'; const tm = document.createElement('time'); tm.className = 'dialog-time'; tm.textContent = fmt(c.created_at); meta.append(nm, tm); if (c.parent_id && names[c.parent_id]) { const rp = document.createElement('span'); rp.className = 'dialog-replyto'; rp.textContent = '↳ ' + names[c.parent_id]; meta.appendChild(rp); } head.append(av, meta); card.appendChild(head); const bd = document.createElement('div'); bd.className = 'dialog-body'; bd.textContent = c.body; card.appendChild(bd); if (token && !c.deleted) { const actions = document.createElement('div'); actions.className = 'dialog-actions'; const rep = document.createElement('button'); rep.textContent = 'Antworten'; rep.onclick = () => { replyTo = { id: c.id, name: c.author_name }; renderComposer(); if (textarea) textarea.focus(); }; const del = document.createElement('button'); del.textContent = 'Löschen'; del.onclick = () => remove(c.id); actions.append(rep, del); card.appendChild(actions); } list.appendChild(card); }); } function renderComposer() { composer.innerHTML = ''; if (token) { if (replyTo) { const r = document.createElement('button'); r.className = 'dialog-replychip'; r.textContent = 'Antwort auf ' + replyTo.name + ' ✕'; r.onclick = () => { replyTo = null; renderComposer(); }; composer.appendChild(r); } textarea = document.createElement('textarea'); textarea.className = 'dialog-textarea'; textarea.placeholder = 'Deine Wortmeldung …'; const row = document.createElement('div'); row.className = 'dialog-row'; const send = document.createElement('button'); send.className = 'dialog-send'; send.textContent = 'Senden'; send.onclick = submit; const out = document.createElement('button'); out.className = 'dialog-logout'; out.textContent = 'Abmelden' + (myName ? ' · ' + myName : ''); out.onclick = logout; row.append(send, out); composer.append(textarea, row); } else { const hint = document.createElement('p'); hint.className = 'dialog-loginhint'; hint.textContent = 'Zum Mitreden anmelden:'; const em = document.createElement('input'); em.type = 'email'; em.placeholder = 'E-Mail'; em.className = 'dialog-input'; const pw = document.createElement('input'); pw.type = 'password'; pw.placeholder = 'Passwort'; pw.className = 'dialog-input'; const btn = document.createElement('button'); btn.className = 'dialog-send'; btn.textContent = 'Anmelden'; btn.onclick = () => doLogin(em.value, pw.value); pw.onkeydown = (e) => { if (e.key === 'Enter') doLogin(em.value, pw.value); }; composer.append(hint, em, pw, btn); } } async function doLogin(email, password) { try { const res = await fetch('/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }), }); const j = await res.json().catch(() => ({})); if (!res.ok) { alert(j.error || 'Login fehlgeschlagen'); return; } token = j.access_token; myName = j.name || ''; localStorage.setItem(TKEY, token); localStorage.setItem(NKEY, myName); renderComposer(); load(); } catch { alert('Login fehlgeschlagen'); } } function logout() { token = null; myName = null; replyTo = null; localStorage.removeItem(TKEY); localStorage.removeItem(NKEY); renderComposer(); load(); } async function submit() { const body = textarea.value.trim(); if (!body) return; const res = await fetch('/api/comments', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + token }, body: JSON.stringify({ thread, body, parent_id: replyTo ? replyTo.id : null }), }); if (res.status === 401) { logout(); alert('Sitzung abgelaufen — bitte neu anmelden.'); return; } const j = await res.json().catch(() => ({})); if (!res.ok) { alert(j.error || 'Senden fehlgeschlagen'); return; } textarea.value = ''; replyTo = null; renderComposer(); load(); } async function remove(id) { if (!confirm('Wortmeldung löschen?')) return; const res = await fetch('/api/comments/' + id, { method: 'DELETE', headers: { Authorization: 'Bearer ' + token } }); if (!res.ok) { const j = await res.json().catch(() => ({})); alert(j.error || 'Löschen fehlgeschlagen'); return; } load(); } renderComposer(); load(); })();