# Product Analytics Dashboard Guide ## Metrics, KPIs, and Data-Driven Decision Making --- ## Overview This document defines the key metrics, analytics dashboards, and monitoring strategies for the Maternal App to enable data-driven product decisions and rapid iteration based on user behavior. ### Success Criteria (from Implementation Plan) **MVP Launch (Month 1)** - 1,000 downloads - 60% day-7 retention - 4.0+ app store rating - <2% crash rate - 5+ activities logged per day per active user - 70% of users trying AI assistant **3-Month Goals** - 10,000 active users - 500 premium subscribers - 50% month-over-month growth - 4.5+ app store rating **6-Month Vision** - 50,000 active users - 2,500 premium subscribers - Break-even on operational costs --- ## Key Performance Indicators (KPIs) ### 1. User Acquisition Metrics #### Download & Registration Funnel ``` Metric Target Formula ───────────────────────────────────────────────────── App Store Impressions 100,000 Total views Download Rate 3% Downloads / Impressions Registration Rate 75% Signups / Downloads Onboarding Completion 90% Completed / Started Time to First Value < 2 min First activity logged ``` **Dashboard Queries**: ```sql -- Daily registration funnel SELECT DATE(created_at) as date, COUNT(*) FILTER (WHERE step = 'download') as downloads, COUNT(*) FILTER (WHERE step = 'registration_started') as started_registration, COUNT(*) FILTER (WHERE step = 'registration_completed') as completed_registration, COUNT(*) FILTER (WHERE step = 'onboarding_completed') as completed_onboarding, COUNT(*) FILTER (WHERE step = 'first_activity') as first_activity FROM user_funnel_events WHERE created_at >= CURRENT_DATE - INTERVAL '30 days' GROUP BY DATE(created_at) ORDER BY date DESC; -- Conversion rates by channel SELECT acquisition_channel, COUNT(*) as total_users, AVG(CASE WHEN onboarding_completed THEN 1 ELSE 0 END) as onboarding_completion_rate, AVG(time_to_first_activity_minutes) as avg_time_to_value FROM users WHERE created_at >= CURRENT_DATE - INTERVAL '30 days' GROUP BY acquisition_channel; ``` ### 2. Engagement Metrics #### Daily Active Users (DAU) / Monthly Active Users (MAU) ```typescript // Analytics service tracking export interface EngagementMetrics { dau: number; // Users active in last 24h wau: number; // Users active in last 7 days mau: number; // Users active in last 30 days dauMauRatio: number; // Stickiness: DAU/MAU (target: >20%) averageSessionDuration: number; // Minutes (target: >5 min) sessionsPerUser: number; // Per day (target: >2) } ``` **Dashboard Queries**: ```sql -- DAU/WAU/MAU trend WITH daily_users AS ( SELECT DATE(last_active_at) as date, user_id FROM user_sessions WHERE last_active_at >= CURRENT_DATE - INTERVAL '30 days' ) SELECT date, COUNT(DISTINCT user_id) as dau, COUNT(DISTINCT user_id) FILTER ( WHERE date >= CURRENT_DATE - INTERVAL '7 days' ) OVER () as wau, COUNT(DISTINCT user_id) OVER () as mau, ROUND(COUNT(DISTINCT user_id)::numeric / NULLIF(COUNT(DISTINCT user_id) OVER (), 0) * 100, 2) as dau_mau_ratio FROM daily_users GROUP BY date ORDER BY date DESC; -- Power users (top 20% by activity) SELECT user_id, COUNT(*) as total_activities, COUNT(DISTINCT DATE(created_at)) as active_days, AVG(session_duration_seconds) / 60 as avg_session_minutes FROM activities WHERE created_at >= CURRENT_DATE - INTERVAL '30 days' GROUP BY user_id HAVING COUNT(*) > ( SELECT PERCENTILE_CONT(0.8) WITHIN GROUP (ORDER BY activity_count) FROM (SELECT COUNT(*) as activity_count FROM activities GROUP BY user_id) counts ) ORDER BY total_activities DESC; ``` #### Feature Adoption ```typescript export interface FeatureAdoption { feature: string; totalUsers: number; adoptionRate: number; // % of total users timeToAdoption: number; // Days since signup retentionAfterAdoption: number; // % still using after 7 days } // Target adoption rates: const targetAdoption = { activityTracking: 0.95, // 95% core feature aiAssistant: 0.70, // 70% AI engagement voiceInput: 0.40, // 40% voice adoption familySharing: 0.60, // 60% multi-user analytics: 0.80, // 80% view insights exportReports: 0.25, // 25% premium feature }; ``` **Dashboard Queries**: ```sql -- Feature adoption over time SELECT feature_name, COUNT(DISTINCT user_id) as users, COUNT(DISTINCT user_id)::float / (SELECT COUNT(*) FROM users WHERE created_at <= CURRENT_DATE) as adoption_rate, AVG(EXTRACT(DAY FROM first_use_at - u.created_at)) as avg_days_to_adoption FROM feature_usage fu JOIN users u ON fu.user_id = u.id WHERE fu.first_use_at >= CURRENT_DATE - INTERVAL '30 days' GROUP BY feature_name ORDER BY adoption_rate DESC; ``` ### 3. Retention Metrics #### Cohort Retention Analysis ```typescript export interface CohortRetention { cohort: string; // e.g., "2025-01-W1" day0: number; // 100% (signup) day1: number; // Target: >40% day7: number; // Target: >60% day30: number; // Target: >40% day90: number; // Target: >30% } ``` **Dashboard Queries**: ```sql -- Weekly cohort retention WITH cohorts AS ( SELECT user_id, DATE_TRUNC('week', created_at) as cohort_week FROM users ), retention AS ( SELECT c.cohort_week, COUNT(DISTINCT c.user_id) as cohort_size, COUNT(DISTINCT CASE WHEN DATE(s.last_active_at) = DATE(c.cohort_week) THEN s.user_id END) as day0, COUNT(DISTINCT CASE WHEN DATE(s.last_active_at) = DATE(c.cohort_week) + INTERVAL '1 day' THEN s.user_id END) as day1, COUNT(DISTINCT CASE WHEN DATE(s.last_active_at) BETWEEN DATE(c.cohort_week) AND DATE(c.cohort_week) + INTERVAL '7 days' THEN s.user_id END) as day7, COUNT(DISTINCT CASE WHEN DATE(s.last_active_at) BETWEEN DATE(c.cohort_week) AND DATE(c.cohort_week) + INTERVAL '30 days' THEN s.user_id END) as day30 FROM cohorts c LEFT JOIN user_sessions s ON c.user_id = s.user_id GROUP BY c.cohort_week ) SELECT cohort_week, cohort_size, ROUND(day0::numeric / cohort_size * 100, 2) as day0_retention, ROUND(day1::numeric / cohort_size * 100, 2) as day1_retention, ROUND(day7::numeric / cohort_size * 100, 2) as day7_retention, ROUND(day30::numeric / cohort_size * 100, 2) as day30_retention FROM retention ORDER BY cohort_week DESC; ``` ### 4. Monetization Metrics #### Conversion & Revenue ```typescript export interface MonetizationMetrics { // Trial & Subscription trialStarts: number; trialToPayingConversion: number; // Target: >30% churnRate: number; // Target: <5% monthly // Revenue mrr: number; // Monthly Recurring Revenue arpu: number; // Average Revenue Per User ltv: number; // Lifetime Value cac: number; // Customer Acquisition Cost ltvCacRatio: number; // Target: >3 // Pricing tiers premiumSubscribers: number; premiumAdoptionRate: number; // % of active users } ``` **Dashboard Queries**: ```sql -- MRR trend and growth SELECT DATE_TRUNC('month', subscription_start_date) as month, COUNT(*) as new_subscriptions, COUNT(*) FILTER (WHERE previous_subscription_id IS NOT NULL) as upgrades, COUNT(*) FILTER (WHERE subscription_end_date IS NOT NULL) as churned, SUM(price) as mrr, LAG(SUM(price)) OVER (ORDER BY DATE_TRUNC('month', subscription_start_date)) as previous_mrr, ROUND((SUM(price) - LAG(SUM(price)) OVER (ORDER BY DATE_TRUNC('month', subscription_start_date))) / NULLIF(LAG(SUM(price)) OVER (ORDER BY DATE_TRUNC('month', subscription_start_date)), 0) * 100, 2 ) as mrr_growth_rate FROM subscriptions GROUP BY month ORDER BY month DESC; -- LTV calculation WITH user_revenue AS ( SELECT user_id, SUM(amount) as total_revenue, MIN(payment_date) as first_payment, MAX(payment_date) as last_payment, COUNT(*) as payment_count FROM payments WHERE status = 'completed' GROUP BY user_id ) SELECT AVG(total_revenue) as avg_ltv, PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY total_revenue) as median_ltv, AVG(EXTRACT(DAY FROM last_payment - first_payment)) as avg_lifetime_days FROM user_revenue; -- Churn analysis SELECT DATE_TRUNC('month', cancelled_at) as month, COUNT(*) as churned_users, AVG(EXTRACT(DAY FROM cancelled_at - subscription_start_date)) as avg_days_before_churn, cancellation_reason, COUNT(*) FILTER (WHERE cancellation_reason IS NOT NULL) as reason_count FROM subscriptions WHERE cancelled_at IS NOT NULL GROUP BY month, cancellation_reason ORDER BY month DESC, reason_count DESC; ``` ### 5. Product Quality Metrics #### Technical Health ```typescript export interface QualityMetrics { // Performance apiResponseTimeP95: number; // Target: <2s apiResponseTimeP99: number; // Target: <3s errorRate: number; // Target: <1% // Reliability uptime: number; // Target: >99.9% crashFreeUsers: number; // Target: >98% crashFreeS essions: number; // Target: >99.5% // User satisfaction appStoreRating: number; // Target: >4.0 nps: number; // Net Promoter Score (target: >50) csat: number; // Customer Satisfaction (target: >80%) } ``` **Dashboard Queries**: ```sql -- API performance monitoring SELECT DATE_TRUNC('hour', timestamp) as hour, endpoint, COUNT(*) as request_count, ROUND(AVG(response_time_ms), 2) as avg_response_time, PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY response_time_ms) as p95_response_time, PERCENTILE_CONT(0.99) WITHIN GROUP (ORDER BY response_time_ms) as p99_response_time, COUNT(*) FILTER (WHERE status_code >= 500) as server_errors, COUNT(*) FILTER (WHERE status_code >= 400 AND status_code < 500) as client_errors FROM api_logs WHERE timestamp >= NOW() - INTERVAL '24 hours' GROUP BY hour, endpoint HAVING COUNT(*) > 100 -- Only endpoints with significant traffic ORDER BY hour DESC, p99_response_time DESC; -- Crash analytics SELECT DATE(created_at) as date, platform, app_version, COUNT(DISTINCT user_id) as affected_users, COUNT(*) as crash_count, error_message, stack_trace FROM error_logs WHERE severity = 'fatal' AND created_at >= CURRENT_DATE - INTERVAL '7 days' GROUP BY date, platform, app_version, error_message, stack_trace ORDER BY affected_users DESC; ``` --- ## Analytics Dashboard Templates ### 1. Executive Dashboard (Daily Review) **Key Metrics Card Layout**: ``` ┌─────────────────────────────────────────────────────┐ │ Daily Active Users │ MRR │ Uptime │ │ 5,234 ↑ 12% │ $12,450 ↑ 8% │ 99.98% │ ├─────────────────────────────────────────────────────┤ │ New Signups │ Churn Rate │ NPS │ │ 342 ↑ 5% │ 4.2% ↓ 0.3% │ 62 ↑ 3 │ └─────────────────────────────────────────────────────┘ 📊 7-Day User Growth Trend [Line chart: DAU over time] 📊 Feature Adoption (Last 7 Days) [Bar chart: % of users by feature] 🚨 Alerts & Issues • P95 response time elevated (2.3s, target: 2.0s) • Crash rate on Android 1.2.0 (3.1%, target: <2%) ``` ### 2. Product Analytics Dashboard **User Journey Funnel**: ```sql -- Onboarding funnel conversion SELECT 'App Download' as step, 1 as step_number, COUNT(*) as users, 100.0 as conversion_rate FROM downloads WHERE created_at >= CURRENT_DATE - INTERVAL '7 days' UNION ALL SELECT 'Registration Started' as step, 2, COUNT(*), ROUND(COUNT(*)::numeric / (SELECT COUNT(*) FROM downloads WHERE created_at >= CURRENT_DATE - INTERVAL '7 days') * 100, 2) FROM users WHERE created_at >= CURRENT_DATE - INTERVAL '7 days' UNION ALL SELECT 'Onboarding Completed' as step, 3, COUNT(*), ROUND(COUNT(*)::numeric / (SELECT COUNT(*) FROM downloads WHERE created_at >= CURRENT_DATE - INTERVAL '7 days') * 100, 2) FROM users WHERE onboarding_completed_at IS NOT NULL AND created_at >= CURRENT_DATE - INTERVAL '7 days' UNION ALL SELECT 'First Activity Logged' as step, 4, COUNT(DISTINCT user_id), ROUND(COUNT(DISTINCT user_id)::numeric / (SELECT COUNT(*) FROM downloads WHERE created_at >= CURRENT_DATE - INTERVAL '7 days') * 100, 2) FROM activities WHERE created_at >= CURRENT_DATE - INTERVAL '7 days' AND created_at <= (SELECT created_at FROM users WHERE user_id = activities.user_id) + INTERVAL '24 hours' ORDER BY step_number; ``` **User Segmentation**: ```typescript export enum UserSegment { NEW_USER = 'new_user', // < 7 days ENGAGED = 'engaged', // 3+ activities/day AT_RISK = 'at_risk', // No activity in 7 days POWER_USER = 'power_user', // Top 20% by activity PREMIUM = 'premium', // Paid subscription CHURNED = 'churned', // No activity in 30 days } // Segment users for targeted interventions const segments = { new_user: { criteria: 'days_since_signup < 7', action: 'Send onboarding emails', }, engaged: { criteria: 'activities_per_day >= 3', action: 'Upsell premium features', }, at_risk: { criteria: 'days_since_last_activity >= 7 AND < 30', action: 'Re-engagement campaign', }, churned: { criteria: 'days_since_last_activity >= 30', action: 'Win-back campaign', }, }; ``` ### 3. A/B Testing Dashboard **Experiment Tracking**: ```typescript export interface ABTest { id: string; name: string; hypothesis: string; variants: { control: { users: number; conversionRate: number; }; variant: { users: number; conversionRate: number; }; }; pValue: number; // Statistical significance winner?: 'control' | 'variant'; status: 'running' | 'completed' | 'cancelled'; } // Example: Test new onboarding flow const onboardingTest: ABTest = { id: 'exp_001', name: 'New Onboarding Flow', hypothesis: 'Simplified 3-step onboarding will increase completion rate from 75% to 85%', variants: { control: { users: 1000, conversionRate: 0.75, }, variant: { users: 1000, conversionRate: 0.82, }, }, pValue: 0.03, // Statistically significant (< 0.05) winner: 'variant', status: 'completed', }; ``` **Dashboard Queries**: ```sql -- A/B test results WITH test_users AS ( SELECT experiment_id, variant, user_id, CASE WHEN action_completed THEN 1 ELSE 0 END as converted FROM ab_test_assignments WHERE experiment_id = 'exp_001' ) SELECT variant, COUNT(*) as total_users, SUM(converted) as conversions, ROUND(AVG(converted) * 100, 2) as conversion_rate, ROUND(STDDEV(converted), 4) as std_dev FROM test_users GROUP BY variant; -- Calculate statistical significance (chi-square test) -- Use external tool or statistics library ``` --- ## Monitoring & Alerting ### Alert Rules **Critical Alerts** (PagerDuty/Slack) ```yaml alerts: - name: "High Error Rate" condition: "error_rate > 5%" window: "5 minutes" severity: "critical" notification: "pagerduty" - name: "API Response Time Degradation" condition: "p95_response_time > 3s" window: "10 minutes" severity: "high" notification: "slack" - name: "Database Connection Pool Exhausted" condition: "active_connections >= 95% of pool_size" window: "1 minute" severity: "critical" notification: "pagerduty" - name: "Crash Rate Spike" condition: "crash_rate > 2%" window: "1 hour" severity: "high" notification: "slack" ``` **Business Alerts** (Email/Slack) ```yaml alerts: - name: "Daily Active Users Drop" condition: "today_dau < yesterday_dau * 0.8" window: "daily" severity: "medium" notification: "email" - name: "Churn Rate Increase" condition: "monthly_churn > 7%" window: "weekly" severity: "medium" notification: "slack" - name: "Low Onboarding Completion" condition: "onboarding_completion_rate < 80%" window: "daily" severity: "low" notification: "email" ``` --- ## Rapid Iteration Framework ### Week 1-2 Post-Launch: Monitoring & Triage ```markdown **Focus**: Identify and fix critical issues Daily Tasks: - [ ] Review crash reports (target: <2%) - [ ] Check error logs and API failures - [ ] Monitor onboarding completion rate (target: >90%) - [ ] Track day-1 retention (target: >40%) Weekly Review: - Analyze user feedback from in-app surveys - Identify top 3 pain points - Prioritize bug fixes vs. feature requests - Plan hotfix releases if needed ``` ### Week 3-4: Optimization ```markdown **Focus**: Improve core metrics Experiments to Run: 1. A/B test onboarding flow variations 2. Test different push notification timings 3. Optimize AI response quality 4. Improve activity tracking UX Success Metrics: - Increase day-7 retention to 60% - Increase AI assistant usage to 70% - Reduce time-to-first-value to <2 minutes ``` ### Month 2: Feature Iteration ```markdown **Focus**: Expand value proposition Based on Data: - Identify most-used features (double down) - Identify least-used features (improve or remove) - Analyze user segments (power users vs. casual) - Test premium feature adoption New Features to Test: - Sleep coaching (if sleep tracking is popular) - Meal planning (if feeding tracking is high-engagement) - Community forums (if users request social features) ``` --- ## Tools & Integration ### Recommended Analytics Stack **Core Analytics**: PostHog (open-source, self-hosted) ```typescript // Backend integration import { PostHog } from 'posthog-node'; const posthog = new PostHog( process.env.POSTHOG_API_KEY, { host: 'https://app.posthog.com' } ); // Track events posthog.capture({ distinctId: userId, event: 'activity_logged', properties: { type: 'feeding', method: 'voice', }, }); // User properties posthog.identify({ distinctId: userId, properties: { email: user.email, isPremium: subscription.isPremium, familySize: family.members.length, }, }); ``` **Error Tracking**: Sentry ```typescript import * as Sentry from '@sentry/node'; Sentry.init({ dsn: process.env.SENTRY_DSN, environment: process.env.NODE_ENV, tracesSampleRate: 1.0, }); // Automatic error capture // Manual events Sentry.captureMessage('User encountered issue', 'warning'); ``` **Uptime Monitoring**: UptimeRobot / Pingdom ```yaml checks: - name: "API Health" url: "https://api.maternalapp.com/health" interval: "1 minute" - name: "Web App" url: "https://app.maternalapp.com" interval: "1 minute" ``` **Performance**: Grafana + Prometheus ```yaml # prometheus.yml scrape_configs: - job_name: 'maternal-app-backend' static_configs: - targets: ['localhost:3000/metrics'] ``` --- ## Conclusion This analytics framework enables: 1. **Data-Driven Decisions**: Track what matters 2. **Rapid Iteration**: Identify and fix issues quickly 3. **User Understanding**: Segment and personalize 4. **Business Growth**: Monitor revenue and churn 5. **Product Quality**: Maintain high standards Review dashboards daily, iterate weekly, and adjust strategy monthly based on real-world usage data.