`;
}
/**
* Renders the deal pipeline table in the Dashboard tab.
*/
function renderDealPipeline() {
// Null check for the deal pipeline table body
if (!dealPipelineTableBody) {
console.error("Deal pipeline table body not found.");
return;
}
dealPipelineTableBody.innerHTML = ''; // Clear existing rows
mnaData.forEach(deal => {
const row = document.createElement('tr');
row.innerHTML = `
${deal.dealName}
${deal.targetCompany}
${deal.acquirerCompany}
${formatCurrency(deal.dealValue)}
${deal.status}
${deal.stage}
${deal.announcementDate || 'N/A'}
${deal.closeDate || 'N/A'}
${deal.notes || 'N/A'}
`;
dealPipelineTableBody.appendChild(row);
});
}
/**
* Renders the data input table in the Deal Configuration tab.
*/
function renderDataInputTable() {
// Null check for the data input table body
if (!dataInputTableBody) {
console.error("Data input table body not found.");
return;
}
dataInputTableBody.innerHTML = ''; // Clear existing rows
mnaData.forEach(deal => {
const row = document.createElement('tr');
row.innerHTML = `
`;
dataInputTableBody.appendChild(row);
});
// Attach event listeners to newly created input fields and delete buttons
document.querySelectorAll('.deal-input').forEach(input => {
input.addEventListener('change', handleInputChange);
});
document.querySelectorAll('.delete-row-btn').forEach(button => {
button.addEventListener('click', handleDeleteRow);
});
}
/**
* Updates all dashboard and data configuration views.
*/
function updateViews() {
renderMnaSummary();
renderDealPipeline();
renderDataInputTable();
}
// --- Event Handlers ---
/**
* Handles tab button clicks to switch between tabs.
* @param {Event} event - The click event.
*/
function handleTabClick(event) {
// Null check for event target
if (!event.target) {
console.error("Event target is null.");
return;
}
const targetTab = event.target.dataset.tab;
// Remove 'active' class from all tab buttons and content
tabButtons.forEach(button => button.classList.remove('active'));
tabContents.forEach(content => content.classList.remove('active'));
// Add 'active' class to the clicked tab button and corresponding content
event.target.classList.add('active');
const activeTabContent = document.getElementById(`${targetTab}-tab`);
if (activeTabContent) { // Null check for activeTabContent
activeTabContent.classList.add('active');
} else {
console.error(`Tab content for ${targetTab} not found.`);
}
updateViews(); // Update views whenever tab changes
}
/**
* Handles changes in data input fields.
* @param {Event} event - The change event.
*/
function handleInputChange(event) {
// Null check for event target
if (!event.target) {
console.error("Event target is null.");
return;
}
const input = event.target;
const id = parseInt(input.dataset.id);
const field = input.dataset.field;
let value = input.value;
// Convert numeric fields to numbers
if (['dealValue'].includes(field)) {
value = parseFloat(value) || 0; // Default to 0 if parsing fails
if (value < 0) value = 0; // Ensure non-negative values
input.value = value; // Update input field with sanitized value
}
const dealIndex = mnaData.findIndex(d => d.id === id);
if (dealIndex !== -1) {
mnaData[dealIndex][field] = value;
updateViews(); // Re-render dashboard and data input tables
} else {
console.error(`Deal with ID ${id} not found.`);
}
}
/**
* Adds a new empty row to the data input table.
*/
function handleAddDeal() {
const newDeal = {
id: nextId++,
dealName: 'New Deal',
targetCompany: '',
acquirerCompany: '',
dealValue: 0.00,
status: 'Active',
stage: 'Prospecting',
announcementDate: new Date().toISOString().slice(0, 10), // Default to today's date
closeDate: '',
notes: ''
};
mnaData.push(newDeal);
updateViews(); // Re-render data input table
}
/**
* Deletes a row from the data input table.
* @param {Event} event - The click event.
*/
function handleDeleteRow(event) {
// Null check for event target
if (!event.target) {
console.error("Event target is null.");
return;
}
const idToDelete = parseInt(event.target.dataset.id);
mnaData = mnaData.filter(d => d.id !== idToDelete);
updateViews(); // Re-render data input table
}
/**
* Handles navigation to the previous tab.
*/
function handlePrevTab() {
const currentActiveTab = document.querySelector('.tab-button.active');
if (!currentActiveTab) {
console.error("No active tab found.");
return;
}
const currentIndex = Array.from(tabButtons).indexOf(currentActiveTab);
if (currentIndex > 0) {
tabButtons[currentIndex - 1].click(); // Simulate click on previous tab
}
}
/**
* Handles navigation to the next tab.
*/
function handleNextTab() {
const currentActiveTab = document.querySelector('.tab-button.active');
if (!currentActiveTab) {
console.error("No active tab found.");
return;
}
const currentIndex = Array.from(tabButtons).indexOf(currentActiveTab);
if (currentIndex < tabButtons.length - 1) {
tabButtons[currentIndex + 1].click(); // Simulate click on next tab
}
}
/**
* Handles the PDF download functionality.
* It uses html2canvas to capture the dashboard content and jsPDF to generate the PDF.
*/
async function handleDownloadPdf() {
const dashboardContent = document.querySelector('.dashboard-content-for-pdf');
if (!dashboardContent) {
console.error("Dashboard content for PDF not found.");
return;
}
// Add a temporary title for the PDF
const tempTitle = document.createElement('h1');
tempTitle.textContent = "Merger & Acquisition Dashboard";
tempTitle.style.textAlign = 'center';
tempTitle.style.marginBottom = '20px';
tempTitle.style.fontSize = '2em';
tempTitle.style.fontWeight = 'bold';
tempTitle.style.color = '#333';
dashboardContent.prepend(tempTitle); // Prepend to capture
// Temporarily hide tab navigation and buttons for PDF capture
const elementsToHide = document.querySelectorAll('.tab-nav, .tab-navigation-buttons, .add-row-btn, .delete-row-btn, .pdf-download-section .btn-primary');
elementsToHide.forEach(el => el.style.display = 'none');
// Ensure the dashboard tab is active before capturing for PDF
const dashboardTabButton = document.querySelector('.tab-button[data-tab="dashboard"]');
if (dashboardTabButton && !dashboardTabButton.classList.contains('active')) {
dashboardTabButton.click(); // Activate dashboard tab if not already active
// A small delay might be needed here if content takes time to render after tab switch
await new Promise(resolve => setTimeout(resolve, 100));
}
try {
const canvas = await html2canvas(dashboardContent, {
scale: 2, // Increase scale for better resolution
useCORS: true, // Required if images are from different origin (though we don't use external images)
windowWidth: dashboardContent.scrollWidth,
windowHeight: dashboardContent.scrollHeight
});
const imgData = canvas.toDataURL('image/png');
const { jsPDF } = window.jspdf;
const pdf = new jsPDF({
orientation: 'landscape', // Landscape for wider tables
unit: 'px',
format: 'a4'
});
const imgWidth = pdf.internal.pageSize.getWidth();
const pageHeight = pdf.internal.pageSize.getHeight();
const imgHeight = canvas.height * imgWidth / canvas.width;
let heightLeft = imgHeight;
let position = 0;
pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
while (heightLeft >= 0) {
position = heightLeft - imgHeight;
pdf.addPage();
pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
}
pdf.save('merger_acquisition_dashboard.pdf');
} catch (error) {
console.error("Error generating PDF:", error);
// Using alert as a fallback for critical error feedback, as per standard specification
alert("Failed to generate PDF. Please try again.");
} finally {
// Restore hidden elements and remove temporary title
elementsToHide.forEach(el => el.style.display = '');
if (tempTitle.parentNode) {
tempTitle.parentNode.removeChild(tempTitle);
}
}
}
// --- Event Listener Attachments ---
tabButtons.forEach(button => {
// Null check for button
if (button) {
button.addEventListener('click', handleTabClick);
}
});
// Null checks before attaching listeners
if (addDealBtn) {
addDealBtn.addEventListener('click', handleAddDeal);
} else {
console.error("Add Deal button not found.");
}
if (downloadPdfBtn) {
downloadPdfBtn.addEventListener('click', handleDownloadPdf);
} else {
console.error("Download PDF button not found.");
}
if (prevTabBtn) {
prevTabBtn.addEventListener('click', handlePrevTab);
} else {
console.error("Previous Tab button not found.");
}
if (nextTabBtn) {
nextTabBtn.addEventListener('click', handleNextTab);
} else {
console.error("Next Tab button not found.");
}
// Initial render of the dashboard and data input table
updateViews();
});
