const Stats = (() => { const ALL_TYPES = ['hiking', 'running', 'cycling', 'driving', 'train', 'other', 'none']; const TYPE_LABELS = { hiking: '🥾 Hiking', running: '🏃 Running', cycling: '🚴 Cycling', driving: '🚗 Driving', train: '🚆 Train', other: '📍 Other', none: '— Untyped' }; // null = all; otherwise Set of selected type strings let selectedTypes = null; async function init() { await loadStats(); } async function loadStats() { const content = document.getElementById('stats-content'); content.innerHTML = '
Loading stats...
'; try { const types = selectedTypes ? [...selectedTypes] : []; const stats = await API.getStats(types); renderStats(stats); } catch (e) { content.innerHTML = '
Error loading stats: ' + escHtml(e.message) + '
'; } } function renderFilterBar() { const allSelected = selectedTypes === null; let html = '
'; html += ``; for (const t of ALL_TYPES) { const active = !allSelected && selectedTypes.has(t); html += ``; } html += '
'; return html; } function renderStats(stats) { const content = document.getElementById('stats-content'); let html = renderFilterBar(); // Summary totals if (stats.total !== undefined || stats.totalDistance !== undefined) { const totalDist = stats.total?.distance || stats.totalDistance || 0; const totalTracks = stats.total?.count || stats.trackCount || 0; html += `

Total

Tracks
${totalTracks}
Distance
${formatDistance(totalDist)}
`; } // By year if (stats.byYear && stats.byYear.length > 0) { const maxDist = Math.max(...stats.byYear.map(s => s.distance || 0)); html += '

By Year

'; for (const s of stats.byYear) { const pct = maxDist > 0 ? ((s.distance || 0) / maxDist * 100) : 0; html += `
${s.year}
${formatDistance(s.distance)}
`; } html += '
'; } // By month (last 24 months) if (stats.byMonth && stats.byMonth.length > 0) { const recent = stats.byMonth.slice(-24); const maxDist = Math.max(...recent.map(s => s.distance || 0)); html += '

By Month (last 24)

'; for (const s of recent) { const pct = maxDist > 0 ? ((s.distance || 0) / maxDist * 100) : 0; const label = `${s.year}-${String(s.month).padStart(2, '0')}`; html += `
${label}
${formatDistance(s.distance)}
`; } html += '
'; } // By week (last 52 weeks) if (stats.byWeek && stats.byWeek.length > 0) { const recent = stats.byWeek.slice(-52); const maxDist = Math.max(...recent.map(s => s.distance || 0)); html += '

By Week (last 52)

'; for (const s of recent) { const pct = maxDist > 0 ? ((s.distance || 0) / maxDist * 100) : 0; const label = `${s.year}-W${String(s.week).padStart(2, '0')}`; html += `
${label}
${formatDistance(s.distance)}
`; } html += '
'; } if (!html.includes('stats-section')) { html += '
No tracks match the current filter.
'; } content.innerHTML = html; // Bind filter button events content.querySelectorAll('.stats-type-btn').forEach(btn => { btn.addEventListener('click', () => { const t = btn.dataset.type; if (t === 'all') { selectedTypes = null; } else { if (selectedTypes === null) { selectedTypes = new Set([t]); } else if (selectedTypes.has(t)) { selectedTypes.delete(t); if (selectedTypes.size === 0) selectedTypes = null; } else { selectedTypes.add(t); } } loadStats(); }); }); } return { init, loadStats }; })();