Fix city/zip filtering and improve map hover behavior
MLS Query Changes: - Use exact city/postal_code matching instead of radius search - Fixes city filter returning 1700+ results instead of 97 for Ramsey Cluster Endpoint: - Parse "City, SS" format to extract city name before querying - Fixes pins not showing when city filter applied Property Filters JS: - Always fit map bounds when filter changes (not just on no intersection) - Fit bounds on initial page load when URL has filters - Show temporary hover pin when marker is clustered or outside viewport - Uses markerCluster.getVisibleParent() to detect clustered markers Property Results: - Add zip code parameter handling for URL filters 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+1
-1
File diff suppressed because one or more lines are too long
@@ -840,13 +840,36 @@
|
||||
|
||||
self.hoveredPropertyId = propertyId;
|
||||
|
||||
// Check if marker exists on map
|
||||
if (self.markers[propertyId]) {
|
||||
// Marker exists - highlight it
|
||||
self.setMarkerColor(propertyId, 'blue');
|
||||
self.setMarkerZIndex(propertyId, 9000); // Blue below amber but above red
|
||||
var marker = self.markers[propertyId];
|
||||
var needsTemporaryMarker = false;
|
||||
|
||||
if (marker) {
|
||||
// Marker exists - check if it's visible (not clustered and in viewport)
|
||||
var markerLatLng = marker.getLatLng();
|
||||
var inViewport = self.map.getBounds().contains(markerLatLng);
|
||||
|
||||
// Check if marker is clustered (part of a cluster group)
|
||||
var isClustered = false;
|
||||
if (self.markerCluster && self.markerCluster.hasLayer(marker)) {
|
||||
var visibleParent = self.markerCluster.getVisibleParent(marker);
|
||||
isClustered = visibleParent && visibleParent !== marker;
|
||||
}
|
||||
|
||||
if (!isClustered && inViewport) {
|
||||
// Marker is visible as individual pin - highlight it
|
||||
self.setMarkerColor(propertyId, 'blue');
|
||||
self.setMarkerZIndex(propertyId, 9000);
|
||||
} else {
|
||||
// Marker is clustered or outside viewport - need temporary marker
|
||||
needsTemporaryMarker = true;
|
||||
}
|
||||
} else {
|
||||
// Marker is clustered - create temporary pin at property location
|
||||
// Marker doesn't exist in current dataset - need temporary marker
|
||||
needsTemporaryMarker = true;
|
||||
}
|
||||
|
||||
// Create temporary marker if needed
|
||||
if (needsTemporaryMarker) {
|
||||
var lat = $card.data('lat');
|
||||
var lng = $card.data('lng');
|
||||
|
||||
@@ -981,7 +1004,7 @@
|
||||
});
|
||||
|
||||
// City/Zip mutual exclusivity in sticky form - city clears zip
|
||||
this.$stickyForm.find('select[name="property_location"]').on('change', function() {
|
||||
this.$stickyForm.find('select[name="city"]').on('change', function() {
|
||||
if ($(this).val()) {
|
||||
self.$stickyForm.find('input[name="zip"]').val('');
|
||||
self.$mainForm.find('input[name="zip"]').val('');
|
||||
@@ -991,8 +1014,8 @@
|
||||
// City/Zip mutual exclusivity in sticky form - zip clears city
|
||||
this.$stickyForm.find('input[name="zip"]').on('input', function() {
|
||||
if ($(this).val()) {
|
||||
self.$stickyForm.find('select[name="property_location"]').val('');
|
||||
self.$mainForm.find('select[name="property_location"]').val('');
|
||||
self.$stickyForm.find('select[name="city"]').val('');
|
||||
self.$mainForm.find('select[name="city"]').val('');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1118,7 +1141,7 @@
|
||||
});
|
||||
|
||||
// City/Zip mutual exclusivity - city clears zip
|
||||
this.$form.find('select[name="property_location"]').on('change', function() {
|
||||
this.$form.find('select[name="city"]').on('change', function() {
|
||||
if ($(this).val()) {
|
||||
self.$form.find('input[name="zip"]').val('');
|
||||
// Also sync to sticky form if it exists
|
||||
@@ -1131,10 +1154,10 @@
|
||||
// City/Zip mutual exclusivity - zip clears city
|
||||
this.$form.find('input[name="zip"]').on('input', function() {
|
||||
if ($(this).val()) {
|
||||
self.$form.find('select[name="property_location"]').val('');
|
||||
self.$form.find('select[name="city"]').val('');
|
||||
// Also sync to sticky form if it exists
|
||||
if (StickyFilters && StickyFilters.$stickyForm) {
|
||||
StickyFilters.$stickyForm.find('select[name="property_location"]').val('');
|
||||
StickyFilters.$stickyForm.find('select[name="city"]').val('');
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1220,7 +1243,7 @@
|
||||
PropertyMap.currentFilters = {
|
||||
status: 'Active',
|
||||
property_type: formData.property_type || '',
|
||||
city: formData.property_location || '',
|
||||
city: formData.city || '',
|
||||
min_price: formData.min_price || '',
|
||||
max_price: formData.max_price || '',
|
||||
min_beds: formData.beds || ''
|
||||
@@ -1291,7 +1314,7 @@
|
||||
action: 'homeproz_filter_properties',
|
||||
nonce: homeprozAjax.nonce,
|
||||
property_type: formData.property_type,
|
||||
property_location: formData.property_location,
|
||||
city: formData.city,
|
||||
zip: formData.zip,
|
||||
min_price: formData.min_price,
|
||||
max_price: formData.max_price,
|
||||
@@ -1529,7 +1552,7 @@
|
||||
action: 'homeproz_filter_properties',
|
||||
nonce: homeprozAjax.nonce,
|
||||
property_type: formData.property_type,
|
||||
property_location: formData.property_location,
|
||||
city: formData.city,
|
||||
zip: formData.zip,
|
||||
min_price: formData.min_price,
|
||||
max_price: formData.max_price,
|
||||
@@ -1628,7 +1651,7 @@
|
||||
getFormData: function() {
|
||||
return {
|
||||
property_type: this.$form.find('[name="property_type"]').val() || '',
|
||||
property_location: this.$form.find('[name="property_location"]').val() || '',
|
||||
city: this.$form.find('[name="city"]').val() || '',
|
||||
zip: this.$form.find('[name="zip"]').val() || '',
|
||||
min_price: this.$form.find('[name="min_price"]').val() || '',
|
||||
max_price: this.$form.find('[name="max_price"]').val() || '',
|
||||
@@ -1729,7 +1752,7 @@
|
||||
return {
|
||||
// Filters
|
||||
property_type: raw.property_type || '',
|
||||
property_location: raw.property_location || '',
|
||||
city: raw.city || '',
|
||||
zip: raw.zip || '',
|
||||
min_price: raw.min_price || '',
|
||||
max_price: raw.max_price || '',
|
||||
@@ -1792,7 +1815,7 @@
|
||||
PropertyMap.currentFilters = {
|
||||
status: 'Active',
|
||||
property_type: formData.property_type || '',
|
||||
city: formData.property_location || '',
|
||||
city: formData.city || '',
|
||||
min_price: formData.min_price || '',
|
||||
max_price: formData.max_price || '',
|
||||
min_beds: formData.beds || ''
|
||||
@@ -1812,7 +1835,7 @@
|
||||
data: {
|
||||
action: 'homeproz_get_filter_bounds',
|
||||
property_type: formData.property_type,
|
||||
city: formData.property_location,
|
||||
city: formData.city,
|
||||
min_price: formData.min_price,
|
||||
max_price: formData.max_price,
|
||||
min_beds: formData.beds
|
||||
@@ -1820,35 +1843,19 @@
|
||||
success: function(response) {
|
||||
if (response.success && response.data) {
|
||||
var filterBounds = response.data;
|
||||
var mapBounds = PropertyMap.map.getBounds();
|
||||
|
||||
// Check if map view intersects with filter bounds
|
||||
var filterLatLngBounds = L.latLngBounds(
|
||||
[filterBounds.sw_lat, filterBounds.sw_lng],
|
||||
[filterBounds.ne_lat, filterBounds.ne_lng]
|
||||
// Add 10% padding to bounds
|
||||
var latPadding = (filterBounds.ne_lat - filterBounds.sw_lat) * 0.1;
|
||||
var lngPadding = (filterBounds.ne_lng - filterBounds.sw_lng) * 0.1;
|
||||
|
||||
var paddedBounds = L.latLngBounds(
|
||||
[filterBounds.sw_lat - latPadding, filterBounds.sw_lng - lngPadding],
|
||||
[filterBounds.ne_lat + latPadding, filterBounds.ne_lng + lngPadding]
|
||||
);
|
||||
|
||||
// Check if ANY of the filter bounds corners are visible in the map
|
||||
// OR if the map view is fully contained within the filter bounds
|
||||
var mapIntersects = mapBounds.intersects(filterLatLngBounds);
|
||||
|
||||
if (!mapIntersects) {
|
||||
// Add 10% padding to bounds
|
||||
var latPadding = (filterBounds.ne_lat - filterBounds.sw_lat) * 0.1;
|
||||
var lngPadding = (filterBounds.ne_lng - filterBounds.sw_lng) * 0.1;
|
||||
|
||||
var paddedBounds = L.latLngBounds(
|
||||
[filterBounds.sw_lat - latPadding, filterBounds.sw_lng - lngPadding],
|
||||
[filterBounds.ne_lat + latPadding, filterBounds.ne_lng + lngPadding]
|
||||
);
|
||||
|
||||
// Reposition map to show filtered properties
|
||||
PropertyMap.map.fitBounds(paddedBounds);
|
||||
// The moveend event will trigger loadClusters and updateFromMap
|
||||
} else {
|
||||
// Map already shows relevant area, just reload clusters and properties
|
||||
PropertyMap.loadClusters();
|
||||
}
|
||||
// Always fit map to show all filtered properties
|
||||
PropertyMap.map.fitBounds(paddedBounds);
|
||||
// The moveend event will trigger loadClusters and updateFromMap
|
||||
} else {
|
||||
// No properties found with these filters, just reload
|
||||
PropertyMap.loadClusters();
|
||||
@@ -1890,7 +1897,7 @@
|
||||
var mapFilters = {
|
||||
status: 'Active',
|
||||
property_type: formData.property_type || '',
|
||||
city: formData.property_location || '',
|
||||
city: formData.city || '',
|
||||
min_price: formData.min_price || '',
|
||||
max_price: formData.max_price || '',
|
||||
min_beds: formData.beds || ''
|
||||
@@ -1901,6 +1908,9 @@
|
||||
// Restore state if we have pending state to restore
|
||||
if (PropertyFilters.pendingRestoreState) {
|
||||
PropertyFilters.restoreState();
|
||||
} else if (formData.city || formData.zip || formData.property_type || formData.min_price || formData.max_price || formData.beds) {
|
||||
// Filters present from URL but no saved map state - fit to filter bounds
|
||||
PropertyFilters.onFilterChange();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1940,7 +1950,7 @@
|
||||
var mapFilters = {
|
||||
status: 'Active',
|
||||
property_type: formData.property_type || '',
|
||||
city: formData.property_location || '',
|
||||
city: formData.city || '',
|
||||
min_price: formData.min_price || '',
|
||||
max_price: formData.max_price || '',
|
||||
min_beds: formData.beds || ''
|
||||
@@ -2480,7 +2490,7 @@
|
||||
action: 'homeproz_filter_properties',
|
||||
nonce: homeprozAjax.nonce,
|
||||
property_type: formData.property_type,
|
||||
property_location: formData.property_location,
|
||||
city: formData.city,
|
||||
zip: formData.zip,
|
||||
min_price: formData.min_price,
|
||||
max_price: formData.max_price,
|
||||
|
||||
@@ -26,7 +26,8 @@ if (!function_exists('mls_get_properties')) {
|
||||
// Get filter values from URL
|
||||
$current_type = isset($_GET['property_type']) ? sanitize_text_field($_GET['property_type']) : '';
|
||||
$current_status = isset($_GET['property_status']) ? sanitize_text_field($_GET['property_status']) : 'Active';
|
||||
$current_location = isset($_GET['property_location']) ? sanitize_text_field($_GET['property_location']) : '';
|
||||
$current_location = isset($_GET['city']) ? sanitize_text_field($_GET['city']) : '';
|
||||
$current_zip = isset($_GET['zip']) ? sanitize_text_field($_GET['zip']) : '';
|
||||
$current_min_price = isset($_GET['min_price']) ? intval($_GET['min_price']) : '';
|
||||
$current_max_price = isset($_GET['max_price']) ? intval($_GET['max_price']) : '';
|
||||
$current_beds = isset($_GET['beds']) ? intval($_GET['beds']) : '';
|
||||
@@ -49,7 +50,15 @@ if ($current_type) {
|
||||
$filter_args['property_type'] = $current_type;
|
||||
}
|
||||
if ($current_location) {
|
||||
$filter_args['city'] = $current_location;
|
||||
// Parse "City, SS" format - extract just the city name
|
||||
$city_name = $current_location;
|
||||
if (preg_match('/^(.+),\s*([A-Z]{2})$/', $current_location, $matches)) {
|
||||
$city_name = $matches[1];
|
||||
}
|
||||
$filter_args['city'] = $city_name;
|
||||
} elseif ($current_zip) {
|
||||
// Zip code filter (only if city not set)
|
||||
$filter_args['postal_code'] = $current_zip;
|
||||
}
|
||||
// Use lat/lng radius search if provided (takes precedence over location)
|
||||
if ($center_lat && $center_lng) {
|
||||
|
||||
Reference in New Issue
Block a user