${aar_escapeHTML(aar_data.planned) || 'No objectives defined.'}
What actually happened? (Summary of Events)
${aar_escapeHTML(aar_data.actual) || 'No summary provided.'}
3. Analysis
What went well? (Strengths)
${aar_data.strengths.length > 0 ? aar_data.strengths.map(s => `- ${aar_escapeHTML(s)}
`).join('') : `- No strengths listed.
`}
What didn't go well? (Weaknesses)
${aar_data.weaknesses.length > 0 ? aar_data.weaknesses.map(w => `- ${aar_escapeHTML(w)}
`).join('') : `- No weaknesses listed.
`}
4. Action Items
| Action Item / Task |
Owner |
Due Date |
${aar_data.actionItems.length > 0 ? aar_data.actionItems.map(item => `
| ${aar_escapeHTML(item.task)} |
${aar_escapeHTML(item.owner)} |
${aar_escapeHTML(item.dueDate)} |
`).join('') : `| No action items listed. |
`}
`;
}
/**
* Reads all values from the config tab and updates the aar_data object
*/
function aar_updateDataFromConfig() {
if (!aar_inputName) return;
aar_data.eventName = aar_inputName.value;
aar_data.eventDate = aar_inputDate.value;
aar_data.participants = aar_inputParticipants.value;
aar_data.planned = aar_inputPlanned.value;
aar_data.actual = aar_inputActual.value;
aar_data.strengths = [];
aar_strengthsContainer.querySelectorAll('.aar-input-strength').forEach(input => {
if (input.value) aar_data.strengths.push(input.value);
});
aar_data.weaknesses = [];
aar_weaknessesContainer.querySelectorAll('.aar-input-weakness').forEach(input => {
if (input.value) aar_data.weaknesses.push(input.value);
});
aar_data.actionItems = [];
aar_actionsContainer.querySelectorAll('.border').forEach(entry => {
const id = parseInt(entry.getAttribute('data-id'), 10);
const task = entry.querySelector('.aar-input-action-task').value;
const owner = entry.querySelector('.aar-input-action-owner').value;
const dueDate = entry.querySelector('.aar-input-action-due').value;
if (task || owner || dueDate) {
aar_data.actionItems.push({ id, task, owner, dueDate });
}
});
}
/**
* Generates and downloads a multi-page PDF of the dashboard
*/
async function aar_downloadPDF() {
// Check for required libraries
if (typeof jspdf === 'undefined' || typeof html2canvas === 'undefined') {
console.error("AAR Tool Error: jsPDF or html2canvas library not loaded.");
alert("Error: PDF libraries failed to load. Please check console.");
return;
}
// 1. Render the full-size report into the clone
aar_renderDashboard(aar_pdfRenderClone);
const { jsPDF } = window.jspdf;
try {
// 2. Render canvas from the clone
const canvas = await html2canvas(aar_pdfRenderClone, {
scale: 2, // High resolution
useCORS: true,
windowWidth: aar_pdfRenderClone.scrollWidth,
windowHeight: aar_pdfRenderClone.scrollHeight
});
const imgData = canvas.toDataURL('image/png');
const imgWidth = canvas.width;
const imgHeight = canvas.height;
// Use 'pt' for units. A4 is 595.28 x 841.89
const pdf = new jsPDF({ orientation: 'p', unit: 'pt', format: 'a4' });
const pdfWidth = pdf.internal.pageSize.getWidth();
const pdfHeight = pdf.internal.pageSize.getHeight();
// Scale image height to fit pdf width
const ratio = imgHeight / imgWidth;
const scaledImgHeight = pdfWidth * ratio;
let heightLeft = scaledImgHeight;
let position = 0; // y-position of the image on the page
const margin = 20; // 20pt margin
const contentWidth = pdfWidth - (margin * 2);
const contentHeight = (contentWidth * imgHeight) / imgWidth;
// 3. Add the first page
pdf.addImage(imgData, 'PNG', margin, position + margin, contentWidth, contentHeight);
heightLeft -= (pdfHeight - margin * 2);
// 4. Add subsequent pages if needed
while (heightLeft > 0) {
position -= (pdfHeight - margin * 2); // Move the image's y-position up
pdf.addPage();
pdf.addImage(imgData, 'PNG', margin, position + margin, contentWidth, contentHeight);
heightLeft -= (pdfHeight - margin * 2);
}
const safeName = (aar_data.eventName || 'aar_report').replace(/[^a-z0-9]/gi, '_').toLowerCase();
pdf.save(`${safeName}.pdf`);
} catch (error) {
console.error("AAR Tool Error: PDF generation failed.", error);
alert("An error occurred while generating the PDF. Please try again.");
}
}
/**
* Utility to escape HTML for display
*/
function aar_escapeHTML(str) {
if (typeof str !== 'string') return '';
return str.replace(/[&<>"']/g, function(m) {
return {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
}[m];
});
}
// --- EVENT LISTENERS ---
// Tab link clicks
aar_tabLinks.forEach((link, index) => {
link.addEventListener('click', () => aar_switchTab(index));
});
// Next/Prev button clicks
if (aar_prevButton) {
aar_prevButton.addEventListener('click', () => {
if (aar_currentTab > 0) aar_switchTab(aar_currentTab - 1);
});
}
if (aar_nextButton) {
aar_nextButton.addEventListener('click', () => {
if (aar_currentTab < aar_tabLinks.length - 1) aar_switchTab(aar_currentTab + 1);
});
}
// PDF download
if (aar_downloadPdfButton) {
aar_downloadPdfButton.addEventListener('click', aar_downloadPDF);
}
// --- Config Tab "Add" Buttons ---
if (aar_addStrengthButton) {
aar_addStrengthButton.addEventListener('click', () => aar_addStrengthInput('', aar_strengthCounter++));
}
if (aar_addWeaknessButton) {
aar_addWeaknessButton.addEventListener('click', () => aar_addWeaknessInput('', aar_weaknessCounter++));
}
if (aar_addActionButton) {
aar_addActionButton.addEventListener('click', () => aar_addActionItemInput());
}
// --- Config Tab "Remove" Buttons (Event Delegation) ---
if (aar_configTab) {
aar_configTab.addEventListener('click', (e) => {
if (e.target.classList.contains('aar-remove-item')) {
e.target.closest('.flex, .border').remove();
}
});
// Auto-update dashboard on config changes
aar_configTab.addEventListener('change', () => {
aar_updateDataFromConfig();
if (aar_currentTab === 0) {
aar_renderDashboard(aar_dashboardOutput);
}
});
}
// --- INITIALIZATION ---
aar_initSampleData();
aar_renderConfig();
aar_renderDashboard(aar_dashboardOutput);
// Set initial tab state
aar_tabPanes.forEach((pane, index) => {
pane.classList.toggle('hidden', index !== 0);
pane.classList.toggle('aar-active', index === 0);
});
});