Analytics Dashboard
Comprehensive business intelligence with revenue tracking, user analytics, and performance metrics.
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 reportsThe analytics dashboard provides comprehensive business intelligence tools to track performance, identify trends, and make data-driven decisions for your SaaS platform growth.
