Feature: Suchfeld in Routen, Events und Places

Alle drei Seiten haben jetzt ein debounced Suchfeld (300ms) mit
Lupe-Icon über der Liste. Die Suche filtert clientseitig:
- Routen: Name, Beschreibung, Username (bereits vorhandener State verbessert)
- Events: Titel, Ort, Beschreibung
- Places: Name, Adresse, Typ
Leerer Zustand zeigt passendes UI.emptyState() mit Suchbegriff.
SW by-v209, APP_VER 178.
This commit is contained in:
rene 2026-04-18 18:42:13 +02:00
parent 5acfa9d8f6
commit 6581a9a88c
3 changed files with 90 additions and 18 deletions

View file

@ -72,8 +72,11 @@ window.Page_routes = (() => {
<button class="rk-mode-btn${_browseMode==='discover'?' active':''}" id="rk-mode-discover">${UI.icon('compass')} Entdecken</button>
</div>
<div class="rk-search-row">
<input class="rk-search" id="rk-search" type="search"
placeholder="🔍 Route suchen…" autocomplete="off">
<div class="diary-search-wrap" style="flex:1;min-width:0">
<svg class="ph-icon diary-search-icon" aria-hidden="true"><use href="/icons/phosphor.svg#magnifying-glass"></use></svg>
<input class="diary-search-input" id="rk-search" type="search"
placeholder="Routen durchsuchen…" autocomplete="off">
</div>
<div class="rk-view-toggle">
<button class="rk-view-btn${_viewMode==='list'?' active':''}" id="rk-view-list" title="Liste">${UI.icon('list')}</button>
<button class="rk-view-btn${_viewMode==='map'?' active':''}" id="rk-view-map" title="Karte">${UI.icon('map-trifold')}</button>
@ -118,8 +121,13 @@ window.Page_routes = (() => {
</div>
`;
let _searchTimer = null;
document.getElementById('rk-search').addEventListener('input', e => {
_search = e.target.value.toLowerCase(); _applyFilter();
clearTimeout(_searchTimer);
_searchTimer = setTimeout(() => {
_search = e.target.value.toLowerCase().trim();
_applyFilter();
}, 300);
});
document.getElementById('rk-view-list').addEventListener('click', () => _switchView('list'));
document.getElementById('rk-view-map').addEventListener('click', () => _switchView('map'));
@ -426,9 +434,12 @@ window.Page_routes = (() => {
if (!_filtered.length) {
if (_data.length) {
// Filter aktiv aber kein Ergebnis
const emptyMsg = _search
? `Keine Routen gefunden für „${UI.escape(_search)}".`
: 'Keine Routen passen zu deinen Filtern.';
grid.innerHTML = `<div class="rk-empty">
<div class="rk-empty-icon">🔍</div>
<p>Keine Routen passen zu deinen Filtern.</p>
<p>${emptyMsg}</p>
<button class="btn btn-secondary" id="rk-empty-reset">Filter zurücksetzen</button>
</div>`;
document.getElementById('rk-empty-reset')?.addEventListener('click', () => {
@ -667,14 +678,20 @@ window.Page_routes = (() => {
`;
const footer = `
<button type="button" class="btn btn-secondary" id="rd-gpx">${UI.icon('download-simple')} GPX</button>
<button type="button" class="btn btn-secondary" id="rd-share" title="Route teilen">${UI.icon('share')}</button>
<button type="button" class="btn btn-secondary" id="rd-send-friend" title="An Freund senden">${UI.icon('chat-circle-dots')} An Freund senden</button>
${isOwn ? `<button type="button" class="btn btn-ghost" id="rd-vis" title="${route.is_public?'Privat machen':'Öffentlich machen'}">
${route.is_public ? UI.icon('lock')+' Privat' : UI.icon('globe')+' Öffentlich'}
</button>
<button type="button" class="btn btn-ghost" id="rd-del" style="color:var(--c-danger)">${UI.icon('trash')}</button>` : ''}
<button type="button" class="btn btn-primary flex-1" id="rd-close">Schließen</button>
<div style="display:flex;gap:var(--space-2);width:100%;flex-wrap:wrap">
<div style="display:flex;gap:var(--space-2);flex:1">
<button type="button" class="btn btn-secondary btn-sm" id="rd-gpx" title="GPX herunterladen">${UI.icon('download-simple')} GPX</button>
<button type="button" class="btn btn-secondary btn-sm" id="rd-share" title="Route teilen">${UI.icon('share')}</button>
<button type="button" class="btn btn-secondary btn-sm" id="rd-send-friend" title="An Freund senden">${UI.icon('chat-circle-dots')}</button>
${isOwn ? `
<button type="button" class="btn btn-secondary btn-sm" id="rd-vis">
${route.is_public ? UI.icon('lock')+' Privat' : UI.icon('globe')+' Öffentlich'}
</button>
<button type="button" class="btn btn-ghost btn-sm" id="rd-del" style="color:var(--c-danger)" title="Route löschen">${UI.icon('trash')} Löschen</button>
` : ''}
</div>
<button type="button" class="btn btn-primary btn-sm" id="rd-close">Schließen</button>
</div>
`;
UI.modal.open({ title: `🥾 ${UI.escape(route.name)}`, body, footer });