import { apiList, apiGet, apiDelete, apiUpdate, apiAction, toast } from './api.js'; function formatUsage(usage){ if (!usage || !usage.total) return ''; const parts=[]; if (usage.templates) parts.push(`${usage.templates} Template${usage.templates!==1?'s':''}`); if (usage.sections) parts.push(`${usage.sections} Section${usage.sections!==1?'s':''}`); if (usage.blocks) parts.push(`${usage.blocks} Block${usage.blocks!==1?'s':''}`); if (usage.snippets) parts.push(`${usage.snippets} Snippet${usage.snippets!==1?'s':''}`); if (!parts.length) return ''; return `
Dieses Element wird aktuell verwendet in: ${parts.join(', ')}.
Das Löschen entfernt diese Referenzen.
`; } function esc(s=''){ return String(s) .replace(/&/g,'&') .replace(//g,'>') .replace(/"/g,'"') .replace(/'/g,'''); } function normalizeApiName(v=''){ return String(v) .trim() .toLowerCase() .replace(/\s+/g,'-') .replace(/[^a-z0-9_-]+/g,'-') .replace(/-+/g,'-') .replace(/^-|-$/g,''); } async function openSnippetEditor(id){ let resp = {}; try { resp = await apiGet('snippets', id) || {}; } catch(e){} const row = resp?.item || resp?.data || resp || {}; const name = row?.name || ''; if (window.EditorUI && typeof window.EditorUI.open === 'function') { window.EditorUI.open({ id: Number(id), name }, 'snippets'); return; } if (window.__openEditor) { window.__openEditor({ resource: 'snippets', id: Number(id), name }); return; } console.warn('Kein Editor-Entry-Point gefunden (EditorUI.open / __openEditor).'); toast('Editor ist nicht initialisiert.', false); } async function openTemplateEditor(id){ 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'); let resp = {}; try { resp = await apiGet('templates', id) || {}; } catch(e){} const row = resp?.item || resp?.data || resp || {}; 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('templates', id, { name: inpName ? inpName.value : '', api_name: inpApiName ? inpApiName.value : '' }); toast(res && res.ok ? 'Template gespeichert' : 'Speichern fehlgeschlagen', !!(res && res.ok)); dlg && dlg.close(); cleanup(); loadList('templates'); }catch(e){ 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(resource){ const el=document.getElementById(`view-${resource}`); if(!el) return; const label = resource.charAt(0).toUpperCase()+resource.slice(1); el.innerHTML=`
${label}
Lade …
`; const data=await apiList(resource); const list=el.querySelector(`#list-${resource}`); const filterInput=el.querySelector(`#filter-${resource}`); const filterReset=el.querySelector(`#filter-${resource}-reset`); const sortSelect=el.querySelector(`#sort-${resource}`); 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 = resource === 'templates' ? String(item?.api_name || '').toLowerCase() : ''; return name.includes(q) || (api && api.includes(q)); } function parentBadge(r,it){ if(r==='sections'&&it.template_id) return ` Template #${it.template_id}${it.template_name ? ' · '+esc(it.template_name) : ''}`; if(r==='blocks'&&it.section_id) return ` Section #${it.section_id}${it.section_name ? ' · '+esc(it.section_name) : ''}`; if(r==='snippets'&&it.block_id) return ` Block #${it.block_id}${it.block_name ? ' · '+esc(it.block_name) : ''}`; return ' frei'; } function render(items){ list.innerHTML=items.map(item=>{ const name = esc(item.name||''); const apiName = resource==='templates' ? esc(item.api_name||'') : ''; const apiLine = (resource==='templates' && apiName) ? `
API: ${apiName}
` : ''; const nameCell = `
${name || '(ohne Name)'}
${apiLine}
`; const openBtn = (['templates','sections','blocks'].includes(resource)) ? `` : ''; const editBtn = (resource==='snippets') ? `` : ''; const editTplBtn = (resource==='templates') ? `` : ''; const testBtn = resource==='templates' ? `` : ''; const prevBtn = ``; const delBtn = ``; return `
${nameCell}
#${item.id}
${parentBadge(resource,item)}
${[openBtn, editBtn, editTplBtn, testBtn, prevBtn, delBtn].filter(Boolean).join('')}
`; }).join(''); bindListHandlers(list, resource); } 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(); const delDlg=document.getElementById('deleteDialog'), delText=document.getElementById('deleteText'), delForm=document.getElementById('deleteForm'), delCancel=document.getElementById('deleteCancel'); let pending=null; delCancel && (delCancel.onclick=()=>{pending=null;delDlg.close();}); // --- Editor öffnen (ANPASSUNG) ----------------------------------------- function bindListHandlers(scope, resName){ scope.querySelectorAll('[data-open]').forEach(b=>b.addEventListener('click', async ()=>{ const [res,id]=b.dataset.open.split(':'); // Detail laden, um Name + aktuellen HTML/Content zu haben const obj = await apiGet(res,id); const name = obj?.name || ''; const html = obj ? (obj.html ?? obj.content ?? '') : ''; // Globale Kontexte (werden von Editor/anderen Modulen genutzt) window.__currentItemId = Number(id); window.__currentEditorCtx = { id:Number(id), mode:res }; // Bevorzugt EditorUI.open nutzen; Fallback: __openEditor (Bestand) if (window.EditorUI && typeof window.EditorUI.open === 'function') { window.EditorUI.open({ id:Number(id), name, html }, res); } else if (window.__openEditor) { window.__openEditor({ resource:res, id:Number(id), name, html }); } else { console.warn('Kein Editor-Entry-Point gefunden (EditorUI.open / __openEditor).'); toast('Editor ist nicht initialisiert.', false); } })); // ----------------------------------------------------------------------- // edit snippet scope.querySelectorAll('[data-edit]').forEach(b=>b.addEventListener('click', async ()=>{ const [res, id] = b.dataset.edit.split(':'); if (res === 'snippets') await openSnippetEditor(id); if (res === 'templates') await openTemplateEditor(id); })); // preview const prevDlg=document.getElementById('previewDialog'), prevFrame=document.getElementById('previewFrame'); scope.querySelectorAll('[data-preview]').forEach(b=>b.addEventListener('click', async ()=>{ const [res,id]=b.dataset.preview.split(':'); const obj=await apiGet(res,id); const html=(obj?.html||obj?.content||'(leer)'; prevDlg.showModal(); })); // test send (templates only) scope.querySelectorAll('[data-test]').forEach(b=>b.addEventListener('click', ()=>{ const id = Number(b.dataset.test || '0'); const nm = b.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); } })); // delete scope.querySelectorAll('[data-del]').forEach(b=>b.addEventListener('click', async ()=>{ const [res,id]=b.dataset.del.split(':'); const nm=b.dataset.name||''; let usage = null; try { const detail = await apiGet(res, id); usage = detail?.usage || null; } catch {} pending={res,id,nm,usage}; const usageWarn = formatUsage(usage); delText && (delText.innerHTML=`Soll ${nm || '(ohne Name)'} #${id} aus ${res} wirklich gelöscht werden?
Achtung: Kinder-Elemente werden nicht automatisch mit gelöscht.${usageWarn}`); delDlg.showModal(); })); } delForm && (delForm.onsubmit=async(e)=>{ e.preventDefault(); if(!pending) return delDlg.close(); const r=await apiDelete(pending.res,pending.id); delDlg.close(); toast(r&&r.ok?'Gelöscht':'Löschen fehlgeschlagen', !!(r&&r.ok), {duration:3000}); loadList(resource); }); } export function initLists(){ loadList('templates'); // Public reload helper (wird vom Snippet-Editor genutzt) window.__reloadList = loadList; // Backwards compat (falls woanders genutzt) window.loadList = loadList; }