PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.16
Yatra – Travel Booking & Tour Operator Software v3.0.16
3.0.16 3.0.15 3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 2.0.1 All 84 releases
← All changes | templates/partials/single-trip/content-sidebar.php +181 -31 3.0.3 → 3.0.16 View file →
@@ -31,14 +31,21 @@
31 31 'has_discount' => $displayPricing['has_discount'],
32 32 'raw_current_price' => $displayPricing['effective_price_min'],
33 33 'raw_original_price' => $displayPricing['min_category_original_price'],
34 34 'is_traveler_based' => $displayPricing['has_traveler_pricing'],
35 + // "per person" / "per group (1-2 pax)" — follows the category the shown price comes from
36 + 'price_unit' => $displayPricing['price_unit'] ?? 'per_person',
37 + 'price_unit_label' => $displayPricing['price_unit_label'] ?? __('per person', 'yatra'),
35 38 ];
36 39
37 40 $discount = [
38 41 'has_discount' => $displayPricing['has_discount'] && $displayPricing['max_discount_percentage'] > 0,
39 42 'discount_text' => $displayPricing['max_discount_percentage'] > 0
40 - ? sprintf(__('Save up to %d%%', 'yatra'), $displayPricing['max_discount_percentage']) : '',
43 + ? sprintf(
44 + /* translators: %d: maximum discount percentage. */
45 + __('Save up to %d%%', 'yatra'),
46 + $displayPricing['max_discount_percentage']
47 + ) : '',
41 48 'discount_percentage' => $displayPricing['max_discount_percentage'],
42 49 ];
43 50
44 51 $dp_display_sidebar = apply_filters('yatra_get_dynamic_pricing_display_settings', [
@@ -76,9 +83,9 @@
76 83 <?php if ($pricing['has_discount'] && !empty($pricing['original_price']) && $show_strike_original_sidebar): ?>
77 84 <span class="yatra-booking-price-original"><?php echo esc_html($pricing['original_price']); ?></span>
78 85 <?php endif; ?>
79 86 <span class="yatra-booking-price-amount" id="display-price"><?php echo esc_html($pricing['current_price']); ?></span>
80 - <span class="yatra-booking-price-label"><?php echo esc_html__('per person', 'yatra'); ?></span>
87 + <span class="yatra-booking-price-label" data-price-unit="<?php echo esc_attr($pricing['price_unit']); ?>"><?php echo esc_html($pricing['price_unit_label']); ?></span>
81 88 <?php if (!empty($yatra_sidebar_urgency)) : ?>
82 89 <?php foreach ($yatra_sidebar_urgency as $yatra_sb_urg) : ?>
83 90 <div class="yatra-booking-dp-urgency" style="background-color:#fef3c7;color:#92400e;padding:6px 8px;border-radius:6px;margin-top:8px;font-size:12px;line-height:1.35;">
84 91 <?php echo esc_html((string) $yatra_sb_urg); ?>
@@ -105,8 +112,14 @@
105 112 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z"/>
106 113 </svg>
107 114 <?php echo esc_html__('Send Enquiry', 'yatra'); ?>
108 115 </button>
116 + <?php
117 + // AI: Chat-with-AI widget. The partial gates itself on the
118 + // operator's enable flag + module-active state so it's a no-op
119 + // when the feature is off.
120 + yatra_get_template('partials/single-trip/ai-chat-widget', ['trip' => $trip]);
121 + ?>
109 122 </div>
110 123 <?php else: ?>
111 124 <div class="yatra-booking-card">
112 125
@@ -134,9 +147,9 @@
134 147 <?php if ($pricing['has_discount'] && !empty($pricing['original_price']) && $show_strike_original_sidebar): ?>
135 148 <span class="yatra-booking-price-original"><?php echo esc_html($pricing['original_price']); ?></span>
136 149 <?php endif; ?>
137 150 <span class="yatra-booking-price-amount" id="display-price"><?php echo esc_html($pricing['current_price']); ?></span>
138 - <span class="yatra-booking-price-label"><?php echo esc_html__('per person', 'yatra'); ?></span>
151 + <span class="yatra-booking-price-label" data-price-unit="<?php echo esc_attr($pricing['price_unit']); ?>"><?php echo esc_html($pricing['price_unit_label']); ?></span>
139 152 <?php if (!empty($yatra_sidebar_urgency)) : ?>
140 153 <?php foreach ($yatra_sidebar_urgency as $yatra_sb_urg) : ?>
141 154 <div class="yatra-booking-dp-urgency" style="background-color:#fef3c7;color:#92400e;padding:6px 8px;border-radius:6px;margin-top:8px;font-size:12px;line-height:1.35;">
142 155 <?php echo esc_html((string) $yatra_sb_urg); ?>
@@ -144,9 +157,8 @@
144 157 <?php endforeach; ?>
145 158 <?php endif; ?>
146 159 <?php else: ?>
147 160 <span class="yatra-booking-price-amount yatra-contact-pricing" id="display-price"><?php echo esc_html__('Contact for pricing', 'yatra'); ?></span>
148 - <span class="yatra-booking-price-label"><?php echo esc_html__('per person', 'yatra'); ?></span>
149 161 <?php endif; ?>
150 162 </div>
151 163 </div>
152 164
@@ -155,25 +167,130 @@
155 167 <!-- DATE-SPECIFIC BOOKING (Has Availability Dates/Rules) -->
156 168 <!-- ========================================== -->
157 169 <div class="yatra-availability-info">
158 170 <span class="yatra-availability-count">
159 - <?php echo esc_html(sprintf(_n('%d departure available', '%d departures available', count($trip->getAvailabilityDates()), 'yatra'), count($trip->getAvailabilityDates()))); ?>
171 + <?php
172 + $allAvailability = $trip->getAvailabilityDates();
173 + $today = date('Y-m-d');
174 +
175 + // Sidebar count should reflect the same resolved availability set (priority:
176 + // manual > recurring rule > trip default). We keep it lightweight here:
177 + // count upcoming departures (date + optional time) from the resolved list.
178 + $filtered = array_filter($allAvailability, static function ($a) use ($today) {
179 + if (!is_object($a)) {
180 + return false;
181 + }
182 + $date = (string) ($a->departure_date ?? '');
183 + if ($date === '' || $date < $today) {
184 + return false;
185 + }
186 + return true;
187 + });
188 +
189 + // Priority: manual availability dates > recurring rules > trip default.
190 + $bySource = [
191 + 'availability_date' => [],
192 + 'recurring_rule' => [],
193 + 'trip_default' => [],
194 + '' => [],
195 + ];
196 + foreach ($filtered as $a) {
197 + $src = strtolower(trim((string) ($a->source ?? '')));
198 + if (!isset($bySource[$src])) {
199 + $src = '';
200 + }
201 + $bySource[$src][] = $a;
202 + }
203 +
204 + $pick = [];
205 + if (!empty($bySource['availability_date'])) {
206 + $pick = $bySource['availability_date'];
207 + } elseif (!empty($bySource['recurring_rule'])) {
208 + $pick = $bySource['recurring_rule'];
209 + } elseif (!empty($bySource['trip_default'])) {
210 + $pick = $bySource['trip_default'];
211 + } else {
212 + $pick = $filtered;
213 + }
214 +
215 + // Count departures:
216 + // - if availability entries include departure_time, count unique (date + time) as departures
217 + // - otherwise count unique dates.
218 + $hasTimeSlots = false;
219 + foreach ($pick as $a) {
220 + $t = isset($a->departure_time) ? trim((string) $a->departure_time) : '';
221 + if ($t !== '' && $t !== '00:00:00') {
222 + $hasTimeSlots = true;
223 + break;
224 + }
225 + }
226 +
227 + $unique = [];
228 + foreach ($pick as $a) {
229 + $date = (string) ($a->departure_date ?? '');
230 + if ($date === '') {
231 + continue;
232 + }
233 + if ($hasTimeSlots) {
234 + $t = isset($a->departure_time) ? trim((string) $a->departure_time) : '';
235 + if ($t !== '' && $t !== '00:00:00') {
236 + $date .= '|' . $t;
237 + } else {
238 + // When some rows have time and others don't, avoid collapsing into date-only.
239 + $date .= '|__no_time__';
240 + }
241 + }
242 + $unique[$date] = true;
243 + }
244 + $countAvailable = count($unique);
245 +
246 + // Pro feature: a Pro module can hook `yatra_use_date_dropdown`
247 + // to replace the flatpickr calendar with a <select> of available
248 + // departures, and supplies the option list via
249 + // `yatra_single_trip_date_dropdown_options`. Free plugin renders
250 + // calendar by default (filter returns false / [] when Pro is
251 + // inactive or the setting is off).
252 + $dropdown_options = (array) apply_filters('yatra_single_trip_date_dropdown_options', [], $trip, $pick);
253 + $yatra_use_date_dropdown = (bool) apply_filters('yatra_use_date_dropdown', false, $trip)
254 + && !empty($dropdown_options);
255 +
256 + echo esc_html(sprintf(
257 + /* translators: %d: number of departures available. */
258 + _n('%d departure available', '%d departures available', $countAvailable, 'yatra'),
259 + $countAvailable
260 + ));
261 + ?>
160 262 </span>
161 263 </div>
162 264
163 265 <form class="yatra-booking-form" data-booking-mode="date_specific">
164 266 <!-- Date Selection -->
165 - <div class="yatra-booking-field-select">
267 + <div class="yatra-booking-field-select<?php echo $yatra_use_date_dropdown ? ' yatra-date-as-dropdown' : ''; ?>">
166 268 <div class="yatra-booking-field-icon">
167 269 <?php echo yatra_svg_icon('calendar', 'yatra-icon-sm'); ?>
168 270 </div>
169 - <input type="text"
170 - id="travel_date"
171 - name="travel_date"
172 - class="yatra-booking-select yatra-datepicker"
173 - placeholder="<?php esc_attr_e('Select date', 'yatra'); ?>"
174 - readonly
175 - required>
271 + <?php if ($yatra_use_date_dropdown): ?>
272 + <select id="travel_date"
273 + name="travel_date"
274 + class="yatra-booking-select yatra-date-dropdown"
275 + required>
276 + <option value=""><?php esc_html_e('Select a departure date', 'yatra'); ?></option>
277 + <?php foreach ($dropdown_options as $opt): ?>
278 + <option value="<?php echo esc_attr($opt['value']); ?>"
279 + <?php if (!empty($opt['time'])): ?>data-departure-time="<?php echo esc_attr($opt['time']); ?>"<?php endif; ?>>
280 + <?php echo esc_html($opt['label']); ?>
281 + </option>
282 + <?php endforeach; ?>
283 + </select>
284 + <?php else: ?>
285 + <input type="text"
286 + id="travel_date"
287 + name="travel_date"
288 + class="yatra-booking-select yatra-datepicker"
289 + placeholder="<?php esc_attr_e('Select date', 'yatra'); ?>"
290 + readonly
291 + required>
292 + <?php endif; ?>
176 293 <svg class="yatra-select-arrow" fill="none" stroke="currentColor" viewBox="0 0 24 24">
177 294 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
178 295 </svg>
179 296 </div>
@@ -214,10 +331,9 @@
214 331 $value = 1;
215 332 $min = $trip->getMinTravelers() ?: 1;
216 333 $max = $trip->getMaxTravelers() ?: 20;
217 334 $data_price = $base_price;
218 - $range_text = sprintf(__('Min: %d, Max: %d', 'yatra'), $min, $max);
219 -
335 +
220 336 include YATRA_PLUGIN_PATH . 'templates/partials/traveler-input-simple.php';
221 337 ?>
222 338 </div>
223 339 <?php endif; ?>
@@ -232,18 +348,24 @@
232 348 <?php if (!empty($trip->getAvailableFrom()) || !empty($trip->getAvailableTo())): ?>
233 349 <span class="yatra-availability-note">
234 350 <?php
235 351 if (!empty($trip->getAvailableFrom()) && !empty($trip->getAvailableTo())) {
236 - echo esc_html(sprintf(__('Available: %s - %s', 'yatra'),
352 + echo esc_html(sprintf(
353 + /* translators: 1: available-from date, 2: available-to date. */
354 + __('Available: %1$s - %2$s', 'yatra'),
237 355 date_i18n(get_option('date_format'), strtotime($trip->getAvailableFrom())),
238 356 date_i18n(get_option('date_format'), strtotime($trip->getAvailableTo()))
239 357 ));
240 358 } elseif (!empty($trip->getAvailableFrom())) {
241 - echo esc_html(sprintf(__('Available from: %s', 'yatra'),
359 + echo esc_html(sprintf(
360 + /* translators: %s: available-from date. */
361 + __('Available from: %s', 'yatra'),
242 362 date_i18n(get_option('date_format'), strtotime($trip->getAvailableFrom()))
243 363 ));
244 364 } else {
245 - echo esc_html(sprintf(__('Available until: %s', 'yatra'),
365 + echo esc_html(sprintf(
366 + /* translators: %s: available-until date. */
367 + __('Available until: %s', 'yatra'),
246 368 date_i18n(get_option('date_format'), strtotime($trip->getAvailableTo()))
247 369 ));
248 370 }
249 371 ?>
@@ -252,21 +374,45 @@
252 374 </div>
253 375
254 376 <form class="yatra-booking-form" data-booking-mode="flexible">
255 377 <!-- Date Selection (Flexible) -->
256 - <div class="yatra-booking-field-select">
378 + <?php
379 + // Same Pro filter contract as the date_specific branch above.
380 + // Flexible trips have no discrete availability set, so $pick is
381 + // passed as null — the Pro hook synthesizes a list from the
382 + // trip's available_from/to window.
383 + $flex_dropdown_options = (array) apply_filters('yatra_single_trip_date_dropdown_options', [], $trip, null);
384 + $flex_use_date_dropdown = (bool) apply_filters('yatra_use_date_dropdown', false, $trip)
385 + && !empty($flex_dropdown_options);
386 + ?>
387 + <div class="yatra-booking-field-select<?php echo $flex_use_date_dropdown ? ' yatra-date-as-dropdown' : ''; ?>">
257 388 <div class="yatra-booking-field-icon">
258 389 <?php echo yatra_svg_icon('calendar', 'yatra-icon-sm'); ?>
259 390 </div>
260 - <input type="text"
261 - id="travel_date"
262 - name="travel_date"
263 - class="yatra-booking-select yatra-datepicker"
264 - placeholder="<?php esc_attr_e('Select date', 'yatra'); ?>"
265 - data-min-date="<?php echo esc_attr($trip->getAvailableFrom() ?: date('Y-m-d')); ?>"
266 - data-max-date="<?php echo esc_attr($trip->getAvailableTo() ?: ''); ?>"
267 - readonly
268 - required>
391 + <?php if ($flex_use_date_dropdown): ?>
392 + <select id="travel_date"
393 + name="travel_date"
394 + class="yatra-booking-select yatra-date-dropdown"
395 + required>
396 + <option value=""><?php esc_html_e('Select a departure date', 'yatra'); ?></option>
397 + <?php foreach ($flex_dropdown_options as $opt): ?>
398 + <option value="<?php echo esc_attr($opt['value']); ?>"
399 + <?php if (!empty($opt['time'])): ?>data-departure-time="<?php echo esc_attr($opt['time']); ?>"<?php endif; ?>>
400 + <?php echo esc_html($opt['label']); ?>
401 + </option>
402 + <?php endforeach; ?>
403 + </select>
404 + <?php else: ?>
405 + <input type="text"
406 + id="travel_date"
407 + name="travel_date"
408 + class="yatra-booking-select yatra-datepicker"
409 + placeholder="<?php esc_attr_e('Select date', 'yatra'); ?>"
410 + data-min-date="<?php echo esc_attr($trip->getAvailableFrom() ?: date('Y-m-d')); ?>"
411 + data-max-date="<?php echo esc_attr($trip->getAvailableTo() ?: ''); ?>"
412 + readonly
413 + required>
414 + <?php endif; ?>
269 415 <svg class="yatra-select-arrow" fill="none" stroke="currentColor" viewBox="0 0 24 24">
270 416 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
271 417 </svg>
272 418 </div>
@@ -307,10 +453,9 @@
307 453 $value = 1;
308 454 $min = $trip->getMinTravelers() ?: 1;
309 455 $max = $trip->getMaxTravelers() ?: 20;
310 456 $data_price = $base_price;
311 - $range_text = sprintf(__('Min: %d, Max: %d', 'yatra'), $min, $max);
312 -
457 +
313 458 include YATRA_PLUGIN_PATH . 'templates/partials/traveler-input-simple.php';
314 459 ?>
315 460 </div>
316 461 <?php endif; ?>
@@ -330,8 +475,12 @@
330 475 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z"/>
331 476 </svg>
332 477 <?php echo esc_html__('Make Enquiry', 'yatra'); ?>
333 478 </button>
479 + <?php
480 + // AI: Chat-with-AI widget — bookable-trip branch.
481 + yatra_get_template('partials/single-trip/ai-chat-widget', ['trip' => $trip]);
482 + ?>
334 483
335 484 <!-- Cancellation Policy Trust Signal -->
336 485 <?php
337 486 $cancellation_policy = '';
@@ -349,9 +498,10 @@
349 498 if (preg_match('/(\d+)\s*(hour|day|week)s?\s*before/i', $cancellation_policy, $matches)) {
350 499 $time_value = $matches[1];
351 500 $time_unit = $matches[2];
352 501 $cancellation_text = sprintf(
353 - __('Free cancellation up to %d %s%s before', 'yatra'),
502 + /* translators: 1: numeric time value, 2: time unit (hour/day/week), 3: plural suffix ("s" or empty). */
503 + __('Free cancellation up to %1$d %2$s%3$s before', 'yatra'),
354 504 $time_value,
355 505 $time_unit,
356 506 $time_value > 1 ? 's' : ''
357 507 );