Loyalty Program Dashboard

Loyalty Program Dashboard

Total Members

7,843

Active Members (30d)

4,120

Redemption Rate

62%

Total Points Redeemed

1.2M

Member Growth (Last 6 Months)

Top Redeemed Rewards

Recent Member Activity

${act.member} ${act.action}

${act.time}

`; elements.recentActivityList.appendChild(div); }); } function renderMemberTable() { if (!elements.membersTableBody) return; elements.membersTableBody.innerHTML = ''; dashboardState.members.forEach(member => { const tr = document.createElement('tr'); tr.innerHTML = ` ${member.name} ${member.points.toLocaleString('en-US')} ${member.since} `; elements.membersTableBody.appendChild(tr); }); } window.addMember = () => { const name = elements.memberNameInput.value.trim(); const points = parseInt(elements.memberPointsInput.value, 10); if (!name || isNaN(points)) { alert('Please enter a valid name and points value.'); return; } const newMember = { id: Date.now(), name, points, since: new Date().toISOString().split('T')[0] }; dashboardState.members.unshift(newMember); renderMemberTable(); elements.memberNameInput.value = ''; elements.memberPointsInput.value = '100'; }; window.deleteMember = (id) => { dashboardState.members = dashboardState.members.filter(m => m.id !== id); renderMemberTable(); }; function populateConfigForm() { const { kpis } = dashboardState; if (elements.configTotalMembers) elements.configTotalMembers.value = kpis.totalMembers; if (elements.configActiveMembers) elements.configActiveMembers.value = kpis.activeMembers; if (elements.configRedemptionRate) elements.configRedemptionRate.value = kpis.redemptionRate; if (elements.configPointsRedeemed) elements.configPointsRedeemed.value = kpis.pointsRedeemed; renderRewardsConfigFields(); } function renderRewardsConfigFields() { if (!elements.rewardsConfigList) return; elements.rewardsConfigList.innerHTML = ''; dashboardState.rewards.forEach(reward => { addRewardField(reward); }); } window.addRewardField = (reward = { id: Date.now(), name: '', redeemed: 0 }) => { if (!elements.rewardsConfigList) return; const div = document.createElement('div'); div.className = 'grid grid-cols-1 md:grid-cols-7 gap-4 items-center reward-field'; div.dataset.id = reward.id; div.innerHTML = `
`; elements.rewardsConfigList.appendChild(div); }; window.updateDashboardData = () => { dashboardState.kpis.totalMembers = parseInt(elements.configTotalMembers.value, 10) || 0; dashboardState.kpis.activeMembers = parseInt(elements.configActiveMembers.value, 10) || 0; dashboardState.kpis.redemptionRate = parseInt(elements.configRedemptionRate.value, 10) || 0; dashboardState.kpis.pointsRedeemed = elements.configPointsRedeemed.value || "0"; const newRewards = []; document.querySelectorAll('.reward-field').forEach(field => { const nameInput = field.querySelector('.reward-name-input'); const redeemedInput = field.querySelector('.reward-redeemed-input'); if (nameInput.value.trim()) { newRewards.push({ id: parseInt(field.dataset.id, 10), name: nameInput.value.trim(), redeemed: parseInt(redeemedInput.value, 10) || 0 }); } }); dashboardState.rewards = newRewards; renderDashboard(); alert('Dashboard has been updated!'); changeTab('dashboard'); }; /** * Generates and downloads a beautiful, well-structured PDF of the dashboard content. */ window.downloadPDF = async () => { const { jsPDF } = window.jspdf; const pdf = new jsPDF({ orientation: 'p', unit: 'mm', format: 'a4' }); // --- Document Settings --- const docWidth = pdf.internal.pageSize.getWidth(); const margin = 15; let cursorY = margin; // --- Title --- pdf.setFont('helvetica', 'bold'); pdf.setFontSize(22); pdf.setTextColor('#374151'); // gray-700 pdf.text('Loyalty Program Dashboard Report', docWidth / 2, cursorY, { align: 'center' }); cursorY += 15; // --- KPIs Section --- pdf.setFont('helvetica', 'bold'); pdf.setFontSize(16); pdf.setTextColor('#4f46e5'); // indigo-600 pdf.text('Key Performance Indicators', margin, cursorY); cursorY += 8; const kpiData = [ { title: 'Total Members', value: dashboardState.kpis.totalMembers.toLocaleString('en-US') }, { title: 'Active Members (30d)', value: dashboardState.kpis.activeMembers.toLocaleString('en-US') }, { title: 'Redemption Rate', value: `${dashboardState.kpis.redemptionRate}%` }, { title: 'Total Points Redeemed', value: dashboardState.kpis.pointsRedeemed } ]; pdf.setFont('helvetica', 'normal'); pdf.setFontSize(11); pdf.setTextColor('#1f2937'); // gray-800 let kpiX = margin; const kpiBoxWidth = (docWidth - margin * 2) / 4; kpiData.forEach(kpi => { pdf.setFont('helvetica', 'bold'); pdf.text(kpi.value, kpiX, cursorY); pdf.setFont('helvetica', 'normal'); pdf.setTextColor('#6b7280'); // gray-500 pdf.text(kpi.title, kpiX, cursorY + 5); pdf.setTextColor('#1f2937'); // gray-800 kpiX += kpiBoxWidth; }); cursorY += 15; // --- Charts Section --- pdf.setFont('helvetica', 'bold'); pdf.setFontSize(16); pdf.setTextColor('#4f46e5'); pdf.text('Visual Analytics', margin, cursorY); cursorY += 8; const chart1Element = document.getElementById('memberGrowthChart'); const chart2Element = document.getElementById('topRewardsChart'); try { const chart1Canvas = await html2canvas(chart1Element, { scale: 3, backgroundColor: null }); const chart2Canvas = await html2canvas(chart2Element, { scale: 3, backgroundColor: null }); const chart1Data = chart1Canvas.toDataURL('image/png'); const chart2Data = chart2Canvas.toDataURL('image/png'); const chart1Width = 110; const chart1Height = (chart1Canvas.height * chart1Width) / chart1Canvas.width; const chart2Width = docWidth - chart1Width - margin * 3; const chart2Height = (chart2Canvas.height * chart2Width) / chart2Canvas.width; const maxChartHeight = Math.max(chart1Height, chart2Height); pdf.setFontSize(12); pdf.setTextColor('#374151'); pdf.text('Member Growth (Last 6 Months)', margin, cursorY); pdf.addImage(chart1Data, 'PNG', margin, cursorY + 2, chart1Width, chart1Height); pdf.text('Top Redeemed Rewards', margin + chart1Width + 10, cursorY); pdf.addImage(chart2Data, 'PNG', margin + chart1Width + 10, cursorY + 2, chart2Width, chart2Height); cursorY += maxChartHeight + 15; } catch (error) { console.error("Error capturing charts with html2canvas:", error); pdf.setTextColor('#ef4444'); // red-500 pdf.text('Could not render charts.', margin, cursorY); cursorY += 10; } // --- Recent Activity Section --- pdf.addPage(); cursorY = margin; pdf.setFont('helvetica', 'bold'); pdf.setFontSize(16); pdf.setTextColor('#4f46e5'); pdf.text('Recent Member Activity', margin, cursorY); cursorY += 10; pdf.setFont('helvetica', 'normal'); pdf.setFontSize(10); pdf.setTextColor('#374151'); dashboardState.activity.forEach(act => { if (cursorY > 270) { // Check for page break pdf.addPage(); cursorY = margin; } const activityText = `${act.member} ${act.action}`; const timeText = act.time; pdf.text(activityText, margin, cursorY); pdf.text(timeText, docWidth - margin, cursorY, { align: 'right' }); cursorY += 7; pdf.setDrawColor('#e5e7eb'); // gray-200 pdf.line(margin, cursorY - 3, docWidth - margin, cursorY - 3); }); // --- Footer --- const pageCount = pdf.internal.getNumberOfPages(); for (let i = 1; i <= pageCount; i++) { pdf.setPage(i); pdf.setFontSize(8); pdf.setTextColor('#6b7280'); const footerText = `Page ${i} of ${pageCount} | Generated on: ${new Date().toLocaleDateString()}`; pdf.text(footerText, docWidth / 2, 287, { align: 'center' }); } // --- Save PDF --- pdf.save('Loyalty_Program_Dashboard_Report.pdf'); }; // --- INITIALIZATION --- // populateConfigForm(); renderDashboard(); renderMemberTable(); updateNavButtons(); });
Scroll to Top