Files
Webklar-Kundenbereich/public/admin.html
Kenso Grimm 177ee6a13d feat: Admin-Bereich im Kundenportal (Ticketsystem-Login, Projektsuche, Kundenverwaltung)
- Login: Appwrite-User mit Label 'admin' (Ticketsystem-Mitarbeiter) bekommen
  eine Admin-Session und landen auf /admin.html
- Admin-Dashboard: Statistik-Uebersicht, Projektsuche ueber alle Projekte
  (inkl. Preview-/Gitea-Links, Vorschau ohne Kunden-Login), Kundenliste mit
  Portal-Status, letztem Login und Projektanzahl
- Kundennummer (code) direkt im Admin-Bereich pflegbar
- 'Als Kunde ansehen': Admin sieht das Portal exakt wie der Kunde (Banner
  mit Rueckweg zur Admin-Uebersicht)
- Kunden-Dashboard: neue Bereiche 'Meine Rechnungen' (InvoiceNinja inkl.
  Link ins Rechnungsportal) und 'Was wurde gemacht' (Tickets + Worksheets
  aus dem Ticketsystem, ohne interne Akquise-Tickets); Kundennummer im Header
- Admin-API /api/portal-admin/* (Session-basiert), Kunden-API /api/invoices
  und /api/activity
- appwriteClient: uebrig gebliebene Debug-Instrumentierung entfernt

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 17:09:39 +00:00

130 lines
6.7 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Admin Webklar Kundenbereich</title>
<link rel="stylesheet" href="/login.css">
<style>
.admin-tabs { display: flex; gap: 8px; margin: 18px 0; flex-wrap: wrap; }
.admin-tab {
background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.14);
color: inherit; padding: 8px 16px; border-radius: 999px; cursor: pointer; font: inherit;
}
.admin-tab.active { background: rgba(94,234,212,0.16); border-color: rgba(94,234,212,0.5); }
.admin-section { display: none; }
.admin-section.active { display: block; }
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin-bottom: 18px; }
.stat-tile { padding: 14px 16px; border-radius: 12px; background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.1); }
.stat-tile .num { font-size: 26px; font-weight: 700; display: block; }
.stat-tile .lbl { font-size: 12px; opacity: 0.7; }
.admin-search { width: 100%; max-width: 380px; padding: 9px 14px; border-radius: 10px;
border: 1px solid rgba(255,255,255,0.18); background: rgba(255,255,255,0.06); color: inherit; margin-bottom: 12px; }
.admin-table-wrap { overflow-x: auto; border-radius: 12px; border: 1px solid rgba(255,255,255,0.1); }
table.admin-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.admin-table th, .admin-table td { text-align: left; padding: 9px 12px; border-bottom: 1px solid rgba(255,255,255,0.08); vertical-align: middle; }
.admin-table th { font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; opacity: 0.65; }
.admin-table tr:last-child td { border-bottom: none; }
.pill { display: inline-block; padding: 2px 10px; border-radius: 999px; font-size: 12px; border: 1px solid transparent; }
.pill-ok { background: rgba(74,222,128,0.15); border-color: rgba(74,222,128,0.4); }
.pill-warn { background: rgba(251,191,36,0.15); border-color: rgba(251,191,36,0.45); }
.pill-muted { background: rgba(255,255,255,0.07); border-color: rgba(255,255,255,0.16); opacity: 0.8; }
.pill-red { background: rgba(248,113,113,0.15); border-color: rgba(248,113,113,0.45); }
.mini-btn { background: rgba(255,255,255,0.08); border: 1px solid rgba(255,255,255,0.18); color: inherit;
padding: 5px 11px; border-radius: 8px; cursor: pointer; font-size: 13px; white-space: nowrap; }
.mini-btn:hover { background: rgba(255,255,255,0.14); }
.mini-btn.primary { background: rgba(94,234,212,0.16); border-color: rgba(94,234,212,0.5); }
.code-input { width: 90px; padding: 4px 8px; border-radius: 7px; border: 1px solid rgba(255,255,255,0.2);
background: rgba(255,255,255,0.06); color: inherit; font: inherit; font-size: 13px; }
.detail-row td { background: rgba(255,255,255,0.03); }
.detail-box { padding: 6px 4px; }
.detail-box h4 { margin: 8px 0 4px; font-size: 13px; }
.detail-box .muted { opacity: 0.65; }
.ws-line { font-size: 13px; opacity: 0.85; margin: 2px 0 2px 14px; }
a.plain { color: #7dd3fc; text-decoration: none; }
a.plain:hover { text-decoration: underline; }
.admin-note { font-size: 13px; opacity: 0.65; margin: 8px 0 0; }
.flex-between { display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap; }
.recent-list { list-style: none; margin: 0; padding: 0; }
.recent-list li { padding: 7px 0; border-bottom: 1px solid rgba(255,255,255,0.07); font-size: 14px; }
.recent-list li:last-child { border-bottom: none; }
.two-col { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 14px; }
</style>
</head>
<body class="portal-page dashboard-page">
<div class="dot-background" aria-hidden="true">
<div class="dot-pattern"></div>
<div class="dot-fade"></div>
</div>
<header class="portal-header">
<a class="portal-logo" href="/admin.html">Webklar <span style="opacity:.6">Admin</span></a>
<div class="portal-header-actions">
<span class="portal-meta" id="admin-meta"></span>
<button type="button" class="portal-btn-outline" id="logout-btn">Abmelden</button>
</div>
</header>
<main class="portal-main dashboard-content-reveal">
<h1 class="portal-title">Admin-Übersicht</h1>
<p id="admin-error" class="error hidden"></p>
<div class="admin-tabs">
<button class="admin-tab active" data-tab="overview">Übersicht</button>
<button class="admin-tab" data-tab="projects">Projekte</button>
<button class="admin-tab" data-tab="customers">Kunden</button>
</div>
<!-- Übersicht -->
<section class="admin-section active" id="tab-overview">
<div class="stat-grid" id="stat-grid"><span class="portal-status">Lade…</span></div>
<div class="two-col">
<div class="portal-card">
<h3>Letzte Portal-Logins</h3>
<ul class="recent-list" id="recent-logins"><li class="muted">Lade…</li></ul>
</div>
<div class="portal-card">
<h3>Zuletzt aktualisierte Projekte</h3>
<ul class="recent-list" id="recent-projects"><li class="muted">Lade…</li></ul>
</div>
</div>
</section>
<!-- Projekte -->
<section class="admin-section" id="tab-projects">
<input class="admin-search" id="project-search" type="search"
placeholder="Projekte suchen (Name, Subdomain, Repo, Kunde)…">
<div class="admin-table-wrap">
<table class="admin-table">
<thead>
<tr><th>Projekt</th><th>Subdomain</th><th>Kunde</th><th>Status</th><th>Typ</th><th>Links</th></tr>
</thead>
<tbody id="project-rows"><tr><td colspan="6" class="muted">Lade…</td></tr></tbody>
</table>
</div>
<p class="admin-note">Als Admin kannst du jede Projekt-Vorschau direkt öffnen ohne Kunden-Login.</p>
</section>
<!-- Kunden -->
<section class="admin-section" id="tab-customers">
<input class="admin-search" id="customer-search" type="search"
placeholder="Kunden suchen (Kundennr., Name, E-Mail, Ort)…">
<div class="admin-table-wrap">
<table class="admin-table">
<thead>
<tr><th>Kundennr.</th><th>Kunde</th><th>Status</th><th>Portal</th><th>Letzter Login</th><th>Projekte</th><th>Aktionen</th></tr>
</thead>
<tbody id="customer-rows"><tr><td colspan="7" class="muted">Lade…</td></tr></tbody>
</table>
</div>
<p class="admin-note">Kundennummer direkt im Feld ändern und mit ✓ speichern. „Als Kunde ansehen“ zeigt dir das Portal genau so, wie der Kunde es sieht.</p>
</section>
</main>
<script src="/admin.js"></script>
<script>
initAdminPage();
</script>
</body>
</html>