Task Progress Tracker

Total Tasks: 0 Tasks Done: 0 Avg. Progress: 0%
0%
Loading tasks...

${escapeHtml(task.notes)||'No notes.'}

`; itemDiv.querySelector('.task-status-select').addEventListener('change',(e)=>updateTaskField(task.id,'status',e.target.value)); itemDiv.querySelector('.task-progress-slider').addEventListener('input',handleProgressChange); itemDiv.querySelector('.delete-btn').addEventListener('click',(e)=>deleteTask(task.id)); itemDiv.querySelector('.edit-notes-btn').addEventListener('click',(e)=>toggleNotesEdit(itemDiv,task.id)); itemDiv.querySelector('.save-notes-btn').addEventListener('click',(e)=>saveNotesEdit(itemDiv,task.id)); itemDiv.querySelector('.cancel-notes-btn').addEventListener('click',(e)=>toggleNotesEdit(itemDiv,task.id,true)); taskListDiv.appendChild(itemDiv); }); updateSummary(); } // --- Update Handlers & Actions (Keep existing logic) --- function updateTaskField(id, field, value) { /* ... (kept as before) ... */ const i=tasks.findIndex(t=>t.id===id); if(i>-1){ if(field==='progress')value=Math.max(0,Math.min(100,Number(value)||0)); if(field==='status'&&!statuses.includes(value))return; tasks[i][field]=value; saveTasks(); if(field==='status'||field==='progress'||(currentSortBy==='name'&&field==='name')||(currentSortBy==='status'&&field==='status')||(currentSortBy==='progress'&&field==='progress')){ renderTaskList(); } else if(field==='notes'){ const itemDiv=document.querySelector(`.task-item[data-task-id="${id}"]`); if(itemDiv){ const notesP=itemDiv.querySelector('p.task-notes'); notesP.textContent=escapeHtml(value)||'No notes.'; notesP.classList.toggle('hidden',!value); }}} } function handleProgressChange(event) { /* ... (kept as before) ... */ const s=event.target; const id=s.dataset.id; const v=s.value; const vs=s.nextElementSibling; if(vs?.classList.contains('progress-value')){vs.textContent=`${v}%`;} const itemDiv=s.closest('.task-item'); if(itemDiv){itemDiv.querySelector('.progress-bar')?.style.setProperty('width',`${v}%`);} updateTaskField(id,'progress',v); } // --- Notes Editing (Keep existing logic) --- function toggleNotesEdit(itemDiv, taskId, cancel = false) { /* ... (kept as before) ... */ const nP=itemDiv.querySelector('p.task-notes'); const eA=itemDiv.querySelector('.notes-edit-area'); const ta=eA.querySelector('textarea'); const eB=itemDiv.querySelector('.edit-notes-btn'); if(!eA||!nP||!ta||!eB)return; const isV=eA.classList.contains('visible'); if(isV&&!cancel){toggleNotesEdit(itemDiv,taskId,true);}else if(!isV){const t=tasks.find(t=>t.id===taskId); ta.value=t?t.notes:''; eA.classList.add('visible'); nP.classList.add('hidden'); ta.focus(); eB.innerHTML='✖'; eB.title='Cancel Edit';} else {eA.classList.remove('visible'); nP.classList.remove('hidden'); eB.innerHTML='✎'; eB.title='Edit Notes';} } function saveNotesEdit(itemDiv, taskId) { /* ... (kept as before) ... */ const ta=itemDiv.querySelector('.notes-edit-area textarea'); if(!ta)return; const nn=ta.value; updateTaskField(taskId,'notes',nn); toggleNotesEdit(itemDiv,taskId,true); } // --- Other Actions (Keep existing logic) --- function addTask() { /* ... (kept as before) ... */ const name=newTaskNameInput.value.trim(); const status=newTaskStatusSelect.value; const progress=parseInt(newTaskProgressInput.value,10); const notes=newTaskNotesTextarea.value; if(name===''){alert("Task name empty.");return;} const newTask={id:`task-${Date.now()}-${Math.random().toString(36).substr(2,5)}`,name,status,progress,notes,dateAdded:Date.now()}; tasks.push(newTask); saveTasks(); renderTaskList(); newTaskNameInput.value=''; newTaskStatusSelect.value=defaultStatus; newTaskProgressInput.value=0; newTaskProgressValue.textContent='0%'; newTaskNotesTextarea.value=''; newTaskNameInput.focus(); } function deleteTask(id) { /* ... (kept as before) ... */ const ttd=tasks.find(t=>t.id===id); if(!ttd)return; if(confirm(`Delete task "${ttd.name}"?`)){tasks=tasks.filter(t=>t.id!==id); saveTasks(); renderTaskList();} } function escapeHtml(unsafe) { /* ... (kept as before) ... */ if(!unsafe) return ''; return unsafe.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'"); } // --- Event Listeners --- addTaskBtn.addEventListener('click', addTask); newTaskNameInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') addTaskBtn.click(); }); newTaskProgressInput.addEventListener('input', (e) => { newTaskProgressValue.textContent = `${e.target.value}%`; }); filterStatusSelect.addEventListener('change', (e) => { currentStatusFilter = e.target.value; renderTaskList(); }); sortBySelect.addEventListener('change', (e) => { currentSortBy = e.target.value; renderTaskList(); }); sortOrderBtn.addEventListener('click', () => { currentSortOrder = currentSortOrder === 'asc' ? 'desc' : 'asc'; sortOrderBtn.innerHTML = currentSortOrder === 'asc' ? '↑' : '↓'; sortOrderBtn.title = `Sort Order (${currentSortOrder === 'asc' ? 'Ascending' : 'Descending'})`; renderTaskList(); }); // --- PDF Download (Robust Table Export - WITH EXTRA LOGGING AROUND SAVE) --- // Ensure the button exists before adding listener if (downloadPdfBtn) { downloadPdfBtn.addEventListener('click', () => { console.log("Progress PDF: Download button clicked."); // Log 1: Click detected try { // Lib Checks, PDF Init (Keep robust checks) console.log("Progress PDF: Checking libs..."); let jsPDF; if (!window.jspdf?.jsPDF) throw new Error("jsPDF lib missing."); jsPDF = window.jspdf.jsPDF; try { new jsPDF().autoTable; } catch (e) { throw new Error("jsPDF AutoTable missing."); } console.log("Progress PDF: Libs OK."); let doc; try { doc = new jsPDF(); } catch (e) { throw new Error(`PDF init error: ${e.message}`); } console.log("Progress PDF: Instance created."); // Prepare Data (Keep robust logic) console.log(`Progress PDF: Preparing data. Filters: S='${currentStatusFilter}', Sort: ${currentSortBy} ${currentSortOrder}`); const filteredTasks = tasks.filter(task => currentStatusFilter === 'all' || task.status === currentStatusFilter); const tasksForPdf = [...filteredTasks].sort((a, b) => { let ca,cb; switch(currentSortBy){ case 'name': ca=a.name.toLowerCase(); cb=b.name.toLowerCase(); break; case 'status': ca=statusOrder[a.status]||0; cb=statusOrder[b.status]||0; break; case 'progress': ca=a.progress; cb=b.progress; break; default: ca=a.dateAdded; cb=b.dateAdded; break; } let comp=(ca>cb)?1:((ca t.status === 'Done').length; const avgProgress = total > 0 ? Math.round(tasks.reduce((sum, t) => sum + t.progress, 0) / total) : 0; console.log(`Progress PDF: Data ready. Tasks: ${tasksForPdf.length}, Total: ${total}, Done: ${done}, Avg: ${avgProgress}%`); // Styles (Keep robust logic) console.log("Progress PDF: Retrieving styles..."); let styles = { primary: '#0dcaf0', secondary: '#6c757d', textDark: '#343a40', textLight: '#ffffff', done: '#198754' }; try { const s = getComputedStyle(container || document.documentElement); styles.primary=s.getPropertyValue('--tpt-primary-color').trim()||styles.primary; styles.secondary=s.getPropertyValue('--tpt-secondary-color').trim()||styles.secondary; styles.textDark=s.getPropertyValue('--tpt-text-dark').trim()||styles.textDark; styles.textLight=s.getPropertyValue('--tpt-text-light').trim()||styles.textLight; styles.done=s.getPropertyValue('--tpt-status-done').trim()||styles.done; } catch(e) { console.warn("Progress PDF: Using default colors.", e); } console.log("Progress PDF: Styles retrieved.", styles); // Generate PDF Content (Keep robust logic) console.log("Progress PDF: Generating content..."); let startY = 15; doc.setFontSize(18); doc.setTextColor(styles.textDark); doc.text("Task Progress Report", 14, startY); startY += 10; doc.setFontSize(10); doc.setTextColor(styles.secondary); doc.text(`Filters Applied: Status = ${currentStatusFilter === 'all' ? 'All' : currentStatusFilter}`, 14, startY); startY += 5; doc.text(`Sort Order: ${capitalize(currentSortBy)} (${currentSortOrder === 'asc' ? 'Asc' : 'Desc'})`, 14, startY); startY += 7; doc.setFontSize(11); doc.setTextColor(styles.textDark); doc.text(`Summary: ${total} Total Tasks | ${done} Done | ${avgProgress}% Average Progress`, 14, startY); startY += 10; if (tasksForPdf.length > 0) { const tableBody = tasksForPdf.map(task => ([ task.name, task.status, `${task.progress}%`, task.notes || '' ])); doc.autoTable({ startY: startY, head: [['Task Name', 'Status', 'Progress', 'Notes']], body: tableBody, theme: 'grid', headStyles: { fillColor: styles.secondary, textColor: styles.textLight, fontStyle: 'bold' }, styles: { fontSize: 9, cellPadding: 2, lineColor: '#ccc', lineWidth: 0.1, textColor: styles.textDark }, columnStyles: { 0: { cellWidth: 'auto' }, 1: { cellWidth: 30, halign: 'center' }, 2: { cellWidth: 20, halign: 'right' }, 3: { cellWidth: 'auto' } }, didParseCell: function (data) { if (data.column.index === 1 && data.cell.raw === 'Done') { data.cell.styles.textColor = styles.done; data.cell.styles.fontStyle = 'bold'; } }, didDrawPage: (data) => { startY = data.cursor.y + 10; }, pageBreak: 'auto', marginBottom: 20, }); console.log("Progress PDF: Task table generated."); } else { doc.setFontSize(10); doc.setTextColor(styles.secondary); doc.text("No tasks match the current filters.", 14, startY); } const pageCount = doc.internal.getNumberOfPages(); doc.setPage(pageCount); const pageHeight = doc.internal.pageSize.height || doc.internal.pageSize.getHeight(); doc.setFontSize(8); doc.setTextColor(styles.secondary); doc.text(`Generated: ${new Date().toLocaleString()}`, 14, pageHeight - 10); console.log("Progress PDF: Content generation complete."); // 6. Save PDF const filename = `task-progress-report.pdf`; console.log(`Progress PDF: Attempting to save as '${filename}'...`); // Log 2: Before save doc.save(filename); console.log("Progress PDF: doc.save() command executed."); // Log 3: After save (might not show if save fails internally) } catch (error) { console.error("Progress PDF: Generation failed.", error); alert(`Failed to generate PDF: ${error.message}\n\nPlease check the browser console for more details.`); } }); // End PDF Button Handler } else { console.error("PDF Download button element not found!"); } // --- Initial Load --- populateSelects(); loadTasks(); renderTaskList(); console.log("Online Task Progress Tracker Initialized."); }); // Utility to capitalize string function capitalize(str) { return str ? str.charAt(0).toUpperCase() + str.slice(1) : ''; }

In today’s dynamic environment, managing tasks effectively is only half the battle; knowing where you stand with each task, understanding your overall progress, and recognizing when adjustments are needed is equally, if not more, important. Whether you’re working on personal goals, academic projects, or professional assignments, seeing your progress unfold can be a huge motivator and a crucial indicator of success. This is where a dedicated online task progress tracker becomes an invaluable asset, transforming your to-do lists into dynamic roadmaps of achievement.

Our Online Task Progress Tracker at WorkToolz.com is designed to provide you with clear visibility into your work, helping you stay motivated and on schedule. We believe that tracking your progress should be straightforward and insightful, without requiring complex software or a steep learning curve. Our tool offers a clean, user-friendly interface that allows you to easily monitor the status and completion percentage of all your tasks. There’s no need for downloads, installations, or complicated setups. Simply open your web browser, and you can begin tracking your tasks instantly.

The core strength of our online task progress tracker lies in its intuitive design and powerful tracking features. When adding a new task, you can enter a clear “Task Name” to describe the work. Crucially, the tool allows you to set the “Status” of each task, with options like “Not Started,” “In Progress,” and “Completed.” This immediately provides a snapshot of where each task stands in its lifecycle. But what truly sets this tool apart is the interactive “Progress” slider. This innovative feature lets you manually adjust the completion percentage of each task from 0% to 100%. This visual progress bar offers an immediate and satisfying sense of accomplishment as you work, helping you stay engaged and focused on reaching your goals.

To further enhance your tracking capabilities, our tool includes a “Notes (Optional)” section for each task. This allows you to add any relevant details, reminders, roadblocks encountered, or key insights related to that specific task. These notes serve as a valuable log, helping you remember important context, document decisions, and even review your approach later. This comprehensive approach ensures that you’re not just tracking progress, but also capturing the journey and any critical information along the way.

At the top of the dashboard, you’ll find a summary of your overall progress, displaying “Total Tasks,” “Tasks Done,” and “Avg. Progress %.” These high-level metrics provide an instant overview of your workload and how far you’ve come, helping you gauge your productivity and identify areas that might need more attention. To help manage your growing list of tasks, the tool also includes filtering options by “Status” and sorting options by “Date Added,” allowing you to quickly organize and focus on what’s most relevant at any given moment.

For record-keeping, sharing, or offline review, our Online Task Progress Tracker includes a convenient “Download PDF” feature. This allows you to generate a clear, printable document of your entire task list, including the task names, their current status, progress percentages, and any associated notes. This is incredibly useful for personal accountability, sharing updates with a team or mentor, or simply maintaining an organized record of your accomplishments.

Utilizing an online task progress tracker like ours offers significant benefits. It helps maintain motivation by visualizing progress, provides clear insights into workload and completion rates, and assists in identifying potential delays or roadblocks early on. By offering a transparent and organized way to monitor your work, our free online Task Progress Tracker at WorkToolz.com empowers you to stay on top of your responsibilities, achieve your objectives efficiently, and experience the satisfaction of seeing your efforts turn into tangible results.

Scroll to Top