feat(admin): CSV-Export-Button im Cockpit
Lädt /admin/export/accounts.csv mit Admin-Token (fetch + Blob-Download). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -399,7 +399,9 @@
|
||||
const html =
|
||||
'<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:20px">' +
|
||||
'<div class="hosting-title" style="text-align:left;margin:0">Cockpit</div>' +
|
||||
'<button class="hosting-link" id="alogout">Abmelden</button></div>' +
|
||||
'<div style="display:flex;gap:14px;align-items:center">' +
|
||||
'<button class="hosting-link" id="aexport">CSV-Export</button>' +
|
||||
'<button class="hosting-link" id="alogout">Abmelden</button></div></div>' +
|
||||
'<div class="admin-stats">' +
|
||||
statCard("Kunden", stats.accounts, "+" + (stats.newAccounts30d || 0) + " (30 T.)") +
|
||||
statCard("Aktive Abos", stats.activeSubscriptions) +
|
||||
@@ -417,6 +419,21 @@
|
||||
|
||||
root.innerHTML = card(html, true);
|
||||
root.querySelector("#alogout").onclick = () => { adminTok.clear(); renderAdminLogin(""); };
|
||||
root.querySelector("#aexport").onclick = async () => {
|
||||
// CSV braucht den Admin-Token im Header → per fetch holen + Blob-Download.
|
||||
try {
|
||||
const res = await fetch("/api/admin/export/accounts.csv", {
|
||||
headers: { Authorization: "Bearer " + adminTok.get() },
|
||||
});
|
||||
if (!res.ok) throw new Error("Export fehlgeschlagen (" + res.status + ")");
|
||||
const blob = await res.blob();
|
||||
const a = document.createElement("a");
|
||||
a.href = URL.createObjectURL(blob);
|
||||
a.download = "rapport-kunden.csv";
|
||||
document.body.appendChild(a); a.click(); a.remove();
|
||||
URL.revokeObjectURL(a.href);
|
||||
} catch (err) { alert(err.message); }
|
||||
};
|
||||
const s = root.querySelector("#asearch");
|
||||
s.oninput = () => { adminSearch = s.value; const pos = s.selectionStart; paintAdmin(); const n = root.querySelector("#asearch"); n.focus(); n.setSelectionRange(pos, pos); };
|
||||
root.querySelector("#afilter").onchange = (e) => { adminPlanFilter = e.target.value; paintAdmin(); };
|
||||
|
||||
Reference in New Issue
Block a user