🚨 CRITICAL: Fixed deployment blockers by adding missing core directories: **Community System (CRITICAL)** - includes/community/ - Login_Handler and all community classes - templates/community/ - Community login forms **Certificate System (CRITICAL)** - includes/certificates/ - 8+ certificate classes and handlers - templates/certificates/ - Certificate reports and generation templates **Core Individual Classes (CRITICAL)** - includes/class-hvac-event-summary.php - includes/class-hvac-trainer-profile-manager.php - includes/class-hvac-master-dashboard-data.php - Plus 40+ other individual HVAC classes **Major Feature Systems (HIGH)** - includes/database/ - Training leads database tables - includes/find-trainer/ - Find trainer directory and MapGeo integration - includes/google-sheets/ - Google Sheets integration system - includes/zoho/ - Complete Zoho CRM integration - includes/communication/ - Communication templates system **Template Infrastructure** - templates/attendee/, templates/email-attendees/ - templates/event-summary/, templates/status/ - templates/template-parts/ - Shared template components **Impact:** - 70+ files added covering 10+ missing directories - Resolves ALL deployment blockers and feature breakdowns - Plugin activation should now work correctly - Multi-machine deployment fully supported 🔧 Generated with Claude Code Co-Authored-By: Ben Reed <ben@tealmaker.com>
1419 lines
No EOL
57 KiB
PHP
1419 lines
No EOL
57 KiB
PHP
<?php
|
||
/**
|
||
* MapGeo Plugin Integration for Find a Trainer
|
||
*
|
||
* @package HVAC_Plugin
|
||
* @since 1.0.0
|
||
*/
|
||
|
||
if (!defined('ABSPATH')) {
|
||
exit;
|
||
}
|
||
|
||
/**
|
||
* Class HVAC_MapGeo_Integration
|
||
* Handles integration with MapGeo plugin for trainer map display
|
||
*/
|
||
class HVAC_MapGeo_Integration {
|
||
|
||
/**
|
||
* Instance of this class
|
||
*
|
||
* @var HVAC_MapGeo_Integration
|
||
*/
|
||
private static $instance = null;
|
||
|
||
/**
|
||
* MapGeo map ID
|
||
*
|
||
* @var string
|
||
*/
|
||
private $map_id = '5872';
|
||
|
||
/**
|
||
* Get instance of this class
|
||
*
|
||
* @return HVAC_MapGeo_Integration
|
||
*/
|
||
public static function get_instance() {
|
||
if (null === self::$instance) {
|
||
self::$instance = new self();
|
||
}
|
||
return self::$instance;
|
||
}
|
||
|
||
/**
|
||
* Constructor
|
||
*/
|
||
private function __construct() {
|
||
$this->init_hooks();
|
||
}
|
||
|
||
/**
|
||
* Check if we're in production environment
|
||
*/
|
||
private function is_production() {
|
||
// Check if we're on upskillhvac.com domain (production)
|
||
$site_url = get_site_url();
|
||
return strpos($site_url, 'upskillhvac.com') !== false;
|
||
}
|
||
|
||
/**
|
||
* Debug logging wrapper
|
||
*/
|
||
private function debug_log($message) {
|
||
if (!$this->is_production() && defined('WP_DEBUG') && WP_DEBUG) {
|
||
error_log('HVAC MapGeo: ' . $message);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Initialize hooks
|
||
*/
|
||
private function init_hooks() {
|
||
// Hook into MapGeo data filter - modify layout and inject trainer modal data
|
||
add_filter('igm_add_meta', [$this, 'modify_map_layout'], 10, 2);
|
||
|
||
// Hook into MapGeo marker data to add our trainer information
|
||
add_filter('igm_marker_data', [$this, 'inject_trainer_modal_data'], 10, 2);
|
||
|
||
// AJAX handlers for our own filtering
|
||
add_action('wp_ajax_hvac_filter_trainers', [$this, 'ajax_filter_trainers']);
|
||
add_action('wp_ajax_nopriv_hvac_filter_trainers', [$this, 'ajax_filter_trainers']);
|
||
|
||
// AJAX handler to get trainer certification type for Champions check
|
||
add_action('wp_ajax_hvac_get_trainer_certification', [$this, 'ajax_get_trainer_certification']);
|
||
add_action('wp_ajax_nopriv_hvac_get_trainer_certification', [$this, 'ajax_get_trainer_certification']);
|
||
|
||
// AJAX handler to get complete trainer profile data
|
||
add_action('wp_ajax_hvac_get_trainer_profile', [$this, 'ajax_get_trainer_profile']);
|
||
add_action('wp_ajax_nopriv_hvac_get_trainer_profile', [$this, 'ajax_get_trainer_profile']);
|
||
|
||
add_action('wp_ajax_hvac_search_trainers', [$this, 'ajax_search_trainers']);
|
||
add_action('wp_ajax_nopriv_hvac_search_trainers', [$this, 'ajax_search_trainers']);
|
||
|
||
// Add JavaScript to handle MapGeo marker clicks
|
||
add_action('wp_footer', [$this, 'add_mapgeo_click_handlers']);
|
||
}
|
||
|
||
/**
|
||
* Debug MapGeo integration
|
||
*/
|
||
public function debug_mapgeo_integration() {
|
||
if (isset($_GET['debug_mapgeo']) && current_user_can('manage_options')) {
|
||
$this->debug_log('Debug: Integration loaded');
|
||
$this->debug_log('Debug: Filters registered for igm_add_meta');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Modify map layout to prevent sidebar display and inject trainer profile IDs
|
||
*
|
||
* @param array $meta Map metadata
|
||
* @param int $map_id Map ID
|
||
* @return array Modified metadata
|
||
*/
|
||
public function modify_map_layout($meta, $map_id = null) {
|
||
// Only process if meta is an array
|
||
if (!is_array($meta)) {
|
||
return $meta;
|
||
}
|
||
|
||
// Only process for our specific map (5872)
|
||
if ($map_id && $map_id != $this->map_id) {
|
||
return $meta;
|
||
}
|
||
|
||
error_log('HVAC MapGeo: Processing map layout modification for map ' . $map_id);
|
||
error_log('HVAC MapGeo: Meta keys: ' . implode(', ', array_keys($meta)));
|
||
error_log('HVAC MapGeo: Full meta structure: ' . print_r($meta, true));
|
||
|
||
// Check for different marker types that MapGeo might use
|
||
$marker_types = ['roundMarkers', 'iconMarkers', 'markers', 'customMarkers'];
|
||
|
||
foreach ($marker_types as $marker_type) {
|
||
if (isset($meta[$marker_type]) && is_array($meta[$marker_type])) {
|
||
error_log('HVAC MapGeo: Found ' . count($meta[$marker_type]) . ' markers of type: ' . $marker_type);
|
||
|
||
foreach ($meta[$marker_type] as $index => &$marker) {
|
||
// Log marker structure for debugging
|
||
error_log('HVAC MapGeo: Marker ' . $index . ' keys: ' . implode(', ', array_keys($marker)));
|
||
|
||
// Check if this marker has trainer data we can identify
|
||
$trainer_name = null;
|
||
if (isset($marker['title']) && !empty($marker['title'])) {
|
||
$trainer_name = $marker['title'];
|
||
} elseif (isset($marker['name']) && !empty($marker['name'])) {
|
||
$trainer_name = $marker['name'];
|
||
}
|
||
|
||
if ($trainer_name) {
|
||
error_log('HVAC MapGeo: Looking for trainer profile for: ' . $trainer_name);
|
||
|
||
// Try to find matching trainer profile by name
|
||
$trainer_profile_id = $this->find_trainer_profile_by_name($trainer_name);
|
||
|
||
if ($trainer_profile_id) {
|
||
error_log('HVAC MapGeo: Found profile ID ' . $trainer_profile_id . ' for trainer: ' . $trainer_name);
|
||
|
||
// Set custom click action for trainer modal with profile ID
|
||
$marker['action'] = 'hvac_show_trainer_modal';
|
||
$marker['hvac_profile_id'] = $trainer_profile_id;
|
||
|
||
// Also add to marker ID for easy identification
|
||
$marker['id'] = 'trainer_' . $trainer_profile_id;
|
||
|
||
// Remove sidebar display if it was set
|
||
if (isset($marker['action']) && $marker['action'] === 'igm_display_right_1_3') {
|
||
$marker['action'] = 'hvac_show_trainer_modal';
|
||
}
|
||
|
||
error_log('HVAC MapGeo: Configured marker for trainer ' . $trainer_name . ' with profile ID ' . $trainer_profile_id);
|
||
} else {
|
||
error_log('HVAC MapGeo: No profile found for trainer: ' . $trainer_name);
|
||
|
||
// Fallback to tooltip for non-trainer markers
|
||
if (isset($marker['action']) && $marker['action'] === 'igm_display_right_1_3') {
|
||
$marker['action'] = 'tooltip';
|
||
}
|
||
}
|
||
} else {
|
||
error_log('HVAC MapGeo: Marker ' . $index . ' has no identifiable trainer name');
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
error_log('HVAC MapGeo: Map layout modification complete');
|
||
return $meta;
|
||
}
|
||
|
||
/**
|
||
* Find trainer profile ID by trainer name
|
||
*
|
||
* @param string $trainer_name
|
||
* @return int|false Profile ID or false if not found
|
||
*/
|
||
private function find_trainer_profile_by_name($trainer_name) {
|
||
$args = [
|
||
'post_type' => 'trainer_profile',
|
||
'posts_per_page' => 1,
|
||
'post_status' => 'publish',
|
||
'meta_query' => [
|
||
[
|
||
'key' => 'trainer_display_name',
|
||
'value' => $trainer_name,
|
||
'compare' => '='
|
||
]
|
||
]
|
||
];
|
||
|
||
$query = new WP_Query($args);
|
||
|
||
if ($query->have_posts()) {
|
||
return $query->posts[0]->ID;
|
||
}
|
||
|
||
wp_reset_postdata();
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* AJAX handler to get trainer certification type
|
||
*/
|
||
public function ajax_get_trainer_certification() {
|
||
// Verify nonce
|
||
if (!wp_verify_nonce($_POST['nonce'], 'hvac_find_trainer')) {
|
||
wp_send_json_error('Invalid nonce');
|
||
return;
|
||
}
|
||
|
||
$profile_id = intval($_POST['profile_id']);
|
||
if (!$profile_id) {
|
||
wp_send_json_error('Invalid profile ID');
|
||
return;
|
||
}
|
||
|
||
// Get the certification type and color for this profile
|
||
$certification_type = get_post_meta($profile_id, 'certification_type', true);
|
||
$certification_color = get_post_meta($profile_id, 'certification_color', true);
|
||
|
||
wp_send_json_success([
|
||
'certification_type' => $certification_type ?: 'HVAC Trainer',
|
||
'certification_color' => $certification_color ?: '#f0f7e8'
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* AJAX handler to get complete trainer profile data
|
||
*/
|
||
public function ajax_get_trainer_profile() {
|
||
error_log('HVAC MapGeo: ajax_get_trainer_profile called');
|
||
|
||
// Verify nonce
|
||
if (!wp_verify_nonce($_POST['nonce'], 'hvac_find_trainer')) {
|
||
error_log('HVAC MapGeo: Invalid nonce in ajax_get_trainer_profile');
|
||
wp_send_json_error('Invalid nonce');
|
||
return;
|
||
}
|
||
|
||
$profile_id = intval($_POST['profile_id']);
|
||
error_log('HVAC MapGeo: Processing profile ID: ' . $profile_id);
|
||
|
||
if (!$profile_id) {
|
||
error_log('HVAC MapGeo: Invalid profile ID: ' . $profile_id);
|
||
wp_send_json_error('Invalid profile ID');
|
||
return;
|
||
}
|
||
|
||
// Get the trainer profile post
|
||
$profile_post = get_post($profile_id);
|
||
if (!$profile_post || $profile_post->post_type !== 'trainer_profile') {
|
||
error_log('HVAC MapGeo: Invalid trainer profile - post type: ' . ($profile_post ? $profile_post->post_type : 'null'));
|
||
wp_send_json_error('Invalid trainer profile');
|
||
return;
|
||
}
|
||
|
||
error_log('HVAC MapGeo: Valid profile found: ' . $profile_post->post_title);
|
||
|
||
// Get user ID for event count
|
||
$user_id = get_post_meta($profile_id, 'user_id', true);
|
||
|
||
// Get event count
|
||
$event_count = 0;
|
||
if ($user_id && function_exists('tribe_get_events')) {
|
||
$events = tribe_get_events([
|
||
'author' => $user_id,
|
||
'eventDisplay' => 'all',
|
||
'posts_per_page' => -1,
|
||
'fields' => 'ids'
|
||
]);
|
||
$event_count = count($events);
|
||
}
|
||
|
||
// Build complete trainer data structure
|
||
try {
|
||
// Get business type safely
|
||
$business_types = wp_get_post_terms($profile_id, 'business_type', ['fields' => 'names']);
|
||
$business_type = 'Independent Contractor';
|
||
if (!is_wp_error($business_types) && !empty($business_types)) {
|
||
$business_type = implode(', ', $business_types);
|
||
} else {
|
||
$business_type_meta = get_post_meta($profile_id, 'business_type', true);
|
||
if ($business_type_meta) {
|
||
$business_type = $business_type_meta;
|
||
}
|
||
}
|
||
error_log('HVAC MapGeo: Business type: ' . $business_type);
|
||
|
||
// Get training formats safely
|
||
$formats = wp_get_post_terms($profile_id, 'training_format', ['fields' => 'names']);
|
||
$training_formats = 'In-Person, Virtual';
|
||
if (!is_wp_error($formats) && !empty($formats)) {
|
||
$training_formats = implode(', ', $formats);
|
||
}
|
||
error_log('HVAC MapGeo: Training formats: ' . $training_formats);
|
||
|
||
// Get training locations safely
|
||
$resources = wp_get_post_terms($profile_id, 'training_resources', ['fields' => 'names']);
|
||
$training_locations = 'On-site, Remote';
|
||
if (!is_wp_error($resources) && !empty($resources)) {
|
||
$training_locations = implode(', ', $resources);
|
||
}
|
||
error_log('HVAC MapGeo: Training locations: ' . $training_locations);
|
||
|
||
$trainer_data = [
|
||
'profile_id' => $profile_id,
|
||
'user_id' => $user_id,
|
||
'name' => get_post_meta($profile_id, 'trainer_display_name', true) ?: $profile_post->post_title,
|
||
'city' => get_post_meta($profile_id, 'trainer_city', true) ?: 'Location not available',
|
||
'state' => get_post_meta($profile_id, 'trainer_state', true) ?: '',
|
||
'certification_type' => get_post_meta($profile_id, 'certification_type', true) ?: 'HVAC Trainer',
|
||
'profile_image' => get_post_meta($profile_id, 'profile_image_url', true) ?: '',
|
||
'business_type' => $business_type,
|
||
'event_count' => $event_count,
|
||
'training_formats' => $training_formats,
|
||
'training_locations' => $training_locations,
|
||
'upcoming_events' => []
|
||
];
|
||
|
||
error_log('HVAC MapGeo: Successfully built trainer data for: ' . $trainer_data['name']);
|
||
wp_send_json_success($trainer_data);
|
||
|
||
} catch (Exception $e) {
|
||
error_log('HVAC MapGeo: Error building trainer data: ' . $e->getMessage());
|
||
wp_send_json_error('Error building trainer data: ' . $e->getMessage());
|
||
} catch (Error $e) {
|
||
error_log('HVAC MapGeo: Fatal error building trainer data: ' . $e->getMessage());
|
||
wp_send_json_error('Fatal error building trainer data: ' . $e->getMessage());
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* Add JavaScript to handle MapGeo custom click actions
|
||
*/
|
||
public function add_mapgeo_click_handlers() {
|
||
// Only add on find trainer page
|
||
if (!is_page() || get_post_field('post_name') !== 'find-a-trainer') {
|
||
return;
|
||
}
|
||
|
||
?>
|
||
<script type="text/javascript">
|
||
jQuery(document).ready(function($) {
|
||
// Disable console logging in production
|
||
var isProduction = window.location.hostname === 'upskillhvac.com';
|
||
var debugLog = isProduction ? function() {} : function() { console.log.apply(console, arguments); };
|
||
|
||
debugLog('🎯 MapGeo integration loading with optimized performance system...');
|
||
|
||
// Request deduplication cache - prevents duplicate AJAX calls
|
||
window.hvacTrainerDataCache = {};
|
||
window.hvacPendingRequests = {};
|
||
|
||
// Register the primary MapGeo custom action handler
|
||
// This function will be called by MapGeo when a marker is clicked
|
||
window.hvac_show_trainer_modal = function(markerData) {
|
||
debugLog('🎯 MapGeo custom action triggered with marker data:', markerData);
|
||
|
||
// Extract profile ID from marker data - MapGeo should pass this via our modify_map_layout filter
|
||
var profileId = null;
|
||
|
||
// Method 1: Check if profile_id is directly available
|
||
if (markerData && markerData.hvac_profile_id) {
|
||
profileId = markerData.hvac_profile_id;
|
||
debugLog('✅ Using hvac_profile_id:', profileId);
|
||
}
|
||
// Method 2: Extract from marker ID (format: trainer_123)
|
||
else if (markerData && markerData.id && markerData.id.toString().startsWith('trainer_')) {
|
||
profileId = markerData.id.replace('trainer_', '');
|
||
debugLog('✅ Extracted profile ID from marker ID:', profileId);
|
||
}
|
||
// Method 3: Try profile_id field directly
|
||
else if (markerData && markerData.profile_id) {
|
||
profileId = markerData.profile_id;
|
||
debugLog('✅ Using profile_id field:', profileId);
|
||
}
|
||
|
||
if (!profileId) {
|
||
debugLog('ℹ️ No profile ID found - likely clicked on non-trainer map element');
|
||
return;
|
||
}
|
||
|
||
// Check cache first for immediate response
|
||
if (window.hvacTrainerDataCache[profileId]) {
|
||
debugLog('⚡ Using cached data for profile:', profileId);
|
||
var cachedData = window.hvacTrainerDataCache[profileId];
|
||
|
||
// Don't show modal for Champions
|
||
if (cachedData.certification_type === 'Certified measureQuick Champion') {
|
||
debugLog('ℹ️ Cached trainer is a measureQuick Champion - no modal shown');
|
||
return;
|
||
}
|
||
|
||
if (typeof window.showTrainerModal === 'function') {
|
||
window.showTrainerModal(cachedData);
|
||
}
|
||
return;
|
||
}
|
||
|
||
// Check if we already have a pending request for this profile
|
||
if (window.hvacPendingRequests[profileId]) {
|
||
debugLog('⏳ Request already pending for profile:', profileId);
|
||
return;
|
||
}
|
||
|
||
// Find the corresponding trainer card to get display data
|
||
var $trainerCard = $('.hvac-trainer-card[data-profile-id="' + profileId + '"]');
|
||
|
||
if ($trainerCard.length > 0 && !$trainerCard.hasClass('hvac-champion-card')) {
|
||
debugLog('✅ Found matching trainer card for profile ID:', profileId);
|
||
|
||
// Extract trainer data from the card (most reliable method)
|
||
var trainerData = {
|
||
profile_id: profileId,
|
||
name: $trainerCard.find('.hvac-trainer-name a, .hvac-trainer-name .hvac-champion-name').text().trim(),
|
||
city: $trainerCard.find('.hvac-trainer-location').text().split(',')[0],
|
||
state: $trainerCard.find('.hvac-trainer-location').text().split(',')[1]?.trim(),
|
||
certification_type: $trainerCard.find('.hvac-trainer-certification').text(),
|
||
profile_image: $trainerCard.find('.hvac-trainer-image img:not(.hvac-mq-badge)').attr('src') || '',
|
||
business_type: 'Independent Contractor',
|
||
event_count: parseInt($trainerCard.data('event-count')) || 0,
|
||
training_formats: 'In-Person, Virtual',
|
||
training_locations: 'On-site, Remote',
|
||
upcoming_events: []
|
||
};
|
||
|
||
// Cache the data immediately
|
||
window.hvacTrainerDataCache[profileId] = trainerData;
|
||
|
||
debugLog('🎉 Successfully correlated trainer data:', trainerData);
|
||
|
||
// Show the modal using the main function if available
|
||
if (typeof window.showTrainerModal === 'function') {
|
||
window.showTrainerModal(trainerData);
|
||
} else {
|
||
debugLog('❌ showTrainerModal function not available');
|
||
}
|
||
|
||
} else if ($trainerCard.length > 0 && $trainerCard.hasClass('hvac-champion-card')) {
|
||
debugLog('ℹ️ Clicked marker is for a Champion - no modal shown');
|
||
// Cache that this is a Champion to avoid future requests
|
||
window.hvacTrainerDataCache[profileId] = { certification_type: 'Certified measureQuick Champion' };
|
||
} else {
|
||
// Fallback: This trainer exists on map but not in visible cards
|
||
// Make a single optimized AJAX call to get complete profile data
|
||
debugLog('🔄 Fetching complete profile data for hidden trainer:', profileId);
|
||
|
||
// Mark request as pending to prevent duplicates
|
||
window.hvacPendingRequests[profileId] = true;
|
||
|
||
$.ajax({
|
||
url: hvac_find_trainer.ajax_url,
|
||
method: 'POST',
|
||
data: {
|
||
action: 'hvac_get_trainer_profile',
|
||
nonce: hvac_find_trainer.nonce,
|
||
profile_id: profileId
|
||
},
|
||
timeout: 10000, // 10 second timeout
|
||
success: function(profileResponse) {
|
||
// Clear pending request flag
|
||
delete window.hvacPendingRequests[profileId];
|
||
|
||
if (profileResponse.success) {
|
||
debugLog('✅ Successfully fetched complete trainer profile data:', profileResponse.data);
|
||
|
||
// Cache the response data
|
||
window.hvacTrainerDataCache[profileId] = profileResponse.data;
|
||
|
||
// Don't show modal for Champions
|
||
if (profileResponse.data.certification_type === 'Certified measureQuick Champion') {
|
||
debugLog('ℹ️ Fetched trainer is a measureQuick Champion - no modal shown');
|
||
return;
|
||
}
|
||
|
||
// Show modal with real profile data
|
||
if (typeof window.showTrainerModal === 'function') {
|
||
window.showTrainerModal(profileResponse.data);
|
||
} else {
|
||
console.error('❌ showTrainerModal function not available');
|
||
}
|
||
} else {
|
||
debugLog('❌ Failed to fetch trainer profile. Error:', profileResponse.data);
|
||
|
||
// Cache basic fallback data to prevent retries
|
||
var fallbackData = {
|
||
profile_id: profileId,
|
||
name: 'Trainer Profile',
|
||
city: 'Location not available',
|
||
state: '',
|
||
certification_type: 'HVAC Trainer',
|
||
profile_image: '',
|
||
business_type: 'Independent Contractor',
|
||
event_count: 0,
|
||
training_formats: 'Contact for details',
|
||
training_locations: 'Contact for details',
|
||
upcoming_events: []
|
||
};
|
||
|
||
window.hvacTrainerDataCache[profileId] = fallbackData;
|
||
|
||
if (typeof window.showTrainerModal === 'function') {
|
||
window.showTrainerModal(fallbackData);
|
||
}
|
||
}
|
||
},
|
||
error: function(jqXHR, textStatus, errorThrown) {
|
||
// Clear pending request flag
|
||
delete window.hvacPendingRequests[profileId];
|
||
|
||
debugLog('❌ AJAX error fetching trainer profile:', {
|
||
status: jqXHR.status,
|
||
textStatus: textStatus,
|
||
errorThrown: errorThrown
|
||
});
|
||
|
||
// Cache error state to prevent retries
|
||
var errorFallbackData = {
|
||
profile_id: profileId,
|
||
name: 'Trainer Profile (Unavailable)',
|
||
city: 'Location not available',
|
||
state: '',
|
||
certification_type: 'HVAC Trainer',
|
||
profile_image: '',
|
||
business_type: 'Contact for details',
|
||
event_count: 0,
|
||
training_formats: 'Contact for details',
|
||
training_locations: 'Contact for details',
|
||
upcoming_events: []
|
||
};
|
||
|
||
window.hvacTrainerDataCache[profileId] = errorFallbackData;
|
||
|
||
if (typeof window.showTrainerModal === 'function') {
|
||
window.showTrainerModal(errorFallbackData);
|
||
}
|
||
}
|
||
});
|
||
}
|
||
};
|
||
|
||
// Enhanced marker click interception to capture MapGeo's logged data (Optimized)
|
||
debugLog('Setting up optimized MapGeo marker click handlers...');
|
||
|
||
// Store trainer data from MapGeo logging (simplified)
|
||
window.lastMapGeoTrainerData = null;
|
||
|
||
// REMOVED: Console override was causing Safari conflicts
|
||
// MapGeo trainer data is now handled through custom action hooks
|
||
// This eliminates browser-level console method conflicts
|
||
|
||
// Optimized marker click handler - reduced complexity
|
||
$(document).on('click', '.imapsSprite-group, .imapsMapObject-group, [class*="imaps"], g[class*="imaps"], circle, path', function(e) {
|
||
// Throttle clicks to prevent rapid firing
|
||
if (window.hvacLastClickTime && (Date.now() - window.hvacLastClickTime) < 500) {
|
||
debugLog('🚫 Click throttled to prevent rapid firing');
|
||
return;
|
||
}
|
||
window.hvacLastClickTime = Date.now();
|
||
|
||
debugLog('🎯 Optimized marker clicked');
|
||
|
||
// Try to correlate with most recent MapGeo data (single attempt)
|
||
if (window.lastMapGeoTrainerData && window.lastMapGeoTrainerData.id && window.lastMapGeoTrainerData.id.startsWith('trainer_')) {
|
||
var profileId = window.lastMapGeoTrainerData.id.replace('trainer_', '');
|
||
|
||
var trainerData = {
|
||
id: window.lastMapGeoTrainerData.id,
|
||
title: window.lastMapGeoTrainerData.title,
|
||
hvac_profile_id: profileId,
|
||
originalMapGeoData: window.lastMapGeoTrainerData
|
||
};
|
||
|
||
debugLog('⚡ Quick correlation successful:', trainerData);
|
||
|
||
// Call our optimized custom action
|
||
if (window.hvac_show_trainer_modal) {
|
||
window.hvac_show_trainer_modal(trainerData);
|
||
}
|
||
} else {
|
||
debugLog('ℹ️ No recent trainer data available for correlation');
|
||
}
|
||
});
|
||
|
||
// Clear cache when filters are applied (to ensure fresh data)
|
||
$(document).on('hvac:filters_applied', function() {
|
||
debugLog('🗑️ Clearing trainer data cache due to filter change');
|
||
window.hvacTrainerDataCache = {};
|
||
window.hvacPendingRequests = {};
|
||
});
|
||
|
||
// Simple debug function for testing
|
||
window.testHvacModal = function() {
|
||
debugLog('Testing MapGeo custom action with sample data...');
|
||
var firstProfileId = $('.hvac-trainer-card').first().data('profile-id');
|
||
if (firstProfileId) {
|
||
window.hvac_show_trainer_modal({ hvac_profile_id: firstProfileId });
|
||
} else {
|
||
debugLog('No trainer cards found for testing');
|
||
}
|
||
};
|
||
|
||
debugLog('✅ MapGeo integration setup complete.');
|
||
debugLog('Available trainer profile IDs:', $('.hvac-trainer-card').map(function() {
|
||
return $(this).data('profile-id');
|
||
}).get());
|
||
debugLog('Use testHvacModal() to test the custom action system.');
|
||
});
|
||
</script>
|
||
<?php
|
||
}
|
||
|
||
/**
|
||
* Format trainer data for MapGeo marker
|
||
*
|
||
* @param int $profile_id Trainer profile post ID
|
||
* @return array|false Marker data or false if invalid
|
||
*/
|
||
private function format_trainer_for_mapgeo($profile_id) {
|
||
$lat = get_post_meta($profile_id, 'latitude', true);
|
||
$lng = get_post_meta($profile_id, 'longitude', true);
|
||
|
||
if (!$lat || !$lng) {
|
||
return false;
|
||
}
|
||
|
||
$trainer_name = get_post_meta($profile_id, 'trainer_display_name', true);
|
||
$city = get_post_meta($profile_id, 'trainer_city', true);
|
||
$state = get_post_meta($profile_id, 'trainer_state', true);
|
||
$certification = get_post_meta($profile_id, 'certification_type', true);
|
||
|
||
// Build tooltip content
|
||
$tooltip = sprintf(
|
||
'<div class="hvac-trainer-tooltip">
|
||
<strong>%s</strong><br>
|
||
%s, %s<br>
|
||
%s<br>
|
||
<a href="#" class="hvac-view-profile" data-profile-id="%d">View Profile</a>
|
||
</div>',
|
||
esc_html($trainer_name),
|
||
esc_html($city),
|
||
esc_html($state),
|
||
esc_html($certification),
|
||
$profile_id
|
||
);
|
||
|
||
return [
|
||
'id' => 'trainer_' . $profile_id,
|
||
'coordinates' => [
|
||
'lat' => floatval($lat),
|
||
'lng' => floatval($lng)
|
||
],
|
||
'tooltipContent' => $tooltip,
|
||
'action' => 'tooltip', // Changed from 'none' to 'tooltip' to show tooltip on click
|
||
'value' => '1',
|
||
'radius' => '10',
|
||
'fill' => $certification === 'Certified measureQuick Champion' ? '#FFD700' : '#0073aa',
|
||
'fillOpacity' => '0.8',
|
||
'borderColor' => '#005a87',
|
||
'borderWidth' => '2'
|
||
];
|
||
}
|
||
|
||
|
||
/**
|
||
* Get all geocoded trainers
|
||
*
|
||
* @return array
|
||
*/
|
||
private function get_geocoded_trainers() {
|
||
$args = [
|
||
'post_type' => 'trainer_profile',
|
||
'posts_per_page' => -1,
|
||
'post_status' => 'publish',
|
||
'meta_query' => [
|
||
'relation' => 'AND',
|
||
[
|
||
'key' => 'is_public_profile',
|
||
'value' => '1',
|
||
'compare' => '='
|
||
],
|
||
[
|
||
'key' => 'latitude',
|
||
'compare' => 'EXISTS'
|
||
],
|
||
[
|
||
'key' => 'longitude',
|
||
'compare' => 'EXISTS'
|
||
]
|
||
]
|
||
];
|
||
|
||
// Add user status filter
|
||
$this->add_user_status_filter($args);
|
||
|
||
$query = new WP_Query($args);
|
||
$trainers = [];
|
||
|
||
if ($query->have_posts()) {
|
||
while ($query->have_posts()) {
|
||
$query->the_post();
|
||
$trainers[] = get_the_ID();
|
||
}
|
||
}
|
||
|
||
wp_reset_postdata();
|
||
|
||
return $trainers;
|
||
}
|
||
|
||
/**
|
||
* Format trainer data as map marker
|
||
*
|
||
* @param int $profile_id Trainer profile post ID
|
||
* @return array Marker data
|
||
*/
|
||
private function format_trainer_marker($profile_id) {
|
||
$user_id = get_post_meta($profile_id, 'user_id', true);
|
||
$trainer_name = get_post_meta($profile_id, 'trainer_display_name', true);
|
||
$city = get_post_meta($profile_id, 'trainer_city', true);
|
||
$state = get_post_meta($profile_id, 'trainer_state', true);
|
||
$lat = get_post_meta($profile_id, 'latitude', true);
|
||
$lng = get_post_meta($profile_id, 'longitude', true);
|
||
$certification = get_post_meta($profile_id, 'certification_type', true);
|
||
$profile_image = get_post_meta($profile_id, 'profile_image_url', true);
|
||
|
||
// Get taxonomies
|
||
$business_types = wp_get_post_terms($profile_id, 'business_type', ['fields' => 'names']);
|
||
$training_formats = wp_get_post_terms($profile_id, 'training_formats', ['fields' => 'names']);
|
||
$training_resources = wp_get_post_terms($profile_id, 'training_resources', ['fields' => 'names']);
|
||
|
||
// Count upcoming events
|
||
$event_count = $this->get_trainer_event_count($user_id);
|
||
|
||
return [
|
||
'id' => 'trainer_' . $profile_id,
|
||
'title' => $trainer_name,
|
||
'lat' => floatval($lat),
|
||
'lng' => floatval($lng),
|
||
'icon' => $this->get_marker_icon($certification),
|
||
'content' => $this->generate_marker_popup($profile_id),
|
||
'data' => [
|
||
'trainer_id' => $user_id,
|
||
'profile_id' => $profile_id,
|
||
'name' => $trainer_name,
|
||
'city' => $city,
|
||
'state' => $state,
|
||
'certification' => $certification,
|
||
'business_type' => $business_types,
|
||
'training_formats' => $training_formats,
|
||
'training_resources' => $training_resources,
|
||
'event_count' => $event_count,
|
||
'profile_image' => $profile_image
|
||
]
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Generate marker popup content
|
||
*
|
||
* @param int $profile_id Trainer profile post ID
|
||
* @return string HTML content
|
||
*/
|
||
private function generate_marker_popup($profile_id) {
|
||
$trainer_name = get_post_meta($profile_id, 'trainer_display_name', true);
|
||
$city = get_post_meta($profile_id, 'trainer_city', true);
|
||
$state = get_post_meta($profile_id, 'trainer_state', true);
|
||
$certification = get_post_meta($profile_id, 'certification_type', true);
|
||
$profile_image = get_post_meta($profile_id, 'profile_image_url', true);
|
||
|
||
ob_start();
|
||
?>
|
||
<div class="hvac-marker-popup" data-profile-id="<?php echo esc_attr($profile_id); ?>">
|
||
<div class="hvac-marker-header">
|
||
<?php if ($profile_image) : ?>
|
||
<img src="<?php echo esc_url($profile_image); ?>" alt="<?php echo esc_attr($trainer_name); ?>" class="hvac-marker-avatar">
|
||
<?php endif; ?>
|
||
<div class="hvac-marker-info">
|
||
<h4><?php echo esc_html($trainer_name); ?></h4>
|
||
<p class="hvac-marker-location"><?php echo esc_html($city); ?>, <?php echo esc_html($state); ?></p>
|
||
<?php if ($certification) : ?>
|
||
<span class="hvac-marker-certification"><?php echo esc_html($certification); ?></span>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
<div class="hvac-marker-actions">
|
||
<button class="hvac-view-profile button button-primary" data-profile-id="<?php echo esc_attr($profile_id); ?>">
|
||
View Profile
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<?php
|
||
return ob_get_clean();
|
||
}
|
||
|
||
/**
|
||
* Get marker icon based on certification type
|
||
*
|
||
* @param string $certification Certification type
|
||
* @return string Icon URL or identifier
|
||
*/
|
||
private function get_marker_icon($certification) {
|
||
// Return different icons based on certification
|
||
if ($certification === 'Certified measureQuick Champion') {
|
||
return 'champion_marker';
|
||
} elseif ($certification === 'Certified measureQuick Trainer') {
|
||
return 'trainer_marker';
|
||
}
|
||
|
||
return 'default_marker';
|
||
}
|
||
|
||
/**
|
||
* Get CSS classes for marker
|
||
*
|
||
* @param array $data Marker data
|
||
* @return string CSS classes
|
||
*/
|
||
private function get_marker_classes($data) {
|
||
$classes = ['hvac-trainer-marker'];
|
||
|
||
if (!empty($data['state'])) {
|
||
$classes[] = 'state-' . sanitize_html_class($data['state']);
|
||
}
|
||
|
||
if (!empty($data['certification'])) {
|
||
$classes[] = 'cert-' . sanitize_html_class(str_replace(' ', '-', strtolower($data['certification'])));
|
||
}
|
||
|
||
if (!empty($data['business_type'])) {
|
||
foreach ($data['business_type'] as $type) {
|
||
$classes[] = 'btype-' . sanitize_html_class(strtolower($type));
|
||
}
|
||
}
|
||
|
||
return implode(' ', $classes);
|
||
}
|
||
|
||
/**
|
||
* Get trainer event count
|
||
*
|
||
* @param int $user_id Trainer user ID
|
||
* @return int Event count
|
||
*/
|
||
private function get_trainer_event_count($user_id) {
|
||
if (!function_exists('tribe_get_events')) {
|
||
return 0;
|
||
}
|
||
|
||
$events = tribe_get_events([
|
||
'author' => $user_id,
|
||
'eventDisplay' => 'all',
|
||
'posts_per_page' => -1,
|
||
'fields' => 'ids'
|
||
]);
|
||
|
||
return count($events);
|
||
}
|
||
|
||
/**
|
||
* Update marker when trainer profile is updated
|
||
*
|
||
* @param int $profile_id Trainer profile post ID
|
||
*/
|
||
public function update_trainer_marker($profile_id) {
|
||
// Clear any cached map data
|
||
delete_transient('hvac_mapgeo_markers_' . $this->map_id);
|
||
|
||
// Trigger geocoding if needed
|
||
$lat = get_post_meta($profile_id, 'latitude', true);
|
||
$lng = get_post_meta($profile_id, 'longitude', true);
|
||
|
||
if (empty($lat) || empty($lng)) {
|
||
$this->geocode_trainer($profile_id);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Geocode a trainer's address
|
||
*
|
||
* @param int $profile_id Trainer profile post ID
|
||
* @return bool Success status
|
||
*/
|
||
private function geocode_trainer($profile_id) {
|
||
$address_parts = [];
|
||
|
||
$street = get_post_meta($profile_id, 'trainer_street', true);
|
||
$city = get_post_meta($profile_id, 'trainer_city', true);
|
||
$state = get_post_meta($profile_id, 'trainer_state', true);
|
||
$zip = get_post_meta($profile_id, 'trainer_zip', true);
|
||
$country = get_post_meta($profile_id, 'trainer_country', true) ?: 'USA';
|
||
|
||
if ($street) $address_parts[] = $street;
|
||
if ($city) $address_parts[] = $city;
|
||
if ($state) $address_parts[] = $state;
|
||
if ($zip) $address_parts[] = $zip;
|
||
if ($country) $address_parts[] = $country;
|
||
|
||
if (empty($address_parts)) {
|
||
return false;
|
||
}
|
||
|
||
$address = implode(', ', $address_parts);
|
||
|
||
// Use Google Maps Geocoding API if available
|
||
$api_key = get_option('hvac_google_maps_api_key');
|
||
if ($api_key) {
|
||
$response = wp_remote_get('https://maps.googleapis.com/maps/api/geocode/json?' . http_build_query([
|
||
'address' => $address,
|
||
'key' => $api_key
|
||
]));
|
||
|
||
if (!is_wp_error($response)) {
|
||
$data = json_decode(wp_remote_retrieve_body($response), true);
|
||
|
||
if ($data['status'] === 'OK' && !empty($data['results'][0])) {
|
||
$location = $data['results'][0]['geometry']['location'];
|
||
update_post_meta($profile_id, 'latitude', $location['lat']);
|
||
update_post_meta($profile_id, 'longitude', $location['lng']);
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* Batch geocode all trainers without coordinates
|
||
*/
|
||
public function batch_geocode_trainers() {
|
||
$args = [
|
||
'post_type' => 'trainer_profile',
|
||
'posts_per_page' => -1,
|
||
'post_status' => 'publish',
|
||
'meta_query' => [
|
||
'relation' => 'OR',
|
||
[
|
||
'key' => 'latitude',
|
||
'compare' => 'NOT EXISTS'
|
||
],
|
||
[
|
||
'key' => 'longitude',
|
||
'compare' => 'NOT EXISTS'
|
||
]
|
||
]
|
||
];
|
||
|
||
$query = new WP_Query($args);
|
||
$geocoded = 0;
|
||
|
||
if ($query->have_posts()) {
|
||
while ($query->have_posts()) {
|
||
$query->the_post();
|
||
if ($this->geocode_trainer(get_the_ID())) {
|
||
$geocoded++;
|
||
// Rate limiting - wait 100ms between requests
|
||
usleep(100000);
|
||
}
|
||
}
|
||
}
|
||
|
||
wp_reset_postdata();
|
||
|
||
return $geocoded;
|
||
}
|
||
|
||
/**
|
||
* AJAX handler for filtering trainers
|
||
*/
|
||
public function ajax_filter_trainers() {
|
||
check_ajax_referer('hvac_find_trainer', 'nonce');
|
||
|
||
$filters = $_POST['filters'] ?? [];
|
||
$page = isset($_POST['page']) ? intval($_POST['page']) : 1;
|
||
$per_page = 12;
|
||
|
||
// Build query args
|
||
$args = $this->build_trainer_query_args($filters, $page, $per_page);
|
||
|
||
$query = new WP_Query($args);
|
||
$trainers = [];
|
||
|
||
if ($query->have_posts()) {
|
||
while ($query->have_posts()) {
|
||
$query->the_post();
|
||
$trainers[] = $this->format_trainer_card_data(get_the_ID());
|
||
}
|
||
}
|
||
|
||
wp_reset_postdata();
|
||
|
||
// Sort trainers: Certified measureQuick Trainers first, Champions last
|
||
usort($trainers, function($a, $b) {
|
||
$a_cert = $a['certification'];
|
||
$b_cert = $b['certification'];
|
||
|
||
// Define sort order: Trainers = 1, Champions = 2, Others = 3
|
||
$a_priority = 3; // Default for others
|
||
$b_priority = 3; // Default for others
|
||
|
||
if ($a_cert === 'Certified measureQuick Trainer') {
|
||
$a_priority = 1;
|
||
} elseif ($a_cert === 'Certified measureQuick Champion') {
|
||
$a_priority = 2;
|
||
}
|
||
|
||
if ($b_cert === 'Certified measureQuick Trainer') {
|
||
$b_priority = 1;
|
||
} elseif ($b_cert === 'Certified measureQuick Champion') {
|
||
$b_priority = 2;
|
||
}
|
||
|
||
// Primary sort by certification priority
|
||
if ($a_priority !== $b_priority) {
|
||
return $a_priority - $b_priority;
|
||
}
|
||
|
||
// Secondary sort by name (alphabetical)
|
||
return strcasecmp($a['name'], $b['name']);
|
||
});
|
||
|
||
// Generate HTML for trainer cards
|
||
ob_start();
|
||
if (!empty($trainers)) {
|
||
foreach ($trainers as $trainer) {
|
||
$this->render_trainer_card($trainer);
|
||
}
|
||
} else {
|
||
echo '<div class="hvac-no-trainers"><p>No trainers found matching your criteria. Try adjusting your filters.</p></div>';
|
||
}
|
||
$html = ob_get_clean();
|
||
|
||
// Generate pagination HTML
|
||
$pagination_html = $this->generate_pagination($query->max_num_pages, $page);
|
||
|
||
wp_send_json_success([
|
||
'html' => $html,
|
||
'pagination' => $pagination_html,
|
||
'count' => $query->found_posts,
|
||
'page' => $page,
|
||
'max_pages' => $query->max_num_pages
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* AJAX handler for searching trainers
|
||
*/
|
||
public function ajax_search_trainers() {
|
||
check_ajax_referer('hvac_find_trainer', 'nonce');
|
||
|
||
$search = isset($_POST['search']) ? sanitize_text_field($_POST['search']) : '';
|
||
$page = isset($_POST['page']) ? intval($_POST['page']) : 1;
|
||
$per_page = 12;
|
||
|
||
$filters = ['search' => $search];
|
||
$args = $this->build_trainer_query_args($filters, $page, $per_page);
|
||
|
||
$query = new WP_Query($args);
|
||
$trainers = [];
|
||
|
||
if ($query->have_posts()) {
|
||
while ($query->have_posts()) {
|
||
$query->the_post();
|
||
$trainers[] = $this->format_trainer_card_data(get_the_ID());
|
||
}
|
||
}
|
||
|
||
wp_reset_postdata();
|
||
|
||
// Sort trainers: Certified measureQuick Trainers first, Champions last
|
||
usort($trainers, function($a, $b) {
|
||
$a_cert = $a['certification'];
|
||
$b_cert = $b['certification'];
|
||
|
||
// Define sort order: Trainers = 1, Champions = 2, Others = 3
|
||
$a_priority = 3; // Default for others
|
||
$b_priority = 3; // Default for others
|
||
|
||
if ($a_cert === 'Certified measureQuick Trainer') {
|
||
$a_priority = 1;
|
||
} elseif ($a_cert === 'Certified measureQuick Champion') {
|
||
$a_priority = 2;
|
||
}
|
||
|
||
if ($b_cert === 'Certified measureQuick Trainer') {
|
||
$b_priority = 1;
|
||
} elseif ($b_cert === 'Certified measureQuick Champion') {
|
||
$b_priority = 2;
|
||
}
|
||
|
||
// Primary sort by certification priority
|
||
if ($a_priority !== $b_priority) {
|
||
return $a_priority - $b_priority;
|
||
}
|
||
|
||
// Secondary sort by name (alphabetical)
|
||
return strcasecmp($a['name'], $b['name']);
|
||
});
|
||
|
||
// Generate HTML
|
||
ob_start();
|
||
foreach ($trainers as $trainer) {
|
||
$this->render_trainer_card($trainer);
|
||
}
|
||
$html = ob_get_clean();
|
||
|
||
$pagination_html = $this->generate_pagination($query->max_num_pages, $page);
|
||
|
||
wp_send_json_success([
|
||
'html' => $html,
|
||
'pagination' => $pagination_html,
|
||
'count' => $query->found_posts
|
||
]);
|
||
}
|
||
|
||
/**
|
||
* Build query args for trainer search/filter
|
||
*/
|
||
private function build_trainer_query_args($filters, $page = 1, $per_page = 12) {
|
||
$args = [
|
||
'post_type' => 'trainer_profile',
|
||
'posts_per_page' => $per_page,
|
||
'paged' => $page,
|
||
'post_status' => 'publish',
|
||
'meta_query' => [
|
||
'relation' => 'AND',
|
||
[
|
||
'key' => 'is_public_profile',
|
||
'value' => '1',
|
||
'compare' => '='
|
||
]
|
||
]
|
||
];
|
||
|
||
// Add user status filter - only show profiles for approved/active/inactive users
|
||
$this->add_user_status_filter($args);
|
||
|
||
// Apply search
|
||
if (!empty($filters['search'])) {
|
||
$args['meta_query'][] = [
|
||
'key' => 'trainer_display_name',
|
||
'value' => $filters['search'],
|
||
'compare' => 'LIKE'
|
||
];
|
||
}
|
||
|
||
// Apply state filter - handle both single value and array
|
||
if (!empty($filters['state'])) {
|
||
if (is_array($filters['state'])) {
|
||
// Multiple states selected
|
||
$state_values = array_map('sanitize_text_field', $filters['state']);
|
||
$args['meta_query'][] = [
|
||
'key' => 'trainer_state',
|
||
'value' => $state_values,
|
||
'compare' => 'IN'
|
||
];
|
||
} else {
|
||
// Single state
|
||
$args['meta_query'][] = [
|
||
'key' => 'trainer_state',
|
||
'value' => sanitize_text_field($filters['state']),
|
||
'compare' => '='
|
||
];
|
||
}
|
||
}
|
||
|
||
// Apply taxonomy filters - handle arrays from multi-select
|
||
$tax_query = [];
|
||
|
||
if (!empty($filters['business_type'])) {
|
||
$terms = is_array($filters['business_type']) ? array_map('sanitize_text_field', $filters['business_type']) : [sanitize_text_field($filters['business_type'])];
|
||
$tax_query[] = [
|
||
'taxonomy' => 'business_type',
|
||
'field' => 'name', // Changed from 'slug' to 'name' to match the values being sent
|
||
'terms' => $terms
|
||
];
|
||
}
|
||
|
||
if (!empty($filters['training_format'])) {
|
||
$terms = is_array($filters['training_format']) ? array_map('sanitize_text_field', $filters['training_format']) : [sanitize_text_field($filters['training_format'])];
|
||
$tax_query[] = [
|
||
'taxonomy' => 'training_formats',
|
||
'field' => 'name', // Changed from 'slug' to 'name'
|
||
'terms' => $terms
|
||
];
|
||
}
|
||
|
||
if (!empty($filters['training_resources'])) {
|
||
$terms = is_array($filters['training_resources']) ? array_map('sanitize_text_field', $filters['training_resources']) : [sanitize_text_field($filters['training_resources'])];
|
||
$tax_query[] = [
|
||
'taxonomy' => 'training_resources',
|
||
'field' => 'name', // Changed from 'slug' to 'name'
|
||
'terms' => $terms
|
||
];
|
||
}
|
||
|
||
if (!empty($tax_query)) {
|
||
$args['tax_query'] = $tax_query;
|
||
}
|
||
|
||
return $args;
|
||
}
|
||
|
||
/**
|
||
* Add user status filter to query args
|
||
* Only show profiles for users with status: approved, active, or inactive
|
||
* Exclude: pending, disabled
|
||
*/
|
||
private function add_user_status_filter(&$args) {
|
||
// Get all trainer profile user IDs first, then filter by status
|
||
$user_query = new WP_User_Query([
|
||
'meta_query' => [
|
||
[
|
||
'key' => 'account_status',
|
||
'value' => ['approved', 'active', 'inactive'],
|
||
'compare' => 'IN'
|
||
]
|
||
],
|
||
'fields' => 'ID'
|
||
]);
|
||
|
||
$approved_user_ids = $user_query->get_results();
|
||
|
||
if (!empty($approved_user_ids)) {
|
||
// Filter trainer profiles to only those belonging to approved users
|
||
$args['meta_query'][] = [
|
||
'key' => 'user_id',
|
||
'value' => $approved_user_ids,
|
||
'compare' => 'IN'
|
||
];
|
||
} else {
|
||
// If no approved users found, still show all profiles (fallback for development)
|
||
// In production, you might want to return no results instead
|
||
error_log('HVAC Debug: No users found with approved account status');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Format trainer data for card display
|
||
*/
|
||
private function format_trainer_card_data($profile_id) {
|
||
$user_id = get_post_meta($profile_id, 'user_id', true);
|
||
$trainer_name = get_post_meta($profile_id, 'trainer_display_name', true);
|
||
$city = get_post_meta($profile_id, 'trainer_city', true);
|
||
$state = get_post_meta($profile_id, 'trainer_state', true);
|
||
$certification = get_post_meta($profile_id, 'certification_type', true);
|
||
$profile_image = get_post_meta($profile_id, 'profile_image_url', true);
|
||
|
||
// Get real event count for this trainer
|
||
$event_count = $this->get_trainer_event_count($user_id);
|
||
|
||
return [
|
||
'profile_id' => $profile_id,
|
||
'user_id' => $user_id,
|
||
'name' => $trainer_name,
|
||
'city' => $city,
|
||
'state' => $state,
|
||
'certification' => $certification,
|
||
'profile_image' => $profile_image,
|
||
'event_count' => $event_count,
|
||
'profile_url' => '#' // Will open modal
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Render trainer card HTML
|
||
*/
|
||
private function render_trainer_card($trainer) {
|
||
?>
|
||
<div class="hvac-trainer-card<?php
|
||
if ($trainer['certification'] === 'Certified measureQuick Champion') {
|
||
echo ' hvac-champion-card';
|
||
} elseif ($trainer['certification'] === 'Certified measureQuick Trainer') {
|
||
echo ' hvac-trainer-card-certified';
|
||
}
|
||
?>" data-profile-id="<?php echo esc_attr($trainer['profile_id']); ?>" data-event-count="<?php echo esc_attr($trainer['event_count']); ?>">
|
||
<div class="hvac-trainer-card-content">
|
||
<div class="hvac-trainer-image">
|
||
<?php if ($trainer['profile_image']) : ?>
|
||
<img src="<?php echo esc_url($trainer['profile_image']); ?>" alt="<?php echo esc_attr($trainer['name']); ?>">
|
||
<?php else : ?>
|
||
<div class="hvac-trainer-avatar">
|
||
<span class="dashicons dashicons-businessperson"></span>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<!-- mQ Certified Trainer Badge Overlay -->
|
||
<?php if ($trainer['certification'] === 'Certified measureQuick Trainer') : ?>
|
||
<div class="hvac-mq-badge-overlay">
|
||
<img src="/wp-content/uploads/2025/08/mQ-Certified-trainer.png" alt="measureQuick Certified Trainer" class="hvac-mq-badge">
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
<div class="hvac-trainer-details">
|
||
<h3 class="hvac-trainer-name">
|
||
<?php if ($trainer['certification'] === 'Certified measureQuick Champion') : ?>
|
||
<!-- Champions are not clickable -->
|
||
<span class="hvac-champion-name"><?php echo esc_html($trainer['name']); ?></span>
|
||
<?php else : ?>
|
||
<!-- Trainers are clickable -->
|
||
<a href="#" class="hvac-open-profile" data-profile-id="<?php echo esc_attr($trainer['profile_id']); ?>">
|
||
<?php echo esc_html($trainer['name']); ?>
|
||
</a>
|
||
<?php endif; ?>
|
||
</h3>
|
||
<p class="hvac-trainer-location">
|
||
<?php echo esc_html($trainer['city']); ?>, <?php echo esc_html($trainer['state']); ?>
|
||
</p>
|
||
<p class="hvac-trainer-certification">
|
||
<?php echo esc_html($trainer['certification'] ?: 'HVAC Trainer'); ?>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<?php
|
||
}
|
||
|
||
/**
|
||
* Generate pagination HTML
|
||
*/
|
||
private function generate_pagination($max_pages, $current_page) {
|
||
if ($max_pages <= 1) {
|
||
return '';
|
||
}
|
||
|
||
$html = '<div class="hvac-pagination">';
|
||
|
||
// Previous
|
||
if ($current_page > 1) {
|
||
$html .= sprintf(
|
||
'<a href="#" data-page="%d" class="hvac-page-link">%s</a>',
|
||
$current_page - 1,
|
||
'«'
|
||
);
|
||
}
|
||
|
||
// Page numbers
|
||
for ($i = 1; $i <= $max_pages; $i++) {
|
||
if ($i == $current_page) {
|
||
$html .= sprintf('<span class="current">%d</span>', $i);
|
||
} else {
|
||
$html .= sprintf(
|
||
'<a href="#" data-page="%d" class="hvac-page-link">%d</a>',
|
||
$i,
|
||
$i
|
||
);
|
||
}
|
||
}
|
||
|
||
// Next
|
||
if ($current_page < $max_pages) {
|
||
$html .= sprintf(
|
||
'<a href="#" data-page="%d" class="hvac-page-link">%s</a>',
|
||
$current_page + 1,
|
||
'»'
|
||
);
|
||
}
|
||
|
||
$html .= '</div>';
|
||
|
||
return $html;
|
||
}
|
||
|
||
/**
|
||
* Check for MapGeo plugin conflicts
|
||
*/
|
||
public function check_conflicts() {
|
||
$known_conflicts = [
|
||
'nextgen-gallery/nggallery.php',
|
||
'wp-ulike/wp-ulike.php',
|
||
'testimonials-widget/testimonials-widget.php',
|
||
'wp-leaflet-maps-pro/wp-leaflet-maps-pro.php'
|
||
];
|
||
|
||
$active_conflicts = [];
|
||
|
||
foreach ($known_conflicts as $plugin) {
|
||
if (is_plugin_active($plugin)) {
|
||
$active_conflicts[] = $plugin;
|
||
}
|
||
}
|
||
|
||
if (!empty($active_conflicts)) {
|
||
add_action('admin_notices', function() use ($active_conflicts) {
|
||
?>
|
||
<div class="notice notice-warning">
|
||
<p>
|
||
<strong>HVAC Plugin Warning:</strong>
|
||
The following plugins may conflict with MapGeo integration:
|
||
<?php echo esc_html(implode(', ', $active_conflicts)); ?>
|
||
</p>
|
||
</div>
|
||
<?php
|
||
});
|
||
}
|
||
}
|
||
}
|