Abandoned Cart Recovery Dashboard

Global Configuration

Add New Data Record

Data Log

Campaign/PeriodDate RangeAbandonedRecoveredValue AbandonedValue RecoveredActions

Recovery Funnel

Campaign Effectiveness (by Value)

Abandoned vs. Recovered Value

${this.formatCurrency(totals.valueAbandoned)}

Total Abandoned Value

${this.formatCurrency(totals.valueRecovered)}

Total Recovered Revenue

${abandonmentRate.toFixed(1)}%

Cart Abandonment Rate

${recoveryRate.toFixed(1)}%

Cart Recovery Rate

`; } renderFunnel(totals) { this.dom.funnelContainer.innerHTML = `
${totals.created.toLocaleString()}
Carts Created
${totals.abandoned.toLocaleString()}
Carts Abandoned
${totals.recovered.toLocaleString()}
Carts Recovered
`; } renderBarChart() { const maxValue = Math.max(...this.state.records.map(r => r.valueAbandoned)); if (maxValue === 0) { this.dom.barChartContainer.innerHTML = '

No value data to display.

'; return; } const chartHtml = this.state.records.map(r => `
${this.formatCurrency(r.valueAbandoned, true)}
${this.formatCurrency(r.valueRecovered, true)}
${this.escapeHTML(r.name)}
`).join(''); this.dom.barChartContainer.innerHTML = `
${chartHtml}
`; } renderPieChart(totals) { if (totals.valueRecovered === 0) { this.dom.pieChartContainer.innerHTML = '

No recovered value to display.

'; return; } const pieColors = ['#4A90E2', '#50E3C2', '#B8E986', '#F8E71C', '#F5A623', '#BD10E0', '#9013FE', '#4A4A4A']; let gradientString = 'conic-gradient('; let currentPercentage = 0; const legendItems = this.state.records.map((r, index) => { const percentage = (r.valueRecovered / totals.valueRecovered) * 100; const color = pieColors[index % pieColors.length]; if (percentage > 0) { gradientString += `${color} ${currentPercentage}% ${currentPercentage + percentage}%, `; currentPercentage += percentage; } return `
  • ${this.escapeHTML(r.name)} (${percentage.toFixed(1)}%)
  • `; }).join(''); gradientString = gradientString.slice(0, -2) + ')'; // Remove last comma and space this.dom.pieChartContainer.innerHTML = `
      ${legendItems}
    `; } // --- Utilities --- formatCurrency(value, compact = false) { const options = { style: 'currency', currency: 'USD', currencyDisplay: 'narrowSymbol' }; if (compact) { options.notation = 'compact'; options.maximumFractionDigits = 1; } else { options.minimumFractionDigits = 2; options.maximumFractionDigits = 2; } // Override currency symbol with user input return new Intl.NumberFormat('en-US', options).format(value).replace('$', this.state.currency); } escapeHTML(str) { const p = document.createElement('p'); p.appendChild(document.createTextNode(str)); return p.innerHTML; } openTab(event, tabName) { this.state.activeTab = tabName; this.updateUI(); } navigateTabs(direction) { const currentIndex = this.TABS_ORDER.indexOf(this.state.activeTab); let newIndex = currentIndex; if (direction === 'next' && currentIndex < this.TABS_ORDER.length - 1) newIndex++; if (direction === 'prev' && currentIndex > 0) newIndex--; this.state.activeTab = this.TABS_ORDER[newIndex]; this.updateUI(); } // --- PDF Generation --- async generatePdf() { // Ensure dashboard is rendered before capture if(this.state.activeTab !== 'dashboard') { this.state.activeTab = 'dashboard'; this.updateUI(); // Wait for render await new Promise(resolve => setTimeout(resolve, 50)); } const { jsPDF } = window.jspdf; const pdfOutputElement = this.dom.pdfOutput; this.dom.downloadPdfBtn.disabled = true; this.dom.downloadPdfBtn.textContent = 'Generating PDF...'; try { const canvas = await html2canvas(pdfOutputElement, { scale: 2, useCORS: true, logging: false, backgroundColor: '#ffffff' }); const imgData = canvas.toDataURL('image/png'); const pdf = new jsPDF({ orientation: 'portrait', unit: 'pt', format: 'a4' }); const pdfWidth = pdf.internal.pageSize.getWidth(); const pdfHeight = pdf.internal.pageSize.getHeight(); const canvasAspectRatio = canvas.width / canvas.height; const margin = 40; const imgWidth = pdfWidth - (margin * 2); const imgHeight = imgWidth / canvasAspectRatio; let heightLeft = imgHeight; let position = margin; pdf.addImage(imgData, 'PNG', margin, position, imgWidth, imgHeight); heightLeft -= (pdfHeight - margin * 2); while (heightLeft > 0) { position = -heightLeft - margin; pdf.addPage(); pdf.addImage(imgData, 'PNG', margin, position, imgWidth, imgHeight); heightLeft -= pdfHeight; } const fileName = `${this.state.dashboardTitle.replace(/\s+/g, '_')}.pdf`; pdf.save(fileName); } catch (error) { console.error("PDF Generation Error:", error); alert("An error occurred while creating the PDF."); } finally { this.dom.downloadPdfBtn.disabled = false; this.dom.downloadPdfBtn.textContent = 'Download Dashboard as PDF'; } } } const app = new CartRecoveryDashboard();
    Scroll to Top