/** * App Analytics and Version Tracking * Tracks app usage, errors, and provides version information */ (function() { 'use strict'; const ANALYTICS_KEY = 'bbplease_analytics'; const USER_ID_KEY = 'bbplease_user_id'; const APP_VERSION = '1.0.0'; /** * Generate or retrieve user ID */ function getUserId() { let userId = localStorage.getItem(USER_ID_KEY); if (!userId) { userId = 'user_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); localStorage.setItem(USER_ID_KEY, userId); } return userId; } /** * Get app version */ function getAppVersion() { return window.APP_CONFIG ? window.APP_CONFIG.VERSION : APP_VERSION; } /** * Get platform info */ function getPlatformInfo() { const isCapacitor = window.Capacitor !== undefined; const platform = isCapacitor ? window.Capacitor.getPlatform() : 'web'; return { platform: platform, isMobile: isCapacitor && (platform === 'android' || platform === 'ios'), userAgent: navigator.userAgent, language: navigator.language, online: navigator.onLine }; } /** * Track event */ function trackEvent(eventName, eventData = {}) { const event = { name: eventName, timestamp: new Date().toISOString(), userId: getUserId(), version: getAppVersion(), platform: getPlatformInfo(), data: eventData }; // Store locally (could be sent to server later) const analytics = getAnalytics(); analytics.events.push(event); // Keep only last 100 events if (analytics.events.length > 100) { analytics.events = analytics.events.slice(-100); } saveAnalytics(analytics); console.log('📊 Event tracked:', eventName, eventData); return event; } /** * Track error */ function trackError(error, context = {}) { const errorEvent = { name: 'error', timestamp: new Date().toISOString(), userId: getUserId(), version: getAppVersion(), platform: getPlatformInfo(), error: { message: error.message || String(error), stack: error.stack, name: error.name, context: context } }; const analytics = getAnalytics(); analytics.errors.push(errorEvent); // Keep only last 50 errors if (analytics.errors.length > 50) { analytics.errors = analytics.errors.slice(-50); } saveAnalytics(analytics); console.error('❌ Error tracked:', errorEvent); // Optionally send to server sendErrorToServer(errorEvent); return errorEvent; } /** * Send error to server (optional) */ async function sendErrorToServer(errorEvent) { try { if (!navigator.onLine) { return; // Skip if offline } const url = (window.APP_CONFIG && window.APP_CONFIG.getApiUrl) ? window.APP_CONFIG.getApiUrl('/api/analytics/error') : '/api/analytics/error'; await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(errorEvent) }); } catch (e) { console.warn('Failed to send error to server:', e); } } /** * Get analytics data */ function getAnalytics() { try { const data = localStorage.getItem(ANALYTICS_KEY); return data ? JSON.parse(data) : { userId: getUserId(), version: getAppVersion(), firstLaunch: new Date().toISOString(), events: [], errors: [], sessionCount: 0 }; } catch (e) { return { userId: getUserId(), version: getAppVersion(), firstLaunch: new Date().toISOString(), events: [], errors: [], sessionCount: 0 }; } } /** * Save analytics data */ function saveAnalytics(analytics) { try { localStorage.setItem(ANALYTICS_KEY, JSON.stringify(analytics)); } catch (e) { console.error('Failed to save analytics:', e); } } /** * Track session start */ function trackSessionStart() { const analytics = getAnalytics(); analytics.sessionCount = (analytics.sessionCount || 0) + 1; analytics.lastSession = new Date().toISOString(); saveAnalytics(analytics); trackEvent('session_start', { sessionNumber: analytics.sessionCount }); } /** * Get analytics summary */ function getAnalyticsSummary() { const analytics = getAnalytics(); return { userId: analytics.userId || getUserId(), version: analytics.version || getAppVersion(), firstLaunch: analytics.firstLaunch, sessionCount: analytics.sessionCount || 0, eventCount: analytics.events ? analytics.events.length : 0, errorCount: analytics.errors ? analytics.errors.length : 0, platform: getPlatformInfo() }; } /** * Clear analytics (privacy) */ function clearAnalytics() { localStorage.removeItem(ANALYTICS_KEY); console.log('🗑️ Analytics cleared'); } /** * Initialize analytics */ function init() { trackSessionStart(); // Track page load trackEvent('app_loaded', { platform: getPlatformInfo() }); // Track online/offline changes window.addEventListener('online', () => { trackEvent('network_online'); }); window.addEventListener('offline', () => { trackEvent('network_offline'); }); // Global error handler window.addEventListener('error', (event) => { trackError(event.error || new Error(event.message), { filename: event.filename, lineno: event.lineno, colno: event.colno }); }); // Unhandled promise rejection handler window.addEventListener('unhandledrejection', (event) => { trackError(event.reason, { type: 'unhandled_promise_rejection' }); }); console.log('📊 Analytics initialized'); } // Export to window window.AppAnalytics = { trackEvent: trackEvent, trackError: trackError, getAnalyticsSummary: getAnalyticsSummary, getUserId: getUserId, getAppVersion: getAppVersion, getPlatformInfo: getPlatformInfo, clearAnalytics: clearAnalytics }; // Auto-initialize if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();