up
This commit is contained in:
@@ -3,10 +3,38 @@ export function initCreate(){
|
||||
const btn=document.getElementById('btn-new'), dlg=document.getElementById('createDialog'), form=document.getElementById('createForm'), fields=document.getElementById('createFields'), hint=document.getElementById('createHint');
|
||||
if(!btn||!dlg||!form||!fields) return;
|
||||
const curTab=()=>{ const a=document.querySelector('nav [data-tab].bg-sky-50')||document.querySelector('nav [data-tab]'); return a?a.getAttribute('data-tab'):'templates'; };
|
||||
const normalizeApiName=(v='')=>{
|
||||
return String(v)
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.replace(/\s+/g,'-')
|
||||
.replace(/[^a-z0-9_-]+/g,'-')
|
||||
.replace(/-+/g,'-')
|
||||
.replace(/^-|-$/g,'');
|
||||
};
|
||||
|
||||
btn.onclick = async ()=>{
|
||||
fields.innerHTML=''; const tab=curTab();
|
||||
const name=document.createElement('input'); name.type='text'; name.required=true; name.placeholder='Name*'; name.className='w-full border rounded-lg px-3 py-2'; name.id='f-name'; fields.appendChild(name);
|
||||
let apiName = null;
|
||||
let apiTouched = false;
|
||||
if(tab==='templates'){
|
||||
apiName=document.createElement('input');
|
||||
apiName.type='text';
|
||||
apiName.required=true;
|
||||
apiName.placeholder='API Name* (ohne Leerzeichen)';
|
||||
apiName.className='w-full border rounded-lg px-3 py-2';
|
||||
apiName.id='f-api-name';
|
||||
fields.appendChild(apiName);
|
||||
apiName.addEventListener('input', ()=>{
|
||||
apiTouched = true;
|
||||
const next = normalizeApiName(apiName.value);
|
||||
if (next !== apiName.value) apiName.value = next;
|
||||
});
|
||||
name.addEventListener('input', ()=>{
|
||||
if (!apiTouched) apiName.value = normalizeApiName(name.value);
|
||||
});
|
||||
}
|
||||
async function addSel(id,label,res){ const sel=document.createElement('select'); sel.id=id; sel.className='w-full border rounded-lg px-3 py-2'; sel.innerHTML=`<option value="">(ohne ${label}-Zuordnung)</option>`; const data=await apiList(res); (data||[]).forEach(t=>{ const o=document.createElement('option'); o.value=t.id; o.textContent=`#${t.id} · ${t.name||''}`; sel.appendChild(o); }); fields.appendChild(sel); }
|
||||
if(tab==='sections') await addSel('f-template','Template','templates');
|
||||
if(tab==='blocks') await addSel('f-section','Section','sections');
|
||||
@@ -15,6 +43,10 @@ export function initCreate(){
|
||||
|
||||
form.onsubmit=async(e)=>{ e.preventDefault();
|
||||
const payload={ name:(document.getElementById('f-name')?.value||'').trim() }; if(!payload.name) return;
|
||||
if(tab==='templates') {
|
||||
payload.api_name=(document.getElementById('f-api-name')?.value||'').trim();
|
||||
if(!payload.api_name) return;
|
||||
}
|
||||
if(tab==='snippets') payload.content=''; else payload.html='';
|
||||
if(tab==='sections') payload.template_id=document.getElementById('f-template')?.value||null;
|
||||
if(tab==='blocks') payload.section_id =document.getElementById('f-section')?.value ||null;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user