PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.9
Yatra – Travel Booking & Tour Operator Software v3.0.9
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 / assets / js / single-trip-sidebar.js

single-trip-sidebar.js in Yatra – Travel Booking & Tour Operator Software 3.0.9, at assets/js/single-trip-sidebar.js

368 lines 15.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 function yatraScrollToBooking() {
2 const bookingForm = document.querySelector('.yatra-booking-form');
3 if (bookingForm) {
4 bookingForm.scrollIntoView({ behavior: 'smooth', block: 'center' });
5 } else {
6 // Fallback to main sidebar
7 const sidebar = document.querySelector('.yatra-trip-sidebar');
8 if (sidebar) {
9 sidebar.scrollIntoView({ behavior: 'smooth', block: 'start' });
10 }
11 }
12 }
13
14 function yatraScrollToContact() {
15 const contactSection = document.querySelector('#contact') || document.querySelector('.yatra-contact-form');
16 if (contactSection) {
17 contactSection.scrollIntoView({ behavior: 'smooth', block: 'center' });
18 }
19 }
20
21
22 function yatraCloseMobileSidebar() {
23 const sidebar = document.getElementById('yatra-mobile-sticky-sidebar');
24 if (sidebar) {
25 sidebar.classList.add('user-closed');
26
27 // Save closed state in sessionStorage (persists until page reload)
28 sessionStorage.setItem('yatra-mobile-sidebar-closed', 'true');
29
30 // Hide sidebar with animation
31 setTimeout(() => {
32 sidebar.style.transform = 'translateY(100%)';
33 sidebar.style.opacity = '0';
34 }, 100);
35 }
36 }
37
38 // Auto-show on page load (reset closed state)
39 document.addEventListener('DOMContentLoaded', function() {
40 const sidebar = document.getElementById('yatra-mobile-sticky-sidebar');
41
42 if (sidebar) {
43 // Clear any previous closed state on page load
44 sessionStorage.removeItem('yatra-mobile-sidebar-closed');
45
46 // Ensure sidebar is visible on page load (CSS will handle responsive)
47 sidebar.classList.remove('user-closed');
48 sidebar.classList.remove('hidden');
49 }
50
51 // Initialize mobile booking form functionality
52 initializeMobileBookingForm();
53 });
54
55 function initializeMobileBookingForm() {
56 // Mobile quantity controls
57 const mobileMinusBtns = document.querySelectorAll('.yatra-mobile-quantity-minus');
58 const mobilePlusBtns = document.querySelectorAll('.yatra-mobile-quantity-plus');
59
60 mobileMinusBtns.forEach(btn => {
61 btn.addEventListener('click', function() {
62 const targetId = this.getAttribute('data-target');
63 const input = document.getElementById(targetId);
64 if (input) {
65 const current = parseInt(input.value) || 1;
66 const min = parseInt(input.getAttribute('min')) || 1;
67 if (current > min) {
68 input.value = current - 1;
69 updateMobileTotal();
70 }
71 }
72 });
73 });
74
75 mobilePlusBtns.forEach(btn => {
76 btn.addEventListener('click', function() {
77 const targetId = this.getAttribute('data-target');
78 const input = document.getElementById(targetId);
79 if (input) {
80 const current = parseInt(input.value) || 1;
81 const max = parseInt(input.getAttribute('max')) || 20;
82 if (current < max) {
83 input.value = current + 1;
84 updateMobileTotal();
85 }
86 }
87 });
88 });
89
90 // Mobile date picker - Initialize exactly like main date picker
91 const mobileDateInput = document.getElementById('mobile_travel_date');
92 if (mobileDateInput && mobileDateInput.tagName === 'SELECT') {
93 // Pro "date-as-dropdown" mode: native <select>. Mirror selection to the
94 // desktop counterpart (kept off-screen on mobile) so the rest of the
95 // booking flow — which reads #travel_date — picks the same value.
96 mobileDateInput.addEventListener('change', function () {
97 var desktop = document.getElementById('travel_date');
98 if (desktop) {
99 if (desktop.tagName === 'SELECT') {
100 desktop.value = mobileDateInput.value;
101 desktop.dispatchEvent(new Event('change', { bubbles: true }));
102 } else if (desktop._flatpickr) {
103 desktop._flatpickr.setDate(mobileDateInput.value, true);
104 } else {
105 desktop.value = mobileDateInput.value;
106 }
107 }
108 updateMobileTotal();
109 });
110 } else if (mobileDateInput) {
111 // Default value: today (desktop behavior). If availability mode doesn't allow today,
112 // we will swap to the first enabled date after Flatpickr initializes.
113 const today = new Date();
114 today.setHours(0, 0, 0, 0);
115 const todayStr = today.getFullYear() + '-' +
116 String(today.getMonth() + 1).padStart(2, '0') + '-' +
117 String(today.getDate()).padStart(2, '0');
118 if (!mobileDateInput.value) {
119 mobileDateInput.value = todayStr;
120 }
121
122 // Check if Flatpickr is available
123 if (typeof flatpickr === 'undefined') {
124 // Make it a regular date input as fallback
125 mobileDateInput.type = 'date';
126 mobileDateInput.removeAttribute('readonly');
127 } else {
128 // Get availability dates from global trip data with fallback
129 let availabilityDates = [];
130 try {
131 if (window.yatraTripData && Array.isArray(window.yatraTripData.availabilityDates)) {
132 availabilityDates = window.yatraTripData.availabilityDates;
133 }
134 } catch (e) {
135 }
136
137 var flatpickrLocale = (window.yatraTripData && window.yatraTripData.flatpickrLocale)
138 ? window.yatraTripData.flatpickrLocale
139 : { firstDayOfWeek: 1 };
140 var mobileFpCommon = {
141 locale: flatpickrLocale,
142 clickOpens: true,
143 appendTo: document.body,
144 static: false,
145 onReady: function(selectedDates, dateStr, instance) {
146 if (instance.calendarContainer) {
147 instance.calendarContainer.style.zIndex = '100050';
148 }
149
150 // Ensure we have a selected date (desktop behavior).
151 // If current value is empty, use today.
152 if (!instance.selectedDates || instance.selectedDates.length === 0) {
153 instance.setDate(mobileDateInput.value || todayStr, true);
154 }
155 },
156 onChange: function(selectedDates, dateStr, instance) {
157 var mainDateInput = document.getElementById('travel_date');
158 if (mainDateInput && mainDateInput._flatpickr) {
159 mainDateInput._flatpickr.setDate(dateStr);
160 }
161 updateMobileTotal();
162 }
163 };
164 function yatraBindMobileDatepickerTouch(inputEl) {
165 if (!inputEl || !inputEl._flatpickr) {
166 return;
167 }
168 var fp = inputEl._flatpickr;
169 var btn = document.querySelector('.yatra-mobile-date-btn');
170 var fallbackWrap = inputEl.closest('.yatra-mobile-date-section');
171 var target = btn || fallbackWrap;
172 if (!target) return;
173 target.addEventListener('click', function(e) {
174 e.stopPropagation();
175 fp.open();
176 });
177 target.addEventListener('keydown', function(e) {
178 var key = e.key || e.code;
179 if (key === 'Enter' || key === ' ' || key === 'Spacebar') {
180 e.preventDefault();
181 e.stopPropagation();
182 fp.open();
183 }
184 });
185 }
186 // Initialize Flatpickr with same configuration as main date picker
187 var altFormat = (window.yatraTripData && (window.yatraTripData.flatpickrAltFormat || window.yatraTripData.altDateFormat))
188 ? (window.yatraTripData.flatpickrAltFormat || window.yatraTripData.altDateFormat)
189 : null;
190 if (!altFormat && window.yatraTripData && (window.yatraTripData.dateFormat || window.yatraTripData.date_format)) {
191 // Keep in sync with assets/js/trip.js helper (minimal mapping)
192 var php = window.yatraTripData.dateFormat || window.yatraTripData.date_format;
193 var map = { d:'d', j:'j', D:'D', l:'l', m:'m', n:'n', M:'M', F:'F', Y:'Y', y:'y' };
194 altFormat = '';
195 var esc = false;
196 for (var i = 0; i < php.length; i++) {
197 var ch = php[i];
198 if (esc) { altFormat += ch; esc = false; continue; }
199 if (ch === '\\\\') { esc = true; continue; }
200 altFormat += map[ch] || ch;
201 }
202 }
203 if (!altFormat) {
204 altFormat = 'F j, Y';
205 }
206
207 if (availabilityDates.length > 0) {
208 // Availability-based booking - enable only specific dates
209 flatpickr(mobileDateInput, Object.assign({}, mobileFpCommon, {
210 dateFormat: 'Y-m-d',
211 altInput: true,
212 altFormat: altFormat,
213 minDate: 'today',
214 enable: availabilityDates,
215 defaultDate: availabilityDates[0] || todayStr,
216 }));
217 } else {
218 // Regular booking - flexible dates with min/max constraints
219 var config = Object.assign({}, mobileFpCommon, {
220 dateFormat: 'Y-m-d',
221 altInput: true,
222 altFormat: altFormat,
223 minDate: 'today',
224 defaultDate: mobileDateInput.value || todayStr,
225 });
226 var minDate = mobileDateInput.getAttribute('data-min-date');
227 var maxDate = mobileDateInput.getAttribute('data-max-date');
228 if (minDate) {
229 config.minDate = minDate;
230 if (!mobileDateInput.value) {
231 config.defaultDate = minDate;
232 }
233 }
234 if (maxDate) {
235 config.maxDate = maxDate;
236 }
237 flatpickr(mobileDateInput, config);
238 }
239 yatraBindMobileDatepickerTouch(mobileDateInput);
240 }
241 }
242
243 // Travelers selector is rendered in the sticky bar using the same component as desktop.
244
245 // Mobile check availability button
246 const mobileCheckBtn = document.getElementById('mobile-check-availability-btn');
247 if (mobileCheckBtn) {
248 mobileCheckBtn.addEventListener('click', function() {
249 // Sync mobile form data with main form
250 syncMobileToMainForm();
251 // Trigger main check availability
252 const mainCheckBtn = document.getElementById('check-availability-btn');
253 if (mainCheckBtn) {
254 mainCheckBtn.click();
255 }
256 });
257 }
258 }
259
260 function syncMobileToMainForm() {
261 // Sync date
262 const mobileDate = document.getElementById('mobile_travel_date');
263 const mainDate = document.getElementById('travel_date');
264 if (mobileDate && mainDate) {
265 // If main date has Flatpickr instance, use it
266 if (mainDate._flatpickr) {
267 mainDate._flatpickr.setDate(mobileDate.value);
268 } else {
269 mainDate.value = mobileDate.value;
270 // Native <select> consumers (date-as-dropdown Pro mode) listen for
271 // `change` — emit one so downstream handlers wake up.
272 if (mainDate.tagName === 'SELECT') {
273 mainDate.dispatchEvent(new Event('change', { bubbles: true }));
274 }
275 }
276 }
277
278 // Sync travelers
279 const mobileTravelers = document.getElementById('mobile_num_travelers');
280 const mainTravelers = document.getElementById('num_travelers');
281 if (mobileTravelers && mainTravelers) {
282 mainTravelers.value = mobileTravelers.value;
283 }
284
285 // Sync traveler-based pricing (mobile selector -> desktop selector)
286 const mobileTravelerInputs = document.querySelectorAll('#mobile-quantity-selector input[id^="mobile_traveler_"]');
287 if (mobileTravelerInputs && mobileTravelerInputs.length > 0) {
288 mobileTravelerInputs.forEach((input) => {
289 const desktopId = input.id.replace(/^mobile_/, '');
290 const desktopInput = document.getElementById(desktopId);
291 if (desktopInput) {
292 desktopInput.value = input.value;
293 }
294 });
295 }
296 }
297
298 function updateMobileTotal() {
299 const sidebar = document.getElementById('yatra-mobile-sticky-sidebar');
300 if (!sidebar) return;
301
302 const pricingType = (window.yatraTripData && window.yatraTripData.pricingType) ? window.yatraTripData.pricingType : 'regular';
303 const mobileTotal = document.getElementById('mobile-total-amount');
304 if (!mobileTotal) return;
305
306 let total = 0;
307
308 if (pricingType === 'traveler_based') {
309 // Traveler-based pricing: sum all category quantities × prices
310 const travelerInputs = document.querySelectorAll('[id^="mobile_traveler_"]');
311 travelerInputs.forEach(input => {
312 const qty = parseInt(input.value) || 0;
313 const price = parseFloat(input.getAttribute('data-price')) || 0;
314 total += qty * price;
315 });
316 } else {
317 // Regular pricing: simple multiplication
318 const travelersInput = document.getElementById('mobile_num_travelers');
319 if (travelersInput) {
320 const travelers = parseInt(travelersInput.value) || 1;
321 const basePrice = parseFloat(travelersInput.getAttribute('data-price')) || 0;
322 total = travelers * basePrice;
323 }
324 }
325
326 // Format and update display (mobile total element has been removed)
327 // The mobile total price display is no longer shown
328 if (mobileTotal) {
329 if (window.yatraTripData && window.yatraTripData.currencySymbol) {
330 mobileTotal.textContent = window.yatraTripData.currencySymbol + total.toFixed(2);
331 } else {
332 mobileTotal.textContent = formatCurrency(total);
333 }
334 }
335
336 // Also sync with main form total if it exists (main total also removed)
337 const mainTotal = document.getElementById('total-amount');
338 if (mainTotal && window.yatraTripData && window.yatraTripData.currencySymbol) {
339 mainTotal.textContent = window.yatraTripData.currencySymbol + total.toFixed(2);
340 }
341 }
342
343 function formatCurrency(amount) {
344 // Simple currency formatting (can be enhanced)
345 return '$' + amount.toFixed(2);
346 }
347
348 // Auto-hide on scroll down, show on scroll up (also works for enquiry-only sticky)
349 let lastScrollTop = 0;
350 const mobileSidebar = document.getElementById('yatra-mobile-sticky-sidebar');
351 const scrollThreshold = 100;
352
353 window.addEventListener('scroll', function() {
354 if (!mobileSidebar) return;
355
356 const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
357
358 if (scrollTop > lastScrollTop && scrollTop > scrollThreshold) {
359 // Scrolling down - hide sidebar
360 mobileSidebar.classList.add('hidden');
361 } else {
362 // Scrolling up - show sidebar
363 mobileSidebar.classList.remove('hidden');
364 }
365
366 lastScrollTop = scrollTop <= 0 ? 0 : scrollTop;
367 }, false);
368