Display Advertising Dashboard

Display Advertising Dashboard

Overall Campaign Performance Summary

Individual Campaign Performance

Campaign Name Start Date End Date Impressions Clicks Conversions Spend ($) CTR (%) Conversion Rate (%) CPA ($) Actions

${totalCampaigns}

Total Impressions

${totalImpressions.toLocaleString()}

Total Clicks

${totalClicks.toLocaleString()}

Total Conversions

${totalConversions.toLocaleString()}

Total Spend

${totalSpend.toLocaleString('en-US', { style: 'currency', currency: 'USD' })}

Overall CTR

${overallCTR.toFixed(2)}%

Overall Conversion Rate

${overallConversionRate.toFixed(2)}%

Overall CPA

${overallCPA === Infinity ? 'N/A' : overallCPA.toLocaleString('en-US', { style: 'currency', currency: 'USD' })}

`; overallSummaryDiv.innerHTML = summaryHtml; } /** * Renders the table of all display advertising campaigns. */ function renderCampaignsTable() { if (!campaignsTableBody) return; // Null check campaignsTableBody.innerHTML = ''; // Clear existing rows // Sort data by start date in descending order for display const sortedCampaigns = [...campaigns].sort((a, b) => new Date(b.startDate) - new Date(a.startDate)); sortedCampaigns.forEach(campaign => { const ctr = calculateCTR(campaign.clicks, campaign.impressions); const conversionRate = calculateConversionRate(campaign.conversions, campaign.clicks); const cpa = calculateCPA(campaign.spend, campaign.conversions); const row = campaignsTableBody.insertRow(); row.innerHTML = ` ${campaign.campaignName} ${campaign.startDate || 'N/A'} ${campaign.endDate || 'N/A'} ${campaign.impressions.toLocaleString()} ${campaign.clicks.toLocaleString()} ${campaign.conversions.toLocaleString()} ${campaign.spend.toLocaleString('en-US', { style: 'currency', currency: 'USD' })} ${ctr.toFixed(2)}% ${conversionRate.toFixed(2)}% ${cpa === Infinity ? 'N/A' : cpa.toLocaleString('en-US', { style: 'currency', currency: 'USD' })} `; }); } /** * Renders the entire dashboard content. */ function renderDashboard() { renderOverallSummary(); renderCampaignsTable(); } // --- Data Configuration Logic --- /** * Generates a unique ID for a new campaign entry. * @returns {string} A unique ID. */ function generateUniqueId() { return 'dad' + Date.now() + Math.floor(Math.random() * 1000); } // Add/Update Campaign Form Submission if (addUpdateCampaignForm) { addUpdateCampaignForm.addEventListener('submit', function(event) { event.preventDefault(); // Prevent default form submission const id = document.getElementById('campaignId').value; const campaignName = document.getElementById('campaignName').value; const startDate = document.getElementById('startDate').value; const endDate = document.getElementById('endDate').value; const impressions = parseInt(document.getElementById('impressions').value); const clicks = parseInt(document.getElementById('clicks').value); const conversions = parseInt(document.getElementById('conversions').value); const spend = parseFloat(document.getElementById('spend').value); // Basic validation if (!campaignName.trim()) { showCustomModal('Input Error', 'Campaign Name cannot be empty.'); return; } if (isNaN(impressions) || impressions < 0 || isNaN(clicks) || clicks < 0 || isNaN(conversions) || conversions < 0 || isNaN(spend) || spend < 0) { showCustomModal('Input Error', 'Please ensure all numerical fields are filled correctly with non-negative numbers.'); return; } if (clicks > impressions) { showCustomModal('Input Error', 'Clicks cannot be greater than Impressions.'); return; } if (conversions > clicks) { showCustomModal('Input Error', 'Conversions cannot be greater than Clicks.'); return; } if (startDate && endDate && new Date(startDate) > new Date(endDate)) { showCustomModal('Input Error', 'End Date cannot be before Start Date.'); return; } const newCampaign = { id: id || generateUniqueId(), // Use existing ID if editing, otherwise generate new campaignName: campaignName, startDate: startDate, endDate: endDate, impressions: impressions, clicks: clicks, conversions: conversions, spend: spend }; if (id) { // Update existing entry const index = campaigns.findIndex(entry => entry.id === id); if (index > -1) { campaigns[index] = newCampaign; } else { console.error('Campaign data entry not found for update:', id); } } else { // Add new entry campaigns.push(newCampaign); } renderDashboard(); // Re-render dashboard with new/updated data addUpdateCampaignForm.reset(); // Clear the form campaignIdInput.value = ''; // Clear hidden ID formSubmitBtn.textContent = 'Add Campaign Data'; // Reset button text switchTab('dashboard'); // Switch back to dashboard to see changes }); } /** * Populates the form for editing an existing campaign entry. * @param {string} id - The ID of the campaign entry to edit. */ window.editCampaign = function(id) { const entryToEdit = campaigns.find(entry => entry.id === id); if (!entryToEdit) { console.error('Campaign data entry not found for editing:', id); return; } document.getElementById('campaignId').value = entryToEdit.id; document.getElementById('campaignName').value = entryToEdit.campaignName; document.getElementById('startDate').value = entryToEdit.startDate; document.getElementById('endDate').value = entryToEdit.endDate; document.getElementById('impressions').value = entryToEdit.impressions; document.getElementById('clicks').value = entryToEdit.clicks; document.getElementById('conversions').value = entryToEdit.conversions; document.getElementById('spend').value = entryToEdit.spend; formSubmitBtn.textContent = 'Update Campaign Data'; // Change button text switchTab('data-config'); // Switch to data config tab }; /** * Deletes a campaign entry from the array after confirmation. * @param {string} id - The ID of the campaign entry to delete. */ window.deleteCampaign = function(id) { showCustomModal('Confirm Deletion', 'Are you sure you want to delete this campaign data entry?', () => { campaigns = campaigns.filter(entry => entry.id !== id); renderDashboard(); // Re-render dashboard }); }; // --- Custom Modal Logic (replaces alert/confirm) --- let currentConfirmCallback = null; /** * Shows a custom modal with a message and optional confirmation. * @param {string} title - The title of the modal. * @param {string} message - The message to display. * @param {function} [onConfirm] - Callback function to execute on confirm. If null, it's an alert. */ function showCustomModal(title, message, onConfirm = null) { if (!confirmModal || !modalTitle || !modalMessage || !confirmActionBtn) return; modalTitle.textContent = title; modalMessage.textContent = message; if (onConfirm) { confirmActionBtn.style.display = 'inline-block'; // Show confirm button confirmActionBtn.textContent = 'Confirm'; confirmActionBtn.classList.remove('btn-primary'); confirmActionBtn.classList.add('btn-danger'); currentConfirmCallback = onConfirm; } else { confirmActionBtn.style.display = 'none'; // Hide confirm button for alerts currentConfirmCallback = null; } confirmModal.style.display = 'flex'; // Show 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'; } }; // Event listener for the custom modal's confirm button if (confirmActionBtn) { confirmActionBtn.addEventListener('click', function() { if (currentConfirmCallback) { currentConfirmCallback(); } closeModal('confirmModal'); }); } // Close modal when clicking outside of it window.onclick = function(event) { if (event.target === confirmModal) { closeModal('confirmModal'); } }; // --- 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('Display_Advertising_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(); });
Scroll to Top