upskill-event-manager/assets/js/find-trainer.js
ben 8a8f1d78df
Some checks are pending
HVAC Plugin CI/CD Pipeline / Deploy to Production (push) Blocked by required conditions
HVAC Plugin CI/CD Pipeline / Notification (push) Blocked by required conditions
HVAC Plugin CI/CD Pipeline / Security Analysis (push) Waiting to run
HVAC Plugin CI/CD Pipeline / Code Quality & Standards (push) Waiting to run
HVAC Plugin CI/CD Pipeline / Unit Tests (push) Waiting to run
HVAC Plugin CI/CD Pipeline / Integration Tests (push) Waiting to run
HVAC Plugin CI/CD Pipeline / Deploy to Staging (push) Blocked by required conditions
Security Monitoring & Compliance / Dependency Vulnerability Scan (push) Waiting to run
Security Monitoring & Compliance / Secrets & Credential Scan (push) Waiting to run
Security Monitoring & Compliance / WordPress Security Analysis (push) Waiting to run
Security Monitoring & Compliance / Static Code Security Analysis (push) Waiting to run
Security Monitoring & Compliance / Security Compliance Validation (push) Waiting to run
Security Monitoring & Compliance / Security Summary Report (push) Blocked by required conditions
Security Monitoring & Compliance / Security Team Notification (push) Blocked by required conditions
fix(find-trainer): Implement Strategy H JavaScript interceptor for map marker repair
- Add Object.defineProperty interceptor to catch iMapsData assignment before IGM plugin corrupts it
- Detect and repair markers with corrupted coordinates (Lat == Lng) using backup lat/lng keys
- Remove PHP query injections that caused 500 errors
- Increase safety timeouts from 6s to 30s for slower resource loading
- Remove Safari blocker bug in find-trainer assets
- Update debug script for mapgeo integration testing

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-05 13:12:46 -04:00

982 lines
No EOL
38 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Find a Trainer Page JavaScript
* Handles filtering, modals, and AJAX interactions
*
* @package HVAC_Plugin
* @since 1.0.0
*/
(function ($) {
'use strict';
// Cache DOM elements
let $filterModal, $trainerModal, $contactForm;
let currentFilter = '';
let activeFilters = {};
let currentPage = 1;
let isLoading = false;
// Initialize on document ready
$(document).ready(function () {
initTrainerDirectory();
});
/**
* Initialize trainer directory with retries
*/
function initTrainerDirectory(attempts = 0) {
// Critical dependency check
if (typeof hvac_find_trainer === 'undefined') {
if (attempts < 10) {
console.log(`[HVAC Find Trainer] Configuration object missing, retrying... (${attempts + 1}/10)`);
setTimeout(() => initTrainerDirectory(attempts + 1), 500);
return;
}
console.error('[HVAC Find Trainer] Failed to initialize: hvac_find_trainer object missing after retries');
return;
}
initializeElements();
bindEvents();
// Handle direct profile URL access
handleDirectProfileAccess();
// Enable MapGeo interaction handling (only if not showing direct profile)
if (!hvac_find_trainer.show_direct_profile) {
preventMapGeoSidebarContent();
interceptMapGeoMarkers();
// Additional MapGeo integration after map loads
setTimeout(function () {
initializeMapGeoEvents();
}, 2000); // Give MapGeo time to initialize
}
}
/**
* Initialize cached elements
*/
function initializeElements() {
$filterModal = $('#hvac-filter-modal');
$trainerModal = $('#hvac-trainer-modal');
$contactForm = $('#hvac-contact-form');
// CRITICAL: Ensure modals are hidden on initialization
if ($filterModal.length) {
$filterModal.removeClass('modal-active active show').css({
'display': 'none',
'visibility': 'hidden',
'opacity': '0'
});
}
if ($trainerModal.length) {
$trainerModal.css('display', 'none');
}
}
/**
* Prevent MapGeo from displaying content in its sidebar
* This ensures trainer cards only appear in our Container 5
*/
function preventMapGeoSidebarContent() {
// Remove any MapGeo sidebar content immediately
$('.igm_content_right_1_3').remove();
$('.igm_content_gutter').remove();
// Watch for any dynamic content injection from MapGeo
const observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
if (mutation.addedNodes.length) {
mutation.addedNodes.forEach(function (node) {
if (node.nodeType === 1) { // Element node
// Remove any MapGeo sidebar that gets added
if ($(node).hasClass('igm_content_right_1_3') ||
$(node).hasClass('igm_content_gutter')) {
$(node).remove();
}
// Also check children
$(node).find('.igm_content_right_1_3, .igm_content_gutter').remove();
}
});
}
});
});
// Observe the map section for changes
const mapSection = document.querySelector('.hvac-map-section');
if (mapSection) {
observer.observe(mapSection, {
childList: true,
subtree: true
});
}
// Also observe the entire page for MapGeo injections
observer.observe(document.body, {
childList: true,
subtree: true
});
}
/**
* Initialize MapGeo-specific event handlers after map loads
*/
function initializeMapGeoEvents() {
console.log('Initializing MapGeo events...');
// Create the main MapGeo handler function
// This replaces the early version created in the page template
window.hvacMainShowTrainerModal = function (data) {
console.log('MapGeo custom action triggered with data:', data);
// Method 1: Use profile_id if available (most reliable)
let profileId = null;
if (data && data.profile_id && data.profile_id.trim() !== '') {
profileId = data.profile_id.trim();
} else if (data && data.id && data.id.toString().startsWith('trainer_')) {
// Extract profile ID from marker ID format "trainer_123"
profileId = data.id.replace('trainer_', '');
} else if (data && data.name && data.name.match(/^\d+$/)) {
// Check if name field actually contains the profile ID (common case)
profileId = data.name;
} else if (data && data.id && data.id.match(/^\d+$/)) {
// Check if id field contains just the profile ID number
profileId = data.id;
}
console.log('Extracted profile ID:', profileId);
if (profileId) {
// Find trainer card by profile ID (most reliable method)
const $matchingCard = $('.hvac-trainer-card[data-profile-id="' + profileId + '"]');
if ($matchingCard.length > 0 && !$matchingCard.hasClass('hvac-champion-card')) {
console.log('Found matching trainer card by profile ID:', profileId);
// Extract trainer data from the card
const trainerData = {
profile_id: profileId,
name: $matchingCard.find('.hvac-trainer-name a, .hvac-trainer-name .hvac-champion-name').text().trim(),
city: $matchingCard.find('.hvac-trainer-location').text().split(',')[0],
state: $matchingCard.find('.hvac-trainer-location').text().split(',')[1]?.trim(),
certification_type: $matchingCard.find('.hvac-trainer-certification').text(), // Legacy compatibility
certifications: [],
profile_image: $matchingCard.find('.hvac-trainer-image img:not(.hvac-mq-badge)').attr('src') || '',
business_type: 'Independent Contractor', // Mock data
event_count: parseInt($matchingCard.data('event-count')) || 0,
training_formats: 'In-Person, Virtual',
training_locations: 'On-site, Remote',
upcoming_events: []
};
// Extract certifications from card badges
$matchingCard.find('.hvac-trainer-cert-badge').each(function () {
const certText = $(this).text().trim();
if (certText && certText !== 'HVAC Trainer') {
trainerData.certifications.push({
type: certText,
status: $(this).hasClass('hvac-cert-legacy') ? 'legacy' : 'active'
});
}
});
// Show the trainer modal
showTrainerModal(trainerData);
return; // Successfully handled
} else if ($matchingCard.length > 0 && $matchingCard.hasClass('hvac-champion-card')) {
console.log('Clicked marker is for a Champion, not showing modal');
return; // Champions don't get modals
} else {
console.warn('No trainer card found for profile ID:', profileId);
}
}
// Fallback Method 2: Try to extract trainer name and match
let trainerName = null;
// Try various name fields
if (data && data.name && data.name.trim() !== '+' && !data.name.match(/^\d+$/)) {
trainerName = data.name.trim();
} else if (data && data.title && data.title.trim() !== '+') {
trainerName = data.title.trim();
}
// Try content field
if (!trainerName && data && data.content) {
console.log('Trying to extract trainer from content:', data.content);
const tempDiv = document.createElement('div');
tempDiv.innerHTML = data.content;
const nameElements = tempDiv.querySelectorAll('h4, strong, .trainer-name, [class*="name"]');
if (nameElements.length > 0) {
trainerName = nameElements[0].textContent.trim();
}
}
// Try tooltipContent
if (!trainerName && data && data.tooltipContent) {
const tempDiv = document.createElement('div');
tempDiv.innerHTML = data.tooltipContent;
const strongElements = tempDiv.querySelectorAll('strong');
if (strongElements.length > 0) {
trainerName = strongElements[0].textContent.trim();
}
}
console.log('Extracted trainer name (fallback):', trainerName);
if (trainerName && trainerName !== '+') {
// Try to find matching trainer by name
let $matchingCard = $('.hvac-trainer-card').filter(function () {
const cardName = $(this).find('.hvac-trainer-name a, .hvac-trainer-name .hvac-champion-name').text().trim();
return cardName === trainerName;
});
// If exact match not found, try partial matching
if ($matchingCard.length === 0) {
$matchingCard = $('.hvac-trainer-card').filter(function () {
const cardName = $(this).find('.hvac-trainer-name a, .hvac-trainer-name .hvac-champion-name').text().trim();
return cardName.toLowerCase().includes(trainerName.toLowerCase()) ||
trainerName.toLowerCase().includes(cardName.toLowerCase());
});
}
if ($matchingCard.length > 0 && !$matchingCard.hasClass('hvac-champion-card')) {
console.log('Found matching trainer card by name:', trainerName);
// Extract trainer data from the card
const trainerData = {
profile_id: $matchingCard.data('profile-id'),
name: $matchingCard.find('.hvac-trainer-name a, .hvac-trainer-name .hvac-champion-name').text().trim(),
city: $matchingCard.find('.hvac-trainer-location').text().split(',')[0],
state: $matchingCard.find('.hvac-trainer-location').text().split(',')[1]?.trim(),
certification_type: $matchingCard.find('.hvac-trainer-certification').text(), // Legacy compatibility
certifications: [],
profile_image: $matchingCard.find('.hvac-trainer-image img:not(.hvac-mq-badge)').attr('src') || '',
business_type: 'Independent Contractor', // Mock data
event_count: parseInt($matchingCard.data('event-count')) || 0,
training_formats: 'In-Person, Virtual',
training_locations: 'On-site, Remote',
upcoming_events: []
};
// Extract certifications from card badges
$matchingCard.find('.hvac-trainer-cert-badge').each(function () {
const certText = $(this).text().trim();
if (certText && certText !== 'HVAC Trainer') {
trainerData.certifications.push({
type: certText,
status: $(this).hasClass('hvac-cert-legacy') ? 'legacy' : 'active'
});
}
});
// Show the trainer modal
showTrainerModal(trainerData);
} else if ($matchingCard.length > 0 && $matchingCard.hasClass('hvac-champion-card')) {
console.log('Matched trainer is a Champion, not showing modal');
} else {
console.warn('No matching trainer found for name:', trainerName);
console.log('Available trainers:',
$('.hvac-trainer-card .hvac-trainer-name a, .hvac-trainer-card .hvac-trainer-name .hvac-champion-name').map(function () {
return $(this).text().trim();
}).get()
);
}
} else {
console.warn('Could not extract valid trainer identifier from MapGeo data:', data);
console.log('Available data properties:', Object.keys(data || {}));
console.log('Available profile IDs on page:',
$('.hvac-trainer-card').map(function () {
return $(this).data('profile-id');
}).get()
);
}
};
// Replace the early function with the main one
window.hvacShowTrainerModal = window.hvacMainShowTrainerModal;
// Process any queued calls from before the main script loaded
if (window.hvacPendingModalCalls && window.hvacPendingModalCalls.length > 0) {
console.log('Processing', window.hvacPendingModalCalls.length, 'queued MapGeo calls');
window.hvacPendingModalCalls.forEach(function (data) {
window.hvacMainShowTrainerModal(data);
});
window.hvacPendingModalCalls = []; // Clear the queue
}
console.log('MapGeo custom action function created: window.hvacShowTrainerModal');
}
/**
* Prevent MapGeo from showing content in sidebar (if needed)
*/
function interceptMapGeoMarkers() {
// This function now primarily handles preventing MapGeo sidebar content
// The actual marker clicks are handled via the MapGeo custom action: window.hvacShowTrainerModal
// Handle any legacy view profile links if they exist in tooltips/popups
$(document).on('click', '.hvac-view-profile, .hvac-marker-popup button', function (e) {
e.preventDefault();
e.stopPropagation();
const profileId = $(this).data('profile-id');
if (profileId) {
// Find the corresponding trainer data from the cards
const $trainerCard = $('.hvac-trainer-card[data-profile-id="' + profileId + '"]');
if ($trainerCard.length > 0 && !$trainerCard.hasClass('hvac-champion-card')) {
// Get trainer name and trigger the MapGeo custom action
const trainerName = $trainerCard.find('.hvac-trainer-name a, .hvac-trainer-name .hvac-champion-name').text().trim();
// Trigger the same function MapGeo would call
if (window.hvacShowTrainerModal) {
window.hvacShowTrainerModal({ name: trainerName });
}
}
}
});
}
/**
* Bind all event handlers
*/
function bindEvents() {
// Filter button clicks - handle both class variations
$('.hvac-filter-btn, .hvac-filter-button').on('click', handleFilterClick);
// Filter modal apply
$('.hvac-filter-apply').on('click', applyFilters);
// Clear all filters button
$('.hvac-clear-filters').on('click', clearAllFilters);
// Trainer profile clicks - using event delegation
$(document).on('click', '.hvac-open-profile', handleProfileClick);
// Modal close buttons and backdrop clicks
$('.hvac-modal-close').on('click', closeModals);
// Click on modal backdrop to close
$filterModal.on('click', function (e) {
if ($(e.target).is('#hvac-filter-modal')) {
closeModals();
}
});
$trainerModal.on('click', function (e) {
if ($(e.target).is('#hvac-trainer-modal')) {
closeModals();
}
});
// Escape key to close modals
$(document).on('keydown', function (e) {
if (e.key === 'Escape') {
closeModals();
}
});
// Search input
$('.hvac-search-input').on('input', debounce(handleSearch, 500));
// Contact form submission (both modal and direct forms)
$contactForm.on('submit', handleContactSubmit);
$(document).on('submit', '#hvac-direct-contact-form', handleContactSubmit);
// Pagination clicks
$(document).on('click', '.hvac-pagination a, .hvac-page-link', handlePagination);
// Active filter removal
$(document).on('click', '.hvac-active-filter button', removeActiveFilter);
}
/**
* Handle filter button click
*/
function handleFilterClick(e) {
e.preventDefault();
e.stopPropagation();
currentFilter = $(this).data('filter');
// Load real filter options via AJAX
loadFilterOptions(currentFilter);
}
/**
* Load filter options via AJAX
*/
function loadFilterOptions(filterType) {
if (isLoading) return;
isLoading = true;
// Show loading state for filter button
$(`.hvac-filter-btn[data-filter="${filterType}"]`).addClass('loading');
$.post(hvac_find_trainer.ajax_url, {
action: 'hvac_get_filter_options',
filter_type: filterType,
nonce: hvac_find_trainer.nonce
})
.done(function (response) {
if (response.success && response.data.options) {
// Convert the different response formats to standard format
let options = [];
if (filterType === 'business_type') {
// Business types have {value, label, count} format
options = response.data.options;
} else {
// States and other simple arrays need to be converted to {value, label} format
options = response.data.options.map(function (option) {
if (typeof option === 'string') {
return { value: option, label: option };
}
return option;
});
}
showFilterModal({ options: options });
} else {
console.error('Failed to load filter options:', response);
// Fallback to empty options
showFilterModal({ options: [] });
}
})
.fail(function (xhr, status, error) {
console.error('AJAX error loading filter options:', status, error);
// Fallback to empty options
showFilterModal({ options: [] });
})
.always(function () {
isLoading = false;
$(`.hvac-filter-btn[data-filter="${filterType}"]`).removeClass('loading');
});
}
/**
* Get mock filter options (kept as fallback)
*/
function getMockFilterOptions(filterType) {
const options = {
state: [
{ value: 'Alabama', label: 'Alabama' },
{ value: 'Alaska', label: 'Alaska' },
{ value: 'Arizona', label: 'Arizona' },
{ value: 'Arkansas', label: 'Arkansas' },
{ value: 'California', label: 'California' },
{ value: 'Colorado', label: 'Colorado' },
{ value: 'Florida', label: 'Florida' },
{ value: 'Georgia', label: 'Georgia' },
{ value: 'Illinois', label: 'Illinois' },
{ value: 'Michigan', label: 'Michigan' },
{ value: 'Minnesota', label: 'Minnesota' },
{ value: 'Ohio', label: 'Ohio' },
{ value: 'Texas', label: 'Texas' },
{ value: 'Wisconsin', label: 'Wisconsin' }
],
business_type: [
{ value: 'Independent Contractor', label: 'Independent Contractor' },
{ value: 'Small Business', label: 'Small Business' },
{ value: 'Corporation', label: 'Corporation' },
{ value: 'Non-Profit', label: 'Non-Profit' }
],
training_format: [
{ value: 'In-Person', label: 'In-Person' },
{ value: 'Virtual', label: 'Virtual' },
{ value: 'Hybrid', label: 'Hybrid' },
{ value: 'Self-Paced', label: 'Self-Paced' }
],
training_resources: [
{ value: 'Video Tutorials', label: 'Video Tutorials' },
{ value: 'Written Guides', label: 'Written Guides' },
{ value: 'Hands-On Training', label: 'Hands-On Training' },
{ value: 'Certification Programs', label: 'Certification Programs' }
]
};
return {
options: options[filterType] || []
};
}
/**
* Show filter modal with options
*/
function showFilterModal(data) {
const $modalTitle = $filterModal.find('.hvac-filter-modal-title');
const $modalOptions = $filterModal.find('.hvac-filter-options');
// Set title
let title = currentFilter.replace(/_/g, ' ');
title = title.charAt(0).toUpperCase() + title.slice(1);
$modalTitle.text(title);
// Build options HTML
let optionsHtml = '';
const currentValues = activeFilters[currentFilter] || [];
data.options.forEach(function (option) {
const checked = currentValues.includes(option.value) ? 'checked' : '';
optionsHtml += `
<div class="hvac-filter-option">
<input type="checkbox" id="filter_${option.value.replace(/\s+/g, '_')}" value="${option.value}" ${checked}>
<label for="filter_${option.value.replace(/\s+/g, '_')}">${option.label}</label>
</div>
`;
});
$modalOptions.html(optionsHtml);
// Show modal with proper CSS class and inline style overrides
$filterModal.addClass('modal-active');
// Force styles with higher specificity by setting them directly on the element
$filterModal[0].style.setProperty('display', 'flex', 'important');
$filterModal[0].style.setProperty('visibility', 'visible', 'important');
$filterModal[0].style.setProperty('opacity', '1', 'important');
}
/**
* Apply selected filters
*/
function applyFilters() {
const selectedValues = [];
$filterModal.find('.hvac-filter-option input:checked').each(function () {
selectedValues.push($(this).val());
});
if (selectedValues.length > 0) {
activeFilters[currentFilter] = selectedValues;
} else {
delete activeFilters[currentFilter];
}
updateActiveFiltersDisplay();
updateClearButtonVisibility();
currentPage = 1;
loadFilteredTrainers();
closeModals();
}
/**
* Update active filters display
*/
function updateActiveFiltersDisplay() {
const $container = $('.hvac-active-filters');
let html = '';
for (const [filter, values] of Object.entries(activeFilters)) {
values.forEach(function (value) {
html += `
<div class="hvac-active-filter" data-filter="${filter}" data-value="${value}">
${value}
<button type="button" aria-label="Remove filter">×</button>
</div>
`;
});
}
$container.html(html);
}
/**
* Remove active filter
*/
function removeActiveFilter(e) {
e.preventDefault();
const $filter = $(this).parent();
const filter = $filter.data('filter');
const value = $filter.data('value');
if (activeFilters[filter]) {
activeFilters[filter] = activeFilters[filter].filter(v => v !== value);
if (activeFilters[filter].length === 0) {
delete activeFilters[filter];
}
}
updateActiveFiltersDisplay();
updateClearButtonVisibility();
currentPage = 1;
loadFilteredTrainers();
}
/**
* Handle trainer profile click
*/
function handleProfileClick(e) {
e.preventDefault();
e.stopPropagation();
const $card = $(this).closest('.hvac-trainer-card');
// Don't allow clicks on Champion cards
if ($card.hasClass('hvac-champion-card')) {
return false;
}
const profileId = $(this).data('profile-id');
// Get trainer data from the card
const trainerData = {
profile_id: profileId,
name: $card.find('.hvac-trainer-name a').text(),
city: $card.find('.hvac-trainer-location').text().split(',')[0],
state: $card.find('.hvac-trainer-location').text().split(',')[1]?.trim(),
certification_type: $card.find('.hvac-trainer-certification').text(), // Legacy field for compatibility
certifications: [], // Will be populated from card badges
profile_image: $card.find('.hvac-trainer-image img:not(.hvac-mq-badge)').attr('src') || '',
business_type: 'Independent Contractor', // Mock data
event_count: parseInt($card.data('event-count')) || 0, // Real event count from data attribute
training_formats: 'In-Person, Virtual',
training_locations: 'On-site, Remote',
upcoming_events: [] // Mock empty events
};
// Extract certifications from card badges
$card.find('.hvac-trainer-cert-badge').each(function () {
const certText = $(this).text().trim();
if (certText && certText !== 'HVAC Trainer') {
trainerData.certifications.push({
type: certText,
status: $(this).hasClass('hvac-cert-legacy') ? 'legacy' : 'active'
});
}
});
showTrainerModal(trainerData);
}
/**
* Show trainer profile modal
* Made global so MapGeo can access it
*/
function showTrainerModal(trainer) {
// Update modal title
$trainerModal.find('.hvac-modal-title').text(trainer.name);
// Update profile image
const $imgContainer = $trainerModal.find('.hvac-modal-image');
let imageHtml = '';
if (trainer.profile_image) {
imageHtml = `<img src="${trainer.profile_image}" alt="${trainer.name}">`;
} else {
imageHtml = '<div class="hvac-trainer-avatar"><span class="dashicons dashicons-businessperson"></span></div>';
}
// Add mQ badge overlay for certified trainers
let hasTrainerCert = false;
if (trainer.certifications && trainer.certifications.length > 0) {
// Check if any certification is a trainer certification
hasTrainerCert = trainer.certifications.some(cert =>
cert.type.toLowerCase().includes('trainer') ||
cert.type === 'measureQuick Certified Trainer'
);
} else if (trainer.certification_type === 'Certified measureQuick Trainer' ||
trainer.certification_type === 'measureQuick Certified Trainer') {
// Fallback for legacy single certification
hasTrainerCert = true;
}
if (hasTrainerCert) {
imageHtml += '<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>';
}
$imgContainer.html(imageHtml);
// Update profile info
$trainerModal.find('.hvac-modal-location').text(`${trainer.city}, ${trainer.state}`);
// Update certifications section - handle both single and multiple certifications
const $certContainer = $trainerModal.find('.hvac-modal-certification-badges');
let certHtml = '';
if (trainer.certifications && trainer.certifications.length > 0) {
// Show multiple certifications as badges
trainer.certifications.forEach(function (cert) {
const badgeClass = cert.type.toLowerCase()
.replace('measurequick certified ', '')
.replace(/\s+/g, '-');
const legacyClass = cert.status === 'legacy' ? ' hvac-cert-legacy' : '';
certHtml += `<span class="hvac-trainer-cert-badge hvac-cert-${badgeClass}${legacyClass}">${cert.type}</span>`;
});
} else if (trainer.certification_type && trainer.certification_type !== 'HVAC Trainer') {
// Fallback to legacy single certification
const badgeClass = trainer.certification_type.toLowerCase()
.replace('measurequick certified ', '')
.replace(/\s+/g, '-');
certHtml = `<span class="hvac-trainer-cert-badge hvac-cert-${badgeClass}">${trainer.certification_type}</span>`;
} else {
// Default fallback
certHtml = '<span class="hvac-trainer-cert-badge hvac-cert-default">HVAC Trainer</span>';
}
$certContainer.html(certHtml);
$trainerModal.find('.hvac-modal-business').text(trainer.business_type || '');
$trainerModal.find('.hvac-modal-events span').text(trainer.event_count || 0);
// Update training details
$trainerModal.find('.hvac-training-formats').text(trainer.training_formats || 'Various');
$trainerModal.find('.hvac-training-locations').text(trainer.training_locations || 'On-site');
// Show loading state for events
$trainerModal.find('.hvac-events-list').html('<li>Loading upcoming events...</li>');
// Set hidden fields for contact form
$contactForm.find('input[name="trainer_id"]').val(trainer.user_id || '');
$contactForm.find('input[name="trainer_profile_id"]').val(trainer.profile_id);
// Reset contact form
$contactForm[0].reset();
$('.hvac-form-message').hide();
// Show modal
$trainerModal.fadeIn(300);
// Fetch upcoming events via AJAX
fetchUpcomingEvents(trainer.profile_id);
}
/**
* Fetch upcoming events for a trainer via AJAX
*/
function fetchUpcomingEvents(profileId) {
if (!profileId) {
$trainerModal.find('.hvac-events-list').html('<li>No upcoming events scheduled</li>');
return;
}
$.post(hvac_find_trainer.ajax_url, {
action: 'hvac_get_trainer_upcoming_events',
nonce: hvac_find_trainer.nonce,
profile_id: profileId
}, function (response) {
if (response.success && response.data.events) {
let eventsHtml = '';
if (response.data.events.length > 0) {
response.data.events.forEach(function (event) {
eventsHtml += `<li><a href="${event.url}" target="_blank">${event.title}</a> - ${event.date}</li>`;
});
} else {
eventsHtml = '<li>No upcoming events scheduled</li>';
}
$trainerModal.find('.hvac-events-list').html(eventsHtml);
} else {
$trainerModal.find('.hvac-events-list').html('<li>No upcoming events scheduled</li>');
}
}).fail(function () {
$trainerModal.find('.hvac-events-list').html('<li>Unable to load events</li>');
});
}
/**
* Handle contact form submission
*/
function handleContactSubmit(e) {
e.preventDefault();
const $form = $(this);
const $submitBtn = $form.find('.hvac-form-submit');
const $successMsg = $form.find('.hvac-form-success');
const $errorMsg = $form.find('.hvac-form-error');
const originalText = $submitBtn.text();
$submitBtn.text('Sending...').prop('disabled', true);
// For now, just show success message
setTimeout(function () {
$successMsg.show();
$errorMsg.hide();
$form[0].reset();
$submitBtn.text(originalText).prop('disabled', false);
// Hide success message after 5 seconds
setTimeout(function () {
$successMsg.fadeOut();
}, 5000);
}, 1000);
}
/**
* Handle search input
*/
function handleSearch() {
const searchTerm = $('.hvac-search-input').val();
if (isLoading) return;
updateClearButtonVisibility();
currentPage = 1;
loadFilteredTrainers();
}
/**
* Handle pagination
*/
function handlePagination(e) {
e.preventDefault();
currentPage = $(this).data('page');
loadFilteredTrainers();
// Scroll to top of trainer grid
$('html, body').animate({
scrollTop: $('.hvac-trainer-directory-container').offset().top - 100
}, 500);
}
/**
* Load filtered trainers via AJAX
*/
function loadFilteredTrainers() {
if (isLoading) return;
isLoading = true;
const $container = $('.hvac-trainer-grid');
let $pagination = $('.hvac-pagination');
// Show loading state
$container.addClass('hvac-loading');
// Prepare data
const data = {
action: 'hvac_filter_trainers',
nonce: hvac_find_trainer.nonce,
page: currentPage,
search: $('.hvac-search-input').val(),
// Flatten the activeFilters for PHP processing
...activeFilters
};
// Make AJAX request
$.post(hvac_find_trainer.ajax_url, data, function (response) {
if (response.success) {
// Our PHP returns an array of trainer card HTML
if (response.data.trainers && response.data.trainers.length > 0) {
const trainersHtml = response.data.trainers.join('');
$container.html(trainersHtml);
} else {
$container.html('<div class="hvac-no-results"><p>No trainers found matching your criteria. Please try adjusting your filters.</p></div>');
}
// Update count display if exists
if (response.data.count !== undefined) {
$('.hvac-trainer-count').text(response.data.count + ' trainers found');
}
// Simple pagination logic - show/hide existing pagination based on results
if (response.data.count > 12) { // Assuming 12 per page
if ($pagination.length > 0) {
$pagination.show();
}
} else {
if ($pagination.length > 0) {
$pagination.hide();
}
}
} else {
console.error('Failed to load trainers:', response);
$container.html('<div class="hvac-no-results"><p>Error loading trainers. Please try again.</p></div>');
}
}).fail(function (xhr) {
console.error('AJAX error:', xhr);
}).always(function () {
isLoading = false;
$container.removeClass('hvac-loading');
});
}
/**
* Close all modals
*/
function closeModals() {
// Remove the modal-active class and force hide styles
$filterModal.removeClass('modal-active');
// Force hide styles with !important
$filterModal[0].style.setProperty('display', 'none', 'important');
$filterModal[0].style.setProperty('visibility', 'hidden', 'important');
$filterModal[0].style.setProperty('opacity', '0', 'important');
$trainerModal.fadeOut(300);
}
/**
* Debounce helper function
*/
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
/**
* Clear all filters
*/
function clearAllFilters() {
activeFilters = {};
$('.hvac-search-input').val('');
updateActiveFiltersDisplay();
updateClearButtonVisibility();
currentPage = 1;
loadFilteredTrainers();
}
/**
* Update clear button visibility
*/
function updateClearButtonVisibility() {
const hasFilters = Object.keys(activeFilters).length > 0;
const hasSearch = $('.hvac-search-input').val().trim() !== '';
if (hasFilters || hasSearch) {
$('.hvac-clear-filters').show();
} else {
$('.hvac-clear-filters').hide();
}
}
/**
* Handle direct profile URL access
* When someone accesses /find-a-trainer/profile/{id}, show the profile and handle interactions
*/
function handleDirectProfileAccess() {
// Check if we're showing a direct profile
if (hvac_find_trainer.show_direct_profile && hvac_find_trainer.direct_profile_id) {
console.log('Direct profile access detected for profile ID:', hvac_find_trainer.direct_profile_id);
// Update page title in browser
if (document.title.includes('Find a Trainer')) {
document.title = document.title.replace('Find a Trainer', 'Trainer Profile');
}
// Bind contact trainer button
$(document).on('click', '.hvac-contact-trainer-btn', function (e) {
e.preventDefault();
const profileId = $(this).data('profile-id');
showTrainerModal(profileId);
});
// Update URL without page reload for clean sharing
const currentUrl = window.location.href;
if (currentUrl.includes('/profile/') && window.history && window.history.replaceState) {
const cleanUrl = currentUrl.split('?')[0]; // Remove any query parameters
window.history.replaceState({}, document.title, cleanUrl);
}
}
}
// Expose showTrainerModal globally for MapGeo integration
window.showTrainerModal = showTrainerModal;
})(jQuery);