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