Property filters overhaul: status sorting, simplified UI

- Remove status dropdown (always show all properties)
- Remove sort dropdown (use status-based sorting)
- Sort order: Active > Pending > Sold, then by modified date
- Map view: half height, 2-column property grid
- Beds field same width as others
- Add CLAUDE.md documentation for property system

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Hanson.xyz Dev
2025-11-30 19:52:50 -06:00
parent 1dd874091c
commit 775c57a678
1182 changed files with 310135 additions and 257 deletions
+9 -9
View File
@@ -15,16 +15,16 @@
RewriteRule (.+)\.jpg$ /wp-content/uploads-webpc/$1.jpg.avif [NC,T=image/avif,L]
RewriteCond %{HTTP_ACCEPT} image/avif
RewriteCond %{REQUEST_FILENAME} -f
RewriteCond %{DOCUMENT_ROOT}/wp-content/uploads-webpc/$1.jpeg.avif -f
RewriteRule (.+)\.jpeg$ /wp-content/uploads-webpc/$1.jpeg.avif [NC,T=image/avif,L]
RewriteCond %{HTTP_ACCEPT} image/avif
RewriteCond %{REQUEST_FILENAME} -f
RewriteCond %{DOCUMENT_ROOT}/wp-content/uploads-webpc/$1.png.avif -f
RewriteRule (.+)\.png$ /wp-content/uploads-webpc/$1.png.avif [NC,T=image/avif,L]
RewriteCond %{HTTP_ACCEPT} image/avif
RewriteCond %{REQUEST_FILENAME} -f
RewriteCond %{DOCUMENT_ROOT}/wp-content/uploads-webpc/$1.webp.avif -f
RewriteRule (.+)\.webp$ /wp-content/uploads-webpc/$1.webp.avif [NC,T=image/avif,L]
RewriteCond %{HTTP_ACCEPT} image/avif
RewriteCond %{REQUEST_FILENAME} -f
RewriteCond %{DOCUMENT_ROOT}/wp-content/uploads-webpc/$1.jpeg.avif -f
RewriteRule (.+)\.jpeg$ /wp-content/uploads-webpc/$1.jpeg.avif [NC,T=image/avif,L]
RewriteCond %{HTTP_ACCEPT} image/webp
RewriteCond %{REQUEST_FILENAME} -f
@@ -32,15 +32,15 @@
RewriteRule (.+)\.jpg$ /wp-content/uploads-webpc/$1.jpg.webp [NC,T=image/webp,L]
RewriteCond %{HTTP_ACCEPT} image/webp
RewriteCond %{REQUEST_FILENAME} -f
RewriteCond %{DOCUMENT_ROOT}/wp-content/uploads-webpc/$1.jpeg.webp -f
RewriteRule (.+)\.jpeg$ /wp-content/uploads-webpc/$1.jpeg.webp [NC,T=image/webp,L]
RewriteCond %{HTTP_ACCEPT} image/webp
RewriteCond %{REQUEST_FILENAME} -f
RewriteCond %{DOCUMENT_ROOT}/wp-content/uploads-webpc/$1.png.webp -f
RewriteRule (.+)\.png$ /wp-content/uploads-webpc/$1.png.webp [NC,T=image/webp,L]
RewriteCond %{HTTP_ACCEPT} image/webp
RewriteCond %{REQUEST_FILENAME} -f
RewriteCond %{DOCUMENT_ROOT}/wp-content/uploads-webpc/$1.jpeg.webp -f
RewriteRule (.+)\.jpeg$ /wp-content/uploads-webpc/$1.jpeg.webp [NC,T=image/webp,L]
</IfModule>
<IfModule mod_headers.c>
<FilesMatch "(?i)\.(jpg|jpeg|png|webp)(\.(webp|avif))?$">
<FilesMatch "(?i)\.(jpg|png|webp|jpeg)(\.(webp|avif))?$">
Header always set Cache-Control "private"
Header always set X-LiteSpeed-Cache-Control "no-cache"
Header append Vary "Accept"
+162 -5
View File
@@ -2,7 +2,7 @@
/**
* Property Archive Template
*
* Displays the properties listing page
* Displays the properties listing page with optional map view
*
* @package HomeProz
*/
@@ -13,9 +13,12 @@ if (!defined('ABSPATH')) {
}
get_header();
// Check if map view is enabled
$show_map = isset($_GET['view']) && $_GET['view'] === 'map';
?>
<main id="primary" class="site-main">
<main id="primary" class="site-main property-archive-main">
<!-- Archive Hero -->
<section class="archive-hero">
<div class="container">
@@ -28,12 +31,166 @@ get_header();
<!-- Filters -->
<?php get_template_part('template-parts/property/property-filters'); ?>
<!-- Results -->
<div id="property-results">
<?php get_template_part('template-parts/property/property-results'); ?>
<!-- View Toggle -->
<div class="view-toggle">
<a href="<?php echo esc_url(remove_query_arg('view')); ?>" class="view-toggle-btn <?php echo !$show_map ? 'active' : ''; ?>">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<rect x="3" y="3" width="7" height="7"/>
<rect x="14" y="3" width="7" height="7"/>
<rect x="3" y="14" width="7" height="7"/>
<rect x="14" y="14" width="7" height="7"/>
</svg>
<span>Grid</span>
</a>
<a href="<?php echo esc_url(add_query_arg('view', 'map')); ?>" class="view-toggle-btn <?php echo $show_map ? 'active' : ''; ?>">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<polygon points="1 6 1 22 8 18 16 22 23 18 23 2 16 6 8 2 1 6"/>
<line x1="8" y1="2" x2="8" y2="18"/>
<line x1="16" y1="6" x2="16" y2="22"/>
</svg>
<span>Map</span>
</a>
</div>
<?php if ($show_map) : ?>
<!-- Map + List View -->
<div class="property-map-layout">
<div class="property-map-container">
<div id="property-map" class="property-map">
<!-- Leaflet map will be initialized here -->
</div>
</div>
<div class="property-list-container">
<div id="property-results">
<?php get_template_part('template-parts/property/property-results'); ?>
</div>
</div>
</div>
<?php else : ?>
<!-- Grid View -->
<div id="property-results">
<?php get_template_part('template-parts/property/property-results'); ?>
</div>
<?php endif; ?>
</div>
</main>
<?php if ($show_map) : ?>
<!-- Leaflet CSS -->
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/>
<!-- Leaflet JS -->
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
<script>
(function($) {
// Wait for DOM
$(document).ready(function() {
// Initialize map centered on Albert Lea area
var map = L.map('property-map').setView([43.6480, -93.3685], 10);
// Add OpenStreetMap tiles
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
}).addTo(map);
// Property data from PHP
var properties = [
<?php
// Get all properties for map markers
$map_properties = new WP_Query(array(
'post_type' => 'property',
'posts_per_page' => -1,
'tax_query' => array(
array(
'taxonomy' => 'property_status',
'field' => 'slug',
'terms' => array('active', 'pending'),
),
),
));
$markers = array();
if ($map_properties->have_posts()) :
while ($map_properties->have_posts()) :
$map_properties->the_post();
$city = get_field('city');
$price = get_field('property_price');
$address = get_field('street_address');
// Approximate coordinates based on city (can be enhanced with geocoding)
$city_coords = array(
'Albert Lea' => array(43.6480, -93.3685),
'Austin' => array(43.6666, -92.9746),
'Glenville' => array(43.5733, -93.2779),
'Emmons' => array(43.5013, -93.4896),
'Clarks Grove' => array(43.7627, -93.3196),
'Alden' => array(43.6719, -93.5768),
'Hartland' => array(43.8030, -93.4846),
'Geneva' => array(43.8255, -93.2682),
'Owatonna' => array(44.0838, -93.2260),
'Faribault' => array(44.2949, -93.2688),
'Rochester' => array(44.0234, -92.4699),
'Mankato' => array(44.1636, -93.9994),
);
// Get coords for city, default to Albert Lea
$coords = isset($city_coords[$city]) ? $city_coords[$city] : $city_coords['Albert Lea'];
// Add small random offset to prevent overlapping markers
$lat = $coords[0] + (rand(-50, 50) / 10000);
$lng = $coords[1] + (rand(-50, 50) / 10000);
$markers[] = array(
'id' => get_the_ID(),
'lat' => $lat,
'lng' => $lng,
'title' => get_the_title(),
'price' => '$' . number_format($price),
'address' => $address . ', ' . $city,
'url' => get_permalink(),
);
endwhile;
wp_reset_postdata();
endif;
// Output JSON
foreach ($markers as $i => $marker) {
echo json_encode($marker);
if ($i < count($markers) - 1) echo ',';
}
?>
];
// Custom marker icon
var propertyIcon = L.divIcon({
className: 'property-marker',
html: '<div class="marker-pin"></div>',
iconSize: [30, 40],
iconAnchor: [15, 40],
popupAnchor: [0, -40]
});
// Add markers for each property
properties.forEach(function(prop) {
if (prop.lat && prop.lng) {
var marker = L.marker([prop.lat, prop.lng], {icon: propertyIcon}).addTo(map);
marker.bindPopup(
'<div class="map-popup">' +
'<strong>' + prop.price + '</strong><br>' +
'<span>' + prop.address + '</span><br>' +
'<a href="' + prop.url + '">View Details</a>' +
'</div>'
);
}
});
// Fit bounds if we have properties
if (properties.length > 0) {
var bounds = L.latLngBounds(properties.map(function(p) { return [p.lat, p.lng]; }));
map.fitBounds(bounds, { padding: [50, 50] });
}
});
})(jQuery);
</script>
<?php endif; ?>
<?php
get_footer();
Binary file not shown.

After

Width:  |  Height:  |  Size: 39 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -116
View File
@@ -67,6 +67,7 @@ $featured_commercial = new WP_Query(array(
'primary_cta_url' => home_url('/properties/'),
'secondary_cta_text' => 'Contact Us',
'secondary_cta_url' => home_url('/contact/'),
'background_image' => get_template_directory_uri() . '/assets/images/hero.webp',
'size' => 'large',
));
@@ -140,122 +141,6 @@ $featured_commercial = new WP_Query(array(
</div>
</section>
<!-- Why Choose Us / Features Section -->
<section class="features-section">
<div class="container">
<header class="features-section-header">
<h2 class="features-section-title">Why HomeProz?</h2>
<p class="features-section-subtitle">We're committed to helping you achieve your real estate goals</p>
</header>
<div class="features-grid">
<?php
get_template_part('template-parts/components/feature-block', null, array(
'icon' => 'local-expertise',
'title' => 'Local Expertise',
'text' => 'Deep knowledge of the Albert Lea area and surrounding Minnesota communities helps us find the perfect property for you.',
));
get_template_part('template-parts/components/feature-block', null, array(
'icon' => 'personalized',
'title' => 'Personalized Service',
'text' => 'We take the time to understand your unique needs and provide tailored guidance throughout your real estate journey.',
));
get_template_part('template-parts/components/feature-block', null, array(
'icon' => 'results',
'title' => 'Results-Driven',
'text' => 'Our track record speaks for itself. We work tirelessly to get you the best possible outcome, whether buying or selling.',
));
?>
</div>
</div>
</section>
<!-- Team Preview Section -->
<section class="team-section">
<div class="container">
<header class="team-section-header">
<h2 class="team-section-title">Meet Our Agents</h2>
<p class="team-section-subtitle">A dedicated team of real estate professionals ready to serve you</p>
</header>
<div class="team-grid">
<?php
// Team member data - can be replaced with ACF repeater or CPT later
$team_members = array(
array(
'name' => 'Heidi Johnson',
'title' => 'REALTOR',
'phone' => '507-402-2310',
'email' => 'heidi@homeprozrealestate.com',
),
array(
'name' => 'Kyra Johnson',
'title' => 'REALTOR',
'phone' => '507-516-4870',
'email' => 'kyra@homeprozrealestate.com',
),
array(
'name' => 'John Hill',
'title' => 'REALTOR',
'phone' => '507-383-1738',
'email' => 'john@homeprozrealestate.com',
),
array(
'name' => 'Mindy Moreno',
'title' => 'REALTOR',
'phone' => '507-438-5900',
'email' => 'mindy@homeprozrealestate.com',
),
);
foreach ($team_members as $member) :
get_template_part('template-parts/components/agent-card', null, $member);
endforeach;
?>
</div>
<div class="section-footer">
<a href="<?php echo esc_url(home_url('/about/')); ?>" class="btn btn-secondary">
Learn More About Us
</a>
</div>
</div>
</section>
<!-- Testimonials Section -->
<section class="testimonials-section">
<div class="container">
<header class="testimonials-section-header">
<h2 class="testimonials-section-title">What Our Clients Say</h2>
<p class="testimonials-section-subtitle">Real stories from satisfied buyers and sellers</p>
</header>
<div class="testimonials-grid">
<?php
// Testimonial data - can be replaced with ACF repeater or CPT later
$testimonials = array(
array(
'quote' => 'HomeProz made our home buying experience incredibly smooth. They were patient, knowledgeable, and always available to answer our questions. We found our dream home faster than we ever expected!',
'name' => 'Sarah & Mike T.',
'location' => 'Home Buyers, Albert Lea',
),
array(
'quote' => 'Selling our home was stress-free thanks to the HomeProz team. They handled everything professionally and got us a great price. Highly recommend!',
'name' => 'Robert K.',
'location' => 'Home Seller, Austin',
),
);
foreach ($testimonials as $testimonial) :
get_template_part('template-parts/components/testimonial', null, $testimonial);
endforeach;
?>
</div>
</div>
</section>
<?php
// Contact CTA Section
get_template_part('template-parts/components/cta-section', null, array(
@@ -107,30 +107,22 @@ function homeproz_ajax_filter_properties() {
}
}
// Sorting
switch ($sort) {
case 'oldest':
$args['orderby'] = 'date';
$args['order'] = 'ASC';
break;
case 'price_low':
$args['meta_key'] = 'property_price';
$args['orderby'] = 'meta_value_num';
$args['order'] = 'ASC';
break;
case 'price_high':
$args['meta_key'] = 'property_price';
$args['orderby'] = 'meta_value_num';
$args['order'] = 'DESC';
break;
case 'newest':
default:
$args['orderby'] = 'date';
$args['order'] = 'DESC';
break;
}
// Fetch all matching properties for status-based sorting
$args['posts_per_page'] = -1;
$args['orderby'] = 'modified';
$args['order'] = 'DESC';
$properties = new WP_Query($args);
$all_properties = get_posts($args);
// Sort by status (Active > Pending > Sold) then by modified date
$sorted_properties = homeproz_sort_properties_by_status($all_properties);
// Handle pagination manually
$per_page = 12;
$total = count($sorted_properties);
$max_pages = ceil($total / $per_page);
$offset = ($paged - 1) * $per_page;
$paged_properties = array_slice($sorted_properties, $offset, $per_page);
ob_start();
@@ -138,45 +130,45 @@ function homeproz_ajax_filter_properties() {
?>
<div class="properties-meta">
<p class="properties-count">
<?php if ($properties->found_posts > 0) : ?>
Showing <strong><?php echo esc_html($properties->found_posts); ?></strong>
<?php echo $properties->found_posts === 1 ? 'property' : 'properties'; ?>
<?php if ($total > 0) : ?>
Showing <strong><?php echo esc_html($total); ?></strong>
<?php echo $total === 1 ? 'property' : 'properties'; ?>
<?php else : ?>
No properties found
<?php endif; ?>
</p>
</div>
<?php if ($properties->have_posts()) : ?>
<?php if (!empty($paged_properties)) : ?>
<div class="properties-grid">
<?php
while ($properties->have_posts()) :
$properties->the_post();
global $post;
foreach ($paged_properties as $property_post) :
$post = $property_post;
setup_postdata($post);
get_template_part('template-parts/property/property-card');
endwhile;
endforeach;
wp_reset_postdata();
?>
</div>
<?php
// Build pagination
$big = 999999999;
$base_url = get_post_type_archive_link('property');
// Build URL with current filters
$filter_args = array();
if ($property_type) $filter_args['property_type'] = $property_type;
if ($property_status) $filter_args['property_status'] = $property_status;
if ($property_location) $filter_args['property_location'] = $property_location;
if ($min_price) $filter_args['min_price'] = $min_price;
if ($max_price) $filter_args['max_price'] = $max_price;
if ($beds) $filter_args['beds'] = $beds;
if ($sort && $sort !== 'newest') $filter_args['sort'] = $sort;
$pagination = paginate_links(array(
'base' => add_query_arg('paged', '%#%', $base_url),
'format' => '',
'current' => max(1, $paged),
'total' => $properties->max_num_pages,
'total' => $max_pages,
'prev_text' => '&larr; Previous',
'next_text' => 'Next &rarr;',
'type' => 'array',
@@ -203,14 +195,12 @@ function homeproz_ajax_filter_properties() {
<?php endif; ?>
<?php
wp_reset_postdata();
$html = ob_get_clean();
wp_send_json_success(array(
'html' => $html,
'found_posts' => $properties->found_posts,
'max_pages' => $properties->max_num_pages,
'found_posts' => $total,
'max_pages' => $max_pages,
));
}
add_action('wp_ajax_homeproz_filter_properties', 'homeproz_ajax_filter_properties');
@@ -158,6 +158,54 @@ function homeproz_footer_fallback_menu() {
echo '</ul>';
}
/**
* Get status sort order value
* Active = 1 (first), Pending = 2, Sold = 3 (last)
*
* @param int $post_id Property post ID
* @return int Sort order value
*/
function homeproz_get_status_sort_order($post_id) {
$status_terms = get_the_terms($post_id, 'property_status');
if (!$status_terms || is_wp_error($status_terms)) {
return 99; // Unknown status goes last
}
$status = strtolower($status_terms[0]->slug);
switch ($status) {
case 'active':
return 1;
case 'pending':
return 2;
case 'sold':
return 3;
default:
return 99;
}
}
/**
* Sort properties array by status order then modified date
*
* @param array $posts Array of WP_Post objects
* @return array Sorted array
*/
function homeproz_sort_properties_by_status($posts) {
usort($posts, function($a, $b) {
$order_a = homeproz_get_status_sort_order($a->ID);
$order_b = homeproz_get_status_sort_order($b->ID);
if ($order_a !== $order_b) {
return $order_a - $order_b;
}
// Same status, sort by modified date (newest first)
return strtotime($b->post_modified) - strtotime($a->post_modified);
});
return $posts;
}
/**
* Get theme option from ACF or defaults
*
@@ -6,7 +6,7 @@
.Agents_Archive {
.agents-section {
padding: 3rem 0 4rem;
padding: 1.125rem 0 5.875rem;
}
// Agents Grid
@@ -0,0 +1,45 @@
# Property Archive System
Property listing and filtering system for the real estate website.
## Files
| File | Purpose |
|------|---------|
| `property-filters.php` | Filter form UI (Type, Location, Beds, Price Range) |
| `property-filters.js` | AJAX filtering without page reload |
| `property-filters.scss` | Filter bar and map view styles |
| `property-results.php` | Property grid with pagination |
| `property-card.php` | Individual property card component |
| `property-agent.php` | Agent sidebar widget on single property |
## Sorting Logic
Properties are sorted by:
1. **Status**: Active first, Pending second, Sold third
2. **Modified Date**: Most recently modified first within each status group
This sorting is handled in PHP via `homeproz_sort_properties_by_status()` in `inc/template-functions.php`.
## Filter Fields
- **Type**: Taxonomy `property_type` (House, Land, Commercial, etc.)
- **Location**: Taxonomy `property_location` (City names)
- **Beds**: ACF field `bedrooms` (minimum bedrooms filter)
- **Price Range**: ACF field `property_price` (min/max numeric filter)
Status filter was removed - all properties (Active, Pending, Sold) are always shown.
## AJAX Handler
`homeproz_ajax_filter_properties()` in `inc/ajax-handlers.php`
- Receives filter params via POST
- Returns HTML for `#property-results` container
- Uses same sorting logic as initial page load
## Map View
Toggle between Grid and Map views. Map view:
- Uses Leaflet.js with OpenStreetMap tiles
- City-based coordinates (no geocoding API)
- Properties displayed in half-height map + 2-column grid
@@ -129,12 +129,10 @@
action: 'homeproz_filter_properties',
nonce: homeprozAjax.nonce,
property_type: formData.property_type,
property_status: formData.property_status,
property_location: formData.property_location,
min_price: formData.min_price,
max_price: formData.max_price,
beds: formData.beds,
sort: formData.sort,
paged: page
},
success: function(response) {
@@ -171,12 +169,10 @@
getFormData: function() {
return {
property_type: this.$form.find('[name="property_type"]').val() || '',
property_status: this.$form.find('[name="property_status"]').val() || '',
property_location: this.$form.find('[name="property_location"]').val() || '',
min_price: this.$form.find('[name="min_price"]').val() || '',
max_price: this.$form.find('[name="max_price"]').val() || '',
beds: this.$form.find('[name="beds"]').val() || '',
sort: this.$form.find('[name="sort"]').val() || 'newest'
beds: this.$form.find('[name="beds"]').val() || ''
};
},
@@ -204,7 +200,7 @@
// Add non-empty filters to URL
for (var key in formData) {
if (formData[key] && formData[key] !== 'newest') {
if (formData[key]) {
url.searchParams.set(key, formData[key]);
}
}
@@ -231,7 +227,6 @@
*/
resetFilters: function() {
this.$form.find('select').val('');
this.$form.find('[name="sort"]').val('newest');
this.filterProperties(1);
}
};
@@ -41,19 +41,6 @@ $property_locations = get_terms(array('taxonomy' => 'property_location', 'hide_e
</select>
</div>
<!-- Property Status -->
<div class="filter-group filter-group-status">
<label for="filter-status" class="filter-label">Status</label>
<select name="property_status" id="filter-status" class="filter-select">
<option value="">All</option>
<?php foreach ($property_statuses as $status) : ?>
<option value="<?php echo esc_attr($status->slug); ?>" <?php selected($current_status, $status->slug); ?>>
<?php echo esc_html($status->name); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<!-- Location -->
<div class="filter-group filter-group-location">
<label for="filter-location" class="filter-label">Location</label>
@@ -111,17 +98,6 @@ $property_locations = get_terms(array('taxonomy' => 'property_location', 'hide_e
</div>
</div>
<!-- Sort -->
<div class="filter-group filter-group-sort">
<label for="filter-sort" class="filter-label">Sort</label>
<select name="sort" id="filter-sort" class="filter-select">
<option value="newest" <?php selected($current_sort, 'newest'); ?>>Newest</option>
<option value="oldest" <?php selected($current_sort, 'oldest'); ?>>Oldest</option>
<option value="price_low" <?php selected($current_sort, 'price_low'); ?>>Price: Low</option>
<option value="price_high" <?php selected($current_sort, 'price_high'); ?>>Price: High</option>
</select>
</div>
<!-- Actions -->
<div class="filter-group filter-group-actions">
<label class="filter-label">&nbsp;</label>
@@ -78,13 +78,13 @@
@media (min-width: 1024px) {
position: sticky;
top: 100px;
height: calc(100vh - 150px);
height: calc(50vh - 75px);
}
}
.property-map {
width: 100%;
height: 400px;
height: 200px;
background-color: var(--color-bg-card);
border-radius: 0.5rem;
overflow: hidden;
@@ -102,14 +102,6 @@
@media (min-width: 640px) {
grid-template-columns: repeat(2, 1fr);
}
@media (min-width: 1024px) {
grid-template-columns: 1fr;
}
@media (min-width: 1200px) {
grid-template-columns: repeat(2, 1fr);
}
}
}
@@ -198,12 +190,12 @@
gap: 0.75rem;
@media (min-width: 768px) {
grid-template-columns: repeat(4, 1fr);
grid-template-columns: repeat(3, 1fr);
}
@media (min-width: 1200px) {
// Type, Status, Location, Beds(narrow), Price(wide), Sort, Actions
grid-template-columns: 1fr 1fr 1fr 70px minmax(200px, 1.5fr) 100px auto;
@media (min-width: 1024px) {
// Type, Location, Beds, Price(wide), Actions
grid-template-columns: 1fr 1fr 1fr minmax(220px, 1.5fr) auto;
gap: 0.75rem;
}
}
@@ -215,18 +207,6 @@
gap: 0.375rem;
}
.filter-group-beds {
@media (min-width: 1200px) {
max-width: 70px;
}
}
.filter-group-sort {
@media (min-width: 1200px) {
max-width: 100px;
}
}
.filter-group-actions {
@media (min-width: 1200px) {
min-width: 160px;
@@ -100,30 +100,44 @@ if (!empty($meta_query)) {
}
}
// Sorting
switch ($current_sort) {
case 'oldest':
$args['orderby'] = 'date';
$args['order'] = 'ASC';
break;
case 'price_low':
$args['meta_key'] = 'property_price';
$args['orderby'] = 'meta_value_num';
$args['order'] = 'ASC';
break;
case 'price_high':
$args['meta_key'] = 'property_price';
$args['orderby'] = 'meta_value_num';
$args['order'] = 'DESC';
break;
case 'newest':
default:
$args['orderby'] = 'date';
$args['order'] = 'DESC';
break;
}
// For status-based sorting, we need to fetch all matching properties and sort in PHP
// This is efficient for real estate sites with < 1000 properties
$args['posts_per_page'] = -1;
$args['orderby'] = 'modified';
$args['order'] = 'DESC';
$properties = new WP_Query($args);
$all_properties = get_posts($args);
// Sort by status (Active > Pending > Sold) then by modified date
$sorted_properties = homeproz_sort_properties_by_status($all_properties);
// Handle pagination manually
$per_page = 12;
$total = count($sorted_properties);
$max_pages = ceil($total / $per_page);
$offset = ($paged - 1) * $per_page;
$paged_properties = array_slice($sorted_properties, $offset, $per_page);
// Create a fake WP_Query-like object for compatibility
$properties = (object) array(
'posts' => $paged_properties,
'found_posts' => $total,
'max_num_pages' => $max_pages,
);
// Helper function to check if we have posts
$properties->have_posts = function() use (&$paged_properties) {
static $index = 0;
if ($index < count($paged_properties)) {
return true;
}
$index = 0;
return false;
};
// Loop through properties manually
global $post;
$property_index = 0;
?>
<!-- Results Meta -->
@@ -138,13 +152,15 @@ $properties = new WP_Query($args);
</p>
</div>
<?php if ($properties->have_posts()) : ?>
<?php if (!empty($paged_properties)) : ?>
<div class="properties-grid">
<?php
while ($properties->have_posts()) :
$properties->the_post();
foreach ($paged_properties as $property_post) :
$post = $property_post;
setup_postdata($post);
get_template_part('template-parts/property/property-card');
endwhile;
endforeach;
wp_reset_postdata();
?>
</div>
@@ -155,7 +171,7 @@ $properties = new WP_Query($args);
'base' => str_replace($big, '%#%', esc_url(get_pagenum_link($big))),
'format' => '?paged=%#%',
'current' => max(1, $paged),
'total' => $properties->max_num_pages,
'total' => $max_pages,
'prev_text' => '&larr; Previous',
'next_text' => 'Next &rarr;',
'type' => 'array',
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB