Berarbeiter dürfen Stundenbuchung löschen

This commit is contained in:
Erik Thiele
2026-05-22 23:43:54 +02:00
parent 0a86f49310
commit 54ae12771e
6 changed files with 930 additions and 231 deletions

View File

@@ -80,6 +80,7 @@ Dazu gehören:
- Benutzerdaten per CSV importieren - Benutzerdaten per CSV importieren
- alle Arbeitsstunden auf 0 zurücksetzen - alle Arbeitsstunden auf 0 zurücksetzen
- Logansicht aller Verwaltungsaktionen - Logansicht aller Verwaltungsaktionen
- Logansicht als CSV exportieren
Die Logansicht ist auf 50 Einträge pro Seite paginiert. Die Logansicht ist auf 50 Einträge pro Seite paginiert.
@@ -127,6 +128,8 @@ In der Logansicht werden unter anderem folgende Aktionen protokolliert:
- Passwort zurücksetzen - Passwort zurücksetzen
- eigenes Passwort ändern - eigenes Passwort ändern
- Passwort per Reset-Link neu setzen - Passwort per Reset-Link neu setzen
- erfolgreiche Anmeldungen
- erfolgreiche Abmeldungen
- Speichern von Vereinswerten - Speichern von Vereinswerten
- CSV-Exporte - CSV-Exporte
- CSV-Import - CSV-Import

View File

@@ -84,7 +84,7 @@ Die Liste ist in Seiten mit jeweils 50 Einträgen aufgeteilt.
Hinweis: Hinweis:
- Das Löschen von Stundenbuchungen ist ausschließlich Administratoren möglich. - Bearbeiter können Stundenbuchungen bei Bedarf auch wieder löschen.
## 6. Vereinskonfiguration ## 6. Vereinskonfiguration
@@ -116,6 +116,7 @@ Dort befinden sich Funktionen wie:
- CSV-Import - CSV-Import
- Zurücksetzen aller Arbeitsstunden auf 0 - Zurücksetzen aller Arbeitsstunden auf 0
- Logansicht der Verwaltungsaktionen - Logansicht der Verwaltungsaktionen
- Log-Export als CSV
## 8. Passwort Vergessen ## 8. Passwort Vergessen

View File

@@ -16,7 +16,7 @@ Strato-taugliche PHP-Webanwendung für die Arbeitszeiterfassung des TC Ingelfing
- Dashboard-Infokasten für alle Mitglieder, pflegbar durch Admins mit einfacher Markdown-Unterstützung - Dashboard-Infokasten für alle Mitglieder, pflegbar durch Admins mit einfacher Markdown-Unterstützung
- Aggregierte Dashboard-Übersicht für Bearbeiter und Admins über alle Mitglieder - Aggregierte Dashboard-Übersicht für Bearbeiter und Admins über alle Mitglieder
- Stundenbuchungen mit Rollenansicht: Mitglieder nur eigene, Bearbeiter und Admins alle - Stundenbuchungen mit Rollenansicht: Mitglieder nur eigene, Bearbeiter und Admins alle
- Admins können einzelne Stundenbuchungen löschen - Bearbeiter und Admins können einzelne Stundenbuchungen löschen
- Paginierung mit 50 Einträgen pro Seite in Benutzerliste, Stundenbuchungen und Logansicht - Paginierung mit 50 Einträgen pro Seite in Benutzerliste, Stundenbuchungen und Logansicht
- Audit-Log für Verwaltungsaktionen sowie Login- und Logout-Vorgänge - Audit-Log für Verwaltungsaktionen sowie Login- und Logout-Vorgänge
- CSV-Export und CSV-Import für Benutzerdaten inklusive geleisteter Stunden - CSV-Export und CSV-Import für Benutzerdaten inklusive geleisteter Stunden
@@ -74,7 +74,7 @@ Die Anwendung ist so aufgebaut, dass sie mit oder ohne DB läuft. Bei gesetzter
- Stundenbuchungen - Stundenbuchungen
- `member` sieht nur eigene Buchungen - `member` sieht nur eigene Buchungen
- `editor` und `admin` sehen alle Buchungen - `editor` und `admin` sehen alle Buchungen
- `admin` kann einzelne Buchungen löschen - `editor` und `admin` können einzelne Buchungen löschen
- Die Liste ist auf 50 Einträge pro Seite paginiert - Die Liste ist auf 50 Einträge pro Seite paginiert
- Vereinskonfiguration - Vereinskonfiguration
- Pflichtstunden und Stundenwert pflegen - Pflichtstunden und Stundenwert pflegen

View File

@@ -629,7 +629,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$notice = 'Stunden gebucht.'; $notice = 'Stunden gebucht.';
} }
if ($user && $action === 'delete_booking' && ($user['role'] ?? '') === 'admin' && $pdo) { if ($user && $action === 'delete_booking' && hasRole($user, ['editor', 'admin']) && $pdo) {
$bookingId = (int)($_POST['booking_id'] ?? 0); $bookingId = (int)($_POST['booking_id'] ?? 0);
$stmt = $pdo->prepare('DELETE FROM work_logs WHERE id = ?'); $stmt = $pdo->prepare('DELETE FROM work_logs WHERE id = ?');
$stmt->execute([$bookingId]); $stmt->execute([$bookingId]);

View File

@@ -645,7 +645,7 @@ function renderAppShell(string $page): void
<?php endif; ?> <?php endif; ?>
</div> </div>
<div class="row row-cards"> <div class="row row-cards">
<div class="col-lg-12"><div class="card"><div class="card-body"><h3 class="section-title"><i class="ti ti-list-details"></i><span>Stundenbuchungen</span></h3><div class="table-responsive"><table class="table table-vcenter"><thead><tr><th>Datum</th><th>Mitglied</th><th>Gebucht von</th><th>Stunden</th><th>Notiz</th><th class="text-end">Aktionen</th></tr></thead><tbody><?php foreach (($bookings['entries'] ?? []) as $booking): ?><tr><td><?= htmlspecialchars(formatDateTime((string)$booking['ts'])) ?></td><td><?= htmlspecialchars((string)$booking['member']) ?></td><td><?= htmlspecialchars((string)$booking['actor']) ?></td><td><?= number_format((float)$booking['hours'], 2, ',', '.') ?> h</td><td><?= htmlspecialchars((string)$booking['note']) ?></td><td class="text-end"><?php if (($current['role'] ?? '') === 'admin'): ?><form method="post" class="m-0 d-inline-block" onsubmit="return confirm('Stundenbuchung wirklich löschen?');"><input type="hidden" name="action" value="delete_booking"><input type="hidden" name="booking_id" value="<?= (int)$booking['id'] ?>"><button class="btn btn-sm btn-outline-danger" type="submit">Löschen</button></form><?php else: ?><span class="text-secondary small">Keine Aktionen</span><?php endif; ?></td></tr><?php endforeach; ?><?php if (empty($bookings['entries'])): ?><tr><td colspan="6" class="text-secondary text-center">Keine Stundenbuchungen vorhanden.</td></tr><?php endif; ?></tbody></table></div><?php if (($bookings['total_pages'] ?? 1) > 1): ?><div class="d-flex align-items-center justify-content-between gap-3 mt-3"><a class="btn btn-outline-secondary<?= ($bookings['page'] ?? 1) <= 1 ? ' disabled' : '' ?>" href="/?page=booking&booking_page=<?= max(1, (int)($bookings['page'] ?? 1) - 1) ?>">Zurück</a><div class="text-secondary small">Seite <?= (int)($bookings['page'] ?? 1) ?> von <?= (int)($bookings['total_pages'] ?? 1) ?>, insgesamt <?= (int)($bookings['total'] ?? 0) ?> Einträge</div><a class="btn btn-outline-secondary<?= ($bookings['page'] ?? 1) >= ($bookings['total_pages'] ?? 1) ? ' disabled' : '' ?>" href="/?page=booking&booking_page=<?= min((int)($bookings['total_pages'] ?? 1), (int)($bookings['page'] ?? 1) + 1) ?>">Weiter</a></div><?php endif; ?></div></div></div></div> <div class="col-lg-12"><div class="card"><div class="card-body"><h3 class="section-title"><i class="ti ti-list-details"></i><span>Stundenbuchungen</span></h3><div class="table-responsive"><table class="table table-vcenter"><thead><tr><th>Datum</th><th>Mitglied</th><th>Gebucht von</th><th>Stunden</th><th>Notiz</th><th class="text-end">Aktionen</th></tr></thead><tbody><?php foreach (($bookings['entries'] ?? []) as $booking): ?><tr><td><?= htmlspecialchars(formatDateTime((string)$booking['ts'])) ?></td><td><?= htmlspecialchars((string)$booking['member']) ?></td><td><?= htmlspecialchars((string)$booking['actor']) ?></td><td><?= number_format((float)$booking['hours'], 2, ',', '.') ?> h</td><td><?= htmlspecialchars((string)$booking['note']) ?></td><td class="text-end"><?php if (hasRole($current, ['editor', 'admin'])): ?><form method="post" class="m-0 d-inline-block" onsubmit="return confirm('Stundenbuchung wirklich löschen?');"><input type="hidden" name="action" value="delete_booking"><input type="hidden" name="booking_id" value="<?= (int)$booking['id'] ?>"><button class="btn btn-sm btn-outline-danger" type="submit">Löschen</button></form><?php else: ?><span class="text-secondary small">Keine Aktionen</span><?php endif; ?></td></tr><?php endforeach; ?><?php if (empty($bookings['entries'])): ?><tr><td colspan="6" class="text-secondary text-center">Keine Stundenbuchungen vorhanden.</td></tr><?php endif; ?></tbody></table></div><?php if (($bookings['total_pages'] ?? 1) > 1): ?><div class="d-flex align-items-center justify-content-between gap-3 mt-3"><a class="btn btn-outline-secondary<?= ($bookings['page'] ?? 1) <= 1 ? ' disabled' : '' ?>" href="/?page=booking&booking_page=<?= max(1, (int)($bookings['page'] ?? 1) - 1) ?>">Zurück</a><div class="text-secondary small">Seite <?= (int)($bookings['page'] ?? 1) ?> von <?= (int)($bookings['total_pages'] ?? 1) ?>, insgesamt <?= (int)($bookings['total'] ?? 0) ?> Einträge</div><a class="btn btn-outline-secondary<?= ($bookings['page'] ?? 1) >= ($bookings['total_pages'] ?? 1) ? ' disabled' : '' ?>" href="/?page=booking&booking_page=<?= min((int)($bookings['total_pages'] ?? 1), (int)($bookings['page'] ?? 1) + 1) ?>">Weiter</a></div><?php endif; ?></div></div></div></div>
</div> </div>
<?php endif; ?> <?php endif; ?>

File diff suppressed because it is too large Load Diff