Paywall Dashboard

Paywall Performance Dashboard

Analyze subscription revenue, conversions, and churn.

Key Performance Indicators

Revenue Over Time

Subscribers by Tier

Recent Subscription Events

Date Subscription Tier Revenue Status

Enter Subscription Data

${formatCurrency(totalRevenue)}

Conversion Rate

${conversionRate.toFixed(2)}%

ARPU

${formatCurrency(arpu)}

Churn Rate

${churnRate.toFixed(1)}%

`; }; const renderDataTable = () => { dataTableBody.innerHTML = ''; const sortedLogs = [...state.logs].sort((a, b) => new Date(b.date) - new Date(a.date)); sortedLogs.forEach(log => { const row = ` ${log.date} ${log.tier} ${formatCurrency(log.revenue)} ${log.status} `; dataTableBody.innerHTML += row; }); }; const renderCharts = () => { // Revenue Trend Chart const revenueByDate = state.logs.reduce((acc, log) => { if (log.status === 'New') { acc[log.date] = (acc[log.date] || 0) + log.revenue; } return acc; }, {}); const sortedDates = Object.keys(revenueByDate).sort((a, b) => new Date(a) - new Date(b)); if (revenueChart) revenueChart.destroy(); revenueChart = new Chart(revenueCanvas.getContext('2d'), { type: 'line', data: { labels: sortedDates, datasets: [{ label: 'Daily Revenue', data: sortedDates.map(date => revenueByDate[date]), borderColor: '#db2777', backgroundColor: 'rgba(219, 39, 119, 0.1)', fill: true, tension: 0.1 }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { type: 'time', time: { unit: 'day' } }, y: { beginAtZero: true, ticks: { callback: (value) => formatCurrency(value) } } } } }); // Tier Chart const subsByTier = state.logs.filter(log => log.status === 'New').reduce((acc, log) => { acc[log.tier] = (acc[log.tier] || 0) + 1; return acc; }, {}); const tierLabels = Object.keys(subsByTier); const tierData = tierLabels.map(label => subsByTier[label]); if (tierChart) tierChart.destroy(); tierChart = new Chart(tierCanvas.getContext('2d'), { type: 'doughnut', data: { labels: tierLabels, datasets: [{ data: tierData, backgroundColor: ['#f0abfc', '#f9a8d4', '#fda4af'] }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'top' } } } }); }; const renderConfigRows = () => { configRowsContainer.innerHTML = ''; const sortedLogs = [...state.logs].sort((a, b) => new Date(b.date) - new Date(a.date)); sortedLogs.forEach(log => { configRowsContainer.innerHTML += `
`.replaceAll('class="config-input"', 'class="config-input w-full border-gray-300 rounded-md shadow-sm"'); }); addConfigEventListeners(); }; // --- EVENT HANDLERS --- const handleConfigChange = (e) => { const id = parseInt(e.target.dataset.id); const field = e.target.dataset.field; const value = (e.target.type === 'number') ? parseFloat(e.target.value) || 0 : e.target.value; const log = state.logs.find(l => l.id === id); if (log) log[field] = value; renderAll(); }; const handleAddLog = () => { const newId = state.logs.length > 0 ? Math.max(...state.logs.map(l => l.id)) + 1 : 1; const today = new Date().toISOString().split('T')[0]; state.logs.push({ id: newId, date: today, tier: "Basic", revenue: 9.99, status: "New" }); renderAll(); }; const handleRemoveLog = (e) => { const id = parseInt(e.target.dataset.id); state.logs = state.logs.filter(l => l.id !== id); renderAll(); }; const addConfigEventListeners = () => { document.querySelectorAll('.config-input').forEach(input => input.addEventListener('change', handleConfigChange)); document.querySelectorAll('.remove-btn').forEach(button => button.addEventListener('click', handleRemoveLog)); }; const handleDownloadPdf = () => { const { jsPDF } = window.jspdf; const pdfContent = document.getElementById('pdf-content'); document.querySelectorAll('.no-print').forEach(el => el.style.visibility = 'hidden'); Chart.defaults.animation = false; html2canvas(pdfContent, { scale: 2, useCORS: true, backgroundColor: '#ffffff' }).then(canvas => { document.querySelectorAll('.no-print').forEach(el => el.style.visibility = 'visible'); Chart.defaults.animation = true; const imgData = canvas.toDataURL('image/png'); const pdf = new jsPDF({ orientation: 'p', unit: 'mm', format: 'a4' }); const pdfWidth = pdf.internal.pageSize.getWidth(); const imgWidth = pdfWidth - 20; const imgHeight = canvas.height * imgWidth / canvas.width; pdf.addImage(imgData, 'PNG', 10, 10, imgWidth, imgHeight); pdf.save('Paywall-Performance-Dashboard.pdf'); }); }; // --- TABBING LOGIC --- let currentTabIndex = 0; const updateTabButtons = () => { prevTabBtn.disabled = currentTabIndex === 0; nextTabBtn.disabled = currentTabIndex === tabContents.length - 1; }; const switchTab = (index) => { tabButtons.forEach(btn => btn.classList.remove('active')); tabContents.forEach(content => content.classList.remove('active')); tabButtons[index].classList.add('active'); tabContents[index].classList.add('active'); currentTabIndex = index; updateTabButtons(); }; tabButtons.forEach((button, index) => button.addEventListener('click', () => switchTab(index))); prevTabBtn.addEventListener('click', () => { if (currentTabIndex > 0) switchTab(currentTabIndex - 1); }); nextTabBtn.addEventListener('click', () => { if (currentTabIndex < tabContents.length - 1) switchTab(currentTabIndex + 1); }); // --- INITIALIZATION --- if (kpiContainer && addLogBtn && downloadPdfBtn) { addLogBtn.addEventListener('click', handleAddLog); downloadPdfBtn.addEventListener('click', handleDownloadPdf); renderAll(); updateTabButtons(); } else { console.error("Essential dashboard elements could not be found."); } });
Scroll to Top