Download script.js from SmartaSakerSeb/newsletter-analytics-dashboard: direct link, hf CLI and curl.
- Browser
- Download file 11.7 kB
-
https://huggingface.co/spaces/SmartaSakerSeb/newsletter-analytics-dashboard/resolve/main/script.js
- Command line
-
hf download hf://spaces/SmartaSakerSeb/newsletter-analytics-dashboard/script.js
-
curl -L -o script.js https://huggingface.co/spaces/SmartaSakerSeb/newsletter-analytics-dashboard/resolve/main/script.js
11.7 kB
| // Sample data processing - in a real app, this would come from an API | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Process data and create visualizations | |
| // Performance overview cards | |
| createPerformanceCards(); | |
| // Top performers table | |
| createTopPerformersTable(); | |
| // Charts | |
| createRevenueByTypeChart(); | |
| createEngagementRatesChart(); | |
| // Detailed analysis table | |
| createNewsletterTable(); | |
| // Event listeners for sorting/filtering | |
| document.getElementById('sortTopPerformers').addEventListener('change', updateTopPerformersTable); | |
| document.getElementById('sortTable').addEventListener('change', updateNewsletterTable); | |
| document.getElementById('dataFilter').addEventListener('change', updateNewsletterTable); | |
| }); | |
| function createPerformanceCards() { | |
| const container = document.getElementById('performanceCards'); | |
| // These would be calculated from real data | |
| const metrics = [ | |
| { title: "Total Revenue", value: "SEK 12,450,821", change: "+7.2%", icon: "dollar-sign" }, | |
| { title: "Total Emails Sent", value: "5,271,883", change: "+3.4%", icon: "mail" }, | |
| { title: "Avg Open Rate", value: "36.5%", change: "+1.1%", icon: "eye" }, | |
| { title: "Avg Click Rate", value: "2.8%", change: "+0.4%", icon: "mouse-pointer" } | |
| ]; | |
| metrics.forEach(metric => { | |
| const card = document.createElement('div'); | |
| card.className = 'card bg-white p-4 rounded-lg shadow'; | |
| card.innerHTML = ` | |
| <div class="flex items-center justify-between"> | |
| <div> | |
| <p class="text-gray-500 text-sm">${metric.title}</p> | |
| <p class="text-2xl font-semibold">${metric.value}</p> | |
| <p class="text-green-500 text-sm">${metric.change} from last period</p> | |
| </div> | |
| <div class="p-3 rounded-full bg-blue-100 text-blue-600"> | |
| <i data-feather="${metric.icon}"></i> | |
| </div> | |
| </div> | |
| `; | |
| container.appendChild(card); | |
| }); | |
| } | |
| function createTopPerformersTable() { | |
| // Sort and display top 5 performers by revenue | |
| updateTopPerformersTable(); | |
| } | |
| function updateTopPerformersTable() { | |
| const sortBy = document.getElementById('sortTopPerformers').value; | |
| const tableBody = document.querySelector('#topPerformersTable tbody'); | |
| // Clear existing rows | |
| tableBody.innerHTML = ''; | |
| // Sample data - in a real app this would come from processed document data | |
| const performers = [ | |
| { name: "Batteridrivna ljus + julbelysning", revenue: "SEK 536,665", openRate: "34.3%", clickRate: "3.9%", orders: 119 }, | |
| { name: "Vakuumpåsar och förvaring", revenue: "SEK 361,063", openRate: "35.4%", clickRate: "3.2%", orders: 92 }, | |
| { name: "Nya Förvara-sortimentet", revenue: "SEK 353,538", openRate: "41.0%", clickRate: "3.9%", orders: 83 }, | |
| { name: "Gör fint hemma", revenue: "SEK 351,538", openRate: "38.9%", clickRate: "4.0%", orders: 112 }, | |
| { name: "Bästsäljare", revenue: "SEK 350,165", openRate: "39.3%", clickRate: "3.8%", orders: 156 } | |
| ]; | |
| // Sort based on selected option | |
| performers.sort((a, b) => { | |
| if (sortBy === 'revenue') { | |
| return parseFloat(b.revenue.replace(/[^0-9.]/g, '')) - parseFloat(a.revenue.replace(/[^0-9.]/g, '')); | |
| } else if (sortBy === 'openRate') { | |
| return parseFloat(b.openRate) - parseFloat(a.openRate); | |
| } else if (sortBy === 'clickRate') { | |
| return parseFloat(b.clickRate) - parseFloat(a.clickRate); | |
| } else if (sortBy === 'orders') { | |
| return b.orders - a.orders; | |
| } | |
| return 0; | |
| }); | |
| // Add rows | |
| performers.forEach(item => { | |
| const row = document.createElement('tr'); | |
| row.className = 'border-b hover:bg-gray-50'; | |
| row.innerHTML = ` | |
| <td class="py-2">${item.name}</td> | |
| <td class="text-right">${item.revenue}</td> | |
| <td class="text-right">${item.openRate}</td> | |
| <td class="text-right">${item.clickRate}</td> | |
| <td class="text-right">${item.orders}</td> | |
| `; | |
| tableBody.appendChild(row); | |
| }); | |
| } | |
| function createRevenueByTypeChart() { | |
| const ctx = document.getElementById('revenueByTypeChart').getContext('2d'); | |
| // Sample data based on document categories | |
| new Chart(ctx, { | |
| type: 'bar', | |
| data: { | |
| labels: ['Home & Living', 'Seasonal', 'Food & Cooking', 'Garden', 'Personal Care', 'Other'], | |
| datasets: [{ | |
| label: 'Revenue (SEK)', | |
| data: [4850000, 3200000, 2100000, 1800000, 950000, 450000], | |
| backgroundColor: [ | |
| 'rgba(59, 130, 246, 0.7)', | |
| 'rgba(16, 185, 129, 0.7)', | |
| 'rgba(245, 158, 11, 0.7)', | |
| 'rgba(139, 92, 246, 0.7)', | |
| 'rgba(236, 72, 153, 0.7)', | |
| 'rgba(75, 85, 99, 0.7)' | |
| ], | |
| borderColor: [ | |
| 'rgba(59, 130, 246, 1)', | |
| 'rgba(16, 185, 129, 1)', | |
| 'rgba(245, 158, 11, 1)', | |
| 'rgba(139, 92, 246, 1)', | |
| 'rgba(236, 72, 153, 1)', | |
| 'rgba(75, 85, 99, 1)' | |
| ], | |
| borderWidth: 1 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| plugins: { | |
| legend: { | |
| display: false | |
| }, | |
| tooltip: { | |
| callbacks: { | |
| label: function(context) { | |
| return `SEK ${context.raw.toLocaleString()}`; | |
| } | |
| } | |
| } | |
| }, | |
| scales: { | |
| y: { | |
| beginAtZero: true, | |
| ticks: { | |
| callback: function(value) { | |
| return `SEK ${(value / 1000).toFixed(0)}k`; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| function createEngagementRatesChart() { | |
| const ctx = document.getElementById('engagementRatesChart').getContext('2d'); | |
| new Chart(ctx, { | |
| type: 'line', | |
| data: { | |
| labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], | |
| datasets: [ | |
| { | |
| label: 'Open Rate', | |
| data: [32.5, 34.1, 35.7, 36.2, 37.8, 38.2, 37.5, 38.1, 39.4, 39.8, 40.2, 42.3], | |
| borderColor: 'rgba(59, 130, 246, 1)', | |
| backgroundColor: 'rgba(59, 130, 246, 0.1)', | |
| tension: 0.4, | |
| fill: true | |
| }, | |
| { | |
| label: 'Click Rate', | |
| data: [2.1, 2.3, 2.5, 2.6, 2.8, 2.9, 3.0, 3.1, 3.2, 3.3, 3.6, 3.9], | |
| borderColor: 'rgba(16, 185, 129, 1)', | |
| backgroundColor: 'rgba(16, 185, 129, 0.1)', | |
| tension: 0.4, | |
| fill: true | |
| } | |
| ] | |
| }, | |
| options: { | |
| responsive: true, | |
| plugins: { | |
| tooltip: { | |
| callbacks: { | |
| label: function(context) { | |
| return `${context.dataset.label}: ${context.raw}%`; | |
| } | |
| } | |
| } | |
| }, | |
| scales: { | |
| y: { | |
| beginAtZero: false, | |
| min: 0, | |
| ticks: { | |
| callback: function(value) { | |
| return `${value}%`; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| function createNewsletterTable() { | |
| // Initial load with all data | |
| updateNewsletterTable(); | |
| } | |
| function updateNewsletterTable() { | |
| const filterValue = document.getElementById('dataFilter').value; | |
| const sortBy = document.getElementById('sortTable').value; | |
| const tableBody = document.querySelector('#newsletterTable tbody'); | |
| // Clear existing rows | |
| tableBody.innerHTML = ''; | |
| // Sample data - in a real app this would be processed from the documents | |
| const newsletters = [ | |
| { name: "Batteridrivna ljus + julbelysning [SV]", sent: "179,638", delivered: "179,426", opens: "24,753", clicks: "2,643", revenue: "SEK 53,666", orders: 119, openRate: "13.8%", clickRate: "1.5%", country: "SE" }, | |
| { name: "Vakuumpåsar och förvaring [SV]", sent: "171,597", delivered: "171,292", opens: "60,651", clicks: "2,853", revenue: "SEK 36,106", orders: 92, openRate: "35.4%", clickRate: "1.7%", country: "SE" }, | |
| { name: "Nya Förvara-sortimentet [SV]", sent: "172,602", delivered: "172,400", opens: "61,560", clicks: "6,103", revenue: "SEK 35,347", orders: 83, openRate: "35.7%", clickRate: "3.5%", country: "SE" }, | |
| { name: "Gör fint hemma [SV]", sent: "172,075", delivered: "171,737", opens: "61,503", clicks: "4,338", revenue: "SEK 31,975", orders: 112, openRate: "35.8%", clickRate: "2.5%", country: "SE" }, | |
| { name: "Bästsäljare [SV]", sent: "172,845", delivered: "172,650", opens: "61,455", clicks: "4,339", revenue: "SEK 31,175", orders: 156, openRate: "35.6%", clickRate: "2.5%", country: "SE" }, | |
| { name: "Utomhusljus - Gravljus [NO]", sent: "30,441", delivered: "30,402", opens: "7,656", clicks: "508", revenue: "SEK 6,493", orders: 11, openRate: "25.2%", clickRate: "1.7%", country: "NO" }, | |
| { name: "Batteridrivna ljus + julbelysning [NO]", sent: "30,423", delivered: "30,387", opens: "7,687", clicks: "459", revenue: "SEK 5,939", orders: 11, openRate: "25.3%", clickRate: "1.5%", country: "NO" }, | |
| { name: "Bakning [DK]", sent: "6,243", delivered: "6,205", opens: "1,973", clicks: "101", revenue: "SEK 1,923", orders: 5, openRate: "31.8%", clickRate: "1.6%", country: "DK" } | |
| ]; | |
| // Filter data | |
| let filteredData = newsletters; | |
| if (filterValue !== 'all') { | |
| filteredData = newsletters.filter(item => item.country === filterValue); | |
| } | |
| // Sort data | |
| filteredData.sort((a, b) => { | |
| if (sortBy === 'name') { | |
| return a.name.localeCompare(b.name); | |
| } else if (sortBy === 'revenue') { | |
| return parseFloat(b.revenue.replace(/[^0-9.]/g, '')) - parseFloat(a.revenue.replace(/[^0-9.]/g, '')); | |
| } else if (sortBy === 'openRate') { | |
| return parseFloat(b.openRate) - parseFloat(a.openRate); | |
| } else if (sortBy === 'clickRate') { | |
| return parseFloat(b.clickRate) - parseFloat(a.clickRate); | |
| } else if (sortBy === 'orders') { | |
| return b.orders - a.orders; | |
| } | |
| return 0; | |
| }); | |
| // Add rows | |
| filteredData.forEach(item => { | |
| const row = document.createElement('tr'); | |
| row.className = 'border-b hover:bg-gray-50'; | |
| row.innerHTML = ` | |
| <td class="py-2">${item.name}</td> | |
| <td class="text-right">${item.sent}</td> | |
| <td class="text-right">${item.delivered}</td> | |
| <td class="text-right">${item.opens}</td> | |
| <td class="text-right">${item.clicks}</td> | |
| <td class="text-right">${item.revenue}</td> | |
| <td class="text-right">${item.orders}</td> | |
| <td class="text-right">${item.openRate}</td> | |
| <td class="text-right">${item.clickRate}</td> | |
| `; | |
| tableBody.appendChild(row); | |
| }); | |
| } |