Real-Time Work Hour Logger
Live Timer
Working on: Task
00:00:00
Quick Start Recent Tasks:
Log time for projects to see them here for quick starting.
Daily Log for Date
Manage Projects/Common Tasks
Generate Time Report
Report for Period
| Date | Project/Task | Description | Start | End | Duration | Notes |
|---|
Real-Time Work Hour Logging Guide
- 1. Define Your Projects (Projects Tab)
- Set up a list of common projects, clients, or general task types you work on. Assigning a color can help in visual identification.
- 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 specific task name.
- Click "Start New Timer". The timer will run, showing elapsed time for the active task. The browser tab title will also show the timer.
- Use "Pause" and "Resume" as needed. The timer accounts for paused duration.
- When finished, click "Stop & Log". A modal will appear allowing you to confirm/edit the auto-filled start/end times, task description (if ad-hoc or needs refinement), select a project if not already, and add notes.
- "Discard Timer" will stop the timer without creating a log entry.
- Select the date for which you want to log time using the "View/Manage Log for Date" picker.
- Click "Add Manual Entry".
- Fill in the task description, select a project (or leave for ad-hoc), set Start Date/Time, and End Date/Time. The duration will be auto-calculated. Add optional notes.
- 3. Recovering Interrupted Timers
- If you accidentally close your browser or refresh the page while a timer is running, the tool will try to detect this when you next open it. You'll be prompted to log the potentially elapsed time, adjust it, or discard it.
- 4. Viewing Reports (Reports Tab)
-
Select a period (Today, This Week, This Month, or a Custom Date Range) and click "Generate Report". You'll see:
- Total time logged for the period.
- A summary of time spent per project/task.
- A detailed chronological list of all individual time entries within that period.
- 5. Exporting Your Data
- Use the "Download Report as PDF" button to save your time analysis for records, billing, or sharing.
- Tips for Effective Time Logging:
-
- Log in Real-Time: Start the timer when you begin a task for maximum accuracy.
- Be Consistent: The more consistently you log, the more valuable your data.
- Use Projects/Categories: This helps in analyzing where your time is spent.
- Add Notes: Brief notes can provide context to your time entries later.
- Review Reports: Periodically check your reports to understand your work patterns and identify areas for improvement or better estimation.
×
Log Time Entry
${projectTagHTML}${entry.taskDescription}
${rtwlFormatTimeForDisplay(entry.startTimeISO)} - ${rtwlFormatTimeForDisplay(entry.endTimeISO)} (Duration: ${rtwlFormatDuration(entry.durationMinutes)})
${entry.notes ? `${entry.notes.replace(/\n/g, '
')}
Log time for projects to see them here for quick starting.
'; return; } recentTaskMap.forEach((taskInfo) => { const btn = document.createElement('button'); btn.className = 'rtwl-button rtwl-button-secondary'; btn.textContent = `Start: ${taskInfo.display.substring(0,25)}${taskInfo.display.length > 25 ? '...' : '' }`; btn.title = `Start timer for ${taskInfo.display}`; btn.onclick = () => { if (taskInfo.projectId) { rtwlTaskSelectTimerEl.value = taskInfo.projectId; if(rtwlAdhocTaskNameGroupEl) rtwlAdhocTaskNameGroupEl.style.display = 'none'; } else { rtwlTaskSelectTimerEl.value = 'adhoc'; if(rtwlAdhocTaskNameTimerEl) rtwlAdhocTaskNameTimerEl.value = taskInfo.taskDescription; // Use original taskDescription for adhoc if(rtwlAdhocTaskNameGroupEl) rtwlAdhocTaskNameGroupEl.style.display = 'block'; } rtwlStartNewTimer(); }; rtwlQuickStartButtonsEl.appendChild(btn); }); } // --- Reports --- window.rtwlGenerateReport = function() { /* ... same as stl, with rtwl prefixes ... */ if (!rtwlReportOutputAreaEl || !rtwlReportPeriodSelectEl || !rtwlReportPeriodDisplayEl || !rtwlReportSummaryEl || !rtwlReportDetailsTableBodyEl) return; const period = rtwlReportPeriodSelectEl.value; let startDateObj, endDateObj; // Use actual Date objects for filtering const todayForReport = new Date(2025, 4, 14); todayForReport.setHours(0,0,0,0); let periodDisplayString = ""; switch(period) { case 'today': startDateObj = new Date(todayForReport); endDateObj = new Date(todayForReport); endDateObj.setHours(23,59,59,999); periodDisplayString = "Today (" + todayForReport.toLocaleDateString() + ")"; break; case 'this_week': startDateObj = new Date(todayForReport); const dayOfWeek = startDateObj.getDay(); const diffToMonday = dayOfWeek === 0 ? -6 : 1 - dayOfWeek; startDateObj.setDate(startDateObj.getDate() + diffToMonday); endDateObj = new Date(startDateObj); endDateObj.setDate(startDateObj.getDate() + 6); endDateObj.setHours(23,59,59,999); periodDisplayString = `This Week (${startDateObj.toLocaleDateString()} - ${endDateObj.toLocaleDateString()})`; break; case 'this_month': startDateObj = new Date(todayForReport.getFullYear(), todayForReport.getMonth(), 1); endDateObj = new Date(todayForReport.getFullYear(), todayForReport.getMonth() + 1, 0); endDateObj.setHours(23,59,59,999); periodDisplayString = todayForReport.toLocaleDateString(undefined, {month: 'long', year: 'numeric'}); break; case 'custom_range': if (!rtwlReportStartDateInputEl.value || !rtwlReportEndDateInputEl.value) { alert("Please select a start and end date for custom range."); return; } startDateObj = new Date(rtwlReportStartDateInputEl.value + "T00:00:00"); endDateObj = new Date(rtwlReportEndDateInputEl.value + "T23:59:59"); if (startDateObj > endDateObj) { alert("End date must be after start date."); return; } periodDisplayString = `Custom (${startDateObj.toLocaleDateString()} - ${endDateObj.toLocaleDateString()})`; break; } if(rtwlReportPeriodDisplayEl) rtwlReportPeriodDisplayEl.textContent = periodDisplayString; const entriesInPeriod = rtwlTimeEntries.filter(entry => { const entryStart = new Date(entry.startTimeISO); return entryStart >= startDateObj && entryStart <= endDateObj; }); let totalLoggedMinutes = 0; const projectTime = {}; if(rtwlReportDetailsTableBodyEl) rtwlReportDetailsTableBodyEl.innerHTML = ''; entriesInPeriod.sort((a,b) => new Date(a.startTimeISO) - new Date(b.startTimeISO)).forEach(entry => { totalLoggedMinutes += entry.durationMinutes; const project = entry.projectId ? rtwlProjects.find(p => p.id === entry.projectId) : null; // For summary, group by project name if available, otherwise use taskDescription as key const summaryKey = project ? project.name : (entry.taskDescription || "Ad-hoc"); projectTime[summaryKey] = (projectTime[summaryKey] || 0) + entry.durationMinutes; if(rtwlReportDetailsTableBodyEl){ const row = rtwlReportDetailsTableBodyEl.insertRow(); row.insertCell().textContent = new Date(entry.startTimeISO).toLocaleDateString(); row.insertCell().textContent = project ? project.name : "Ad-hoc"; row.insertCell().textContent = entry.taskDescription; row.insertCell().textContent = rtwlFormatTimeForDisplay(entry.startTimeISO); row.insertCell().textContent = rtwlFormatTimeForDisplay(entry.endTimeISO); row.insertCell().textContent = rtwlFormatDuration(entry.durationMinutes); row.insertCell().textContent = entry.notes || ""; } }); let summaryHTML = `Total Time Logged: ${rtwlFormatDuration(totalLoggedMinutes)}
Time by Project/Task:
- `;
Object.keys(projectTime).sort((a,b) => projectTime[b] - projectTime[a]).forEach(projName => {
summaryHTML += `
- ${projName}: ${rtwlFormatDuration(projectTime[projName])} `; }); summaryHTML += `
