big
All checks were successful
Deploy / deploy (push) Successful in 1m8s

This commit is contained in:
2026-08-23 02:47:01 +02:00
parent 9bbbd94b98
commit 49c13dfd80
19 changed files with 555 additions and 24 deletions

View File

@@ -264,22 +264,60 @@ if ($isAdminArea && !empty($canManageSystemSettings)) {
<?php endif; ?>
<?php if ($section === 'listing-review' && !empty($canReviewListings)): ?>
<section class="account-panel">
<div class="account-panel__head"><h2>Orte &amp; Veranstaltungen freigeben</h2><p class="muted">Nur noch nicht freigegebene Orte und Veranstaltungen prüfen und bei Bedarf vor der Freigabe bearbeiten.</p></div>
<div class="account-panel__head">
<h2>Orte &amp; Veranstaltungen</h2>
<p class="muted">Freigaben bearbeiten sowie externe Ortsdaten für bestehende Einträge ergänzen oder aktualisieren.</p>
<div class="flex gap-12" style="flex-wrap:wrap; margin-top:14px;">
<a class="btn <?= ($listingReviewTab ?? 'approvals') === 'approvals' ? '' : 'ghost' ?>" href="/dashboard?area=admin&amp;section=listing-review&amp;listing_tab=approvals">Freigaben</a>
<a class="btn <?= ($listingReviewTab ?? '') === 'existing' ? '' : 'ghost' ?>" href="/dashboard?area=admin&amp;section=listing-review&amp;listing_tab=existing">Bestehende Ortsbearbeitung</a>
<a class="btn <?= ($listingReviewTab ?? '') === 'missing' ? '' : 'ghost' ?>" href="/dashboard?area=admin&amp;section=listing-review&amp;listing_tab=missing">Fehlende Angaben</a>
</div>
</div>
<div class="account-panel__body">
<?php if (!$listingReviewRequests): ?><p class="muted">Keine offenen Orte oder Veranstaltungen zur Freigabe.</p><?php endif; ?>
<?php if (($listingReviewTab ?? 'approvals') !== 'approvals'): ?>
<form method="get" class="flex gap-12" style="align-items:end; flex-wrap:wrap; margin-bottom:18px;">
<input type="hidden" name="area" value="admin"><input type="hidden" name="section" value="listing-review"><input type="hidden" name="listing_tab" value="<?= htmlspecialchars((string)$listingReviewTab, ENT_QUOTES) ?>">
<label class="stack gap-6" style="min-width:280px; flex:1;"><span class="label">Ort oder Veranstaltung suchen</span><input class="input" name="listing_query" value="<?= htmlspecialchars((string)($listingManagementSearchQuery ?? ''), ENT_QUOTES) ?>" placeholder="Name, Straße, Ort oder PLZ"></label>
<button class="btn" type="submit">Suchen</button>
</form>
<?php if (($listingReviewTab ?? '') === 'missing'): ?><p class="muted small">Gezeigt werden veröffentlichte Einträge, denen mindestens einer der aktuell aktivierten Anbieter fehlt: <?= $activePlaceProviders ? htmlspecialchars(implode(', ', array_map('strtoupper', $activePlaceProviders)), ENT_QUOTES) : 'keine aktiven Anbieter' ?>.</p><?php endif; ?>
<?php endif; ?>
<?php if (($listingReviewTab ?? 'approvals') === 'approvals' && !$listingReviewRequests): ?><p class="muted">Keine offenen Orte oder Veranstaltungen zur Freigabe.</p><?php endif; ?>
<?php if (($listingReviewTab ?? 'approvals') !== 'approvals' && !$listingManagementEntries): ?><p class="muted">Keine passenden bestehenden Orte oder Veranstaltungen gefunden.</p><?php endif; ?>
<div class="stack gap-12">
<?php foreach ($listingReviewRequests as $request): ?>
<?php foreach (($listingReviewTab ?? 'approvals') === 'approvals' ? $listingReviewRequests : $listingManagementEntries as $request): ?>
<article class="card" style="padding:18px;">
<strong><?= htmlspecialchars((string)$request['title'], ENT_QUOTES) ?></strong>
<span class="badge"><?= (string)($request['listing_type'] ?? '') === 'place' ? 'Ort' : 'Veranstaltung' ?></span>
<p class="muted small"><?= htmlspecialchars(trim(implode(', ', array_filter([(string)($request['street'] ?? ''), trim((string)($request['zip'] ?? '') . ' ' . (string)($request['city'] ?? '')), (string)($request['region'] ?? '')]))), ENT_QUOTES) ?></p>
<p class="muted small">Eingereicht von <?= htmlspecialchars((string)($request['requested_by_name'] ?: 'Mitglied'), ENT_QUOTES) ?>.</p>
<div class="flex gap-12" style="flex-wrap:wrap; margin:14px 0;"><a class="btn ghost" href="/dashboard?area=admin&amp;section=listing-review&amp;edit_listing=<?= (int)$request['listing_id'] ?>">Bearbeiten</a></div>
<form method="post" class="stack gap-8">
<?php if (($listingReviewTab ?? 'approvals') === 'approvals'): ?><p class="muted small">Eingereicht von <?= htmlspecialchars((string)($request['requested_by_name'] ?: 'Mitglied'), ENT_QUOTES) ?>.</p><?php endif; ?>
<?php if (!empty($request['provider_links'])): ?><p class="muted small">Verknüpft: <?= htmlspecialchars(implode(', ', array_map('strtoupper', array_keys((array)$request['provider_links']))), ENT_QUOTES) ?><?= $request['rating_value'] !== null ? ' · Google: ' . htmlspecialchars((string)$request['rating_value'], ENT_QUOTES) . ' / 5 (' . (int)$request['rating_count'] . ')' : '' ?></p><?php endif; ?>
<?php if (!empty($request['missing_providers'])): ?><p class="muted small">Fehlt: <?= htmlspecialchars(implode(', ', array_map('strtoupper', (array)$request['missing_providers'])), ENT_QUOTES) ?></p><?php endif; ?>
<div class="flex gap-12" style="flex-wrap:wrap; margin:14px 0;">
<?php if (($listingReviewTab ?? 'approvals') === 'approvals'): ?><a class="btn ghost" href="/dashboard?area=admin&amp;section=listing-review&amp;listing_tab=approvals&amp;edit_listing=<?= (int)$request['listing_id'] ?>">Bearbeiten</a><?php endif; ?>
<form method="post"><input type="hidden" name="action" value="place_provider_search"><input type="hidden" name="listing_id" value="<?= (int)$request['listing_id'] ?>"><button class="btn ghost" type="submit">Externe Ortsdaten suchen</button></form>
</div>
<?php $providerSearch = $placeProviderSearches[(int)$request['listing_id']] ?? null; ?>
<?php if (is_array($providerSearch)): ?>
<div class="card stack gap-10" style="padding:14px; margin:14px 0;">
<strong>Treffer für <?= htmlspecialchars((string)($providerSearch['query'] ?? ''), ENT_QUOTES) ?></strong>
<?php foreach ((array)($providerSearch['notices'] ?? []) as $notice): ?><p class="muted small" style="margin:0;"><?= htmlspecialchars((string)$notice, ENT_QUOTES) ?></p><?php endforeach; ?>
<?php if (empty($providerSearch['results'])): ?><p class="muted small" style="margin:0;">Keine Treffer. Adresse oder Name bitte über „Bearbeiten“ prüfen.</p><?php endif; ?>
<?php foreach ((array)($providerSearch['results'] ?? []) as $resultIndex => $result): ?>
<div class="card" style="padding:12px;">
<div class="flex gap-12" style="align-items:flex-start; justify-content:space-between; flex-wrap:wrap;">
<div><strong><?= htmlspecialchars((string)($result['name'] ?? ''), ENT_QUOTES) ?></strong> <span class="badge"><?= htmlspecialchars(strtoupper((string)($result['provider'] ?? '')), ENT_QUOTES) ?></span><p class="muted small" style="margin:6px 0 0;"><?= htmlspecialchars((string)($result['address'] ?? ''), ENT_QUOTES) ?><?= isset($result['distance_m']) && $result['distance_m'] !== null ? ' · ca. ' . (int)$result['distance_m'] . ' m entfernt' : '' ?><?= isset($result['rating']) && $result['rating'] !== null ? ' · Google: ' . htmlspecialchars((string)$result['rating'], ENT_QUOTES) . ' / 5 (' . (int)($result['rating_count'] ?? 0) . ')' : '' ?></p></div>
<form method="post"><input type="hidden" name="action" value="place_provider_link"><input type="hidden" name="listing_id" value="<?= (int)$request['listing_id'] ?>"><input type="hidden" name="result_index" value="<?= (int)$resultIndex ?>"><button class="btn ghost" type="submit">Diesen Treffer verwenden</button></form>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<?php if (($listingReviewTab ?? 'approvals') === 'approvals'): ?><form method="post" class="stack gap-8">
<input type="hidden" name="action" value="pending_listing_decide"><input type="hidden" name="listing_id" value="<?= (int)$request['listing_id'] ?>">
<textarea name="review_note" class="textarea" rows="2" placeholder="Hinweis für die Entscheidung"></textarea>
<div class="flex gap-12"><button class="btn" type="submit" name="decision" value="approved">Freigeben</button><button class="btn ghost" type="submit" name="decision" value="rejected">Ablehnen</button></div>
</form>
</form><?php endif; ?>
</article>
<?php endforeach; ?>
</div>
@@ -1000,20 +1038,35 @@ if ($isAdminArea && !empty($canManageSystemSettings)) {
<div class="card">
<strong>Orts- und Bewertungsdienste</strong>
<div class="stack gap-10" style="margin-top:12px;">
<label class="chip" style="cursor:pointer; align-items:flex-start;">
<input type="checkbox" name="osm_places_enabled" value="1" <?= !isset($systemSettingsValues['osm_places_enabled']) || $systemSettingsValues['osm_places_enabled'] === '1' ? 'checked' : '' ?> style="margin-right:8px; margin-top:4px;">
<span>OpenStreetMap / Nominatim aktivieren
<span class="muted small" style="display:block;">Kostenlose Orts- und Adresssuche. Bei Deaktivierung werden keine OSM-Anfragen für neue Ortsverknüpfungen ausgelöst.</span>
</span>
</label>
<label class="chip" style="cursor:pointer; align-items:flex-start;">
<input type="checkbox" name="google_places_enabled" value="1" <?= !empty($systemSettingsValues['google_places_enabled']) && $systemSettingsValues['google_places_enabled'] === '1' ? 'checked' : '' ?> style="margin-right:8px; margin-top:4px;">
<span>Google Places vorbereitend aktivieren
<span class="muted small" style="display:block;">Aktuell noch ohne produktive Anbindung. Der Schalter ist als technische Vorbereitung für spätere Place-ID-, Bewertungs- und Review-Anreicherung gedacht.</span>
<span>Google Places aktivieren
<span class="muted small" style="display:block;">Erlaubt die Google-Verknüpfung und aktuelle Gesamtwertung mit Anzahl der Bewertungen.</span>
</span>
</label>
<label class="chip" style="cursor:pointer; align-items:flex-start;">
<input type="checkbox" name="azure_maps_enabled" value="1" <?= !empty($systemSettingsValues['azure_maps_enabled']) && $systemSettingsValues['azure_maps_enabled'] === '1' ? 'checked' : '' ?> style="margin-right:8px; margin-top:4px;">
<span>Azure Maps aktivieren
<span class="muted small" style="display:block;">Erlaubt die Microsoft-POI-Suche für Ortsverknüpfungen.</span>
</span>
</label>
<div class="stack gap-6">
<label class="label" for="placeDataProvider">Standardanbieter für Ortsdaten</label>
<select id="placeDataProvider" name="place_data_provider" class="select">
<option value="osm" <?= (($systemSettingsValues['place_data_provider'] ?? 'osm') === 'osm') ? 'selected' : '' ?>>OpenStreetMap / Nominatim</option>
<option value="osm_google_optional" <?= (($systemSettingsValues['place_data_provider'] ?? 'osm') === 'osm_google_optional') ? 'selected' : '' ?>>OpenStreetMap primär, Google optional</option>
<option value="google" <?= (($systemSettingsValues['place_data_provider'] ?? '') === 'google') ? 'selected' : '' ?>>Google Places primär</option>
<option value="azure" <?= (($systemSettingsValues['place_data_provider'] ?? '') === 'azure') ? 'selected' : '' ?>>Azure Maps primär</option>
<option value="all_enabled" <?= (($systemSettingsValues['place_data_provider'] ?? '') === 'all_enabled') ? 'selected' : '' ?>>Alle aktivierten Anbieter durchsuchen</option>
</select>
<p class="muted small">So bleibt eine kostenfreie Basis aktiv, während Google später zusätzlich vorbereitet werden kann.</p>
</div>
<p class="muted small" style="margin:0;">Bei einer manuellen Admin-Suche werden Name und Adressdaten des offenen Eintrags an die hier aktivierten Anbieter übertragen. Datenschutzhinweise und gegebenenfalls Consent-Text bitte vor dem produktiven Aktivieren prüfen.</p>
</div>
</div>