This commit is contained in:
cay 2026-03-28 09:20:25 +00:00
parent 0a275f170d
commit ac03d796bf
3 changed files with 54 additions and 3 deletions

View File

@ -1498,3 +1498,42 @@ body:not(.admin-body) > * {
.mail-type-direct { background: #dbeafe; color: #1e40af; }
.mail-type-renewal { background: #dcfce7; color: var(--success); }
.mail-type-renewal_auto { background: #fef3c7; color: var(--warning); }
/* ---- Pending Badge in Sidebar ---- */
.nav-badge {
display: inline-block;
background: var(--error);
color: white;
font-size: 0.7rem;
font-weight: 700;
padding: 1px 6px;
border-radius: 10px;
margin-left: 6px;
vertical-align: middle;
animation: pulse-badge 2s infinite;
}
@keyframes pulse-badge {
0%, 100% { opacity: 1; }
50% { opacity: 0.6; }
}
/* ---- Pending Stat Card ---- */
.stat-card-pending {
border: 1.5px solid #fecaca !important;
background: #fff5f5 !important;
transition: box-shadow 0.2s;
}
.stat-card-pending:hover {
box-shadow: 0 0 0 3px rgba(220,38,38,0.15);
}
/* ---- Pending Member Row ---- */
.member-row-pending td {
background: #fffbeb !important;
}
.member-row-pending:hover td {
background: #fef3c7 !important;
}
.member-row-pending::before {
content: '⏳';
}

View File

@ -50,7 +50,8 @@ router.get('/', requireAdmin, async (req, res) => {
COUNT(*) as total,
SUM(CASE WHEN status = 'active' THEN 1 ELSE 0 END) as active_count,
SUM(CASE WHEN is_minor = 1 THEN 1 ELSE 0 END) as minors,
SUM(CASE WHEN created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY) THEN 1 ELSE 0 END) as last_30_days
SUM(CASE WHEN created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY) THEN 1 ELSE 0 END) as last_30_days,
SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending_count
FROM memberships
`);
res.render('admin/dashboard', {

View File

@ -15,7 +15,12 @@
<nav class="admin-nav">
<a href="#" class="nav-link active" onclick="showSection('tarife', this)">📋 Tarife</a>
<a href="#" class="nav-link" onclick="showSection('kategorien', this)">🏷️ Kategorien</a>
<a href="#" class="nav-link" onclick="showSection('mitglieder', this)">👥 Mitglieder</a>
<a href="#" class="nav-link" onclick="showSection('mitglieder', this)">
👥 Mitglieder
<% if (stats.pending_count > 0) { %>
<span class="nav-badge"><%= stats.pending_count %></span>
<% } %>
</a>
<a href="/admin/contracts" class="nav-link">📑 Verträge</a>
<a href="/admin/billing" class="nav-link">💶 Abrechnung</a>
<a href="/admin/finance" class="nav-link">📊 Finanzen</a>
@ -44,6 +49,12 @@
<div class="stat-number"><%= stats.last_30_days || 0 %></div>
<div class="stat-label">Letzte 30 Tage</div>
</div>
<% if (stats.pending_count > 0) { %>
<div class="stat-card stat-card-pending" onclick="showSection('mitglieder', document.querySelector('[onclick*=mitglieder]'))" style="cursor:pointer" title="Klicken um ausstehende Verträge anzuzeigen">
<div class="stat-number" style="color:var(--error)"><%= stats.pending_count %></div>
<div class="stat-label">⚠️ Ausstehend</div>
</div>
<% } %>
<div class="stat-card">
<div class="stat-number"><%= stats.minors || 0 %></div>
<div class="stat-label">Minderjährige</div>
@ -154,7 +165,7 @@
</thead>
<tbody>
<% memberships.forEach(m => { %>
<tr class="member-row" onclick="window.location='/admin/members/<%= m.id %>'" style="cursor:pointer">
<tr class="member-row <%= m.status === 'pending' ? 'member-row-pending' : '' %>" onclick="window.location='/admin/members/<%= m.id %>'" style="cursor:pointer">
<td><strong><%= m.salutation %> <%= m.first_name %> <%= m.last_name %></strong></td>
<td><%= m.email %></td>
<td><%= m.tariff_name || '' %></td>