feat(find-training): measureQuick Approved Training Labs implementation
Add venue taxonomies and filter /find-training to show only approved labs: - Create venue_type, venue_equipment, venue_amenities taxonomies - Filter venue markers by mq-approved-lab taxonomy term - Add equipment and amenities badges to venue modal - Add venue contact form with AJAX handler and email notification - Include POC (Point of Contact) meta for each training lab 9 approved training labs configured: - Fast Track Learning Lab, Progressive Training Lab, NAVAC Technical Training Center - Stevens Equipment Phoenix/Johnstown, San Jacinto College, Johnstone Supply - TruTech Tools Training Center (new), Auer Steel & Heating Supply (new) Note: Venues not displaying on map - to be debugged next session. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
19147d978e
commit
5c15b27935
8 changed files with 1859 additions and 518 deletions
81
Status.md
81
Status.md
|
|
@ -1,12 +1,87 @@
|
|||
# HVAC Community Events - Project Status
|
||||
|
||||
**Last Updated:** February 1, 2026
|
||||
**Current Session:** Find Training Page Enhancements - Complete
|
||||
**Version:** 2.2.4 (Deployed to Production)
|
||||
**Current Session:** measureQuick Approved Training Labs Implementation
|
||||
**Version:** 2.3.0 (Deployed to Staging)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 CURRENT SESSION - FIND TRAINING PAGE ENHANCEMENTS (Feb 1, 2026)
|
||||
## 🎯 CURRENT SESSION - MEASUREQUICK APPROVED TRAINING LABS (Feb 1, 2026)
|
||||
|
||||
### Status: 🔄 **IN PROGRESS - Deployed to Staging, Venues Not Displaying**
|
||||
|
||||
**Objective:** Transform /find-training to showcase only measureQuick Approved Training Labs with venue categories, equipment/amenities tags, and contact forms.
|
||||
|
||||
### Changes Made
|
||||
|
||||
1. ✅ **Venue Taxonomies Created** (`includes/class-hvac-venue-categories.php` NEW)
|
||||
- `venue_type` - For lab classification (e.g., "measureQuick Approved Training Lab")
|
||||
- `venue_equipment` - Furnace, Heat Pump, AC, Mini-Split, Boiler, etc.
|
||||
- `venue_amenities` - Coffee, Water, Projector, WiFi, Parking, etc.
|
||||
- All taxonomies registered on `tribe_venue` post type
|
||||
|
||||
2. ✅ **9 Approved Training Labs Configured** (via WP-CLI script)
|
||||
- Fast Track Learning Lab (ID: 6631) - Joe Medosch
|
||||
- Progressive Training Lab (ID: 6284) - Samantha Brazie
|
||||
- NAVAC Technical Training Center (ID: 6476) - Andrew Greaves
|
||||
- Stevens Equipment Supply - Phoenix (ID: 6448) - Robert Cone
|
||||
- San Jacinto College South Campus (ID: 6521) - Terry McWilliams
|
||||
- Johnstone Supply - Live Fire Training Lab (ID: 4864) - Dave Petz
|
||||
- Stevens Equipment Supply - Johnstown (ID: 1648) - Phil Sweren
|
||||
- TruTech Tools Training Center (NEW) - Val Buckles
|
||||
- Auer Steel & Heating Supply (NEW) - Mike Breen
|
||||
|
||||
3. ✅ **Map Data Filtered by Taxonomy**
|
||||
- `get_venue_markers()` now includes `tax_query` for `mq-approved-lab` term
|
||||
- Only approved training labs appear as venue markers
|
||||
- Equipment, amenities, and POC data included in venue info
|
||||
|
||||
4. ✅ **Venue Modal Enhanced**
|
||||
- Equipment badges (teal outline chips)
|
||||
- Amenities badges (gray outline chips)
|
||||
- Contact form with name, email, phone, company, message
|
||||
- POC receives email notification on submission
|
||||
|
||||
5. ✅ **6 POC Trainer Accounts Created**
|
||||
- Samantha Brazie, Andrew Greaves, Terry McWilliams
|
||||
- Phil Sweren, Robert Cone, Dave Petz
|
||||
- All with `hvac_trainer` role and approved status
|
||||
|
||||
### Files Created
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `includes/class-hvac-venue-categories.php` | Venue taxonomy registration (singleton) |
|
||||
| `scripts/setup-approved-labs.php` | WP-CLI script to configure all labs |
|
||||
|
||||
### Files Modified
|
||||
|
||||
| File | Change |
|
||||
|------|--------|
|
||||
| `includes/class-hvac-plugin.php` | Load venue categories class |
|
||||
| `includes/find-training/class-hvac-training-map-data.php` | Add tax_query filter, include equipment/amenities |
|
||||
| `includes/class-hvac-ajax-handlers.php` | Add venue contact form AJAX handler |
|
||||
| `templates/page-find-training.php` | Add equipment/amenities badges, contact form |
|
||||
| `assets/js/find-training-map.js` | Render badges, bind contact form handler |
|
||||
| `assets/css/find-training-map.css` | Equipment/amenities badge styles |
|
||||
|
||||
### Known Issue: Venues Not Displaying on Map
|
||||
|
||||
**Symptom:** Map at `/find-training` shows no venue markers despite data being correctly configured.
|
||||
|
||||
**Verified:**
|
||||
- ✅ 9 venues have `mq-approved-lab` taxonomy term assigned
|
||||
- ✅ API endpoint returns 9 approved labs correctly
|
||||
- ✅ Equipment and amenities data populated
|
||||
|
||||
**To Debug Next Session:**
|
||||
- Check JavaScript console for marker rendering errors
|
||||
- Verify Google Maps marker creation for venues
|
||||
- Check venue toggle state (`#hvac-show-venues`)
|
||||
|
||||
---
|
||||
|
||||
## 📋 PREVIOUS SESSION - FIND TRAINING PAGE ENHANCEMENTS (Feb 1, 2026)
|
||||
|
||||
### Status: ✅ **COMPLETE - Deployed to Production**
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -607,17 +607,68 @@
|
|||
* Populate venue modal with data
|
||||
*/
|
||||
populateVenueModal: function(venue) {
|
||||
const self = this;
|
||||
const $modal = $('#hvac-venue-modal');
|
||||
|
||||
// Title
|
||||
$modal.find('#venue-modal-title').text(venue.name);
|
||||
|
||||
// Address
|
||||
const addressParts = [venue.address, venue.city, venue.state].filter(Boolean);
|
||||
const addressParts = [venue.address, venue.city, venue.state, venue.zip].filter(Boolean);
|
||||
$modal.find('.hvac-venue-address').text(addressParts.join(', '));
|
||||
|
||||
// Phone
|
||||
if (venue.phone) {
|
||||
$modal.find('.hvac-venue-phone').html('<strong>Phone:</strong> ' + this.escapeHtml(venue.phone)).show();
|
||||
} else {
|
||||
$modal.find('.hvac-venue-phone').hide();
|
||||
}
|
||||
|
||||
// Capacity
|
||||
if (venue.capacity) {
|
||||
$modal.find('.hvac-venue-capacity').html('<strong>Capacity:</strong> ' + this.escapeHtml(venue.capacity)).show();
|
||||
} else {
|
||||
$modal.find('.hvac-venue-capacity').hide();
|
||||
}
|
||||
|
||||
// Description
|
||||
if (venue.description) {
|
||||
$modal.find('.hvac-venue-description').html(venue.description).show();
|
||||
} else {
|
||||
$modal.find('.hvac-venue-description').hide();
|
||||
}
|
||||
|
||||
// Equipment badges
|
||||
const $equipmentSection = $modal.find('.hvac-venue-equipment');
|
||||
const $equipmentBadges = $modal.find('.hvac-equipment-badges');
|
||||
$equipmentBadges.empty();
|
||||
|
||||
if (venue.equipment && venue.equipment.length > 0) {
|
||||
venue.equipment.forEach(item => {
|
||||
$equipmentBadges.append(`<span class="hvac-badge hvac-badge-equipment">${this.escapeHtml(item)}</span>`);
|
||||
});
|
||||
$equipmentSection.show();
|
||||
} else {
|
||||
$equipmentSection.hide();
|
||||
}
|
||||
|
||||
// Amenities badges
|
||||
const $amenitiesSection = $modal.find('.hvac-venue-amenities');
|
||||
const $amenitiesBadges = $modal.find('.hvac-amenities-badges');
|
||||
$amenitiesBadges.empty();
|
||||
|
||||
if (venue.amenities && venue.amenities.length > 0) {
|
||||
venue.amenities.forEach(item => {
|
||||
$amenitiesBadges.append(`<span class="hvac-badge hvac-badge-amenity">${this.escapeHtml(item)}</span>`);
|
||||
});
|
||||
$amenitiesSection.show();
|
||||
} else {
|
||||
$amenitiesSection.hide();
|
||||
}
|
||||
|
||||
// Events list
|
||||
const $eventsList = $modal.find('.hvac-venue-events-list');
|
||||
const $noEvents = $modal.find('.hvac-venue-no-events');
|
||||
$eventsList.empty();
|
||||
|
||||
if (venue.events && venue.events.length > 0) {
|
||||
|
|
@ -629,13 +680,87 @@
|
|||
</li>
|
||||
`);
|
||||
});
|
||||
$eventsList.show();
|
||||
$noEvents.hide();
|
||||
} else {
|
||||
$eventsList.html('<li>No upcoming events at this venue.</li>');
|
||||
$eventsList.hide();
|
||||
$noEvents.show();
|
||||
}
|
||||
|
||||
// Directions link
|
||||
const directionsUrl = `https://www.google.com/maps/dir/?api=1&destination=${venue.lat},${venue.lng}`;
|
||||
$modal.find('.hvac-venue-directions').attr('href', directionsUrl);
|
||||
|
||||
// Set up contact form
|
||||
const $form = $modal.find('.hvac-venue-contact-form');
|
||||
$form.data('venue-id', venue.id);
|
||||
$form.attr('data-venue-id', venue.id);
|
||||
$form.show();
|
||||
$modal.find('.hvac-form-success').hide();
|
||||
$modal.find('.hvac-form-error').hide();
|
||||
$form[0].reset();
|
||||
|
||||
// Bind contact form submission
|
||||
this.bindVenueContactForm();
|
||||
},
|
||||
|
||||
/**
|
||||
* Bind venue contact form submission
|
||||
*/
|
||||
bindVenueContactForm: function() {
|
||||
const self = this;
|
||||
|
||||
$('.hvac-venue-contact-form').off('submit').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
self.submitVenueContactForm($(this));
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Submit venue contact form
|
||||
*/
|
||||
submitVenueContactForm: function($form) {
|
||||
const venueId = $form.data('venue-id');
|
||||
const $successMsg = $form.siblings('.hvac-form-success');
|
||||
const $errorMsg = $form.siblings('.hvac-form-error');
|
||||
const $submit = $form.find('button[type="submit"]');
|
||||
|
||||
// Collect form data
|
||||
const formData = {
|
||||
action: 'hvac_submit_venue_contact',
|
||||
nonce: hvacFindTraining.nonce,
|
||||
venue_id: venueId
|
||||
};
|
||||
|
||||
$form.serializeArray().forEach(field => {
|
||||
formData[field.name] = field.value;
|
||||
});
|
||||
|
||||
// Submit
|
||||
$submit.prop('disabled', true).text('Sending...');
|
||||
$successMsg.hide();
|
||||
$errorMsg.hide();
|
||||
|
||||
$.ajax({
|
||||
url: hvacFindTraining.ajax_url,
|
||||
type: 'POST',
|
||||
data: formData,
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
$form.hide();
|
||||
$successMsg.show();
|
||||
} else {
|
||||
$errorMsg.find('p').text(response.data?.message || 'There was a problem sending your message.');
|
||||
$errorMsg.show();
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
$errorMsg.show();
|
||||
},
|
||||
complete: function() {
|
||||
$submit.prop('disabled', false).text('Send Message');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -69,6 +69,10 @@ class HVAC_Ajax_Handlers {
|
|||
// Contact trainer form (Find Training page)
|
||||
add_action('wp_ajax_hvac_submit_contact_form', array($this, 'submit_trainer_contact_form'));
|
||||
add_action('wp_ajax_nopriv_hvac_submit_contact_form', array($this, 'submit_trainer_contact_form'));
|
||||
|
||||
// Contact venue form (Find Training page - Approved Labs)
|
||||
add_action('wp_ajax_hvac_submit_venue_contact', array($this, 'submit_venue_contact_form'));
|
||||
add_action('wp_ajax_nopriv_hvac_submit_venue_contact', array($this, 'submit_venue_contact_form'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1185,6 +1189,177 @@ class HVAC_Ajax_Handlers {
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle venue contact form submission from Find Training page
|
||||
*
|
||||
* Sends an email to the venue POC (Point of Contact) with the visitor's inquiry.
|
||||
* Available to both logged-in and anonymous users.
|
||||
*/
|
||||
public function submit_venue_contact_form() {
|
||||
// Verify nonce
|
||||
if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'hvac_find_training')) {
|
||||
wp_send_json_error(['message' => 'Invalid security token'], 403);
|
||||
return;
|
||||
}
|
||||
|
||||
// Rate limiting - max 5 submissions per IP per hour
|
||||
$ip = $this->get_client_ip();
|
||||
$rate_key = 'hvac_venue_contact_rate_' . md5($ip);
|
||||
$submissions = get_transient($rate_key) ?: 0;
|
||||
|
||||
if ($submissions >= 5) {
|
||||
wp_send_json_error(['message' => 'Too many submissions. Please try again later.'], 429);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
$required_fields = ['first_name', 'last_name', 'email', 'venue_id'];
|
||||
foreach ($required_fields as $field) {
|
||||
if (empty($_POST[$field])) {
|
||||
wp_send_json_error(['message' => "Missing required field: {$field}"], 400);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Sanitize inputs
|
||||
$first_name = sanitize_text_field($_POST['first_name']);
|
||||
$last_name = sanitize_text_field($_POST['last_name']);
|
||||
$email = sanitize_email($_POST['email']);
|
||||
$phone = sanitize_text_field($_POST['phone'] ?? '');
|
||||
$company = sanitize_text_field($_POST['company'] ?? '');
|
||||
$message = sanitize_textarea_field($_POST['message'] ?? '');
|
||||
$venue_id = absint($_POST['venue_id']);
|
||||
|
||||
// Validate email
|
||||
if (!is_email($email)) {
|
||||
wp_send_json_error(['message' => 'Invalid email address'], 400);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get venue data
|
||||
$venue = get_post($venue_id);
|
||||
if (!$venue || $venue->post_type !== 'tribe_venue') {
|
||||
wp_send_json_error(['message' => 'Venue not found'], 404);
|
||||
return;
|
||||
}
|
||||
|
||||
$venue_name = $venue->post_title;
|
||||
|
||||
// Get POC information from venue meta
|
||||
$poc_user_id = get_post_meta($venue_id, '_venue_poc_user_id', true);
|
||||
$poc_email = get_post_meta($venue_id, '_venue_poc_email', true);
|
||||
$poc_name = get_post_meta($venue_id, '_venue_poc_name', true);
|
||||
|
||||
// Fallback to post author if no POC meta
|
||||
if (empty($poc_user_id)) {
|
||||
$poc_user_id = $venue->post_author;
|
||||
}
|
||||
|
||||
if (empty($poc_email)) {
|
||||
$author = get_userdata($poc_user_id);
|
||||
if ($author) {
|
||||
$poc_email = $author->user_email;
|
||||
$poc_name = $poc_name ?: $author->display_name;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($poc_email)) {
|
||||
wp_send_json_error(['message' => 'Unable to find contact for this venue'], 500);
|
||||
return;
|
||||
}
|
||||
|
||||
// Build email content
|
||||
$subject = sprintf(
|
||||
'[Upskill HVAC] Training Lab Inquiry - %s',
|
||||
$venue_name
|
||||
);
|
||||
|
||||
$body = sprintf(
|
||||
"Hello %s,\n\n" .
|
||||
"You have received an inquiry about your training lab through the Upskill HVAC directory.\n\n" .
|
||||
"--- Training Lab ---\n" .
|
||||
"%s\n\n" .
|
||||
"--- Contact Details ---\n" .
|
||||
"Name: %s %s\n" .
|
||||
"Email: %s\n" .
|
||||
"%s" . // Phone (optional)
|
||||
"%s" . // Company (optional)
|
||||
"\n--- Message ---\n%s\n\n" .
|
||||
"---\n" .
|
||||
"This message was sent via the Find Training page at %s\n" .
|
||||
"Please respond directly to the sender's email address.\n",
|
||||
$poc_name ?: 'Training Lab Contact',
|
||||
$venue_name,
|
||||
$first_name,
|
||||
$last_name,
|
||||
$email,
|
||||
$phone ? "Phone: {$phone}\n" : '',
|
||||
$company ? "Company: {$company}\n" : '',
|
||||
$message ?: '(No message provided)',
|
||||
home_url('/find-training/')
|
||||
);
|
||||
|
||||
// Email headers
|
||||
$headers = [
|
||||
'Content-Type: text/plain; charset=UTF-8',
|
||||
sprintf('Reply-To: %s %s <%s>', $first_name, $last_name, $email),
|
||||
sprintf('From: Upskill HVAC <noreply@%s>', parse_url(home_url(), PHP_URL_HOST))
|
||||
];
|
||||
|
||||
// Send email to POC
|
||||
$sent = wp_mail($poc_email, $subject, $body, $headers);
|
||||
|
||||
if (!$sent) {
|
||||
// Log failure
|
||||
if (class_exists('HVAC_Logger')) {
|
||||
HVAC_Logger::error('Failed to send venue contact email', 'AJAX', [
|
||||
'venue_id' => $venue_id,
|
||||
'poc_email' => $poc_email,
|
||||
'sender_email' => $email
|
||||
]);
|
||||
}
|
||||
wp_send_json_error(['message' => 'Failed to send message. Please try again.'], 500);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update rate limit
|
||||
set_transient($rate_key, $submissions + 1, HOUR_IN_SECONDS);
|
||||
|
||||
// Log success
|
||||
if (class_exists('HVAC_Logger')) {
|
||||
HVAC_Logger::info('Venue contact form submitted', 'AJAX', [
|
||||
'venue_id' => $venue_id,
|
||||
'venue_name' => $venue_name,
|
||||
'poc_email' => $poc_email,
|
||||
'sender_email' => $email,
|
||||
'has_message' => !empty($message)
|
||||
]);
|
||||
}
|
||||
|
||||
// Store lead if training leads system exists
|
||||
if (class_exists('HVAC_Training_Leads')) {
|
||||
$leads = HVAC_Training_Leads::instance();
|
||||
if (method_exists($leads, 'create_lead')) {
|
||||
$leads->create_lead([
|
||||
'first_name' => $first_name,
|
||||
'last_name' => $last_name,
|
||||
'email' => $email,
|
||||
'phone' => $phone,
|
||||
'company' => $company,
|
||||
'message' => $message,
|
||||
'venue_id' => $venue_id,
|
||||
'venue_name' => $venue_name,
|
||||
'source' => 'find_training_venue_contact'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
wp_send_json_success([
|
||||
'message' => 'Your message has been sent to the training lab.',
|
||||
'venue_name' => $venue_name
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get client IP address safely
|
||||
*
|
||||
|
|
|
|||
|
|
@ -271,6 +271,9 @@ final class HVAC_Plugin {
|
|||
'find-training/class-hvac-venue-geocoding.php',
|
||||
];
|
||||
|
||||
// Venue Categories (taxonomies for training labs)
|
||||
require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-venue-categories.php';
|
||||
|
||||
// Load feature files with memory-efficient generator
|
||||
foreach ($this->loadFeatureFiles($featureFiles) as $file => $status) {
|
||||
if ($status === 'loaded') {
|
||||
|
|
@ -593,6 +596,11 @@ final class HVAC_Plugin {
|
|||
HVAC_Venues::instance();
|
||||
}
|
||||
|
||||
// Initialize venue categories (taxonomies for training labs)
|
||||
if (class_exists('HVAC_Venue_Categories')) {
|
||||
HVAC_Venue_Categories::instance();
|
||||
}
|
||||
|
||||
// Initialize trainer profile manager
|
||||
if (class_exists('HVAC_Trainer_Profile_Manager')) {
|
||||
HVAC_Trainer_Profile_Manager::get_instance();
|
||||
|
|
|
|||
282
includes/class-hvac-venue-categories.php
Normal file
282
includes/class-hvac-venue-categories.php
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
<?php
|
||||
/**
|
||||
* Venue Categories - Taxonomy Registration for Training Labs
|
||||
*
|
||||
* Registers custom taxonomies for TEC venues:
|
||||
* - venue_type: Training lab types (e.g., "measureQuick Approved Training Lab")
|
||||
* - venue_equipment: Equipment available at venues
|
||||
* - venue_amenities: Amenities available at venues
|
||||
*
|
||||
* @package HVAC_Community_Events
|
||||
* @since 2.3.0
|
||||
*/
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class HVAC_Venue_Categories
|
||||
*
|
||||
* Manages venue taxonomies for categorizing training labs.
|
||||
*/
|
||||
class HVAC_Venue_Categories {
|
||||
|
||||
/**
|
||||
* Singleton instance
|
||||
*
|
||||
* @var HVAC_Venue_Categories|null
|
||||
*/
|
||||
private static ?self $instance = null;
|
||||
|
||||
/**
|
||||
* Get singleton instance
|
||||
*
|
||||
* @return HVAC_Venue_Categories
|
||||
*/
|
||||
public static function instance(): self {
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
private function __construct() {
|
||||
add_action('init', [$this, 'register_taxonomies'], 5);
|
||||
add_action('init', [$this, 'create_default_terms'], 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register venue taxonomies
|
||||
*/
|
||||
public function register_taxonomies(): void {
|
||||
// Venue Type taxonomy (e.g., "measureQuick Approved Training Lab")
|
||||
register_taxonomy('venue_type', 'tribe_venue', [
|
||||
'labels' => [
|
||||
'name' => __('Venue Types', 'hvac-community-events'),
|
||||
'singular_name' => __('Venue Type', 'hvac-community-events'),
|
||||
'search_items' => __('Search Venue Types', 'hvac-community-events'),
|
||||
'all_items' => __('All Venue Types', 'hvac-community-events'),
|
||||
'parent_item' => __('Parent Venue Type', 'hvac-community-events'),
|
||||
'parent_item_colon' => __('Parent Venue Type:', 'hvac-community-events'),
|
||||
'edit_item' => __('Edit Venue Type', 'hvac-community-events'),
|
||||
'update_item' => __('Update Venue Type', 'hvac-community-events'),
|
||||
'add_new_item' => __('Add New Venue Type', 'hvac-community-events'),
|
||||
'new_item_name' => __('New Venue Type Name', 'hvac-community-events'),
|
||||
'menu_name' => __('Venue Types', 'hvac-community-events'),
|
||||
],
|
||||
'hierarchical' => true,
|
||||
'public' => true,
|
||||
'show_ui' => true,
|
||||
'show_in_menu' => true,
|
||||
'show_admin_column' => true,
|
||||
'show_in_rest' => true,
|
||||
'query_var' => true,
|
||||
'rewrite' => ['slug' => 'venue-type'],
|
||||
]);
|
||||
|
||||
// Venue Equipment taxonomy
|
||||
register_taxonomy('venue_equipment', 'tribe_venue', [
|
||||
'labels' => [
|
||||
'name' => __('Equipment', 'hvac-community-events'),
|
||||
'singular_name' => __('Equipment', 'hvac-community-events'),
|
||||
'search_items' => __('Search Equipment', 'hvac-community-events'),
|
||||
'all_items' => __('All Equipment', 'hvac-community-events'),
|
||||
'parent_item' => __('Parent Equipment', 'hvac-community-events'),
|
||||
'parent_item_colon' => __('Parent Equipment:', 'hvac-community-events'),
|
||||
'edit_item' => __('Edit Equipment', 'hvac-community-events'),
|
||||
'update_item' => __('Update Equipment', 'hvac-community-events'),
|
||||
'add_new_item' => __('Add New Equipment', 'hvac-community-events'),
|
||||
'new_item_name' => __('New Equipment Name', 'hvac-community-events'),
|
||||
'menu_name' => __('Equipment', 'hvac-community-events'),
|
||||
],
|
||||
'hierarchical' => true,
|
||||
'public' => true,
|
||||
'show_ui' => true,
|
||||
'show_in_menu' => true,
|
||||
'show_admin_column' => true,
|
||||
'show_in_rest' => true,
|
||||
'query_var' => true,
|
||||
'rewrite' => ['slug' => 'venue-equipment'],
|
||||
]);
|
||||
|
||||
// Venue Amenities taxonomy
|
||||
register_taxonomy('venue_amenities', 'tribe_venue', [
|
||||
'labels' => [
|
||||
'name' => __('Amenities', 'hvac-community-events'),
|
||||
'singular_name' => __('Amenity', 'hvac-community-events'),
|
||||
'search_items' => __('Search Amenities', 'hvac-community-events'),
|
||||
'all_items' => __('All Amenities', 'hvac-community-events'),
|
||||
'parent_item' => __('Parent Amenity', 'hvac-community-events'),
|
||||
'parent_item_colon' => __('Parent Amenity:', 'hvac-community-events'),
|
||||
'edit_item' => __('Edit Amenity', 'hvac-community-events'),
|
||||
'update_item' => __('Update Amenity', 'hvac-community-events'),
|
||||
'add_new_item' => __('Add New Amenity', 'hvac-community-events'),
|
||||
'new_item_name' => __('New Amenity Name', 'hvac-community-events'),
|
||||
'menu_name' => __('Amenities', 'hvac-community-events'),
|
||||
],
|
||||
'hierarchical' => true,
|
||||
'public' => true,
|
||||
'show_ui' => true,
|
||||
'show_in_menu' => true,
|
||||
'show_admin_column' => true,
|
||||
'show_in_rest' => true,
|
||||
'query_var' => true,
|
||||
'rewrite' => ['slug' => 'venue-amenities'],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create default taxonomy terms
|
||||
*/
|
||||
public function create_default_terms(): void {
|
||||
// Only run once
|
||||
if (get_option('hvac_venue_categories_initialized')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Venue Types
|
||||
$venue_types = [
|
||||
'measureQuick Approved Training Lab' => 'mq-approved-lab',
|
||||
];
|
||||
|
||||
foreach ($venue_types as $name => $slug) {
|
||||
if (!term_exists($slug, 'venue_type')) {
|
||||
wp_insert_term($name, 'venue_type', ['slug' => $slug]);
|
||||
}
|
||||
}
|
||||
|
||||
// Equipment
|
||||
$equipment = [
|
||||
'Furnace',
|
||||
'Heat Pump',
|
||||
'Air Conditioner',
|
||||
'Mini-Split',
|
||||
'Boiler',
|
||||
'Gas Meter',
|
||||
'TrueFlow Grid',
|
||||
'Flow Hood',
|
||||
];
|
||||
|
||||
foreach ($equipment as $name) {
|
||||
$slug = sanitize_title($name);
|
||||
if (!term_exists($slug, 'venue_equipment')) {
|
||||
wp_insert_term($name, 'venue_equipment', ['slug' => $slug]);
|
||||
}
|
||||
}
|
||||
|
||||
// Amenities
|
||||
$amenities = [
|
||||
'Coffee',
|
||||
'Water',
|
||||
'Tea',
|
||||
'Soda',
|
||||
'Snacks',
|
||||
'Projector',
|
||||
'Whiteboard',
|
||||
'WiFi',
|
||||
'Parking',
|
||||
'Vending Machines',
|
||||
];
|
||||
|
||||
foreach ($amenities as $name) {
|
||||
$slug = sanitize_title($name);
|
||||
if (!term_exists($slug, 'venue_amenities')) {
|
||||
wp_insert_term($name, 'venue_amenities', ['slug' => $slug]);
|
||||
}
|
||||
}
|
||||
|
||||
update_option('hvac_venue_categories_initialized', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get venues by type
|
||||
*
|
||||
* @param string $type_slug Type slug (e.g., 'mq-approved-lab')
|
||||
* @return array Venue IDs
|
||||
*/
|
||||
public function get_venues_by_type(string $type_slug): array {
|
||||
$query = new WP_Query([
|
||||
'post_type' => 'tribe_venue',
|
||||
'posts_per_page' => -1,
|
||||
'post_status' => 'publish',
|
||||
'fields' => 'ids',
|
||||
'tax_query' => [
|
||||
[
|
||||
'taxonomy' => 'venue_type',
|
||||
'field' => 'slug',
|
||||
'terms' => $type_slug,
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
return $query->posts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if venue is an approved training lab
|
||||
*
|
||||
* @param int $venue_id Venue post ID
|
||||
* @return bool
|
||||
*/
|
||||
public function is_approved_training_lab(int $venue_id): bool {
|
||||
return has_term('mq-approved-lab', 'venue_type', $venue_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get venue equipment
|
||||
*
|
||||
* @param int $venue_id Venue post ID
|
||||
* @return array Equipment names
|
||||
*/
|
||||
public function get_venue_equipment(int $venue_id): array {
|
||||
$terms = wp_get_post_terms($venue_id, 'venue_equipment', ['fields' => 'names']);
|
||||
return is_wp_error($terms) ? [] : $terms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get venue amenities
|
||||
*
|
||||
* @param int $venue_id Venue post ID
|
||||
* @return array Amenity names
|
||||
*/
|
||||
public function get_venue_amenities(int $venue_id): array {
|
||||
$terms = wp_get_post_terms($venue_id, 'venue_amenities', ['fields' => 'names']);
|
||||
return is_wp_error($terms) ? [] : $terms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set venue as approved training lab
|
||||
*
|
||||
* @param int $venue_id Venue post ID
|
||||
* @return bool|WP_Error
|
||||
*/
|
||||
public function set_as_approved_lab(int $venue_id) {
|
||||
return wp_set_post_terms($venue_id, ['mq-approved-lab'], 'venue_type', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set venue equipment
|
||||
*
|
||||
* @param int $venue_id Venue post ID
|
||||
* @param array $equipment Equipment slugs or names
|
||||
* @return bool|WP_Error
|
||||
*/
|
||||
public function set_venue_equipment(int $venue_id, array $equipment) {
|
||||
return wp_set_post_terms($venue_id, $equipment, 'venue_equipment', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set venue amenities
|
||||
*
|
||||
* @param int $venue_id Venue post ID
|
||||
* @param array $amenities Amenity slugs or names
|
||||
* @return bool|WP_Error
|
||||
*/
|
||||
public function set_venue_amenities(int $venue_id, array $amenities) {
|
||||
return wp_set_post_terms($venue_id, $amenities, 'venue_amenities', false);
|
||||
}
|
||||
}
|
||||
|
|
@ -215,6 +215,8 @@ class HVAC_Training_Map_Data {
|
|||
/**
|
||||
* Get venue markers for map
|
||||
*
|
||||
* Filters to show only measureQuick Approved Training Labs.
|
||||
*
|
||||
* @param array $filters Optional filters
|
||||
* @return array Venue markers data
|
||||
*/
|
||||
|
|
@ -225,18 +227,26 @@ class HVAC_Training_Map_Data {
|
|||
}
|
||||
|
||||
// Generate cache key
|
||||
$cache_key = 'venues_' . md5(serialize($filters));
|
||||
$cache_key = 'venues_approved_labs_' . md5(serialize($filters));
|
||||
$cached = wp_cache_get($cache_key, $this->cache_group);
|
||||
|
||||
if ($cached !== false && empty($filters)) {
|
||||
return $cached;
|
||||
}
|
||||
|
||||
// Build query args
|
||||
// Build query args - filter for approved training labs only
|
||||
$query_args = [
|
||||
'post_type' => 'tribe_venue',
|
||||
'posts_per_page' => -1,
|
||||
'post_status' => 'publish',
|
||||
// Only show venues tagged as measureQuick Approved Training Labs
|
||||
'tax_query' => [
|
||||
[
|
||||
'taxonomy' => 'venue_type',
|
||||
'field' => 'slug',
|
||||
'terms' => 'mq-approved-lab',
|
||||
],
|
||||
],
|
||||
'meta_query' => [
|
||||
'relation' => 'AND',
|
||||
[
|
||||
|
|
@ -508,6 +518,30 @@ class HVAC_Training_Map_Data {
|
|||
$data['country'] = get_post_meta($venue_id, '_VenueCountry', true);
|
||||
$data['phone'] = get_post_meta($venue_id, '_VenuePhone', true);
|
||||
$data['website'] = get_post_meta($venue_id, '_VenueURL', true);
|
||||
$data['description'] = $venue->post_content;
|
||||
$data['capacity'] = get_post_meta($venue_id, '_VenueCapacity', true);
|
||||
|
||||
// Get equipment and amenities taxonomies
|
||||
$equipment = wp_get_post_terms($venue_id, 'venue_equipment', ['fields' => 'names']);
|
||||
$data['equipment'] = is_wp_error($equipment) ? [] : $equipment;
|
||||
|
||||
$amenities = wp_get_post_terms($venue_id, 'venue_amenities', ['fields' => 'names']);
|
||||
$data['amenities'] = is_wp_error($amenities) ? [] : $amenities;
|
||||
|
||||
// Get POC (Point of Contact) information
|
||||
$data['poc_user_id'] = get_post_meta($venue_id, '_venue_poc_user_id', true);
|
||||
$data['poc_name'] = get_post_meta($venue_id, '_venue_poc_name', true);
|
||||
$data['poc_email'] = get_post_meta($venue_id, '_venue_poc_email', true);
|
||||
|
||||
// If no POC meta, use post author as fallback
|
||||
if (empty($data['poc_user_id'])) {
|
||||
$data['poc_user_id'] = $venue->post_author;
|
||||
$author = get_userdata($venue->post_author);
|
||||
if ($author) {
|
||||
$data['poc_name'] = $author->display_name;
|
||||
$data['poc_email'] = $author->user_email;
|
||||
}
|
||||
}
|
||||
|
||||
// Get upcoming events list
|
||||
if (function_exists('tribe_get_events')) {
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
/**
|
||||
* Template Name: Find Training
|
||||
*
|
||||
* Template for displaying the Find Training page with Google Maps
|
||||
* showing trainers and venues on an interactive map.
|
||||
* Google Maps-style full-screen layout with left sidebar panel
|
||||
* for trainer directory and compact filter toolbar.
|
||||
*
|
||||
* @package HVAC_Community_Events
|
||||
* @since 2.2.0
|
||||
|
|
@ -17,149 +17,187 @@ get_header();
|
|||
// Get page handler instance
|
||||
$find_training = HVAC_Find_Training_Page::get_instance();
|
||||
$filter_options = $find_training->get_filter_options();
|
||||
$api_key_configured = $find_training->is_api_key_configured();
|
||||
?>
|
||||
|
||||
<div class="hvac-find-training-page">
|
||||
<div class="ast-container">
|
||||
<!-- Skip link for accessibility -->
|
||||
<a href="#hvac-trainer-grid" class="hvac-skip-link">Skip to trainer results</a>
|
||||
|
||||
<!-- Page Title -->
|
||||
<h1 class="hvac-page-title">Find Training</h1>
|
||||
|
||||
<!-- Intro Section -->
|
||||
<div class="hvac-find-training-intro">
|
||||
<p>Upskill HVAC is proud to be the only training body offering Certified measureQuick training.</p>
|
||||
<p><strong>Certified measureQuick Trainers</strong> have demonstrated their skills and mastery of HVAC science and the measureQuick app, and are authorized to provide measureQuick training to the industry.</p>
|
||||
<p>Use the interactive map and filters below to discover trainers and training venues near you. Click on any marker to view details.</p>
|
||||
</div>
|
||||
|
||||
<!-- Map and Filters Container -->
|
||||
<div class="hvac-map-filters-wrapper">
|
||||
|
||||
<!-- Map Section -->
|
||||
<div class="hvac-map-section">
|
||||
<div id="hvac-training-map" class="hvac-google-map">
|
||||
<div class="hvac-map-loading">
|
||||
<span class="dashicons dashicons-location"></span>
|
||||
<p>Loading map...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Map Legend -->
|
||||
<div class="hvac-map-legend">
|
||||
<div class="hvac-legend-item">
|
||||
<span class="hvac-legend-marker hvac-legend-trainer"></span>
|
||||
<span>Trainer</span>
|
||||
</div>
|
||||
<div class="hvac-legend-item">
|
||||
<span class="hvac-legend-marker hvac-legend-venue"></span>
|
||||
<span>Training Venue</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Compact Filter Bar -->
|
||||
<div class="hvac-filter-bar" role="search" aria-label="Filter trainers and venues">
|
||||
<div class="hvac-filter-bar-inner">
|
||||
<!-- Search -->
|
||||
<div class="hvac-filter-item hvac-filter-search">
|
||||
<span class="dashicons dashicons-search" aria-hidden="true"></span>
|
||||
<input type="text" id="hvac-training-search" placeholder="Search trainers..." aria-label="Search trainers">
|
||||
</div>
|
||||
|
||||
<!-- Filters Section -->
|
||||
<div class="hvac-filters-section">
|
||||
<!-- Search Box -->
|
||||
<div class="hvac-search-box">
|
||||
<input type="text" id="hvac-training-search" class="hvac-search-input" placeholder="Search trainers or venues..." aria-label="Search">
|
||||
<span class="dashicons dashicons-search"></span>
|
||||
<!-- Filter Dropdowns (hidden on mobile, shown in panel) -->
|
||||
<div class="hvac-filter-dropdowns">
|
||||
<select id="hvac-filter-state" class="hvac-filter-select" aria-label="Filter by state">
|
||||
<option value="">All States</option>
|
||||
<?php foreach ($filter_options['states'] as $state): ?>
|
||||
<option value="<?php echo esc_attr($state); ?>"><?php echo esc_html($state); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
|
||||
<select id="hvac-filter-certification" class="hvac-filter-select" aria-label="Filter by certification">
|
||||
<option value="">All Certifications</option>
|
||||
<?php foreach ($filter_options['certifications'] as $cert): ?>
|
||||
<option value="<?php echo esc_attr($cert); ?>"><?php echo esc_html($cert); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
|
||||
<select id="hvac-filter-format" class="hvac-filter-select" aria-label="Filter by training format">
|
||||
<option value="">All Formats</option>
|
||||
<?php foreach ($filter_options['training_formats'] as $format): ?>
|
||||
<option value="<?php echo esc_attr($format); ?>"><?php echo esc_html($format); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Near Me Button -->
|
||||
<button type="button" id="hvac-near-me-btn" class="hvac-near-me-btn">
|
||||
<span class="dashicons dashicons-location-alt" aria-hidden="true"></span>
|
||||
<span class="hvac-btn-text">Near Me</span>
|
||||
</button>
|
||||
|
||||
<!-- Clear Filters -->
|
||||
<button type="button" class="hvac-clear-filters" style="display: none;">
|
||||
Clear
|
||||
</button>
|
||||
|
||||
<!-- Mobile Filter Toggle -->
|
||||
<button type="button"
|
||||
class="hvac-mobile-filter-toggle"
|
||||
aria-expanded="false"
|
||||
aria-controls="hvac-mobile-filter-panel">
|
||||
<span class="dashicons dashicons-filter" aria-hidden="true"></span>
|
||||
<span class="hvac-btn-text">Filters</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Active Filters Chips -->
|
||||
<div class="hvac-active-filters" aria-live="polite"></div>
|
||||
|
||||
<!-- Mobile Filter Panel (hidden by default) -->
|
||||
<div id="hvac-mobile-filter-panel" class="hvac-mobile-filter-panel" hidden>
|
||||
<div class="hvac-mobile-filter-group">
|
||||
<label for="hvac-filter-state-mobile">State / Province</label>
|
||||
<select id="hvac-filter-state-mobile" class="hvac-filter-select" aria-label="Filter by state">
|
||||
<option value="">All States</option>
|
||||
<?php foreach ($filter_options['states'] as $state): ?>
|
||||
<option value="<?php echo esc_attr($state); ?>"><?php echo esc_html($state); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="hvac-mobile-filter-group">
|
||||
<label for="hvac-filter-certification-mobile">Certification</label>
|
||||
<select id="hvac-filter-certification-mobile" class="hvac-filter-select" aria-label="Filter by certification">
|
||||
<option value="">All Certifications</option>
|
||||
<?php foreach ($filter_options['certifications'] as $cert): ?>
|
||||
<option value="<?php echo esc_attr($cert); ?>"><?php echo esc_html($cert); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="hvac-mobile-filter-group">
|
||||
<label for="hvac-filter-format-mobile">Training Format</label>
|
||||
<select id="hvac-filter-format-mobile" class="hvac-filter-select" aria-label="Filter by training format">
|
||||
<option value="">All Formats</option>
|
||||
<?php foreach ($filter_options['training_formats'] as $format): ?>
|
||||
<option value="<?php echo esc_attr($format); ?>"><?php echo esc_html($format); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Content: Sidebar + Map -->
|
||||
<div class="hvac-map-layout">
|
||||
<!-- Left Sidebar -->
|
||||
<aside class="hvac-sidebar" role="region" aria-label="Trainer directory">
|
||||
<div class="hvac-sidebar-header">
|
||||
<span class="hvac-results-summary" aria-live="polite">
|
||||
<span id="hvac-trainer-count">0</span> trainers
|
||||
</span>
|
||||
<!-- Mobile collapse toggle -->
|
||||
<button type="button"
|
||||
class="hvac-sidebar-toggle"
|
||||
aria-expanded="true"
|
||||
aria-controls="hvac-sidebar-content"
|
||||
aria-label="Toggle trainer list">
|
||||
<span class="dashicons dashicons-arrow-down-alt2" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="hvac-sidebar-content" class="hvac-sidebar-content">
|
||||
<!-- Trainer List (scrollable) -->
|
||||
<div id="hvac-trainer-grid" class="hvac-trainer-list">
|
||||
<div class="hvac-grid-loading">
|
||||
<span class="dashicons dashicons-update-alt hvac-spin" aria-hidden="true"></span>
|
||||
Loading trainers...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Near Me Button -->
|
||||
<button type="button" id="hvac-near-me-btn" class="hvac-near-me-btn">
|
||||
<span class="dashicons dashicons-location-alt"></span>
|
||||
Near Me
|
||||
</button>
|
||||
|
||||
<!-- Filters Header -->
|
||||
<div class="hvac-filters-header">
|
||||
<span class="hvac-filters-label">Filters:</span>
|
||||
<button type="button" class="hvac-clear-filters" style="display: none;">
|
||||
Clear All
|
||||
<!-- Load More Button -->
|
||||
<div class="hvac-load-more-wrapper" style="display: none;">
|
||||
<button type="button" id="hvac-load-more" class="hvac-btn-secondary">
|
||||
Load More
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- State Filter -->
|
||||
<div class="hvac-filter-group">
|
||||
<label for="hvac-filter-state">State / Province</label>
|
||||
<select id="hvac-filter-state" class="hvac-filter-select">
|
||||
<option value="">All States</option>
|
||||
<?php foreach ($filter_options['states'] as $state): ?>
|
||||
<option value="<?php echo esc_attr($state); ?>"><?php echo esc_html($state); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Certification Filter -->
|
||||
<div class="hvac-filter-group">
|
||||
<label for="hvac-filter-certification">Certification</label>
|
||||
<select id="hvac-filter-certification" class="hvac-filter-select">
|
||||
<option value="">All Certifications</option>
|
||||
<?php foreach ($filter_options['certifications'] as $cert): ?>
|
||||
<option value="<?php echo esc_attr($cert); ?>"><?php echo esc_html($cert); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Training Format Filter -->
|
||||
<div class="hvac-filter-group">
|
||||
<label for="hvac-filter-format">Training Format</label>
|
||||
<select id="hvac-filter-format" class="hvac-filter-select">
|
||||
<option value="">All Formats</option>
|
||||
<?php foreach ($filter_options['training_formats'] as $format): ?>
|
||||
<option value="<?php echo esc_attr($format); ?>"><?php echo esc_html($format); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Marker Type Toggles -->
|
||||
<div class="hvac-marker-toggles">
|
||||
<label class="hvac-toggle">
|
||||
<input type="checkbox" id="hvac-show-trainers" checked>
|
||||
<span class="hvac-toggle-slider"></span>
|
||||
<span class="hvac-toggle-label">Show Trainers</span>
|
||||
</label>
|
||||
<label class="hvac-toggle">
|
||||
<input type="checkbox" id="hvac-show-venues" checked>
|
||||
<span class="hvac-toggle-slider"></span>
|
||||
<span class="hvac-toggle-label">Show Venues</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Active Filters -->
|
||||
<div class="hvac-active-filters"></div>
|
||||
|
||||
<!-- Results Count -->
|
||||
<div class="hvac-results-count">
|
||||
<span id="hvac-trainer-count">0</span> trainers, <span id="hvac-venue-count">0</span> venues
|
||||
<!-- CTA -->
|
||||
<div class="hvac-sidebar-cta">
|
||||
<p>Want to be listed in our directory?</p>
|
||||
<a href="<?php echo esc_url(site_url('/trainer/registration/')); ?>" class="hvac-btn-primary hvac-btn-small">
|
||||
Become A Trainer
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Trainer Directory Grid -->
|
||||
<div class="hvac-trainer-directory-section">
|
||||
<h2>Trainers Directory</h2>
|
||||
<div id="hvac-trainer-grid" class="hvac-trainer-grid">
|
||||
<div class="hvac-grid-loading">
|
||||
<span class="dashicons dashicons-update-alt hvac-spin"></span>
|
||||
Loading trainers...
|
||||
<!-- Map Container -->
|
||||
<div class="hvac-map-container" role="region" aria-label="Training locations map">
|
||||
<div id="hvac-training-map" class="hvac-google-map">
|
||||
<?php if (!$api_key_configured): ?>
|
||||
<div class="hvac-map-error">
|
||||
<span class="dashicons dashicons-warning" aria-hidden="true"></span>
|
||||
<p><strong>Map Configuration Required</strong></p>
|
||||
<p>The Google Maps API key is not configured. <?php if (current_user_can('manage_options')): ?>Please configure it in <a href="<?php echo admin_url('admin.php?page=hvac-trainer-profile-settings'); ?>">Trainer Profile Settings</a>.<?php else: ?>Please contact the site administrator.<?php endif; ?></p>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="hvac-map-loading">
|
||||
<span class="dashicons dashicons-location" aria-hidden="true"></span>
|
||||
<p>Loading map...</p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- Map Legend Overlay -->
|
||||
<div class="hvac-map-legend">
|
||||
<div class="hvac-legend-item">
|
||||
<span class="hvac-legend-marker hvac-legend-trainer" aria-hidden="true"></span>
|
||||
<span>Trainer</span>
|
||||
</div>
|
||||
<div class="hvac-legend-item">
|
||||
<span class="hvac-legend-marker hvac-legend-venue" aria-hidden="true"></span>
|
||||
<span>Venue</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Load More Button -->
|
||||
<div class="hvac-load-more-wrapper" style="display: none;">
|
||||
<button type="button" id="hvac-load-more" class="hvac-btn-secondary">
|
||||
Load More
|
||||
</button>
|
||||
<!-- Map Toggles Overlay -->
|
||||
<div class="hvac-map-toggles">
|
||||
<label class="hvac-toggle-compact">
|
||||
<input type="checkbox" id="hvac-show-trainers" checked>
|
||||
<span class="hvac-toggle-label">Trainers</span>
|
||||
</label>
|
||||
<label class="hvac-toggle-compact">
|
||||
<input type="checkbox" id="hvac-show-venues" checked>
|
||||
<span class="hvac-toggle-label">Venues</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CTA Section -->
|
||||
<div class="hvac-cta-section">
|
||||
<p>Are you an HVAC Trainer that wants to be listed in our directory?</p>
|
||||
<a href="<?php echo esc_url(site_url('/trainer/registration/')); ?>" class="hvac-btn-primary">Become A Trainer</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -168,7 +206,7 @@ $filter_options = $find_training->get_filter_options();
|
|||
<div class="hvac-modal-overlay"></div>
|
||||
<div class="hvac-modal-content">
|
||||
<div class="hvac-modal-loading">
|
||||
<span class="dashicons dashicons-update-alt hvac-spin"></span>
|
||||
<span class="dashicons dashicons-update-alt hvac-spin" aria-hidden="true"></span>
|
||||
Loading...
|
||||
</div>
|
||||
<div class="hvac-modal-body"></div>
|
||||
|
|
@ -178,21 +216,88 @@ $filter_options = $find_training->get_filter_options();
|
|||
<!-- Venue Info Modal -->
|
||||
<div id="hvac-venue-modal" class="hvac-training-modal" style="display: none;" role="dialog" aria-modal="true" aria-labelledby="venue-modal-title">
|
||||
<div class="hvac-modal-overlay"></div>
|
||||
<div class="hvac-modal-content">
|
||||
<div class="hvac-modal-content hvac-venue-modal-content">
|
||||
<div class="hvac-venue-modal-header">
|
||||
<h2 id="venue-modal-title"></h2>
|
||||
<button class="hvac-modal-close" aria-label="Close modal">×</button>
|
||||
</div>
|
||||
<div class="hvac-venue-modal-body">
|
||||
<p class="hvac-venue-address"></p>
|
||||
<div class="hvac-venue-events">
|
||||
<h4>Upcoming Events at this Venue</h4>
|
||||
<ul class="hvac-venue-events-list"></ul>
|
||||
<p class="hvac-venue-phone"></p>
|
||||
<p class="hvac-venue-capacity"></p>
|
||||
|
||||
<div class="hvac-venue-description"></div>
|
||||
|
||||
<!-- Equipment Badges -->
|
||||
<div class="hvac-venue-equipment" style="display: none;">
|
||||
<h4>Equipment</h4>
|
||||
<div class="hvac-badge-list hvac-equipment-badges"></div>
|
||||
</div>
|
||||
|
||||
<!-- Amenities Badges -->
|
||||
<div class="hvac-venue-amenities" style="display: none;">
|
||||
<h4>Amenities</h4>
|
||||
<div class="hvac-badge-list hvac-amenities-badges"></div>
|
||||
</div>
|
||||
|
||||
<!-- Upcoming Events -->
|
||||
<div class="hvac-venue-events">
|
||||
<h4>Upcoming Events</h4>
|
||||
<ul class="hvac-venue-events-list"></ul>
|
||||
<p class="hvac-venue-no-events" style="display: none;">No upcoming events scheduled.</p>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="hvac-venue-actions">
|
||||
<a href="#" class="hvac-venue-directions hvac-btn-secondary" target="_blank" rel="noopener">
|
||||
<span class="dashicons dashicons-location" aria-hidden="true"></span>
|
||||
Get Directions
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Contact Form -->
|
||||
<div class="hvac-venue-contact-section">
|
||||
<h4>Contact This Training Lab</h4>
|
||||
<form class="hvac-venue-contact-form" data-venue-id="">
|
||||
<div class="hvac-form-row">
|
||||
<div class="hvac-form-group">
|
||||
<label for="venue-contact-first-name">First Name <span class="required">*</span></label>
|
||||
<input type="text" id="venue-contact-first-name" name="first_name" required>
|
||||
</div>
|
||||
<div class="hvac-form-group">
|
||||
<label for="venue-contact-last-name">Last Name <span class="required">*</span></label>
|
||||
<input type="text" id="venue-contact-last-name" name="last_name" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hvac-form-row">
|
||||
<div class="hvac-form-group">
|
||||
<label for="venue-contact-email">Email <span class="required">*</span></label>
|
||||
<input type="email" id="venue-contact-email" name="email" required>
|
||||
</div>
|
||||
<div class="hvac-form-group">
|
||||
<label for="venue-contact-phone">Phone</label>
|
||||
<input type="tel" id="venue-contact-phone" name="phone">
|
||||
</div>
|
||||
</div>
|
||||
<div class="hvac-form-group">
|
||||
<label for="venue-contact-company">Company</label>
|
||||
<input type="text" id="venue-contact-company" name="company">
|
||||
</div>
|
||||
<div class="hvac-form-group">
|
||||
<label for="venue-contact-message">Message</label>
|
||||
<textarea id="venue-contact-message" name="message" rows="4" placeholder="Tell us about your training needs..."></textarea>
|
||||
</div>
|
||||
<button type="submit" class="hvac-btn-primary">Send Message</button>
|
||||
</form>
|
||||
<div class="hvac-form-success" style="display: none;">
|
||||
<span class="dashicons dashicons-yes-alt" aria-hidden="true"></span>
|
||||
<p>Your message has been sent! The training lab will be in touch soon.</p>
|
||||
</div>
|
||||
<div class="hvac-form-error" style="display: none;">
|
||||
<span class="dashicons dashicons-warning" aria-hidden="true"></span>
|
||||
<p>There was a problem sending your message. Please try again.</p>
|
||||
</div>
|
||||
</div>
|
||||
<a href="#" class="hvac-venue-directions hvac-btn-secondary" target="_blank">
|
||||
<span class="dashicons dashicons-location"></span>
|
||||
Get Directions
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue