From 016cf17971a9406582ad586218bc3c9b2231097f Mon Sep 17 00:00:00 2001 From: Lars Gebhardt-Kusche Date: Tue, 11 Aug 2026 21:58:47 +0200 Subject: [PATCH] dsad --- Internal/de/PROJECT_CONTEXT.md | 1 + Internal/en/PROJECT_CONTEXT.md | 1 + partials/landing/account/dashboard.php | 121 ++++++++++++++++--------- src/App/ListingCatalog.php | 11 +++ 4 files changed, 92 insertions(+), 42 deletions(-) diff --git a/Internal/de/PROJECT_CONTEXT.md b/Internal/de/PROJECT_CONTEXT.md index 99321d7..12cd89b 100644 --- a/Internal/de/PROJECT_CONTEXT.md +++ b/Internal/de/PROJECT_CONTEXT.md @@ -35,6 +35,7 @@ Papa-Kind-Treff ist eine PHP-basierte Plattform für Väter. Kernbereiche sind l - Eigene Events nutzen jetzt als Pflichtfelder nur noch Titel, Datum und `Mit Kindern`; die Beschreibung ist optional. - Für die Event-Location gibt es jetzt die Auswahl `Ort aus Datenbank`, `Veranstaltung aus Datenbank` oder `Benutzerdefiniert`. - Bei `Ort` oder `Veranstaltung` werden Adresse und Kategorie direkt aus dem gewählten Datenbank-Eintrag übernommen. +- Die Auswahl für Datenbank-Locations läuft im Event-Formular jetzt über ein echtes Auswahlfeld statt über Freitext; `Ort aus Datenbank` zeigt nur Orte, `Veranstaltung aus Datenbank` nur aktuell laufende Veranstaltungen im gültigen Zeitraum. - 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. - Aus dem Formular für eigene Events kann direkt in das Anlegen eines neuen Orts oder einer neuen Veranstaltung gewechselt werden. diff --git a/Internal/en/PROJECT_CONTEXT.md b/Internal/en/PROJECT_CONTEXT.md index c981fbc..400d6d4 100644 --- a/Internal/en/PROJECT_CONTEXT.md +++ b/Internal/en/PROJECT_CONTEXT.md @@ -35,6 +35,7 @@ Papa-Kind-Treff is a PHP-based platform for fathers. Core areas are local events - Own events now only require title, date, and the `with children` choice; the description is optional. - Event locations now support the modes `place from database`, `event from database`, or `custom`. - When a database-backed place or event is selected, the address and category are taken directly from that source entry. +- Database-backed event locations now use a real select field instead of free text; `place from database` only shows places, while `event from database` only shows currently active event-style entries inside their valid date window. - 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. - The own-event form now offers direct links into creating a new place or a new event-style listing. diff --git a/partials/landing/account/dashboard.php b/partials/landing/account/dashboard.php index aa29c5e..cb2c431 100755 --- a/partials/landing/account/dashboard.php +++ b/partials/landing/account/dashboard.php @@ -61,14 +61,20 @@ foreach (($listingCategories ?? []) as $categoryRow) { $categoryInputValue = $categoryTitleMap[$categorySlugValue] ?? $categorySlugValue; $eventLocationModeValue = $editingEvent ? (string)($editEvent['location_source_type'] ?? 'custom') : 'custom'; $eventLocationSourceIdValue = $editingEvent && !empty($editEvent['location_source_listing_id']) ? (int)$editEvent['location_source_listing_id'] : 0; -$eventLocationSourceInputValue = ''; $usesDatabaseEventLocation = $currentEntryKind === 'event' && $eventLocationModeValue !== 'custom'; $eventAddressInfoValue = trim(implode(', ', array_filter([ $streetValue, trim($zipValue . ' ' . $cityValue), $regionValue, ]))); +$eventLocationPlaceOptions = []; +$eventLocationEditorialOptions = []; foreach (($eventLocationOptions ?? []) as $locationOption) { + if ((string)($locationOption['listing_type'] ?? '') === 'place') { + $eventLocationPlaceOptions[] = $locationOption; + } elseif ((string)($locationOption['listing_type'] ?? '') === 'editorial_event') { + $eventLocationEditorialOptions[] = $locationOption; + } if ((int)($locationOption['id'] ?? 0) !== $eventLocationSourceIdValue) { continue; } @@ -78,12 +84,6 @@ foreach (($eventLocationOptions ?? []) as $locationOption) { trim((string)($locationOption['zip'] ?? '') . ' ' . (string)($locationOption['city'] ?? '')), ]))); $optionDateLabel = !empty($locationOption['starts_at']) ? date('d.m.Y', strtotime((string)$locationOption['starts_at'])) : ''; - $eventLocationSourceInputValue = trim(implode(' · ', array_filter([ - $optionTypeLabel, - (string)($locationOption['title'] ?? ''), - $optionDateLabel, - $optionAddressLabel, - ]))); if ($eventLocationModeValue !== 'custom' && !empty($locationOption['category_title'])) { $categoryInputValue = (string)$locationOption['category_title']; $categorySlugValue = (string)($locationOption['category_slug'] ?? $categorySlugValue); @@ -1144,25 +1144,6 @@ if (!empty($canManageSystemSettings)) { - - - - - -
@@ -1197,8 +1178,29 @@ if (!empty($canManageSystemSettings)) {
- - + +
@@ -1450,7 +1452,7 @@ if (!empty($canManageSystemSettings)) { const eventLocationModeWrap = document.getElementById('eventLocationModeWrap'); const eventLocationModeSelect = document.getElementById('eventLocationMode'); const eventLocationSourceWrap = document.getElementById('eventLocationSourceWrap'); - const eventLocationSourceInput = document.getElementById('eventLocationSourceInput'); + const eventLocationSourceSelect = document.getElementById('eventLocationSourceSelect'); const eventLocationSourceIdInput = document.getElementById('eventLocationSourceId'); const eventLocationSourceLabel = document.getElementById('eventLocationSourceLabel'); const categoryInputWrap = document.getElementById('categoryInputWrap'); @@ -1532,16 +1534,50 @@ if (!empty($canManageSystemSettings)) { }); } - function findEventLocationOptionByLabel(label) { - const normalized = (label || '').trim(); - if (!normalized) return null; - const option = document.querySelector(`#eventLocationOptionList option[value="${CSS.escape(normalized)}"]`); + const allEventLocationOptions = ; + + function buildEventLocationLabel(option) { + if (!option) return ''; + const addressLabel = [String(option.street || ''), `${String(option.zip || '')} ${String(option.city || '')}`.trim()] + .filter(Boolean) + .join(', '); + const dateLabel = option.starts_at ? new Date(String(option.starts_at)).toLocaleDateString('de-DE') : ''; + return [String(option.title || ''), dateLabel, addressLabel].filter(Boolean).join(' · '); + } + + function findEventLocationOptionById(id) { + const normalizedId = Number(id || 0); + if (!normalizedId) return null; + const option = allEventLocationOptions.find((entry) => Number(entry.id || 0) === normalizedId); if (!option) return null; - try { - return JSON.parse(option.getAttribute('data-location') || 'null'); - } catch (error) { - return null; - } + return option; + } + + function rebuildEventLocationOptions() { + if (!eventLocationSourceSelect) return; + const mode = eventLocationModeSelect?.value || 'custom'; + const selectedId = Number(eventLocationSourceIdInput?.value || eventLocationSourceSelect.value || 0); + const filteredOptions = allEventLocationOptions.filter((option) => String(option.listing_type || '') === mode); + const emptyLabel = mode === 'editorial_event' + ? 'Keine aktuell verfügbaren Veranstaltungen' + : 'Ort aus der Datenbank auswählen'; + + eventLocationSourceSelect.innerHTML = ''; + const placeholderOption = document.createElement('option'); + placeholderOption.value = ''; + placeholderOption.textContent = filteredOptions.length === 0 ? emptyLabel : (mode === 'editorial_event' ? 'Veranstaltung aus der Datenbank auswählen' : 'Ort aus der Datenbank auswählen'); + eventLocationSourceSelect.appendChild(placeholderOption); + eventLocationSourceSelect.disabled = filteredOptions.length === 0; + + filteredOptions.forEach((option) => { + const element = document.createElement('option'); + element.value = String(option.id || ''); + element.textContent = buildEventLocationLabel(option); + if (Number(option.id || 0) === selectedId) { + element.selected = true; + } + eventLocationSourceSelect.appendChild(element); + }); } function applyEventLocationOption(option) { @@ -1585,7 +1621,7 @@ if (!empty($canManageSystemSettings)) { function clearEventLocationSelection() { if (eventLocationSourceIdInput) eventLocationSourceIdInput.value = ''; - if (eventLocationSourceInput) eventLocationSourceInput.value = ''; + if (eventLocationSourceSelect) eventLocationSourceSelect.value = ''; if (streetInput) streetInput.value = ''; if (zipInput) zipInput.value = ''; if (cityInput) cityInput.value = ''; @@ -1614,6 +1650,7 @@ if (!empty($canManageSystemSettings)) { if (eventLocationSourceLabel) { eventLocationSourceLabel.textContent = mode === 'editorial_event' ? 'Veranstaltung aus Datenbank' : 'Ort aus Datenbank'; } + rebuildEventLocationOptions(); if (categoryInput) { if (usesDatabaseLocation) { @@ -1624,7 +1661,7 @@ if (!empty($canManageSystemSettings)) { } if (usesDatabaseLocation) { - const option = findEventLocationOptionByLabel(eventLocationSourceInput?.value || ''); + const option = findEventLocationOptionById(eventLocationSourceSelect?.value || eventLocationSourceIdInput?.value || ''); if (option && String(option.listing_type || '') === mode) { applyEventLocationOption(option); } else { @@ -1683,9 +1720,9 @@ if (!empty($canManageSystemSettings)) { clearEventLocationSelection(); syncEventLocationMode(); }); - eventLocationSourceInput?.addEventListener('change', () => { + eventLocationSourceSelect?.addEventListener('change', () => { const mode = eventLocationModeSelect?.value || 'custom'; - const option = findEventLocationOptionByLabel(eventLocationSourceInput.value || ''); + const option = findEventLocationOptionById(eventLocationSourceSelect.value || ''); if (!option || String(option.listing_type || '') !== mode) { clearEventLocationSelection(); return; diff --git a/src/App/ListingCatalog.php b/src/App/ListingCatalog.php index 195f4fc..209ffcd 100644 --- a/src/App/ListingCatalog.php +++ b/src/App/ListingCatalog.php @@ -519,6 +519,17 @@ final class ListingCatalog LEFT JOIN listing_categories lc ON lc.id = lcm.category_id WHERE l.listing_type IN ("place","editorial_event") AND l.status IN ("draft","published") + AND ( + l.listing_type = "place" + OR ( + lo.starts_at IS NOT NULL + AND DATE(lo.starts_at) <= CURRENT_DATE() + AND ( + lo.recurrence_until IS NULL + OR DATE(lo.recurrence_until) >= CURRENT_DATE() + ) + ) + ) ORDER BY CASE l.listing_type WHEN "place" THEN 1