stats.js 3.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. const Stats = (() => {
  2. async function init() {
  3. await loadStats();
  4. }
  5. async function loadStats() {
  6. const content = document.getElementById('stats-content');
  7. content.innerHTML = '<div class="loading">Loading stats...</div>';
  8. try {
  9. const stats = await API.getStats();
  10. renderStats(stats);
  11. } catch (e) {
  12. content.innerHTML = '<div class="error-msg">Error loading stats: ' + escHtml(e.message) + '</div>';
  13. }
  14. }
  15. function renderStats(stats) {
  16. const content = document.getElementById('stats-content');
  17. let html = '';
  18. // Summary totals
  19. if (stats.total !== undefined || stats.totalDistance !== undefined) {
  20. const totalDist = stats.total?.distance || stats.totalDistance || 0;
  21. const totalTracks = stats.total?.count || stats.trackCount || 0;
  22. html += `<div class="stats-section">
  23. <h3>Total</h3>
  24. <div class="stat-row">
  25. <span class="stat-label">Tracks</span>
  26. <div class="stat-bar-wrap"><div class="stat-bar" style="width:100%"></div></div>
  27. <span class="stat-value">${totalTracks}</span>
  28. </div>
  29. <div class="stat-row">
  30. <span class="stat-label">Distance</span>
  31. <div class="stat-bar-wrap"><div class="stat-bar" style="width:100%"></div></div>
  32. <span class="stat-value">${formatDistance(totalDist)}</span>
  33. </div>
  34. </div>`;
  35. }
  36. // By year
  37. if (stats.byYear && stats.byYear.length > 0) {
  38. const maxDist = Math.max(...stats.byYear.map(s => s.distance || 0));
  39. html += '<div class="stats-section"><h3>By Year</h3>';
  40. for (const s of stats.byYear) {
  41. const pct = maxDist > 0 ? ((s.distance || 0) / maxDist * 100) : 0;
  42. html += `<div class="stat-row">
  43. <span class="stat-label">${s.year}</span>
  44. <div class="stat-bar-wrap"><div class="stat-bar" style="width:${pct.toFixed(1)}%"></div></div>
  45. <span class="stat-value">${formatDistance(s.distance)}</span>
  46. </div>`;
  47. }
  48. html += '</div>';
  49. }
  50. // By month (last 24 months)
  51. if (stats.byMonth && stats.byMonth.length > 0) {
  52. const recent = stats.byMonth.slice(-24);
  53. const maxDist = Math.max(...recent.map(s => s.distance || 0));
  54. html += '<div class="stats-section"><h3>By Month (last 24)</h3>';
  55. for (const s of recent) {
  56. const pct = maxDist > 0 ? ((s.distance || 0) / maxDist * 100) : 0;
  57. const label = `${s.year}-${String(s.month).padStart(2, '0')}`;
  58. html += `<div class="stat-row">
  59. <span class="stat-label">${label}</span>
  60. <div class="stat-bar-wrap"><div class="stat-bar" style="width:${pct.toFixed(1)}%"></div></div>
  61. <span class="stat-value">${formatDistance(s.distance)}</span>
  62. </div>`;
  63. }
  64. html += '</div>';
  65. }
  66. // By week (last 52 weeks)
  67. if (stats.byWeek && stats.byWeek.length > 0) {
  68. const recent = stats.byWeek.slice(-52);
  69. const maxDist = Math.max(...recent.map(s => s.distance || 0));
  70. html += '<div class="stats-section"><h3>By Week (last 52)</h3>';
  71. for (const s of recent) {
  72. const pct = maxDist > 0 ? ((s.distance || 0) / maxDist * 100) : 0;
  73. const label = `${s.year}-W${String(s.week).padStart(2, '0')}`;
  74. html += `<div class="stat-row">
  75. <span class="stat-label">${label}</span>
  76. <div class="stat-bar-wrap"><div class="stat-bar" style="width:${pct.toFixed(1)}%"></div></div>
  77. <span class="stat-value">${formatDistance(s.distance)}</span>
  78. </div>`;
  79. }
  80. html += '</div>';
  81. }
  82. if (!html) {
  83. html = '<div class="empty-list">No tracks yet. Upload some GPX files to see stats.</div>';
  84. }
  85. content.innerHTML = html;
  86. }
  87. return { init, loadStats };
  88. })();