/** * Monitor 3: User Analytics Service * Tracks feature usage and user interactions */ import { apiClient } from './api/client'; import { logger } from '@/utils/logger'; const MOCK_GLOBAL_STATS = { total_users: 12500, total_tracks: 3420, total_plays: 1205430, total_revenue: 14250.5, followers: 24500, profile_views: 45200, trends: { plays: 8.2, revenue: 12.5, followers: 2.1, views: -2.4 }, sparklines: { plays: [40, 35, 50, 60, 55, 70, 80, 75, 90], revenue: [10, 12, 15, 14, 18, 20, 22, 25, 28], followers: [20, 21, 21, 22, 22, 23, 23, 24, 24], views: [50, 48, 45, 42, 40, 43, 41, 40, 38], }, }; const TOP_TRACKS = [ { id: 't1', title: 'Neon Nights', plays: 15420, change: 12, revenue: 145.5 }, { id: 't2', title: 'Cyber City', plays: 12100, change: -5, revenue: 98.2 }, { id: 't3', title: 'System Failure', plays: 8500, change: 24, revenue: 65.0 }, { id: 't4', title: 'Mainframe', plays: 6200, change: 8, revenue: 42.1 }, ]; export const analyticsService = { /** * Monitor 3: Record a user analytics event * Tracks feature usage and user interactions * @param eventName - Name of the event (e.g., 'feature_used', 'button_clicked') * @param payload - Optional event data (e.g., { feature: 'search', query: '...' }) */ recordEvent: async (eventName: string, payload?: Record) => { try { // Send event to backend analytics endpoint await apiClient.post('/analytics/events', { event_name: eventName, payload: payload || {}, }); // Log in development for debugging if (import.meta.env.DEV) { logger.debug('[Analytics] Event recorded', { event_name: eventName, payload, }); } } catch (error) { // Don't throw errors for analytics - fail silently to avoid disrupting user experience if (import.meta.env.DEV) { logger.warn('[Analytics] Failed to record event', { event_name: eventName, error: error instanceof Error ? error.message : String(error), }); } } }, getGlobalStats: async (range: string = '30d') => { try { // Action 1.1: Connect to real backend analytics const response = await apiClient.get('/analytics', { params: { days: range.replace('d', '') } }); // Transform backend data to UI format if needed // If backend is empty, fallback to mock but log it if (!response.data || Object.keys(response.data).length === 0) { if (import.meta.env.DEV) logger.info('[Analytics] Backend returned empty stats, using baseline defaults'); return MOCK_GLOBAL_STATS; } return { ...MOCK_GLOBAL_STATS, // Use as baseline for fields not yet in backend ...response.data, }; } catch (error) { logger.error('[Analytics] Failed to fetch global stats', { error }); return MOCK_GLOBAL_STATS; } }, getTopTracks: async (range: string = '30d') => { try { const response = await apiClient.get('/analytics/tracks/top', { params: { days: range.replace('d', '') } }); if (!response.data || response.data.length === 0) { return TOP_TRACKS; } return response.data; } catch (error) { logger.error('[Analytics] Failed to fetch top tracks', { error }); return TOP_TRACKS; } }, getTrafficSources: async () => { // Traffic sources might not be in backend yet, keep mock but ready for API return [ { label: 'Direct', val: 45, color: 'bg-primary' }, { label: 'Social Media', val: 30, color: 'bg-destructive' }, { label: 'Search', val: 15, color: 'bg-success' }, { label: 'Referral', val: 10, color: 'bg-warning' }, ]; }, getDeviceBreakdown: async () => { return { mobile: 65, desktop: 35 }; }, };