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:
parent
0113ee2fbb
commit
b6d2606a23
5 changed files with 137 additions and 57 deletions
|
|
@ -2054,19 +2054,58 @@ html.modal-open {
|
|||
.rk-rec-btn--active {
|
||||
animation: rec-pulse 1.2s ease-in-out infinite;
|
||||
}
|
||||
/* Filter-Panel (aufklappbar) */
|
||||
.rk-filter-toggle-btn {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.rk-filter-toggle-btn.active {
|
||||
background: var(--c-primary);
|
||||
border-color: var(--c-primary);
|
||||
color: #fff;
|
||||
}
|
||||
.rk-filter-badge {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 2px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: var(--c-danger, #EF4444);
|
||||
border: 1.5px solid var(--c-surface);
|
||||
pointer-events: none;
|
||||
}
|
||||
.rk-filter-panel {
|
||||
border-top: 1px solid var(--c-border-light);
|
||||
padding: var(--space-3) var(--space-3) var(--space-2);
|
||||
background: var(--c-surface);
|
||||
}
|
||||
.rk-filters {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
.rk-filter-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-1);
|
||||
}
|
||||
.rk-filter-label {
|
||||
font-size: var(--text-xs);
|
||||
font-weight: 600;
|
||||
color: var(--c-text-muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
padding-left: 2px;
|
||||
}
|
||||
.rk-chips-row {
|
||||
display: flex;
|
||||
gap: var(--space-1);
|
||||
overflow-x: auto;
|
||||
scrollbar-width: none;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
.rk-filter-group::-webkit-scrollbar { display: none; }
|
||||
.rk-chips-row::-webkit-scrollbar { display: none; }
|
||||
.rk-chip {
|
||||
padding: 4px 10px;
|
||||
border-radius: var(--radius-full);
|
||||
|
|
|
|||
|
|
@ -284,24 +284,24 @@
|
|||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#book-open"></use></svg>
|
||||
<span class="nav-item-label">Tagebuch</span>
|
||||
</div>
|
||||
<div class="nav-item" data-page="health">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#first-aid"></use></svg>
|
||||
<span class="nav-item-label">Gesundheit</span>
|
||||
<div class="nav-item" data-page="routes">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#path"></use></svg>
|
||||
<span class="nav-item-label">Routen</span>
|
||||
</div>
|
||||
<!-- Mittlerer + Button -->
|
||||
<div class="nav-item nav-item-center" id="nav-add">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#plus"></use></svg>
|
||||
</div>
|
||||
<div class="nav-item" data-page="poison">
|
||||
<span style="position:relative;display:inline-flex">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#warning-octagon"></use></svg>
|
||||
<span class="nav-badge hidden" id="poison-nav-badge">0</span>
|
||||
</span>
|
||||
<span class="nav-item-label">Alarm</span>
|
||||
<div class="nav-item" data-page="forum">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#push-pin"></use></svg>
|
||||
<span class="nav-item-label">Forum</span>
|
||||
</div>
|
||||
<div class="nav-item" data-page="settings">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#gear"></use></svg>
|
||||
<span class="nav-item-label">Ich</span>
|
||||
<div class="nav-item" data-page="notifications">
|
||||
<span style="position:relative;display:inline-flex">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#bell"></use></svg>
|
||||
<span class="nav-badge hidden" id="notif-nav-badge">0</span>
|
||||
</span>
|
||||
<span class="nav-item-label">Benachrichtigungen</span>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
Router, State-Management, Navigation, Initialisierung.
|
||||
============================================================ */
|
||||
|
||||
const APP_VER = '208'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||
const APP_VER = '209'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||
|
||||
const App = (() => {
|
||||
|
||||
|
|
@ -440,9 +440,15 @@ const App = (() => {
|
|||
try {
|
||||
const { count } = await API.notifications.unreadCount();
|
||||
const badge = document.getElementById('notif-badge');
|
||||
if (!badge) return;
|
||||
badge.textContent = count;
|
||||
badge.style.display = count > 0 ? '' : 'none';
|
||||
if (badge) {
|
||||
badge.textContent = count;
|
||||
badge.style.display = count > 0 ? '' : 'none';
|
||||
}
|
||||
const navBadge = document.getElementById('notif-nav-badge');
|
||||
if (navBadge) {
|
||||
navBadge.textContent = count;
|
||||
navBadge.classList.toggle('hidden', count === 0);
|
||||
}
|
||||
} catch { /* ignorieren */ }
|
||||
}
|
||||
|
||||
|
|
@ -611,9 +617,9 @@ const App = (() => {
|
|||
<span class="${titleClass} dog-sw-title" style="cursor:pointer" title="${UI.escape(dog.name)} bearbeiten">${UI.escape(dog.name)}</span>
|
||||
${othersHtml}`;
|
||||
|
||||
// Klick aktiver Avatar oder Name → Hund-Profil
|
||||
// Klick aktiver Avatar → Welcome-Seite; Klick Name → Hund-Profil
|
||||
el.querySelector(`#dog-sw-active-${ctxId}`)?.addEventListener('click', () => {
|
||||
navigate('dog-profile');
|
||||
navigate('welcome');
|
||||
});
|
||||
el.querySelector(`.dog-sw-title`)?.addEventListener('click', () => {
|
||||
navigate('dog-profile');
|
||||
|
|
|
|||
|
|
@ -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') {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
Offline-Cache + Push Notifications + Tile-Cache
|
||||
============================================================ */
|
||||
|
||||
const CACHE_VERSION = 'by-v231';
|
||||
const CACHE_VERSION = 'by-v232';
|
||||
const CACHE_STATIC = `${CACHE_VERSION}-static`;
|
||||
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue