Feature: Bottom-Nav umgebaut, Dog-Avatar→Welcome, Routen-Filter-Panel, Recording-Fix

- Bottom-Nav: Tagebuch | Routen | [+] | Forum | Benachrichtigungen (mit Badge)
- Benachrichtigungs-Badge auch in Bottom-Nav (notif-nav-badge)
- Dog-Avatar-Klick → Welcome-Seite (Name bleibt → Hund-Profil)
- Routen: Filter in aufklappbarem Panel, aktive Filter zeigen roten Punkt
- Routen: Start/Stop-Button fragt Page_map.isRecording() ab, kein veralteter lokaler State
- SW by-v232, APP_VER 209
This commit is contained in:
rene 2026-04-19 10:09:02 +02:00
parent 0113ee2fbb
commit b6d2606a23
5 changed files with 137 additions and 57 deletions

View file

@ -18,6 +18,7 @@ window.Page_routes = (() => {
let _onlyMine = false;
let _isRecording = false;
let _filterOpen = false;
// 'mine' | 'discover'
let _browseMode = 'mine';
@ -83,37 +84,58 @@ window.Page_routes = (() => {
<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>
</div>
<button class="btn btn-secondary btn-sm rk-filter-toggle-btn" id="rk-filter-btn" title="Filter">
${UI.icon('funnel')} Filter
<span class="rk-filter-badge" id="rk-filter-badge" style="display:none"></span>
</button>
<label class="btn btn-secondary btn-sm rk-imp-btn" id="rk-imp-wrap" title="GPX / KML / TCX importieren">
${UI.icon('download-simple')} Import
<input type="file" id="rk-import-input" accept=".gpx,.kml,.tcx" style="display:none">
</label>
<button class="btn btn-primary btn-sm rk-rec-btn" id="rk-rec-btn">${UI.icon('path')} Aufzeichnen</button>
</div>
<div class="rk-filters" id="rk-filters">
<div class="rk-filter-group">
<button class="rk-chip active" data-filter="difficulty" data-val="">Alle</button>
<button class="rk-chip" data-filter="difficulty" data-val="leicht">🟢 Leicht</button>
<button class="rk-chip" data-filter="difficulty" data-val="mittel">🟡 Mittel</button>
<button class="rk-chip" data-filter="difficulty" data-val="anspruchsvoll">🔴 Anspruchsvoll</button>
</div>
<div class="rk-filter-group">
<button class="rk-chip active" data-filter="terrain" data-val="">Alle Wege</button>
<button class="rk-chip" data-filter="terrain" data-val="wald">🌲 Wald</button>
<button class="rk-chip" data-filter="terrain" data-val="asphalt">🛣 Asphalt</button>
<button class="rk-chip" data-filter="terrain" data-val="wiese">🌿 Wiese</button>
<button class="rk-chip" data-filter="terrain" data-val="mix">🔀 Mix</button>
</div>
<div class="rk-filter-group">
<button class="rk-chip active" data-filter="sort" data-val="newest">Neueste</button>
<button class="rk-chip" data-filter="sort" data-val="distance">Längste</button>
<button class="rk-chip" data-filter="sort" data-val="rating">Beste</button>
<button class="rk-chip" data-filter="sort" data-val="dog">Hundefreundlich</button>
</div>
<div class="rk-filter-group" id="rk-mine-group" style="display:none">
<button class="rk-chip" data-filter="mine" data-val="mine">🔒 Nur meine</button>
</div>
<div class="rk-filter-group" id="rk-nearby-group" style="display:none">
<button class="rk-chip" id="rk-nearby-btn" data-filter="nearby" data-val="">${UI.icon('map-pin')} In meiner Nähe</button>
<div class="rk-filter-panel" id="rk-filter-panel" style="display:none">
<div class="rk-filters" id="rk-filters">
<div class="rk-filter-group">
<div class="rk-filter-label">Schwierigkeit</div>
<div class="rk-chips-row">
<button class="rk-chip active" data-filter="difficulty" data-val="">Alle</button>
<button class="rk-chip" data-filter="difficulty" data-val="leicht">🟢 Leicht</button>
<button class="rk-chip" data-filter="difficulty" data-val="mittel">🟡 Mittel</button>
<button class="rk-chip" data-filter="difficulty" data-val="anspruchsvoll">🔴 Anspruchsvoll</button>
</div>
</div>
<div class="rk-filter-group">
<div class="rk-filter-label">Untergrund</div>
<div class="rk-chips-row">
<button class="rk-chip active" data-filter="terrain" data-val="">Alle Wege</button>
<button class="rk-chip" data-filter="terrain" data-val="wald">🌲 Wald</button>
<button class="rk-chip" data-filter="terrain" data-val="asphalt">🛣 Asphalt</button>
<button class="rk-chip" data-filter="terrain" data-val="wiese">🌿 Wiese</button>
<button class="rk-chip" data-filter="terrain" data-val="mix">🔀 Mix</button>
</div>
</div>
<div class="rk-filter-group">
<div class="rk-filter-label">Sortierung</div>
<div class="rk-chips-row">
<button class="rk-chip active" data-filter="sort" data-val="newest">Neueste</button>
<button class="rk-chip" data-filter="sort" data-val="distance">Längste</button>
<button class="rk-chip" data-filter="sort" data-val="rating">Beste</button>
<button class="rk-chip" data-filter="sort" data-val="dog">Hundefreundlich</button>
</div>
</div>
<div class="rk-filter-group" id="rk-mine-group" style="display:none">
<div class="rk-filter-label">Eigene</div>
<div class="rk-chips-row">
<button class="rk-chip" data-filter="mine" data-val="mine">🔒 Nur meine</button>
</div>
</div>
<div class="rk-filter-group" id="rk-nearby-group" style="display:none">
<div class="rk-filter-label">Umgebung</div>
<div class="rk-chips-row">
<button class="rk-chip" id="rk-nearby-btn" data-filter="nearby" data-val="">${UI.icon('map-pin')} In meiner Nähe</button>
</div>
</div>
</div>
</div>
</div>
@ -133,14 +155,12 @@ window.Page_routes = (() => {
});
document.getElementById('rk-view-list').addEventListener('click', () => _switchView('list'));
document.getElementById('rk-view-map').addEventListener('click', () => _switchView('map'));
document.getElementById('rk-filter-btn').addEventListener('click', _toggleFilterPanel);
document.getElementById('rk-rec-btn').addEventListener('click', () => {
if (_isRecording) {
_isRecording = false;
if (window.Page_map?.isRecording?.()) {
window.Page_map.stopRecording();
_syncRecBtn();
window.Page_map?.stopRecording?.();
} else {
_isRecording = true;
_syncRecBtn();
App.navigate('map');
setTimeout(() => window.Page_map?.startRecording?.(), 600);
}
@ -154,7 +174,7 @@ window.Page_routes = (() => {
const chip = e.target.closest('.rk-chip');
if (!chip) return;
const { filter, val } = chip.dataset;
chip.closest('.rk-filter-group').querySelectorAll('.rk-chip')
chip.closest('.rk-chips-row').querySelectorAll('.rk-chip')
.forEach(c => c.classList.remove('active'));
chip.classList.add('active');
if (filter === 'difficulty') _difficulty = val;
@ -162,6 +182,7 @@ window.Page_routes = (() => {
if (filter === 'sort') _sortBy = val;
if (filter === 'mine') _onlyMine = chip.classList.contains('active') && val === 'mine';
if (filter === 'nearby') { _loadDataNearby(); return; } // async, calls _applyFilter itself
_updateFilterBadge();
_applyFilter();
});
// Mode toggle
@ -170,21 +191,34 @@ window.Page_routes = (() => {
}
function _syncRecBtn() {
// Falls Page_map bereits initialisiert ist, echten State abfragen
if (window.Page_map?.isRecording) {
_isRecording = window.Page_map.isRecording();
}
const recording = window.Page_map?.isRecording?.() ?? false;
_isRecording = recording;
const btn = document.getElementById('rk-rec-btn');
if (!btn) return;
if (_isRecording) {
if (recording) {
btn.className = 'btn btn-danger btn-sm rk-rec-btn rk-rec-btn--active';
btn.innerHTML = UI.icon('stop-circle') + ' Stopp';
btn.innerHTML = UI.icon('path') + ' Stopp aufnehmen';
} else {
btn.className = 'btn btn-primary btn-sm rk-rec-btn';
btn.innerHTML = UI.icon('path') + ' Aufzeichnen';
}
}
function _toggleFilterPanel() {
_filterOpen = !_filterOpen;
const panel = document.getElementById('rk-filter-panel');
const btn = document.getElementById('rk-filter-btn');
if (panel) panel.style.display = _filterOpen ? '' : 'none';
if (btn) btn.classList.toggle('active', _filterOpen);
}
function _updateFilterBadge() {
const badge = document.getElementById('rk-filter-badge');
if (!badge) return;
const hasFilter = _difficulty !== '' || _terrain !== '' || _sortBy !== 'newest' || _onlyMine;
badge.style.display = hasFilter ? '' : 'none';
}
function _setBrowseMode(mode) {
_browseMode = mode;
document.getElementById('rk-mode-mine')?.classList.toggle('active', mode === 'mine');
@ -474,6 +508,7 @@ window.Page_routes = (() => {
document.querySelectorAll('.rk-chip').forEach(c => c.classList.remove('active'));
document.querySelectorAll('.rk-chip[data-val=""]').forEach(c => c.classList.add('active'));
document.querySelector('.rk-chip[data-val="newest"]')?.classList.add('active');
_updateFilterBadge();
_applyFilter();
});
} else if (_browseMode === 'discover') {