Skip to content

Analytics Dashboard

Comprehensive business intelligence with revenue tracking, user analytics, and performance metrics.

Preview

Contact Form Component Preview

Overview

The analytics dashboard provides deep insights into your SaaS business performance with real-time metrics, trend analysis, and actionable business intelligence.

Key Performance Indicators (KPIs)

Business Metrics

typescript
const businessKPIs = computed(() => ({
  // Revenue Metrics
  mrr: analyticsData.value?.mrr || 0,                    // Monthly Recurring Revenue
  arr: (analyticsData.value?.mrr || 0) * 12,            // Annual Recurring Revenue
  totalRevenue: analyticsData.value?.totalRevenue || 0,
  
  // Customer Metrics
  totalCustomers: analyticsData.value?.totalCustomers || 0,
  activeCustomers: analyticsData.value?.activeCustomers || 0,
  churnRate: analyticsData.value?.churnRate || 0,
  
  // Business Intelligence
  ltv: analyticsData.value?.ltv || 0,                   // Customer Lifetime Value
  cac: analyticsData.value?.cac || 0,                   // Customer Acquisition Cost
  ltvCacRatio: (analyticsData.value?.ltv || 0) / (analyticsData.value?.cac || 1),
  
  // Growth Metrics
  monthlyGrowthRate: analyticsData.value?.monthlyGrowthRate || 0,
  userGrowthRate: analyticsData.value?.userGrowthRate || 0
}))

KPI Cards Display

typescript
const kpiCards = [
  {
    title: 'Monthly Recurring Revenue',
    value: businessKPIs.value.mrr,
    format: 'currency',
    trend: calculateTrend('mrr'),
    icon: 'trending-up',
    color: 'green'
  },
  {
    title: 'Annual Recurring Revenue',
    value: businessKPIs.value.arr,
    format: 'currency',
    trend: calculateTrend('arr'),
    icon: 'calendar',
    color: 'blue'
  },
  {
    title: 'Customer Lifetime Value',
    value: businessKPIs.value.ltv,
    format: 'currency',
    trend: calculateTrend('ltv'),
    icon: 'users',
    color: 'purple'
  },
  {
    title: 'Churn Rate',
    value: businessKPIs.value.churnRate,
    format: 'percentage',
    trend: calculateTrend('churnRate', true), // Inverted trend (lower is better)
    icon: 'user-x',
    color: 'red'
  }
]

Revenue Analytics

Revenue Chart

Multi-period revenue visualization:

typescript
const createRevenueChart = () => {
  const ctx = revenueChart.value?.getContext('2d')
  if (!ctx) return

  revenueChartInstance = new Chart(ctx, {
    type: 'line',
    data: {
      labels: revenueData.value.map(d => 
        new Date(d.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })
      ),
      datasets: [
        {
          label: 'Daily Revenue',
          data: revenueData.value.map(d => d.daily_revenue),
          borderColor: '#000000',
          backgroundColor: 'rgba(0, 0, 0, 0.1)',
          tension: 0.4,
          fill: true
        },
        {
          label: 'MRR',
          data: revenueData.value.map(d => d.mrr),
          borderColor: '#3b82f6',
          backgroundColor: 'rgba(59, 130, 246, 0.1)',
          tension: 0.4,
          fill: false
        }
      ]
    },
    options: {
      responsive: true,
      interaction: {
        intersect: false,
        mode: 'index'
      },
      plugins: {
        legend: {
          display: true,
          position: 'top'
        },
        tooltip: {
          callbacks: {
            label: (context) => `${context.dataset.label}: $${context.parsed.y.toLocaleString()}`
          }
        }
      },
      scales: {
        y: {
          beginAtZero: true,
          ticks: {
            callback: (value) => `$${value.toLocaleString()}`
          }
        }
      }
    }
  })
}

Revenue Breakdown

typescript
const revenueBreakdown = computed(() => {
  const plans = analyticsData.value?.planRevenue || []
  
  return {
    byPlan: plans.map(plan => ({
      name: plan.plan_name,
      revenue: plan.revenue,
      percentage: (plan.revenue / businessKPIs.value.totalRevenue) * 100,
      subscribers: plan.subscriber_count
    })),
    
    byPeriod: {
      monthly: plans.filter(p => p.interval === 'month').reduce((sum, p) => sum + p.revenue, 0),
      yearly: plans.filter(p => p.interval === 'year').reduce((sum, p) => sum + p.revenue, 0)
    }
  }
})

User Analytics

User Growth Chart

typescript
const createUserGrowthChart = () => {
  const ctx = userGrowthChart.value?.getContext('2d')
  if (!ctx) return

  userGrowthChartInstance = new Chart(ctx, {
    type: 'bar',
    data: {
      labels: userGrowthData.value.map(d => 
        new Date(d.date).toLocaleDateString('en-US', { month: 'short' })
      ),
      datasets: [
        {
          label: 'New Users',
          data: userGrowthData.value.map(d => d.new_users),
          backgroundColor: 'rgba(34, 197, 94, 0.8)',
          borderColor: 'rgb(34, 197, 94)',
          borderWidth: 1
        },
        {
          label: 'Churned Users',
          data: userGrowthData.value.map(d => -d.churned_users), // Negative for visual effect
          backgroundColor: 'rgba(239, 68, 68, 0.8)',
          borderColor: 'rgb(239, 68, 68)',
          borderWidth: 1
        }
      ]
    },
    options: {
      responsive: true,
      plugins: {
        legend: {
          display: true
        }
      },
      scales: {
        y: {
          beginAtZero: true,
          ticks: {
            callback: (value) => Math.abs(value)
          }
        }
      }
    }
  })
}

Cohort Analysis

typescript
const cohortAnalysis = computed(() => {
  const cohorts = analyticsData.value?.cohorts || []
  
  return cohorts.map(cohort => ({
    period: cohort.signup_period,
    size: cohort.initial_users,
    retention: cohort.retention_rates,
    revenue: cohort.revenue_by_period,
    ltv: cohort.projected_ltv
  }))
})

const generateCohortTable = () => {
  const periods = ['Month 1', 'Month 2', 'Month 3', 'Month 6', 'Month 12']
  
  return cohortAnalysis.value.map(cohort => ({
    period: cohort.period,
    size: cohort.size,
    ...periods.reduce((acc, period, index) => {
      acc[period] = cohort.retention[index] || 0
      return acc
    }, {})
  }))
}

Geographic Analytics

User Distribution Map

typescript
const geographicData = computed(() => {
  const countries = analyticsData.value?.geography?.countries || []
  
  return countries.map(country => ({
    name: country.country_name,
    code: country.country_code,
    users: country.user_count,
    revenue: country.revenue,
    coordinates: [country.longitude, country.latitude],
    color: getColorByUsers(country.user_count)
  }))
})

const getColorByUsers = (userCount) => {
  if (userCount > 100) return '#dc2626' // Red
  if (userCount > 50) return '#ea580c'  // Orange
  if (userCount > 20) return '#ca8a04'  // Yellow
  if (userCount > 10) return '#16a34a'  // Green
  return '#6b7280' // Gray
}

Top Performing Regions

typescript
const topRegions = computed(() => {
  return geographicData.value
    .sort((a, b) => b.revenue - a.revenue)
    .slice(0, 10)
    .map((region, index) => ({
      ...region,
      rank: index + 1,
      revenuePerUser: region.revenue / region.users
    }))
})

Conversion Analytics

Funnel Analysis

typescript
const conversionFunnel = computed(() => {
  const funnel = analyticsData.value?.conversionFunnel || {}
  
  const stages = [
    { name: 'Visitors', count: funnel.visitors || 0, color: '#e5e7eb' },
    { name: 'Signups', count: funnel.signups || 0, color: '#d1d5db' },
    { name: 'Trial Users', count: funnel.trials || 0, color: '#9ca3af' },
    { name: 'Paid Users', count: funnel.paid || 0, color: '#6b7280' },
    { name: 'Active Users', count: funnel.active || 0, color: '#374151' }
  ]
  
  return stages.map((stage, index) => ({
    ...stage,
    conversionRate: index > 0 ? (stage.count / stages[index - 1].count) * 100 : 100,
    dropoffRate: index > 0 ? 100 - (stage.count / stages[index - 1].count) * 100 : 0
  }))
})

A/B Test Results

typescript
const abTestResults = computed(() => {
  return analyticsData.value?.abTests?.map(test => ({
    name: test.test_name,
    status: test.status,
    variants: test.variants.map(variant => ({
      name: variant.name,
      users: variant.user_count,
      conversions: variant.conversions,
      conversionRate: (variant.conversions / variant.user_count) * 100,
      revenue: variant.revenue,
      isWinner: variant.is_winner
    })),
    significance: test.statistical_significance,
    startDate: test.start_date,
    endDate: test.end_date
  })) || []
})

Product Analytics

Feature Usage

typescript
const featureUsage = computed(() => {
  const features = analyticsData.value?.featureUsage || []
  
  return features.map(feature => ({
    name: feature.feature_name,
    activeUsers: feature.active_users,
    totalUsers: feature.total_users,
    usageRate: (feature.active_users / feature.total_users) * 100,
    avgSessionsPerUser: feature.avg_sessions_per_user,
    avgTimeSpent: feature.avg_time_spent
  }))
})

User Engagement Metrics

typescript
const engagementMetrics = computed(() => ({
  dailyActiveUsers: analyticsData.value?.dau || 0,
  weeklyActiveUsers: analyticsData.value?.wau || 0,
  monthlyActiveUsers: analyticsData.value?.mau || 0,
  averageSessionDuration: analyticsData.value?.avgSessionDuration || 0,
  sessionsPerUser: analyticsData.value?.sessionsPerUser || 0,
  bounceRate: analyticsData.value?.bounceRate || 0
}))

Time Period Filtering

Date Range Selector

typescript
const dateRanges = [
  { label: 'Last 7 days', value: 7 },
  { label: 'Last 30 days', value: 30 },
  { label: 'Last 90 days', value: 90 },
  { label: 'Last 12 months', value: 365 },
  { label: 'Custom range', value: 'custom' }
]

const selectedDateRange = ref(30)
const customDateRange = ref({
  start: null,
  end: null
})

const applyDateRange = () => {
  let startDate, endDate
  
  if (selectedDateRange.value === 'custom') {
    startDate = customDateRange.value.start
    endDate = customDateRange.value.end
  } else {
    endDate = new Date()
    startDate = new Date(endDate.getTime() - selectedDateRange.value * 24 * 60 * 60 * 1000)
  }
  
  refreshAnalytics({ startDate, endDate })
}

Real-time Analytics

Live Metrics

typescript
const liveMetrics = ref({
  activeUsers: 0,
  currentRevenue: 0,
  newSignups: 0,
  activeTrials: 0
})

onMounted(() => {
  const { $socket } = useNuxtApp()
  
  // Real-time user activity
  $socket.on('analytics:active_users', (count) => {
    liveMetrics.value.activeUsers = count
  })
  
  // Real-time revenue updates
  $socket.on('analytics:revenue_update', (revenue) => {
    liveMetrics.value.currentRevenue = revenue
  })
  
  // New signups
  $socket.on('user:registered', (user) => {
    liveMetrics.value.newSignups++
    updateCharts()
  })
})

Export & Reporting

Data Export

typescript
const exportAnalytics = async (type, format = 'csv') => {
  const exportData = {
    type, // 'revenue', 'users', 'conversion', 'geographic'
    format,
    dateRange: {
      start: getDateRangeStart(),
      end: getDateRangeEnd()
    },
    filters: currentFilters.value
  }
  
  const response = await $fetch('/api/admin/analytics/export', {
    method: 'POST',
    body: exportData
  })
  
  downloadFile(response.data, `${type}-analytics-${new Date().toISOString().split('T')[0]}.${format}`)
  toast.success(`${type} analytics exported successfully`)
}

Scheduled Reports

typescript
const scheduleReport = async (reportConfig) => {
  await $fetch('/api/admin/analytics/schedule-report', {
    method: 'POST',
    body: {
      name: reportConfig.name,
      type: reportConfig.type,
      frequency: reportConfig.frequency, // 'daily', 'weekly', 'monthly'
      recipients: reportConfig.recipients,
      format: reportConfig.format,
      filters: reportConfig.filters
    }
  })
  
  toast.success('Report scheduled successfully')
}

Performance Optimization

Data Caching

typescript
const analyticsCache = ref(new Map())

const getCachedAnalytics = (key, ttl = 300000) => { // 5 minutes TTL
  const cached = analyticsCache.value.get(key)
  
  if (cached && Date.now() - cached.timestamp < ttl) {
    return cached.data
  }
  
  return null
}

const setCachedAnalytics = (key, data) => {
  analyticsCache.value.set(key, {
    data,
    timestamp: Date.now()
  })
}

Lazy Loading

typescript
const visibleSections = ref(new Set(['kpis']))

const observeSection = (sectionName) => {
  const observer = new IntersectionObserver((entries) => {
    entries.forEach((entry) => {
      if (entry.isIntersecting) {
        visibleSections.value.add(sectionName)
        loadSectionData(sectionName)
      }
    })
  })
  
  return observer
}

API Endpoints

Analytics APIs

GET  /api/admin/analytics/overview        # Main dashboard metrics
GET  /api/admin/analytics/revenue         # Revenue analytics
GET  /api/admin/analytics/users           # User analytics
GET  /api/admin/analytics/conversion      # Conversion funnel
GET  /api/admin/analytics/geographic      # Geographic data
GET  /api/admin/analytics/cohorts         # Cohort analysis
GET  /api/admin/analytics/features        # Feature usage
POST /api/admin/analytics/export          # Export analytics data
POST /api/admin/analytics/schedule-report # Schedule reports

The analytics dashboard provides comprehensive business intelligence tools to track performance, identify trends, and make data-driven decisions for your SaaS platform growth.

Built with love by mhdevfr