- Add comprehensive Training Leads system for HVAC trainers * New /trainer/training-leads/ page with tabular contact submission display * HVAC_Training_Leads class with AJAX status updates and filtering * Empty state messaging and profile sharing CTA * Database integration with existing contact forms system - Restructure trainer navigation menu for better UX * Rename "Customize" to "Profile" with logical groupings * Move "Logout" under "Profile" submenu * Change "Personal Profile" to "Trainer Profile" * Add "Training Leads" under Profile section * Update help menu to show only question mark icon positioned far right - Enhance documentation system * Fix /trainer/documentation/ page styling and navigation integration * Update content to reflect current platform features * Add Training Leads documentation and navigation guide * Implement proper WordPress template structure - Update user management * Change joe@upskillhvac.com display name to "Joe Medosch" * Assign Joe as author of measureQuick headquarters venue * Assign Joe as author of measureQuick and Upskill HVAC organizers 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1035 lines
No EOL
27 KiB
Markdown
1035 lines
No EOL
27 KiB
Markdown
# HVAC Trainer System Documentation
|
|
|
|
This document provides comprehensive documentation for all functionality related to the HVAC Trainer custom post type and Find A Trainer page in the HVAC Community Events plugin.
|
|
|
|
## Table of Contents
|
|
|
|
1. [System Overview](#system-overview)
|
|
2. [Trainer Profile Custom Post Type](#trainer-profile-custom-post-type)
|
|
3. [Find A Trainer Page](#find-a-trainer-page)
|
|
4. [MapGeo Integration](#mapgeo-integration)
|
|
5. [User Roles & Permissions](#user-roles--permissions)
|
|
6. [API Reference](#api-reference)
|
|
7. [Frontend Components](#frontend-components)
|
|
8. [Database Schema](#database-schema)
|
|
9. [Performance Optimizations](#performance-optimizations)
|
|
10. [Troubleshooting](#troubleshooting)
|
|
|
|
## System Overview
|
|
|
|
The HVAC Trainer System is a comprehensive solution for managing trainer profiles and providing a public directory with interactive map functionality. The system consists of:
|
|
|
|
- **Custom Post Type**: `trainer_profile` for storing trainer information
|
|
- **Find A Trainer Page**: Public-facing directory with search and filtering
|
|
- **MapGeo Integration**: Interactive map with trainer markers and modals
|
|
- **Profile Management**: Complete CRUD operations for trainer profiles
|
|
- **Taxonomy System**: Categorization for business types, training formats, etc.
|
|
|
|
### Architecture Diagram
|
|
|
|
```
|
|
WordPress Users (hvac_trainer/hvac_master_trainer)
|
|
↓
|
|
Trainer Profile Custom Post Type (trainer_profile)
|
|
↓
|
|
Find A Trainer Page ← MapGeo Integration → Interactive Map
|
|
↓
|
|
Search/Filter System → AJAX Handlers → Database Queries
|
|
```
|
|
|
|
## Trainer Profile Custom Post Type
|
|
|
|
### Overview
|
|
|
|
The `trainer_profile` custom post type stores comprehensive information about HVAC trainers, including personal details, certifications, business information, and location data.
|
|
|
|
### Core Files
|
|
|
|
- **Main Handler**: `includes/class-hvac-trainer-profile-manager.php`
|
|
- **Settings**: `includes/class-hvac-trainer-profile-settings.php`
|
|
- **Status Management**: `includes/class-hvac-trainer-status.php`
|
|
|
|
### Custom Fields Reference
|
|
|
|
#### Personal Information
|
|
|
|
| Field Name | Type | Description | Example |
|
|
|------------|------|-------------|---------|
|
|
| `trainer_first_name` | string | First name | "John" |
|
|
| `trainer_last_name` | string | Last name | "Smith" |
|
|
| `trainer_display_name` | string | Public display name | "John Smith" |
|
|
| `trainer_city` | string | City location | "Chicago" |
|
|
| `trainer_state` | string | State/Province | "Illinois" |
|
|
| `trainer_country` | string | Country | "USA" |
|
|
| `profile_image_url` | string | Profile photo URL | "https://..." |
|
|
| `linkedin_profile_url` | string | LinkedIn profile | "https://linkedin..." |
|
|
|
|
#### Certification Details
|
|
|
|
| Field Name | Type | Description | Values |
|
|
|------------|------|-------------|--------|
|
|
| `certification_type` | string | Certification level | "Certified measureQuick Trainer", "Certified measureQuick Champion" |
|
|
| `certification_status` | string | Current status | "Active", "Expired", "Pending", "Disabled" |
|
|
| `certification_color` | string | Hex color for MapGeo | "#5077bb", "#f19a42", "#f0f7e8" |
|
|
| `date_certified` | string | Certification date | "2024-01-15" |
|
|
| `personal_accreditation` | string | Additional certifications | Free text |
|
|
|
|
#### Business Information
|
|
|
|
| Field Name | Type | Description |
|
|
|------------|------|-------------|
|
|
| `annual_revenue_target` | string | Revenue goals |
|
|
| `application_details` | string | Registration application details |
|
|
|
|
#### System Fields
|
|
|
|
| Field Name | Type | Description |
|
|
|------------|------|-------------|
|
|
| `user_id` | integer | WordPress user ID |
|
|
| `is_public_profile` | string | Visibility flag ("1" or "0") |
|
|
| `_last_geocode_attempt` | string | Last geocoding timestamp |
|
|
| `geocoded_lat` | string | Latitude coordinate |
|
|
| `geocoded_lng` | string | Longitude coordinate |
|
|
|
|
### Taxonomies
|
|
|
|
#### Business Type (`business_type`)
|
|
- Manufacturer
|
|
- Distributor
|
|
- Contractor
|
|
- Consultant
|
|
- Educator
|
|
- Government
|
|
- Other
|
|
|
|
#### Training Audience (`training_audience`)
|
|
- Anyone (open to the public)
|
|
- Industry professionals
|
|
- Internal staff in my company
|
|
- Registered students/members of my org/institution
|
|
|
|
#### Training Formats (`training_formats`)
|
|
- In-person
|
|
- Virtual
|
|
- Hybrid
|
|
- On-demand
|
|
|
|
#### Training Locations (`training_locations`)
|
|
- Online
|
|
- Local
|
|
- Regional Travel
|
|
- National Travel
|
|
- International Travel
|
|
|
|
#### Training Resources (`training_resources`)
|
|
- Classroom
|
|
- Training Lab
|
|
- Ducted Furnace(s)
|
|
- Ducted Air Handler(s)
|
|
- Ducted Air Conditioner(s)
|
|
- Ducted Heat Pump(s)
|
|
- Ductless Heat Pump(s)
|
|
- Training Manuals
|
|
- Presentation Slides
|
|
- LMS Platform / SCORM Files
|
|
- Custom Curriculum
|
|
- Other
|
|
|
|
### Profile Creation Workflow
|
|
|
|
1. **User Registration**: User completes registration form
|
|
2. **Role Assignment**: User assigned `hvac_trainer` or `hvac_master_trainer` role
|
|
3. **Profile Creation**: System automatically creates `trainer_profile` post
|
|
4. **Data Population**: Registration data mapped to profile fields
|
|
5. **Geocoding**: System attempts to geocode trainer location
|
|
6. **Approval Process**: Admin reviews and approves profile for public display
|
|
|
|
### Profile Management
|
|
|
|
#### Creating Profiles Programmatically
|
|
|
|
```php
|
|
$profile_manager = HVAC_Trainer_Profile_Manager::get_instance();
|
|
|
|
$profile_data = [
|
|
'trainer_first_name' => 'John',
|
|
'trainer_last_name' => 'Smith',
|
|
'trainer_city' => 'Chicago',
|
|
'trainer_state' => 'Illinois',
|
|
'certification_type' => 'Certified measureQuick Trainer',
|
|
'user_id' => 123,
|
|
'is_public_profile' => '1'
|
|
];
|
|
|
|
$profile_id = $profile_manager->create_profile($profile_data);
|
|
```
|
|
|
|
#### Updating Profiles
|
|
|
|
```php
|
|
$profile_manager->update_profile($profile_id, $updated_data);
|
|
```
|
|
|
|
#### Retrieving Profiles
|
|
|
|
```php
|
|
// Get single profile
|
|
$profile = $profile_manager->get_profile($profile_id);
|
|
|
|
// Get all public profiles
|
|
$public_profiles = $profile_manager->get_public_profiles();
|
|
|
|
// Get profiles by criteria
|
|
$filtered_profiles = $profile_manager->get_profiles_by_criteria([
|
|
'state' => 'Illinois',
|
|
'certification_type' => 'Certified measureQuick Trainer'
|
|
]);
|
|
```
|
|
|
|
## Find A Trainer Page
|
|
|
|
### Overview
|
|
|
|
The Find A Trainer page (`/find-a-trainer/`) provides a public directory of approved trainer profiles with interactive search, filtering, and map functionality.
|
|
|
|
### Core Files
|
|
|
|
- **Page Handler**: `includes/find-trainer/class-hvac-find-trainer-page.php`
|
|
- **Directory Query**: `includes/find-trainer/class-hvac-trainer-directory-query.php`
|
|
- **Contact Handler**: `includes/find-trainer/class-hvac-contact-form-handler.php`
|
|
- **Template**: `templates/page-find-trainer.php`
|
|
|
|
### Features
|
|
|
|
#### Search Functionality
|
|
- **Text Search**: Search by trainer name, city, or state
|
|
- **Real-time Results**: AJAX-powered instant search results
|
|
- **Autocomplete**: Suggestions as user types
|
|
|
|
#### Filtering System
|
|
- **State/Province Filter**: Location-based filtering
|
|
- **Business Type Filter**: Filter by trainer business type
|
|
- **Training Format Filter**: Filter by available training formats
|
|
- **Training Resources Filter**: Filter by available resources
|
|
- **Multiple Filters**: Combine multiple filter criteria
|
|
|
|
#### Directory Display
|
|
- **Card Layout**: Clean trainer cards with essential information
|
|
- **Pagination**: Performance-optimized pagination system
|
|
- **Sorting**: Certified Trainers first, then Champions, then alphabetical
|
|
- **Champion Distinction**: Special styling for measureQuick Champions
|
|
|
|
#### Contact Integration
|
|
- **Contact Forms**: Direct contact forms for each trainer
|
|
- **Email Integration**: Automated email delivery to trainers
|
|
- **Lead Tracking**: Track contact form submissions
|
|
|
|
### AJAX Endpoints
|
|
|
|
| Endpoint | Purpose | Parameters |
|
|
|----------|---------|------------|
|
|
| `hvac_filter_trainers` | Filter trainers | `filters`, `page`, `per_page` |
|
|
| `hvac_search_trainers` | Search trainers | `search`, `page`, `per_page` |
|
|
| `hvac_get_filter_options` | Get filter options | None |
|
|
| `hvac_submit_contact_form` | Submit contact form | `trainer_id`, `name`, `email`, `message` |
|
|
|
|
### Frontend Implementation
|
|
|
|
#### JavaScript Integration
|
|
|
|
```javascript
|
|
// Initialize Find A Trainer functionality
|
|
jQuery(document).ready(function($) {
|
|
HVAC_FindTrainer.init({
|
|
ajax_url: hvac_find_trainer.ajax_url,
|
|
nonce: hvac_find_trainer.nonce,
|
|
filters: {
|
|
state: [],
|
|
business_type: [],
|
|
training_format: []
|
|
}
|
|
});
|
|
});
|
|
```
|
|
|
|
#### CSS Classes
|
|
|
|
| Class | Purpose |
|
|
|-------|---------|
|
|
| `.hvac-trainer-card` | Individual trainer card container |
|
|
| `.hvac-trainer-card-certified` | Certified trainer styling |
|
|
| `.hvac-champion-card` | Champion trainer styling |
|
|
| `.hvac-trainer-filters` | Filter panel container |
|
|
| `.hvac-search-box` | Search input container |
|
|
| `.hvac-trainer-modal` | Trainer profile modal |
|
|
|
|
## MapGeo Integration
|
|
|
|
### Overview
|
|
|
|
The MapGeo integration provides an interactive map showing trainer locations with clickable markers that open detailed trainer modals.
|
|
|
|
### Core Files
|
|
|
|
- **Integration Handler**: `includes/find-trainer/class-hvac-mapgeo-integration.php`
|
|
- **Map Configuration**: Integrated in Find A Trainer page template
|
|
|
|
### Features
|
|
|
|
#### Interactive Map
|
|
- **Map ID**: 5872 (configured in MapGeo plugin)
|
|
- **Marker Display**: Trainers with geocoded coordinates appear as markers
|
|
- **Color Coding**: Different colors based on certification type
|
|
- **Click Handlers**: Custom click actions for trainer markers
|
|
|
|
#### Performance Optimizations
|
|
- **Request Deduplication**: Prevents duplicate AJAX calls
|
|
- **Caching System**: Caches trainer data for instant subsequent access
|
|
- **Click Throttling**: Prevents rapid-fire clicking issues
|
|
- **Fallback Handling**: Graceful degradation when data unavailable
|
|
|
|
#### Modal System
|
|
- **Trainer Profiles**: Complete trainer information in popup modals
|
|
- **Champion Filtering**: Champions don't show modals (directory-only display)
|
|
- **Contact Integration**: Direct contact forms within modals
|
|
- **Responsive Design**: Mobile-optimized modal display
|
|
|
|
### Technical Implementation
|
|
|
|
#### MapGeo Marker Configuration
|
|
|
|
```php
|
|
public function modify_map_layout($meta, $map_id = null) {
|
|
// Only process for our specific map (5872)
|
|
if ($map_id && $map_id != $this->map_id) {
|
|
return $meta;
|
|
}
|
|
|
|
// Configure markers with trainer profile data
|
|
foreach ($meta['markers'] as &$marker) {
|
|
$trainer_profile_id = $this->find_trainer_profile_by_name($marker['title']);
|
|
if ($trainer_profile_id) {
|
|
$marker['action'] = 'hvac_show_trainer_modal';
|
|
$marker['hvac_profile_id'] = $trainer_profile_id;
|
|
$marker['id'] = 'trainer_' . $trainer_profile_id;
|
|
}
|
|
}
|
|
|
|
return $meta;
|
|
}
|
|
```
|
|
|
|
#### JavaScript Modal Handler
|
|
|
|
```javascript
|
|
window.hvac_show_trainer_modal = function(markerData) {
|
|
var profileId = markerData.hvac_profile_id;
|
|
|
|
// Check cache first for immediate response
|
|
if (window.hvacTrainerDataCache[profileId]) {
|
|
var cachedData = window.hvacTrainerDataCache[profileId];
|
|
if (typeof window.showTrainerModal === 'function') {
|
|
window.showTrainerModal(cachedData);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Fetch profile data via AJAX if not cached
|
|
// Implementation handles caching, error handling, and modal display
|
|
};
|
|
```
|
|
|
|
### Certification Color Coding
|
|
|
|
| Certification Type | Color | Usage |
|
|
|--------------------|-------|-------|
|
|
| Certified measureQuick Trainer | `#5077bb` | Primary trainer markers |
|
|
| Certified measureQuick Champion | `#f19a42` | Champion markers (no modals) |
|
|
| Default/Other | `#f0f7e8` | Fallback color |
|
|
|
|
## User Roles & Permissions
|
|
|
|
### HVAC Trainer (`hvac_trainer`)
|
|
|
|
#### Core Capabilities
|
|
- `read` - Basic WordPress read access
|
|
- `upload_files` - File upload capability
|
|
- `edit_hvac_profile` - Edit own trainer profile
|
|
- `view_hvac_dashboard` - Access trainer dashboard
|
|
- `manage_hvac_events` - Manage own events
|
|
- `manage_attendees` - Manage event attendees
|
|
- `email_attendees` - Send emails to attendees
|
|
|
|
#### Events Calendar Integration
|
|
- `publish_tribe_events` - Create and publish events
|
|
- `edit_tribe_events` - Edit events
|
|
- `delete_tribe_events` - Delete events
|
|
- `edit_published_tribe_events` - Edit published events
|
|
- `delete_published_tribe_events` - Delete published events
|
|
- `read_private_tribe_events` - View private events
|
|
|
|
### HVAC Master Trainer (`hvac_master_trainer`)
|
|
|
|
#### Extended Capabilities
|
|
- All trainer capabilities
|
|
- Access to master dashboard with analytics
|
|
- View aggregate trainer statistics
|
|
- Enhanced reporting capabilities
|
|
|
|
### Permission Checks
|
|
|
|
```php
|
|
// Check if user can edit trainer profiles
|
|
if (current_user_can('edit_hvac_profile')) {
|
|
// Allow profile editing
|
|
}
|
|
|
|
// Check for master trainer capabilities
|
|
if (current_user_can('hvac_master_trainer')) {
|
|
// Show master trainer features
|
|
}
|
|
```
|
|
|
|
## API Reference
|
|
|
|
### AJAX Handlers
|
|
|
|
#### Get Trainer Profile
|
|
**Endpoint**: `hvac_get_trainer_profile`
|
|
**Method**: POST
|
|
**Parameters**:
|
|
- `profile_id` (integer) - Trainer profile ID
|
|
- `nonce` (string) - Security nonce
|
|
|
|
**Response**:
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"profile_id": 5840,
|
|
"user_id": "31",
|
|
"name": "Jeremy Begley",
|
|
"city": "Knoxville",
|
|
"state": "Tennessee",
|
|
"certification_type": "Certified measureQuick Trainer",
|
|
"profile_image": "https://...",
|
|
"business_type": "Independent Contractor",
|
|
"event_count": 5,
|
|
"training_formats": "In-Person, Virtual",
|
|
"training_locations": "On-site, Remote",
|
|
"upcoming_events": []
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Filter Trainers
|
|
**Endpoint**: `hvac_filter_trainers`
|
|
**Method**: POST
|
|
**Parameters**:
|
|
- `filters` (array) - Filter criteria
|
|
- `page` (integer) - Page number
|
|
- `per_page` (integer) - Results per page
|
|
- `nonce` (string) - Security nonce
|
|
|
|
**Response**:
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": {
|
|
"html": "<div class='hvac-trainer-card'>...</div>",
|
|
"pagination": "<div class='hvac-pagination'>...</div>",
|
|
"count": 25,
|
|
"page": 1,
|
|
"max_pages": 3
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Search Trainers
|
|
**Endpoint**: `hvac_search_trainers`
|
|
**Method**: POST
|
|
**Parameters**:
|
|
- `search` (string) - Search query
|
|
- `page` (integer) - Page number
|
|
- `per_page` (integer) - Results per page
|
|
- `nonce` (string) - Security nonce
|
|
|
|
**Response**: Same format as filter trainers
|
|
|
|
### PHP API Methods
|
|
|
|
#### HVAC_Trainer_Profile_Manager Methods
|
|
|
|
```php
|
|
// Get instance
|
|
$manager = HVAC_Trainer_Profile_Manager::get_instance();
|
|
|
|
// Create profile
|
|
$profile_id = $manager->create_profile($data);
|
|
|
|
// Update profile
|
|
$manager->update_profile($profile_id, $data);
|
|
|
|
// Get profile
|
|
$profile = $manager->get_profile($profile_id);
|
|
|
|
// Get public profiles
|
|
$profiles = $manager->get_public_profiles();
|
|
|
|
// Delete profile
|
|
$manager->delete_profile($profile_id);
|
|
```
|
|
|
|
#### HVAC_Find_Trainer_Page Methods
|
|
|
|
```php
|
|
// Get instance
|
|
$page = HVAC_Find_Trainer_Page::get_instance();
|
|
|
|
// Render trainer cards
|
|
$page->render_trainer_cards($trainers);
|
|
|
|
// Get filter options
|
|
$options = $page->get_filter_options();
|
|
|
|
// Build query args
|
|
$args = $page->build_query_args($filters);
|
|
```
|
|
|
|
## Frontend Components
|
|
|
|
### CSS Architecture
|
|
|
|
#### Find A Trainer Styles (`assets/css/find-trainer.css`)
|
|
|
|
```css
|
|
/* Main container */
|
|
.hvac-find-trainer-container {
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
padding: 20px;
|
|
}
|
|
|
|
/* Search and filters */
|
|
.hvac-search-filters {
|
|
display: flex;
|
|
gap: 20px;
|
|
margin-bottom: 30px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
/* Trainer cards */
|
|
.hvac-trainer-cards {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
|
gap: 20px;
|
|
margin-bottom: 30px;
|
|
}
|
|
|
|
.hvac-trainer-card {
|
|
border: 1px solid #ddd;
|
|
border-radius: 8px;
|
|
padding: 20px;
|
|
background: white;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
transition: transform 0.2s, box-shadow 0.2s;
|
|
}
|
|
|
|
.hvac-trainer-card:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 8px rgba(0,0,0,0.15);
|
|
}
|
|
|
|
/* Certified trainer styling */
|
|
.hvac-trainer-card-certified {
|
|
border-color: #5077bb;
|
|
}
|
|
|
|
.hvac-trainer-card-certified::before {
|
|
content: "✓ Certified Trainer";
|
|
background: #5077bb;
|
|
color: white;
|
|
padding: 4px 8px;
|
|
border-radius: 4px;
|
|
font-size: 12px;
|
|
position: absolute;
|
|
top: -8px;
|
|
right: 10px;
|
|
}
|
|
|
|
/* Champion styling */
|
|
.hvac-champion-card {
|
|
border-color: #f19a42;
|
|
background: linear-gradient(135deg, #fff 0%, #fef9f5 100%);
|
|
}
|
|
|
|
.hvac-champion-card .hvac-trainer-name {
|
|
color: #f19a42;
|
|
font-weight: bold;
|
|
}
|
|
```
|
|
|
|
#### MapGeo Integration Styles
|
|
|
|
```css
|
|
/* MapGeo modal styling */
|
|
.hvac-trainer-modal {
|
|
position: fixed;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
background: white;
|
|
border-radius: 8px;
|
|
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
|
|
max-width: 500px;
|
|
width: 90%;
|
|
max-height: 80vh;
|
|
overflow-y: auto;
|
|
z-index: 10000;
|
|
}
|
|
|
|
.hvac-modal-overlay {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: rgba(0,0,0,0.5);
|
|
z-index: 9999;
|
|
}
|
|
|
|
/* Map markers */
|
|
.hvac-trainer-marker {
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.hvac-trainer-marker:hover {
|
|
transform: scale(1.1);
|
|
}
|
|
```
|
|
|
|
### JavaScript Components
|
|
|
|
#### Find A Trainer JavaScript (`assets/js/find-trainer.js`)
|
|
|
|
```javascript
|
|
var HVAC_FindTrainer = {
|
|
// Configuration
|
|
config: {
|
|
ajax_url: '',
|
|
nonce: '',
|
|
filters: {},
|
|
current_page: 1,
|
|
per_page: 12
|
|
},
|
|
|
|
// Initialize
|
|
init: function(options) {
|
|
this.config = Object.assign(this.config, options);
|
|
this.bindEvents();
|
|
this.loadInitialResults();
|
|
},
|
|
|
|
// Event bindings
|
|
bindEvents: function() {
|
|
// Search functionality
|
|
jQuery('#hvac-trainer-search').on('input', this.handleSearch.bind(this));
|
|
|
|
// Filter changes
|
|
jQuery('.hvac-filter-select').on('change', this.handleFilterChange.bind(this));
|
|
|
|
// Pagination
|
|
jQuery(document).on('click', '.hvac-page-link', this.handlePagination.bind(this));
|
|
|
|
// Trainer card clicks
|
|
jQuery(document).on('click', '.hvac-open-profile', this.openTrainerModal.bind(this));
|
|
},
|
|
|
|
// Handle search input
|
|
handleSearch: function(e) {
|
|
var query = jQuery(e.target).val();
|
|
this.performSearch(query);
|
|
},
|
|
|
|
// Handle filter changes
|
|
handleFilterChange: function(e) {
|
|
var $filter = jQuery(e.target);
|
|
var filterType = $filter.data('filter');
|
|
var value = $filter.val();
|
|
|
|
this.config.filters[filterType] = value;
|
|
this.applyFilters();
|
|
},
|
|
|
|
// Perform AJAX search
|
|
performSearch: function(query) {
|
|
jQuery.ajax({
|
|
url: this.config.ajax_url,
|
|
method: 'POST',
|
|
data: {
|
|
action: 'hvac_search_trainers',
|
|
search: query,
|
|
page: 1,
|
|
per_page: this.config.per_page,
|
|
nonce: this.config.nonce
|
|
},
|
|
success: this.updateResults.bind(this)
|
|
});
|
|
},
|
|
|
|
// Apply filters
|
|
applyFilters: function() {
|
|
jQuery.ajax({
|
|
url: this.config.ajax_url,
|
|
method: 'POST',
|
|
data: {
|
|
action: 'hvac_filter_trainers',
|
|
filters: this.config.filters,
|
|
page: 1,
|
|
per_page: this.config.per_page,
|
|
nonce: this.config.nonce
|
|
},
|
|
success: this.updateResults.bind(this)
|
|
});
|
|
},
|
|
|
|
// Update results display
|
|
updateResults: function(response) {
|
|
if (response.success) {
|
|
jQuery('.hvac-trainer-cards').html(response.data.html);
|
|
jQuery('.hvac-pagination').html(response.data.pagination);
|
|
this.updateResultsCount(response.data.count);
|
|
}
|
|
},
|
|
|
|
// Open trainer modal
|
|
openTrainerModal: function(e) {
|
|
e.preventDefault();
|
|
var profileId = jQuery(e.target).data('profile-id');
|
|
this.showTrainerProfile(profileId);
|
|
}
|
|
};
|
|
```
|
|
|
|
## Database Schema
|
|
|
|
### Trainer Profile Posts Table
|
|
```sql
|
|
-- wp_posts table entries for trainer_profile post type
|
|
SELECT
|
|
ID,
|
|
post_title,
|
|
post_status,
|
|
post_type,
|
|
post_date
|
|
FROM wp_posts
|
|
WHERE post_type = 'trainer_profile';
|
|
```
|
|
|
|
### Trainer Profile Meta Data
|
|
```sql
|
|
-- wp_postmeta table entries for trainer profiles
|
|
SELECT
|
|
post_id,
|
|
meta_key,
|
|
meta_value
|
|
FROM wp_postmeta
|
|
WHERE post_id IN (
|
|
SELECT ID FROM wp_posts WHERE post_type = 'trainer_profile'
|
|
)
|
|
ORDER BY post_id, meta_key;
|
|
```
|
|
|
|
### Taxonomy Relationships
|
|
```sql
|
|
-- Get trainer profiles with business type taxonomy
|
|
SELECT
|
|
p.ID,
|
|
p.post_title,
|
|
t.name as business_type
|
|
FROM wp_posts p
|
|
LEFT JOIN wp_term_relationships tr ON p.ID = tr.object_id
|
|
LEFT JOIN wp_term_taxonomy tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
|
|
LEFT JOIN wp_terms t ON tt.term_id = t.term_id
|
|
WHERE p.post_type = 'trainer_profile'
|
|
AND tt.taxonomy = 'business_type';
|
|
```
|
|
|
|
### Key Database Queries
|
|
|
|
#### Get Public Trainer Profiles
|
|
```php
|
|
$args = [
|
|
'post_type' => 'trainer_profile',
|
|
'post_status' => 'publish',
|
|
'posts_per_page' => -1,
|
|
'meta_query' => [
|
|
'relation' => 'AND',
|
|
[
|
|
'key' => 'is_public_profile',
|
|
'value' => '1',
|
|
'compare' => '='
|
|
],
|
|
[
|
|
'key' => 'user_id',
|
|
'value' => $approved_user_ids,
|
|
'compare' => 'IN'
|
|
]
|
|
]
|
|
];
|
|
$query = new WP_Query($args);
|
|
```
|
|
|
|
#### Get Geocoded Trainers for Map
|
|
```php
|
|
$args = [
|
|
'post_type' => 'trainer_profile',
|
|
'post_status' => 'publish',
|
|
'posts_per_page' => -1,
|
|
'meta_query' => [
|
|
'relation' => 'AND',
|
|
[
|
|
'key' => 'is_public_profile',
|
|
'value' => '1',
|
|
'compare' => '='
|
|
],
|
|
[
|
|
'key' => 'geocoded_lat',
|
|
'compare' => 'EXISTS'
|
|
],
|
|
[
|
|
'key' => 'geocoded_lng',
|
|
'compare' => 'EXISTS'
|
|
]
|
|
]
|
|
];
|
|
```
|
|
|
|
## Performance Optimizations
|
|
|
|
### MapGeo Integration Optimizations
|
|
|
|
#### Request Deduplication
|
|
```javascript
|
|
// Prevent duplicate AJAX requests
|
|
window.hvacTrainerDataCache = {};
|
|
window.hvacPendingRequests = {};
|
|
|
|
function getTrainerProfile(profileId) {
|
|
// Check cache first
|
|
if (window.hvacTrainerDataCache[profileId]) {
|
|
return Promise.resolve(window.hvacTrainerDataCache[profileId]);
|
|
}
|
|
|
|
// Check if request already pending
|
|
if (window.hvacPendingRequests[profileId]) {
|
|
return window.hvacPendingRequests[profileId];
|
|
}
|
|
|
|
// Make new request and cache promise
|
|
window.hvacPendingRequests[profileId] = makeAjaxRequest(profileId)
|
|
.then(function(data) {
|
|
window.hvacTrainerDataCache[profileId] = data;
|
|
delete window.hvacPendingRequests[profileId];
|
|
return data;
|
|
});
|
|
|
|
return window.hvacPendingRequests[profileId];
|
|
}
|
|
```
|
|
|
|
#### Click Throttling
|
|
```javascript
|
|
// Prevent rapid-fire clicking
|
|
var lastClickTime = 0;
|
|
function handleMarkerClick(e) {
|
|
var now = Date.now();
|
|
if (now - lastClickTime < 500) {
|
|
return; // Throttle clicks to 500ms intervals
|
|
}
|
|
lastClickTime = now;
|
|
|
|
// Process click
|
|
processMarkerClick(e);
|
|
}
|
|
```
|
|
|
|
### Database Query Optimization
|
|
|
|
#### Efficient Trainer Queries
|
|
```php
|
|
// Use meta_query for better performance with indexes
|
|
$args = [
|
|
'post_type' => 'trainer_profile',
|
|
'posts_per_page' => 12,
|
|
'paged' => $page,
|
|
'meta_query' => [
|
|
'relation' => 'AND',
|
|
[
|
|
'key' => 'is_public_profile',
|
|
'value' => '1',
|
|
'compare' => '='
|
|
]
|
|
],
|
|
'fields' => 'ids' // Only get IDs when possible
|
|
];
|
|
|
|
// Add user status filter efficiently
|
|
$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)) {
|
|
$args['meta_query'][] = [
|
|
'key' => 'user_id',
|
|
'value' => $approved_user_ids,
|
|
'compare' => 'IN'
|
|
];
|
|
}
|
|
```
|
|
|
|
#### Caching Strategies
|
|
```php
|
|
// Cache expensive queries
|
|
$cache_key = 'hvac_public_trainers_' . md5(serialize($args));
|
|
$trainers = wp_cache_get($cache_key, 'hvac_trainers');
|
|
|
|
if (false === $trainers) {
|
|
$query = new WP_Query($args);
|
|
$trainers = $query->posts;
|
|
wp_cache_set($cache_key, $trainers, 'hvac_trainers', HOUR_IN_SECONDS);
|
|
}
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Common Issues
|
|
|
|
#### Trainers Not Appearing on Map
|
|
**Symptoms**: Trainers visible in directory but not on MapGeo map
|
|
**Causes**:
|
|
1. Missing geocoding data (latitude/longitude)
|
|
2. MapGeo configuration issues
|
|
3. Profile not public
|
|
|
|
**Solutions**:
|
|
```php
|
|
// Check geocoding status
|
|
$lat = get_post_meta($profile_id, 'geocoded_lat', true);
|
|
$lng = get_post_meta($profile_id, 'geocoded_lng', true);
|
|
|
|
if (empty($lat) || empty($lng)) {
|
|
// Trigger geocoding
|
|
$geocoding_service = HVAC_Geocoding_Service::get_instance();
|
|
$geocoding_service->geocode_trainer($profile_id);
|
|
}
|
|
|
|
// Check public status
|
|
$is_public = get_post_meta($profile_id, 'is_public_profile', true);
|
|
if ($is_public !== '1') {
|
|
update_post_meta($profile_id, 'is_public_profile', '1');
|
|
}
|
|
```
|
|
|
|
#### Modal Not Opening
|
|
**Symptoms**: Clicking map markers doesn't open trainer modal
|
|
**Causes**:
|
|
1. JavaScript errors preventing modal system
|
|
2. Missing trainer data
|
|
3. Champion profiles (intentionally no modal)
|
|
|
|
**Solutions**:
|
|
```javascript
|
|
// Debug modal system
|
|
console.log('MapGeo integration loaded:', typeof window.hvac_show_trainer_modal);
|
|
console.log('Modal function available:', typeof window.showTrainerModal);
|
|
|
|
// Check if trainer is Champion (no modal should show)
|
|
if (trainerData.certification_type === 'Certified measureQuick Champion') {
|
|
console.log('Champion detected - no modal shown');
|
|
return;
|
|
}
|
|
```
|
|
|
|
#### Search/Filter Not Working
|
|
**Symptoms**: Search and filters not returning results
|
|
**Causes**:
|
|
1. AJAX endpoint errors
|
|
2. Nonce verification failures
|
|
3. Database query issues
|
|
|
|
**Solutions**:
|
|
```php
|
|
// Debug AJAX handlers
|
|
add_action('wp_ajax_hvac_filter_trainers', function() {
|
|
error_log('Filter trainers AJAX called');
|
|
error_log('POST data: ' . print_r($_POST, true));
|
|
|
|
// Verify nonce
|
|
if (!wp_verify_nonce($_POST['nonce'], 'hvac_find_trainer')) {
|
|
error_log('Nonce verification failed');
|
|
wp_send_json_error('Invalid nonce');
|
|
return;
|
|
}
|
|
|
|
// Continue with handler...
|
|
});
|
|
```
|
|
|
|
#### Performance Issues
|
|
**Symptoms**: Slow loading times, multiple AJAX requests
|
|
**Causes**:
|
|
1. Duplicate requests not prevented
|
|
2. Missing caching
|
|
3. Inefficient database queries
|
|
|
|
**Solutions**:
|
|
1. Implement request deduplication
|
|
2. Add caching layers
|
|
3. Optimize database queries with proper indexes
|
|
4. Use pagination for large datasets
|
|
|
|
### Debug Mode
|
|
|
|
Enable debug logging for troubleshooting:
|
|
|
|
```php
|
|
// Add to wp-config.php
|
|
define('HVAC_DEBUG', true);
|
|
|
|
// In plugin code
|
|
if (defined('HVAC_DEBUG') && HVAC_DEBUG) {
|
|
error_log('HVAC Debug: ' . $message);
|
|
}
|
|
```
|
|
|
|
### Performance Monitoring
|
|
|
|
Monitor system performance:
|
|
|
|
```javascript
|
|
// Track AJAX request timing
|
|
var startTime = performance.now();
|
|
jQuery.ajax({
|
|
// ... ajax config
|
|
success: function(response) {
|
|
var endTime = performance.now();
|
|
console.log('AJAX request took:', (endTime - startTime), 'milliseconds');
|
|
}
|
|
});
|
|
```
|
|
|
|
## Conclusion
|
|
|
|
The HVAC Trainer System provides a comprehensive solution for managing trainer profiles and presenting them through an interactive Find A Trainer directory. The system is designed for performance, scalability, and user experience, with extensive customization options and robust error handling.
|
|
|
|
For additional support or feature requests, refer to the main plugin documentation or contact the development team. |