Smart Time Logger

Smart Time Logger

Live Timer


Daily Log for Date

Manage Projects/Common Tasks

Generate Time Report

Smart Time Logging Guide

1. Define Your Projects/Tasks (Projects Tab)
Set up a list of common projects or task types you work on. Assigning a color can help in visual identification in future reports (though not heavily used in this version's display).
2. Logging Time (Timer & Daily Log Tab)
Using the Live Timer:
  • Select a project from the dropdown or choose "Log Ad-hoc Task" and enter a name.
  • Click "Start Timer". The timer will run, showing elapsed time for the active task.
  • Use "Pause" and "Resume" as needed.
  • When finished, click "Stop & Log". You'll be prompted to add notes before the entry is saved.
  • "Discard" will stop the timer without saving an entry.
Manual Entry:
  • Select the date for which you want to log time.
  • Click "Add Manual Entry".
  • Fill in the task description, select a project (or enter ad-hoc), set Start Time, End Time (or Duration), and add optional notes.
3. Recovering Interrupted Timers
If you close your browser or navigate away while a timer is running, the tool will attempt to detect this when you next open it. You'll be prompted to log, adjust, or discard the time that seems to have elapsed.
4. Viewing Reports (Reports Tab)
Select a period (Today, This Week, This Month, or a Custom Range) and click "Generate Report". You'll see:
  • Total time logged for the period.
  • A summary of time spent per project/task.
  • A detailed list of all individual time entries.
5. Exporting
Use the "Download Report as PDF" button (available after a report is generated) to save your time analysis.
Tips for Effective Time Logging:
  • Be Consistent: Log time as you work or immediately after to ensure accuracy.
  • Be Granular (Enough): Log distinct tasks separately if you need to analyze them. Broad categories are fine if you don't need deep detail.
  • Use Notes: Add brief notes to entries to remember specific details about the work done.
  • Review Regularly: Use the reports to understand where your time goes. This can help identify inefficiencies or areas where you're spending more or less time than intended.

Select a date to view log entries.

'; stlCurrentDateDisplayEl.textContent = 'N/A'; return; } stlCurrentDateDisplayEl.textContent = new Date(selectedDateKey + "T00:00:00").toLocaleDateString(); const entriesForDay = stlTimeEntries.filter(entry => stlFormatDateKey(new Date(entry.startTimeISO)) === selectedDateKey ).sort((a,b) => new Date(a.startTimeISO) - new Date(b.startTimeISO)); // Sort by start time stlDailyLogEntriesListEl.innerHTML = ''; if (entriesForDay.length === 0) { stlDailyLogEntriesListEl.innerHTML = '

No time logged for this day.

'; return; } entriesForDay.forEach(entry => { const itemEl = document.createElement('div'); itemEl.className = 'stl-entry-item'; const project = entry.projectId ? stlProjects.find(p => p.id === entry.projectId) : null; const projectTag = project ? `${project.name}` : ''; itemEl.innerHTML = `

${projectTag}${entry.taskDescription}

${stlFormatTimeForDisplay(entry.startTimeISO)} - ${stlFormatTimeForDisplay(entry.endTimeISO)} (Duration: ${stlFormatDuration(entry.durationMinutes)})

${entry.notes ? `

Notes: ${entry.notes}

` : ''}
`; stlDailyLogEntriesListEl.appendChild(itemEl); }); } // --- Reports --- // (To be filled: stlGenerateReport, handlePdfDownload for reports) // ... Placeholder ... window.stlGenerateReport = function() { if (!stlReportOutputAreaEl || !stlReportPeriodSelectEl || !stlReportPeriodDisplayEl || !stlReportSummaryEl || !stlReportDetailsTableBodyEl) return; const period = stlReportPeriodSelectEl.value; let startDate, endDate; const today = new Date(2025, 4, 14); // May 14, 2025 today.setHours(0,0,0,0); switch(period) { case 'today': startDate = new Date(today); endDate = new Date(today); endDate.setHours(23,59,59,999); // End of today stlReportPeriodDisplayEl.textContent = "Today (" + today.toLocaleDateString() + ")"; break; case 'this_week': startDate = new Date(today); const dayOfWeek = startDate.getDay(); // 0=Sun, 1=Mon,... const diffToMonday = dayOfWeek === 0 ? -6 : 1 - dayOfWeek; startDate.setDate(startDate.getDate() + diffToMonday); endDate = new Date(startDate); endDate.setDate(startDate.getDate() + 6); endDate.setHours(23,59,59,999); stlReportPeriodDisplayEl.textContent = `This Week (${startDate.toLocaleDateString()} - ${endDate.toLocaleDateString()})`; break; case 'this_month': startDate = new Date(today.getFullYear(), today.getMonth(), 1); endDate = new Date(today.getFullYear(), today.getMonth() + 1, 0); endDate.setHours(23,59,59,999); stlReportPeriodDisplayEl.textContent = today.toLocaleDateString(undefined, {month: 'long', year: 'numeric'}); break; case 'custom_range': if (!stlReportStartDateInputEl.value || !stlReportEndDateInputEl.value) { alert("Please select a start and end date for custom range."); return; } startDate = new Date(stlReportStartDateInputEl.value + "T00:00:00"); endDate = new Date(stlReportEndDateInputEl.value + "T23:59:59"); if (startDate > endDate) { alert("End date must be after start date."); return; } stlReportPeriodDisplayEl.textContent = `Custom (${startDate.toLocaleDateString()} - ${endDate.toLocaleDateString()})`; break; } const entriesInPeriod = stlTimeEntries.filter(entry => { const entryStart = new Date(entry.startTimeISO); return entryStart >= startDate && entryStart <= endDate; }); let totalLoggedMinutes = 0; const projectTime = {}; // { projectId: minutes, ... } stlReportDetailsTableBodyEl.innerHTML = ''; entriesInPeriod.sort((a,b) => new Date(a.startTimeISO) - new Date(b.startTimeISO)).forEach(entry => { totalLoggedMinutes += entry.durationMinutes; const project = entry.projectId ? stlProjects.find(p => p.id === entry.projectId) : null; const projectName = project ? project.name : (entry.taskDescription || "Ad-hoc"); if (projectName) { // Ensure we have a name to group by projectTime[projectName] = (projectTime[projectName] || 0) + entry.durationMinutes; } const row = stlReportDetailsTableBodyEl.insertRow(); row.insertCell().textContent = new Date(entry.startTimeISO).toLocaleDateString(); row.insertCell().textContent = projectName; row.insertCell().textContent = (project && entry.taskDescription !== projectName) ? entry.taskDescription : "-"; // Show specific desc if different from project row.insertCell().textContent = stlFormatTimeForDisplay(entry.startTimeISO); row.insertCell().textContent = stlFormatTimeForDisplay(entry.endTimeISO); row.insertCell().textContent = stlFormatDuration(entry.durationMinutes); row.insertCell().textContent = entry.notes || ""; }); let summaryHTML = `

Total Time Logged: ${stlFormatDuration(totalLoggedMinutes)}

Time by Project/Task:

    `; Object.keys(projectTime).sort((a,b) => projectTime[b] - projectTime[a]).forEach(projName => { summaryHTML += `
  • ${projName}: ${stlFormatDuration(projectTime[projName])}
  • `; }); summaryHTML += `
`; stlReportSummaryEl.innerHTML = summaryHTML; stlReportOutputAreaEl.style.display = 'block'; if(stlDownloadPdfBtn) stlDownloadPdfBtn.style.display = entriesInPeriod.length > 0 ? 'block' : 'none'; } function handlePdfDownload() { /* PDF generation for reports */ const periodText = stlReportPeriodDisplayEl.textContent; const totalTimeText = stlReportSummaryEl.querySelector('p strong')?.parentElement.textContent || "Total Time Logged: N/A"; const projectSummary = []; const projectLis = stlReportSummaryEl.querySelectorAll('ul li'); projectLis.forEach(li => projectSummary.push(li.textContent)); const detailRows = []; const detailTableRows = stlReportDetailsTableBodyEl.querySelectorAll('tr'); detailTableRows.forEach(row => { const cells = Array.from(row.cells).map(cell => cell.textContent); detailRows.push(cells); }); if(detailRows.length === 0 && !totalTimeText.includes("N/A")) { alert("No detailed data to export for the selected report."); // return; // Allow export if summary exists } const { jsPDF } = window.jspdf; const doc = new jsPDF(); doc.setFontSize(18); doc.setTextColor(getComputedStyle(document.documentElement).getPropertyValue('--stl-primary-color').trim()); doc.text("Time Log Report", 14, 22); doc.setFontSize(12); doc.setTextColor(100); doc.text(`Period: ${periodText}`, 14, 30); doc.text(totalTimeText, 14, 38); let yPos = 46; if (projectSummary.length > 0) { doc.setFontSize(11); doc.setTextColor(getComputedStyle(document.documentElement).getPropertyValue('--stl-text-color').trim()); doc.text("Summary by Project/Task:", 14, yPos); yPos += 7; doc.setFontSize(9); projectSummary.forEach(item => { if (yPos > 270) { doc.addPage(); yPos = 20; } doc.text(`\u2022 ${item}`, 16, yPos); yPos += 5; }); yPos += 5; } if (detailRows.length > 0) { if (yPos > 250) { doc.addPage(); yPos = 20; } doc.setFontSize(11); doc.setTextColor(getComputedStyle(document.documentElement).getPropertyValue('--stl-text-color').trim()); doc.text("Detailed Log:", 14, yPos); yPos += 7; doc.autoTable({ head: [['Date', 'Project/Task', 'Description', 'Start', 'End', 'Duration', 'Notes']], body: detailRows, startY: yPos, theme: 'striped', headStyles: { fillColor: getComputedStyle(document.documentElement).getPropertyValue('--stl-primary-color').trim() }, styles: { fontSize: 8, cellPadding: 1.5, overflow: 'linebreak' }, columnStyles: { 1: { cellWidth: 40 }, // Project/Task 2: { cellWidth: 50 }, // Description 6: { cellWidth: 'auto'} // Notes } }); } doc.save(`TimeLogReport_${periodText.replace(/[^a-zA-Z0-9]/g, '_')}.pdf`); } // --- Close Modal --- window.stlCloseModal = (modalId) => { const modal = document.getElementById(modalId); if(modal) modal.style.display = 'none'; } // --- Initialization (within DOMContentLoaded) --- // (All DOM element assignments and event listener attachments are now inside DOMContentLoaded)
Scroll to Top