/** * 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() { 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(), 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: [] }; // 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(), 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: [] }; // 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 $('.hvac-filter-btn').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-trainer-search').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'); // For now, show mock filter options showFilterModal(getMockFilterOptions(currentFilter)); } /** * Get mock filter options (replace with AJAX later) */ 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 += `
`; }); $modalOptions.html(optionsHtml); // Use the new modal-active class for proper visibility control $filterModal.addClass('modal-active').css('display', 'flex').fadeIn(300); } /** * 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 += `
${value}
`; }); } $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(), profile_image: $card.find('.hvac-trainer-image img').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 }; 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 = `${trainer.name}`; } else { imageHtml = '
'; } // Add mQ badge overlay for certified trainers if (trainer.certification_type === 'Certified measureQuick Trainer') { imageHtml += '
measureQuick Certified Trainer
'; } $imgContainer.html(imageHtml); // Update profile info $trainerModal.find('.hvac-modal-location').text(`${trainer.city}, ${trainer.state}`); $trainerModal.find('.hvac-modal-certification').text(trainer.certification_type || 'HVAC Trainer'); $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('
  • Loading upcoming events...
  • '); // 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('
  • No upcoming events scheduled
  • '); 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 += `
  • ${event.title} - ${event.date}
  • `; }); } else { eventsHtml = '
  • No upcoming events scheduled
  • '; } $trainerModal.find('.hvac-events-list').html(eventsHtml); } else { $trainerModal.find('.hvac-events-list').html('
  • No upcoming events scheduled
  • '); } }).fail(function() { $trainerModal.find('.hvac-events-list').html('
  • Unable to load events
  • '); }); } /** * 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-trainer-search').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, filters: { ...activeFilters, search: $('#hvac-trainer-search').val() } }; // Make AJAX request $.post(hvac_find_trainer.ajax_url, data, function(response) { if (response.success) { // Update trainer grid $container.html(response.data.html); // Update pagination if (response.data.pagination && response.data.pagination.length > 0) { // Pagination HTML returned - replace existing or create new if ($pagination.length > 0) { $pagination.replaceWith(response.data.pagination); } else { $('.hvac-trainer-directory-container').append(response.data.pagination); } } else { // No pagination HTML - either hide existing or ensure container exists for later if ($pagination.length > 0) { $pagination.empty(); } else if (response.data.max_pages > 1) { // Add empty pagination container for when pages increase later $('.hvac-trainer-directory-container').append('
    '); } } // Update pagination reference after potential DOM changes $pagination = $('.hvac-pagination'); // Update count display if exists if (response.data.count !== undefined) { $('.hvac-trainer-count').text(response.data.count + ' trainers found'); } } else { console.error('Failed to load trainers:', response); } }).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 to ensure proper hiding $filterModal.removeClass('modal-active').fadeOut(300, function() { $(this).css('display', 'none'); }); $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-trainer-search').val(''); updateActiveFiltersDisplay(); updateClearButtonVisibility(); currentPage = 1; loadFilteredTrainers(); } /** * Update clear button visibility */ function updateClearButtonVisibility() { const hasFilters = Object.keys(activeFilters).length > 0; const hasSearch = $('#hvac-trainer-search').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);