Plugin Directory

Changeset 3322295


Ignore:
Timestamp:
07/04/2025 11:51:47 AM (8 months ago)
Author:
havenlytics
Message:

Release version 1.0.3 – Added weekly roadmap section and feedback support

Location:
havenlytics
Files:
121 added
12 edited

Legend:

Unmodified
Added
Removed
  • havenlytics/trunk/havenlytics.php

    r3315596 r3322295  
    44Plugin URI: https://wordpress.org/plugins/havenlytics/
    55Description: A property listing plugin for WordPress that allows users to easily manage and display property listings.
    6 Version: 1.0.2
     6Version: 1.0.3
    77Author: Havenlytics
    88Author URI: https://havenlytics.com
     
    3030
    3131// Define constants
    32 define('HVNLY_PROPERTY_VERSION', '1.0.2');
     32define('HVNLY_PROPERTY_VERSION', '1.0.3');
    3333define('HVNLY_PROPERTY_URL', plugin_dir_url(__FILE__));
    3434define('HVNLY_PROPERTY_PATH', plugin_dir_path(__FILE__));
    3535
    36 // Options/Settings
    37 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');
    4236
    4337// Include the plugin files
  • havenlytics/trunk/includes/class-meta-fields.php

    r3315596 r3322295  
    2727        public static function hvnly_property_add_meta_boxes()
    2828        {
     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            );
    2938            // Main details
    3039            add_meta_box(
     
    99108
    100109        /**
     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        /**
    101138         * Render property details metabox
    102139         */
     
    106143
    107144            $values = array(
     145                'currency'         => get_post_meta($post->ID, '_havenlytics_currency', true),
    108146                'price'         => get_post_meta($post->ID, '_havenlytics_price', true),
    109147                'bedrooms'      => get_post_meta($post->ID, '_havenlytics_bedrooms', true),
     
    117155                'status'        => get_post_meta($post->ID, '_havenlytics_status', true)
    118156            );
    119 ?>
     157        ?>
    120158            <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
    121201                <div class="havenlytics-meta-field">
    122202                    <div class="havenlytics-meta-label">
     
    155235                        <label for="havenlytics_sqft"><?php esc_html_e('Square Footage', 'havenlytics'); ?></label>
    156236                    </div>
     237
    157238                    <div class="havenlytics-meta-input">
    158239                        <input type="number" id="havenlytics_sqft" name="_havenlytics_sqft"
     
    274355                'cooling'       => get_post_meta($post->ID, '_havenlytics_cooling', true),
    275356                '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)
    277359            );
    278360        ?>
     
    447529                            ?>
    448530                                <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); ?>>
    449556                                    <?php echo esc_html($label); ?>
    450557                                </option>
     
    758865            }
    759866
     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
    760873            // Save hvnly_property details fields
    761874            $detail_fields = array(
     875                '_havenlytics_currency',
    762876                '_havenlytics_price',
    763877                '_havenlytics_bedrooms',
     
    795909                '_havenlytics_cooling',
    796910                '_havenlytics_water',
    797                 '_havenlytics_sewer'
     911                '_havenlytics_sewer',
     912                '_havenlytics_rating_star'
    798913            );
    799914            foreach ($additional_fields as $field) {
     
    868983            }
    869984
     985
    870986            if (isset($_POST['_havenlytics_youtube_title'])) {
    871987                $youtube_title = sanitize_text_field(wp_unslash($_POST['_havenlytics_youtube_title']));
  • havenlytics/trunk/includes/class-post-type.php

    r3315596 r3322295  
    5252                'menu_position'       => 15,
    5353                'menu_icon'           => 'dashicons-admin-home',
    54                 'supports'            => array('title', 'editor', 'excerpt', 'thumbnail'),
     54                'supports'            => array('title', 'editor', 'thumbnail'),
    5555                'taxonomies'          => array('hvnly_property_category', 'hvnly_property_tag'),
    5656                'has_archive'  => true, // Allows archive page
    57                 'rewrite' => array('slug' => 'hvnly-property', 'with_front' => false),
     57                'rewrite' => array('slug' => 'property', 'with_front' => false),
    5858                'show_in_rest'        => true,
    5959                'publicly_queryable'  => true,
     
    9090                'show_admin_column' => true,
    9191                'show_in_rest'      => true,
    92                 'rewrite'           => array('slug' => 'hvnly-property-category', 'with_front' => false),
     92                'rewrite'           => array('slug' => 'property-category', 'with_front' => false),
    9393            ));
    9494
     
    116116                'show_admin_column' => true,
    117117                'show_in_rest'      => true,
    118                 'rewrite'           => array('slug' => 'hvnly-property-tag', 'with_front' => false),
     118                'rewrite'           => array('slug' => 'property-tag', 'with_front' => false),
    119119            ));
    120120        }
  • havenlytics/trunk/includes/functions-core.php

    r3315596 r3322295  
    44  return get_post_meta($post_id, $key, true);
    55}
     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 */
     15function 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  
    7070    });
    7171
     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
    72555   
     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
    73807})(jQuery);
    74808
  • havenlytics/trunk/public/assets/js/havenlytics-map.js

    r3315596 r3322295  
    1717                    // Add OpenStreetMap tiles
    1818                    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
    19                         attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
     19                        attribution: '&copy; <a href="https://wordpress.org/plugins/havenlytics/">Havenlytics</a> Plugin &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
    2020                        maxZoom: 19
    2121                    }).addTo(map);
  • havenlytics/trunk/public/class-frontend-assets.php

    r3315596 r3322295  
    2121        {
    2222
    23             // Front End CSS
     23            // ✅ fontawesome CSS
    2424            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',
    2758                [],
    2859                HVNLY_PROPERTY_VERSION
     
    3061
    3162
     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
    3272
    3373            // Only load on single property pages
    3474            if (is_singular('hvnly_property')) {
    3575
    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
    37102                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',
    42107                    true
    43108                );
    44109
    45 
    46                 // Leaflet CSS
     110                // ✅ Leaflet CSS
    47111                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',
    50114                    array(),
    51115                    '1.9.4'
    52116                );
    53117
    54                 // Leaflet JS
     118                // Leaflet JS
    55119                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',
    58122                    array(),
    59123                    '1.9.4',
     
    62126
    63127
     128
     129                // ✅ Havenlytics Map JS
    64130                wp_enqueue_script(
    65131                    'havenlytics-map',
    66132                    HVNLY_PROPERTY_URL . 'public/assets/js/havenlytics-map.js',
    67                     ['havenlytics-leaflet-js'],
     133                    ['havenlytics-leaflet'],
    68134                    HVNLY_PROPERTY_VERSION,
    69135                    true
    70136                );
     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                );
    71145            }
     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            );
    72155        }
    73156    }
  • havenlytics/trunk/public/class-frontend.php

    r3315596 r3322295  
    175175
    176176    if ($query->have_posts()) {
    177       echo '<div class="heavenlytics-container">';
    178       echo $view_type === 'grid' ? '<div class="heavenlytics-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">';
    179179
    180180      while ($query->have_posts()) {
     
    184184
    185185      wp_reset_postdata();
    186       echo '</div>';
     186      echo '</div></div>';
    187187
    188188      $this->render_pagination($query);
    189       echo '</div>';
     189      echo '</div></div>';
    190190    } else {
    191191      $this->render_no_properties_found();
     
    225225  {
    226226?>
    227     <div class="heavenlytics-col">
    228       <div class="heavenlytics-property-card">
     227<div class="heavenlytics-col">
     228    <div class="heavenlytics-property-card">
    229229        <div class="heavenlytics-property-card-image">
    230           <?php if ($meta_fields['featured_image']) : ?>
     230            <?php if ($meta_fields['featured_image']) : ?>
    231231            <img src="<?php echo esc_url($meta_fields['featured_image']); ?>" alt="<?php the_title_attribute(); ?>">
    232           <?php else : ?>
     232            <?php else : ?>
    233233            <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']) : ?>
    237237            <span class="heavenlytics-property-badge"><?php echo esc_html($meta_fields['status_badge']); ?></span>
    238           <?php endif; ?>
     238            <?php endif; ?>
    239239        </div>
    240240
    241241        <div class="heavenlytics-property-card-content">
    242           <?php if ($meta_fields['price']) : ?>
     242            <?php if ($meta_fields['price']) : ?>
    243243            <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']) : ?>
    249249            <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                   <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>
    272250            <?php endif; ?>
    273251
    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>
    278278        </div>
    279       </div>
    280279    </div>
    281   <?php
     280</div>
     281<?php
    282282  }
    283283
     
    285285  {
    286286  ?>
    287     <div class="heavenlytics-list-item">
    288       <div class="heavenlytics-property-card">
     287<div class="heavenlytics-list-item">
     288    <div class="heavenlytics-property-card">
    289289        <div class="heavenlytics-property-card-image">
    290           <?php if ($meta_fields['featured_image']) : ?>
     290            <?php if ($meta_fields['featured_image']) : ?>
    291291            <img src="<?php echo esc_url($meta_fields['featured_image']); ?>" alt="<?php the_title_attribute(); ?>">
    292           <?php else : ?>
     292            <?php else : ?>
    293293            <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']) : ?>
    297297            <span class="heavenlytics-property-badge"><?php echo esc_html($meta_fields['status_badge']); ?></span>
    298           <?php endif; ?>
     298            <?php endif; ?>
    299299        </div>
    300300
    301301        <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>
    308313            <?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>
    340340        </div>
    341       </div>
    342341    </div>
    343   <?php
     342</div>
     343<?php
    344344  }
    345345
     
    401401
    402402    $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>',
    406439    ];
    407440
     
    412445        'width' => true,
    413446        'height' => true,
    414         'viewBox' => true,
     447        'viewbox' => true,
    415448        'aria-hidden' => true,
    416449        'role' => true,
    417450        'focusable' => true,
    418451        '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,
    419467      ],
    420468      'path' => [
     
    424472        'stroke-width' => true,
    425473      ],
    426       'g' => ['fill' => true],
    427474    ];
    428475
    429476  ?>
    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>
    433491    </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
    436589
    437590
     
    439592  {
    440593    if ($query->max_num_pages <= 1) return;
    441 
    442594    echo '<div class="heavenlytics-pagination">';
    443     echo wp_kses_post(paginate_links([
     595    echo
     596    wp_kses_post(paginate_links([
    444597      'base' => str_replace(999999999, '%#%', esc_url(get_pagenum_link(999999999))),
    445598      'format' => '?paged=%#%',
     
    456609  {
    457610  ?>
    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>
    461614<?php
    462615  }
  • havenlytics/trunk/readme.txt

    r3315596 r3322295  
    66Tested up to: 6.8
    77Requires PHP: 7.2 
    8 Stable tag: 1.0.2
     8Stable tag: 1.0.3
    99License: GPLv2 or later
    1010License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    1616Havenlytics 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.
    1717
     18We’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! 
     24We are releasing **weekly updates** with new features, enhancements, and performance improvements to make this the **most advanced real estate plugin for WordPress**.
     25
     26Our 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?** 
     29We’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
    1842= Key Features =
     43
    1944- Display properties in grid or list view
    2045- Advanced filtering by price, bedrooms, bathrooms, status, and type
     
    106131== Changelog ==
    107132
     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
    108153= 1.0.0 =
    109154* Initial release of Havenlytics plugin
     
    114159== Upgrade Notice ==
    115160
    116 = 1.0.0 =
     161= 1.0.0 = 
    117162Initial release of Havenlytics plugin.
    118163
    119 == License ==
    120 GNU General Public License v2 or later
     164== License == 
     165GNU General Public License v2 or later 
     166
     167== Support ==
     168
     169💬 We welcome your feedback to make Havenlytics better. 
     170Send your ideas or suggestions to [[email protected]](mailto:[email protected]).
     171
     172Need 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
     179We’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  
    1212
    1313        // 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);
    1418        $price = get_post_meta($property_id, '_havenlytics_price', true);
    1519        $bedrooms = get_post_meta($property_id, '_havenlytics_bedrooms', true);
     
    8387                $status_badge = '';
    8488        }
     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
    85120    ?>
    86121
    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>
    198189                    <?php endif; ?>
    199190                </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
    200873            </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                     <iframe
    233                         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 
    247874        </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; ?>
    307875
    308876    <?php endwhile; ?>
  • havenlytics/trunk/templates/property-card-grid.php

    r3315596 r3322295  
    66$property_id = get_the_ID();
    77$meta_fields = $this->get_property_meta_fields($property_id);
     8
    89?>
    910
    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; ?>
    1811
    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">
    2317
    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();
    2822
    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) :
    3025
    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']) : '';
    3428
    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>
    4034
    41             <div class="heavenlytics-property-excerpt">
    42                 <?php echo esc_html(wp_trim_words(get_the_excerpt(), 20)); ?>
    43             </div>
    4435
    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
    5469                <?php endif; ?>
    5570
    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>
    59169            </div>
    60170        </div>
    61171    </div>
    62172</div>
     173<!-- /Property grid -->
  • havenlytics/trunk/templates/property-card-list.php

    r3315596 r3322295  
    66$property_id = get_the_ID();
    77$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
    813?>
    914
    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; ?>
    1815
    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">
    2318
    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
    2883                <?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
    3191                <?php endif; ?>
     92
    3293            </div>
    3394
    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>
    36102            <?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            ];
    37111
    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());
    43133
    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]);
    47136
    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');
    58140
    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'); ?>
    61161                </a>
    62162            </div>
    63163        </div>
    64164    </div>
     165
    65166</div>
     167<!-- /Property card list -->
Note: See TracChangeset for help on using the changeset viewer.