UX: Nearby-POIs einklappbar + klickbar + nur relevante Typen

- NEARBY_TYPES: Bänke/Wasserstellen entfernt → nur Restaurant, Tierarzt, Zoobedarf
- Gruppen einklappbar (Chevron dreht sich)
- POI-Namen anklickbar → öffnet Apple Maps/Google Maps am POI-Standort
- CSS: Card-Stil für Gruppen, gute Touch-Targets
This commit is contained in:
rene 2026-04-19 11:29:46 +02:00
parent 9a56978f81
commit 3144e100f3
3 changed files with 95 additions and 29 deletions

View file

@ -3,7 +3,7 @@
Router, State-Management, Navigation, Initialisierung.
============================================================ */
const APP_VER = '226'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const APP_VER = '227'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const App = (() => {

View file

@ -34,12 +34,11 @@ window.Page_routes = (() => {
const TERRAIN_LABEL = { wald: '🌲 Wald', asphalt: '🛣️ Asphalt', wiese: '🌿 Wiese', mix: '🔀 Mix' };
const HUNDE_LABEL = { eingeschränkt: '🐾', gut: '🐾🐾', sehr_gut: '🐾🐾🐾', premium: '🐾🐾🐾🐾' };
// POI-Typen die entlang einer Route gezeigt werden
// POI-Typen entlang der Route — nur relevante/interessante Orte
const NEARBY_TYPES = [
{ type: 'restaurant', icon: '🍽️', label: 'Restaurant/Café' },
{ type: 'parkplatz', icon: '🅿️', label: 'Parkplatz' },
{ type: 'drinking_water', icon: '💧', label: 'Wasserstelle' },
{ type: 'bank', icon: '🪑', label: 'Bank' },
{ type: 'restaurant', icon: '🍽️', label: 'Restaurant/Café' },
{ type: 'tierarzt', icon: '🏥', label: 'Tierarzt' },
{ type: 'shop', icon: '🐾', label: 'Zoobedarf' },
];
// _esc und _emptyState ersetzt durch UI.escape() / UI.emptyState()
@ -936,7 +935,6 @@ window.Page_routes = (() => {
if (!el) return;
if (!pois.length) { el.innerHTML = ''; return; }
// Gruppieren nach Typ
const byType = {};
pois.forEach(p => {
const key = p._label;
@ -944,22 +942,64 @@ window.Page_routes = (() => {
byType[key].items.push(p);
});
let gIdx = 0;
el.innerHTML = `
<div class="rk-nearby-title">${UI.icon('map-pin')} Entlang der Route</div>
${Object.values(byType).map(group => `
<div class="rk-nearby-group">
<div class="rk-nearby-group-label">${group.icon} ${UI.escape(group.label)} (${group.items.length})</div>
${group.items.slice(0, 5).map(p => `
<div class="rk-nearby-item">
<span class="rk-nearby-name">${UI.escape(p.name || group.label)}</span>
${p.opening_hours ? `<span class="rk-nearby-detail">${UI.icon('clock')} ${UI.escape(p.opening_hours)}</span>` : ''}
${p.phone ? `<a href="tel:${UI.escape(p.phone)}" class="rk-nearby-detail rk-nearby-phone">${UI.icon('phone')} ${UI.escape(p.phone)}</a>` : ''}
${Object.values(byType).map(group => {
const id = `rk-ng-${gIdx++}`;
return `
<div class="rk-nearby-group">
<button class="rk-nearby-group-header" data-target="${id}" type="button">
<span>${group.icon} ${UI.escape(group.label)} (${group.items.length})</span>
<svg class="ph-icon rk-nearby-chevron" aria-hidden="true" style="transition:transform 0.2s">
<use href="/icons/phosphor.svg#caret-down"></use>
</svg>
</button>
<div id="${id}" class="rk-nearby-items">
${group.items.map(p => `
<button class="rk-nearby-item rk-nearby-item--link" type="button"
data-lat="${p.lat}" data-lon="${p.lon}"
data-name="${UI.escape(p.name || group.label)}">
<div>
<span class="rk-nearby-name">${UI.escape(p.name || group.label)}</span>
${p.opening_hours ? `<span class="rk-nearby-detail">${UI.icon('clock')} ${UI.escape(p.opening_hours)}</span>` : ''}
${p.phone ? `<span class="rk-nearby-detail">${UI.icon('phone')} ${UI.escape(p.phone)}</span>` : ''}
</div>
<svg class="ph-icon" style="width:14px;height:14px;color:var(--c-text-muted);flex-shrink:0" aria-hidden="true">
<use href="/icons/phosphor.svg#map-pin"></use>
</svg>
</button>
`).join('')}
</div>
`).join('')}
${group.items.length > 5 ? `<div style="font-size:var(--text-xs);color:var(--c-text-muted);padding:2px 0">+${group.items.length-5} weitere</div>` : ''}
</div>
`).join('')}
</div>`;
}).join('')}
`;
// Einklapp-Logik
el.querySelectorAll('.rk-nearby-group-header').forEach(btn => {
const target = document.getElementById(btn.dataset.target);
const chevron = btn.querySelector('.rk-nearby-chevron');
let open = true;
btn.addEventListener('click', () => {
open = !open;
target.style.display = open ? '' : 'none';
chevron.style.transform = open ? '' : 'rotate(-90deg)';
});
});
// POI auf Karte zeigen
el.querySelectorAll('.rk-nearby-item--link').forEach(btn => {
btn.addEventListener('click', () => {
const lat = parseFloat(btn.dataset.lat);
const lon = parseFloat(btn.dataset.lon);
const name = btn.dataset.name;
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
const url = isIOS
? `maps://maps.apple.com/?q=${encodeURIComponent(name)}&ll=${lat},${lon}`
: `https://www.google.com/maps/search/?api=1&query=${lat},${lon}`;
window.open(url, '_blank');
});
});
}
// ----------------------------------------------------------