Onboarding System

User onboarding system providing guided setup, welcome flows, and new user engagement features for the Extra Chill Platform.

Onboarding Flow

Onboarding Steps

// Define onboarding steps
function ec_get_onboarding_steps() {
    return [
        'welcome' => [
            'title' => 'Welcome to Extra Chill!',
            'description' => 'Let's get your profile set up',
            'template' => 'welcome-step',
            'required' => true,
            'skippable' => false
        ],
        'profile_completion' => [
            'title' => 'Complete Your Profile',
            'description' => 'Add your display name and bio',
            'template' => 'profile-step',
            'required' => true,
            'skippable' => false
        ],
        'avatar_upload' => [
            'title' => 'Add Your Avatar',
            'description' => 'Upload a profile picture',
            'template' => 'avatar-step',
            'required' => false,
            'skippable' => true
        ],
        'interests' => [
            'title' => 'Your Interests',
            'description' => 'Tell us what you're into',
            'template' => 'interests-step',
            'required' => false,
            'skippable' => true
        ],
        'community_intro' => [
            'title' => 'Join the Community',
            'description' => 'Learn about our community features',
            'template' => 'community-step',
            'required' => true,
            'skippable' => false
        ],
        'complete' => [
            'title' => 'You're All Set!',
            'description' => 'Start exploring Extra Chill',
            'template' => 'complete-step',
            'required' => true,
            'skippable' => false
        ]
    ];
}

Onboarding State Management

// Get user's onboarding progress
function ec_get_onboarding_progress($user_id) {
    $progress = get_user_meta($user_id, 'onboarding_progress', true) ?: [];
    $current_step = get_user_meta($user_id, 'onboarding_current_step', true) ?: 'welcome';
    $completed = get_user_meta($user_id, 'onboarding_completed', true) ?: false;
    $started_at = get_user_meta($user_id, 'onboarding_started', true);
    
    return [
        'current_step' => $current_step,
        'completed_steps' => $progress,
        'completed' => $completed,
        'started_at' => $started_at,
        'total_steps' => count(ec_get_onboarding_steps()),
        'completion_percentage' => ec_calculate_onboarding_percentage($progress)
    ];
}

Onboarding Block

Block Registration

// Register onboarding Gutenberg block
function ec_register_onboarding_block() {
    register_block_type('extrachill/onboarding', [
        'editor_script' => 'extrachill-onboarding-block',
        'editor_style' => 'extrachill-onboarding-block',
        'style' => 'extrachill-onboarding',
        'render_callback' => 'ec_render_onboarding_block',
        'attributes' => [
            'showToLoggedInOnly' => [
                'type' => 'boolean',
                'default' => true
            ],
            'allowSkip' => [
                'type' => 'boolean',
                'default' => true
            ]
        ]
    ]);
}

Block Rendering

// Render onboarding block
function ec_render_onboarding_block($attributes) {
    $user_id = get_current_user_id();
    
    if (!$user_id && $attributes['showToLoggedInOnly']) {
        return '';
    }
    
    if (!$user_id) {
        return ec_render_login_prompt();
    }
    
    $progress = ec_get_onboarding_progress($user_id);
    
    if ($progress['completed']) {
        return ec_render_onboarding_complete($user_id);
    }
    
    return ec_render_onboarding_flow($user_id, $progress, $attributes);
}

Step Rendering

// Render individual onboarding step
function ec_render_onboarding_step($user_id, $step_key, $step_data, $progress) {
    $template_path = plugin_dir_path(__FILE__) . 'templates/onboarding/' . $step_data['template'] . '.php';
    
    ob_start();
    
    // Step header
    echo '<div class="onboarding-step" data-step="' . esc_attr($step_key) . '">';
    echo '<div class="step-header">';
    echo '<h2>' . esc_html($step_data['title']) . '</h2>';
    echo '<p>' . esc_html($step_data['description']) . '</p>';
    echo '</div>';
    
    // Step content
    echo '<div class="step-content">';
    
    if (file_exists($template_path)) {
        include $template_path;
    } else {
        ec_render_default_step_content($step_key, $step_data);
    }
    
    echo '</div>';
    
    // Step navigation
    echo '<div class="step-navigation">';
    ec_render_step_navigation($step_key, $step_data, $progress);
    echo '</div>';
    
    echo '</div>';
    
    return ob_get_clean();
}

Profile Completion Step

Profile Form

// Render profile completion form
function ec_render_profile_completion_step($user_id) {
    $user = get_userdata($user_id);
    
    echo '<form class="onboarding-profile-form" method="post">';
    wp_nonce_field('onboarding_profile_' . $user_id, 'onboarding_nonce');
    
    echo '<div class="form-group">';
    echo '<label for="display_name">Display Name</label>';
    echo '<input type="text" id="display_name" name="display_name" value="' . esc_attr($user->display_name) . '" required />';
    echo '</div>';
    
    echo '<div class="form-group">';
    echo '<label for="user_bio">Bio</label>';
    echo '<textarea id="user_bio" name="user_bio" rows="4" placeholder="Tell us about yourself...">' . esc_textarea(get_the_author_meta('description', $user_id)) . '</textarea>';
    echo '</div>';
    
    echo '<div class="form-group">';
    echo '<label for="user_url">Website</label>';
    echo '<input type="url" id="user_url" name="user_url" value="' . esc_url($user->user_url) . '" placeholder="https://yourwebsite.com" />';
    echo '</div>';
    
    echo '<div class="form-group">';
    echo '<label for="user_location">Location</label>';
    echo '<input type="text" id="user_location" name="user_location" value="' . esc_attr(get_user_meta($user_id, 'location', true)) . '" placeholder="City, Country" />';
    echo '</div>';
    
    echo '<button type="submit" class="button button-primary">Continue</button>';
    
    echo '</form>';
}

Profile Processing

// Handle profile completion submission
function ec_handle_profile_completion($user_id, $data) {
    // Verify nonce
    if (!wp_verify_nonce($data['onboarding_nonce'], 'onboarding_profile_' . $user_id)) {
        return new WP_Error('invalid_nonce', 'Security check failed');
    }
    
    // Update user data
    $update_data = [
        'ID' => $user_id,
        'display_name' => sanitize_text_field($data['display_name']),
        'user_url' => esc_url_raw($data['user_url'])
    ];
    
    $result = wp_update_user($update_data);
    
    if (is_wp_error($result)) {
        return $result;
    }
    
    // Update user meta
    if (!empty($data['user_bio'])) {
        update_user_meta($user_id, 'description', sanitize_textarea_field($data['user_bio']));
    }
    
    if (!empty($data['user_location'])) {
        update_user_meta($user_id, 'location', sanitize_text_field($data['user_location']));
    }
    
    // Mark step as complete
    ec_mark_onboarding_step_complete($user_id, 'profile_completion');
    
    // Award points for profile completion
    ec_award_points($user_id, 'profile_completed');
    
    return true;
}

Avatar Upload Step

Avatar Upload Form

// Render avatar upload step
function ec_render_avatar_upload_step($user_id) {
    echo '<div class="avatar-upload-container">';
    
    // Current avatar
    $current_avatar = get_avatar($user_id, 120);
    echo '<div class="current-avatar">' . $current_avatar . '</div>';
    
    // Upload form
    echo '<form class="avatar-upload-form" method="post" enctype="multipart/form-data">';
    wp_nonce_field('onboarding_avatar_' . $user_id, 'avatar_nonce');
    
    echo '<div class="upload-area" id="avatar-drop-zone">';
    echo '<input type="file" id="avatar-file" name="avatar_file" accept="image/*" />';
    echo '<label for="avatar-file" class="upload-label">';
    echo '<span class="upload-icon">' . ec_get_icon_svg('upload') . '</span>';
    echo '<span class="upload-text">Click or drag to upload avatar</span>';
    echo '</label>';
    echo '</div>';
    
    echo '<div class="avatar-preview" id="avatar-preview"></div>';
    
    echo '<div class="button-group">';
    echo '<button type="submit" class="button button-primary" id="save-avatar">Save Avatar</button>';
    echo '<button type="button" class="button" id="skip-avatar">Skip This Step</button>';
    echo '</div>';
    
    echo '</form>';
    echo '</div>';
}

Avatar Processing

// Handle avatar upload
function ec_handle_avatar_upload($user_id, $file) {
    // Verify nonce
    if (!wp_verify_nonce($_POST['avatar_nonce'], 'onboarding_avatar_' . $user_id)) {
        return new WP_Error('invalid_nonce', 'Security check failed');
    }
    
    // Validate file
    if (!$file || $file['error'] !== UPLOAD_ERR_OK) {
        return new WP_Error('upload_error', 'File upload failed');
    }
    
    // Check file type
    $allowed_types = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
    $file_type = wp_check_filetype_and_ext($file['tmp_name'], $file['name']);
    
    if (!in_array($file_type['type'], $allowed_types)) {
        return new WP_Error('invalid_file_type', 'Invalid file type. Please upload an image.');
    }
    
    // Upload to media library
    $upload = wp_handle_upload($file, ['test_form' => false]);
    
    if (isset($upload['error'])) {
        return new WP_Error('upload_failed', $upload['error']);
    }
    
    // Create attachment
    $attachment_id = wp_insert_attachment([
        'post_mime_type' => $upload['type'],
        'post_title' => 'Avatar for user ' . $user_id,
        'post_content' => '',
        'post_status' => 'inherit'
    ], $upload['file'], 0);
    
    // Generate thumbnails
    require_once(ABSPATH . 'wp-admin/includes/image.php');
    $attachment_data = wp_generate_attachment_metadata($attachment_id, $upload['file']);
    wp_update_attachment_metadata($attachment_id, $attachment_data);
    
    // Set as user avatar
    update_user_meta($user_id, 'avatar_url', wp_get_attachment_url($attachment_id));
    
    // Mark step as complete
    ec_mark_onboarding_step_complete($user_id, 'avatar_upload');
    
    // Award points for avatar upload
    ec_award_points($user_id, 'avatar_uploaded');
    
    return true;
}

Interests Selection

Interest Categories

// Get interest categories for selection
function ec_get_interest_categories() {
    return [
        'music_genres' => [
            'label' => 'Music Genres',
            'options' => [
                'indie' => 'Indie Rock',
                'electronic' => 'Electronic',
                'hip_hop' => 'Hip Hop',
                'folk' => 'Folk',
                'punk' => 'Punk',
                'jazz' => 'Jazz',
                'experimental' => 'Experimental'
            ]
        ],
        'activities' => [
            'label' => 'Activities',
            'options' => [
                'live_shows' => 'Attending Live Shows',
                'discovering_music' => 'Discovering New Music',
                'playing_music' => 'Playing Music',
                'collecting_vinyl' => 'Collecting Vinyl',
                'music_production' => 'Music Production',
                'writing_reviews' => 'Writing Reviews'
            ]
        ],
        'content' => [
            'label' => 'Content Preferences',
            'options' => [
                'album_reviews' => 'Album Reviews',
                'artist_interviews' => 'Artist Interviews',
                'festival_coverage' => 'Festival Coverage',
                'music_news' => 'Music News',
                'playlists' => 'Curated Playlists',
                'discovery_guides' => 'Discovery Guides'
            ]
        ]
    ];
}

Interests Form

// Render interests selection step
function ec_render_interests_step($user_id) {
    $categories = ec_get_interest_categories();
    $user_interests = get_user_meta($user_id, 'user_interests', true) ?: [];
    
    echo '<form class="interests-form" method="post">';
    wp_nonce_field('onboarding_interests_' . $user_id, 'interests_nonce');
    
    foreach ($categories as $category_key => $category) {
        echo '<div class="interest-category">';
        echo '<h3>' . esc_html($category['label']) . '</h3>';
        echo '<div class="interest-options">';
        
        foreach ($category['options'] as $option_key => $option_label) {
            $checked = in_array($option_key, $user_interests) ? 'checked' : '';
            
            echo '<label class="interest-option">';
            echo '<input type="checkbox" name="interests[]" value="' . esc_attr($option_key) . '" ' . $checked . ' />';
            echo '<span class="checkbox-label">' . esc_html($option_label) . '</span>';
            echo '</label>';
        }
        
        echo '</div>';
        echo '</div>';
    }
    
    echo '<div class="button-group">';
    echo '<button type="submit" class="button button-primary">Continue</button>';
    echo '<button type="button" class="button" id="skip-interests">Skip This Step</button>';
    echo '</div>';
    
    echo '</form>';
}

Onboarding Progress Tracking

Step Completion

// Mark onboarding step as complete
function ec_mark_onboarding_step_complete($user_id, $step_key) {
    $progress = get_user_meta($user_id, 'onboarding_progress', true) ?: [];
    
    if (!in_array($step_key, $progress)) {
        $progress[] = $step_key;
        update_user_meta($user_id, 'onboarding_progress', $progress);
    }
    
    // Move to next step
    $steps = array_keys(ec_get_onboarding_steps());
    $current_index = array_search($step_key, $steps);
    
    if ($current_index !== false && $current_index < count($steps) - 1) {
        $next_step = $steps[$current_index + 1];
        update_user_meta($user_id, 'onboarding_current_step', $next_step);
    } else {
        // Onboarding complete
        ec_complete_onboarding($user_id);
    }
}

Onboarding Completion

// Complete onboarding process
function ec_complete_onboarding($user_id) {
    update_user_meta($user_id, 'onboarding_completed', true);
    delete_user_meta($user_id, 'onboarding_current_step');
    
    // Award completion bonus
    ec_award_points($user_id, 'onboarding_completed');
    
    // Check for onboarding badges
    ec_check_and_award_badges($user_id);
    
    // Send welcome email
    ec_send_welcome_email($user_id);
    
    // Trigger completion action
    do_action('ec_onboarding_completed', $user_id);
}

Integration Points

Community Integration

// Community onboarding integration
function ec_community_onboarding_integration($user_id) {
    // Auto-join community forums
    if (function_exists('bbp_add_user_to_forum')) {
        $main_forum_id = get_option('_bbp_root_forum_group_id');
        bbp_add_user_to_forum($user_id, $main_forum_id);
    }
    
    // Create welcome topic
    ec_create_welcome_topic($user_id);
}

Newsletter Integration

// Newsletter onboarding integration
function ec_newsletter_onboarding_integration($user_id, $interests) {
    if (in_array('music_news', $interests)) {
        // Subscribe to newsletter
        if (function_exists('extrachill_subscribe_user')) {
            $user = get_userdata($user_id);
            extrachill_subscribe_user($user->user_email, 'onboarding');
        }
    }
}

API Integration

Onboarding API

// Register onboarding API endpoints
function ec_register_onboarding_api_endpoints() {
    register_rest_route('extrachill/v1', '/onboarding/progress', [
        'methods' => 'GET',
        'callback' => 'ec_api_get_onboarding_progress',
        'permission_callback' => function() {
            return is_user_logged_in();
        }
    ]);
    
    register_rest_route('extrachill/v1', '/onboarding/step', [
        'methods' => 'POST',
        'callback' => 'ec_api_complete_onboarding_step',
        'permission_callback' => function() {
            return is_user_logged_in();
        }
    ]);
}

Security and Validation

Input Validation

// Validate onboarding submission
function ec_validate_onboarding_submission($step, $data) {
    switch ($step) {
        case 'profile_completion':
            return ec_validate_profile_data($data);
            
        case 'avatar_upload':
            return ec_validate_avatar_upload($data);
            
        case 'interests':
            return ec_validate_interests_selection($data);
            
        default:
            return true;
    }
}

Performance Optimization

Lazy Loading

// Load onboarding resources only when needed
function ec_enqueue_onboarding_resources() {
    if (is_page('onboarding') || ec_should_show_onboarding()) {
        wp_enqueue_style('extrachill-onboarding');
        wp_enqueue_script('extrachill-onboarding');
    }
}