PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.8
Yatra – Travel Booking & Tour Operator Software v3.0.8
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.8, at templates/partials/single-trip/sticky-sidebar.php

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