Corporate Credit Ratings & Market Sentiment Dashboard

Corporate Credit Ratings & Market Sentiment Dashboard

Market Snapshot

Overall Sentiment: Calculating...

Avg. Sentiment Score: N/A

Companies with Positive Outlook: 0

Companies with Stable Outlook: 0

Companies with Negative Outlook: 0

${stockPriceDisplay} ${priceChangeDisplay} ${priceChangePercentDisplay}

`; companiesGrid.appendChild(card); }); } function updateSummaryMetrics(dataToSummarize) { if (!overallSentimentLabelEl || !avgSentimentScoreEl || !positiveOutlookCountEl || !stableOutlookCountEl || !negativeOutlookCountEl) { console.error("One or more summary metric DOM elements not found."); return; } if (dataToSummarize.length === 0) { overallSentimentLabelEl.textContent = 'N/A'; avgSentimentScoreEl.textContent = 'N/A'; positiveOutlookCountEl.textContent = '0'; stableOutlookCountEl.textContent = '0'; negativeOutlookCountEl.textContent = '0'; return; } let totalSentimentScore = 0; let positiveOutlooks = 0; let stableOutlooks = 0; let negativeOutlooks = 0; dataToSummarize.forEach(company => { totalSentimentScore += company.marketSentimentScore; // Count outlooks from S&P as primary, can be extended const spOutlook = company.outlook.sp ? company.outlook.sp.toLowerCase() : ''; if (spOutlook === 'positive') positiveOutlooks++; else if (spOutlook === 'stable') stableOutlooks++; else if (spOutlook === 'negative') negativeOutlooks++; }); const avgScore = totalSentimentScore / dataToSummarize.length; avgSentimentScoreEl.textContent = `${avgScore.toFixed(1)}/100`; if (avgScore >= 70) overallSentimentLabelEl.textContent = 'Positive'; else if (avgScore >= 50) overallSentimentLabelEl.textContent = 'Neutral'; else overallSentimentLabelEl.textContent = 'Negative'; overallSentimentLabelEl.className = `font-bold ${getSentimentTextClass(avgScore)}`; positiveOutlookCountEl.textContent = positiveOutlooks; stableOutlookCountEl.textContent = stableOutlooks; negativeOutlookCountEl.textContent = negativeOutlooks; } function filterCompanies() { if (!searchInput) { console.error("Error: searchInput element not found."); renderCompanies(companiesData); // Render all if search input is missing updateSummaryMetrics(companiesData); return; } const searchTerm = searchInput.value.toLowerCase().trim(); const filtered = companiesData.filter(company => company.name.toLowerCase().includes(searchTerm) || company.ticker.toLowerCase().includes(searchTerm) ); renderCompanies(filtered); updateSummaryMetrics(filtered); // Update summary based on filtered data } function generatePdf() { if (!pdfContentEl) { console.error("Error: pdfContent element not found. Cannot generate PDF."); // You could show a user-friendly message here instead of an alert 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; } const opt = { margin: [0.5, 0.5, 0.5, 0.5], // inches [top, left, bottom, right] filename: 'corporate_ratings_sentiment_dashboard.pdf', image: { type: 'jpeg', quality: 0.98 }, html2canvas: { scale: 2, logging: false, useCORS: true, letterRendering: true, scrollY: 0 }, // Added scrollY:0 jsPDF: { unit: 'in', format: 'letter', orientation: 'portrait' }, pagebreak: { mode: ['avoid-all', 'css', 'legacy'] } // Helps with page breaks }; // Temporarily make the hidden PDF title visible for html2pdf const pdfTitleElement = pdfContentEl.querySelector('.print\\:block'); if (pdfTitleElement) { pdfTitleElement.classList.remove('hidden'); } // Create a clone of the content to be included in the PDF. // This allows us to modify it for PDF without affecting the live view. const contentCloneForPdf = pdfContentEl.cloneNode(true); // Ensure company cards in the clone are styled for PDF (e.g., visible borders) // This can be more robust by adding specific PDF print styles. // For now, relying on @media print styles and html2pdf's rendering. html2pdf().from(contentCloneForPdf).set(opt).save().then(() => { // Re-hide the PDF title element after PDF generation if (pdfTitleElement) { pdfTitleElement.classList.add('hidden'); } }).catch(err => { console.error("Error generating PDF:", err); // Re-hide the PDF title element in case of error too if (pdfTitleElement) { pdfTitleElement.classList.add('hidden'); } 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); }); } // --- Event Listeners --- if (searchInput) { searchInput.addEventListener('input', filterCompanies); } else { console.warn("Search input element not found. Search functionality will be disabled."); } if (downloadPdfButton) { downloadPdfButton.addEventListener('click', generatePdf); } else { console.warn("PDF download button not found. PDF functionality will be disabled."); } // --- Initial Render --- renderCompanies(companiesData); updateSummaryMetrics(companiesData); });
Scroll to Top