/** * @fileoverview Time tracking summary panel for the Day view. * * Collapsible section showing: * - Today's total tracked time * - This week's per-project breakdown (CSS bar chart, no library) */ (function() { 'use strict'; const esc = (s) => GoingsOn.utils.escapeHtml(s); /** * Format a minute count as a human-readable duration. * @param {number} mins - Minutes to format * @returns {string} Formatted string (e.g., "2h 15m", "45m", "3h") */ function formatMinutes(mins) { if (mins < 60) return `${mins}m`; const h = Math.floor(mins / 60); const m = mins % 60; return m > 0 ? `${h}h ${m}m` : `${h}h`; } // ============ Render ============ /** * Render the time tracking summary panel (today's total + weekly per-project bars). * @param {HTMLElement} container - DOM element to render into */ async function render(container) { if (!container) return; const now = new Date(); const todayStr = now.toISOString().slice(0, 10); // Week start (Monday) const day = now.getDay(); const diff = day === 0 ? 6 : day - 1; const weekStart = new Date(now); weekStart.setDate(now.getDate() - diff); weekStart.setHours(0, 0, 0, 0); const weekEnd = new Date(weekStart); weekEnd.setDate(weekStart.getDate() + 7); let summaries = []; try { summaries = await GoingsOn.api.timeTracking.getSummary( weekStart.toISOString(), weekEnd.toISOString() ); } catch (err) { console.error('Failed to load time summary:', err); return; } // Calculate today's total const todayTotal = summaries .filter(s => s.date === todayStr) .reduce((sum, s) => sum + s.totalMinutes, 0); // Aggregate by project for the week const projectMap = new Map(); for (const s of summaries) { const key = s.projectId || '__none__'; const existing = projectMap.get(key) || { name: s.projectName || 'No Project', minutes: 0, sessions: 0 }; existing.minutes += s.totalMinutes; existing.sessions += s.sessionCount; projectMap.set(key, existing); } const projects = Array.from(projectMap.values()).sort((a, b) => b.minutes - a.minutes); const maxMinutes = projects.length > 0 ? projects[0].minutes : 1; let html = `
Today ${esc(formatMinutes(todayTotal))}
`; if (projects.length > 0) { html += `
This Week
`; for (const p of projects) { const pct = Math.round((p.minutes / maxMinutes) * 100); html += `
${esc(p.name)} ${esc(formatMinutes(p.minutes))}
`; } } html += `
`; container.innerHTML = html; // Toggle collapse const toggle = container.querySelector('.time-summary-toggle'); const body = container.querySelector('.time-summary-body'); if (toggle && body) { toggle.addEventListener('click', () => { const expanded = toggle.getAttribute('aria-expanded') === 'true'; toggle.setAttribute('aria-expanded', String(!expanded)); body.classList.toggle('collapsed', expanded); toggle.querySelector('.time-summary-toggle-icon').textContent = expanded ? '\u25B6' : '\u25BC'; }); } } // ============ Namespace ============ GoingsOn.timeSummary = { render, formatMinutes, }; })();