upskill-event-manager/docs/DEVELOPMENT-GUIDE.md
bengizmo 705e6b563c feat: Implement Training Leads system and restructure navigation menu
- Add comprehensive Training Leads system for HVAC trainers
  * New /trainer/training-leads/ page with tabular contact submission display
  * HVAC_Training_Leads class with AJAX status updates and filtering
  * Empty state messaging and profile sharing CTA
  * Database integration with existing contact forms system

- Restructure trainer navigation menu for better UX
  * Rename "Customize" to "Profile" with logical groupings
  * Move "Logout" under "Profile" submenu
  * Change "Personal Profile" to "Trainer Profile"
  * Add "Training Leads" under Profile section
  * Update help menu to show only question mark icon positioned far right

- Enhance documentation system
  * Fix /trainer/documentation/ page styling and navigation integration
  * Update content to reflect current platform features
  * Add Training Leads documentation and navigation guide
  * Implement proper WordPress template structure

- Update user management
  * Change joe@upskillhvac.com display name to "Joe Medosch"
  * Assign Joe as author of measureQuick headquarters venue
  * Assign Joe as author of measureQuick and Upskill HVAC organizers

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-05 16:02:57 -03:00

27 KiB

HVAC Plugin Development Guide

Table of Contents

Development Setup

Prerequisites

  • PHP 7.4+ (8.0+ recommended)
  • Node.js 16+ and npm
  • Composer
  • WP-CLI
  • Git

Local Environment Setup

  1. Clone Repository
git clone <repository-url>
cd upskill-event-manager
  1. Install Dependencies
# PHP dependencies
composer install

# Node dependencies
npm install

# Create .env file
cp .env.example .env
# Edit .env with your environment details
  1. Configure WordPress
  • Install WordPress locally
  • Create database
  • Configure wp-config.php
  • Install The Events Calendar plugin
  1. Activate Plugin
wp plugin activate hvac-community-events

Coding Standards

PHP Standards

  1. Follow WordPress Coding Standards
// Good
function hvac_get_trainer_events( $trainer_id ) {
    $args = array(
        'post_type'      => 'tribe_events',
        'posts_per_page' => -1,
        'meta_query'     => array(
            array(
                'key'     => '_EventTrainerID',
                'value'   => $trainer_id,
                'compare' => '='
            )
        )
    );
    
    return get_posts( $args );
}

// Bad
function getTrainerEvents($trainerId) {
    $args = [
        'post_type' => 'tribe_events',
        'posts_per_page' => -1,
        'meta_query' => [
            ['key' => '_EventTrainerID', 'value' => $trainerId, 'compare' => '=']
        ]
    ];
    return get_posts($args);
}
  1. Namespace Usage
// Use prefixes instead of PHP namespaces for WordPress compatibility
class HVAC_Event_Manager {
    // Class implementation
}
  1. Security First
// Always escape output
echo esc_html( $trainer_name );
echo esc_url( $profile_url );
echo esc_attr( $css_class );

// Sanitize input
$trainer_id = absint( $_GET['trainer_id'] );
$search = sanitize_text_field( $_POST['search'] );

// Verify nonces
if ( ! wp_verify_nonce( $_POST['hvac_nonce'], 'hvac_action' ) ) {
    wp_die( 'Security check failed' );
}

JavaScript Standards

  1. jQuery Usage
// Always use jQuery in no-conflict mode
jQuery(document).ready(function($) {
    // $ is now safe to use
    $('.hvac-button').on('click', function(e) {
        e.preventDefault();
        // Handle click
    });
});
  1. AJAX Requests
jQuery.ajax({
    url: hvac_ajax.ajax_url,
    type: 'POST',
    data: {
        action: 'hvac_update_profile',
        nonce: hvac_ajax.nonce,
        data: formData
    },
    success: function(response) {
        if (response.success) {
            // Handle success
        } else {
            // Handle error
            console.error(response.data.message);
        }
    }
});

CSS Standards

  1. BEM Methodology
/* Block */
.hvac-trainer-card {}

/* Element */
.hvac-trainer-card__header {}
.hvac-trainer-card__content {}

/* Modifier */
.hvac-trainer-card--featured {}
  1. Specificity Rules
/* Avoid overly specific selectors */
/* Bad */
body.logged-in .hvac-wrapper div.container .hvac-trainer-card {}

/* Good */
.hvac-trainer-card {}

/* When specificity is needed for theme overrides */
.hvac-astra-integrated .hvac-trainer-card {}

Architecture Principles

1. Single Responsibility Principle

Each class should have one primary responsibility:

// Good - Focused classes
class HVAC_Certificate_Generator {}  // Only handles PDF generation
class HVAC_Event_Manager {}         // Only manages events
class HVAC_Email_Sender {}          // Only sends emails

// Bad - Kitchen sink class
class HVAC_Everything {}  // Does certificates, events, emails, etc.

2. Dependency Injection

class HVAC_Certificate_Service {
    private $generator;
    private $emailer;
    
    public function __construct( $generator, $emailer ) {
        $this->generator = $generator;
        $this->emailer = $emailer;
    }
}

3. Hook-Driven Architecture

// Use WordPress hooks for extensibility
do_action( 'hvac_before_event_save', $event_data );
$event_data = apply_filters( 'hvac_event_data', $event_data );
do_action( 'hvac_after_event_save', $event_id );

4. Singleton Pattern (where appropriate)

class HVAC_Plugin {
    private static $instance = null;
    
    public static function instance() {
        if ( null === self::$instance ) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    private function __construct() {
        // Initialize
    }
}

Development Workflow

1. Feature Development

# Create feature branch
git checkout -b feature/new-trainer-dashboard

# Make changes
# Test thoroughly
# Commit with meaningful messages
git add .
git commit -m "feat: Add advanced filtering to trainer dashboard

- Add date range picker
- Add event status filter
- Add export functionality"

# Push and create PR
git push origin feature/new-trainer-dashboard

2. Git Commit Messages

Follow conventional commits:

  • feat: New feature
  • fix: Bug fix
  • docs: Documentation
  • style: Formatting changes
  • refactor: Code restructuring
  • test: Test additions/changes
  • chore: Maintenance tasks

3. Code Review Checklist

  • Follows WordPress coding standards
  • Security measures implemented (escaping, sanitization)
  • Performance impact considered
  • Backward compatibility maintained
  • Documentation updated
  • Tests written/updated

Testing Guidelines

1. Unit Testing

class Test_HVAC_Event_Manager extends WP_UnitTestCase {
    public function test_create_event() {
        $event_data = array(
            'post_title' => 'Test Event',
            'EventStartDate' => '2025-08-15 09:00:00'
        );
        
        $event_id = HVAC_Event_Manager::create_event( $event_data );
        
        $this->assertIsInt( $event_id );
        $this->assertGreaterThan( 0, $event_id );
    }
}

2. E2E Testing with Playwright

test('Trainer can create event', async ({ page }) => {
    // Login
    await loginAsTrainer(page);
    
    // Navigate to event creation
    await page.goto('/trainer/event/manage/');
    
    // Fill form
    await page.fill('#event-title', 'HVAC Training Session');
    await page.selectOption('#event-venue', 'Main Campus');
    
    // Submit
    await page.click('button[type="submit"]');
    
    // Verify
    await expect(page).toHaveURL(/event-created/);
});

3. Manual Testing Checklist

  • Test on staging before production
  • Test with different user roles
  • Test on mobile devices
  • Test with different themes (especially Astra)
  • Test with caching enabled

Deployment Process

1. Pre-Deployment Checks

# Run validation
bin/pre-deployment-check.sh

# Run tests
npm test
phpunit

2. Staging Deployment

# Deploy to staging
scripts/deploy.sh staging

# Verify deployment
scripts/verify-plugin-fixes.sh

3. Production Deployment

# Only when explicitly requested
scripts/deploy.sh production
# Requires double confirmation

4. Post-Deployment

  • Monitor error logs
  • Check critical functionality
  • Be ready to rollback if needed

Security Best Practices

1. Data Validation

// Input validation
if ( ! is_email( $email ) ) {
    wp_die( 'Invalid email address' );
}

// Type checking
$event_id = absint( $_POST['event_id'] );
if ( ! $event_id ) {
    wp_die( 'Invalid event ID' );
}

2. SQL Queries

// Always use prepared statements
global $wpdb;
$results = $wpdb->get_results( 
    $wpdb->prepare(
        "SELECT * FROM {$wpdb->posts} WHERE post_author = %d AND post_type = %s",
        $user_id,
        'tribe_events'
    )
);

3. File Uploads

// Validate file types
$allowed_types = array( 'jpg', 'jpeg', 'png', 'pdf' );
$file_type = wp_check_filetype( $file['name'] );

if ( ! in_array( $file_type['ext'], $allowed_types ) ) {
    wp_die( 'Invalid file type' );
}

4. Capability Checks

// Always verify user permissions
if ( ! current_user_can( 'manage_own_events' ) ) {
    wp_die( 'Insufficient permissions' );
}

Performance Optimization

1. Database Queries

// Cache expensive queries
$cache_key = 'hvac_trainer_events_' . $trainer_id;
$events = wp_cache_get( $cache_key );

if ( false === $events ) {
    $events = get_posts( $args );
    wp_cache_set( $cache_key, $events, '', 3600 ); // 1 hour
}

2. Asset Loading

// Load assets only where needed
public function enqueue_scripts() {
    if ( ! $this->is_plugin_page() ) {
        return;
    }
    
    wp_enqueue_script( 'hvac-dashboard' );
}

3. AJAX Optimization

// Debounce search requests
let searchTimeout;
$('#search').on('keyup', function() {
    clearTimeout(searchTimeout);
    searchTimeout = setTimeout(function() {
        performSearch();
    }, 300);
});

4. Image Optimization

  • Use appropriate image sizes
  • Lazy load images
  • Compress before upload
  • Use WebP format when possible

Common Patterns

1. AJAX Handler Pattern

class HVAC_Ajax_Handler {
    public function __construct() {
        add_action( 'wp_ajax_hvac_action', array( $this, 'handle_request' ) );
    }
    
    public function handle_request() {
        // Verify nonce
        if ( ! wp_verify_nonce( $_POST['nonce'], 'hvac_nonce' ) ) {
            wp_send_json_error( 'Invalid nonce' );
        }
        
        // Check capabilities
        if ( ! current_user_can( 'required_capability' ) ) {
            wp_send_json_error( 'Insufficient permissions' );
        }
        
        // Process request
        $result = $this->process_data( $_POST['data'] );
        
        // Return response
        if ( $result ) {
            wp_send_json_success( $result );
        } else {
            wp_send_json_error( 'Processing failed' );
        }
    }
}

2. Settings Page Pattern

class HVAC_Settings {
    public function __construct() {
        add_action( 'admin_menu', array( $this, 'add_menu' ) );
        add_action( 'admin_init', array( $this, 'register_settings' ) );
    }
    
    public function add_menu() {
        add_options_page(
            'HVAC Settings',
            'HVAC Events',
            'manage_options',
            'hvac-settings',
            array( $this, 'render_page' )
        );
    }
    
    public function register_settings() {
        register_setting( 'hvac_settings', 'hvac_options' );
    }
}

Access Control Patterns

Role-Based Field Access

The plugin implements sophisticated role-based access control for sensitive data:

// Check permissions for certification field editing
$can_edit_certifications = current_user_can('administrator') || current_user_can('hvac_master_trainer');

if ( $can_edit_certifications ) {
    // Allow editing certification fields
    update_user_meta($user_id, 'certification_status', sanitize_text_field($_POST['certification_status']));
} else {
    // Show read-only display for regular trainers
    echo '<div class="hvac-read-only-field">' . esc_html($certification_status) . '</div>';
}

Field-Level Access Control Implementation

// Different access levels for different user types
if ( current_user_can('hvac_trainer') && ! current_user_can('hvac_master_trainer') ) {
    // Trainer: read-only access to certification fields
    echo '<input type="hidden" name="certification_type" value="' . esc_attr($certification_type) . '" />';
    echo '<div class="hvac-read-only-field">' . esc_html($certification_type) . '</div>';
} else if ( current_user_can('administrator') || current_user_can('hvac_master_trainer') ) {
    // Admin/Master Trainer: full edit access
    echo '<select name="certification_type">';
    // ... render editable dropdown
    echo '</select>';
}

Access Control Best Practices

  1. Always validate permissions server-side - Never rely on frontend-only restrictions
  2. Use capability checks - Check specific capabilities rather than user roles when possible
  3. Implement graceful degradation - Show read-only versions rather than hiding fields entirely
  4. Visual indicators - Clearly indicate when fields are read-only
  5. Consistent patterns - Use the same access control patterns throughout the plugin

CSV Import System

Enhanced CSV Import Architecture

The plugin includes a comprehensive CSV import system designed to process trainer profile data with full taxonomy integration.

File Structure

includes/
├── enhanced-csv-import-from-file.php    # Main CSV import class
├── class-hvac-geocoding-ajax.php        # AJAX handlers (updated)
└── taxonomy-migration.php               # Taxonomy migration utilities

CSV Import Class

class HVAC_Enhanced_CSV_Import {
    /**
     * Execute comprehensive CSV import
     * @return array Import results and statistics
     */
    public function execute_import()
    
    /**
     * Process individual CSV row
     * @param array $headers CSV headers
     * @param array $row CSV row data
     * @param int $row_number Row number for error reporting
     */
    private function process_row($headers, $row, $row_number)
    
    /**
     * Create or update trainer profile
     * @param int $user_id WordPress user ID
     * @param array $data CSV row data
     * @return int Profile post ID
     */
    private function create_or_update_profile($user_id, $data)
    
    /**
     * Assign taxonomy terms to profile
     * @param int $profile_id Profile post ID
     * @param array $data CSV row data
     */
    private function assign_taxonomies($profile_id, $data)
}

Supported CSV Fields

The import system processes 19 CSV fields:

Contact Information:

  • Name, Last Name, Email, Phone Number

Professional Data:

  • Company Name, Role, Company Website

Location Data:

  • Country, State, City

Certification Information:

  • Date Certified, Certification Type, Certification Status

Taxonomy Fields:

  • Business Typebusiness_type taxonomy
  • Training Audiencetraining_audience taxonomy

System Fields:

  • User ID, Application Details, Create Venue, Create Organizer

Multi-Value Taxonomy Handling

The system handles comma-separated taxonomy values:

// Input: "Educator,Consultant" 
// Output: Two taxonomy terms assigned to profile

private function parse_comma_separated($value) {
    $items = explode(',', $value);
    return array_map('trim', $items);
}

private function assign_taxonomy_terms($profile_id, $taxonomy, $terms) {
    foreach ($terms as $term_name) {
        // Get or create term
        $term = get_term_by('name', $term_name, $taxonomy);
        if (!$term) {
            $term_data = wp_insert_term($term_name, $taxonomy);
        }
    }
    wp_set_object_terms($profile_id, $term_ids, $taxonomy);
}

AJAX Integration

The CSV import integrates with the existing AJAX system:

// Execute enhanced CSV import
jQuery.post(hvac_ajax.ajax_url, {
    action: 'hvac_run_enhanced_import',
    nonce: hvac_ajax.nonce
}, function(response) {
    if (response.success) {
        console.log('Import completed:', response.data);
        // Display results to user
    }
});

Error Handling and Logging

try {
    $results = $this->process_row($headers, $row, $row_number);
} catch (Exception $e) {
    $this->results['errors']++;
    $this->results['details'][] = "Row $row_number error: " . $e->getMessage();
    error_log("CSV Import Row $row_number Error: " . $e->getMessage());
}

Testing the CSV Import

# Test import locally
php test-enhanced-csv-import.php

# Test on staging
ssh user@staging-server "cd /path/to/plugin && php test-csv-import.php"

Development Best Practices for CSV Import

  1. Always validate CSV structure before processing
  2. Handle missing/malformed data gracefully
  3. Use transactions for large imports when possible
  4. Provide detailed progress feedback to users
  5. Log all import activities for debugging
  6. Test with various CSV formats and edge cases

Trainer Profile Sharing Implementation

Overview

The trainer profile sharing system allows trainers to generate shareable URLs and QR codes for their profiles, enabling professional marketing and networking opportunities.

Core Components

1. HVAC_QR_Generator Class

The main class handling QR code generation and profile sharing functionality:

class HVAC_QR_Generator {
    private static $instance = null;
    
    public static function instance() {
        if (null === self::$instance) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    private function __construct() {
        $this->init_hooks();
    }
    
    private function init_hooks() {
        // AJAX handlers
        add_action('wp_ajax_hvac_get_profile_share_data', [$this, 'ajax_get_profile_share_data']);
        add_action('wp_ajax_nopriv_hvac_get_profile_share_data', [$this, 'ajax_get_profile_share_data']);
        
        // URL rewrite rules
        add_action('init', [$this, 'add_profile_rewrite_rules']);
        add_filter('query_vars', [$this, 'add_profile_query_vars']);
    }
}

2. QR Code Generation

The system uses QR Server API for generating QR codes (switched from deprecated Google Charts API):

public function generate_qr_url($data, $size = 200, $error_correction = 'M') {
    $encoded_data = urlencode($data);
    
    $qr_url = sprintf(
        'https://api.qrserver.com/v1/create-qr-code/?size=%dx%d&data=%s&ecc=%s',
        $size,
        $size,
        $encoded_data,
        strtoupper($error_correction)
    );
    
    return $qr_url;
}

Key Implementation Notes:

  • API Migration: Switched from Google Charts API (deprecated) to QR Server API
  • URL Encoding: Proper URL encoding prevents malformed QR codes
  • Error Correction: Medium level (M) provides good balance of data capacity and error recovery
  • Size Flexibility: Configurable size for different use cases

3. Share URL Structure

Profile sharing URLs follow a hierarchical pattern with proper WordPress rewrite rules:

public function add_profile_rewrite_rules() {
    add_rewrite_rule(
        '^find-a-trainer/profile/([0-9]+)/?$',
        'index.php?pagename=find-a-trainer&trainer_profile_id=$matches[1]',
        'top'
    );
}

public function get_trainer_profile_share_url($profile_id) {
    $find_trainer_page = get_page_by_path('find-a-trainer');
    if (!$find_trainer_page) {
        return false;
    }
    
    $base_url = get_permalink($find_trainer_page->ID);
    
    // CRITICAL: Include trailing slash for WordPress rewrite rules
    $profile_url = trailingslashit($base_url) . 'profile/' . $profile_id . '/';
    
    return $profile_url;
}

Important Implementation Details:

  • Trailing Slash Required: WordPress rewrite rules require trailing slash
  • Query Variables: Custom query var trainer_profile_id for profile identification
  • URL Pattern: /find-a-trainer/profile/{profile_id}/ structure

4. AJAX Handler Implementation

The AJAX handler follows security best practices with nonce verification:

public function ajax_get_profile_share_data() {
    // Security: Verify nonce
    if (!wp_verify_nonce($_POST['nonce'], 'hvac_profile_sharing')) {
        wp_send_json_error(['message' => 'Security check failed']);
        return;
    }
    
    $profile_id = intval($_POST['profile_id']);
    if (!$profile_id) {
        wp_send_json_error(['message' => 'Invalid profile ID']);
        return;
    }
    
    // Get comprehensive share data
    $share_data = $this->get_trainer_share_data($profile_id);
    
    if (!$share_data) {
        wp_send_json_error(['message' => 'Profile not found or not accessible']);
        return;
    }
    
    wp_send_json_success($share_data);
}

5. Frontend JavaScript Implementation

The JavaScript component handles the share modal and user interactions:

var ProfileSharing = {
    init: function() {
        this.bindEvents();
    },
    
    bindEvents: function() {
        $(document).on('click', '.hvac-share-profile-btn', this.openShareModal);
        $(document).on('click', '.hvac-copy-url-btn', this.copyShareUrl);
        $(document).on('click', '.hvac-modal-close', this.closeShareModal);
        
        // Escape key support
        $(document).on('keydown', function(e) {
            if (e.keyCode === 27 && $('.hvac-share-modal:visible').length) {
                ProfileSharing.closeShareModal();
            }
        });
    },
    
    loadShareData: function(profileId) {
        $.ajax({
            url: hvac_sharing.ajax_url,
            type: 'POST',
            data: {
                action: 'hvac_get_profile_share_data',
                profile_id: profileId,
                nonce: hvac_sharing.nonce
            },
            success: function(response) {
                if (response.success && response.data) {
                    ProfileSharing.populateShareData(response.data);
                } else {
                    ProfileSharing.showError(response.data ? response.data.message : 'Unknown error occurred');
                }
            },
            error: function(xhr, status, error) {
                ProfileSharing.showError('Network error occurred. Please try again.');
            }
        });
    }
};

Development Guidelines for Profile Sharing

1. Security Considerations

// Always verify nonces in AJAX handlers
if (!wp_verify_nonce($_POST['nonce'], 'hvac_profile_sharing')) {
    wp_send_json_error(['message' => 'Security check failed']);
    return;
}

// Sanitize and validate input
$profile_id = intval($_POST['profile_id']);
if (!$profile_id || $profile_id <= 0) {
    wp_send_json_error(['message' => 'Invalid profile ID']);
    return;
}

// Check profile accessibility
$profile = get_post($profile_id);
if (!$profile || $profile->post_type !== 'trainer_profile') {
    wp_send_json_error(['message' => 'Profile not found']);
    return;
}

2. Error Handling Patterns

// Graceful degradation for missing data
$trainer_name = get_post_meta($profile_id, 'trainer_display_name', true) ?: $user->display_name ?: 'Unknown Trainer';

// API fallback handling
$qr_url = $this->generate_qr_url($profile_url, $size);
if (!$qr_url) {
    error_log("QR code generation failed for profile {$profile_id}");
    // Continue without QR code rather than failing completely
}

3. Frontend User Experience

// Loading states
$cardContainer.html(
    '<div class="hvac-share-card-loading">' +
        '<span class="dashicons dashicons-update spin"></span>' +
        '<p>Loading profile card...</p>' +
    '</div>'
);

// Error states with helpful messaging
ProfileSharing.showError = function(message) {
    $cardContainer.html(
        '<div class="hvac-share-card-loading">' +
            '<span class="dashicons dashicons-warning" style="color: #dc3545;"></span>' +
            '<p style="color: #dc3545;">' + message + '</p>' +
        '</div>'
    );
};

// Success feedback for copy operations
ProfileSharing.showCopySuccess = function($button) {
    var originalText = $button.text();
    $button.addClass('copied').text('Copied!');
    
    setTimeout(function() {
        $button.removeClass('copied').text(originalText);
    }, 2000);
};

4. Template Integration

// In page templates, add the share button
<button type="button" class="hvac-button hvac-button-secondary hvac-share-profile-btn" 
        data-profile-id="<?php echo esc_attr($profile->ID); ?>">
    <span class="dashicons dashicons-share"></span> Share Profile
</button>

// Include the modal HTML at the end of the template
<div id="hvac-share-profile-modal" class="hvac-share-modal" style="display: none;">
    <!-- Modal content -->
</div>

5. Asset Management

// In HVAC_Scripts_Styles class
public function localize_sharing_data($additional_data = []) {
    wp_localize_script('hvac-profile-sharing', 'hvac_sharing', array_merge([
        'ajax_url' => admin_url('admin-ajax.php'),
        'nonce' => wp_create_nonce('hvac_profile_sharing'),
        'strings' => [
            'copied' => 'Copied!',
            'copy_error' => 'Copy failed'
        ]
    ], $additional_data));
}

Testing Profile Sharing

1. Unit Testing

// Test QR URL generation
public function test_qr_url_generation() {
    $qr_generator = HVAC_QR_Generator::instance();
    $test_url = 'https://example.com/find-a-trainer/profile/123/';
    
    $qr_url = $qr_generator->generate_qr_url($test_url, 200, 'M');
    
    $this->assertStringContains('api.qrserver.com', $qr_url);
    $this->assertStringContains('size=200x200', $qr_url);
    $this->assertStringContains(urlencode($test_url), $qr_url);
}

// Test share URL generation
public function test_share_url_generation() {
    $qr_generator = HVAC_QR_Generator::instance();
    $profile_id = 123;
    
    $share_url = $qr_generator->get_trainer_profile_share_url($profile_id);
    
    $this->assertStringEndsWith('/', $share_url); // Must have trailing slash
    $this->assertStringContains('/profile/' . $profile_id . '/', $share_url);
}

2. End-to-End Testing

// Playwright test for profile sharing
test('trainer can share profile with QR code', async ({ page }) => {
    await page.goto('/trainer/profile/');
    await page.click('.hvac-share-profile-btn');
    
    // Wait for modal to appear
    await page.waitForSelector('#hvac-share-profile-modal');
    
    // Verify share URL is populated
    const shareUrl = await page.inputValue('#hvac-share-url');
    expect(shareUrl).toContain('/find-a-trainer/profile/');
    
    // Verify QR code image is loaded
    const qrImage = page.locator('.hvac-share-qr img');
    await expect(qrImage).toBeVisible();
    
    // Test copy functionality
    await page.click('.hvac-copy-url-btn');
    await expect(page.locator('.hvac-copy-url-btn')).toHaveClass(/copied/);
});

Deployment Considerations

1. URL Rewrite Rules

After deploying profile sharing functionality:

# Flush rewrite rules to activate new URL patterns
wp rewrite flush

# Verify rewrite rules are working
wp rewrite list | grep "find-a-trainer"

2. QR Code API Dependencies

  • External Dependency: QR Server API (https://api.qrserver.com)
  • Fallback: Consider implementing fallback QR generation if API becomes unavailable
  • Caching: QR URLs are generated dynamically but could be cached for performance

3. Performance Monitoring

// Add timing monitoring for QR generation
$start_time = microtime(true);
$qr_url = $this->generate_qr_url($data, $size);
$generation_time = microtime(true) - $start_time;

if ($generation_time > 2.0) {
    error_log("Slow QR generation: {$generation_time}s for profile {$profile_id}");
}

Resources