This commit is contained in:
2026-01-11 01:54:38 +01:00
parent aeea724854
commit af865bc6e7
5 changed files with 220 additions and 9 deletions

View File

@@ -23,6 +23,16 @@ function esc(s=''){
.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){
const dlg = document.getElementById('editSnippetDialog');
const form = document.getElementById('editSnippetForm');
@@ -67,6 +77,61 @@ async function openSnippetEditor(id){
dlg && dlg.showModal();
}
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;
@@ -91,12 +156,21 @@ export async function loadList(resource){
list.innerHTML=data.map(item=>{
const name = esc(item.name||'');
const apiName = resource==='templates' ? esc(item.api_name||'') : '';
const apiLine = (resource==='templates' && apiName) ? `<div class='text-xs text-slate-500'>API: ${apiName}</div>` : '';
const nameCell = `<div class='min-w-48'>
<div class='font-medium truncate' title="${name}">${name || '(ohne Name)'}</div>
${apiLine}
</div>`;
const openBtn = (['templates','sections','blocks'].includes(resource))
? `<button class='btn' data-open='${resource}:${item.id}'>Im E-Mail-Editor öffnen</button>` : '';
const editBtn = (resource==='snippets')
? `<button class='btn' data-edit='snippets:${item.id}'>Bearbeiten</button>` : '';
const editTplBtn = (resource==='templates')
? `<button class='btn' data-edit='templates:${item.id}'>Bearbeiten</button>` : '';
const testBtn = resource==='templates'
? `<button class='btn' data-test='${item.id}' data-name='${name}'>Testversand</button>` : '';
@@ -104,10 +178,10 @@ export async function loadList(resource){
const delBtn = `<button class='btn btn-danger' data-del='${resource}:${item.id}' data-name='${name}'>Löschen</button>`;
return `<div class='p-3 flex items-center gap-3'>
<div class='min-w-48 font-medium truncate' title="${name}">${name || '(ohne Name)'}</div>
${nameCell}
<div class='text-xs text-gray-500'>#${item.id}</div>
<div class='text-xs'>${parentBadge(resource,item)}</div>
<div class='ms-auto flex gap-2'>${[openBtn, editBtn, testBtn, prevBtn, delBtn].filter(Boolean).join('')}</div>
<div class='ms-auto flex gap-2'>${[openBtn, editBtn, editTplBtn, testBtn, prevBtn, delBtn].filter(Boolean).join('')}</div>
</div>`;
}).join('');
@@ -138,8 +212,9 @@ export async function loadList(resource){
// edit snippet
list.querySelectorAll('[data-edit]').forEach(b=>b.addEventListener('click', async ()=>{
const [, id] = b.dataset.edit.split(':');
await openSnippetEditor(id);
const [res, id] = b.dataset.edit.split(':');
if (res === 'snippets') await openSnippetEditor(id);
if (res === 'templates') await openTemplateEditor(id);
}));
// preview