Plugin Directory

Changeset 3457384


Ignore:
Timestamp:
02/09/2026 07:31:06 PM (7 weeks ago)
Author:
nazmul111
Message:

Upload review next version 1.0.3

Location:
review-next-for-woocommerce
Files:
155 added
19 edited

Legend:

Unmodified
Added
Removed
  • review-next-for-woocommerce/trunk/CHANGELOG.md

    r3317206 r3457384  
    33All notable changes to the Review Next for WooCommerce plugin will be documented in this file.
    44
     5## [1.0.3] - 2026-02-10
     6### Added
     7- **Pro Release:** Launched Review Next for WooCommerce Pro with premium features and functionality.
     8- **New:** Added support for Video Reviews! Customers can now upload MP4/WebM videos
     9- **New:** Implemented Automated Coupon Generation system to reward reviewers
     10- **New:** Added Advanced Review Filtering and Sorting options.
     11- **New:** Implemented Review Moderation Dashboard with bulk actions (Pro feature).
     12- **Enhancement:** Improved frontend review display and grid layout.
     13- **Enhancement:** Enhanced "Verified Owner" logic and display.
     14- **Enhancement:** Performance optimization for high-volume reviews.
     15- **Enhancement:** Improved UI/UX across the plugin interface.
     16
     17### Fixed
     18- Resolved compatibility issues with WooCommerce 8.x+
     19- Fixed review display issues on mobile devices
     20- General bug fixes and improvements
     21
     22### Updated
     23- Updated plugin version to 1.0.3
     24- Updated documentation with Pro feature information
     25
    526## [1.0.2] - 2025-06-25
    6 ### Added
    7 - Initial version bump with version number update
    8 - Created CHANGELOG.md to track all future changes
    9 
    10 ### Changed
    11 - Updated plugin version to 1.0.2
    12 
    1327### Fixed
    1428- Fixed issue with review display on the frontend
     
    1933- General bug fixes and improvements
    2034
    21 ### Security
    22 - No security updates in this release
    23 
    2435---
    2536*Note: This is the initial changelog entry. Future updates will document all notable changes.*
  • review-next-for-woocommerce/trunk/README.txt

    r3317215 r3457384  
    11=== Review Next for WooCommerce ===
    22Contributors: nazmul111
    3 Tags: woocommerce, reviews, product reviews, customer reviews, image reviews
     3Tags: woocommerce, reviews, product reviews, customer reviews, photo reviews, video reviews, review reminders, coupons
    44Requires at least: 5.0
    55Tested up to: 6.8.1
    66Requires PHP: 7.2
    7 Stable tag: 1.0.2
     7Stable tag: 1.0.3
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
    1010
    11 Advanced review system for WooCommerce products, allowing image uploads and enhanced display.
     11Boost sales with Photo & Video reviews, automated Review Reminder emails, and Coupon incentives. The ultimate social proof solution for WooCommerce.
    1212
    1313== Description ==
    1414
    15 Review Next for WooCommerce enhances the WooCommerce review system by allowing customers to upload images with their reviews, providing a richer user experience. This plugin offers advanced features for managing and displaying customer feedback, helping you build trust and social proof for your products.
     15**Review Next for WooCommerce** is the ultimate social proof solution designed to transform how you collect and display customer feedback. In a world where visual trust drives sales, standard text reviews just aren’t enough. Review Next empowers your customers to share their real-life experiences through **Photo and Video Reviews**, creating authentic social proof that instantly builds trust with potential buyers.
    1616
    17 Key Features:
     17But we don't just help you *display* reviews—we help you *get* them. With our powerful **Automated Review Reminders** and **Coupon Incentives**, you can turn every purchase into a 5-star opportunity. Automatically send follow-up emails requesting a review, and reward customers with a unique coupon code when they leave one. It’s a seamless loop that drives engagement, loyalty, and repeat sales.
    1818
    19 *   **Image Uploads with Reviews:** Customers can attach images to their product reviews.
    20 *   **WooCommerce Integration:** Seamlessly integrates with WooCommerce product pages.
    21 *   **Review Management:** Enhanced options for moderating and managing reviews.
    22 *   **Customizable Display:** Control how reviews are displayed on your site.
    23 *   **Schema Markup:** Optimized for SEO with appropriate review schema.
    24 *   **AJAX Powered:** Smooth review submission and display without page reloads.
     19Packed with premium features like smart filtering, voting systems (Helpful/Not Helpful), verified buyer badges, and a fully customizable design, Review Next looks stunning on any theme. Whether you are a small store or a large brand, Review Next gives you the tools to leverage the voice of your customers and skyrocket your conversion rates.
     20
     21### 🌟 Why Choose Review Next?
     22
     23*   **📸 Visual Social Proof:** Let customers upload **Photos and Videos** with their reviews. Seeing is believing!
     24*   **🎁 Drive More Reviews:** Automatically generate and send **Discount Coupons** to customers who leave a review.
     25*   **📧 Automated Emails:** Send fully customizable **Review Reminder Emails** to customers after purchase (Pro feature included).
     26*   **🛡️ Trust Badges:** Highlight **Verified Owners** to show authenticity and eliminate doubt.
     27*   **⚡ Smart Filtering:** Allow shoppers to filter reviews by Rating, With Images/Videos, or Most Recent/Helpful.
     28*   **🎨 Beautiful Design:** A modern, responsive interface that looks perfect on every device and integrates with any theme.
     29
     30### 🔥 Key Features
     31
     32#### 📸 Visual Reviews (Photo & Video)
     33Allow customers to attach multiple images and videos to their reviews. A picture is worth a thousand words, and a video is worth even more!
     34*   **Drag & Drop Upload:** Easy interface for customers.
     35*   **Video Support:** Customers can upload MP4/WebM videos directly.
     36*   **Media Gallery:** Beautiful layout for review media.
     37
     38#### 🎫 Automated Coupon System
     39Reward your loyal customers! The plugin tracks reviews and automatically generates unique coupon codes for customers who meet your review criteria.
     40*   **Auto-Generation:** Creates unique WooCommerce coupons automatically.
     41*   **Custom Rewards:** Set discount type (Percent/Fixed), amount, and expiry.
     42*   **Email Notification:** Automatically emails the coupon code to the customer.
     43
     44#### 📧 Email Notifications & SMTP
     45Ensure your emails land in the inbox, not the spam folder.
     46*   **Admin Notifications:** Get notified instantly when a new review is submitted.
     47*   **Coupon Emails:** Customizable emails delivering rewards to customers.
     48*   **Built-in SMTP:** Configure SMTP settings directly within the plugin for reliable email delivery.
     49
     50#### 🎛️ Advanced Review Management
     51Users can interact with reviews to help others make better decisions.
     52*   **Voting System:** "Helpful" / "Not Helpful" buttons.
     53*   **Sorting & Filtering:** Sort by High/Low rating, Most Recent, or Most Helpful.
     54*   **Verified Badge:** Automatically marks reviews from actual purchasers.
     55
     56#### 🎨 Customization & Control
     57*   **Watermarking:** Protect your customer images by automatically adding your logo/text watermark.
     58*   **Style Editor:** Customize colors, fonts, and button styles to match your brand identity.
     59*   **Restrictions:** Control who can leave reviews (All, Logged-in, or Verified Buyers only).
    2560
    2661== Installation ==
     
    28631.  Upload the `review-next` plugin folder to the `/wp-content/plugins/` directory, or install the plugin through the WordPress plugins screen directly.
    29642.  Activate the plugin through the 'Plugins' screen in WordPress.
    30 3.  Navigate to WooCommerce > Settings > Products > Reviews (or a similar path created by the plugin) to configure review settings if available.
    31 4.  Customers can now submit reviews with images on product pages.
     653.  Navigate to **Review Next > Settings** to configure your review collection and display preferences.
     664.  Configure your **Email Settings** to ensure notifications and coupons are delivered.
     675.  Sit back and watch the reviews (and sales) roll in!
    3268
    3369== Frequently Asked Questions ==
    3470
    35 = How do customers submit reviews with images? =
     71= How do video reviews work? =
     72Customers can upload video files (MP4, WebM) directly through the review form. You can control the maximum file size allowed.
    3673
    37 After activating the plugin, the review form on WooCommerce product pages will include an option to upload images along with the review text and rating.
     74= Can I moderate reviews? =
     75Yes! Review Next respects the standard WordPress discussion settings. You can choose to manually approve reviews before they appear on your site.
    3876
    39 = Is this plugin compatible with all WooCommerce themes? =
     77= Does it work with my theme? =
     78Review Next is designed to be compatible with all well-coded WooCommerce themes. It uses standard hooks to display the review section.
    4079
    41 Review Next for WooCommerce is designed to be compatible with most WooCommerce themes that follow standard WooCommerce templating practices. However, minor styling adjustments might be needed for perfect integration with some themes.
    42 
    43 = Can I moderate reviews before they are published? =
    44 
    45 Yes, Review Next for WooCommerce respects WordPress's default comment moderation settings. You can set reviews to be held for moderation via Settings > Discussion in your WordPress admin area.
    46 
    47 = Where are the uploaded review images stored? =
    48 
    49 Review images are stored in your WordPress media library, making them easy to manage.
     80= How does the Coupon System work? =
     81You set a threshold (e.g., 1 review). When a customer's review is approved, the system generates a unique coupon code based on your settings (e.g., 10% off) and emails it to them.
    5082
    5183== Screenshots ==
    5284
    53 1.  The review submission form on a product page, showing the image upload field.
    54 2.  Example of how reviews with images are displayed on the product page.
    55 3.  Plugin settings page (if applicable).
     851.  **Stunning Review Display:** Modern grid layout showing customer photos and videos.
     862.  **Review Submission Form:** Easy-to-use form with drag-and-drop media upload.
     873.  **Smart Filtering:** Customers filtering reviews by "With Images" and "Verified Purchase".
     884.  **Coupon Email:** Example of the automated email customers receive with their reward.
     895.  **Settings Panel:** Powerful options to customize every aspect of the plugin.
    5690
    5791== Changelog ==
    5892
     93= 1.0.3 =
     94*   **New:** Added support for Video Reviews! Customers can now upload MP4/WebM videos.
     95*   **New:** Implemented Automated Coupon Generation system to reward reviewers.
     96*   **New:** Added comprehensive Email Settings with built-in SMTP configuration.
     97*   **New:** Added `CHANGELOG.md` for better version tracking.
     98*   **Enhancement:** Improved frontend review display and grid layout.
     99*   **Enhancement:** Enhanced "Verified Owner" logic and display.
     100
    59101= 1.0.2 =
    60 * Fixed issue with review display on the frontend
    61 * Resolved file not found issues
    62 * Fixed PCP (Product Comparison Page) related issues
    63 * Addressed prefix and other minor issues
    64 * Fixed feedback submission issues
    65 * General bug fixes and improvements
    66 * Added: CHANGELOG.md file for better version tracking
    67 * Updated: Plugin version number to 1.0.2
     102*   Fixed issue with review display on the frontend
     103*   Resolved file not found issues
     104*   Fixed PCP (Product Comparison Page) related issues
     105*   Addressed prefix and other minor issues
     106*   Fixed feedback submission issues
     107*   General bug fixes and improvements
    68108
    69 = 1.0 =
    70 * Initial release of Review Next for WooCommerce.
    71 * Feature: Allow customers to upload images with their WooCommerce product reviews.
    72 * Feature: Integration with WooCommerce product tabs.
    73 * Feature: Basic review management and display.
    74 
    75 == Upgrade Notice ==
     109= 1.0.0 =
     110*   Initial release of Review Next for WooCommerce.
     111*   Feature: Photo reviews, verified badges, and voting system.
  • review-next-for-woocommerce/trunk/admin/class-revnextwoo-admin.php

    r3315734 r3457384  
    2323class Revnextwoo_Admin
    2424{
     25    public function __construct($plugin_name, $version)
     26    {
     27        $this->plugin_name = $plugin_name;
     28        $this->version = $version;
     29        add_action('admin_init', array(
     30            $this,
     31            'revnextwoo_register_triggers_settings'
     32        ));
     33    }
     34
     35    public function revnextwoo_register_triggers_settings() {
     36        register_setting('revnextwoo_triggers_settings_group', 'revnextwoo_enable_photo_upload');
     37        register_setting('revnextwoo_triggers_settings_group', 'revnextwoo_max_photos');
     38
     39        register_setting('revnextwoo_triggers_settings_group', 'revnextwoo_enable_video_upload');
     40
     41            }
     42
    2543
    2644    /**
     
    4361
    4462    /**
    45      * Initialize the class and set its properties.
    46      *
    47      * @since    1.0.0
    48      * @param      string    $plugin_name       The name of this plugin.
    49      * @param      string    $version    The version of this plugin.
    50      */
    51     public function __construct($plugin_name, $version)
    52     {
    53 
    54         $this->plugin_name = $plugin_name;
    55         $this->version = $version;
    56     }
    57 
    58     /**
    5963     * Register the stylesheets for the admin area.
    6064     *
     
    7579         * class.
    7680         */
     81        if (isset($_GET['page']) && $_GET['page'] === 'revnextwoo-coupon-settings') {
     82            wp_enqueue_style('select2-css', 'https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css');
     83            wp_enqueue_script('select2-js', 'https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js', ['jquery'], null, true);
     84        }
    7785
    7886        wp_enqueue_style($this->plugin_name, plugin_dir_url(__FILE__) . 'css/revnextwoo-admin.css', array(), $this->version, 'all');
     
    102110        wp_enqueue_script('quicktags', false, array(), $this->version, true); // Set version for quicktags
    103111    }
     112
     113    public function admin_footer_scripts()
     114    {
     115        if (isset($_GET['page']) && $_GET['page'] === 'revnextwoo-coupon-settings') {
     116            ?>
     117            <script>
     118                    jQuery(document).ready(function ($) {
     119                        $('#coupon_categories').select2({
     120                            placeholder: 'Select Categories',
     121                            width: 'resolve',
     122                            allowClear: true
     123                        });
     124                    });
     125                    jQuery(document).ready(function ($) {
     126                        $('#coupon_brands').select2({
     127                            placeholder: 'Select Brands',
     128                            width: 'resolve',
     129                            allowClear: true
     130                        });
     131                    });
     132                </script>
     133            <?php
     134        }
     135    }
    104136}
  • review-next-for-woocommerce/trunk/admin/css/revnextwoo-admin.css

    r3315734 r3457384  
     1.revnextwoo_logo_bg {
     2    position: absolute;
     3    bottom: 20px;
     4    right: 20px;
     5    font-size: 64px;
     6    font-weight: 900;
     7    opacity: 0.15;
     8    user-select: none;
     9    pointer-events: none;
     10    font-family: "Helvetica", "Arial", sans-serif;
     11
     12}
     13
     14.logo-img {
     15    position: absolute;
     16    bottom: 120px;
     17    right: 20px;
     18    width: 120px;
     19    opacity: 0.15;
     20    user-select: none;
     21    pointer-events: none;
     22}
     23
     24
    125.slide-left {
    226    transition: transform 0.5s ease-in-out;
     
    529}
    630
    7 
    8 
    931.input_group {
    1032    display: flex;
     
    1335    margin-bottom: 25px;
    1436    margin-right: 14px;
    15   }
    16 #toplevel_page_review-settings ul li:last-child{
     37}
     38
     39#toplevel_page_review-settings ul li:last-child {
    1740    display: none;
    1841}
     42
     43/* DEBUG WATERMARK */
     44
     45
     46/* ReviewNextWoo Admin Tabs Styling */
     47.reviewnextwoo-header-row {
     48    display: flex;
     49    align-items: flex-end;
     50    justify-content: flex-start;
     51    margin-bottom: 0;
     52}
     53
     54.reviewnextwoo-dashboard-title {
     55    text-align: left;
     56    margin: 0 0 18px 0;
     57    font-size: 2.1rem;
     58    font-weight: 700;
     59    color: #1a2433;
     60    letter-spacing: -1px;
     61    padding-left: 8px;
     62    line-height: 1.2;
     63}
     64
     65.reviewnextwoo-tabs {
     66    display: flex;
     67    flex-wrap: nowrap;
     68    border-radius: 16px 16px 0 0;
     69    box-shadow: 0 2px 8px rgba(60, 80, 180, 0.06);
     70    overflow-x: auto;
     71    overflow-y: hidden;
     72    justify-content: flex-start;
     73    background: #f8faff;
     74    scrollbar-width: thin;
     75    scrollbar-color: #bed2fd #f8faff;
     76}
     77
     78.reviewnextwoo-tabs::-webkit-scrollbar {
     79    height: 4px;
     80}
     81
     82.reviewnextwoo-tabs::-webkit-scrollbar-thumb {
     83    background: #bed2fd;
     84    border-radius: 4px;
     85}
     86
     87.reviewnextwoo-tabs::-webkit-scrollbar-track {
     88    background: #f8faff;
     89    border-radius: 4px;
     90}
     91
     92.reviewnextwoo-tab {
     93    background: linear-gradient(90deg, #f8faff 60%, #eaf6ff 100%);
     94    border: none;
     95    outline: none;
     96    cursor: pointer;
     97    padding: 10px 35px;
     98    font-size: 1.2rem;
     99    font-weight: 510;
     100    color: #3b4d63;
     101    /* border-radius: 12px 12px 0 0; */
     102    transition: color 0.25s, border-bottom 0.25s, background 0.25s, box-shadow 0.25s;
     103    position: relative;
     104    letter-spacing: 0.01em;
     105    min-width: 120px;
     106    box-shadow: 0 2px 4px rgba(60, 80, 180, 0.08);
     107    /* flex: 0 0 auto; */
     108    display: inline-flex;
     109    flex-direction: column;
     110    align-items: center;
     111    justify-content: center;
     112    gap: 4px;
     113    line-height: 1.2;
     114}
     115
     116.reviewnextwoo-tab.active,
     117.reviewnextwoo-tab:focus {
     118    background: #bed2fd;
     119    color: #1a237e;
     120    z-index: 2;
     121    box-shadow: 0 4px 12px rgba(60, 80, 180, 0.10);
     122
     123}
     124
     125.reviewnextwoo-tab:hover {
     126    box-shadow: inset 5px 5px 10px #aaaaaa;
     127}
     128
     129.reviewnextwoo-tab-content {
     130    display: none;
     131    background: linear-gradient(120deg, #f8faff 60%, #e0f7fa 100%);
     132    border-radius: 0 0 18px 18px;
     133    box-shadow: 0 6px 32px rgba(60, 80, 180, 0.08);
     134    min-height: 180px;
     135    opacity: 0;
     136    transform: translateX(40px);
     137    transition: opacity 0.45s cubic-bezier(.4, 0, .2, 1), transform 0.45s cubic-bezier(.4, 0, .2, 1);
     138    position: relative;
     139}
     140
     141/* Active tab */
     142.reviewnextwoo-tab-content.active {
     143    display: block !important;
     144    opacity: 1;
     145    transform: translateX(0);
     146    z-index: 2;
     147    border: none;
     148    background: linear-gradient(120deg, #f8faff 60%, #e0f7fa 100%);
     149    animation: reviewnextwoo-fade-slide-in 0.4s ease-out;
     150}
     151
     152/* Smooth fade + horizontal slide */
     153@keyframes reviewnextwoo-fade-slide-in {
     154    from {
     155        opacity: 0;
     156        transform: translateX(30px);
     157        /* slight right slide */
     158    }
     159
     160    to {
     161        opacity: 1;
     162        transform: translateX(0);
     163    }
     164}
     165
     166/* Slide left transition */
     167.sliding-left {
     168    animation: reviewnextwoo-slide-left 0.35s ease-out;
     169}
     170
     171/* Slide right transition */
     172.sliding-right {
     173    animation: reviewnextwoo-slide-right 0.35s ease-out;
     174}
     175
     176/* Left slide */
     177@keyframes reviewnextwoo-slide-left {
     178    from {
     179        opacity: 0;
     180        transform: translateX(40px);
     181        /* slide from right */
     182    }
     183
     184    to {
     185        opacity: 1;
     186        transform: translateX(0);
     187    }
     188}
     189
     190/* Right slide */
     191@keyframes reviewnextwoo-slide-right {
     192    from {
     193        opacity: 0;
     194        transform: translateX(-40px);
     195        /* slide from left */
     196    }
     197
     198    to {
     199        opacity: 1;
     200        transform: translateX(0);
     201    }
     202}
     203
     204/* Dashboard Tab Typography (except analytics tab) */
     205.reviewnextwoo-tab-content:not([data-tab='analytics']) label,
     206.reviewnextwoo-tab-content:not([data-tab='analytics']) .reviewnextwoo-section-title,
     207.reviewnextwoo-tab-content:not([data-tab='analytics']) th,
     208.reviewnextwoo-tab-content:not([data-tab='analytics']) legend {
     209    font-size: 16px !important;
     210    font-weight: 500;
     211}
     212
     213.reviewnextwoo-tab-content:not([data-tab='analytics']) td,
     214.reviewnextwoo-tab-content:not([data-tab='analytics']) .reference,
     215.reviewnextwoo-tab-content:not([data-tab='analytics']) .small,
     216.reviewnextwoo-tab-content:not([data-tab='analytics']) .desc,
     217.reviewnextwoo-tab-content:not([data-tab='analytics']) .description,
     218.reviewnextwoo-tab-content:not([data-tab='analytics']) p.ref,
     219.reviewnextwoo-tab-content:not([data-tab='analytics']) p.small {
     220    font-size: 14px !important;
     221    font-weight: 350;
     222    color: #444;
     223}
     224
     225/* Triggers Tab Controls Styling */
     226.revnextwoo-list-controls {
     227    list-style: none;
     228    padding: 0;
     229    margin: 0;
     230    display: flex;
     231    flex-direction: column;
     232    gap: 18px;
     233    width: 100%;
     234    max-width: 100%;
     235    align-items: flex-start;
     236}
     237
     238.revnextwoo-list-controls li {
     239    padding: 14px 16px;
     240    display: flex;
     241    align-items: center;
     242    justify-content: center;
     243    gap: 18px;
     244    transition: background 0.2s, opacity 0.2s;
     245}
     246
     247.revnextwoo-fieldset {
     248    border: 3px solid #bed2fd;
     249    border-radius: 0px 0px 8px 8px;
     250    padding: 3px 15px;
     251    width: 100%;
     252    max-width: 100%;
     253    box-sizing: border-box;
     254    text-align: left;
     255}
     256
     257.revnextwoo-legend {
     258    font-size: 1.5rem;
     259    font-weight: 700;
     260    color: #1a2433;
     261    margin-bottom: 16px;
     262    letter-spacing: -0.5px;
     263}
     264
     265.feature-title {
     266    font-weight: 700;
     267    font-size: 1.18rem;
     268    color: #222;
     269    letter-spacing: -0.5px;
     270}
     271
     272.feature-desc {
     273    font-size: 1.06rem;
     274    color: #444;
     275    margin-left: 8px;
     276    font-weight: 500;
     277    letter-spacing: -0.2px;
     278}
     279
     280.faded {
     281    opacity: 0.6;
     282    pointer-events: none;
     283}
     284
     285.revnextwoo-upload-box {
     286    border: 1px dashed #bbb;
     287    border-radius: 6px;
     288    min-height: 48px;
     289    padding: 10px;
     290    background: #fff;
     291    display: flex;
     292    align-items: center;
     293    gap: 12px;
     294}
     295
     296.revnextwoo-list-controls input[type="number"],
     297.revnextwoo-list-controls input[type="text"],
     298.revnextwoo-list-controls input[type="file"],
     299.revnextwoo-list-controls select,
     300.revnextwoo-list-controls button,
     301.revnextwoo-list-controls .button {
     302    padding: 8px 14px;
     303    border-radius: 5px;
     304    border: 1px solid #c2c8d0;
     305    min-width: 52px;
     306    color: #1a2433;
     307    transition: border-color 0.2s, box-shadow 0.2s;
     308}
     309
     310.revnextwoo-list-controls button,
     311.revnextwoo-list-controls .button {
     312    background: #0a69c7;
     313    color: #fff;
     314    font-weight: 700;
     315    border: none;
     316    cursor: pointer;
     317    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
     318}
     319
     320.revnextwoo-list-controls button:hover,
     321.revnextwoo-list-controls .button:hover {
     322    background: #094b8b;
     323}
     324
     325.drag-drop-desc {
     326    color: #888;
     327    font-size: 13px;
     328    margin-left: 8px;
     329}
     330
     331/* Styling Settings Section */
     332.style-settings .form-table {
     333    margin-top: 0;
     334    max-width: 800px;
     335}
     336
     337.style-settings fieldset {
     338    border: 1px solid #ddd;
     339    border-radius: 4px;
     340    padding: 20px 25px;
     341    margin-bottom: 30px;
     342    background: #fff;
     343    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
     344    position: relative;
     345}
     346
     347.style-settings legend {
     348    font-size: 14px;
     349    font-weight: 600;
     350    color: #1d2327;
     351    background: #f8f9fa;
     352    border: 1px solid #ddd;
     353    border-radius: 4px;
     354    padding: 8px 15px;
     355    margin-left: 15px;
     356    position: relative;
     357    top: -10px;
     358    background: #fff;
     359    box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
     360}
     361
     362/* Style for the field groups */
     363.style-settings h3 {
     364    font-size: 16px;
     365    color: #1d2327;
     366    margin: 25px 0 15px 0;
     367    padding-bottom: 8px;
     368    border-bottom: 1px solid #eee;
     369}
     370
     371/* Color picker fields */
     372.color-picker-field {
     373    display: flex;
     374    align-items: center;
     375    margin: 10px 0;
     376}
     377
     378.color-picker-field .wp-picker-container {
     379    position: relative;
     380    display: inline-block;
     381    vertical-align: top;
     382}
     383
     384.color-picker-field .wp-picker-input-wrap {
     385    display: inline-block;
     386    margin-right: 10px;
     387}
     388
     389.color-picker-field .wp-color-result {
     390    margin: 0;
     391    height: 30px;
     392    border-radius: 3px;
     393    border: 1px solid #8c8f94;
     394    box-shadow: 0 1px 0 #fff, 0 0 2px 1px rgba(0, 0, 0, 0.08);
     395}
     396
     397.color-picker-field .wp-color-result-text {
     398    line-height: 28px;
     399}
     400
     401/* Form table improvements */
     402.style-settings .form-table th {
     403    width: 250px;
     404    padding: 15px 10px 15px 0;
     405    vertical-align: middle;
     406}
     407
     408.style-settings .form-table td {
     409    padding: 15px 10px;
     410    vertical-align: middle;
     411    border-bottom: 1px solid #f0f0f1;
     412}
     413
     414.style-settings .form-table tr:last-child td {
     415    border-bottom: none;
     416}
     417
     418/* Description text */
     419.style-settings .description {
     420    color: #646970;
     421    font-style: italic;
     422    font-size: 13px;
     423    margin-top: 5px;
     424    display: block;
     425    line-height: 1.4;
     426}
     427
     428/* Submit button */
     429.style-settings .submit {
     430    margin-top: 20px;
     431    padding: 15px 0;
     432    border-top: 1px solid #eee;
     433}
     434
     435/* Responsive adjustments */
     436@media screen and (max-width: 782px) {
     437    .style-settings .form-table th {
     438        width: 100%;
     439        display: block;
     440        padding: 10px 0 5px;
     441    }
     442
     443    .style-settings .form-table td {
     444        display: block;
     445        padding: 5px 0 15px;
     446    }
     447
     448    .style-settings fieldset {
     449        padding: 15px;
     450    }
     451}
     452
     453.style-settings .form-table th {
     454    width: 250px;
     455    padding: 20px 10px 20px 0;
     456    vertical-align: middle;
     457}
     458
     459.style-settings .form-table td {
     460    padding: 15px 10px;
     461    vertical-align: middle;
     462}
     463
     464/* Color Picker Styling */
     465.color-picker-field {
     466    display: flex;
     467    align-items: center;
     468    gap: 10px;
     469}
     470
     471.color-picker-field .wp-color-picker {
     472    width: 100px !important;
     473    height: 35px;
     474    padding: 3px 5px;
     475    border-radius: 4px;
     476    border: 1px solid #8c8f94;
     477}
     478
     479.color-picker-field .wp-picker-container {
     480    position: relative;
     481}
     482
     483.color-picker-field .wp-picker-holder {
     484    position: absolute;
     485    z-index: 100;
     486}
     487
     488/* Form Table Adjustments */
     489.form-table th {
     490    font-weight: 500;
     491}
     492
     493.form-table .description {
     494    display: block;
     495    margin-top: 5px;
     496    font-style: italic;
     497    color: #646970;
     498    font-size: 13px;
     499}
     500
     501/* Submit Button */
     502.revnextwoo-save-btn-row {
     503    text-align: right;
     504    margin: 30px 0 32px;
     505}
     506
     507/* Highlight questions with no replies */
     508.no-reply-highlight,
     509.no-reply-highlight td {
     510    background: #fbe3cc !important;
     511}
     512
     513/* Remove styling that breaks row layout */
     514.no-reply-highlight {
     515    color: inherit;
     516    border-radius: 0;
     517    padding: 0;
     518    display: table-row;
     519}
     520
     521/* Make S/N (serial number) column narrow */
     522.wp-list-table th.column-sn,
     523.wp-list-table td.column-sn {
     524    width: 40px;
     525    min-width: 40px;
     526    max-width: 60px;
     527    text-align: center;
     528    padding-left: 0;
     529    padding-right: 0;
     530}
     531
     532/* Hide action links by default, show on row hover */
     533.wp-list-table .question-actions {
     534    opacity: 0;
     535    transition: opacity 0.2s;
     536}
     537
     538.wp-list-table tr:hover .question-actions {
     539    opacity: 1;
     540}
     541
     542/* ==========================================================================
     543   Advanced Settings Page (CAS Settings)
     544   ========================================================================== */
     545
     546/* Main Settings Wrapper */
     547.revnextwoo-settings-wrap {
     548    max-width: 1200px;
     549    margin: 20px 20px 20px 0;
     550    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
     551}
     552
     553/* Settings Header */
     554.revnextwoo-settings-header {
     555    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
     556    border-radius: 12px 12px 0 0;
     557    padding: 28px 32px;
     558    margin-bottom: 0;
     559}
     560
     561.revnextwoo-settings-header-content {
     562    max-width: 800px;
     563}
     564
     565.revnextwoo-settings-title {
     566    margin: 0 0 10px 0;
     567    font-size: 24px;
     568    font-weight: 600;
     569    color: #fff;
     570    display: flex;
     571    align-items: center;
     572    gap: 12px;
     573    letter-spacing: -0.3px;
     574}
     575
     576.revnextwoo-settings-title .dashicons {
     577    font-size: 28px;
     578    width: 28px;
     579    height: 28px;
     580    background: rgba(255, 255, 255, 0.2);
     581    border-radius: 8px;
     582    padding: 6px;
     583    box-sizing: content-box;
     584}
     585
     586.revnextwoo-settings-desc {
     587    margin: 0;
     588    font-size: 15px;
     589    color: rgba(255, 255, 255, 0.9);
     590    line-height: 1.5;
     591}
     592
     593/* Settings Form */
     594.revnextwoo-settings-form {
     595    background: #f8f9fa;
     596    border-radius: 0 0 12px 12px;
     597    padding: 28px;
     598    border: 1px solid #e1e5eb;
     599    border-top: none;
     600}
     601
     602/* Settings Grid Layout - Horizontal Flex */
     603.revnextwoo-settings-grid {
     604    display: flex;
     605    flex-wrap: wrap;
     606    gap: 24px;
     607    margin-bottom: 24px;
     608}
     609
     610.revnextwoo-settings-grid>.revnextwoo-settings-card:not(.revnextwoo-settings-card-full) {
     611    flex: 1 1 calc(50% - 12px);
     612    min-width: 380px;
     613}
     614
     615/* Settings Cards */
     616.revnextwoo-settings-card {
     617    background: #fff;
     618    border-radius: 10px;
     619    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
     620    border: 1px solid #e8ecf1;
     621    overflow: hidden;
     622    transition: box-shadow 0.2s ease, transform 0.2s ease;
     623}
     624
     625.revnextwoo-settings-card:hover {
     626    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
     627}
     628
     629.revnextwoo-settings-card-full {
     630    flex: 1 1 100%;
     631    min-width: 100%;
     632}
     633
     634/* Card Header */
     635.revnextwoo-card-header {
     636    background: linear-gradient(to right, #f8f9fa, #fff);
     637    padding: 18px 24px;
     638    border-bottom: 1px solid #e8ecf1;
     639    display: flex;
     640    align-items: center;
     641    gap: 12px;
     642}
     643
     644.revnextwoo-card-header .dashicons {
     645    font-size: 20px;
     646    width: 20px;
     647    height: 20px;
     648    color: #667eea;
     649    background: rgba(102, 126, 234, 0.1);
     650    border-radius: 6px;
     651    padding: 8px;
     652    box-sizing: content-box;
     653}
     654
     655.revnextwoo-card-header h3 {
     656    margin: 0;
     657    font-size: 16px;
     658    font-weight: 600;
     659    color: #1d2327;
     660    letter-spacing: -0.2px;
     661}
     662
     663/* Card Body */
     664.revnextwoo-card-body {
     665    padding: 24px;
     666}
     667
     668/* Form Rows */
     669.revnextwoo-form-row {
     670    margin-bottom: 24px;
     671}
     672
     673.revnextwoo-form-row:last-child {
     674    margin-bottom: 0;
     675}
     676
     677/* Labels */
     678.revnextwoo-label {
     679    display: block;
     680    font-size: 14px;
     681    font-weight: 600;
     682    color: #1d2327;
     683    margin-bottom: 8px;
     684}
     685
     686/* Field Wrapper */
     687.revnextwoo-field-wrap {
     688    position: relative;
     689}
     690
     691/* Input Styling */
     692.revnextwoo-input {
     693    width: 100%;
     694    padding: 12px 16px;
     695    font-size: 15px;
     696    line-height: 1.4;
     697    color: #1d2327;
     698    background: #fff;
     699    border: 1.5px solid #d0d5dd;
     700    border-radius: 8px;
     701    transition: all 0.2s ease;
     702    box-sizing: border-box;
     703}
     704
     705.revnextwoo-input:hover {
     706    border-color: #98a2b3;
     707}
     708
     709.revnextwoo-input:focus {
     710    outline: none;
     711    border-color: #667eea;
     712    box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.15);
     713}
     714
     715.revnextwoo-input-number {
     716    font-family: "SF Mono", "Monaco", "Inconsolata", "Fira Mono", "Droid Sans Mono", monospace;
     717    font-size: 16px;
     718    font-weight: 500;
     719}
     720
     721/* Input Groups */
     722.revnextwoo-input-group {
     723    display: flex;
     724    align-items: stretch;
     725}
     726
     727.revnextwoo-input-prefix,
     728.revnextwoo-input-suffix {
     729    display: flex;
     730    align-items: center;
     731    justify-content: center;
     732    padding: 0 14px;
     733    font-size: 15px;
     734    font-weight: 600;
     735    color: #667085;
     736    background: #f9fafb;
     737    border: 1.5px solid #d0d5dd;
     738}
     739
     740.revnextwoo-input-prefix {
     741    border-right: none;
     742    border-radius: 8px 0 0 8px;
     743}
     744
     745.revnextwoo-input-suffix {
     746    border-left: none;
     747    border-radius: 0 8px 8px 0;
     748    color: #f59e0b;
     749    font-size: 18px;
     750}
     751
     752.revnextwoo-input-group .revnextwoo-input {
     753    flex: 1;
     754}
     755
     756.revnextwoo-input-group .revnextwoo-input-currency {
     757    border-radius: 0 8px 8px 0;
     758}
     759
     760.revnextwoo-input-group .revnextwoo-input-rating {
     761    border-radius: 8px 0 0 8px;
     762}
     763
     764/* Badge Input Styling */
     765.revnextwoo-badge-input-wrap {
     766    display: flex;
     767    align-items: center;
     768    gap: 12px;
     769}
     770
     771.revnextwoo-badge-icon {
     772    font-size: 28px;
     773    filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.1));
     774}
     775
     776.revnextwoo-badge-input-wrap .revnextwoo-input {
     777    flex: 1;
     778    max-width: 180px;
     779}
     780
     781/* Badge Colors */
     782.revnextwoo-badge-gold .revnextwoo-input:focus {
     783    border-color: #f59e0b;
     784    box-shadow: 0 0 0 3px rgba(245, 158, 11, 0.15);
     785}
     786
     787.revnextwoo-badge-silver .revnextwoo-input:focus {
     788    border-color: #9ca3af;
     789    box-shadow: 0 0 0 3px rgba(156, 163, 175, 0.15);
     790}
     791
     792.revnextwoo-badge-bronze .revnextwoo-input:focus {
     793    border-color: #d97706;
     794    box-shadow: 0 0 0 3px rgba(217, 119, 6, 0.15);
     795}
     796
     797/* Date Input */
     798.revnextwoo-input-date {
     799    max-width: 220px;
     800    cursor: pointer;
     801}
     802
     803.revnextwoo-input-date::-webkit-calendar-picker-indicator {
     804    cursor: pointer;
     805    opacity: 0.6;
     806    transition: opacity 0.2s;
     807}
     808
     809.revnextwoo-input-date::-webkit-calendar-picker-indicator:hover {
     810    opacity: 1;
     811}
     812
     813/* Field Descriptions */
     814.revnextwoo-field-desc {
     815    margin: 8px 0 0 0;
     816    font-size: 13px;
     817    color: #667085;
     818    line-height: 1.4;
     819}
     820
     821/* Settings Footer */
     822.revnextwoo-settings-footer {
     823    padding-top: 24px;
     824    border-top: 1px solid #e8ecf1;
     825    text-align: right;
     826}
     827
     828/* Submit Button */
     829.revnextwoo-submit-btn {
     830    display: inline-flex !important;
     831    align-items: center;
     832    justify-content: center;
     833    min-width: 160px;
     834    padding: 14px 28px !important;
     835    font-size: 15px !important;
     836    font-weight: 600 !important;
     837    color: #fff !important;
     838    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
     839    border: none !important;
     840    border-radius: 8px !important;
     841    cursor: pointer;
     842    transition: all 0.2s ease;
     843    box-shadow: 0 4px 12px rgba(102, 126, 234, 0.35);
     844    text-shadow: none !important;
     845}
     846
     847.revnextwoo-submit-btn:hover {
     848    transform: translateY(-1px);
     849    box-shadow: 0 6px 20px rgba(102, 126, 234, 0.45);
     850    background: linear-gradient(135deg, #5a6fd6 0%, #6a4190 100%) !important;
     851}
     852
     853.revnextwoo-submit-btn:active {
     854    transform: translateY(0);
     855    box-shadow: 0 2px 8px rgba(102, 126, 234, 0.35);
     856}
     857
     858.revnextwoo-submit-btn:focus {
     859    outline: none !important;
     860    box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.3), 0 4px 12px rgba(102, 126, 234, 0.35) !important;
     861}
     862
     863/* Responsive Design for Settings Page */
     864@media screen and (max-width: 1024px) {
     865    .revnextwoo-settings-grid {
     866        flex-direction: column;
     867    }
     868
     869    .revnextwoo-settings-grid>.revnextwoo-settings-card:not(.revnextwoo-settings-card-full) {
     870        flex: 1 1 100%;
     871        min-width: 100%;
     872    }
     873}
     874
     875@media screen and (max-width: 782px) {
     876    .revnextwoo-settings-wrap {
     877        margin: 10px 10px 10px 0;
     878    }
     879
     880    .revnextwoo-settings-header {
     881        padding: 20px 20px;
     882        border-radius: 10px 10px 0 0;
     883    }
     884
     885    .revnextwoo-settings-title {
     886        font-size: 20px;
     887        flex-direction: column;
     888        align-items: flex-start;
     889        gap: 10px;
     890    }
     891
     892    .revnextwoo-settings-desc {
     893        font-size: 14px;
     894    }
     895
     896    .revnextwoo-settings-form {
     897        padding: 20px;
     898        border-radius: 0 0 10px 10px;
     899    }
     900
     901    .revnextwoo-settings-grid {
     902        gap: 16px;
     903    }
     904
     905    .revnextwoo-card-header {
     906        padding: 14px 18px;
     907    }
     908
     909    .revnextwoo-card-body {
     910        padding: 18px;
     911    }
     912
     913    .revnextwoo-badge-input-wrap .revnextwoo-input {
     914        max-width: 100%;
     915    }
     916
     917    .revnextwoo-settings-footer {
     918        text-align: center;
     919    }
     920
     921    .revnextwoo-submit-btn {
     922        width: 100%;
     923    }
     924}
     925
     926@media screen and (max-width: 480px) {
     927    .revnextwoo-settings-header {
     928        padding: 16px;
     929    }
     930
     931    .revnextwoo-settings-form {
     932        padding: 16px;
     933    }
     934
     935    .revnextwoo-card-header,
     936    .revnextwoo-card-body {
     937        padding: 14px;
     938    }
     939
     940    .revnextwoo-input {
     941        padding: 10px 14px;
     942        font-size: 14px;
     943    }
     944
     945    .revnextwoo-label {
     946        font-size: 13px;
     947    }
     948
     949    .revnextwoo-badge-icon {
     950        font-size: 24px;
     951    }
     952}
  • review-next-for-woocommerce/trunk/admin/js/revnextwoo-admin.js

    r3315734 r3457384  
    1 jQuery(document).ready(function($) {
    2    
    3         $('.revnextwoo-color-field').wpColorPicker();
    4 
    5         $('.revnextwoo-advanced-setting-wrapper a').on('click', function (e) {
    6             e.preventDefault();
    7             $('.nav-tab').removeClass('nav-tab-active');
    8             $(this).addClass('nav-tab-active');
    9             $('.tab-content').hide();
    10             $($(this).attr('href')).show();
    11         });
    12 
    13          // Open the "View" popup
    14         $('.view-link').click(function(e) {
    15             e.preventDefault();
    16             var commentId = $(this).data('comment-id');
    17             var commentContent = $('#comment-content-' + commentId).html();
    18 
    19             $('.popup-content').html(commentContent);
    20             $('.popup').show();
     1jQuery(document).ready(function ($) {
     2    // Media uploader for avatar images
     3    $(document).on('click', '.revnextwoo-upload-button', function (e) {
     4        e.preventDefault();
     5
     6        var button = $(this);
     7        var wrapper = button.closest('.image-upload-wrapper');
     8        var inputField = wrapper.find('input[type="hidden"]');
     9        var previewContainer = wrapper.find('.avatar-preview');
     10        var previewImage = wrapper.find('.avatar-preview-image');
     11
     12        // Create the media frame
     13        var frame = wp.media({
     14            title: button.data('uploader_title') || 'Select Avatar',
     15            button: {
     16                text: button.data('uploader_button_text') || 'Use this avatar'
     17            },
     18            library: {
     19                type: 'image'
     20            },
     21            multiple: false
    2122        });
    2223
    23         // Close the popup
    24         $('.popup').click(function() {
    25             $(this).hide();
     24        // When an image is selected
     25        frame.on('select', function () {
     26            var attachment = frame.state().get('selection').first().toJSON();
     27
     28            // Update the hidden input with the image URL
     29            inputField.val(attachment.url).trigger('change');
     30
     31            // Update the preview
     32            previewImage.html('<img src="' + attachment.url + '" style="max-width: 100%; max-height: 100%; object-fit: cover;" />');
     33
     34            // Show the preview and hide the upload button
     35            previewContainer.removeClass('hidden');
     36            button.addClass('hidden');
     37
     38            // Show the remove button if hidden
     39            wrapper.find('.remove-avatar').show();
    2640        });
    2741
    28         // Prevent closing when clicking inside the popup
    29         $('.popup-content').click(function(e) {
    30             e.stopPropagation();
    31         });
     42        // Open the media frame
     43        frame.open();
     44    });
     45
     46    // Remove avatar
     47    $(document).on('click', '.remove-avatar', function (e) {
     48        e.preventDefault();
     49
     50        var wrapper = $(this).closest('.image-upload-wrapper');
     51        var inputField = wrapper.find('input[type="hidden"]');
     52        var previewContainer = wrapper.find('.avatar-preview');
     53        var previewImage = wrapper.find('.avatar-preview-image');
     54        var uploadButton = wrapper.find('.revnextwoo-upload-button');
     55
     56        // Clear the input
     57        inputField.val('').trigger('change');
     58
     59        // Reset the preview
     60        previewImage.html('<span style="color: #999; font-size: 12px;">' + wp.i18n.__('No image selected', 'review-next-for-woocommerce') + '</span>');
     61
     62        // Show upload button and hide preview
     63        uploadButton.removeClass('hidden');
     64        previewContainer.addClass('hidden');
     65
     66        // Hide the remove button
     67        $(this).hide();
     68    });
     69
     70    // Handle initial state on page load
     71    $('.image-upload-wrapper').each(function () {
     72        var wrapper = $(this);
     73        var inputField = wrapper.find('input[type="hidden"]');
     74        var previewContainer = wrapper.find('.avatar-preview');
     75        var uploadButton = wrapper.find('.revnextwoo-upload-button');
     76        var removeButton = wrapper.find('.remove-avatar');
     77
     78        if (inputField.val()) {
     79            // If there's a value, show the preview and remove button, hide upload button
     80            previewContainer.removeClass('hidden');
     81            uploadButton.addClass('hidden');
     82            removeButton.show();
     83        } else {
     84            // If no value, show upload button, hide preview and remove button
     85            previewContainer.addClass('hidden');
     86            uploadButton.removeClass('hidden');
     87            removeButton.hide();
     88        }
     89    });
     90
     91
     92
     93    // wpColorPicker logic start here
     94    $('.revnextwoo-color-field').wpColorPicker();
     95
     96    $('.revnextwoo-advanced-setting-wrapper a').on('click', function (e) {
     97        e.preventDefault();
     98        $('.nav-tab').removeClass('nav-tab-active');
     99        $(this).addClass('nav-tab-active');
     100        $('.tab-content').hide();
     101        $($(this).attr('href')).show();
     102    });
     103
     104    // Open the "View" popup
     105    $('.view-link').click(function (e) {
     106        e.preventDefault();
     107        var commentId = $(this).data('comment-id');
     108        var commentContent = $('#comment-content-' + commentId).html();
     109
     110        $('.popup-content').html(commentContent);
     111        $('.popup').show();
     112    });
     113
     114    // Close the popup
     115    $('.popup').click(function () {
     116        $(this).hide();
     117    });
     118
     119    // Prevent closing when clicking inside the popup
     120    $('.popup-content').click(function (e) {
     121        e.stopPropagation();
     122    });
    32123
    33124
    34125    //    question reply
    35         $(document).on('click', '.vim-r-question-reply', function() {
    36             // Get comment and post IDs from data attributes
    37             var commentId = $(this).data('comment-id');
    38             var postId = $(this).data('post-id');
    39    
    40             // Find the table row of the clicked 'Reply' button
    41             var $row = $(this).closest('tr');
    42    
    43             // Check if a reply form already exists and hide it
    44             if ($row.next().hasClass('reply-form-row')) {
    45                 $row.next().remove();
    46             } else {
    47                 // Create a reply form
    48                 var replyForm = '<tr class="reply-form-row">' +
    49                     '<td colspan="6" class="colspanchange">' +
    50                     '<fieldset class="comment-reply">' +
    51                     '<legend>' +
    52                     '<span class="hidden" id="editlegend" style="display: none;">Edit question</span>' +
    53                     '<span class="hidden" id="replyhead" style="display: inline;">edit to question</span>' +
    54                     '<span class="hidden" id="addhead" style="display: none;">Add new question</span>' +
    55                     '</legend>' +
    56                     '<div id="replycontainer">' +
    57                     '<label for="replycontent" class="screen-reader-text">question</label>' +
    58                     '<div id="wp-replycontent-wrap" class="wp-core-ui wp-editor-wrap html-active">' +
    59                     '<link rel="stylesheet" id="editor-buttons-css" href="http://localhost/review/wp-includes/css/editor.css?ver=6.3.2" media="all">' +
    60                     '<div id="wp-replycontent-editor-container" class="wp-editor-container">' +
    61                     '<div id="qt_replycontent_toolbar" class="quicktags-toolbar hide-if-no-js">' +
    62                     '<!-- Quicktags buttons for text formatting -->' +
    63                     '</div>' +
    64                     '<textarea class="wp-editor-area" rows="5" cols="40" name="replycontent" id="qustion-reply-content"></textarea>' +
    65                     '</div>' +
    66                     '</div>' +
    67                     '</div>' +
    68                     '</fieldset>' +
    69                     '<div id="replysubmit" class="submit">' +
    70                     '<p class="reply-submit-buttons">' +
    71                     '<button type="button" class="save button button-primary question-submit-reply" data-comment-id="' + commentId + '" data-post-id="' + postId + '" >Submit Reply</button>' +
    72                     '<button type="button" class="question-cancel button">Cancel</button>' +
    73                     '<span class="waiting spinner"></span>' +
    74                     '</p>' +
    75                     '<div class="notice notice-error notice-alt inline hidden">' +
    76                     '<p class="error"></p>' +
    77                     '</div>' +
    78                     '</div>' +
    79                     '</td>' +
    80                     '</tr>';
    81    
    82                 // Insert the reply form after the current row
    83                 $row.after(replyForm);
    84    
    85                 // Initialize quicktags for the textarea
    86                 quicktags({ id: "replycontent" });
    87             }
    88         });
    89    
    90        // Event delegation for the "Submit Reply" button click
    91         $(document).on('click', '.question-submit-reply', function() {
    92             var questionId = $(this).data('comment-id');
    93             var replyContent = $('#qustion-reply-content').val();
    94             var postId = $(this).data('post-id');
    95             // Create an object to hold the data you want to send via AJAX
    96             var data = {
    97                 action: 'revnextwoo_submit_question_reply',
    98                 comment_id: questionId,
    99                 post_id: postId,
    100                 reply_content: replyContent
    101             };
    102             var that = this;
    103             // Send an AJAX POST request to the server
    104             $.ajax({
    105                 url: ajaxurl,
    106                 type: 'POST',
    107                 data: data,
    108                 success: function(response) {
    109                     if(response.success){
     126    $(document).on('click', '.vim-r-question-reply', function () {
     127        // Get comment and post IDs from data attributes
     128        var commentId = $(this).data('comment-id');
     129        var postId = $(this).data('post-id');
     130
     131        // Find the table row of the clicked 'Reply' button
     132        var $row = $(this).closest('tr');
     133
     134        // Check if a reply form already exists and hide it
     135        if ($row.next().hasClass('reply-form-row')) {
     136            $row.next().remove();
     137        } else {
     138            // Create a reply form
     139            var replyForm = '<tr class="reply-form-row">' +
     140                '<td colspan="6" class="colspanchange">' +
     141                '<fieldset class="comment-reply">' +
     142                '<legend>' +
     143                '<span class="hidden" id="editlegend" style="display: none;">Edit question</span>' +
     144                '<span class="hidden" id="replyhead" style="display: inline;">edit to question</span>' +
     145                '<span class="hidden" id="addhead" style="display: none;">Add new question</span>' +
     146                '</legend>' +
     147                '<div id="replycontainer">' +
     148                '<label for="replycontent" class="screen-reader-text">question</label>' +
     149                '<div id="wp-replycontent-wrap" class="wp-core-ui wp-editor-wrap html-active">' +
     150                '<link rel="stylesheet" id="editor-buttons-css" href="http://localhost/review/wp-includes/css/editor.css?ver=6.3.2" media="all">' +
     151                '<div id="wp-replycontent-editor-container" class="wp-editor-container">' +
     152                '<div id="qt_replycontent_toolbar" class="quicktags-toolbar hide-if-no-js">' +
     153                '<!-- Quicktags buttons for text formatting -->' +
     154                '</div>' +
     155                '<textarea class="wp-editor-area" rows="5" cols="40" name="replycontent" id="qustion-reply-content"></textarea>' +
     156                '</div>' +
     157                '</div>' +
     158                '</div>' +
     159                '</fieldset>' +
     160                '<div id="replysubmit" class="submit">' +
     161                '<p class="reply-submit-buttons">' +
     162                '<button type="button" class="save button button-primary question-submit-reply" data-comment-id="' + commentId + '" data-post-id="' + postId + '" >Submit Reply</button>' +
     163                '<button type="button" class="question-cancel button">Cancel</button>' +
     164                '<span class="waiting spinner"></span>' +
     165                '</p>' +
     166                '<div class="notice notice-error notice-alt inline hidden">' +
     167                '<p class="error"></p>' +
     168                '</div>' +
     169                '</div>' +
     170                '</td>' +
     171                '</tr>';
     172
     173            // Insert the reply form after the current row
     174            $row.after(replyForm);
     175
     176            // Initialize quicktags for the textarea
     177            quicktags({ id: "replycontent" });
     178        }
     179    });
     180
     181    // Event delegation for the "Submit Reply" button click
     182    $(document).on('click', '.question-submit-reply', function () {
     183        var questionId = $(this).data('comment-id');
     184        var replyContent = $('#qustion-reply-content').val();
     185        var postId = $(this).data('post-id');
     186        // Create an object to hold the data you want to send via AJAX
     187        var data = {
     188            action: 'revnextwoo_submit_question_reply',
     189            comment_id: questionId,
     190            post_id: postId,
     191            reply_content: replyContent
     192        };
     193        var that = this;
     194        // Send an AJAX POST request to the server
     195        $.ajax({
     196            url: ajaxurl,
     197            type: 'POST',
     198            data: data,
     199            success: function (response) {
     200                if (response.success) {
    110201                    // Handle the server response, e.g., display a success message
    111202                    console.log('AJAX response:', response.success);
    112203                    // After processing, you can remove the reply form row
    113204                    $(that).closest('tr.reply-form-row').remove();
    114                     }
    115                    
    116                 },
    117                 error: function(error) {
    118                     // Handle any errors that occur during the AJAX request
    119                     console.error('AJAX request error', error);
    120                 },
    121             });
     205                }
     206
     207            },
     208            error: function (error) {
     209                // Handle any errors that occur during the AJAX request
     210                console.error('AJAX request error', error);
     211            },
    122212        });
    123 
    124         $(document).on('click', '.question-cancel', function() {
    125             // Remove the reply form
    126             $(this).closest('tr.reply-form-row').remove();
    127         });
    128 
    129 
    130         $(document).on('click', '[data-action="question_edit"]', function() {
    131             // Get comment and post IDs from data attributes
    132             var commentId = $(this).data('comment-id');
    133             var postId = $(this).data('post-id');
    134    
    135             // Find the table row of the clicked 'Quick Edit' button
    136             var $row = $(this).closest('tr');
    137 
    138             // Get the question content from the row
    139             var questionContent = $row.find('#question_content').text();
    140             console.log(questionContent);
    141             var user_name = $row.find('#username').text();
    142             var email = $row.find('#user-email').text();
    143             var ip = $row.find('#user-ip').text();
    144 
    145            
    146             if ($row.next().hasClass('update-question-form-row')) {
    147                 $row.next().remove();
    148             } else {
    149                 // Create a reply form
    150                 var replyForm = '<tr class="update-question-form-row">' +
    151                     '<td colspan="6" class="colspanchange">' +
    152                     '<fieldset class="update_question">' +
    153                     '<legend>' +
    154                     '<span class="hidden" id="editlegend" style="display: none;">Edit Question</span>' +
    155                     '<span class="hidden" id="replyhead" style="display: inline;">Edit to Question</span>' +
    156                     '<span class="hidden" id="addhead" style="display: none;">Update Question</span>' +
    157                     '</legend>' +
    158                     '<div id="container">' +
    159                     '<label for="replycontent" class="screen-reader-text">Question</label>' +
    160                     '<div id="wp-replycontent-wrap" class="wp-core-ui wp-editor-wrap html-active">' +
    161                     '<link rel="stylesheet" id="editor-buttons-css" href="http://localhost/review/wp-includes/css/editor.css?ver=6.3.2" media="all">' +
    162                     '<div id="wp-replycontent-editor-container" class="wp-editor-container">' +
    163                     '<div id="qt_replycontent_toolbar" class="quicktags-toolbar hide-if-no-js">' +
    164                     '<!-- Quicktags buttons for text formatting -->' +
    165                     '</div>' +
    166                     '<textarea class="wp-editor-area" rows="5" cols="40"  name="update-qustion-content" id="update-qustion-content">' + questionContent + '</textarea>' +
    167                     '</div>' +
    168                     '</div>' +
    169                     '</div>' +
    170                     '<div id="edithead" style="">' +
    171                     '<div class="inside">' +
    172                     '<label for="author-name">Name</label>' +
    173                     '<input type="text" name="newcomment_author" value="' + user_name + '" size="50" value="" id="author-name">' +
    174                     '</div>' +
    175                     '<div class="inside">' +
    176                     '<label for="author-email">Email</label>' +
    177                     '<input type="text" name="newcomment_author_email" value="' + email + '" size="50" value="" id="author-email">' +
    178                     '</div>' +
    179                     '<div class="inside">' +
    180                     '<label for="author-url">URL</label>' +
    181                     '<input type="text" id="author-url" name="newcomment_author_url" value="' + ip + '" class="code" size="50" >' +
    182                     '</div>' +
    183                     '</div>' +
    184                     '</fieldset>' +
    185                     '<div id="replysubmit" class="submit">' +
    186                     '<p class="reply-submit-buttons">' +
    187                     '<button type="button" class="save button button-primary update-question" data-comment-id="' + commentId + '">Update Question</button>' +
    188                     '<button type="button" class="update-question-cancel button">Cancel</button>' +
    189                     '<span class="waiting spinner"></span>' +
    190                     '</p>' +
    191                     '<div class="notice notice-error notice-alt inline hidden">' +
    192                     '<p class="error"></p>' +
    193                     '</div>' +
    194                     '</div>' +
    195                     '</td>' +
    196                     '</tr>';
    197    
    198                 // Insert the reply form after the current row
    199                 $row.after(replyForm);
    200    
    201                 // Initialize quicktags for the textarea
    202                 quicktags({ id: "replycontent" });
     213    });
     214
     215    $(document).on('click', '.question-cancel', function () {
     216        // Remove the reply form
     217        $(this).closest('tr.reply-form-row').remove();
     218    });
     219
     220
     221    $(document).on('click', '[data-action="question_edit"]', function () {
     222        // Get comment and post IDs from data attributes
     223        var commentId = $(this).data('comment-id');
     224        var postId = $(this).data('post-id');
     225
     226        // Find the table row of the clicked 'Quick Edit' button
     227        var $row = $(this).closest('tr');
     228
     229        // Get the question content from the row
     230        var questionContent = $row.find('#question_content').text();
     231        console.log(questionContent);
     232        var user_name = $row.find('#username').text();
     233        var email = $row.find('#user-email').text();
     234        var ip = $row.find('#user-ip').text();
     235
     236
     237        if ($row.next().hasClass('update-question-form-row')) {
     238            $row.next().remove();
     239        } else {
     240            // Create a reply form
     241            var replyForm = '<tr class="update-question-form-row">' +
     242                '<td colspan="6" class="colspanchange">' +
     243                '<fieldset class="update_question">' +
     244                '<legend>' +
     245                '<span class="hidden" id="editlegend" style="display: none;">Edit Question</span>' +
     246                '<span class="hidden" id="replyhead" style="display: inline;">Edit to Question</span>' +
     247                '<span class="hidden" id="addhead" style="display: none;">Update Question</span>' +
     248                '</legend>' +
     249                '<div id="container">' +
     250                '<label for="replycontent" class="screen-reader-text">Question</label>' +
     251                '<div id="wp-replycontent-wrap" class="wp-core-ui wp-editor-wrap html-active">' +
     252                '<link rel="stylesheet" id="editor-buttons-css" href="http://localhost/review/wp-includes/css/editor.css?ver=6.3.2" media="all">' +
     253                '<div id="wp-replycontent-editor-container" class="wp-editor-container">' +
     254                '<div id="qt_replycontent_toolbar" class="quicktags-toolbar hide-if-no-js">' +
     255                '<!-- Quicktags buttons for text formatting -->' +
     256                '</div>' +
     257                '<textarea class="wp-editor-area" rows="5" cols="40"  name="update-qustion-content" id="update-qustion-content">' + questionContent + '</textarea>' +
     258                '</div>' +
     259                '</div>' +
     260                '</div>' +
     261                '<div id="edithead" style="">' +
     262                '<div class="inside">' +
     263                '<label for="author-name">Name</label>' +
     264                '<input type="text" name="newcomment_author" value="' + user_name + '" size="50" value="" id="author-name">' +
     265                '</div>' +
     266                '<div class="inside">' +
     267                '<label for="author-email">Email</label>' +
     268                '<input type="text" name="newcomment_author_email" value="' + email + '" size="50" value="" id="author-email">' +
     269                '</div>' +
     270                '<div class="inside">' +
     271                '<label for="author-url">URL</label>' +
     272                '<input type="text" id="author-url" name="newcomment_author_url" value="' + ip + '" class="code" size="50" >' +
     273                '</div>' +
     274                '</div>' +
     275                '</fieldset>' +
     276                '<div id="replysubmit" class="submit">' +
     277                '<p class="reply-submit-buttons">' +
     278                '<button type="button" class="save button button-primary update-question" data-comment-id="' + commentId + '">Update Question</button>' +
     279                '<button type="button" class="update-question-cancel button">Cancel</button>' +
     280                '<span class="waiting spinner"></span>' +
     281                '</p>' +
     282                '<div class="notice notice-error notice-alt inline hidden">' +
     283                '<p class="error"></p>' +
     284                '</div>' +
     285                '</div>' +
     286                '</td>' +
     287                '</tr>';
     288
     289            // Insert the reply form after the current row
     290            $row.after(replyForm);
     291
     292            // Initialize quicktags for the textarea
     293            quicktags({ id: "replycontent" });
     294        }
     295    });
     296
     297    // question update
     298    $(document).on('click', '.update-question', function () {
     299        // Get the updated values from the form fields
     300        var updatedQuestionContent = $('#update-qustion-content').val();
     301        var updatedUserName = $('#author-name').val();
     302        var updatedEmail = $('#author-email').val();
     303        var updatedIP = $('#author-url').val();
     304        // Create an object to hold the data you want to send via AJAX
     305        var data = {
     306            action: 'revnextwoo_question_update',
     307            commentId: $(this).data('comment-id'),
     308            questionContent: updatedQuestionContent,
     309            userName: updatedUserName,
     310            email: updatedEmail,
     311            ip: updatedIP
     312        }
     313        var that = this;
     314
     315        // Example AJAX request:
     316        $.ajax({
     317            url: ajaxurl,
     318            type: 'POST',
     319            data: data,
     320            success: function (response) {
     321                // Handle the success response from the server
     322                console.log('Data updated successfully:', response);
     323                if (response.data) {
     324                    $(`.question_content_${response.comment_id}`).text(response.data);
     325                    $(that).closest('tr').remove();
     326                }
     327
     328                // Reference to the current row
     329
     330            },
     331            error: function (error) {
     332                // Handle the error from the server
     333                console.error('Error updating data:', error);
    203334            }
    204335        });
    205 
    206         // question update
    207         $(document).on('click', '.update-question', function() {
    208             // Get the updated values from the form fields
    209             var updatedQuestionContent = $('#update-qustion-content').val();
    210             var updatedUserName = $('#author-name').val();
    211             var updatedEmail = $('#author-email').val();
    212             var updatedIP = $('#author-url').val();
    213             // Create an object to hold the data you want to send via AJAX
    214             var data = {
    215                 action: 'revnextwoo_question_update',
    216                 commentId: $(this).data('comment-id'),
    217                 questionContent: updatedQuestionContent,
    218                 userName: updatedUserName,
    219                 email: updatedEmail,
    220                 ip: updatedIP
    221             }           
    222             var that = this;
    223            
    224             // Example AJAX request:
    225             $.ajax({
    226                 url: ajaxurl,
    227                 type: 'POST',
    228                 data: data,
    229                 success: function(response) {
    230                     // Handle the success response from the server
    231                     console.log('Data updated successfully:', response);
    232                     if(response.data){
    233                         $(`.question_content_${response.comment_id}`).text(response.data);
    234                         $(that).closest('tr').remove();
     336    });
     337
     338    $(document).on('click', '.update-question-cancel', function () {
     339        // Remove the update-question-form
     340        $(this).closest('tr.update-question-form-row').remove();
     341    });
     342
     343    // Event handler for the "status_update_question" link
     344    $('.status_update_question').on('click', function (e) {
     345        e.preventDefault();
     346
     347        // Get the comment ID and action from the data attributes
     348        var questionID = $(this).data('question-id');
     349        var action = $(this).data('action');
     350
     351        // Reference to the current row
     352        var $row = $(this).closest('tr');
     353
     354        // Call the handleCommentAction function
     355        handleCommentAction(action, questionID, $row);
     356    });
     357
     358    // Function to handle unapprove, spam, and trash actions
     359    function handleCommentAction(action, questionID, $row) {
     360        // Confirm the action (you can customize this if needed)
     361        if (!confirm('Are you sure you want to ' + action + ' this comment?')) {
     362            return;
     363        }
     364        var comment_status = action;
     365
     366        // Create an object to pass to the server
     367        var data = {
     368            action: 'revnextwoo_status_update_question',
     369            question_id: questionID,
     370            status: comment_status,
     371        };
     372
     373        // Send the AJAX request
     374        $.ajax({
     375            url: ajaxurl,
     376            type: 'POST',
     377            data: data,
     378            success: function (response) {
     379                if (response.success) {
     380                    // Handle the server response, e.g., display a success message
     381                    console.log('Comment ' + action + 'ed successfully:', response.success);
     382
     383                    // Remove the row if the action is "Trash" or "Spam"
     384                    if (action === 'Trash' || action === 'Spam') {
     385                        $row.addClass('slide-left');
     386
     387                        // After the animation, remove the row
     388                        $row.on('transitionend', function () {
     389                            $(this).remove();
     390                        });
     391                    } else {
     392                        var change_status = action === 'Approved' ? 'Unapproved' : 'Approved';
     393
     394                        // Find the span element by class and update its data-action and text
     395                        const $findSpan = $row.find(`.${action} a`);
     396                        $findSpan.attr('data-action', change_status).text(change_status);
     397                        $row.find(`.${action}`).removeClass(action).addClass(change_status);
     398
    235399                    }
    236                    
    237                      // Reference to the current row
    238            
    239                 },
    240                 error: function(error) {
    241                     // Handle the error from the server
    242                     console.error('Error updating data:', error);
     400                } else {
     401                    console.error('Comment ' + action + ' failed.');
    243402                }
    244             });
     403            },
     404            error: function (error) {
     405                // Handle any errors that occur during the AJAX request
     406                console.error('AJAX request error', error);
     407            }
    245408        });
    246 
    247         $(document).on('click', '.update-question-cancel', function() {
    248             // Remove the update-question-form
    249             $(this).closest('tr.update-question-form-row').remove();
    250         });
    251 
    252         // Event handler for the "status_update_question" link
    253         $('.status_update_question').on('click', function (e) {
    254             e.preventDefault();
    255 
    256             // Get the comment ID and action from the data attributes
    257             var questionID = $(this).data('question-id');
    258             var action = $(this).data('action');
    259 
    260             // Reference to the current row
    261             var $row = $(this).closest('tr');
    262 
    263             // Call the handleCommentAction function
    264             handleCommentAction(action, questionID, $row);
    265         });
    266 
    267         // Function to handle unapprove, spam, and trash actions
    268         function handleCommentAction(action, questionID, $row) {
    269             // Confirm the action (you can customize this if needed)
    270             if (!confirm('Are you sure you want to ' + action + ' this comment?')) {
    271                 return;
    272             }
    273             var comment_status = action;
    274 
    275             // Create an object to pass to the server
    276             var data = {
    277                 action: 'revnextwoo_status_update_question',
    278                 question_id: questionID,
    279                 status: comment_status,
    280             };
    281 
    282             // Send the AJAX request
     409    }
     410
     411
     412
     413    $('.view-reply').on('click', function (e) {
     414        e.preventDefault();
     415        var questionId = $(this).data('question-id');
     416        var data = {
     417            action: 'revnextwoo_get_question_replies',
     418            question_id: questionId,
     419        };
     420        var that = this;
     421        var newRow = $(that).closest('tr').next('.replies-row');
     422
     423        if (newRow.length) {
     424            // Replies row already exists, toggle its visibility
     425            newRow.toggle();
     426        } else {
     427            // Replies row doesn't exist, fetch and append replies
    283428            $.ajax({
    284429                url: ajaxurl,
     
    286431                data: data,
    287432                success: function (response) {
    288                     if (response.success) {
    289                         // Handle the server response, e.g., display a success message
    290                         console.log('Comment ' + action + 'ed successfully:', response.success);
    291                        
    292                         // Remove the row if the action is "Trash" or "Spam"
    293                         if (action === 'Trash' || action === 'Spam') {
    294                             $row.addClass('slide-left');
    295 
    296                             // After the animation, remove the row
    297                             $row.on('transitionend', function () {
    298                                 $(this).remove();
    299                             });
    300                         }else {
    301                             var change_status = action === 'Approved' ? 'Unapproved' : 'Approved';
    302 
    303                             // Find the span element by class and update its data-action and text
    304                             const $findSpan = $row.find(`.${action} a`);
    305                             $findSpan.attr('data-action', change_status).text(change_status);
    306                             $row.find(`.${action}`).removeClass(action).addClass(change_status);
    307                            
    308                         }
    309                     } else {
    310                         console.error('Comment ' + action + ' failed.');
    311                     }
    312                 },
    313                 error: function (error) {
    314                     // Handle any errors that occur during the AJAX request
    315                     console.error('AJAX request error', error);
    316                 }
    317             });
    318         }
    319 
    320        
    321        
    322         $('.view-reply').on('click', function(e) {
    323             e.preventDefault();
    324             var questionId = $(this).data('question-id');
    325             var data = {
    326                 action: 'revnextwoo_get_question_replies',
    327                 question_id: questionId,
    328             };
    329             var that = this;
    330             var newRow = $(that).closest('tr').next('.replies-row');
    331    
    332             if (newRow.length) {
    333                 // Replies row already exists, toggle its visibility
    334                 newRow.toggle();
    335             } else {
    336                 // Replies row doesn't exist, fetch and append replies
    337                 $.ajax({
    338                     url: ajaxurl,
    339                     type: 'POST',
    340                     data: data,
    341                     success: function(response) {
    342                         // Append the replies in a new row below the clicked "View Reply" link
    343                         newRow = $('<tr class="replies-row"><td colspan="6">'+
     433                    // Append the replies in a new row below the clicked "View Reply" link
     434                    newRow = $('<tr class="replies-row"><td colspan="6">' +
    344435                        '<span class="hidden" id="replyhead" style="display: inline;">Question reply</span>' +
    345436                        '<div id="wp-replycontent-wrap" class="wp-core-ui wp-editor-wrap html-active">' +
     
    352443                        '</div>' +
    353444                        '</div>' +
    354                         '</div>' + 
     445                        '</div>' +
    355446                        '</td></tr>');
    356                         $(that).closest('tr').after(newRow);
    357                     }
    358                 });
    359             }
    360         });
    361 
    362 
    363 
    364         $('#sendEmailButton').on('click', function() {
    365             var checkboxes = document.querySelectorAll('input[name="order_id[]"]:checked');
    366             if (checkboxes.length > 0) {
    367                 var orderIds = [];
    368                 checkboxes.forEach(function (checkbox) {
    369                     var orderId = checkbox.value;
    370                     orderIds.push(orderId);
    371                 });
    372 
    373                 // AJAX request to send emails
    374                 var data = {
    375                     action: 'revnextwoo_send_emails',
    376                     order_ids: orderIds,
    377                 };
    378 
    379                // Use AJAX to trigger the server-side action
    380                     $.ajax({
    381                         url: ajaxurl,
    382                         type: 'POST',
    383                         data: data,
    384                         success: function(response) {
    385                             // Display the response (e.g., success message)
    386                             alert(response);
    387                         },
    388                         error: function(error) {
    389                             // Handle errors
    390                             console.error(error);
    391                         }
    392                     });
     447                    $(that).closest('tr').after(newRow);
    393448                }
    394 
    395449            });
    396 
    397    
    398 
    399 
    400 
    401 
    402 
     450        }
     451    });
     452
     453
     454
     455    $('#sendEmailButton').on('click', function () {
     456        var checkboxes = document.querySelectorAll('input[name="order_id[]"]:checked');
     457        if (checkboxes.length > 0) {
     458            var orderIds = [];
     459            checkboxes.forEach(function (checkbox) {
     460                var orderId = checkbox.value;
     461                orderIds.push(orderId);
     462            });
     463
     464            // AJAX request to send emails
     465            var data = {
     466                action: 'revnextwoo_send_emails',
     467                order_ids: orderIds,
     468            };
     469
     470            // Use AJAX to trigger the server-side action
     471            $.ajax({
     472                url: ajaxurl,
     473                type: 'POST',
     474                data: data,
     475                success: function (response) {
     476                    // Display the response (e.g., success message)
     477                    alert(response);
     478                },
     479                error: function (error) {
     480                    // Handle errors
     481                    console.error(error);
     482                }
     483            });
     484        }
     485
     486    });
    403487
    404488});
  • review-next-for-woocommerce/trunk/assets/css/revnextwoo-styles.css

    r3315734 r3457384  
    1 /* Review Plugin Dynamic Styles */
     1.revnextwoo_text_box {
     2    max-width: 100%;
     3    word-wrap: break-word;
     4    overflow: hidden;
     5    position: relative;
     6    padding: 2px 10px;
     7}
     8
     9.revnextwoo_review_text {
     10    white-space: normal;
     11    margin: 0;
     12    line-height: 1.5;
     13}
     14
     15.revnextwoo_review_text.truncated {
     16    max-height: 4.5em;
     17    /* 3 lines of text (1.5em line-height * 3) */
     18    overflow: hidden;
     19    display: -webkit-box;
     20    -webkit-line-clamp: 3;
     21    -webkit-box-orient: vertical;
     22}
     23
     24.revnextwoo_read_more {
     25    color: #2271b1;
     26    cursor: pointer;
     27    font-size: 0.9em;
     28    display: inline-block;
     29    margin-top: 5px;
     30}
     31
     32
     33
     34.revnextwoo_like_btn,
     35.revnextwoo_report_btn {
     36    background: #f5f6fa;
     37    border: none;
     38    border-radius: 7px;
     39    padding: 6px 13px;
     40    color: #555;
     41    font-size: 0.98rem;
     42    cursor: pointer;
     43    transition: background 0.15s;
     44    display: flex;
     45    align-items: center;
     46    gap: 5px;
     47    z-index: 10;
     48}
     49
     50/*
     51.revnextwoo_like_count,
     52.revnextwoo_dislike_count {
     53    font-weight: 600;
     54    margin-left: 2px;
     55}*/
     56
     57
     58
     59
     60.revnextwoo_review_actions {
     61    display: flex;
     62    gap: 16px;
     63    width: 100%;
     64    margin-top: auto;
     65    padding: 12px 0px;
     66    border: none;
     67    color: #e0e0e0;
     68    font-size: 1rem;
     69}
    270
    371/* Rating Icons */
     
    674}
    775
    8 .write-revnextwoo-wrapper .rating_field:checked + .icon,
     76.write-revnextwoo-wrapper .rating_field:checked+.icon,
    977.write-revnextwoo-wrapper .rating-group .icon:hover {
    1078    color: var(--revnextwoo-rating-icon-active-color, #ffc700);
     
    1987}
    2088
    21 #full-stars .rating__input--none:checked + .rating__label .rating__icon--none {
    22     color: var(--revnextwoo-rating-icon-active-color, #ffc700);
    23 }
    24 
    25 #full-stars .rating__input:checked ~ .rating__label .rating__icon--star {
     89#full-stars .rating__input--none:checked+.rating__label .rating__icon--none {
     90    color: var(--revnextwoo-rating-icon-active-color, #ffc700);
     91}
     92
     93#full-stars .rating__input:checked~.rating__label .rating__icon--star {
    2694    color: var(--revnextwoo-rating-icon-color, #cccccc);
    2795}
     
    32100}
    33101
    34 #full-stars .rating__input:hover ~ .rating__label .rating__icon--star,
    35 #full-stars .rating__input:hover ~ .rating__label--half .rating__icon--star {
     102#full-stars .rating__input:hover~.rating__label .rating__icon--star,
     103#full-stars .rating__input:hover~.rating__label--half .rating__icon--star {
    36104    color: var(--revnextwoo-rating-icon-color, #cccccc);
    37105}
    38106
    39 #full-stars .rating__input--none:hover + .rating__label .rating__icon--none {
     107#full-stars .rating__input--none:hover+.rating__label .rating__icon--none {
    40108    color: var(--revnextwoo-rating-icon-active-color, #ffc700);
    41109}
     
    68136}
    69137
    70 .revnextwoo_product_show .revnextwoo-item,
     138.revnextwoo_review_item {
     139    background-color: var(--revnextwoo-item-bg, #ffffff);
     140    border: 1px solid #eee;
     141    border-radius: 12px;
     142    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.03);
     143    margin-bottom: 24px;
     144    padding: 24px;
     145    transition: transform 0.2s ease, box-shadow 0.2s ease;
     146}
     147
     148.revnextwoo_review_item:hover {
     149    transform: translateY(-2px);
     150    box-shadow: 0 6px 16px rgba(0, 0, 0, 0.06);
     151}
     152
    71153.revnextwoo-reply-card {
    72     border-bottom: 1px solid var(--revnextwoo-box-border, #ddd);
    73 }
    74 
    75 .revnextwoo_product_show .revnextwoo-item:last-child {
    76     border-bottom: none;
    77 }
     154    margin-top: 15px;
     155    margin-left: 20px;
     156    background-color: #f9f9f9;
     157}
     158
     159
    78160
    79161/* Headers */
     
    81163.average_ratings .title {
    82164    background-color: var(--revnextwoo-header-bg, #f9f9f9);
     165}
     166
     167.user_question_section h3 {
     168    font-weight: 500;
     169    margin: 0px 0px;
     170    color: #232323;
     171    padding: 10px 18px;
    83172}
    84173
     
    151240    font-weight: var(--revnextwoo-content-weight, 400);
    152241    font-family: var(--revnextwoo-content-family, 'Arial, sans-serif');
     242    line-height: 1.6;
     243    color: #444;
     244}
     245
     246.revnextwoo-item .comment-author .fn {
     247    font-size: 1.1rem;
     248    font-weight: 600;
     249    color: #333;
     250}
     251
     252.revnextwoo-item .comment-metadata {
     253    font-size: 0.9rem;
     254    color: #888;
     255    margin-bottom: 10px;
    153256}
    154257
  • review-next-for-woocommerce/trunk/includes/class-revnextwoo-activator.php

    r3315734 r3457384  
    3131     */
    3232    public static function activate() {
    33 
    34     }
     33        // Ensure DB helper is available
     34        if (!class_exists('Revnextwoo_QA_DB')) {
     35            require_once dirname(__FILE__) . '/class-revnextwoo-qa-db.php';
     36        }
     37        // Create custom QA table
     38        if (class_exists('Revnextwoo_QA_DB')) {
     39            Revnextwoo_QA_DB::create_table();
     40        }
     41    }
    3542
    3643}
  • review-next-for-woocommerce/trunk/includes/class-revnextwoo.php

    r3315734 r3457384  
    159159        $this->loader->add_action('admin_enqueue_scripts', $plugin_admin, 'enqueue_styles');
    160160        $this->loader->add_action('admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts');
     161        $this->loader->add_action('admin_footer', $plugin_admin, 'admin_footer_scripts');
     162
    161163    }
    162164
  • review-next-for-woocommerce/trunk/public/class-revnextwoo-public.php

    r3317206 r3457384  
    4040   
    4141    /**
     42     * Freemium limit for total reviews
     43     *
     44     * @since 1.0.0
     45     * @var int
     46     */
     47    const FREEMIUM_REVIEW_LIMIT = 300;
     48   
     49    /**
    4250     * Allowed file types for uploads
    4351     *
     
    8593        add_action('wp_ajax_revnextwoo_submit_review', [$this, 'revnextwoo_handle_review_submission']);
    8694        add_action('wp_ajax_nopriv_revnextwoo_submit_review', [$this, 'revnextwoo_handle_review_submission']);
     95
     96
     97       
    8798    }
     99
    88100
    89101    /**
     
    107119         */
    108120        wp_enqueue_style($this->plugin_name, plugin_dir_url(__FILE__) . 'css/revnextwoo-plugin.css', array('dashicons'), $this->version, 'all');
    109         // Fancybox CSS
     121       
     122        // Add dynamic CSS for review form styling
     123        $form_bg = get_option('revnextwoo_review_form_background', '#ffffff');
     124        $form_border = get_option('revnextwoo_review_form_border', '#e0e0e0');
     125        $submit_button_bg = get_option('revnextwoo_review_submit_button_bg', '#4a90e2');
     126        $rating_icon_color = get_option('revnextwoo_review_rating_icon_color', '#ffc107');
     127        $link_color = get_option('revnextwoo_review_link_color', '#4a90e2');
     128        $box_item_boxshadow = get_option('revnextwoo_review_box_item_boxshadow', 'rgba(0, 0, 0, 0.3) 0px 19px 38px, rgba(0, 0, 0, 0.22) 0px 15px 12px');       
     129       
     130        $custom_css = ".revnextwoo_review_form_wrapper {
     131            background-color: {$form_bg} !important;
     132            border: 1px solid {$form_border} !important;
     133        }
     134        .revnextwoo_form_submit .button.alt {
     135            background-color: {$submit_button_bg} !important;
     136            border-color: {$submit_button_bg} !important;
     137        }
     138        .revnextwoo_review_rating {
     139            color: {$rating_icon_color} !important;
     140        }
     141        .revnextwoo_read_more {
     142            color: {$link_color} !important;
     143        }
     144        .revnextwoo_review_item {
     145            box-shadow: {$box_item_boxshadow} !important;
     146        }";
     147       
     148        wp_add_inline_style($this->plugin_name, $custom_css);
     149       
     150        // Fancybox CSS
    110151        wp_enqueue_style('fancybox-css', plugin_dir_url(__FILE__) . 'css/vendor/jquery.fancybox.min.css', array(), $this->version);
    111152        wp_enqueue_style('font-awesome', plugin_dir_url(__FILE__) . 'css/vendor/all.min.css', array(), $this->version);
     
    132173         */
    133174
    134         wp_enqueue_script('revnextwoo-review-form-validation', plugin_dir_url(__FILE__) . 'js/revnextwoo-form-validation.js', array('jquery'), $this->version, false);
    135175        wp_enqueue_script('revnextwoo-filter-review', plugin_dir_url(__FILE__) . 'js/revnextwoo-filter-review.js', array('jquery'), $this->version, true);
    136176        // Fancybox JS
     
    139179        // Pass the WordPress AJAX URL to your 'filter-review' script
    140180        wp_localize_script('revnextwoo-filter-review', 'revnextwooAjax', array('ajaxurl' => admin_url('admin-ajax.php')));
     181
     182        // Enqueue and localize the reviews AJAX pagination script
     183        wp_enqueue_script('revnextwoo-reviews', plugin_dir_url(__FILE__) . 'js/revnextwoo-reviews.js', array('jquery'), $this->version, true);
     184        wp_localize_script('revnextwoo-reviews', 'revnextwoo_vars', array(
     185            'ajax_url' => admin_url('admin-ajax.php'),
     186            'nonce'    => wp_create_nonce('revnextwoo_nonce')
     187        ));
     188
    141189        wp_enqueue_script('revnextwoo-review-main', plugin_dir_url(__FILE__) . 'js/revnextwoo-main.js', array('jquery'), $this->version, true);
    142190    }
     
    178226        $review_title = isset($_POST['review_title']) ? sanitize_text_field($_POST['review_title']) : '';
    179227        $review_text = isset($_POST['review_text']) ? sanitize_textarea_field($_POST['review_text']) : '';
     228       
     229
    180230       
    181231        // Validate rating
     
    199249            error_log('User has already reviewed this product');
    200250            wp_send_json_error(array('message' => 'You have already reviewed this product.'));
     251            return;
     252        }
     253       
     254        // Check freemium review limit
     255        $total_reviews = $this->count_total_reviews();
     256        if ($total_reviews >= self::FREEMIUM_REVIEW_LIMIT) {
     257            error_log('Review limit reached: ' . $total_reviews . '/' . self::FREEMIUM_REVIEW_LIMIT);
     258            wp_send_json_error(array(
     259                'message' => sprintf(
     260                    __('You have reached the limit of %d reviews in the free version. Upgrade to Pro to unlock unlimited review submissions.', 'review-next-for-woocommerce'),
     261                    self::FREEMIUM_REVIEW_LIMIT
     262                )
     263            ));
    201264            return;
    202265        }
     
    236299        // Ensure comment_id is an integer
    237300        $comment_id = (int) $comment_id;
     301       
     302
     303       
     304        // Save video ID if present
     305        if (isset($_POST['review_video_id']) && is_numeric($_POST['review_video_id'])) {
     306            add_comment_meta($comment_id, 'review_video', intval($_POST['review_video_id']));
     307        }
    238308       
    239309        // Handle image uploads if any
     
    305375        ));
    306376    }
    307    
     377
     378    /**
     379     * Count total reviews submitted through the plugin for freemium limit
     380     *
     381     * @since 1.0.0
     382     * @return int Total number of reviews
     383     */
     384    private function count_total_reviews() {
     385        global $wpdb;
     386       
     387        $count = $wpdb->get_var(
     388            "SELECT COUNT(*) FROM {$wpdb->comments} WHERE comment_type = 'review'"
     389        );
     390       
     391        return (int) $count;
     392    }
     393
    308394    /**
    309395     * Reorganize the $_FILES array for multiple uploads
  • review-next-for-woocommerce/trunk/public/css/revnextwoo-plugin.css

    r3317206 r3457384  
    2424  -webkit-appearance: none;
    2525}
    26 .login_redirect a{
    27   color: #ffc700 !important;
     26.login_redirect button a{
     27  color: black !important;
     28  padding: 5px 8px;
     29  background-color: #2196f3;
    2830}
    2931.write-review-wrapper .rating-group .group{
     
    8082}
    8183
    82 .write-review-wrapper .review_field {
    83   overflow-y: hidden;
    84   resize: none;
    85   min-height: 120px;
    86   max-height: 160px;
     84.write-review-wrapper .review_field,
     85.revnextwoo_review_form_wrapper input[type="text"],
     86.revnextwoo_review_form_wrapper textarea {
    8787  box-sizing: border-box;
     88  width: 100%;
    8889  max-width: 100%;
    89   height: auto;
    90   padding: 4px 11px;
     90  padding: 8px 12px;
    9191  color: rgba(0, 0, 0, 0.88);
    9292  font-size: 14px;
    9393  line-height: 1.5714285714285714;
    94   list-style: none;
    9594  background-color: #ffffff;
    96   background-image: none;
    97   border-width: 1px !important;
    98   border-style: dashed !important;
    99   border-color: #d9d9d9 !important;
     95  border: 1px solid #d9d9d9;
    10096  border-radius: 6px;
    10197  transition: all 0.2s;
     98  margin: 0;
     99}
     100
     101.revnextwoo_review_form_wrapper textarea {
     102  min-height: 120px;
     103  max-height: 160px;
     104  resize: vertical;
     105  overflow-y: auto;
     106}
     107
     108.revnextwoo_review_form_wrapper input[type="text"] {
     109  height: 40px;
     110}
     111
     112.revnextwoo_form_group {
     113  margin-bottom: 15px;
     114  width: 100%;
     115}
     116
     117.revnextwoo_form_group label {
    102118  display: block;
     119  margin-bottom: 5px;
     120  font-weight: 500;
     121}
     122
     123/* Ensure form wrapper has proper width and padding */
     124.revnextwoo_review_form_wrapper {
    103125  width: 100%;
    104 }
     126  max-width: 100%;
     127  box-sizing: border-box;
     128  padding: 20px;
     129}
     130
    105131.write-review-wrapper textarea:focus {
    106132  outline: 0;
     
    123149  height: 74px;
    124150  transition: width 0.2s;
     151}
     152.revnextwoo_review_date,.review-location{
     153  font-size: 12px !important
    125154}
    126155
     
    317346
    318347/* Review Form Styles */
    319 .revnextwoo_review_form_wrapper {
     348/* .revnextwoo_review_form_wrapper {
    320349    background: #fff;
    321350    border-radius: 8px;
     
    330359    font-size: 18px;
    331360    color: #333;
    332 }
     361} */
    333362
    334363.revnextwoo_form_group {
     
    381410.revnextwoo_form_group input[type="text"],
    382411.revnextwoo_form_group textarea {
    383     width: 100%;
     412    /* width: 100%; */
    384413    padding: 10px 12px;
    385414    border: 1px solid #ddd;
     
    493522
    494523/* Review Item Styles */
    495 .revnextwoo_review_item {
    496     padding: 20px 0;
    497     border-bottom: 1px solid #eee;
    498 }
    499524
    500525.revnextwoo_review_item:last-child {
     
    505530    display: flex;
    506531    align-items: center;
    507     margin-bottom: 10px;
     532    padding:10px 30px;
     533    border-radius: 15px;
    508534}
    509535
    510536.revnextwoo_reviewer_avatar {
    511537    margin-right: 15px;
    512 }
    513 
    514 .revnextwoo_reviewer_avatar img {
     538    object-fit: cover;
     539}
     540.revnextwoo_reviewer_avatar img{
    515541    width: 50px;
    516542    height: 50px;
    517543    border-radius: 50%;
    518     object-fit: cover;
    519544}
    520545
    521546.revnextwoo_reviewer_details h4 {
    522     margin: 0 0 5px;
    523     font-size: 16px;
    524547    font-weight: 600;
    525548    color: #333;
     549    margin:0px 0px;
    526550}
    527551
     
    556580}
    557581
    558 .revnextwoo_review_content {
    559     margin-left: 65px;
    560 }
    561 
    562582.revnextwoo_review_title {
    563583    font-size: 16px;
     
    580600    gap: 10px;
    581601    margin: 15px 0;
    582 }
    583 
    584 .revnextwoo_review_image {
     602    padding-left: 10px;
     603}
     604
     605.revnextwoo_review_image{
    585606    width: 80px;
    586607    height: 80px;
     
    615636}
    616637
    617 /* Review Actions */
    618 .revnextwoo_review_actions {
    619     display: flex;
    620     gap: 15px;
    621     margin-top: 15px;
    622     padding-left: 65px;
    623 }
    624 
    625638.revnextwoo_like_btn,
    626639.revnextwoo_report_btn {
     
    628641    border: none;
    629642    color: #666;
     643    cursor: pointer;
    630644    font-size: 13px;
     645    padding: 5px 10px;
     646    margin-left: 5px;
     647    border-radius: 3px;
     648    transition: all 0.2s ease;
     649    z-index: 15;
     650}
     651
     652/* Social Media Sharing Buttons */
     653.revnextwoo_social_sharing {
     654    display: inline-flex;
     655    gap: 12px;
     656}
     657
     658.revnextwoo_share_btn {
     659    display: inline-flex;
     660    align-items: center;
     661    justify-content: center;
     662    width: 28px;
     663    height: 28px;
     664    border-radius: 50%;
     665    background: #fff;
     666    border: 1px solid #e0e0e0;
    631667    cursor: pointer;
    632     display: flex;
    633     align-items: center;
    634     gap: 5px;
    635     padding: 5px 10px;
    636     border-radius: 4px;
    637     transition: all 0.2s;
     668    transition: all 0.2s ease;
     669    padding: 0;
     670    text-decoration: none;
     671}
     672
     673.revnextwoo_share_btn:hover {
     674    transform: translateY(-2px);
     675    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
     676}
     677
     678.revnextwoo_facebook_share:hover {
     679    background-color: #1877F2;
     680}
     681
     682.revnextwoo_facebook_share:hover svg {
     683    fill: #fff;
     684}
     685
     686.revnextwoo_whatsapp_share:hover {
     687    background-color: #25D366;
     688}
     689
     690.revnextwoo_whatsapp_share:hover svg {
     691    fill: #fff;
     692}
     693
     694.revnextwoo_twitter_share:hover {
     695    background-color: #1DA1F2;
     696}
     697
     698.revnextwoo_twitter_share:hover svg {
     699    fill: #fff;
     700}
     701
     702.revnextwoo_share_btn svg {
     703    width: 14px;
     704    height: 14px;
     705    transition: all 0.2s ease;
    638706}
    639707
     
    656724/* Review Form Styles */
    657725.revnextwoo_review_form_wrapper {
    658     margin-top: 30px;
    659     background: #fff;
     726    background-color:var(--revnextwoo-form-bg, #ffffff);
     727    padding: 20px;
     728    margin-top: 20px;
    660729    border-radius: 8px;
    661     padding: 25px;
    662730    box-shadow: 0 1px 3px rgba(0,0,0,0.1);
    663731}
     
    782850
    783851.revnextwoo_upload_btn .dashicons {
    784     font-size: 16px;
    785     width: 16px;
    786     height: 16px;
     852    font-size: 35px;
     853    width: 35px;
     854    height: 35px;
    787855}
    788856
     
    804872}
    805873
    806 /* Login Notice */
    807 .revnextwoo_login_notice {
    808     margin-top: 20px;
    809     padding: 15px;
    810     background: #fff8e1;
    811     border-left: 4px solid #ffc107;
    812     color: #5d4037;
    813 }
    814 
    815 .revnextwoo_login_notice a {
    816     color: #1976d2;
    817     text-decoration: none;
    818 }
    819 
    820 .revnextwoo_login_notice a:hover {
    821     text-decoration: underline;
    822 }
    823 
    824874/* No Reviews Message */
    825875.revnextwoo_no_reviews {
     
    841891        width: 100%;
    842892        justify-content: space-between;
    843     }
    844    
    845     .revnextwoo_review_content {
    846         margin-left: 0;
    847     }
    848    
    849     .revnextwoo_review_actions {
    850         padding-left: 0;
    851893    }
    852894}
     
    13941436}
    13951437
    1396 
     1438/* user question section start */
    13971439.user_question_section {
    1398   max-width: 1050px;
     1440  max-width: 100%;
    13991441  overflow: hidden;
    14001442  width: 100%;
     
    14041446  margin: 20px 0px;
    14051447}
    1406 .user_question_section  .title{
     1448/* .user_question_section .title{
     1449  font-weight: 600;
     1450  font-family: var(--wp--preset--font-family--inter);
    14071451  background-color: #f2f2f2;
    14081452  padding: 10px;
    14091453  text-align: left !important;
    1410   font-weight: 500 !important;
    14111454  font-size: 18px !important;
    14121455  position: static !important;
     
    14141457  margin-bottom: 3px !important;
    14151458
    1416 }
     1459} */
    14171460.user_question_section .login_redirect{
    14181461  font-size: 14px;
     
    14211464  margin-bottom: 0px !important;
    14221465}
    1423 .user_question_section .login_redirect a{
    1424   color: #F85606;
    1425 }
     1466
    14261467.user_question_section form {
    14271468  padding: 18px;
     
    14291470}
    14301471.user_question_section  .input_group {
    1431   display: flex;
    1432   text-align: center;
    14331472  margin: 0 auto;
    14341473  justify-content: center;
     
    14501489
    14511490.user_question_section .question_field {
    1452   width: 100%;
    1453   height: 40px;
    1454   min-height: 40px;
    1455   padding: 10px;
     1491  width: 96%;
     1492  height: 50px;
    14561493  resize: vertical;
    14571494  cursor: text;
    1458   border: 1px solid #ccc;
     1495  border: 1px solid #8e8e8e;
    14591496  resize: none;
    14601497  overflow: hidden;
    1461   background: none;
    1462   box-shadow: none;
    1463   outline: none;
    1464   margin: 0px;
     1498  border-radius: 5px;
     1499  padding:20px;
    14651500}
    14661501
    14671502.user_question_section .submit_btn {
    1468   border-radius: 0;
    1469   display: block;
    1470   color: #fff;
    1471   font-size: 17px;
    1472   text-transform: uppercase;
    1473   font-weight: 400;
    1474   padding: 10px;
    1475   line-height: 18px;
    1476   transition: all ease 0.35s;
    1477   -webkit-transition: all ease 0.35s;
    1478   -moz-transition: all ease 0.35s;
    1479   margin: 0px;
     1503    background: #4a90e2;
     1504    color: #fff;
     1505    border: none;
     1506    padding: 10px 20px;
     1507    border-radius: 4px;
     1508    font-size: 14px;
     1509    font-weight: 500;
     1510    cursor: pointer;
     1511    transition: background 0.2s;
    14801512}
    14811513.write-review-wrapper .submit_btn:hover, .reply-comment-form .submit_btn:hover {
     
    14831515}
    14841516.user_question_show {
    1485   padding: 20px;
     1517  width:96%;
     1518  padding: 24px 0;
     1519  background: #fff;
     1520  border-radius: 8px;
     1521  box-shadow: 0 2px 8px rgba(0,0,0,0.06);
     1522  margin: 20px 0;
     1523  overflow-wrap: break-word;
     1524  word-break: break-word;
     1525  margin: 0 auto;
    14861526}
    14871527
    14881528.user_question_show .item {
    1489   align-content: center;
    1490   align-items: center;
    1491   display: grid;
    1492   grid-template-columns: auto auto;
    1493   justify-content: space-between;
    1494   margin-top: -1px;
    1495   border-top: 1px solid #ececec;
    1496   padding: 5px;
     1529  display: flex;
     1530  flex-direction: column;
     1531  gap: 8px;
     1532  background: #f9f9f9;
     1533  border-radius: 8px;
     1534  padding: 18px 18px 12px 18px;
     1535  margin-bottom: 18px;
     1536  border: 1px solid #ececec;
     1537  box-shadow: 0 1px 4px rgba(0,0,0,0.07);
     1538  overflow: hidden;
    14971539}
    14981540.user_question_show .item:last-child {
     
    15101552.user_question_show .item  p{
    15111553  margin-bottom: 0px !important;
     1554  padding: 12px 10px 0 0;
     1555  font-size: 15px;
     1556  color: #222;
     1557  line-height: 1.7;
     1558  overflow-wrap: anywhere;
     1559  word-break: break-word;
    15121560}
    15131561.user_question_show .short_title{
     
    15181566}
    15191567.user_question_show .short_content {
    1520   display: flex;
     1568  display: block;
     1569  position: relative;
     1570  gap: 12px;
    15211571  align-content: center;
    1522   /* align-items: center; */
    1523   gap: 5px 17px;
    1524   width: 90%;
     1572  width: 100%;
     1573  border: none;
     1574  padding: 0;
     1575  overflow-wrap: anywhere;
     1576  word-break: break-word;
     1577}
     1578.revnext_que_info{
     1579    display: flex;
     1580    align-items: center;
     1581    gap: 10px;
     1582    margin-bottom: 10px;
     1583    border: none;
     1584}
     1585.revnextwoo_question_avatar {
     1586    margin-right: 15px;
     1587    min-width: 50px;
     1588    min-height: 50px;
     1589}
     1590.revnextwoo_question_avatar img {
     1591    width: 50px;
     1592    height: 50px;
     1593    border-radius: 50%;
     1594    object-fit: cover;
     1595    border: 2px solid #e3e3e3;
     1596    background: #fafafa;
     1597}
     1598.revnextwoo_question_username h4 {
     1599    margin: 0 0 2px;
     1600    font-size: 16px;
     1601    font-weight: 600;
     1602    color: #333;
     1603    line-height: 1.2;
     1604}
     1605.user_question_show .short_content p{
     1606  padding: 10px 0 0 0;
     1607  margin: 0;
     1608  width: 100%;
     1609  background: none;
     1610  font-size: 15px;
     1611  color: #222;
     1612  line-height: 1.7;
     1613  overflow-wrap: anywhere;
     1614  word-break: break-word;
     1615}
     1616.revnextwoo_question_username{
     1617  float: top;
    15251618}
    15261619.user_question_show .question_icon{
     
    15281621  height: 20px;
    15291622}
     1623
    15301624/* responsive small, medium, large */
    15311625
     
    15391633    width: 100%;
    15401634  }
    1541   .user_question_show .short_content{
    1542     display: block;
     1635
     1636  .user_question_show {
     1637    width: 100%;
     1638    padding: 12px 0;
    15431639  }
    15441640  .user_question_show .item {
    1545     align-items: first baseline;
    1546     grid-template-columns: auto;
     1641    flex-direction: column;
     1642    padding: 14px 8px 10px 8px;
     1643    gap: 6px;
     1644  }
     1645  .user_question_show .short_content {
     1646    width: 100%;
     1647    padding: 0;
     1648  }
     1649  .user_question_show .item p,
     1650  .user_question_show .short_content p {
     1651    font-size: 14px;
     1652    padding: 8px 0 0 0;
    15471653  }
    15481654  .user_question_section #question-form {
     
    16031709}
    16041710@media screen and (max-width: 768px) {
     1711  .user_question_show {
     1712    width: 100%;
     1713    padding: 8px 0;
     1714  }
     1715  .user_question_show .item {
     1716    padding: 10px 4px 8px 4px;
     1717    gap: 4px;
     1718  }
     1719  .user_question_show .item p,
     1720  .user_question_show .short_content p {
     1721    font-size: 13px;
     1722    padding: 6px 0 0 0;
     1723  }
    16051724  #five-stars .rating-group {
    16061725    display: block;
  • review-next-for-woocommerce/trunk/public/js/revnextwoo-filter-review.js

    r3317206 r3457384  
    11jQuery(document).ready(function($) {
     2    // Delegated event for thumbs-up
     3    $(document).on('click', '.revnextwoo-thumb-up', function () {
     4        var target = $(this).data('target');
     5        var $loader = $('#revnextwoo-loader');
     6        if (target) {
     7            var voteCount = $('#revnextwoo-vote-count-' + target);
     8            recordVote(target, 'like', voteCount, $loader);
     9        }
     10    });
     11
     12    // Delegated event for thumbs-down
     13    $(document).on('click', '.revnextwoo-thumb-down', function () {
     14        var target = $(this).data('target');
     15        var $loader = $('#revnextwoo-loader');
     16        if (target) {
     17            var voteCount = $('#revnextwoo-vote-count-' + target);
     18            recordVote(target, 'dislike', voteCount, $loader);
     19        }
     20    });
     21
     22    // Delegated event for comment reply form show/hide
     23    $(document).on('click', '.revnextwoo-comment-reply-trigger', function () {
     24        $('.revnextwoo-reply-dropdown').hide();
     25        var target = $(this).data('target');
     26        if (target) {
     27            $('#revnextwoo-reply-comment-form' + target).toggle();
     28        }
     29    });
     30
     31    // Star ratings initialization function
     32    function initStarRatings() {
     33        var $fillRatings = $('.revnextwoo-fill-ratings span');
     34        if ($fillRatings.length) {
     35            var starRatingWidth = $fillRatings.width();
     36            $('.revnextwoo-star-ratings').width(starRatingWidth);
     37        }
     38    }
     39
     40    // Call star ratings initialization on page load
     41    initStarRatings();
     42
     43    // Expose for AJAX success usage
     44    window.revnextwooInitStarRatings = initStarRatings;
     45
    246    // Function to update comments based on selected filters
    347    function updateComments() {
     
    90134                }
    91135
    92                 // When the thumbs-up icon is clicked
    93                 $('.revnextwoo-thumb-up').on('click', function() {
    94                     var target = $(this).data('target');
    95                     if (target) {
    96                         var voteCount = $('#revnextwoo-vote-count-' + target);
    97                         recordVote(target, 'like', voteCount);
    98                         if ($loader.length) {
    99                             $loader.show();
    100                         }
    101                     }
    102                 });
    103 
    104                 // When the thumbs-down icon is clicked
    105                 $('.revnextwoo-thumb-down').on('click', function() {
    106                     var target = $(this).data('target');
    107                     if (target) {
    108                         var voteCount = $('#revnextwoo-vote-count-' + target);
    109                         recordVote(target, 'dislike', voteCount);
    110                         if ($loader.length) {
    111                             $loader.show();
    112                         }
    113                     }
    114                 });
    115 
    116                 function recordVote(commentId, voteType, voteCount) {
    117                     if (typeof revnextwooAjax === 'undefined' && typeof ajaxurl === 'undefined') {
    118                         console.error('AJAX URL not defined');
    119                         return;
    120                     }
    121 
    122                     $.ajax({
    123                         type: 'POST',
    124                         url: typeof revnextwooAjax !== 'undefined' ? revnextwooAjax.ajaxurl : ajaxurl,
    125                         data: {
    126                             action: 'revnextwoo_record_comment_vote',
    127                             comment_id: commentId,
    128                             vote_type: voteType,
    129                             nonce: typeof revnextwooAjax !== 'undefined' ? revnextwooAjax.nonce : ''
    130                         },
    131                         success: function(response) {
    132                             if ($loader.length) {
    133                                 $loader.hide();
    134                             }
    135                            
    136                             if (response && response.success) {
    137                                 var data = response.data;
    138                                 if (voteCount.length) {
    139                                     voteCount.text(data.vote_count);
    140                                 }
    141                                
    142                                 // Update active states
    143                                 if (data.increment) {
    144                                     $('#revnextwoo-thumb-up-' + data.comment_id).addClass('active');
    145                                     $('#revnextwoo-thumb-down-' + data.comment_id).removeClass('active');
    146                                 } else if (data.decrement) {
    147                                     $('#revnextwoo-thumb-down-' + data.comment_id).addClass('active');
    148                                     $('#revnextwoo-thumb-up-' + data.comment_id).removeClass('active');
    149                                 } else {
    150                                     $('#revnextwoo-thumb-up-' + data.comment_id).removeClass('active');
    151                                     $('#revnextwoo-thumb-down-' + data.comment_id).removeClass('active');
    152                                 }
    153                             }
    154                         },
    155                         error: function(xhr, status, error) {
    156                             console.error('Error recording vote:', error);
    157                             if ($loader.length) {
    158                                 $loader.hide();
    159                             }
    160                         }
    161                     });
    162                 }
    163 
    164136                // Initialize star ratings width
    165                 var $fillRatings = $('.revnextwoo-fill-ratings span');
    166                 if ($fillRatings.length) {
    167                     var starRatingWidth = $fillRatings.width();
    168                     $('.revnextwoo-star-ratings').width(starRatingWidth);
     137                if (window.revnextwooInitStarRatings) {
     138                    window.revnextwooInitStarRatings();
    169139                }
    170140            },
  • review-next-for-woocommerce/trunk/public/js/revnextwoo-form-validation.js

    r3315734 r3457384  
    1 document.addEventListener('DOMContentLoaded', function () {
    2     const imageUpload = document.getElementById('revnextwoo-image-upload');
    3     const imagePreview = document.getElementById('revnextwoo-image-preview');
    4     const selectedFileCount = document.getElementById('revnextwoo-selected-file-count');
    5    
    6     // Maximum allowed images
    7     const maxImages = 6;
    8    
    9     // Initialize the count
    10     let selectedImages = 0;
    11 
    12     // Listen for changes in the file input
    13     if (imageUpload) {
    14         imageUpload.addEventListener('change', handleImageUpload);
    15     }
    16 
    17     // Handle image removal using JavaScript
    18     if (imagePreview) {
    19         imagePreview.addEventListener('click', function (event) {
    20             if (event.target.classList.contains('revnextwoo-remove-button')) {
    21                 const removedImage = event.target.parentNode.querySelector('img');
    22                 if (removedImage) {
    23                     const imageName = removedImage.getAttribute('data-filename');
    24                    
    25                     // Remove the image preview
    26                     event.target.parentNode.remove();
    27 
    28                     // Decrement the selected images count
    29                     selectedImages -= 1;
    30                     updateSelectedFileCount();
    31                 }
    32             }
    33         });
    34     }
    35 
    36     function handleImageUpload(event) {
    37         const files = event.target.files;
    38        
    39         // Ensure we don't exceed the maximum allowed images
    40         if (selectedImages + files.length > maxImages) {
    41             alert('Maximum of ' + maxImages + ' images allowed.');
    42             event.target.value = ''; // Clear the file input
    43             return;
    44         }
    45 
    46         for (const file of files) {
    47             if (file.type.startsWith('image/')) {
    48                 const imageItem = document.createElement('div');
    49                 imageItem.classList.add('revnextwoo-image-item');
    50 
    51                 const image = document.createElement('img');
    52                 image.src = URL.createObjectURL(file);
    53                 image.alt = 'Image Preview';
    54                 image.width = 100;
    55                 image.height = 100;
    56                 image.setAttribute('data-filename', file.name); // Store the filename
    57 
    58                 const removeButton = document.createElement('button');
    59                 removeButton.classList.add('revnextwoo-remove-button');
    60                 removeButton.innerHTML = '&times;';
    61                 removeButton.type = 'button'; // Prevent form submission
    62 
    63                 imageItem.appendChild(image);
    64                 imageItem.appendChild(removeButton);
    65                
    66                 if (imagePreview) {
    67                     imagePreview.appendChild(imageItem);
    68                    
    69                     // Increment the selected images count
    70                     selectedImages += 1;
    71                     updateSelectedFileCount();
    72                 }
    73             }
    74         }
    75     }
    76 
    77     function updateSelectedFileCount() {
    78         if (selectedFileCount) {
    79             selectedFileCount.textContent = `Selected file: ${selectedImages}/${maxImages}`;
    80         }
    81     }
    82 });
     1// Disabled default image preview logic to prevent double-handling and interference with compression logic in average-rating.php
     2// document.addEventListener('DOMContentLoaded', function () {
     3//     ...
     4// });
  • review-next-for-woocommerce/trunk/public/js/revnextwoo-main.js

    r3315734 r3457384  
    44    // Cache jQuery objects
    55    var $document = $(document);
     6   
     7
    68   
    79    // Initialize question form functionality
     
    6870        });
    6971
    70         // Question reply toggle handler
    71         $document.on('click', '.revnextwoo-question-reply-trigger', function(e) {
    72             e.preventDefault();
    73             var target = $(this).data('target');
    74             if (target) {
    75                 $('.revnextwoo-reply-question-form').hide();
    76                 $('#revnextwoo-reply-question-form-' + target).toggle();
    77             }
    78         });
     72        // // Question reply toggle handler
     73        // $document.on('click', '.question-reply-trigger', function(e) {
     74        //     e.preventDefault();
     75        //     var target = $(this).data('target');
     76        //     if (target) {
     77        //         $('.reply_question_fm').hide();
     78        //         $('reply_question_form' + target).toggle();
     79        //     }
     80        // });
    7981    }
    8082
     
    104106    init();
    105107});
     108
     109
     110
     111
     112    // Toggle reply form display when "Reply" is clicked (delegated for dynamic content)
     113    $(document).on('click', '.question-reply-trigger', function (e) {
     114        e.preventDefault();
     115        var commentID = $(this).attr('target');
     116        var $form = $('#reply_question_form' + commentID);
     117        if ($form.length) {
     118            if ($form.css('display') === 'none' || $form.css('display') === '') {
     119                $form.show();
     120            } else {
     121                $form.hide();
     122            }
     123        }
     124    });
  • review-next-for-woocommerce/trunk/review-next-for-woocommerce.php

    r3317206 r3457384  
    11<?php
    2 
    32/**
    43 * The plugin bootstrap file
     
    1817 * Description:       Review Next for WooCommerce is a plugin that adds a new tab to your WooCommerce product pages, allowing customers to leave reviews and ratings.
    1918 * Author:            Nazmul Hosen
    20  * Version:           1.0.2
     19 * Version:           1.0.3
    2120 * Requires at least: 5.0
    2221 * Tested up to:      6.8.1
    2322 * Requires PHP:      7.2
    24  * Requires Plugins:  woocommerce
     23 * WC requires at least: 6.0
     24 * WC tested up to: 8.0
     25 * HPOS Compatible: Yes
     26 * WC HPOS Compatible: Yes
    2527 * Author URI:        https://profiles.wordpress.org/nazmul111/
    2628 * License:           GPLv2 or later
     
    3436    exit; // Exit if accessed directly
    3537}
     38
     39// --- Dependency Check ---
     40require_once plugin_dir_path( __FILE__ ) . 'includes/class-revnextwoo-dependency-checker.php';
     41
     42if ( ! Revnextwoo_Dependency_Checker::check_dependencies() ) {
     43    return;
     44}
     45// --- END Dependency Check ---
     46
     47// --- Ensure coupon generator is loaded and initialized ---
     48if (function_exists('error_log')) error_log('[revnextwoo][DEBUG] Main plugin file loaded');
     49require_once plugin_dir_path(__FILE__) . 'includes/class-revnextwoo-coupon-generator.php';
     50if (class_exists('Revnextwoo_Coupon_Generator')) {
     51    if (function_exists('error_log')) error_log('[revnextwoo][DEBUG] Coupon generator class found, initializing');
     52    new Revnextwoo_Coupon_Generator();
     53} else {
     54    if (function_exists('error_log')) error_log('[revnextwoo][ERROR] Coupon generator class NOT found');
     55}
     56// --- END coupon generator loading ---
    3657/**
    3758 * Currently plugin version.
     
    3960 * Rename this for your plugin and update it as you release new versions.
    4061 */
    41 define('REVNEXTWOO_PLUGIN_VERSION', '1.0');
     62define('REVNEXTWOO_PLUGIN_VERSION', '1.0.3');
    4263
    4364/**
     
    6990 */
    7091require plugin_dir_path(__FILE__) . 'includes/class-revnextwoo.php';
    71 
     92require plugin_dir_path(__FILE__) . 'includes/revnextwoo-helper.php';
     93require plugin_dir_path(__FILE__) . 'includes/class-revnextwoo-qa-db.php';
     94
     95// Load email settings and handler
     96require plugin_dir_path(__FILE__) . 'admin/class-revnextwoo-email-settings.php';
     97require plugin_dir_path(__FILE__) . 'includes/class-revnextwoo-email-integrations.php';
     98
     99// Initialize email settings
     100if (is_admin()) {
     101    RevNextWoo_Email_Settings::get_instance();
     102}
    72103/**
    73104 * Begins execution of the plugin.
     
    89120
    90121/**
     122 * Declare HPOS compatibility
     123 */
     124function revnextwoo_declare_hpos_compatibility()
     125{
     126    if (class_exists('Automattic\WooCommerce\Internal\Features\FeaturesController')) {
     127        $features_controller = wc_get_container()->get('Automattic\WooCommerce\Internal\Features\FeaturesController');
     128        $features_controller->declare_compatibility('custom_order_tables', plugin_basename(__FILE__), true);
     129    }
     130}
     131add_action('before_woocommerce_init', 'revnextwoo_declare_hpos_compatibility');
     132
     133/**
    91134 * Enqueue plugin styles and scripts
    92135 */
    93136function revnextwoo_enqueue_plugin_assets()
    94137{
     138    // Enqueue Watermarker.js only on product page (where review form is shown)
     139    if (is_product()) {
     140        wp_enqueue_script(
     141            'revnextwoo-watermarker',
     142            plugin_dir_url(__FILE__) . 'assets/js/vendor/watermarker/protect-image-watermarker/Watermarker.js',
     143            array('jquery'),
     144            '1.0',
     145            true
     146        );
     147        // Pass watermark settings to JS
     148        $watermark_enabled = get_option('revnextwoo_img_watermark', 'no') === 'yes';
     149        $watermark_text = get_option('revnextwoo_img_watermark_text', '');
     150        wp_localize_script('revnextwoo-watermarker', 'revnextwooWatermarkSettings', array(
     151            'enabled' => $watermark_enabled,
     152            'text' => $watermark_text,
     153        ));
     154    }
    95155    // Enqueue CSS
    96156    wp_enqueue_style(
     
    98158        plugin_dir_url(__FILE__) . 'assets/css/revnextwoo-styles.css',
    99159        array(),
    100         REVNEXTWOO_PLUGIN_VERSION
     160        time()
    101161    );
    102162
     
    106166        plugin_dir_url(__FILE__) . 'assets/js/revnextwoo-styles.js',
    107167        array(),
    108         REVNEXTWOO_PLUGIN_VERSION,
     168        time(),
    109169        true
    110170    );
     
    139199
    140200    wp_localize_script('revnextwoo-plugin-styles-js', 'revnextwooPluginStyles', $style_data);
     201
     202    // Enqueue Social Sharing Script
     203    if (is_product()) {
     204        wp_enqueue_script(
     205            'revnextwoo-social-share',
     206            plugin_dir_url(__FILE__) . 'public/js/revnextwoo-social-share.js',
     207            array('jquery'),
     208            time(),
     209            true
     210        );
     211    }
    141212}
    142213add_action('wp_enqueue_scripts', 'revnextwoo_enqueue_plugin_assets');
     
    156227        plugin_dir_url(__FILE__) . 'assets/css/revnextwoo-styles.css',
    157228        array(),
    158         REVNEXTWOO_PLUGIN_VERSION
     229        time()
    159230    );
    160231}
     
    243314function revnextwoo_add_reviews_and_comments_tab($tabs)
    244315{
    245     // Add a new tab with the title "Reviews & Comments"
    246     $tabs['reviews_comments_tab'] = array(
    247         'title' => __('Reviews', 'review-next-for-woocommerce'),
    248         'priority' => 50,
    249         'callback' => 'revnextwoo_reviews_and_comments_tab_content',
    250         // Callback function to display tab content
    251     );
    252 
    253     // Get the product ID
    254     global $product;
    255     $args = array(
    256         'post_id' => $product->get_id(),
    257         'type' => 'review',
    258         'count' => true // return only the count
    259     );
    260     // Get the number of reviews for the product
    261     $review_count = get_comments($args);
    262 
    263     // Modify the tab title to include the review count
    264     $tabs['reviews_comments_tab']['title'] = sprintf(
    265         /* translators: %d: number of reviews */
    266         __('Reviews (%d)', 'review-next-for-woocommerce'),
    267         $review_count
    268     );
    269 
    270 
     316    // Only add the review tab if the setting is enabled
     317    if (get_option('revnextwoo_show_revnextwoo_box_singular', 1)) {
     318        global $product;
     319        $args = array(
     320            'post_id' => $product->get_id(),
     321            'type' => 'review',
     322            'status'  => 'approve',
     323            'count' => true // return only the count
     324        );
     325        $review_count = get_comments($args);
     326        $tabs['reviews_comments_tab'] = array(
     327            'title' => sprintf(
     328                /* translators: %d: number of reviews */
     329                __('Reviews (%d)', 'review-next-for-woocommerce'),
     330                $review_count
     331            ),
     332            'priority' => 2,
     333            'callback' => 'revnextwoo_reviews_and_comments_tab_content',
     334        );
     335    }
    271336    return $tabs;
    272337}
     
    277342{
    278343    global $product;
    279    
     344
    280345    // Get the review box display setting
    281     $show_review_box_singular = get_option('revnextwoo_show_review_box_singular', 1);
     346    $show_review_box_singular = get_option('revnextwoo_show_revnextwoo_box_singular', 1);
    282347
    283348    // Get the current user's ID
    284     $show_revnextwoo_box_singular = get_option('revnextwoo_show_review_box_singular', 'registered_users_and_guests');
     349    $show_revnextwoo_box_singular = get_option('revnextwoo_show_revnextwoo_box_singular', 'registered_users_and_guests');
    285350    // Get the product ID
    286351    $product_id = $product->get_id();
    287    
     352
    288353    // Initialize comment data array
    289354    $comment_data = array(
     
    298363        'comment_approved' => 1,
    299364    );
    300    
    301     // Check if the user has purchased the product
     365
     366    // Enforce 'Who is allowed to rate' setting
     367    $allowed_to_rate = get_option('revnextwoo_allowed_to_rate', 'registered_users_and_guests');
     368    $current_user_id = get_current_user_id();
     369    $user_can_review = true;
     370    $error_message = '';
     371    if ($allowed_to_rate === 'no_one') {
     372        $user_can_review = false;
     373        $error_message = __('Review submissions are currently disabled.', 'review-next-for-woocommerce');
     374    } elseif ($allowed_to_rate === 'completed_orders') {
     375        // Only logged-in users who purchased the product
     376        if (!$current_user_id) {
     377            $user_can_review = false;
     378            $error_message = __('You must be logged in and have purchased this product to leave a review.', 'review-next-for-woocommerce');
     379        } else {
     380            $current_user = get_user_by('ID', $current_user_id);
     381            $user_email = isset($current_user->user_email) ? $current_user->user_email : '';
     382            if (!wc_customer_bought_product($user_email, $current_user_id, $product->get_id())) {
     383                $user_can_review = false;
     384                $error_message = __('Only verified purchasers can leave a review for this product.', 'review-next-for-woocommerce');
     385            }
     386        }
     387    }
     388
     389    // Block review submission if not allowed
    302390    if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['comment'])) {
     391        if (!$user_can_review) {
     392            wp_die(esc_html($error_message));
     393        }
    303394
    304395        // Verify the nonce
     
    329420                    // $product_id is the post ID to attach the image to (the product)
    330421                    $attachment_id = media_handle_upload('image_upload', $product_id);
    331                    
     422
    332423                    // Check for errors
    333424                    if (is_wp_error($attachment_id)) {
     
    364455                    'comment_content' => $comment,
    365456                    'comment_type' => 'review',
    366                     'comment_meta' => array(
    367                         'rating' => $rating,
    368                         // Save the rating as comment metadata
    369                     ),
     457                    'comment_approved' => 1, // Auto-approve reviews
    370458                    'user_id' => $current_user_id,
    371459                );
     
    373461                add_filter('duplicate_comment_id', '__return_false');
    374462
    375                
     463                // Insert the comment
     464                $comment_id = wp_insert_comment($comment_data);
     465
     466                // Add rating meta after comment is created
     467                if ($comment_id && !is_wp_error($comment_id)) {
     468                    update_comment_meta($comment_id, 'rating', $rating);
     469
     470                    // Mark as verified owner if user has purchased the product
     471                    if (wc_customer_bought_product($user_email, $current_user_id, $product_id)) {
     472                        update_comment_meta($comment_id, 'verified', true);
     473                    }
     474                }
    376475
    377476                if ($comment_id) {
     
    420519    }
    421520
    422     if ($show_review_box_singular == 1) {
    423         // Show the review boxes
    424         include(plugin_dir_path(__FILE__) . 'template/frontend/review-ratting-form.php');
    425     }
    426521
    427522    if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['reply_comment'])) {
     
    466561
    467562
    468     // }
    469 
    470563    if ($show_review_box_singular == 1) {
    471564        // Display existing average ratings
    472565        include(plugin_dir_path(__FILE__) . 'template/frontend/average-rating.php');
    473 
    474         // Display existing reviews with uploaded images and videos
    475         include(plugin_dir_path(__FILE__) . 'template/frontend/product-review-show.php');
    476     }
    477 }
    478 
     566    }
     567}
     568
     569
     570// Video Review AJAX handler (NEW)
     571add_action('wp_ajax_revnextwoo_upload_review_video', 'revnextwoo_upload_review_video');
     572add_action('wp_ajax_nopriv_revnextwoo_upload_review_video', 'revnextwoo_upload_review_video');
     573function revnextwoo_upload_review_video() {
     574    check_ajax_referer('revnextwoo_nonce', 'security');
     575    if (!isset($_FILES['review_video']) || !is_user_logged_in()) {
     576        wp_send_json_error(['message' => __('No video or not logged in.', 'review-next-for-woocommerce')]);
     577        wp_die();
     578    }
     579    $file = $_FILES['review_video'];
     580    // Validate file type and size
     581    $allowed_types = ['video/mp4', 'video/webm'];
     582    if (!in_array($file['type'], $allowed_types)) {
     583        wp_send_json_error(['message' => __('Invalid video type.', 'review-next-for-woocommerce')]);
     584        wp_die();
     585    }
     586    $max_video_size_mb = 50; // Hardcoded limit for free version
     587    if ($file['size'] > $max_video_size_mb * 1024 * 1024) {
     588        wp_send_json_error(['message' => sprintf(__('Video exceeds %dMB.', 'review-next-for-woocommerce'), $max_video_size_mb)]);
     589        wp_die();
     590    }
     591    // Upload to media library
     592    require_once(ABSPATH . 'wp-admin/includes/file.php');
     593    require_once(ABSPATH . 'wp-admin/includes/media.php');
     594    require_once(ABSPATH . 'wp-admin/includes/image.php');
     595    $attachment_id = media_handle_upload('review_video', 0);
     596    if (is_wp_error($attachment_id)) {
     597        wp_send_json_error(['message' => __('Video upload failed.', 'review-next-for-woocommerce')]);
     598        wp_die();
     599    }
     600    wp_send_json_success(['video_id' => $attachment_id, 'url' => wp_get_attachment_url($attachment_id)]);
     601    wp_die();
     602}
     603
     604// Save video attachment ID to comment meta (NEW, safe addition)
     605add_action('comment_post', function($comment_id) {
     606    if (isset($_POST['review_video_id']) && is_numeric($_POST['review_video_id'])) {
     607        add_comment_meta($comment_id, 'review_video', intval($_POST['review_video_id']));
     608    }
     609}, 20);
    479610
    480611// Define the AJAX action for logged-in users
     
    629760            echo '</div>';
    630761            echo '</div>';
    631             // Display star rating based on the "rating" value
     762            // Display star rating based on per-review rating value (fresh from meta)
    632763            echo '<div class="rating_start">';
    633             if ($rating > 0) {
     764            $review_rating = intval(get_comment_meta($comment->comment_ID, 'rating', true));
     765            if ($review_rating > 0) {
    634766                for ($i = 1; $i <= 5; $i++) {
    635                     $iconType = ($i <= $rating) ? 'filled' : 'empty';
     767                    $iconType = ($i <= $review_rating) ? 'filled' : 'empty';
    636768                    $starClass = $icon_classes[$rating_images][$iconType];
    637769                    echo '<i class="' . esc_attr($starClass) . '"></i>';
     
    661793            $comment_image_id = get_comment_meta($comment->comment_ID, 'revnextwoo_image_id', true);
    662794            $comment_image_url = get_comment_meta($comment->comment_ID, 'revnextwoo_image_url', true);
     795            // Display uploaded video for this comment (NEW)
     796            $review_video_id = get_comment_meta($comment->comment_ID, 'review_video', true);
     797            if (!empty($review_video_id)) {
     798                $video_url = wp_get_attachment_url($review_video_id);
     799                if ($video_url) {
     800                    echo '<div class="revnextwoo_review_video"><video style="margin: auto;" width="320" controls><source src="' . esc_url($video_url) . '" type="video/mp4">'.__('Your browser does not support the video tag.','review-next-for-woocommerce').'</video></div>';
     801                }
     802            }
    663803
    664804            if (!empty($comment_image_id)) {
     
    778918            echo '</div>'; // Close review-item
    779919        }
    780         // Display comment pagination links
    781         // echo '<div class="comment-pagination">';
    782         // paginate_comments_links();
    783         // echo '</div>';
    784920    } else {
    785921        echo '<p class="not_found"> No comments found.</p>';
     
    8851021    // Get the product ID
    8861022    global $product;
    887     $args = array(
    888         'post_id' => $product->get_id(),
    889         'type' => 'question',
    890         'status' => 'approve',
    891         'count' => true // return only the count
    892     );
    893     // Get the number of reviews for the product
    894     $question_count = get_comments($args);
     1023    // Count questions from custom QA table
     1024    $question_count = class_exists('Revnextwoo_QA_DB') ? Revnextwoo_QA_DB::count_questions($product->get_id()) : 0;
    8951025
    8961026    // Modify the tab title to include the review count
     
    9041034    return $tabs;
    9051035}
    906 add_filter('woocommerce_product_tabs', 'revnextwoo_add_question_tab');
    907 
    908 function revnextwoo_question_tab_content()
    909 {
    910     if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['question'])) {
    911         // Verify nonce
    912         if (!isset($_POST['question_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['question_nonce'])), 'submit_question_nonce')) {
    913             wp_die('Security check failed');
    914             return;
    915         }
    916 
    917         // Validate and sanitize question content
    918         $question = isset($_POST['question']) ? wp_kses_post(wp_unslash($_POST['question'])) : '';
    919         if (empty($question)) {
    920             wp_die('Question content is required');
    921             return;
    922         }
    923 
    924         $current_user_id = get_current_user_id();
    925         if (!$current_user_id) {
    926             wp_die('You must be logged in to submit a question');
    927             return;
    928         } // Create a new comment reply
    929         $question = sanitize_text_field(wp_unslash($_POST['question']));
    930         $current_user_id = get_current_user_id();
    931         $current_user = get_user_by('ID', $current_user_id);
    932         $display_name = !isset($current_user->display_name) ? '' : $current_user->display_name;
    933         $user_email = !isset($current_user->user_email) ? '' : $current_user->user_email;
    934         $author_url = !isset($current_user->user_url) ? '' : $current_user->user_url;
    935         $valid = revnextwoo_validate_question($question);
    936         if ($valid) {
    937             // Sanitize and validate data as needed
    938             $comment_post_ID = isset($_POST['comment_post_ID']) ? intval($_POST['comment_post_ID']) : 0;
    939             $comment_parent = isset($_POST['comment_parent']) ? intval($_POST['comment_parent']) : 0;
    940 
    941             $comment_data = array(
    942                 'comment_content' => $question,
    943                 'comment_post_ID' => $comment_post_ID,
    944                 'comment_parent' => $comment_parent,
    945                 'comment_author' => $display_name,
    946                 'comment_author_email' => $user_email,
    947                 'comment_author_url' => $author_url,
    948                 'comment_type' => 'question',
    949                 'user_id' => $current_user_id,
    950             );
    951 
    952             // Insert the reply comment
    953             wp_insert_comment($comment_data);
    954 
    955             echo '<p style="color:#F85606">Your question has been submitted successfully.</p>';
    956             wp_safe_redirect(esc_url(get_permalink()));
    957             exit;
    958         } else {
    959             echo '<p style="color: #F85606;">Your question should not contain contact information.</p>';
    960         }
    961     }
    962     if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['question_reply'])) {
    963         // Verify nonce
    964         if (!isset($_POST['question_reply_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['question_reply_nonce'])), 'submit_question_reply_nonce')) {
    965             wp_die('Security check failed');
    966         }
    967 
    968         // Create a new comment reply
    969         $question = sanitize_text_field(wp_unslash($_POST['question_reply']));
    970         $current_user_id = get_current_user_id();
    971         $current_user = get_user_by('ID', $current_user_id);
    972         $display_name = !isset($current_user->display_name) ? '' : $current_user->display_name;
    973         $user_email = !isset($current_user->user_email) ? '' : $current_user->user_email;
    974         $author_url = !isset($current_user->user_url) ? '' : $current_user->user_url;
    975         $valid = revnextwoo_validate_question($question);
    976         if ($valid) {
    977             // Sanitize and validate data as needed
    978             $comment_post_ID = isset($_POST['comment_post_ID']) ? intval($_POST['comment_post_ID']) : 0;
    979             $comment_parent = isset($_POST['comment_parent']) ? intval($_POST['comment_parent']) : 0;
    980 
    981             $comment_data = array(
    982                 'comment_content' => $question,
    983                 'comment_post_ID' => $comment_post_ID,
    984                 'comment_parent' => $comment_parent,
    985                 'comment_author' => $display_name,
    986                 'comment_author_email' => $user_email,
    987                 'comment_author_url' => $author_url,
    988                 'comment_type' => 'question_reply',
    989                 'user_id' => $current_user_id,
    990             );
    991 
    992             // Insert the reply comment
    993             wp_insert_comment($comment_data);
    994 
    995             echo '<p style="color:#F85606">Your question reply has been submitted successfully.</p>';
    996         } else {
    997             echo '<p style="color: #F85606;">Your question reply should not contain contact information.</p>';
    998         }
    999     }
     1036
     1037//-------AJAX handler for product questions pagination----------------------------
     1038add_action('wp_ajax_revnextwoo_load_questions', 'revnextwoo_load_questions_ajax_handler');
     1039add_action('wp_ajax_nopriv_revnextwoo_load_questions', 'revnextwoo_load_questions_ajax_handler');
     1040function revnextwoo_load_questions_ajax_handler() {
     1041    include plugin_dir_path(__FILE__) . 'template/frontend/user-question.php';
     1042    exit;
     1043}
     1044//-------Add the question tab to the product page---------------------------------
     1045add_filter('woocommerce_product_tabs', function($tabs) {
    10001046    $show_question_box_singular = get_option('revnextwoo_show_question_box_singular', '1');
    10011047    if ($show_question_box_singular == 1) {
     1048        return revnextwoo_add_question_tab($tabs);
     1049    }
     1050    return $tabs;
     1051});
     1052function revnextwoo_question_tab_content() {
     1053    if (get_option('revnextwoo_show_question_box_singular', '1') == 1) {
    10021054        include(plugin_dir_path(__FILE__) . 'template/frontend/user-question.php');
    10031055    }
    10041056}
    10051057
    1006 function revnextwoo_validate_question($question)
    1007 {
    1008     // Define regular expressions to match email, phone, and web links
    1009     $emailRegex = '/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}/';
    1010     $phoneRegex = '/\b\d{10,11}\b/';
    1011     $linkRegex = '/https?:\/\/[^\s\/$.?#]+[^\s]*/';
    1012 
    1013     // Check if the question matches any of the prohibited patterns
    1014     if (preg_match($emailRegex, $question) || preg_match($phoneRegex, $question) || preg_match($linkRegex, $question)) {
    1015         return false; // Question contains prohibited content
    1016     }
    1017 
    1018     return true; // Question is valid
    1019 }
    1020 
    1021 
    1022 
    1023 
    1024 // --------------------------- admin dashboard setting start -----------------------
    1025 function revnextwoo_custom_admin_menu()
    1026 {
    1027     // Create the main "Review" menu
    1028     add_menu_page(
    1029         'Review',
    1030         'Review',
    1031         'manage_options', // Capability required to access
    1032         'review-settings', // Menu slug
    1033         'revnextwoo_review_settings_page', // Callback function to display content
    1034         'dashicons-star-filled' // Icon (change to your preferred icon)
    1035     );
    1036     add_submenu_page(
    1037         'review-settings',
    1038         'Question',
    1039         'Question',
    1040         'manage_options',
    1041         'review-question',
    1042         'revnextwoo_question_setting_page'
    1043     );
    1044     // Create submenu pages
    1045     add_submenu_page(
    1046         'review-settings', // Parent menu slug
    1047         'General Setting', // Page title
    1048         'General Setting', // Menu title
    1049         'manage_options',
    1050         'review-general-setting', // Submenu slug
    1051         'revnextwoo_general_setting_page' // Callback function to display content
    1052     );
    1053 
    1054     add_submenu_page(
    1055         'review-settings',
    1056         'Styling',
    1057         'Styling',
    1058         'manage_options',
    1059         'review-styling-setting',
    1060         'revnextwoo_styling_setting_page'
    1061     );
    1062 
    1063     add_submenu_page(
    1064         'review-settings',
    1065         'Typography',
    1066         'Typography',
    1067         'manage_options',
    1068         'review-typography-setting',
    1069         'revnextwoo_typography_setting_page'
    1070     );
    1071 
    1072     add_submenu_page(
    1073         'review-settings',
    1074         'Advanced Setting',
    1075         'Advanced Setting',
    1076         'manage_options',
    1077         'review-advanced-setting',
    1078         'revnextwoo_advanced_setting_page'
    1079     );
    1080     // Add a submenu item under 'review-settings'
    1081     add_submenu_page(
    1082         'review-settings',
    1083         'Order Details',
    1084         'Order Details',
    1085         'manage_options',
    1086         'order-details',
    1087         'revnextwoo_custom_order_details_callback'
    1088     );
    1089 
    1090     // Add Email Reminder submenu
    1091     add_submenu_page(
    1092         'review-settings',
    1093         'Email Reminder',
    1094         'Email Reminder',
    1095         'manage_options',
    1096         'email-reminder',
    1097         'revnextwoo_email_reminder_callback'
    1098     );
    1099 }
    1100 // Hook the menu creation function
    1101 add_action('admin_menu', 'revnextwoo_custom_admin_menu');
    1102 // Callback functions to display content for each page
    1103 function revnextwoo_review_settings_page()
    1104 {
    1105     echo '<div class="wrap"><h2>RevNextWoo Settings</h2>';
    1106     echo '<p>This is the General Setting submenu.</p>';
    1107     // You can add your HTML content here
    1108     echo '</div>';
    1109 }
    1110 
    1111 function revnextwoo_custom_order_details_callback()
    1112 {
    1113     include_once(plugin_dir_path(__FILE__) . 'template/admin/order-count-details.php');
     1058
     1059// -------------- admin dashboard setting moved to class -----------------------
     1060require_once plugin_dir_path(__FILE__) . 'includes/class-revnextwoo-admin-menus.php';
     1061if (class_exists('Revnextwoo_Admin_Menus')) {
     1062    new Revnextwoo_Admin_Menus();
     1063}
     1064
     1065
     1066// Enqueue admin-tab.js only for Review Dashboard
     1067function revnextwoo_enqueue_review_dashboard_assets($hook) {
     1068    // Check if we are on the review dashboard admin page
     1069    if (isset($_GET['page']) && $_GET['page'] === 'review-dashboard') {
     1070        wp_enqueue_script(
     1071            'reviewnextwoo-admin-tab',
     1072            plugin_dir_url(__FILE__) . 'admin/js/admin-tab.js',
     1073            array('jquery'),
     1074            '1.0.0',
     1075            true
     1076        );
     1077        wp_enqueue_style(
     1078            'reviewnextwoo-admin-css',
     1079            plugin_dir_url(__FILE__) . 'admin/css/revnextwoo-admin.css',
     1080            array(),
     1081            '1.0.0'
     1082        );
     1083        wp_enqueue_style(
     1084            'revnextwoo-freemium-css',
     1085            plugin_dir_url(__FILE__) . 'admin/css/freemium.css',
     1086            array(),
     1087            '1.0.0'
     1088        );
     1089    }
     1090}
     1091add_action('admin_enqueue_scripts', 'revnextwoo_enqueue_review_dashboard_assets');
     1092
     1093// --- Notification Bell System for WooCommerce Account Dashboard ---
     1094require_once plugin_dir_path(__FILE__) . 'includes/class-revnextwoo-notification-bell.php';
     1095if (class_exists('Revnextwoo_Notification_Bell')) {
     1096    new Revnextwoo_Notification_Bell();
    11141097}
    11151098
     
    11221105{
    11231106    include(plugin_dir_path(__FILE__) . 'template/admin/all-question.php');
     1107}
     1108
     1109// Coupon Settings submenu callback
     1110function revnextwoo_coupon_settings_page()
     1111{
     1112
     1113    ob_start();
     1114    include_once(plugin_dir_path(__FILE__) . 'template/admin/coupon-settings.php');
     1115    $content = ob_get_clean();
     1116    echo $content;
    11241117}
    11251118
     
    11381131<?php
    11391132}
    1140 // Callback for the "Show Review Box on Singular Pages" field
    1141 function revnextwoo_show_review_box_singular_callback()
    1142 {
    1143     $show_revnextwoo_box_singular = get_option('revnextwoo_show_revnextwoo_box_singular', '1');
    1144 ?>
    1145     <input type="checkbox" name="revnextwoo_show_revnextwoo_box_singular" id="revnextwoo_show_revnextwoo_box_singular" value="1"
    1146         <?php checked($show_revnextwoo_box_singular, 1); ?> />
    1147     <label for="revnextwoo_show_revnextwoo_box_singular">Show review boxes in singular pages only</label>
    1148 <?php
     1133// Callback for the "Show Review section enabling" field
     1134function revnextwoo_show_review_box_singular_callback() {
     1135    $checked = checked(get_option('revnextwoo_show_revnextwoo_box_singular', 1), 1, false);
     1136    echo '<input type="checkbox" name="revnextwoo_show_revnextwoo_box_singular" id="revnextwoo_show_revnextwoo_box_singular" value="1" ' . $checked . ' />';
     1137    echo '<label for="revnextwoo_show_revnextwoo_box_singular">Show Review Section in Product Page</label>';
    11491138}
    11501139
     
    11561145    $choices = array(
    11571146        'no_one' => 'No one',
    1158         'registered_users_and_guests' => 'Registered users and guests',
     1147        'registered_users_and_guests' => 'Registered users',
    11591148        'completed_orders' => 'Completed Orders',
    11601149    );
     
    11731162    $rating_images = get_option('revnextwoo_rating_images', 'star'); // Default is 'star'
    11741163    $icon_choices = array(
    1175         'star'  => '<span class="dashicons dashicons-star-filled" style="color: gold;"></span> Star',
     1164        'star'  => '<span class="dashicons dashicons-star-filled" style="color: #ffc107;"></span> Star',
    11761165        'heart' => '<span class="dashicons dashicons-heart" style="color: red;"></span> Heart',
    1177         'thumb' => '<span class="dashicons dashicons-thumbs-up" style="color: blue;"></span> Thumbs-up',
    11781166    );
    11791167?>
     
    11861174}
    11871175
    1188 
    1189 
    1190 function revnextwoo_review_images_video_upload_option_show_callback()
    1191 {
    1192     $revnextwoo_images_video_upload = get_option('revnextwoo_review_images_video_upload', '1');
    1193 ?>
    1194     <input type="checkbox" name="revnextwoo_revnextwoo_images_video_upload" id="revnextwoo_revnextwoo_images_video_upload" value="1"
    1195         <?php checked($revnextwoo_images_video_upload, 1); ?> />
    1196     <label for="revnextwoo_revnextwoo_images_video_upload">Review images video upload option show</label>
    1197 <?php
    1198 }
     1176function revnextwoo_show_location_callback()
     1177{
     1178    ?>
     1179    <div class="revnextwoo-upsell-box">
     1180        <h4><?php _e('Switch to Pro', 'review-next-for-woocommerce'); ?></h4>
     1181        <p><?php _e('Upgrade to the Pro version to show customer location in reviews.', 'review-next-for-woocommerce'); ?></p>
     1182    </div>
     1183    <?php
     1184}
     1185
     1186
     1187
    11991188function revnextwoo_show_question_box_singular_callback()
    12001189{
     
    12031192    <input type="checkbox" name="revnextwoo_show_question_box_singular" id="revnextwoo_show_question_box_singular" value="1"
    12041193        <?php checked($show_question_box_singular, 1); ?> />
    1205     <label for="revnextwoo_show_question_box_singular">Show Question Box on Singular</label>
     1194    <label for="revnextwoo_show_question_box_singular">Show Question Answer section in Product Page</label>
    12061195<?php
    12071196}
     
    12111200{
    12121201    add_settings_section('general_settings_section', 'General Settings', 'revnextwoo_general_settings_section_callback', 'general-settings');
    1213     add_settings_field('show_revnextwoo_box_singular', 'Show Review Box on Singular Pages', 'revnextwoo_show_review_box_singular_callback', 'general-settings', 'general_settings_section');
     1202   
     1203    //show or hide Review section in prouct page
     1204    add_settings_field('show_revnextwoo_box_singular', 'Show Review Section in Product Page', 'revnextwoo_show_review_box_singular_callback', 'general-settings', 'general_settings_section');
     1205   
    12141206    add_settings_field('revnextwoo_allowed_to_rate', 'Who is allowed to rate', 'revnextwoo_allowed_to_rate_callback', 'general-settings', 'general_settings_section');
    12151207    add_settings_field('revnextwoo_rating_images', 'Rating Images', 'revnextwoo_rating_images_callback', 'general-settings', 'general_settings_section');
    1216     add_settings_field('revnextwoo_review_images_video_upload', 'Review images video upload option show', 'revnextwoo_review_images_video_upload_option_show_callback', 'general-settings', 'general_settings_section');
    1217     add_settings_field('revnextwoo_show_question_box_singular', 'Show Question Box on Singular', 'revnextwoo_show_question_box_singular_callback', 'general-settings', 'general_settings_section');
     1208    add_settings_field('revnextwoo_show_question_box_singular', 'Show Question Answer section in Product Page', 'revnextwoo_show_question_box_singular_callback', 'general-settings', 'general_settings_section');
     1209    add_settings_field('revnextwoo_show_location', 'Show Reviewer Location', 'revnextwoo_show_location_callback', 'general-settings', 'general_settings_section');
     1210
     1211    // Visitor Counter Checkbox
     1212    add_settings_field(
     1213        'revnextwoo_enable_visitor_counter',
     1214        __('Enable Visitor Counter on Product Pages', 'review-next-for-woocommerce'),
     1215        'revnextwoo_enable_visitor_counter_callback',
     1216        'general-settings',
     1217        'general_settings_section'
     1218    );
    12181219    register_setting('general_settings_group', 'revnextwoo_show_revnextwoo_box_singular', array(
    12191220        'sanitize_callback' => 'absint'
     
    12251226        'sanitize_callback' => 'sanitize_text_field'
    12261227    ));
    1227     register_setting('general_settings_group', 'revnextwoo_review_images_video_upload', array(
    1228         'sanitize_callback' => 'absint'
    1229     ));
     1228
     1229
    12301230    register_setting('general_settings_group', 'revnextwoo_show_question_box_singular', array(
    12311231        'sanitize_callback' => 'absint'
    12321232    ));
    1233 }
     1233
     1234    // Register Visitor Counter Setting (radio)
     1235    register_setting('general_settings_group', 'revnextwoo_visitor_counter_mode', array(
     1236        'sanitize_callback' => 'sanitize_text_field'
     1237    ));
     1238
     1239    // --- BEGIN: Review Pagination Settings ---
     1240    add_settings_field(
     1241        'revnextwoo_enable_review_pagination',
     1242        __('Enable Review Pagination', 'review-next-for-woocommerce'),
     1243        'revnextwoo_enable_review_pagination_callback',
     1244        'general-settings',
     1245        'general_settings_section'
     1246    );
     1247    // --- END: Review Pagination Settings ---
     1248}
     1249
     1250// Visitor Counter Checkbox Callback
     1251function revnextwoo_enable_visitor_counter_callback() {
     1252    ?>
     1253    <div class="revnextwoo-upsell-box">
     1254        <h4><?php _e('Switch to Pro', 'review-next-for-woocommerce'); ?></h4>
     1255        <p><?php _e('Upgrade to the Pro version to unlock the Visitor Counter feature.', 'review-next-for-woocommerce'); ?></p>
     1256    </div>
     1257    <?php
     1258}
     1259
     1260
     1261
     1262// --- BEGIN: Pagination Settings Callbacks ---
     1263function revnextwoo_enable_review_pagination_callback() {
     1264    ?>
     1265    <div class="revnextwoo-upsell-box">
     1266        <h4><?php _e('Switch to Pro', 'review-next-for-woocommerce'); ?></h4>
     1267        <p><?php _e('Upgrade to the Pro version to unlock AJAX Review Pagination.', 'review-next-for-woocommerce'); ?></p>
     1268    </div>
     1269    <?php
     1270}
     1271// --- END: Pagination Settings Callbacks ---
     1272
    12341273function revnextwoo_general_settings_section_callback()
    12351274{
     
    12391278// general setting end
    12401279
    1241 function revnextwoo_styling_setting_page()
    1242 {
    1243 ?>
     1280// Include product views table creation
     1281require_once plugin_dir_path(__FILE__) . 'inc/revnextwoo-product-views-table.php';
     1282// Include style settings class
     1283require_once plugin_dir_path(__FILE__) . 'admin/class-revnextwoo-style-settings.php';
     1284
     1285// Include analytics class
     1286require_once plugin_dir_path(__FILE__) . 'admin/class-revnextwoo-analytics.php';
     1287
     1288// Include social settings class
     1289require_once plugin_dir_path(__FILE__) . 'admin/class-revnextwoo-social-settings.php';
     1290
     1291// Initialize social settings
     1292if (class_exists('Revnextwoo_Social_Settings')) {
     1293    $revnextwoo_social_settings = new Revnextwoo_Social_Settings('review-next-for-woocommerce');
     1294}
     1295
     1296// Styling settings page callback
     1297function revnextwoo_styling_setting_page() {
     1298    ?>
    12441299    <div class="wrap">
    12451300        <div class="card" style='max-width:100%'>
    1246             <form method="post" action="options.php">
     1301            <?php settings_errors(); ?>
     1302            <form method="post" action="options.php" class="style-settings">
    12471303                <?php
    12481304                settings_fields('style_settings_group');
    12491305                do_settings_sections('style-settings');
    1250                 submit_button();
     1306                submit_button('Save Changes', 'primary', 'submit', false);
    12511307                ?>
    12521308            </form>
    12531309        </div>
    12541310    </div>
    1255 <?php
    1256 }
    1257 
    1258 function revnextwoo_review_rating_icon_color_callback()
    1259 {
    1260     $rating_icon_color = get_option('revnextwoo_rating_icon_color', '#cccccccc');
    1261 ?>
    1262     <input type="text" name="revnextwoo_rating_icon_color" class="review-color-field"
    1263            value="<?php echo esc_attr($rating_icon_color); ?>" data-default-color="#cccccc" />
    1264 <?php
    1265 }
    1266 function revnextwoo_review_rating_icon_active_color_callback()
    1267 {
    1268     $rating_icon_active_color = get_option('revnextwoo_rating_icon_active_color', '#ffc700');
    1269 ?>
    1270     <input type="text" name="revnextwoo_rating_icon_active_color" class="review-color-field"
    1271            value="<?php echo esc_attr($rating_icon_active_color); ?>" data-default-color="#ffc700" />
    1272 <?php
    1273 }
    1274 function revnextwoo_review_rating_input_box_border_color_callback()
    1275 {
    1276     $rating_input_box_border_color = get_option('revnextwoo_rating_input_box_border_color', '#F85606');
    1277 ?>
    1278     <input type="text" name="revnextwoo_rating_input_box_border_color" class="review-color-field"
    1279            value="<?php echo esc_attr($rating_input_box_border_color); ?>" data-default-color="#F85606" />
    1280 <?php
    1281 }
    1282 function revnextwoo_review_rating_form_button_bg_callback()
    1283 {
    1284     $rating_form_button_bg = get_option('revnextwoo_rating_form_button_bg', '#F85606');
    1285 ?>
    1286     <input type="text" name="revnextwoo_rating_form_button_bg" class="review-color-field"
    1287            value="<?php echo esc_attr($rating_form_button_bg); ?>" data-default-color="#F85606" />
    1288 <?php
    1289 }
    1290 function revnextwoo_review_box_outer_border_callback()
    1291 {
    1292     $box_outer_border = get_option('revnextwoo_box_outer_border', '#e2eded');
    1293 ?>
    1294     <input type="text" name="revnextwoo_box_outer_border" class="review-color-field"
    1295            value="<?php echo esc_attr($box_outer_border); ?>" data-default-color="#e2eded" />
    1296 <?php
    1297 }
    1298 function revnextwoo_review_box_header_footer_bg_callback()
    1299 {
    1300     $box_header_footer_bg = get_option('revnextwoo_box_header_footer_bg', '#f2f2f2');
    1301 ?>
    1302     <input type="text" name="revnextwoo_box_header_footer_bg" class="review-color-field"
    1303            value="<?php echo esc_attr($box_header_footer_bg); ?>" data-default-color="#f2f2f2" />
    1304 <?php
    1305 }
    1306 function revnextwoo_review_item_background_callback()
    1307 {
    1308     $item_bg = get_option('revnextwoo_item_bg', '#fff');
    1309 ?>
    1310     <input type="text" name="revnextwoo_item_bg" class="review-color-field"
    1311            value="<?php echo esc_attr($item_bg); ?>" data-default-color="#fff" />
    1312 <?php
    1313 }
    1314 function revnextwoo_final_score_percentage_bar_background_callback()
    1315 {
    1316     $final_score_percentage_bar_bg = get_option('revnextwoo_final_score_percentage_bar_bg', '030303');
    1317 ?>
    1318     <input type="text" name="revnextwoo_final_score_percentage_bar_bg" class="review-color-field"
    1319         value="<?php echo esc_attr($final_score_percentage_bar_bg); ?>" data-default-color="#ff0000" />
    1320 <?php
    1321 }
    1322 function revnextwoo_link_color_callback()
    1323 {
    1324     $link_color = get_option('revnextwoo_link_color', '#030303');
    1325 ?>
    1326     <input type="text" name="revnextwoo_link_color" class="review-color-field"
    1327            value="<?php echo esc_attr($link_color); ?>" data-default-color="#030303" />
    1328 <?php
    1329 }
    1330 function revnextwoo_link_decoration_callback()
    1331 {
    1332     $link_decoration = get_option('revnextwoo_review_link_decoration');
    1333 ?>
    1334     <input type="text" name="revnextwoo_review_link_decoration" class="review-color-field"
    1335         value="<?php echo esc_attr($link_decoration); ?>" data-default-color="#ff0000" />
    1336 <?php
    1337 }
    1338 function revnextwoo_link_color_on_mouse_over_callback()
    1339 {
    1340     $revnextwoo_link_color_on_mouse_over = get_option('revnextwoo_link_color_on_mouse_over', '#ffc700');
    1341 ?>
    1342     <input type="text" name="revnextwoo_link_color_on_mouse_over" class="review-color-field"
    1343         value="<?php echo esc_attr($revnextwoo_link_color_on_mouse_over); ?>" data-default-color="#ffc700" />
    1344 <?php
    1345 }
    1346 function revnextwoo_link_decoration_on_mouse_over_callback()
    1347 {
    1348     $link_decoration_hover = get_option('revnextwoo_review_link_decoration_on_mouse_over');
    1349 ?>
    1350     <input type="text" name="revnextwoo_review_link_decoration_on_mouse_over" class="review-color-field"
    1351         value="<?php echo esc_attr($link_decoration_hover); ?>" data-default-color="#ff0000" />
    1352 <?php
    1353 }
    1354 
    1355 function revnextwoo_style_settings_fields()
    1356 {
    1357     add_settings_section('style_settings_section', 'Style Settings', 'revnextwoo_style_settings_section_callback', 'style-settings');
    1358     add_settings_field('revnextwoo_review_rating_icon_color', 'Review rating form icon Color', 'revnextwoo_review_rating_icon_color_callback', 'style-settings', 'style_settings_section');
    1359     add_settings_field('revnextwoo_review_rating_icon_active_color', 'Review rating form icon active Color', 'revnextwoo_review_rating_icon_active_color_callback', 'style-settings', 'style_settings_section');
    1360     add_settings_field('revnextwoo_review_rating_input_box_border_color', 'Review rating form input box border color', 'revnextwoo_review_rating_input_box_border_color_callback', 'style-settings', 'style_settings_section');
    1361     add_settings_field('revnextwoo_review_rating_form_button_bg', 'Review rating form button Color', 'revnextwoo_review_rating_form_button_bg_callback', 'style-settings', 'style_settings_section');
    1362     add_settings_field('revnextwoo_review_box_outer_border', 'Review Box Outer Border', 'revnextwoo_review_box_outer_border_callback', 'style-settings', 'style_settings_section');
    1363     add_settings_field('revnextwoo_review_box_header_footer_bg', 'Review Box Header & Footer Background', 'revnextwoo_review_box_header_footer_bg_callback', 'style-settings', 'style_settings_section');
    1364     add_settings_field('revnextwoo_review_item_bg', 'Review Item Background', 'revnextwoo_review_item_background_callback', 'style-settings', 'style_settings_section');
    1365     add_settings_field('revnextwoo_final_score_percentage_bar_bg', 'Final Score and Percentage bar Background', 'revnextwoo_final_score_percentage_bar_background_callback', 'style-settings', 'style_settings_section');
    1366     add_settings_field('revnextwoo_link_color', 'Link Color', 'revnextwoo_link_color_callback', 'style-settings', 'style_settings_section');
    1367     add_settings_field('revnextwoo_review_link_decoration', 'Link Decoration', 'revnextwoo_link_decoration_callback', 'style-settings', 'style_settings_section');
    1368     add_settings_field('revnextwoo_link_color_on_mouse_over', 'Link color on mouse over', 'revnextwoo_link_color_on_mouse_over_callback', 'style-settings', 'style_settings_section');
    1369     add_settings_field('revnextwoo_review_link_decoration_on_mouse_over', 'Link decoration on mouse over', 'revnextwoo_link_decoration_on_mouse_over_callback', 'style-settings', 'style_settings_section');
    1370     // Helper function to sanitize color values
    1371     $sanitize_color = function ($color) {
    1372         $color = sanitize_text_field($color);
    1373         // Remove any whitespace
    1374         $color = trim($color);
    1375         // Validate hex color format
    1376         if (
    1377             preg_match('/#([a-fA-F0-9]{3}){1,2}\b/', $color) ||
    1378             preg_match('/^rgba?\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*(?:,\s*[\d.]+\s*)?\)$/', $color)
    1379         ) {
    1380             return $color;
    1381         }
    1382         return '';
    1383     };
    1384 
    1385     register_setting('style_settings_group', 'revnextwoo_review_rating_icon_color', array(
    1386         'sanitize_callback' => $sanitize_color
    1387     ));
    1388     register_setting('style_settings_group', 'revnextwoo_review_rating_icon_active_color', array(
    1389         'sanitize_callback' => $sanitize_color
    1390     ));
    1391     register_setting('style_settings_group', 'revnextwoo_review_rating_input_box_border_color', array(
    1392         'sanitize_callback' => $sanitize_color
    1393     ));
    1394     register_setting('style_settings_group', 'revnextwoo_review_rating_form_button_bg', array(
    1395         'sanitize_callback' => $sanitize_color
    1396     ));
    1397     register_setting('style_settings_group', 'revnextwoo_review_box_outer_border', array(
    1398         'sanitize_callback' => $sanitize_color
    1399     ));
    1400     register_setting('style_settings_group', 'revnextwoo_review_box_header_footer_bg', array(
    1401         'sanitize_callback' => $sanitize_color
    1402     ));
    1403     register_setting('style_settings_group', 'revnextwoo_review_item_bg', array(
    1404         'sanitize_callback' => $sanitize_color
    1405     ));
    1406     register_setting('style_settings_group', 'revnextwoo_final_score_percentage_bar_bg', array(
    1407         'sanitize_callback' => $sanitize_color
    1408     ));
    1409     register_setting('style_settings_group', 'revnextwoo_link_color', array(
    1410         'sanitize_callback' => $sanitize_color
    1411     ));
    1412     register_setting('style_settings_group', 'revnextwoo_review_link_decoration', array(
    1413         'sanitize_callback' => 'sanitize_text_field'
    1414     ));
    1415     register_setting('style_settings_group', 'revnextwoo_link_color_on_mouse_over', array(
    1416         'sanitize_callback' => $sanitize_color
    1417     ));
    1418     register_setting('style_settings_group', 'revnextwoo_review_link_decoration_on_mouse_over', array(
    1419         'sanitize_callback' => 'sanitize_text_field'
    1420     ));
    1421 }
    1422 function revnextwoo_style_settings_section_callback()
    1423 {
    1424     echo '<p>Customize the appearance of your review plugin.</p>';
    1425 }
    1426 add_action('admin_init', 'revnextwoo_style_settings_fields');
    1427 
    1428 function revnextwoo_typography_setting_page()
    1429 {
    1430 ?>
    1431     <div class="wrap">
    1432         <div class="card" style='max-width:100%'>
    1433             <form method="post" action="options.php">
    1434                 <?php
    1435                 settings_fields('typography_settings_group');
    1436                 do_settings_sections('typography-settings');
    1437                 ?>
    1438                 <?php submit_button(); ?>
    1439             </form>
    1440         </div>
    1441     </div>
    1442 <?php
    1443 }
    1444 function revnextwoo_review_box_title_callback()
    1445 {
    1446     $box_title_family = get_option('revnextwoo_box_title_family', 'Arial, sans-serif');
    1447     $google_fonts = array(
    1448         'Arial, sans-serif' => 'Arial',
    1449         'Helvetica, sans-serif' => 'Helvetica',
    1450         'Times New Roman, serif' => 'Times New Roman',
    1451         // Add more Google Font families here
    1452     );
    1453     $box_title_size = get_option('revnextwoo_box_title_size', '25');
    1454     $box_title_weight = get_option('revnextwoo_box_title_weight', '600');
    1455 ?>
    1456 
    1457     <label for="revnextwoo_box_title_size">Size(px):</label>
    1458     <input type="number" name="revnextwoo_box_title_size" min="10" max="50"
    1459         value="<?php echo esc_attr($box_title_size); ?>" />
    1460 
    1461     <label for="revnextwoo_box_title_weight">Weight:</label>
    1462     <input type="text" name="revnextwoo_box_title_weight" class="review_box_title_weight"
    1463         value="<?php echo esc_attr($box_title_weight); ?>" />
    1464     <label for="revnextwoo_box_title_family">Select Family:</label>
    1465     <select name="revnextwoo_box_title_family">
    1466         <?php
    1467         foreach ($google_fonts as $font_value => $font_name) {
    1468             $is_selected = $box_title_family === $font_value ? ' selected="selected"' : '';
    1469             echo sprintf(
    1470                 '<option value="%s"%s>%s</option>',
    1471                 esc_attr($font_value),
    1472                 esc_attr($is_selected),
    1473                 esc_html($font_name)
    1474             );
    1475         }
    1476         ?>
    1477     </select>
    1478 
    1479 <?php
    1480 }
    1481 
    1482 function revnextwoo_review_box_header_footer_callback()
    1483 {
    1484     $header_footer_family = get_option('revnextwoo_box_header_footer_family', 'Arial, sans-serif');
    1485     $google_fonts = array(
    1486         'Arial, sans-serif' => 'Arial',
    1487         'Helvetica, sans-serif' => 'Helvetica',
    1488         'Times New Roman, serif' => 'Times New Roman',
    1489         // Add more Google Font families here
    1490     );
    1491     $header_footer_size = get_option('revnextwoo_box_header_footer_size', '19');
    1492     $header_footer_weight = get_option('revnextwoo_box_header_footer_weight', '500');
    1493 ?>
    1494     <label for="revnextwoo_box_header_footer_size">Size(px):</label>
    1495     <input type="number" name="revnextwoo_box_header_footer_size" min="10" max="50"
    1496            value="<?php echo esc_attr($header_footer_size); ?>" />
    1497 
    1498     <label for="revnextwoo_box_header_footer_weight">Weight:</label>
    1499     <input type="text" name="revnextwoo_box_header_footer_weight" class="review_box_header_footer_weight"
    1500            value="<?php echo esc_attr($header_footer_weight); ?>" />
    1501     <label for="revnextwoo_box_header_footer_family">Select Family:</label>
    1502     <select name="revnextwoo_box_header_footer_family">
    1503         <?php
    1504         foreach ($google_fonts as $font_value => $font_name) {
    1505             $is_selected = $header_footer_family === $font_value ? ' selected="selected"' : '';
    1506             echo sprintf(
    1507                 '<option value="%s"%s>%s</option>',
    1508                 esc_attr($font_value),
    1509                 esc_attr($is_selected),
    1510                 esc_html($font_name)
    1511             );
    1512         }
    1513         ?>
    1514     </select>
    1515 
    1516 <?php
    1517 }
    1518 function revnextwoo_review_summary_callback()
    1519 {
    1520     $summary_family = get_option('revnextwoo_summary_family', 'Arial, sans-serif');
    1521     $google_fonts = array(
    1522         'Arial, sans-serif' => 'Arial',
    1523         'Helvetica, sans-serif' => 'Helvetica',
    1524         'Times New Roman, serif' => 'Times New Roman',
    1525         // Add more Google Font families here
    1526     );
    1527     $summary_size = get_option('revnextwoo_summary_size', '15');
    1528     $summary_weight = get_option('revnextwoo_summary_weight', '400');
    1529 ?>
    1530 
    1531     <label for="revnextwoo_summary_size">Size(px):</label>
    1532     <input type="number" name="revnextwoo_summary_size" min="10" max="50"
    1533            value="<?php echo esc_attr($summary_size); ?>" />
    1534 
    1535     <label for="revnextwoo_summary_weight">Weight:</label>
    1536     <input type="text" name="revnextwoo_summary_weight" class="review_summary_weight"
    1537            value="<?php echo esc_attr($summary_weight); ?>" />
    1538     <label for="revnextwoo_summary_family">Select Family:</label>
    1539     <select name="revnextwoo_summary_family">
    1540         <?php
    1541         foreach ($google_fonts as $font_value => $font_name) {
    1542             $is_selected = $summary_family === $font_value ? ' selected="selected"' : '';
    1543             echo sprintf(
    1544                 '<option value="%s"%s>%s</option>',
    1545                 esc_attr($font_value),
    1546                 esc_attr($is_selected),
    1547                 esc_html($font_name)
    1548             );
    1549         }
    1550         ?>
    1551     </select>
    1552 
    1553 <?php
    1554 }
    1555 function revnextwoo_total_score_callback()
    1556 {
    1557     $total_score_family = get_option('revnextwoo_total_score_family', 'Arial, sans-serif');
    1558     $google_fonts = array(
    1559         'Arial, sans-serif' => 'Arial',
    1560         'Helvetica, sans-serif' => 'Helvetica',
    1561         'Times New Roman, serif' => 'Times New Roman',
    1562         // Add more Google Font families here
    1563     );
    1564     $total_score_size = get_option('revnextwoo_total_score_size', '48');
    1565     $total_score_weight = get_option('revnextwoo_total_score_weight', '400');
    1566 ?>
    1567 
    1568     <label for="revnextwoo_total_score_size">Size(px):</label>
    1569     <input type="text" name="revnextwoo_total_score_size" class="total_score_size"
    1570         value="<?php echo esc_attr($total_score_size); ?>" />
    1571 
    1572     <label for="revnextwoo_total_score_weight">Weight:</label>
    1573     <input type="text" name="revnextwoo_total_score_weight" class="total_score_weight"
    1574         value="<?php echo esc_attr($total_score_weight); ?>" />
    1575     <label for="revnextwoo_total_score_family">Select Family:</label>
    1576     <select name="revnextwoo_total_score_family">
    1577         <?php
    1578         foreach ($google_fonts as $font => $font_name) {
    1579             $selected = ($total_score_family === $font) ? 'selected' : '';
    1580             echo sprintf(
    1581                 '<option value="%s"%s>%s</option>',
    1582                 esc_attr($font),
    1583                 esc_attr($selected),
    1584                 esc_html($font_name)
    1585             );
    1586         }
    1587         ?>
    1588     </select>
    1589 
    1590 <?php
    1591 }
    1592 function revnextwoo_user_rating_icon_callback()
    1593 {
    1594     $user_rating_icon_family = get_option('revnextwoo_user_rating_icon_family', 'Arial, sans-serif');
    1595     $google_fonts = array(
    1596         'Arial, sans-serif' => 'Arial',
    1597         'Helvetica, sans-serif' => 'Helvetica',
    1598         'Times New Roman, serif' => 'Times New Roman',
    1599         // Add more Google Font families here
    1600     );
    1601     $user_rating_icon_size = get_option('revnextwoo_user_rating_icon_size', '15');
    1602     $user_rating_icon_weight = get_option('revnextwoo_user_rating_icon_weight', '900');
    1603 ?>
    1604 
    1605     <label for="revnextwoo_user_rating_icon_size">Size(px):</label>
    1606     <input type="text" name="revnextwoo_user_rating_icon_size" class="user_rating_icon_size"
    1607         value="<?php echo esc_attr($user_rating_icon_size); ?>" />
    1608 
    1609     <label for="revnextwoo_user_rating_icon_weight">Weight:</label>
    1610     <input type="text" name="revnextwoo_user_rating_icon_weight" class="user_rating_icon_weight"
    1611         value="<?php echo esc_attr($user_rating_icon_weight); ?>" />
    1612     <label for="revnextwoo_user_rating_icon_family">Select Family:</label>
    1613     <select name="revnextwoo_user_rating_icon_family">
    1614         <?php
    1615         foreach ($google_fonts as $font => $font_name) {
    1616             $selected = ($user_rating_icon_family === $font) ? 'selected' : '';
    1617             echo sprintf(
    1618                 '<option value="%s"%s>%s</option>',
    1619                 esc_attr($font),
    1620                 esc_attr($selected),
    1621                 esc_html($font_name)
    1622             );
    1623         }
    1624         ?>
    1625     </select>
    1626 
    1627 <?php
    1628 }
    1629 function revnextwoo_typography_settings_fields()
    1630 {
    1631     add_settings_section('typography_settings_section', 'Typography Settings', 'revnextwoo_typography_settings_section_callback', 'typography-settings');
    1632     add_settings_field('user_rating_icon', 'User Rating icon', 'revnextwoo_user_rating_icon_callback', 'typography-settings', 'typography_settings_section');
    1633     add_settings_field('review_box_title', 'Review box title', 'revnextwoo_review_box_title_callback', 'typography-settings', 'typography_settings_section');
    1634     add_settings_field('review_box_header_footer', 'Review box header & footer', 'revnextwoo_review_box_header_footer_callback', 'typography-settings', 'typography_settings_section');
    1635     add_settings_field('total_score', 'Average Total Score', 'revnextwoo_total_score_callback', 'typography-settings', 'typography_settings_section');
    1636     add_settings_field('review_summary', 'Review Summary', 'revnextwoo_review_summary_callback', 'typography-settings', 'typography_settings_section');
    1637 
    1638     // Helper function to sanitize font family
    1639     $sanitize_font_family = function ($font) {
    1640         $allowed_fonts = array(
    1641             'Arial, sans-serif',
    1642             'Helvetica, sans-serif',
    1643             'Times New Roman, serif'
    1644         );
    1645         $font = sanitize_text_field($font);
    1646         return in_array($font, $allowed_fonts) ? $font : 'Arial, sans-serif';
    1647     };
    1648 
    1649     // Helper function to sanitize font size and weight
    1650     $sanitize_numeric = function ($value) {
    1651         return absint($value);
    1652     };
    1653 
    1654     // Register settings for typography
    1655     register_setting('typography_settings_group', 'revnextwoo_user_rating_icon_size', array(
    1656         'sanitize_callback' => $sanitize_numeric
    1657     ));
    1658     register_setting('typography_settings_group', 'revnextwoo_user_rating_icon_weight', array(
    1659         'sanitize_callback' => $sanitize_numeric
    1660     ));
    1661     register_setting('typography_settings_group', 'revnextwoo_user_rating_icon_family', array(
    1662         'sanitize_callback' => $sanitize_font_family
    1663     ));
    1664     register_setting('typography_settings_group', 'revnextwoo_review_box_title_size', array(
    1665         'sanitize_callback' => $sanitize_numeric
    1666     ));
    1667     register_setting('typography_settings_group', 'revnextwoo_review_box_title_weight', array(
    1668         'sanitize_callback' => $sanitize_numeric
    1669     ));
    1670     register_setting('typography_settings_group', 'revnextwoo_review_box_title_family', array(
    1671         'sanitize_callback' => $sanitize_font_family
    1672     ));
    1673     register_setting('typography_settings_group', 'revnextwoo_review_box_header_footer_size', array(
    1674         'sanitize_callback' => $sanitize_numeric
    1675     ));
    1676     register_setting('typography_settings_group', 'revnextwoo_review_box_header_footer_weight', array(
    1677         'sanitize_callback' => $sanitize_numeric
    1678     ));
    1679     register_setting('typography_settings_group', 'revnextwoo_review_box_header_footer_family', array(
    1680         'sanitize_callback' => $sanitize_font_family
    1681     ));
    1682     register_setting('typography_settings_group', 'revnextwoo_total_score_size', array(
    1683         'sanitize_callback' => $sanitize_numeric
    1684     ));
    1685     register_setting('typography_settings_group', 'revnextwoo_total_score_weight', array(
    1686         'sanitize_callback' => $sanitize_numeric
    1687     ));
    1688     register_setting('typography_settings_group', 'revnextwoo_total_score_family', array(
    1689         'sanitize_callback' => $sanitize_font_family
    1690     ));
    1691     register_setting('typography_settings_group', 'revnextwoo_summary_size', array(
    1692         'sanitize_callback' => $sanitize_numeric
    1693     ));
    1694     register_setting('typography_settings_group', 'revnextwoo_summary_weight', array(
    1695         'sanitize_callback' => $sanitize_numeric
    1696     ));
    1697     register_setting('typography_settings_group', 'revnextwoo_summary_family', array(
    1698         'sanitize_callback' => $sanitize_font_family
    1699     ));
    1700 }
    1701 function revnextwoo_typography_settings_section_callback()
    1702 {
    1703     echo '<p>Customize the typography of your review plugin.</p>';
    1704 }
    1705 add_action('admin_init', 'revnextwoo_typography_settings_fields');
    1706 
    1707 
    1708 function revnextwoo_advanced_setting_page()
    1709 {
    1710     echo '<div class="wrap">';
    1711     echo '<h2>Advanced Settings</h2>';
    1712 
    1713     echo '<h2 class="nav-tab-wrapper review-advanced-setting-wrapper">';
    1714     echo '<a class="nav-tab nav-tab-active" href="#tab-1">Review Format</a>';
    1715     echo '<a class="nav-tab" href="#tab-2">Email Reminder</a>';
    1716     echo '<a class="nav-tab" href="#tab-3">Order Count</a>';
    1717     echo '<a class="nav-tab" href="#tab-4">Top Order Customer</a>';
    1718     echo '</h2>';
    1719 
    1720     echo '<div class="tab-content">';
    1721     echo '<div id="tab-1" class="tab-pane active">';
    1722     revnextwoo_advanced_tab_1_settings_page();
    1723     echo '</div>';
    1724 
    1725     echo '<div id="tab-2" class="tab-pane">';
    1726     revnextwoo_advanced_tab_2_settings_page();
    1727     echo '</div>';
    1728 
    1729     echo '<div id="tab-3" class="tab-pane">';
    1730     revnextwoo_advanced_tab_3_settings_page();
    1731     echo '</div>';
    1732 
    1733     echo '<div id="tab-4" class="tab-pane">';
    1734     revnextwoo_advanced_tab_4_settings_page();
    1735     echo '</div>';
    1736     echo '</div>';
    1737 
    1738     echo '</div>';
    1739 }
    1740 
    1741 function revnextwoo_advanced_tab_1_settings_page()
    1742 {
    1743 ?>
    1744     <div class="wrap">
    1745         <form method="post" action="options.php">
    1746             <?php
    1747             settings_fields('advanced_tab1_settings_group');
    1748             do_settings_sections('advanced-tab1-settings');
    1749             submit_button();
    1750             ?>
    1751         </form>
    1752     </div>
    1753 <?php
    1754 }
    1755 function revnextwoo_advanced_tab1_settings_fields()
    1756 {
    1757     // Tab 1
    1758     add_settings_section('advanced_setting_tab1_section', 'Review Formate Setting', 'revnextwoo_advanced_setting_tab1_section_callback', 'advanced-tab1-settings');
    1759     add_settings_field('review_rating_formate', 'Review Formate', 'revnextwoo_review_rating_formate_callback', 'advanced-tab1-settings', 'advanced_setting_tab1_section');
    1760     register_setting('advanced_tab1_settings_group', 'revnextwoo_review_rating_formate', array(
    1761         'sanitize_callback' => 'sanitize_text_field'
    1762     ));
    1763 }
    1764 
    1765 add_action('admin_init', 'revnextwoo_advanced_tab1_settings_fields');
    1766 function revnextwoo_advanced_setting_tab1_section_callback()
    1767 {
    1768     echo '<p>Choose your preferred rating format:</p>';
    1769 }
    1770 
    1771 function revnextwoo_review_rating_formate_callback()
    1772 {
    1773     $image_url = plugins_url('admin/images/', __FILE__);
    1774 
    1775 
    1776     $rating_formate = get_option('revnextwoo_review_rating_formate', 'half_star'); // Default is 'star'
    1777     // Get image attachments for review formats
    1778     $icon_choices = array(
    1779         'no_rating_star' => wp_get_attachment_image(get_option('no_rating_star_image_id'), 'thumbnail', false, array('class' => 'user_img', 'alt' => 'review-format-img')),
    1780         'half_star' => wp_get_attachment_image(get_option('half_star_image_id'), 'thumbnail', false, array('class' => 'user_img', 'alt' => 'review-format-img')),
    1781         'full_star' => wp_get_attachment_image(get_option('full_star_image_id'), 'thumbnail', false, array('class' => 'user_img', 'alt' => 'review-format-img')),
    1782         'five_star' => wp_get_attachment_image(get_option('five_star_image_id'), 'thumbnail', false, array('class' => 'user_img', 'alt' => 'review-format-img')),
    1783     );
    1784 
    1785     // Fallback to direct image URLs if attachment IDs not set
    1786     if (empty($icon_choices['no_rating_star'])) {
    1787         $attachment_id = attachment_url_to_postid($image_url . 'no_rating.png');
    1788         if ($attachment_id) {
    1789             $icon_choices['no_rating_star'] = wp_get_attachment_image($attachment_id, 'thumbnail', false, array('class' => 'user_img', 'alt' => 'review-format-img'));
    1790         } else {
    1791             // phpcs:ignore PluginCheck.CodeAnalysis.ImageFunctions.NonEnqueuedImage -- Fallback for admin settings images
    1792             $icon_choices['no_rating_star'] = '<img class="user_img" src="' . esc_url($image_url . 'no_rating.png') . '" alt="review-format-img">';
    1793         }
    1794     }
    1795     if (empty($icon_choices['half_star'])) {
    1796         $attachment_id = attachment_url_to_postid($image_url . 'half_star.png');
    1797         if ($attachment_id) {
    1798             $icon_choices['half_star'] = wp_get_attachment_image($attachment_id, 'thumbnail', false, array('class' => 'user_img', 'alt' => 'review-format-img'));
    1799         } else {
    1800             // phpcs:ignore PluginCheck.CodeAnalysis.ImageFunctions.NonEnqueuedImage -- Fallback for admin settings images
    1801             $icon_choices['half_star'] = '<img class="user_img" src="' . esc_url($image_url . 'half_star.png') . '" alt="review-format-img">';
    1802         }
    1803     }
    1804     if (empty($icon_choices['full_star'])) {
    1805         $attachment_id = attachment_url_to_postid($image_url . 'full_star.png');
    1806         if ($attachment_id) {
    1807             $icon_choices['full_star'] = wp_get_attachment_image($attachment_id, 'thumbnail', false, array('class' => 'user_img', 'alt' => 'review-format-img'));
    1808         } else {
    1809             // phpcs:ignore PluginCheck.CodeAnalysis.ImageFunctions.NonEnqueuedImage -- Fallback for admin settings images
    1810             $icon_choices['full_star'] = '<img class="user_img" src="' . esc_url($image_url . 'full_star.png') . '" alt="review-format-img">';
    1811         }
    1812     }
    1813     if (empty($icon_choices['five_star'])) {
    1814         $attachment_id = attachment_url_to_postid($image_url . 'five_star.png');
    1815         if ($attachment_id) {
    1816             $icon_choices['five_star'] = wp_get_attachment_image($attachment_id, 'thumbnail', false, array('class' => 'user_img', 'alt' => 'review-format-img'));
    1817         } else {
    1818             // phpcs:ignore PluginCheck.CodeAnalysis.ImageFunctions.NonEnqueuedImage -- Fallback for admin settings images
    1819             $icon_choices['five_star'] = '<img class="user_img" src="' . esc_url($image_url . 'five_star.png') . '" alt="review-format-img">';
    1820         }
    1821     }
    1822 ?>
    1823     <?php foreach ($icon_choices as $value => $label) { ?>
    1824         <div class="input_group">
    1825             <input type="radio" id="revnextwoo_review_rating_formate<?php echo esc_attr($value); ?>" name="revnextwoo_review_rating_formate"
    1826                 value="<?php echo esc_attr($value); ?>" <?php checked($rating_formate, $value); ?> />
    1827             <label for="revnextwoo_review_rating_formate<?php echo esc_attr($value); ?>"><?php echo wp_kses_post($label); ?></label><br>
    1828         </div>
    1829     <?php } ?>
    1830 <?php
    1831 }
    1832 function revnextwoo_advanced_tab_2_settings_page()
    1833 {
    1834 ?>
    1835     <div class="wrap">
    1836         <form method="post" action="options.php">
    1837             <?php
    1838             settings_fields('advanced_tab2_settings_group');
    1839             do_settings_sections('advanced-tab2-settings');
    1840             submit_button();
    1841             ?>
    1842         </form>
    1843     </div>
    1844 <?php
    1845 }
    1846 
     1311    <?php
     1312}
    18471313function revnextwoo_advanced_tab_3_settings_page()
    18481314{
     
    18501316}
    18511317
     1318// Advanced Settings (CAS) - moved to class-revnextwoo-advanced-settings.php
     1319require_once plugin_dir_path(__FILE__) . 'admin/class-revnextwoo-advanced-settings.php';
     1320if (class_exists('RevNextWoo_Advanced_Settings')) {
     1321    new RevNextWoo_Advanced_Settings();
     1322}
     1323
     1324
    18521325function revnextwoo_advanced_tab_4_settings_page()
    18531326{
    18541327    include plugin_dir_path(__FILE__) . 'template/admin/top-order-customer.php';
    1855 }
    1856 function revnextwoo_advanced_tab2_settings_fields()
    1857 {
    1858     // Tab 2
    1859     add_settings_section('advanced_setting_tab2_section', 'Email Reminder', 'revnextwoo_advanced_setting_tab2_section_callback', 'advanced-tab2-settings');
    1860     add_settings_field('review_email_reminder', 'Set Reminder Time', 'revnextwoo_review_email_reminder_callback', 'advanced-tab2-settings', 'advanced_setting_tab2_section');
    1861     register_setting('advanced_tab2_settings_group', 'revnextwoo_review_email_reminder', array(
    1862         'sanitize_callback' => function ($value) {
    1863             $allowed_values = array('3', '7');
    1864             $value = sanitize_text_field($value);
    1865             // Allow custom values that are positive integers
    1866             if (!in_array($value, $allowed_values) && !ctype_digit($value)) {
    1867                 return '3'; // Default value
    1868             }
    1869             return $value;
    1870         }
    1871     ));
    1872 }
    1873 
    1874 add_action('admin_init', 'revnextwoo_advanced_tab2_settings_fields');
    1875 function revnextwoo_advanced_setting_tab2_section_callback()
    1876 {
    1877     echo '<p>Set your no review user reminder time:</p>';
    18781328}
    18791329
     
    19481398add_action('admin_enqueue_scripts', 'revnextwoo_enqueue_color_picker');
    19491399
    1950 // Add this in your theme's functions.php or a custom plugin file
    1951 add_action('wp_ajax_revnextwoo_submit_question_reply', 'revnextwoo_submit_question_reply');
    1952 add_action('wp_ajax_nopriv_revnextwoo_submit_question_reply', 'revnextwoo_submit_question_reply');
    1953 
    1954 function revnextwoo_submit_question_reply()
    1955 {
    1956     // Verify nonce
    1957     if (!isset($_POST['question_reply_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['question_reply_nonce'])), 'submit_question_reply_nonce')) {
    1958         wp_send_json_error('Security check failed');
    1959         return;
    1960     }
    1961 
    1962     // Validate required fields
    1963     if (!isset($_POST['comment_id'], $_POST['reply_content'], $_POST['post_id'])) {
    1964         wp_send_json_error('Missing required fields');
    1965         return;
    1966     }
    1967 
    1968     // Sanitize and validate input
    1969     $post_id = isset($_POST['post_id']) ? intval(wp_unslash($_POST['post_id'])) : 0;
    1970     $comment_id = isset($_POST['comment_id']) ? intval(wp_unslash($_POST['comment_id'])) : 0;
    1971     $reply_content = isset($_POST['reply_content']) ? wp_kses_post(wp_unslash($_POST['reply_content'])) : '';
    1972 
    1973     if (!$post_id || !$comment_id || empty($reply_content)) {
    1974         wp_send_json_error('Invalid input data');
    1975         return;
    1976     }
    1977     $current_user_id = get_current_user_id();
    1978     $current_user = get_user_by('ID', $current_user_id);
    1979     $display_name = !isset($current_user->display_name) ? '' : $current_user->display_name;
    1980     $user_email = !isset($current_user->user_email) ? '' : $current_user->user_email;
    1981     $author_url = !isset($current_user->user_url) ? '' : $current_user->user_url;
    1982     // Sanitize and validate data as needed
    1983     $commentdata = array(
    1984         'comment_post_ID' => $post_id,
    1985         'comment_parent' => $comment_id,
    1986         'comment_content' => $reply_content,
    1987         'comment_approved' => 1, // Approve the comment by default
    1988         'comment_author' => $display_name,
    1989         'comment_author_email' => $user_email,
    1990         'comment_author_url' => $author_url,
    1991         'comment_type' => 'question_reply',
    1992         'user_id' => $current_user_id,
    1993     );
    1994     // Insert the reply
    1995     $reply_id = wp_insert_comment($commentdata);
    1996 
    1997     if ($reply_id) {
    1998         // Return a response, e.g., a success message
    1999         wp_send_json_success('question reply added successfully');
    2000     } else {
    2001         // Return a response, e.g., a success message
    2002         wp_send_json_success('Failed to add reply.');
    2003     }
    2004     wp_die();
    2005 }
    2006 
    2007 
    2008 
    2009 function revnextwoo_status_update_question()
    2010 {
    2011     // Verify nonce
    2012     if (!isset($_POST['status_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['status_nonce'])), 'question_status_nonce')) {
    2013         wp_send_json_error('Security check failed');
    2014         return;
    2015     }
    2016 
    2017     // Validate required fields
    2018     if (!isset($_POST['question_id']) || !isset($_POST['status'])) {
    2019         wp_send_json_error('Missing required fields');
    2020         return;
    2021     }
    2022 
    2023     // Sanitize and validate input
    2024     $comment_id = isset($_POST['question_id']) ? intval(wp_unslash($_POST['question_id'])) : 0;
    2025     $action = isset($_POST['status']) ? sanitize_text_field(wp_unslash($_POST['status'])) : '';
    2026 
    2027     if (!$comment_id || empty($action)) {
    2028         wp_send_json_error('Invalid input data');
    2029         return;
    2030     }
    2031     $status_mapping = array(
    2032         'Approved' => '0',
    2033         'Unapproved' => '1',
    2034         'Spam' => 'spam',
    2035         'Trash' => 'trash',
    2036     );
    2037 
    2038     // Check if the provided action is valid.
    2039     if (array_key_exists($action, $status_mapping)) {
    2040         $comment_status = $status_mapping[$action];
    2041 
    2042         // Create an array with multiple parameters to send back.
    2043         $response_data = array(
    2044             'message' => 'Question status updated successfully.',
    2045             'comment_id' => $comment_id,
    2046             'status' => $action,
    2047         );
    2048 
    2049         // Update the comment status.
    2050         if (wp_set_comment_status($comment_id, $comment_status)) {
    2051             $response_data['success'] = true;
    2052         } else {
    2053             $response_data['success'] = false;
    2054             $response_data['message'] = 'Failed to update question status.';
    2055         }
    2056 
    2057         // Send the JSON response with the array.
    2058         wp_send_json($response_data);
    2059     } else {
    2060         wp_send_json_error('Invalid status action.');
    2061     }
    2062 }
    2063 
    2064 // Hook the above function to WordPress AJAX action
    2065 add_action('wp_ajax_revnextwoo_status_update_question', 'revnextwoo_status_update_question');
    2066 add_action('wp_ajax_nopriv_revnextwoo_status_update_question', 'revnextwoo_status_update_question');
    2067 
    2068 
    2069 
    2070 // Add AJAX action to handle the comment update
    2071 add_action('wp_ajax_revnextwoo_question_update', 'revnextwoo_question_update_callback');
    2072 add_action('wp_ajax_nopriv_revnextwoo_question_update', 'revnextwoo_question_update_callback');
    2073 
    2074 // Callback function to handle the comment update
    2075 function revnextwoo_question_update_callback()
    2076 {
    2077     // Verify nonce
    2078     if (!isset($_POST['update_question_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['update_question_nonce'])), 'update_question_nonce')) {
    2079         wp_send_json_error('Security check failed');
    2080     }
    2081 
    2082     // Get the data sent via AJAX
    2083     $comment_id = isset($_POST['commentId']) ? intval(wp_unslash($_POST['commentId'])) : 0;
    2084     $question_content = isset($_POST['questionContent']) ? sanitize_text_field(wp_unslash($_POST['questionContent'])) : '';
    2085     $user_name = isset($_POST['userName']) ? sanitize_text_field(wp_unslash($_POST['userName'])) : '';
    2086     $email = isset($_POST['email']) ? sanitize_email(wp_unslash($_POST['email'])) : '';
    2087     $ip = isset($_POST['ip']) ? sanitize_text_field(wp_unslash($_POST['ip'])) : '';
    2088 
    2089     // Update the comment data (replace with your own logic)
    2090     if ($comment_id > 0) {
    2091         $updated_data = array(
    2092             'comment_ID' => $comment_id,
    2093             'comment_content' => $question_content,
    2094             'comment_author' => $user_name,
    2095             'comment_author_email' => $email,
    2096             'comment_author_IP' => $ip,
    2097         );
    2098 
    2099         $updated = wp_update_comment($updated_data);
    2100         // Create an array with multiple parameters to send back.
    2101         $response_data = array(
    2102             'message' => 'Question updated successfully.',
    2103             'data' => $question_content,
    2104             'comment_id' =>  $comment_id,
    2105         );
    2106         if ($updated) {
    2107             $response_data['success'] = true;
    2108         } else {
    2109             $response_data['success'] = false;
    2110             $response_data['message'] = 'Failed to update question status.';
    2111         }
    2112         // Send the JSON response with the array.
    2113         wp_send_json($response_data);
    2114     } else {
    2115         wp_send_json_error(array('message' => esc_html__('Invalid comment ID.', 'review-next-for-woocommerce')));
    2116     }
    2117 
    2118     wp_die();
    2119 }
    2120 
    2121 
    2122 
    2123 // view reply
    2124 add_action('wp_ajax_revnextwoo_get_question_replies', 'revnextwoo_get_question_replies');
    2125 add_action('wp_ajax_nopriv_revnextwoo_get_question_replies', 'revnextwoo_get_question_replies');
    2126 
    2127 function revnextwoo_get_question_replies()
    2128 {
    2129     // Verify nonce
    2130     if (!isset($_POST['question_replies_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['question_replies_nonce'])), 'get_question_replies_nonce')) {
    2131         wp_send_json_error('Security check failed');
    2132         return;
    2133     }
    2134 
    2135     if (isset($_POST['question_id'])) {
    2136         $questionId = intval(wp_unslash($_POST['question_id']));
    2137         $args = array(
    2138             'parent' => $questionId,
    2139             'status' => 'approve',
    2140             'order' => 'ASC',
    2141             'type' => 'question_reply',
    2142         );
    2143         $replies = get_comments($args);
    2144         if (!empty($replies)) {
    2145             foreach ($replies as $reply) {
    2146                 echo '<textarea class="wp-editor-area"
    2147                  rows="2" cols="20"
    2148                   name="update-qustion-content"
    2149                    id="update-qustion-content">
    2150                    ' . esc_textarea($reply->comment_content) . '
    2151                    </textarea>';
    2152             }
    2153         } else {
    2154             echo 'No replies found.';
    2155         }
    2156     }
    2157     wp_die();
    2158 }
    2159 
    2160 // function revnextwoo_mailtrap($phpmailer)
    2161 // {
    2162 //     // Only configure mailtrap if credentials are defined
    2163 //     if (!defined('REVIEW_SMTP_HOST') || !defined('REVIEW_SMTP_USER') || !defined('REVIEW_SMTP_PASS')) {
    2164 //         return;
    2165 //     }
    2166 
    2167 //     $phpmailer->isSMTP();
    2168 //     $phpmailer->Host = REVIEW_SMTP_HOST;
    2169 //     $phpmailer->SMTPAuth = true;
    2170 //     $phpmailer->Port = defined('REVIEW_SMTP_PORT') ? REVIEW_SMTP_PORT : 2525;
    2171 //     $phpmailer->Username = REVIEW_SMTP_USER;
    2172 //     $phpmailer->Password = REVIEW_SMTP_PASS;
    2173 // }
    2174 
    2175 // add_action('phpmailer_init', 'revnextwoo_mailtrap');
    2176 
    2177 
     1400//send email callback start here--------
    21781401function revnextwoo_send_emails_callback()
    21791402{
     
    22481471
    22491472add_action('wp_ajax_revnextwoo_send_emails', 'revnextwoo_send_emails_callback');
     1473
     1474// Enqueue frontend scripts for AJAX review sorting
     1475add_action('wp_enqueue_scripts', 'revnextwoo_enqueue_review_scripts');
     1476function revnextwoo_enqueue_review_scripts() {
     1477    if (is_product()) {
     1478        wp_enqueue_script(
     1479            'revnextwoo-reviews',
     1480            plugin_dir_url(__FILE__) . 'public/js/revnextwoo-reviews.js',
     1481            array('jquery'),
     1482            '1.0.0',
     1483            true
     1484        );
     1485        wp_enqueue_script(
     1486            'revnextwoo-review-layout',
     1487            plugin_dir_url(__FILE__) . 'public/js/revnextwoo-review-layout.js',
     1488            array('jquery'),
     1489            '1.0.0',
     1490            true
     1491        );
     1492        // Ensure questions pagination + submission JS is present on product pages
     1493        wp_enqueue_script(
     1494            'revnextwoo-questions-pagination',
     1495            plugin_dir_url(__FILE__) . 'public/js/revnextwoo-questions-pagination.js',
     1496            array('jquery'),
     1497            '1.0.0',
     1498            true
     1499        );
     1500        // Localize ajax url and product id for questions script
     1501        global $product;
     1502        $product_id = is_object($product) ? $product->get_id() : (get_the_ID() ?: 0);
     1503        wp_localize_script('revnextwoo-questions-pagination', 'revnextwoo_questions_ajax', array(
     1504            'ajax_url'   => admin_url('admin-ajax.php'),
     1505            'product_id' => $product_id,
     1506        ));
     1507        wp_localize_script('revnextwoo-reviews', 'revnextwoo_ajax', array(
     1508            'ajax_url' => admin_url('admin-ajax.php'),
     1509            'nonce'    => wp_create_nonce('revnextwoo_vote_nonce')
     1510        ));
     1511        // Add a second object for video upload nonce (do not remove the above)
     1512        wp_localize_script('revnextwoo-reviews', 'revnextwoo_vars', array(
     1513            'ajax_url' => admin_url('admin-ajax.php'),
     1514            'nonce'    => wp_create_nonce('revnextwoo_nonce')
     1515        ));
     1516        // Pass layout to JS via body class
     1517        add_filter('body_class', function($classes) {
     1518            $layout = get_option('revnextwoo_review_layout_style', 'default');
     1519            $classes[] = 'revnextwoo-layout-' . esc_attr($layout);
     1520            return $classes;
     1521        });
     1522        // Add data attribute for JS detection
     1523        add_action('wp_head', function() {
     1524            $layout = esc_attr(get_option('revnextwoo_review_layout_style', 'default'));
     1525            echo "<script>document.body.setAttribute('data-revnextwoo-layout', '" . $layout . "');</script>";
     1526        });
     1527    }
     1528}
     1529
     1530
     1531// AJAX handler for sorting product reviews
     1532add_action('wp_ajax_revnextwoo_sort_reviews', 'revnextwoo_sort_reviews_ajax');
     1533add_action('wp_ajax_nopriv_revnextwoo_sort_reviews', 'revnextwoo_sort_reviews_ajax');
     1534function revnextwoo_sort_reviews_ajax() {
     1535    // Security check
     1536    if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'revnextwoo_vote_nonce')) {
     1537        wp_send_json_error(['message' => __('Invalid request.', 'review-next-for-woocommerce')]);
     1538        wp_die();
     1539    }
     1540    $product_id = isset($_POST['product_id']) ? intval($_POST['product_id']) : 0;
     1541    $sort = isset($_POST['sort']) ? sanitize_text_field($_POST['sort']) : 'newest';
     1542    if (!$product_id) {
     1543        wp_send_json_error(['message' => __('Invalid product.', 'review-next-for-woocommerce')]);
     1544        wp_die();
     1545    }
     1546    // Fetch approved reviews
     1547    $args = array(
     1548        'post_id' => $product_id,
     1549        'status'  => 'approve',
     1550        'type'    => 'review',
     1551        'meta_query' => array(),
     1552        'orderby' => 'comment_date',
     1553        'order' => 'DESC',
     1554    );
     1555    // Sort logic
     1556    if ($sort === 'highest') {
     1557        $args['meta_key'] = 'rating';
     1558        $args['orderby'] = 'meta_value_num';
     1559        $args['order'] = 'DESC';
     1560    } elseif ($sort === 'lowest') {
     1561        $args['meta_key'] = 'rating';
     1562        $args['orderby'] = 'meta_value_num';
     1563        $args['order'] = 'ASC';
     1564    } elseif ($sort === 'helpful') {
     1565        $args['meta_key'] = 'likes';
     1566        $args['orderby'] = 'meta_value_num';
     1567        $args['order'] = 'DESC';
     1568    } elseif ($sort === 'media') {
     1569        $args['meta_query'][] = array(
     1570            'key'     => 'review_images',
     1571            'compare' => 'EXISTS',
     1572        );
     1573    }
     1574    $reviews = get_comments($args);
     1575    ob_start();
     1576        if (!empty($reviews)) : ?>
     1577<?php $layout = get_option('revnextwoo_review_layout_style', 'default'); ?>
     1578 <div class="revnextwoo_reviews_container revnextwoo_layout_<?php echo esc_attr($layout); ?>" data-product-id="<?php echo esc_attr($product_id); ?>">
     1579            <?php foreach ($reviews as $review) :
     1580                $review_rating = intval(get_comment_meta($review->comment_ID, 'rating', true));
     1581                $verified = wc_review_is_from_verified_owner($review->comment_ID);
     1582                $review_timestamp = strtotime($review->comment_date_gmt ?: $review->comment_date);
     1583                if (!$review_timestamp) {
     1584                    // Fallback to WP helper if parsing fails
     1585                    $review_timestamp = (int) get_comment_time('U', false, true, $review->comment_ID);
     1586                }
     1587                $review_date = human_time_diff($review_timestamp, current_time('timestamp')) . ' ago';
     1588                $review_images = get_comment_meta($review->comment_ID, 'review_images', true);
     1589            ?>
     1590            <?php
     1591                $grid_colors = ['#ffb6b6','#6a7ee7','#8df7b1','#f9d56e','#f97e7e','#7ee8fa','#b6ffb6','#c6a1ff'];
     1592                $masonry_color = '';
     1593                $style_attr = '';
     1594                if($layout === 'default'){
     1595                    $style_attr = 'margin-bottom:20px;';
     1596                    $style_attr .= 'border-radius:10px;';
     1597                    $style_attr .= 'border:1px solid #ccc;';
     1598                    $style_attr .= 'overflow:hidden;';
     1599                    $item_bg = get_option('revnextwoo_review_item_bg', '#fff');
     1600                    $style_attr .= ' background-color: ' . esc_attr($item_bg) . ';';
     1601                }
     1602                if ($layout === 'grid') {
     1603                    $masonry_color = $grid_colors[array_rand($grid_colors)];
     1604                    $style_attr = '--masonry-color: ' . $masonry_color . ';';
     1605                }
     1606                if ($layout === 'card') {
     1607                    $item_bg = get_option('revnextwoo_review_item_bg', '#fff');
     1608                    $style_attr .= ' background-color: ' . esc_attr($item_bg) . ';';
     1609                }
     1610            ?>
     1611        <div class="revnextwoo_review_item"<?php echo !empty($style_attr) ? ' style="' . $style_attr . '"' : ''; ?>>
     1612                    <?php $header_bg = get_option('revnextwoo_review_box_header_footer_bg', '#ffffff'); ?>
     1613                    <div class="revnextwoo_review_header" style="background-color: <?php echo esc_attr($header_bg); ?>;">
     1614                        <div class="revnextwoo_reviewer_info">
     1615                            <div class="revnextwoo_reviewer_avatar">
     1616                                <?php
     1617                                $user_avatar_url = '';
     1618                                if (!empty($review->user_id)) {
     1619                                    $user_avatar_url = get_user_meta($review->user_id, 'revnextwoo_profile_avatar', true);
     1620                                }
     1621                                if (!$user_avatar_url) {
     1622                                    $settings = get_option('revnextwoo_social_settings');
     1623                                    $user_avatar_url = isset($settings['custom_avatar_user']) ? $settings['custom_avatar_user'] : '';
     1624                                }
     1625                                if ($user_avatar_url) {
     1626                                    echo '<img src="' . esc_url($user_avatar_url) . '" class="avatar avatar-50 photo" width="50" height="50" style="border-radius:50%;object-fit:cover;" alt="User Avatar" />';
     1627                                } else {
     1628                                    $avatar_key = !empty($review->user_id) ? intval($review->user_id) : (isset($review->comment_author_email) ? $review->comment_author_email : '');
     1629                                    echo get_avatar($avatar_key, 50);
     1630                                }
     1631                                ?>
     1632                            </div>
     1633                            <div class="revnextwoo_reviewer_details">
     1634                                <?php if ($verified) : ?>
     1635                                    <span class="revnextwoo_verified_badge">Verified Purchase</span>
     1636                                <?php endif; ?>
     1637                                <h4 class="revnextwoo_reviewer_name"><b><?php echo esc_html($review->comment_author); ?></b></h4>
     1638                                <div class="revnextwoo_review_meta">
     1639                                    <div class="revnextwoo_review_rating">
     1640                                        <?php
     1641                                        $rating_images = get_option('revnextwoo_rating_images', 'star');
     1642                                        $icon_classes = array(
     1643                                                    'star' => array(
     1644                                                        'filled' => 'fas fa-star icon_filled',
     1645                                                        'empty' => 'far fa-star icon_empty',
     1646                                                    ),
     1647                                                    'heart' => array(
     1648                                                        'filled' => 'fas fa-heart icon_filled',
     1649                                                        'empty' => 'far fa-heart icon_empty',
     1650                                                    ),
     1651                                                    'thumb' => array(
     1652                                                        'filled' => 'fas fa-thumbs-up icon_filled',
     1653                                                        'empty' => 'far fa-thumbs-up icon_empty',
     1654                                                    ),
     1655                                                );
     1656                                        $current_icon_set = isset($icon_classes[$rating_images]) ? $icon_classes[$rating_images] : $icon_classes['star'];
     1657                                        for ($i = 1; $i <= 5; $i++) :
     1658                                            $iconType = ($i <= $review_rating) ? 'filled' : 'empty';
     1659                                            $iconClass = isset($current_icon_set[$iconType]) ? $current_icon_set[$iconType] : '';
     1660                                        ?>
     1661                                            <i class="<?php echo esc_attr($iconClass); ?>"></i>
     1662                                        <?php endfor; ?>
     1663                                    </div>
     1664                                </div>
     1665                                <span class="revnextwoo_review_date"><?php echo esc_html($review_date); ?></span>
     1666                                <?php
     1667                                    // Location display logic (same as comment-item-display.php)
     1668                                    $reviewer_city = get_comment_meta($review->comment_ID, 'reviewer_city', true);
     1669                                    $reviewer_country = get_comment_meta($review->comment_ID, 'reviewer_country', true);
     1670                                    $location_display = '';
     1671                                    if (!empty($reviewer_city) || !empty($reviewer_country)) {
     1672                                        $location_parts = array();
     1673                                        if (!empty($reviewer_city)) {
     1674                                            $location_parts[] = esc_html($reviewer_city);
     1675                                        }
     1676                                        if (!empty($reviewer_country)) {
     1677                                            $location_parts[] = esc_html($reviewer_country);
     1678                                        }
     1679                                        $location_display = '<span class="review-location">' . implode(', ', $location_parts) . '</span>';
     1680                                    }
     1681                                    if (!empty($location_display)) {
     1682                                        echo $location_display;
     1683                                    }
     1684                                    ?>
     1685                            </div>
     1686                        </div>
     1687                    </div>
     1688                    <div class="revnextwoo_review_content">
     1689                        <?php
     1690                        // Get the review title from comment meta
     1691                        $review_title = get_comment_meta($review->comment_ID, 'title', true);
     1692                        // If no title exists (for backward compatibility), use a default
     1693                        if (empty($review_title)) {
     1694                            $review_title = 'Great Product';
     1695                        }
     1696                        ?>
     1697                        <div class="revnextwoo_text_box">
     1698                            <h3 class="revnextwoo_review_title"><b><?php echo esc_html($review_title); ?></b></h3>
     1699                            <p class="revnextwoo_review_text" data-full-text="<?php echo esc_attr(wp_strip_all_tags($review->comment_content)); ?>">
     1700                                <?php
     1701                                $content = wp_strip_all_tags($review->comment_content);
     1702                                $words = explode(' ', $content);
     1703                                if (count($words) > 12) {
     1704                                    $short_content = implode(' ', array_slice($words, 0, 12));
     1705                                    echo esc_html($short_content . '...');
     1706                                } else {
     1707                                    echo esc_html($content);
     1708                                }
     1709                                ?>
     1710                            </p>
     1711                            <?php if (count($words) > 12) : ?>
     1712                                <span class="revnextwoo_read_more">Read More</span>
     1713                            <?php endif; ?>
     1714                        </div>
     1715                       
     1716                        <?php if (!empty($review_images) && is_array($review_images)) : ?>
     1717                            <div class="revnextwoo_review_images">
     1718                                <?php foreach ($review_images as $image_id) : ?>
     1719                                    <div class="revnextwoo_review_image">
     1720                                        <a href="<?php echo esc_url(wp_get_attachment_url($image_id)); ?>"  data-lightbox="example-set" ><?php echo wp_get_attachment_image($image_id, 'thumbnail'); ?></a>
     1721                                    </div>
     1722                                <?php endforeach; ?>
     1723                            </div>
     1724                        <?php endif; ?>
     1725                         <?php
     1726                         $video_id = get_comment_meta($review->comment_ID, 'review_video', true);
     1727                         if ($video_id) {
     1728                             $video_url = wp_get_attachment_url($video_id);
     1729                             if ($video_url) {
     1730                                 echo '<div class="revnextwoo_review_video">';
     1731                                 echo '<video controls width="320" height="240" style="max-width:100%;margin-top:10px;">';
     1732                                 echo '<source src="' . esc_url($video_url) . '" type="video/mp4">';
     1733                                 echo __('Your browser does not support the video tag.', 'review-next-for-woocommerce');
     1734                                 echo '</video>';
     1735                                 echo '</div>';
     1736                             }
     1737                         }
     1738                         ?>
     1739                     </div>
     1740                     <?php $header_bg = get_option('revnextwoo_review_box_header_footer_bg', '#ffffff'); ?>
     1741                    <div class="revnextwoo_review_actions" style="background-color: <?php echo esc_attr($header_bg); ?>">
     1742                        <?php
     1743                        $settings = get_option('revnextwoo_social_settings');
     1744                        $show_thumbs_up = isset($settings['enable_thumbs_up']) && $settings['enable_thumbs_up'] === 'yes';
     1745                        $show_thumbs_down = isset($settings['enable_thumbs_down']) && $settings['enable_thumbs_down'] === 'yes';
     1746                        ?>
     1747                        <?php if ($show_thumbs_up): ?>
     1748                        <button class="revnextwoo_like_btn revnextwoo_vote_btn" data-review-id="<?php echo esc_attr($review->comment_ID); ?>" data-vote-type="up">
     1749                            <span class="dashicons dashicons-thumbs-up"></span>
     1750                            <span class="revnextwoo_like_count"><?php echo get_comment_meta($review->comment_ID, 'likes', true) ?: 0; ?></span>
     1751                        </button>
     1752                        <?php endif; ?>
     1753
     1754                       
     1755                        <div class="revnextwoo_social_sharing">
     1756                            <a href="#" class="revnextwoo_share_btn revnextwoo_facebook_share" title="Share on Facebook">
     1757                                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="#1877F2"><path d="M22.675 0h-21.35c-.732 0-1.325.593-1.325 1.325v21.351c0 .731.593 1.324 1.325 1.324h11.495v-9.294h-3.128v-3.622h3.128v-2.671c0-3.1 1.893-4.788 4.659-4.788 1.325 0 2.463.099 2.795.143v3.24l-1.918.001c-1.504 0-1.795.715-1.795 1.763v2.313h3.587l-.467 3.622h-3.12v9.293h6.116c.73 0 1.323-.593 1.323-1.325v-21.35c0-.732-.593-1.325-1.325-1.325z"/></svg>
     1758                            </a>
     1759
     1760                        </div>
     1761                       
     1762
     1763                    </div>
     1764                    <span class="revnextwoo_vote_message" style="display:none;"></span>
     1765                </div>
     1766            <?php endforeach; ?>
     1767        </div>
     1768    <?php else : ?>
     1769        <p class="revnextwoo_no_reviews">No reviews yet. Be the first to review!</p>
     1770    <?php endif; ?>
     1771    <?php
     1772    $html = ob_get_clean();
     1773    wp_send_json_success(['html' => $html]);
     1774    wp_die();
     1775}
     1776
     1777// AJAX handler for review voting (like/dislike)
     1778add_action('wp_ajax_revnextwoo_vote_review', 'revnextwoo_vote_review_handler');
     1779function revnextwoo_vote_review_handler() {
     1780    // Only allow for logged-in users
     1781    if (!is_user_logged_in()) {
     1782        wp_send_json_error(['message' => __('You must be logged in to vote.', 'review-next-for-woocommerce')]);
     1783    }
     1784    // Nonce check
     1785    if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'revnextwoo_vote_nonce')) {
     1786        wp_send_json_error(['message' => __('Invalid request.', 'review-next-for-woocommerce')]);
     1787    }
     1788    $user_id = get_current_user_id();
     1789    $review_id = isset($_POST['review_id']) ? intval($_POST['review_id']) : 0;
     1790    $vote_type = isset($_POST['vote_type']) ? sanitize_text_field($_POST['vote_type']) : '';
     1791    if (!$review_id || !in_array($vote_type, ['up', 'down'])) {
     1792        wp_send_json_error(['message' => __('Invalid data.', 'review-next-for-woocommerce')]);
     1793    }
     1794    // Track users who voted in comment meta as an array of user IDs
     1795    $voted_users = get_comment_meta($review_id, 'revnextwoo_voted_users', true);
     1796    if (!is_array($voted_users)) {
     1797        $voted_users = [];
     1798    }
     1799    if (in_array($user_id, $voted_users)) {
     1800        wp_send_json_error(['message' => __('You have already voted once, Thanks', 'review-next-for-woocommerce')]);
     1801    }
     1802    // Update vote count
     1803    if ($vote_type === 'up') {
     1804        $likes = intval(get_comment_meta($review_id, 'likes', true));
     1805        $likes++;
     1806        update_comment_meta($review_id, 'likes', $likes);
     1807    } else {
     1808        $dislikes = intval(get_comment_meta($review_id, 'dislikes', true));
     1809        $dislikes++;
     1810        update_comment_meta($review_id, 'dislikes', $dislikes);
     1811    }
     1812    // Store user as voted
     1813    $voted_users[] = $user_id;
     1814    update_comment_meta($review_id, 'revnextwoo_voted_users', $voted_users);
     1815    // Return new counts
     1816    $likes = intval(get_comment_meta($review_id, 'likes', true));
     1817    $dislikes = intval(get_comment_meta($review_id, 'dislikes', true));
     1818    wp_send_json_success([
     1819        'likes' => $likes,
     1820        'dislikes' => $dislikes
     1821    ]);
     1822}
     1823// AJAX handler for hard delete of question and replies
     1824add_action('wp_ajax_revnextwoo_hard_delete_question', function() {
     1825    if (!current_user_can('administrator')) {
     1826        wp_send_json_error('Unauthorized');
     1827    }
     1828    global $wpdb;
     1829    if (!isset($_POST['question_id'])) {
     1830        wp_send_json_error('No question ID');
     1831    }
     1832    $question_id = intval($_POST['question_id']);
     1833    $qa_table = class_exists('Revnextwoo_QA_DB') ? Revnextwoo_QA_DB::table_name() : '';
     1834    if (!$qa_table) {
     1835        wp_send_json_error('Table not found');
     1836    }
     1837    // Delete the question
     1838    $wpdb->delete($qa_table, ['id' => $question_id]);
     1839    // Delete all replies to this question
     1840    $wpdb->delete($qa_table, ['parent_id' => $question_id]);
     1841    wp_send_json_success('Deleted');
     1842});
     1843
  • review-next-for-woocommerce/trunk/template/admin/all-question.php

    r3315734 r3457384  
    11<?php
     2// Handle actions (approve, unapprove, spam, trash) for admin table
     3if (isset($_GET['action'], $_GET['id']) && in_array($_GET['action'], ['approve','unapprove','spam','trash'])) {
     4    global $wpdb;
     5    $table = class_exists('Revnextwoo_QA_DB') ? Revnextwoo_QA_DB::table_name() : '';
     6    $id = intval($_GET['id']);
     7    $status = $_GET['action'];
     8    if ($status === 'trash') {
     9        $wpdb->delete($table, ['id'=>$id]);
     10        $wpdb->delete($table, ['parent_id'=>$id]); // delete all replies
     11    } else {
     12        $wpdb->update($table, ['status'=>$status], ['id'=>$id]);
     13    }
     14    wp_redirect(remove_query_arg(['action','id']));
     15    exit;
     16}
     17
    218// Exit if accessed directly
    319if (! defined('ABSPATH'))
     
    4258            'pending' => 0,  // Initialize to 0
    4359            'approved' => 0, // Initialize to 0
    44             'spam' => 0,     // Initialize to 0
    45             'trash' => 0,    // Initialize to 0
    4660        );
    47 
    48         // Calculate counts for statuses other than 'all'
    49         foreach ($data as $item) {
    50             if ($item['status'] === 'hold') {
    51                 $question_statuses['pending']++;
    52             } elseif ($item['status'] === 'approve') {
    53                 $question_statuses['approved']++;
    54             } elseif ($item['status'] === 'spam') {
    55                 $question_statuses['spam']++;
    56             } elseif ($item['status'] === 'trash') {
    57                 $question_statuses['trash']++;
    58             }
    59         }
    6061
    6162        // Output the sub-navigation menu
     
    116117    {
    117118        return array(
    118             'id' => 'ID',
     119            'sn' => 'S/N',
    119120            'user_name' => 'User Name',
    120             'question_content' => 'Question Content',
     121            'question_content' => 'Questions',
     122            'view_reply'  => 'View Reply',
    121123            'product' => 'Product',
    122             'question_date'  => 'Question Date',
    123             'view_reply'  => 'View Reply',
     124            'question_date'  => 'Date',
     125            'status' => 'Status',
    124126        );
    125127    }
     
    128130    {
    129131        return array(
    130             'id' => array('id', false),
    131132            'user_name' => array('user_name', false),
    132             'product' => 'Product',
    133133            'question_date' => array('question_date', false),
    134134            'view_reply'  => array('view_reply', false),
     135            'status' => array('status', false),
    135136        );
    136137    }
     
    138139    private function table_data()
    139140    {
    140         $args = array(
    141             'status' => array('approve', 'hold'),
    142             'order' => 'ASC',
    143             'type' => 'question',
    144         );
    145         $questions = get_comments($args);
     141        $rows = array();
     142        if (class_exists('Revnextwoo_QA_DB')) {
     143            global $wpdb;
     144            $qa_table = Revnextwoo_QA_DB::table_name();
     145            $rows = $wpdb->get_results("SELECT * FROM {$qa_table} WHERE type = 'question' ORDER BY created_at DESC LIMIT 500");
     146        }
    146147        $data = array();
    147         foreach ($questions as $question) {
    148             $product = wc_get_product($question->comment_post_ID);
    149             $questionDate = gmdate('F j, Y', strtotime($question->comment_date));
    150             $authorUsername = get_comment_author($question->comment_ID);
    151             $authorEmail = get_comment_author_email($question->comment_ID);
    152             $authorIP = get_comment_author_IP($question->comment_ID);
    153             $product = wc_get_product($question->comment_post_ID);
     148        foreach ($rows as $row) {
     149            $product = wc_get_product($row->product_id);
    154150            $product_name = $product ? $product->get_name() : 'N/A';
    155             // Get the product URL
    156151            $product_url = $product ? get_permalink($product->get_id()) : '';
    157152            $view_product_link = $product_url ? '<a href="' . esc_url($product_url) . '">View Product</a>' : 'N/A';
    158             $args = array(
    159                 'parent' => $question->comment_ID,
    160                 'status' => 'approve',
    161                 'order' => 'ASC',
    162                 'type' => 'question_reply',
    163                 'count' => true
     153            $replyCount = ($row->type === 'question' && class_exists('Revnextwoo_QA_DB')) ? Revnextwoo_QA_DB::count_replies($row->id) : 0;
     154            $data[] = array(
     155                'id' => $row->id,
     156                'type' => ($row->type === 'question' && $replyCount == 0)
     157                ? '<span class="no-reply-highlight">' . esc_html($row->type) . '</span>'
     158                : esc_html($row->type),
     159                'user_name' => esc_html($row->author_name) . '<br><span style="font-size:11px;color:#888">' . esc_html($row->author_email) . '</span>',
     160                'question_content' => '<span id="question_content" class="question_content_' . $row->id . '">' . esc_html($row->content) . '</span>',
     161                'product' => $product_name . '<br>' . $view_product_link,
     162                'question_date' => $row->created_at,
     163                'status' => $row->status,
     164                'view_reply' => $row->type === 'question'
     165                    ? '<span class="post-com-count-wrapper post-com-count-' . $row->id . '">
     166                            <a href="#" class="post-com-count post-com-count-approved">
     167                            <span data-question-id="' . $row->id . '" class="view-reply dashicons dashicons-visibility"></span>
     168                            <span class="dashicons dashicons-admin-comments"></span>
     169                            ' . $replyCount . '
     170                            </a>
     171                        </span>'
     172                    : '',
    164173            );
    165             $replyCount = get_comments($args);
    166             $data[] = array(
    167                 'id' => $question->comment_ID,
    168                 'product_id' => $product->get_id(),
    169                 'user_name' => '<span id="username">' . $authorUsername . '</span>' . '<br>' . '<span id="user-email">' . $authorEmail . '</span>' . '<br>' . '<span id="user-ip">' . $authorIP . '</span>',
    170                 'question_content' => '<span id="question_content" class="question_content_' . $question->comment_ID . '">' . $question->comment_content . '</span>',
    171                 'product' => $product_name . '<br>' . $view_product_link,
    172                 'question_date' => $questionDate,
    173                 'status' => $question->comment_approved,
    174                 'view_reply' => '<span class="post-com-count-wrapper post-com-count-' . $question->comment_ID . '">
    175                 <a href="#" class="post-com-count post-com-count-approved">
    176                 <span data-question-id="' . $question->comment_ID . '" class="view-reply dashicons dashicons-visibility"></span>
    177                 <span class="dashicons dashicons-admin-comments"></span>
    178                 ' . $replyCount . '
    179                 </a>
    180             </span>',
    181 
    182             );
    183         }
    184 
     174        }
    185175        return $data;
    186176    }
    187177
    188     public function column_default($item, $column_name)
    189     {
    190 
    191         return $item[$column_name];
    192     }
    193 
    194     public function column_product_name($item)
    195     {
    196         return $item['product_name'];
    197     }
    198 
    199     public function column_question_date($item)
    200     {
    201         return $item['question_date'];
    202     }
    203 
    204 
    205 
     178    public function column_sn($item) {
     179        // Calculate serial number based on pagination
     180        $current_page = $this->get_pagenum();
     181        $items_per_page = $this->get_items_per_page($this->_args['plural'] . '_per_page', 10);
     182        static $sn = 0;
     183        if ($sn == 0) {
     184            $sn = ($current_page - 1) * $items_per_page + 1;
     185        }
     186        return $sn++;
     187    }
     188    public function column_user_name($item) {
     189        return $item['user_name'];
     190    }
    206191    public function column_question_content($item)
    207192    {
    208         $comment_status = $item['status'] === '1' ? 'Approved' : 'Unapproved';
    209 
    210         $actions = array(
    211             $comment_status => '<a href="#" class="status_update_question" data-question-id="' . $item['id'] . '" data-action="' . $comment_status . '">' . $comment_status . '</a>',
    212             'reply' => '<button type="button" data-comment-id="' . $item['id'] . '" data-post-id="' . $item['product_id'] . '" data-action="replyto" class="vim-r-question-reply comment-inline button-link" aria-expanded="false" aria-label="Reply to this review">Reply</button>',
    213             'quick_edit' => '<button type="button" data-comment-id="' . $item['id'] . '" data-post-id="' . $item['product_id'] . '" data-action="question_edit" class="vim-q comment-inline button-link" aria-expanded="false" aria-label="Quick edit this review inline">Quick Edit</button>',
    214             'spam' => '<a href="#" class="spam-question status_update_question" data-question-id="' . $item['id'] . '" data-action="Spam">Spam</a>',
    215             'trash' => '<a href="#" class="trash-question status_update_question" data-question-id="' . $item['id'] . '" data-action="Trash">Trash</a>',
    216         );
    217 
    218         return sprintf('%1$s %2$s', $item['question_content'],  $this->row_actions($actions));
    219     }
    220 }
    221 
    222 
     193        // Define possible statuses and their labels
     194        $statuses = [
     195            'approve' => 'Approved',
     196            'unapprove' => 'Unapproved',
     197            'spam' => 'Spam',
     198        ];
     199        $current_status = isset($statuses[$item['status']]) ? $statuses[$item['status']] : ucfirst($item['status']);
     200        $actions = '';
     201        foreach(['approve','unapprove','spam','trash'] as $act) {
     202            // Only show the action if it's not the current status, or always show trash
     203            if ($act !== $item['status'] || $act === 'trash') {
     204                $actions .= '<a href="' . esc_url(add_query_arg(['action' => $act, 'id' => $item['id']])) . '">' . ucfirst($act) . '</a> ';
     205            }
     206        }
     207        return sprintf('%1$s <div class="question-actions">%2$s</div>', $item['question_content'], $actions);
     208    }
     209    public function column_product($item) {
     210        return $item['product'];
     211    }
     212    public function column_question_date($item) {
     213        $parts = revnextwoo_get_relative_time_parts($item['question_date'], 'F j, Y g:i a');
     214        $label = !empty($parts['relative']) ? $parts['relative'] : $parts['absolute'];
     215        $title = !empty($parts['absolute']) ? ' title="' . esc_attr($parts['absolute']) . '"' : '';
     216        return '<span class="revnextwoo-question-date"' . $title . '>' . esc_html($label) . '</span>';
     217    }
     218    public function column_status($item) {
     219        return $item['status'];
     220    }
     221    public function column_view_reply($item) {
     222        global $wpdb;
     223        if (!isset($item['id'])) return '';
     224        $qa_table = class_exists('Revnextwoo_QA_DB') ? Revnextwoo_QA_DB::table_name() : '';
     225        $replies = array();
     226        if ($qa_table) {
     227            $replies = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$qa_table} WHERE parent_id = %d AND type = 'question_reply' ORDER BY created_at ASC", $item['id']));
     228        }
     229        if ($replies && count($replies) > 0) {
     230            $reply_html = '';
     231            foreach ($replies as $reply) {
     232                $reply_html .= '<div class="admin-reply-content"><strong>Admin:</strong> ' . esc_html($reply->content) . '</div>';
     233            }
     234            return $reply_html;
     235        } else {
     236            return '<span class="no-reply-message"><h4>No Reply</h4></span>';
     237        }
     238    }
     239
     240    // Highlight entire row if this is a question with no replies
     241    public function single_row($item) {
     242        $highlight_class = '';
     243        // If the type cell contains the no-reply-highlight span, add class to <tr>
     244        if (isset($item['type']) && strpos($item['type'], 'no-reply-highlight') !== false) {
     245            $highlight_class = 'no-reply-highlight';
     246        }
     247        echo '<tr class="' . esc_attr($highlight_class) . '">';
     248        list($columns, $hidden) = $this->get_column_info();
     249        foreach ($columns as $column_name => $column_display_name) {
     250            $class = "class=\"column-$column_name\"";
     251            $style = in_array($column_name, $hidden) ? ' style="display:none;"' : '';
     252            if (method_exists($this, 'column_' . $column_name)) {
     253                echo "<td $class$style>" . call_user_func(array(
     254                    $this,
     255                    'column_' . $column_name
     256                ), $item) . "</td>";
     257            } else {
     258                echo "<td $class$style>" . $this->column_default($item, $column_name) . "</td>";
     259            }
     260        }
     261        echo '</tr>';
     262    }
     263}
    223264
    224265function revnextwoo_custom_table_plugin_page()
    225266{
    226267    echo '<div class="wrap"><h2>Question list</h2>';
     268    echo '<div style="float:right;margin-top:-32px;">
     269        <span id="pending-bell" style="position:relative;cursor:pointer;font-size:24px;">
     270            <span class="dashicons dashicons-bell"></span>
     271            <span id="pending-count" style="position:absolute;top:-10px;right:-10px;background:#e53935;color:#fff;border-radius:50%;font-size:13px;padding:2px 7px;min-width:18px;text-align:center;">0</span>
     272        </span>
     273    </div>';
     274
    227275    echo '<form method="post" action="">';
    228276    wp_nonce_field('bulk-product_questions', '_wpnonce', true);
  • review-next-for-woocommerce/trunk/template/admin/email-reminder.php

    r3315734 r3457384  
    431431    }
    432432}
    433 
    434 
    435 
    436 
    437 function revnextwoo_email_reminder_table_plugin_page()
    438 {
    439     $output = '<div class="wrap"><h2>No Review Order list</h2>';
    440     $output .= '<form method="post" action="">';
    441     $output .= wp_nonce_field('rnfw_email_reminder', 'rnfw_er_nonce');
    442     $output .= wp_nonce_field('bulk-email-reminders', '_wpnonce', true, false);
    443 
    444     $custom_table = new Revnextwoo_Email_Reminder_Table_Plugin();
    445     $custom_table->search_box('Search Orders', 'order_id');
    446     $custom_table->prepare_items();
    447     ob_start();
    448     $custom_table->display();
    449     $output .= ob_get_clean();
    450     $output .= '</form>';
    451     $output .= '</div>';
    452 
    453     echo wp_kses_post($output);
    454 }
    455 
    456 
    457 
    458 revnextwoo_email_reminder_table_plugin_page();
  • review-next-for-woocommerce/trunk/template/admin/top-order-customer.php

    r3315734 r3457384  
    55}
    66
    7 if (!class_exists('WP_List_Table')) {
    8     require_once(ABSPATH . 'wp-admin/includes/class-wp-list-table.php');
    9 }
    10 
    11 class REVNEXTWOO_Top_Order_Customer_Table_Plugin extends WP_List_Table
    12 {
    13     public function __construct()
    14     {
    15         parent::__construct(array(
    16             'singular' => 'top-order-customer',
    17             'plural'   => 'top-order-customers',
    18             'ajax'     => true,
    19         ));
    20     }
    21 
    22     public function prepare_items()
    23     {
    24         // Define the columns and data
    25         $columns = $this->get_columns();
    26         $hidden = array();
    27         $sortable = $this->get_sortable_columns();
    28         $data = $this->table_data();
    29         // Verify nonce for form submission
    30         if (isset($_POST['_wpnonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['_wpnonce'])), 'bulk-' . $this->_args['plural'])) {
    31             // Get the search query from the form submission and sanitize it
    32             $search_query = isset($_POST['s']) ? sanitize_text_field(wp_unslash($_POST['s'])) : '';
    33         } else {
    34             $search_query = '';
    35         }
    36 
    37         // If a search query is provided, filter the data
    38         if (!empty($search_query)) {
    39             $data = array_filter($this->table_data(), function ($item) use ($search_query) {
    40                 return stripos($item['sl'], $search_query) !== false;
    41             });
    42         } else {
    43             $data = $this->table_data();
    44         }
    45         // Get the current page number and items per page from the URL
    46         $current_page = $this->get_pagenum();
    47         $items_per_page = 10; // You can adjust the number of items per page
    48 
    49         // Calculate the total number of items
    50         $total_items = count($this->table_data());
    51 
    52         // Create a paginated data array based on the current page
    53         $data = array_slice($this->table_data(), (($current_page - 1) * $items_per_page), $items_per_page);
    54 
    55         // Set up the pagination
    56         $this->set_pagination_args(array(
    57             'total_items' => $total_items,
    58             'per_page'    => $items_per_page,
    59         ));
    60         $this->_column_headers = array($columns, $hidden, $sortable);
    61         $this->items = $data;
    62     }
    63 
    64     public function get_columns()
    65     {
    66         return array(
    67             'cb' => '<input type="checkbox" />',
    68             'sl' => 'SL NO',
    69             'user_name' => 'User-Name',
    70             'user_email' => 'User-Email',
    71             'total_order'  => 'Total-Order',
    72             'total' => 'Total',
    73             'view'  => 'View',
    74         );
    75     }
    76 
    77 
    78     protected function column_cb($item)
    79     {
    80         return sprintf(
    81             '<input type="checkbox" name="sl[]" value="%s" />',
    82             $item['sl']
    83         );
    84     }
    85 
    86     public function get_sortable_columns()
    87     {
    88         return array(
    89             'sl' => array('id', false),
    90             'user_name' => array('user', false),
    91             'total_order' => array('total_order', false),
    92             'total'  => array('total', false),
    93         );
    94     }
    95 
    96     public function process_bulk_action() {}
    97 
    98 
    99     private function table_data()
    100     {
    101         // Check if WooCommerce is active and function exists
    102         if (!function_exists('wc_get_orders') || !class_exists('WooCommerce')) {
    103             return array(); // Return empty array if WooCommerce is not available
    104         }
    105 
    106         // Define the arguments for the order query
    107         $args = array(
    108             'numberposts' => -1,
    109             'post_type' => 'shop_order',
    110             'post_status' => array('wc-delivered'),
    111         );
    112 
    113         // Get the orders based on the query
    114         $orders = wc_get_orders($args);
    115         $data = array();
    116 
    117         foreach ($orders as $key => $order) {
    118             $user_id = $order->get_customer_id();
    119             $user = get_user_by('ID', $user_id);
    120 
    121             // Check if user exists and has required data
    122             if (!$user || !isset($user->user_login) || !isset($user->user_email)) {
    123                 continue; // Skip this order if user data is invalid
    124             }
    125 
    126             $user_name = $user->user_login;
    127             $user_email = $user->user_email;
    128 
    129             $data[] = array(
    130                 'sl' => $key + 1,
    131                 'user_name' => '<span id="username">' . esc_html($user_name) . '</span>',
    132                 'user_email' => '<span id="user-email">' . esc_html($user_email) . '</span>',
    133                 'total' => $order->get_total(),
    134                 'total_order' => '',
    135                 'view' => 'dd',
    136             );
    137         }
    138 
    139         return $data;
    140     }
    141 
    142 
    143     public function column_default($item, $column_name)
    144     {
    145         switch ($column_name) {
    146             case 'view':
    147                 return '<a href="' . wp_nonce_url('?page=order-details&order_id=' . $item['sl'], 'view_order_details') . '">View Details</a>';
    148             default:
    149                 return $item[$column_name];
    150         }
    151     }
    152 
    153 
    154     public function column_order_status($item)
    155     {
    156         return $item['order_status'];
    157     }
    158 
    159     public function column_order_date($item)
    160     {
    161         return $item['order_date'];
    162     }
    163 }
    164 
    165 
    166 
    167 
    1687function revnextwoo_top_order_customer_table_plugin_page()
    1698{
    170     $output = '<div class="wrap"><h2>Most Ordered Customers</h2>';
     9    $output = '<div class="wrap"><h2>Qualified Cuatomers</h2>';
    17110
    17211    // Check if WooCommerce is active
    17312    if (!function_exists('wc_get_orders') || !class_exists('WooCommerce')) {
    174         $output .= '<div class="notice notice-warning"><p>';
    175         $output .= '<strong>WooCommerce Required:</strong> This feature requires WooCommerce to be installed and activated.';
    176         $output .= '</p></div>';
     13        // $output .= '<div class="notice notice-warning"><p>';
     14        // $output .= '<strong>WooCommerce Required:</strong> This feature requires WooCommerce to be installed and activated.';
     15        // $output .= '</p></div>';
    17716        $output .= '</div>';
    17817        echo wp_kses_post($output);
     
    18019    }
    18120
    182     $output .= '<form method="post" action="">';
    183     $output .= wp_nonce_field('bulk-top-order-customers', '_wpnonce', true, false);
    184 
    185     $custom_table = new REVNEXTWOO_Top_Order_Customer_Table_Plugin();
    186     $custom_table->search_box('Search Orders', 'sl');
    187     $custom_table->prepare_items();
     21    // --- Filtered Customer List ---
     22    require_once dirname(__DIR__, 2) . '/admin/class-revnextwoo-cas-list-table.php';
    18823    ob_start();
    189     $custom_table->display();
     24    $table = new RevNextWoo_CAS_List_Table();
     25    $table->prepare_items();
     26    $table->display();
    19027    $output .= ob_get_clean();
    191     $output .= '</form>';
    192     $output .= '</div>';
    193 
    19428    echo wp_kses_post($output);
    19529}
  • review-next-for-woocommerce/trunk/template/frontend/average-rating.php

    r3317206 r3457384  
    1313$current_user_id = $current_user->ID;
    1414
     15// Make sure we have a valid WC_Product instance
     16if (!isset($product) || !is_a($product, 'WC_Product')) {
     17    $maybe_product = wc_get_product(get_the_ID());
     18    if ($maybe_product && is_a($maybe_product, 'WC_Product')) {
     19        $product = $maybe_product;
     20    } else {
     21        return; // Cannot proceed without a valid product
     22    }
     23}
     24
    1525// Get product reviews
     26// Pagination disabled - showing all reviews
    1627$args = array(
    1728    'post_id' => $product->get_id(),
     
    4758}
    4859
    49 // Get rating counts
    50 $rating_counts = array(
    51     1 => $product->get_rating_count(1),
    52     2 => $product->get_rating_count(2),
    53     3 => $product->get_rating_count(3),
    54     4 => $product->get_rating_count(4),
    55     5 => $product->get_rating_count(5),
    56 );
    57 $total_ratings = array_sum($rating_counts);
     60// Build fresh rating counts from approved WooCommerce reviews (ensures immediate accuracy)
     61$rating_counts = array(1 => 0, 2 => 0, 3 => 0, 4 => 0, 5 => 0);
     62$rating_sum = 0;
     63$rating_total = 0;
     64$rating_review_ids = get_comments(array(
     65    'post_id'       => $product->get_id(),
     66    'status'        => 'approve',
     67    'type'          => 'review',
     68    'meta_key'      => 'rating',
     69    'meta_compare'  => 'EXISTS',
     70    'fields'        => 'ids',
     71    'number'        => 0, // all
     72));
     73foreach ($rating_review_ids as $cid) {
     74    $r = intval(get_comment_meta($cid, 'rating', true));
     75    if ($r >= 1 && $r <= 5) {
     76        $rating_counts[$r] += 1;
     77        $rating_sum += $r;
     78        $rating_total += 1;
     79    }
     80}
     81$total_ratings = $rating_total;
    5882
    5983// Calculate the percentage for each star rating
     
    6993$percentage_5_star = $rating_percentages[5];
    7094
    71 // Get average rating
    72 $average_rating = $product->get_average_rating();
     95// Get average rating (computed live for accuracy)
     96$average_rating = $rating_total > 0 ? ($rating_sum / $rating_total) : 0;
    7397$average_rating_formatted = number_format($average_rating, 1);
    7498
     
    97121
    98122<!-- Average Rating Summary -->
    99 <div class="revnextwoo_rating_summary">
     123<?php $rating_summary_bg = get_option('revnextwoo_review_rating_input_box_border_color', '#ffffff'); ?>
     124<div class="revnextwoo_rating_summary" style="background-color: <?php echo esc_attr($rating_summary_bg); ?>">
    100125    <div class="revnextwoo_rating_overview">
    101126        <div class="revnextwoo_average_rating">
     
    125150                $count = isset($rating_counts[$i]) ? $rating_counts[$i] : 0;
    126151            ?>
     152            <?php //echo $percentage ?>
    127153            <div class="revnextwoo_rating_bar">
    128154                <span class="revnextwoo_star_label"><?php echo $i; ?> star</span>
     
    140166<div class="revnextwoo_review_form_wrapper">
    141167    <h3>Write a Review</h3>
    142     <?php if (is_user_logged_in()) : ?>
     168    <?php
     169    $allowed_to_rate = get_option('revnextwoo_allowed_to_rate', 'registered_users_and_guests');
     170    $current_user_id = get_current_user_id();
     171    $user_can_see_form = false;
     172    if ($allowed_to_rate === 'no_one') {
     173        $user_can_see_form = false;
     174    } elseif ($allowed_to_rate === 'registered_users_and_guests') {
     175        $user_can_see_form = is_user_logged_in();
     176    } elseif ($allowed_to_rate === 'completed_orders') {
     177        if ($current_user_id && isset($product)) {
     178            $current_user = get_user_by('ID', $current_user_id);
     179            $user_email = isset($current_user->user_email) ? $current_user->user_email : '';
     180            if (function_exists('wc_customer_bought_product') && wc_customer_bought_product($user_email, $current_user_id, $product->get_id())) {
     181                $user_can_see_form = true;
     182            }
     183        }
     184    }
     185    ?>
     186    <?php if ($user_can_see_form) : ?>
    143187        <?php if (!$user_has_reviewed) :
    144188            // Debug: Show if user is logged in but hasn't reviewed
     
    147191            }
    148192        ?>
    149             <form class="revnextwoo_review_form" method="post" enctype="multipart/form-data">
     193            <?php
     194            // Get max photos from options with a default of 6
     195            $max_photos = get_option('revnextwoo_max_photos', 6);
     196            ?>
     197            <form id="revnextwoo-review-form" class="revnextwoo_review_form" method="post" enctype="multipart/form-data" data-max-photos="<?php echo esc_attr($max_photos); ?>">
    150198                <?php wp_nonce_field('submit_review_' . $product->get_id(), 'review_nonce'); ?>
    151199                <input type="hidden" name="product_id" value="<?php echo esc_attr($product->get_id()); ?>">
     
    163211                <div class="revnextwoo_form_group">
    164212                    <label for="review_title">Review Title</label>
    165                     <input type="text" id="review_title" name="review_title" placeholder="Summarize your review" required>
     213                    <input type="text" id="review_title" name="review_title" maxlength="40" placeholder="Summarize your review">
    166214                </div>
    167215               
    168216                <div class="revnextwoo_form_group">
    169217                    <label for="review_text">Your Review</label>
    170                     <textarea id="review_text" name="review_text" rows="5" placeholder="Share your experience with this product" required></textarea>
     218                    <textarea id="review_text" name="review_text" rows="5" required></textarea>
    171219                </div>
    172220               
    173                 <div class="revnextwoo_form_group">
    174                     <label>Add Photos (Optional)</label>
    175                     <div class="revnextwoo_media_upload">
    176                         <div class="revnextwoo_media_preview"></div>
    177                         <label for="review_images" class="revnextwoo_upload_btn">
    178                             <span class="dashicons dashicons-format-image"></span>
    179                             <span>Add Images</span>
    180                             <input type="file" id="review_images" name="review_images[]" multiple accept="image/*" style="display: none;">
    181                         </label>
    182                         <small>Max 5 images. Supported formats: JPG, PNG, GIF. Max size: 2MB per image.</small>
    183                     </div>
    184                 </div>
    185                
     221<!-- image upload section start here -->
     222<?php if (get_option('revnextwoo_enable_photo_upload', 'yes') === 'yes') : ?>
     223    <!-- image upload section -->
     224    <div class="revnextwoo_form_group">
     225        <label>Add Photos (Optional)</label>
     226        <div class="revnextwoo_media_upload">
     227                <div style="width:50%;height:50%;overflow:hidden;" class="revnextwoo_media_preview"></div>
     228                <label for="review_images" class="revnextwoo_upload_btn">
     229                    <span class="dashicons dashicons-format-image"></span>
     230                    <span>Add Images</span>
     231                <input type="file" id="review_images" name="review_images[]" multiple accept="image/*" style="display: none;">
     232            </label>
     233            <small>Max <?php echo esc_html(get_option('revnextwoo_max_photos', 3)); ?> images. Supported formats: JPG, PNG, GIF.</small>
     234        </div>
     235    </div>
     236<?php endif; ?>
     237<!-- Video Review Upload -->
     238<?php
     239$revnextwoo_video_enabled = get_option('revnextwoo_enable_video_upload', 'yes');
     240$revnextwoo_max_video_size = 50; // Hardcoded limit for free version
     241if ($revnextwoo_video_enabled === 'yes') : ?>
     242<div class="revnextwoo_form_group">
     243    <label>Add Video (Optional)</label>
     244    <div class="revnextwoo_video_upload">
     245        <div class="revnextwoo_video_preview"><!-- Video preview and cancel button will appear here --></div>
     246        <label for="review_video" class="revnextwoo_upload_btn">
     247            <span class="dashicons dashicons-format-video"></span>
     248            <span>Add Video</span>
     249            <input type="file" id="review_video" name="review_video" accept="video/mp4,video/webm" style="display: none;" data-max-size-mb="<?php echo esc_attr($revnextwoo_max_video_size); ?>">
     250        </label>
     251        <small>Max 1 video. Supported formats: MP4, WebM. Max size: <?php echo esc_html($revnextwoo_max_video_size); ?>MB.</small>
     252    </div>
     253</div>
     254<?php endif; ?>
     255
    186256                <?php wp_nonce_field('revnextwoo_review_images', 'revnextwoo_review_images_nonce'); ?>
    187257               
     
    195265    <?php else : ?>
    196266        <div class="revnextwoo_login_notice">
    197             <p>Please <a href="<?php echo esc_url(wp_login_url(get_permalink())); ?>">login</a> to leave a review.</p>
     267            <?php
     268            if ($allowed_to_rate === 'no_one') {
     269                echo '<p>The Review System is Currently OFF.</p>';
     270            } elseif ($allowed_to_rate === 'completed_orders') {
     271                if (!is_user_logged_in()) {
     272                    echo '<p>Please <a href="' . esc_url(wp_login_url(get_permalink())) . '">login</a> to leave a review.</p>';
     273                } else {
     274                    echo '<p>Please Place an order to make a review.</p>';
     275                }
     276            } else {
     277                // Default: registered_users_and_guests
     278                echo '<p>Please <a href="' . esc_url(wp_login_url(get_permalink())) . '">login</a> to leave a review.</p>';
     279            }
     280            ?>
    198281        </div>
    199282    <?php endif; ?>
     
    201284
    202285<!-- Reviews List -->
    203 <div class="revnextwoo_reviews_list">
     286<?php $review_section_bg = get_option('revnextwoo_product_review_section_bg', '#ffffff'); ?>
     287<div class="revnextwoo_reviews_list" style="background-color: <?php echo esc_attr($review_section_bg); ?>;">
    204288    <div class="revnextwoo_reviews_header">
    205289        <h3>Product Reviews</h3>
     
    210294                <option value="highest">Highest Rating</option>
    211295                <option value="lowest">Lowest Rating</option>
     296                <option value="helpful">Most Voted</option>
     297                <option value="media">With Images/Videos</option>
    212298            </select>
    213299        </div>
     
    215301
    216302    <?php if (!empty($reviews)) : ?>
    217         <div class="revnextwoo_reviews_container">
     303        <?php $layout = get_option('revnextwoo_review_layout_style', 'default'); ?>
     304<div class="revnextwoo_reviews_container revnextwoo_layout_<?php echo esc_attr($layout); ?>" data-product-id="<?php echo esc_attr($product->get_id()); ?>">
    218305            <?php foreach ($reviews as $review) :
    219                 $rating = get_comment_meta($review->comment_ID, 'rating', true);
     306                $review_rating = intval(get_comment_meta($review->comment_ID, 'rating', true));
    220307                $verified = wc_review_is_from_verified_owner($review->comment_ID);
    221                 $review_date = get_comment_date('F j, Y', $review->comment_ID);
     308                $review_timestamp = strtotime($review->comment_date_gmt ?: $review->comment_date);
     309                if (!$review_timestamp) {
     310                    // Fallback to WP helper if parsing fails
     311                    $review_timestamp = (int) get_comment_time('U', false, true, $review->comment_ID);
     312                }
     313                $review_date = human_time_diff($review_timestamp, current_time('timestamp')) . ' ago';
    222314                $review_images = get_comment_meta($review->comment_ID, 'review_images', true);
    223315            ?>
    224                 <div class="revnextwoo_review_item">
    225                     <div class="revnextwoo_review_header">
     316            <?php
     317                $grid_colors = ['#ffb6b6','#6a7ee7','#8df7b1','#f9d56e','#f97e7e','#7ee8fa','#b6ffb6','#c6a1ff'];
     318                $masonry_color = '';
     319                $style_attr = '';
     320                if($layout === 'default'){
     321                    $style_attr = 'margin-bottom:20px;';
     322                    $style_attr .= 'border-radius:10px;';
     323                    $style_attr .= 'border:1px solid #ccc;';
     324                    $style_attr .= 'overflow:hidden;';
     325                    $item_bg = get_option('revnextwoo_review_item_bg', '#fff');
     326                    $style_attr .= ' background-color: ' . esc_attr($item_bg) . ';';
     327                }
     328                if ($layout === 'grid') {
     329                    $masonry_color = $grid_colors[array_rand($grid_colors)];
     330                    $style_attr = '--masonry-color: ' . $masonry_color . ';';
     331                }
     332                if ($layout === 'card') {
     333                    $item_bg = get_option('revnextwoo_review_item_bg', '#fff');
     334                    $style_attr .= ' background-color: ' . esc_attr($item_bg) . ';';
     335                }
     336            ?>
     337            <div class="revnextwoo_review_item"<?php echo !empty($style_attr) ? ' style="' . $style_attr . '"' : ''; ?>>
     338                <?php
     339                // BADGE LOGIC: Get CAS and badge for this user
     340                $user_id = $review->user_id;
     341                $cas = 0;
     342                $badge = '';
     343                // Fetch admin thresholds
     344                $min_bronze = (int) get_option('revnextwoo_min_bronze_cas', 300);
     345                // Calculate CAS for this user (same as backend logic)
     346                $order_count = 0; $total_spend = 0; $review_count = 0; $rating_sum = 0; $rating_num = 0;
     347                if ($user_id) {
     348                    if (function_exists('wc_get_orders')) {
     349                        $orders = wc_get_orders([
     350                            'customer_id' => $user_id,
     351                            'status' => ['wc-completed', 'wc-processing', 'wc-delivered'],
     352                            'return' => 'ids',
     353                            'limit' => -1,
     354                        ]);
     355                        $order_count = count($orders);
     356                        foreach ($orders as $order_id) {
     357                            $order = wc_get_order($order_id);
     358                            if ($order) $total_spend += $order->get_total();
     359                        }
     360                    }
     361                    $review_count = get_comments([
     362                        'user_id' => $user_id,
     363                        'status' => 'approve',
     364                        'type' => 'review',
     365                        'count' => true,
     366                    ]);
     367                    $review_comments = get_comments([
     368                        'user_id' => $user_id,
     369                        'status' => 'approve',
     370                        'type' => 'review',
     371                        'fields' => 'ids',
     372                    ]);
     373                    foreach ($review_comments as $cid) {
     374                        $user_rating_value = get_comment_meta($cid, 'rating', true);
     375                        if ($user_rating_value !== '') {
     376                            $rating_sum += floatval($user_rating_value);
     377                            $rating_num++;
     378                        }
     379                    }
     380                    $cas = ($order_count * 2) + ($review_count * 1.5) + ($total_spend / 100);
     381                    if ($cas >= $min_bronze) {
     382                        $badge = 'bronze';
     383                    }
     384                }
     385                ?>
     386                <?php if ($badge === 'bronze'): ?>
     387                    <span class="revnextwoo-badge revnextwoo-badge-bronze">
     388                        <svg viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" stroke-width="3" stroke="#CD7F32" fill="none" width="36" height="36"><g><path d="M45.41,11.16l.86,2.61a3.75,3.75,0,0,0,1.5,2L50,17.21a3.85,3.85,0,0,1,1.43,4.62l-.84,2.09a3.83,3.83,0,0,0,0,2.88l.83,2a3.83,3.83,0,0,1-1.32,4.58L48.13,34.8a3.86,3.86,0,0,0-1.46,2.08L46,39.32a3.84,3.84,0,0,1-4,2.78l-2.1-.18a3.88,3.88,0,0,0-2.74.84l-2,1.63a3.83,3.83,0,0,1-4.85,0L28.54,43a3.85,3.85,0,0,0-2.45-.88H23a3.83,3.83,0,0,1-3.74-3l-.55-2.35a3.82,3.82,0,0,0-1.58-2.31L15.3,33.19a3.84,3.84,0,0,1-1.45-4.48l.86-2.36a3.86,3.86,0,0,0,0-2.66l-.77-2.06a3.86,3.86,0,0,1,1.51-4.58l2-1.31a3.87,3.87,0,0,0,1.61-2.19l.61-2.18a3.85,3.85,0,0,1,4-2.79l1.93.17a3.88,3.88,0,0,0,2.74-.84l1.83-1.48a3.84,3.84,0,0,1,4.87,0L36.7,7.81a3.82,3.82,0,0,0,2.75.88l2-.15A3.84,3.84,0,0,1,45.41,11.16Z"></path><path d="M44.59,41.57l7,12.21c.18.31,0,.64-.23.55l-8.61-3.5a.24.24,0,0,0-.31.19l-1.06,9c-.06.29-.42.25-.6-.06L32.65,45.25" stroke-linecap="round"></path><path d="M32.65,45.25,25.14,60c-.19.31-.53.35-.6.07l-1.27-9.2a.24.24,0,0,0-.31-.18L14.59,54.3c-.28.08-.43-.24-.24-.56l7-12.17" stroke-linecap="round"></path><path d="M32.74,16.89l2.7,5.49a.1.1,0,0,0,.08.05l6,.88c.08,0,.12.12.06.17l-4.38,4.27a.09.09,0,0,0,0,.09l1,6a.1.1,0,0,1-.14.11l-5.42-2.85a.07.07,0,0,0-.09,0L27.19,34a.11.11,0,0,1-.15-.11l1-6a.1.1,0,0,0,0-.09l-4.39-4.27a.11.11,0,0,1,.06-.17l6.05-.88a.09.09,0,0,0,.08-.05l2.71-5.49A.1.1,0,0,1,32.74,16.89Z" stroke-linecap="round"></path></g></svg>
     389                    </span>
     390                <?php endif; ?>
     391                    <?php $header_bg = get_option('revnextwoo_review_box_header_footer_bg', '#ffffff'); ?>
     392                    <div class="revnextwoo_review_header" style="background-color: <?php echo esc_attr($header_bg); ?>;">
     393                       
    226394                        <div class="revnextwoo_reviewer_info">
    227395                            <div class="revnextwoo_reviewer_avatar">
    228                                 <?php echo get_avatar($review->comment_author_email, 50); ?>
     396                                <?php
     397                                $user_avatar_url = '';
     398                                if (!empty($review->user_id)) {
     399                                    $user_avatar_url = get_user_meta($review->user_id, 'revnextwoo_profile_avatar', true);
     400                                }
     401                                if (!$user_avatar_url) {
     402                                    $settings = get_option('revnextwoo_social_settings');
     403                                    $user_avatar_url = isset($settings['custom_avatar_user']) ? $settings['custom_avatar_user'] : '';
     404                                }
     405                                if ($user_avatar_url) {
     406                                    echo '<img src="' . esc_url($user_avatar_url) . '" class="avatar avatar-50 photo" width="50" height="50" style="border-radius:50%;object-fit:cover;" alt="User Avatar" />';
     407                                } else {
     408                                    $avatar_key = !empty($review->user_id) ? intval($review->user_id) : (isset($review->comment_author_email) ? $review->comment_author_email : '');
     409                                    echo get_avatar($avatar_key, 50);
     410                                }
     411                                ?>
    229412                            </div>
    230413                            <div class="revnextwoo_reviewer_details">
    231                                 <h4 class="revnextwoo_reviewer_name"><?php echo esc_html($review->comment_author); ?></h4>
     414                                <?php if ($verified) : ?>
     415                                    <span class="revnextwoo_verified_badge">Verified Purchase</span>
     416                                <?php endif; ?>
     417                                <h4 class="revnextwoo_reviewer_name"><b><?php echo esc_html($review->comment_author); ?></b></h4>
    232418                                <div class="revnextwoo_review_meta">
    233419                                    <div class="revnextwoo_review_rating">
    234                                         <?php for ($i = 1; $i <= 5; $i++) : ?>
    235                                             <span class="dashicons dashicons-star-<?php echo $i <= $rating ? 'filled' : 'empty'; ?>"></span>
     420                                        <?php
     421                                        $current_icon_set = isset($icon_classes[$rating_images]) ? $icon_classes[$rating_images] : $icon_classes['star'];
     422                                        for ($i = 1; $i <= 5; $i++) :
     423                                            $iconType = ($i <= $review_rating) ? 'filled' : 'empty';
     424                                            $iconClass = isset($current_icon_set[$iconType]) ? $current_icon_set[$iconType] : '';
     425                                        ?>
     426                                            <i class="<?php echo esc_attr($iconClass); ?>"></i>
    236427                                        <?php endfor; ?>
    237428                                    </div>
    238                                     <span class="revnextwoo_review_date"><?php echo esc_html($review_date); ?></span>
    239                                     <?php if ($verified) : ?>
    240                                         <span class="revnextwoo_verified_badge">Verified Purchase</span>
    241                                     <?php endif; ?>
    242429                                </div>
     430                                <span class="revnextwoo_review_date"><?php echo esc_html($review_date); ?></span>
     431                                <?php
     432                                    // Location display logic (same as comment-item-display.php)
     433                                    $reviewer_city = get_comment_meta($review->comment_ID, 'reviewer_city', true);
     434                                    $reviewer_country = get_comment_meta($review->comment_ID, 'reviewer_country', true);
     435                                    $location_display = '';
     436                                    if (!empty($reviewer_city) || !empty($reviewer_country)) {
     437                                        $location_parts = array();
     438                                        if (!empty($reviewer_city)) {
     439                                            $location_parts[] = esc_html($reviewer_city);
     440                                        }
     441                                        if (!empty($reviewer_country)) {
     442                                            $location_parts[] = esc_html($reviewer_country);
     443                                        }
     444                                        $location_display = '<span class="review-location">' . implode(', ', $location_parts) . '</span>';
     445                                    }
     446                                    if (!empty($location_display)) {
     447                                        echo $location_display;
     448                                    }
     449                                    ?>
    243450                            </div>
    244451                        </div>
     
    253460                        }
    254461                        ?>
    255                         <h4 class="revnextwoo_review_title"><?php echo esc_html($review_title); ?></h4>
    256                         <p class="revnextwoo_review_text"><?php echo esc_html($review->comment_content); ?></p>
     462                        <div class="revnextwoo_text_box">
     463                            <h3 class="revnextwoo_review_title"><b><?php echo esc_html($review_title); ?></b></h3>
     464                            <p class="revnextwoo_review_text" data-full-text="<?php echo esc_attr(wp_strip_all_tags($review->comment_content)); ?>">
     465                                <?php
     466                                $content = wp_strip_all_tags($review->comment_content);
     467                                $words = explode(' ', $content);
     468                                if (count($words) > 12) {
     469                                    $short_content = implode(' ', array_slice($words, 0, 12));
     470                                    echo esc_html($short_content . '...');
     471                                } else {
     472                                    echo esc_html($content);
     473                                }
     474                                ?>
     475                            </p>
     476                            <?php if (count($words) > 12) : ?>
     477                                <span class="revnextwoo_read_more">Read More</span>
     478                            <?php endif; ?>
     479                        </div>
    257480                       
    258481                        <?php if (!empty($review_images) && is_array($review_images)) : ?>
    259482                            <div class="revnextwoo_review_images">
    260                                 <?php foreach (array_slice($review_images, 0, 3) as $image_id) : ?>
     483                                <?php foreach ($review_images as $image_id) : ?>
    261484                                    <div class="revnextwoo_review_image">
    262                                         <?php echo wp_get_attachment_image($image_id, 'thumbnail'); ?>
     485                                        <a href="<?php echo esc_url(wp_get_attachment_url($image_id)); ?>"  data-lightbox="example-set" ><?php echo wp_get_attachment_image($image_id, 'thumbnail'); ?></a>
    263486                                    </div>
    264487                                <?php endforeach; ?>
    265                                 <?php if (count($review_images) > 3) : ?>
    266                                     <div class="revnextwoo_review_image_more">
    267                                         +<?php echo count($review_images) - 3; ?> more
    268                                     </div>
    269                                 <?php endif; ?>
    270488                            </div>
    271489                        <?php endif; ?>
    272                     </div>
    273                     <div class="revnextwoo_review_actions">
    274                         <button class="revnextwoo_like_btn" data-review-id="<?php echo esc_attr($review->comment_ID); ?>">
     490                         <?php
     491                         $video_id = get_comment_meta($review->comment_ID, 'review_video', true);
     492                         if ($video_id) {
     493                             $video_url = wp_get_attachment_url($video_id);
     494                             if ($video_url) {
     495                                 echo '<div class="revnextwoo_review_video">';
     496                                 echo '<video controls width="320" height="240" style="max-width:100%;margin-top:10px;">';
     497                                 echo '<source src="' . esc_url($video_url) . '" type="video/mp4">';
     498                                 echo __('Your browser does not support the video tag.', 'review-next-for-woocommerce');
     499                                 echo '</video>';
     500                                 echo '</div>';
     501                             }
     502                         }
     503                         ?>
     504                     </div>
     505                     <?php $header_bg = get_option('revnextwoo_review_box_header_footer_bg', '#ffffff'); ?>
     506                    <div class="revnextwoo_review_actions" style="background-color: <?php echo esc_attr($header_bg); ?>">
     507                        <?php
     508                        $settings = get_option('revnextwoo_social_settings');
     509                        $show_thumbs_up = isset($settings['enable_thumbs_up']) && $settings['enable_thumbs_up'] === 'yes';
     510                        $show_thumbs_down = isset($settings['enable_thumbs_down']) && $settings['enable_thumbs_down'] === 'yes';
     511                        ?>
     512                        <?php if ($show_thumbs_up): ?>
     513                        <button class="revnextwoo_like_btn revnextwoo_vote_btn" data-review-id="<?php echo esc_attr($review->comment_ID); ?>" data-vote-type="up">
    275514                            <span class="dashicons dashicons-thumbs-up"></span>
    276515                            <span class="revnextwoo_like_count"><?php echo get_comment_meta($review->comment_ID, 'likes', true) ?: 0; ?></span>
    277516                        </button>
    278                         <button class="revnextwoo_report_btn">Report</button>
     517                        <?php endif; ?>
     518
     519                       
     520                        <div class="revnextwoo_social_sharing">
     521                            <?php
     522                            $show_facebook = isset($settings['enable_facebook_share']) && $settings['enable_facebook_share'] === 'yes';
     523                            $show_whatsapp = isset($settings['enable_whatsapp_share']) && $settings['enable_whatsapp_share'] === 'yes';
     524                            $show_twitter = isset($settings['enable_twitter_share']) && $settings['enable_twitter_share'] === 'yes';
     525                            ?>
     526                            <?php if ($show_facebook): ?>
     527                            <a href="#" class="revnextwoo_share_btn revnextwoo_facebook_share" title="Share on Facebook">
     528                                <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="#1877F2"><path d="M22.675 0h-21.35c-.732 0-1.325.593-1.325 1.325v21.351c0 .731.593 1.324 1.325 1.324h11.495v-9.294h-3.128v-3.622h3.128v-2.671c0-3.1 1.893-4.788 4.659-4.788 1.325 0 2.463.099 2.795.143v3.24l-1.918.001c-1.504 0-1.795.715-1.795 1.763v2.313h3.587l-.467 3.622h-3.12v9.293h6.116c.73 0 1.323-.593 1.323-1.325v-21.35c0-.732-.593-1.325-1.325-1.325z"/></svg>
     529                            </a>
     530                            <?php endif; ?>
     531
     532
     533                        </div>
     534                       
     535
    279536                    </div>
     537                    <span class="revnextwoo_vote_message" style="display:none;"></span>
    280538                </div>
    281539            <?php endforeach; ?>
     
    284542        <p class="revnextwoo_no_reviews">No reviews yet. Be the first to review!</p>
    285543    <?php endif; ?>
     544
     545    <?php
     546    // Pagination controls
     547    // Pagination controls removed
     548    ?>
    286549</div>
    287550
    288 
     551<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.8.2/css/lightbox.css'>
     552<style>
     553.revnextwoo-pagination {
     554    display: flex;
     555    align-items: center;
     556    justify-content: center;
     557    gap: 22px;
     558    margin: 32px 0 10px 0;
     559    padding: 0;
     560}
     561.revnextwoo-pagination li {
     562    list-style: none;
     563    margin: 0;
     564    padding: 0;
     565}
     566.revnextwoo-page-num, .revnextwoo-page-prev, .revnextwoo-page-next {
     567    display: flex;
     568    align-items: center;
     569    justify-content: center;
     570    width: 44px;
     571    height: 44px;
     572    border-radius: 50%;
     573    background: #fff;
     574    border: 2px solid #e1e1e1;
     575    color: #222;
     576    font-size: 1.2rem;
     577    font-weight: 500;
     578    cursor: pointer;
     579    transition: background 0.18s, color 0.18s, border 0.18s;
     580    user-select: none;
     581}
     582.revnextwoo-page-num.active {
     583    background: #111;
     584    color: #fff;
     585    border: 2px solid #111;
     586    font-weight: 700;
     587}
     588.revnextwoo-page-num:hover:not(.active), .revnextwoo-page-prev:hover:not(.disabled), .revnextwoo-page-next:hover:not(.disabled) {
     589    background: #f2f2f2;
     590    color: #111;
     591    border-color: #bdbdbd;
     592}
     593.revnextwoo-page-prev.disabled, .revnextwoo-page-next.disabled, .revnextwoo-page-num.disabled {
     594    opacity: 0.5;
     595    pointer-events: none;
     596    background: #fafafa;
     597    color: #bdbdbd;
     598    border-color: #e1e1e1;
     599}
     600.revnextwoo-page-prev span, .revnextwoo-page-next span, .revnextwoo-page-num span {
     601    display: block;
     602    line-height: 1;
     603    font-size: 1.35rem;
     604}
     605
     606.revnextwoo-badge {
     607    position: absolute;
     608    top: 12px;
     609    right: 12px;
     610    z-index: 10;
     611    display: flex;
     612    align-items: center;
     613    justify-content: center;
     614    width: 40px;
     615    height: 40px;
     616    border-radius: 50%;
     617    background: #fff;
     618    box-shadow: 0 2px 8px rgba(0,0,0,0.08);
     619    padding: 2px;
     620}
     621.revnextwoo-badge-gold svg {
     622    stroke: #ffd900;
     623}
     624.revnextwoo-badge-bronze svg {
     625    stroke: #CD7F32;
     626}
     627.revnextwoo-badge-silver svg {
     628    stroke: #C0C0C0;
     629}
     630.revnextwoo_review_item {
     631    position: relative;
     632}
     633
     634.revnextwoo_actions_menu {
     635    position: relative;
     636    display: inline-block;
     637    vertical-align: middle;
     638    margin-left: 10px;
     639}
     640
     641.revnextwoo_actions_toggle {
     642    background: none;
     643    border: none;
     644    cursor: pointer;
     645    padding: 5px;
     646    border-radius: 4px;
     647    display: flex;
     648    align-items: center;
     649    justify-content: center;
     650    color: #666;
     651    transition: all 0.2s ease;
     652}
     653
     654.revnextwoo_actions_toggle:hover {
     655    background: rgba(0, 0, 0, 0.05);
     656    color: #333;
     657}
     658
     659.revnextwoo_actions_dropdown {
     660    position: absolute;
     661    right: 0;
     662    top: 100%;
     663    margin-top: 5px;
     664    background: #fff;
     665    border: 1px solid #ddd;
     666    border-radius: 4px;
     667    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
     668    z-index: 1000;
     669    min-width: 160px;
     670    overflow: hidden;
     671}
     672
     673.revnextwoo_action_item {
     674    display: block;
     675    width: 100%;
     676    padding: 8px 16px;
     677    text-align: left;
     678    background: none;
     679    border: none;
     680    color: #333;
     681    cursor: pointer;
     682    transition: all 0.2s ease;
     683}
     684
     685.revnextwoo_action_item:hover {
     686    background: #f5f5f5;
     687    color: #000;
     688}
     689
     690.revnextwoo_actions_dropdown.show {
     691    display: block !important;
     692    animation: fadeIn 0.2s ease;
     693}
     694
     695@keyframes fadeIn {
     696    from { opacity: 0; transform: translateY(-5px); }
     697    to { opacity: 1; transform: translateY(0); }
     698}
     699</style>
    289700
    290701<script>
     702var revnextwoo_img_compression = '<?php echo esc_js(get_option('revnextwoo_img_compression', 'yes')); ?>';
     703var revnextwoo_img_compression_scale = '<?php echo esc_js(get_option('revnextwoo_img_compression_scale', 0.15)); ?>';
     704var revnextwoo_img_watermark_enabled = '<?php echo esc_js(get_option('revnextwoo_img_watermark', 'no')); ?>';
     705var revnextwoo_img_watermark_text = '<?php echo esc_js(get_option('revnextwoo_img_watermark_text', '')); ?>';
    291706jQuery(document).ready(function($) {
     707    // Toggle actions dropdown
     708    $(document).on('click', '.revnextwoo_actions_toggle', function(e) {
     709        e.stopPropagation();
     710        var $dropdown = $(this).siblings('.revnextwoo_actions_dropdown');
     711        $('.revnextwoo_actions_dropdown').not($dropdown).removeClass('show').hide();
     712        $dropdown.toggleClass('show').toggle();
     713    });
     714
     715    // Close dropdown when clicking outside
     716    $(document).on('click', function(e) {
     717        if (!$(e.target).closest('.revnextwoo_actions_menu').length) {
     718            $('.revnextwoo_actions_dropdown').removeClass('show').hide();
     719        }
     720    });
     721
     722    // Handle action items click
     723    $(document).on('click', '.revnextwoo_action_item', function() {
     724        var action = $(this).data('action');
     725        var $review = $(this).closest('.revnextwoo_review_item');
     726       
     727        if (action === 'report') {
     728            if (confirm('Are you sure you want to report this review?')) {
     729                // Add your report logic here
     730                alert('Thank you for reporting this review. Our team will review it shortly.');
     731            }
     732        } else if (action === 'edit') {
     733            // Add your edit logic here
     734            alert('Edit review functionality will be implemented here.');
     735        }
     736       
     737        // Close the dropdown
     738        $(this).closest('.revnextwoo_actions_dropdown').removeClass('show').hide();
     739    });
     740
     741    // Handle image preview and upload
    292742    // Handle image preview and upload
    293743    $('body').on('change', '#review_images', function(e) {
     
    296746        preview.empty();
    297747       
    298         // Limit to 5 images
    299         if (files.length > 5) {
    300             alert('You can upload a maximum of 5 images.');
     748        // Get max images from form data attribute or default to 6
     749        const maxImages = $('#revnextwoo-review-form').data('max-photos') || 6;
     750       
     751        // Check against dynamic limit
     752        if (files.length > maxImages) {
     753            alert('You can upload a maximum of ' + maxImages + ' images.');
    301754            this.value = '';
    302755            return false;
    303756        }
    304757       
    305         // Check file sizes and types
    306758        for (let i = 0; i < files.length; i++) {
    307759            const file = files[i];
    308            
    309             // Check file size (2MB max)
    310             if (file.size > 2 * 1024 * 1024) {
    311                 alert('One or more files exceed the 2MB size limit.');
    312                 this.value = '';
    313                 preview.empty();
    314                 return false;
    315             }
    316760           
    317761            // Check file type
     
    322766                return false;
    323767            }
    324            
    325             // Create preview
     768
    326769            const reader = new FileReader();
    327770            reader.onload = function(e) {
     
    439882
    440883   
    441     // Handle like button click
    442     $('.revnextwoo_like_btn').on('click', function() {
     884        // Handle Read More functionality
     885    $('body').on('click', '.revnextwoo_read_more', function() {
     886        const $this = $(this);
     887        const $text = $this.siblings('.revnextwoo_review_text');
     888        const fullText = $text.data('full-text');
     889       
     890        if ($this.text() === 'Read More') {
     891            $text.text(fullText);
     892            $this.text('Read Less');
     893        } else {
     894            const words = fullText.split(' ');
     895            const shortText = words.slice(0, 12).join(' ') + '...';
     896            $text.text(shortText);
     897            $this.text('Read More');
     898        }
     899    });
     900   
     901    // Handle like/dislike voting (both buttons)
     902    $('.revnextwoo_vote_btn').on('click', function(e) {
     903        e.preventDefault();
    443904        var button = $(this);
    444905        var reviewId = button.data('review-id');
    445        
     906        var voteType = button.data('vote-type');
     907        var reviewItem = button.closest('.revnextwoo_review_item');
     908        var messageSpan = reviewItem.find('.revnextwoo_vote_message');
     909        var nonce = '<?php echo wp_create_nonce('revnextwoo_vote_nonce'); ?>';
     910        // Prevent double click
     911        if (button.hasClass('voted')) return;
    446912        $.ajax({
    447913            url: '<?php echo admin_url('admin-ajax.php'); ?>',
    448914            type: 'POST',
     915            dataType: 'json',
    449916            data: {
    450                 action: 'revnextwoo_like_review',
     917                action: 'revnextwoo_vote_review',
    451918                review_id: reviewId,
    452                 nonce: '<?php echo wp_create_nonce('like_review_nonce'); ?>'
     919                vote_type: voteType,
     920                nonce: nonce
    453921            },
    454922            success: function(response) {
    455923                if (response.success) {
    456                     var count = button.find('.revnextwoo_like_count');
    457                     count.text(parseInt(count.text()) + 1);
    458                     button.addClass('liked');
     924                    if (voteType === 'up') {
     925                        var countSpan = button.find('.revnextwoo_like_count');
     926                        countSpan.text(response.data.likes);
     927                    } else if (voteType === 'down') {
     928                        var countSpan = button.find('.revnextwoo_dislike_count');
     929                        countSpan.text(response.data.dislikes);
     930                    }
     931                    button.addClass('voted');
     932                    messageSpan.hide();
    459933                } else {
    460                     alert('You have already liked this review.');
     934                    // Show vote message
     935                    messageSpan.text(response.data.message || 'You have already voted once, Thanks').show();
    461936                }
     937            },
     938            error: function() {
     939                messageSpan.text('An error occurred. Please try again.').show();
    462940            }
    463941        });
     
    465943});
    466944</script>
     945<!-- jquery image lightbox cdn  -->
     946<script src='https://code.jquery.com/jquery-2.2.4.min.js'></script>
     947<script src='https://cdnjs.cloudflare.com/ajax/libs/lightbox2/2.8.2/js/lightbox.min.js'></script><script  src="..assets/js/vendor/script.js"></script>
  • review-next-for-woocommerce/trunk/template/frontend/user-question.php

    r3315734 r3457384  
    33if (! defined('ABSPATH')) exit;
    44
     5
     6// --- Validation logic for questions and replies ---
     7function revnextwoo_validate_question($question) {
     8    $emailRegex = '/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}/';
     9    $phoneRegex = '/\b\d{10,11}\b/';
     10    $linkRegex = '/https?:\/\/[^
     11\s\/$.#?]+[^
     12\s]*/';
     13    if (preg_match($emailRegex, $question) || preg_match($phoneRegex, $question) || preg_match($linkRegex, $question)) {
     14        return false;
     15    }
     16    return true;
     17  }
     18
     19// --- Handle user question form POST ---
     20if (
     21    isset(
     22        $_POST['question'],
     23        $_POST['comment_post_ID'],
     24        $_POST['question_nonce']) &&
     25    wp_verify_nonce($_POST['question_nonce'], 'submit_question_nonce') &&
     26    is_user_logged_in()
     27) {
     28    $question_content = wp_strip_all_tags($_POST['question']);
     29    if (!revnextwoo_validate_question($question_content)) {
     30        wp_die('Your question should not contain contact information.');
     31    }
     32    $question_data = array(
     33        'product_id'   => intval($_POST['comment_post_ID']),
     34        'user_id'      => get_current_user_id(),
     35        'author_name'  => wp_get_current_user()->display_name,
     36        'author_email' => wp_get_current_user()->user_email,
     37        'content'      => $question_content,
     38        'status'       => 'approve',
     39    );
     40    if (class_exists('Revnextwoo_QA_DB')) {
     41        Revnextwoo_QA_DB::insert_question($question_data);
     42        wp_safe_redirect(add_query_arg(array('question_added' => 1)));
     43        exit;
     44    }
     45}
     46
     47// --- Handle admin/user reply form POST ---
     48if (
     49    isset(
     50        $_POST['question_reply'],
     51        $_POST['comment_post_ID'],
     52        $_POST['comment_parent'],
     53        $_POST['question_reply_nonce']) &&
     54    wp_verify_nonce($_POST['question_reply_nonce'], 'submit_question_reply_nonce') &&
     55    is_user_logged_in()
     56) {
     57    $reply_content = wp_strip_all_tags($_POST['question_reply']);
     58    if (!revnextwoo_validate_question($reply_content)) {
     59        wp_die('Your question reply should not contain contact information.');
     60    }
     61    $reply_data = array(
     62        'product_id'   => intval($_POST['comment_post_ID']),
     63        'parent_id'    => intval($_POST['comment_parent']),
     64        'user_id'      => get_current_user_id(),
     65        'author_name'  => wp_get_current_user()->display_name,
     66        'author_email' => wp_get_current_user()->user_email,
     67        'content'      => $reply_content,
     68        'status'       => 'approve',
     69    );
     70    if (class_exists('Revnextwoo_QA_DB')) {
     71        Revnextwoo_QA_DB::insert_reply($reply_data);
     72        wp_safe_redirect(add_query_arg(array('reply_added' => 1)));
     73        exit;
     74    }
     75}
     76
     77
     78
    579global $product;
    6 $product_id = $product->get_id();
    7 $comments_per_page = 5;
    8 $page = get_query_var('paged') ? get_query_var('paged') : 1; // Get the current page or default to page 1
    9 $questions = get_comments(
    10   array(
    11     'post_id' => $product->get_id(),
     80$product_id = 0;
     81if (is_object($product) && method_exists($product, 'get_id')) {
     82  $product_id = $product->get_id();
     83}
     84if (!$product_id && isset($_POST['product_id'])) {
     85  $product_id = intval($_POST['product_id']);
     86}
     87if (!$product_id) {
     88  $product_id = get_the_ID();
     89}
     90$comments_per_page = 4;
     91$page = isset($_POST['paged']) ? intval($_POST['paged']) : (isset($_GET['paged']) ? intval($_GET['paged']) : 1); // AJAX or normal
     92if ($page < 1) { $page = 1; }
     93// Fetch from custom QA table
     94$questions = array();
     95$question_count = 0;
     96$question_reply_count = 0;
     97if (class_exists('Revnextwoo_QA_DB')) {
     98  $questions = Revnextwoo_QA_DB::get_questions($product_id, array(
    1299    'status' => 'approve',
    13     // Display approved comments
    14100    'number' => $comments_per_page,
    15     // Limit the number of comments to 15
    16     'order' => 'DESC',
    17     // Order by most recent comments
    18     'type' => 'question',
    19101    'offset' => ($page - 1) * $comments_per_page,
    20   )
    21 );
    22 $args = array(
    23   'post_id' => $product->get_id(),
    24   'type' => 'question',
    25   'count' => true // return only the count
    26 );
    27 // Get the number of reviews for the product
    28 $question_count = get_comments($args);
    29 $args = array(
    30   'post_id' => $product->get_id(),
    31   'type' => 'question_reply',
    32   'count' => true // return only the count
    33 );
    34 // Get the number of reviews for the product
    35 $question_reply_count = get_comments($args);
    36 
     102    'order'  => 'DESC',
     103  ));
     104  $question_count = Revnextwoo_QA_DB::count_questions($product_id);
     105  // Total replies for this product
     106  global $wpdb;
     107  $qa_table = Revnextwoo_QA_DB::table_name();
     108  $question_reply_count = (int) $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$qa_table} WHERE product_id=%d AND type='question_reply'", $product_id));
     109}
    37110
    38111// Customize your tab title here (e.g., "Custom Tab (X)")
    39 echo '<div class="revnextwoo_question_section" id="revnextwoo_question">';
    40 echo '<h2 class="revnextwoo_title">Questions About This Product (' . esc_html($question_count) . ')</h2>';
     112echo '<div class="user_question_section" id="revnextwoo_question">';
     113echo '<h3 class="title";>Questions About This Product (' . esc_html($question_count) . ')</h3>';
    41114if (is_user_logged_in()) {
    42115  // Output the form HTML here with your specific requirements
     
    46119  echo '<input type="hidden" name="comment_post_ID" value="' . esc_attr($product_id) . '" id="comment_post_ID">';
    47120  echo '<input type="hidden" name="comment_parent" id="comment_parent" value="0">';
    48   echo '<textarea class="question_field" name="question" value="" id="question" placeholder="Ask a Question" required></textarea>';
     121  echo '<textarea class="question_field" name="question" value="" id="question" maxlength="300" placeholder="Ask Your Question" required></textarea>';
    49122  echo '<p id="error-message" class="">Your question should not contain contact information such as email, phone or external web links. Visit "My Orders" if you have questions about your previous order. </p>';
    50   echo '<input class="submit_btn" type="submit" value="ASK QUESTION">';
     123  echo '<div><input class="submit_btn" type="submit" value="ASK QUESTION"></div>';
    51124  echo '</div>';
    52125  echo '</form>';
     
    60133  $register_link = '<a href="' . esc_url($registration_url) . '">Register</a>';
    61134
    62   $login_message = 'Please ' . $login_link . ' or ' . $register_link . ' to ask a question.';
     135
     136  $login_message = '<div class="revnextwoo_login_notice">Please ' . $login_link . ' or ' . $register_link . ' to ask a question.</div>';
    63137
    64138  echo '<p class="login_redirect">' . wp_kses_post($login_message) . '</p>';
    65139}
    66 echo '<div class="revnextwoo_question_show">';
     140// question and replay list show start here----------------------------------------
     141echo '<div class="user_question_show">';
    67142echo '<h4 class="short_title">Other questions answered by Admin (' . esc_html($question_reply_count) . ')</h4>';
    68143echo '<div class="card">';
    69144
    70 // Get the question icon image
    71 $question_icon_path = plugin_dir_path(dirname(dirname(__FILE__))) . 'public/images/problem.png';
    72 $question_icon_id = revnextwoo_get_attachment_id_by_path($question_icon_path, 'problem.png');
    73145foreach ($questions as $question) {
    74   $authorUsername = get_comment_author($question->comment_ID);
    75   $questionDate = gmdate('F j, Y', strtotime($question->comment_date)); // Format the comment date
     146  // Rows from custom table
     147  $authorUsername = isset($question->author_name) ? $question->author_name : '';
     148  $questionDate = revnextwoo_get_relative_time_parts($question->created_at);
    76149  $currentUserId = get_current_user_id();
    77   echo '<div class="item">';
    78   echo '<div class="short_content">';
    79   if ($question_icon_id) {
    80     echo wp_get_attachment_image($question_icon_id, 'thumbnail', false, array('class' => 'question_icon', 'alt' => 'question-icon'));
     150  $avatar_url = '';
     151  if (!empty($question->user_id)) {
     152    $avatar_url = get_user_meta($question->user_id, 'revnextwoo_profile_avatar', true);
     153  }
     154  if (!$avatar_url) {
     155    $settings = get_option('revnextwoo_social_settings');
     156    $avatar_url = isset($settings['custom_avatar_user']) ? $settings['custom_avatar_user'] : '';
     157  }
     158  if ($avatar_url) {
     159    $authorAvatar = '<img src="' . esc_url($avatar_url) . '" class="avatar avatar-50 photo" width="50" height="50" style="border-radius:50%;object-fit:cover;" alt="User Avatar" />';
    81160  } else {
    82     // Fallback if attachment ID not found, use WooCommerce placeholder
    83     echo wp_kses_post(wc_placeholder_img('thumbnail', array('class' => 'question_icon', 'alt' => 'Question Icon Placeholder')));
    84   }
    85   echo '<p>' . esc_html($question->comment_content) . '</p>';
    86   echo '</div>';
    87   echo '<div class="question_dropdown" id="question_dropdown' . esc_attr($question->comment_ID) . '" >';
     161    $avatar_key = !empty($question->user_id) ? intval($question->user_id) : (isset($question->author_email) ? $question->author_email : '');
     162    $authorAvatar = get_avatar($avatar_key, 50);
     163  }
     164
     165
     166  // the replay div start haere --------------------------------------------
     167  $question_has_reply = !empty($question_replies);
     168  $highlight_class = $question_has_reply ? '' : ' no-reply-highlight';
     169  echo '<div class="item' . esc_attr($highlight_class) . '">';
     170  echo "<div class='short_content'>";
     171    echo '<div class="revnext_que_info">';
     172      echo '<div class="revnextwoo_question_avatar">'.$authorAvatar.'</div>';
     173      echo '<div class="revnextwoo_question_username"><h4>'.esc_html($authorUsername).'</h4></div>';
     174    echo '</div>';
     175    echo '<p>' . esc_html($question->content) . '</p>';
     176  echo '</div>';
     177
     178  //question replay button in right side
     179  echo '<div class="question_dropdown" id="question_dropdown' . esc_attr($question->id) . '" >';
    88180  if (current_user_can('administrator')) {
    89     echo '<h5 class="question-reply-trigger" target="' . esc_attr($question->comment_ID) . '"><i class="fas fa-reply"></i></span> Reply</h5>';
    90   }
    91   echo '</div>';
     181    echo '<h5 class="question-reply-trigger" target="' . esc_attr($question->id) . '"><i class="fas fa-reply"></i></span> Reply</h5>';
     182  }
     183  echo '</div>';
     184
     185  //this include the question date below
    92186  echo '<div class="content">';
    93   echo '<p class="create_date">' . esc_html($questionDate) . '</p>';
    94   echo '</div>';
    95   echo '</div>';
    96   echo '<form class="reply_question_fm" id="reply_question_form' . esc_attr($question->comment_ID) . '" method="post" style="display:none">';
     187  $question_date_label = !empty($questionDate['relative']) ? $questionDate['relative'] : $questionDate['absolute'];
     188  $question_date_title = !empty($questionDate['absolute']) ? ' title="' . esc_attr($questionDate['absolute']) . '"' : '';
     189  echo '<p class="create_date"' . $question_date_title . '>' . esc_html($question_date_label).'</p>';
     190  echo '</div>';
     191  echo '</div>';
     192
     193//reply form visible start here ============
     194  echo '<form class="reply_question_fm" id="reply_question_form' . esc_attr($question->id) . '" method="post" style="display:none">';
    97195  wp_nonce_field('submit_question_reply_nonce', 'question_reply_nonce');
    98196  echo '<div class="input_group" id="question_group">';
    99197  echo '<input type="hidden" name="comment_post_ID" value="' . esc_attr($product_id) . '" id="comment_post_ID">';
    100   echo '<input type="hidden" name="comment_parent" id="comment_parent" value="' . esc_attr($question->comment_ID) . '">';
    101   echo '<textarea class="question_field" name="question_reply" value="" id="reply_question_field" placeholder="Ask a Question" required></textarea>';
    102   echo '<p id="reply-error-message" class="">Your question should not contain contact information such as email, phone or external web links. Visit "My Orders" if you have questions about your previous order. </p>';
    103   echo '<input class="submit_btn" type="submit" value="ASK QUESTION">';
     198  echo '<input type="hidden" name="comment_parent" id="comment_parent" value="' . esc_attr($question->id) . '">';
     199  echo '<textarea class="question_field" name="question_reply" value="" id="reply_question_field" placeholder="Reply to this Question" required></textarea>';
     200  echo '<p id="reply-error-message" class="">Your reply should not contain contact information such as email, phone or external web links.</p>';
     201  echo '<input class="submit_btn" type="submit" value="Reply">';
    104202  echo '</div>';
    105203  echo '</form>';
    106204
    107205  // Get and output the replies to this comment
    108   $args = array(
    109     'parent' => $question->comment_ID,
    110     'status' => 'approve',
    111     'order' => 'ASC',
    112     'type' => 'question_reply',
    113   );
    114   $question_replies = get_comments($args);
     206  $question_replies = array();
     207  if (class_exists('Revnextwoo_QA_DB')) {
     208    $question_replies = Revnextwoo_QA_DB::get_replies($question->id, array(
     209      'status' => 'approve',
     210      'order' => 'ASC',
     211    ));
     212  }
    115213  // Get the answer icon image
    116214  $answer_icon_path = plugin_dir_path(dirname(dirname(__FILE__))) . 'public/images/answer.png';
    117215  $answer_icon_id = revnextwoo_get_attachment_id_by_path($answer_icon_path, 'answer.png');
    118216  foreach ($question_replies as $question_reply) {
    119     $questionDate = gmdate('F j, Y', strtotime($question_reply->comment_date));
    120     $authorUsername = get_comment_author($question_reply->comment_ID);
    121     $questionDate = gmdate('F j, Y', strtotime($question_reply->comment_date)); // Format the comment date
     217    $questionDate = revnextwoo_get_relative_time_parts($question_reply->created_at);
     218    $authorUsername = isset($question_reply->author_name) ? $question_reply->author_name : '';
    122219    $currentUserId = get_current_user_id();
    123     echo '<div class="item" style="padding-left:25px">';
    124     echo '<div class="short_content">';
    125     if ($answer_icon_id) {
    126       echo wp_get_attachment_image($answer_icon_id, 'thumbnail', false, array('class' => 'question_icon', 'alt' => 'answer-icon'));
     220    $avatar_url = '';
     221    if (!empty($question_reply->user_id)) {
     222      $avatar_url = get_user_meta($question_reply->user_id, 'revnextwoo_profile_avatar', true);
     223    }
     224    if (!$avatar_url) {
     225      $settings = get_option('revnextwoo_social_settings');
     226      $avatar_url = isset($settings['custom_avatar_admin']) ? $settings['custom_avatar_admin'] : '';
     227    }
     228    if ($avatar_url) {
     229      $admin_replay_avatar = '<img src="' . esc_url($avatar_url) . '" class="avatar avatar-50 photo" width="50" height="50" style="border-radius:50%;object-fit:cover;" alt="Admin Avatar" />';
    127230    } else {
    128       // Fallback if attachment ID not found, use WooCommerce placeholder
    129       echo wp_kses_post(wc_placeholder_img('thumbnail', array('class' => 'question_icon', 'alt' => 'Answer Icon Placeholder')));
    130     }
    131     echo '<p>' . esc_html($question_reply->comment_content) . '</p>';
     231      $avatar_key = !empty($question_reply->user_id) ? intval($question_reply->user_id) : (isset($question_reply->author_email) ? $question_reply->author_email : '');
     232      $admin_replay_avatar = get_avatar($avatar_key, 50);
     233    }
     234    echo '<div class="item" style="background:#f6faff; border-left:4px solid #4a90e2; margin-left:48px; margin-top:8px; padding:16px 16px 12px 28px; width:auto; float:none;">';
     235        echo '<div class="short_content">';
     236            echo '<div class="revnext_que_info">';
     237            echo '<div class="revnextwoo_question_avatar">'.$admin_replay_avatar.'</div>';
     238            echo '<div class="revnextwoo_question_username"><h4>'.esc_html($authorUsername).'</h4></div>';
     239            echo '</div>';
     240        echo '<p>' . esc_html($question_reply->content) . '</p>';
     241        echo '</div>';
     242      echo '<div class="question_dropdown" id="question_dropdown' . esc_attr($question_reply->id) . '" >';
     243      echo '</div>';
     244      echo '<div class="content">';
     245        $reply_date_label = !empty($questionDate['relative']) ? $questionDate['relative'] : $questionDate['absolute'];
     246        $reply_date_title = !empty($questionDate['absolute']) ? ' title="' . esc_attr($questionDate['absolute']) . '"' : '';
     247        echo '<p class="create_date"' . $reply_date_title . '>' . esc_html($reply_date_label) . '</p>';
     248      echo '</div>';
    132249    echo '</div>';
    133     echo '<div class="question_dropdown" id="question_dropdown' . esc_attr($question_reply->comment_ID) . '" >';
    134     echo '</div>';
    135     echo '<div class="content">';
    136     echo '<p class="create_date">' . esc_html($questionDate) . '</p>';
    137     echo '</div>';
    138     echo '</div>';
    139   }
    140 }
    141 // Pagination links
    142 // $total_comments = get_comments_number($product->get_id());
    143 // if ($total_comments > $comments_per_page) {
    144 //     echo '<div class="pagination">';
    145 //     echo paginate_comments_links(array(
    146 //         'total' => ceil($total_comments / $comments_per_page),
    147 //         'current' => $page,
    148 //     ));
    149 //     echo '</div>';
    150 // }
    151 
     250  }
     251}
    152252echo '</div>';
    153253echo '</div>';
    154 echo '</div>';
     254//pagination start here
     255if (defined('DOING_AJAX') && DOING_AJAX && isset($_POST['action']) && $_POST['action'] === 'revnextwoo_load_questions') {
     256    // AJAX response logic (mimic inc/revnextwoo-questions-pagination.php)
     257    ob_start();
     258    foreach ($questions as $question) {
     259        $authorUsername = isset($question->author_name) ? $question->author_name : '';
     260        $questionDate = revnextwoo_get_relative_time_parts($question->created_at);
     261        $avatar_url = '';
     262        if (!empty($question->user_id)) {
     263            $avatar_url = get_user_meta($question->user_id, 'revnextwoo_profile_avatar', true);
     264        }
     265        if (!$avatar_url) {
     266            $settings = get_option('revnextwoo_social_settings');
     267            $avatar_url = isset($settings['custom_avatar_user']) ? $settings['custom_avatar_user'] : '';
     268        }
     269        if ($avatar_url) {
     270            $authorAvatar = '<img src="' . esc_url($avatar_url) . '" class="avatar avatar-50 photo" width="50" height="50" style="border-radius:50%;object-fit:cover;" alt="User Avatar" />';
     271        } else {
     272            $avatar_key = !empty($question->user_id) ? intval($question->user_id) : (isset($question->author_email) ? $question->author_email : '');
     273            $authorAvatar = get_avatar($avatar_key, 50);
     274        }
     275        echo '<div class="item">';
     276        echo "<div class='short_content'>";
     277        echo '<div class="revnext_que_info">';
     278        echo '<div class="revnextwoo_question_avatar">'.$authorAvatar.'</div>';
     279        echo '<div class="revnextwoo_question_username"><h4>'.esc_html($authorUsername).'</h4></div>';
     280        echo '</div>';
     281        echo '<p>' . esc_html($question->content) . '</p>';
     282        echo '</div>';
     283        $question_date_label = !empty($questionDate['relative']) ? $questionDate['relative'] : $questionDate['absolute'];
     284        $question_date_title = !empty($questionDate['absolute']) ? ' title="' . esc_attr($questionDate['absolute']) . '"' : '';
     285        echo '<div class="content"><p class="create_date"' . $question_date_title . '>' . esc_html($question_date_label).'</p></div>';
     286        echo '</div>';
     287        // Replies
     288        $question_replies = array();
     289        if (class_exists('Revnextwoo_QA_DB')) {
     290            $question_replies = Revnextwoo_QA_DB::get_replies($question->id, array(
     291                'status' => 'approve',
     292                'order' => 'ASC',
     293            ));
     294        }
     295        if (!empty($question_replies)) {
     296            echo '<div class="revnextwoo-question-replies" style="margin-left:48px;">';
     297            foreach ($question_replies as $question_reply) {
     298              $replyDate = revnextwoo_get_relative_time_parts($question_reply->created_at);
     299                $replyUsername = isset($question_reply->author_name) ? $question_reply->author_name : '';
     300                $reply_avatar_url = '';
     301                if (!empty($question_reply->user_id)) {
     302                    $reply_avatar_url = get_user_meta($question_reply->user_id, 'revnextwoo_profile_avatar', true);
     303                }
     304                if (!$reply_avatar_url) {
     305                    $settings = get_option('revnextwoo_social_settings');
     306                    $reply_avatar_url = isset($settings['custom_avatar_admin']) ? $settings['custom_avatar_admin'] : '';
     307                }
     308                if ($reply_avatar_url) {
     309                    $replyAvatar = '<img src="' . esc_url($reply_avatar_url) . '" class="avatar avatar-50 photo" width="50" height="50" style="border-radius:50%;object-fit:cover;" alt="Admin Avatar" />';
     310                } else {
     311                    $reply_avatar_key = !empty($question_reply->user_id) ? intval($question_reply->user_id) : (isset($question_reply->author_email) ? $question_reply->author_email : '');
     312                    $replyAvatar = get_avatar($reply_avatar_key, 50);
     313                }
     314                echo '<div class="item" style="background:#f6faff; border-left:4px solid #4a90e2; margin-top:8px; padding:16px 16px 12px 28px; width:auto; float:none;">';
     315                echo '<div class="short_content">';
     316                echo '<div class="revnext_que_info">';
     317                echo '<div class="revnextwoo_question_avatar">'.$replyAvatar.'</div>';
     318                echo '<div class="revnextwoo_question_username"><h4>'.esc_html($replyUsername).'</h4></div>';
     319                echo '</div>';
     320                echo '<p>' . esc_html($question_reply->content) . '</p>';
     321                echo '</div>';
     322                $reply_date_label = !empty($replyDate['relative']) ? $replyDate['relative'] : $replyDate['absolute'];
     323                $reply_date_title = !empty($replyDate['absolute']) ? ' title="' . esc_attr($replyDate['absolute']) . '"' : '';
     324                echo '<div class="content"><p class="create_date"' . $reply_date_title . '>' . esc_html($reply_date_label) . '</p></div>';
     325                echo '</div>';
     326            }
     327            echo '</div>';
     328        }
     329    }
     330    $card_html = '<div class="card">' . ob_get_clean() . '</div>';
     331    $total_pages = ceil($question_count / $comments_per_page);
     332    $next_page = $page + 1; // always point to the subsequent page
     333    echo '<div class="revnextwoo-questions-response">'
     334      . $card_html
     335      . '<div class="revnextwoo-load-more-meta" data-next-page="' . esc_attr($next_page) . '" data-total-pages="' . esc_attr($total_pages) . '"></div>'
     336      . '</div>';
     337    wp_die();
     338} else {
     339    // Normal HTML render: show Load More button
     340    $total_pages = ceil($question_count / $comments_per_page);
     341    if ($page < $total_pages) {
     342      $next_page = $page + 1;
     343      echo '<div class="revnextwoo-load-more" style="display:flex;justify-content:center;margin:24px 0 0 0;">';
     344      echo '<button id="revnextwoo_load_more_questions" data-next-page="' . esc_attr($next_page) . '" data-total-pages="' . esc_attr($total_pages) . '" style="background:#17a2b8;color:#fff;border:none;padding:10px 14px;border-radius:4px;cursor:pointer;">Load more Q&A</button>';
     345      echo '</div>';
     346    }
     347    echo '</div>';
     348}
     349
Note: See TracChangeset for help on using the changeset viewer.