Verbessere Mitgliederfilter und Zahlungszuordnung

This commit is contained in:
2026-08-13 15:40:43 +02:00
parent 0f263c3a19
commit a93e067e04
6 changed files with 892 additions and 29 deletions
+211 -12
View File
@@ -33,6 +33,66 @@ if ($saasUser !== null && saas_user_has_role(['owner', 'admin'], $saasUser)) {
$hasAccess = true;
}
function mitglieder_parse_filters(array $source): array
{
$status = (string)($source['status'] ?? 'alle');
if (!in_array($status, ['alle', 'aktiv', 'inaktiv'], true)) {
$status = 'alle';
}
$zugang = (string)($source['zugang'] ?? 'alle');
if (!in_array($zugang, ['alle', 'mit_zugang', 'ohne_zugang', 'entzogen'], true)) {
$zugang = 'alle';
}
$paypal = (string)($source['paypal'] ?? 'alle');
if (!in_array($paypal, ['alle', 'hinterlegt', 'fehlt', 'doppelt'], true)) {
$paypal = 'alle';
}
return [
'q' => trim((string)($source['q'] ?? '')),
'status' => $status,
'zugang' => $zugang,
'paypal' => $paypal,
];
}
function mitglieder_build_filter_query(array $filters): string
{
$params = [];
foreach (['q', 'status', 'zugang', 'paypal'] as $key) {
$value = (string)($filters[$key] ?? '');
if ($value !== '' && !in_array($value, ['alle'], true)) {
$params[$key] = $value;
}
}
return http_build_query($params);
}
function mitglieder_filter_fields(array $filters): string
{
return '<input type="hidden" name="filter_q" value="' . saas_html((string)$filters['q']) . '">'
. '<input type="hidden" name="filter_status" value="' . saas_html((string)$filters['status']) . '">'
. '<input type="hidden" name="filter_zugang" value="' . saas_html((string)$filters['zugang']) . '">'
. '<input type="hidden" name="filter_paypal" value="' . saas_html((string)$filters['paypal']) . '">';
}
function mitglieder_status_badge(bool $active): string
{
return '<span class="status-badge ' . ($active ? 'success' : 'muted') . '">' . ($active ? 'Aktiv' : 'Inaktiv') . '</span>';
}
function mitglieder_access_label(array $mitglied, array $rollenLabels): string
{
if ($mitglied['membership_status'] === 'active') {
return $rollenLabels[$mitglied['role']] ?? (string)$mitglied['role'];
}
return $mitglied['membership_status'] === 'revoked' ? 'Zugang entzogen' : 'Kein Zugang';
}
if($hasAccess){
$meldung = null;
@@ -40,6 +100,17 @@ if($hasAccess){
$einladungslink = null;
$bearbeitenId = null;
$actorUserId = $saasUser['user_id'] ?? null;
$filterSource = $_SERVER['REQUEST_METHOD'] === 'POST'
? [
'q' => $_POST['filter_q'] ?? '',
'status' => $_POST['filter_status'] ?? 'alle',
'zugang' => $_POST['filter_zugang'] ?? 'alle',
'paypal' => $_POST['filter_paypal'] ?? 'alle',
]
: $_GET;
$filter = mitglieder_parse_filters($filterSource);
$filterQuery = mitglieder_build_filter_query($filter);
$filterSuffix = $filterQuery !== '' ? '?' . $filterQuery : '';
if (isset($_SESSION['flash_mitglieder']) && is_array($_SESSION['flash_mitglieder'])) {
$flash = $_SESSION['flash_mitglieder'];
@@ -224,7 +295,7 @@ if($hasAccess){
'fehler' => $fehler,
'einladungslink' => $einladungslink,
];
header('Location: mitarbeiterverwalten.php');
header('Location: mitarbeiterverwalten.php' . $filterSuffix);
exit;
}
}
@@ -235,6 +306,77 @@ if($hasAccess){
$billingPlan = billing_plans()[$billingInfo['plan_code']] ?? null;
$aktiveMitgliederAnzahl = billing_count_active_participants($pdo, $tenantId);
$rollenLabels = ['owner' => 'Inhaber', 'admin' => 'Administrator', 'treasurer' => 'Kassenwart', 'member' => 'Mitglied', 'viewer' => 'Betrachter'];
$paypalNameCounts = [];
foreach ($mitglieder as $mitglied) {
$paypalNorm = imports_normalize_match_text((string)($mitglied['paypal_name'] ?? ''));
if ($paypalNorm !== '') {
$paypalNameCounts[$paypalNorm] = ($paypalNameCounts[$paypalNorm] ?? 0) + 1;
}
}
$mitgliederStats = [
'gesamt' => count($mitglieder),
'aktiv' => 0,
'inaktiv' => 0,
'mit_zugang' => 0,
'ohne_zugang' => 0,
'paypal_fehlt' => 0,
'paypal_doppelt' => 0,
];
$gefilterteMitglieder = [];
foreach ($mitglieder as $mitglied) {
$isActive = (bool)$mitglied['active'];
$hasAccessAccount = $mitglied['membership_status'] === 'active';
$paypalName = trim((string)($mitglied['paypal_name'] ?? ''));
$paypalNorm = imports_normalize_match_text($paypalName);
$paypalDuplicate = $paypalNorm !== '' && ($paypalNameCounts[$paypalNorm] ?? 0) > 1;
$mitgliederStats[$isActive ? 'aktiv' : 'inaktiv']++;
$mitgliederStats[$hasAccessAccount ? 'mit_zugang' : 'ohne_zugang']++;
if ($paypalName === '') {
$mitgliederStats['paypal_fehlt']++;
}
if ($paypalDuplicate) {
$mitgliederStats['paypal_doppelt']++;
}
$matches = true;
$q = imports_normalize_match_text((string)$filter['q']);
if ($q !== '') {
$searchText = imports_normalize_match_text(
(string)$mitglied['display_name'] . ' ' . (string)($mitglied['email'] ?? '') . ' ' . $paypalName
);
$matches = str_contains($searchText, $q);
}
if ($matches && $filter['status'] === 'aktiv') {
$matches = $isActive;
}
if ($matches && $filter['status'] === 'inaktiv') {
$matches = !$isActive;
}
if ($matches && $filter['zugang'] === 'mit_zugang') {
$matches = $hasAccessAccount;
}
if ($matches && $filter['zugang'] === 'ohne_zugang') {
$matches = $mitglied['membership_status'] === null || $mitglied['membership_status'] === 'pending';
}
if ($matches && $filter['zugang'] === 'entzogen') {
$matches = $mitglied['membership_status'] === 'revoked';
}
if ($matches && $filter['paypal'] === 'hinterlegt') {
$matches = $paypalName !== '';
}
if ($matches && $filter['paypal'] === 'fehlt') {
$matches = $paypalName === '';
}
if ($matches && $filter['paypal'] === 'doppelt') {
$matches = $paypalDuplicate;
}
if ($matches) {
$mitglied['paypal_duplicate'] = $paypalDuplicate;
$gefilterteMitglieder[] = $mitglied;
}
}
$bearbeitenMitglied = null;
if ($bearbeitenId !== null) {
foreach ($mitglieder as $m) {
@@ -263,12 +405,21 @@ if($hasAccess){
Limit erreicht für weitere aktive Mitglieder ist ein <a href="mandant-einstellungen.php">Upgrade</a> nötig.
<?php endif; ?></p>
<div class="admin-summary-grid">
<div><strong><?php echo (int)$mitgliederStats['gesamt']; ?></strong><span>Mitglieder</span></div>
<div><strong><?php echo (int)$mitgliederStats['aktiv']; ?></strong><span>aktiv</span></div>
<div><strong><?php echo (int)$mitgliederStats['mit_zugang']; ?></strong><span>mit Zugang</span></div>
<div><strong><?php echo (int)$mitgliederStats['paypal_fehlt']; ?></strong><span>ohne PayPal-Name</span></div>
<div><strong><?php echo (int)$mitgliederStats['paypal_doppelt']; ?></strong><span>PayPal-Konflikte</span></div>
</div>
<?php if ($bearbeitenMitglied !== null): ?>
<h3>Bearbeiten von <?php echo saas_html($bearbeitenMitglied['display_name']); ?></h3>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>">
<input type="hidden" name="aktion" value="bearbeitenspeichern">
<input type="hidden" name="mitgliedID" value="<?php echo $bearbeitenMitglied['participant_id']; ?>">
<?php echo app_csrf_field(); ?>
<?php echo mitglieder_filter_fields($filter); ?>
<label for="name">Name:</label>
<input type="text" name="name" id="name" value="<?php echo saas_html($bearbeitenMitglied['display_name']); ?>" required>
@@ -292,6 +443,7 @@ if($hasAccess){
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>">
<input type="hidden" name="aktion" id="aktion" value="anlegen">
<?php echo app_csrf_field(); ?>
<?php echo mitglieder_filter_fields($filter); ?>
<label for="new-name">Name:</label>
<input type="text" name="name" id="new-name" required>
@@ -316,6 +468,7 @@ if($hasAccess){
<p><a class="button" href="mitglieder-vorlage.php">Vorlage herunterladen</a></p>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>" enctype="multipart/form-data">
<?php echo app_csrf_field(); ?>
<?php echo mitglieder_filter_fields($filter); ?>
<input type="hidden" name="aktion" value="csv_import">
<label for="mitglieder_csv">CSV-Datei mit Mitgliedern:</label>
<input type="file" name="mitglieder_csv" id="mitglieder_csv" accept=".csv" required>
@@ -326,8 +479,38 @@ if($hasAccess){
<p>Name und E-Mail dienen der Kaffeeliste und Benachrichtigungen. Ein Login-Zugang
ist davon unabhängig und wird separat je Mitglied gewährt oder entzogen.</p>
<form method="get" action="mitarbeiterverwalten.php" class="admin-filter-bar">
<label for="mitglieder-q">Suche</label>
<input type="search" name="q" id="mitglieder-q" value="<?php echo saas_html((string)$filter['q']); ?>" placeholder="Name, E-Mail oder PayPal">
<label for="mitglieder-status">Status</label>
<select name="status" id="mitglieder-status">
<option value="alle" <?php echo $filter['status'] === 'alle' ? 'selected' : ''; ?>>Alle</option>
<option value="aktiv" <?php echo $filter['status'] === 'aktiv' ? 'selected' : ''; ?>>Aktiv</option>
<option value="inaktiv" <?php echo $filter['status'] === 'inaktiv' ? 'selected' : ''; ?>>Inaktiv</option>
</select>
<label for="mitglieder-zugang">Zugang</label>
<select name="zugang" id="mitglieder-zugang">
<option value="alle" <?php echo $filter['zugang'] === 'alle' ? 'selected' : ''; ?>>Alle</option>
<option value="mit_zugang" <?php echo $filter['zugang'] === 'mit_zugang' ? 'selected' : ''; ?>>Mit Zugang</option>
<option value="ohne_zugang" <?php echo $filter['zugang'] === 'ohne_zugang' ? 'selected' : ''; ?>>Ohne Zugang</option>
<option value="entzogen" <?php echo $filter['zugang'] === 'entzogen' ? 'selected' : ''; ?>>Entzogen</option>
</select>
<label for="mitglieder-paypal">PayPal</label>
<select name="paypal" id="mitglieder-paypal">
<option value="alle" <?php echo $filter['paypal'] === 'alle' ? 'selected' : ''; ?>>Alle</option>
<option value="hinterlegt" <?php echo $filter['paypal'] === 'hinterlegt' ? 'selected' : ''; ?>>Hinterlegt</option>
<option value="fehlt" <?php echo $filter['paypal'] === 'fehlt' ? 'selected' : ''; ?>>Fehlt</option>
<option value="doppelt" <?php echo $filter['paypal'] === 'doppelt' ? 'selected' : ''; ?>>Konflikte</option>
</select>
<button type="submit">Filtern</button>
<a class="button alt" href="mitarbeiterverwalten.php">Zurücksetzen</a>
</form>
<p><?php echo count($gefilterteMitglieder); ?> von <?php echo count($mitglieder); ?> Mitgliedern angezeigt.</p>
<!-- Tabelle zur Anzeige und Bearbeitung von Mitgliedern -->
<table class="table table-striped">
<div class="table-wrapper">
<table class="table table-striped admin-table">
<tr>
<th>Name</th>
<th>E-Mail</th>
@@ -336,22 +519,28 @@ if($hasAccess){
<th>Zugang</th>
<th>Aktionen</th>
</tr>
<?php foreach ($mitglieder as $mitglied): ?>
<?php foreach ($gefilterteMitglieder as $mitglied): ?>
<tr>
<td><?php echo saas_html($mitglied['display_name']); ?></td>
<td><?php echo saas_html($mitglied['email'] ?? ''); ?></td>
<td><?php echo saas_html($mitglied['paypal_name'] ?? ''); ?></td>
<td><?php echo $mitglied['active'] ? '1' : '0'; ?></td>
<td>
<?php echo saas_html($mitglied['paypal_name'] ?? ''); ?>
<?php if (!empty($mitglied['paypal_duplicate'])): ?>
<br><span class="status-badge warning">Konflikt</span>
<?php endif; ?>
</td>
<td><?php echo mitglieder_status_badge((bool)$mitglied['active']); ?></td>
<td>
<?php if ($mitglied['membership_status'] === 'active'): ?>
<?php if ($mitglied['role'] === 'owner'): ?>
Inhaber
<span class="status-badge info">Inhaber</span>
<?php else: ?>
<?php echo saas_html($rollenLabels[$mitglied['role']] ?? $mitglied['role']); ?>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>" style="display:inline">
<span class="status-badge success"><?php echo saas_html($rollenLabels[$mitglied['role']] ?? $mitglied['role']); ?></span>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>" class="inline-admin-form">
<input type="hidden" name="aktion" value="zugang_gewaehren">
<input type="hidden" name="mitgliedID" value="<?php echo $mitglied['participant_id']; ?>">
<?php echo app_csrf_field(); ?>
<?php echo mitglieder_filter_fields($filter); ?>
<select name="rolle">
<?php foreach (saas_grantable_roles() as $rolle): ?>
<option value="<?php echo saas_html($rolle); ?>" <?php echo $rolle === $mitglied['role'] ? 'selected' : ''; ?>><?php echo saas_html($rollenLabels[$rolle] ?? $rolle); ?></option>
@@ -359,20 +548,22 @@ if($hasAccess){
</select>
<button type="submit">Rolle ändern</button>
</form>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>" style="display:inline">
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>" class="inline-admin-form">
<input type="hidden" name="aktion" value="zugang_entziehen">
<input type="hidden" name="mitgliedID" value="<?php echo $mitglied['participant_id']; ?>">
<?php echo app_csrf_field(); ?>
<?php echo mitglieder_filter_fields($filter); ?>
<button type="submit">Zugang entziehen</button>
</form>
<?php endif; ?>
<?php else: ?>
<?php echo $mitglied['membership_status'] === 'revoked' ? 'Zugang entzogen' : 'Kein Zugang'; ?>
<span class="status-badge <?php echo $mitglied['membership_status'] === 'revoked' ? 'warning' : 'muted'; ?>"><?php echo saas_html(mitglieder_access_label($mitglied, $rollenLabels)); ?></span>
<?php if ($mitglied['email'] !== null && $mitglied['email'] !== ''): ?>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>" style="display:inline">
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>" class="inline-admin-form">
<input type="hidden" name="aktion" value="zugang_gewaehren">
<input type="hidden" name="mitgliedID" value="<?php echo $mitglied['participant_id']; ?>">
<?php echo app_csrf_field(); ?>
<?php echo mitglieder_filter_fields($filter); ?>
<select name="rolle">
<?php foreach (saas_grantable_roles() as $rolle): ?>
<option value="<?php echo saas_html($rolle); ?>"><?php echo saas_html($rollenLabels[$rolle] ?? $rolle); ?></option>
@@ -384,12 +575,13 @@ if($hasAccess){
<?php endif; ?>
</td>
<td>
<ul class="actions">
<ul class="actions compact-actions">
<li>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>">
<input type="hidden" name="aktion" value="bearbeiten">
<input type="hidden" name="mitgliedID" value="<?php echo $mitglied['participant_id']; ?>">
<?php echo app_csrf_field(); ?>
<?php echo mitglieder_filter_fields($filter); ?>
<button type="submit">Bearbeiten</button>
</form>
</li>
@@ -399,6 +591,7 @@ if($hasAccess){
<input type="hidden" name="aktion" value="deaktivieren">
<input type="hidden" name="mitgliedID" value="<?php echo $mitglied['participant_id']; ?>">
<?php echo app_csrf_field(); ?>
<?php echo mitglieder_filter_fields($filter); ?>
<button type="submit">Deaktivieren</button>
</form>
<?php else: ?>
@@ -406,6 +599,7 @@ if($hasAccess){
<input type="hidden" name="aktion" value="aktivieren">
<input type="hidden" name="mitgliedID" value="<?php echo $mitglied['participant_id']; ?>">
<?php echo app_csrf_field(); ?>
<?php echo mitglieder_filter_fields($filter); ?>
<button type="submit">Aktivieren</button>
</form>
<?php endif; ?>
@@ -416,6 +610,7 @@ if($hasAccess){
<input type="hidden" name="aktion" value="anonymisieren">
<input type="hidden" name="mitgliedID" value="<?php echo $mitglied['participant_id']; ?>">
<?php echo app_csrf_field(); ?>
<?php echo mitglieder_filter_fields($filter); ?>
<button type="submit">Anonymisieren</button>
</form>
</li>
@@ -424,7 +619,11 @@ if($hasAccess){
</td>
</tr>
<?php endforeach; ?>
<?php if ($gefilterteMitglieder === []): ?>
<tr><td colspan="6">Keine Mitglieder für diese Filter gefunden.</td></tr>
<?php endif; ?>
</table>
</div>
<?php
}else{