Customer Nurturing Campaign Dashboard

Customer Nurturing Campaign Dashboard

Campaign Performance Summary

All Nurturing Campaigns

Campaign Name Start Date End Date Status Target Segment Leads Nurtured Converted Customers Conversion Rate (%) Engagement Rate (%) Cost ($) ROI (%)

${item.title}

${item.value}

`; dashboardSummary.appendChild(card); }); // Populate campaigns table (sorted by start date descending) const sortedCampaignData = [...campaignData].sort((a, b) => new Date(b.startDate) - new Date(a.startDate)); sortedCampaignData.forEach(item => { const conversionRate = item.leadsNurtured > 0 ? ((item.convertedCustomers / item.leadsNurtured) * 100).toFixed(2) : '0.00'; const roi = calculateROI(item.convertedCustomers, item.cost).toFixed(2); const row = campaignsTableBody.insertRow(); row.innerHTML = ` ${item.campaignName} ${item.startDate} ${item.endDate} ${item.status} ${item.targetSegment} ${item.leadsNurtured.toLocaleString()} ${item.convertedCustomers.toLocaleString()} ${conversionRate}% ${item.engagementRate.toFixed(2)}% $${item.cost.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} ${roi}% `; }); } // --- Data Configuration Table Rendering and Editing --- /** * Renders the data in the configuration table. */ function renderConfigTable() { // Null check for config data table body if (!configDataTableBody) { console.error("Config data table body not found."); return; } configDataTableBody.innerHTML = ''; // Clear existing rows campaignData.forEach((item, index) => { const row = configDataTableBody.insertRow(); row.innerHTML = ` ${item.campaignName} ${item.startDate} ${item.endDate} ${item.status} ${item.targetSegment} ${item.leadsNurtured.toLocaleString()} ${item.convertedCustomers.toLocaleString()} ${item.engagementRate.toFixed(2)}% $${item.cost.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })} `; }); } /** * Adds a new row or updates an existing row in the campaign data. */ window.addRow = function() { // Null checks for input fields if (!campaignNameInput || !startDateInput || !endDateInput || !campaignStatusSelect || !targetSegmentSelect || !leadsNurturedInput || !convertedCustomersInput || !engagementRateInput || !campaignCostInput || !notesInput) { console.error("One or more input fields not found."); return; } const newCampaign = { campaignName: campaignNameInput.value.trim(), startDate: startDateInput.value.trim(), endDate: endDateInput.value.trim(), status: campaignStatusSelect.value.trim(), targetSegment: targetSegmentSelect.value.trim(), leadsNurtured: parseInt(leadsNurturedInput.value) || 0, convertedCustomers: parseInt(convertedCustomersInput.value) || 0, engagementRate: parseFloat(engagementRateInput.value) || 0, cost: parseFloat(campaignCostInput.value) || 0, notes: notesInput.value.trim() }; // Simple validation if (!newCampaign.campaignName || !newCampaign.startDate || !newCampaign.endDate || newCampaign.leadsNurtured < 0 || newCampaign.convertedCustomers < 0 || newCampaign.engagementRate < 0 || newCampaign.engagementRate > 100 || newCampaign.cost < 0) { showModal("Please fill in all required fields with valid values (e.g., Campaign Name, Dates, positive numbers for metrics, Engagement Rate between 0-100)."); return; } // Check for duplicate Campaign Name if adding a new row if (editingIndex === -1) { const isDuplicate = campaignData.some(campaign => campaign.campaignName === newCampaign.campaignName); if (isDuplicate) { showModal(`Campaign "${newCampaign.campaignName}" already exists. Please use a unique name.`); return; } campaignData.push(newCampaign); } else { // Update existing row campaignData[editingIndex] = newCampaign; editingIndex = -1; // Reset editing state updateRowButton.classList.add('hidden'); cancelEditButton.classList.add('hidden'); addRowButton.classList.remove('hidden'); campaignNameInput.disabled = false; // Re-enable campaign name for new entries } clearForm(); renderConfigTable(); updateDashboard(); // Keep dashboard in sync }; /** * Populates the form with data from the row to be edited. * @param {HTMLElement} button - The edit button clicked. */ window.editRow = function(button) { // Null checks for input fields and edit buttons if (!campaignNameInput || !startDateInput || !endDateInput || !campaignStatusSelect || !targetSegmentSelect || !leadsNurturedInput || !convertedCustomersInput || !engagementRateInput || !campaignCostInput || !notesInput || !addRowButton || !updateRowButton || !cancelEditButton) { console.error("One or more form/button elements not found for editing."); return; } const index = parseInt(button.dataset.index); editingIndex = index; const item = campaignData[index]; campaignNameInput.value = item.campaignName; startDateInput.value = item.startDate; endDateInput.value = item.endDate; campaignStatusSelect.value = item.status; targetSegmentSelect.value = item.targetSegment; leadsNurturedInput.value = item.leadsNurtured; convertedCustomersInput.value = item.convertedCustomers; engagementRateInput.value = item.engagementRate; campaignCostInput.value = item.cost; notesInput.value = item.notes; addRowButton.classList.add('hidden'); updateRowButton.classList.remove('hidden'); cancelEditButton.classList.remove('hidden'); campaignNameInput.disabled = true; // Disable editing Campaign Name during update }; /** * Deletes a row from the campaign data. * @param {HTMLElement} button - The delete button clicked. */ window.deleteRow = function(button) { const index = parseInt(button.dataset.index); showModal("Are you sure you want to delete this campaign record?", true, (confirmed) => { if (confirmed) { campaignData.splice(index, 1); renderConfigTable(); updateDashboard(); // Keep dashboard in sync clearForm(); // Clear form if the deleted item was being edited editingIndex = -1; updateRowButton.classList.add('hidden'); cancelEditButton.classList.add('hidden'); addRowButton.classList.remove('hidden'); campaignNameInput.disabled = false; // Re-enable campaign name } }); }; /** * Clears the input form fields. */ function clearForm() { // Null checks for input fields if (!campaignNameInput || !startDateInput || !endDateInput || !campaignStatusSelect || !targetSegmentSelect || !leadsNurturedInput || !convertedCustomersInput || !engagementRateInput || !campaignCostInput || !notesInput) { console.error("One or more input fields not found for clearing."); return; } campaignNameInput.value = ''; startDateInput.value = ''; endDateInput.value = ''; campaignStatusSelect.value = 'Planned'; // Reset to default status targetSegmentSelect.value = 'New Leads'; // Reset to default segment leadsNurturedInput.value = ''; convertedCustomersInput.value = ''; engagementRateInput.value = ''; campaignCostInput.value = ''; notesInput.value = ''; } /** * Cancels the current edit operation and clears the form. */ window.cancelEdit = function() { editingIndex = -1; clearForm(); updateRowButton.classList.add('hidden'); cancelEditButton.classList.add('hidden'); addRowButton.classList.remove('hidden'); campaignNameInput.disabled = false; // Re-enable campaign name }; // --- PDF Download Functionality --- /** * Downloads the dashboard content as a PDF. */ window.downloadPdf = async function() { // Null check for dashboard content if (!dashboardTabContent) { console.error("Dashboard content for PDF not found."); return; } // Temporarily show the dashboard content if it's hidden, for accurate PDF generation const wasHidden = dashboardTabContent.classList.contains('hidden'); if (wasHidden) { dashboardTabContent.classList.remove('hidden'); } const { jsPDF } = window.jspdf; const doc = new jsPDF('p', 'pt', 'letter'); // 'p' for portrait, 'pt' for points, 'letter' size const element = dashboardTabContent; // The element to convert to PDF // Use html2canvas to render the HTML element to a canvas await html2canvas(element, { scale: 2, // Increase scale for better quality useCORS: true, // Important for images/external resources if any (though we don't use them here) logging: false // Disable logging for cleaner console }).then(canvas => { const imgData = canvas.toDataURL('image/png'); const imgWidth = 550; // A fixed width for the image in PDF const pageHeight = doc.internal.pageSize.height; const imgHeight = (canvas.height * imgWidth) / canvas.width; let heightLeft = imgHeight; let position = 30; // Initial Y position for content doc.setFontSize(22); doc.setTextColor(31, 41, 55); // Gray 900 equivalent doc.text("Customer Nurturing Campaign Dashboard", doc.internal.pageSize.getWidth() / 2, position, { align: "center" }); position += 30; // Move down for image doc.addImage(imgData, 'PNG', 30, position, imgWidth, imgHeight); heightLeft -= pageHeight; // If content overflows, add new pages while (heightLeft >= 0) { position = heightLeft - imgHeight + 30; // Adjust position for new page doc.addPage(); doc.addImage(imgData, 'PNG', 30, position, imgWidth, imgHeight); heightLeft -= pageHeight; } doc.save('Customer_Nurturing_Campaign_Dashboard.pdf'); }); // Restore hidden state if it was hidden initially if (wasHidden) { dashboardTabContent.classList.add('hidden'); } }; // --- Event Listeners --- if (addRowButton) { addRowButton.addEventListener('click', addRow); } else { console.error("Add Row Button not found."); } if (updateRowButton) { updateRowButton.addEventListener('click', addRow); // addRow function handles both add and update based on editingIndex } else { console.error("Update Row Button not found."); } if (cancelEditButton) { cancelEditButton.addEventListener('click', cancelEdit); } else { console.error("Cancel Edit Button not found."); } // Initial rendering and setup openTab('dashboard'); // Open dashboard by default updateDashboard(); // Populate dashboard with initial data renderConfigTable(); // Populate config table with initial data });
Scroll to Top