`;
// Order of stages for display
const orderedStages = ['Prospecting', 'Qualification', 'Proposal', 'Negotiation', 'Closed Won', 'Closed Lost'];
orderedStages.forEach(stage => {
const data = stageSummary[stage] || { count: 0, totalValue: 0 };
const valueFormatted = data.totalValue.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
summaryHtml += `
`;
});
dealSummaryDiv.innerHTML = summaryHtml;
}
/**
* Renders the table of all deals.
*/
function renderDealsTable() {
if (!dealsTableBody) return; // Null check
dealsTableBody.innerHTML = ''; // Clear existing rows
deals.forEach(deal => {
const row = dealsTableBody.insertRow();
row.innerHTML = `
${deal.dealName}
${deal.stage}
${deal.value.toLocaleString('en-US', { style: 'currency', currency: 'USD' })}
${deal.closeDate || 'N/A'}
${deal.notes || 'N/A'}
`;
});
}
/**
* Renders the entire dashboard content.
*/
function renderDashboard() {
renderDealSummary();
renderDealsTable();
}
// --- Data Configuration Logic ---
/**
* Generates a unique ID for a new deal.
* @returns {string} A unique ID.
*/
function generateUniqueId() {
return 'd' + Date.now() + Math.floor(Math.random() * 1000);
}
// Add Deal Form Submission
if (addDealForm) {
addDealForm.addEventListener('submit', function(event) {
event.preventDefault(); // Prevent default form submission
const newDeal = {
id: generateUniqueId(),
dealName: document.getElementById('dealName').value,
stage: document.getElementById('dealStage').value,
value: parseFloat(document.getElementById('dealValue').value),
closeDate: document.getElementById('dealCloseDate').value,
notes: document.getElementById('dealNotes').value
};
deals.push(newDeal); // Add new deal to the array
renderDashboard(); // Re-render dashboard with new data
addDealForm.reset(); // Clear the form
switchTab('dashboard'); // Switch back to dashboard to see changes
});
}
// --- Edit Deal Modal Logic ---
/**
* Opens the edit modal and populates it with deal data.
* @param {string} id - The ID of the deal to edit.
*/
window.openEditModal = function(id) {
const dealToEdit = deals.find(deal => deal.id === id);
if (!dealToEdit || !editDealModal || !editDealForm) return; // Null check
document.getElementById('editDealId').value = dealToEdit.id;
document.getElementById('editDealName').value = dealToEdit.dealName;
document.getElementById('editDealStage').value = dealToEdit.stage;
document.getElementById('editDealValue').value = dealToEdit.value;
document.getElementById('editDealCloseDate').value = dealToEdit.closeDate;
document.getElementById('editDealNotes').value = dealToEdit.notes;
editDealModal.style.display = 'flex'; // Show the modal
};
/**
* Closes the specified modal.
* @param {string} modalId - The ID of the modal to close.
*/
window.closeModal = function(modalId) {
const modal = document.getElementById(modalId);
if (modal) {
modal.style.display = 'none';
}
};
// Close modal when clicking outside of it
window.onclick = function(event) {
if (event.target === editDealModal) {
closeModal('editDealModal');
}
};
// Edit Deal Form Submission
if (editDealForm) {
editDealForm.addEventListener('submit', function(event) {
event.preventDefault(); // Prevent default form submission
const updatedDealId = document.getElementById('editDealId').value;
const dealIndex = deals.findIndex(deal => deal.id === updatedDealId);
if (dealIndex > -1) {
deals[dealIndex] = {
id: updatedDealId,
dealName: document.getElementById('editDealName').value,
stage: document.getElementById('editDealStage').value,
value: parseFloat(document.getElementById('editDealValue').value),
closeDate: document.getElementById('editDealCloseDate').value,
notes: document.getElementById('editDealNotes').value
};
renderDashboard(); // Re-render dashboard with updated data
closeModal('editDealModal'); // Close the modal
} else {
console.error('Deal not found for update:', updatedDealId);
}
});
}
/**
* Deletes a deal from the array.
* @param {string} id - The ID of the deal to delete.
*/
window.deleteDeal = function(id) {
// Using a custom message box instead of alert/confirm
const confirmDelete = window.confirm('Are you sure you want to delete this deal?');
if (confirmDelete) {
deals = deals.filter(deal => deal.id !== id);
renderDashboard(); // Re-render dashboard
}
};
// --- PDF Download Logic ---
/**
* Downloads the dashboard content as a PDF.
*/
if (downloadPdfBtn) {
downloadPdfBtn.addEventListener('click', function() {
const input = document.getElementById('dashboardTabContent'); // Target the dashboard content
if (!input) {
console.error('Dashboard content element not found for PDF generation.');
return;
}
// Temporarily hide elements that should not be in the PDF
const elementsToHide = input.querySelectorAll('.no-print');
elementsToHide.forEach(el => el.style.display = 'none');
// Use html2canvas to capture the content as an image
html2canvas(input, {
scale: 2, // Increase scale for better quality
useCORS: true, // Needed if external images are used (not in this case, but good practice)
logging: false // Disable logging for cleaner console
}).then(canvas => {
const imgData = canvas.toDataURL('image/png');
const pdf = new window.jspdf.jsPDF('p', 'mm', 'a4'); // 'p' for portrait, 'mm' for units, 'a4' for size
const imgWidth = 210; // A4 width in mm
const pageHeight = 297; // A4 height in mm
const imgHeight = canvas.height * imgWidth / canvas.width;
let heightLeft = imgHeight;
let position = 0;
// Add image to PDF
pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
// If content spans multiple pages, add new pages
while (heightLeft >= 0) {
position = heightLeft - imgHeight;
pdf.addPage();
pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
}
pdf.save('Deal_Stage_Dashboard.pdf');
// Restore visibility of hidden elements
elementsToHide.forEach(el => el.style.display = '');
}).catch(error => {
console.error('Error generating PDF:', error);
// Restore visibility of hidden elements in case of error
elementsToHide.forEach(el => el.style.display = '');
});
});
}
// Initial render of the dashboard when the page loads
renderDashboard();
});
${stage}
${data.count} Deals
Value: ${valueFormatted}
