feat(find-training): Reorder tabs (Events first), add marker highlighting and map reset

- Move Events tab to first/default position, update search placeholder and
  skip link to match
- Add highlighted icon variants for all marker types (larger scale, brighter
  stroke, higher zIndex) that activate when their category tab is selected
- Add reset map button (bottom-right) to restore original zoom/bounds
- Fix mobile sidebar overflow caused by long event titles pushing beyond
  viewport (overflow-x: hidden, overflow-y: visible)
- Bump version to 2.2.17

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
ben 2026-02-09 18:53:44 -04:00
parent 4104c80669
commit 9dbe472c45
4 changed files with 203 additions and 22 deletions

View file

@ -668,6 +668,37 @@ body .hvac-find-training-page {
background: var(--hvac-event-color); background: var(--hvac-event-color);
} }
/* Reset Map View Button */
.hvac-reset-map-btn {
position: absolute;
bottom: 24px;
right: 12px;
width: 36px;
height: 36px;
background: #fff;
border: none;
border-radius: 6px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
cursor: pointer;
z-index: 10;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.2s, box-shadow 0.2s;
}
.hvac-reset-map-btn:hover {
background: #f0f0f0;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
}
.hvac-reset-map-btn .dashicons {
font-size: 18px;
width: 18px;
height: 18px;
color: var(--hvac-text-muted);
}
/* Map Toggles Overlay */ /* Map Toggles Overlay */
.hvac-map-toggles { .hvac-map-toggles {
position: absolute; position: absolute;
@ -1264,7 +1295,8 @@ body .hvac-find-training-page {
.hvac-sidebar { .hvac-sidebar {
border-right: none; border-right: none;
border-top: 1px solid var(--hvac-border); border-top: 1px solid var(--hvac-border);
overflow: visible !important; overflow-x: hidden !important;
overflow-y: visible !important;
max-height: none !important; max-height: none !important;
} }
@ -1732,10 +1764,12 @@ body .hvac-find-training-page {
border-bottom: 2px solid var(--hvac-border); border-bottom: 2px solid var(--hvac-border);
margin: 0 -16px; margin: 0 -16px;
padding: 0 16px; padding: 0 16px;
overflow: hidden;
} }
.hvac-tab { .hvac-tab {
flex: 1; flex: 1;
min-width: 0;
padding: 10px 8px; padding: 10px 8px;
background: transparent; background: transparent;
border: none; border: none;
@ -1747,6 +1781,8 @@ body .hvac-find-training-page {
cursor: pointer; cursor: pointer;
transition: all 0.2s; transition: all 0.2s;
white-space: nowrap; white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} }
.hvac-tab:hover { .hvac-tab:hover {

View file

@ -39,7 +39,7 @@
visibleEvents: [], visibleEvents: [],
// Active tab // Active tab
activeTab: 'trainers', activeTab: 'events',
// Items per page for load more // Items per page for load more
itemsPerPage: 6, itemsPerPage: 6,
@ -327,6 +327,9 @@
// Fit bounds if we have markers // Fit bounds if we have markers
this.fitBounds(); this.fitBounds();
// Highlight markers for active tab
this.highlightMarkersForTab(this.activeTab);
}, },
/** /**
@ -510,6 +513,122 @@
}; };
}, },
/**
* Get highlighted trainer marker icon (larger, brighter stroke)
*/
getTrainerIconHighlighted: function() {
if (hvacFindTraining.marker_icons?.trainer) {
return {
url: hvacFindTraining.marker_icons.trainer,
scaledSize: new google.maps.Size(40, 40),
anchor: new google.maps.Point(20, 20)
};
}
return {
path: google.maps.SymbolPath.CIRCLE,
fillColor: '#f0f7e8',
fillOpacity: 1,
strokeColor: '#3d6e00',
strokeWeight: 3,
scale: 14
};
},
/**
* Get highlighted champion marker icon (larger, brighter stroke)
*/
getChampionIconHighlighted: function() {
return {
path: google.maps.SymbolPath.CIRCLE,
fillColor: '#f0f7e8',
fillOpacity: 1,
strokeColor: '#e0e0e0',
strokeWeight: 3.5,
scale: 14
};
},
/**
* Get highlighted venue marker icon (larger, brighter stroke)
*/
getVenueIconHighlighted: function() {
if (hvacFindTraining.marker_icons?.venue) {
return {
url: hvacFindTraining.marker_icons.venue,
scaledSize: new google.maps.Size(40, 40),
anchor: new google.maps.Point(20, 20)
};
}
return {
path: google.maps.SymbolPath.BACKWARD_CLOSED_ARROW,
fillColor: '#89c92e',
fillOpacity: 1,
strokeColor: '#e0e0e0',
strokeWeight: 3,
scale: 8
};
},
/**
* Get highlighted event marker icon (larger, brighter stroke)
*/
getEventIconHighlighted: function() {
if (hvacFindTraining.marker_icons?.event) {
return {
url: hvacFindTraining.marker_icons.event,
scaledSize: new google.maps.Size(40, 40),
anchor: new google.maps.Point(20, 20)
};
}
return {
path: google.maps.SymbolPath.CIRCLE,
fillColor: '#0ebaa6',
fillOpacity: 1,
strokeColor: '#e0e0e0',
strokeWeight: 3,
scale: 11
};
},
/**
* Highlight markers for the active tab category.
* Active tab markers get larger icons + higher zIndex.
* Inactive tab markers revert to normal icons + default zIndex.
*/
highlightMarkersForTab: function(tab) {
if (!this.map) return;
const DEFAULT_Z = 1;
const HIGHLIGHT_Z = 100;
// Trainer markers
const trainerHighlighted = (tab === 'trainers');
this.trainerMarkers.forEach(function(marker) {
const isChampion = marker.trainerData && marker.trainerData.is_champion;
if (trainerHighlighted) {
marker.setIcon(isChampion ? HVACTrainingMap.getChampionIconHighlighted() : HVACTrainingMap.getTrainerIconHighlighted());
marker.setZIndex(HIGHLIGHT_Z);
} else {
marker.setIcon(isChampion ? HVACTrainingMap.getChampionIcon() : HVACTrainingMap.getTrainerIcon());
marker.setZIndex(DEFAULT_Z);
}
});
// Venue markers
const venueHighlighted = (tab === 'venues');
this.venueMarkers.forEach(function(marker) {
marker.setIcon(venueHighlighted ? HVACTrainingMap.getVenueIconHighlighted() : HVACTrainingMap.getVenueIcon());
marker.setZIndex(venueHighlighted ? HIGHLIGHT_Z : DEFAULT_Z);
});
// Event markers
const eventHighlighted = (tab === 'events');
this.eventMarkers.forEach(function(marker) {
marker.setIcon(eventHighlighted ? HVACTrainingMap.getEventIconHighlighted() : HVACTrainingMap.getEventIcon());
marker.setZIndex(eventHighlighted ? HIGHLIGHT_Z : DEFAULT_Z);
});
},
/** /**
* Initialize marker clustering * Initialize marker clustering
*/ */
@ -1157,6 +1276,11 @@
$('#hvac-load-more').on('click', function() { $('#hvac-load-more').on('click', function() {
self.loadMoreTrainers(); self.loadMoreTrainers();
}); });
// Reset map view button
$('#hvac-reset-map').on('click', function() {
self.resetMapView();
});
}, },
/** /**
@ -1362,6 +1486,14 @@
this.map.setZoom(zoom || 10); this.map.setZoom(zoom || 10);
}, },
/**
* Reset map to fit all markers (or default view if none)
*/
resetMapView: function() {
if (!this.map) return;
this.fitBounds();
},
/** /**
* Sync sidebar lists with visible map viewport * Sync sidebar lists with visible map viewport
*/ */
@ -1417,14 +1549,16 @@
* Show loading state * Show loading state
*/ */
showLoading: function() { showLoading: function() {
$('#hvac-trainer-grid').html('<div class="hvac-grid-loading"><span class="dashicons dashicons-update-alt hvac-spin"></span> Loading trainers...</div>'); const gridId = this.activeTab === 'events' ? '#hvac-event-grid' : this.activeTab === 'venues' ? '#hvac-venue-grid' : '#hvac-trainer-grid';
const label = this.activeTab === 'events' ? 'events' : this.activeTab === 'venues' ? 'venues' : 'trainers';
$(gridId).html('<div class="hvac-grid-loading"><span class="dashicons dashicons-update-alt hvac-spin"></span> Loading ' + label + '...</div>');
}, },
/** /**
* Hide loading state * Hide loading state
*/ */
hideLoading: function() { hideLoading: function() {
$('#hvac-trainer-grid .hvac-grid-loading').remove(); $('.hvac-grid-loading').remove();
}, },
/** /**
@ -1507,6 +1641,9 @@
// Update search placeholder // Update search placeholder
this.updateSearchPlaceholder(tab); this.updateSearchPlaceholder(tab);
// Highlight markers for active tab
this.highlightMarkersForTab(tab);
// Reset displayed count for current tab // Reset displayed count for current tab
this.displayedCounts[tab] = 0; this.displayedCounts[tab] = 0;

View file

@ -112,10 +112,10 @@ final class HVAC_Plugin {
*/ */
private function defineConstants(): void { private function defineConstants(): void {
if (!defined('HVAC_PLUGIN_VERSION')) { if (!defined('HVAC_PLUGIN_VERSION')) {
define('HVAC_PLUGIN_VERSION', '2.2.14'); define('HVAC_PLUGIN_VERSION', '2.2.17');
} }
if (!defined('HVAC_VERSION')) { if (!defined('HVAC_VERSION')) {
define('HVAC_VERSION', '2.2.14'); define('HVAC_VERSION', '2.2.17');
} }
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');

View file

@ -22,15 +22,15 @@ $api_key_configured = $find_training->is_api_key_configured();
<div class="hvac-find-training-page"> <div class="hvac-find-training-page">
<!-- Skip link for accessibility --> <!-- Skip link for accessibility -->
<a href="#hvac-trainer-grid" class="hvac-skip-link">Skip to trainer results</a> <a href="#hvac-event-grid" class="hvac-skip-link">Skip to event results</a>
<!-- Compact Filter Bar --> <!-- Compact Filter Bar -->
<div class="hvac-filter-bar" role="search" aria-label="Filter trainers and venues"> <div class="hvac-filter-bar" role="search" aria-label="Filter events, trainers, and venues">
<div class="hvac-filter-bar-inner"> <div class="hvac-filter-bar-inner">
<!-- Search --> <!-- Search -->
<div class="hvac-filter-item hvac-filter-search"> <div class="hvac-filter-item hvac-filter-search">
<span class="dashicons dashicons-search" aria-hidden="true"></span> <span class="dashicons dashicons-search" aria-hidden="true"></span>
<input type="text" id="hvac-training-search" placeholder="Search trainers..." aria-label="Search trainers"> <input type="text" id="hvac-training-search" placeholder="Search events..." aria-label="Search events">
</div> </div>
<!-- Info Button --> <!-- Info Button -->
@ -137,15 +137,15 @@ $api_key_configured = $find_training->is_api_key_configured();
<div class="hvac-sidebar-header"> <div class="hvac-sidebar-header">
<!-- Tab Navigation --> <!-- Tab Navigation -->
<div class="hvac-sidebar-tabs" role="tablist" aria-label="Browse by category"> <div class="hvac-sidebar-tabs" role="tablist" aria-label="Browse by category">
<button role="tab" class="hvac-tab active" data-tab="trainers" aria-selected="true" aria-controls="hvac-panel-trainers"> <button role="tab" class="hvac-tab active" data-tab="events" aria-selected="true" aria-controls="hvac-panel-events">
Events (<span data-count="events">0</span>)
</button>
<button role="tab" class="hvac-tab" data-tab="trainers" aria-selected="false" aria-controls="hvac-panel-trainers">
Trainers (<span data-count="trainers">0</span>) Trainers (<span data-count="trainers">0</span>)
</button> </button>
<button role="tab" class="hvac-tab" data-tab="venues" aria-selected="false" aria-controls="hvac-panel-venues"> <button role="tab" class="hvac-tab" data-tab="venues" aria-selected="false" aria-controls="hvac-panel-venues">
Venues (<span data-count="venues">0</span>) Venues (<span data-count="venues">0</span>)
</button> </button>
<button role="tab" class="hvac-tab" data-tab="events" aria-selected="false" aria-controls="hvac-panel-events">
Events (<span data-count="events">0</span>)
</button>
</div> </div>
<!-- Visibility Toggles (moved from map overlay) --> <!-- Visibility Toggles (moved from map overlay) -->
@ -175,26 +175,26 @@ $api_key_configured = $find_training->is_api_key_configured();
</div> </div>
<div id="hvac-sidebar-content" class="hvac-sidebar-content"> <div id="hvac-sidebar-content" class="hvac-sidebar-content">
<!-- Trainers Panel --> <!-- Events Panel -->
<div role="tabpanel" id="hvac-panel-trainers" class="hvac-tab-panel active" aria-labelledby="tab-trainers"> <div role="tabpanel" id="hvac-panel-events" class="hvac-tab-panel active" aria-labelledby="tab-events">
<div id="hvac-trainer-grid" class="hvac-item-list"> <div id="hvac-event-grid" class="hvac-item-list">
<div class="hvac-grid-loading"> <div class="hvac-grid-loading">
<span class="dashicons dashicons-update-alt hvac-spin" aria-hidden="true"></span> <span class="dashicons dashicons-update-alt hvac-spin" aria-hidden="true"></span>
Loading trainers... Loading events...
</div> </div>
</div> </div>
</div> </div>
<!-- Trainers Panel -->
<div role="tabpanel" id="hvac-panel-trainers" class="hvac-tab-panel" aria-labelledby="tab-trainers" hidden>
<div id="hvac-trainer-grid" class="hvac-item-list"></div>
</div>
<!-- Venues Panel --> <!-- Venues Panel -->
<div role="tabpanel" id="hvac-panel-venues" class="hvac-tab-panel" aria-labelledby="tab-venues" hidden> <div role="tabpanel" id="hvac-panel-venues" class="hvac-tab-panel" aria-labelledby="tab-venues" hidden>
<div id="hvac-venue-grid" class="hvac-item-list"></div> <div id="hvac-venue-grid" class="hvac-item-list"></div>
</div> </div>
<!-- Events Panel -->
<div role="tabpanel" id="hvac-panel-events" class="hvac-tab-panel" aria-labelledby="tab-events" hidden>
<div id="hvac-event-grid" class="hvac-item-list"></div>
</div>
<!-- Load More Button --> <!-- Load More Button -->
<div class="hvac-load-more-wrapper" style="display: none;"> <div class="hvac-load-more-wrapper" style="display: none;">
<button type="button" id="hvac-load-more" class="hvac-btn-secondary"> <button type="button" id="hvac-load-more" class="hvac-btn-secondary">
@ -229,6 +229,11 @@ $api_key_configured = $find_training->is_api_key_configured();
<?php endif; ?> <?php endif; ?>
</div> </div>
<!-- Reset Map View Button -->
<button type="button" id="hvac-reset-map" class="hvac-reset-map-btn" aria-label="Reset map view" title="Reset map view">
<span class="dashicons dashicons-image-rotate" aria-hidden="true"></span>
</button>
<!-- Map Legend Overlay --> <!-- Map Legend Overlay -->
<div class="hvac-map-legend"> <div class="hvac-map-legend">
<div class="hvac-legend-item"> <div class="hvac-legend-item">
@ -335,6 +340,9 @@ $api_key_configured = $find_training->is_api_key_configured();
<label for="venue-contact-message">Message</label> <label for="venue-contact-message">Message</label>
<textarea id="venue-contact-message" name="message" rows="4" placeholder="Tell us about your training needs..."></textarea> <textarea id="venue-contact-message" name="message" rows="4" placeholder="Tell us about your training needs..."></textarea>
</div> </div>
<div class="hvac-form-group hvac-recaptcha-wrapper">
<div class="g-recaptcha" data-sitekey="<?php echo esc_attr(class_exists('HVAC_Recaptcha') ? HVAC_Recaptcha::SITE_KEY : ''); ?>"></div>
</div>
<button type="submit" class="hvac-btn-primary">Send Message</button> <button type="submit" class="hvac-btn-primary">Send Message</button>
</form> </form>
<div class="hvac-form-success" style="display: none;"> <div class="hvac-form-success" style="display: none;">