Karten-Ausbau (OSM), Forum-Erweiterung, UI-Komponenten, Refactor Tagebuch/Gassi (DRY), Landing/SEO — APP_VER 1155
This commit is contained in:
parent
2d907f6370
commit
10e39ed135
18 changed files with 871 additions and 405 deletions
|
|
@ -897,8 +897,6 @@ window.Page_walks = (() => {
|
|||
let _locLon = v.lon != null ? parseFloat(v.lon) : null;
|
||||
let _locName = v.ort_name || null;
|
||||
|
||||
const _pinSvg = '<svg xmlns="http://www.w3.org/2000/svg" width="28" height="36" viewBox="0 0 32 40"><path d="M16 0C7.163 0 0 7.163 0 16c0 10 16 24 16 24S32 26 32 16C32 7.163 24.837 0 16 0z" fill="#C4843A"/><circle cx="16" cy="16" r="7" fill="white"/></svg>';
|
||||
|
||||
const body = `
|
||||
<form id="walk-form" autocomplete="off">
|
||||
|
||||
|
|
@ -924,48 +922,7 @@ window.Page_walks = (() => {
|
|||
|
||||
<div class="form-group" id="wf-location-group">
|
||||
<label class="form-label">Treffpunkt</label>
|
||||
|
||||
<!-- Mini-Karte -->
|
||||
<div style="position:relative">
|
||||
<div id="wf-map-wrap" style="border-radius:var(--radius-md);overflow:hidden;height:200px;background:var(--c-surface-2)"></div>
|
||||
<button type="button" id="wf-map-pin-here" style="
|
||||
position:absolute;bottom:10px;left:50%;transform:translateX(-50%);
|
||||
z-index:1000;background:var(--c-primary);color:#fff;border:none;
|
||||
border-radius:var(--radius-full);padding:6px 14px;font-size:var(--text-xs);
|
||||
font-weight:600;box-shadow:var(--shadow-md);cursor:pointer;
|
||||
display:flex;align-items:center;gap:6px;white-space:nowrap">
|
||||
${UI.icon('map-pin')} Pin hier setzen
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Ort-Chip -->
|
||||
<div class="mt-2">
|
||||
<div id="wf-location-chip-wrap" style="${_locName ? '' : 'display:none'}">
|
||||
<div class="diary-location-chip">
|
||||
${UI.icon('map-pin')}
|
||||
<span id="wf-location-label">${UI.escape(_locName || '')}</span>
|
||||
<button type="button" id="wf-location-clear" aria-label="Name entfernen">
|
||||
${UI.icon('x')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display:flex;gap:var(--space-2);margin-top:var(--space-2)">
|
||||
<button type="button" class="btn btn-danger btn-sm" id="wf-coords-clear">Ort entfernen</button>
|
||||
<button type="button" class="btn btn-secondary flex-1" id="wf-location-btn">
|
||||
${UI.icon('map-pin')}
|
||||
<span id="wf-location-btn-label">${_locLat ? 'POI suchen' : 'GPS → POI suchen'}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Vorschläge -->
|
||||
<div id="wf-location-suggestions" style="display:none;margin-top:var(--space-2)"></div>
|
||||
</div>
|
||||
|
||||
<!-- Versteckte Koordinaten-Felder -->
|
||||
<input type="hidden" name="lat" id="wf-lat" value="${_locLat || ''}">
|
||||
<input type="hidden" name="lon" id="wf-lon" value="${_locLon || ''}">
|
||||
<input type="hidden" name="ort_name" id="wf-ort-name" value="${UI.escape(_locName || '')}">
|
||||
<div id="wf-location-picker"></div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
|
|
@ -996,157 +953,16 @@ window.Page_walks = (() => {
|
|||
|
||||
document.getElementById('wf-cancel')?.addEventListener('click', UI.modal.close);
|
||||
|
||||
// --- Mini-Karte ---
|
||||
let _miniMap = null, _miniMarker = null, _mapEditing = false;
|
||||
|
||||
const _mkIcon = () => L.divIcon({ html: _pinSvg, className: '', iconSize: [28, 36], iconAnchor: [14, 36] });
|
||||
|
||||
function _placeMarker(lat, lon) {
|
||||
if (_miniMarker) { _miniMarker.setLatLng([lat, lon]); return; }
|
||||
_miniMarker = L.marker([lat, lon], { draggable: true, icon: _mkIcon() }).addTo(_miniMap);
|
||||
_miniMarker.on('dragend', () => {
|
||||
const p = _miniMarker.getLatLng();
|
||||
_locLat = p.lat; _locLon = p.lng;
|
||||
document.getElementById('wf-lat').value = _locLat;
|
||||
document.getElementById('wf-lon').value = _locLon;
|
||||
document.getElementById('wf-location-btn-label').textContent = 'POI suchen';
|
||||
// Location Picker
|
||||
let _wfPicker = null;
|
||||
setTimeout(() => {
|
||||
_wfPicker = UI.locationPicker({
|
||||
containerId: 'wf-location-picker',
|
||||
onSelect: (lat, lon, name) => { _locLat = lat; _locLon = lon; _locName = name; },
|
||||
});
|
||||
}
|
||||
if (_locLat != null) _wfPicker.setValue(_locLat, _locLon, _locName);
|
||||
}, 50);
|
||||
|
||||
function _setCoords(lat, lon) {
|
||||
_locLat = lat; _locLon = lon;
|
||||
document.getElementById('wf-lat').value = lat;
|
||||
document.getElementById('wf-lon').value = lon;
|
||||
}
|
||||
|
||||
function _setName(name) {
|
||||
_locName = name;
|
||||
document.getElementById('wf-location-label').textContent = name;
|
||||
document.getElementById('wf-location-chip-wrap').style.display = '';
|
||||
document.getElementById('wf-ort-name').value = name;
|
||||
document.getElementById('wf-location-suggestions').style.display = 'none';
|
||||
}
|
||||
|
||||
UI.loadLeaflet().then(() => {
|
||||
setTimeout(() => {
|
||||
const lat = _locLat || 48.0, lon = _locLon || 11.9, zoom = _locLat ? 15 : 7;
|
||||
_miniMap = L.map('wf-map-wrap', {
|
||||
zoomControl: true, attributionControl: false,
|
||||
dragging: true, scrollWheelZoom: false,
|
||||
}).setView([lat, lon], zoom);
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19 })
|
||||
.addTo(_miniMap);
|
||||
_miniMap.invalidateSize();
|
||||
if (_locLat) _placeMarker(lat, lon);
|
||||
_miniMap.on('click', e => {
|
||||
_setCoords(e.latlng.lat, e.latlng.lng);
|
||||
_placeMarker(_locLat, _locLon);
|
||||
document.getElementById('wf-location-btn-label').textContent = 'POI suchen';
|
||||
});
|
||||
document.getElementById('wf-map-pin-here')?.addEventListener('click', () => {
|
||||
const c = _miniMap.getCenter();
|
||||
_setCoords(c.lat, c.lng);
|
||||
_placeMarker(c.lat, c.lng);
|
||||
document.getElementById('wf-location-btn-label').textContent = 'POI suchen';
|
||||
});
|
||||
}, 150);
|
||||
});
|
||||
|
||||
// Ort-Name-Chip entfernen
|
||||
document.getElementById('wf-location-clear')?.addEventListener('click', () => {
|
||||
_locName = null;
|
||||
document.getElementById('wf-location-chip-wrap').style.display = 'none';
|
||||
document.getElementById('wf-ort-name').value = '';
|
||||
});
|
||||
|
||||
// Koordinaten + Name entfernen (Zwei-Klick)
|
||||
const clearBtn = document.getElementById('wf-coords-clear');
|
||||
let _clearPending = false;
|
||||
clearBtn?.addEventListener('click', () => {
|
||||
if (!_clearPending) {
|
||||
_clearPending = true;
|
||||
clearBtn.textContent = 'Wirklich entfernen?';
|
||||
clearBtn.style.color = 'var(--c-danger)';
|
||||
setTimeout(() => {
|
||||
_clearPending = false;
|
||||
if (clearBtn) {
|
||||
clearBtn.textContent = 'Ort entfernen';
|
||||
clearBtn.style.color = '';
|
||||
}
|
||||
}, 3000);
|
||||
return;
|
||||
}
|
||||
_clearPending = false;
|
||||
clearBtn.textContent = 'Ort entfernen';
|
||||
clearBtn.style.color = '';
|
||||
_locLat = null; _locLon = null; _locName = null;
|
||||
document.getElementById('wf-lat').value = '';
|
||||
document.getElementById('wf-lon').value = '';
|
||||
document.getElementById('wf-ort-name').value = '';
|
||||
document.getElementById('wf-location-chip-wrap').style.display = 'none';
|
||||
document.getElementById('wf-location-suggestions').style.display = 'none';
|
||||
document.getElementById('wf-location-btn-label').textContent = 'GPS → POI suchen';
|
||||
if (_miniMarker) { _miniMarker.remove(); _miniMarker = null; }
|
||||
if (_miniMap) { _miniMap.setView([48.0, 11.9], 7); }
|
||||
});
|
||||
|
||||
// GPS → POI-Suche (wie diary.js)
|
||||
async function _showSuggestions() {
|
||||
const btn = document.getElementById('wf-location-btn');
|
||||
UI.setLoading(btn, true);
|
||||
try {
|
||||
let lat = _locLat, lon = _locLon;
|
||||
if (lat == null || lon == null) {
|
||||
const pos = await API.getLocation({ enableHighAccuracy: true });
|
||||
lat = pos.lat; lon = pos.lon;
|
||||
_setCoords(lat, lon);
|
||||
if (_miniMap) {
|
||||
_miniMap.setView([lat, lon], 15);
|
||||
_placeMarker(lat, lon);
|
||||
if (_miniMarker) _miniMarker.dragging.disable();
|
||||
}
|
||||
document.getElementById('wf-location-btn-label').textContent = 'POI suchen';
|
||||
}
|
||||
|
||||
const suggestions = _appState.user
|
||||
? await API.walks.nearby(lat, lon)
|
||||
: [];
|
||||
|
||||
const sugEl = document.getElementById('wf-location-suggestions');
|
||||
if (!suggestions.length) {
|
||||
sugEl.innerHTML = '<p style="font-size:var(--text-sm);color:var(--c-text-secondary);padding:var(--space-2) 0">Keine Orte in der Nähe gefunden.</p>';
|
||||
} else {
|
||||
sugEl.innerHTML = suggestions.map(s => `
|
||||
<button type="button" class="diary-location-suggestion"
|
||||
data-name="${UI.escape(s.name)}" data-lat="${s.lat}" data-lon="${s.lon}">
|
||||
${UI.icon(_sourceIcon(s.source))}
|
||||
<span>${UI.escape(s.name)}</span>
|
||||
<small>${s.distance_m < 1000 ? s.distance_m + ' m' : (s.distance_m / 1000).toFixed(1) + ' km'}</small>
|
||||
</button>`).join('');
|
||||
sugEl.querySelectorAll('.diary-location-suggestion').forEach(el => {
|
||||
el.addEventListener('click', () => {
|
||||
const slat = parseFloat(el.dataset.lat);
|
||||
const slon = parseFloat(el.dataset.lon);
|
||||
_setCoords(slat, slon);
|
||||
_setName(el.dataset.name);
|
||||
if (_miniMap) {
|
||||
_miniMap.setView([slat, slon], 16);
|
||||
_placeMarker(slat, slon);
|
||||
if (_miniMarker) _miniMarker.dragging.disable();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
sugEl.style.display = '';
|
||||
} catch (err) {
|
||||
UI.toast.error(err?.message?.includes('GPS') || _locLat == null
|
||||
? 'GPS nicht verfügbar.' : 'Ortssuche fehlgeschlagen.');
|
||||
} finally {
|
||||
UI.setLoading(btn, false);
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('wf-location-btn')?.addEventListener('click', _showSuggestions);
|
||||
|
||||
// Formular absenden
|
||||
document.getElementById('walk-form')?.addEventListener('submit', async e => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue