Sector-Specific Economic Recovery Model
Simulated Sector Recovery
Recovery Data
| Period | Value ($) |
|---|
PDF generation libraries are still loading or failed to load. Please ensure internet connectivity and try again in a moment.
`; document.body.appendChild(messageBox); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF(); // Initialize a new PDF document // Initialize imgHeight outside the try block let imgHeight = 0; // --- Add Title to PDF --- doc.setFontSize(22); doc.text("Sector-Specific Economic Recovery Report", doc.internal.pageSize.getWidth() / 2, 20, { align: 'center' }); // --- Add Input Parameters to PDF --- doc.setFontSize(12); let currentY = 40; // Starting Y position for text const marginX = 15; // Left margin // Helper to add text and increment Y position const addText = (text) => { doc.text(text, marginX, currentY); currentY += 7; // Increment Y for next line }; addText(`Sector Name: ${sectorNameInput.value}`); addText(`Initial Economic Output: $${parseFloat(initialValueInput.value).toLocaleString()}`); addText(`Recovery Shape: ${recoveryShapeSelect.options[recoveryShapeSelect.selectedIndex].text}`); addText(`Recession Magnitude: ${recessionMagnitudeInput.value}%`); addText(`Total Simulation Periods: ${totalPeriodsInput.value}`); // Add shape-specific parameters const recoveryShape = recoveryShapeSelect.value; if (recoveryShape === 'V') { addText(`Recovery Speed (Periods to recover 90% of dip): ${recoverySpeedInput.value}`); } else if (recoveryShape === 'U') { addText(`Recovery Speed (Periods to recover 90% of dip): ${recoverySpeedInput.value}`); addText(`Trough Duration (Periods at bottom): ${troughDurationInput.value}`); } else if (recoveryShape === 'W') { addText(`Recovery Speed (Periods to recover 90% of dip): ${recoverySpeedInput.value}`); addText(`Trough Duration (Periods at bottom): ${troughDurationInput.value}`); addText(`Second Dip Magnitude: ${secondDipMagnitudeInput.value}%`); addText(`Second Dip Duration (Periods): ${secondDipDurationInput.value}`); } doc.addPage(); // Start a new page for the chart and table // --- Capture Chart as Image and Add to PDF --- const canvas = document.getElementById('recovery-chart'); if (canvas) { try { const canvasImg = await html2canvas(canvas, { scale: 2 }); // Increase scale for better resolution const imgData = canvasImg.toDataURL('image/png'); const pdfWidth = doc.internal.pageSize.getWidth() - 30; // PDF width minus margins (15px each side) imgHeight = canvasImg.height * pdfWidth / canvasImg.width; // Maintain aspect ratio doc.addImage(imgData, 'PNG', 15, 20, pdfWidth, imgHeight); // Add image to PDF } catch (error) { console.error("Error capturing canvas for PDF:", error); doc.text("Chart could not be rendered in PDF.", 15, 30); // Fallback message } } else { doc.text("Chart element not found for PDF generation.", 15, 30); } // --- Add Data Table to PDF (using jspdf-autotable plugin) --- const tableData = window.recoveryData || []; if (tableData.length > 0) { doc.setFontSize(14); doc.text("Recovery Data Table", doc.internal.pageSize.getWidth() / 2, (imgHeight || 0) + 40, { align: 'center' }); const columns = ["Period", "Value ($)"]; const rows = tableData.map((value, index) => [ `Period ${index}`, `$${value.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` ]); // Check if autoTable function is available before calling if (typeof doc.autoTable !== 'undefined') { doc.autoTable({ startY: (imgHeight || 0) + 50, // Position table below chart head: [columns], body: rows, theme: 'grid', // Apply grid theme for borders styles: { font: 'Inter', // Try to use Inter font if available in jsPDF fontSize: 10, halign: 'left', cellPadding: 2 }, headStyles: { fillColor: [243, 244, 246], // Tailwind gray-100 for header background textColor: [75, 85, 99], // Tailwind gray-600 for header text fontStyle: 'bold' }, alternateRowStyles: { fillColor: [255, 255, 255] // White for alternate rows }, bodyStyles: { textColor: [55, 65, 81] // Tailwind gray-700 for body text }, columnStyles: { 0: { cellWidth: 40 }, // Width for Period column 1: { cellWidth: 60 } // Width for Value column } }); } else { console.error("jsPDF-AutoTable plugin not loaded. Cannot generate table in PDF."); doc.text("Table could not be rendered in PDF.", 15, (imgHeight || 0) + 50); } } else { doc.text("No data table available for this report.", 15, (imgHeight || 0) + 50); } doc.save('sector_recovery_report.pdf'); // Download the PDF file } // --- Event Listeners --- // Attach click event listeners to all tab buttons tabButtons.forEach((button, index) => { if (button) { button.addEventListener('click', function() { showTab(index); }); } }); // Attach click event listeners for "Previous" and "Next" navigation buttons prevButton.addEventListener('click', function() { showTab(currentTab - 1); }); nextButton.addEventListener('click', function() { showTab(currentTab + 1); }); // Attach change event listener to the recovery shape dropdown to update input visibility recoveryShapeSelect.addEventListener('change', updateShapeInputs); // Attach click event listener to the "Simulate Recovery" button simulateButton.addEventListener('click', simulateRecovery); // Attach click event listener to the "Download PDF Report" button downloadPdfButton.addEventListener('click', downloadPdf); // --- Initialization --- // Show the initial tab (Inputs & Configuration) when the tool loads showTab(0); // Update input visibility based on the default selected recovery shape updateShapeInputs(); // Function to resize the canvas dynamically based on its parent container's width // This ensures the chart is responsive and looks good on various screen sizes. function resizeCanvas() { // Get the width of the parent container of the canvas const containerWidth = recoveryChart.parentElement.clientWidth; // Set canvas width to be the minimum of container width or a max width (e.g., 800px) recoveryChart.width = Math.min(containerWidth, 800); // Maintain the aspect ratio of the canvas (e.g., 800x400 -> 2:1 aspect ratio) recoveryChart.height = recoveryChart.width * (400 / 800); // Redraw the chart with the current data when the canvas is resized drawChart(window.recoveryData || []); } // Initial call to resize canvas on load resizeCanvas(); // Attach an event listener to redraw the chart whenever the window is resized window.addEventListener('resize', resizeCanvas); // Removed dynamic loading of jspdf-autotable as it's now loaded directly in the head. // const script = document.createElement('script'); // script.src = 'https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.5.14/jspdf.autotable.min.js'; // script.onload = () => { // console.log("jsPDF-AutoTable plugin loaded successfully."); // }; // script.onerror = () => { // console.error("Failed to load jsPDF-AutoTable plugin. PDF table generation may be affected."); // }; // document.head.appendChild(script); });