This commit is contained in:
@@ -530,8 +530,19 @@ $sectionLinks = [
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-12" style="flex-wrap:wrap;">
|
||||
<button class="btn ghost" type="button" id="btnProfileLocate">Adresse lokalisieren</button>
|
||||
<p class="muted small" style="margin:0;">Verwendet dieselben Karten-/Adressdienste wie bei Events und nur nach Einwilligung in externe Dienste.</p>
|
||||
<button class="btn ghost" type="button" id="btnProfileLocate">Adresse suchen</button>
|
||||
<button class="btn ghost" type="button" id="btnProfileBrowserLocation">Browser-Adresse übernehmen</button>
|
||||
<button class="btn ghost" type="button" data-modal-open="modalProfileAddressHelp" aria-label="Hilfe zur Profiladresse">?</button>
|
||||
</div>
|
||||
<p class="muted small" style="margin:0;">Für Suche und Browser-Übernahme werden dieselben Karten-/Adressdienste wie bei Events genutzt und nur nach Einwilligung in externe Dienste.</p>
|
||||
<div id="profileAddressStatus" class="muted small" style="margin:0;"></div>
|
||||
<div id="profileAddressSelectedLabel" class="muted small" style="margin:0;<?= empty($profile['street']) && empty($profile['city']) ? ' display:none;' : '' ?>">
|
||||
<?php if (!empty($profile['street']) || !empty($profile['city'])): ?>
|
||||
Gewaehlte Adresse: <?= htmlspecialchars(trim(implode(', ', array_filter([(string)($profile['street'] ?? ''), trim((string)$profile['zip'] . ' ' . (string)$profile['city']), (string)($profile['region'] ?? '')]))), ENT_QUOTES) ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div id="profileAddressNeedsValidation" class="muted small" style="margin:0; display:none;">
|
||||
Die Adresse wurde manuell geaendert. Vor dem Speichern bitte erneut suchen oder die Browser-Adresse uebernehmen.
|
||||
</div>
|
||||
<div class="stack gap-6">
|
||||
<label class="label">Sprachen (Mehrfachauswahl)</label>
|
||||
@@ -566,6 +577,39 @@ $sectionLinks = [
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal" id="modalProfileAddressHelp">
|
||||
<div class="panel">
|
||||
<div class="head flex between center-y">
|
||||
<h3>Warum wir die Adresse abfragen</h3>
|
||||
<button class="btn ghost" type="button" data-modal-close>✕</button>
|
||||
</div>
|
||||
<div class="stack gap-12">
|
||||
<p class="muted" style="margin:0;">Deine Adresse hilft uns, dir die neuesten Termine und Events in deiner Umgebung gezielter anzuzeigen.</p>
|
||||
<p class="muted" style="margin:0;">Wenn keine Adresse hinterlegt ist, koennen wir lokale Bedarfe und passende Angebote deutlich schlechter einschaetzen.</p>
|
||||
<p class="muted small" style="margin:0;">Die Adresse wird nur fuer diese Plattform-Funktionen genutzt. Strasse und Hausnummer werden verschluesselt gespeichert.</p>
|
||||
<div class="flex gap-12">
|
||||
<button class="btn" type="button" data-modal-close>Verstanden</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal" id="modalProfileAddressResults">
|
||||
<div class="panel">
|
||||
<div class="head flex between center-y">
|
||||
<h3>Adresse auswählen</h3>
|
||||
<button class="btn ghost" type="button" data-modal-close>✕</button>
|
||||
</div>
|
||||
<div class="stack gap-12">
|
||||
<p class="muted small" style="margin:0;">Waehle den passenden Treffer fuer dein Profil.</p>
|
||||
<div id="profileAddressResults" class="stack gap-12"></div>
|
||||
<div class="flex gap-12">
|
||||
<button class="btn ghost" type="button" data-modal-close>Abbrechen</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal" id="modalChild">
|
||||
<div class="panel">
|
||||
<div class="head flex between center-y">
|
||||
@@ -746,6 +790,7 @@ $sectionLinks = [
|
||||
const cityInput = document.getElementById('evCity');
|
||||
const regionInput = document.getElementById('evRegion');
|
||||
const profileLocateButton = document.getElementById('btnProfileLocate');
|
||||
const profileBrowserLocationButton = document.getElementById('btnProfileBrowserLocation');
|
||||
const profileStreetInput = document.getElementById('pStreet');
|
||||
const profileZipInput = document.getElementById('pZip');
|
||||
const profileCityInput = document.getElementById('pCity');
|
||||
@@ -753,6 +798,11 @@ $sectionLinks = [
|
||||
const profileRegionViewInput = document.getElementById('pRegionView');
|
||||
const profileLatInput = document.getElementById('pLat');
|
||||
const profileLngInput = document.getElementById('pLng');
|
||||
const profileAddressStatus = document.getElementById('profileAddressStatus');
|
||||
const profileAddressSelectedLabel = document.getElementById('profileAddressSelectedLabel');
|
||||
const profileAddressNeedsValidation = document.getElementById('profileAddressNeedsValidation');
|
||||
const profileAddressResults = document.getElementById('profileAddressResults');
|
||||
const profileAddressResultsModal = document.getElementById('modalProfileAddressResults');
|
||||
let map, marker;
|
||||
|
||||
function ensureLeaflet(callback) {
|
||||
@@ -843,24 +893,50 @@ $sectionLinks = [
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
function geocodeAddressData(query, onSuccess) {
|
||||
function geocodeAddressCandidates(query, onSuccess) {
|
||||
if (!window.PKTConsent || !window.PKTConsent.has('external_services')) {
|
||||
alert('Für Karten und Adresssuche bitte zuerst die externen Dienste in den Cookie-Einstellungen erlauben.');
|
||||
window.PKTConsent?.openPreferences?.();
|
||||
return;
|
||||
}
|
||||
fetch('https://nominatim.openstreetmap.org/search?format=jsonv2&limit=1&addressdetails=1&q=' + encodeURIComponent(query), {
|
||||
fetch('https://nominatim.openstreetmap.org/search?format=jsonv2&limit=5&addressdetails=1&q=' + encodeURIComponent(query), {
|
||||
headers: { 'Accept-Language': 'de', 'User-Agent': 'papa-kind-treff/1.0' },
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (Array.isArray(data) && data[0]) {
|
||||
onSuccess?.(data[0]);
|
||||
if (Array.isArray(data) && data.length) {
|
||||
onSuccess?.(data);
|
||||
return;
|
||||
}
|
||||
if (profileAddressStatus) profileAddressStatus.textContent = '';
|
||||
alert('Adresse konnte nicht eindeutig lokalisiert werden.');
|
||||
})
|
||||
.catch(() => {
|
||||
if (profileAddressStatus) profileAddressStatus.textContent = '';
|
||||
alert('Adresssuche aktuell nicht erreichbar.');
|
||||
});
|
||||
}
|
||||
|
||||
function reverseGeocodeProfileAddress(lat, lng, onSuccess) {
|
||||
if (!window.PKTConsent || !window.PKTConsent.has('external_services')) {
|
||||
alert('Für Karten und Adresssuche bitte zuerst die externen Dienste in den Cookie-Einstellungen erlauben.');
|
||||
window.PKTConsent?.openPreferences?.();
|
||||
return;
|
||||
}
|
||||
fetch(`https://nominatim.openstreetmap.org/reverse?format=jsonv2&addressdetails=1&lat=${encodeURIComponent(lat)}&lon=${encodeURIComponent(lng)}`, {
|
||||
headers: { 'Accept-Language': 'de', 'User-Agent': 'papa-kind-treff/1.0' },
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data && data.lat && data.lon) {
|
||||
onSuccess?.(data);
|
||||
return;
|
||||
}
|
||||
if (profileAddressStatus) profileAddressStatus.textContent = '';
|
||||
alert('Die Browser-Position konnte nicht in eine Adresse umgewandelt werden.');
|
||||
})
|
||||
.catch(() => {
|
||||
if (profileAddressStatus) profileAddressStatus.textContent = '';
|
||||
alert('Adresssuche aktuell nicht erreichbar.');
|
||||
});
|
||||
}
|
||||
@@ -911,6 +987,85 @@ $sectionLinks = [
|
||||
});
|
||||
|
||||
(function(){
|
||||
const setProfileStatus = (message = '') => {
|
||||
if (!profileAddressStatus) return;
|
||||
profileAddressStatus.textContent = message;
|
||||
};
|
||||
|
||||
const renderSelectedAddress = (label) => {
|
||||
if (!profileAddressSelectedLabel) return;
|
||||
if (!label) {
|
||||
profileAddressSelectedLabel.textContent = '';
|
||||
profileAddressSelectedLabel.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
profileAddressSelectedLabel.textContent = `Gewaehlte Adresse: ${label}`;
|
||||
profileAddressSelectedLabel.style.display = '';
|
||||
};
|
||||
|
||||
const setNeedsValidation = (needsValidation) => {
|
||||
if (profileAddressNeedsValidation) {
|
||||
profileAddressNeedsValidation.style.display = needsValidation ? '' : 'none';
|
||||
}
|
||||
};
|
||||
|
||||
const normalizeResult = (result) => {
|
||||
const address = result.address || {};
|
||||
const street = [address.road || '', address.house_number || ''].filter(Boolean).join(' ').trim();
|
||||
const region = address.city_district || address.suburb || address.state || address.county || address.region || address.state_district || '';
|
||||
const city = address.city || address.town || address.village || '';
|
||||
|
||||
return {
|
||||
label: result.display_name || '',
|
||||
street,
|
||||
zip: address.postcode || '',
|
||||
city,
|
||||
region,
|
||||
lat: parseFloat(result.lat),
|
||||
lng: parseFloat(result.lon),
|
||||
};
|
||||
};
|
||||
|
||||
const applyProfileAddress = (result) => {
|
||||
const normalized = normalizeResult(result);
|
||||
|
||||
if (profileStreetInput && normalized.street) profileStreetInput.value = normalized.street;
|
||||
if (profileZipInput && normalized.zip) profileZipInput.value = normalized.zip;
|
||||
if (profileCityInput && normalized.city) profileCityInput.value = normalized.city;
|
||||
if (profileRegionViewInput) profileRegionViewInput.value = normalized.region;
|
||||
if (profileRegionInput) profileRegionInput.value = normalized.region;
|
||||
if (profileLatInput) profileLatInput.value = Number.isNaN(normalized.lat) ? '' : normalized.lat.toFixed(7);
|
||||
if (profileLngInput) profileLngInput.value = Number.isNaN(normalized.lng) ? '' : normalized.lng.toFixed(7);
|
||||
|
||||
renderSelectedAddress(normalized.label || [normalized.street, `${normalized.zip} ${normalized.city}`.trim(), normalized.region].filter(Boolean).join(', '));
|
||||
setNeedsValidation(false);
|
||||
setProfileStatus('Adresse uebernommen.');
|
||||
};
|
||||
|
||||
const openProfileResultsModal = (results) => {
|
||||
if (!profileAddressResults || !profileAddressResultsModal) return;
|
||||
profileAddressResults.innerHTML = '';
|
||||
setProfileStatus('Bitte waehle den passenden Adress-Treffer aus.');
|
||||
|
||||
results.forEach((result) => {
|
||||
const normalized = normalizeResult(result);
|
||||
const button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.className = 'btn ghost';
|
||||
button.style.width = '100%';
|
||||
button.style.textAlign = 'left';
|
||||
button.style.justifyContent = 'flex-start';
|
||||
button.innerHTML = `<strong>${normalized.street || normalized.city || 'Treffer'}</strong><br><span class="muted small">${normalized.label || ''}</span>`;
|
||||
button.addEventListener('click', () => {
|
||||
applyProfileAddress(result);
|
||||
profileAddressResultsModal.classList.remove('open');
|
||||
});
|
||||
profileAddressResults.appendChild(button);
|
||||
});
|
||||
|
||||
profileAddressResultsModal.classList.add('open');
|
||||
};
|
||||
|
||||
const syncProfileRegionView = () => {
|
||||
if (profileRegionViewInput && profileRegionInput) {
|
||||
profileRegionInput.value = profileRegionViewInput.value.trim();
|
||||
@@ -920,6 +1075,9 @@ $sectionLinks = [
|
||||
const clearProfileCoordinates = () => {
|
||||
if (profileLatInput) profileLatInput.value = '';
|
||||
if (profileLngInput) profileLngInput.value = '';
|
||||
renderSelectedAddress('');
|
||||
setNeedsValidation(true);
|
||||
setProfileStatus('');
|
||||
};
|
||||
|
||||
[profileStreetInput, profileZipInput, profileCityInput].forEach((input) => {
|
||||
@@ -944,26 +1102,41 @@ $sectionLinks = [
|
||||
return;
|
||||
}
|
||||
|
||||
geocodeAddressData(parts.join(', '), (result) => {
|
||||
const address = result.address || {};
|
||||
const street = [address.road || '', address.house_number || ''].filter(Boolean).join(' ').trim();
|
||||
const region = address.suburb || address.state || address.county || '';
|
||||
const city = address.city || address.town || address.village || '';
|
||||
setProfileStatus('Suche passende Adressen ...');
|
||||
geocodeAddressCandidates(parts.join(', '), (results) => openProfileResultsModal(results));
|
||||
});
|
||||
|
||||
if (profileStreetInput && street) profileStreetInput.value = street;
|
||||
if (profileZipInput && address.postcode) profileZipInput.value = address.postcode;
|
||||
if (profileCityInput && city) profileCityInput.value = city;
|
||||
if (profileRegionViewInput) profileRegionViewInput.value = region;
|
||||
if (profileRegionInput) profileRegionInput.value = region;
|
||||
profileBrowserLocationButton?.addEventListener('click', () => {
|
||||
if (!window.PKTConsent || !window.PKTConsent.has('external_services')) {
|
||||
alert('Für die Browser-Adresse bitte zuerst die externen Dienste in den Cookie-Einstellungen erlauben.');
|
||||
window.PKTConsent?.openPreferences?.();
|
||||
return;
|
||||
}
|
||||
if (!navigator.geolocation) {
|
||||
alert('Dieser Browser unterstuetzt keine Geolokalisierung.');
|
||||
return;
|
||||
}
|
||||
|
||||
const lat = parseFloat(result.lat);
|
||||
const lng = parseFloat(result.lon);
|
||||
if (profileLatInput) profileLatInput.value = Number.isNaN(lat) ? '' : lat.toFixed(7);
|
||||
if (profileLngInput) profileLngInput.value = Number.isNaN(lng) ? '' : lng.toFixed(7);
|
||||
setProfileStatus('Browser-Standort wird abgefragt ...');
|
||||
navigator.geolocation.getCurrentPosition((position) => {
|
||||
const lat = position.coords.latitude;
|
||||
const lng = position.coords.longitude;
|
||||
reverseGeocodeProfileAddress(lat, lng, (result) => {
|
||||
applyProfileAddress(result);
|
||||
setProfileStatus('Adresse aus dem Browser-Standort uebernommen.');
|
||||
});
|
||||
}, () => {
|
||||
setProfileStatus('');
|
||||
alert('Die Browser-Position konnte nicht gelesen werden.');
|
||||
}, {
|
||||
enableHighAccuracy: true,
|
||||
timeout: 10000,
|
||||
maximumAge: 300000,
|
||||
});
|
||||
});
|
||||
|
||||
syncProfileRegionView();
|
||||
setNeedsValidation(false);
|
||||
})();
|
||||
|
||||
<?php if ($editing): ?>
|
||||
|
||||
Reference in New Issue
Block a user