adsad
All checks were successful
Deploy / deploy (push) Successful in 57s

This commit is contained in:
2026-08-11 22:23:21 +02:00
parent 016cf17971
commit d3ebe6fa68
7 changed files with 182 additions and 28 deletions

View File

@@ -39,6 +39,7 @@ Papa-Kind-Treff ist eine PHP-basierte Plattform für Väter. Kernbereiche sind l
- Im Formular für eigene Events sind `Gültig bis` und `Wiederholung` ausgeblendet. - Im Formular für eigene Events sind `Gültig bis` und `Wiederholung` ausgeblendet.
- Bei eigener Event-Location aus der Datenbank werden Adresse und Kategorie nur als Info angezeigt und nicht direkt bearbeitet. - Bei eigener Event-Location aus der Datenbank werden Adresse und Kategorie nur als Info angezeigt und nicht direkt bearbeitet.
- Aus dem Formular für eigene Events kann direkt in das Anlegen eines neuen Orts oder einer neuen Veranstaltung gewechselt werden. - Aus dem Formular für eigene Events kann direkt in das Anlegen eines neuen Orts oder einer neuen Veranstaltung gewechselt werden.
- Nach diesem Zwischenschritt kehrt der Nutzer wieder in sein Event zurück; die bisherige Event-Eingabe bleibt erhalten und der neu angelegte Ort bzw. die neue Veranstaltung wird direkt ausgewählt.
- Bei `Veranstaltung aus Datenbank` muss das Event-Datum innerhalb des hinterlegten Gültigkeitszeitraums dieser Veranstaltungs-Quelle liegen. - Bei `Veranstaltung aus Datenbank` muss das Event-Datum innerhalb des hinterlegten Gültigkeitszeitraums dieser Veranstaltungs-Quelle liegen.
- Kategorien werden im Mitgliederbereich per Sucheingabe mit bestehenden Vorschlägen und automatischer Neuanlage gepflegt. - Kategorien werden im Mitgliederbereich per Sucheingabe mit bestehenden Vorschlägen und automatischer Neuanlage gepflegt.
- Eigene Events unterstützen optionalen Beschreibungstext, Ja/Nein-Angabe `Mit Kindern`, optionale Platzzahl, optionales Bild sowie eine Location aus Datenbank oder benutzerdefinierte Adresse. - Eigene Events unterstützen optionalen Beschreibungstext, Ja/Nein-Angabe `Mit Kindern`, optionale Platzzahl, optionales Bild sowie eine Location aus Datenbank oder benutzerdefinierte Adresse.

View File

@@ -25,6 +25,7 @@ Papa-Kind-Treff ist eine PHP-basierte Plattform für Väter mit Fokus auf lokale
- Mitgliederbereich `Events` ist wieder auf echte eigene Events und Event-Teilnahmen fokussiert - Mitgliederbereich `Events` ist wieder auf echte eigene Events und Event-Teilnahmen fokussiert
- Eigene Events haben jetzt optionalen Beschreibungstext, klare Kinderangabe, optionale Platzzahl, optionales Bild sowie eine Location aus Datenbank oder benutzerdefinierte Adresse - Eigene Events haben jetzt optionalen Beschreibungstext, klare Kinderangabe, optionale Platzzahl, optionales Bild sowie eine Location aus Datenbank oder benutzerdefinierte Adresse
- Bei eigener Event-Location aus der Datenbank werden Adresse und Kategorie nur informativ übernommen und nicht direkt im Event geändert - Bei eigener Event-Location aus der Datenbank werden Adresse und Kategorie nur informativ übernommen und nicht direkt im Event geändert
- Der Zwischenschritt vom eigenen Event zum Anlegen eines neuen Orts oder einer neuen Veranstaltung springt danach wieder in das Event zurück und wählt den neuen Eintrag direkt aus
- Orte und sonstige Veranstaltungen wurden vorerst in einen separaten Bereich `Orte & Veranstaltungen` verschoben - Orte und sonstige Veranstaltungen wurden vorerst in einen separaten Bereich `Orte & Veranstaltungen` verschoben
- Neue Orte und Veranstaltungen müssen vor Veröffentlichung erst durch einen Admin freigegeben werden; Änderungs- und Löschwünsche laufen ebenfalls nur als begründete Anfrage - Neue Orte und Veranstaltungen müssen vor Veröffentlichung erst durch einen Admin freigegeben werden; Änderungs- und Löschwünsche laufen ebenfalls nur als begründete Anfrage
- Die Eingabemasken sind jetzt progressiv aufgebaut: erst Pflichtangaben, optionale Daten in einklappbaren Bereichen - Die Eingabemasken sind jetzt progressiv aufgebaut: erst Pflichtangaben, optionale Daten in einklappbaren Bereichen

View File

@@ -39,6 +39,7 @@ Papa-Kind-Treff is a PHP-based platform for fathers. Core areas are local events
- Inside the own-event form, `valid until` and `recurrence` are hidden. - Inside the own-event form, `valid until` and `recurrence` are hidden.
- When an own event uses a database-backed location, address and category are shown as informational values and are not edited directly. - When an own event uses a database-backed location, address and category are shown as informational values and are not edited directly.
- The own-event form now offers direct links into creating a new place or a new event-style listing. - The own-event form now offers direct links into creating a new place or a new event-style listing.
- After that intermediate step, the user returns to the own-event form with the previous event input restored and the newly created place or event-style listing already selected.
- When `event from database` is selected, the event date must fall inside that source event's valid date window. - When `event from database` is selected, the event date must fall inside that source event's valid date window.
- Categories in the member area are handled via a search input with existing suggestions and automatic creation when needed. - Categories in the member area are handled via a search input with existing suggestions and automatic creation when needed.
- Own events support an optional description, a clear `with children` yes/no field, optional capacity, an optional image, and either a database-backed location or a custom address. - Own events support an optional description, a clear `with children` yes/no field, optional capacity, an optional image, and either a database-backed location or a custom address.

View File

@@ -25,6 +25,7 @@ Papa-Kind-Treff is a PHP-based platform for fathers focused on local events, app
- member area `Events` is now limited again to real own events and event participations - member area `Events` is now limited again to real own events and event participations
- own events now support an optional description, explicit child suitability, optional capacity, an optional image, and either a database-backed location or a custom address - own events now support an optional description, explicit child suitability, optional capacity, an optional image, and either a database-backed location or a custom address
- when an own event uses a database-backed location, address and category are shown as informational values and are not edited directly inside the event - when an own event uses a database-backed location, address and category are shown as informational values and are not edited directly inside the event
- when a user creates a new place or event-style listing from inside an own event, the flow returns to that event afterwards and preselects the newly created entry
- places and other event-style entries were moved for now into a separate member-area section `Places & Events` - places and other event-style entries were moved for now into a separate member-area section `Places & Events`
- new places and event-style entries must be approved by an admin before publication; change and deletion wishes also run as reasoned requests - new places and event-style entries must be approved by an admin before publication; change and deletion wishes also run as reasoned requests
- the member-area entry forms now follow a progressive approach: required fields first, optional data inside collapsible sections - the member-area entry forms now follow a progressive approach: required fields first, optional data inside collapsible sections

View File

@@ -45,6 +45,7 @@ Papa-Kind-Treff ist eine PHP-basierte Plattform für Väter mit Fokus auf:
- Eigene Events unterstützen aktuell optionalen Beschreibungstext, klare Kinderangabe, optionale Platzzahl, Bild-Upload sowie eine Location aus Datenbank oder benutzerdefinierte Adresse - Eigene Events unterstützen aktuell optionalen Beschreibungstext, klare Kinderangabe, optionale Platzzahl, Bild-Upload sowie eine Location aus Datenbank oder benutzerdefinierte Adresse
- Bei eigener Event-Location aus der Datenbank werden Adresse und Kategorie nur informativ aus dem gewählten Ort bzw. der gewählten Veranstaltung übernommen und nicht manuell geändert - Bei eigener Event-Location aus der Datenbank werden Adresse und Kategorie nur informativ aus dem gewählten Ort bzw. der gewählten Veranstaltung übernommen und nicht manuell geändert
- Aus dem Formular für eigene Events kann direkt in das Anlegen eines neuen Orts oder einer neuen Veranstaltung verzweigt werden - Aus dem Formular für eigene Events kann direkt in das Anlegen eines neuen Orts oder einer neuen Veranstaltung verzweigt werden
- Wenn aus einem eigenen Event heraus ein neuer Ort oder eine neue Veranstaltung angelegt wird, kehrt der Ablauf danach wieder in das Event zurück und wählt den neuen Eintrag direkt vor
- Die Eingabe im Mitgliederbereich ist jetzt bewusst vereinfacht: zuerst nur Pflichtangaben, optionale Angaben in einklappbaren Bereichen - Die Eingabe im Mitgliederbereich ist jetzt bewusst vereinfacht: zuerst nur Pflichtangaben, optionale Angaben in einklappbaren Bereichen
- Kategorien werden jetzt über eine Sucheingabe mit bestehenden Vorschlägen gepflegt; neue Kategorien werden automatisch angelegt und sind für Seiten-Admins im Systembereich zusammenführbar - Kategorien werden jetzt über eine Sucheingabe mit bestehenden Vorschlägen gepflegt; neue Kategorien werden automatisch angelegt und sind für Seiten-Admins im Systembereich zusammenführbar
- Im Mitgliederbereich `Events` gibt es jetzt zusätzlich einen ICS-Download und einen persönlichen abonnierbaren Kalender-Feed für alle eigenen Events und Event-Teilnahmen - Im Mitgliederbereich `Events` gibt es jetzt zusätzlich einen ICS-Download und einen persönlichen abonnierbaren Kalender-Feed für alle eigenen Events und Event-Teilnahmen

View File

@@ -9,9 +9,13 @@ $dashboardUserId = (int)($_SESSION['user_id'] ?? 0);
$requestedCreateListingKind = isset($_GET['create_listing']) && in_array((string)$_GET['create_listing'], ['place', 'editorial_event'], true) $requestedCreateListingKind = isset($_GET['create_listing']) && in_array((string)$_GET['create_listing'], ['place', 'editorial_event'], true)
? (string)$_GET['create_listing'] ? (string)$_GET['create_listing']
: null; : null;
$restoreEventDraftActive = !empty($restoreEventDraftActive);
$editingEvent = isset($editEvent) && $editEvent !== null; $editingEvent = isset($editEvent) && $editEvent !== null;
$editingListing = isset($editListing) && $editListing !== null; $editingListing = isset($editListing) && $editListing !== null;
$editing = $editingEvent || $editingListing; $editing = $editingEvent || $editingListing;
$eventDraftValues = (!$editing && (($section ?? '') === 'events') && $restoreEventDraftActive && is_array($restoredEventDraft ?? null))
? $restoredEventDraft
: [];
$currentEntryKind = $editingListing $currentEntryKind = $editingListing
? (string)($editListing['listing_type'] ?? 'editorial_event') ? (string)($editListing['listing_type'] ?? 'editorial_event')
: ((($section ?? 'events') === 'places') ? ($requestedCreateListingKind ?? 'place') : 'event'); : ((($section ?? 'events') === 'places') ? ($requestedCreateListingKind ?? 'place') : 'event');
@@ -21,23 +25,27 @@ if ($editingEvent && !empty($editEvent['starts_at'])) {
$startVal = date('Y-m-d', strtotime((string)$editEvent['starts_at'])); $startVal = date('Y-m-d', strtotime((string)$editEvent['starts_at']));
} elseif ($editingListing && !empty($editListing['starts_at'])) { } elseif ($editingListing && !empty($editListing['starts_at'])) {
$startVal = date('Y-m-d', strtotime((string)$editListing['starts_at'])); $startVal = date('Y-m-d', strtotime((string)$editListing['starts_at']));
} elseif (!empty($eventDraftValues['starts_at'])) {
$startVal = (string)$eventDraftValues['starts_at'];
} }
$withChildrenValue = $editingEvent ? (((int)($editEvent['allow_kids'] ?? 1)) === 1 ? 'yes' : 'no') : (($editingListing && $currentEntryKind === 'editorial_event' && !empty($editListing['supports_registration'])) ? 'yes' : 'yes'); $withChildrenValue = $editingEvent
? (((int)($editEvent['allow_kids'] ?? 1)) === 1 ? 'yes' : 'no')
: (!empty($eventDraftValues['with_children']) ? (string)$eventDraftValues['with_children'] : (($editingListing && $currentEntryKind === 'editorial_event' && !empty($editListing['supports_registration'])) ? 'yes' : 'yes'));
$eventTitle = $editingEvent ? 'Event bearbeiten' : ($editingListing ? ($currentEntryKind === 'place' ? 'Änderung für Ort anfragen' : 'Änderung für Veranstaltung anfragen') : ((($section ?? 'events') === 'places') ? 'Ort oder Veranstaltung anlegen' : 'Neues Event anlegen')); $eventTitle = $editingEvent ? 'Event bearbeiten' : ($editingListing ? ($currentEntryKind === 'place' ? 'Änderung für Ort anfragen' : 'Änderung für Veranstaltung anfragen') : ((($section ?? 'events') === 'places') ? 'Ort oder Veranstaltung anlegen' : 'Neues Event anlegen'));
$submitLabel = $editingEvent ? 'Event speichern' : ($editingListing ? 'Änderungsanfrage senden' : ((($section ?? 'events') === 'places') ? 'Eintrag zur Freigabe einreichen' : 'Event anlegen')); $submitLabel = $editingEvent ? 'Event speichern' : ($editingListing ? 'Änderungsanfrage senden' : ((($section ?? 'events') === 'places') ? 'Eintrag zur Freigabe einreichen' : 'Event anlegen'));
$titleValue = $editingEvent ? (string)($editEvent['title'] ?? '') : (string)($editListing['title'] ?? ''); $titleValue = $editingEvent ? (string)($editEvent['title'] ?? '') : (!empty($eventDraftValues['title']) ? (string)$eventDraftValues['title'] : (string)($editListing['title'] ?? ''));
$teaserValue = $editingEvent ? (string)($editEvent['teaser_public'] ?? '') : (string)($editListing['teaser_public'] ?? ''); $teaserValue = $editingEvent ? (string)($editEvent['teaser_public'] ?? '') : (string)($editListing['teaser_public'] ?? '');
$descriptionValue = $editingEvent ? (string)($editEvent['description'] ?? '') : (string)($editListing['description'] ?? ''); $descriptionValue = $editingEvent ? (string)($editEvent['description'] ?? '') : (!empty($eventDraftValues['description']) ? (string)$eventDraftValues['description'] : (string)($editListing['description'] ?? ''));
$streetValue = $editingEvent ? (string)($editEvent['street'] ?? '') : (string)($editListing['street'] ?? ''); $streetValue = $editingEvent ? (string)($editEvent['street'] ?? '') : (isset($eventDraftValues['street']) ? (string)$eventDraftValues['street'] : (string)($editListing['street'] ?? ''));
$zipValue = $editingEvent ? (string)($editEvent['zip'] ?? '') : (string)($editListing['zip'] ?? ''); $zipValue = $editingEvent ? (string)($editEvent['zip'] ?? '') : (isset($eventDraftValues['zip']) ? (string)$eventDraftValues['zip'] : (string)($editListing['zip'] ?? ''));
$cityValue = $editingEvent ? (string)($editEvent['city'] ?? '') : (string)($editListing['city'] ?? ''); $cityValue = $editingEvent ? (string)($editEvent['city'] ?? '') : (isset($eventDraftValues['city']) ? (string)$eventDraftValues['city'] : (string)($editListing['city'] ?? ''));
$regionValue = $editingEvent ? (string)($editEvent['region'] ?? '') : (string)($editListing['region'] ?? ''); $regionValue = $editingEvent ? (string)($editEvent['region'] ?? '') : (isset($eventDraftValues['region']) ? (string)$eventDraftValues['region'] : (string)($editListing['region'] ?? ''));
$latValue = $editingEvent ? (string)($editEvent['lat'] ?? '') : (string)($editListing['lat'] ?? ''); $latValue = $editingEvent ? (string)($editEvent['lat'] ?? '') : (isset($eventDraftValues['lat']) ? (string)$eventDraftValues['lat'] : (string)($editListing['lat'] ?? ''));
$lngValue = $editingEvent ? (string)($editEvent['lng'] ?? '') : (string)($editListing['lng'] ?? ''); $lngValue = $editingEvent ? (string)($editEvent['lng'] ?? '') : (isset($eventDraftValues['lng']) ? (string)$eventDraftValues['lng'] : (string)($editListing['lng'] ?? ''));
$visibilityValue = $editingEvent ? (string)($editEvent['visibility'] ?? 'public') : (string)($editListing['visibility'] ?? 'public'); $visibilityValue = $editingEvent ? (string)($editEvent['visibility'] ?? 'public') : (!empty($eventDraftValues['visibility']) ? (string)$eventDraftValues['visibility'] : (string)($editListing['visibility'] ?? 'public'));
$categorySlugValue = $editingEvent ? (string)($editEvent['category_slug'] ?? '') : (string)($editListing['category_slug'] ?? ''); $categorySlugValue = $editingEvent ? (string)($editEvent['category_slug'] ?? '') : (!empty($eventDraftValues['category_slug']) ? (string)$eventDraftValues['category_slug'] : (string)($editListing['category_slug'] ?? ''));
$imagePathValue = $editingEvent ? (string)($editEvent['image_path'] ?? '') : (string)($editListing['image_path'] ?? ''); $imagePathValue = $editingEvent ? (string)($editEvent['image_path'] ?? '') : (string)($editListing['image_path'] ?? '');
$capacityValue = $editingEvent ? (string)($editEvent['max_participants'] ?? '') : (string)($editListing['capacity_total'] ?? ''); $capacityValue = $editingEvent ? (string)($editEvent['max_participants'] ?? '') : (isset($eventDraftValues['max_participants']) ? (string)$eventDraftValues['max_participants'] : (string)($editListing['capacity_total'] ?? ''));
$phoneValue = $editingListing ? (string)($editListing['phone'] ?? '') : ''; $phoneValue = $editingListing ? (string)($editListing['phone'] ?? '') : '';
$websiteValue = $editingListing ? (string)($editListing['website_url'] ?? '') : ''; $websiteValue = $editingListing ? (string)($editListing['website_url'] ?? '') : '';
$specialConditionsValue = $editingListing ? (string)($editListing['special_conditions_note'] ?? '') : ''; $specialConditionsValue = $editingListing ? (string)($editListing['special_conditions_note'] ?? '') : '';
@@ -58,15 +66,11 @@ $categoryTitleMap = [];
foreach (($listingCategories ?? []) as $categoryRow) { foreach (($listingCategories ?? []) as $categoryRow) {
$categoryTitleMap[(string)($categoryRow['slug'] ?? '')] = (string)($categoryRow['title'] ?? ''); $categoryTitleMap[(string)($categoryRow['slug'] ?? '')] = (string)($categoryRow['title'] ?? '');
} }
$categoryInputValue = $categoryTitleMap[$categorySlugValue] ?? $categorySlugValue; $categoryInputValue = !empty($eventDraftValues['category_input'])
$eventLocationModeValue = $editingEvent ? (string)($editEvent['location_source_type'] ?? 'custom') : 'custom'; ? (string)$eventDraftValues['category_input']
$eventLocationSourceIdValue = $editingEvent && !empty($editEvent['location_source_listing_id']) ? (int)$editEvent['location_source_listing_id'] : 0; : ($categoryTitleMap[$categorySlugValue] ?? $categorySlugValue);
$usesDatabaseEventLocation = $currentEntryKind === 'event' && $eventLocationModeValue !== 'custom'; $eventLocationModeValue = $editingEvent ? (string)($editEvent['location_source_type'] ?? 'custom') : (!empty($eventDraftValues['event_location_mode']) ? (string)$eventDraftValues['event_location_mode'] : 'custom');
$eventAddressInfoValue = trim(implode(', ', array_filter([ $eventLocationSourceIdValue = $editingEvent && !empty($editEvent['location_source_listing_id']) ? (int)$editEvent['location_source_listing_id'] : (!empty($eventDraftValues['event_location_source_id']) ? (int)$eventDraftValues['event_location_source_id'] : 0);
$streetValue,
trim($zipValue . ' ' . $cityValue),
$regionValue,
])));
$eventLocationPlaceOptions = []; $eventLocationPlaceOptions = [];
$eventLocationEditorialOptions = []; $eventLocationEditorialOptions = [];
foreach (($eventLocationOptions ?? []) as $locationOption) { foreach (($eventLocationOptions ?? []) as $locationOption) {
@@ -83,13 +87,24 @@ foreach (($eventLocationOptions ?? []) as $locationOption) {
(string)($locationOption['street'] ?? ''), (string)($locationOption['street'] ?? ''),
trim((string)($locationOption['zip'] ?? '') . ' ' . (string)($locationOption['city'] ?? '')), trim((string)($locationOption['zip'] ?? '') . ' ' . (string)($locationOption['city'] ?? '')),
]))); ])));
$optionDateLabel = !empty($locationOption['starts_at']) ? date('d.m.Y', strtotime((string)$locationOption['starts_at'])) : '';
if ($eventLocationModeValue !== 'custom' && !empty($locationOption['category_title'])) { if ($eventLocationModeValue !== 'custom' && !empty($locationOption['category_title'])) {
$categoryInputValue = (string)$locationOption['category_title']; $categoryInputValue = (string)$locationOption['category_title'];
$categorySlugValue = (string)($locationOption['category_slug'] ?? $categorySlugValue); $categorySlugValue = (string)($locationOption['category_slug'] ?? $categorySlugValue);
$streetValue = (string)($locationOption['street'] ?? $streetValue);
$zipValue = (string)($locationOption['zip'] ?? $zipValue);
$cityValue = (string)($locationOption['city'] ?? $cityValue);
$regionValue = (string)($locationOption['region'] ?? $regionValue);
$latValue = (string)($locationOption['lat'] ?? $latValue);
$lngValue = (string)($locationOption['lng'] ?? $lngValue);
} }
break; break;
} }
$usesDatabaseEventLocation = $currentEntryKind === 'event' && $eventLocationModeValue !== 'custom';
$eventAddressInfoValue = trim(implode(', ', array_filter([
$streetValue,
trim($zipValue . ' ' . $cityValue),
$regionValue,
])));
$priceRows = []; $priceRows = [];
if ($editingListing && !empty($editListing['prices']) && is_array($editListing['prices'])) { if ($editingListing && !empty($editListing['prices']) && is_array($editListing['prices'])) {
foreach ($editListing['prices'] as $priceRow) { foreach ($editListing['prices'] as $priceRow) {
@@ -1127,7 +1142,7 @@ if (!empty($canManageSystemSettings)) {
<h3><?= htmlspecialchars($eventTitle, ENT_QUOTES) ?></h3> <h3><?= htmlspecialchars($eventTitle, ENT_QUOTES) ?></h3>
<button class="btn ghost" type="button" data-modal-close>✕</button> <button class="btn ghost" type="button" data-modal-close>✕</button>
</div> </div>
<form class="stack gap-12" style="margin-top: 10px;" method="post" action="/dashboard?section=<?= htmlspecialchars($modalSectionTarget, ENT_QUOTES) ?>#<?= htmlspecialchars($modalSectionTarget, ENT_QUOTES) ?>" enctype="multipart/form-data"> <form id="eventEditorForm" class="stack gap-12" style="margin-top: 10px;" method="post" action="/dashboard?section=<?= htmlspecialchars($modalSectionTarget, ENT_QUOTES) ?>#<?= htmlspecialchars($modalSectionTarget, ENT_QUOTES) ?>" enctype="multipart/form-data">
<input type="hidden" name="action" value="<?= htmlspecialchars($actionEvent, ENT_QUOTES) ?>"> <input type="hidden" name="action" value="<?= htmlspecialchars($actionEvent, ENT_QUOTES) ?>">
<input type="hidden" name="entry_kind" id="entryKind" value="<?= htmlspecialchars($currentEntryKind, ENT_QUOTES) ?>"> <input type="hidden" name="entry_kind" id="entryKind" value="<?= htmlspecialchars($currentEntryKind, ENT_QUOTES) ?>">
<?php if ($editingEvent && $editEvent): ?> <?php if ($editingEvent && $editEvent): ?>
@@ -1204,8 +1219,8 @@ if (!empty($canManageSystemSettings)) {
</div> </div>
<?php if (($section ?? 'events') === 'events' && !$editingListing): ?> <?php if (($section ?? 'events') === 'events' && !$editingListing): ?>
<div class="flex gap-8" style="flex-wrap:wrap;" id="eventCreateListingActionsWrap"> <div class="flex gap-8" style="flex-wrap:wrap;" id="eventCreateListingActionsWrap">
<a class="btn ghost" href="/dashboard?section=places&create_listing=place#places">Neuen Ort anlegen</a> <button class="btn ghost" type="button" data-event-create-listing="place">Neuen Ort anlegen</button>
<a class="btn ghost" href="/dashboard?section=places&create_listing=editorial_event#places">Neue Veranstaltung anlegen</a> <button class="btn ghost" type="button" data-event-create-listing="editorial_event">Neue Veranstaltung anlegen</button>
</div> </div>
<?php endif; ?> <?php endif; ?>
<div class="stack gap-6" id="categoryInputWrap"<?= $usesDatabaseEventLocation ? ' hidden' : '' ?>> <div class="stack gap-6" id="categoryInputWrap"<?= $usesDatabaseEventLocation ? ' hidden' : '' ?>>
@@ -1405,6 +1420,11 @@ if (!empty($canManageSystemSettings)) {
</div> </div>
</main> </main>
<form id="eventDraftRedirectForm" method="post" action="/dashboard?section=events#events" hidden>
<input type="hidden" name="action" value="event_draft_store">
<input type="hidden" name="target_kind" id="eventDraftTargetKind" value="">
</form>
<script> <script>
document.querySelectorAll('[data-modal-open]').forEach(btn => { document.querySelectorAll('[data-modal-open]').forEach(btn => {
btn.addEventListener('click', () => { btn.addEventListener('click', () => {
@@ -1451,6 +1471,10 @@ if (!empty($canManageSystemSettings)) {
const eventCapacityWrap = document.getElementById('eventCapacityWrap'); const eventCapacityWrap = document.getElementById('eventCapacityWrap');
const eventLocationModeWrap = document.getElementById('eventLocationModeWrap'); const eventLocationModeWrap = document.getElementById('eventLocationModeWrap');
const eventLocationModeSelect = document.getElementById('eventLocationMode'); const eventLocationModeSelect = document.getElementById('eventLocationMode');
const eventEditorForm = document.getElementById('eventEditorForm');
const eventDraftRedirectForm = document.getElementById('eventDraftRedirectForm');
const eventDraftTargetKind = document.getElementById('eventDraftTargetKind');
const eventCreateListingButtons = document.querySelectorAll('[data-event-create-listing]');
const eventLocationSourceWrap = document.getElementById('eventLocationSourceWrap'); const eventLocationSourceWrap = document.getElementById('eventLocationSourceWrap');
const eventLocationSourceSelect = document.getElementById('eventLocationSourceSelect'); const eventLocationSourceSelect = document.getElementById('eventLocationSourceSelect');
const eventLocationSourceIdInput = document.getElementById('eventLocationSourceId'); const eventLocationSourceIdInput = document.getElementById('eventLocationSourceId');
@@ -1803,6 +1827,49 @@ if (!empty($canManageSystemSettings)) {
}); });
} }
function submitEventDraftRedirect(targetKind) {
if (!eventEditorForm || !eventDraftRedirectForm || !eventDraftTargetKind) return;
const allowedFieldNames = new Set([
'title',
'description',
'starts_at',
'with_children',
'max_participants',
'visibility',
'event_location_mode',
'event_location_source_id',
'street',
'zip',
'city',
'region',
'lat',
'lng',
'category_input',
'category_slug',
]);
eventDraftRedirectForm.querySelectorAll('[data-event-draft-copy]').forEach((node) => node.remove());
const formData = new FormData(eventEditorForm);
formData.forEach((value, key) => {
if (!allowedFieldNames.has(key) || typeof value !== 'string') {
return;
}
const input = document.createElement('input');
input.type = 'hidden';
input.name = key;
input.value = value;
input.setAttribute('data-event-draft-copy', 'true');
eventDraftRedirectForm.appendChild(input);
});
eventDraftTargetKind.value = targetKind;
eventDraftRedirectForm.submit();
}
eventCreateListingButtons.forEach((button) => {
button.addEventListener('click', () => {
submitEventDraftRedirect(button.getAttribute('data-event-create-listing') || 'place');
});
});
eventTabButtons.forEach((button) => { eventTabButtons.forEach((button) => {
button.addEventListener('click', () => { button.addEventListener('click', () => {
setEventTab(button.getAttribute('data-event-tab-trigger') || 'my-events'); setEventTab(button.getAttribute('data-event-tab-trigger') || 'my-events');
@@ -2223,7 +2290,7 @@ if (!empty($canManageSystemSettings)) {
setNeedsValidation(false); setNeedsValidation(false);
})(); })();
<?php if ($editing || $requestedCreateListingKind !== null): ?> <?php if ($editing || $requestedCreateListingKind !== null || $restoreEventDraftActive): ?>
(function(){ (function(){
const modal = document.getElementById('modalEvent'); const modal = document.getElementById('modalEvent');
if (modal) { if (modal) {

View File

@@ -8,6 +8,7 @@ use App\Avatar\Lorelei;
final class AccountPages final class AccountPages
{ {
private const EVENT_DRAFT_SESSION_KEY = 'dashboard_event_draft';
private const ENCRYPTED_PROFILE_FIELDS = [ private const ENCRYPTED_PROFILE_FIELDS = [
'first_name', 'first_name',
'last_name', 'last_name',
@@ -199,7 +200,14 @@ final class AccountPages
if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? ''; $action = $_POST['action'] ?? '';
try { try {
if ($action === 'profile') { if ($action === 'event_draft_store') {
$targetKind = (string)($_POST['target_kind'] ?? '');
if (!in_array($targetKind, ['place', 'editorial_event'], true)) {
throw new \RuntimeException('Ungültiger Zieltyp für den Zwischenschritt.');
}
self::storeEventDraftInSession($_POST);
redirect('/dashboard?section=places&create_listing=' . rawurlencode($targetKind) . '#places');
} elseif ($action === 'profile') {
$crypto = self::requireCrypto($crypto, 'Profil'); $crypto = self::requireCrypto($crypto, 'Profil');
$email = trim((string)($_POST['email'] ?? '')); $email = trim((string)($_POST['email'] ?? ''));
$languages = $_POST['languages'] ?? ''; $languages = $_POST['languages'] ?? '';
@@ -441,8 +449,13 @@ final class AccountPages
); );
$info = $entryKind === 'place' ? 'Änderungsanfrage für den Ort eingereicht.' : 'Änderungsanfrage für die Veranstaltung eingereicht.'; $info = $entryKind === 'place' ? 'Änderungsanfrage für den Ort eingereicht.' : 'Änderungsanfrage für die Veranstaltung eingereicht.';
} else { } else {
$listingCatalog->submitCreateSuggestion($userId, $payload); $createdListingId = $listingCatalog->submitCreateSuggestion($userId, $payload);
$info = $entryKind === 'place' ? 'Ort zur Freigabe eingereicht.' : 'Veranstaltung zur Freigabe eingereicht.'; $info = $entryKind === 'place' ? 'Ort zur Freigabe eingereicht.' : 'Veranstaltung zur Freigabe eingereicht.';
if (self::hasStoredEventDraft()) {
self::attachListingToStoredEventDraft($entryKind, $createdListingId);
$app->flash()->set('info', $info);
redirect('/dashboard?section=events&restore_event_draft=1#events');
}
} }
} else { } else {
$existingEvent = null; $existingEvent = null;
@@ -547,6 +560,7 @@ final class AccountPages
'status' => 'published', 'status' => 'published',
]); ]);
$info = 'Event gespeichert.'; $info = 'Event gespeichert.';
self::clearStoredEventDraft();
try { try {
$cfgPath = dirname(__DIR__, 2) . '/config/community.php'; $cfgPath = dirname(__DIR__, 2) . '/config/community.php';
$communityCfg = file_exists($cfgPath) ? require $cfgPath : []; $communityCfg = file_exists($cfgPath) ? require $cfgPath : [];
@@ -580,6 +594,7 @@ final class AccountPages
'vis' => $_POST['visibility'] ?? 'public', 'vis' => $_POST['visibility'] ?? 'public',
]); ]);
$info = 'Event aktualisiert.'; $info = 'Event aktualisiert.';
self::clearStoredEventDraft();
} }
} }
} elseif ($action === 'event_delete') { } elseif ($action === 'event_delete') {
@@ -743,6 +758,8 @@ final class AccountPages
$userSubmittedListings = []; $userSubmittedListings = [];
$listingRequestMap = []; $listingRequestMap = [];
$editListing = null; $editListing = null;
$restoreEventDraftActive = $section === 'events' && ((string)($_GET['restore_event_draft'] ?? '')) === '1' && self::hasStoredEventDraft();
$restoredEventDraft = $restoreEventDraftActive ? self::getStoredEventDraft() : null;
$listingCategories = $listingCatalog ? $listingCatalog->listCategories(['general', 'place', 'food', 'event', 'family']) : []; $listingCategories = $listingCatalog ? $listingCatalog->listCategories(['general', 'place', 'food', 'event', 'family']) : [];
$categoryReviewItems = $listingCatalog && $canManageSystemSettings ? $listingCatalog->listCategoryReviewItems() : []; $categoryReviewItems = $listingCatalog && $canManageSystemSettings ? $listingCatalog->listCategoryReviewItems() : [];
$placeSuggestions = $listingCatalog ? $listingCatalog->listPlaceSuggestions(60) : []; $placeSuggestions = $listingCatalog ? $listingCatalog->listPlaceSuggestions(60) : [];
@@ -863,6 +880,8 @@ final class AccountPages
'directoryListings', 'directoryListings',
'userSubmittedListings', 'userSubmittedListings',
'listingRequestMap', 'listingRequestMap',
'restoreEventDraftActive',
'restoredEventDraft',
'editListing', 'editListing',
'listingCategories', 'listingCategories',
'categoryReviewItems', 'categoryReviewItems',
@@ -1163,4 +1182,67 @@ final class AccountPages
$resp = @file_get_contents($url, false, $ctx); $resp = @file_get_contents($url, false, $ctx);
return $resp === false ? null : $resp; return $resp === false ? null : $resp;
} }
private static function storeEventDraftInSession(array $source): void
{
$_SESSION[self::EVENT_DRAFT_SESSION_KEY] = self::normalizeEventDraft($source);
}
private static function getStoredEventDraft(): ?array
{
$draft = $_SESSION[self::EVENT_DRAFT_SESSION_KEY] ?? null;
return is_array($draft) ? $draft : null;
}
private static function hasStoredEventDraft(): bool
{
return is_array($_SESSION[self::EVENT_DRAFT_SESSION_KEY] ?? null);
}
private static function clearStoredEventDraft(): void
{
unset($_SESSION[self::EVENT_DRAFT_SESSION_KEY]);
}
private static function attachListingToStoredEventDraft(string $listingType, int $listingId): void
{
$draft = self::getStoredEventDraft();
if (!is_array($draft) || $listingId <= 0 || !in_array($listingType, ['place', 'editorial_event'], true)) {
return;
}
$draft['event_location_mode'] = $listingType;
$draft['event_location_source_id'] = (string)$listingId;
$draft['street'] = '';
$draft['zip'] = '';
$draft['city'] = '';
$draft['region'] = '';
$draft['lat'] = '';
$draft['lng'] = '';
$draft['category_input'] = '';
$draft['category_slug'] = '';
$_SESSION[self::EVENT_DRAFT_SESSION_KEY] = $draft;
}
private static function normalizeEventDraft(array $source): array
{
return [
'title' => trim((string)($source['title'] ?? '')),
'description' => trim((string)($source['description'] ?? '')),
'starts_at' => trim((string)($source['starts_at'] ?? '')),
'with_children' => ((string)($source['with_children'] ?? 'yes')) === 'no' ? 'no' : 'yes',
'max_participants' => trim((string)($source['max_participants'] ?? '')),
'visibility' => in_array((string)($source['visibility'] ?? 'public'), ['public', 'members'], true) ? (string)$source['visibility'] : 'public',
'event_location_mode' => in_array((string)($source['event_location_mode'] ?? 'custom'), ['custom', 'place', 'editorial_event'], true) ? (string)$source['event_location_mode'] : 'custom',
'event_location_source_id' => trim((string)($source['event_location_source_id'] ?? '')),
'street' => trim((string)($source['street'] ?? '')),
'zip' => trim((string)($source['zip'] ?? '')),
'city' => trim((string)($source['city'] ?? '')),
'region' => trim((string)($source['region'] ?? '')),
'lat' => trim((string)($source['lat'] ?? '')),
'lng' => trim((string)($source['lng'] ?? '')),
'category_input' => trim((string)($source['category_input'] ?? '')),
'category_slug' => trim((string)($source['category_slug'] ?? '')),
];
}
} }