upskill-event-manager/templates/page-find-training.php
ben 21c908af81 feat(find-training): New Google Maps page replacing buggy MapGeo implementation
Implements /find-training page with Google Maps JavaScript API:
- Interactive map showing trainers (teal) and venues (orange) markers
- MarkerClusterer for dense areas
- Filter by State, Certification, Training Format
- Search by name/location
- "Near Me" geolocation with proximity filtering
- Trainer profile modal with contact form
- Venue info modal with upcoming events
- 301 redirect from /find-a-trainer to /find-training
- Auto-geocoding for new TEC venues via Google API

Multi-model code review fixes (GPT-5, Gemini 3, Zen MCP):
- Added missing contact form AJAX handler with rate limiting
- Fixed XSS risk in InfoWindow (DOM creation vs inline onclick)
- Added caching for filter dropdown queries (1-hour TTL)
- Added AJAX abort handling to prevent race conditions
- Replaced alert() with inline error notifications

New files:
- includes/find-training/class-hvac-find-training-page.php
- includes/find-training/class-hvac-training-map-data.php
- includes/find-training/class-hvac-venue-geocoding.php
- templates/page-find-training.php
- assets/js/find-training-map.js
- assets/js/find-training-filters.js
- assets/css/find-training-map.css
- assets/images/marker-trainer.svg
- assets/images/marker-venue.svg

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 23:20:34 -04:00

200 lines
8.4 KiB
PHP

<?php
/**
* Template Name: Find Training
*
* Template for displaying the Find Training page with Google Maps
* showing trainers and venues on an interactive map.
*
* @package HVAC_Community_Events
* @since 2.2.0
*/
defined('ABSPATH') || exit;
define('HVAC_IN_PAGE_TEMPLATE', true);
get_header();
// Get page handler instance
$find_training = HVAC_Find_Training_Page::get_instance();
$filter_options = $find_training->get_filter_options();
?>
<div class="hvac-find-training-page">
<div class="ast-container">
<!-- 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>
</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>
</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
</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
</div>
</div>
</div>
<!-- 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...
</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>
</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>
<!-- Trainer Profile Modal -->
<div id="hvac-trainer-modal" class="hvac-training-modal" style="display: none;" role="dialog" aria-modal="true" aria-labelledby="trainer-modal-title">
<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>
Loading...
</div>
<div class="hvac-modal-body"></div>
</div>
</div>
<!-- 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-venue-modal-header">
<h2 id="venue-modal-title"></h2>
<button class="hvac-modal-close" aria-label="Close modal">&times;</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>
</div>
<a href="#" class="hvac-venue-directions hvac-btn-secondary" target="_blank">
<span class="dashicons dashicons-location"></span>
Get Directions
</a>
</div>
</div>
</div>
<?php get_footer(); ?>