import { $, api, esc, showError, renderHeader, renderFooter, isAdmin, prepareImage } from './common.js'; renderHeader('patches'); renderFooter(); let patches = []; let admin = false; // nur der Admin-Zugang darf Patches hochladen, ändern, löschen let editingId = null; let imageData = null; // data:-URL des gewählten (verkleinerten) Bildes const imgUrl = (p) => `/patch-img/${encodeURIComponent(p.image)}`; // ---------- Galerie ---------- function render() { $('#patch-empty').hidden = patches.length > 0; $('#patch-empty-hint').textContent = admin ? 'Lade über „Patch hochladen“ den ersten Patch hoch.' : 'Sobald der Admin einen Patch hochlädt, erscheint er hier.'; for (const status of ['vorhanden', 'geplant']) { const list = patches.filter((p) => p.status === status); $(`#group-${status}`).hidden = list.length === 0; $(`#count-${status}`).textContent = list.length === 1 ? '1 Patch' : `${list.length} Patches`; $(`#grid-${status}`).innerHTML = list.map(patchHtml).join(''); } } function patchHtml(p) { return `
${p.status === 'geplant' ? 'In Planung' : ''}

${esc(p.name)}

${p.description ? `

${esc(p.description)}

` : ''} ${admin ? `
` : ''}
`; } document.addEventListener('click', async (e) => { if (!e.target.closest('.patch-grid')) return; const zoom = e.target.closest('[data-zoom]'); if (zoom) return openLightbox(zoom.dataset.zoom); const edit = e.target.closest('[data-edit]'); if (edit) return openForm(patches.find((p) => p.id === edit.dataset.edit)); const del = e.target.closest('[data-delete]'); if (del) { const p = patches.find((x) => x.id === del.dataset.delete); if (!confirm(`Patch „${p.name}“ wirklich löschen?`)) return; try { await api('DELETE', `/patches/${p.id}`); await load(); } catch (err) { showError(err); } } }); function openLightbox(id) { const p = patches.find((x) => x.id === id); if (!p) return; $('#lightbox-img').src = imgUrl(p); $('#lightbox-img').alt = p.name; $('#lightbox-name').textContent = p.name; $('#lightbox-text').textContent = p.description; $('#lightbox').showModal(); } // Klick neben das Bild schließt $('#lightbox').addEventListener('click', (e) => { if (e.target === e.currentTarget) e.currentTarget.close(); }); // ---------- Formular ---------- function openForm(patch) { editingId = patch?.id ?? null; imageData = null; $('#patch-form').reset(); $('#patch-error').hidden = true; $('#patch-form-title').textContent = patch ? 'Patch bearbeiten' : 'Patch hochladen'; $('#patch-submit').textContent = patch ? 'Speichern' : 'Hochladen'; $('#patch-name').value = patch?.name ?? ''; $('#patch-description').value = patch?.description ?? ''; $(`#patch-status-${patch?.status ?? 'vorhanden'}`).checked = true; showPreview(patch ? imgUrl(patch) : null); $('#patch-form-card').hidden = false; $('#patch-form-card').scrollIntoView({ behavior: 'smooth', block: 'start' }); $('#patch-name').focus({ preventScroll: true }); } function closeForm() { $('#patch-form-card').hidden = true; editingId = null; imageData = null; } function showPreview(src) { $('#patch-preview').hidden = !src; if (src) $('#patch-preview').src = src; $('#drop-text').innerHTML = src ? 'Anderes Bild wählen' : 'Bild auswählen
oder hierher ziehen · PNG, JPEG, WebP oder SVG'; } async function pickFile(file) { if (!file) return; $('#patch-error').hidden = true; try { imageData = await prepareImage(file); showPreview(imageData); if (!$('#patch-name').value) $('#patch-name').value = file.name.replace(/\.[^.]+$/, '').replace(/[-_]+/g, ' ').slice(0, 80); } catch (err) { imageData = null; $('#patch-error').textContent = err.message || 'Das Bild konnte nicht gelesen werden.'; $('#patch-error').hidden = false; } } $('#patch-file').addEventListener('change', (e) => pickFile(e.target.files[0])); const drop = $('#dropzone'); drop.addEventListener('dragover', (e) => { e.preventDefault(); drop.classList.add('over'); }); drop.addEventListener('dragleave', () => drop.classList.remove('over')); drop.addEventListener('drop', (e) => { e.preventDefault(); drop.classList.remove('over'); pickFile(e.dataTransfer.files[0]); }); $('#add-btn').addEventListener('click', () => openForm(null)); $('#patch-cancel').addEventListener('click', closeForm); $('#patch-form').addEventListener('submit', async (e) => { e.preventDefault(); const error = $('#patch-error'); error.hidden = true; if (!editingId && !imageData) { error.textContent = 'Bitte zuerst ein Bild auswählen.'; error.hidden = false; return; } const button = $('#patch-submit'); button.disabled = true; try { const body = { name: $('#patch-name').value, description: $('#patch-description').value, status: document.querySelector('input[name="patch-status"]:checked').value, }; if (imageData) body.image = imageData; if (editingId) await api('PUT', `/patches/${editingId}`, body); else await api('POST', '/patches', body); closeForm(); await load(); } catch (err) { error.textContent = err.message; error.hidden = false; } finally { button.disabled = false; } }); // ---------- Laden ---------- async function load() { patches = await api('GET', '/patches'); render(); } admin = await isAdmin(); $('#add-btn').hidden = !admin; await load().catch(showError); // Link von der Startseite (/patches#id) springt zum Patch if (location.hash) document.getElementById(location.hash.slice(1))?.scrollIntoView({ block: 'center' });