iüpdate
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { apiList, apiGet, apiDelete, apiUpdate, toast } from './api.js';
|
||||
import { apiList, apiGet, apiDelete, apiUpdate, apiAction, toast } from './api.js';
|
||||
|
||||
function formatUsage(usage){
|
||||
if (!usage || !usage.total) return '';
|
||||
@@ -109,18 +109,72 @@ async function openTemplateEditor(id){
|
||||
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=`<div class='rounded-2xl border bg-white overflow-hidden'>
|
||||
<div class='px-4 py-2 border-b bg-gray-50 text-sm font-medium'>${resource.charAt(0).toUpperCase()+resource.slice(1)}</div>
|
||||
<div class='px-4 py-2 border-b bg-gray-50 text-sm font-medium flex items-center gap-3'>
|
||||
<span>${label}</span>
|
||||
<div class='ms-auto flex items-center gap-2'>
|
||||
<div class='flex items-center gap-1'>
|
||||
<input id='filter-${resource}' class='input text-sm' placeholder='Suche Name/API' />
|
||||
<button id='filter-${resource}-reset' class='btn' type='button' title='Suche zurücksetzen'>×</button>
|
||||
</div>
|
||||
<select id='sort-${resource}' class='input text-sm'>
|
||||
<option value='created_asc'>Erstelldatum (aufsteigend)</option>
|
||||
<option value='name_asc'>Name A–Z</option>
|
||||
<option value='name_desc'>Name Z–A</option>
|
||||
<option value='updated_desc'>Zuletzt bearbeitet</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div id='list-${resource}' class='divide-y'>Lade …</div></div>`;
|
||||
|
||||
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=`<div class='p-4 text-sm text-gray-500'>Keine Einträge</div>`;
|
||||
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 `<span class="chip"><span class="dot"></span> Template #${it.template_id}${it.template_name ? ' · '+esc(it.template_name) : ''}</span>`;
|
||||
if(r==='blocks'&&it.section_id) return `<span class="chip"><span class="dot"></span> Section #${it.section_id}${it.section_name ? ' · '+esc(it.section_name) : ''}</span>`;
|
||||
@@ -128,7 +182,8 @@ export async function loadList(resource){
|
||||
return '<span class="chip"><span class="dot"></span> frei</span>';
|
||||
}
|
||||
|
||||
list.innerHTML=data.map(item=>{
|
||||
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) ? `<div class='text-xs text-slate-500'>API: ${apiName}</div>` : '';
|
||||
@@ -159,8 +214,49 @@ export async function loadList(resource){
|
||||
</div>`;
|
||||
}).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();
|
||||
|
||||
// --- Editor öffnen (ANPASSUNG) -----------------------------------------
|
||||
list.querySelectorAll('[data-open]').forEach(b=>b.addEventListener('click', async ()=>{
|
||||
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
|
||||
@@ -185,7 +281,7 @@ export async function loadList(resource){
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
// edit snippet
|
||||
list.querySelectorAll('[data-edit]').forEach(b=>b.addEventListener('click', async ()=>{
|
||||
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);
|
||||
@@ -193,7 +289,7 @@ export async function loadList(resource){
|
||||
|
||||
// preview
|
||||
const prevDlg=document.getElementById('previewDialog'), prevFrame=document.getElementById('previewFrame');
|
||||
list.querySelectorAll('[data-preview]').forEach(b=>b.addEventListener('click', async ()=>{
|
||||
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||'<em>(leer)</em)');
|
||||
@@ -202,7 +298,7 @@ export async function loadList(resource){
|
||||
}));
|
||||
|
||||
// test send (templates only)
|
||||
list.querySelectorAll('[data-test]').forEach(b=>b.addEventListener('click', ()=>{
|
||||
scope.querySelectorAll('[data-test]').forEach(b=>b.addEventListener('click', ()=>{
|
||||
const id = Number(b.dataset.test || '0');
|
||||
const nm = b.dataset.name || '';
|
||||
if (!id) {
|
||||
@@ -225,7 +321,7 @@ export async function loadList(resource){
|
||||
let pending=null;
|
||||
delCancel && (delCancel.onclick=()=>{pending=null;delDlg.close();});
|
||||
|
||||
list.querySelectorAll('[data-del]').forEach(b=>b.addEventListener('click', async ()=>{
|
||||
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 {
|
||||
@@ -237,7 +333,7 @@ export async function loadList(resource){
|
||||
delText && (delText.innerHTML=`Soll <strong>${nm || '(ohne Name)'} #${id}</strong> aus <strong>${res}</strong> wirklich gelöscht werden?<br><span class="text-rose-600">Achtung:</span> Kinder-Elemente werden <em>nicht</em> automatisch mit gelöscht.${usageWarn}`);
|
||||
delDlg.showModal();
|
||||
}));
|
||||
|
||||
}
|
||||
delForm && (delForm.onsubmit=async(e)=>{
|
||||
e.preventDefault();
|
||||
if(!pending) return delDlg.close();
|
||||
|
||||
Reference in New Issue
Block a user