Session 2026-04-23: Desktop Multi-Column, Forum, Fixes, Analytics
- Desktop ≥1024px: page-container 680→860px
- Walks: Liste+Karte nebeneinander, View-Toggle ausgeblendet
- Forum: Rubriken 2-zeilig via CSS Grid (ceil(n/2) Spalten, zentriert)
- Welcome: max-width 920px, Feature-Sections 2-spaltig
- Wissen: Toggle-Mechanismus entfernt, Items immer sichtbar
- Übungen Plan-Karten: vertikal statt horizontal gestapelt
- Admin Analytics: Umami v2 gibt plain numbers statt {value:X}
- CSS-Spezifität: #page-forum nötig wegen layout.css < components.css
- SW by-v312, APP_VER 300
This commit is contained in:
parent
44081a6b9d
commit
71a1371b44
10 changed files with 149 additions and 72 deletions
|
|
@ -210,6 +210,7 @@
|
|||
}
|
||||
.by-tabs::-webkit-scrollbar { display: none; }
|
||||
|
||||
|
||||
.by-tab {
|
||||
flex-shrink: 0;
|
||||
padding: var(--space-2) var(--space-3);
|
||||
|
|
|
|||
|
|
@ -567,6 +567,89 @@
|
|||
.grid-3 { grid-template-columns: 1fr 1fr; }
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
Desktop Multi-Column Layouts (min-width: 1024px)
|
||||
============================================================ */
|
||||
@media (min-width: 1024px) {
|
||||
|
||||
/* Etwas breiterer Standard-Container auf großen Screens */
|
||||
.page-container { max-width: 860px; }
|
||||
|
||||
/* ----------------------------------------------------------
|
||||
WELCOME: 2-spaltige Feature-Sections, zentrierter Hero
|
||||
---------------------------------------------------------- */
|
||||
.welcome-layout { max-width: 920px; }
|
||||
|
||||
.welcome-sections {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: var(--space-4);
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.welcome-sections .card { margin-bottom: 0; }
|
||||
|
||||
/* ----------------------------------------------------------
|
||||
WALKS: Liste (links) + Karte (rechts) nebeneinander
|
||||
---------------------------------------------------------- */
|
||||
#page-walks .page-container { max-width: none; }
|
||||
|
||||
.walks-layout {
|
||||
flex-direction: row;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
/* View-Toggle auf Desktop nicht nötig */
|
||||
.walks-view-toggle { display: none; }
|
||||
|
||||
/* Beide Panels immer sichtbar */
|
||||
#walks-list-view,
|
||||
#walks-map-view {
|
||||
display: flex !important;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#walks-list-view {
|
||||
width: 340px;
|
||||
flex-shrink: 0;
|
||||
border-right: 1px solid var(--c-border-light);
|
||||
}
|
||||
|
||||
#walks-map-view { flex: 1; }
|
||||
|
||||
/* Toolbar-Zeile kompakter da Toggle wegfällt */
|
||||
#page-walks .by-toolbar { padding: var(--space-2) var(--space-4); }
|
||||
|
||||
/* ----------------------------------------------------------
|
||||
FORUM: Rubriken über volle Breite, Threads darunter
|
||||
---------------------------------------------------------- */
|
||||
#page-forum .page-container { max-width: 1100px; }
|
||||
|
||||
/* Rubriken auf genau 2 Zeilen verteilen, zentriert
|
||||
— #page-forum nötig für Spezifität > .by-tabs (components.css lädt später) */
|
||||
#page-forum .forum-category-tabs {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(var(--forum-tab-cols, 7), minmax(0, 1fr));
|
||||
overflow: visible;
|
||||
gap: var(--space-1);
|
||||
padding-bottom: var(--space-2);
|
||||
}
|
||||
|
||||
#page-forum .forum-category-tabs .by-tab {
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Suche + Threads volle Breite */
|
||||
.forum-main-col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
Phosphor Icons — SVG Sprite
|
||||
============================================================ */
|
||||
|
|
|
|||
|
|
@ -169,23 +169,18 @@
|
|||
<span class="sidebar-item-badge" id="lost-badge" style="display:none">0</span>
|
||||
</div>
|
||||
|
||||
<button class="sidebar-section-toggle" id="wissen-toggle" aria-expanded="false">
|
||||
<span>Wissen</span>
|
||||
<svg class="ph-icon wissen-caret" aria-hidden="true"><use href="/icons/phosphor.svg#caret-right"></use></svg>
|
||||
</button>
|
||||
<div class="sidebar-section-body" id="wissen-body">
|
||||
<div class="sidebar-item" data-page="wiki">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#books"></use></svg> Wiki
|
||||
</div>
|
||||
<div class="sidebar-item" data-page="knigge">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#handshake"></use></svg> Knigge
|
||||
</div>
|
||||
<div class="sidebar-item" data-page="movies">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#film-slate"></use></svg> Filme
|
||||
</div>
|
||||
<div class="sidebar-item" data-page="erste-hilfe">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#first-aid"></use></svg> Erste Hilfe
|
||||
</div>
|
||||
<span class="sidebar-section-label">Wissen</span>
|
||||
<div class="sidebar-item" data-page="wiki">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#books"></use></svg> Wiki
|
||||
</div>
|
||||
<div class="sidebar-item" data-page="knigge">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#handshake"></use></svg> Knigge
|
||||
</div>
|
||||
<div class="sidebar-item" data-page="movies">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#film-slate"></use></svg> Filme
|
||||
</div>
|
||||
<div class="sidebar-item" data-page="erste-hilfe">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#first-aid"></use></svg> Erste Hilfe
|
||||
</div>
|
||||
|
||||
<div class="sidebar-item" data-page="admin" id="sidebar-admin"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
Router, State-Management, Navigation, Initialisierung.
|
||||
============================================================ */
|
||||
|
||||
const APP_VER = '290'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||
const APP_VER = '300'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||
|
||||
const App = (() => {
|
||||
|
||||
|
|
@ -103,7 +103,6 @@ const App = (() => {
|
|||
|
||||
state.page = pageId;
|
||||
UI.scrollTop();
|
||||
_expandWissenIfActive(pageId);
|
||||
|
||||
// Seiten-Modul lazy laden (einmalig)
|
||||
_loadPage(pageId, params);
|
||||
|
|
@ -310,12 +309,6 @@ const App = (() => {
|
|||
return;
|
||||
}
|
||||
|
||||
// Wissen-Toggle aufklappen/zuklappen
|
||||
if (e.target.closest('#wissen-toggle')) {
|
||||
_toggleWissen();
|
||||
return;
|
||||
}
|
||||
|
||||
// Sidebar-Item auf Mobile → schließen nach Navigation
|
||||
if (e.target.closest('#sidebar .sidebar-item')) {
|
||||
_closeSidebar();
|
||||
|
|
@ -353,22 +346,6 @@ const App = (() => {
|
|||
document.getElementById('sidebar-backdrop')?.classList.remove('visible');
|
||||
}
|
||||
|
||||
const _WISSEN_PAGES = new Set(['wiki', 'knigge', 'movies', 'erste-hilfe']);
|
||||
|
||||
function _toggleWissen(force) {
|
||||
const toggle = document.getElementById('wissen-toggle');
|
||||
const body = document.getElementById('wissen-body');
|
||||
if (!toggle || !body) return;
|
||||
const open = force !== undefined ? force : toggle.getAttribute('aria-expanded') !== 'true';
|
||||
toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
|
||||
body.classList.toggle('open', open);
|
||||
try { localStorage.setItem('by_wissen_open', open ? '1' : '0'); } catch (_) {}
|
||||
}
|
||||
|
||||
function _expandWissenIfActive(page) {
|
||||
if (_WISSEN_PAGES.has(page)) _toggleWissen(true);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// SCHNELL-HINZUFÜGEN (+ Button)
|
||||
// ----------------------------------------------------------
|
||||
|
|
@ -770,10 +747,7 @@ const App = (() => {
|
|||
|
||||
_bindNavigation();
|
||||
|
||||
// Wissen-Sektion: gespeicherten Zustand wiederherstellen
|
||||
try {
|
||||
if (localStorage.getItem('by_wissen_open') === '1') _toggleWissen(true);
|
||||
} catch (_) {}
|
||||
try { localStorage.removeItem('by_wissen_open'); } catch (_) {}
|
||||
|
||||
await _checkAuth();
|
||||
|
||||
|
|
|
|||
|
|
@ -114,17 +114,20 @@ window.Page_admin = (() => {
|
|||
</svg>`;
|
||||
}
|
||||
|
||||
const tv = v => v?.value ?? 0;
|
||||
// Umami v2 liefert plain numbers, v1 liefert {value: X} — beide abfangen
|
||||
const tv = v => (v != null && typeof v === 'object') ? (v.value ?? 0) : (v ?? 0);
|
||||
const fmt = v => Number(v).toLocaleString('de');
|
||||
|
||||
// Bounce Rate & Verweildauer
|
||||
const bounceToday = d.today?.bounceRate?.value != null
|
||||
? (d.today.bounceRate.value * 100).toFixed(0) + ' %'
|
||||
: (d.today?.bounces?.value != null && d.today?.visits?.value > 0
|
||||
? ((d.today.bounces.value / d.today.visits.value) * 100).toFixed(0) + ' %'
|
||||
: '—');
|
||||
const timeWeek = d.week?.totaltime?.value > 0 && d.week?.visits?.value > 0
|
||||
? Math.round(d.week.totaltime.value / d.week.visits.value) + ' s'
|
||||
const _bounces = tv(d.today?.bounces);
|
||||
const _visits = tv(d.today?.visits);
|
||||
const bounceToday = _visits > 0
|
||||
? ((_bounces / _visits) * 100).toFixed(0) + ' %'
|
||||
: '—';
|
||||
const _totaltime = tv(d.week?.totaltime);
|
||||
const _visitsW = tv(d.week?.visits);
|
||||
const timeWeek = _totaltime > 0 && _visitsW > 0
|
||||
? Math.round(_totaltime / _visitsW) + ' s'
|
||||
: '—';
|
||||
|
||||
el.innerHTML = `
|
||||
|
|
|
|||
|
|
@ -104,22 +104,31 @@ window.Page_forum = (() => {
|
|||
data-section="map">${UI.icon('users')} Mitgliederkarte</button>
|
||||
</div>
|
||||
|
||||
<!-- Suchleiste -->
|
||||
<div class="forum-search-wrap">
|
||||
<input type="search" class="forum-search" id="forum-search"
|
||||
placeholder="Forum durchsuchen…" autocomplete="off">
|
||||
</div>
|
||||
<!-- Rechte Spalte: Suche + Threads -->
|
||||
<div class="forum-main-col">
|
||||
|
||||
<div class="forum-search-wrap">
|
||||
<input type="search" class="forum-search" id="forum-search"
|
||||
placeholder="Forum durchsuchen…" autocomplete="off">
|
||||
</div>
|
||||
|
||||
<!-- Thread-Liste / Karte / Suche -->
|
||||
<div id="forum-main">
|
||||
<p style="color:var(--c-text-secondary);text-align:center;padding:var(--space-8)">Lädt…</p>
|
||||
</div>
|
||||
|
||||
<!-- Thread-Liste / Karte / Suche -->
|
||||
<div id="forum-main">
|
||||
<p style="color:var(--c-text-secondary);text-align:center;padding:var(--space-8)">Lädt…</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Desktop: Tabs gleichmäßig auf 2 Zeilen verteilen
|
||||
const _tabsEl = document.getElementById('forum-tabs');
|
||||
const _tabCount = _tabsEl.querySelectorAll('.by-tab').length;
|
||||
_tabsEl.style.setProperty('--forum-tab-cols', Math.ceil(_tabCount / 2));
|
||||
|
||||
// Tab-Klicks
|
||||
document.getElementById('forum-tabs').addEventListener('click', e => {
|
||||
_tabsEl.addEventListener('click', e => {
|
||||
const btn = e.target.closest('[data-kat], [data-section]');
|
||||
if (!btn) return;
|
||||
|
||||
|
|
|
|||
|
|
@ -632,7 +632,7 @@ window.Page_uebungen = (() => {
|
|||
<div style="font-size:var(--text-sm);font-weight:var(--weight-semibold);
|
||||
color:var(--c-text);line-height:1.3">${_esc(r.exercise_name)}</div>
|
||||
<div style="font-size:var(--text-xs);color:var(--c-text-secondary);line-height:1.4">${_esc(r.reason)}</div>
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;margin-top:auto;padding-top:var(--space-1)">
|
||||
<div style="display:flex;flex-direction:column;gap:var(--space-2);margin-top:auto;padding-top:var(--space-1)">
|
||||
<div>
|
||||
<span style="font-size:var(--text-xs);color:var(--c-text-secondary)">${r.suggested_reps}× empfohlen</span>
|
||||
<span style="font-size:var(--text-xs);font-weight:700;color:${trendColor};margin-left:4px">${trend}</span>
|
||||
|
|
@ -640,7 +640,7 @@ window.Page_uebungen = (() => {
|
|||
</div>
|
||||
<button class="ueb-trainer-btn btn btn-primary"
|
||||
data-tab="${_esc(r.tab)}" data-name="${_esc(r.exercise_name)}" data-reps="${r.suggested_reps}"
|
||||
style="font-size:var(--text-xs);padding:4px 10px;flex-shrink:0">
|
||||
style="font-size:var(--text-xs);padding:4px 10px;width:100%">
|
||||
Üben
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -656,7 +656,7 @@ window.Page_uebungen = (() => {
|
|||
Dein Plan für heute
|
||||
</span>
|
||||
</div>
|
||||
<div style="display:flex;gap:var(--space-2);align-items:stretch">
|
||||
<div style="display:flex;flex-direction:column;gap:var(--space-2)">
|
||||
${cards.join('')}
|
||||
</div>`;
|
||||
|
||||
|
|
|
|||
|
|
@ -111,6 +111,10 @@ window.Page_walks = (() => {
|
|||
_view = view;
|
||||
document.querySelectorAll('.walks-view-btn').forEach(b =>
|
||||
b.classList.toggle('active', b.dataset.view === view));
|
||||
|
||||
// Desktop: beide Panels bleiben via CSS sichtbar
|
||||
if (window.innerWidth >= 1024) return;
|
||||
|
||||
document.getElementById('walks-list-view').style.display = view === 'liste' ? '' : 'none';
|
||||
document.getElementById('walks-map-view').style.display = view === 'karte' ? '' : 'none';
|
||||
|
||||
|
|
@ -134,6 +138,14 @@ window.Page_walks = (() => {
|
|||
);
|
||||
_renderList();
|
||||
_renderMarkers();
|
||||
// Desktop: Karte direkt initialisieren (beide Panels sichtbar)
|
||||
if (window.innerWidth >= 1024) {
|
||||
UI.loadLeaflet().then(() => {
|
||||
_initMap();
|
||||
setTimeout(() => _map?.invalidateSize(), 150);
|
||||
setTimeout(() => _map?.invalidateSize(), 400);
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
UI.toast.error(err.message || 'Fehler beim Laden.');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ window.Page_welcome = (() => {
|
|||
|| window.navigator.standalone === true;
|
||||
|
||||
_container.innerHTML = `
|
||||
<div style="max-width:480px;margin:0 auto;padding:var(--space-6) var(--space-4) var(--space-8)">
|
||||
<div class="welcome-layout" style="margin:0 auto;padding:var(--space-6) var(--space-4) var(--space-8)">
|
||||
|
||||
<!-- Hero -->
|
||||
<div style="text-align:center;margin-bottom:var(--space-8)">
|
||||
|
|
@ -68,7 +68,9 @@ window.Page_welcome = (() => {
|
|||
</div>
|
||||
</button>
|
||||
|
||||
<!-- Features: Mein Hund -->
|
||||
<!-- Feature-Abschnitte (auf Desktop 2-spaltig) -->
|
||||
<div class="welcome-sections">
|
||||
|
||||
${_featureCard('Mein Hund', [
|
||||
['book-open', 'Tagebuch', 'Momente, Fotos und Meilensteine festhalten', 'diary'],
|
||||
['first-aid', 'Gesundheit', 'Impfungen, Tierarztbesuche & Medikamente', 'health'],
|
||||
|
|
@ -76,21 +78,18 @@ window.Page_welcome = (() => {
|
|||
['clipboard-text', 'Trainingspläne', 'Strukturierte Pläne für jedes Lernziel', 'trainingsplaene'],
|
||||
])}
|
||||
|
||||
<!-- Features: Entdecken -->
|
||||
${_featureCard('Entdecken', [
|
||||
['map-trifold', 'Karte', 'Orte, Routen und Meldungen in der Nähe', 'map'],
|
||||
['path', 'Routen', 'GPS-Routen aufzeichnen und bewerten', 'routes'],
|
||||
['calendar-dots', 'Events', 'Turniere und Veranstaltungen', 'events'],
|
||||
])}
|
||||
|
||||
<!-- Features: Soziales -->
|
||||
${_featureCard('Soziales', [
|
||||
['users', 'Freunde', 'Verbinde dich mit anderen Hundebesitzern', 'friends'],
|
||||
['chat-circle-dots', 'Nachrichten', 'Private Chats mit deinen Freunden', 'chat'],
|
||||
['bell', 'Aktuelles', 'Benachrichtigungen und Neuigkeiten', 'notifications'],
|
||||
])}
|
||||
|
||||
<!-- Features: Community -->
|
||||
${_featureCard('Community', [
|
||||
['warning-octagon', 'Giftköder-Alarm', 'Warnungen sofort melden und empfangen', 'poison'],
|
||||
['paw-print', 'Gassi-Treffen', 'Hunde-Dates mit anderen Besitzern', 'walks'],
|
||||
|
|
@ -99,7 +98,6 @@ window.Page_welcome = (() => {
|
|||
['magnifying-glass', 'Verlorene Hunde', 'Hilf vermisste Hunde zu finden', 'lost'],
|
||||
])}
|
||||
|
||||
<!-- Features: Wissen -->
|
||||
${_featureCard('Wissen', [
|
||||
['books', 'Wiki', 'Rassendatenbank, Gesundheits-Wiki, Quiz', 'wiki'],
|
||||
['handshake', 'Knigge', 'Regeln, Begegnungen, Leinenpflicht', 'knigge'],
|
||||
|
|
@ -107,6 +105,8 @@ window.Page_welcome = (() => {
|
|||
['first-aid', 'Erste Hilfe','Notfallratgeber für häufige Situationen', 'erste-hilfe'],
|
||||
])}
|
||||
|
||||
</div>
|
||||
|
||||
<!-- App installieren -->
|
||||
<div class="card" style="margin-bottom:var(--space-5)" id="install-section">
|
||||
<div style="padding:var(--space-3) var(--space-4);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
Offline-Cache + Push Notifications + Tile-Cache
|
||||
============================================================ */
|
||||
|
||||
const CACHE_VERSION = 'by-v302';
|
||||
const CACHE_VERSION = 'by-v312';
|
||||
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