Changeset 3322295
- Timestamp:
- 07/04/2025 11:51:47 AM (8 months ago)
- Location:
- havenlytics
- Files:
-
- 121 added
- 12 edited
-
tags/1.0.3 (added)
-
tags/1.0.3/admin (added)
-
tags/1.0.3/admin/assets (added)
-
tags/1.0.3/admin/assets/css (added)
-
tags/1.0.3/admin/assets/css/havenlytics-metabox.css (added)
-
tags/1.0.3/admin/assets/css/leaflet (added)
-
tags/1.0.3/admin/assets/css/leaflet/leaflet.css (added)
-
tags/1.0.3/admin/assets/js (added)
-
tags/1.0.3/admin/assets/js/havenlytics-metabox.js (added)
-
tags/1.0.3/admin/assets/js/leaflet (added)
-
tags/1.0.3/admin/assets/js/leaflet/leaflet.js (added)
-
tags/1.0.3/admin/class-admin-assets.php (added)
-
tags/1.0.3/havenlytics.php (added)
-
tags/1.0.3/includes (added)
-
tags/1.0.3/includes/class-init.php (added)
-
tags/1.0.3/includes/class-meta-fields.php (added)
-
tags/1.0.3/includes/class-post-type.php (added)
-
tags/1.0.3/includes/functions-core.php (added)
-
tags/1.0.3/languages (added)
-
tags/1.0.3/languages/havenlytics.pot (added)
-
tags/1.0.3/public (added)
-
tags/1.0.3/public/assets (added)
-
tags/1.0.3/public/assets/css (added)
-
tags/1.0.3/public/assets/css/havenlytics-property-grid.css (added)
-
tags/1.0.3/public/assets/css/havenlytics-property-list.css (added)
-
tags/1.0.3/public/assets/css/havenlytics-property-related-carousel.css (added)
-
tags/1.0.3/public/assets/css/havenlytics-property-single.css (added)
-
tags/1.0.3/public/assets/css/havenlytics-property-video.css (added)
-
tags/1.0.3/public/assets/css/havenlytics-root-style.css (added)
-
tags/1.0.3/public/assets/img (added)
-
tags/1.0.3/public/assets/img/layers-2x.png (added)
-
tags/1.0.3/public/assets/img/layers.png (added)
-
tags/1.0.3/public/assets/img/marker-icon.png (added)
-
tags/1.0.3/public/assets/img/no-thumb.png (added)
-
tags/1.0.3/public/assets/img/video-placeholder.png (added)
-
tags/1.0.3/public/assets/js (added)
-
tags/1.0.3/public/assets/js/havenlytics-frontend-scripts.js (added)
-
tags/1.0.3/public/assets/js/havenlytics-map.js (added)
-
tags/1.0.3/public/assets/plugins (added)
-
tags/1.0.3/public/assets/plugins/bootstrap (added)
-
tags/1.0.3/public/assets/plugins/bootstrap/css (added)
-
tags/1.0.3/public/assets/plugins/bootstrap/css/bootstrap.min.css (added)
-
tags/1.0.3/public/assets/plugins/bootstrap/css/leaflet.css (added)
-
tags/1.0.3/public/assets/plugins/bootstrap/js (added)
-
tags/1.0.3/public/assets/plugins/bootstrap/js/bootstrap.bundle.min.js (added)
-
tags/1.0.3/public/assets/plugins/bootstrap/js/leaflet.js (added)
-
tags/1.0.3/public/assets/plugins/fontawesome (added)
-
tags/1.0.3/public/assets/plugins/fontawesome/css (added)
-
tags/1.0.3/public/assets/plugins/fontawesome/css/all.min.css (added)
-
tags/1.0.3/public/assets/plugins/fontawesome/css/fontawesome.min.css (added)
-
tags/1.0.3/public/assets/plugins/fontawesome/webfonts (added)
-
tags/1.0.3/public/assets/plugins/fontawesome/webfonts/fa-brands-400.ttf (added)
-
tags/1.0.3/public/assets/plugins/fontawesome/webfonts/fa-brands-400.woff2 (added)
-
tags/1.0.3/public/assets/plugins/fontawesome/webfonts/fa-regular-400.ttf (added)
-
tags/1.0.3/public/assets/plugins/fontawesome/webfonts/fa-regular-400.woff2 (added)
-
tags/1.0.3/public/assets/plugins/fontawesome/webfonts/fa-solid-900.ttf (added)
-
tags/1.0.3/public/assets/plugins/fontawesome/webfonts/fa-solid-900.woff2 (added)
-
tags/1.0.3/public/assets/plugins/fontawesome/webfonts/fa-v4compatibility.ttf (added)
-
tags/1.0.3/public/assets/plugins/fontawesome/webfonts/fa-v4compatibility.woff2 (added)
-
tags/1.0.3/public/assets/plugins/leaflet (added)
-
tags/1.0.3/public/assets/plugins/leaflet/css (added)
-
tags/1.0.3/public/assets/plugins/leaflet/css/leaflet.css (added)
-
tags/1.0.3/public/assets/plugins/leaflet/js (added)
-
tags/1.0.3/public/assets/plugins/leaflet/js/leaflet.js (added)
-
tags/1.0.3/public/assets/plugins/owl.carouse (added)
-
tags/1.0.3/public/assets/plugins/owl.carouse/css (added)
-
tags/1.0.3/public/assets/plugins/owl.carouse/css/owl.carousel.min.css (added)
-
tags/1.0.3/public/assets/plugins/owl.carouse/js (added)
-
tags/1.0.3/public/assets/plugins/owl.carouse/js/owl.carousel.min.js (added)
-
tags/1.0.3/public/assets/plugins/theia-sticky-sidebar (added)
-
tags/1.0.3/public/assets/plugins/theia-sticky-sidebar/ResizeSensor.js (added)
-
tags/1.0.3/public/assets/plugins/theia-sticky-sidebar/theia-sticky-sidebar.js (added)
-
tags/1.0.3/public/class-frontend-assets.php (added)
-
tags/1.0.3/public/class-frontend.php (added)
-
tags/1.0.3/readme.txt (added)
-
tags/1.0.3/templates (added)
-
tags/1.0.3/templates/hvnly_property-single.php (added)
-
tags/1.0.3/templates/property-card-grid.php (added)
-
tags/1.0.3/templates/property-card-list.php (added)
-
tags/1.0.3/templates/property-card.php (added)
-
tags/1.0.3/uninstall.php (added)
-
trunk/havenlytics.php (modified) (2 diffs)
-
trunk/includes/class-meta-fields.php (modified) (10 diffs)
-
trunk/includes/class-post-type.php (modified) (3 diffs)
-
trunk/includes/functions-core.php (modified) (1 diff)
-
trunk/public/assets/css/havenlytics-property-grid.css (added)
-
trunk/public/assets/css/havenlytics-property-list.css (added)
-
trunk/public/assets/css/havenlytics-property-related-carousel.css (added)
-
trunk/public/assets/css/havenlytics-property-single.css (added)
-
trunk/public/assets/css/havenlytics-property-video.css (added)
-
trunk/public/assets/css/havenlytics-root-style.css (added)
-
trunk/public/assets/img/video-placeholder.png (added)
-
trunk/public/assets/js/havenlytics-frontend-scripts.js (modified) (1 diff)
-
trunk/public/assets/js/havenlytics-map.js (modified) (1 diff)
-
trunk/public/assets/plugins/bootstrap (added)
-
trunk/public/assets/plugins/bootstrap/css (added)
-
trunk/public/assets/plugins/bootstrap/css/bootstrap.min.css (added)
-
trunk/public/assets/plugins/bootstrap/css/leaflet.css (added)
-
trunk/public/assets/plugins/bootstrap/js (added)
-
trunk/public/assets/plugins/bootstrap/js/bootstrap.bundle.min.js (added)
-
trunk/public/assets/plugins/bootstrap/js/leaflet.js (added)
-
trunk/public/assets/plugins/fontawesome (added)
-
trunk/public/assets/plugins/fontawesome/css (added)
-
trunk/public/assets/plugins/fontawesome/css/all.min.css (added)
-
trunk/public/assets/plugins/fontawesome/css/fontawesome.min.css (added)
-
trunk/public/assets/plugins/fontawesome/webfonts (added)
-
trunk/public/assets/plugins/fontawesome/webfonts/fa-brands-400.ttf (added)
-
trunk/public/assets/plugins/fontawesome/webfonts/fa-brands-400.woff2 (added)
-
trunk/public/assets/plugins/fontawesome/webfonts/fa-regular-400.ttf (added)
-
trunk/public/assets/plugins/fontawesome/webfonts/fa-regular-400.woff2 (added)
-
trunk/public/assets/plugins/fontawesome/webfonts/fa-solid-900.ttf (added)
-
trunk/public/assets/plugins/fontawesome/webfonts/fa-solid-900.woff2 (added)
-
trunk/public/assets/plugins/fontawesome/webfonts/fa-v4compatibility.ttf (added)
-
trunk/public/assets/plugins/fontawesome/webfonts/fa-v4compatibility.woff2 (added)
-
trunk/public/assets/plugins/leaflet (added)
-
trunk/public/assets/plugins/leaflet/css (added)
-
trunk/public/assets/plugins/leaflet/css/leaflet.css (added)
-
trunk/public/assets/plugins/leaflet/js (added)
-
trunk/public/assets/plugins/leaflet/js/leaflet.js (added)
-
trunk/public/assets/plugins/owl.carouse (added)
-
trunk/public/assets/plugins/owl.carouse/css (added)
-
trunk/public/assets/plugins/owl.carouse/css/owl.carousel.min.css (added)
-
trunk/public/assets/plugins/owl.carouse/js (added)
-
trunk/public/assets/plugins/owl.carouse/js/owl.carousel.min.js (added)
-
trunk/public/assets/plugins/theia-sticky-sidebar (added)
-
trunk/public/assets/plugins/theia-sticky-sidebar/ResizeSensor.js (added)
-
trunk/public/assets/plugins/theia-sticky-sidebar/theia-sticky-sidebar.js (added)
-
trunk/public/class-frontend-assets.php (modified) (3 diffs)
-
trunk/public/class-frontend.php (modified) (9 diffs)
-
trunk/readme.txt (modified) (4 diffs)
-
trunk/templates/hvnly_property-single.php (modified) (2 diffs)
-
trunk/templates/property-card-grid.php (modified) (1 diff)
-
trunk/templates/property-card-list.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
havenlytics/trunk/havenlytics.php
r3315596 r3322295 4 4 Plugin URI: https://wordpress.org/plugins/havenlytics/ 5 5 Description: A property listing plugin for WordPress that allows users to easily manage and display property listings. 6 Version: 1.0. 26 Version: 1.0.3 7 7 Author: Havenlytics 8 8 Author URI: https://havenlytics.com … … 30 30 31 31 // Define constants 32 define('HVNLY_PROPERTY_VERSION', '1.0. 2');32 define('HVNLY_PROPERTY_VERSION', '1.0.3'); 33 33 define('HVNLY_PROPERTY_URL', plugin_dir_url(__FILE__)); 34 34 define('HVNLY_PROPERTY_PATH', plugin_dir_path(__FILE__)); 35 35 36 // Options/Settings37 define('HVNLY_PROPERTY_OPTION_PREFIX', 'hvnly_property_');38 update_option(HVNLY_PROPERTY_OPTION_PREFIX . 'settings', $data);39 40 // Database Tables (if needed)41 define('HVNLY_PROPERTY_DB_TABLE', $wpdb->prefix . 'hvnly_properties');42 36 43 37 // Include the plugin files -
havenlytics/trunk/includes/class-meta-fields.php
r3315596 r3322295 27 27 public static function hvnly_property_add_meta_boxes() 28 28 { 29 // Short details 30 add_meta_box( 31 'havenlytics_property_short_description', 32 __('Short Description', 'havenlytics'), 33 array(__CLASS__, 'havenlytics_render_short_description_metabox'), 34 'hvnly_property', 35 'normal', 36 'high' 37 ); 29 38 // Main details 30 39 add_meta_box( … … 99 108 100 109 /** 110 * Render property short description metabox 111 */ 112 public static function havenlytics_render_short_description_metabox($post) 113 { 114 wp_nonce_field('havenlytics_meta_save', 'havenlytics_meta_nonce'); 115 116 $short_description = get_post_meta($post->ID, '_havenlytics_short_description', true); 117 118 ?> 119 <div class="havenlytics-meta-container"> 120 <div class="havenlytics-meta-field"> 121 <div class="havenlytics-meta-label"> 122 <label for="havenlytics_short_description"><?php esc_html_e('Short Description', 'havenlytics'); ?></label> 123 124 </div> 125 <div class="havenlytics-meta-input"> 126 <textarea style="height: 100px;" id="havenlytics_short_description" name="_havenlytics_short_description" 127 class="havenlytics-large-text"><?php echo esc_textarea($short_description); ?></textarea> 128 </div> 129 </div> 130 131 132 133 </div> 134 <?php 135 } 136 137 /** 101 138 * Render property details metabox 102 139 */ … … 106 143 107 144 $values = array( 145 'currency' => get_post_meta($post->ID, '_havenlytics_currency', true), 108 146 'price' => get_post_meta($post->ID, '_havenlytics_price', true), 109 147 'bedrooms' => get_post_meta($post->ID, '_havenlytics_bedrooms', true), … … 117 155 'status' => get_post_meta($post->ID, '_havenlytics_status', true) 118 156 ); 119 ?>157 ?> 120 158 <div class="havenlytics-meta-container"> 159 160 <div class="havenlytics-meta-field"> 161 <div class="havenlytics-meta-label"> 162 <label for="havenlytics_currency"><?php esc_html_e('Currency', 'havenlytics'); ?></label> 163 </div> 164 <div class="havenlytics-meta-input"> 165 <select id="havenlytics_currency" name="_havenlytics_currency"> 166 <option value=""><?php esc_html_e('Select Currency', 'havenlytics'); ?></option> 167 <?php 168 $options = array( 169 'USD' => __('US Dollar', 'havenlytics'), 170 'EUR' => __('Euro', 'havenlytics'), 171 'GBP' => __('British Pound', 'havenlytics'), 172 'AUD' => __('Australian Dollar', 'havenlytics'), 173 'CAD' => __('Canadian Dollar', 'havenlytics'), 174 'JPY' => __('Japanese Yen', 'havenlytics'), 175 'CHF' => __('Swiss Franc', 'havenlytics'), 176 'CNY' => __('Chinese Yuan', 'havenlytics'), 177 'INR' => __('Indian Rupee', 'havenlytics'), 178 'NZD' => __('New Zealand Dollar', 'havenlytics'), 179 'SGD' => __('Singapore Dollar', 'havenlytics'), 180 'HKD' => __('Hong Kong Dollar', 'havenlytics'), 181 'SEK' => __('Swedish Krona', 'havenlytics'), 182 'NOK' => __('Norwegian Krone', 'havenlytics'), 183 'MXN' => __('Mexican Peso', 'havenlytics'), 184 'ZAR' => __('South African Rand', 'havenlytics'), 185 'BRL' => __('Brazilian Real', 'havenlytics'), 186 'RUB' => __('Russian Ruble', 'havenlytics'), 187 'KRW' => __('South Korean Won', 'havenlytics'), 188 'TRY' => __('Turkish Lira', 'havenlytics'), 189 ); 190 191 foreach ($options as $key => $label) : 192 ?> 193 <option value="<?php echo esc_attr($key); ?>" <?php selected($values['currency'], $key); ?>> 194 <?php echo esc_html($label); ?> 195 </option> 196 <?php endforeach; ?> 197 </select> 198 </div> 199 </div> 200 121 201 <div class="havenlytics-meta-field"> 122 202 <div class="havenlytics-meta-label"> … … 155 235 <label for="havenlytics_sqft"><?php esc_html_e('Square Footage', 'havenlytics'); ?></label> 156 236 </div> 237 157 238 <div class="havenlytics-meta-input"> 158 239 <input type="number" id="havenlytics_sqft" name="_havenlytics_sqft" … … 274 355 'cooling' => get_post_meta($post->ID, '_havenlytics_cooling', true), 275 356 'water' => get_post_meta($post->ID, '_havenlytics_water', true), 276 'sewer' => get_post_meta($post->ID, '_havenlytics_sewer', true) 357 'sewer' => get_post_meta($post->ID, '_havenlytics_sewer', true), 358 'rating_star' => get_post_meta($post->ID, '_havenlytics_rating_star', true) 277 359 ); 278 360 ?> … … 447 529 ?> 448 530 <option value="<?php echo esc_attr($key); ?>" <?php selected($values['sewer'], $key); ?>> 531 <?php echo esc_html($label); ?> 532 </option> 533 <?php endforeach; ?> 534 </select> 535 </div> 536 </div> 537 538 <div class="havenlytics-meta-field"> 539 <div class="havenlytics-meta-label"> 540 <label for="havenlytics_rating_star"><?php esc_html_e('Rating Star', 'havenlytics'); ?></label> 541 </div> 542 <div class="havenlytics-meta-input"> 543 <select id="havenlytics_rating_star" name="_havenlytics_rating_star"> 544 <option value=""><?php esc_html_e('Select Star', 'havenlytics'); ?></option> 545 <?php 546 $options = array( 547 '1' => __('1 Star', 'havenlytics'), 548 '2' => __('2 Stars', 'havenlytics'), 549 '3' => __('3 Stars', 'havenlytics'), 550 '4' => __('4 Stars', 'havenlytics'), 551 '5' => __('5 Stars', 'havenlytics'), 552 ); 553 foreach ($options as $key => $label) : 554 ?> 555 <option value="<?php echo esc_attr($key); ?>" <?php selected($values['rating_star'], $key); ?>> 449 556 <?php echo esc_html($label); ?> 450 557 </option> … … 758 865 } 759 866 867 // Save short description 868 if (isset($_POST['_havenlytics_short_description'])) { 869 $short_description = sanitize_textarea_field(wp_unslash($_POST['_havenlytics_short_description'])); 870 update_post_meta($post_id, '_havenlytics_short_description', $short_description); 871 } 872 760 873 // Save hvnly_property details fields 761 874 $detail_fields = array( 875 '_havenlytics_currency', 762 876 '_havenlytics_price', 763 877 '_havenlytics_bedrooms', … … 795 909 '_havenlytics_cooling', 796 910 '_havenlytics_water', 797 '_havenlytics_sewer' 911 '_havenlytics_sewer', 912 '_havenlytics_rating_star' 798 913 ); 799 914 foreach ($additional_fields as $field) { … … 868 983 } 869 984 985 870 986 if (isset($_POST['_havenlytics_youtube_title'])) { 871 987 $youtube_title = sanitize_text_field(wp_unslash($_POST['_havenlytics_youtube_title'])); -
havenlytics/trunk/includes/class-post-type.php
r3315596 r3322295 52 52 'menu_position' => 15, 53 53 'menu_icon' => 'dashicons-admin-home', 54 'supports' => array('title', 'editor', ' excerpt', 'thumbnail'),54 'supports' => array('title', 'editor', 'thumbnail'), 55 55 'taxonomies' => array('hvnly_property_category', 'hvnly_property_tag'), 56 56 'has_archive' => true, // Allows archive page 57 'rewrite' => array('slug' => ' hvnly-property', 'with_front' => false),57 'rewrite' => array('slug' => 'property', 'with_front' => false), 58 58 'show_in_rest' => true, 59 59 'publicly_queryable' => true, … … 90 90 'show_admin_column' => true, 91 91 'show_in_rest' => true, 92 'rewrite' => array('slug' => ' hvnly-property-category', 'with_front' => false),92 'rewrite' => array('slug' => 'property-category', 'with_front' => false), 93 93 )); 94 94 … … 116 116 'show_admin_column' => true, 117 117 'show_in_rest' => true, 118 'rewrite' => array('slug' => ' hvnly-property-tag', 'with_front' => false),118 'rewrite' => array('slug' => 'property-tag', 'with_front' => false), 119 119 )); 120 120 } -
havenlytics/trunk/includes/functions-core.php
r3315596 r3322295 4 4 return get_post_meta($post_id, $key, true); 5 5 } 6 7 /** 8 * Trims a string to a specified number of words. 9 * 10 * @param string $text The text to trim. 11 * @param int $limit The maximum number of words to return. 12 * @param string $append The string to append if the text is trimmed. 13 * @return string The trimmed text. 14 */ 15 function havenlytics_trim_words($text, $limit = 20, $append = '...') 16 { 17 $words = explode(' ', $text); 18 if (count($words) > $limit) { 19 $words = array_slice($words, 0, $limit); 20 return implode(' ', $words) . $append; 21 } 22 return $text; 23 } -
havenlytics/trunk/public/assets/js/havenlytics-frontend-scripts.js
r3315596 r3322295 70 70 }); 71 71 72 // Sticky sidebar functionality 73 document.addEventListener("DOMContentLoaded", function () { 74 const sidebar = document.querySelector(".havenlytics_sticky_sidebar"); 75 76 if (!sidebar) return; 77 78 // Create progress line element 79 const progressBar = document.createElement("div"); 80 progressBar.id = "havenlytics_progress_line"; 81 document.body.appendChild(progressBar); 82 83 // Style it via JS (no external CSS needed) 84 Object.assign(progressBar.style, { 85 position: "fixed", 86 top: "0", 87 left: "0", 88 height: "2px", 89 width: "0%", 90 backgroundColor: "#6c60fe", 91 zIndex: "9999", 92 transition: "width 0.5s ease-out", 93 pointerEvents: "none", 94 opacity: "0" 95 }); 96 97 let sidebarVisible = false; 98 99 const observer = new IntersectionObserver(entries => { 100 entries.forEach(entry => { 101 if (entry.isIntersecting) { 102 sidebar.classList.add("sticky-visible"); 103 sidebarVisible = true; 104 progressBar.style.opacity = "1"; 105 } 106 }); 107 }, { 108 threshold: 0.1 109 }); 110 111 observer.observe(sidebar); 112 113 // Scroll progress tracking 114 window.addEventListener("scroll", () => { 115 if (!sidebarVisible) return; 116 117 const scrollTop = window.scrollY; 118 const docHeight = document.documentElement.scrollHeight - window.innerHeight; 119 const scrolled = (scrollTop / docHeight) * 100; 120 121 progressBar.style.width = `${scrolled}%`; 122 }); 123 }); 124 125 126 127 128 129 // Single property carousel 130 document.addEventListener('DOMContentLoaded', function() { 131 // Initialize all carousels 132 document.querySelectorAll('.havenlytics-carousel-gallery').forEach(carousel => { 133 const track = carousel.querySelector('.havenlytics-carousel-track'); 134 const slides = carousel.querySelectorAll('.havenlytics-carousel-slide'); 135 const prevBtn = carousel.querySelector('.havenlytics-carousel-prev-btn'); 136 const nextBtn = carousel.querySelector('.havenlytics-carousel-next-btn'); 137 const indicators = carousel.querySelectorAll('.havenlytics-carousel-indicator'); 138 139 let currentIndex = 0; 140 const slideCount = slides.length; 141 142 // Update carousel position 143 function updateCarousel() { 144 track.style.transform = `translateX(-${currentIndex * 100}%)`; 145 146 // Update indicators 147 indicators.forEach((indicator, index) => { 148 if (index === currentIndex) { 149 indicator.classList.add('havenlytics-carousel-active'); 150 } else { 151 indicator.classList.remove('havenlytics-carousel-active'); 152 } 153 }); 154 } 155 156 // Next slide 157 function nextSlide() { 158 currentIndex = (currentIndex + 1) % slideCount; 159 updateCarousel(); 160 } 161 162 // Previous slide 163 function prevSlide() { 164 currentIndex = (currentIndex - 1 + slideCount) % slideCount; 165 updateCarousel(); 166 } 167 168 // Add event listeners 169 nextBtn.addEventListener('click', nextSlide); 170 prevBtn.addEventListener('click', prevSlide); 171 172 // Add click events to indicators 173 indicators.forEach((indicator, index) => { 174 indicator.addEventListener('click', () => { 175 currentIndex = index; 176 updateCarousel(); 177 }); 178 }); 179 180 // Auto-advance carousel (optional) 181 let slideInterval = setInterval(nextSlide, 5000); 182 183 // Pause auto-advance on hover 184 carousel.addEventListener('mouseenter', () => { 185 clearInterval(slideInterval); 186 }); 187 188 carousel.addEventListener('mouseleave', () => { 189 slideInterval = setInterval(nextSlide, 5000); 190 }); 191 }); 192 193 // Toggle favorite button 194 document.querySelectorAll('.havenlytics-property-favourite').forEach(button => { 195 button.addEventListener('click', function(e) { 196 e.preventDefault(); 197 this.classList.toggle('havenlytics-property-selected'); 198 const icon = this.querySelector('i'); 199 if (this.classList.contains('havenlytics-property-selected')) { 200 icon.classList.remove('fa-regular'); 201 icon.classList.add('fa-solid'); 202 } else { 203 icon.classList.remove('fa-solid'); 204 icon.classList.add('fa-regular'); 205 } 206 }); 207 }); 208 }); 209 210 211 // Enhanced Gallery thumbnail functionality with scrolling and navigation 212 document.querySelectorAll('.havenlytics-gallery-thumb').forEach(thumb => { 213 thumb.addEventListener('click', function() { 214 const gallery = this.closest('.havenlytics-property-list-gallery'); 215 const mainImage = gallery.querySelector('.havenlytics-gallery-main'); 216 const thumbnails = this.parentElement.querySelectorAll('.havenlytics-gallery-thumb'); 217 218 // Remove active class from all thumbnails 219 thumbnails.forEach(t => { 220 t.classList.remove('active'); 221 }); 222 223 // Add active class to clicked thumbnail with animation 224 this.classList.add('active'); 225 226 // Add smooth transition to main image 227 mainImage.classList.add('fade-out'); 228 229 // Get the high-resolution image URL from data attribute 230 const highResUrl = this.getAttribute('data-highres'); 231 232 // After the fade-out transition, change the image 233 setTimeout(() => { 234 mainImage.style.backgroundImage = `url('${highResUrl}')`; 235 mainImage.classList.remove('fade-out'); 236 }, 300); 237 238 // Scroll to center the active thumbnail 239 this.scrollIntoView({ 240 behavior: 'smooth', 241 block: 'nearest', 242 inline: 'center' 243 }); 244 }); 245 }); 246 247 // Navigation buttons for thumbnails 248 document.querySelectorAll('.havenlytics-thumb-nav').forEach(nav => { 249 nav.addEventListener('click', function() { 250 const thumbnailsContainer = this.parentElement.querySelector('.havenlytics-gallery-thumbnails'); 251 const scrollAmount = thumbnailsContainer.clientWidth * 0.8; 252 253 if (this.classList.contains('havenlytics-thumb-prev')) { 254 thumbnailsContainer.scrollBy({ 255 left: -scrollAmount, 256 behavior: 'smooth' 257 }); 258 } else { 259 thumbnailsContainer.scrollBy({ 260 left: scrollAmount, 261 behavior: 'smooth' 262 }); 263 } 264 }); 265 }); 266 267 268 // Enhanced gallery functionality with prefix 269 document.addEventListener('DOMContentLoaded', function() { 270 const prefix = '.havenlytics_property_single '; 271 272 // Elements 273 const thumbnails = document.querySelectorAll(prefix + '.property-thumbnail'); 274 const slides = document.querySelectorAll(prefix + '.gallery-slide'); 275 const prevBtn = document.querySelector(prefix + '.prev-btn'); 276 const nextBtn = document.querySelector(prefix + '.next-btn'); 277 const galleryCounter = document.querySelector(prefix + '.gallery-counter'); 278 const indicatorContainer = document.querySelector(prefix + '.transition-indicator'); 279 const thumbnailContainer = document.querySelector(prefix + '.thumbnail-container'); 280 281 // Create indicator dots 282 slides.forEach((slide, index) => { 283 const dot = document.createElement('div'); 284 dot.classList.add('indicator-dot'); 285 if(index === 0) dot.classList.add('active'); 286 dot.setAttribute('data-index', index); 287 indicatorContainer.appendChild(dot); 288 }); 289 290 const indicatorDots = document.querySelectorAll(prefix + '.indicator-dot'); 291 292 // Gallery images 293 let currentIndex = 0; 294 const totalSlides = slides.length; 295 296 // Function to update slide 297 function updateSlide(index) { 298 // Update slides 299 slides.forEach((slide, i) => { 300 slide.classList.toggle('active', i === index); 301 }); 302 303 // Update thumbnails 304 thumbnails.forEach((thumb, i) => { 305 thumb.classList.toggle('active', i === index); 306 }); 307 308 // Update indicators 309 indicatorDots.forEach((dot, i) => { 310 dot.classList.toggle('active', i === index); 311 }); 312 313 // Update counter 314 galleryCounter.textContent = `${index + 1}/${totalSlides}`; 315 316 // Scroll to active thumbnail 317 const activeThumb = thumbnails[index]; 318 if (activeThumb) { 319 const containerWidth = thumbnailContainer.offsetWidth; 320 const thumbLeft = activeThumb.offsetLeft; 321 const thumbWidth = activeThumb.offsetWidth; 322 323 thumbnailContainer.scrollTo({ 324 left: thumbLeft - (containerWidth / 2) + (thumbWidth / 2), 325 behavior: 'smooth' 326 }); 327 } 328 329 currentIndex = index; 330 } 331 332 // Thumbnail click functionality 333 thumbnails.forEach(thumb => { 334 thumb.addEventListener('click', function() { 335 const index = parseInt(this.getAttribute('data-index')); 336 updateSlide(index); 337 }); 338 }); 339 340 // Indicator dots click functionality 341 indicatorDots.forEach(dot => { 342 dot.addEventListener('click', function() { 343 const index = parseInt(this.getAttribute('data-index')); 344 updateSlide(index); 345 }); 346 }); 347 348 // Navigation buttons 349 prevBtn.addEventListener('click', function() { 350 const newIndex = (currentIndex - 1 + totalSlides) % totalSlides; 351 updateSlide(newIndex); 352 }); 353 354 nextBtn.addEventListener('click', function() { 355 const newIndex = (currentIndex + 1) % totalSlides; 356 updateSlide(newIndex); 357 }); 358 359 // Auto-advance gallery 360 setInterval(() => { 361 const newIndex = (currentIndex + 1) % totalSlides; 362 updateSlide(newIndex); 363 }, 5000); 364 }); 365 366 367 // Popup functionality for gallery images 368 document.addEventListener('DOMContentLoaded', function() { 369 const galleryItems = document.querySelectorAll('.havenlytics_gallery_fancybox_gallery-item'); 370 const lightbox = document.querySelector('.havenlytics_gallery_fancybox_fancybox'); 371 const lightboxImg = document.querySelector('.havenlytics_gallery_fancybox_fancybox_img'); 372 const lightboxCaption = document.querySelector('.havenlytics_gallery_fancybox_fancybox_caption'); 373 const lightboxPropertyButton = document.querySelector('.havenlytics_gallery_fancybox_property_button'); 374 const lightboxClose = document.querySelector('.havenlytics_gallery_fancybox_fancybox_close'); 375 const lightboxPrev = document.querySelector('.havenlytics_gallery_fancybox_fancybox_prev'); 376 const lightboxNext = document.querySelector('.havenlytics_gallery_fancybox_fancybox_next'); 377 const lightboxCounter = document.querySelector('.havenlytics_gallery_fancybox_fancybox_counter'); 378 const lightboxThumbnails = document.querySelector('.havenlytics_gallery_fancybox_fancybox_thumbnails'); 379 const scrollTopBtn = document.querySelector('.havenlytics_gallery_fancybox_scroll_top'); 380 381 let currentIndex = 0; 382 let isAnimating = false; 383 384 // Create thumbnail navigation on the right 385 galleryItems.forEach((item, index) => { 386 const thumb = document.createElement('div'); 387 thumb.className = 'havenlytics_gallery_fancybox_fancybox_thumb'; 388 if(index === 0) thumb.classList.add('havenlytics_gallery_fancybox_active'); 389 thumb.innerHTML = `<img src="${item.querySelector('img').src}" alt="Thumb ${index + 1}" data-index="${index}">`; 390 lightboxThumbnails.appendChild(thumb); 391 392 thumb.addEventListener('click', () => { 393 if(isAnimating) return; 394 const index = parseInt(thumb.querySelector('img').dataset.index); 395 navigateTo(index); 396 }); 397 }); 398 399 // Add click events to gallery items 400 galleryItems.forEach((item, index) => { 401 item.addEventListener('click', () => { 402 currentIndex = index; 403 openLightbox(); 404 updateLightbox(); 405 }); 406 }); 407 408 // Open lightbox with animation 409 function openLightbox() { 410 document.body.style.overflow = 'hidden'; 411 lightbox.classList.add('active'); 412 413 // Scroll to active thumbnail 414 scrollToActiveThumbnail(); 415 } 416 417 // Update lightbox content 418 function updateLightbox() { 419 const activeItem = galleryItems[currentIndex]; 420 const largeImg = activeItem.querySelector('img').dataset.large; 421 const caption = activeItem.querySelector('.havenlytics_gallery_fancybox_gallery-caption').textContent; 422 const buttonTitle = activeItem.dataset.buttonTitle; 423 const buttonLink = activeItem.dataset.buttonLink; 424 425 lightboxImg.src = largeImg; 426 lightboxImg.alt = caption; 427 lightboxCaption.textContent = caption; 428 lightboxCounter.textContent = `${currentIndex + 1} / ${galleryItems.length}`; 429 430 // Update property button 431 lightboxPropertyButton.textContent = buttonTitle; 432 lightboxPropertyButton.href = buttonLink; 433 434 // Update active thumbnail 435 document.querySelectorAll('.havenlytics_gallery_fancybox_fancybox_thumb').forEach((thumb, idx) => { 436 thumb.classList.toggle('havenlytics_gallery_fancybox_active', idx === currentIndex); 437 }); 438 439 // Scroll to active thumbnail 440 scrollToActiveThumbnail(); 441 } 442 443 // Scroll active thumbnail into view - ENHANCED 444 function scrollToActiveThumbnail() { 445 const activeThumb = document.querySelector('.havenlytics_gallery_fancybox_fancybox_thumb.havenlytics_gallery_fancybox_active'); 446 if(activeThumb) { 447 // Calculate position to scroll to make active thumbnail visible on the left 448 const container = lightboxThumbnails; 449 const thumbOffset = activeThumb.offsetTop; 450 const thumbHeight = activeThumb.offsetHeight; 451 const containerHeight = container.offsetHeight; 452 453 // Calculate scroll position to show thumbnail at the top 454 let scrollPosition = thumbOffset - (containerHeight / 2) + (thumbHeight / 2); 455 456 // Ensure we don't scroll past container boundaries 457 scrollPosition = Math.max(0, Math.min(scrollPosition, container.scrollHeight - containerHeight)); 458 459 // Smooth scroll to position 460 container.scrollTo({ 461 top: scrollPosition, 462 behavior: 'smooth' 463 }); 464 } 465 } 466 467 // Navigation functions with animations 468 function navigateTo(index) { 469 if(isAnimating || index === currentIndex) return; 470 471 isAnimating = true; 472 const direction = index > currentIndex ? 'next' : 'prev'; 473 474 // Add slide-out animation 475 lightboxImg.classList.add(direction === 'next' ? 'slide-out-left' : 'slide-out-right'); 476 477 setTimeout(() => { 478 currentIndex = index; 479 updateLightbox(); 480 481 // Add slide-in animation 482 lightboxImg.classList.remove('slide-out-left', 'slide-out-right'); 483 lightboxImg.classList.add(direction === 'next' ? 'slide-in-right' : 'slide-in-left'); 484 485 // Force reflow to restart animation 486 void lightboxImg.offsetWidth; 487 488 // Start slide-in animation 489 lightboxImg.classList.remove('slide-in-right', 'slide-in-left'); 490 491 setTimeout(() => { 492 isAnimating = false; 493 }, 400); 494 }, 400); 495 } 496 497 function showNext() { 498 const nextIndex = (currentIndex + 1) % galleryItems.length; 499 navigateTo(nextIndex); 500 } 501 502 function showPrev() { 503 const prevIndex = (currentIndex - 1 + galleryItems.length) % galleryItems.length; 504 navigateTo(prevIndex); 505 } 506 507 // Event listeners for navigation 508 lightboxClose.addEventListener('click', () => { 509 lightbox.classList.remove('active'); 510 document.body.style.overflow = 'auto'; 511 }); 512 513 lightboxNext.addEventListener('click', showNext); 514 lightboxPrev.addEventListener('click', showPrev); 515 516 // Keyboard navigation 517 document.addEventListener('keydown', (e) => { 518 if (!lightbox.classList.contains('active') || isAnimating) return; 519 520 if (e.key === 'Escape') { 521 lightbox.classList.remove('active'); 522 document.body.style.overflow = 'auto'; 523 } else if (e.key === 'ArrowRight') { 524 showNext(); 525 } else if (e.key === 'ArrowLeft') { 526 showPrev(); 527 } 528 }); 529 530 // Close lightbox when clicking on overlay 531 lightbox.addEventListener('click', (e) => { 532 if (e.target === lightbox) { 533 lightbox.classList.remove('active'); 534 document.body.style.overflow = 'auto'; 535 } 536 }); 537 538 // Scroll to top button functionality 539 window.addEventListener('scroll', () => { 540 if (window.scrollY > 300) { 541 scrollTopBtn.classList.add('visible'); 542 } else { 543 scrollTopBtn.classList.remove('visible'); 544 } 545 }); 546 547 scrollTopBtn.addEventListener('click', () => { 548 window.scrollTo({ 549 top: 0, 550 behavior: 'smooth' 551 }); 552 }); 553 }); 554 72 555 556 557 558 // Property related Carousel js 559 document.addEventListener('DOMContentLoaded', function () { 560 const carousel = document.querySelector('.havenlytics_related_carousel'); 561 const track = carousel.querySelector('.havenlytics_related_carousel__track'); 562 const items = track.querySelectorAll('.havenlytics_related_carousel__item'); 563 const prevBtn = carousel.querySelector('.havenlytics_related_carousel__nav--prev'); 564 const nextBtn = carousel.querySelector('.havenlytics_related_carousel__nav--next'); 565 const dotsContainer = carousel.querySelector('.havenlytics_related_carousel__pagination'); 566 const dots = dotsContainer.querySelectorAll('.havenlytics_related_carousel__dot'); 567 568 // Calculate item width including margins 569 const itemStyle = window.getComputedStyle(items[0]); 570 const itemWidth = items[0].offsetWidth + 571 parseInt(itemStyle.marginLeft) + 572 parseInt(itemStyle.marginRight); 573 574 // Track width and container width 575 const trackWidth = track.offsetWidth; 576 const containerWidth = carousel.offsetWidth; 577 578 // Calculate how many items fit in the viewport 579 let itemsPerView = Math.floor(containerWidth / itemWidth); 580 581 // Set initial position 582 let position = 0; 583 let currentIndex = 0; 584 585 // Update indicators 586 function updateIndicators(index) { 587 dots.forEach((dot, i) => { 588 if (i === index) { 589 dot.classList.add('active'); 590 } else { 591 dot.classList.remove('active'); 592 } 593 }); 594 } 595 596 // Scroll to position 597 function scrollToPosition(pos) { 598 track.style.transform = `translateX(${pos}px)`; 599 } 600 601 // Next button click 602 nextBtn.addEventListener('click', function() { 603 const maxPosition = (items.length - itemsPerView) * itemWidth; 604 605 if (position > -maxPosition) { 606 position -= itemWidth * itemsPerView; 607 currentIndex += itemsPerView; 608 scrollToPosition(position); 609 updateIndicators(currentIndex / itemsPerView); 610 } else { 611 // Return to start 612 position = 0; 613 currentIndex = 0; 614 scrollToPosition(position); 615 updateIndicators(currentIndex); 616 } 617 }); 618 619 // Previous button click 620 prevBtn.addEventListener('click', function() { 621 if (position < 0) { 622 position += itemWidth * itemsPerView; 623 currentIndex -= itemsPerView; 624 scrollToPosition(position); 625 updateIndicators(currentIndex / itemsPerView); 626 } else { 627 // Go to end 628 const maxPosition = (items.length - itemsPerView) * itemWidth; 629 position = -maxPosition; 630 currentIndex = items.length - itemsPerView; 631 scrollToPosition(position); 632 updateIndicators(currentIndex / itemsPerView); 633 } 634 }); 635 636 // Dot click navigation 637 dots.forEach((dot, index) => { 638 dot.addEventListener('click', function() { 639 position = -index * itemsPerView * itemWidth; 640 currentIndex = index * itemsPerView; 641 scrollToPosition(position); 642 updateIndicators(index); 643 }); 644 }); 645 646 // Auto-scroll every 5 seconds 647 setInterval(function() { 648 const maxPosition = (items.length - itemsPerView) * itemWidth; 649 650 if (position > -maxPosition) { 651 position -= itemWidth * itemsPerView; 652 currentIndex += itemsPerView; 653 } else { 654 position = 0; 655 currentIndex = 0; 656 } 657 658 scrollToPosition(position); 659 updateIndicators(currentIndex / itemsPerView); 660 }, 5000); 661 662 // Recalculate on window resize 663 window.addEventListener('resize', function() { 664 // Recalculate items per view 665 const newItemWidth = items[0].offsetWidth + 666 parseInt(window.getComputedStyle(items[0]).marginLeft) + 667 parseInt(window.getComputedStyle(items[0]).marginRight); 668 669 itemsPerView = Math.floor(carousel.offsetWidth / newItemWidth); 670 671 // Adjust position based on new item width 672 position = -currentIndex * newItemWidth; 673 scrollToPosition(position); 674 }); 675 }); 676 677 678 // Related Property Slider 679 if($('.havenlytics-similar-slider').length > 0) { 680 $('.havenlytics-similar-slider').owlCarousel({ 681 loop:true, 682 margin:24, 683 nav:false, 684 dots:true, 685 smartSpeed: 2000, 686 navText : ["<i class='fa-solid fa-arrow-left'></i>","<i class='fa-solid fa-arrow-right'></i>"], 687 responsive:{ 688 0:{ 689 items:1 690 }, 691 692 768:{ 693 items:1 694 }, 695 992:{ 696 items:2 697 }, 698 1200:{ 699 items:3 700 }, 701 } 702 }) 703 } 704 705 706 707 // Property Video Popup 708 document.addEventListener('DOMContentLoaded', function() { 709 const playButton = document.getElementById('play-button'); 710 if (!playButton) return; 711 712 const videoOverlay = document.querySelector('.havenlytics_property_video-overlay'); 713 const videoPlayer = document.getElementById('video-player'); 714 const videoClose = document.querySelector('.havenlytics_property_video-close'); 715 const spinner = document.querySelector('.havenlytics_property_video-spinner'); 716 717 // Get video URL from data attribute 718 const videoUrl = playButton.dataset.videoUrl; 719 720 playButton.addEventListener('click', openVideoPlayer); 721 videoClose.addEventListener('click', closeVideoPlayer); 722 723 videoOverlay.addEventListener('click', function(e) { 724 if (e.target === videoOverlay) { 725 closeVideoPlayer(); 726 } 727 }); 728 729 document.addEventListener('keydown', function(e) { 730 if (e.key === 'Escape') { 731 closeVideoPlayer(); 732 } 733 }); 734 735 function openVideoPlayer() { 736 spinner.style.display = 'block'; 737 videoOverlay.classList.add('active'); 738 document.body.style.overflow = 'hidden'; 739 740 setTimeout(() => { 741 let videoContent = ''; 742 let videoId = ''; 743 744 // YouTube 745 if (videoUrl.includes('youtube.com') || videoUrl.includes('youtu.be')) { 746 if (videoUrl.includes('youtube.com')) { 747 const urlParams = new URLSearchParams(new URL(videoUrl).search); 748 videoId = urlParams.get('v'); 749 } else if (videoUrl.includes('youtu.be')) { 750 videoId = videoUrl.split('/').pop().split('?')[0]; 751 } 752 753 if (videoId) { 754 videoContent = 755 `<iframe src="https://www.youtube.com/embed/${videoId}?autoplay=1&mute=0&rel=0&modestbranding=1&controls=1" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>`; 756 } 757 } 758 // Vimeo 759 else if (videoUrl.includes('vimeo.com')) { 760 const regExp = /https:\/\/(?:www\.)?vimeo.com\/(\d+)(?:$|\/|\?)/; 761 const match = videoUrl.match(regExp); 762 763 if (match && match[1]) { 764 videoContent = 765 `<iframe src="https://player.vimeo.com/video/${match[1]}?autoplay=1&loop=0&title=0&byline=0&portrait=0" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen></iframe>`; 766 } 767 } 768 // Direct video files 769 else if (videoUrl.match(/\.(mp4|m4v|webm|ogg)(?:$|\?)/i)) { 770 videoContent = `<video controls autoplay playsinline> 771 <source src="${videoUrl}" type="${getVideoType(videoUrl)}"> 772 Your browser does not support the video tag. 773 </video>`; 774 } 775 776 if (videoContent) { 777 videoPlayer.innerHTML = videoContent; 778 spinner.style.display = 'none'; 779 } else { 780 videoPlayer.innerHTML = 781 '<div class="video-error" style="color: white; text-align: center; padding: 50px; background: rgba(255,0,0,0.1);"><h3>Video Loading Error</h3><p>Unsupported video URL format</p></div>'; 782 spinner.style.display = 'none'; 783 } 784 }, 500); 785 } 786 787 function closeVideoPlayer() { 788 videoOverlay.classList.remove('active'); 789 document.body.style.overflow = ''; 790 791 setTimeout(() => { 792 videoPlayer.innerHTML = ''; 793 }, 300); 794 } 795 796 function getVideoType(url) { 797 const ext = url.split('.').pop().split('?')[0].toLowerCase(); 798 if (ext === 'mp4' || ext === 'm4v') return 'video/mp4'; 799 if (ext === 'webm') return 'video/webm'; 800 if (ext === 'ogg') return 'video/ogg'; 801 return 'video/mp4'; 802 } 803 }); 804 805 806 73 807 })(jQuery); 74 808 -
havenlytics/trunk/public/assets/js/havenlytics-map.js
r3315596 r3322295 17 17 // Add OpenStreetMap tiles 18 18 L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { 19 attribution: '© <a href="https://w ww.openstreetmap.org/copyright">OpenStreetMap</a> contributors',19 attribution: '© <a href="https://wordpress.org/plugins/havenlytics/">Havenlytics</a> Plugin © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors', 20 20 maxZoom: 19 21 21 }).addTo(map); -
havenlytics/trunk/public/class-frontend-assets.php
r3315596 r3322295 21 21 { 22 22 23 // Front End CSS23 // ✅ fontawesome CSS 24 24 wp_enqueue_style( 25 'havenlytics-frontend', 26 HVNLY_PROPERTY_URL . 'public/assets/css/havenlytics-frontend-style.css', 25 'havenlytics-fontawesome-min', 26 HVNLY_PROPERTY_URL . 'public/assets/plugins/fontawesome/css/all.min.css', 27 array(), 28 '6.6.0' 29 ); 30 31 // ✅ Bootstrap CSS 32 wp_enqueue_style( 33 'havenlytics-bootstrap-min', 34 HVNLY_PROPERTY_URL . 'public/assets/plugins/bootstrap/css/bootstrap.min.css', 35 array(), 36 '5.3.7' 37 ); 38 39 // ✅ Property Grid CSS 40 wp_enqueue_style( 41 'havenlytics-property-grid', 42 HVNLY_PROPERTY_URL . 'public/assets/css/havenlytics-property-grid.css', 43 [], 44 HVNLY_PROPERTY_VERSION 45 ); 46 // ✅ Property List CSS 47 wp_enqueue_style( 48 'havenlytics-property-list', 49 HVNLY_PROPERTY_URL . 'public/assets/css/havenlytics-property-list.css', 50 [], 51 HVNLY_PROPERTY_VERSION 52 ); 53 54 // ✅ Property root CSS 55 wp_enqueue_style( 56 'havenlytics-root-style', 57 HVNLY_PROPERTY_URL . 'public/assets/css/havenlytics-root-style.css', 27 58 [], 28 59 HVNLY_PROPERTY_VERSION … … 30 61 31 62 63 // ✅ Bootstrap JS 64 wp_enqueue_script( 65 'havenlytics-bootstrap-bundle', 66 HVNLY_PROPERTY_URL . 'public/assets/plugins/bootstrap/js/bootstrap.bundle.min.js', 67 ['jquery'], 68 '5.3.7', 69 true 70 ); 71 32 72 33 73 // Only load on single property pages 34 74 if (is_singular('hvnly_property')) { 35 75 36 // Front End JS 76 // ✅ Property property-related-carousel CSS 77 wp_enqueue_style( 78 'havenlytics-property-related-carousel', 79 HVNLY_PROPERTY_URL . 'public/assets/css/havenlytics-property-related-carousel.css', 80 [], 81 HVNLY_PROPERTY_VERSION 82 ); 83 84 85 // ✅ Owl carousel css 86 wp_enqueue_style( 87 'havenlytics-property-video', 88 HVNLY_PROPERTY_URL . 'public/assets/css/havenlytics-property-video.css', 89 array(), 90 HVNLY_PROPERTY_VERSION 91 ); 92 93 // ✅ Owl carousel css 94 wp_enqueue_style( 95 'havenlytics-owl-carousel', 96 HVNLY_PROPERTY_URL . 'public/assets/plugins/owl.carouse/css/owl.carousel.min.css', 97 array(), 98 '2.3.4' 99 ); 100 101 // ✅ Owl carousel JS 37 102 wp_enqueue_script( 38 'havenlytics- frontend',39 HVNLY_PROPERTY_URL . 'public/assets/ js/havenlytics-frontend-scripts.js',40 ['jquery'],41 HVNLY_PROPERTY_VERSION,103 'havenlytics-owl-carousel', 104 HVNLY_PROPERTY_URL . 'public/assets/plugins/owl.carouse/js/owl.carousel.min.js', 105 array(), 106 '2.3.4', 42 107 true 43 108 ); 44 109 45 46 // Leaflet CSS 110 // ✅ Leaflet CSS 47 111 wp_enqueue_style( 48 'havenlytics-leaflet -css',49 HVNLY_PROPERTY_URL . 'public/assets/plugins/ css/leaflet.css',112 'havenlytics-leaflet', 113 HVNLY_PROPERTY_URL . 'public/assets/plugins/leaflet/css/leaflet.css', 50 114 array(), 51 115 '1.9.4' 52 116 ); 53 117 54 // Leaflet JS118 // ✅ Leaflet JS 55 119 wp_enqueue_script( 56 'havenlytics-leaflet -js',57 HVNLY_PROPERTY_URL . 'public/assets/plugins/ js/leaflet.js',120 'havenlytics-leaflet', 121 HVNLY_PROPERTY_URL . 'public/assets/plugins/leaflet/js/leaflet.js', 58 122 array(), 59 123 '1.9.4', … … 62 126 63 127 128 129 // ✅ Havenlytics Map JS 64 130 wp_enqueue_script( 65 131 'havenlytics-map', 66 132 HVNLY_PROPERTY_URL . 'public/assets/js/havenlytics-map.js', 67 ['havenlytics-leaflet -js'],133 ['havenlytics-leaflet'], 68 134 HVNLY_PROPERTY_VERSION, 69 135 true 70 136 ); 137 138 // ✅ Property Single CSS 139 wp_enqueue_style( 140 'havenlytics-property-single', 141 HVNLY_PROPERTY_URL . 'public/assets/css/havenlytics-property-single.css', 142 [], 143 HVNLY_PROPERTY_VERSION 144 ); 71 145 } 146 147 // ✅ Front End JS 148 wp_enqueue_script( 149 'havenlytics-frontend-scripts', 150 HVNLY_PROPERTY_URL . 'public/assets/js/havenlytics-frontend-scripts.js', 151 ['jquery'], 152 HVNLY_PROPERTY_VERSION, 153 true 154 ); 72 155 } 73 156 } -
havenlytics/trunk/public/class-frontend.php
r3315596 r3322295 175 175 176 176 if ($query->have_posts()) { 177 echo '<div class="h eavenlytics-container">';178 echo $view_type === 'grid' ? '<div class="h eavenlytics-row">' : '<div class="heavenlytics-list">';177 echo '<div class="havenlytics-main-section"><div class="container">'; 178 echo $view_type === 'grid' ? '<div class="havenlytics-property havenlytics-for-rent havenlytics-for-sale havenlytics-for-featured p-0"><div class="row">' : '<div class="heavenlytics-grid-view">'; 179 179 180 180 while ($query->have_posts()) { … … 184 184 185 185 wp_reset_postdata(); 186 echo '</div> ';186 echo '</div></div>'; 187 187 188 188 $this->render_pagination($query); 189 echo '</div> ';189 echo '</div></div>'; 190 190 } else { 191 191 $this->render_no_properties_found(); … … 225 225 { 226 226 ?> 227 <div class="heavenlytics-col">228 <div class="heavenlytics-property-card">227 <div class="heavenlytics-col"> 228 <div class="heavenlytics-property-card"> 229 229 <div class="heavenlytics-property-card-image"> 230 <?php if ($meta_fields['featured_image']) : ?>230 <?php if ($meta_fields['featured_image']) : ?> 231 231 <img src="<?php echo esc_url($meta_fields['featured_image']); ?>" alt="<?php the_title_attribute(); ?>"> 232 <?php else : ?>232 <?php else : ?> 233 233 <img src="<?php echo esc_url($meta_fields['default_image']); ?>" alt="<?php the_title_attribute(); ?>"> 234 <?php endif; ?>235 236 <?php if ($meta_fields['status_badge']) : ?>234 <?php endif; ?> 235 236 <?php if ($meta_fields['status_badge']) : ?> 237 237 <span class="heavenlytics-property-badge"><?php echo esc_html($meta_fields['status_badge']); ?></span> 238 <?php endif; ?>238 <?php endif; ?> 239 239 </div> 240 240 241 241 <div class="heavenlytics-property-card-content"> 242 <?php if ($meta_fields['price']) : ?>242 <?php if ($meta_fields['price']) : ?> 243 243 <div class="heavenlytics-property-price"><span>$ </span><?php echo esc_html($meta_fields['price']); ?></div> 244 <?php endif; ?>245 246 <h3 class="heavenlytics-property-title"><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h3>247 248 <?php if ($meta_fields['type_label']) : ?>244 <?php endif; ?> 245 246 <h3 class="heavenlytics-property-title"><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h3> 247 248 <?php if ($meta_fields['type_label']) : ?> 249 249 <div class="heavenlytics-property-type"><?php echo esc_html($meta_fields['type_label']); ?></div> 250 <?php endif; ?>251 252 <div class="heavenlytics-property-meta">253 <?php $this->render_meta_item($meta_fields['bedrooms'], __('Beds', 'havenlytics'), 'beds'); ?>254 <?php $this->render_meta_item($meta_fields['bathrooms'], __('Baths', 'havenlytics'), 'baths'); ?>255 <?php $this->render_meta_item($meta_fields['sqft'], __('sqft', 'havenlytics'), 'sqft'); ?>256 </div>257 258 <div class="heavenlytics-property-excerpt">259 <?php echo esc_html(wp_trim_words(get_the_excerpt(), 20)); ?>260 261 </div>262 263 <div class="heavenlytics-property-footer">264 <?php if ($meta_fields['city']) : ?>265 <div class="heavenlytics-property-location">266 <svg viewBox="0 0 24 24">267 <path268 d="M12,11.5A2.5,2.5 0 0,1 9.5,9A2.5,2.5 0 0,1 12,6.5A2.5,2.5 0 0,1 14.5,9A2.5,2.5 0 0,1 12,11.5M12,2A7,7 0 0,0 5,9C5,14.25 12,22 12,22C12,22 19,14.25 19,9A7,7 0 0,0 12,2Z" />269 </svg>270 <?php echo esc_html($meta_fields['city']); ?>271 </div>272 250 <?php endif; ?> 273 251 274 <a href="<?php the_permalink(); ?>" class="heavenlytics-property-link"> 275 <?php esc_html_e('View Details', 'havenlytics'); ?> 276 </a> 277 </div> 252 <div class="heavenlytics-property-meta"> 253 <?php $this->render_meta_item($meta_fields['bedrooms'], __('Beds', 'havenlytics'), 'beds'); ?> 254 <?php $this->render_meta_item($meta_fields['bathrooms'], __('Baths', 'havenlytics'), 'baths'); ?> 255 <?php $this->render_meta_item($meta_fields['sqft'], __('sqft', 'havenlytics'), 'sqft'); ?> 256 </div> 257 258 <div class="heavenlytics-property-excerpt"> 259 <?php echo esc_html(wp_trim_words(get_the_excerpt(), 20)); ?> 260 261 </div> 262 263 <div class="heavenlytics-property-footer"> 264 <?php if ($meta_fields['city']) : ?> 265 <div class="heavenlytics-property-location"> 266 <svg viewBox="0 0 24 24"> 267 <path 268 d="M12,11.5A2.5,2.5 0 0,1 9.5,9A2.5,2.5 0 0,1 12,6.5A2.5,2.5 0 0,1 14.5,9A2.5,2.5 0 0,1 12,11.5M12,2A7,7 0 0,0 5,9C5,14.25 12,22 12,22C12,22 19,14.25 19,9A7,7 0 0,0 12,2Z" /> 269 </svg> 270 <?php echo esc_html($meta_fields['city']); ?> 271 </div> 272 <?php endif; ?> 273 274 <a href="<?php the_permalink(); ?>" class="heavenlytics-property-link"> 275 <?php esc_html_e('View Details', 'havenlytics'); ?> 276 </a> 277 </div> 278 278 </div> 279 </div>280 279 </div> 281 <?php 280 </div> 281 <?php 282 282 } 283 283 … … 285 285 { 286 286 ?> 287 <div class="heavenlytics-list-item">288 <div class="heavenlytics-property-card">287 <div class="heavenlytics-list-item"> 288 <div class="heavenlytics-property-card"> 289 289 <div class="heavenlytics-property-card-image"> 290 <?php if ($meta_fields['featured_image']) : ?>290 <?php if ($meta_fields['featured_image']) : ?> 291 291 <img src="<?php echo esc_url($meta_fields['featured_image']); ?>" alt="<?php the_title_attribute(); ?>"> 292 <?php else : ?>292 <?php else : ?> 293 293 <img src="<?php echo esc_url($meta_fields['default_image']); ?>" alt="<?php the_title_attribute(); ?>"> 294 <?php endif; ?>295 296 <?php if ($meta_fields['status_badge']) : ?>294 <?php endif; ?> 295 296 <?php if ($meta_fields['status_badge']) : ?> 297 297 <span class="heavenlytics-property-badge"><?php echo esc_html($meta_fields['status_badge']); ?></span> 298 <?php endif; ?>298 <?php endif; ?> 299 299 </div> 300 300 301 301 <div class="heavenlytics-property-card-content"> 302 <div class="heavenlytics-property-header"> 303 <h3 class="heavenlytics-property-title"><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a> 304 </h3> 305 <?php if ($meta_fields['price']) : ?> 306 <div class="heavenlytics-property-price"><span>$ </span><?php echo esc_html($meta_fields['price']); ?> 307 </div> 302 <div class="heavenlytics-property-header"> 303 <h3 class="heavenlytics-property-title"><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a> 304 </h3> 305 <?php if ($meta_fields['price']) : ?> 306 <div class="heavenlytics-property-price"><span>$ </span><?php echo esc_html($meta_fields['price']); ?> 307 </div> 308 <?php endif; ?> 309 </div> 310 311 <?php if ($meta_fields['type_label']) : ?> 312 <div class="heavenlytics-property-type"><?php echo esc_html($meta_fields['type_label']); ?></div> 308 313 <?php endif; ?> 309 </div> 310 311 <?php if ($meta_fields['type_label']) : ?> 312 <div class="heavenlytics-property-type"><?php echo esc_html($meta_fields['type_label']); ?></div> 313 <?php endif; ?> 314 315 <div class="heavenlytics-property-meta"> 316 <?php $this->render_meta_item($meta_fields['bedrooms'], __('Beds', 'havenlytics'), 'beds'); ?> 317 <?php $this->render_meta_item($meta_fields['bathrooms'], __('Baths', 'havenlytics'), 'baths'); ?> 318 <?php $this->render_meta_item($meta_fields['sqft'], __('sqft', 'havenlytics'), 'sqft'); ?> 319 </div> 320 321 <div class="heavenlytics-property-excerpt"> 322 <?php echo esc_html(wp_trim_words(get_the_excerpt(), 30)); ?> 323 </div> 324 325 <div class="heavenlytics-property-footer"> 326 <?php if ($meta_fields['city']) : ?> 327 <div class="heavenlytics-property-location"> 328 <svg viewBox="0 0 24 24"> 329 <path 330 d="M12,11.5A2.5,2.5 0 0,1 9.5,9A2.5,2.5 0 0,1 12,6.5A2.5,2.5 0 0,1 14.5,9A2.5,2.5 0 0,1 12,11.5M12,2A7,7 0 0,0 5,9C5,14.25 12,22 12,22C12,22 19,14.25 19,9A7,7 0 0,0 12,2Z" /> 331 </svg> 332 <?php echo esc_html($meta_fields['city']); ?> 333 </div> 334 <?php endif; ?> 335 336 <a href="<?php the_permalink(); ?>" class="heavenlytics-property-link"> 337 <?php esc_html_e('View Details', 'havenlytics'); ?> 338 </a> 339 </div> 314 315 <div class="heavenlytics-property-meta"> 316 <?php $this->render_meta_item($meta_fields['bedrooms'], __('Beds', 'havenlytics'), 'beds'); ?> 317 <?php $this->render_meta_item($meta_fields['bathrooms'], __('Baths', 'havenlytics'), 'baths'); ?> 318 <?php $this->render_meta_item($meta_fields['sqft'], __('sqft', 'havenlytics'), 'sqft'); ?> 319 </div> 320 321 <div class="heavenlytics-property-excerpt"> 322 <?php echo esc_html(wp_trim_words(get_the_excerpt(), 30)); ?> 323 </div> 324 325 <div class="heavenlytics-property-footer"> 326 <?php if ($meta_fields['city']) : ?> 327 <div class="heavenlytics-property-location"> 328 <svg viewBox="0 0 24 24"> 329 <path 330 d="M12,11.5A2.5,2.5 0 0,1 9.5,9A2.5,2.5 0 0,1 12,6.5A2.5,2.5 0 0,1 14.5,9A2.5,2.5 0 0,1 12,11.5M12,2A7,7 0 0,0 5,9C5,14.25 12,22 12,22C12,22 19,14.25 19,9A7,7 0 0,0 12,2Z" /> 331 </svg> 332 <?php echo esc_html($meta_fields['city']); ?> 333 </div> 334 <?php endif; ?> 335 336 <a href="<?php the_permalink(); ?>" class="heavenlytics-property-link"> 337 <?php esc_html_e('View Details', 'havenlytics'); ?> 338 </a> 339 </div> 340 340 </div> 341 </div>342 341 </div> 343 <?php 342 </div> 343 <?php 344 344 } 345 345 … … 401 401 402 402 $icons = [ 403 'beds' => '<svg viewBox="0 0 24 24"><path d="M17,12H15V14H17M17,8H15V10H17M3,3V21H21V3M7,18H5V16H7M7,14H5V12H7M7,10H5V8H7M11,18H9V16H11M11,14H9V12H11M11,10H9V8H11M19,18H13V16H15V14H13V12H15V10H13V8H19"/></svg>', 404 'baths' => '<svg viewBox="0 0 24 24"><path d="M19,5H17V3H7V5H5C3.9,5 3,5.9 3,7V8C3,10.55 4.92,12.63 7.39,12.94C8.02,14.44 9.37,15.57 11,15.9V19H7V21H17V19H13V15.9C14.63,15.57 15.98,14.44 16.61,12.94C19.08,12.63 21,10.55 21,8V7C21,5.9 20.1,5 19,5M5,8V7H7V10.82C5.84,10.4 5,9.3 5,8M19,8C19,9.3 18.16,10.4 17,10.82V7H19V8Z"/></svg>', 405 'sqft' => '<svg viewBox="0 0 24 24"><path d="M12,6C14.5,6 16.64,7.44 17.64,9.59L19,8.86C17.81,6.14 15.06,4.5 12,4.5C8.94,4.5 6.19,6.14 5,8.86L6.36,9.59C7.36,7.44 9.5,6 12,6M12,15A2,2 0 0,1 10,13A2,2 0 0,1 12,11A2,2 0 0,1 14,13A2,2 0 0,1 12,15M4.5,16.5V19.5H7.5V16.5H4.5M16.5,16.5V19.5H19.5V16.5H16.5Z"/></svg>', 403 'beds' => '<svg fill="#6C60FE" width="20px" height="20px" viewBox="0 -11.47 122.88 122.88" version="1.1" 404 id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" 405 style="enable-background:new 0 0 122.88 99.94" xml:space="preserve"> 406 <g id="SVGRepo_bgCarrier" stroke-width="0"></g> 407 <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g> 408 <g id="SVGRepo_iconCarrier"> 409 <g> 410 <path 411 d="M4.22,67.36h114.31v-4.67c0-1.13-0.22-2.18-0.61-3.12c-0.42-1-1.04-1.89-1.81-2.66c-0.47-0.47-1-0.9-1.57-1.28 c-0.58-0.39-1.2-0.73-1.85-1.02c-1.75-0.38-3.49-0.74-5.22-1.08c-1.74-0.34-3.49-0.66-5.25-0.96c-0.08-0.01-0.14-0.02-0.22-0.04 c-0.89-0.15-1.74-0.29-2.55-0.42c-0.81-0.13-1.67-0.26-2.57-0.4l-0.02,0c-6.12-0.78-12.22-1.38-18.31-1.78 c-6.1-0.4-12.17-0.6-18.2-0.61c-3.58,0-7.15,0.06-10.72,0.2c-3.55,0.14-7.12,0.34-10.69,0.62l-0.02,0 c-3.34,0.31-6.67,0.7-10.01,1.15c-3.33,0.45-6.67,0.98-10.03,1.57l-0.37,0.09c-0.07,0.02-0.14,0.03-0.2,0.03 c-0.06,0.01-0.12,0.01-0.18,0.01c-1.57,0.28-3.18,0.59-4.84,0.92c-1.61,0.32-3.22,0.66-4.82,1.01c-0.4,0.22-0.78,0.47-1.14,0.73 c-0.36,0.27-0.71,0.56-1.02,0.87v0c-0.67,0.67-1.2,1.44-1.56,2.3c-0.34,0.81-0.53,1.71-0.53,2.69V67.36L4.22,67.36z M14.2,0h92.99 c1.21,0,2.37,0.24,3.43,0.68c1.1,0.46,2.09,1.13,2.92,1.95c0.83,0.83,1.5,1.82,1.95,2.92c0.44,1.06,0.68,2.22,0.68,3.43v42.69 c0.51,0.3,1.01,0.63,1.47,0.99c0.52,0.4,1.01,0.82,1.46,1.27c1.16,1.16,2.1,2.51,2.73,4.03c0.6,1.43,0.93,3.02,0.93,4.74v6.09 c0.03,0.1,0.06,0.2,0.08,0.3l0,0.02c0.02,0.13,0.03,0.25,0.03,0.37c0,0.13-0.01,0.26-0.04,0.39l0,0c-0.02,0.1-0.05,0.2-0.08,0.3 v27.66c0,0.58-0.24,1.11-0.62,1.49c-0.38,0.38-0.91,0.62-1.49,0.62h-4.35c-0.49,0-0.94-0.17-1.3-0.45 c-0.36-0.28-0.63-0.68-0.74-1.14c-0.8-2.3-1.61-4.12-2.48-5.54c-0.86-1.4-1.78-2.4-2.84-3.11c-1.07-0.71-2.35-1.16-3.9-1.43 c-1.58-0.28-3.42-0.37-5.61-0.36l-79.76,0.1l-0.04,0c-1.57-0.03-2.86,0.17-3.94,0.59c-1.07,0.42-1.94,1.05-2.66,1.86 c-0.81,0.9-1.49,2.05-2.11,3.39c-0.63,1.37-1.2,2.93-1.77,4.64l0,0c-0.14,0.44-0.42,0.79-0.77,1.04c-0.33,0.24-0.73,0.38-1.14,0.4 c-0.03,0.01-0.06,0.01-0.09,0.01H2.11c-0.58,0-1.11-0.24-1.49-0.62C0.24,98.94,0,98.41,0,97.83V61.52c0-1.57,0.3-3.01,0.84-4.31 c0.58-1.38,1.43-2.61,2.49-3.67c0.3-0.3,0.63-0.6,0.98-0.88c0.3-0.24,0.6-0.47,0.92-0.68V8.89c0-1.21,0.24-2.36,0.68-3.4 c0.46-1.09,1.13-2.07,1.96-2.89c0.83-0.82,1.82-1.47,2.91-1.92C11.84,0.24,12.99,0,14.2,0L14.2,0z M107.19,4.22H14.2 c-0.65,0-1.27,0.13-1.84,0.36c-0.59,0.24-1.11,0.59-1.55,1.02c-0.43,0.42-0.78,0.94-1.02,1.5C9.57,7.65,9.45,8.25,9.45,8.89v41.06 c0.3-0.1,0.6-0.18,0.91-0.26c0.49-0.13,0.98-0.24,1.47-0.32c0.68-0.12,1.42-0.25,2.22-0.39c0.6-0.1,1.24-0.21,1.9-0.31V38.19 c0-1.58,0.32-3.09,0.89-4.47c0.6-1.44,1.47-2.73,2.55-3.81c1.08-1.08,2.37-1.95,3.81-2.55c1.38-0.57,2.89-0.89,4.47-0.89h19.82 c1.58,0,3.09,0.32,4.47,0.89c1.44,0.6,2.73,1.47,3.81,2.55c1.08,1.08,1.95,2.37,2.55,3.81c0.57,1.38,0.89,2.89,0.89,4.47v6.69 c0.7-0.01,1.4-0.01,2.11-0.01v-6.68c0-1.58,0.32-3.09,0.89-4.47c0.6-1.44,1.47-2.73,2.55-3.81c1.08-1.08,2.37-1.95,3.81-2.55 c1.38-0.57,2.89-0.89,4.47-0.89h19.82c1.58,0,3.09,0.32,4.47,0.89c1.44,0.6,2.73,1.47,3.81,2.55c1.08,1.08,1.95,2.37,2.55,3.81 c0.57,1.38,0.89,2.89,0.89,4.47v10.34c0.75,0.11,1.55,0.24,2.41,0.38c0.95,0.15,1.86,0.3,2.74,0.45c0.45,0.08,0.91,0.17,1.37,0.28 c0.29,0.07,0.57,0.14,0.84,0.22V8.98c0-0.64-0.13-1.25-0.36-1.81c-0.24-0.58-0.6-1.1-1.04-1.55c-0.44-0.44-0.97-0.8-1.54-1.04 C108.44,4.35,107.83,4.22,107.19,4.22L107.19,4.22z M43.21,45.56c2.01-0.15,4.03-0.28,6.08-0.38c1.89-0.1,3.8-0.17,5.71-0.22v-6.77 c0-1.01-0.2-1.98-0.57-2.86c-0.38-0.92-0.94-1.74-1.64-2.44c-0.69-0.69-1.52-1.25-2.44-1.64c-0.88-0.37-1.85-0.57-2.86-0.57H27.67 c-1.01,0-1.98,0.2-2.86,0.57c-0.92,0.38-1.74,0.94-2.44,1.64c-0.69,0.69-1.25,1.52-1.64,2.44c-0.37,0.88-0.57,1.85-0.57,2.86V48 c1.62-0.24,3.26-0.46,4.94-0.68c1.81-0.23,3.61-0.44,5.39-0.64c0.69-0.08,1.43-0.17,2.2-0.25c0.72-0.08,1.47-0.15,2.27-0.23 c1.36-0.13,2.71-0.25,4.04-0.36C40.37,45.75,41.77,45.65,43.21,45.56L43.21,45.56z M65.54,44.9c1.21,0.02,2.42,0.05,3.63,0.09 c1.34,0.04,2.68,0.1,4.01,0.16l0.01,0c2.19,0.08,4.33,0.18,6.41,0.3c2.08,0.12,4.11,0.27,6.05,0.44c2.82,0.25,5.55,0.55,8.14,0.9 c2.32,0.32,4.52,0.68,6.58,1.08v-9.68c0-1.01-0.2-1.98-0.57-2.86c-0.38-0.92-0.94-1.74-1.64-2.44c-0.69-0.69-1.52-1.25-2.44-1.64 c-0.88-0.37-1.85-0.57-2.86-0.57H73.05c-1.01,0-1.98,0.2-2.86,0.57c-0.92,0.38-1.74,0.94-2.44,1.64c-0.69,0.69-1.25,1.52-1.64,2.44 c-0.37,0.88-0.57,1.85-0.57,2.86V44.9L65.54,44.9z M118.54,71.59H4.22v24.13h1.43c0.56-1.58,1.14-3.05,1.79-4.36 c0.7-1.4,1.49-2.64,2.45-3.71c1.14-1.28,2.48-2.27,4.09-2.93c1.61-0.65,3.49-0.98,5.75-0.93l79.69-0.1c2.57,0,4.77,0.12,6.69,0.49 c1.95,0.37,3.63,1,5.14,2c1.4,0.93,2.6,2.16,3.68,3.77c1.03,1.54,1.95,3.43,2.83,5.76h0.76V71.59L118.54,71.59z"> 412 </path> 413 </g> 414 </g> 415 </svg>', 416 'baths' => '<svg fill="#6C60FE" width="20px" height="20px" viewBox="0 0 512 512" id="Layer_1" 417 enable-background="new 0 0 512 512" xmlns="http://www.w3.org/2000/svg"> 418 <g id="SVGRepo_bgCarrier" stroke-width="0"></g> 419 <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g> 420 <g id="SVGRepo_iconCarrier"> 421 <g> 422 <path 423 d="m496 288c-38.154 0-437.487 0-448 0v-56h32c8.837 0 16-7.164 16-16v-40c0-8.836-7.163-16-16-16s-16 7.164-16 16v24h-16v-138.745c0-25.903 31.562-39.064 49.941-20.686l16.94 16.94c-13.424 23.401-10.164 53.835 9.805 73.805l8 8c6.247 6.248 16.379 6.249 22.627 0l64-64c6.249-6.248 6.249-16.379 0-22.627l-8-8c-20.35-20.351-50.837-23.06-73.817-9.817l-16.928-16.928c-11.57-11.57-26.952-17.942-43.313-17.942-33.776 0-61.255 27.479-61.255 61.255v226.745c-8.837 0-16 7.164-16 16s7.163 16 16 16v32c0 43.889 19.742 83.247 50.806 109.681l-22.338 23.229c-9.803 10.193-2.445 27.09 11.53 27.09 4.199 0 8.394-1.644 11.534-4.91l26.218-27.263c19.844 10.326 42.376 16.173 66.25 16.173h192c23.874 0 46.406-5.847 66.25-16.173l26.218 27.263c6.106 6.35 16.234 6.585 22.623.442 6.369-6.125 6.566-16.254.441-22.623l-22.338-23.229c31.064-26.433 50.806-65.791 50.806-109.68v-32c8.837 0 16-7.164 16-16s-7.163-16-16-16zm-310.89-223.738-40.845 40.845c-8.246-11.427-7.23-27.515 3.048-37.794 10.378-10.377 26.461-11.259 37.797-3.051zm278.89 287.738c0 61.757-50.243 112-112 112h-192c-61.757 0-112-50.243-112-112v-32h416z"> 424 </path> 425 </g> 426 </g> 427 </svg>', 428 'sqft' => '<svg fill="#6C60FE" width="20px" height="20px" viewBox="0 0 256 256" id="Flat" 429 xmlns="http://www.w3.org/2000/svg"> 430 <g id="SVGRepo_bgCarrier" stroke-width="0"></g> 431 <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round" 432 stroke="#CCCCCC" stroke-width="7.68"></g> 433 <g id="SVGRepo_iconCarrier"> 434 <path 435 d="M240,211.98316H227.99414v-108a12.01343,12.01343,0,0,0-12-12h-68v-52a12.01343,12.01343,0,0,0-12-12h-96a12.01343,12.01343,0,0,0-12,12v172H16a4,4,0,0,0,0,8H240a4,4,0,0,0,0-8Zm-24.00586-112a4.00426,4.00426,0,0,1,4,4v108h-72v-112Zm-180-60a4.00427,4.00427,0,0,1,4-4h96a4.00426,4.00426,0,0,1,4,4v172h-104Zm24,32a4.0002,4.0002,0,0,1,4-4h32a4,4,0,1,1,0,8h-32A4.0002,4.0002,0,0,1,59.99414,71.98316Zm56,64a4.0002,4.0002,0,0,1-4,4h-32a4,4,0,0,1,0-8h32A4.0002,4.0002,0,0,1,115.99414,135.98316Zm-16,40a4.0002,4.0002,0,0,1-4,4h-32a4,4,0,0,1,0-8h32A4.0002,4.0002,0,0,1,99.99414,175.98316Zm96,0a4.0002,4.0002,0,0,1-4,4h-16a4,4,0,0,1,0-8h16A4.0002,4.0002,0,0,1,195.99414,175.98316Zm-24-40a4.0002,4.0002,0,0,1,4-4h16a4,4,0,1,1,0,8h-16A4.0002,4.0002,0,0,1,171.99414,135.98316Z"> 436 </path> 437 </g> 438 </svg>', 406 439 ]; 407 440 … … 412 445 'width' => true, 413 446 'height' => true, 414 'view Box' => true,447 'viewbox' => true, 415 448 'aria-hidden' => true, 416 449 'role' => true, 417 450 'focusable' => true, 418 451 'fill' => true, 452 'id' => true, 453 'xmlns:xlink' => true, 454 'xlink:href' => true, 455 'style' => true, 456 'xml:space' => true, 457 'enable-background' => true, 458 'version' => true, 459 ], 460 'g' => [ 461 'id' => true, 462 'fill' => true, 463 'stroke' => true, 464 'stroke-width' => true, 465 'stroke-linecap' => true, 466 'stroke-linejoin' => true, 419 467 ], 420 468 'path' => [ … … 424 472 'stroke-width' => true, 425 473 ], 426 'g' => ['fill' => true],427 474 ]; 428 475 429 476 ?> 430 <div class="heavenlytics-property-meta-item"> 431 <?php echo wp_kses($icons[$icon_type], $allowed_svg_tags); ?> 432 <?php echo esc_html($value); ?> <?php echo esc_html($label); ?> 477 <div class="heavenlytics-property-meta-item"> 478 <?php echo wp_kses($icons[$icon_type], $allowed_svg_tags); ?> 479 <?php echo esc_html($value); ?> <?php echo esc_html($label); ?> 480 </div> 481 <?php 482 } 483 484 private function render_meta_list_item($value, $label, $icon_class_name) 485 { 486 if (empty($value)) return; 487 ?> 488 <div class="havenlytics-detail-item"> 489 <div class="havenlytics-detail-icon"> 490 <i class="<?php echo esc_attr($icon_class_name); ?>"></i> 433 491 </div> 434 <?php 435 } 492 <div class="havenlytics-detail-value"><?php echo esc_html($value); ?></div> 493 <div class="havenlytics-detail-label"><?php echo esc_html($label); ?></div> 494 </div> 495 <?php 496 } 497 498 /** 499 * Render star rating output for a property. 500 * 501 * @param int $property_id The post ID (typically a property post). 502 * @param string $rating_metabox The custom meta key containing the rating value. 503 */ 504 private function render_star_feedback($property_id, $rating_metabox) 505 { 506 $rating = get_post_meta($property_id, $rating_metabox, true); 507 508 if (empty($rating)) { 509 return; 510 } 511 512 $rating = floatval($rating); 513 $full_stars = floor($rating); 514 $half_star = ($rating - $full_stars) >= 0.5 ? 1 : 0; 515 $empty_stars = 5 - $full_stars - $half_star; 516 517 echo '<div class="havenlytics-property-rating">'; 518 echo '<span class="havenlytics-property-rating-count">'; 519 520 for ($i = 0; $i < $full_stars; $i++) { 521 echo '<i class="fa-solid fa-star checked"></i>'; 522 } 523 524 if ($half_star) { 525 echo '<i class="fa-solid fa-star-half-stroke"></i>'; 526 } 527 528 for ($i = 0; $i < $empty_stars; $i++) { 529 echo '<i class="fa-regular fa-star"></i>'; 530 } 531 532 echo '</span>'; 533 534 // Optional: Rating label 535 $text = ''; 536 537 if ($rating >= 4.5) { 538 $text = __('Excellent', 'havenlytics'); 539 } elseif ($rating >= 3.5) { 540 $text = __('Good', 'havenlytics'); 541 } elseif ($rating >= 2.5) { 542 $text = __('Average', 'havenlytics'); 543 } else { 544 $text = __('Poor', 'havenlytics'); 545 } 546 547 echo '<span class="havenlytics-property-rating-review">' . esc_html($text) . '</span>'; 548 echo '</div>'; 549 } 550 551 /** 552 * Get currency symbol based on currency code stored in post meta. 553 * 554 * @param int $property_id Post ID. 555 * @param string $meta_key Meta key where currency code is stored. 556 * 557 * @return string Currency symbol. 558 */ 559 private function get_currency_symbol($property_id, $meta_key = '_havenlytics_currency') 560 { 561 $symbols = array( 562 'USD' => '$', 563 'EUR' => '€', 564 'GBP' => '£', 565 'AUD' => 'A$', 566 'CAD' => 'C$', 567 'JPY' => '¥', 568 'CHF' => 'CHF', 569 'CNY' => '¥', 570 'INR' => '₹', 571 'NZD' => 'NZ$', 572 'SGD' => 'S$', 573 'HKD' => 'HK$', 574 'SEK' => 'kr', 575 'NOK' => 'kr', 576 'MXN' => 'Mex$', 577 'ZAR' => 'R', 578 'BRL' => 'R$', 579 'RUB' => '₽', 580 'KRW' => '₩', 581 'TRY' => '₺', 582 ); 583 584 $currency_code = get_post_meta($property_id, $meta_key, true); 585 586 return isset($symbols[$currency_code]) ? $symbols[$currency_code] : '$'; 587 } 588 436 589 437 590 … … 439 592 { 440 593 if ($query->max_num_pages <= 1) return; 441 442 594 echo '<div class="heavenlytics-pagination">'; 443 echo wp_kses_post(paginate_links([ 595 echo 596 wp_kses_post(paginate_links([ 444 597 'base' => str_replace(999999999, '%#%', esc_url(get_pagenum_link(999999999))), 445 598 'format' => '?paged=%#%', … … 456 609 { 457 610 ?> 458 <div class="heavenlytics-col heavenlytics-col-12">459 <p><?php esc_html_e('No properties found.', 'havenlytics'); ?></p>460 </div>611 <div class="heavenlytics-col heavenlytics-col-12"> 612 <p><?php esc_html_e('No properties found.', 'havenlytics'); ?></p> 613 </div> 461 614 <?php 462 615 } -
havenlytics/trunk/readme.txt
r3315596 r3322295 6 6 Tested up to: 6.8 7 7 Requires PHP: 7.2 8 Stable tag: 1.0. 28 Stable tag: 1.0.3 9 9 License: GPLv2 or later 10 10 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 16 16 Havenlytics is a powerful real estate and property listing plugin for WordPress that helps you showcase properties with beautiful grid and list views. With advanced filtering, sorting, and display options, it's perfect for real estate agencies, property managers, and individual agents. 17 17 18 We’re actively improving Havenlytics with weekly updates. Got an idea, feature request, or found a bug? 19 👉 Email us directly at [[email protected]](mailto:[email protected]) — we’d love your feedback! 20 21 == Development & Roadmap == 22 23 🚀 Havenlytics is currently in active beta and rapidly evolving! 24 We are releasing **weekly updates** with new features, enhancements, and performance improvements to make this the **most advanced real estate plugin for WordPress**. 25 26 Our goal is to build a large-scale, powerful solution for real estate agencies, property managers, and individual agents. We’re listening to our users and prioritizing features based on your feedback. 27 28 💡 **Got suggestions or need a feature?** 29 We’d love to hear from you! Email us at [[email protected]](mailto:[email protected]) and help shape the future of Havenlytics. 30 31 📌 **Upcoming features may include:** 32 - Saved listings for users 33 - Advanced map search 34 - Agent profile pages 35 - CRM-style lead tracking 36 - Property comparison tool 37 - REST API support 38 - Frontend submission forms 39 - Multilingual and RTL support 40 41 18 42 = Key Features = 43 19 44 - Display properties in grid or list view 20 45 - Advanced filtering by price, bedrooms, bathrooms, status, and type … … 106 131 == Changelog == 107 132 133 = 1.0.3 = 134 * Implemented UI/UX improvements using Bootstrap framework. 135 * Improved frontend responsiveness across devices. 136 * Optimized layout structure for better accessibility and readability. 137 * Refactored frontend templates for cleaner HTML output. 138 * Minor CSS enhancements and bug fixes. 139 140 141 = 1.0.2 = 142 * Initial release of Havenlytics plugin 143 * Grid and list view shortcodes 144 * Advanced filtering and sorting options 145 * Customizable display settings 146 147 = 1.0.1 = 148 * Initial release of Havenlytics plugin 149 * Grid and list view shortcodes 150 * Advanced filtering and sorting options 151 * Customizable display settings 152 108 153 = 1.0.0 = 109 154 * Initial release of Havenlytics plugin … … 114 159 == Upgrade Notice == 115 160 116 = 1.0.0 = 161 = 1.0.0 = 117 162 Initial release of Havenlytics plugin. 118 163 119 == License == 120 GNU General Public License v2 or later 164 == License == 165 GNU General Public License v2 or later 166 167 == Support == 168 169 💬 We welcome your feedback to make Havenlytics better. 170 Send your ideas or suggestions to [[email protected]](mailto:[email protected]). 171 172 Need help or have questions? 173 174 - 📧 Email: [[email protected]](mailto:[email protected]) 175 - 🌐 Website: [https://havenlytics.com](https://havenlytics.com) 176 - 💬 GitHub Issues: [Submit a bug or feature request](https://github.com/havenlytics/Havenlytics/issues) 177 - 🧩 Plugin Page: [https://wordpress.org/plugins/havenlytics/](https://wordpress.org/plugins/havenlytics/) 178 179 We’re here to help you get the most out of Havenlytics. For customization, feature requests, or bug reports, feel free to reach out via email or open a ticket on GitHub. -
havenlytics/trunk/templates/hvnly_property-single.php
r3315596 r3322295 12 12 13 13 // Get all meta values 14 15 $currency_code = get_post_meta($property_id, '_havenlytics_currency', true); 16 $short_description = get_post_meta($property_id, '_havenlytics_short_description', true); 17 $rating = get_post_meta($property_id, '_havenlytics_rating_star', true); 14 18 $price = get_post_meta($property_id, '_havenlytics_price', true); 15 19 $bedrooms = get_post_meta($property_id, '_havenlytics_bedrooms', true); … … 83 87 $status_badge = ''; 84 88 } 89 90 // Define currency symbols array 91 $currency_symbols = array( 92 'USD' => '$', 93 'EUR' => '€', 94 'GBP' => '£', 95 'AUD' => 'A$', 96 'CAD' => 'C$', 97 'JPY' => '¥', 98 'CHF' => 'CHF', 99 'CNY' => '¥', 100 'INR' => '₹', 101 'NZD' => 'NZ$', 102 'SGD' => 'S$', 103 'HKD' => 'HK$', 104 'SEK' => 'kr', 105 'NOK' => 'kr', 106 'MXN' => 'Mex$', 107 'ZAR' => 'R', 108 'BRL' => 'R$', 109 'RUB' => '₽', 110 'KRW' => '₩', 111 'TRY' => '₺', 112 ); 113 114 // Get the symbol for the currency code, fallback to $ 115 $currency_symbol = isset($currency_symbols[$currency_code]) ? $currency_symbols[$currency_code] : '$'; 116 117 118 119 85 120 ?> 86 121 87 <div class="havenlytics-single-header"> 88 <div class="havenlytics-single-title"> 89 <h1><?php the_title(); ?></h1> 90 <?php if ($address) : ?> 91 <div class="havenlytics-single-address"> 92 <svg viewBox="0 0 24 24" class="havenlytics-icon"> 93 <path 94 d="M12,11.5A2.5,2.5 0 0,1 9.5,9A2.5,2.5 0 0,1 12,6.5A2.5,2.5 0 0,1 14.5,9A2.5,2.5 0 0,1 12,11.5M12,2A7,7 0 0,0 5,9C5,14.25 12,22 12,22C12,22 19,14.25 19,9A7,7 0 0,0 12,2Z" /> 95 </svg> 96 <?php echo esc_html($address); ?> 97 </div> 98 <?php endif; ?> 99 </div> 100 101 <div class="havenlytics-single-price-status"> 102 <?php if ($price) : ?> 103 <div class="havenlytics-single-price"><span>$ </span><?php echo esc_html($price); ?></div> 104 <?php endif; ?> 105 106 <?php if ($status_badge) : ?> 107 <div class="havenlytics-single-status"><?php echo esc_html($status_badge); ?></div> 108 <?php endif; ?> 109 </div> 110 </div> 111 112 <div class="havenlytics-single-gallery"> 113 <?php if (!empty($gallery_images)) : ?> 114 <div class="havenlytics-gallery-main"> 115 <?php echo wp_get_attachment_image($gallery_images[0], 'large', false, array('class' => 'havenlytics-main-image')); ?> 116 </div> 117 118 <div class="havenlytics-gallery-thumbs"> 119 <?php foreach ($gallery_images as $image_id) : ?> 120 <div class="havenlytics-gallery-thumb"> 121 <?php echo wp_get_attachment_image($image_id, 'thumbnail', false, array('data-large' => wp_get_attachment_image_url($image_id, 'large'))); ?> 122 </div> 123 <?php endforeach; ?> 124 </div> 125 <?php elseif (has_post_thumbnail()) : ?> 126 <div class="havenlytics-gallery-main"> 127 <?php the_post_thumbnail('large', array('class' => 'havenlytics-main-image')); ?> 128 </div> 129 <?php else : ?> 130 <div class="havenlytics-gallery-main"> 131 <img src="<?php echo esc_url(plugins_url('public/assets/img/no-thumb.png', dirname(__FILE__))); ?>" 132 class="havenlytics-main-image" alt="<?php the_title_attribute(); ?>"> 133 134 </div> 135 <?php endif; ?> 136 </div> 137 138 139 <div class="havenlytics-single-content"> 140 <div class="havenlytics-single-main"> 141 <div class="havenlytics-single-description"> 142 <h2><?php esc_html_e('Property Description', 'havenlytics'); ?></h2> 143 <?php the_content(); ?> 144 </div> 145 146 <div class="havenlytics-single-details"> 147 <h2><?php esc_html_e('Property Details', 'havenlytics'); ?></h2> 148 <div class="havenlytics-details-grid"> 149 <?php if ($bedrooms) : ?> 150 <div class="havenlytics-detail-item"> 151 <span class="havenlytics-detail-label"><?php esc_html_e('Bedrooms', 'havenlytics'); ?></span> 152 <span class="havenlytics-detail-value"><?php echo esc_html($bedrooms); ?></span> 153 </div> 154 <?php endif; ?> 155 156 <?php if ($bathrooms) : ?> 157 <div class="havenlytics-detail-item"> 158 <span class="havenlytics-detail-label"><?php esc_html_e('Bathrooms', 'havenlytics'); ?></span> 159 <span class="havenlytics-detail-value"><?php echo esc_html($bathrooms); ?></span> 160 </div> 161 <?php endif; ?> 162 163 <?php if ($sqft) : ?> 164 <div class="havenlytics-detail-item"> 165 <span class="havenlytics-detail-label"><?php esc_html_e('Area', 'havenlytics'); ?></span> 166 <span class="havenlytics-detail-value"><?php echo esc_html(number_format($sqft)); ?> sqft</span> 167 </div> 168 <?php endif; ?> 169 170 <?php if ($property_type) : ?> 171 <div class="havenlytics-detail-item"> 172 <span 173 class="havenlytics-detail-label"><?php esc_html_e('Property Type', 'havenlytics'); ?></span> 174 <span class="havenlytics-detail-value"><?php //echo esc_html($this->get_property_type_label($property_type)); 175 ?></span> 176 </div> 177 <?php endif; ?> 178 179 <?php if ($year_built) : ?> 180 <div class="havenlytics-detail-item"> 181 <span class="havenlytics-detail-label"><?php esc_html_e('Year Built', 'havenlytics'); ?></span> 182 <span class="havenlytics-detail-value"><?php echo esc_html($year_built); ?></span> 183 </div> 184 <?php endif; ?> 185 186 <?php if ($garage) : ?> 187 <div class="havenlytics-detail-item"> 188 <span class="havenlytics-detail-label"><?php esc_html_e('Garage', 'havenlytics'); ?></span> 189 <span class="havenlytics-detail-value"><?php echo esc_html($garage); ?></span> 190 </div> 191 <?php endif; ?> 192 193 <?php if ($mls_number) : ?> 194 <div class="havenlytics-detail-item"> 195 <span class="havenlytics-detail-label"><?php esc_html_e('MLS #', 'havenlytics'); ?></span> 196 <span class="havenlytics-detail-value"><?php echo esc_html($mls_number); ?></span> 197 </div> 122 <div class="container"> 123 <!-- Property Gallery with Prefix --> 124 <div class="havenlytics_property_single"> 125 <div class="property-gallery"> 126 <?php if (!empty($gallery_images)) : ?> 127 <div class="gallery-main"> 128 <?php foreach ($gallery_images as $index => $image_id) : ?> 129 <div class="gallery-slide <?php echo $index === 0 ? 'active' : ''; ?>" 130 style="background-image: url('<?php echo esc_url(wp_get_attachment_image_url($image_id, 'large')); ?>')"> 131 </div> 132 133 <?php endforeach; ?> 134 135 <div class="gallery-counter">1/<?php echo count($gallery_images); ?></div> 136 <div class="transition-indicator"></div> 137 138 <div class="gallery-controls"> 139 <button class="gallery-btn prev-btn"> 140 <i class="fas fa-chevron-left"></i> 141 </button> 142 <button class="gallery-btn next-btn"> 143 <i class="fas fa-chevron-right"></i> 144 </button> 145 </div> 146 </div> 147 148 <div class="thumbnail-container"> 149 <?php foreach ($gallery_images as $index => $image_id) : ?> 150 <div class="property-thumbnail <?php echo $index === 0 ? 'active' : ''; ?>" 151 data-index="<?php echo esc_attr($index); ?>" 152 style="background-image: url('<?php echo esc_url(wp_get_attachment_image_url($image_id, 'thumbnail')); ?>')"> 153 </div> 154 <?php endforeach; ?> 155 </div> 156 157 <?php elseif (has_post_thumbnail($property_id)) : ?> 158 <div class="gallery-main"> 159 <div class="gallery-slide active" 160 style="background-image: url('<?php echo esc_url(get_the_post_thumbnail_url($property_id, 'large')); ?>')"> 161 </div> 162 163 <div class="gallery-counter">1/1</div> 164 <div class="transition-indicator"></div> 165 166 <div class="gallery-controls"> 167 <button class="gallery-btn prev-btn"> 168 <i class="fas fa-chevron-left"></i> 169 </button> 170 <button class="gallery-btn next-btn"> 171 <i class="fas fa-chevron-right"></i> 172 </button> 173 </div> 174 </div> 175 176 <div class="thumbnail-container"> 177 <div class="property-thumbnail active" data-index="0" 178 style="background-image: url('<?php echo esc_url(get_the_post_thumbnail_url($property_id, 'thumbnail')); ?>')"> 179 </div> 180 </div> 181 182 <?php else : ?> 183 <div class="gallery-main"> 184 <div class="gallery-slide active" 185 style="background-image: url('<?php echo esc_url(plugins_url('public/assets/img/no-thumb.png', dirname(__FILE__))); ?>')"> 186 </div> 187 <div class="gallery-counter">0/0</div> 188 </div> 198 189 <?php endif; ?> 199 190 </div> 191 192 <!-- Property Header --> 193 <div class="property-header"> 194 <div> 195 <h1 class="property-title"><?php the_title(); ?></h1> 196 <div class="property-address"> 197 <i class="fas fa-map-marker-alt"></i> 198 <?php echo esc_html($address); ?> 199 </div> 200 201 202 </div> 203 <div class="property-price"> 204 205 <?php echo esc_html($currency_symbol); ?> <?php echo esc_html($price); ?> 206 </div> 207 </div> 208 209 210 211 <div class="row"> 212 <div class="col-lg-8"> 213 <?php if ($short_description) : ?> 214 <!-- Property Summary --> 215 <div class="property-details-card"> 216 <h2 class="section-title">Property Summary</h2> 217 <p class="property-description"> 218 <?php echo esc_html($short_description); ?> 219 </p> 220 221 </div> 222 <?php endif; ?> 223 <!-- Property Details --> 224 <div class="property-details-card"> 225 <h2 class="section-title">Property Details</h2> 226 <div class="property-description"> 227 <?php the_content(); ?> 228 </div> 229 230 </div> 231 232 <!-- Property Overview --> 233 <div class="property-details-card"> 234 <h2 class="section-title">Overview</h2> 235 236 <div class="row"> 237 <div class="col-lg-4 col-md-6"> 238 239 <?php if ($bedrooms) : ?> 240 <div class="detail-item"> 241 <div class="detail-icon"> 242 <i class="fas fa-bed"></i> 243 </div> 244 <div class="detail-content"> 245 <h4><?php esc_html_e('Bedrooms', 'havenlytics'); ?></h4> 246 <p><?php echo esc_html($bedrooms); ?></p> 247 </div> 248 </div> 249 <?php endif; ?> 250 251 <?php if ($sqft) : ?> 252 <div class="detail-item"> 253 <div class="detail-icon"> 254 <i class="fas fa-ruler-combined"></i> 255 </div> 256 <div class="detail-content"> 257 <h4> 258 <?php esc_html_e('Square Feet', 'havenlytics'); ?></h4> 259 <p> <?php echo esc_html($sqft); ?> 260 <?php esc_html_e('sqft', 'havenlytics'); ?> 261 </p> 262 </div> 263 </div> 264 <?php endif; ?> 265 <?php if ($year_built) : ?> 266 <div class="detail-item"> 267 <div class="detail-icon"> 268 <i class="fas fa-calendar-alt"></i> 269 </div> 270 <div class="detail-content"> 271 <h4><?php esc_html_e('Year Built', 'havenlytics'); ?></h4> 272 <p><?php echo esc_html($year_built); ?></p> 273 </div> 274 </div> 275 <?php endif; ?> 276 </div> 277 278 <div class="col-lg-4 col-md-6"> 279 <?php if ($bathrooms) : ?> 280 <div class="detail-item"> 281 <div class="detail-icon"> 282 <i class="fas fa-bath"></i> 283 </div> 284 <div class="detail-content"> 285 <h4><?php esc_html_e('Bathrooms', 'havenlytics'); ?></h4> 286 <p><?php echo esc_html($bathrooms); ?></p> 287 </div> 288 </div> 289 <?php endif; ?> 290 <?php if ($lot_size) : ?> 291 <div class="detail-item"> 292 <div class="detail-icon"> 293 <i class="fas fa-vector-square"></i> 294 </div> 295 <div class="detail-content"> 296 <h4><?php esc_html_e('Lot Size', 'havenlytics'); ?></h4> 297 <p><?php echo esc_html($lot_size); ?> acres</p> 298 </div> 299 </div> 300 <?php endif; ?> 301 <?php if ($garage) : ?> 302 <div class="detail-item"> 303 <div class="detail-icon"> 304 <i class="fas fa-car"></i> 305 </div> 306 <div class="detail-content"> 307 <h4><?php esc_html_e('Garage', 'havenlytics'); ?></h4> 308 <p><?php echo esc_html($garage); ?> 309 car<?php echo esc_html($garage > 1 ? 's' : ''); ?></p> 310 </div> 311 </div> 312 <?php endif; ?> 313 </div> 314 315 <div class="col-lg-4 col-md-6"> 316 <?php if ($tax_amount) : ?> 317 <div class="detail-item"> 318 <div class="detail-icon"> 319 <i class="fas fa-dollar-sign"></i> 320 </div> 321 <div class="detail-content"> 322 <h4><?php esc_html_e('Annual Tax', 'havenlytics'); ?></h4> 323 <p><?php echo esc_html($currency_symbol); ?> 324 <?php echo esc_html($tax_amount); ?></p> 325 </div> 326 </div> 327 <?php endif; ?> 328 <?php if ($rating) : ?> 329 <div class="detail-item"> 330 <div class="detail-icon"> 331 <i class="fas fa-star"></i> 332 </div> 333 <div class="detail-content"> 334 <h4><?php esc_html_e('Rating', 'havenlytics'); ?></h4> 335 <p><?php echo esc_html($rating); ?> / 5</p> 336 </div> 337 </div> 338 <?php endif; ?> 339 340 </div> 341 </div> 342 </div> 343 344 345 <?php if (!empty(array_filter($features))) : ?> 346 <!-- Features Section --> 347 <div class="property-details-card"> 348 <h2 class="section-title"><?php esc_html_e('Features & Amenities', 'havenlytics'); ?></h2> 349 <div class="features-grid"> 350 <?php foreach ($features as $feature => $value) : 351 if ($value) : ?> 352 353 <div class="feature-item"> 354 <i class="fas fa-check-circle"></i> 355 <span><?php echo esc_html(ucfirst(str_replace('_', ' ', $feature))); ?></span> 356 </div> 357 <?php endif; 358 endforeach; ?> 359 </div> 360 </div> 361 <?php endif; ?> 362 <!-- Floor Plans --> 363 364 365 366 <!-- Video --> 367 <div class="property-details-card"> 368 <div class="property-video-card"> 369 <h2 class="section-title"><?php esc_html_e('Property Video', 'havenlytics'); ?></h2> 370 371 <div class="video-container"> 372 <?php 373 $video_url = get_post_meta(get_the_ID(), '_havenlytics_youtube_url', true); 374 $thumbnail_url = get_post_meta(get_the_ID(), 'property_video_thumbnail', true); 375 376 if ($video_url) : 377 // Use default thumbnail if not provided 378 if (empty($thumbnail_url)) { 379 $thumbnail_url = HVNLY_PROPERTY_URL . 'public/assets/img/video-placeholder.png'; 380 } 381 ?> 382 <div class="video-wrapper"> 383 <div class="video-thumbnail"> 384 <img src="<?php echo esc_url($thumbnail_url); ?>" 385 alt="Property Video Thumbnail"> 386 <div class="havenlytics_property_video-btn-main"> 387 <div class="havenlytics_property_video-play-button" id="play-button" 388 data-video-url="<?php echo esc_url($video_url); ?>"> 389 <div class="havenlytics_property_video-icon"></div> 390 <div class="havenlytics_property_video-pulse-ring"></div> 391 </div> 392 </div> 393 </div> 394 </div> 395 396 <!-- Video Popup --> 397 <div class="havenlytics_property_video-overlay"> 398 <div class="havenlytics_property_video-spinner"></div> 399 <div class="havenlytics_property_video-container"> 400 <div class="havenlytics_property_video-close"> 401 <i class="fas fa-times"></i> 402 </div> 403 <div class="havenlytics_property_video-player" id="video-player"></div> 404 </div> 405 </div> 406 <?php else : ?> 407 <p><?php esc_html_e('No video available for this property.', 'havenlytics'); ?></p> 408 <?php endif; ?> 409 </div> 410 </div> 411 </div> 412 413 414 <?php if ($latitude && $longitude) : ?> 415 <!-- Location Map --> 416 <div class="property-details-card"> 417 <h2 class="section-title"><?php esc_html_e('Property Location', 'havenlytics'); ?></h2> 418 419 <div class="map-container"> 420 <div id="havenlytics-property-map" data-lat="<?php echo esc_attr($latitude); ?>" 421 data-lng="<?php echo esc_attr($longitude); ?>" 422 style="height: 400px; width: 100%; background: #f5f5f5;"> 423 </div> 424 <!-- <div class="map-overlay"> 425 426 <h3>Neighborhood Information</h3> 427 <p class="text-muted">Located in the prestigious South Beach neighborhood, this property 428 is just steps from the ocean and within walking distance to top restaurants, 429 shopping, and entertainment venues.</p> 430 <p><strong>Walk Score:</strong> 95/100</p> 431 <p><strong>Schools:</strong> Excellent rated schools nearby</p> 432 </div> --> 433 434 </div> 435 </div> 436 <?php endif; ?> 437 <!-- Popup Gallery --> 438 <div class="property-details-card"> 439 <h2 class="section-title">Property Gallery</h2> 440 <div class="row"> 441 <div class="col-md-12 mb-4"> 442 <div class="havenlytics_gallery_fancybox_gallery"> 443 <?php if (!empty($gallery_images)) : ?> 444 <?php foreach ($gallery_images as $index => $image_id) : 445 $thumb_url = wp_get_attachment_image_url($image_id, 'medium'); 446 $large_url = wp_get_attachment_image_url($image_id, 'large'); 447 $alt_text = get_post_meta($image_id, '_wp_attachment_image_alt', true); 448 $title_text = get_the_title($image_id); 449 $animation_delay = 0.2 + ($index * 0.1); // optional animated delay 450 ?> 451 <div class="havenlytics_gallery_fancybox_gallery-item havenlytics_gallery_fancybox_floating" 452 style="animation-delay: <?php echo esc_attr(number_format($animation_delay, 1)); ?>s;" 453 data-button-title="<?php the_title(); ?>" 454 data-button-link="<?php echo esc_url(get_permalink($property_id)); ?>"> 455 <img src="<?php echo esc_url($thumb_url); ?>" 456 data-large="<?php echo esc_url($large_url); ?>" 457 alt="<?php echo esc_attr($alt_text ? $alt_text : $title_text); ?>"> 458 <div class="havenlytics_gallery_fancybox_gallery-caption"> 459 <?php echo esc_html($title_text); ?> 460 </div> 461 </div> 462 <?php endforeach; ?> 463 <?php else : ?> 464 <p><?php esc_html_e('No gallery images found.', 'havenlytics'); ?></p> 465 <?php endif; ?> 466 </div> 467 468 <!-- Heavenlytics FancyBox Structure --> 469 <div class="havenlytics_gallery_fancybox_fancybox"> 470 <span class="havenlytics_gallery_fancybox_fancybox_close"><i 471 class="fas fa-times"></i></span> 472 <span 473 class="havenlytics_gallery_fancybox_fancybox_nav havenlytics_gallery_fancybox_fancybox_prev"><i 474 class="fas fa-chevron-left"></i></span> 475 <span 476 class="havenlytics_gallery_fancybox_fancybox_nav havenlytics_gallery_fancybox_fancybox_next"><i 477 class="fas fa-chevron-right"></i></span> 478 <div class="havenlytics_gallery_fancybox_fancybox_counter">1 / 8</div> 479 480 <div class="havenlytics_gallery_fancybox_fancybox_content"> 481 <div class="havenlytics_gallery_fancybox_fancybox_main"> 482 <div class="havenlytics_gallery_fancybox_fancybox_img_wrap"> 483 <img class="havenlytics_gallery_fancybox_fancybox_img" src="" alt=""> 484 </div> 485 <div class="havenlytics_gallery_fancybox_fancybox_caption"></div> 486 <div class="havenlytics_gallery_fancybox_fancybox_property"> 487 <a href="#" class="havenlytics_gallery_fancybox_property_button" 488 target="_blank"> 489 View Property Details <i class="fas fa-arrow-right"></i> 490 </a> 491 </div> 492 </div> 493 <div class="havenlytics_gallery_fancybox_fancybox_sidebar"> 494 <div class="havenlytics_gallery_fancybox_fancybox_thumbnails"></div> 495 </div> 496 </div> 497 </div> 498 <!-- Heavenlytics FancyBox Structure --> 499 </div> 500 501 502 </div> 503 </div> 504 505 506 </div> 507 508 <!-- Sidebar --> 509 <div class="col-lg-4"> 510 <div class="havenlytics_sticky_sidebar"> 511 512 513 <!-- Agent Contact --> 514 <div class="property-details-card"> 515 <div class="agent-card"> 516 <img src="https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=400&q=80" 517 class="agent-avatar" alt="Agent"> 518 <h3 class="agent-name">Robert Johnson</h3> 519 <p class="agent-title">Licensed Real Estate Agent</p> 520 521 <div class="contact-info"> 522 <div class="contact-item"> 523 <i class="fas fa-phone"></i> 524 <span>(305) 555-0123</span> 525 </div> 526 <div class="contact-item"> 527 <i class="fas fa-envelope"></i> 528 <span>[email protected]</span> 529 </div> 530 <div class="contact-item"> 531 <i class="fas fa-certificate"></i> 532 <span>License #: SL3456723</span> 533 </div> 534 </div> 535 536 <form class="contact-form"> 537 <input type="text" placeholder="Your Name" required> 538 <input type="email" placeholder="Your Email" required> 539 <input type="tel" placeholder="Your Phone"> 540 <textarea placeholder="Your Message"></textarea> 541 <button type="submit" 542 class="havenlytics-btn-primary havenlytics-btn-style-2">Schedule a 543 Viewing</button> 544 </form> 545 </div> 546 </div> 547 548 <!-- Amenities Section --> 549 <div class="property-details-card"> 550 <h2 class="section-title">Property Amenities</h2> 551 <div class="amenities-grid"> 552 <div class="amenity-card"> 553 <div class="amenity-icon"> 554 <i class="fas fa-swimming-pool"></i> 555 </div> 556 <h4>Infinity Pool</h4> 557 </div> 558 <div class="amenity-card"> 559 <div class="amenity-icon"> 560 <i class="fas fa-utensils"></i> 561 </div> 562 <h4>Gourmet Kitchen</h4> 563 </div> 564 <div class="amenity-card"> 565 <div class="amenity-icon"> 566 <i class="fas fa-wind"></i> 567 </div> 568 <h4>Central A/C</h4> 569 </div> 570 <div class="amenity-card"> 571 <div class="amenity-icon"> 572 <i class="fas fa-fireplace"></i> 573 </div> 574 <h4>Fireplace</h4> 575 </div> 576 <div class="amenity-card"> 577 <div class="amenity-icon"> 578 <i class="fas fa-wine-bottle"></i> 579 </div> 580 <h4>Wine Cellar</h4> 581 </div> 582 <div class="amenity-card"> 583 <div class="amenity-icon"> 584 <i class="fas fa-home"></i> 585 </div> 586 <h4>Smart Home</h4> 587 </div> 588 <div class="amenity-card"> 589 <div class="amenity-icon"> 590 <i class="fas fa-garage"></i> 591 </div> 592 <h4>Attached Garage</h4> 593 </div> 594 <div class="amenity-card"> 595 <div class="amenity-icon"> 596 <i class="fas fa-tree"></i> 597 </div> 598 <h4>Landscaped Garden</h4> 599 </div> 600 </div> 601 </div> 602 603 604 </div> 605 </div> 606 </div> 607 608 609 <!-- Similar Listings --> 610 <div class="havenlytics-similar-list"> 611 <div class="havenlytics-similar-section-heading"> 612 <h2><?php esc_html_e('Similar Property Listings', 'havenlytics'); ?></h2> 613 <div class="havenlytics-similar-line"> 614 <span class="havenlytics-similar-line1"></span> 615 <span class="havenlytics-similar-line2"></span> 616 </div> 617 <p><?php esc_html_e('Explore properties with similar features and locations', 'havenlytics'); ?></p> 618 </div> 619 <div class="row"> 620 <div class="col-md-12"> 621 <div class="havenlytics-similar-slider owl-carousel"> 622 <?php 623 $current_post_id = get_the_ID(); 624 625 // Get taxonomies for related properties 626 $taxonomies = array('hvnly_property_category', 'hvnly_property_tag'); 627 $tax_query = array('relation' => 'OR'); 628 $has_terms = false; 629 630 foreach ($taxonomies as $taxonomy) { 631 $terms = wp_get_post_terms($current_post_id, $taxonomy, array('fields' => 'ids')); 632 if (!empty($terms) && !is_wp_error($terms)) { 633 $tax_query[] = array( 634 'taxonomy' => $taxonomy, 635 'field' => 'term_id', 636 'terms' => $terms 637 ); 638 $has_terms = true; 639 } 640 } 641 642 // Query arguments 643 $args = array( 644 'post_type' => 'hvnly_property', 645 'posts_per_page' => 6, // Better than -1 for performance 646 'post__not_in' => array($current_post_id), 647 ); 648 649 if ($has_terms) { 650 $args['tax_query'] = $tax_query; 651 $args['orderby'] = 'rand'; 652 } else { 653 $args['orderby'] = 'date'; 654 $args['order'] = 'DESC'; 655 } 656 657 658 $related_properties = new WP_Query($args); 659 660 if ($related_properties->have_posts()) : 661 // Currency symbols array 662 $currency_symbols = array( 663 'USD' => '$', 664 'EUR' => '€', 665 'GBP' => '£', 666 'AUD' => 'A$', 667 'CAD' => 'C$', 668 'JPY' => '¥', 669 'CHF' => 'CHF', 670 'CNY' => '¥', 671 'INR' => '₹', 672 'NZD' => 'NZ$', 673 'SGD' => 'S$', 674 'HKD' => 'HK$', 675 'SEK' => 'kr', 676 'NOK' => 'kr', 677 'MXN' => 'Mex$', 678 'ZAR' => 'R', 679 'BRL' => 'R$', 680 'RUB' => '₽', 681 'KRW' => '₩', 682 'TRY' => '₺', 683 ); 684 685 while ($related_properties->have_posts()) : $related_properties->the_post(); 686 $property_id = get_the_ID(); 687 688 // Get all property meta 689 $currency_code = get_post_meta($property_id, '_havenlytics_currency', true); 690 $price = get_post_meta($property_id, '_havenlytics_price', true); 691 $bedrooms = get_post_meta($property_id, '_havenlytics_bedrooms', true); 692 $bathrooms = get_post_meta($property_id, '_havenlytics_bathrooms', true); 693 $sqft = get_post_meta($property_id, '_havenlytics_sqft', true); 694 $status = get_post_meta($property_id, '_havenlytics_status', true); 695 $address = get_post_meta($property_id, '_havenlytics_address', true); 696 697 // Get currency symbol 698 $currency_symbol = $currency_symbols[$currency_code] ?? '$'; 699 700 // Status badge text 701 $status_badge = ''; 702 switch ($status) { 703 case 'for_sale': 704 $status_badge = __('For Sale', 'havenlytics'); 705 break; 706 case 'for_rent': 707 $status_badge = __('For Rent', 'havenlytics'); 708 break; 709 case 'pending': 710 $status_badge = __('Pending', 'havenlytics'); 711 break; 712 case 'sold': 713 $status_badge = __('Sold', 'havenlytics'); 714 break; 715 case 'off_market': 716 $status_badge = __('Off Market', 'havenlytics'); 717 break; 718 } 719 ?> 720 <!-- Property Card --> 721 <div class="havenlytics-property-card-grid"> 722 <div class="havenlytics-property-grid-item"> 723 <div class="havenlytics-property-thumbnail-item"> 724 <div class="havenlytics-related-carousel-thumbnail"> 725 <?php if (has_post_thumbnail()) : ?> 726 <?php the_post_thumbnail('havenlytics-property-thumb', array( 727 'alt' => esc_attr(get_the_title()), 728 'class' => 'img-fluid' 729 )); ?> 730 <?php else : ?> 731 <img src="<?php echo esc_url(HVNLY_PROPERTY_URL . 'public/assets/img/video-placeholder.png'); ?>" 732 alt="<?php esc_attr_e('Property Image', 'havenlytics'); ?>" 733 class="img-fluid"> 734 <?php endif; ?> 735 </div> 736 737 <?php if ($price) : ?> 738 <div class="havenlytics-property-amount"> 739 <h5> 740 <span> 741 <?php echo esc_html($currency_symbol); ?> 742 <?php echo esc_html($price); ?> 743 </span> 744 </h5> 745 </div> 746 <?php endif; ?> 747 748 <?php if ($status_badge) : ?> 749 <div class="havenlytics-property-featured"> 750 <span><?php echo esc_html($status_badge); ?></span> 751 </div> 752 <?php endif; ?> 753 </div> 754 755 <div class="havenlytics-property--content"> 756 <h3 class="havenlytics-property-title"> 757 <a href="<?php the_permalink(); ?>"><?php the_title(); ?></a> 758 </h3> 759 760 <?php if ($address) : ?> 761 <p class="havenlytics-property-location"> 762 <svg class="custom-icon" width="20px" height="20px" viewBox="0 0 24 24" 763 fill="#6C60FE"> 764 <path 765 d="M12,11.5A2.5,2.5 0 0,1 9.5,9A2.5,2.5 0 0,1 12,6.5A2.5,2.5 0 0,1 14.5,9A2.5,2.5 0 0,1 12,11.5M12,2A7,7 0 0,0 5,9C5,14.25 12,22 12,22C12,22 19,14.25 19,9A7,7 0 0,0 12,2Z"> 766 </path> 767 </svg> 768 <?php echo esc_html($address); ?> 769 </p> 770 <?php endif; ?> 771 772 <ul class="d-flex havenlytics-property-short-details"> 773 <?php if ($bedrooms) : ?> 774 <li> 775 <div class="heavenlytics-property-meta-item"> 776 <!-- Bedrooms SVG --> 777 <svg fill="#6C60FE" width="20px" height="20px" 778 viewBox="0 -11.47 122.88 122.88" version="1.1" id="Layer_1" 779 xmlns="http://www.w3.org/2000/svg" 780 xmlns:xlink="http://www.w3.org/1999/xlink" 781 style="enable-background:new 0 0 122.88 99.94" 782 xml:space="preserve"> 783 <g id="SVGRepo_bgCarrier" stroke-width="0"></g> 784 <g id="SVGRepo_tracerCarrier" stroke-linecap="round" 785 stroke-linejoin="round"></g> 786 <g id="SVGRepo_iconCarrier"> 787 <g> 788 <path 789 d="M4.22,67.36h114.31v-4.67c0-1.13-0.22-2.18-0.61-3.12c-0.42-1-1.04-1.89-1.81-2.66c-0.47-0.47-1-0.9-1.57-1.28 c-0.58-0.39-1.2-0.73-1.85-1.02c-1.75-0.38-3.49-0.74-5.22-1.08c-1.74-0.34-3.49-0.66-5.25-0.96c-0.08-0.01-0.14-0.02-0.22-0.04 c-0.89-0.15-1.74-0.29-2.55-0.42c-0.81-0.13-1.67-0.26-2.57-0.4l-0.02,0c-6.12-0.78-12.22-1.38-18.31-1.78 c-6.1-0.4-12.17-0.6-18.2-0.61c-3.58,0-7.15,0.06-10.72,0.2c-3.55,0.14-7.12,0.34-10.69,0.62l-0.02,0 c-3.34,0.31-6.67,0.7-10.01,1.15c-3.33,0.45-6.67,0.98-10.03,1.57l-0.37,0.09c-0.07,0.02-0.14,0.03-0.2,0.03 c-0.06,0.01-0.12,0.01-0.18,0.01c-1.57,0.28-3.18,0.59-4.84,0.92c-1.61,0.32-3.22,0.66-4.82,1.01c-0.4,0.22-0.78,0.47-1.14,0.73 c-0.36,0.27-0.71,0.56-1.02,0.87v0c-0.67,0.67-1.2,1.44-1.56,2.3c-0.34,0.81-0.53,1.71-0.53,2.69V67.36L4.22,67.36z M14.2,0h92.99 c1.21,0,2.37,0.24,3.43,0.68c1.1,0.46,2.09,1.13,2.92,1.95c0.83,0.83,1.5,1.82,1.95,2.92c0.44,1.06,0.68,2.22,0.68,3.43v42.69 c0.51,0.3,1.01,0.63,1.47,0.99c0.52,0.4,1.01,0.82,1.46,1.27c1.16,1.16,2.1,2.51,2.73,4.03c0.6,1.43,0.93,3.02,0.93,4.74v6.09 c0.03,0.1,0.06,0.2,0.08,0.3l0,0.02c0.02,0.13,0.03,0.25,0.03,0.37c0,0.13-0.01,0.26-0.04,0.39l0,0c-0.02,0.1-0.05,0.2-0.08,0.3 v27.66c0,0.58-0.24,1.11-0.62,1.49c-0.38,0.38-0.91,0.62-1.49,0.62h-4.35c-0.49,0-0.94-0.17-1.3-0.45 c-0.36-0.28-0.63-0.68-0.74-1.14c-0.8-2.3-1.61-4.12-2.48-5.54c-0.86-1.4-1.78-2.4-2.84-3.11c-1.07-0.71-2.35-1.16-3.9-1.43 c-1.58-0.28-3.42-0.37-5.61-0.36l-79.76,0.1l-0.04,0c-1.57-0.03-2.86,0.17-3.94,0.59c-1.07,0.42-1.94,1.05-2.66,1.86 c-0.81,0.9-1.49,2.05-2.11,3.39c-0.63,1.37-1.2,2.93-1.77,4.64l0,0c-0.14,0.44-0.42,0.79-0.77,1.04c-0.33,0.24-0.73,0.38-1.14,0.4 c-0.03,0.01-0.06,0.01-0.09,0.01H2.11c-0.58,0-1.11-0.24-1.49-0.62C0.24,98.94,0,98.41,0,97.83V61.52c0-1.57,0.3-3.01,0.84-4.31 c0.58-1.38,1.43-2.61,2.49-3.67c0.3-0.3,0.63-0.6,0.98-0.88c0.3-0.24,0.6-0.47,0.92-0.68V8.89c0-1.21,0.24-2.36,0.68-3.4 c0.46-1.09,1.13-2.07,1.96-2.89c0.83-0.82,1.82-1.47,2.91-1.92C11.84,0.24,12.99,0,14.2,0L14.2,0z M107.19,4.22H14.2 c-0.65,0-1.27,0.13-1.84,0.36c-0.59,0.24-1.11,0.59-1.55,1.02c-0.43,0.42-0.78,0.94-1.02,1.5C9.57,7.65,9.45,8.25,9.45,8.89v41.06 c0.3-0.1,0.6-0.18,0.91-0.26c0.49-0.13,0.98-0.24,1.47-0.32c0.68-0.12,1.42-0.25,2.22-0.39c0.6-0.1,1.24-0.21,1.9-0.31V38.19 c0-1.58,0.32-3.09,0.89-4.47c0.6-1.44,1.47-2.73,2.55-3.81c1.08-1.08,2.37-1.95,3.81-2.55c1.38-0.57,2.89-0.89,4.47-0.89h19.82 c1.58,0,3.09,0.32,4.47,0.89c1.44,0.6,2.73,1.47,3.81,2.55c1.08,1.08,1.95,2.37,2.55,3.81c0.57,1.38,0.89,2.89,0.89,4.47v6.69 c0.7-0.01,1.4-0.01,2.11-0.01v-6.68c0-1.58,0.32-3.09,0.89-4.47c0.6-1.44,1.47-2.73,2.55-3.81c1.08-1.08,2.37-1.95,3.81-2.55 c1.38-0.57,2.89-0.89,4.47-0.89h19.82c1.58,0,3.09,0.32,4.47,0.89c1.44,0.6,2.73,1.47,3.81,2.55c1.08,1.08,1.95,2.37,2.55,3.81 c0.57,1.38,0.89,2.89,0.89,4.47v10.34c0.75,0.11,1.55,0.24,2.41,0.38c0.95,0.15,1.86,0.3,2.74,0.45c0.45,0.08,0.91,0.17,1.37,0.28 c0.29,0.07,0.57,0.14,0.84,0.22V8.98c0-0.64-0.13-1.25-0.36-1.81c-0.24-0.58-0.6-1.1-1.04-1.55c-0.44-0.44-0.97-0.8-1.54-1.04 C108.44,4.35,107.83,4.22,107.19,4.22L107.19,4.22z M43.21,45.56c2.01-0.15,4.03-0.28,6.08-0.38c1.89-0.1,3.8-0.17,5.71-0.22v-6.77 c0-1.01-0.2-1.98-0.57-2.86c-0.38-0.92-0.94-1.74-1.64-2.44c-0.69-0.69-1.52-1.25-2.44-1.64c-0.88-0.37-1.85-0.57-2.86-0.57H27.67 c-1.01,0-1.98,0.2-2.86,0.57c-0.92,0.38-1.74,0.94-2.44,1.64c-0.69,0.69-1.25,1.52-1.64,2.44c-0.37,0.88-0.57,1.85-0.57,2.86V48 c1.62-0.24,3.26-0.46,4.94-0.68c1.81-0.23,3.61-0.44,5.39-0.64c0.69-0.08,1.43-0.17,2.2-0.25c0.72-0.08,1.47-0.15,2.27-0.23 c1.36-0.13,2.71-0.25,4.04-0.36C40.37,45.75,41.77,45.65,43.21,45.56L43.21,45.56z M65.54,44.9c1.21,0.02,2.42,0.05,3.63,0.09 c1.34,0.04,2.68,0.1,4.01,0.16l0.01,0c2.19,0.08,4.33,0.18,6.41,0.3c2.08,0.12,4.11,0.27,6.05,0.44c2.82,0.25,5.55,0.55,8.14,0.9 c2.32,0.32,4.52,0.68,6.58,1.08v-9.68c0-1.01-0.2-1.98-0.57-2.86c-0.38-0.92-0.94-1.74-1.64-2.44c-0.69-0.69-1.52-1.25-2.44-1.64 c-0.88-0.37-1.85-0.57-2.86-0.57H73.05c-1.01,0-1.98,0.2-2.86,0.57c-0.92,0.38-1.74,0.94-2.44,1.64c-0.69,0.69-1.25,1.52-1.64,2.44 c-0.37,0.88-0.57,1.85-0.57,2.86V44.9L65.54,44.9z M118.54,71.59H4.22v24.13h1.43c0.56-1.58,1.14-3.05,1.79-4.36 c0.7-1.4,1.49-2.64,2.45-3.71c1.14-1.28,2.48-2.27,4.09-2.93c1.61-0.65,3.49-0.98,5.75-0.93l79.69-0.1c2.57,0,4.77,0.12,6.69,0.49 c1.95,0.37,3.63,1,5.14,2c1.4,0.93,2.6,2.16,3.68,3.77c1.03,1.54,1.95,3.43,2.83,5.76h0.76V71.59L118.54,71.59z"> 790 </path> 791 </g> 792 </g> 793 </svg> 794 <?php echo esc_html($bedrooms); ?> 795 <?php esc_html_e('Beds', 'havenlytics'); ?> 796 </div> 797 </li> 798 <?php endif; ?> 799 800 <?php if ($bathrooms) : ?> 801 <li> 802 <div class="heavenlytics-property-meta-item"> 803 <!-- Bathrooms SVG --> 804 <svg fill="#6C60FE" width="20px" height="20px" viewBox="0 0 512 512" 805 id="Layer_1" enable-background="new 0 0 512 512" 806 xmlns="http://www.w3.org/2000/svg"> 807 <g id="SVGRepo_bgCarrier" stroke-width="0"></g> 808 <g id="SVGRepo_tracerCarrier" stroke-linecap="round" 809 stroke-linejoin="round"></g> 810 <g id="SVGRepo_iconCarrier"> 811 <g> 812 <path 813 d="m496 288c-38.154 0-437.487 0-448 0v-56h32c8.837 0 16-7.164 16-16v-40c0-8.836-7.163-16-16-16s-16 7.164-16 16v24h-16v-138.745c0-25.903 31.562-39.064 49.941-20.686l16.94 16.94c-13.424 23.401-10.164 53.835 9.805 73.805l8 8c6.247 6.248 16.379 6.249 22.627 0l64-64c6.249-6.248 6.249-16.379 0-22.627l-8-8c-20.35-20.351-50.837-23.06-73.817-9.817l-16.928-16.928c-11.57-11.57-26.952-17.942-43.313-17.942-33.776 0-61.255 27.479-61.255 61.255v226.745c-8.837 0-16 7.164-16 16s7.163 16 16 16v32c0 43.889 19.742 83.247 50.806 109.681l-22.338 23.229c-9.803 10.193-2.445 27.09 11.53 27.09 4.199 0 8.394-1.644 11.534-4.91l26.218-27.263c19.844 10.326 42.376 16.173 66.25 16.173h192c23.874 0 46.406-5.847 66.25-16.173l26.218 27.263c6.106 6.35 16.234 6.585 22.623.442 6.369-6.125 6.566-16.254.441-22.623l-22.338-23.229c31.064-26.433 50.806-65.791 50.806-109.68v-32c8.837 0 16-7.164 16-16s-7.163-16-16-16zm-310.89-223.738-40.845 40.845c-8.246-11.427-7.23-27.515 3.048-37.794 10.378-10.377 26.461-11.259 37.797-3.051zm278.89 287.738c0 61.757-50.243 112-112 112h-192c-61.757 0-112-50.243-112-112v-32h416z"> 814 </path> 815 </g> 816 </g> 817 </svg> 818 <?php echo esc_html($bathrooms); ?> 819 <?php esc_html_e('Baths', 'havenlytics'); ?> 820 </div> 821 </li> 822 <?php endif; ?> 823 824 <?php if ($sqft) : ?> 825 <li> 826 <div class="heavenlytics-property-meta-item"> 827 <!-- Area SVG --> 828 <svg fill="#6C60FE" width="20px" height="20px" viewBox="0 0 256 256" 829 id="Flat" xmlns="http://www.w3.org/2000/svg"> 830 <g id="SVGRepo_bgCarrier" stroke-width="0"></g> 831 <g id="SVGRepo_tracerCarrier" stroke-linecap="round" 832 stroke-linejoin="round" stroke="#CCCCCC" 833 stroke-width="7.68"></g> 834 <g id="SVGRepo_iconCarrier"> 835 <path 836 d="M240,211.98316H227.99414v-108a12.01343,12.01343,0,0,0-12-12h-68v-52a12.01343,12.01343,0,0,0-12-12h-96a12.01343,12.01343,0,0,0-12,12v172H16a4,4,0,0,0,0,8H240a4,4,0,0,0,0-8Zm-24.00586-112a4.00426,4.00426,0,0,1,4,4v108h-72v-112Zm-180-60a4.00427,4.00427,0,0,1,4-4h96a4.00426,4.00426,0,0,1,4,4v172h-104Zm24,32a4.0002,4.0002,0,0,1,4-4h32a4,4,0,1,1,0,8h-32A4.0002,4.0002,0,0,1,59.99414,71.98316Zm56,64a4.0002,4.0002,0,0,1-4,4h-32a4,4,0,0,1,0-8h32A4.0002,4.0002,0,0,1,115.99414,135.98316Zm-16,40a4.0002,4.0002,0,0,1-4,4h-32a4,4,0,0,1,0-8h32A4.0002,4.0002,0,0,1,99.99414,175.98316Zm96,0a4.0002,4.0002,0,0,1-4,4h-16a4,4,0,0,1,0-8h16A4.0002,4.0002,0,0,1,195.99414,175.98316Zm-24-40a4.0002,4.0002,0,0,1,4-4h16a4,4,0,1,1,0,8h-16A4.0002,4.0002,0,0,1,171.99414,135.98316Z"> 837 </path> 838 </g> 839 </svg> 840 <?php echo esc_html(number_format($sqft)); ?> 841 <?php esc_html_e('sqft', 'havenlytics'); ?> 842 </div> 843 </li> 844 <?php endif; ?> 845 </ul> 846 847 <ul 848 class="havenlytics-property-category d-flex justify-content-between align-items-center"> 849 <li> 850 <a href="<?php the_permalink(); ?>" 851 class="havenlytics-btn-primary havenlytics-btn-style-1"> 852 <?php esc_html_e('View Property', 'havenlytics'); ?> 853 </a> 854 </li> 855 </ul> 856 </div> 857 </div> 858 </div> 859 <!-- /Property Card --> 860 <?php 861 endwhile; 862 wp_reset_postdata(); 863 else : ?> 864 <p><?php esc_html_e('No similar properties found.', 'havenlytics'); ?></p> 865 <?php endif; ?> 866 </div> 867 </div> 868 </div> 869 </div> 870 <!-- /Similar Listings --> 871 872 200 873 </div> 201 202 <?php if (!empty(array_filter($features))) : ?>203 <div class="havenlytics-single-features">204 <h2><?php esc_html_e('Features', 'havenlytics'); ?></h2>205 <ul class="havenlytics-features-list">206 <?php foreach ($features as $feature => $value) :207 if ($value) : ?>208 <li><?php echo esc_html(ucfirst(str_replace('_', ' ', $feature))); ?></li>209 <?php endif;210 endforeach; ?>211 </ul>212 </div>213 <?php endif; ?>214 215 216 <!-- YouTube Video Section -->217 <?php if ($youtube_url) :218 $video_id = '';219 $pattern = '/(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|watch\?v=)|youtu\.be\/)([^"&?\/\s]{11})/i';220 preg_match($pattern, $youtube_url, $matches);221 $video_id = $matches[1] ?? '';222 ?>223 <div class="havenlytics-property-video">224 <h2><?php esc_html_e('Video Tour', 'havenlytics'); ?></h2>225 226 <?php if ($youtube_title) : ?>227 <h3><?php echo esc_html($youtube_title); ?></h3>228 <?php endif; ?>229 230 <?php if ($video_id) : ?>231 <div class="havenlytics-video-container">232 <iframe233 src="https://www.youtube.com/embed/<?php echo esc_attr($video_id); ?>?rel=0&modestbranding=1"234 frameborder="0"235 allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"236 allowfullscreen>237 </iframe>238 </div>239 <?php else : ?>240 <p class="havenlytics-video-error"><?php esc_html_e('Invalid YouTube URL provided', 'havenlytics'); ?>241 </p>242 <?php endif; ?>243 </div>244 <?php endif; ?>245 246 247 874 </div> 248 249 <div class="havenlytics-single-sidebar">250 <div class="havenlytics-contact-form">251 <h3><?php esc_html_e('Contact Agent', 'havenlytics'); ?></h3>252 <?php echo do_shortcode('[your_contact_form_shortcode]'); ?>253 </div>254 255 <div class="havenlytics-additional-info">256 <?php if ($lot_size) : ?>257 <div class="havenlytics-info-item">258 <span class="havenlytics-info-label"><?php esc_html_e('Lot Size', 'havenlytics'); ?></span>259 <span class="havenlytics-info-value"><?php echo esc_html($lot_size); ?>260 <?php echo esc_html($lot_unit); ?></span>261 </div>262 <?php endif; ?>263 264 <?php if ($hoa_fee) : ?>265 <div class="havenlytics-info-item">266 <span class="havenlytics-info-label"><?php esc_html_e('HOA Fee', 'havenlytics'); ?></span>267 <span class="havenlytics-info-value"><?php echo esc_html($hoa_fee); ?>268 <?php echo esc_html($hoa_period); ?></span>269 </div>270 <?php endif; ?>271 272 <?php if ($tax_amount) : ?>273 <div class="havenlytics-info-item">274 <span class="havenlytics-info-label"><?php esc_html_e('Tax Amount', 'havenlytics'); ?></span>275 <span class="havenlytics-info-value"><?php echo esc_html($tax_amount); ?>276 (<?php echo esc_html($tax_year); ?>)</span>277 </div>278 <?php endif; ?>279 280 <?php if ($neighborhood) : ?>281 <div class="havenlytics-info-item">282 <span class="havenlytics-info-label"><?php esc_html_e('Neighborhood', 'havenlytics'); ?></span>283 <span class="havenlytics-info-value"><?php echo esc_html($neighborhood); ?></span>284 </div>285 <?php endif; ?>286 </div>287 288 <?php if ($virtual_tour) : ?>289 <div class="havenlytics-virtual-tour">290 <h3><?php esc_html_e('Virtual Tour', 'havenlytics'); ?></h3>291 <a href="<?php echo esc_url($virtual_tour); ?>" class="havenlytics-tour-button" target="_blank">292 <?php esc_html_e('View Virtual Tour', 'havenlytics'); ?>293 </a>294 </div>295 <?php endif; ?>296 </div>297 </div>298 299 <?php if ($latitude && $longitude) : ?>300 <div class="havenlytics-single-map">301 <h2><?php esc_html_e('Location', 'havenlytics'); ?></h2>302 <div id="havenlytics-property-map" data-lat="<?php echo esc_attr($latitude); ?>"303 data-lng="<?php echo esc_attr($longitude); ?>" style="height: 400px; width: 100%; background: #f5f5f5;">304 </div>305 </div>306 <?php endif; ?>307 875 308 876 <?php endwhile; ?> -
havenlytics/trunk/templates/property-card-grid.php
r3315596 r3322295 6 6 $property_id = get_the_ID(); 7 7 $meta_fields = $this->get_property_meta_fields($property_id); 8 8 9 ?> 9 10 10 <div class="heavenlytics-col">11 <div class="heavenlytics-property-card">12 <div class="heavenlytics-property-card-image">13 <?php if ($meta_fields['featured_image']) : ?>14 <img src="<?php echo esc_url($meta_fields['featured_image']); ?>" alt="<?php the_title_attribute(); ?>">15 <?php else : ?>16 <img src="<?php echo esc_url($meta_fields['default_image']); ?>" alt="<?php the_title_attribute(); ?>">17 <?php endif; ?>18 11 19 <?php if ($meta_fields['status_badge']) : ?> 20 <span class="heavenlytics-property-badge"><?php echo esc_html($meta_fields['status_badge']); ?></span> 21 <?php endif; ?> 22 </div> 12 <!-- Property grid --> 13 <div class="col-lg-4 col-md-6"> 14 <div class="havenlytics-property-card-grid"> 15 <div class="havenlytics-property-grid-item"> 16 <div class="havenlytics-property-gallery"> 23 17 24 <div class="heavenlytics-property-card-content">25 <?php if ($meta_fields['price']) : ?>26 <div class="heavenlytics-property-price"><span>$ </span><?php echo esc_html($meta_fields['price']); ?></div>27 <?php endif; ?>18 <?php 19 // Prepare gallery data 20 $gallery = get_post_meta($property_id, '_havenlytics_gallery', true); 21 $gallery_images = ! empty($gallery) ? explode(',', $gallery) : array(); 28 22 29 <h3 class="heavenlytics-property-title"><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h3> 23 // Show only the featured or default image if 4 or fewer gallery images exist. 24 if (count($gallery_images) <= 3) : 30 25 31 <?php if ($meta_fields['type_label']) : ?> 32 <div class="heavenlytics-property-type"><?php echo esc_html($meta_fields['type_label']); ?></div> 33 <?php endif; ?> 26 $featured_image = isset($meta_fields['featured_image']) ? esc_url($meta_fields['featured_image']) : ''; 27 $default_image = isset($meta_fields['default_image']) ? esc_url($meta_fields['default_image']) : ''; 34 28 35 <div class="heavenlytics-property-meta">36 <?php $this->render_meta_item($meta_fields['bedrooms'], __('Beds', 'havenlytics'), 'beds'); ?>37 <?php $this->render_meta_item($meta_fields['bathrooms'], __('Baths', 'havenlytics'), 'baths'); ?>38 <?php $this->render_meta_item($meta_fields['sqft'], __('sqft', 'havenlytics'), 'sqft'); ?>39 </div>29 ?> 30 <a href="<?php echo esc_url(get_the_permalink()); ?>" class="havenlytics-property-img"> 31 <img src="<?php echo esc_url($featured_image ? $featured_image : $default_image); ?>" 32 class="img-fluid" alt="<?php echo esc_attr(get_the_title()); ?>"> 33 </a> 40 34 41 <div class="heavenlytics-property-excerpt">42 <?php echo esc_html(wp_trim_words(get_the_excerpt(), 20)); ?>43 </div>44 35 45 <div class="heavenlytics-property-footer"> 46 <?php if ($meta_fields['city']) : ?> 47 <div class="heavenlytics-property-location"> 48 <svg viewBox="0 0 24 24"> 49 <path 50 d="M12,11.5A2.5,2.5 0 0,1 9.5,9A2.5,2.5 0 0,1 12,6.5A2.5,2.5 0 0,1 14.5,9A2.5,2.5 0 0,1 12,11.5M12,2A7,7 0 0,0 5,9C5,14.25 12,22 12,22C12,22 19,14.25 19,9A7,7 0 0,0 12,2Z" /> 51 </svg> 52 <?php echo esc_html($meta_fields['city']); ?> 53 </div> 36 <?php else : ?> 37 38 <div class="havenlytics-carousel-gallery" 39 id="havenlytics-carousel-gallery-<?php echo esc_attr($property_id); ?>"> 40 <div class="havenlytics-carousel-track"> 41 <?php 42 foreach ($gallery_images as $image_id) : 43 $image_url = wp_get_attachment_image_url($image_id, 'large'); 44 ?> 45 <div class="havenlytics-carousel-slide" 46 style="background-image: url('<?php echo esc_url($image_url); ?>');"> 47 </div> 48 <?php endforeach; ?> 49 </div> 50 51 <div class="havenlytics-carousel-nav"> 52 <button class="havenlytics-carousel-nav-btn havenlytics-carousel-prev-btn"> 53 <i class="fas fa-chevron-left"></i> 54 </button> 55 <button class="havenlytics-carousel-nav-btn havenlytics-carousel-next-btn"> 56 <i class="fas fa-chevron-right"></i> 57 </button> 58 </div> 59 60 <div class="havenlytics-carousel-indicators"> 61 <?php foreach ($gallery_images as $index => $image_id) : ?> 62 <div 63 class="havenlytics-carousel-indicator <?php echo (0 === $index) ? 'havenlytics-carousel-active' : ''; ?>"> 64 </div> 65 <?php endforeach; ?> 66 </div> 67 </div> 68 54 69 <?php endif; ?> 55 70 56 <a href="<?php the_permalink(); ?>" class="heavenlytics-property-link"> 57 <?php esc_html_e('View Details', 'havenlytics'); ?> 58 </a> 71 72 73 <?php if ($meta_fields['price']) : ?> 74 <?php 75 // Get the currency symbol 76 $currency_sign = $this->get_currency_symbol($property_id); 77 ?> 78 <div class="havenlytics-property-amount"> 79 <h5><span><?php echo esc_html($currency_sign); ?> <?php echo esc_html($meta_fields['price']); ?> 80 </span></h5> 81 </div> 82 <?php endif; ?> 83 84 85 <?php if ($meta_fields['status_badge']) : ?> 86 <div class="havenlytics-property-featured"> 87 <span><?php echo esc_html($meta_fields['status_badge']); ?></span> 88 </div> 89 <?php endif; ?> 90 <!-- <div class="new-featured"> 91 <span>New</span> 92 </div> --> 93 <!-- <a href="javascript:void(0)"> 94 <div class="havenlytics-property-favourite havenlytics-property-selected"> 95 <i class="fa-regular fa-heart"></i> 96 </div> 97 </a> --> 98 </div> 99 <div class="havenlytics-property--content"> 100 <?php $this->render_star_feedback($property_id, '_havenlytics_rating_star'); ?> 101 102 <h3 class="havenlytics-property-title"> 103 <a href="<?php echo esc_url(get_the_permalink()); ?>"> 104 <?php echo esc_html(wp_trim_words(get_the_title(), 4, '...')); ?> 105 </a> 106 </h3> 107 108 109 110 <?php if ($meta_fields['city']) : ?> 111 <p class="havenlytics-property-location"> 112 <svg class="custom-icon" width="20px" height="20px" viewBox="0 0 24 24" fill="#6C60FE"> 113 <path 114 d="M12,11.5A2.5,2.5 0 0,1 9.5,9A2.5,2.5 0 0,1 12,6.5A2.5,2.5 0 0,1 14.5,9A2.5,2.5 0 0,1 12,11.5M12,2A7,7 0 0,0 5,9C5,14.25 12,22 12,22C12,22 19,14.25 19,9A7,7 0 0,0 12,2Z" /> 115 </svg> 116 <?php echo esc_html($meta_fields['city']); ?> 117 </p> 118 <?php endif; ?> 119 <?php if (! empty($meta_fields['bedrooms']) || ! empty($meta_fields['bathrooms']) || ! empty($meta_fields['sqft'])) : ?> 120 <ul class="d-flex havenlytics-property-short-details"> 121 <li> 122 123 <?php $this->render_meta_item($meta_fields['bedrooms'], __('Beds', 'havenlytics'), 'beds'); ?> 124 </li> 125 <li> 126 127 <?php $this->render_meta_item($meta_fields['bathrooms'], __('Baths', 'havenlytics'), 'baths'); ?> 128 </li> 129 <li> 130 131 <?php $this->render_meta_item($meta_fields['sqft'], __('sqft', 'havenlytics'), 'sqft'); ?> 132 </li> 133 </ul> 134 <?php endif; ?> 135 136 <ul class="havenlytics-property-category d-flex justify-content-between align-items-center"> 137 <!-- <li class="havenlytics-property-user-info"> 138 <?php 139 // $author_id = get_post_field('post_author', get_the_ID()); 140 // $avatar_url = get_avatar_url($author_id, ['size' => 96]); 141 // $author_name = get_the_author_meta('display_name', $author_id); 142 // $author_url = get_author_posts_url($author_id); 143 // $user_location = get_user_meta($author_id, 'location', true); // Optional custom meta 144 // $user_location = $user_location ? $user_location : esc_html__('Agent', 'havenlytics'); 145 ?> 146 147 <a href="<?php // echo esc_url($author_url); 148 ?>"> 149 <img src="<?php // echo esc_url($avatar_url); 150 ?>" class="img-fluid avatar" 151 alt="<?php // echo esc_attr($author_name); 152 ?>"> 153 </a> 154 <div class="havenlytics-property-user-name"> 155 <a href="<?php //echo esc_url($author_url); 156 ?>"><?php //echo esc_html($author_name); 157 ?></a> 158 <p><?php //echo esc_html($user_location); 159 ?></p> 160 </div> 161 162 </li> --> 163 <li> 164 <a href="<?php the_permalink(); ?>" class="havenlytics-btn-primary havenlytics-btn-style-2"> 165 <?php esc_html_e('View Property', 'havenlytics'); ?> 166 </a> 167 </li> 168 </ul> 59 169 </div> 60 170 </div> 61 171 </div> 62 172 </div> 173 <!-- /Property grid --> -
havenlytics/trunk/templates/property-card-list.php
r3315596 r3322295 6 6 $property_id = get_the_ID(); 7 7 $meta_fields = $this->get_property_meta_fields($property_id); 8 $short_description = get_post_meta($property_id, '_havenlytics_short_description', true); 9 $short_info = wp_trim_words($short_description, 18, '...'); 10 11 12 8 13 ?> 9 14 10 <div class="heavenlytics-list-item">11 <div class="heavenlytics-property-card">12 <div class="heavenlytics-property-card-image">13 <?php if ($meta_fields['featured_image']) : ?>14 <img src="<?php echo esc_url($meta_fields['featured_image']); ?>" alt="<?php the_title_attribute(); ?>">15 <?php else : ?>16 <img src="<?php echo esc_url($meta_fields['default_image']); ?>" alt="<?php the_title_attribute(); ?>">17 <?php endif; ?>18 15 19 <?php if ($meta_fields['status_badge']) : ?> 20 <span class="heavenlytics-property-badge"><?php echo esc_html($meta_fields['status_badge']); ?></span> 21 <?php endif; ?> 22 </div> 16 <!-- Property card list--> 17 <div class="col-lg-12 col-md-6"> 23 18 24 <div class="heavenlytics-property-card-content"> 25 <div class="heavenlytics-property-header"> 26 <h3 class="heavenlytics-property-title"><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a> 27 </h3> 19 <div class="havenlytics-property-card"> 20 <?php 21 $gallery = get_post_meta($property_id, '_havenlytics_gallery', true); 22 $gallery_images = !empty($gallery) ? explode(',', $gallery) : array(); 23 ?> 24 25 <?php if (!empty($gallery_images)) : ?> 26 <div class="havenlytics-property-list-gallery"> 27 <div class="havenlytics-gallery-container"> 28 <div class="havenlytics-gallery-main" 29 style="background-image: url('<?php echo esc_url(wp_get_attachment_image_url($gallery_images[0], 'large')); ?>')"> 30 </div> 31 </div> 32 <div class="havenlytics-thumb-nav havenlytics-thumb-prev"> 33 <i class="fas fa-chevron-left"></i> 34 </div> 35 <div class="havenlytics-thumb-nav havenlytics-thumb-next"> 36 <i class="fas fa-chevron-right"></i> 37 </div> 38 <div class="havenlytics-gallery-thumbnails"> 39 <?php foreach ($gallery_images as $index => $image_id) : 40 $thumb_url = wp_get_attachment_image_url($image_id, 'large'); 41 $is_active = ($index === 0) ? 'active' : ''; 42 ?> 43 <div class="havenlytics-gallery-thumb <?php echo esc_attr($is_active); ?>" 44 data-highres="<?php echo esc_url($thumb_url); ?>" 45 style="background-image: url('<?php echo esc_url($thumb_url); ?>')"> 46 </div> 47 <?php endforeach; ?> 48 </div> 49 <?php if ($meta_fields['status_badge']) : ?> 50 <div class="havenlytics-property-featured"> 51 <span><?php echo esc_html($meta_fields['status_badge']); ?></span> 52 </div> 53 <?php endif; ?> 54 55 <!-- <button class="havenlytics-favorite-btn active"> 56 <i class="fas fa-heart"></i> 57 </button> --> 58 </div> 59 <?php endif; ?> 60 61 <div class="havenlytics-property-content"> 62 <div class="havenlytics-property-header"> 63 <div class="havenlytics-property-title-container"> 64 <h3 class="havenlytics-property-title"> 65 <a href="<?php echo esc_url(get_the_permalink()); ?>"> 66 <?php echo esc_html(wp_trim_words(get_the_title(), 5, '...')); ?> 67 </a> 68 </h3> 69 <?php if ($meta_fields['city']) : ?> 70 <p class="havenlytics-property-location"> 71 <svg class="custom-icon" width="20px" height="20px" viewBox="0 0 24 24" fill="#6C60FE"> 72 <path 73 d="M12,11.5A2.5,2.5 0 0,1 9.5,9A2.5,2.5 0 0,1 12,6.5A2.5,2.5 0 0,1 14.5,9A2.5,2.5 0 0,1 12,11.5M12,2A7,7 0 0,0 5,9C5,14.25 12,22 12,22C12,22 19,14.25 19,9A7,7 0 0,0 12,2Z" /> 74 </svg> 75 <?php echo esc_html($meta_fields['city']); ?> 76 </p> 77 <?php endif; ?> 78 79 </div> 80 81 82 28 83 <?php if ($meta_fields['price']) : ?> 29 <div class="heavenlytics-property-price"><span>$ </span><?php echo esc_html($meta_fields['price']); ?> 30 </div> 84 <?php 85 // Get the currency symbol 86 $currency_sign = $this->get_currency_symbol($property_id); 87 ?> 88 <div class="havenlytics-property-price"><?php echo esc_html($currency_sign); ?> 89 <?php echo esc_html($meta_fields['price']); ?></div> 90 31 91 <?php endif; ?> 92 32 93 </div> 33 94 34 <?php if ($meta_fields['type_label']) : ?> 35 <div class="heavenlytics-property-type"><?php echo esc_html($meta_fields['type_label']); ?></div> 95 <?php $this->render_star_feedback($property_id, '_havenlytics_rating_star'); ?> 96 97 <?php if ($short_info) : ?> 98 <p class="havenlytics-property-description"> 99 <?php echo esc_html($short_info); ?> 100 101 </p> 36 102 <?php endif; ?> 103 <?php 104 $meta_fields = [ 105 'bedrooms' => get_post_meta($property_id, '_havenlytics_bedrooms', true), 106 'bathrooms' => get_post_meta($property_id, '_havenlytics_bathrooms', true), 107 'sqft' => get_post_meta($property_id, '_havenlytics_sqft', true), 108 'garage' => get_post_meta($property_id, '_havenlytics_garage', true), 109 'year_built' => get_post_meta($property_id, '_havenlytics_year_built', true), 110 ]; 37 111 38 <div class="heavenlytics-property-meta"> 39 <?php $this->render_meta_item($meta_fields['bedrooms'], __('Beds', 'havenlytics'), 'beds'); ?> 40 <?php $this->render_meta_item($meta_fields['bathrooms'], __('Baths', 'havenlytics'), 'baths'); ?> 41 <?php $this->render_meta_item($meta_fields['sqft'], __('sqft', 'havenlytics'), 'sqft'); ?> 42 </div> 112 // Only show section if any meta is not empty 113 if ( 114 !empty($meta_fields['bedrooms']) || 115 !empty($meta_fields['bathrooms']) || 116 !empty($meta_fields['sqft']) || 117 !empty($meta_fields['garage']) || 118 !empty($meta_fields['year_built']) 119 ) : 120 ?> 121 <div class="havenlytics-property-details"> 122 <?php $this->render_meta_list_item($meta_fields['bedrooms'], __('Beds', 'havenlytics'), 'fas fa-bed'); ?> 123 <?php $this->render_meta_list_item($meta_fields['bathrooms'], __('Baths', 'havenlytics'), 'fas fa-bath'); ?> 124 <?php $this->render_meta_list_item($meta_fields['sqft'], __('Sqft', 'havenlytics'), 'fas fa-ruler-combined'); ?> 125 <?php $this->render_meta_list_item($meta_fields['garage'], __('Garage', 'havenlytics'), 'fas fa-car'); ?> 126 <?php $this->render_meta_list_item($meta_fields['year_built'], __('Built', 'havenlytics'), 'fas fa-calendar-alt'); ?> 127 </div> 128 <?php endif; ?> 129 <div class="havenlytics-property-footer"> 130 <?php 131 // // Get the post author ID 132 // $author_id = get_post_field('post_author', get_the_ID()); 43 133 44 <div class="heavenlytics-property-excerpt"> 45 <?php echo esc_html(wp_trim_words(get_the_excerpt(), 30)); ?> 46 </div> 134 // // Get avatar URL safely 135 // $avatar_url = get_avatar_url($author_id, ['size' => 200]); 47 136 48 <div class="heavenlytics-property-footer"> 49 <?php if ($meta_fields['city']) : ?> 50 <div class="heavenlytics-property-location"> 51 <svg viewBox="0 0 24 24"> 52 <path 53 d="M12,11.5A2.5,2.5 0 0,1 9.5,9A2.5,2.5 0 0,1 12,6.5A2.5,2.5 0 0,1 14.5,9A2.5,2.5 0 0,1 12,11.5M12,2A7,7 0 0,0 5,9C5,14.25 12,22 12,22C12,22 19,14.25 19,9A7,7 0 0,0 12,2Z" /> 54 </svg> 55 <?php echo esc_html($meta_fields['city']); ?> 56 </div> 57 <?php endif; ?> 137 // // Get author name and fallback 138 // $author_name = get_the_author_meta('display_name', $author_id); 139 // $author_name = $author_name ? $author_name : esc_html__('Agent', 'havenlytics'); 58 140 59 <a href="<?php the_permalink(); ?>" class="heavenlytics-property-link"> 60 <?php esc_html_e('View Details', 'havenlytics'); ?> 141 // // Get custom title (optional), fallback if empty 142 // $author_title = get_user_meta($author_id, 'agent_title', true); 143 // $author_title = $author_title ? $author_title : esc_html__('Licensed Real Estate Agent', 'havenlytics'); 144 ?> 145 146 <!-- <div class="havenlytics-agent-info"> 147 <img src="<?php //echo esc_url($avatar_url); 148 ?>" class="havenlytics-agent-avatar" 149 alt="<?php //echo esc_attr($author_name); 150 ?>"> 151 <div class="havenlytics-agent-details"> 152 <div class="havenlytics-agent-name"><?php //echo esc_html($author_name); 153 ?></div> 154 <div class="havenlytics-agent-title"><?php //echo esc_html($author_title); 155 ?></div> 156 </div> 157 </div> --> 158 159 <a href="<?php the_permalink(); ?>" class="havenlytics-btn-primary havenlytics-btn-style-1"> 160 <?php esc_html_e('View Property', 'havenlytics'); ?> 61 161 </a> 62 162 </div> 63 163 </div> 64 164 </div> 165 65 166 </div> 167 <!-- /Property card list -->
Note: See TracChangeset
for help on using the changeset viewer.