`;
plannedTasksListUL.appendChild(li);
});
if(completed.length === 0) completedTasksListUL.innerHTML = "
No tasks were completed in this period.";
completed.forEach(task => {
const li = document.createElement('li');
li.className = 'task-item status-done'; // All completed tasks are 'done'
let timelinessInfo = task.timelinessStatus && task.timelinessStatus !== 'N/A' ? ` | Status: ${task.timelinessStatus}` : '';
li.innerHTML = `
${task.description}
Achieved Value: ${task.achievedValueWithBonus.toFixed(1)} (Base: ${task.value}) | Completed: ${formatDate(task.completionDate)}${timelinessInfo}
`;
completedTasksListUL.appendChild(li);
});
}
// --- PDF Download ---
downloadReportPdfBtn.addEventListener('click', () => {
const { jsPDF } = window.jspdf;
const doc = new jsPDF();
const periodText = analysisPeriodSelect.options[analysisPeriodSelect.selectedIndex].text;
let dateRangeText = periodText;
if (periodText === 'Custom Range') {
dateRangeText = `${formatDate(customStartDateInput.value)} to ${formatDate(customEndDateInput.value)}`;
}
doc.setFontSize(18);
doc.setTextColor(parseInt(getComputedStyle(document.documentElement).getPropertyValue('--primary-color').substring(1,3),16),
parseInt(getComputedStyle(document.documentElement).getPropertyValue('--primary-color').substring(3,5),16),
parseInt(getComputedStyle(document.documentElement).getPropertyValue('--primary-color').substring(5,7),16));
doc.text('Productivity Analysis Report', 14, 22);
doc.setFontSize(12);
doc.setTextColor(100);
doc.text(`Period: ${dateRangeText}`, 14, 30);
doc.text(`Generated on: ${new Date().toLocaleString()}`, 14, 36);
doc.setFontSize(14);
doc.text(`Overall Productivity Score: ${productivityScoreDiv.textContent}`, 14, 46);
doc.setFontSize(11);
let yPos = 54;
doc.text(`Value Planned for Period: ${totalValuePlannedDiv.textContent} points`, 14, yPos); yPos +=6;
doc.text(`Value Achieved (incl. bonus): ${totalValueAchievedDiv.textContent} points`, 14, yPos); yPos +=6;
doc.text(`Tasks Completed: ${tasksCompletedCountDiv.textContent}`, 14, yPos); yPos +=6;
doc.text(`Tasks On Time: ${tasksOnTimeCountDiv.textContent}`, 14, yPos); yPos +=6;
doc.text(`Tasks Late: ${tasksLateCountDiv.textContent}`, 14, yPos); yPos +=10;
doc.setFontSize(12);
doc.text('Tasks Considered Planned:', 14, yPos); yPos +=6;
const plannedData = Array.from(plannedTasksListUL.children).map(li => {
const desc = li.querySelector('.task-description').textContent;
const meta = li.querySelector('.task-meta').textContent;
const value = meta.match(/Value: (\d+)/) ? meta.match(/Value: (\d+)/)[1] : 'N/A';
const dueDate = meta.match(/Due: (.*?)$/) ? meta.match(/Due: (.*?)$/)[1].trim() : 'N/A';
return [desc, value, dueDate];
});
if (plannedData.length > 0) {
doc.autoTable({ startY: yPos, head: [['Description', 'Value', 'Due Date']], body: plannedData, theme: 'grid' });
yPos = doc.lastAutoTable.finalY + 10;
} else {
doc.text('No tasks were planned for this period.', 16, yPos); yPos += 7;
}
doc.setFontSize(12);
doc.text('Tasks Completed in Period:', 14, yPos); yPos +=6;
const completedData = Array.from(completedTasksListUL.children).map(li => {
const desc = li.querySelector('.task-description').textContent;
const meta = li.querySelector('.task-meta').textContent; // "Achieved Value: X (Base: Y) | Completed: Z | Status: W"
const achieved = meta.match(/Achieved Value: ([\d\.]+)/) ? meta.match(/Achieved Value: ([\d\.]+)/)[1] : 'N/A';
const completed = meta.match(/Completed: ([\d\-]+)/) ? meta.match(/Completed: ([\d\-]+)/)[1] : 'N/A';
const status = meta.match(/Status: (.*?)$/) ? meta.match(/Status: (.*?)$/)[1].trim() : 'N/A';
return [desc, achieved, completed, status];
});
if (completedData.length > 0) {
doc.autoTable({ startY: yPos, head: [['Description', 'Achieved Value', 'Completed Date', 'Timeliness']], body: completedData, theme: 'grid' });
} else {
doc.text('No tasks were completed in this period.', 16, yPos);
}
doc.save(`Productivity_Report_${dateRangeText.replace(/[\s/:]/g, '_')}.pdf`);
});
// --- Initializations ---
setDateInputConstraints();
loadSettings();
renderTaskList();
// Set default custom range for analysis tab
const today = new Date();
const todayStr = formatDate(today);
const sevenDaysAgo = new Date(today);
sevenDaysAgo.setDate(today.getDate() - 6);
customStartDateInput.value = formatDate(sevenDaysAgo);
customEndDateInput.value = todayStr;
})(); // End IIFE