Version 1.6 - Strato Verzeichnis unterhalb tc-ingelfingen
This commit is contained in:
@@ -115,6 +115,26 @@ function appUrl(): string
|
||||
return $scheme . '://' . $host;
|
||||
}
|
||||
|
||||
function basePath(): string
|
||||
{
|
||||
$scriptName = $_SERVER['SCRIPT_NAME'] ?? '/index.php';
|
||||
$dir = rtrim(str_replace('\\', '/', dirname($scriptName)), '/');
|
||||
return $dir === '' ? '' : $dir;
|
||||
}
|
||||
|
||||
function appPath(string $path = ''): string
|
||||
{
|
||||
$base = basePath();
|
||||
if ($path === '') {
|
||||
return $base === '' ? '/' : $base . '/';
|
||||
}
|
||||
if ($path[0] === '?') {
|
||||
return ($base === '' ? '/' : $base . '/') . $path;
|
||||
}
|
||||
$path = ltrim($path, '/');
|
||||
return $base === '' ? '/' . $path : $base . '/' . $path;
|
||||
}
|
||||
|
||||
function sendPasswordResetMail(array $smtpConfig, string $toEmail, string $resetUrl): bool
|
||||
{
|
||||
if (($smtpConfig['from_email'] ?? '') === '') {
|
||||
@@ -497,7 +517,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
addAuditLog($pdo, (int)$found['id'], 'Anmeldung erfolgreich.');
|
||||
session_regenerate_id(true);
|
||||
$_SESSION['user'] = ['id' => (int)$found['id'], 'firstname' => $found['firstname'] ?? '', 'lastname' => $found['lastname'] ?? '', 'name' => displayName($found), 'email' => $found['email'], 'role' => $found['role']];
|
||||
header('Location: /?page=dashboard');
|
||||
header('Location: ' . appPath('?page=home'));
|
||||
exit;
|
||||
}
|
||||
$error = 'Login fehlgeschlagen.';
|
||||
@@ -557,7 +577,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$stmt->execute([(int)$resetRow['user_id']]);
|
||||
addAuditLog($pdo, (int)$resetRow['user_id'], 'Passwort über Reset-Link neu gesetzt.');
|
||||
$_SESSION['login_notice'] = 'Passwort erfolgreich zurueckgesetzt. Bitte anmelden.';
|
||||
header('Location: /?page=login');
|
||||
header('Location: ' . appPath('?page=login'));
|
||||
exit;
|
||||
}
|
||||
}
|
||||
@@ -827,6 +847,16 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$error = 'Log-Export ist ohne Datenbank nicht verfuegbar.';
|
||||
}
|
||||
|
||||
if ($user && $action === 'clear_audit_logs' && $user['role'] === 'admin') {
|
||||
if ($pdo) {
|
||||
$pdo->exec('TRUNCATE TABLE audit_logs');
|
||||
addAuditLog($pdo, (int)$user['id'], 'Audit-Log geleert.');
|
||||
$notice = 'Logeintraege wurden geloescht.';
|
||||
} else {
|
||||
$error = 'Logeintraege koennen ohne Datenbank nicht geloescht werden.';
|
||||
}
|
||||
}
|
||||
|
||||
if ($user && $action === 'import_sql_dump' && $user['role'] === 'admin') {
|
||||
if (!$pdo) {
|
||||
$error = 'SQL-Wiederherstellung ist ohne Datenbank nicht verfuegbar.';
|
||||
@@ -928,7 +958,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if ($user && $action === 'logout') {
|
||||
addAuditLog($pdo, (int)$user['id'], 'Abmeldung erfolgt.');
|
||||
session_destroy();
|
||||
header('Location: /?page=login');
|
||||
header('Location: ' . appPath('?page=login'));
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -477,7 +477,7 @@ function renderLoginPage(): void
|
||||
?>
|
||||
<div class="container-tight py-4">
|
||||
<div class="login-hero text-center mb-4">
|
||||
<div class="mb-3"><img src="/logo%20neu.png" alt="TC Ingelfingen Logo" style="max-width: 140px; width: 100%; height: auto;"></div>
|
||||
<div class="mb-3"><img src="<?= htmlspecialchars(appPath('logo%20neu.png')) ?>" alt="TC Ingelfingen Logo" style="max-width: 140px; width: 100%; height: auto;"></div>
|
||||
<h1 class="mb-1 section-title justify-content-center"><i class="ti ti-tennis"></i><span>TC Ingelfingen</span></h1>
|
||||
<p class="text-secondary mb-0">Arbeitsstundenverwaltung</p>
|
||||
</div>
|
||||
@@ -519,7 +519,7 @@ function renderResetPasswordPage(): void
|
||||
<div class="mb-3"><label class="form-label">Neues Passwort wiederholen</label><input class="form-control" name="confirm_password" type="password" minlength="6" required></div>
|
||||
<button class="btn btn-primary w-100" type="submit">Passwort speichern</button>
|
||||
</form>
|
||||
<div class="text-center mt-3"><a href="/?page=login">Zurück zur Anmeldung</a></div>
|
||||
<div class="text-center mt-3"><a href="<?= htmlspecialchars(appPath('?page=login')) ?>">Zurück zur Anmeldung</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -565,12 +565,12 @@ function renderAppShell(string $page): void
|
||||
<div class="page-wrapper">
|
||||
<div class="navbar navbar-expand-md d-print-none">
|
||||
<div class="container-xl">
|
||||
<a class="navbar-brand navbar-brand-wrap" href="/">
|
||||
<span class="navbar-mark" aria-hidden="true"><img src="/logo%20neu.png" alt=""></span>
|
||||
<a class="navbar-brand navbar-brand-wrap" href="<?= htmlspecialchars(appPath('')) ?>">
|
||||
<span class="navbar-mark" aria-hidden="true"><img src="<?= htmlspecialchars(appPath('logo%20neu.png')) ?>" alt=""></span>
|
||||
<span class="navbar-brand-text"><span>Arbeitsstunden</span><span>Dashboard</span></span>
|
||||
</a>
|
||||
<div class="ms-auto d-flex align-items-center gap-2 flex-wrap justify-content-end">
|
||||
<a class="btn btn-outline-secondary d-inline-flex align-items-center gap-2 text-decoration-none" href="/?page=profile" style="height: 38px;">
|
||||
<a class="btn btn-outline-secondary d-inline-flex align-items-center gap-2 text-decoration-none" href="<?= htmlspecialchars(appPath('?page=profile')) ?>" style="height: 38px;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 12a5 5 0 1 0-5-5a5 5 0 0 0 5 5"/><path d="M4 20a8 8 0 0 1 16 0"/></svg>
|
||||
<span class="d-flex flex-column align-items-start justify-content-center lh-1"><span class="text-truncate"><?= htmlspecialchars(trim(((string)($current['firstname'] ?? '')) . ' ' . ((string)($current['lastname'] ?? ''))) ?: ($current['name'] ?? 'guest')) ?></span><span class="small opacity-75"><?= htmlspecialchars(roleLabel((string)($current['role'] ?? ''))) ?></span></span>
|
||||
</a>
|
||||
@@ -583,19 +583,19 @@ function renderAppShell(string $page): void
|
||||
<aside class="sidebar">
|
||||
<div class="sidebar-title">Menü</div>
|
||||
<nav class="sidebar-nav">
|
||||
<a class="<?= $page === 'dashboard' ? 'active' : '' ?>" href="/?page=dashboard"><i class="ti ti-layout-dashboard"></i><span>Dashboard</span></a>
|
||||
<a class="<?= $page === 'profile' ? 'active' : '' ?>" href="/?page=profile"><i class="ti ti-user-circle"></i><span>Profil</span></a>
|
||||
<?php if (($current['role'] ?? '') !== 'member'): ?><a class="<?= $page === 'members' ? 'active' : '' ?>" href="/?page=members"><i class="ti ti-user-plus"></i><span>Benutzerverwaltung</span></a><?php endif; ?>
|
||||
<a class="<?= $page === 'booking' ? 'active' : '' ?>" href="/?page=booking"><i class="ti ti-clock-plus"></i><span>Stundenbuchung</span></a>
|
||||
<?php if (($current['role'] ?? '') !== 'member'): ?><a class="<?= $page === 'admin' ? 'active' : '' ?>" href="/?page=admin"><i class="ti ti-settings"></i><span>Vereinskonfiguration</span></a><?php endif; ?>
|
||||
<?php if (($current['role'] ?? '') === 'admin'): ?><a class="<?= $page === 'administration' ? 'active' : '' ?>" href="/?page=administration"><i class="ti ti-tool"></i><span>Administration</span></a><?php endif; ?>
|
||||
<a class="<?= $page === 'home' ? 'active' : '' ?>" href="<?= htmlspecialchars(appPath('?page=home')) ?>"><i class="ti ti-layout-dashboard"></i><span>Dashboard</span></a>
|
||||
<a class="<?= $page === 'profile' ? 'active' : '' ?>" href="<?= htmlspecialchars(appPath('?page=profile')) ?>"><i class="ti ti-user-circle"></i><span>Profil</span></a>
|
||||
<?php if (($current['role'] ?? '') !== 'member'): ?><a class="<?= $page === 'members' ? 'active' : '' ?>" href="<?= htmlspecialchars(appPath('?page=members')) ?>"><i class="ti ti-user-plus"></i><span>Benutzerverwaltung</span></a><?php endif; ?>
|
||||
<a class="<?= $page === 'booking' ? 'active' : '' ?>" href="<?= htmlspecialchars(appPath('?page=booking')) ?>"><i class="ti ti-clock-plus"></i><span>Stundenbuchung</span></a>
|
||||
<?php if (($current['role'] ?? '') !== 'member'): ?><a class="<?= $page === 'admin' ? 'active' : '' ?>" href="<?= htmlspecialchars(appPath('?page=admin')) ?>"><i class="ti ti-settings"></i><span>Vereinskonfiguration</span></a><?php endif; ?>
|
||||
<?php if (($current['role'] ?? '') === 'admin'): ?><a class="<?= $page === 'administration' ? 'active' : '' ?>" href="<?= htmlspecialchars(appPath('?page=administration')) ?>"><i class="ti ti-tool"></i><span>Administration</span></a><?php endif; ?>
|
||||
</nav>
|
||||
</aside>
|
||||
<main class="app-main page-body">
|
||||
<div class="container-xl py-4">
|
||||
<?php if ($notice): ?><div class="alert alert-success"><?= htmlspecialchars($notice) ?></div><?php endif; ?>
|
||||
<?php if ($error): ?><div class="alert alert-danger"><?= htmlspecialchars($error) ?></div><?php endif; ?>
|
||||
<?php if ($page === 'dashboard'): ?>
|
||||
<?php if ($page === 'home'): ?>
|
||||
<div class="row row-cards mb-3">
|
||||
<div class="col-md-3"><div class="card stat-card"><div class="card-body"><i class="ti ti-target stat-icon"></i><div class="text-secondary small mb-1"><?= htmlspecialchars($dashboardScopeLabel) ?></div><div class="h1 mb-1"><?= number_format($dashboardTargetHours, 1, ',', '.') ?> h</div><div class="text-secondary"><?= $canSeeAllMembers ? 'Pflichtstunden gesamt' : 'Pflichtstunden' ?></div></div></div></div>
|
||||
<div class="col-md-3"><div class="card stat-card"><div class="card-body"><i class="ti ti-currency-euro stat-icon"></i><div class="text-secondary small mb-1">Pro Stunde</div><div class="h1 mb-1"><?= money((float)$config['hourly_rate_eur']) ?></div><div class="text-secondary">Stundensatz</div></div></div></div>
|
||||
@@ -631,7 +631,7 @@ function renderAppShell(string $page): void
|
||||
<div class="col-lg-4"><div class="card soft-note"><div class="card-body"><h3 class="section-title"><i class="ti ti-info-circle"></i><span>Hinweis</span></h3><p class="text-secondary mb-2">Nur Admins dürfen Rollen setzen oder ändern.</p><p class="text-secondary mb-0">Neue Benutzer erhalten automatisch das Startpasswort <strong>mitglied123</strong>.</p></div></div></div>
|
||||
</div>
|
||||
<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-users"></i><span>Benutzerliste</span></h3><div class="table-responsive"><table class="table table-vcenter"><thead><tr><th>Nachname</th><th>Vorname</th><th>E-Mail</th><th>Rolle</th><th>Geleistet</th><th class="text-end">Aktionen</th></tr></thead><tbody><?php foreach (($memberUsers['entries'] ?? []) as $item): ?><tr><td><?= htmlspecialchars((string)($item['lastname'] ?? '')) ?></td><td><?= htmlspecialchars((string)($item['firstname'] ?? '')) ?></td><td><?= htmlspecialchars($item['email']) ?></td><td><?php if (($current['role'] ?? '') === 'admin'): ?><form method="post" class="d-flex gap-2 align-items-center justify-content-start"><input type="hidden" name="action" value="update_user_role"><input type="hidden" name="user_id" value="<?= (int)$item['id'] ?>"><select class="form-select form-select-sm" name="role"<?= ((int)$item['id'] === (int)($current['id'] ?? 0)) ? ' disabled' : '' ?>><option value="member" <?= (string)$item['role'] === 'member' ? 'selected' : '' ?>>Mitglied</option><option value="editor" <?= (string)$item['role'] === 'editor' ? 'selected' : '' ?>>Bearbeiter</option><option value="admin" <?= (string)$item['role'] === 'admin' ? 'selected' : '' ?>>Admin</option></select><?php if ((int)$item['id'] !== (int)($current['id'] ?? 0)): ?><button class="btn btn-sm btn-outline-primary" type="submit">Speichern</button><?php endif; ?></form><?php else: ?><span class="badge bg-secondary-lt"><?= htmlspecialchars(roleLabel((string)$item['role'])) ?></span><?php endif; ?></td><td><?= number_format((float)$item['hours_worked'], 1, ',', '.') ?> h</td><td class="text-end"><div class="d-inline-flex gap-2 flex-wrap justify-content-end"><?php if (canManageMembers($current) && (string)$item['role'] !== 'admin'): ?><form method="post" class="m-0"><input type="hidden" name="action" value="reset_user_password"><input type="hidden" name="user_id" value="<?= (int)$item['id'] ?>"><button class="btn btn-sm btn-outline-secondary" type="submit">Passwort zurücksetzen</button></form><?php endif; ?><?php if (canManageMembers($current) && (string)$item['role'] !== 'admin'): ?><form method="post" class="m-0" onsubmit="return confirm('Benutzer wirklich löschen?');"><input type="hidden" name="action" value="delete_user"><input type="hidden" name="user_id" value="<?= (int)$item['id'] ?>"><button class="btn btn-sm btn-outline-danger" type="submit">Löschen</button></form><?php endif; ?><?php if (!canManageMembers($current) || (string)$item['role'] === 'admin'): ?><span class="text-secondary small">Keine Aktionen</span><?php endif; ?></div></td></tr><?php endforeach; ?><?php if (empty($memberUsers['entries'])): ?><tr><td colspan="6" class="text-secondary text-center">Keine Benutzer vorhanden.</td></tr><?php endif; ?></tbody></table></div><?php if (($memberUsers['total_pages'] ?? 1) > 1): ?><div class="d-flex align-items-center justify-content-between gap-3 mt-3"><a class="btn btn-outline-secondary<?= ($memberUsers['page'] ?? 1) <= 1 ? ' disabled' : '' ?>" href="/?page=members&member_page=<?= max(1, (int)($memberUsers['page'] ?? 1) - 1) ?>">Zurück</a><div class="text-secondary small">Seite <?= (int)($memberUsers['page'] ?? 1) ?> von <?= (int)($memberUsers['total_pages'] ?? 1) ?>, insgesamt <?= (int)($memberUsers['total'] ?? 0) ?> Einträge</div><a class="btn btn-outline-secondary<?= ($memberUsers['page'] ?? 1) >= ($memberUsers['total_pages'] ?? 1) ? ' disabled' : '' ?>" href="/?page=members&member_page=<?= min((int)($memberUsers['total_pages'] ?? 1), (int)($memberUsers['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-users"></i><span>Benutzerliste</span></h3><div class="table-responsive"><table class="table table-vcenter"><thead><tr><th>Nachname</th><th>Vorname</th><th>E-Mail</th><th>Rolle</th><th>Geleistet</th><th class="text-end">Aktionen</th></tr></thead><tbody><?php foreach (($memberUsers['entries'] ?? []) as $item): ?><tr><td><?= htmlspecialchars((string)($item['lastname'] ?? '')) ?></td><td><?= htmlspecialchars((string)($item['firstname'] ?? '')) ?></td><td><?= htmlspecialchars($item['email']) ?></td><td><?php if (($current['role'] ?? '') === 'admin'): ?><form method="post" class="d-flex gap-2 align-items-center justify-content-start"><input type="hidden" name="action" value="update_user_role"><input type="hidden" name="user_id" value="<?= (int)$item['id'] ?>"><select class="form-select form-select-sm" name="role"<?= ((int)$item['id'] === (int)($current['id'] ?? 0)) ? ' disabled' : '' ?>><option value="member" <?= (string)$item['role'] === 'member' ? 'selected' : '' ?>>Mitglied</option><option value="editor" <?= (string)$item['role'] === 'editor' ? 'selected' : '' ?>>Bearbeiter</option><option value="admin" <?= (string)$item['role'] === 'admin' ? 'selected' : '' ?>>Admin</option></select><?php if ((int)$item['id'] !== (int)($current['id'] ?? 0)): ?><button class="btn btn-sm btn-outline-primary" type="submit">Speichern</button><?php endif; ?></form><?php else: ?><span class="badge bg-secondary-lt"><?= htmlspecialchars(roleLabel((string)$item['role'])) ?></span><?php endif; ?></td><td><?= number_format((float)$item['hours_worked'], 1, ',', '.') ?> h</td><td class="text-end"><div class="d-inline-flex gap-2 flex-wrap justify-content-end"><?php if (canManageMembers($current) && (string)$item['role'] !== 'admin'): ?><form method="post" class="m-0"><input type="hidden" name="action" value="reset_user_password"><input type="hidden" name="user_id" value="<?= (int)$item['id'] ?>"><button class="btn btn-sm btn-outline-secondary" type="submit">Passwort zurücksetzen</button></form><form method="post" class="m-0" onsubmit="return confirm('Benutzer wirklich löschen?');"><input type="hidden" name="action" value="delete_user"><input type="hidden" name="user_id" value="<?= (int)$item['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; ?></div></td></tr><?php endforeach; ?><?php if (empty($memberUsers['entries'])): ?><tr><td colspan="6" class="text-secondary text-center">Keine Benutzer vorhanden.</td></tr><?php endif; ?></tbody></table></div><?php if (($memberUsers['total_pages'] ?? 1) > 1): ?><div class="d-flex align-items-center justify-content-between gap-3 mt-3"><a class="btn btn-outline-secondary<?= ($memberUsers['page'] ?? 1) <= 1 ? ' disabled' : '' ?>" href="<?= htmlspecialchars(appPath('?page=members&member_page=' . max(1, (int)($memberUsers['page'] ?? 1) - 1))) ?>">Zurück</a><div class="text-secondary small">Seite <?= (int)($memberUsers['page'] ?? 1) ?> von <?= (int)($memberUsers['total_pages'] ?? 1) ?>, insgesamt <?= (int)($memberUsers['total'] ?? 0) ?> Einträge</div><a class="btn btn-outline-secondary<?= ($memberUsers['page'] ?? 1) >= ($memberUsers['total_pages'] ?? 1) ? ' disabled' : '' ?>" href="<?= htmlspecialchars(appPath('?page=members&member_page=' . min((int)($memberUsers['total_pages'] ?? 1), (int)($memberUsers['page'] ?? 1) + 1))) ?>">Weiter</a></div><?php endif; ?></div></div></div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
@@ -645,7 +645,7 @@ function renderAppShell(string $page): void
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<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 (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 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="<?= htmlspecialchars(appPath('?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="<?= htmlspecialchars(appPath('?page=booking&booking_page=' . min((int)($bookings['total_pages'] ?? 1), (int)($bookings['page'] ?? 1) + 1))) ?>">Weiter</a></div><?php endif; ?></div></div></div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
@@ -671,7 +671,7 @@ function renderAppShell(string $page): void
|
||||
<div class="col-lg-12"><div class="card"><div class="card-body admin-actions"><h3 class="section-title"><i class="ti ti-file-import"></i><span>CSV-Import</span></h3><p class="text-secondary">Importiert Benutzerdaten aus einer CSV-Datei mit den Spalten <strong>firstname</strong>, <strong>lastname</strong>, <strong>email</strong>, <strong>role</strong> und <strong>hours_worked</strong>.</p><div class="text-secondary small mb-3"><strong>Beispiel-Kopfzeile:</strong> <code>firstname,lastname,email,role,hours_worked</code></div><form method="post" enctype="multipart/form-data"><input type="hidden" name="action" value="import_csv"><div class="mb-3"><input class="form-control" name="csv_file" type="file" accept=".csv,text/csv" required></div><button class="btn btn-primary" type="submit">Benutzerdaten aus CSV importieren</button></form></div></div></div>
|
||||
<div class="col-lg-12"><div class="card"><div class="card-body admin-actions"><h3 class="section-title"><i class="ti ti-rotate-clockwise-2"></i><span>Arbeitsstunden zurücksetzen</span></h3><p class="text-secondary">Setzt alle gebuchten Arbeitsstunden für alle Mitglieder auf 0. Diese Aktion ist für den Jahreswechsel gedacht.</p><form method="post" onsubmit="return confirm('Wirklich alle Arbeitsstunden auf 0 zurücksetzen?');"><input type="hidden" name="action" value="reset_all_hours"><button class="btn btn-outline-danger" type="submit">Alle Arbeitsstunden auf 0 setzen</button></form></div></div></div>
|
||||
</div>
|
||||
<div class="row row-cards mt-2"><div class="col-lg-12"><div class="card"><div class="card-body admin-actions"><h3 class="section-title"><i class="ti ti-history"></i><span>Logansicht</span></h3><form method="post" class="mb-3"><input type="hidden" name="action" value="export_logs_csv"><button class="btn btn-primary" type="submit">Log als CSV exportieren</button></form><ul class="list-group list-group-flush"><?php foreach (($logs['entries'] ?? []) as $entry): ?><li class="list-group-item px-0"><strong><?= htmlspecialchars($entry['ts']) ?></strong><br><?= htmlspecialchars($entry['actor']) ?>: <?= htmlspecialchars($entry['action']) ?></li><?php endforeach; ?><?php if (empty($logs['entries'])): ?><li class="list-group-item px-0 text-secondary">Keine Logeinträge vorhanden.</li><?php endif; ?></ul><?php if (($logs['total_pages'] ?? 1) > 1): ?><div class="d-flex align-items-center justify-content-between gap-3 mt-3"><a class="btn btn-outline-secondary<?= ($logs['page'] ?? 1) <= 1 ? ' disabled' : '' ?>" href="/?page=administration&log_page=<?= max(1, (int)($logs['page'] ?? 1) - 1) ?>">Zurück</a><div class="text-secondary small">Seite <?= (int)($logs['page'] ?? 1) ?> von <?= (int)($logs['total_pages'] ?? 1) ?>, insgesamt <?= (int)($logs['total'] ?? 0) ?> Einträge</div><a class="btn btn-outline-secondary<?= ($logs['page'] ?? 1) >= ($logs['total_pages'] ?? 1) ? ' disabled' : '' ?>" href="/?page=administration&log_page=<?= min((int)($logs['total_pages'] ?? 1), (int)($logs['page'] ?? 1) + 1) ?>">Weiter</a></div><?php endif; ?></div></div></div></div>
|
||||
<div class="row row-cards mt-2"><div class="col-lg-12"><div class="card"><div class="card-body admin-actions"><h3 class="section-title"><i class="ti ti-history"></i><span>Logansicht</span></h3><form method="post" class="mb-3"><input type="hidden" name="action" value="export_logs_csv"><button class="btn btn-primary" type="submit">Log als CSV exportieren</button></form><form method="post" class="mb-3" onsubmit="return confirm('Wirklich alle Logeinträge löschen?');"><input type="hidden" name="action" value="clear_audit_logs"><button class="btn btn-outline-danger" type="submit">Logeinträge löschen</button></form><ul class="list-group list-group-flush"><?php foreach (($logs['entries'] ?? []) as $entry): ?><li class="list-group-item px-0"><strong><?= htmlspecialchars($entry['ts']) ?></strong><br><?= htmlspecialchars($entry['actor']) ?>: <?= htmlspecialchars($entry['action']) ?></li><?php endforeach; ?><?php if (empty($logs['entries'])): ?><li class="list-group-item px-0 text-secondary">Keine Logeinträge vorhanden.</li><?php endif; ?></ul><?php if (($logs['total_pages'] ?? 1) > 1): ?><div class="d-flex align-items-center justify-content-between gap-3 mt-3"><a class="btn btn-outline-secondary<?= ($logs['page'] ?? 1) <= 1 ? ' disabled' : '' ?>" href="<?= htmlspecialchars(appPath('?page=administration&log_page=' . max(1, (int)($logs['page'] ?? 1) - 1))) ?>">Zurück</a><div class="text-secondary small">Seite <?= (int)($logs['page'] ?? 1) ?> von <?= (int)($logs['total_pages'] ?? 1) ?>, insgesamt <?= (int)($logs['total'] ?? 0) ?> Einträge</div><a class="btn btn-outline-secondary<?= ($logs['page'] ?? 1) >= ($logs['total_pages'] ?? 1) ? ' disabled' : '' ?>" href="<?= htmlspecialchars(appPath('?page=administration&log_page=' . min((int)($logs['total_pages'] ?? 1), (int)($logs['page'] ?? 1) + 1))) ?>">Weiter</a></div><?php endif; ?></div></div></div></div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
Reference in New Issue
Block a user