feat(find-training): Add viewport sync and marker hover interactions
- Add viewport sync: sidebar shows only trainers visible in map area - Add mouseover event on markers showing info window on hover - Set optimized:false on markers for reliable hover events - Add legacy URL redirects (/find-a-trainer → /find-training) - Remove deprecated find-a-trainer page from Page Manager - Update Status.md with session changes - Bump version to 2.2.4 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
21c908af81
commit
19147d978e
6 changed files with 301 additions and 49 deletions
64
Status.md
64
Status.md
|
|
@ -1,14 +1,57 @@
|
||||||
# HVAC Community Events - Project Status
|
# HVAC Community Events - Project Status
|
||||||
|
|
||||||
**Last Updated:** February 1, 2026
|
**Last Updated:** February 1, 2026
|
||||||
**Current Session:** Find Training Page Implementation - Complete
|
**Current Session:** Find Training Page Enhancements - Complete
|
||||||
**Version:** 2.2.0 (Ready for Staging Deployment)
|
**Version:** 2.2.4 (Deployed to Production)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🎯 CURRENT SESSION - FIND TRAINING PAGE IMPLEMENTATION (Jan 31 - Feb 1, 2026)
|
## 🎯 CURRENT SESSION - FIND TRAINING PAGE ENHANCEMENTS (Feb 1, 2026)
|
||||||
|
|
||||||
### Status: ✅ **COMPLETE - Ready for Staging Deployment & E2E Testing**
|
### Status: ✅ **COMPLETE - Deployed to Production**
|
||||||
|
|
||||||
|
**Objective:** Improve Find Training page UX with viewport sync and marker hover interactions.
|
||||||
|
|
||||||
|
### Changes Made
|
||||||
|
|
||||||
|
1. ✅ **Viewport Sync** - Sidebar now shows only trainers visible in current map area
|
||||||
|
- Added `visibleTrainers` array to track filtered trainers
|
||||||
|
- Added `syncSidebarWithViewport()` method filtering by map bounds
|
||||||
|
- Map `idle` event triggers sync on pan/zoom
|
||||||
|
- Count shows "X of Y trainers" when zoomed in
|
||||||
|
|
||||||
|
2. ✅ **Marker Hover Interaction** - Info window appears on hover
|
||||||
|
- Added `mouseover` event listener to trainer/venue markers
|
||||||
|
- Set `optimized: false` on markers for reliable hover events
|
||||||
|
- Hover shows info window preview with "View Profile" button
|
||||||
|
- Click on "View Profile" opens full modal with contact form
|
||||||
|
|
||||||
|
3. ✅ **Legacy URL Redirects**
|
||||||
|
- `/find-a-trainer/` → `/find-training/` (301 redirect)
|
||||||
|
- `/find-trainer/` → `/find-training/` (301 redirect)
|
||||||
|
- Removed old page from Page Manager
|
||||||
|
|
||||||
|
### Files Modified
|
||||||
|
|
||||||
|
| File | Change |
|
||||||
|
|------|--------|
|
||||||
|
| `assets/js/find-training-map.js` | Added viewport sync, hover events, optimized:false |
|
||||||
|
| `assets/js/find-training-filters.js` | Updated filter handler for visibleTrainers |
|
||||||
|
| `includes/class-hvac-route-manager.php` | Added legacy URL redirects |
|
||||||
|
| `includes/class-hvac-page-manager.php` | Removed find-a-trainer page definition |
|
||||||
|
| `includes/class-hvac-plugin.php` | Version bumped to 2.2.4 |
|
||||||
|
|
||||||
|
### Verified Behavior
|
||||||
|
- ✅ Hover over marker → Info window appears immediately
|
||||||
|
- ✅ Click "View Profile" → Full modal with trainer details + contact form
|
||||||
|
- ✅ Pan/zoom map → Sidebar updates to show visible trainers only
|
||||||
|
- ✅ Legacy URLs redirect to new page
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 PREVIOUS SESSION - FIND TRAINING PAGE IMPLEMENTATION (Jan 31 - Feb 1, 2026)
|
||||||
|
|
||||||
|
### Status: ✅ **COMPLETE - Deployed to Production**
|
||||||
|
|
||||||
**Objective:** Replace the buggy MapGeo-based `/find-a-trainer` page with a new `/find-training` page built from scratch using Google Maps JavaScript API.
|
**Objective:** Replace the buggy MapGeo-based `/find-a-trainer` page with a new `/find-training` page built from scratch using Google Maps JavaScript API.
|
||||||
|
|
||||||
|
|
@ -69,13 +112,12 @@ Ran comprehensive code review using GPT-5, Gemini 3, and Zen MCP tools. Found an
|
||||||
- ✅ Auto-geocoding for new venues
|
- ✅ Auto-geocoding for new venues
|
||||||
- ✅ Rate-limited batch geocoding for existing venues
|
- ✅ Rate-limited batch geocoding for existing venues
|
||||||
|
|
||||||
### Next Steps
|
### Deployment Status
|
||||||
1. ⏳ Deploy to staging: `./scripts/deploy.sh staging`
|
- ✅ Deployed to staging
|
||||||
2. ⏳ Run E2E tests on Find Training page
|
- ✅ Map loads with markers and clustering
|
||||||
3. ⏳ Verify map loads with markers
|
- ✅ Filters working (state, certification, format)
|
||||||
4. ⏳ Test filters and geolocation
|
- ✅ Contact form functional
|
||||||
5. ⏳ Verify contact form sends email
|
- ✅ Deployed to production
|
||||||
6. ⏳ Deploy to production after validation
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@
|
||||||
*/
|
*/
|
||||||
init: function() {
|
init: function() {
|
||||||
this.bindEvents();
|
this.bindEvents();
|
||||||
|
this.initMobileFilterToggle();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -133,12 +134,11 @@
|
||||||
// Update map data
|
// Update map data
|
||||||
HVACTrainingMap.trainers = response.data.trainers || [];
|
HVACTrainingMap.trainers = response.data.trainers || [];
|
||||||
HVACTrainingMap.venues = response.data.venues || [];
|
HVACTrainingMap.venues = response.data.venues || [];
|
||||||
|
HVACTrainingMap.visibleTrainers = HVACTrainingMap.trainers.slice(); // Reset to all
|
||||||
HVACTrainingMap.updateMarkers();
|
HVACTrainingMap.updateMarkers();
|
||||||
HVACTrainingMap.updateCounts(
|
HVACTrainingMap.updateCounts(HVACTrainingMap.trainers.length);
|
||||||
response.data.total_trainers,
|
|
||||||
response.data.total_venues
|
|
||||||
);
|
|
||||||
HVACTrainingMap.updateTrainerGrid();
|
HVACTrainingMap.updateTrainerGrid();
|
||||||
|
// Note: syncSidebarWithViewport will be called by map 'idle' event
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
complete: function() {
|
complete: function() {
|
||||||
|
|
@ -375,6 +375,66 @@
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
div.textContent = text;
|
div.textContent = text;
|
||||||
return div.innerHTML;
|
return div.innerHTML;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize mobile filter toggle
|
||||||
|
*/
|
||||||
|
initMobileFilterToggle: function() {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
// Mobile filter panel toggle
|
||||||
|
$(document).on('click', '.hvac-mobile-filter-toggle', function() {
|
||||||
|
const $toggle = $(this);
|
||||||
|
const $panel = $('#hvac-mobile-filter-panel');
|
||||||
|
const isExpanded = $toggle.attr('aria-expanded') === 'true';
|
||||||
|
|
||||||
|
if (isExpanded) {
|
||||||
|
$panel.attr('hidden', '');
|
||||||
|
$toggle.attr('aria-expanded', 'false');
|
||||||
|
} else {
|
||||||
|
$panel.removeAttr('hidden');
|
||||||
|
$toggle.attr('aria-expanded', 'true');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sync mobile filter selects with desktop selects
|
||||||
|
$('#hvac-filter-state-mobile').on('change', function() {
|
||||||
|
const value = $(this).val();
|
||||||
|
$('#hvac-filter-state').val(value);
|
||||||
|
self.activeFilters.state = value;
|
||||||
|
self.applyFilters();
|
||||||
|
self.updateActiveFiltersDisplay();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#hvac-filter-certification-mobile').on('change', function() {
|
||||||
|
const value = $(this).val();
|
||||||
|
$('#hvac-filter-certification').val(value);
|
||||||
|
self.activeFilters.certification = value;
|
||||||
|
self.applyFilters();
|
||||||
|
self.updateActiveFiltersDisplay();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#hvac-filter-format-mobile').on('change', function() {
|
||||||
|
const value = $(this).val();
|
||||||
|
$('#hvac-filter-format').val(value);
|
||||||
|
self.activeFilters.training_format = value;
|
||||||
|
self.applyFilters();
|
||||||
|
self.updateActiveFiltersDisplay();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Also sync desktop to mobile when desktop changes
|
||||||
|
$('#hvac-filter-state').on('change', function() {
|
||||||
|
$('#hvac-filter-state-mobile').val($(this).val());
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#hvac-filter-certification').on('change', function() {
|
||||||
|
$('#hvac-filter-certification-mobile').val($(this).val());
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#hvac-filter-format').on('change', function() {
|
||||||
|
$('#hvac-filter-format-mobile').val($(this).val());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,9 @@
|
||||||
trainers: [],
|
trainers: [],
|
||||||
venues: [],
|
venues: [],
|
||||||
|
|
||||||
|
// Visible trainers (filtered by map bounds)
|
||||||
|
visibleTrainers: [],
|
||||||
|
|
||||||
// Configuration
|
// Configuration
|
||||||
config: {
|
config: {
|
||||||
mapElementId: 'hvac-training-map',
|
mapElementId: 'hvac-training-map',
|
||||||
|
|
@ -45,21 +48,29 @@
|
||||||
init: function() {
|
init: function() {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
|
// Check if API key is configured
|
||||||
|
if (typeof hvacFindTraining === 'undefined' || !hvacFindTraining.api_key_configured) {
|
||||||
|
console.warn('Google Maps API key not configured');
|
||||||
|
// Still load trainer directory data
|
||||||
|
this.loadTrainerDirectory();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if Google Maps is loaded
|
// Check if Google Maps is loaded
|
||||||
if (typeof google === 'undefined' || typeof google.maps === 'undefined') {
|
if (typeof google === 'undefined' || typeof google.maps === 'undefined') {
|
||||||
console.error('Google Maps API not loaded');
|
console.error('Google Maps API not loaded');
|
||||||
this.showMapError('Google Maps failed to load. Please refresh the page.');
|
this.showMapError('Google Maps failed to load. Please refresh the page.');
|
||||||
|
// Still load trainer directory data
|
||||||
|
this.loadTrainerDirectory();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override config with localized data
|
// Override config with localized data
|
||||||
if (typeof hvacFindTraining !== 'undefined') {
|
if (hvacFindTraining.map_center) {
|
||||||
if (hvacFindTraining.map_center) {
|
this.config.defaultCenter = hvacFindTraining.map_center;
|
||||||
this.config.defaultCenter = hvacFindTraining.map_center;
|
}
|
||||||
}
|
if (hvacFindTraining.default_zoom) {
|
||||||
if (hvacFindTraining.default_zoom) {
|
this.config.defaultZoom = parseInt(hvacFindTraining.default_zoom);
|
||||||
this.config.defaultZoom = parseInt(hvacFindTraining.default_zoom);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the map
|
// Create the map
|
||||||
|
|
@ -73,6 +84,36 @@
|
||||||
|
|
||||||
// Bind events
|
// Bind events
|
||||||
this.bindEvents();
|
this.bindEvents();
|
||||||
|
|
||||||
|
// Initialize responsive features
|
||||||
|
this.handleWindowResize();
|
||||||
|
this.initSidebarToggle();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load just the trainer directory (no map)
|
||||||
|
*/
|
||||||
|
loadTrainerDirectory: function() {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: hvacFindTraining.ajax_url,
|
||||||
|
type: 'POST',
|
||||||
|
data: {
|
||||||
|
action: 'hvac_get_training_map_data',
|
||||||
|
nonce: hvacFindTraining.nonce
|
||||||
|
},
|
||||||
|
success: function(response) {
|
||||||
|
if (response.success && response.data) {
|
||||||
|
self.trainers = response.data.trainers || [];
|
||||||
|
self.updateTrainerGrid(self.trainers);
|
||||||
|
self.updateCounts(self.trainers.length, 0);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function() {
|
||||||
|
self.hideLoading();
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -110,6 +151,12 @@
|
||||||
this.map.addListener('click', () => {
|
this.map.addListener('click', () => {
|
||||||
this.infoWindow.close();
|
this.infoWindow.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Sync sidebar with map viewport on pan/zoom
|
||||||
|
const self = this;
|
||||||
|
this.map.addListener('idle', function() {
|
||||||
|
self.syncSidebarWithViewport();
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -157,10 +204,13 @@
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
self.trainers = response.data.trainers || [];
|
self.trainers = response.data.trainers || [];
|
||||||
self.venues = response.data.venues || [];
|
self.venues = response.data.venues || [];
|
||||||
|
self.visibleTrainers = self.trainers.slice(); // Initially all trainers are "visible"
|
||||||
|
|
||||||
self.updateMarkers();
|
self.updateMarkers();
|
||||||
self.updateCounts(response.data.total_trainers, response.data.total_venues);
|
self.updateCounts(self.trainers.length);
|
||||||
self.updateTrainerGrid();
|
self.updateTrainerGrid();
|
||||||
|
// Note: syncSidebarWithViewport will be called by map 'idle' event
|
||||||
|
// to filter trainers to current viewport
|
||||||
} else {
|
} else {
|
||||||
self.showMapError(response.data?.message || 'Failed to load data');
|
self.showMapError(response.data?.message || 'Failed to load data');
|
||||||
}
|
}
|
||||||
|
|
@ -222,14 +272,19 @@
|
||||||
map: this.map,
|
map: this.map,
|
||||||
title: trainer.name,
|
title: trainer.name,
|
||||||
icon: this.getTrainerIcon(),
|
icon: this.getTrainerIcon(),
|
||||||
optimized: true
|
optimized: false // Required for reliable hover events
|
||||||
});
|
});
|
||||||
|
|
||||||
// Store trainer data on marker
|
// Store trainer data on marker
|
||||||
marker.trainerData = trainer;
|
marker.trainerData = trainer;
|
||||||
marker.markerType = 'trainer';
|
marker.markerType = 'trainer';
|
||||||
|
|
||||||
// Add click listener
|
// Add hover listener to show info window preview
|
||||||
|
marker.addListener('mouseover', function() {
|
||||||
|
self.showTrainerInfoWindow(this);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add click listener (also shows info window, for touch devices)
|
||||||
marker.addListener('click', function() {
|
marker.addListener('click', function() {
|
||||||
self.showTrainerInfoWindow(this);
|
self.showTrainerInfoWindow(this);
|
||||||
});
|
});
|
||||||
|
|
@ -249,14 +304,19 @@
|
||||||
map: this.map,
|
map: this.map,
|
||||||
title: venue.name,
|
title: venue.name,
|
||||||
icon: this.getVenueIcon(),
|
icon: this.getVenueIcon(),
|
||||||
optimized: true
|
optimized: false // Required for reliable hover events
|
||||||
});
|
});
|
||||||
|
|
||||||
// Store venue data on marker
|
// Store venue data on marker
|
||||||
marker.venueData = venue;
|
marker.venueData = venue;
|
||||||
marker.markerType = 'venue';
|
marker.markerType = 'venue';
|
||||||
|
|
||||||
// Add click listener
|
// Add hover listener to show info window preview
|
||||||
|
marker.addListener('mouseover', function() {
|
||||||
|
self.showVenueInfoWindow(this);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add click listener (also shows info window, for touch devices)
|
||||||
marker.addListener('click', function() {
|
marker.addListener('click', function() {
|
||||||
self.showVenueInfoWindow(this);
|
self.showVenueInfoWindow(this);
|
||||||
});
|
});
|
||||||
|
|
@ -585,21 +645,30 @@
|
||||||
const $grid = $('#hvac-trainer-grid');
|
const $grid = $('#hvac-trainer-grid');
|
||||||
$grid.empty();
|
$grid.empty();
|
||||||
|
|
||||||
if (this.trainers.length === 0) {
|
// Use visible trainers if available (viewport sync), otherwise use all trainers
|
||||||
$grid.html('<div class="hvac-no-results"><p>No trainers found matching your criteria.</p></div>');
|
const trainersToShow = this.visibleTrainers.length > 0 || this.map
|
||||||
|
? this.visibleTrainers
|
||||||
|
: this.trainers;
|
||||||
|
|
||||||
|
if (trainersToShow.length === 0) {
|
||||||
|
const message = this.trainers.length > 0
|
||||||
|
? 'No trainers visible in this area. Zoom out or pan the map to see more.'
|
||||||
|
: 'No trainers found matching your criteria.';
|
||||||
|
$grid.html('<div class="hvac-no-results"><p>' + message + '</p></div>');
|
||||||
|
$('.hvac-load-more-wrapper').hide();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display trainers (show first 12, load more on request)
|
// Display trainers (show first 6 for compact sidebar, load more on request)
|
||||||
const displayCount = Math.min(this.trainers.length, 12);
|
const displayCount = Math.min(trainersToShow.length, 6);
|
||||||
|
|
||||||
for (let i = 0; i < displayCount; i++) {
|
for (let i = 0; i < displayCount; i++) {
|
||||||
const trainer = this.trainers[i];
|
const trainer = trainersToShow[i];
|
||||||
$grid.append(this.createTrainerCard(trainer));
|
$grid.append(this.createTrainerCard(trainer));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show load more if there are more trainers
|
// Show load more if there are more trainers
|
||||||
if (this.trainers.length > 12) {
|
if (trainersToShow.length > 6) {
|
||||||
$('.hvac-load-more-wrapper').show();
|
$('.hvac-load-more-wrapper').show();
|
||||||
} else {
|
} else {
|
||||||
$('.hvac-load-more-wrapper').hide();
|
$('.hvac-load-more-wrapper').hide();
|
||||||
|
|
@ -632,10 +701,19 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update counts display
|
* Update counts display
|
||||||
|
* @param {number} visible - Number of visible trainers (or total if no viewport filtering)
|
||||||
|
* @param {number} total - Total number of trainers (optional, for "X of Y" format)
|
||||||
*/
|
*/
|
||||||
updateCounts: function(trainers, venues) {
|
updateCounts: function(visible, total) {
|
||||||
$('#hvac-trainer-count').text(trainers || 0);
|
const $count = $('#hvac-trainer-count');
|
||||||
$('#hvac-venue-count').text(venues || 0);
|
|
||||||
|
if (total && total !== visible) {
|
||||||
|
// Show "X of Y" format when viewport is filtering
|
||||||
|
$count.text(visible + ' of ' + total);
|
||||||
|
} else {
|
||||||
|
// Show just the count
|
||||||
|
$count.text(visible || 0);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -741,17 +819,64 @@
|
||||||
loadMoreTrainers: function() {
|
loadMoreTrainers: function() {
|
||||||
const $grid = $('#hvac-trainer-grid');
|
const $grid = $('#hvac-trainer-grid');
|
||||||
const currentCount = $grid.find('.hvac-trainer-card').length;
|
const currentCount = $grid.find('.hvac-trainer-card').length;
|
||||||
const loadMore = 12;
|
const loadMore = 6; // Load 6 more at a time for sidebar
|
||||||
|
|
||||||
for (let i = currentCount; i < currentCount + loadMore && i < this.trainers.length; i++) {
|
// Use visible trainers if available (viewport sync), otherwise use all trainers
|
||||||
$grid.append(this.createTrainerCard(this.trainers[i]));
|
const trainersToShow = this.visibleTrainers.length > 0 || this.map
|
||||||
|
? this.visibleTrainers
|
||||||
|
: this.trainers;
|
||||||
|
|
||||||
|
for (let i = currentCount; i < currentCount + loadMore && i < trainersToShow.length; i++) {
|
||||||
|
$grid.append(this.createTrainerCard(trainersToShow[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($grid.find('.hvac-trainer-card').length >= this.trainers.length) {
|
if ($grid.find('.hvac-trainer-card').length >= trainersToShow.length) {
|
||||||
$('.hvac-load-more-wrapper').hide();
|
$('.hvac-load-more-wrapper').hide();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle window resize for map
|
||||||
|
*/
|
||||||
|
handleWindowResize: function() {
|
||||||
|
const self = this;
|
||||||
|
let resizeTimer;
|
||||||
|
|
||||||
|
window.addEventListener('resize', function() {
|
||||||
|
clearTimeout(resizeTimer);
|
||||||
|
resizeTimer = setTimeout(function() {
|
||||||
|
if (self.map) {
|
||||||
|
google.maps.event.trigger(self.map, 'resize');
|
||||||
|
}
|
||||||
|
}, 250);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize sidebar toggle for mobile
|
||||||
|
*/
|
||||||
|
initSidebarToggle: function() {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
$(document).on('click', '.hvac-sidebar-toggle', function() {
|
||||||
|
const $sidebar = $('.hvac-sidebar');
|
||||||
|
const $toggle = $(this);
|
||||||
|
const isCollapsed = $sidebar.hasClass('collapsed');
|
||||||
|
|
||||||
|
$sidebar.toggleClass('collapsed');
|
||||||
|
|
||||||
|
// Update ARIA attributes
|
||||||
|
$toggle.attr('aria-expanded', isCollapsed ? 'true' : 'false');
|
||||||
|
|
||||||
|
// Trigger map resize after sidebar animation
|
||||||
|
setTimeout(function() {
|
||||||
|
if (self.map) {
|
||||||
|
google.maps.event.trigger(self.map, 'resize');
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get user's location
|
* Get user's location
|
||||||
*/
|
*/
|
||||||
|
|
@ -782,6 +907,36 @@
|
||||||
this.map.setZoom(zoom || 10);
|
this.map.setZoom(zoom || 10);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sync sidebar trainer list with visible map viewport
|
||||||
|
*/
|
||||||
|
syncSidebarWithViewport: function() {
|
||||||
|
if (!this.map || this.trainers.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current map bounds
|
||||||
|
const bounds = this.map.getBounds();
|
||||||
|
if (!bounds) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter trainers to those visible in current viewport
|
||||||
|
this.visibleTrainers = this.trainers.filter(trainer => {
|
||||||
|
if (!trainer.lat || !trainer.lng) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const position = new google.maps.LatLng(trainer.lat, trainer.lng);
|
||||||
|
return bounds.contains(position);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update the sidebar grid with visible trainers
|
||||||
|
this.updateTrainerGrid();
|
||||||
|
|
||||||
|
// Update count to show visible vs total
|
||||||
|
this.updateCounts(this.visibleTrainers.length, this.trainers.length);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show loading state
|
* Show loading state
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -31,13 +31,7 @@ class HVAC_Page_Manager {
|
||||||
'parent' => null,
|
'parent' => null,
|
||||||
'capability' => null
|
'capability' => null
|
||||||
],
|
],
|
||||||
'find-a-trainer' => [
|
// Note: find-a-trainer removed - redirects to find-training (see HVAC_Route_Manager)
|
||||||
'title' => 'Find a Trainer',
|
|
||||||
'template' => 'page-find-trainer.php',
|
|
||||||
'public' => true,
|
|
||||||
'parent' => null,
|
|
||||||
'capability' => null
|
|
||||||
],
|
|
||||||
'find-training' => [
|
'find-training' => [
|
||||||
'title' => 'Find Training',
|
'title' => 'Find Training',
|
||||||
'template' => 'page-find-training.php',
|
'template' => 'page-find-training.php',
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,7 @@ final class HVAC_Plugin {
|
||||||
define('HVAC_PLUGIN_VERSION', '2.0.0');
|
define('HVAC_PLUGIN_VERSION', '2.0.0');
|
||||||
}
|
}
|
||||||
if (!defined('HVAC_VERSION')) {
|
if (!defined('HVAC_VERSION')) {
|
||||||
define('HVAC_VERSION', '2.1.7');
|
define('HVAC_VERSION', '2.2.4');
|
||||||
}
|
}
|
||||||
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');
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,8 @@ class HVAC_Route_Manager {
|
||||||
'communication-templates' => 'trainer/communication-templates',
|
'communication-templates' => 'trainer/communication-templates',
|
||||||
'communication-schedules' => 'trainer/communication-schedules',
|
'communication-schedules' => 'trainer/communication-schedules',
|
||||||
'trainer-registration' => 'trainer/registration',
|
'trainer-registration' => 'trainer/registration',
|
||||||
'find-trainer' => 'find-a-trainer', // Fix E2E testing URL mismatch
|
'find-trainer' => 'find-training', // Legacy URL redirect
|
||||||
|
'find-a-trainer' => 'find-training', // Old page redirect to new Google Maps page
|
||||||
);
|
);
|
||||||
|
|
||||||
// Parent pages that redirect to dashboards
|
// Parent pages that redirect to dashboards
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue