Add massive collection of CSS, JavaScript and theme assets that were previously excluded: **CSS Files (681 total):** - HVAC plugin-specific styles (hvac-*.css): 34 files including dashboard, certificates, registration, mobile nav, accessibility fixes, animations, and welcome popup - Theme framework files (Astra, builder systems, layouts): 200+ files - Plugin compatibility styles (WooCommerce, WPForms, Elementor, Contact Form 7): 150+ files - WordPress core and editor styles: 50+ files - Responsive and RTL language support: 200+ files **JavaScript Files (400+ total):** - HVAC plugin functionality (hvac-*.js): 27 files including menu systems, dashboard enhancements, profile sharing, mobile responsive features, accessibility, and animations - Framework and library files: jQuery plugins, GSAP, AOS, Swiper, Chart.js, Lottie, Isotope - Plugin compatibility scripts: WPForms, WooCommerce, Elementor, Contact Form 7, LifterLMS - WordPress core functionality: customizer, admin, block editor compatibility - Third-party integrations: Stripe, SMTP, analytics, search functionality **Assets:** - Certificate background images and logos - Comprehensive theme styling infrastructure - Mobile-responsive design systems - Cross-browser compatibility assets - Performance-optimized minified versions **Updated .gitignore:** - Fixed asset directory whitelisting patterns to properly include CSS/JS/images - Added proper directory structure recognition (!/assets/css/, !/assets/js/, etc.) - Maintains security by excluding sensitive files while including essential assets This commit provides the complete frontend infrastructure needed for: - Full theme functionality and styling - Plugin feature implementations - Mobile responsiveness and accessibility - Cross-browser compatibility - Performance optimization - Developer workflow support
832 lines
No EOL
33 KiB
JavaScript
832 lines
No EOL
33 KiB
JavaScript
/**
|
||
* 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 += `
|
||
<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);
|
||
// 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 += `
|
||
<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(),
|
||
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 = `<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
|
||
if (trainer.certification_type === 'Certified measureQuick Trainer') {
|
||
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}`);
|
||
$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('<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-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('<div class="hvac-pagination"></div>');
|
||
}
|
||
}
|
||
|
||
// 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); |