2. Materials & Methodology
Key Materials:
${data.exp_materials || 'N/A'}
Detailed Methodology:
${data.exp_methodology || 'N/A'}
3. Replication Verification Checklist
Items to verify before attempting replication:
`;
// --- 2. Checklist ---
const groupedChecks = verificationChecks.reduce((acc, check) => {
acc[check.category] = acc[check.category] || [];
acc[check.category].push(check);
return acc;
}, {});
Object.entries(groupedChecks).forEach(([category, checks]) => {
html += `${category} (${checks.filter(c => c.type === 'Required').length} Required)
`;
checks.forEach(check => {
const color = check.type === 'Required' ? 'var(--danger-color)' : 'var(--success-color)';
html += `- [ ] ${check.type.toUpperCase()}: ${check.point}
`;
});
});
html += `
`;
reviewContent.innerHTML = html;
pdfDownloadBtn.disabled = false;
switchTab(2); // Switch to review tab
};
/**
* PDF Generation Function
*/
const downloadPDF = () => {
const data = getMetaFormData();
const jsPDF = window.jspdf.jsPDF;
const doc = new jsPDF('p', 'pt', 'a4');
let currentY = 40;
const margin = 40;
const pageWidth = doc.internal.pageSize.width;
const maxWidth = pageWidth - (margin * 2);
const checkPageBreak = (spaceNeeded) => {
if (currentY + spaceNeeded > doc.internal.pageSize.height - margin) {
doc.addPage();
currentY = margin;
}
};
const addSectionHeader = (title, color = [0, 0, 0]) => {
checkPageBreak(25);
doc.setFontSize(16);
doc.setFont('Helvetica', 'bold');
doc.setTextColor(color[0], color[1], color[2]);
doc.text(title, margin, currentY);
currentY += 10;
doc.setLineWidth(0.5);
doc.setDrawColor(200);
doc.line(margin, currentY, pageWidth - margin, currentY);
currentY += 12;
doc.setTextColor(0);
};
const addText = (text, size = 10, style = 'normal', indent = 0) => {
doc.setFontSize(size);
doc.setFont('Helvetica', style);
const lines = doc.splitTextToSize(text, maxWidth - indent);
checkPageBreak(lines.length * 9);
doc.text(lines, margin + indent, currentY);
currentY += (lines.length * 9);
};
const addKeyValue = (key, value, isBold = false) => {
doc.setFontSize(10);
doc.setFont('Helvetica', isBold ? 'bold' : 'normal');
const keyText = `${key}: `;
const keyWidth = doc.getStringUnitWidth(keyText) * 10;
const valueLines = doc.splitTextToSize(value, maxWidth - keyWidth);
doc.text(keyText, margin, currentY);
doc.setFont('Helvetica', 'normal');
doc.text(valueLines[0], margin + keyWidth, currentY);
currentY += 10;
for(let i = 1; i < valueLines.length; i++) {
doc.text(valueLines[i], margin + keyWidth, currentY);
currentY += 10;
}
};
// --- PDF Content ---
// Title Block
doc.setFontSize(20);
doc.setFont('Helvetica', 'bold');
doc.setTextColor(0, 128, 128);
doc.text(`EXPERIMENT REPLICATION CHECKLIST`, pageWidth / 2, currentY, { align: 'center' });
currentY += 15;
// 1. Metadata
addSectionHeader("1. Experiment Identification", [0, 128, 128]);
addKeyValue("Title", data.exp_title, true);
addKeyValue("Researcher", data.lead_researcher, true);
addKeyValue("Date Completed", data.exp_date, true);
addKeyValue("Domain", data.exp_domain, true);
currentY += 5;
addText("Summary / Goal:", 10, 'bold');
addText(data.exp_summary, 10, 'normal', 10);
currentY += 10;
// 2. Materials & Methodology
addSectionHeader("2. Materials & Methodology", [0, 128, 128]);
addText("Key Materials:", 10, 'bold');
addText(data.exp_materials, 10, 'normal', 10);
currentY += 5;
addText("Detailed Procedure:", 10, 'bold');
addText(data.exp_methodology, 10, 'normal', 10);
currentY += 10;
// 3. Verification Checklist
addSectionHeader("3. Replication Verification Checklist", [0, 128, 128]);
const groupedChecks = verificationChecks.reduce((acc, check) => {
acc[check.category] = acc[check.category] || [];
acc[check.category].push(check);
return acc;
}, {});
const allChecks = Object.entries(groupedChecks).flatMap(([category, checks]) => {
const header = { content: category, styles: { fontStyle: 'bold', fillColor: [240, 240, 240], textColor: [0] } };
const checkRows = checks.map(check => {
const color = check.type === 'Required' ? [244, 67, 54] : [76, 175, 80];
return [`[ ]`, check.type, check.point, color];
});
return [header, ...checkRows];
});
const finalChecks = allChecks.filter(item => typeof item !== 'string');
const checkTableHead = [["", "Type", "Verification Requirement"]];
const checkTableBody = finalChecks.map(item => {
if (item.content) {
return [{ content: item.content, colSpan: 3, styles: item.styles }];
} else {
return [
item[0], // [ ]
{ content: item[1], styles: { textColor: item[3] } }, // Type
item[2] // Requirement
];
}
});
doc.autoTable({
startY: currentY,
head: checkTableHead,
body: checkTableBody,
theme: 'grid',
headStyles: { fillColor: [74, 78, 105], textColor: [255] }, // Secondary color
styles: { fontSize: 9, cellPadding: 5, font: 'Helvetica' },
columnStyles: {
0: { cellWidth: 20, font: 'Courier' },
1: { cellWidth: 70, fontStyle: 'bold' }
}
});
currentY = doc.autoTable.previous.finalY + 10;
doc.save(`${data.exp_title.replace(/\s/g, '_')}_Replication_Checklist.pdf`);
};
// --- Tab Navigation ---
const switchTab = (tabIndex) => {
tabs.forEach((tab, index) => {
tab.classList.toggle('active', index === tabIndex);
contents[index].classList.toggle('active', index === tabIndex);
});
currentTab = tabIndex;
updateNavButtons();
if (tabIndex === 2) {
renderReviewSheet();
}
}
const updateNavButtons = () => {
prevBtn.disabled = currentTab === 0;
nextBtn.disabled = currentTab === tabs.length - 1;
}
tabs.forEach((tab, index) => {
tab.addEventListener('click', () => {
const tabNode = tab.closest('.checklist-tab-button');
const newIndex = Array.from(tabNode.parentNode.children).indexOf(tabNode);
switchTab(newIndex);
});
});
nextBtn.addEventListener('click', () => { if (currentTab < tabs.length - 1) switchTab(currentTab + 1); });
prevBtn.addEventListener('click', () => { if (currentTab > 0) switchTab(currentTab - 1); });
// --- Event Listeners and Initial Load ---
generateReviewBtn.addEventListener('click', renderReviewSheet);
pdfDownloadBtn.addEventListener('click', downloadPDF);
// Initial Setup
setInitialDate();
renderVerificationChecks();
updateNavButtons();
});