diff --git a/public/css/style.css b/public/css/style.css index 1d97557..8fe6c64 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -1537,3 +1537,23 @@ body:not(.admin-body) > * { .member-row-pending::before { content: '⏳'; } + +/* ---- Neue Mitglieder Hervorhebung ---- */ +.stat-card-new { + border: 1.5px solid #bae6fd !important; + background: #f0f9ff !important; + cursor: pointer; + transition: box-shadow 0.2s; +} +.stat-card-new:hover { + box-shadow: 0 0 0 3px rgba(8,145,178,0.15); +} +.member-row-new td { + background: #f0f9ff !important; +} +.member-row-new td:first-child::before { + content: '🆕 '; +} +.member-row-new:hover td { + background: #e0f2fe !important; +} diff --git a/routes/admin.js b/routes/admin.js index cca314e..145996b 100644 --- a/routes/admin.js +++ b/routes/admin.js @@ -51,7 +51,8 @@ router.get('/', requireAdmin, async (req, res) => { 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 status = 'pending' THEN 1 ELSE 0 END) as pending_count + SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending_count, + SUM(CASE WHEN reviewed = 0 AND status = 'active' THEN 1 ELSE 0 END) as new_count FROM memberships `); res.render('admin/dashboard', { @@ -334,4 +335,15 @@ router.post('/members/:id/confirm', requireAdmin, async (req, res) => { } }); + +// Mitglied als gesehen markieren +router.post('/members/:id/reviewed', requireAdmin, async (req, res) => { + try { + await db.query('UPDATE memberships SET reviewed = 1 WHERE id = ?', [req.params.id]); + res.json({ success: true }); + } catch (err) { + res.json({ success: false }); + } +}); + module.exports = router; diff --git a/routes/api.js b/routes/api.js index 129d7f4..7672d7b 100644 --- a/routes/api.js +++ b/routes/api.js @@ -190,8 +190,8 @@ router.post('/submit-membership', async (req, res) => { start_package_price, signup_date, contract_start, contract_end, effective_end, first_payment_date, first_payment_amt, - status) - VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) + status, reviewed) + VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) `, [ tariff_id, salutation, title || '', first_name, last_name, birth_date, email, phone || '', street, address_addition || '', zip, city, @@ -208,7 +208,7 @@ router.post('/submit-membership', async (req, res) => { contractEnd.toISOString().split('T')[0], contractStart.toISOString().split('T')[0], firstPaymentAmt, - 'pending' + 'pending', 0 ]); // Bestätigungs-E-Mail senden diff --git a/views/admin/dashboard.ejs b/views/admin/dashboard.ejs index 54ac6a5..44ec3a8 100644 --- a/views/admin/dashboard.ejs +++ b/views/admin/dashboard.ejs @@ -17,8 +17,8 @@ 🏷️ Kategorien 👥 Mitglieder - <% if (stats.pending_count > 0) { %> - <%= stats.pending_count %> + <% if ((stats.pending_count || 0) + (stats.new_count || 0) > 0) { %> + <%= (stats.pending_count || 0) + (stats.new_count || 0) %> <% } %> 📑 Verträge @@ -50,9 +50,15 @@
Letzte 30 Tage
<% if (stats.pending_count > 0) { %> -
+
<%= stats.pending_count %>
-
⚠️ Ausstehend
+
⏳ Ausstehend
+
+ <% } %> + <% if (stats.new_count > 0) { %> +
+
<%= stats.new_count %>
+
🆕 Neu
<% } %>
@@ -165,7 +171,7 @@ <% memberships.forEach(m => { %> - + <%= m.salutation %> <%= m.first_name %> <%= m.last_name %> <%= m.email %> <%= m.tariff_name || '–' %> @@ -378,6 +384,13 @@ toggleModal('editCategoryModal'); } + function openMember(id, reviewed) { + if (!reviewed) { + fetch('/admin/members/' + id + '/reviewed', { method: 'POST' }); + } + window.location = '/admin/members/' + id; + } + function filterMembers() { const q = document.getElementById('memberSearch').value.toLowerCase(); document.querySelectorAll('.member-row').forEach(row => {