let friends = []; let shareTargetId = null; let settings = { publicBaseUrl: '' }; async function checkSession() { const res = await fetch('/api/session'); const data = await res.json(); if (data.role !== 'admin') { window.location.href = '/'; } } // Einstellungen werden im Hintergrund geladen (für den Freigabe-Link), // bearbeitet werden sie im separaten "Einstellungen"-Tab. async function loadSettings() { const res = await fetch('/api/settings'); if (!res.ok) return; const data = await res.json(); settings = data.settings || { publicBaseUrl: '', bookTitle: '' }; } function baseUrl() { return settings.publicBaseUrl && settings.publicBaseUrl.length ? settings.publicBaseUrl : window.location.origin; } async function loadFriends() { const res = await fetch('/api/friends'); if (res.status === 401) { window.location.href = '/'; return; } const data = await res.json(); friends = data.friends || []; renderFriends(); } function renderFriends() { const grid = document.getElementById('friendGrid'); const emptyMsg = document.getElementById('emptyMsg'); grid.innerHTML = ''; emptyMsg.style.display = friends.length ? 'none' : 'block'; friends.forEach((f, idx) => { const el = document.createElement('div'); el.className = 'friend-card'; const thumbHtml = f.profileImage ? `` : `
${(f.name || '?')[0].toUpperCase()}
`; el.innerHTML = `
${idx + 1}
${thumbHtml}

${escapeHtml(f.name)}

${f.shareEnabled ? 'Freigegeben' : 'Nicht freigegeben'}
✏️ Bearbeiten
`; grid.appendChild(el); }); grid.querySelectorAll('[data-share]').forEach((btn) => { btn.addEventListener('click', () => openShareModal(btn.dataset.share)); }); grid.querySelectorAll('[data-delete]').forEach((btn) => { btn.addEventListener('click', () => deleteFriend(btn.dataset.delete)); }); grid.querySelectorAll('[data-export]').forEach((btn) => { btn.addEventListener('click', () => window.location.href = `/api/friends/${btn.dataset.export}/export`); }); grid.querySelectorAll('[data-moveup]').forEach((btn) => { btn.addEventListener('click', () => moveFriend(btn.dataset.moveup, -1)); }); grid.querySelectorAll('[data-movedown]').forEach((btn) => { btn.addEventListener('click', () => moveFriend(btn.dataset.movedown, 1)); }); } async function moveFriend(id, dir) { const idx = friends.findIndex((f) => f.id === id); const swapIdx = idx + dir; if (idx === -1 || swapIdx < 0 || swapIdx >= friends.length) return; const newOrder = [...friends]; [newOrder[idx], newOrder[swapIdx]] = [newOrder[swapIdx], newOrder[idx]]; const order = newOrder.map((f) => f.id); const res = await fetch('/api/friends/reorder', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ order }), }); if (res.ok) loadFriends(); } // wir brauchen den vollen Ordnernamen für Bild-URLs; da wir ihn nicht separat liefern, // nutzen wir /uploads/-Lookup über eine kleine Konvention: Backend akzeptiert auch // reine IDs nicht direkt, daher holen wir folderName aus friend-detail bei Bedarf. // Einfachere Lösung: Backend liefert profileImage-Pfad bereits vollständig auf. function folderFor(f) { return f.folderName || f.id; } function escapeHtml(str) { const div = document.createElement('div'); div.textContent = str || ''; return div.innerHTML; } document.getElementById('createForm').addEventListener('submit', async (e) => { e.preventDefault(); const name = document.getElementById('newName').value.trim(); const errBox = document.getElementById('createErr'); errBox.innerHTML = ''; if (!name) return; const res = await fetch('/api/friends', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name }), }); const data = await res.json(); if (!res.ok) { errBox.innerHTML = `

${data.error}

`; return; } document.getElementById('newName').value = ''; loadFriends(); }); async function deleteFriend(id) { const f = friends.find((x) => x.id === id); if (!confirm(`"${f ? f.name : 'Freund'}" wirklich löschen? Alle Fotos und Einträge gehen dabei verloren.`)) return; const res = await fetch(`/api/friends/${id}`, { method: 'DELETE' }); if (res.ok) loadFriends(); } function openShareModal(id) { shareTargetId = id; const f = friends.find((x) => x.id === id); document.getElementById('shareFriendName').textContent = f ? f.name : ''; document.getElementById('sharePw').value = ''; document.getElementById('shareErr').innerHTML = ''; const linkBox = document.getElementById('shareLinkBox'); if (f && f.shareEnabled) { linkBox.style.display = 'block'; document.getElementById('shareLinkInput').value = `${baseUrl()}/share/${id}`; } else { linkBox.style.display = 'none'; } document.getElementById('shareModal').style.display = 'flex'; } document.getElementById('shareCloseBtn').addEventListener('click', () => { document.getElementById('shareModal').style.display = 'none'; }); document.getElementById('shareEnableBtn').addEventListener('click', async () => { const pw = document.getElementById('sharePw').value; const errBox = document.getElementById('shareErr'); errBox.innerHTML = ''; const res = await fetch(`/api/friends/${shareTargetId}/share`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ enabled: true, password: pw }), }); const data = await res.json(); if (!res.ok) { errBox.innerHTML = `

${data.error}

`; return; } document.getElementById('shareLinkBox').style.display = 'block'; document.getElementById('shareLinkInput').value = `${baseUrl()}/share/${shareTargetId}`; await loadFriends(); }); document.getElementById('shareDisableBtn').addEventListener('click', async () => { const res = await fetch(`/api/friends/${shareTargetId}/share`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ enabled: false }), }); if (res.ok) { document.getElementById('shareLinkBox').style.display = 'none'; await loadFriends(); } }); document.getElementById('copyLinkBtn').addEventListener('click', () => { const input = document.getElementById('shareLinkInput'); input.select(); navigator.clipboard.writeText(input.value).catch(() => {}); }); document.getElementById('logoutBtn').addEventListener('click', async () => { await fetch('/api/logout', { method: 'POST' }); window.location.href = '/'; }); checkSession(); loadSettings(); loadFriends();