bookwiz.io / supabase / migrations / 047_enhance_welcome_tour_tracking.sql
047_enhance_welcome_tour_tracking.sql
Raw
-- Enhance welcome tour tracking with detailed analytics
-- This adds comprehensive onboarding progress and engagement tracking

-- Enhanced tour progress tracking
ALTER TABLE profiles 
ADD COLUMN IF NOT EXISTS welcome_tour_started_at TIMESTAMP WITH TIME ZONE;

ALTER TABLE profiles 
ADD COLUMN IF NOT EXISTS welcome_tour_current_step INTEGER DEFAULT 0;

ALTER TABLE profiles 
ADD COLUMN IF NOT EXISTS welcome_tour_steps_viewed INTEGER[] DEFAULT '{}';

-- Tour outcome tracking
ALTER TABLE profiles 
ADD COLUMN IF NOT EXISTS welcome_tour_completion_type TEXT CHECK (welcome_tour_completion_type IN ('completed', 'skipped', 'abandoned'));

ALTER TABLE profiles 
ADD COLUMN IF NOT EXISTS welcome_tour_selected_plan TEXT;

ALTER TABLE profiles 
ADD COLUMN IF NOT EXISTS welcome_tour_selected_billing TEXT CHECK (welcome_tour_selected_billing IN ('month', 'year'));

-- Engagement metrics
ALTER TABLE profiles 
ADD COLUMN IF NOT EXISTS welcome_tour_total_time_seconds INTEGER DEFAULT 0;

ALTER TABLE profiles 
ADD COLUMN IF NOT EXISTS welcome_tour_video_watched BOOLEAN DEFAULT false;

-- Tour version for A/B testing
ALTER TABLE profiles 
ADD COLUMN IF NOT EXISTS welcome_tour_version TEXT DEFAULT 'v1';

-- Create optimized indexes for analytics
CREATE INDEX IF NOT EXISTS idx_profiles_welcome_tour_analytics 
ON profiles (welcome_tour_completed, welcome_tour_completion_type, welcome_tour_selected_plan);

CREATE INDEX IF NOT EXISTS idx_profiles_welcome_tour_progress 
ON profiles (welcome_tour_completed, welcome_tour_current_step) 
WHERE welcome_tour_completed = false;

CREATE INDEX IF NOT EXISTS idx_profiles_welcome_tour_engagement 
ON profiles (welcome_tour_video_watched, welcome_tour_total_time_seconds) 
WHERE welcome_tour_completed = true;

-- Add comprehensive comments
COMMENT ON COLUMN profiles.welcome_tour_started_at IS 'When the user first opened the welcome tour';
COMMENT ON COLUMN profiles.welcome_tour_current_step IS 'Current step in the tour (0-based index)';
COMMENT ON COLUMN profiles.welcome_tour_steps_viewed IS 'Array of step indices the user has viewed';
COMMENT ON COLUMN profiles.welcome_tour_completion_type IS 'How the tour ended: completed, skipped, or abandoned';
COMMENT ON COLUMN profiles.welcome_tour_selected_plan IS 'Plan selected during tour (free, explorer, storyteller, professional)';
COMMENT ON COLUMN profiles.welcome_tour_selected_billing IS 'Billing interval selected during tour (month, year)';
COMMENT ON COLUMN profiles.welcome_tour_total_time_seconds IS 'Total time spent in the tour in seconds';
COMMENT ON COLUMN profiles.welcome_tour_video_watched IS 'Whether the user watched the demo video';
COMMENT ON COLUMN profiles.welcome_tour_version IS 'Version of the tour shown for A/B testing';

-- Create a function to get tour analytics
CREATE OR REPLACE FUNCTION get_tour_analytics()
RETURNS TABLE (
  total_users BIGINT,
  tour_started BIGINT,
  tour_completed BIGINT,
  tour_skipped BIGINT,
  tour_abandoned BIGINT,
  completion_rate NUMERIC,
  avg_time_seconds NUMERIC,
  video_watch_rate NUMERIC,
  most_popular_plan TEXT,
  monthly_vs_annual_ratio NUMERIC
) AS $$
BEGIN
  RETURN QUERY
  WITH tour_stats AS (
    SELECT 
      COUNT(*) as total_users,
      COUNT(CASE WHEN welcome_tour_started_at IS NOT NULL THEN 1 END) as tour_started,
      COUNT(CASE WHEN welcome_tour_completion_type = 'completed' THEN 1 END) as tour_completed,
      COUNT(CASE WHEN welcome_tour_completion_type = 'skipped' THEN 1 END) as tour_skipped,
      COUNT(CASE WHEN welcome_tour_completion_type = 'abandoned' THEN 1 END) as tour_abandoned,
      AVG(CASE WHEN welcome_tour_total_time_seconds > 0 THEN welcome_tour_total_time_seconds END) as avg_time,
      COUNT(CASE WHEN welcome_tour_video_watched = true THEN 1 END)::NUMERIC / NULLIF(COUNT(CASE WHEN welcome_tour_started_at IS NOT NULL THEN 1 END), 0) as video_rate,
      MODE() WITHIN GROUP (ORDER BY welcome_tour_selected_plan) as popular_plan,
      COUNT(CASE WHEN welcome_tour_selected_billing = 'month' THEN 1 END)::NUMERIC / NULLIF(COUNT(CASE WHEN welcome_tour_selected_billing = 'year' THEN 1 END), 0) as billing_ratio
    FROM profiles
    WHERE created_at >= NOW() - INTERVAL '30 days' -- Last 30 days
  )
  SELECT 
    s.total_users,
    s.tour_started,
    s.tour_completed,
    s.tour_skipped,
    s.tour_abandoned,
    CASE WHEN s.tour_started > 0 THEN s.tour_completed::NUMERIC / s.tour_started * 100 ELSE 0 END as completion_rate,
    s.avg_time,
    s.video_rate * 100 as video_watch_rate,
    s.popular_plan,
    s.billing_ratio
  FROM tour_stats s;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- Grant access to the analytics function
GRANT EXECUTE ON FUNCTION get_tour_analytics() TO authenticated;

-- Create a function to get step-by-step analytics
CREATE OR REPLACE FUNCTION get_step_analytics()
RETURNS TABLE (
  step_number INTEGER,
  views BIGINT,
  drop_off_rate NUMERIC
) AS $$
BEGIN
  RETURN QUERY
  WITH step_data AS (
    SELECT 
      unnest(welcome_tour_steps_viewed) as step_num,
      COUNT(*) as total_started
    FROM profiles 
    WHERE welcome_tour_started_at IS NOT NULL
    GROUP BY step_num
  ),
  total_started AS (
    SELECT COUNT(*) as total FROM profiles WHERE welcome_tour_started_at IS NOT NULL
  )
  SELECT 
    sd.step_num,
    COUNT(*) as views,
    (ts.total - COUNT(*))::NUMERIC / ts.total * 100 as drop_rate
  FROM step_data sd
  CROSS JOIN total_started ts
  GROUP BY sd.step_num, ts.total
  ORDER BY sd.step_num;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- Grant access to the step analytics function
GRANT EXECUTE ON FUNCTION get_step_analytics() TO authenticated;