ycys
All checks were successful
Deploy / deploy (push) Successful in 53s

This commit is contained in:
2026-08-07 23:59:12 +02:00
parent a102b2aa0c
commit ddac1c182b
5 changed files with 170 additions and 109 deletions

View File

@@ -282,75 +282,24 @@ if (!empty($canManageSystemSettings)) {
<p class="muted">Hier verwaltest du nur echte eigene Events und Events, an denen du teilnimmst.</p>
</div>
<div class="account-panel__body">
<div class="flex gap-12" style="margin:0 0 16px 0; flex-wrap: wrap;">
<div class="flex gap-12" style="margin:0 0 18px 0; flex-wrap: wrap;">
<button class="btn" type="button" data-modal-open="modalEvent">Eigenes Event anlegen</button>
<?php if (!empty($calendarExportUrl)): ?>
<a class="btn ghost" href="<?= htmlspecialchars((string)$calendarExportUrl, ENT_QUOTES) ?>">ICS herunterladen</a>
<?php endif; ?>
</div>
<?php if (!empty($calendarExportUrl) && !empty($calendarFeedUrl)): ?>
<div class="account-inline-tabs" role="tablist" aria-label="Event-Bereiche">
<button class="account-inline-tabs__button is-active" type="button" role="tab" aria-selected="true" data-event-tab-trigger="my-events">Meine Events</button>
<button class="account-inline-tabs__button" type="button" role="tab" aria-selected="false" data-event-tab-trigger="sync">Synchronisation</button>
</div>
<div class="account-inline-tab-panel" data-event-tab-panel="my-events">
<div class="card" style="margin-bottom:18px;">
<strong>Kalender-Synchronisierung</strong>
<p class="muted small" style="margin:8px 0 0;">Exportiert werden immer alle eigenen Events und alle Events, an denen du teilnimmst.</p>
<p class="muted small" style="margin:8px 0 0;">„ICS herunterladen“ ist nur eine Momentaufnahme für den manuellen Import. Änderungen, Absagen oder Löschungen in Papa-Kind-Treff werden dadurch später nicht automatisch aus deinem privaten Kalender entfernt.</p>
<p class="muted small" style="margin:8px 0 0;">Für automatische Aktualisierungen nutze den persönlichen Kalender-Feed. Dann können unterstützte Kalender gelöschte oder abgesagte Einträge bei der nächsten Synchronisierung übernehmen.</p>
<div class="stack gap-6" style="margin-top:12px;">
<label class="label" for="calendarFeedUrl">Persönlicher Kalender-Feed</label>
<input id="calendarFeedUrl" class="input" type="text" readonly value="<?= htmlspecialchars((string)$calendarFeedUrl, ENT_QUOTES) ?>">
</div>
<strong>Eigene Events</strong>
<p class="muted small" style="margin:8px 0 0;">Von dir organisierte Treffen oder Events mit eigener Teilnahme-Logik.</p>
</div>
<?php endif; ?>
<div class="card" style="margin-bottom:18px;">
<strong>Eigene Events</strong>
<p class="muted small" style="margin:8px 0 0;">Von dir organisierte Treffen oder Events mit eigener Teilnahme-Logik.</p>
</div>
<?php if (!$eventsUpcoming): ?>
<p class="muted small">Keine zukünftigen eigenen 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 if (!$eventsUpcoming): ?>
<p class="muted small">Keine zukünftigen eigenen Events angelegt.</p>
<?php else: ?>
<ul class="dash-list" style="margin-top:10px;">
<?php foreach ($eventsPast as $e): ?>
<?php foreach ($eventsUpcoming as $e): ?>
<li>
<div style="display:flex; justify-content:space-between; gap:12px; align-items:center; flex-wrap: wrap;">
<div>
@@ -363,64 +312,138 @@ if (!empty($canManageSystemSettings)) {
· 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>
<div class="card" style="margin:22px 0 14px;">
<strong>Events mit deiner Teilnahme</strong>
<p class="muted small" style="margin:8px 0 0;">Events anderer Nutzer, bei denen du eingetragen bist.</p>
</div>
<?php if (!$eventsJoinedUpcoming): ?>
<p class="muted small">Aktuell nimmst du an keinen zukünftigen Events teil.</p>
<?php else: ?>
<ul class="dash-list" style="margin-top:10px;">
<?php foreach ($eventsJoinedUpcoming as $entry): ?>
<li>
<div style="display:flex; justify-content:space-between; gap:12px; align-items:center; flex-wrap: wrap;">
<div>
<strong><?= htmlspecialchars((string)$entry['title'], ENT_QUOTES) ?></strong>
<div class="muted small" style="margin-top:4px;">
<?= htmlspecialchars(trim(implode(' · ', array_filter([
(string)($entry['city'] ?? ''),
!empty($entry['starts_at']) ? (string)$entry['starts_at'] : null,
!empty($entry['host_name']) ? 'Von ' . (string)$entry['host_name'] : null,
!empty($entry['participation_status']) ? 'Status: ' . (string)$entry['participation_status'] : null,
]))), ENT_QUOTES) ?>
<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>
</div>
</div>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<details style="margin-top:12px;">
<summary style="cursor:pointer;">Vergangene Teilnahmen anzeigen</summary>
<?php if (!$eventsJoinedPast): ?>
<p class="muted small">Keine vergangenen Event-Teilnahmen.</p>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</details>
<div class="card" style="margin:22px 0 14px;">
<strong>Events mit deiner Teilnahme</strong>
<p class="muted small" style="margin:8px 0 0;">Events anderer Nutzer, bei denen du eingetragen bist.</p>
</div>
<?php if (!$eventsJoinedUpcoming): ?>
<p class="muted small">Aktuell nimmst du an keinen zukünftigen Events teil.</p>
<?php else: ?>
<ul class="dash-list" style="margin-top:10px;">
<?php foreach ($eventsJoinedPast as $entry): ?>
<?php foreach ($eventsJoinedUpcoming as $entry): ?>
<li>
<div>
<strong><?= htmlspecialchars((string)$entry['title'], ENT_QUOTES) ?></strong>
<div class="muted small" style="margin-top:4px;">
<?= htmlspecialchars(trim(implode(' · ', array_filter([
(string)($entry['city'] ?? ''),
!empty($entry['starts_at']) ? (string)$entry['starts_at'] : null,
!empty($entry['host_name']) ? 'Von ' . (string)$entry['host_name'] : null,
!empty($entry['participation_status']) ? 'Status: ' . (string)$entry['participation_status'] : null,
]))), ENT_QUOTES) ?>
<div style="display:flex; justify-content:space-between; gap:12px; align-items:center; flex-wrap: wrap;">
<div>
<strong><?= htmlspecialchars((string)$entry['title'], ENT_QUOTES) ?></strong>
<div class="muted small" style="margin-top:4px;">
<?= htmlspecialchars(trim(implode(' · ', array_filter([
(string)($entry['city'] ?? ''),
!empty($entry['starts_at']) ? (string)$entry['starts_at'] : null,
!empty($entry['host_name']) ? 'Von ' . (string)$entry['host_name'] : null,
!empty($entry['participation_status']) ? 'Status: ' . (string)$entry['participation_status'] : null,
]))), ENT_QUOTES) ?>
</div>
</div>
</div>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</details>
<details style="margin-top:12px;">
<summary style="cursor:pointer;">Vergangene Teilnahmen anzeigen</summary>
<?php if (!$eventsJoinedPast): ?>
<p class="muted small">Keine vergangenen Event-Teilnahmen.</p>
<?php else: ?>
<ul class="dash-list" style="margin-top:10px;">
<?php foreach ($eventsJoinedPast as $entry): ?>
<li>
<div>
<strong><?= htmlspecialchars((string)$entry['title'], ENT_QUOTES) ?></strong>
<div class="muted small" style="margin-top:4px;">
<?= htmlspecialchars(trim(implode(' · ', array_filter([
(string)($entry['city'] ?? ''),
!empty($entry['starts_at']) ? (string)$entry['starts_at'] : null,
!empty($entry['host_name']) ? 'Von ' . (string)$entry['host_name'] : null,
!empty($entry['participation_status']) ? 'Status: ' . (string)$entry['participation_status'] : null,
]))), ENT_QUOTES) ?>
</div>
</div>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</details>
</div>
<div class="account-inline-tab-panel" data-event-tab-panel="sync" hidden>
<div class="card">
<strong>Kalender-Synchronisierung</strong>
<p class="muted small" style="margin:8px 0 0;">Exportiert werden immer alle eigenen Events und alle Events, an denen du teilnimmst.</p>
<?php if (!empty($calendarExportUrl)): ?>
<div class="flex gap-12" style="margin-top:14px; flex-wrap:wrap;">
<a class="btn" href="<?= htmlspecialchars((string)$calendarExportUrl, ENT_QUOTES) ?>">ICS herunterladen</a>
</div>
<?php endif; ?>
</div>
<div class="card" style="margin-top:18px;">
<strong>Kalender abonnieren</strong>
<p class="muted small" style="margin:8px 0 0;">Der persönliche Kalender-Feed ist für Apps gedacht, die Internet-Kalender abonnieren können. So bleiben Änderungen, Absagen oder Löschungen bei der nächsten Synchronisierung aktuell.</p>
<div class="stack gap-6" style="margin-top:14px;">
<label class="label" for="calendarFeedUrl">Persönlicher Kalender-Feed</label>
<input id="calendarFeedUrl" class="input" type="text" readonly value="<?= htmlspecialchars((string)$calendarFeedUrl, ENT_QUOTES) ?>">
</div>
<div class="stack gap-6" style="margin-top:14px;">
<strong style="font-size:15px;">So nutzt du den Feed</strong>
<p class="muted small" style="margin:0;">1. Feed-Link kopieren.</p>
<p class="muted small" style="margin:0;">2. Im Kalender-Anbieter `Kalender abonnieren` oder `Per URL hinzufügen` wählen.</p>
<p class="muted small" style="margin:0;">3. Den Link einfügen und speichern.</p>
</div>
</div>
<div class="card" style="margin-top:18px;">
<strong>Wichtiger Unterschied</strong>
<p class="muted small" style="margin:8px 0 0;">„ICS herunterladen“ ist nur eine Momentaufnahme für den manuellen Import. Spätere Änderungen in Papa-Kind-Treff werden dadurch nicht automatisch in deinem privaten Kalender nachgezogen.</p>
<p class="muted small" style="margin:8px 0 0;">Für laufende Aktualisierungen solltest du deshalb den persönlichen Kalender-Feed abonnieren.</p>
</div>
</div>
</div>
</section>
<?php endif; ?>
@@ -1309,6 +1332,8 @@ if (!empty($canManageSystemSettings)) {
const profileAddressNeedsValidation = document.getElementById('profileAddressNeedsValidation');
const profileAddressResults = document.getElementById('profileAddressResults');
const profileAddressResultsModal = document.getElementById('modalProfileAddressResults');
const eventTabButtons = document.querySelectorAll('[data-event-tab-trigger]');
const eventTabPanels = document.querySelectorAll('[data-event-tab-panel]');
let map, marker;
const existingCategories = <?= json_encode($listingCategories ?? [], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
let lastAutoGeocodeQuery = '';
@@ -1437,6 +1462,25 @@ if (!empty($canManageSystemSettings)) {
categoryInput?.addEventListener('input', syncCategoryInputState);
syncCategoryInputState();
function setEventTab(nextTab) {
const activeTab = nextTab || 'my-events';
eventTabButtons.forEach((button) => {
const isActive = button.getAttribute('data-event-tab-trigger') === activeTab;
button.classList.toggle('is-active', isActive);
button.setAttribute('aria-selected', isActive ? 'true' : 'false');
});
eventTabPanels.forEach((panel) => {
panel.hidden = panel.getAttribute('data-event-tab-panel') !== activeTab;
});
}
eventTabButtons.forEach((button) => {
button.addEventListener('click', () => {
setEventTab(button.getAttribute('data-event-tab-trigger') || 'my-events');
});
});
setEventTab('my-events');
function ensureLeaflet(callback) {
if (!window.PKTConsent || !window.PKTConsent.has('external_services')) {
alert('Für Karten und Adresssuche bitte zuerst die externen Dienste in den Cookie-Einstellungen erlauben.');