gchk,
This commit is contained in:
parent
ac03d796bf
commit
fed7951a98
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -17,8 +17,8 @@
|
||||
<a href="#" class="nav-link" onclick="showSection('kategorien', this)">🏷️ Kategorien</a>
|
||||
<a href="#" class="nav-link" onclick="showSection('mitglieder', this)">
|
||||
👥 Mitglieder
|
||||
<% if (stats.pending_count > 0) { %>
|
||||
<span class="nav-badge"><%= stats.pending_count %></span>
|
||||
<% if ((stats.pending_count || 0) + (stats.new_count || 0) > 0) { %>
|
||||
<span class="nav-badge"><%= (stats.pending_count || 0) + (stats.new_count || 0) %></span>
|
||||
<% } %>
|
||||
</a>
|
||||
<a href="/admin/contracts" class="nav-link">📑 Verträge</a>
|
||||
@ -50,9 +50,15 @@
|
||||
<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-card stat-card-pending" onclick="showSection('mitglieder', document.querySelector('[onclick*=mitglieder]'))" style="cursor:pointer" title="Ausstehende Bestätigungen">
|
||||
<div class="stat-number" style="color:var(--error)"><%= stats.pending_count %></div>
|
||||
<div class="stat-label">⚠️ Ausstehend</div>
|
||||
<div class="stat-label">⏳ Ausstehend</div>
|
||||
</div>
|
||||
<% } %>
|
||||
<% if (stats.new_count > 0) { %>
|
||||
<div class="stat-card stat-card-new" onclick="showSection('mitglieder', document.querySelector('[onclick*=mitglieder]'))" style="cursor:pointer" title="Neue Mitglieder die noch nicht bearbeitet wurden">
|
||||
<div class="stat-number" style="color:#0891b2"><%= stats.new_count %></div>
|
||||
<div class="stat-label">🆕 Neu</div>
|
||||
</div>
|
||||
<% } %>
|
||||
<div class="stat-card">
|
||||
@ -165,7 +171,7 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<% memberships.forEach(m => { %>
|
||||
<tr class="member-row <%= m.status === 'pending' ? 'member-row-pending' : '' %>" onclick="window.location='/admin/members/<%= m.id %>'" style="cursor:pointer">
|
||||
<tr class="member-row <%= m.status === 'pending' ? 'member-row-pending' : (!m.reviewed ? 'member-row-new' : '') %>" onclick="openMember(<%= m.id %>, <%= m.reviewed ? 1 : 0 %>)" style="cursor:pointer">
|
||||
<td><strong><%= m.salutation %> <%= m.first_name %> <%= m.last_name %></strong></td>
|
||||
<td><%= m.email %></td>
|
||||
<td><%= m.tariff_name || '–' %></td>
|
||||
@ -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 => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user