| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- 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 = '<div class="loading">Loading stats...</div>';
- try {
- const types = selectedTypes ? [...selectedTypes] : [];
- const stats = await API.getStats(types);
- renderStats(stats);
- } catch (e) {
- content.innerHTML = '<div class="error-msg">Error loading stats: ' + escHtml(e.message) + '</div>';
- }
- }
- function renderFilterBar() {
- const allSelected = selectedTypes === null;
- let html = '<div class="stats-filter">';
- html += `<button class="stats-type-btn${allSelected ? ' active' : ''}" data-type="all">All</button>`;
- for (const t of ALL_TYPES) {
- const active = !allSelected && selectedTypes.has(t);
- html += `<button class="stats-type-btn${active ? ' active' : ''}" data-type="${escAttr(t)}">${TYPE_LABELS[t]}</button>`;
- }
- html += '</div>';
- 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 += `<div class="stats-section">
- <h3>Total</h3>
- <div class="stat-row">
- <span class="stat-label">Tracks</span>
- <div class="stat-bar-wrap"><div class="stat-bar" style="width:100%"></div></div>
- <span class="stat-value">${totalTracks}</span>
- </div>
- <div class="stat-row">
- <span class="stat-label">Distance</span>
- <div class="stat-bar-wrap"><div class="stat-bar" style="width:100%"></div></div>
- <span class="stat-value">${formatDistance(totalDist)}</span>
- </div>
- </div>`;
- }
- // By year
- if (stats.byYear && stats.byYear.length > 0) {
- const maxDist = Math.max(...stats.byYear.map(s => s.distance || 0));
- html += '<div class="stats-section"><h3>By Year</h3>';
- for (const s of stats.byYear) {
- const pct = maxDist > 0 ? ((s.distance || 0) / maxDist * 100) : 0;
- html += `<div class="stat-row">
- <span class="stat-label">${s.year}</span>
- <div class="stat-bar-wrap"><div class="stat-bar" style="width:${pct.toFixed(1)}%"></div></div>
- <span class="stat-value">${formatDistance(s.distance)}</span>
- </div>`;
- }
- html += '</div>';
- }
- // 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 += '<div class="stats-section"><h3>By Month (last 24)</h3>';
- 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 += `<div class="stat-row">
- <span class="stat-label">${label}</span>
- <div class="stat-bar-wrap"><div class="stat-bar" style="width:${pct.toFixed(1)}%"></div></div>
- <span class="stat-value">${formatDistance(s.distance)}</span>
- </div>`;
- }
- html += '</div>';
- }
- // 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 += '<div class="stats-section"><h3>By Week (last 52)</h3>';
- 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 += `<div class="stat-row">
- <span class="stat-label">${label}</span>
- <div class="stat-bar-wrap"><div class="stat-bar" style="width:${pct.toFixed(1)}%"></div></div>
- <span class="stat-value">${formatDistance(s.distance)}</span>
- </div>`;
- }
- html += '</div>';
- }
- if (!html.includes('stats-section')) {
- html += '<div class="empty-list">No tracks match the current filter.</div>';
- }
- 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 };
- })();
|