| 1 |
<?php |
| 2 |
if (!defined('ABSPATH')) { |
| 3 |
exit; |
| 4 |
} |
| 5 |
|
| 6 |
// Reviews Section |
| 7 |
// Expected variables: $trip |
| 8 |
?> |
| 9 |
<!-- Reviews: full width within unified trip container (same max-width + padding as .yatra-trip-container) --> |
| 10 |
<section class="yatra-reviews-section" id="reviews" itemscope itemtype="https://schema.org/Review"> |
| 11 |
<div class="yatra-reviews-section-container"> |
| 12 |
<div class="yatra-reviews-header"> |
| 13 |
<h2 class="yatra-reviews-section-title"> |
| 14 |
<?php echo yatra_svg_icon('star', 'yatra-reviews-section-icon'); ?> |
| 15 |
<?php echo esc_html__('Reviews', 'yatra'); ?> |
| 16 |
</h2> |
| 17 |
<meta itemprop="about" content="<?php echo esc_attr($trip->getTitle()); ?>"> |
| 18 |
</div> |
| 19 |
|
| 20 |
<?php |
| 21 |
// Use dynamic reviews from database (via SingleTripController) |
| 22 |
$display_reviews = !empty($trip->reviews) && is_array($trip->reviews) ? $trip->reviews : []; |
| 23 |
|
| 24 |
// Total approved count and average come from SQL on the Trip model (list may be capped at N rows). |
| 25 |
$total_reviews = (int) ($trip->review_count ?? 0); |
| 26 |
if ($total_reviews < 1) { |
| 27 |
$total_reviews = count($display_reviews); |
| 28 |
} |
| 29 |
$avg_rating = (isset($trip->average_rating) && (float) $trip->average_rating > 0) |
| 30 |
? (float) $trip->average_rating |
| 31 |
: 0.0; |
| 32 |
|
| 33 |
// Rating distribution: prefer full aggregate from Trip (accurate when review list is limited). |
| 34 |
$rating_distribution = [5 => 0, 4 => 0, 3 => 0, 2 => 0, 1 => 0]; |
| 35 |
$dist = $trip->rating_distribution ?? null; |
| 36 |
if (is_array($dist) && $dist !== [] && ($trip->review_count ?? 0) > 0) { |
| 37 |
foreach ([5, 4, 3, 2, 1] as $star) { |
| 38 |
$rating_distribution[$star] = (int) ($dist[$star] ?? $dist[(string) $star] ?? 0); |
| 39 |
} |
| 40 |
} else { |
| 41 |
foreach ($display_reviews as $review) { |
| 42 |
$review_rating = (int) (is_object($review) ? ($review->rating ?? 0) : ($review['rating'] ?? 0)); |
| 43 |
if ($review_rating >= 1 && $review_rating <= 5) { |
| 44 |
$rating_distribution[$review_rating]++; |
| 45 |
} |
| 46 |
} |
| 47 |
} |
| 48 |
|
| 49 |
// If we have reviews, calculate average from them |
| 50 |
if ($total_reviews > 0 && $avg_rating == 0) { |
| 51 |
$total_rating = 0; |
| 52 |
foreach ($display_reviews as $review) { |
| 53 |
$total_rating += (int) (is_object($review) ? ($review->rating ?? 0) : ($review['rating'] ?? 0)); |
| 54 |
} |
| 55 |
$avg_rating = round($total_rating / $total_reviews, 1); |
| 56 |
} |
| 57 |
?> |
| 58 |
|
| 59 |
<div class="yatra-reviews-container"> |
| 60 |
<!-- Left: Overall Rating Summary --> |
| 61 |
<div class="yatra-reviews-summary"> |
| 62 |
<div class="yatra-overall-rating"> |
| 63 |
<div class="yatra-overall-rating-score"> |
| 64 |
<span class="yatra-rating-big"><?php echo esc_html(number_format($avg_rating, 1)); ?></span> |
| 65 |
<span class="yatra-rating-max">/5</span> |
| 66 |
</div> |
| 67 |
<div class="yatra-overall-stars"> |
| 68 |
<?php |
| 69 |
$full_stars = floor($avg_rating); |
| 70 |
$has_half = ($avg_rating - $full_stars) >= 0.3; |
| 71 |
for ($i = 1; $i <= 5; $i++): |
| 72 |
if ($i <= $full_stars): |
| 73 |
?> |
| 74 |
<span class="yatra-star-icon filled">� |
| 75 |
</span> |
| 76 |
<?php elseif ($i == $full_stars + 1 && $has_half): ?> |
| 77 |
<span class="yatra-star-icon half">� |
| 78 |
</span> |
| 79 |
<?php else: ?> |
| 80 |
<span class="yatra-star-icon">� |
| 81 |
</span> |
| 82 |
<?php endif; endfor; ?> |
| 83 |
</div> |
| 84 |
<p class="yatra-reviews-based-on"> |
| 85 |
<?php echo esc_html(sprintf(_n('based on %s review', 'based on %s reviews', $total_reviews, 'yatra'), number_format($total_reviews))); ?> |
| 86 |
</p> |
| 87 |
</div> |
| 88 |
|
| 89 |
<div class="yatra-rating-distribution"> |
| 90 |
<h4 class="yatra-rating-distribution-title"><?php echo esc_html__('Rating breakdown', 'yatra'); ?></h4> |
| 91 |
<?php for ($star = 5; $star >= 1; $star--): |
| 92 |
$count = $rating_distribution[$star]; |
| 93 |
$percentage = $total_reviews > 0 ? ($count / $total_reviews) * 100 : 0; |
| 94 |
?> |
| 95 |
<div class="yatra-rating-row"> |
| 96 |
<span class="yatra-rating-star-label"> |
| 97 |
<?php for ($s = 1; $s <= 5; $s++): ?> |
| 98 |
<span class="yatra-breakdown-star <?php echo $s <= $star ? 'filled' : ''; ?>">� |
| 99 |
</span> |
| 100 |
<?php endfor; ?> |
| 101 |
</span> |
| 102 |
<div class="yatra-rating-bar"> |
| 103 |
<div class="yatra-rating-bar-fill" style="width: <?php echo esc_attr($percentage); ?>%"></div> |
| 104 |
</div> |
| 105 |
<span class="yatra-rating-count"><?php echo esc_html($count); ?></span> |
| 106 |
</div> |
| 107 |
<?php endfor; ?> |
| 108 |
</div> |
| 109 |
</div> |
| 110 |
|
| 111 |
<!-- Right: Reviews List --> |
| 112 |
<div class="yatra-reviews-list"> |
| 113 |
<?php if (!empty($display_reviews)): ?> |
| 114 |
<!-- Sort & Filter Toolbar --> |
| 115 |
<div class="yatra-reviews-toolbar"> |
| 116 |
<div class="yatra-reviews-sort"> |
| 117 |
<span class="yatra-sort-label"><?php echo esc_html__('Sort by:', 'yatra'); ?></span> |
| 118 |
<select class="yatra-sort-select" id="review-sort"> |
| 119 |
<option value="newest"><?php echo esc_html__('Newest first', 'yatra'); ?></option> |
| 120 |
<option value="highest"><?php echo esc_html__('Highest rated', 'yatra'); ?></option> |
| 121 |
<option value="lowest"><?php echo esc_html__('Lowest rated', 'yatra'); ?></option> |
| 122 |
</select> |
| 123 |
</div> |
| 124 |
</div> |
| 125 |
<!-- Reviews --> |
| 126 |
<div class="yatra-reviews-items"> |
| 127 |
<?php foreach ($display_reviews as $review): |
| 128 |
$review_rating = is_object($review) ? ($review->rating ?? 5) : ($review['rating'] ?? 5); |
| 129 |
$review_title = is_object($review) ? ($review->title ?? '') : ($review['title'] ?? ''); |
| 130 |
$review_content = is_object($review) ? ($review->content ?? '') : ($review['content'] ?? ''); |
| 131 |
$review_author = is_object($review) ? ($review->author_name ?? __('Anonymous', 'yatra')) : ($review['author_name'] ?? __('Anonymous', 'yatra')); |
| 132 |
$review_location = is_object($review) ? ($review->author_location ?? '') : ($review['author_location'] ?? ''); |
| 133 |
$review_date = is_object($review) ? ($review->created_at ?? '') : ($review['created_at'] ?? ''); |
| 134 |
$review_user_id = is_object($review) ? ($review->user_id ?? 0) : ($review['user_id'] ?? 0); |
| 135 |
$is_verified = $review_user_id > 0; |
| 136 |
$author_initial = strtoupper(substr($review_author, 0, 1)); |
| 137 |
$avatar_colors = ['#ef4444', '#f97316', '#eab308', '#22c55e', '#3b82f6', '#8b5cf6', '#ec4899']; |
| 138 |
$avatar_color = $avatar_colors[ord($author_initial) % count($avatar_colors)]; |
| 139 |
?> |
| 140 |
<div class="yatra-review-item"> |
| 141 |
<div class="yatra-review-header"> |
| 142 |
<div class="yatra-review-header-left"> |
| 143 |
<div class="yatra-review-avatar" style="background: linear-gradient(135deg, <?php echo esc_attr($avatar_color); ?> 0%, <?php echo esc_attr($avatar_color); ?>dd 100%);"> |
| 144 |
<?php echo esc_html($author_initial); ?> |
| 145 |
</div> |
| 146 |
<div class="yatra-review-author-info"> |
| 147 |
<div class="yatra-review-author-top"> |
| 148 |
<span class="yatra-review-author-name"><?php echo esc_html($review_author); ?></span> |
| 149 |
<?php if ($is_verified): ?> |
| 150 |
<span class="yatra-verified-badge"> |
| 151 |
<svg class="yatra-icon-xs" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"> |
| 152 |
<path d="M20 6L9 17l-5-5"/> |
| 153 |
</svg> |
| 154 |
<?php echo esc_html__('Verified', 'yatra'); ?> |
| 155 |
</span> |
| 156 |
<?php endif; ?> |
| 157 |
</div> |
| 158 |
<div class="yatra-review-meta"> |
| 159 |
<?php if ($review_date): ?> |
| 160 |
<span class="yatra-review-date"><?php echo esc_html(date_i18n('F j, Y', strtotime($review_date))); ?></span> |
| 161 |
<?php endif; ?> |
| 162 |
<?php if ($review_location): ?> |
| 163 |
<span class="yatra-review-author-location">• <?php echo esc_html($review_location); ?></span> |
| 164 |
<?php endif; ?> |
| 165 |
</div> |
| 166 |
</div> |
| 167 |
</div> |
| 168 |
<div class="yatra-review-stars"> |
| 169 |
<?php for ($i = 1; $i <= 5; $i++): ?> |
| 170 |
<span class="yatra-star-sm <?php echo $i <= $review_rating ? 'filled' : ''; ?>">� |
| 171 |
</span> |
| 172 |
<?php endfor; ?> |
| 173 |
</div> |
| 174 |
</div> |
| 175 |
<div class="yatra-review-body"> |
| 176 |
<?php if ($review_title): ?> |
| 177 |
<h4 class="yatra-review-title"><?php echo esc_html($review_title); ?></h4> |
| 178 |
<?php endif; ?> |
| 179 |
<p class="yatra-review-content"><?php echo esc_html($review_content); ?></p> |
| 180 |
</div> |
| 181 |
</div> |
| 182 |
<?php endforeach; ?> |
| 183 |
</div> |
| 184 |
|
| 185 |
<!-- Load More --> |
| 186 |
<?php if ($total_reviews > 3): ?> |
| 187 |
<div class="yatra-reviews-load-more"> |
| 188 |
<button class="yatra-load-more-btn" type="button"> |
| 189 |
<?php echo esc_html__('Show more reviews', 'yatra'); ?> |
| 190 |
</button> |
| 191 |
</div> |
| 192 |
<?php endif; ?> |
| 193 |
<?php else: ?> |
| 194 |
<div class="yatra-no-reviews-message"> |
| 195 |
<div class="yatra-no-reviews-icon"> |
| 196 |
<?php echo yatra_svg_icon('message-circle', 'yatra-icon-lg'); ?> |
| 197 |
</div> |
| 198 |
<p><?php echo esc_html__('No reviews yet. Be the first to review this trip!', 'yatra'); ?></p> |
| 199 |
</div> |
| 200 |
<?php endif; ?> |
| 201 |
</div> |
| 202 |
</div> |
| 203 |
|
| 204 |
<!-- Review Form --> |
| 205 |
<?php |
| 206 |
$can_review = yatra_can_review((int) $trip->id); |
| 207 |
$require_booking = yatra_setting_enabled('require_booking_to_review'); |
| 208 |
$minimum_rating = (int) yatra_get_setting('minimum_rating', 1); |
| 209 |
|
| 210 |
// Check for existing review that can be edited |
| 211 |
$existing_review = null; |
| 212 |
$can_edit_review = false; |
| 213 |
$edit_time_remaining = ''; |
| 214 |
|
| 215 |
if (is_user_logged_in()) { |
| 216 |
$existing_review = yatra_get_user_review((int) $trip->id); |
| 217 |
if ($existing_review) { |
| 218 |
$can_edit_review = yatra_can_edit_review($existing_review); |
| 219 |
if ($can_edit_review) { |
| 220 |
$edit_time_remaining = yatra_get_review_edit_time_remaining($existing_review); |
| 221 |
} |
| 222 |
} |
| 223 |
} |
| 224 |
?> |
| 225 |
<div class="yatra-review-form-section"> |
| 226 |
<?php if ($existing_review && $can_edit_review): ?> |
| 227 |
<h3 class="yatra-review-form-title"><?php echo esc_html__('Edit Your Review', 'yatra'); ?></h3> |
| 228 |
<div class="yatra-review-edit-notice"> |
| 229 |
<svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| 230 |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/> |
| 231 |
</svg> |
| 232 |
<p><?php echo esc_html(sprintf(__('You can edit your review for %s more.', 'yatra'), $edit_time_remaining)); ?></p> |
| 233 |
</div> |
| 234 |
<?php else: ?> |
| 235 |
<h3 class="yatra-review-form-title"><?php echo esc_html__('Write a Review', 'yatra'); ?></h3> |
| 236 |
<?php endif; ?> |
| 237 |
|
| 238 |
<?php if (!is_user_logged_in()): ?> |
| 239 |
<div class="yatra-review-login-notice"> |
| 240 |
<p><?php echo esc_html__('Please', 'yatra'); ?> <a href="<?php echo esc_url(wp_login_url(get_permalink())); ?>"><?php echo esc_html__('log in', 'yatra'); ?></a> <?php echo esc_html__('to write a review.', 'yatra'); ?></p> |
| 241 |
</div> |
| 242 |
<?php elseif (!$can_review && $require_booking): ?> |
| 243 |
<div class="yatra-review-booking-notice"> |
| 244 |
<p><?php echo esc_html__('Only customers who have booked this trip can leave a review.', 'yatra'); ?></p> |
| 245 |
</div> |
| 246 |
<?php elseif ($existing_review && !$can_edit_review): ?> |
| 247 |
<div class="yatra-review-already-notice"> |
| 248 |
<?php if (isset($existing_review->status) && $existing_review->status === 'approved'): ?> |
| 249 |
<p><?php echo esc_html__('Thank you! Your review has been approved and published.', 'yatra'); ?></p> |
| 250 |
<?php else: ?> |
| 251 |
<p><?php echo esc_html__('You have already reviewed this trip.', 'yatra'); ?></p> |
| 252 |
<?php endif; ?> |
| 253 |
</div> |
| 254 |
<?php elseif ($can_review || ($existing_review && $can_edit_review)): ?> |
| 255 |
<form class="yatra-review-form" id="yatra-review-form" method="post"> |
| 256 |
<?php wp_nonce_field('yatra_submit_review', 'yatra_review_nonce'); ?> |
| 257 |
<input type="hidden" name="trip_id" value="<?php echo esc_attr($trip->id); ?>"> |
| 258 |
<?php if ($existing_review && $can_edit_review): ?> |
| 259 |
<input type="hidden" name="review_id" value="<?php echo esc_attr($existing_review->id); ?>"> |
| 260 |
<input type="hidden" name="action_type" value="edit"> |
| 261 |
<?php endif; ?> |
| 262 |
|
| 263 |
<!-- Rating --> |
| 264 |
<div class="yatra-form-field yatra-rating-field"> |
| 265 |
<label class="yatra-form-label"><?php echo esc_html__('Your Rating', 'yatra'); ?> <span class="yatra-required">*</span></label> |
| 266 |
<div class="yatra-star-rating-input" data-min-rating="<?php echo esc_attr($minimum_rating); ?>"> |
| 267 |
<?php |
| 268 |
$selected_rating = $existing_review ? (int) $existing_review->rating : 5; |
| 269 |
for ($i = 1; $i <= 5; $i++): |
| 270 |
?> |
| 271 |
<input type="radio" name="rating" id="rating-<?php echo $i; ?>" value="<?php echo $i; ?>" <?php echo $i === $selected_rating ? 'checked' : ''; ?> <?php echo $i < $minimum_rating ? 'disabled' : ''; ?>> |
| 272 |
<label for="rating-<?php echo $i; ?>" class="yatra-star-label" title="<?php echo esc_attr(sprintf(__('%d star', 'yatra'), $i)); ?>">� |
| 273 |
</label> |
| 274 |
<?php endfor; ?> |
| 275 |
</div> |
| 276 |
<?php if ($minimum_rating > 1): ?> |
| 277 |
<p class="yatra-field-hint"><?php echo esc_html(sprintf(__('Minimum rating: %d star(s)', 'yatra'), $minimum_rating)); ?></p> |
| 278 |
<?php endif; ?> |
| 279 |
</div> |
| 280 |
|
| 281 |
<!-- Title --> |
| 282 |
<div class="yatra-form-field"> |
| 283 |
<label for="review-title" class="yatra-form-label"><?php echo esc_html__('Review Title', 'yatra'); ?></label> |
| 284 |
<input type="text" id="review-title" name="title" class="yatra-form-input" placeholder="<?php echo esc_attr__('Summarize your experience', 'yatra'); ?>" maxlength="100" value="<?php echo $existing_review ? esc_attr($existing_review->title ?? '') : ''; ?>"> |
| 285 |
</div> |
| 286 |
|
| 287 |
<!-- Review Content --> |
| 288 |
<div class="yatra-form-field"> |
| 289 |
<label for="review-content" class="yatra-form-label"><?php echo esc_html__('Your Review', 'yatra'); ?> <span class="yatra-required">*</span></label> |
| 290 |
<textarea id="review-content" name="content" class="yatra-form-textarea" rows="5" placeholder="<?php echo esc_attr__('Share your experience with this trip...', 'yatra'); ?>" required minlength="20"><?php echo $existing_review ? esc_textarea($existing_review->content ?? '') : ''; ?></textarea> |
| 291 |
<p class="yatra-field-hint"><?php echo esc_html__('Minimum 20 characters', 'yatra'); ?></p> |
| 292 |
</div> |
| 293 |
|
| 294 |
<!-- Submit Button --> |
| 295 |
<div class="yatra-form-field"> |
| 296 |
<button type="submit" class="yatra-review-submit-btn"> |
| 297 |
<?php echo yatra_svg_icon('check', 'yatra-icon-sm'); ?> |
| 298 |
<?php echo $existing_review && $can_edit_review ? esc_html__('Update Review', 'yatra') : esc_html__('Submit Review', 'yatra'); ?> |
| 299 |
</button> |
| 300 |
<?php if (yatra_setting_enabled('enable_review_moderation')): ?> |
| 301 |
<p class="yatra-review-moderation-notice"><?php echo esc_html__('Your review will be published after moderation.', 'yatra'); ?></p> |
| 302 |
<?php endif; ?> |
| 303 |
</div> |
| 304 |
</form> |
| 305 |
<?php endif; ?> |
| 306 |
</div> |
| 307 |
</div> |
| 308 |
</section> |
| 309 |
|