// Performance Dashboard Service - Comprehensive analytics dashboard // Path: src/modules/analytics/services/performance-dashboard.service.ts import { Injectable, Logger } from '@nestjs/common'; export interface DashboardOverview { period: string; totalPosts: number; totalEngagements: number; avgEngagementRate: number; totalReach: number; totalImpressions: number; followerGrowth: number; topPlatform: string; goldPostsCount: number; abTestsActive: number; } export interface PlatformBreakdown { platform: string; posts: number; engagements: number; avgEngagementRate: number; reach: number; impressions: number; bestContentType: string; growthTrend: 'up' | 'down' | 'stable'; comparedToLastPeriod: number; } export interface ContentTypeAnalysis { type: string; count: number; avgEngagement: number; avgReach: number; performanceScore: number; trend: 'improving' | 'declining' | 'stable'; } export interface TimeAnalysis { dayOfWeek: string; hourSlot: string; avgEngagement: number; postCount: number; recommendation: string; } export interface DashboardWidget { id: string; type: 'chart' | 'metric' | 'table' | 'heatmap' | 'comparison'; title: string; data: any; config: Record; } export interface DashboardLayout { userId: string; widgets: DashboardWidget[]; customization: { theme: 'light' | 'dark'; refreshInterval: number; dateRange: string; }; } @Injectable() export class PerformanceDashboardService { private readonly logger = new Logger(PerformanceDashboardService.name); private dashboardData: Map = new Map(); private userLayouts: Map = new Map(); /** * Get dashboard overview */ getDashboardOverview(userId: string, period: 'day' | 'week' | 'month' | 'quarter' | 'year'): DashboardOverview { // Generate realistic dashboard data const multipliers = { day: 1, week: 7, month: 30, quarter: 90, year: 365 }; const m = multipliers[period]; return { period, totalPosts: Math.floor(3 * m + Math.random() * 2 * m), totalEngagements: Math.floor(1500 * m + Math.random() * 1000 * m), avgEngagementRate: 2.5 + Math.random() * 3, totalReach: Math.floor(15000 * m + Math.random() * 10000 * m), totalImpressions: Math.floor(25000 * m + Math.random() * 15000 * m), followerGrowth: Math.floor(50 * m + Math.random() * 30 * m), topPlatform: ['instagram', 'twitter', 'linkedin', 'tiktok'][Math.floor(Math.random() * 4)], goldPostsCount: Math.floor(m / 10) + 1, abTestsActive: Math.floor(Math.random() * 5) + 1, }; } /** * Get platform breakdown */ getPlatformBreakdown(userId: string, period: string): PlatformBreakdown[] { const platforms = ['twitter', 'instagram', 'linkedin', 'facebook', 'tiktok', 'youtube']; const contentTypes = ['video', 'carousel', 'single_image', 'text', 'story', 'reel']; return platforms.map(platform => ({ platform, posts: Math.floor(10 + Math.random() * 40), engagements: Math.floor(500 + Math.random() * 2000), avgEngagementRate: 1.5 + Math.random() * 4, reach: Math.floor(5000 + Math.random() * 20000), impressions: Math.floor(10000 + Math.random() * 40000), bestContentType: contentTypes[Math.floor(Math.random() * contentTypes.length)], growthTrend: ['up', 'down', 'stable'][Math.floor(Math.random() * 3)] as 'up' | 'down' | 'stable', comparedToLastPeriod: -15 + Math.random() * 40, })); } /** * Get content type analysis */ getContentTypeAnalysis(userId: string): ContentTypeAnalysis[] { const types = [ { type: 'video', base: 4.5 }, { type: 'carousel', base: 3.8 }, { type: 'single_image', base: 2.5 }, { type: 'text', base: 1.8 }, { type: 'story', base: 3.2 }, { type: 'reel', base: 5.5 }, { type: 'live', base: 6.0 }, { type: 'poll', base: 4.0 }, ]; return types.map(t => ({ type: t.type, count: Math.floor(5 + Math.random() * 30), avgEngagement: t.base + Math.random() * 2, avgReach: Math.floor(1000 + Math.random() * 5000), performanceScore: Math.floor(50 + Math.random() * 50), trend: ['improving', 'declining', 'stable'][Math.floor(Math.random() * 3)] as 'improving' | 'declining' | 'stable', })); } /** * Get time-based analysis (best times to post) */ getTimeAnalysis(userId: string, platform?: string): TimeAnalysis[] { const days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']; const hours = ['6-9 AM', '9-12 PM', '12-3 PM', '3-6 PM', '6-9 PM', '9-12 AM']; const analysis: TimeAnalysis[] = []; for (const day of days) { for (const hour of hours) { const engagement = 1 + Math.random() * 6; const postCount = Math.floor(Math.random() * 10); let recommendation = 'Good time to post'; if (engagement > 4) recommendation = 'Excellent time - high engagement expected'; else if (engagement < 2) recommendation = 'Avoid posting - low engagement expected'; analysis.push({ dayOfWeek: day, hourSlot: hour, avgEngagement: engagement, postCount, recommendation, }); } } return analysis; } /** * Get engagement heatmap data */ getEngagementHeatmap(userId: string): number[][] { // 7 days x 24 hours matrix of engagement scores (0-100) return Array.from({ length: 7 }, () => Array.from({ length: 24 }, () => Math.floor(Math.random() * 100)) ); } /** * Get comparison data (vs previous period) */ getComparisonData(userId: string, currentPeriod: string): { current: DashboardOverview; previous: DashboardOverview; changes: Record; } { const current = this.getDashboardOverview(userId, currentPeriod as any); const previous = this.getDashboardOverview(userId, currentPeriod as any); const calculate = (curr: number, prev: number) => { const change = curr - prev; const percentage = prev > 0 ? (change / prev) * 100 : 0; const trend: 'up' | 'down' | 'stable' = percentage > 5 ? 'up' : percentage < -5 ? 'down' : 'stable'; return { value: change, percentage, trend }; }; return { current, previous, changes: { posts: calculate(current.totalPosts, previous.totalPosts), engagements: calculate(current.totalEngagements, previous.totalEngagements), engagementRate: calculate(current.avgEngagementRate, previous.avgEngagementRate), reach: calculate(current.totalReach, previous.totalReach), followers: calculate(current.followerGrowth, previous.followerGrowth), }, }; } /** * Get dashboard widgets */ getDefaultWidgets(): DashboardWidget[] { return [ { id: 'overview', type: 'metric', title: 'Performance Overview', data: null, config: { size: 'large', position: { row: 0, col: 0 } }, }, { id: 'engagement-trend', type: 'chart', title: 'Engagement Trend', data: null, config: { chartType: 'line', size: 'medium', position: { row: 0, col: 1 } }, }, { id: 'platform-breakdown', type: 'chart', title: 'Platform Performance', data: null, config: { chartType: 'bar', size: 'medium', position: { row: 1, col: 0 } }, }, { id: 'content-types', type: 'chart', title: 'Content Type Analysis', data: null, config: { chartType: 'pie', size: 'small', position: { row: 1, col: 1 } }, }, { id: 'best-times', type: 'heatmap', title: 'Best Times to Post', data: null, config: { size: 'large', position: { row: 2, col: 0 } }, }, { id: 'top-posts', type: 'table', title: 'Top Performing Posts', data: null, config: { columns: ['title', 'platform', 'engagement', 'reach'], position: { row: 2, col: 1 } }, }, { id: 'growth-metrics', type: 'comparison', title: 'Growth vs Last Period', data: null, config: { size: 'medium', position: { row: 3, col: 0 } }, }, { id: 'gold-posts', type: 'table', title: 'Gold Posts', data: null, config: { highlight: true, position: { row: 3, col: 1 } }, }, ]; } /** * Get/create user dashboard layout */ getDashboardLayout(userId: string): DashboardLayout { let layout = this.userLayouts.get(userId); if (!layout) { layout = { userId, widgets: this.getDefaultWidgets(), customization: { theme: 'dark', refreshInterval: 60000, dateRange: 'week', }, }; this.userLayouts.set(userId, layout); } return layout; } /** * Update dashboard layout */ updateDashboardLayout(userId: string, updates: Partial): DashboardLayout { const current = this.getDashboardLayout(userId); const updated = { ...current, ...updates }; this.userLayouts.set(userId, updated); return updated; } /** * Add custom widget */ addWidget(userId: string, widget: Omit): DashboardWidget { const layout = this.getDashboardLayout(userId); const newWidget: DashboardWidget = { ...widget, id: `widget-${Date.now()}`, }; layout.widgets.push(newWidget); this.userLayouts.set(userId, layout); return newWidget; } /** * Remove widget */ removeWidget(userId: string, widgetId: string): boolean { const layout = this.getDashboardLayout(userId); const index = layout.widgets.findIndex(w => w.id === widgetId); if (index === -1) return false; layout.widgets.splice(index, 1); this.userLayouts.set(userId, layout); return true; } /** * Export dashboard data */ exportDashboardData(userId: string, format: 'json' | 'csv'): string { const overview = this.getDashboardOverview(userId, 'month'); const platforms = this.getPlatformBreakdown(userId, 'month'); const contentTypes = this.getContentTypeAnalysis(userId); if (format === 'json') { return JSON.stringify({ overview, platforms, contentTypes }, null, 2); } // CSV format let csv = 'Metric,Value\n'; csv += `Total Posts,${overview.totalPosts}\n`; csv += `Total Engagements,${overview.totalEngagements}\n`; csv += `Avg Engagement Rate,${overview.avgEngagementRate.toFixed(2)}%\n`; csv += `Total Reach,${overview.totalReach}\n`; csv += `Follower Growth,${overview.followerGrowth}\n`; csv += '\nPlatform,Posts,Engagements,Avg Rate\n'; for (const p of platforms) { csv += `${p.platform},${p.posts},${p.engagements},${p.avgEngagementRate.toFixed(2)}%\n`; } return csv; } /** * Get insights and recommendations */ getInsights(userId: string): Array<{ type: 'success' | 'warning' | 'info' | 'action'; title: string; description: string; priority: 'high' | 'medium' | 'low'; }> { return [ { type: 'success', title: 'Strong Video Performance', description: 'Your video content is outperforming other formats by 45%. Consider increasing video production.', priority: 'high', }, { type: 'warning', title: 'Engagement Drop on Weekends', description: 'Weekend posts show 30% lower engagement. Consider rescheduling to weekday evenings.', priority: 'medium', }, { type: 'action', title: 'Untapped LinkedIn Potential', description: 'LinkedIn shows high engagement but low posting frequency. Increase LinkedIn content.', priority: 'high', }, { type: 'info', title: 'Optimal Posting Time Detected', description: 'Best engagement window: Tuesday-Thursday, 6-9 PM in your timezone.', priority: 'medium', }, { type: 'success', title: 'Gold Post Streak', description: 'You\'ve had 3 Gold Posts this week! Your viral content formula is working.', priority: 'low', }, ]; } }