import { apiAction, apiUpdate, toast } from './api.js'; function esc(s = '') { return String(s) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function formatVersionDate(value) { if (!value) return ''; try { const date = new Date(value); if (Number.isNaN(date.getTime())) return value; return date.toLocaleString('de-DE'); } catch { return value; } } function normalizeApiName(v = '') { return String(v) .trim() .toLowerCase() .replace(/\s+/g, '-') .replace(/[^a-z0-9_-]+/g, '-') .replace(/-+/g, '-') .replace(/^-|-$/g, ''); } async function fetchContentList(sectionId) { const res = await apiAction('content.list', { method: 'GET', data: { section_id: sectionId } }); return Array.isArray(res?.items) ? res.items : []; } async function fetchContentItem(id, sectionId) { return await apiAction('content.get', { method: 'GET', data: { id, section_id: sectionId } }); } async function openContentEditor(item, section) { const versionId = Number(item?.version_id || 0); const id = Number(item?.id || 0); const name = item?.name || ''; if (!id) return; const detail = await fetchContentItem(id, section.id).catch(() => ({})); const html = detail?.html ?? detail?.item?.html ?? detail?.content ?? ''; window.__currentItemId = id; window.__currentEditorCtx = { id, mode: section.slug, section }; if (window.EditorUI && typeof window.EditorUI.open === 'function') { window.EditorUI.open({ id, name, html, section, version_id: versionId }, 'content'); } else if (window.__openEditor) { window.__openEditor({ resource: 'content', id, name, html, section, version_id: versionId }); } else { toast('Editor ist nicht initialisiert.', false); } } async function openTemplateEditor(item, section) { const dlg = document.getElementById('editTemplateDialog'); const form = document.getElementById('editTemplateForm'); const inpName = document.getElementById('edit_tpl_name'); const inpApiName = document.getElementById('edit_tpl_api_name'); const apiWarn = document.getElementById('edit_tpl_api_warn'); const btnCancel = document.getElementById('editTemplateCancel'); const detail = await fetchContentItem(item.id, section.id).catch(() => ({})); const row = detail?.item || detail?.data || detail || {}; const initialApi = row.api_name || ''; if (inpName) inpName.value = row.name || ''; if (inpApiName) inpApiName.value = initialApi; if (apiWarn) apiWarn.classList.add('hidden'); const onApiInput = () => { if (!inpApiName) return; const next = normalizeApiName(inpApiName.value); if (next !== inpApiName.value) inpApiName.value = next; if (apiWarn) { apiWarn.classList.toggle('hidden', inpApiName.value.trim() === initialApi); } }; function cleanup() { form && form.removeEventListener('submit', onSubmit); btnCancel && (btnCancel.onclick = null); inpApiName && inpApiName.removeEventListener('input', onApiInput); } async function onSubmit(ev) { ev.preventDefault(); try { const res = await apiUpdate('content', item.id, { name: inpName ? inpName.value : '', api_name: inpApiName ? inpApiName.value : '', section_id: section.id, }); toast(res && res.ok ? 'Template gespeichert' : 'Speichern fehlgeschlagen', !!(res && res.ok)); dlg && dlg.close(); cleanup(); if (typeof window.loadList === 'function') window.loadList(section); } catch { toast('Speichern fehlgeschlagen', false); } } inpApiName && inpApiName.addEventListener('input', onApiInput); form && form.addEventListener('submit', onSubmit, { once: false }); btnCancel && (btnCancel.onclick = () => { dlg && dlg.close(); cleanup(); }); dlg && dlg.showModal(); } export async function loadList(section) { const el = document.getElementById('view-content'); if (typeof section === 'string') { const sections = window.__sectionsConfig || []; section = sections.find(s => String(s.slug || '').toLowerCase() === section.toLowerCase()) || sections.find(s => String(s.name || '').toLowerCase() === section.toLowerCase()) || null; } else if (typeof section === 'number') { const sections = window.__sectionsConfig || []; section = sections.find(s => Number(s.id) === section) || null; } section = section || window.__activeSection || null; if (!el || !section) return; const label = section.name || 'Section'; const isTemplate = Number(section.is_template) === 1; el.innerHTML = `
${esc(label)}
Lade …
`; let data = []; try { data = await fetchContentList(section.id); } catch (err) { list.innerHTML = `
${esc(err.message || 'Laden fehlgeschlagen')}
`; return; } const list = el.querySelector('#list-section'); const filterInput = el.querySelector('#filter-section'); const filterReset = el.querySelector('#filter-section-reset'); const sortSelect = el.querySelector('#sort-section'); if (!Array.isArray(data) || data.length === 0) { list.innerHTML = `
Keine Einträge
`; return; } const sortDefault = (window.__listSortDefault || 'created_asc'); if (sortSelect) sortSelect.value = sortDefault; function compareByName(a, b) { const av = String(a?.name || ''); const bv = String(b?.name || ''); return av.localeCompare(bv, undefined, { numeric: true, sensitivity: 'base' }); } function parseDate(value) { const t = Date.parse(value || ''); return Number.isFinite(t) ? t : 0; } function sortItems(items, key) { const listCopy = items.slice(); if (key === 'name_asc') { return listCopy.sort(compareByName); } if (key === 'name_desc') { return listCopy.sort((a, b) => compareByName(b, a)); } if (key === 'updated_desc') { return listCopy.sort((a, b) => parseDate(b.updated_at) - parseDate(a.updated_at)); } return listCopy.sort((a, b) => parseDate(a.created_at) - parseDate(b.created_at)); } function matchesQuery(item, query) { if (!query) return true; const q = query.toLowerCase(); const name = String(item?.name || '').toLowerCase(); const api = isTemplate ? String(item?.api_name || '').toLowerCase() : ''; return name.includes(q) || (api && api.includes(q)); } const versionCache = new Map(); async function loadVersionOptions(selectEl, itemId) { if (!selectEl || !itemId) return; if (versionCache.has(itemId)) { const cached = versionCache.get(itemId); renderVersionSelect(selectEl, cached.items, cached.activeId); return; } try { const res = await apiAction('content_versions.list', { method: 'GET', data: { content_id: itemId } }); const items = Array.isArray(res?.items) ? res.items : []; const active = items.find(v => Number(v.is_active) === 1); const activeId = active ? String(active.id) : (items[0] ? String(items[0].id) : ''); versionCache.set(itemId, { items, activeId }); renderVersionSelect(selectEl, items, activeId); } catch { renderVersionSelect(selectEl, [], ''); } } function renderVersionSelect(selectEl, items, activeId) { selectEl.innerHTML = ''; if (!items.length) { const opt = document.createElement('option'); opt.value = ''; opt.textContent = 'Keine Versionen'; opt.disabled = true; selectEl.appendChild(opt); selectEl.disabled = true; return; } selectEl.disabled = false; items.forEach(item => { const opt = document.createElement('option'); const label = `#${item.version_no} – ${formatVersionDate(item.created_at)}` + (Number(item.is_active) === 1 ? ' (aktiv)' : ''); opt.value = String(item.id); opt.textContent = label; selectEl.appendChild(opt); }); if (activeId) selectEl.value = activeId; } function render(items) { list.innerHTML = items.map(item => { const name = esc(item.name || ''); const apiName = isTemplate ? esc(item.api_name || '') : ''; const apiLine = (isTemplate && apiName) ? `
API: ${apiName}
` : ''; const versionSelect = ``; const nameCell = `
${name || '(ohne Name)'}
${apiLine}
`; const openBtn = ``; const editTplBtn = isTemplate ? `` : ''; const testBtn = isTemplate ? `` : ''; const prevBtn = ``; const delBtn = ``; return `
${nameCell}
#${item.id}
${openBtn} ${versionSelect} ${[editTplBtn, testBtn, prevBtn, delBtn].filter(Boolean).join('')}
`; }).join(''); bindListHandlers(list); } function applyFilter() { const query = (filterInput?.value || '').trim(); const sortKey = sortSelect?.value || sortDefault; const filtered = data.filter(item => matchesQuery(item, query)); const sorted = sortItems(filtered, sortKey); render(sorted); } async function persistSort(nextValue) { window.__listSortDefault = nextValue; try { await apiAction('account.settings.update', { method: 'POST', data: { list_sort: nextValue } }); } catch {} } if (filterInput) filterInput.addEventListener('input', applyFilter); if (filterReset) { filterReset.addEventListener('click', () => { if (filterInput) { filterInput.value = ''; filterInput.focus(); } applyFilter(); }); } if (sortSelect) { sortSelect.addEventListener('change', () => { const next = sortSelect.value || sortDefault; persistSort(next); applyFilter(); }); } applyFilter(); function bindListHandlers(scope) { scope.querySelectorAll('[data-version-select]').forEach(sel => { const id = Number(sel.getAttribute('data-version-select') || 0); loadVersionOptions(sel, id); sel.addEventListener('focus', () => loadVersionOptions(sel, id)); sel.addEventListener('click', () => loadVersionOptions(sel, id)); sel.addEventListener('change', () => { const versionId = Number(sel.value || 0); if (!versionId) return; const item = data.find(it => Number(it.id) === id); if (!item) return; openContentEditor({ ...item, version_id: versionId }, section); }); }); scope.querySelectorAll('[data-open]').forEach(btn => btn.addEventListener('click', () => { const id = Number(btn.dataset.open || 0); const item = data.find(it => Number(it.id) === id); if (!item) return; openContentEditor(item, section); })); scope.querySelectorAll('[data-edit]').forEach(btn => btn.addEventListener('click', () => { const id = Number(btn.dataset.edit || 0); const item = data.find(it => Number(it.id) === id); if (item) openTemplateEditor(item, section); })); const prevDlg = document.getElementById('previewDialog'); const prevFrame = document.getElementById('previewFrame'); scope.querySelectorAll('[data-preview]').forEach(btn => btn.addEventListener('click', async () => { const id = Number(btn.dataset.preview || 0); const obj = await fetchContentItem(id, section.id); const html = (obj?.html || obj?.content || '(leer)'); prevFrame.srcdoc = '' + html + ''; prevDlg.showModal(); })); scope.querySelectorAll('[data-test]').forEach(btn => btn.addEventListener('click', () => { const id = Number(btn.dataset.test || 0); const nm = btn.dataset.name || ''; if (!id) { toast('Testversand: Ungültige ID', false); return; } if (window.AdminTestSend && typeof window.AdminTestSend.open === 'function') { window.AdminTestSend.open({ id, name: nm }); } else { toast('Testversand ist aktuell nicht verfügbar.', false); } })); const delDlg = document.getElementById('deleteDialog'); const delText = document.getElementById('deleteText'); const delForm = document.getElementById('deleteForm'); const delCancel = document.getElementById('deleteCancel'); let pending = null; delCancel && (delCancel.onclick = () => { pending = null; delDlg.close(); }); scope.querySelectorAll('[data-del]').forEach(btn => btn.addEventListener('click', () => { const id = Number(btn.dataset.del || 0); const nm = btn.dataset.name || ''; pending = { id, name: nm }; if (delText) { delText.innerHTML = `Soll ${nm || '(ohne Name)'} #${id} wirklich gelöscht werden?`; } delDlg.showModal(); })); delForm && (delForm.onsubmit = async (ev) => { ev.preventDefault(); if (!pending) return delDlg.close(); const res = await apiAction('content.delete', { method: 'POST', data: { id: pending.id, section_id: section.id } }); delDlg.close(); toast(res && res.ok ? 'Gelöscht' : 'Löschen fehlgeschlagen', !!(res && res.ok), { duration: 3000 }); loadList(section); }); } } export function initLists() { if (window.__sectionsReady && typeof window.__sectionsReady.then === 'function') { window.__sectionsReady.then(() => { if (window.__activeSection) loadList(window.__activeSection); }); } else if (window.__activeSection) { loadList(window.__activeSection); } window.__reloadList = loadList; window.loadList = loadList; }