adasd
All checks were successful
Deploy / deploy (push) Successful in 52s

This commit is contained in:
2026-07-22 22:19:31 +02:00
parent 2e800d4839
commit cbecbef830
8 changed files with 450 additions and 194 deletions

View File

@@ -6,6 +6,16 @@ $editing = isset($editEvent) && $editEvent !== null;
$actionEvent = $editing ? 'event_update' : 'event_add';
$startVal = $editEvent ? date('Y-m-d\TH:i', strtotime((string)$editEvent['starts_at'])) : '';
$allowNoKidsChecked = $editEvent ? ((int)$editEvent['allow_kids'] === 0) : false;
$sectionLinks = [
'profile' => 'Profil',
'children' => 'Kinder',
'events' => 'Termine',
'community' => 'Community',
'settings' => 'Einstellungen',
];
if (in_array('admin', $allowedSections ?? [], true)) {
$sectionLinks['admin'] = 'Admin';
}
?>
<main class="section">
<div class="container" style="display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:12px;">
@@ -29,177 +39,272 @@ $allowNoKidsChecked = $editEvent ? ((int)$editEvent['allow_kids'] === 0) : false
</div>
<div class="container dash-section">
<div class="dash-grid">
<?php if (in_array('forum_admin', $communityRoles ?? [], true) || !empty($communityIsSiteAdmin)): ?>
<div class="card dash-card">
<div class="badge">Admin</div>
<h3>Community-Verwaltung</h3>
<div class="flex gap-12" style="margin:12px 0; flex-wrap:wrap;">
<a class="btn" href="/community-admin">Zum Admin-Bereich</a>
<div class="account-layout">
<aside class="account-sidebar">
<div class="account-sidebar__inner">
<div class="account-sidebar__head">
<span class="badge">Mitgliederbereich</span>
<h2>Navigation</h2>
</div>
<?php if ($communityMigrationStatus): ?>
<ul class="dash-list">
<li>Status: <?= !empty($communityMigrationStatus['complete']) ? 'Vollständig eingerichtet' : 'Migration noch offen' ?></li>
<li>Fehlende Bausteine: <?= !empty($communityMigrationStatus['missing']) ? htmlspecialchars(implode(', ', $communityMigrationStatus['missing']), ENT_QUOTES) : 'Keine' ?></li>
</ul>
<?php else: ?>
<p class="muted small">Migrationsstatus konnte nicht ermittelt werden.</p>
<?php endif; ?>
<?php if (!empty($communityIsSiteAdmin)): ?>
<form method="post" style="margin-top:12px;">
<input type="hidden" name="action" value="community_migrate">
<button class="btn" type="submit">Community-Migration ausführen</button>
</form>
<?php endif; ?>
<p class="muted small" style="margin-top:10px;">Bündelt Moderation, Bewerbungen, Rollen und bei Bedarf auch die Community-Migration.</p>
</div>
<?php endif; ?>
<div class="card dash-card">
<div class="badge">Community</div>
<h3>Dein Community-Status</h3>
<ul class="dash-list">
<li>Rang: <?= htmlspecialchars(trim(($communityLevel['icon'] ?? '') . ' ' . ($communityLevel['label'] ?? '')), ENT_QUOTES) ?></li>
<li>Punkte: <?= number_format((float)$communityPoints, 1, ',', '.') ?></li>
<li>Rollen: <?= htmlspecialchars($communityRoles ? implode(', ', $communityRoles) : 'Keine besonderen Rollen', ENT_QUOTES) ?></li>
<li>Themen erstellen: <?= $communityRestrictions['thread_create_blocked'] ? 'Gesperrt' : 'Erlaubt' ?></li>
<li>Antworten schreiben: <?= $communityRestrictions['reply_blocked'] ? 'Gesperrt' : 'Erlaubt' ?></li>
</ul>
<?php if (!empty($communityRestrictions['reason'])): ?>
<p class="muted small" style="margin-top:10px;">Hinweis zur Community-Sperre: <?= htmlspecialchars((string)$communityRestrictions['reason'], ENT_QUOTES) ?></p>
<?php endif; ?>
<?php if ($communityCanApply): ?>
<form method="post" class="stack gap-6" style="margin-top:12px;">
<input type="hidden" name="action" value="community_admin_apply">
<label class="label" for="communityMotivation">Bewerbung als Forum-Admin</label>
<textarea id="communityMotivation" name="motivation" class="textarea" rows="4" placeholder="Warum möchtest du die Community als Forum-Admin unterstützen?" required></textarea>
<button class="btn" type="submit">Bewerbung absenden</button>
</form>
<?php elseif ($communityApplication): ?>
<div style="margin-top:12px;">
<p class="muted small" style="margin:0;">Letzte Bewerbung: <strong><?= htmlspecialchars((string)$communityApplication['status'], ENT_QUOTES) ?></strong></p>
<?php if (!empty($communityApplication['decision_reason'])): ?>
<p class="muted small" style="margin-top:6px;"><?= htmlspecialchars((string)$communityApplication['decision_reason'], ENT_QUOTES) ?></p>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
<div class="card dash-card">
<div class="badge">Profil</div>
<h3>Deine Angaben</h3>
<ul class="dash-list">
<li>Name: <?= htmlspecialchars(trim($profile['first_name'] . ' ' . $profile['last_name']), ENT_QUOTES) ?></li>
<li>Anzeigename: <?= htmlspecialchars($profile['display_name'], ENT_QUOTES) ?></li>
<li>Ort: <?= htmlspecialchars($profile['city'], ENT_QUOTES) ?> <?= htmlspecialchars($profile['zip'], ENT_QUOTES) ?></li>
<li>E-Mail: <?= htmlspecialchars($profile['email'], ENT_QUOTES) ?></li>
<li>Telefon: <?= htmlspecialchars($profile['contact_phone'], ENT_QUOTES) ?></li>
<li>Standortfreigabe: <?=
htmlspecialchars(match ($profile['location_tracking_preference'] ?? 'prompt') {
'enabled' => 'Immer verwenden',
'disabled' => 'Deaktiviert',
default => 'Beim nächsten Mal fragen',
}, ENT_QUOTES)
?></li>
<li>Beruf: <?= htmlspecialchars($profile['profession'], ENT_QUOTES) ?></li>
<li>Sprachen: <?= htmlspecialchars($profile['languages'], ENT_QUOTES) ?></li>
<li>About: <?= htmlspecialchars($profile['about'], ENT_QUOTES) ?></li>
</ul>
<div class="flex gap-12" style="margin-top:12px;">
<button class="btn" type="button" data-modal-open="modalProfile">Bearbeiten</button>
</div>
</div>
<div class="card dash-card">
<div class="badge">Kinder</div>
<h3>Deine Kids</h3>
<?php if (!$children): ?>
<p class="muted small">Noch keine Kinder eingetragen.</p>
<?php else: ?>
<ul class="dash-list">
<?php foreach ($children as $c): ?>
<li><?= htmlspecialchars($c['first_name'], ENT_QUOTES) ?>, <?= htmlspecialchars($c['gender'], ENT_QUOTES) ?> <?= $c['age_years'] ? '(' . (int)$c['age_years'] . ' Jahre)' : '' ?></li>
<nav class="account-sidebar__nav">
<?php foreach ($sectionLinks as $sectionKey => $sectionLabel): ?>
<a href="/dashboard?section=<?= rawurlencode($sectionKey) ?>" class="<?= $section === $sectionKey ? 'is-active' : '' ?>">
<span><?= htmlspecialchars($sectionLabel, ENT_QUOTES) ?></span>
</a>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<div class="flex gap-12" style="margin-top:12px;">
<button class="btn" type="button" data-modal-open="modalChild">Kind hinzufügen</button>
</nav>
</div>
</div>
</div>
</div>
</aside>
<div class="container dash-section" id="events">
<div class="card dash-card">
<div class="badge">Deine Events</div>
<div class="flex gap-12" style="margin:10px 0 16px 0; flex-wrap: wrap;">
<button class="btn" type="button" data-modal-open="modalEvent">Event anlegen</button>
</div>
<?php if (!$eventsUpcoming): ?>
<p class="muted small">Keine zukünftigen Events angelegt.</p>
<?php else: ?>
<ul class="dash-list" style="margin-top:10px;">
<?php foreach ($eventsUpcoming as $e): ?>
<li>
<div style="display:flex; justify-content:space-between; gap:12px; align-items:center; flex-wrap: wrap;">
<div>
<strong><?= htmlspecialchars($e['title'], ENT_QUOTES) ?></strong>
<?php if ($e['status'] === 'cancelled'): ?>
<span class="badge" style="background:#fee2e2; color:#991b1b;">Abgesagt</span>
<?php endif; ?>
<div class="muted small" style="margin-top:4px;">
<?= htmlspecialchars($e['city'], ENT_QUOTES) ?> · <?= htmlspecialchars($e['starts_at'], ENT_QUOTES) ?> · <?= htmlspecialchars($e['visibility'], ENT_QUOTES) ?>
· Anmeldungen: <?= (int)$e['participant_count'] ?>
</div>
</div>
<div class="flex gap-8" style="flex-wrap: wrap;">
<a class="btn ghost" href="/dashboard?edit_event=<?= (int)$e['id'] ?>#events">Bearbeiten</a>
<?php if ((int)$e['participant_count'] === 0): ?>
<form method="post" action="/dashboard#events" onsubmit="return confirm('Event wirklich löschen?');">
<input type="hidden" name="action" value="event_delete">
<input type="hidden" name="event_id" value="<?= (int)$e['id'] ?>">
<button class="btn ghost" type="submit">Löschen</button>
</form>
<?php else: ?>
<?php if ($e['status'] !== 'cancelled'): ?>
<form method="post" action="/dashboard#events" onsubmit="return confirm('Event für alle absagen?');">
<input type="hidden" name="action" value="event_cancel">
<input type="hidden" name="event_id" value="<?= (int)$e['id'] ?>">
<button class="btn ghost" type="submit">Absagen</button>
</form>
<?php endif; ?>
<?php endif; ?>
</div>
<div class="account-main">
<?php if ($section === 'profile'): ?>
<section class="account-panel">
<div class="account-panel__head">
<h2>Dein Profil</h2>
<p class="muted">Hier pflegst du deine persönlichen Angaben für den Mitgliederbereich.</p>
</div>
<div class="account-panel__body">
<dl class="account-kv">
<div><dt>Name</dt><dd><?= htmlspecialchars(trim($profile['first_name'] . ' ' . $profile['last_name']), ENT_QUOTES) ?></dd></div>
<div><dt>Anzeigename</dt><dd><?= htmlspecialchars($profile['display_name'], ENT_QUOTES) ?></dd></div>
<div><dt>Ort</dt><dd><?= htmlspecialchars(trim($profile['city'] . ' ' . $profile['zip']), ENT_QUOTES) ?></dd></div>
<div><dt>E-Mail</dt><dd><?= htmlspecialchars($profile['email'], ENT_QUOTES) ?></dd></div>
<div><dt>Telefon</dt><dd><?= htmlspecialchars($profile['contact_phone'], ENT_QUOTES) ?></dd></div>
<div><dt>Beruf</dt><dd><?= htmlspecialchars($profile['profession'], ENT_QUOTES) ?></dd></div>
<div><dt>Sprachen</dt><dd><?= htmlspecialchars($profile['languages'], ENT_QUOTES) ?></dd></div>
<div><dt>Über dich</dt><dd><?= htmlspecialchars($profile['about'], ENT_QUOTES) ?></dd></div>
</dl>
<div class="flex gap-12" style="margin-top:18px;">
<button class="btn" type="button" data-modal-open="modalProfile">Profil bearbeiten</button>
</div>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<details style="margin-top:12px;">
<summary style="cursor:pointer;">Vergangene Events anzeigen</summary>
<?php if (!$eventsPast): ?>
<p class="muted small">Keine vergangenen Events.</p>
<?php else: ?>
<ul class="dash-list" style="margin-top:10px;">
<?php foreach ($eventsPast as $e): ?>
<li>
<div style="display:flex; justify-content:space-between; gap:12px; align-items:center; flex-wrap: wrap;">
<div>
<strong><?= htmlspecialchars($e['title'], ENT_QUOTES) ?></strong>
<?php if ($e['status'] === 'cancelled'): ?>
<span class="badge" style="background:#fee2e2; color:#991b1b;">Abgesagt</span>
<?php endif; ?>
<div class="muted small" style="margin-top:4px;">
<?= htmlspecialchars($e['city'], ENT_QUOTES) ?> · <?= htmlspecialchars($e['starts_at'], ENT_QUOTES) ?> · <?= htmlspecialchars($e['visibility'], ENT_QUOTES) ?>
· Anmeldungen: <?= (int)$e['participant_count'] ?>
</div>
</div>
</div>
</li>
<?php endforeach; ?>
</ul>
</div>
</section>
<?php endif; ?>
</details>
<?php if ($section === 'children'): ?>
<section class="account-panel">
<div class="account-panel__head">
<h2>Deine Kinder</h2>
<p class="muted">Optionale Angaben, die dir bei Planung und passenden Treffen helfen können.</p>
</div>
<div class="account-panel__body">
<?php if (!$children): ?>
<p class="muted small">Noch keine Kinder eingetragen.</p>
<?php else: ?>
<ul class="dash-list">
<?php foreach ($children as $c): ?>
<li><?= htmlspecialchars($c['first_name'], ENT_QUOTES) ?>, <?= htmlspecialchars($c['gender'], ENT_QUOTES) ?> <?= $c['age_years'] ? '(' . (int)$c['age_years'] . ' Jahre)' : '' ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<div class="flex gap-12" style="margin-top:18px;">
<button class="btn" type="button" data-modal-open="modalChild">Kind hinzufügen</button>
</div>
</div>
</section>
<?php endif; ?>
<?php if ($section === 'community'): ?>
<section class="account-panel">
<div class="account-panel__head">
<h2>Dein Community-Status</h2>
<p class="muted">Rang, Punkte, Rollen und dein aktueller Stand in der Community.</p>
</div>
<div class="account-panel__body">
<dl class="account-kv">
<div><dt>Rang</dt><dd><?= htmlspecialchars(trim(($communityLevel['icon'] ?? '') . ' ' . ($communityLevel['label'] ?? '')), ENT_QUOTES) ?></dd></div>
<div><dt>Punkte</dt><dd><?= number_format((float)$communityPoints, 1, ',', '.') ?></dd></div>
<div><dt>Rollen</dt><dd><?= htmlspecialchars($communityRoles ? implode(', ', $communityRoles) : 'Keine besonderen Rollen', ENT_QUOTES) ?></dd></div>
<div><dt>Themen erstellen</dt><dd><?= $communityRestrictions['thread_create_blocked'] ? 'Gesperrt' : 'Erlaubt' ?></dd></div>
<div><dt>Antworten schreiben</dt><dd><?= $communityRestrictions['reply_blocked'] ? 'Gesperrt' : 'Erlaubt' ?></dd></div>
</dl>
<?php if (!empty($communityRestrictions['reason'])): ?>
<p class="muted small" style="margin-top:14px;">Hinweis zur Community-Sperre: <?= htmlspecialchars((string)$communityRestrictions['reason'], ENT_QUOTES) ?></p>
<?php endif; ?>
<?php if ($communityCanApply): ?>
<form method="post" class="stack gap-6" style="margin-top:18px;">
<input type="hidden" name="action" value="community_admin_apply">
<label class="label" for="communityMotivation">Bewerbung als Forum-Admin</label>
<textarea id="communityMotivation" name="motivation" class="textarea" rows="4" placeholder="Warum möchtest du die Community als Forum-Admin unterstützen?" required></textarea>
<button class="btn" type="submit">Bewerbung absenden</button>
</form>
<?php elseif ($communityApplication): ?>
<div style="margin-top:18px;">
<p class="muted small" style="margin:0;">Letzte Bewerbung: <strong><?= htmlspecialchars((string)$communityApplication['status'], ENT_QUOTES) ?></strong></p>
<?php if (!empty($communityApplication['decision_reason'])): ?>
<p class="muted small" style="margin-top:6px;"><?= htmlspecialchars((string)$communityApplication['decision_reason'], ENT_QUOTES) ?></p>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
</section>
<?php endif; ?>
<?php if ($section === 'events'): ?>
<section class="account-panel" id="events">
<div class="account-panel__head">
<h2>Deine Termine</h2>
<p class="muted">Erstelle, bearbeite und verwalte deine eigenen Treffen.</p>
</div>
<div class="account-panel__body">
<div class="flex gap-12" style="margin:0 0 16px 0; flex-wrap: wrap;">
<button class="btn" type="button" data-modal-open="modalEvent">Event anlegen</button>
</div>
<?php if (!$eventsUpcoming): ?>
<p class="muted small">Keine zukünftigen Events angelegt.</p>
<?php else: ?>
<ul class="dash-list" style="margin-top:10px;">
<?php foreach ($eventsUpcoming as $e): ?>
<li>
<div style="display:flex; justify-content:space-between; gap:12px; align-items:center; flex-wrap: wrap;">
<div>
<strong><?= htmlspecialchars($e['title'], ENT_QUOTES) ?></strong>
<?php if ($e['status'] === 'cancelled'): ?>
<span class="badge" style="background:#fee2e2; color:#991b1b;">Abgesagt</span>
<?php endif; ?>
<div class="muted small" style="margin-top:4px;">
<?= htmlspecialchars($e['city'], ENT_QUOTES) ?> · <?= htmlspecialchars($e['starts_at'], ENT_QUOTES) ?> · <?= htmlspecialchars($e['visibility'], ENT_QUOTES) ?>
· Anmeldungen: <?= (int)$e['participant_count'] ?>
</div>
</div>
<div class="flex gap-8" style="flex-wrap: wrap;">
<a class="btn ghost" href="/dashboard?section=events&edit_event=<?= (int)$e['id'] ?>#events">Bearbeiten</a>
<?php if ((int)$e['participant_count'] === 0): ?>
<form method="post" action="/dashboard?section=events#events" onsubmit="return confirm('Event wirklich löschen?');">
<input type="hidden" name="action" value="event_delete">
<input type="hidden" name="event_id" value="<?= (int)$e['id'] ?>">
<button class="btn ghost" type="submit">Löschen</button>
</form>
<?php else: ?>
<?php if ($e['status'] !== 'cancelled'): ?>
<form method="post" action="/dashboard?section=events#events" onsubmit="return confirm('Event für alle absagen?');">
<input type="hidden" name="action" value="event_cancel">
<input type="hidden" name="event_id" value="<?= (int)$e['id'] ?>">
<button class="btn ghost" type="submit">Absagen</button>
</form>
<?php endif; ?>
<?php endif; ?>
</div>
</div>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<details style="margin-top:12px;">
<summary style="cursor:pointer;">Vergangene Events anzeigen</summary>
<?php if (!$eventsPast): ?>
<p class="muted small">Keine vergangenen Events.</p>
<?php else: ?>
<ul class="dash-list" style="margin-top:10px;">
<?php foreach ($eventsPast as $e): ?>
<li>
<div style="display:flex; justify-content:space-between; gap:12px; align-items:center; flex-wrap: wrap;">
<div>
<strong><?= htmlspecialchars($e['title'], ENT_QUOTES) ?></strong>
<?php if ($e['status'] === 'cancelled'): ?>
<span class="badge" style="background:#fee2e2; color:#991b1b;">Abgesagt</span>
<?php endif; ?>
<div class="muted small" style="margin-top:4px;">
<?= htmlspecialchars($e['city'], ENT_QUOTES) ?> · <?= htmlspecialchars($e['starts_at'], ENT_QUOTES) ?> · <?= htmlspecialchars($e['visibility'], ENT_QUOTES) ?>
· Anmeldungen: <?= (int)$e['participant_count'] ?>
</div>
</div>
</div>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</details>
</div>
</section>
<?php endif; ?>
<?php if ($section === 'settings'): ?>
<section class="account-panel">
<div class="account-panel__head">
<h2>Einstellungen</h2>
<p class="muted">Standort, Browserfreigabe und Cookie-Einstellungen an einem Ort.</p>
</div>
<div class="account-panel__body">
<form method="post" class="stack gap-12">
<input type="hidden" name="action" value="settings_location">
<div class="stack gap-6">
<label class="label" for="pLocationPref">Standort auf Papa-Kind-Treff verwenden</label>
<select id="pLocationPref" name="location_tracking_preference" class="select">
<option value="disabled" <?= (($profile['location_tracking_preference'] ?? 'prompt') === 'disabled') ? 'selected' : '' ?>>Nie verwenden</option>
<option value="prompt" <?= (($profile['location_tracking_preference'] ?? 'prompt') === 'prompt') ? 'selected' : '' ?>>Bei Bedarf nachfragen</option>
<option value="enabled" <?= (($profile['location_tracking_preference'] ?? 'prompt') === 'enabled') ? 'selected' : '' ?>>Wenn im Browser erlaubt, automatisch verwenden</option>
</select>
<p class="muted small">Diese Einstellung steuert nur, ob Papa-Kind-Treff Standortdaten nutzen darf. Die eigentliche Browserfreigabe wird separat im Browser verwaltet.</p>
</div>
<div class="flex gap-12">
<button class="btn" type="submit">Einstellungen speichern</button>
<button class="btn ghost" type="button" data-consent-open>Cookie-Einstellungen öffnen</button>
</div>
</form>
<div class="card" style="margin-top:18px;" id="locationBrowserStatusCard">
<strong>Browser-Status für Standort</strong>
<p class="muted small" id="locationBrowserStatusText" style="margin:8px 0 0;">Wird geprüft …</p>
<p class="muted small" id="locationBrowserStatusHint" style="margin:8px 0 0;">
Deine Website-Einstellung und die Browserfreigabe sind getrennte Ebenen. Wenn der Browser blockiert, kann Papa-Kind-Treff den Standort auch bei aktiver Einstellung nicht nutzen.
</p>
</div>
<div class="settings-help" style="margin-top:18px;">
<details>
<summary>Warum gibt es zwei Einstellungen?</summary>
<p class="muted small" style="margin:10px 0 0;">Der Browser entscheidet technisch, ob Standortzugriffe erlaubt sind. Papa-Kind-Treff entscheidet mit deiner Website-Einstellung, ob diese Möglichkeit auf der Plattform überhaupt verwendet werden soll.</p>
</details>
<details>
<summary>Chrome und Edge</summary>
<p class="muted small" style="margin:10px 0 0;">Auf das Schloss-Symbol links neben der Adresszeile klicken, dann `Website-Einstellungen` oder `Standort` öffnen und die Freigabe dort anpassen.</p>
</details>
<details>
<summary>Firefox</summary>
<p class="muted small" style="margin:10px 0 0;">Auf das Schloss-Symbol links neben der Adresse klicken und unter Berechtigungen den Standort prüfen oder zurücksetzen. Alternativ über Einstellungen → Datenschutz & Sicherheit → Berechtigungen.</p>
</details>
<details>
<summary>Safari</summary>
<p class="muted small" style="margin:10px 0 0;">In Safari die Website-Einstellungen für den aktuellen Tab oder die Datenschutz-/Standort-Einstellungen des Browsers beziehungsweise des Geräts prüfen.</p>
</details>
<details>
<summary>Direkt in die Browser-Einstellungen springen?</summary>
<p class="muted small" style="margin:10px 0 0;">Nein, das kann eine Website nicht zuverlässig und browserübergreifend auslösen. Deshalb zeigt Papa-Kind-Treff nur den Status an und erklärt, wo du die Freigabe findest.</p>
</details>
</div>
</div>
</section>
<?php endif; ?>
<?php if ($section === 'admin' && in_array('admin', $allowedSections ?? [], true)): ?>
<section class="account-panel">
<div class="account-panel__head">
<h2>Community-Verwaltung</h2>
<p class="muted">Moderation, Bewerbungen, Rollen und Migration.</p>
</div>
<div class="account-panel__body">
<div class="flex gap-12" style="margin:0 0 16px 0; flex-wrap:wrap;">
<a class="btn" href="/community-admin">Zum Admin-Bereich</a>
</div>
<?php if ($communityMigrationStatus): ?>
<ul class="dash-list">
<li>Status: <?= !empty($communityMigrationStatus['complete']) ? 'Vollständig eingerichtet' : 'Migration noch offen' ?></li>
<li>Fehlende Bausteine: <?= !empty($communityMigrationStatus['missing']) ? htmlspecialchars(implode(', ', $communityMigrationStatus['missing']), ENT_QUOTES) : 'Keine' ?></li>
</ul>
<?php else: ?>
<p class="muted small">Migrationsstatus konnte nicht ermittelt werden.</p>
<?php endif; ?>
<?php if (!empty($communityIsSiteAdmin)): ?>
<form method="post" style="margin-top:12px;">
<input type="hidden" name="action" value="community_migrate">
<button class="btn" type="submit">Community-Migration ausführen</button>
</form>
<?php endif; ?>
</div>
</section>
<?php endif; ?>
</div>
</div>
</div>
@@ -263,15 +368,6 @@ $allowNoKidsChecked = $editEvent ? ((int)$editEvent['allow_kids'] === 0) : false
<label class="label" for="pProf">Beruf</label>
<input id="pProf" name="profession" class="input" value="<?= htmlspecialchars($profile['profession'], ENT_QUOTES) ?>">
</div>
<div class="stack gap-6">
<label class="label" for="pLocationPref">Standortfreigabe</label>
<select id="pLocationPref" name="location_tracking_preference" class="select">
<option value="disabled" <?= (($profile['location_tracking_preference'] ?? 'prompt') === 'disabled') ? 'selected' : '' ?>>Deaktiviert</option>
<option value="prompt" <?= (($profile['location_tracking_preference'] ?? 'prompt') === 'prompt') ? 'selected' : '' ?>>Beim nächsten Mal fragen</option>
<option value="enabled" <?= (($profile['location_tracking_preference'] ?? 'prompt') === 'enabled') ? 'selected' : '' ?>>Immer verwenden</option>
</select>
<p class="muted small">Bei aktiver Freigabe wird dein aktueller Standort für passende Treffen in deiner Nähe verwendet. Du kannst die Abfrage hier jederzeit wieder auf Nachfrage oder komplett aus stellen.</p>
</div>
<div class="stack gap-6">
<label class="label" for="pAbout">Kurzvorstellung</label>
<textarea id="pAbout" name="about" class="textarea" rows="3"><?= htmlspecialchars($profile['about'], ENT_QUOTES) ?></textarea>
@@ -335,7 +431,7 @@ $allowNoKidsChecked = $editEvent ? ((int)$editEvent['allow_kids'] === 0) : false
<h3><?= $editing ? 'Event bearbeiten' : 'Neues Event' ?></h3>
<button class="btn ghost" type="button" data-modal-close>✕</button>
</div>
<form class="stack gap-12" style="margin-top: 10px;" method="post" action="/dashboard#events">
<form class="stack gap-12" style="margin-top: 10px;" method="post" action="/dashboard?section=events#events">
<input type="hidden" name="action" value="<?= htmlspecialchars($actionEvent, ENT_QUOTES) ?>">
<?php if ($editing && $editEvent): ?>
<input type="hidden" name="event_id" value="<?= (int)$editEvent['id'] ?>">
@@ -600,4 +696,45 @@ $allowNoKidsChecked = $editEvent ? ((int)$editEvent['allow_kids'] === 0) : false
if (modal) { modal.classList.add('open'); }
})();
<?php endif; ?>
(function(){
const statusText = document.getElementById('locationBrowserStatusText');
const hintText = document.getElementById('locationBrowserStatusHint');
const profilePreference = document.body?.dataset.locationPreference || 'prompt';
if (!statusText || !hintText) return;
const profileLabels = {
disabled: 'Deaktiviert',
prompt: 'Beim nächsten Mal fragen',
enabled: 'Immer verwenden',
};
const render = (browserState) => {
if (browserState === 'granted') {
statusText.textContent = 'Der Browser erlaubt Standortzugriffe derzeit.';
} else if (browserState === 'denied') {
statusText.textContent = 'Der Browser blockiert Standortzugriffe derzeit.';
} else if (browserState === 'prompt') {
statusText.textContent = 'Der Browser fragt bei Standortzugriffen erneut nach.';
} else {
statusText.textContent = 'Der Browserstatus konnte nicht eindeutig ermittelt werden.';
}
const profileState = profileLabels[profilePreference] || profileLabels.prompt;
hintText.textContent = `Website-Einstellung: ${profileState}. Wenn Browser und Website-Einstellung sich unterscheiden, gilt technisch zuerst die Browserfreigabe.`;
};
if (!('permissions' in navigator) || typeof navigator.permissions.query !== 'function') {
render('unknown');
return;
}
navigator.permissions.query({ name: 'geolocation' }).then((result) => {
render(result.state);
if (typeof result.onchange !== 'undefined') {
result.onchange = () => render(result.state);
}
}).catch(() => render('unknown'));
})();
</script>

View File

@@ -116,6 +116,7 @@ if ($pdo && ($q !== '' || $loc !== '' || ($lat !== null && $lng !== null))) {
</main>
<script>
(function(){
const body = document.body;
const locInput = document.getElementById('loc');
const latInput = document.getElementById('lat');
const lngInput = document.getElementById('lng');
@@ -125,8 +126,64 @@ if ($pdo && ($q !== '' || $loc !== '' || ($lat !== null && $lng !== null))) {
const btnMap = document.getElementById('btnMap');
const mapWrapper = document.getElementById('mapWrapper');
const mapContainer = document.getElementById('mapContainer');
const isLoggedIn = body?.dataset.auth === '1';
const profileLocationPreference = body?.dataset.locationPreference || 'prompt';
const locationStorageKey = 'pkt_user_location';
const locationSessionStorageKey = 'pkt_user_location_session';
let map, marker;
function effectiveLocationPreference() {
if (!isLoggedIn) {
try {
const guestPreference = window.localStorage.getItem('pkt_location_preference_guest');
if (guestPreference && ['disabled', 'prompt', 'enabled'].includes(guestPreference)) {
return guestPreference;
}
} catch (err) {
// ignore
}
}
return profileLocationPreference;
}
function getStoredLocation() {
const candidates = [];
try {
const sessionValue = window.sessionStorage.getItem(locationSessionStorageKey);
if (sessionValue) candidates.push(sessionValue);
} catch (err) {
// ignore
}
try {
const localValue = window.localStorage.getItem(locationStorageKey);
if (localValue) candidates.push(localValue);
} catch (err) {
// ignore
}
for (const value of candidates) {
try {
const parsed = JSON.parse(value);
if (parsed && Number.isFinite(parsed.lat) && Number.isFinite(parsed.lng)) {
return parsed;
}
} catch (err) {
// ignore
}
}
return null;
}
function applyLocation(lat, lng, label = 'Mein Standort') {
latInput.value = Number(lat).toFixed(6);
lngInput.value = Number(lng).toFixed(6);
if (locInput && !locInput.value.trim()) {
locInput.value = label;
}
toggleRadius(true);
}
function toggleRadius(show) {
if (!radiusWrap) return;
radiusWrap.hidden = !show;
@@ -198,17 +255,25 @@ if ($pdo && ($q !== '' || $loc !== '' || ($lat !== null && $lng !== null))) {
window.PKTConsent?.openPreferences?.();
return;
}
const preference = effectiveLocationPreference();
if (preference === 'disabled') {
alert('Die Standortverwendung ist in deinem Profil deaktiviert.');
return;
}
const storedLocation = getStoredLocation();
if (preference === 'enabled' && storedLocation) {
applyLocation(storedLocation.lat, storedLocation.lng, storedLocation.label || 'Mein Standort');
return;
}
if (!navigator.geolocation) {
alert('Geolocation wird nicht unterstützt.');
return;
}
navigator.geolocation.getCurrentPosition((pos) => {
const lat = pos.coords.latitude;
const lng = pos.coords.longitude;
latInput.value = lat.toFixed(6);
lngInput.value = lng.toFixed(6);
if (locInput && !locInput.value.trim()) locInput.value = 'Mein Standort';
toggleRadius(true);
applyLocation(pos.coords.latitude, pos.coords.longitude, 'Mein Standort');
}, () => alert('Standort konnte nicht ermittelt werden.'));
});

View File

@@ -45,8 +45,11 @@ if ($isLoggedIn) {
<span class="profile-menu__name"><?= htmlspecialchars($displayName, ENT_QUOTES) ?></span>
</button>
<div class="profile-menu__dropdown" role="menu">
<a href="/dashboard" role="menuitem">Übersicht</a>
<a href="/dashboard" role="menuitem">Mitgliederbereich</a>
<a href="/dashboard?section=profile" role="menuitem">Profil</a>
<a href="/dashboard?section=children" role="menuitem">Kinder</a>
<a href="/dashboard?section=events" role="menuitem">Termine</a>
<a href="/dashboard?section=community" role="menuitem">Community</a>
<a href="/dashboard?section=settings" role="menuitem">Einstellungen</a>
<a href="/logout" role="menuitem">Abmelden</a>
</div>
</div>
@@ -62,8 +65,11 @@ if ($isLoggedIn) {
<a href="/search">Suche</a>
<a href="/community">Community</a>
<?php if ($isLoggedIn): ?>
<a class="btn ghost" href="/dashboard">Übersicht</a>
<a class="btn ghost" href="/dashboard">Mitgliederbereich</a>
<a class="btn ghost" href="/dashboard?section=profile">Profil</a>
<a class="btn ghost" href="/dashboard?section=children">Kinder</a>
<a class="btn ghost" href="/dashboard?section=events">Termine</a>
<a class="btn ghost" href="/dashboard?section=community">Community</a>
<a class="btn ghost" href="/dashboard?section=settings">Einstellungen</a>
<a class="btn block" href="/logout">Abmelden</a>
<?php else: ?>
<a class="btn ghost" href="/login">Anmelden</a>