This commit is contained in:
@@ -54,6 +54,31 @@ foreach (($listingCategories ?? []) as $categoryRow) {
|
||||
$categoryTitleMap[(string)($categoryRow['slug'] ?? '')] = (string)($categoryRow['title'] ?? '');
|
||||
}
|
||||
$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 = '';
|
||||
foreach (($eventLocationOptions ?? []) as $locationOption) {
|
||||
if ((int)($locationOption['id'] ?? 0) !== $eventLocationSourceIdValue) {
|
||||
continue;
|
||||
}
|
||||
$optionTypeLabel = (string)($locationOption['listing_type'] ?? '') === 'place' ? 'Ort' : 'Veranstaltung';
|
||||
$optionAddressLabel = trim(implode(', ', array_filter([
|
||||
(string)($locationOption['street'] ?? ''),
|
||||
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);
|
||||
}
|
||||
break;
|
||||
}
|
||||
$priceRows = [];
|
||||
if ($editingListing && !empty($editListing['prices']) && is_array($editListing['prices'])) {
|
||||
foreach ($editListing['prices'] as $priceRow) {
|
||||
@@ -1100,6 +1125,7 @@ if (!empty($canManageSystemSettings)) {
|
||||
<?php if ($editingListing && $editListing): ?>
|
||||
<input type="hidden" name="listing_id" value="<?= (int)$editListing['id'] ?>">
|
||||
<?php endif; ?>
|
||||
<input type="hidden" name="event_location_source_id" id="eventLocationSourceId" value="<?= $eventLocationSourceIdValue > 0 ? (int)$eventLocationSourceIdValue : '' ?>">
|
||||
<datalist id="existingPlacesList">
|
||||
<?php foreach ($placeSuggestions as $placeSuggestion): ?>
|
||||
<option value="<?= htmlspecialchars((string)$placeSuggestion['title'], ENT_QUOTES) ?>" data-place='<?= htmlspecialchars(json_encode($placeSuggestion, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), ENT_QUOTES) ?>'>
|
||||
@@ -1107,6 +1133,25 @@ if (!empty($canManageSystemSettings)) {
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</datalist>
|
||||
<datalist id="eventLocationOptionList">
|
||||
<?php foreach (($eventLocationOptions ?? []) as $locationOption): ?>
|
||||
<?php
|
||||
$optionTypeLabel = (string)($locationOption['listing_type'] ?? '') === 'place' ? 'Ort' : 'Veranstaltung';
|
||||
$optionAddressLabel = trim(implode(', ', array_filter([
|
||||
(string)($locationOption['street'] ?? ''),
|
||||
trim((string)($locationOption['zip'] ?? '') . ' ' . (string)($locationOption['city'] ?? '')),
|
||||
])));
|
||||
$optionDateLabel = !empty($locationOption['starts_at']) ? date('d.m.Y', strtotime((string)$locationOption['starts_at'])) : '';
|
||||
$optionLabel = trim(implode(' · ', array_filter([
|
||||
$optionTypeLabel,
|
||||
(string)($locationOption['title'] ?? ''),
|
||||
$optionDateLabel,
|
||||
$optionAddressLabel,
|
||||
])));
|
||||
?>
|
||||
<option value="<?= htmlspecialchars($optionLabel, ENT_QUOTES) ?>" data-location='<?= htmlspecialchars(json_encode($locationOption, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), ENT_QUOTES) ?>'></option>
|
||||
<?php endforeach; ?>
|
||||
</datalist>
|
||||
<?php if (!$editing && $showEventTypeChoice): ?>
|
||||
<div class="stack gap-8">
|
||||
<label class="label">Was möchtest du anlegen?</label>
|
||||
@@ -1130,8 +1175,19 @@ if (!empty($canManageSystemSettings)) {
|
||||
</div>
|
||||
<div class="stack gap-6">
|
||||
<label class="label" for="evDesc">Beschreibung</label>
|
||||
<textarea id="evDesc" name="description" class="textarea" rows="4" placeholder="Was erwartet Familien vor Ort?" required><?= htmlspecialchars($descriptionValue, ENT_QUOTES) ?></textarea>
|
||||
<p class="muted small" style="margin:0;">Die Kurzbeschreibung wird automatisch aus dem Text erzeugt.</p>
|
||||
<textarea id="evDesc" name="description" class="textarea" rows="4" placeholder="Was erwartet Familien vor Ort?"><?= htmlspecialchars($descriptionValue, ENT_QUOTES) ?></textarea>
|
||||
</div>
|
||||
<div class="stack gap-6" id="eventLocationModeWrap">
|
||||
<label class="label" for="eventLocationMode">Location</label>
|
||||
<select id="eventLocationMode" name="event_location_mode" class="select">
|
||||
<option value="place" <?= $eventLocationModeValue === 'place' ? 'selected' : '' ?>>Ort aus Datenbank</option>
|
||||
<option value="editorial_event" <?= $eventLocationModeValue === 'editorial_event' ? 'selected' : '' ?>>Veranstaltung aus Datenbank</option>
|
||||
<option value="custom" <?= $eventLocationModeValue === 'custom' ? 'selected' : '' ?>>Benutzerdefiniert</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="stack gap-6" id="eventLocationSourceWrap">
|
||||
<label class="label" for="eventLocationSourceInput" id="eventLocationSourceLabel">Ort aus Datenbank</label>
|
||||
<input id="eventLocationSourceInput" class="input" list="eventLocationOptionList" placeholder="Eintrag aus der Datenbank auswählen" value="<?= htmlspecialchars($eventLocationSourceInputValue, ENT_QUOTES) ?>">
|
||||
</div>
|
||||
<div class="stack gap-6">
|
||||
<label class="label" for="categoryInput">Kategorie</label>
|
||||
@@ -1184,7 +1240,7 @@ if (!empty($canManageSystemSettings)) {
|
||||
<?php elseif (($section ?? '') === 'places'): ?>
|
||||
<p class="muted small" style="margin:0;">Neue Orte und Veranstaltungen werden erst nach Prüfung durch einen Admin freigegeben.</p>
|
||||
<?php endif; ?>
|
||||
<div class="form-grid">
|
||||
<div class="form-grid" id="eventAddressFieldsWrap">
|
||||
<div class="stack gap-6">
|
||||
<label class="label" for="evZip">PLZ</label>
|
||||
<input id="evZip" name="zip" class="input" maxlength="5" value="<?= htmlspecialchars($zipValue, ENT_QUOTES) ?>">
|
||||
@@ -1194,18 +1250,17 @@ if (!empty($canManageSystemSettings)) {
|
||||
<input id="evCity" name="city" class="input" value="<?= htmlspecialchars($cityValue, ENT_QUOTES) ?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-grid">
|
||||
<div class="form-grid" id="eventAddressStreetWrap">
|
||||
<div class="stack gap-6">
|
||||
<label class="label" for="evStreet">Straße / Adresse</label>
|
||||
<input id="evStreet" name="street" class="input" placeholder="z. B. Musterstraße 12" value="<?= htmlspecialchars($streetValue, ENT_QUOTES) ?>">
|
||||
<p class="muted small">Wird zur Karten-/Umkreissuche genutzt.</p>
|
||||
</div>
|
||||
<div class="stack gap-6">
|
||||
<label class="label" for="evRegion">Region/Bezirk</label>
|
||||
<input id="evRegion" name="region" class="input" value="<?= htmlspecialchars($regionValue, ENT_QUOTES) ?>">
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-8" style="flex-wrap:wrap; align-items:center;">
|
||||
<div class="flex gap-8" style="flex-wrap:wrap; align-items:center;" id="eventAddressActionsWrap">
|
||||
<button class="btn ghost" type="button" id="btnEventAddressSearch">Adresse suchen</button>
|
||||
<button class="btn ghost" type="button" id="btnEventBrowserLocation">📍 Aktuellen Standort</button>
|
||||
</div>
|
||||
@@ -1355,6 +1410,7 @@ if (!empty($canManageSystemSettings)) {
|
||||
const btnEventBrowserLocation = document.getElementById('btnEventBrowserLocation');
|
||||
const eventAddressStatus = document.getElementById('eventAddressStatus');
|
||||
const mapSearch = document.getElementById('mapSearch');
|
||||
const mapDetails = document.getElementById('mapDetails');
|
||||
const latInput = document.getElementById('evLat');
|
||||
const lngInput = document.getElementById('evLng');
|
||||
const entryKindInput = document.getElementById('entryKind');
|
||||
@@ -1364,6 +1420,15 @@ if (!empty($canManageSystemSettings)) {
|
||||
const eventDateLabel = document.getElementById('eventDateLabel');
|
||||
const eventChildrenWrap = document.getElementById('eventChildrenWrap');
|
||||
const eventCapacityWrap = document.getElementById('eventCapacityWrap');
|
||||
const eventLocationModeWrap = document.getElementById('eventLocationModeWrap');
|
||||
const eventLocationModeSelect = document.getElementById('eventLocationMode');
|
||||
const eventLocationSourceWrap = document.getElementById('eventLocationSourceWrap');
|
||||
const eventLocationSourceInput = document.getElementById('eventLocationSourceInput');
|
||||
const eventLocationSourceIdInput = document.getElementById('eventLocationSourceId');
|
||||
const eventLocationSourceLabel = document.getElementById('eventLocationSourceLabel');
|
||||
const eventAddressFieldsWrap = document.getElementById('eventAddressFieldsWrap');
|
||||
const eventAddressStreetWrap = document.getElementById('eventAddressStreetWrap');
|
||||
const eventAddressActionsWrap = document.getElementById('eventAddressActionsWrap');
|
||||
const eventValidUntilWrap = document.getElementById('eventValidUntilWrap');
|
||||
const eventPricingWrap = document.getElementById('eventPricingWrap');
|
||||
const visibilityDetails = document.getElementById('visibilityDetails');
|
||||
@@ -1371,6 +1436,7 @@ if (!empty($canManageSystemSettings)) {
|
||||
const placeOpeningHoursWrap = document.getElementById('placeOpeningHoursWrap');
|
||||
const eventRecurrenceWrap = document.getElementById('eventRecurrenceWrap');
|
||||
const eventDateInput = document.getElementById('evDate');
|
||||
const eventDescriptionInput = document.getElementById('evDesc');
|
||||
const categoryInput = document.getElementById('categoryInput');
|
||||
const categorySlugInput = document.getElementById('listingCategorySlug');
|
||||
const categoryInputStatus = document.getElementById('categoryInputStatus');
|
||||
@@ -1429,6 +1495,77 @@ if (!empty($canManageSystemSettings)) {
|
||||
});
|
||||
}
|
||||
|
||||
function findEventLocationOptionByLabel(label) {
|
||||
const normalized = (label || '').trim();
|
||||
if (!normalized) return null;
|
||||
const option = document.querySelector(`#eventLocationOptionList option[value="${CSS.escape(normalized)}"]`);
|
||||
if (!option) return null;
|
||||
try {
|
||||
return JSON.parse(option.getAttribute('data-location') || 'null');
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function applyEventLocationOption(option) {
|
||||
if (!option) return;
|
||||
if (eventLocationSourceIdInput) eventLocationSourceIdInput.value = String(option.id || '');
|
||||
if (streetInput) streetInput.value = String(option.street || '');
|
||||
if (zipInput) zipInput.value = String(option.zip || '');
|
||||
if (cityInput) cityInput.value = String(option.city || '');
|
||||
if (regionInput) regionInput.value = String(option.region || '');
|
||||
if (latInput) latInput.value = option.lat !== null && option.lat !== '' ? Number(option.lat).toFixed(7) : '';
|
||||
if (lngInput) lngInput.value = option.lng !== null && option.lng !== '' ? Number(option.lng).toFixed(7) : '';
|
||||
if (categoryInput) categoryInput.value = String(option.category_title || '');
|
||||
if (categorySlugInput) categorySlugInput.value = String(option.category_slug || '');
|
||||
if (categoryInputStatus) categoryInputStatus.textContent = '';
|
||||
if (eventAddressStatus) eventAddressStatus.textContent = '';
|
||||
}
|
||||
|
||||
function clearEventLocationSelection() {
|
||||
if (eventLocationSourceIdInput) eventLocationSourceIdInput.value = '';
|
||||
if (eventLocationSourceInput) eventLocationSourceInput.value = '';
|
||||
if (categoryInputStatus) categoryInputStatus.textContent = '';
|
||||
}
|
||||
|
||||
function syncEventLocationMode() {
|
||||
const kind = entryKindInput?.value || 'event';
|
||||
const mode = eventLocationModeSelect?.value || 'custom';
|
||||
const isOwnEvent = kind === 'event';
|
||||
const usesDatabaseLocation = isOwnEvent && mode !== 'custom';
|
||||
|
||||
setSectionState(eventLocationModeWrap, isOwnEvent);
|
||||
setSectionState(eventLocationSourceWrap, usesDatabaseLocation);
|
||||
setSectionState(eventAddressFieldsWrap, !usesDatabaseLocation);
|
||||
setSectionState(eventAddressStreetWrap, !usesDatabaseLocation);
|
||||
setSectionState(eventAddressActionsWrap, !usesDatabaseLocation);
|
||||
setSectionState(mapDetails, !usesDatabaseLocation);
|
||||
|
||||
if (eventLocationSourceLabel) {
|
||||
eventLocationSourceLabel.textContent = mode === 'editorial_event' ? 'Veranstaltung aus Datenbank' : 'Ort aus Datenbank';
|
||||
}
|
||||
|
||||
if (categoryInput) {
|
||||
categoryInput.readOnly = usesDatabaseLocation;
|
||||
if (usesDatabaseLocation) {
|
||||
categoryInput.removeAttribute('list');
|
||||
} else {
|
||||
categoryInput.setAttribute('list', 'listingCategoryList');
|
||||
}
|
||||
}
|
||||
|
||||
if (usesDatabaseLocation) {
|
||||
const option = findEventLocationOptionByLabel(eventLocationSourceInput?.value || '');
|
||||
if (option && String(option.listing_type || '') === mode) {
|
||||
applyEventLocationOption(option);
|
||||
} else {
|
||||
clearEventLocationSelection();
|
||||
}
|
||||
} else if (isOwnEvent) {
|
||||
if (eventLocationSourceIdInput) eventLocationSourceIdInput.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
function syncEventEntryKind(nextKind) {
|
||||
const kind = nextKind || entryKindInput?.value || 'event';
|
||||
if (entryKindInput) entryKindInput.value = kind;
|
||||
@@ -1451,7 +1588,8 @@ if (!empty($canManageSystemSettings)) {
|
||||
|
||||
setSectionState(eventDateRow, kind !== 'place');
|
||||
if (eventDateInput) eventDateInput.required = kind !== 'place';
|
||||
if (categoryInput) categoryInput.required = true;
|
||||
if (eventDescriptionInput) eventDescriptionInput.required = kind !== 'event';
|
||||
if (categoryInput) categoryInput.required = kind !== 'event';
|
||||
setSectionState(eventChildrenWrap, kind === 'event' || kind === 'editorial_event');
|
||||
setSectionState(eventCapacityWrap, kind === 'event');
|
||||
setSectionState(eventValidUntilWrap, kind === 'editorial_event');
|
||||
@@ -1463,6 +1601,7 @@ if (!empty($canManageSystemSettings)) {
|
||||
if (eventDateLabel) {
|
||||
eventDateLabel.textContent = kind === 'editorial_event' ? 'Gültig von' : 'Datum';
|
||||
}
|
||||
syncEventLocationMode();
|
||||
}
|
||||
|
||||
entryKindButtons.forEach((button) => {
|
||||
@@ -1470,6 +1609,19 @@ if (!empty($canManageSystemSettings)) {
|
||||
syncEventEntryKind(button.getAttribute('data-entry-kind-choice') || 'event');
|
||||
});
|
||||
});
|
||||
eventLocationModeSelect?.addEventListener('change', () => {
|
||||
clearEventLocationSelection();
|
||||
syncEventLocationMode();
|
||||
});
|
||||
eventLocationSourceInput?.addEventListener('change', () => {
|
||||
const mode = eventLocationModeSelect?.value || 'custom';
|
||||
const option = findEventLocationOptionByLabel(eventLocationSourceInput.value || '');
|
||||
if (!option || String(option.listing_type || '') !== mode) {
|
||||
clearEventLocationSelection();
|
||||
return;
|
||||
}
|
||||
applyEventLocationOption(option);
|
||||
});
|
||||
syncEventEntryKind(entryKindInput?.value || 'event');
|
||||
|
||||
function createOpeningRow() {
|
||||
@@ -1503,8 +1655,12 @@ if (!empty($canManageSystemSettings)) {
|
||||
btnAddPriceRow?.addEventListener('click', createPriceRow);
|
||||
|
||||
function syncCategoryInputState() {
|
||||
const value = (categoryInput?.value || '').trim().toLowerCase();
|
||||
if (!categorySlugInput || !categoryInputStatus) return;
|
||||
if ((entryKindInput?.value || 'event') === 'event' && (eventLocationModeSelect?.value || 'custom') !== 'custom') {
|
||||
categoryInputStatus.textContent = '';
|
||||
return;
|
||||
}
|
||||
const value = (categoryInput?.value || '').trim().toLowerCase();
|
||||
if (!value) {
|
||||
categorySlugInput.value = '';
|
||||
categoryInputStatus.textContent = '';
|
||||
|
||||
Reference in New Issue
Block a user