PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.4
Yatra – Travel Booking & Tour Operator Software v3.0.4
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
yatra / templates / partials / single-trip / sticky-sidebar.php

sticky-sidebar.php in Yatra – Travel Booking & Tour Operator Software 3.0.4, at templates/partials/single-trip/sticky-sidebar.php

287 lines 14.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Mobile Sticky Sidebar Template
4 *
5 * Displays booking card in sticky bottom position on mobile devices
6 * SEO-friendly approach - separate template, no content duplication
7 *
8 * @package Yatra
9 */
10
11 if (!defined('ABSPATH')) {
12 exit;
13 }
14
15 // Expected variables: $trip, $has_availability, $has_traveler_pricing, $base_price, $pricing_type
16
17 $booking_mode = $trip->getBookingMode();
18
19 // Calculate pricing - use pre-computed values from SingleTripController
20 $pricing = [
21 'has_price' => false,
22 'current_price' => '',
23 'original_price' => '',
24 'price_prefix' => '',
25 'has_discount' => false,
26 'raw_current_price' => 0,
27 'raw_original_price' => 0,
28 'is_traveler_based' => $has_traveler_pricing
29 ];
30
31 $discount = [
32 'has_discount' => false,
33 'discount_text' => '',
34 'discount_percentage' => 0
35 ];
36
37 // Use effective_price_min computed in SingleTripController
38 $effective_min = (float) ($trip->effective_price_min ?? 0);
39 $original_min = (float) ($trip->min_category_original_price ?? 0);
40 $max_discount_pct = (int) ($trip->max_discount_percentage ?? 0);
41
42 if ($effective_min > 0) {
43 $pricing['has_price'] = true;
44 $pricing['raw_current_price'] = $effective_min;
45 $pricing['current_price'] = yatra_format_price($effective_min);
46
47 if ($has_traveler_pricing || $has_availability) {
48 $pricing['price_prefix'] = __('From ', 'yatra');
49 }
50
51 if ($max_discount_pct > 0 && $original_min > $effective_min) {
52 $pricing['has_discount'] = true;
53 $pricing['raw_original_price'] = $original_min;
54 $pricing['original_price'] = yatra_format_price($original_min);
55
56 $discount['has_discount'] = true;
57 $discount['discount_percentage'] = $max_discount_pct;
58 $discount['discount_text'] = sprintf(__('Up to %d%%', 'yatra'), $max_discount_pct);
59 }
60 } else {
61 $original = (float) ($trip->getOriginalPrice() ?? 0);
62 $discounted = (float) ($trip->discounted_price ?? 0);
63
64 if ($discounted > 0) {
65 $current = $discounted;
66 } elseif ($original > 0) {
67 $current = $original;
68 } elseif (!empty($trip->getSalePrice()) && (float)$trip->getSalePrice() > 0) {
69 $current = (float) $trip->getSalePrice();
70 } else {
71 $current = 0;
72 }
73
74 if ($current > 0) {
75 $pricing['has_price'] = true;
76 $pricing['raw_current_price'] = $current;
77 $pricing['current_price'] = yatra_format_price($current);
78
79 if ($current < $original && $original > 0) {
80 $pct = round((($original - $current) / $original) * 100);
81
82 // Only show discount if percentage is greater than 0
83 if ($pct > 0) {
84 $pricing['has_discount'] = true;
85 $pricing['raw_original_price'] = $original;
86 $pricing['original_price'] = yatra_format_price($original);
87
88 $discount['has_discount'] = true;
89 $discount['discount_percentage'] = $pct;
90 $discount['discount_text'] = sprintf(__('%d%%', 'yatra'), $pct);
91 }
92 }
93 }
94 }
95 // Pro: check whether this trip has booking disabled (enquiry-only mode)
96 $booking_disabled = method_exists($trip, 'isBookingDisabled') && $trip->isBookingDisabled();
97
98 // Pro feature: same filter contract as the desktop sidebar — the Pro module
99 // decides whether to switch to a dropdown and supplies the option list.
100 $yatra_mobile_date_options = (array) apply_filters('yatra_single_trip_date_dropdown_options', [], $trip, null);
101 $yatra_mobile_use_date_dropdown = (bool) apply_filters('yatra_use_date_dropdown', false, $trip)
102 && !empty($yatra_mobile_date_options);
103 ?>
104 <?php if ($booking_disabled): ?>
105 <div class="yatra-mobile-sticky-sidebar yatra-enquiry-only-sticky" id="yatra-mobile-sticky-sidebar">
106 <div class="yatra-mobile-sticky-content">
107 <div class="yatra-mobile-row-1" style="justify-content:space-between;align-items:center;">
108 <div style="font-size:0.82rem;color:#1a5fa8;padding:0 4px;">
109 <?php esc_html_e('Enquiry only — no direct booking', 'yatra'); ?>
110 </div>
111 <button type="button"
112 class="yatra-mobile-book-btn"
113 style="background:var(--yatra-primary,#3b82f6);"
114 onclick="window.YatraEnquiry?.open?.(); return false;">
115 <?php esc_html_e('Send Enquiry', 'yatra'); ?>
116 </button>
117 </div>
118 </div>
119 </div>
120 <?php else: ?>
121 <div class="yatra-mobile-sticky-sidebar" id="yatra-mobile-sticky-sidebar"
122 data-booking-mode="<?php echo esc_attr($booking_mode); ?>">
123 <!-- Two-Row Layout -->
124 <div class="yatra-mobile-sticky-content">
125
126 <!-- Row 1: Compact fields + close (clean grid layout) -->
127 <div class="yatra-mobile-row-1">
128
129 <div class="yatra-mobile-date-section">
130 <div class="yatra-mobile-field-btn yatra-mobile-date-btn<?php echo $yatra_mobile_use_date_dropdown ? ' yatra-mobile-date-as-dropdown' : ''; ?>"
131 <?php if (!$yatra_mobile_use_date_dropdown): ?>role="button" tabindex="0"<?php endif; ?>
132 aria-label="<?php echo esc_attr__('Select date', 'yatra'); ?>">
133 <span class="yatra-mobile-field-icon" aria-hidden="true">
134 <?php echo yatra_svg_icon('calendar', 'yatra-mobile-icon'); ?>
135 </span>
136 <?php if ($yatra_mobile_use_date_dropdown): ?>
137 <select id="mobile_travel_date"
138 name="travel_date"
139 class="yatra-mobile-datepicker-input yatra-mobile-date-dropdown"
140 required>
141 <option value=""><?php esc_html_e('Select date', 'yatra'); ?></option>
142 <?php foreach ($yatra_mobile_date_options as $opt): ?>
143 <option value="<?php echo esc_attr($opt['value']); ?>"
144 <?php if (!empty($opt['time'])): ?>data-departure-time="<?php echo esc_attr($opt['time']); ?>"<?php endif; ?>>
145 <?php echo esc_html($opt['label']); ?>
146 </option>
147 <?php endforeach; ?>
148 </select>
149 <?php else: ?>
150 <input type="text"
151 id="mobile_travel_date"
152 name="travel_date"
153 class="yatra-mobile-datepicker-input yatra-mobile-datepicker"
154 placeholder="<?php esc_attr_e('Date', 'yatra'); ?>"
155 data-min-date="<?php echo esc_attr($trip->getAvailableFrom() ?: date('Y-m-d')); ?>"
156 data-max-date="<?php echo esc_attr($trip->getAvailableTo() ?: ''); ?>"
157 readonly
158 required>
159 <?php endif; ?>
160 <svg class="yatra-mobile-arrow" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
161 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
162 </svg>
163 </div>
164 </div>
165
166 <div class="yatra-mobile-travelers-section">
167 <?php if ($has_traveler_pricing): ?>
168 <?php
169 // Reuse the same traveler selector component as desktop, but with mobile-specific IDs
170 $traveler_data = \Yatra\Controllers\SingleTripController::prepareTravelerSelectorData($trip, 'sidebar');
171 $rows = $traveler_data['traveler_rows'] ?? [];
172
173 // Prefix IDs so they don't collide with the desktop sidebar.
174 foreach ($rows as &$row) {
175 if (!empty($row['minus_attrs']['data-target'])) {
176 $row['minus_attrs']['data-target'] = 'mobile_' . $row['minus_attrs']['data-target'];
177 }
178 if (!empty($row['plus_attrs']['data-target'])) {
179 $row['plus_attrs']['data-target'] = 'mobile_' . $row['plus_attrs']['data-target'];
180 }
181 if (!empty($row['input_attrs']['id'])) {
182 $row['input_attrs']['id'] = 'mobile_' . $row['input_attrs']['id'];
183 }
184 if (!empty($row['input_attrs']['name'])) {
185 $row['input_attrs']['name'] = 'mobile_' . $row['input_attrs']['name'];
186 }
187 }
188 unset($row);
189
190 // Setup variables for traveler-selector.php
191 $root_id = 'mobile-participants';
192 $root_class = 'yatra-booking-field-select yatra-participants-select yatra-mobile-participants';
193 $container_attrs = [];
194
195 $display_id = 'mobile-participants-display';
196 $display_class = 'yatra-participants-display yatra-mobile-participants-display';
197 $display_attrs = [];
198
199 $dropdown_id = 'mobile-quantity-selector';
200 $dropdown_class = 'yatra-booking-quantity-selector yatra-mobile-quantity-selector';
201 $dropdown_attrs = [];
202
203 $display_text = $traveler_data['traveler_display_text'] ?? __('Select travelers', 'yatra');
204 $icon_html = yatra_svg_icon('users', 'yatra-mobile-icon');
205
206 include YATRA_PLUGIN_PATH . 'templates/partials/traveler-selector.php';
207 ?>
208 <?php else: ?>
209 <!-- Regular Pricing -->
210 <div class="yatra-mobile-booking-field">
211 <div class="yatra-mobile-field-icon">
212 <?php echo yatra_svg_icon('users', 'yatra-mobile-icon'); ?>
213 </div>
214 <div class="yatra-mobile-quantity-controls">
215 <button type="button" class="yatra-mobile-quantity-btn yatra-mobile-quantity-minus" data-target="mobile_num_travelers">
216 <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
217 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 12H4"/>
218 </svg>
219 </button>
220 <input type="number"
221 id="mobile_num_travelers"
222 name="num_travelers"
223 class="yatra-mobile-quantity-input"
224 value="1"
225 min="<?php echo esc_attr($trip->getMinTravelers() ?: 1); ?>"
226 max="<?php echo esc_attr($trip->getMaxTravelers() ?: 20); ?>"
227 readonly
228 data-price="<?php echo esc_attr($base_price); ?>">
229 <button type="button" class="yatra-mobile-quantity-btn yatra-mobile-quantity-plus" data-target="mobile_num_travelers">
230 <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
231 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
232 </svg>
233 </button>
234 </div>
235 </div>
236 <?php
237 $mobile_tr_min = (int) ($trip->getMinTravelers() ?: 1);
238 $mobile_tr_max = (int) ($trip->getMaxTravelers() ?: 20);
239 ?>
240 <span class="yatra-travelers-range yatra-mobile-travelers-range"><?php
241 echo esc_html(
242 __('Min', 'yatra') . ': ' . $mobile_tr_min . ', ' . __('Max', 'yatra') . ': ' . $mobile_tr_max
243 );
244 ?></span>
245 <?php endif; ?>
246 </div>
247 </div>
248
249 <!-- Close Button (fixed in top-right of sticky container) -->
250 <div class="yatra-mobile-action-buttons">
251 <button type="button"
252 class="yatra-mobile-close-btn"
253 aria-label="<?php echo esc_attr__('Close', 'yatra'); ?>"
254 onclick="yatraCloseMobileSidebar()">
255 <span class="yatra-mobile-close-fallback" aria-hidden="true">&times;</span>
256 </button>
257 </div>
258
259 <!-- Row 2: Primary Actions -->
260 <div class="yatra-mobile-row-2">
261 <button type="button" class="yatra-mobile-check-btn" id="mobile-check-availability-btn" data-trip-id="<?php echo esc_attr($trip->getId()); ?>">
262 <svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
263 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
264 </svg>
265 <span><?php echo esc_html__('Check', 'yatra'); ?></span>
266 </button>
267 <button type="button" class="yatra-mobile-enquire-btn" id="mobile-open-enquiry-modal" onclick="if (window.enquiryModal) { window.enquiryModal.open(); } else if (window.YatraEnquiry) { window.YatraEnquiry.open(); } return false;">
268 <svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
269 <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"/>
270 </svg>
271 <span><?php echo esc_html__('Enquire', 'yatra'); ?></span>
272 </button>
273 </div>
274
275 </div>
276
277 </div>
278 <?php endif; // end else (booking not disabled) ?>
279
280 <?php
281 // Mobile sticky-sidebar + flatpickr initializer is enqueued as
282 // assets/js/single-trip-sidebar.js by FrontendAssetsProvider::enqueueTripDetailAssets()
283 // — not inlined here. Keeping it inline caused WordPress's `convert_chars` content
284 // filter to rewrite the `&&` operators in this JS as `&#038;&#038;`, which the
285 // browser then refuses to parse. The external file is delivered untouched.
286 ?>
287