Research Project Timeline Chart
The interactive chart below visualizes the duration and status of each research task. Use the legend to see the meaning of the status colors.
Project Visualization
Task Legend
Project Metrics
Define Project Tasks
Total Tasks Defined: ${metrics.totalTasks}
Tasks Completed: ${metrics.completedTasks}
Estimated Project Span: ${metrics.overallDays} days
`; } function rptc_renderDashboard() { const chartData = rptc_calculateChartData(); rptc_renderChart(chartData); if (chartData) { rptc_renderLegend(chartData.metrics); } } function rptc_renderPdfClone() { rptc_pdfRenderClone.innerHTML = `Research Project Timeline Summary
Metrics
${rptc_metricsContainer.innerHTML}
Timeline Visualization
Task Legend
${rptc_legendContainer.innerHTML}
Task Details
${rptc_escapeHTML(t.name)}: Owner: ${rptc_escapeHTML(t.owner)}, Status: ${rptc_escapeHTML(t.status)}, Duration: ${rptc_formatDate(t.start)} to ${rptc_formatDate(t.end)}
`).join('');
// Style and render chart clone
const pdfChartCanvas = rptc_pdfRenderClone.querySelector('#pdf-timeline-chart');
const chartData = rptc_calculateChartData();
// Set dimensions for Chart.js rendering on clone
pdfChartCanvas.parentNode.style.height = '400px';
pdfChartCanvas.parentNode.style.width = '800px';
// Temporarily render chart into PDF clone
const tempChart = new Chart(pdfChartCanvas, {
type: 'bar',
data: rptc_chart.data,
options: { ...rptc_chart.options, animation: false, responsive: false, maintainAspectRatio: false } // Disable animation for capture
});
tempChart.update();
return tempChart;
}
/**
* Generates and downloads a PDF of the checklist
*/
async function rptc_downloadPDF() {
if (rptc_data.tasks.length === 0) {
alert("Please add tasks before downloading.");
return;
}
if (typeof jspdf === 'undefined' || typeof html2canvas === 'undefined') {
alert("Error: PDF libraries failed to load.");
return;
}
// 1. Render the clone and the chart into the clone
const tempChart = rptc_renderPdfClone();
const { jsPDF } = window.jspdf;
try {
const canvas = await html2canvas(rptc_pdfRenderClone, { scale: 1.5, useCORS: true });
const imgData = canvas.toDataURL('image/png');
const imgProps = { width: canvas.width, height: canvas.height };
const pdf = new jsPDF({ orientation: 'l', unit: 'pt', format: 'a4' });
const pdfWidth = pdf.internal.pageSize.getWidth();
const pdfHeight = pdf.internal.pageSize.getHeight();
const margin = 40;
const contentWidth = pdfWidth - (margin * 2);
const contentHeight = (contentWidth * imgProps.height) / imgProps.width;
let heightLeft = contentHeight;
let position = 0;
pdf.addImage(imgData, 'PNG', margin, position + margin, contentWidth, contentHeight);
heightLeft -= (pdfHeight - margin * 2);
while (heightLeft > 0) {
position -= (pdfHeight - margin * 2);
pdf.addPage();
pdf.addImage(imgData, 'PNG', margin, position + margin, contentWidth, contentHeight);
heightLeft -= (pdfHeight - margin * 2);
}
pdf.save('Research_Project_Timeline.pdf');
} catch (error) {
console.error("PDF generation failed:", error);
alert("An error occurred while generating the PDF.");
} finally {
tempChart.destroy(); // Clean up temporary chart instance
}
}
// --- EVENT LISTENERS ---
// Tab link clicks
rptc_tabLinks.forEach((link, index) => {
link.addEventListener('click', () => rptc_switchTab(index));
});
// Next/Prev button clicks
if (rptc_prevButton) {
rptc_prevButton.addEventListener('click', () => {
if (rptc_currentTab > 0) rptc_switchTab(rptc_currentTab - 1);
});
}
if (rptc_nextButton) {
rptc_nextButton.addEventListener('click', () => {
if (rptc_currentTab === rptc_tabLinks.length - 1) {
rptc_updateDataFromConfig();
rptc_switchTab(0);
} else {
if (rptc_currentTab < rptc_tabLinks.length - 1) rptc_switchTab(rptc_currentTab + 1);
}
});
}
// PDF download
if (rptc_downloadPdfButton) {
rptc_downloadPdfButton.addEventListener('click', rptc_downloadPDF);
}
// --- Config Tab Listeners ---
if (rptc_addItemButton) {
rptc_addItemButton.addEventListener('click', () => {
rptc_itemsContainer.appendChild(rptc_createTaskInput());
});
}
if (rptc_configTab) {
// Handle remove
rptc_configTab.addEventListener('click', (e) => {
const removeButton = e.target.closest('.rptc-remove-item');
if (removeButton) {
removeButton.closest('.border[data-id]').remove();
if(rptc_itemsContainer.children.length === 0){
rptc_itemsContainer.appendChild(rptc_createTaskInput());
}
}
});
}
// --- INITIALIZATION ---
rptc_renderConfig();
rptc_renderDashboard();
// Set initial tab state
rptc_tabPanes.forEach((pane, index) => {
pane.classList.toggle('hidden', index !== 0);
pane.classList.toggle('rptc-active', index === 0);
});
rptc_tabLinks.forEach((link, index) => {
TAB_CLASSES.active.forEach(cls => link.classList.remove(cls));
TAB_CLASSES.inactive.forEach(cls => link.classList.remove(cls));
if (index === 0) {
TAB_CLASSES.active.forEach(cls => link.classList.add(cls));
} else {
TAB_CLASSES.inactive.forEach(cls => link.classList.add(cls));
}
});
});
