ads
This commit is contained in:
@@ -99,13 +99,12 @@ export async function loadList(resource){
|
||||
|
||||
const prevBtn = `<button class='btn' data-preview='${resource}:${item.id}'>Vorschau</button>`;
|
||||
const delBtn = `<button class='btn btn-danger' data-del='${resource}:${item.id}' data-name='${name}'>Löschen</button>`;
|
||||
const debugBtn= `<a class='btn' href='api.php?resource=${resource}&action=get&id=${item.id}' target='_blank' rel='noopener'>GET</a>`;
|
||||
|
||||
return `<div class='p-3 flex items-center gap-3'>
|
||||
<div class='min-w-48 font-medium truncate' title="${name}">${name || '(ohne Name)'}</div>
|
||||
<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, prevBtn, delBtn, debugBtn].filter(Boolean).join('')}</div>
|
||||
<div class='ms-auto flex gap-2'>${[openBtn, editBtn, prevBtn, delBtn].filter(Boolean).join('')}</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
// assets/js/ui-tools.js
|
||||
// Öffnet API-Health (JSON), DB-Doctor (Iframe) & beliebige JSON-GETs im Popup,
|
||||
// ohne die Seite zu verlassen. Links bleiben als Fallback nutzbar.
|
||||
|
||||
(function () {
|
||||
const dlg = document.getElementById('toolsDialog');
|
||||
if (!dlg) return;
|
||||
|
||||
const title = document.getElementById('toolsTitle');
|
||||
const btnX = document.getElementById('toolsClose');
|
||||
const btnCopy = document.getElementById('toolsCopy');
|
||||
const btnDl = document.getElementById('toolsDownload');
|
||||
|
||||
const jsonWrap = document.getElementById('toolsJsonWrap');
|
||||
const jsonPre = document.getElementById('toolsJsonPre');
|
||||
const frame = document.getElementById('toolsFrame');
|
||||
|
||||
function showJson(obj, ttl) {
|
||||
title.textContent = ttl || 'Antwort (JSON)';
|
||||
const txt = (typeof obj === 'string') ? obj : JSON.stringify(obj, null, 2);
|
||||
jsonPre.textContent = txt;
|
||||
jsonWrap.classList.remove('hidden');
|
||||
frame.classList.add('hidden');
|
||||
btnCopy.classList.remove('hidden');
|
||||
btnDl.classList.remove('hidden');
|
||||
try { dlg.showModal(); } catch {}
|
||||
}
|
||||
|
||||
function showFrame(url, ttl) {
|
||||
title.textContent = ttl || 'Werkzeug';
|
||||
frame.src = url + (url.includes('?') ? '&' : '?') + 't=' + Date.now();
|
||||
frame.classList.remove('hidden');
|
||||
jsonWrap.classList.add('hidden');
|
||||
btnCopy.classList.add('hidden');
|
||||
btnDl.classList.add('hidden');
|
||||
try { dlg.showModal(); } catch {}
|
||||
}
|
||||
|
||||
btnX?.addEventListener('click', () => {
|
||||
try { dlg.close(); } catch {}
|
||||
frame.src = 'about:blank';
|
||||
});
|
||||
|
||||
btnCopy?.addEventListener('click', async () => {
|
||||
const txt = jsonPre.textContent || '';
|
||||
try {
|
||||
await navigator.clipboard.writeText(txt);
|
||||
// optional: kleines Feedback
|
||||
(window.Toast?.show || window.toast || (()=>{}))('In Zwischenablage kopiert');
|
||||
} catch {}
|
||||
});
|
||||
|
||||
btnDl?.addEventListener('click', () => {
|
||||
const blob = new Blob([jsonPre.textContent || ''], { type:'application/json;charset=utf-8' });
|
||||
const a = document.createElement('a');
|
||||
a.href = URL.createObjectURL(blob);
|
||||
const stamp = new Date().toISOString().replace(/[:.]/g,'-');
|
||||
a.download = `response-${stamp}.json`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
setTimeout(()=>{ URL.revokeObjectURL(a.href); a.remove(); }, 0);
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// 1) Offizieller Weg: Links mit data-popup="json" | "frame"
|
||||
// ------------------------------------------------------------
|
||||
document.addEventListener('click', async (ev) => {
|
||||
const a = ev.target.closest('a[data-popup]');
|
||||
if (!a) return;
|
||||
|
||||
const mode = (a.getAttribute('data-popup') || '').toLowerCase();
|
||||
const href = a.getAttribute('href') || '#';
|
||||
const ttl = a.getAttribute('data-title') || a.textContent.trim() || 'Werkzeug';
|
||||
|
||||
if (!/^json|frame$/.test(mode)) return;
|
||||
// Cmd/Strg-Klick & Mittelklick respektieren (neuer Tab)
|
||||
if (ev.metaKey || ev.ctrlKey || ev.button === 1) return;
|
||||
|
||||
ev.preventDefault();
|
||||
|
||||
if (mode === 'frame') {
|
||||
showFrame(href, ttl);
|
||||
return;
|
||||
}
|
||||
|
||||
// mode === 'json'
|
||||
try {
|
||||
const r = await fetch(href, { credentials: 'include' });
|
||||
const txt = await r.text();
|
||||
let data = null;
|
||||
try { data = JSON.parse(txt); } catch { data = txt; }
|
||||
showJson(data, ttl);
|
||||
} catch (e) {
|
||||
showJson({ ok:false, error: String(e) }, ttl);
|
||||
}
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// 2) Fallback: *alle* API-GET-Links (action=get) ohne data-popup
|
||||
// -> automatisch im JSON-Popup öffnen
|
||||
// ------------------------------------------------------------
|
||||
document.addEventListener('click', async (ev) => {
|
||||
const a = ev.target.closest('a');
|
||||
if (!a) return;
|
||||
// bereits oben behandelt
|
||||
if (a.hasAttribute('data-popup')) return;
|
||||
|
||||
const href = a.getAttribute('href') || '';
|
||||
// nur api.php-GET-Routen mit action=get abfangen
|
||||
if (!/api\.php/i.test(href) || !/[?&]action=get(&|$)/i.test(href)) return;
|
||||
|
||||
// Cmd/Strg/Mittelklick respektieren
|
||||
if (ev.metaKey || ev.ctrlKey || ev.button === 1) return;
|
||||
|
||||
ev.preventDefault();
|
||||
|
||||
const makeTitle = () => {
|
||||
try {
|
||||
const u = new URL(href, location.href);
|
||||
const res = u.searchParams.get('resource') || 'resource';
|
||||
const id = u.searchParams.get('id') || '';
|
||||
// Optional: Name aus data-title wenn vorhanden
|
||||
const custom = a.getAttribute('data-title');
|
||||
return custom || `GET ${res}${id ? ` #${id}` : ''}`;
|
||||
} catch { return 'GET'; }
|
||||
};
|
||||
|
||||
const title = makeTitle();
|
||||
|
||||
// Popup öffnen (wie oben)
|
||||
try {
|
||||
const r = await fetch(href, { credentials: 'include' });
|
||||
const txt = await r.text();
|
||||
let data = null;
|
||||
try { data = JSON.parse(txt); } catch { data = txt; }
|
||||
showJson(data, title);
|
||||
} catch (e) {
|
||||
showJson({ ok:false, error: String(e) }, title);
|
||||
}
|
||||
}, true);
|
||||
|
||||
// Utility: Öffnen aus Code
|
||||
window.AdminTools = {
|
||||
openJson(url, title) {
|
||||
fetch(url, { credentials: 'include' })
|
||||
.then(r => r.text())
|
||||
.then(txt => {
|
||||
try { showJson(JSON.parse(txt), title); }
|
||||
catch { showJson(txt, title); }
|
||||
})
|
||||
.catch(err => showJson({ ok:false, error:String(err) }, title));
|
||||
},
|
||||
openFrame(url, title) {
|
||||
showFrame(url, title);
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -49,17 +49,6 @@ $assetVersion = defined('ASSET_VERSION') ? ASSET_VERSION : time();
|
||||
</nav>
|
||||
<div class="ms-auto flex gap-2">
|
||||
<button id="btn-new" type="button" class="btn">Neu …</button>
|
||||
|
||||
<!-- Tools: werden von ui-tools.js abgefangen und im Popup gezeigt -->
|
||||
<a href="api.php?action=health"
|
||||
class="btn btn-light"
|
||||
data-popup="json"
|
||||
data-title="API Health">API-Health</a>
|
||||
|
||||
<a href="tools/db-doctor.php?profile=template"
|
||||
class="btn btn-light"
|
||||
data-popup="frame"
|
||||
data-title="DB-Doctor">DB-Doctor</a>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
@@ -141,30 +130,9 @@ $assetVersion = defined('ASSET_VERSION') ? ASSET_VERSION : time();
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<!-- Tools Dialog (NEU) -->
|
||||
<dialog id="toolsDialog" class="rounded-2xl p-0 w-[92vw] h-[86vh]">
|
||||
<div class="flex flex-col h-full">
|
||||
<div class="px-4 py-2 border-b bg-white/80 backdrop-blur flex items-center gap-3">
|
||||
<strong id="toolsTitle" class="me-auto">Werkzeug</strong>
|
||||
<button id="toolsCopy" type="button" class="btn hidden">Kopieren</button>
|
||||
<button id="toolsDownload" type="button" class="btn hidden">Download</button>
|
||||
<button id="toolsClose" type="button" class="btn">Schließen</button>
|
||||
</div>
|
||||
|
||||
<!-- JSON Ansicht -->
|
||||
<div id="toolsJsonWrap" class="flex-1 overflow-auto hidden bg-slate-50">
|
||||
<pre id="toolsJsonPre" class="p-4 text-sm leading-5 font-mono text-slate-800"></pre>
|
||||
</div>
|
||||
|
||||
<!-- Iframe Ansicht -->
|
||||
<iframe id="toolsFrame" class="flex-1 w-full hidden bg-white"></iframe>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
<div id="toast-root"></div>
|
||||
|
||||
<script src="assets/js/toast.js?v=<?= htmlspecialchars($assetVersion, ENT_QUOTES) ?>"></script>
|
||||
<script type="module" src="assets/js/app.js?v=<?= htmlspecialchars($assetVersion, ENT_QUOTES) ?>"></script>
|
||||
<script type="module" src="assets/js/ui-tools.js?v=<?= htmlspecialchars($assetVersion, ENT_QUOTES) ?>"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user