dreepj's picture
aunch a localized monetization toolkit that leverages culturally appropriate, user-friendly engagement methods—such as rewarded ads with minimal intrusion and region-compliant microtransactions—supported by A/B testing to optimize player retention and revenu
bae8c14 verified
Raw History Blame Contribute Delete
19.1 kB
// Monetization Toolkit JavaScript
// Initialize the application
document.addEventListener('DOMContentLoaded', function() {
initializeApp();
setupEventListeners();
initializeCharts();
updateDashboardData();
// Handle hash-based navigation
window.addEventListener('hashchange', function() {
const section = window.location.hash.substring(1);
if (section) {
showSection(section);
}
});
// Initial section based on hash
if (window.location.hash) {
showSection(window.location.hash.substring(1));
}
});
// Global variables
let currentSection = 'dashboard';
let charts = {};
let timeRange = '7d';
// Initialize the application
function initializeApp() {
// Set default section
showSection('dashboard');
// Initialize with sample data
updateKPIs();
updateCharts();
// Set up periodic updates
setInterval(updateKPIs, 30000); // Update every 30 seconds
}
// Setup event listeners
function setupEventListeners() {
// Navigation
document.getElementById('region-select').addEventListener('change', function() {
updateRegionData(this.value);
});
// A/B Test Form
document.getElementById('ab-test-form').addEventListener('submit', function(e) {
e.preventDefault();
createNewTest();
});
// Modal buttons
document.getElementById('watch-ad-btn').addEventListener('click', function() {
simulateAdWatching();
});
document.getElementById('complete-purchase-btn').addEventListener('click', function() {
completePurchase();
});
// Ad modal close on backdrop click
document.getElementById('ad-modal').addEventListener('click', function(e) {
if (e.target === this) closeAdModal();
});
document.getElementById('purchase-modal').addEventListener('click', function(e) {
if (e.target === this) closePurchaseModal();
});
}
// Section navigation
function showSection(sectionName) {
// Hide all sections
const sections = document.querySelectorAll('.content-section');
sections.forEach(section => {
section.classList.add('hidden');
});
// Show selected section
document.getElementById(sectionName).classList.remove('hidden');
// Update navigation
const navButtons = document.querySelectorAll('.nav-btn');
navButtons.forEach(btn => {
btn.classList.remove('active');
});
event.target.classList.add('active');
currentSection = sectionName;
// Update charts if needed
if (sectionName === 'analytics') {
updateAnalyticsCharts();
}
}
// Update KPIs
function updateKPIs() {
const kpis = {
'daily-revenue': generateRandomValue(2500, 3200, '$'),
'active-users': generateRandomValue(14500, 15800, ''),
'conversion-rate': generateRandomValue(2.8, 3.6, '%'),
'arpu': generateRandomValue(0.15, 0.22, '$')
};
Object.keys(kpis).forEach(id => {
const element = document.getElementById(id);
if (element) {
animateValue(element, element.textContent, kpis[id]);
}
});
}
// Generate random values for simulation
function generateRandomValue(min, max, prefix = '') {
const value = (Math.random() * (max - min) + min).toFixed(max < 10 ? 2 : 0);
return prefix + value;
}
// Animate value changes
function animateValue(element, start, end) {
const startTime = performance.now();
const duration = 1000;
function updateValue(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
// Simple interpolation
const startNum = parseFloat(start.replace(/[^\d.-]/g, '')) || 0;
const endNum = parseFloat(end.replace(/[^\d.-]/g, '')) || 0;
const current = startNum + (endNum - startNum) * progress;
const prefix = start.match(/^[^\d]*/)?.[0] || '';
const suffix = start.match(/[^\d]*$/)?.[0] || '';
element.textContent = prefix + current.toFixed(start.includes('%') ? 2 : start.includes('$') && startNum < 1 ? 3 : 0) + suffix;
if (progress < 1) {
requestAnimationFrame(updateValue);
}
}
requestAnimationFrame(updateValue);
}
// Initialize Charts
function initializeCharts() {
// Revenue Chart
const revenueCtx = document.getElementById('revenue-chart');
if (revenueCtx) {
charts.revenue = new Chart(revenueCtx, {
type: 'line',
data: {
labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
datasets: [{
label: 'Revenue',
data: generateChartData(7, 2000, 3000),
borderColor: '#ec4899',
backgroundColor: 'rgba(236, 72, 153, 0.1)',
tension: 0.4,
fill: true
}]
},
options: {
responsive: true,
plugins: {
legend: {
display: false
}
},
scales: {
y: {
beginAtZero: false,
ticks: {
callback: function(value) {
return '$' + value;
}
}
}
}
}
});
}
// User Chart
const userCtx = document.getElementById('user-chart');
if (userCtx) {
charts.user = new Chart(userCtx, {
type: 'bar',
data: {
labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
datasets: [{
label: 'New Users',
data: generateChartData(7, 500, 1000),
backgroundColor: '#14b8a6',
borderRadius: 4
}]
},
options: {
responsive: true,
plugins: {
legend: {
display: false
}
},
scales: {
y: {
beginAtZero: true
}
}
}
});
}
// Ads Performance Chart
const adsCtx = document.getElementById('ads-performance-chart');
if (adsCtx) {
charts.ads = new Chart(adsCtx, {
type: 'doughnut',
data: {
labels: ['Completed', 'Skipped', 'Failed'],
datasets: [{
data: [75, 20, 5],
backgroundColor: ['#14b8a6', '#ec4899', '#f3f4f6'],
borderWidth: 0
}]
},
options: {
responsive: true,
plugins: {
legend: {
position: 'bottom'
}
}
}
});
}
// Purchase Chart
const purchaseCtx = document.getElementById('purchase-chart');
if (purchaseCtx) {
charts.purchase = new Chart(purchaseCtx, {
type: 'pie',
data: {
labels: ['Starter Pack', 'Value Pack', 'Mega Pack'],
datasets: [{
data: [25, 45, 30],
backgroundColor: ['#ec4899', '#14b8a6', '#f3f4f6'],
borderWidth: 0
}]
},
options: {
responsive: true,
plugins: {
legend: {
position: 'bottom'
}
}
}
});
}
// Analytics Charts
initializeAnalyticsCharts();
}
// Initialize Analytics Charts
function initializeAnalyticsCharts() {
// Revenue Source Chart
const revenueSourceCtx = document.getElementById('revenue-source-chart');
if (revenueSourceCtx) {
charts.revenueSource = new Chart(revenueSourceCtx, {
type: 'bar',
data: {
labels: ['Rewarded Ads', 'In-App Purchases', 'Subscriptions'],
datasets: [{
label: 'Revenue ($)',
data: [15000, 25000, 8000],
backgroundColor: ['#ec4899', '#14b8a6', '#f3f4f6'],
borderRadius: 4
}]
},
options: {
responsive: true,
indexAxis: 'y',
plugins: {
legend: {
display: false
}
}
}
});
}
// Retention Chart
const retentionCtx = document.getElementById('retention-chart');
if (retentionCtx) {
charts.retention = new Chart(retentionCtx, {
type: 'line',
data: {
labels: ['Day 1', 'Day 3', 'Day 7', 'Day 14', 'Day 30'],
datasets: [{
label: 'Retention Rate',
data: [100, 65, 45, 35, 28],
borderColor: '#14b8a6',
backgroundColor: 'rgba(20, 184, 166, 0.1)',
tension: 0.4,
fill: true
}]
},
options: {
responsive: true,
plugins: {
legend: {
display: false
}
},
scales: {
y: {
beginAtZero: true,
max: 100,
ticks: {
callback: function(value) {
return value + '%';
}
}
}
}
}
});
}
}
// Generate chart data
function generateChartData(days, min, max) {
const data = [];
for (let i = 0; i < days; i++) {
data.push(Math.floor(Math.random() * (max - min) + min));
}
return data;
}
// Update charts
function updateCharts() {
Object.keys(charts).forEach(chartKey => {
if (charts[chartKey]) {
// Update with new data
if (chartKey === 'revenue') {
charts[chartKey].data.datasets[0].data = generateChartData(7, 2000, 3000);
} else if (chartKey === 'user') {
charts[chartKey].data.datasets[0].data = generateChartData(7, 500, 1000);
}
charts[chartKey].update('none');
}
});
}
// Update analytics charts
function updateAnalyticsCharts() {
// Add some animation to charts when section is shown
Object.keys(charts).forEach(chartKey => {
if (charts[chartKey] && charts[chartKey].canvas.closest('#analytics')) {
charts[chartKey].update('active');
}
});
}
// Time range update
function updateTimeRange(range) {
timeRange = range;
// Update button states
const buttons = document.querySelectorAll('.time-range-btn');
buttons.forEach(btn => {
btn.classList.remove('bg-pink-500', 'text-white');
btn.classList.add('bg-gray-200', 'text-gray-700');
});
event.target.classList.remove('bg-gray-200', 'text-gray-700');
event.target.classList.add('bg-pink-500', 'text-white');
// Update charts with new time range
updateChartsForTimeRange(range);
}
// Update charts for specific time range
function updateChartsForTimeRange(range) {
let days, min, max;
switch(range) {
case '7d':
days = 7;
min = 2000;
max = 3000;
break;
case '30d':
days = 30;
min = 1800;
max = 3200;
break;
case '90d':
days = 90;
min = 1600;
max = 3400;
break;
}
if (charts.revenue) {
charts.revenue.data.labels = generateTimeLabels(days);
charts.revenue.data.datasets[0].data = generateChartData(days, min, max);
charts.revenue.update('active');
}
}
// Generate time labels
function generateTimeLabels(days) {
const labels = [];
const today = new Date();
for (let i = days - 1; i >= 0; i--) {
const date = new Date(today);
date.setDate(date.getDate() - i);
labels.push(date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }));
}
return labels;
}
// Create new A/B test
function createNewTest() {
const form = document.getElementById('ab-test-form');
const formData = new FormData(form);
// Simulate test creation
showNotification('A/B Test created successfully!', 'success');
form.reset();
// Add to active tests list (simulation)
setTimeout(() => {
updateActiveTests();
}, 1000);
}
// Update active tests list
function updateActiveTests() {
// This would typically fetch from an API
console.log('Updating active tests...');
}
// Region data update
function updateRegionData(region) {
// Simulate region-specific data updates
showNotification(`Region changed to ${region.toUpperCase()}`, 'info');
// Update KPIs based on region
updateKPIsForRegion(region);
}
// Update KPIs for specific region
function updateKPIsForRegion(region) {
const regionMultipliers = {
'global': 1.0,
'us': 1.2,
'eu': 1.1,
'asia': 0.8,
'latam': 0.6
};
const multiplier = regionMultipliers[region] || 1.0;
// Update with regional data
const baseValues = {
'daily-revenue': 2847,
'active-users': 15234,
'conversion-rate': 3.24,
'arpu': 0.187
};
Object.keys(baseValues).forEach(id => {
const element = document.getElementById(id);
if (element) {
const baseValue = baseValues[id];
const newValue = baseValue * multiplier * (0.9 + Math.random() * 0.2);
let formattedValue;
if (id === 'daily-revenue') {
formattedValue = '$' + newValue.toFixed(0);
} else if (id === 'active-users') {
formattedValue = Math.round(newValue).toLocaleString();
} else if (id === 'conversion-rate') {
formattedValue = newValue.toFixed(2) + '%';
} else if (id === 'arpu') {
formattedValue = '$' + newValue.toFixed(3);
}
animateValue(element, element.textContent, formattedValue);
}
});
}
// Simulate ad watching
function simulateAdWatching() {
const button = document.getElementById('watch-ad-btn');
const originalText = button.textContent;
button.disabled = true;
button.innerHTML = '<div class="spinner mx-auto"></div>';
// Simulate ad duration
setTimeout(() => {
button.textContent = 'Ad Complete!';
button.classList.remove('bg-pink-500', 'hover:bg-pink-600');
button.classList.add('bg-green-500', 'hover:bg-green-600');
setTimeout(() => {
closeAdModal();
showNotification('Ad watched successfully! +3 extra lives', 'success');
button.textContent = originalText;
button.disabled = false;
button.classList.remove('bg-green-500', 'hover:bg-green-600');
button.classList.add('bg-pink-500', 'hover:bg-pink-600');
}, 1500);
}, 3000);
}
// Complete purchase
function completePurchase() {
const button = document.getElementById('complete-purchase-btn');
const originalText = button.textContent;
button.disabled = true;
button.innerHTML = '<div class="spinner mx-auto"></div>';
// Simulate purchase processing
setTimeout(() => {
button.textContent = 'Purchase Complete!';
button.classList.remove('bg-teal-500', 'hover:bg-teal-600');
button.classList.add('bg-green-500', 'hover:bg-green-600');
setTimeout(() => {
closePurchaseModal();
showNotification('Purchase successful! 2,500 coins added to your account', 'success');
button.textContent = originalText;
button.disabled = false;
button.classList.remove('bg-green-500', 'hover:bg-green-600');
button.classList.add('bg-teal-500', 'hover:bg-teal-600');
}, 1500);
}, 2000);
}
// Modal functions
function closeAdModal() {
document.getElementById('ad-modal').classList.add('hidden');
}
function closePurchaseModal() {
document.getElementById('purchase-modal').classList.add('hidden');
}
// Show notification
function showNotification(message, type = 'info') {
// Create notification element
const notification = document.createElement('div');
notification.className = `fixed top-4 right-4 z-50 p-4 rounded-lg shadow-lg max-w-sm transform transition-all duration-300 translate-x-full`;
// Set colors based on type
switch(type) {
case 'success':
notification.classList.add('bg-green-500', 'text-white');
break;
case 'error':
notification.classList.add('bg-red-500', 'text-white');
break;
case 'warning':
notification.classList.add('bg-yellow-500', 'text-white');
break;
default:
notification.classList.add('bg-blue-500', 'text-white');
}
notification.innerHTML = `
<div class="flex items-center">
<div class="flex-1">${message}</div>
<button onclick="this.parentElement.parentElement.remove()" class="ml-3 text-white hover:text-gray-200">
<i data-feather="x"></i>
</button>
</div>
`;
document.body.appendChild(notification);
// Animate in
setTimeout(() => {
notification.classList.remove('translate-x-full');
}, 100);
// Auto remove after 5 seconds
setTimeout(() => {
notification.classList.add('translate-x-full');
setTimeout(() => {
if (notification.parentNode) {
notification.remove();
}
}, 300);
}, 5000);
// Refresh feather icons
if (typeof feather !== 'undefined') {
feather.replace();
}
}
// Dashboard data updates
function updateDashboardData() {
// Simulate real-time data updates
setInterval(() => {
if (currentSection === 'dashboard') {
updateKPIs();
updateCharts();
}
}, 10000); // Update every 10 seconds
}
// Export functions for testing (if needed)
if (typeof module !== 'undefined' && module.exports) {
module.exports = {
generateRandomValue,
animateValue,
showNotification
};
}