diff --git a/public/js/hosting-app.js b/public/js/hosting-app.js index 41dbad3..80e4fbb 100644 --- a/public/js/hosting-app.js +++ b/public/js/hosting-app.js @@ -399,7 +399,9 @@ const html = '
' + '
Cockpit
' + - '
' + + '
' + + '' + + '
' + '
' + 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(); }; diff --git a/static/js/hosting-app.js b/static/js/hosting-app.js index 41dbad3..80e4fbb 100644 --- a/static/js/hosting-app.js +++ b/static/js/hosting-app.js @@ -399,7 +399,9 @@ const html = '
' + '
Cockpit
' + - '
' + + '
' + + '' + + '
' + '
' + 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(); };