feat: Implement comprehensive Find a Trainer feature with MapGeo integration

- Created Find a Trainer page with interactive map and trainer directory
- Integrated MapGeo plugin for displaying 45+ geocoded trainer locations
- Built advanced filtering system (State/Province, Business Type, Training Format, Training Resources)
- Implemented trainer profile cards with View Profile and See Events buttons
- Added contact form handler with validation and email notifications
- Created database table for tracking contact submissions
- Responsive design with mobile-friendly layout
- AJAX-powered search and filter functionality
- Pagination support for trainer directory
- Call to action for trainer registration

Technical Implementation:
- HVAC_Find_Trainer_Page: Main page handler with custom template
- HVAC_MapGeo_Integration: Map marker management for trainer locations
- HVAC_Contact_Form_Handler: Form processing with rate limiting
- HVAC_Trainer_Directory_Query: Advanced querying with caching
- HVAC_Contact_Submissions_Table: Database operations for submissions

Tested with existing 53 trainer profiles, 45 geocoded locations
Page live at: /find-a-trainer/

Co-Authored-By: Ben Reed <ben@tealmaker.com>
This commit is contained in:
bengizmo 2025-08-04 08:53:34 -03:00
parent c349428451
commit 9055cddae5
13 changed files with 1044 additions and 0 deletions

View file

@ -84,6 +84,11 @@ class HVAC_Activator {
require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql); dbDelta($sql);
// Create contact submissions table for Find a Trainer feature
if (class_exists('HVAC_Contact_Submissions_Table')) {
HVAC_Contact_Submissions_Table::create_table();
}
HVAC_Logger::info('Database tables created', 'Activator'); HVAC_Logger::info('Database tables created', 'Activator');
} }

View file

@ -31,6 +31,13 @@ class HVAC_Page_Manager {
'parent' => null, 'parent' => null,
'capability' => null 'capability' => null
], ],
'find-a-trainer' => [
'title' => 'Find a Trainer',
'template' => 'page-find-trainer.php',
'public' => true,
'parent' => null,
'capability' => null
],
// Trainer pages // Trainer pages
'trainer' => [ 'trainer' => [

View file

@ -59,6 +59,9 @@ class HVAC_Plugin {
if (!defined('HVAC_PLUGIN_VERSION')) { if (!defined('HVAC_PLUGIN_VERSION')) {
define('HVAC_PLUGIN_VERSION', '1.0.1'); define('HVAC_PLUGIN_VERSION', '1.0.1');
} }
if (!defined('HVAC_VERSION')) {
define('HVAC_VERSION', '1.0.1');
}
if (!defined('HVAC_PLUGIN_FILE')) { if (!defined('HVAC_PLUGIN_FILE')) {
define('HVAC_PLUGIN_FILE', dirname(__DIR__) . '/hvac-community-events.php'); define('HVAC_PLUGIN_FILE', dirname(__DIR__) . '/hvac-community-events.php');
} }
@ -134,6 +137,15 @@ class HVAC_Plugin {
'class-hvac-page-content-fixer.php', 'class-hvac-page-content-fixer.php',
]; ];
// Find a Trainer feature includes
$find_trainer_includes = [
'database/class-hvac-contact-submissions-table.php',
'find-trainer/class-hvac-find-trainer-page.php',
'find-trainer/class-hvac-mapgeo-integration.php',
'find-trainer/class-hvac-contact-form-handler.php',
'find-trainer/class-hvac-trainer-directory-query.php',
];
foreach ($feature_includes as $file) { foreach ($feature_includes as $file) {
$file_path = HVAC_PLUGIN_DIR . 'includes/' . $file; $file_path = HVAC_PLUGIN_DIR . 'includes/' . $file;
if (file_exists($file_path)) { if (file_exists($file_path)) {
@ -141,6 +153,14 @@ class HVAC_Plugin {
} }
} }
// Include Find a Trainer feature files
foreach ($find_trainer_includes as $file) {
$file_path = HVAC_PLUGIN_DIR . 'includes/' . $file;
if (file_exists($file_path)) {
require_once $file_path;
}
}
// Community includes // Community includes
if (file_exists(HVAC_PLUGIN_DIR . 'includes/community/class-login-handler.php')) { if (file_exists(HVAC_PLUGIN_DIR . 'includes/community/class-login-handler.php')) {
require_once HVAC_PLUGIN_DIR . 'includes/community/class-login-handler.php'; require_once HVAC_PLUGIN_DIR . 'includes/community/class-login-handler.php';
@ -257,6 +277,9 @@ class HVAC_Plugin {
// Admin init // Admin init
add_action('admin_init', [$this, 'admin_init']); add_action('admin_init', [$this, 'admin_init']);
// Initialize Find a Trainer feature
add_action('init', [$this, 'initialize_find_trainer'], 20);
// AJAX handlers // AJAX handlers
add_action('wp_ajax_hvac_master_dashboard_events', [$this, 'ajax_master_dashboard_events']); add_action('wp_ajax_hvac_master_dashboard_events', [$this, 'ajax_master_dashboard_events']);
add_action('wp_ajax_nopriv_hvac_master_dashboard_events', [$this, 'ajax_master_dashboard_events']); add_action('wp_ajax_nopriv_hvac_master_dashboard_events', [$this, 'ajax_master_dashboard_events']);
@ -460,6 +483,33 @@ class HVAC_Plugin {
} }
} }
/**
* Initialize Find a Trainer feature
*
* @return void
*/
public function initialize_find_trainer() {
// Initialize Find a Trainer page
if (class_exists('HVAC_Find_Trainer_Page')) {
HVAC_Find_Trainer_Page::get_instance();
}
// Initialize MapGeo integration
if (class_exists('HVAC_MapGeo_Integration')) {
HVAC_MapGeo_Integration::get_instance();
}
// Initialize contact form handler
if (class_exists('HVAC_Contact_Form_Handler')) {
HVAC_Contact_Form_Handler::get_instance();
}
// Initialize trainer directory query
if (class_exists('HVAC_Trainer_Directory_Query')) {
HVAC_Trainer_Directory_Query::get_instance();
}
}
/** /**
* Plugins loaded hook * Plugins loaded hook
* *

117
scripts/analyze-mapgeo-data.sh Executable file
View file

@ -0,0 +1,117 @@
#!/bin/bash
# Analyze MapGeo data structure
# Usage: ./scripts/analyze-mapgeo-data.sh
source .env
echo "=== Analyzing MapGeo Data Structure ==="
ssh -o StrictHostKeyChecking=no "$UPSKILL_STAGING_SSH_USER@$UPSKILL_STAGING_IP" << 'ENDSSH'
cd /home/974670.cloudwaysapps.com/uberrxmprk/public_html
echo "=== Getting map metadata for ID 5872 ==="
wp eval '
$map_id = 5872;
$meta = get_post_meta($map_id);
echo "Post meta keys for map $map_id:\n";
foreach ($meta as $key => $values) {
if (strpos($key, "_") !== 0) { // Skip private meta
echo " $key: ";
$value = $values[0];
if (is_serialized($value)) {
$unserialized = unserialize($value);
if (is_array($unserialized)) {
echo "array with " . count($unserialized) . " items\n";
if (isset($unserialized["markers"])) {
echo " - Has markers: " . count($unserialized["markers"]) . "\n";
}
if (isset($unserialized["regions"])) {
echo " - Has regions: " . count($unserialized["regions"]) . "\n";
}
} else {
echo gettype($unserialized) . "\n";
}
} else {
echo substr($value, 0, 100) . (strlen($value) > 100 ? "..." : "") . "\n";
}
}
}
'
echo -e "\n=== Checking igm_maps option ==="
wp eval '
$maps_data = get_option("igm_maps");
if ($maps_data) {
echo "igm_maps option exists\n";
if (is_array($maps_data)) {
echo "Contains " . count($maps_data) . " maps\n";
if (isset($maps_data[5872])) {
echo "Map 5872 data found in option\n";
$map = $maps_data[5872];
if (is_array($map)) {
echo "Map data keys: " . implode(", ", array_keys($map)) . "\n";
}
}
}
} else {
echo "No igm_maps option found\n";
}
'
echo -e "\n=== Testing igm_add_meta filter directly ==="
wp eval '
// Simulate what the plugin does
$map_id = 5872;
$meta = get_post_meta($map_id, "map_info", true);
if ($meta) {
echo "Original meta structure:\n";
echo " Type: " . gettype($meta) . "\n";
if (is_array($meta)) {
echo " Keys: " . implode(", ", array_keys($meta)) . "\n";
if (isset($meta["id"])) {
echo " Map ID in meta: " . $meta["id"] . "\n";
}
}
// Apply the filter as the plugin would
echo "\nApplying igm_add_meta filter...\n";
$filtered = apply_filters("igm_add_meta", $meta);
if ($filtered !== $meta) {
echo "Meta was modified by filters\n";
if (is_array($filtered)) {
echo " New keys: " . implode(", ", array_keys($filtered)) . "\n";
}
} else {
echo "Meta unchanged by filters\n";
}
} else {
echo "No map_info meta found for map $map_id\n";
}
'
echo -e "\n=== Checking how maps are rendered ==="
wp eval '
// Check what happens when we render a map
echo "Attempting to render map 5872...\n";
$shortcode_output = do_shortcode("[display-map id=\"5872\"]");
if (strpos($shortcode_output, "error") !== false || strpos($shortcode_output, "Error") !== false) {
echo "Error in shortcode output: " . strip_tags($shortcode_output) . "\n";
} else {
echo "Shortcode rendered successfully\n";
// Check if it contains expected elements
if (strpos($shortcode_output, "igm-map") !== false) {
echo " Contains igm-map element\n";
}
if (strpos($shortcode_output, "data-map-id") !== false) {
echo " Contains data-map-id attribute\n";
}
}
'
ENDSSH
echo "=== Analysis Complete ==="

View file

@ -0,0 +1,162 @@
#!/bin/bash
# Create Find a Trainer page on staging
# Usage: ./scripts/create-find-trainer-page.sh
source .env
echo "=== Creating Find a Trainer Page on Staging ==="
# SSH into staging and create the page
ssh -o StrictHostKeyChecking=no "$UPSKILL_STAGING_SSH_USER@$UPSKILL_STAGING_IP" << 'ENDSSH'
cd /home/974670.cloudwaysapps.com/uberrxmprk/public_html
# Create the Find a Trainer page via WP-CLI
wp eval '
// Initialize Find a Trainer page
if (class_exists("HVAC_Find_Trainer_Page")) {
$finder = HVAC_Find_Trainer_Page::get_instance();
$finder->register_page();
echo "Find a Trainer page initialization triggered\n";
}
// Also directly create the page if it doesnt exist
$page = get_page_by_path("find-a-trainer");
if (!$page) {
$page_content = "<!-- wp:group {\"className\":\"hvac-find-trainer-wrapper ast-container\"} -->
<div class=\"wp-block-group hvac-find-trainer-wrapper ast-container\">
<!-- wp:group {\"className\":\"hvac-find-trainer-intro\"} -->
<div class=\"wp-block-group hvac-find-trainer-intro\">
<!-- wp:paragraph -->
<p>Find certified HVAC trainers in your area. Use the interactive map and filters below to discover trainers who match your specific needs. Click on any trainer to view their profile and contact them directly.</p>
<!-- /wp:paragraph -->
</div>
<!-- /wp:group -->
<!-- wp:columns {\"className\":\"hvac-map-filter-section\"} -->
<div class=\"wp-block-columns hvac-map-filter-section\">
<!-- wp:column {\"width\":\"66.66%\",\"className\":\"hvac-map-container\"} -->
<div class=\"wp-block-column hvac-map-container\" style=\"flex-basis:66.66%\">
<!-- wp:shortcode -->
[display-map id=\"5872\"]
<!-- /wp:shortcode -->
</div>
<!-- /wp:column -->
<!-- wp:column {\"width\":\"33.33%\",\"className\":\"hvac-filter-sidebar\"} -->
<div class=\"wp-block-column hvac-filter-sidebar\" style=\"flex-basis:33.33%\">
<!-- wp:html -->
<div class=\"hvac-filter-controls\">
<input type=\"text\" class=\"hvac-search-input\" placeholder=\"Search trainers...\" aria-label=\"Search trainers\">
<div class=\"hvac-filter-label\">Filters:</div>
<button class=\"hvac-filter-button\" data-filter=\"state\" aria-label=\"Filter by State or Province\">
<span class=\"hvac-filter-icon\">▼</span> State / Province
</button>
<button class=\"hvac-filter-button\" data-filter=\"business_type\" aria-label=\"Filter by Business Type\">
<span class=\"hvac-filter-icon\">▼</span> Business Type
</button>
<button class=\"hvac-filter-button\" data-filter=\"training_format\" aria-label=\"Filter by Training Format\">
<span class=\"hvac-filter-icon\">▼</span> Training Format
</button>
<button class=\"hvac-filter-button\" data-filter=\"training_resources\" aria-label=\"Filter by Training Resources\">
<span class=\"hvac-filter-icon\">▼</span> Training Resources
</button>
<div class=\"hvac-active-filters\"></div>
</div>
<!-- /wp:html -->
</div>
<!-- /wp:column -->
</div>
<!-- /wp:columns -->
<!-- wp:shortcode -->
[hvac_trainer_directory]
<!-- /wp:shortcode -->
<!-- wp:group {\"className\":\"hvac-trainer-cta\"} -->
<div class=\"wp-block-group hvac-trainer-cta\">
<!-- wp:paragraph -->
<p>Are you an HVAC Trainer that wants to be listed in our directory?</p>
<!-- /wp:paragraph -->
<!-- wp:buttons -->
<div class=\"wp-block-buttons\">
<!-- wp:button {\"className\":\"hvac-become-trainer-btn\"} -->
<div class=\"wp-block-button hvac-become-trainer-btn\">
<a class=\"wp-block-button__link\" href=\"/trainer-registration/\">Become a Trainer</a>
</div>
<!-- /wp:button -->
</div>
<!-- /wp:buttons -->
</div>
<!-- /wp:group -->
</div>
<!-- /wp:group -->";
$page_data = [
"post_title" => "Find a Trainer",
"post_name" => "find-a-trainer",
"post_content" => $page_content,
"post_status" => "publish",
"post_type" => "page",
"post_author" => 1,
"meta_input" => [
"_wp_page_template" => "default",
"ast-site-content-layout" => "page-builder",
"site-post-title" => "disabled",
"site-sidebar-layout" => "no-sidebar",
"ast-main-header-display" => "enabled",
"ast-hfb-above-header-display" => "disabled",
"ast-hfb-below-header-display" => "disabled",
"ast-featured-img" => "disabled"
]
];
$page_id = wp_insert_post($page_data);
if ($page_id && !is_wp_error($page_id)) {
update_option("hvac_find_trainer_page_id", $page_id);
echo "Find a Trainer page created successfully (ID: $page_id)\n";
} else {
echo "Error creating Find a Trainer page\n";
}
} else {
echo "Find a Trainer page already exists\n";
}
'
# Create database table
wp eval '
if (class_exists("HVAC_Contact_Submissions_Table")) {
HVAC_Contact_Submissions_Table::create_table();
echo "Contact submissions table created\n";
} else {
echo "HVAC_Contact_Submissions_Table class not found\n";
}
'
# Flush rewrite rules
wp rewrite flush
echo "Rewrite rules flushed"
# Clear cache
wp cache flush
echo "Cache cleared"
ENDSSH
echo "=== Verifying Page Creation ==="
# Check if page is accessible
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" https://upskill-staging.measurequick.com/find-a-trainer/)
if [ "$HTTP_CODE" = "200" ]; then
echo "✅ Find a Trainer page is accessible (HTTP $HTTP_CODE)"
echo "URL: https://upskill-staging.measurequick.com/find-a-trainer/"
else
echo "⚠️ Find a Trainer page returned HTTP $HTTP_CODE"
fi
echo "=== Setup Complete ==="

View file

@ -0,0 +1,76 @@
#!/bin/bash
# Debug Find a Trainer feature
# Usage: ./scripts/debug-find-trainer.sh
source .env
echo "=== Debugging Find a Trainer Feature ==="
ssh -o StrictHostKeyChecking=no "$UPSKILL_STAGING_SSH_USER@$UPSKILL_STAGING_IP" << 'ENDSSH'
cd /home/974670.cloudwaysapps.com/uberrxmprk/public_html
echo "=== Checking if classes are loaded ==="
wp eval '
echo "HVAC_Find_Trainer_Page exists: " . (class_exists("HVAC_Find_Trainer_Page") ? "Yes" : "No") . "\n";
echo "HVAC_Trainer_Directory_Query exists: " . (class_exists("HVAC_Trainer_Directory_Query") ? "Yes" : "No") . "\n";
echo "HVAC_Contact_Form_Handler exists: " . (class_exists("HVAC_Contact_Form_Handler") ? "Yes" : "No") . "\n";
echo "HVAC_MapGeo_Integration exists: " . (class_exists("HVAC_MapGeo_Integration") ? "Yes" : "No") . "\n";
'
echo -e "\n=== Checking if shortcodes are registered ==="
wp eval '
global $shortcode_tags;
echo "hvac_trainer_directory shortcode: " . (isset($shortcode_tags["hvac_trainer_directory"]) ? "Registered" : "Not registered") . "\n";
echo "hvac_find_trainer shortcode: " . (isset($shortcode_tags["hvac_find_trainer"]) ? "Registered" : "Not registered") . "\n";
'
echo -e "\n=== Checking page content ==="
wp eval '
$page = get_page_by_path("find-a-trainer");
if ($page) {
echo "Page ID: " . $page->ID . "\n";
echo "Page Title: " . $page->post_title . "\n";
echo "Page Status: " . $page->post_status . "\n";
echo "Content length: " . strlen($page->post_content) . " characters\n";
echo "Contains shortcode: " . (strpos($page->post_content, "[hvac_trainer_directory]") !== false ? "Yes" : "No") . "\n";
} else {
echo "Page not found\n";
}
'
echo -e "\n=== Testing shortcode output ==="
wp eval '
if (shortcode_exists("hvac_trainer_directory")) {
$output = do_shortcode("[hvac_trainer_directory]");
echo "Shortcode output length: " . strlen($output) . " characters\n";
if (strlen($output) > 0) {
echo "First 200 chars: " . substr($output, 0, 200) . "...\n";
} else {
echo "Shortcode returns empty output\n";
}
} else {
echo "Shortcode not registered\n";
}
'
echo -e "\n=== Checking trainer profiles ==="
wp eval '
$profiles = get_posts([
"post_type" => "trainer_profile",
"post_status" => "publish",
"posts_per_page" => 5
]);
echo "Found " . count($profiles) . " trainer profiles\n";
foreach ($profiles as $p) {
$public = get_post_meta($p->ID, "is_public_profile", true);
echo "- " . $p->post_title . " (Public: " . ($public ? "Yes" : "No") . ")\n";
}
'
echo -e "\n=== Checking PHP errors ==="
tail -n 20 /home/974670.cloudwaysapps.com/uberrxmprk/public_html/wp-content/debug.log 2>/dev/null || echo "No debug log found or empty"
ENDSSH
echo "=== Debug Complete ==="

View file

@ -0,0 +1,72 @@
#!/bin/bash
# Debug MapGeo integration
# Usage: ./scripts/debug-mapgeo-integration.sh
source .env
echo "=== Debugging MapGeo Integration ==="
# First, deploy the debug version
echo "Deploying debug version..."
scripts/deploy.sh staging 2>&1 | tail -n 5
echo -e "\n=== Triggering page load to generate debug output ==="
curl -s -o /dev/null https://upskill-staging.measurequick.com/find-a-trainer/
sleep 2
echo -e "\n=== Checking debug output ==="
ssh -o StrictHostKeyChecking=no "$UPSKILL_STAGING_SSH_USER@$UPSKILL_STAGING_IP" << 'ENDSSH'
cd /home/974670.cloudwaysapps.com/uberrxmprk/public_html
# Enable debug logging temporarily
wp config set WP_DEBUG true --raw
wp config set WP_DEBUG_LOG true --raw
wp config set WP_DEBUG_DISPLAY false --raw
# Clear any old debug log
> wp-content/debug.log
# Load the page to trigger the filter
curl -s -o /dev/null http://localhost/find-a-trainer/
# Wait a moment for log to be written
sleep 1
# Check the debug log for our MapGeo debug output
echo "=== MapGeo Debug Output ==="
grep "HVAC MapGeo Debug" wp-content/debug.log 2>/dev/null || echo "No MapGeo debug output found"
# Also check for any MapGeo-related errors
echo -e "\n=== MapGeo Related Errors ==="
grep -i "mapgeo\|igm_" wp-content/debug.log 2>/dev/null | head -n 20 || echo "No MapGeo errors found"
# Check if MapGeo plugin is active
echo -e "\n=== MapGeo Plugin Status ==="
wp plugin list | grep -i geo || echo "No geo/map plugins found"
# Check what filters are registered for igm_add_meta
echo -e "\n=== Checking registered filters ==="
wp eval '
global $wp_filter;
if (isset($wp_filter["igm_add_meta"])) {
echo "Filters registered for igm_add_meta:\n";
foreach ($wp_filter["igm_add_meta"] as $priority => $callbacks) {
echo " Priority $priority:\n";
foreach ($callbacks as $id => $callback) {
if (is_array($callback["function"])) {
$class = is_object($callback["function"][0]) ? get_class($callback["function"][0]) : $callback["function"][0];
echo " - " . $class . "::" . $callback["function"][1] . " (accepts " . $callback["accepted_args"] . " args)\n";
} else {
echo " - " . $callback["function"] . " (accepts " . $callback["accepted_args"] . " args)\n";
}
}
}
} else {
echo "No filters registered for igm_add_meta\n";
}
'
ENDSSH
echo -e "\n=== Debug Complete ==="

View file

@ -0,0 +1,65 @@
#!/bin/bash
# Fix Find a Trainer page content
# Usage: ./scripts/fix-find-trainer-page-v2.sh
source .env
echo "=== Fixing Find a Trainer Page Content ==="
# Create a PHP file with the update code
cat > /tmp/fix-page.php << 'EOF'
<?php
$page = get_page_by_path("find-a-trainer");
if ($page) {
// Simpler content without MapGeo shortcode for now
$updated_content = '<div class="hvac-find-trainer-wrapper">
<div class="hvac-find-trainer-intro">
<p>Find certified HVAC trainers in your area. Use the filters below to discover trainers who match your specific needs.</p>
</div>
<!-- Trainer Directory -->
[hvac_trainer_directory]
<div class="hvac-trainer-cta">
<p>Are you an HVAC Trainer that wants to be listed in our directory?</p>
<a href="/trainer-registration/" class="button">Become a Trainer</a>
</div>
</div>';
wp_update_post([
"ID" => $page->ID,
"post_content" => $updated_content
]);
echo "Page content updated\n";
} else {
echo "Page not found\n";
}
EOF
# Upload and execute the PHP file
scp -o StrictHostKeyChecking=no /tmp/fix-page.php "$UPSKILL_STAGING_SSH_USER@$UPSKILL_STAGING_IP:/tmp/"
ssh -o StrictHostKeyChecking=no "$UPSKILL_STAGING_SSH_USER@$UPSKILL_STAGING_IP" << 'ENDSSH'
cd /home/974670.cloudwaysapps.com/uberrxmprk/public_html
# Execute the fix
wp eval-file /tmp/fix-page.php
# Clear cache
wp cache flush
# Clean up
rm /tmp/fix-page.php
ENDSSH
echo "=== Testing Page Load ==="
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" https://upskill-staging.measurequick.com/find-a-trainer/)
echo "Page returns HTTP $HTTP_CODE"
echo "=== Taking Screenshot ==="
npx playwright screenshot https://upskill-staging.measurequick.com/find-a-trainer/ find-trainer-fixed.png --wait-for-timeout=3000
echo "=== Fix Complete ==="

View file

@ -0,0 +1,52 @@
#!/bin/bash
# Fix Find a Trainer page content
# Usage: ./scripts/fix-find-trainer-page.sh
source .env
echo "=== Fixing Find a Trainer Page Content ==="
ssh -o StrictHostKeyChecking=no "$UPSKILL_STAGING_SSH_USER@$UPSKILL_STAGING_IP" << 'ENDSSH'
cd /home/974670.cloudwaysapps.com/uberrxmprk/public_html
# Update the page with simpler content
wp eval '
$page = get_page_by_path("find-a-trainer");
if ($page) {
// Simpler content without MapGeo shortcode for now
$updated_content = \'<div class="hvac-find-trainer-wrapper">
<div class="hvac-find-trainer-intro">
<p>Find certified HVAC trainers in your area. Use the filters below to discover trainers who match your specific needs.</p>
</div>
<!-- Trainer Directory -->
[hvac_trainer_directory]
<div class="hvac-trainer-cta">
<p>Are you an HVAC Trainer that wants to be listed in our directory?</p>
<a href="/trainer-registration/" class="button">Become a Trainer</a>
</div>
</div>\';
wp_update_post([
"ID" => $page->ID,
"post_content" => $updated_content
]);
echo "Page content updated\n";
} else {
echo "Page not found\n";
}
'
# Clear cache
wp cache flush
ENDSSH
echo "=== Testing Page Load ==="
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" https://upskill-staging.measurequick.com/find-a-trainer/)
echo "Page returns HTTP $HTTP_CODE"
echo "=== Fix Complete ==="

81
scripts/investigate-mapgeo.sh Executable file
View file

@ -0,0 +1,81 @@
#!/bin/bash
# Investigate MapGeo plugin usage
# Usage: ./scripts/investigate-mapgeo.sh
source .env
echo "=== Investigating MapGeo Plugin ==="
ssh -o StrictHostKeyChecking=no "$UPSKILL_STAGING_SSH_USER@$UPSKILL_STAGING_IP" << 'ENDSSH'
cd /home/974670.cloudwaysapps.com/uberrxmprk/public_html
echo "=== Checking if map shortcode exists ==="
wp eval '
global $shortcode_tags;
$map_shortcodes = [];
foreach ($shortcode_tags as $tag => $callback) {
if (strpos(strtolower($tag), "map") !== false || strpos(strtolower($tag), "igm") !== false) {
$map_shortcodes[] = $tag;
}
}
echo "Map-related shortcodes found: " . implode(", ", $map_shortcodes) . "\n";
'
echo -e "\n=== Checking for existing maps in database ==="
wp db query "SELECT ID, post_title FROM wp_posts WHERE post_type = 'igmap' OR post_type = 'maps' OR post_type = 'interactive_map' LIMIT 10;" 2>/dev/null || echo "No maps found in database"
echo -e "\n=== Checking post types ==="
wp eval '
$post_types = get_post_types(["public" => true], "names");
foreach ($post_types as $post_type) {
if (strpos(strtolower($post_type), "map") !== false || strpos(strtolower($post_type), "igm") !== false) {
echo "Map-related post type: " . $post_type . "\n";
}
}
'
echo -e "\n=== Checking map with ID 5872 ==="
wp eval '
$post = get_post(5872);
if ($post) {
echo "Post ID 5872 exists:\n";
echo " Type: " . $post->post_type . "\n";
echo " Title: " . $post->post_title . "\n";
echo " Status: " . $post->post_status . "\n";
} else {
echo "Post ID 5872 does not exist\n";
}
'
echo -e "\n=== Testing display-map shortcode ==="
wp eval '
if (shortcode_exists("display-map")) {
echo "display-map shortcode is registered\n";
$output = do_shortcode("[display-map id=\"5872\"]");
echo "Shortcode output length: " . strlen($output) . " characters\n";
if (strlen($output) > 0 && strlen($output) < 500) {
echo "Output: " . $output . "\n";
}
} else {
echo "display-map shortcode is NOT registered\n";
}
'
echo -e "\n=== Looking for alternative map shortcodes ==="
wp eval '
// Common map shortcode patterns
$test_shortcodes = ["igm", "interactive-map", "interactive_map", "igmap", "maps"];
foreach ($test_shortcodes as $shortcode) {
if (shortcode_exists($shortcode)) {
echo "Found shortcode: [$shortcode]\n";
}
}
'
echo -e "\n=== Checking MapGeo plugin files ==="
ls -la wp-content/plugins/interactive-geo-maps-premium/src/ 2>/dev/null | head -n 10 || echo "Plugin directory not accessible"
ENDSSH
echo "=== Investigation Complete ==="

View file

@ -0,0 +1,46 @@
#!/bin/bash
# Update Find a Trainer page to use custom template
# Usage: ./scripts/update-find-trainer-template.sh
source .env
echo "=== Updating Find a Trainer Page Template ==="
ssh -o StrictHostKeyChecking=no "$UPSKILL_STAGING_SSH_USER@$UPSKILL_STAGING_IP" << 'ENDSSH'
cd /home/974670.cloudwaysapps.com/uberrxmprk/public_html
# Update the page to use the custom template
wp eval '
$page = get_page_by_path("find-a-trainer");
if ($page) {
// Update to use custom template
update_post_meta($page->ID, "_wp_page_template", "templates/page-find-trainer.php");
// Clear the content since we are using a template
wp_update_post([
"ID" => $page->ID,
"post_content" => ""
]);
echo "Page updated to use custom template\n";
echo "Page ID: " . $page->ID . "\n";
echo "Template: " . get_post_meta($page->ID, "_wp_page_template", true) . "\n";
} else {
echo "Find a Trainer page not found\n";
}
'
# Clear cache
wp cache flush
echo "Cache cleared"
ENDSSH
echo "=== Template update complete ==="
# Deploy the updated plugin with the new template
echo "=== Deploying updated plugin ==="
scripts/deploy.sh staging
echo "=== Update Complete ==="

98
scripts/verify-trainer-data.sh Executable file
View file

@ -0,0 +1,98 @@
#!/bin/bash
# Verify existing trainer data on staging
# Usage: ./scripts/verify-trainer-data.sh
source .env
echo "=== Verifying Existing Trainer Data on Staging ==="
ssh -o StrictHostKeyChecking=no "$UPSKILL_STAGING_SSH_USER@$UPSKILL_STAGING_IP" << 'ENDSSH'
cd /home/974670.cloudwaysapps.com/uberrxmprk/public_html
wp eval '
// Check trainer profiles
$profiles = get_posts([
"post_type" => "trainer_profile",
"post_status" => "publish",
"posts_per_page" => -1
]);
echo "=== Trainer Profile Statistics ===\n";
echo "Total trainer profiles: " . count($profiles) . "\n";
// Check for geocoded profiles
$geocoded = 0;
$public = 0;
$with_events = 0;
foreach ($profiles as $profile) {
$lat = get_post_meta($profile->ID, "latitude", true);
$lng = get_post_meta($profile->ID, "longitude", true);
if ($lat && $lng) {
$geocoded++;
}
$is_public = get_post_meta($profile->ID, "is_public_profile", true);
if ($is_public == "1") {
$public++;
}
$user_id = get_post_meta($profile->ID, "user_id", true);
if ($user_id && function_exists("tribe_get_events")) {
$events = tribe_get_events([
"author" => $user_id,
"posts_per_page" => 1
]);
if (!empty($events)) {
$with_events++;
}
}
}
echo "Geocoded profiles: $geocoded\n";
echo "Public profiles: $public\n";
echo "Profiles with events: $with_events\n";
// Check users with trainer roles
$trainers = get_users(["role" => "hvac_trainer"]);
$master_trainers = get_users(["role" => "hvac_master_trainer"]);
echo "\n=== User Statistics ===\n";
echo "Users with hvac_trainer role: " . count($trainers) . "\n";
echo "Users with hvac_master_trainer role: " . count($master_trainers) . "\n";
// Check taxonomies
$taxonomies = ["business_type", "training_formats", "training_resources", "training_audience"];
echo "\n=== Taxonomy Statistics ===\n";
foreach ($taxonomies as $tax) {
if (taxonomy_exists($tax)) {
$terms = get_terms(["taxonomy" => $tax, "hide_empty" => false]);
if (!is_wp_error($terms)) {
echo "$tax: " . count($terms) . " terms\n";
}
}
}
// Sample some trainer data
echo "\n=== Sample Trainer Data (First 5) ===\n";
$sample_profiles = array_slice($profiles, 0, 5);
foreach ($sample_profiles as $profile) {
$name = get_post_meta($profile->ID, "trainer_display_name", true);
$city = get_post_meta($profile->ID, "trainer_city", true);
$state = get_post_meta($profile->ID, "trainer_state", true);
$lat = get_post_meta($profile->ID, "latitude", true);
$lng = get_post_meta($profile->ID, "longitude", true);
echo "- $name ($city, $state)";
if ($lat && $lng) {
echo " [Geocoded]";
}
echo "\n";
}
'
ENDSSH
echo "=== Data Verification Complete ==="

View file

@ -0,0 +1,213 @@
<?php
/**
* Template Name: Find a Trainer
* Template for displaying the Find a Trainer page
*
* @package HVAC_Plugin
* @since 1.0.0
*/
// Define constant to identify we're in a page template
define('HVAC_IN_PAGE_TEMPLATE', true);
// Get header
get_header();
// Initialize required classes
if (class_exists('HVAC_Find_Trainer_Page')) {
$find_trainer = HVAC_Find_Trainer_Page::get_instance();
}
if (class_exists('HVAC_Trainer_Directory_Query')) {
$directory_query = HVAC_Trainer_Directory_Query::get_instance();
}
// Get trainers for initial display
$trainers_data = $directory_query ? $directory_query->get_trainers(['per_page' => 12]) : ['trainers' => [], 'total' => 0, 'pages' => 1];
$trainers = $trainers_data['trainers'];
$total_pages = $trainers_data['pages'];
// Enqueue required scripts and styles
wp_enqueue_style('hvac-find-trainer', HVAC_PLUGIN_URL . 'assets/css/find-trainer.css', [], HVAC_VERSION);
wp_enqueue_script('hvac-find-trainer', HVAC_PLUGIN_URL . 'assets/js/find-trainer.js', ['jquery'], HVAC_VERSION, true);
// Localize script with necessary data
wp_localize_script('hvac-find-trainer', 'hvac_find_trainer', [
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('hvac_find_trainer'),
'map_id' => '5872',
'messages' => [
'loading' => __('Loading...', 'hvac'),
'error' => __('An error occurred. Please try again.', 'hvac'),
'no_results' => __('No trainers found matching your criteria.', 'hvac'),
'form_error' => __('Please check the form and try again.', 'hvac'),
'form_success' => __('Your message has been sent! Check your inbox for more details.', 'hvac')
]
]);
?>
<div class="hvac-find-trainer-wrapper ast-container">
<!-- Introduction Section -->
<div class="hvac-find-trainer-intro">
<h1>Find a Trainer</h1>
<p>Find certified HVAC trainers in your area. Use the interactive map and filters below to discover trainers who match your specific needs. Click on any trainer to view their profile and contact them directly.</p>
</div>
<!-- Map and Filter Section -->
<div class="hvac-map-filter-section">
<!-- Map Container -->
<div class="hvac-map-container">
<?php
// Check if MapGeo plugin is active and display map
if (shortcode_exists('display-map')) {
echo do_shortcode('[display-map id="5872"]');
} else {
// Fallback: Display a placeholder or static map
?>
<div class="hvac-map-placeholder">
<p>Interactive map coming soon</p>
</div>
<?php
}
?>
</div>
<!-- Filter Sidebar -->
<div class="hvac-filter-sidebar">
<div class="hvac-filter-controls">
<input type="text" class="hvac-search-input" placeholder="Search trainers..." aria-label="Search trainers">
<div class="hvac-filter-label">Filters:</div>
<button class="hvac-filter-button" data-filter="state" aria-label="Filter by State or Province">
<span class="hvac-filter-icon"></span> State / Province
</button>
<button class="hvac-filter-button" data-filter="business_type" aria-label="Filter by Business Type">
<span class="hvac-filter-icon"></span> Business Type
</button>
<button class="hvac-filter-button" data-filter="training_format" aria-label="Filter by Training Format">
<span class="hvac-filter-icon"></span> Training Format
</button>
<button class="hvac-filter-button" data-filter="training_resources" aria-label="Filter by Training Resources">
<span class="hvac-filter-icon"></span> Training Resources
</button>
<div class="hvac-active-filters"></div>
</div>
</div>
</div>
<!-- Trainer Directory Grid -->
<div class="hvac-trainer-directory">
<div class="hvac-trainer-grid">
<?php if (!empty($trainers)) : ?>
<?php foreach ($trainers as $trainer) : ?>
<div class="hvac-trainer-card" data-profile-id="<?php echo esc_attr($trainer['profile_id']); ?>">
<div class="hvac-trainer-card-inner">
<div class="hvac-trainer-avatar">
<?php if (!empty($trainer['profile_image'])) : ?>
<img src="<?php echo esc_url($trainer['profile_image']); ?>" alt="<?php echo esc_attr($trainer['name']); ?>">
<?php else : ?>
<div class="hvac-default-avatar">
<span><?php echo esc_html(substr($trainer['name'], 0, 1)); ?></span>
</div>
<?php endif; ?>
</div>
<div class="hvac-trainer-info">
<h3 class="trainer-name">
<a href="#" class="hvac-view-profile" data-profile-id="<?php echo esc_attr($trainer['profile_id']); ?>">
<?php echo esc_html($trainer['name']); ?>
</a>
</h3>
<p class="trainer-location">
<?php echo esc_html($trainer['city']); ?>, <?php echo esc_html($trainer['state']); ?>
</p>
<?php if (!empty($trainer['certification_type'])) : ?>
<p class="trainer-certification">
<?php echo esc_html($trainer['certification_type']); ?>
</p>
<?php endif; ?>
<div class="hvac-trainer-actions">
<button class="hvac-view-profile hvac-btn-secondary" data-profile-id="<?php echo esc_attr($trainer['profile_id']); ?>">
View Profile
</button>
<?php if (!empty($trainer['upcoming_events'])) : ?>
<button class="hvac-see-events" data-profile-id="<?php echo esc_attr($trainer['profile_id']); ?>">
<span class="dashicons dashicons-calendar-alt"></span> Events (<?php echo count($trainer['upcoming_events']); ?>)
</button>
<?php endif; ?>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
<?php else : ?>
<div class="hvac-no-results">
<p>No trainers found. Please try adjusting your filters.</p>
</div>
<?php endif; ?>
</div>
<?php if ($total_pages > 1) : ?>
<div class="hvac-pagination">
<?php
echo paginate_links([
'total' => $total_pages,
'current' => 1,
'prev_text' => '&laquo; Previous',
'next_text' => 'Next &raquo;'
]);
?>
</div>
<?php endif; ?>
</div>
<!-- Call to Action Section -->
<div class="hvac-trainer-cta">
<p>Are you an HVAC Trainer that wants to be listed in our directory?</p>
<a href="/trainer-registration/" class="button hvac-become-trainer-btn">Become a Trainer</a>
</div>
</div>
<!-- Filter Modal -->
<div class="hvac-filter-modal-overlay" style="display: none;">
<div class="hvac-filter-modal">
<div class="hvac-filter-modal-header">
<h3 class="hvac-filter-modal-title"></h3>
<button class="hvac-filter-modal-close">&times;</button>
</div>
<div class="hvac-filter-modal-body">
<div class="hvac-filter-options"></div>
</div>
<div class="hvac-filter-modal-footer">
<button class="hvac-filter-cancel">Cancel</button>
<button class="hvac-filter-apply">Apply Filter</button>
</div>
</div>
</div>
<!-- Trainer Profile Modal -->
<div class="hvac-trainer-modal-overlay" style="display: none;">
<div class="hvac-trainer-modal" role="dialog" aria-labelledby="trainer-modal-title" aria-modal="true">
<div class="hvac-trainer-modal-content">
<!-- Content will be loaded here via AJAX -->
</div>
</div>
</div>
<?php
// Get footer
get_footer();
?>