Revenue Growth: ${formatNumber(stock.revenueGrowthYoY,1)}%
EPS Growth: ${formatNumber(stock.epsGrowthYoY,0)}%
P/E Ratio: ${formatNumber(stock.peRatio,0)}
`;
growthGrid.appendChild(card);
});
updateGrowthSummary(data);
}
// --- Summary Update Functions ---
function updateAristocratsSummary(data) {
if (!avgAristocratYieldEl || !avgAristocratGrowthYearsEl || !aristocratCountEl) return;
if (data.length === 0) {
avgAristocratYieldEl.textContent = 'N/A';
avgAristocratGrowthYearsEl.textContent = 'N/A';
aristocratCountEl.textContent = '0';
return;
}
const totalYield = data.reduce((sum, s) => sum + s.yield, 0);
const totalGrowthYears = data.reduce((sum, s) => sum + s.growthYears, 0);
avgAristocratYieldEl.textContent = `${formatNumber(totalYield / data.length, 2)}%`;
avgAristocratGrowthYearsEl.textContent = formatNumber(totalGrowthYears / data.length, 1);
aristocratCountEl.textContent = data.length;
}
function updateGrowthSummary(data) {
if (!avgGrowthRevGrowthEl || !avgGrowthPERatioEl || !growthCountEl) return;
if (data.length === 0) {
avgGrowthRevGrowthEl.textContent = 'N/A';
avgGrowthPERatioEl.textContent = 'N/A';
growthCountEl.textContent = '0';
return;
}
const totalRevGrowth = data.reduce((sum, s) => sum + s.revenueGrowthYoY, 0);
const totalPERatio = data.reduce((sum, s) => sum + s.peRatio, 0);
avgGrowthRevGrowthEl.textContent = `${formatNumber(totalRevGrowth / data.length, 1)}%`;
avgGrowthPERatioEl.textContent = formatNumber(totalPERatio / data.length, 1);
growthCountEl.textContent = data.length;
}
// --- Filtering and Combined Render ---
function filterAndRender() {
if (!searchInput) {
renderAristocrats(dividendAristocratsData);
renderGrowthStocks(growthStocksData);
return;
}
const searchTerm = searchInput.value.toLowerCase().trim();
if (currentTab === 'aristocrats') {
const filteredAristocrats = dividendAristocratsData.filter(stock =>
stock.name.toLowerCase().includes(searchTerm) ||
stock.ticker.toLowerCase().includes(searchTerm)
);
renderAristocrats(filteredAristocrats);
} else if (currentTab === 'growth') {
const filteredGrowth = growthStocksData.filter(stock =>
stock.name.toLowerCase().includes(searchTerm) ||
stock.ticker.toLowerCase().includes(searchTerm)
);
renderGrowthStocks(filteredGrowth);
}
}
if (searchInput) {
searchInput.addEventListener('input', filterAndRender);
}
// --- PDF Generation ---
function generatePdf() {
if (!pdfContentEl || !pdfTitleForReport) {
console.error("PDF content or title element not found.");
// User-friendly message
const msgBox = document.createElement('div');
msgBox.textContent = "Error preparing PDF content. Please try again.";
msgBox.style.cssText = "position:fixed; top:20px; left:50%; transform:translateX(-50%); background:red; color:white; padding:10px; border-radius:5px; z-index:1000;";
document.body.appendChild(msgBox);
setTimeout(() => msgBox.remove(), 3000);
return;
}
// Update PDF title based on current tab
const activeTabName = currentTab === 'aristocrats' ? 'Dividend Aristocrats' : 'Growth Stocks';
pdfTitleForReport.textContent = `${activeTabName} Analysis Report`;
pdfTitleForReport.classList.remove('hidden-for-pdf-title'); // Make it visible for PDF
// Temporarily hide the inactive tab content for PDF generation
const inactiveTabId = currentTab === 'aristocrats' ? 'growthContent' : 'aristocratsContent';
const inactiveTabContent = document.getElementById(inactiveTabId);
let inactiveTabOriginalDisplay = '';
if (inactiveTabContent) {
inactiveTabOriginalDisplay = inactiveTabContent.style.display;
inactiveTabContent.style.display = 'none';
}
const opt = {
margin: [0.5, 0.5, 0.5, 0.5], // inches
filename: `${currentTab}_stock_analysis_report.pdf`,
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { scale: 2, logging: false, useCORS: true, letterRendering: true, scrollY: 0 },
jsPDF: { unit: 'in', format: 'letter', orientation: 'portrait' },
pagebreak: { mode: ['avoid-all', 'css', 'legacy'] }
};
html2pdf().from(pdfContentEl).set(opt).save().then(() => {
pdfTitleForReport.classList.add('hidden-for-pdf-title'); // Hide PDF title on screen again
if (inactiveTabContent) { // Restore display of inactive tab
inactiveTabContent.style.display = inactiveTabOriginalDisplay;
}
}).catch(err => {
console.error("Error generating PDF:", err);
pdfTitleForReport.classList.add('hidden-for-pdf-title');
if (inactiveTabContent) { // Restore display of inactive tab
inactiveTabContent.style.display = inactiveTabOriginalDisplay;
}
// User-friendly error message
const errorBox = document.createElement('div');
errorBox.textContent = "Failed to generate PDF. See console for details.";
errorBox.style.cssText = "position:fixed; top:20px; left:50%; transform:translateX(-50%); background:red; color:white; padding:10px; border-radius:5px; z-index:1000;";
document.body.appendChild(errorBox);
setTimeout(() => errorBox.remove(), 3000);
});
}
if (downloadPdfButton) {
downloadPdfButton.addEventListener('click', generatePdf);
}
// --- Initial Render ---
switchTab('aristocrats'); // Set initial tab and render
updateNavButtons(); // Initialize nav button states
});