Work & Leisure Time Tracker

Work & Leisure Time Tracker

Track Your Time

Start New Activity

Live Session Summary (for today):

Total Work Time: 0h 0m

Total Leisure Time: 0h 0m

Work/Leisure Balance:

Work 0%
Leisure 0%

Daily Summary & Report

×

Log Past Activity

OR

`; summaryHTML += `

Total Leisure Time: ${wlttFormatDuration(totalLeisureMins)}

`; summaryHTML += `

Total Logged: ${wlttFormatDuration(totalLoggedMins)}

`; summaryHTML += `
${workPercent > 10 ? 'Work ' + workPercent.toFixed(1)+'%' : ''}
${leisurePercent > 10 ? 'Leisure ' + leisurePercent.toFixed(1)+'%' : ''}
`; summaryHTML += `

Time by Category:

    `; for (const category in categoryTimes) { const catData = categoryTimes[category]; summaryHTML += `
  • ${category}: ${wlttFormatDuration(catData.total)} (${catData.work > 0 ? `W: ${wlttFormatDuration(catData.work)}` : ''} ${catData.leisure > 0 ? (catData.work > 0 ? ', ': '') + `L: ${wlttFormatDuration(catData.leisure)}` : ''})
  • `; } summaryHTML += `
`; summaryHTML += `

Detailed Activity Log:

    `; activitiesForDate.sort((a,b) => new Date(a.startTime) - new Date(b.startTime)).forEach(act => { const start = new Date(act.startTime).toLocaleTimeString([],{hour:'2-digit',minute:'2-digit'}); const end = new Date(act.endTime).toLocaleTimeString([],{hour:'2-digit',minute:'2-digit'}); summaryHTML += `
  • ${act.name} (${act.category} - ${act.type})
    ${start} - ${end} (${wlttFormatDuration(act.durationMinutes)})
  • `; }); summaryHTML += `
`; reportArea.innerHTML = summaryHTML; reportArea.style.display = 'block'; reflectionArea.style.display = 'block'; if(pdfBtn) pdfBtn.style.display = 'inline-block'; } function wlttDownloadPDF() { if (!jsPDF_constructor_wltt) { alert("PDF library error. Cannot generate PDF."); return; } const activitiesForDate = wlttTimeTrackerData.activities.filter(act => new Date(act.startTime).toISOString().split('T')[0] === wlttTimeTrackerData.trackingDate ); if (activitiesForDate.length === 0 && !document.getElementById('wltt-daily-reflection').value.trim()) { alert("No data to download. Please log activities or add a reflection."); return; } wlttTimeTrackerData.dailyReflection = document.getElementById('wltt-daily-reflection').value.trim(); const doc = new jsPDF_constructor_wltt('p', 'pt', 'a4'); const FONT_FAMILY = "helvetica"; const ACCENT_COLOR_HEX = getComputedStyle(document.documentElement).getPropertyValue('--accent-color').trim(); const PRIMARY_TEXT_COLOR_HEX = getComputedStyle(document.documentElement).getPropertyValue('--primary-text').trim(); const SECTION_TITLE_COLOR_HEX = getComputedStyle(document.documentElement).getPropertyValue('--section-title-color').trim(); const BUTTON_TEXT_COLOR_HEX = getComputedStyle(document.documentElement).getPropertyValue('--button-text-color').trim(); const WORK_PDF_COLOR = getComputedStyle(document.documentElement).getPropertyValue('--work-color').trim(); const LEISURE_PDF_COLOR = getComputedStyle(document.documentElement).getPropertyValue('--leisure-color').trim(); let yPos = 40; const pageWidth = doc.internal.pageSize.getWidth(); const margin = 40; const usableWidth = pageWidth - (2 * margin); doc.setFillColor(ACCENT_COLOR_HEX); doc.rect(0, 0, pageWidth, yPos + 10, 'F'); doc.setFont(FONT_FAMILY, "bold"); doc.setFontSize(16); doc.setTextColor(BUTTON_TEXT_COLOR_HEX); doc.text("Daily Time Log & Work-Leisure Balance", pageWidth / 2, yPos, { align: "center" }); yPos += 25; doc.setFont(FONT_FAMILY, "normal"); doc.setFontSize(10); doc.setTextColor(PRIMARY_TEXT_COLOR_HEX); const loggedDateFormatted = new Date(wlttTimeTrackerData.trackingDate+'T00:00:00').toLocaleDateString(undefined, { weekday:'long', year: 'numeric', month: 'long', day: 'numeric' }); doc.text(`Log for Date: ${loggedDateFormatted}`, margin, yPos); const genDate = new Date(2025, 4, 18).toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' }); // Context date doc.text(`Report Generated: ${genDate}`, pageWidth - margin, yPos, { align: 'right'}); yPos += 25; let totalWorkMins = 0, totalLeisureMins = 0; let categoryTimes = {}; activitiesForDate.forEach(act => { if (act.type === "Work") totalWorkMins += act.durationMinutes; else totalLeisureMins += act.durationMinutes; if (!categoryTimes[act.category]) categoryTimes[act.category] = { work: 0, leisure: 0, total: 0 }; categoryTimes[act.category].total += act.durationMinutes; if (act.type === "Work") categoryTimes[act.category].work += act.durationMinutes; else categoryTimes[act.category].leisure += act.durationMinutes; }); const totalLoggedMins = totalWorkMins + totalLeisureMins; const workPercent = totalLoggedMins > 0 ? (totalWorkMins / totalLoggedMins * 100) : 0; const leisurePercent = totalLoggedMins > 0 ? (totalLeisureMins / totalLoggedMins * 100) : 0; doc.setFont(FONT_FAMILY, "bold"); doc.setFontSize(12); doc.setTextColor(SECTION_TITLE_COLOR_HEX); doc.text("Overall Summary:", margin, yPos); yPos += 18; doc.setFont(FONT_FAMILY, "normal"); doc.setFontSize(10); doc.text(`Total Work Time: ${wlttFormatDuration(totalWorkMins)} (${workPercent.toFixed(1)}%)`, margin, yPos); yPos += 14; doc.text(`Total Leisure Time: ${wlttFormatDuration(totalLeisureMins)} (${leisurePercent.toFixed(1)}%)`, margin, yPos); yPos += 14; doc.text(`Total Logged Time: ${wlttFormatDuration(totalLoggedMins)}`, margin, yPos); yPos += 20; // Time by Category if (yPos > doc.internal.pageSize.getHeight() - 80) { doc.addPage(); yPos = margin; } doc.setFont(FONT_FAMILY, "bold"); doc.setFontSize(12); doc.setTextColor(SECTION_TITLE_COLOR_HEX); doc.text("Time Allocation by Category:", margin, yPos); yPos += 18; doc.setFont(FONT_FAMILY, "normal"); doc.setFontSize(9); if (Object.keys(categoryTimes).length > 0) { for (const category in categoryTimes) { if (yPos > doc.internal.pageSize.getHeight() - 25) { doc.addPage(); yPos = margin; } const catData = categoryTimes[category]; let typeBreakdown = ""; if (catData.work > 0) typeBreakdown += `Work: ${wlttFormatDuration(catData.work)}`; if (catData.leisure > 0) typeBreakdown += `${catData.work > 0 ? ', ' : ''}Leisure: ${wlttFormatDuration(catData.leisure)}`; doc.text(`• ${category}: ${wlttFormatDuration(catData.total)} ${typeBreakdown ? `(${typeBreakdown})` : ''}`, margin + 10, yPos); yPos += 12; } } else { doc.text("No categories logged.", margin + 10, yPos); yPos += 12; } yPos += 10; // Detailed Activity Log if (yPos > doc.internal.pageSize.getHeight() - 60) { doc.addPage(); yPos = margin; } doc.setFont(FONT_FAMILY, "bold"); doc.setFontSize(12); doc.setTextColor(SECTION_TITLE_COLOR_HEX); doc.text("Detailed Activity Log:", margin, yPos); yPos += 5; if (jsPDF_autoTable_plugin_wltt && activitiesForDate.length > 0) { const activityTableBody = activitiesForDate.sort((a,b) => new Date(a.startTime) - new Date(b.startTime)).map(act => [ act.name, act.category, act.type, new Date(act.startTime).toLocaleTimeString([],{hour:'2-digit',minute:'2-digit'}), new Date(act.endTime).toLocaleTimeString([],{hour:'2-digit',minute:'2-digit'}), wlttFormatDuration(act.durationMinutes) ]); doc.autoTable({ startY: yPos, head: [['Activity', 'Category', 'Type', 'Start', 'End', 'Duration']], body: activityTableBody, theme: 'grid', margin: {left: margin, right: margin}, headStyles: { fillColor: ACCENT_COLOR_HEX, textColor: BUTTON_TEXT_COLOR_HEX, fontSize: 8, fontStyle:'bold'}, styles: { fontSize: 8, cellPadding: 2, overflow: 'linebreak' }, didParseCell: function(data) { // Color Type column if (data.section === 'body' && data.column.index === 2) { // Type column if (data.cell.raw === "Work") data.cell.styles.textColor = WORK_PDF_COLOR; else if (data.cell.raw === "Leisure") data.cell.styles.textColor = LEISURE_PDF_COLOR; data.cell.styles.fontStyle = 'bold'; } } }); yPos = doc.lastAutoTable.finalY + 15; } else if (activitiesForDate.length > 0) { // Fallback if no autoTable doc.setFont(FONT_FAMILY, "normal"); doc.setFontSize(8); activitiesForDate.sort((a,b) => new Date(a.startTime) - new Date(b.startTime)).forEach(act => { if (yPos > doc.internal.pageSize.getHeight() - 20) { doc.addPage(); yPos = margin; } const start = new Date(act.startTime).toLocaleTimeString([],{hour:'2-digit',minute:'2-digit'}); const end = new Date(act.endTime).toLocaleTimeString([],{hour:'2-digit',minute:'2-digit'}); doc.text(`• ${act.name} (${act.category}/${act.type}) [${start}-${end}] - ${wlttFormatDuration(act.durationMinutes)}`, margin, yPos); yPos += 10; }); yPos += 5; } else { doc.setFont(FONT_FAMILY, "normal"); doc.setFontSize(9); doc.text("No activities logged for this day.", margin, yPos); yPos += 15; } if (wlttTimeTrackerData.dailyReflection) { if (yPos > doc.internal.pageSize.getHeight() - 50) { doc.addPage(); yPos = margin; } doc.setFont(FONT_FAMILY, "bold"); doc.setFontSize(12); doc.setTextColor(SECTION_TITLE_COLOR_HEX); doc.text("Reflection on Time Allocation:", margin, yPos); yPos += 18; doc.setFont(FONT_FAMILY, "normal"); doc.setFontSize(10); const reflectionLines = doc.splitTextToSize(wlttTimeTrackerData.dailyReflection, usableWidth); doc.text(reflectionLines, margin, yPos); yPos += (reflectionLines.length * 12) + 10; } // Footer const pageCount = doc.internal.getNumberOfPages(); for (let i = 1; i <= pageCount; i++) { doc.setPage(i); const pageHeight = doc.internal.pageSize.getHeight(); doc.setLineWidth(0.5); doc.setDrawColor(ACCENT_COLOR_HEX); doc.line(margin, pageHeight - 30, pageWidth - margin, pageHeight - 30); doc.setFont(FONT_FAMILY, "normal"); doc.setFontSize(8); doc.setTextColor(PRIMARY_TEXT_COLOR_HEX); doc.text(`Page ${i} of ${pageCount}`, pageWidth / 2, pageHeight - 20, { align: "center" }); } doc.save(`Time_Log_Report_${wlttTimeTrackerData.trackingDate}.pdf`); }
Scroll to Top