/** * Monitor 3: User Analytics Service * Tracks feature usage and user interactions */ import { apiClient } from './api/client'; import { logger } from '@/utils/logger'; 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 { const response = await apiClient.get<{ data?: Record }>('/analytics', { params: { days: range.replace('d', '') } }); const data = (response.data?.data ?? response.data) as Record | undefined; if (!data || typeof data !== 'object') { return {}; } return { total_tracks: (data.total_tracks as number | undefined) ?? 0, total_plays: (data.total_plays as number | undefined) ?? 0, total_revenue: (data.total_revenue as number | undefined) ?? 0, followers: (data.followers as number | undefined) ?? 0, profile_views: (data.profile_views as number | undefined) ?? 0, trends: (data.trends as Record | undefined) ?? { plays: 0, revenue: 0, followers: 0, views: 0 }, sparklines: (data.sparklines as Record | undefined) ?? { plays: [0], revenue: [0], followers: [0], views: [0] }, }; } catch (error) { logger.error('[Analytics] Failed to fetch global stats', { error }); throw error; } }, getTopTracks: async (range: string = '30d') => { try { const response = await apiClient.get<{ data?: { tracks?: { top_tracks?: unknown[] } } }>('/analytics', { params: { days: range.replace('d', '') } }); const data = response.data?.data ?? response.data; const topTracks = (data as Record)?.tracks as { top_tracks?: Array<{ id?: string; title?: string; plays?: number; play_count?: number; change?: number; revenue?: number }> } | undefined; const tracks = topTracks?.top_tracks ?? []; return tracks.map((t) => ({ id: t.id ?? '', title: t.title ?? '', plays: t.plays ?? t.play_count ?? 0, change: t.change ?? 0, revenue: t.revenue ?? 0, })); } catch (error) { logger.error('[Analytics] Failed to fetch top tracks', { error }); throw error; } }, getTrafficSources: async () => { try { const response = await apiClient.get<{ sources?: unknown[] }>('/analytics/traffic-sources'); return (response.data?.sources ?? []) as unknown[]; } catch (error) { logger.warn('[Analytics] Failed to fetch traffic sources', { error }); return []; } }, getCreatorStats: async (params?: { start_date?: string; end_date?: string; days?: number }) => { try { const searchParams = new URLSearchParams(); if (params?.days != null) searchParams.set('days', String(params.days)); if (params?.start_date) searchParams.set('start_date', params.start_date); if (params?.end_date) searchParams.set('end_date', params.end_date); const qs = searchParams.toString(); const url = `/analytics/creator/stats${qs ? `?${qs}` : ''}`; const response = await apiClient.get<{ data?: { total_plays?: number; unique_listeners?: number; average_completion_rate?: number; plays_by_day?: number[]; period?: { start_date?: string; end_date?: string; days?: number }; }; }>(url); const data = (response.data?.data ?? response.data) as Record | undefined; return data ?? {}; } catch (error) { logger.error('[Analytics] Failed to fetch creator stats', { error }); throw error; } }, getDeviceBreakdown: async () => { try { const response = await apiClient.get<{ mobile?: number; desktop?: number }>('/analytics/device-breakdown'); return { mobile: response.data?.mobile ?? 0, desktop: response.data?.desktop ?? 0, }; } catch (error) { logger.warn('[Analytics] Failed to fetch device breakdown', { error }); return { mobile: 0, desktop: 0 }; } }, };