Fan Engagement Dashboard

Total Engagement

${(totalEng/1000).toFixed(1)}k

Total Impressions

${(totalImp/1000000).toFixed(2)}M

Engagement Rate

${(totalEng / totalImp * 100).toFixed(2)}%

Avg. Sentiment

${getSentimentLabel(avgSent)}

Merch Sales

${formatCurrency(totalSales)}

`; } const renderChart = (id, type, data, options) => { const ctx = document.getElementById(id)?.getContext('2d'); if(!ctx) return; if(charts[id]) charts[id].destroy(); charts[id] = new Chart(ctx, { type, data, options }); }; function renderCharts(data) { const chartOptions = { responsive: true, maintainAspectRatio: false, plugins: { legend: { labels: { color: '#e2e8f0' } } }, scales: { x: { ticks:{color:'#94a3b8'} }, y: { ticks:{color:'#94a3b8'} } } }; // Trend Chart const dailyData = data.reduce((acc, r) => { const date = r.date.split('T')[0]; if(!acc[date]) acc[date] = { eng: 0, sent: [], count: 0 }; acc[date].eng += r.engagement_count; acc[date].sent.push(r.sentiment_score); acc[date].count++; return acc; }, {}); const labels = Object.keys(dailyData).sort(); renderChart('fan-trend-chart', 'bar', { labels, datasets: [ { type: 'bar', label: 'Total Engagement', data: labels.map(d => dailyData[d].eng), backgroundColor: 'rgba(59, 130, 246, 0.5)', yAxisID: 'y' }, { type: 'line', label: 'Avg Sentiment', data: labels.map(d => dailyData[d].sent.reduce((a,b)=>a+b,0)/dailyData[d].count), borderColor: '#f97316', yAxisID: 'y1' } ]}, { ...chartOptions, scales: { ...chartOptions.scales, y: {position:'left', title:{display:true, text:'Engagement'}}, y1: {position:'right', title:{display:true, text:'Sentiment Score'}, grid:{drawOnChartArea:false}} }}); // Platform Pie const byPlatform = data.reduce((acc, r) => { acc[r.platform] = (acc[r.platform] || 0) + r.engagement_count; return acc; }, {}); renderChart('fan-platform-pie-chart', 'doughnut', { labels: Object.keys(byPlatform), datasets: [{ data: Object.values(byPlatform) }] }, { ...chartOptions, plugins: { ...chartOptions.plugins, title: { display: true, text: 'Engagement Share', color: '#fff' } } }); // Platform Sentiment Bar const platforms = [...new Set(data.map(r => r.platform))]; const sentimentByPlatform = platforms.map(p => { const platformData = data.filter(r => r.platform === p); return { Positive: platformData.filter(r => getSentimentLabel(r.sentiment_score) === 'Positive').reduce((s,r)=>s+r.engagement_count,0), Neutral: platformData.filter(r => getSentimentLabel(r.sentiment_score) === 'Neutral').reduce((s,r)=>s+r.engagement_count,0), Negative: platformData.filter(r => getSentimentLabel(r.sentiment_score) === 'Negative').reduce((s,r)=>s+r.engagement_count,0), } }); renderChart('fan-platform-bar-chart', 'bar', { labels: platforms, datasets: [ { label: 'Positive', data: sentimentByPlatform.map(d => d.Positive), backgroundColor: '#22c55e' }, { label: 'Neutral', data: sentimentByPlatform.map(d => d.Neutral), backgroundColor: '#64748b' }, { label: 'Negative', data: sentimentByPlatform.map(d => d.Negative), backgroundColor: '#ef4444' } ]}, { ...chartOptions, scales: { x: { stacked: true }, y: { stacked: true } } }); } window.fanDownloadPDF = () => { html2canvas(document.getElementById('fan-pdf-content'), { backgroundColor: '#0f172a', scale: 2 }).then(canvas => { const pdf = new jspdf.jsPDF({ orientation: 'landscape', unit: 'pt', format: 'a4' }); pdf.addImage(canvas.toDataURL('image/png'), 'PNG', 40, 40, pdf.internal.pageSize.getWidth() - 80, 0); pdf.save('Fan_Engagement_Dashboard.pdf'); }); }; loadSampleData(); });
Scroll to Top