Automated Task Review System

Setup Tasks and Checklists

Task Overview

Task TitleDue DateAssigned ToStatusChecklist %Actions
No main tasks defined yet.

Track Progress & Review Tasks

No tasks available for tracking. Add tasks on Tab 1.
×

Checklist for Task

    Checklist: ${atrsCalculateChecklistCompletion(task.checklist)}% complete

    `; atrsTaskTrackingAreaEl.appendChild(taskDiv); }); } function atrsUpdateTaskStatus(taskId, newStatus) { const task = atrsTasksData.find(t => t.id === taskId); if (task) { task.status = newStatus; atrsSaveDataToStorage(); atrsRenderMainTaskList(); const taskDivInTracking = Array.from(document.getElementById('atrsTaskTrackingArea').children).find(div => div.querySelector('h4')?.textContent.includes(`(ID: ${taskId})`)); if(taskDivInTracking) { taskDivInTracking.className = 'atrs-review-item'; // Reset classes taskDivInTracking.classList.add(`status-${newStatus.toLowerCase().replace(/\s+/g, '')}`); } // Clear previous system notes when status is manually changed by user const reviewNoteElTrack = document.getElementById(`reviewNoteTrack-${taskId}`); if(reviewNoteElTrack) reviewNoteElTrack.textContent = ''; } } function atrsPerformGlobalReview() { if(!atrsReviewResultItemsEl || !atrsNoReviewIssuesMessageEl || !atrsReviewResultsDisplayAreaEl || !atrsDownloadReportBtnEl) return; atrsReviewResultItemsEl.innerHTML = ''; let issuesFound = 0; const today = new Date(); today.setHours(0,0,0,0); atrsTasksData.forEach(task => { let notes = []; const checklistCompletion = atrsCalculateChecklistCompletion(task.checklist); if (task.dueDate) { const dueDate = new Date(task.dueDate); // Already YYYY-MM-DD, new Date will parse as local dueDate.setHours(0,0,0,0); if (dueDate < today && task.status !== 'Completed') { notes.push("Task is OVERDUE."); } } if (task.status === 'Blocked') notes.push("Task is BLOCKED. Requires attention."); if (task.status === 'Pending Review' && checklistCompletion < 100) notes.push("'Pending Review' but checklist is not 100% complete."); if (task.status === 'Completed' && checklistCompletion < 100) notes.push("'Completed' but checklist was not 100% complete (Potential Discrepancy)."); if (task.status !== 'Completed' && checklistCompletion === 100 && task.status !== 'Pending Review') notes.push("Checklist 100% complete, but task not 'Completed' or 'Pending Review'. Consider updating status."); const reviewNoteElTrack = document.getElementById(`reviewNoteTrack-${task.id}`); if(reviewNoteElTrack) reviewNoteElTrack.textContent = notes.join(' '); if (notes.length > 0) { issuesFound++; const itemDiv = document.createElement('div'); itemDiv.classList.add('atrs-review-item'); if(notes.some(n => n.includes("OVERDUE"))) itemDiv.classList.add('status-overdue'); else if(notes.some(n => n.includes("BLOCKED"))) itemDiv.classList.add('status-blocked'); else if(notes.some(n => n.includes("Pending Review"))) itemDiv.classList.add('status-pendingreview', 'incomplete'); else if(notes.some(n => n.includes("Completed") && n.includes("Discrepancy"))) itemDiv.classList.add('status-completed', 'discrepancy'); else if(notes.some(n => n.includes("Consider updating status"))) itemDiv.classList.add('status-completed','ok'); itemDiv.innerHTML = `

    ${atrsEscapeHtml(task.title)} (ID: ${task.id} | Status: ${task.status})

    ${notes.map(atrsEscapeHtml).join('
    ')}

    `; atrsReviewResultItemsEl.appendChild(itemDiv); } }); atrsNoReviewIssuesMessageEl.style.display = issuesFound === 0 ? 'block' : 'none'; atrsReviewResultsDisplayAreaEl.style.display = 'block'; atrsDownloadReportBtnEl.style.display = 'block'; } function atrsDownloadReviewReport() { if (atrsReviewResultsDisplayAreaEl.style.display === 'none' && atrsTasksData.length === 0) { alert("Please perform a review or add tasks first."); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF('p', 'pt', 'a4'); const primaryColorPDF = getComputedStyle(document.documentElement).getPropertyValue('--atrs-primary-color').trim(); const whiteColorPDF = getComputedStyle(document.documentElement).getPropertyValue('--atrs-text-color-light').trim(); const textColorPDF = getComputedStyle(document.documentElement).getPropertyValue('--atrs-text-color-dark').trim(); const todayStr = new Date().toLocaleDateString(); let yPos = 40; const leftMargin = 40; const contentWidth = doc.internal.pageSize.getWidth() - (2 * leftMargin); doc.setFontSize(18); doc.setTextColor(primaryColorPDF); doc.text("Automated Task Review Report", doc.internal.pageSize.getWidth() / 2, yPos, { align: 'center' }); yPos += 20; doc.setFontSize(10); doc.setTextColor(textColorPDF); doc.text(`Report Date: ${todayStr}`, doc.internal.pageSize.getWidth() / 2, yPos, { align: 'center' }); yPos += 30; const reviewItemNodes = atrsReviewResultItemsEl.children; if (reviewItemNodes.length > 0) { doc.setFontSize(12); doc.setFont(undefined, 'bold'); doc.setTextColor(primaryColorPDF); doc.text("Review Findings / Tasks Needing Attention:", leftMargin, yPos); yPos += 20; doc.setFont(undefined, 'normal'); Array.from(reviewItemNodes).forEach(itemNode => { const titleText = itemNode.querySelector('h4')?.textContent || "Task"; const notesHTML = itemNode.querySelector('p.system-note')?.innerHTML || "No specific notes."; const notesText = notesHTML.replace(//gi, "\n"); // Convert
    to newlines for PDF yPos = checkPdfPageBreak(doc, yPos, 45); // Estimate space needed doc.setFontSize(10); doc.setFont(undefined,'bold'); doc.text(titleText, leftMargin, yPos); yPos += 14; doc.setFontSize(9); doc.setFont(undefined,'italic'); const noteLines = doc.splitTextToSize(notesText, contentWidth - 10); // Small indent doc.text(noteLines, leftMargin + 10, yPos); yPos += (noteLines.length * 11) + 8; }); } else if (atrsTasksData.length > 0) { doc.setFontSize(12); doc.setTextColor(primaryColorPDF); doc.text("No specific review issues flagged based on automated checks.", leftMargin, yPos); yPos += 20; } if (atrsTasksData.length > 0) { yPos = checkPdfPageBreak(doc, yPos, 50); doc.setFontSize(12); doc.setFont(undefined, 'bold'); doc.setTextColor(primaryColorPDF); doc.text("All Tasks Overview:", leftMargin, yPos); yPos += 20; const tableColumns = ["ID", "Task Title", "Due Date", "Assigned To", "Status", "Checklist %"]; const tableRows = atrsTasksData.map(task => [ task.id, task.title, task.dueDate ? new Date(task.dueDate+'T00:00:00Z').toLocaleDateString() : 'N/A', task.assignedTo || 'N/A', task.status, `${atrsCalculateChecklistCompletion(task.checklist)}%` ]); doc.autoTable({ head: [tableColumns], body: tableRows, startY: yPos, theme: 'grid', headStyles: { fillColor: primaryColorPDF, textColor: whiteColorPDF, fontSize: 9, fontStyle: 'bold' }, styles: { fontSize: 8, cellPadding: 3, textColor: textColorPDF, overflow: 'linebreak' }, columnStyles: { 1: {cellWidth: 'auto'}} }); } else { yPos = checkPdfPageBreak(doc, yPos, 20); doc.setFontSize(10); doc.text("No tasks were defined in the system for this report.", leftMargin, yPos); } doc.save(`TaskReviewReport_${todayStr.replace(/\//g,'-')}.pdf`); } function checkPdfPageBreak(doc, currentY, spaceNeeded) { if (currentY + spaceNeeded > doc.internal.pageSize.getHeight() - 40) { doc.addPage(); return 40; } return currentY; } function atrsEscapeHtml(unsafe) { if (typeof unsafe !== 'string') return ''; return unsafe.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'"); }
    Scroll to Top