Work-Life Balance Tracker

Log your daily activities to visualize your time distribution.

Log Activity

Entries for

No entries for this day.

`; return; } entriesForDay.forEach(entry => { const item = document.createElement('div'); item.className = 'p-3 bg-white rounded-lg shadow-sm border border-gray-200 flex items-center justify-between'; item.innerHTML = `

${entry.activity} (${entry.hours} hrs)

${entry.category}

`; dailyLogList.appendChild(item); }); }; const renderReport = () => { const today = new Date(entryDateInput.value + 'T00:00:00'); const dayOfWeek = today.getDay(); const mondayOffset = (dayOfWeek === 0) ? -6 : 1 - dayOfWeek; const monday = new Date(today); monday.setDate(today.getDate() + mondayOffset); const sunday = new Date(monday); sunday.setDate(monday.getDate() + 6); reportWeekDates.textContent = `${formatDate(getIsoDate(monday))} - ${formatDate(getIsoDate(sunday))}`; const weekEntries = timeEntries.filter(e => { const entryDate = new Date(e.date + 'T00:00:00'); return entryDate >= monday && entryDate <= sunday; }); const categoryTotals = categories.reduce((acc, cat) => ({...acc, [cat]: 0}), {}); weekEntries.forEach(e => { categoryTotals[e.category] = (categoryTotals[e.category] || 0) + parseFloat(e.hours); }); // Render summary list categorySummary.innerHTML = ''; categories.forEach(cat => { const total = categoryTotals[cat] || 0; const li = document.createElement('li'); li.className = 'flex justify-between items-center text-sm'; li.innerHTML = ` ${cat} ${total.toFixed(1)} hrs `; categorySummary.appendChild(li); }); // Render Chart if (myChart) { myChart.destroy(); } myChart = new Chart(chartCanvas, { type: 'doughnut', data: { labels: categories, datasets: [{ data: categories.map(cat => categoryTotals[cat] || 0), backgroundColor: categories.map(cat => categoryColors[cat]), borderColor: '#ffffff', borderWidth: 2 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'bottom', labels: { padding: 20, boxWidth: 12 } } } } }); }; // --- FORM & DATA HANDLING --- const clearForm = () => { logForm.reset(); entryIdInput.value = ''; entryDateInput.value = getIsoDate(new Date()); }; const handleFormSubmit = (e) => { e.preventDefault(); const id = entryIdInput.value ? parseInt(entryIdInput.value) : Date.now(); const entryData = { id, date: entryDateInput.value, activity: activityNameInput.value, category: activityCategoryInput.value, hours: activityHoursInput.value }; const existingIndex = timeEntries.findIndex(entry => entry.id === id); if (existingIndex > -1) { timeEntries[existingIndex] = entryData; } else { timeEntries.push(entryData); } renderDailyLog(entryDateInput.value); clearForm(); }; const handleDailyLogClick = (e) => { const editBtn = e.target.closest('.edit-btn'); const deleteBtn = e.target.closest('.delete-btn'); if (editBtn) { const id = parseInt(editBtn.dataset.id); const entry = timeEntries.find(e => e.id === id); if (entry) { entryIdInput.value = entry.id; entryDateInput.value = entry.date; activityNameInput.value = entry.activity; activityCategoryInput.value = entry.category; activityHoursInput.value = entry.hours; } } else if (deleteBtn) { const id = parseInt(deleteBtn.dataset.id); timeEntries = timeEntries.filter(e => e.id !== id); renderDailyLog(entryDateInput.value); } }; // --- TAB NAVIGATION --- const switchTab = (tabName) => { const isLog = tabName === 'log'; tabLog.classList.toggle('active', isLog); tabLog.classList.toggle('inactive', !isLog); tabReport.classList.toggle('active', !isLog); tabReport.classList.toggle('inactive', isLog); contentLog.classList.toggle('hidden', !isLog); contentReport.classList.toggle('hidden', isLog); prevBtn.disabled = isLog; nextBtn.disabled = !isLog; if (!isLog) { renderReport(); } }; // --- PDF DOWNLOAD --- const handleDownloadPdf = () => { const { jsPDF } = window.jspdf; const doc = new jsPDF(); doc.setFontSize(18); doc.text("Work-Life Balance Weekly Report", 14, 22); doc.setFontSize(11); doc.setTextColor(100); doc.text(reportWeekDates.textContent, 14, 30); const chartImage = chartCanvas.toDataURL('image/png'); doc.addImage(chartImage, 'PNG', 15, 40, 90, 90); const summaryData = categories.map(cat => { const total = (timeEntries.filter(e => e.category === cat) .reduce((sum, e) => sum + parseFloat(e.hours), 0)).toFixed(1); return [cat, `${total} hrs`]; }); doc.autoTable({ startY: 40, startX: 115, head: [['Category', 'Total Hours']], body: summaryData, theme: 'grid', headStyles: { fillColor: [79, 70, 229] }, }); doc.save('Work-Life-Balance-Report.pdf'); }; // --- EVENT LISTENERS --- tabLog.addEventListener('click', () => switchTab('log')); tabReport.addEventListener('click', () => switchTab('report')); prevBtn.addEventListener('click', () => switchTab('log')); nextBtn.addEventListener('click', () => switchTab('report')); logForm.addEventListener('submit', handleFormSubmit); clearFormBtn.addEventListener('click', clearForm); entryDateInput.addEventListener('change', (e) => renderDailyLog(e.target.value)); dailyLogList.addEventListener('click', handleDailyLogClick); downloadPdfBtn.addEventListener('click', handleDownloadPdf); // --- INITIALIZATION --- const initializeTool = () => { const today = new Date(); const todayStr = getIsoDate(today); const yesterdayStr = getIsoDate(new Date(today.setDate(today.getDate() - 1))); timeEntries = [ { id: 1, date: todayStr, activity: 'Team Sync Meeting', category: 'Work', hours: 1 }, { id: 2, date: todayStr, activity: 'Coding new feature', category: 'Work', hours: 4.5 }, { id: 3, date: todayStr, activity: 'Lunch Break', category: 'Leisure & Hobbies', hours: 1 }, { id: 4, date: todayStr, activity: 'Gym Session', category: 'Health & Fitness', hours: 1.5 }, { id: 5, date: yesterdayStr, activity: 'Client Presentation Prep', category: 'Work', hours: 3 }, { id: 6, date: yesterdayStr, activity: 'Sleep', category: 'Sleep', hours: 8 }, { id: 7, date: yesterdayStr, activity: 'Grocery Shopping', category: 'Chores & Errands', hours: 1 }, ]; clearForm(); renderDailyLog(entryDateInput.value); switchTab('log'); }; initializeTool(); });
Scroll to Top