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 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 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'>
|
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='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 text-gray-500'>#${item.id}</div>
|
||||||
<div class='text-xs'>${parentBadge(resource,item)}</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>`;
|
</div>`;
|
||||||
}).join('');
|
}).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>
|
</nav>
|
||||||
<div class="ms-auto flex gap-2">
|
<div class="ms-auto flex gap-2">
|
||||||
<button id="btn-new" type="button" class="btn">Neu …</button>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
@@ -141,30 +130,9 @@ $assetVersion = defined('ASSET_VERSION') ? ASSET_VERSION : time();
|
|||||||
</form>
|
</form>
|
||||||
</dialog>
|
</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>
|
<div id="toast-root"></div>
|
||||||
|
|
||||||
<script src="assets/js/toast.js?v=<?= htmlspecialchars($assetVersion, ENT_QUOTES) ?>"></script>
|
<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/app.js?v=<?= htmlspecialchars($assetVersion, ENT_QUOTES) ?>"></script>
|
||||||
<script type="module" src="assets/js/ui-tools.js?v=<?= htmlspecialchars($assetVersion, ENT_QUOTES) ?>"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user