1// Booking Confirm Page Load Handler 2document.addEventListener("DOMContentLoaded", function () { 3 // Check if current URL contains /booking-confirm 4 if ( 5 window.location.pathname.includes("/booking-confirm") || 6 isTicketDetailsPage() 7 ) { 8 initBookingConfirmationPage(); 9 initTicketDetailsPage(); 10 } 11}); 12 13function initBookingConfirmationPage() { 14 window.printTicketTemplate = 15 typeof print_ticket === "function" ? print_ticket() : ""; 16 window.ticketDetail = null; 17 let params = getParamsData(window.location.search); 18 startLoading('[data-widget-id="print_ticket"]'); 19 loadPrintTicket(params); 20} 21 22function initTicketDetailsPage() { 23 initModifyTicketModals(); 24} 25 26function initModifyTicketModals() { 27 window.cancelTicketModal = new CustomModal({ 28 header: cancelTicketHeader(), 29 body: `<div></div>`, 30 className: "modify-ticket-modal", 31 }); 32 window.modifyTicketModal = new CustomModal({ 33 header: "", 34 width: "800px", 35 showHeader: false, 36 body: `<div></div>`, 37 className: "modify-ticket-modal", 38 }); 39} 40 41function modifyTicket() { 42 let { email = "", mobile = "" } = 43 window.ticketDetail?.onward?.passenger_details[0] || {}; 44 let mobileNumber = mobile.split("-")[1] || mobile.split("-")[0] || ""; 45 window.modifyTicketModal.setBody( 46 constructModifyTicketContent({ email, mobileNumber }), 47 ); 48 window.modifyTicketModal.show(); 49 let boardingPointOptions = []; 50 window.ticketDetail?.onward?.available_stages?.forEach((stages) => { 51 boardingPointOptions.push({ 52 value: stages[1], 53 label: stages[0], 54 }); 55 }); 56 let droppingPointOptions = []; 57 window.ticketDetail?.onward?.available_droppoff_stages?.forEach((stages) => { 58 droppingPointOptions.push({ 59 value: stages[1], 60 label: stages[0], 61 }); 62 }); 63 const modifyBoardingElement = document.getElementById("boardingPoint"); 64 const modifyDroppingElement = document.getElementById("droppingPoint"); 65 66 window.modifyVirtualBoardingElement = VirtualSelect.init({ 67 ele: modifyBoardingElement, 68 options: boardingPointOptions, 69 hideClearButton: true, 70 dropboxWrapper: ".cms-modal-overlay", 71 popupDropboxBreakpoint: "200px", 72 }); 73 74 window.modifyVirtualDroppingElement = VirtualSelect.init({ 75 ele: modifyDroppingElement, 76 options: droppingPointOptions, 77 hideClearButton: true, 78 dropboxWrapper: ".cms-modal-overlay", 79 popupDropboxBreakpoint: "200px", 80 }); 81 setTimeout(() => { 82 window.modifyVirtualBoardingElement.setValue( 83 window.ticketDetail?.onward?.boarding_details.stage_id.toString(), 84 ); 85 window.modifyVirtualBoardingElement.$valueText.innerText = 86 boardingPointOptions.find( 87 (option) => 88 option.value === 89 window.ticketDetail?.onward?.boarding_details.stage_id, 90 )?.label || ""; 91 window.modifyVirtualDroppingElement.setValue( 92 window.ticketDetail?.onward?.drop_off_details.stage_id.toString(), 93 ); 94 window.modifyVirtualDroppingElement.$valueText.innerText = 95 droppingPointOptions.find( 96 (option) => 97 option.value === 98 window.ticketDetail?.onward?.drop_off_details.stage_id, 99 )?.label || ""; 100 }, 100); 101 102 window.searchWidgetForm = new window.JustValidate( 103 ".modify-ticket-modal-form", 104 ); 105 106 window.searchWidgetForm 107 .addField("#mobileNumber", [ 108 { 109 rule: "required", 110 errorMessage: "Please enter mobile number", 111 }, 112 { 113 rule: "minLength", 114 value: 10, 115 errorMessage: "Mobile number must be 10 digits", 116 }, 117 { 118 rule: "maxLength", 119 value: 10, 120 errorMessage: "Mobile number must be 10 digits", 121 }, 122 ]) 123 .addField("#email", [ 124 { 125 rule: "required", 126 errorMessage: "Please enter email", 127 }, 128 { 129 rule: "email", 130 errorMessage: "Please enter a valid email address", 131 }, 132 ]) 133 .addField("#boardingPoint", [ 134 { 135 rule: "required", 136 errorMessage: "Please select boarding point", 137 }, 138 ]) 139 .addField("#droppingPoint", [ 140 { 141 rule: "required", 142 errorMessage: "Please select dropping point", 143 }, 144 ]); 145 146 window.searchWidgetForm.onSuccess(async (event) => { 147 buttonLoading("#modify-ticket-submit-button"); 148 updateTicketDetails(); 149 }); 150 window.searchWidgetForm.onFail((event) => { 151 buttonStopLoading("#modify-ticket-submit-button"); 152 }); 153} 154 155function updateTicketDetails() { 156 const data = {}; 157 const ticket_number = window.ticketDetail.onward.ticket_number; 158 const phoneNumber = document.getElementById("mobileNumber").value; 159 data.query = "q15"; 160 data.pnr_number = ticket_number; 161 data.phone_number = phoneNumber; 162 data.boarding_at = window.modifyVirtualBoardingElement.getValue(); 163 data.country_code = "91"; 164 data.email = document.getElementById("email").value; 165 data.dropping_at = window.modifyVirtualDroppingElement.getValue(); 166 167 apiClient.post("/api/cms_booking_engine.json", data).then((res) => { 168 if (res.status === 200) { 169 if (res.data.code == 500) { 170 message.error(res.data.message); 171 buttonStopLoading("#modify-ticket-submit-button"); 172 } else { 173 if (res.data != undefined && res.data.key != undefined) { 174 var url_data = `key=${res.data.key}`; 175 } else { 176 var url_data = `pnr_number=${ticket_number}&email=${phoneNumber}`; 177 } 178 buttonStopLoading("#modify-ticket-submit-button"); 179 window.modifyTicketModal.hide(); 180 setTimeout(() => { 181 message.success( 182 res.data.message ? res.data.message : "Ticket Updated Successfully", 183 ); 184 setTimeout(() => { 185 window.location = `${window.location.origin}${window.location.pathname}?${url_data}`; 186 }, 1000); 187 }, 1000); 188 } 189 //window.location.reload(); 190 } else { 191 buttonStopLoading("#modify-ticket-submit-button"); 192 window.modifyTicketModal.hide(); 193 setTimeout(() => { 194 message.error(res.data.message); 195 }, 1000); 196 } 197 }); 198 199 // if (sysConfigurationModifyTicket) { 200 // } 201} 202 203function isTicketDetailsPage() { 204 return window.location.pathname.includes("/ticket-details"); //|| window.location.pathname.includes('/my-bookings'); 205} 206 207async function loadPrintTicket(params = {}, cb = null) { 208 const state = window.store?.getState(); 209 if (!state || !params) return; 210 const { operatorDetails } = state.config; 211 try { 212 const response = await apiClient.post("/api/cms_booking_engine.json", { 213 query: "q12", 214 ...params, 215 });
216 if (response.data.code === 419) { 217 message.error( 218 response.data.message || 219 "Could not found this E-ticket. Please check the ticket number!", 220 ); 221 setTimeout(() => { 222 window.location.href = "/manage-booking"; 223 }, 2000); 224 return false; 225 } 226 if (response.status === 200 && response.data) { 227 const data = response.data; 228 window.ticketDetail = data || null; 229 230 if (cb && typeof cb === "function") { 231 cb(); 232 } 233 234 let html = ""; 235 html += constructTicketDetailsPageHeader(); 236 237 html += renderTemplate(window.printTicketTemplate, { 238 logoUrl: window.logoUrl || "", 239 operator_address: operatorDetails.operator_address, 240 operator_work_number: operatorDetails.operator_work_number, 241 ...data, 242 }); 243 //data-widget-id="print_ticket" append inside the div 244 const container = document.querySelector( 245 '[data-widget-id="print_ticket"]', 246 ); 247 if (!container) { 248 return false; 249 } 250 document.querySelector('[data-widget-id="print_ticket"]').innerHTML = 251 html; 252 stopLoading('[data-widget-id="print_ticket"]'); 253 const headerContentContainer = document.querySelector( 254 ".print_ticket-1-header-content-container", 255 ); 256 if (isTicketDetailsPage()) { 257 if ( 258 headerContentContainer && 259 headerContentContainer.children.length > 0 260 ) { 261 headerContentContainer.children[0].style.display = "none"; 262 headerContentContainer.children[1].style.display = "none"; 263 } 264 document.getElementById("tracking_details_container").style.display = 265 "flex"; 266 } 267 } 268 } catch (error) { 269 stopLoading('[data-widget-id="print_ticket"]'); 270 console.error("Error fetching booking data:", error); 271 } 272} 273 274function printTicket() { 275 window.print(); 276} 277 278function downloadTicket() { 279 if (!ticketDetail.onward.ticket_number) { 280 message.error("Ticket number not found!"); 281 return; 282 } 283 let api_url = window.API_URL; 284 window.open( 285 `${api_url}/t_tickets/cms_download_pdf_ticket?pnr_number=${ticketDetail.onward.ticket_number}&is_new_cms=true`, 286 "_blank", 287 ); 288} 289 290function constructTicketDetailsPageHeader() { 291 let actionList = window.ticketDetail.onward.is_eticket 292 ? [ 293 { 294 label: "Print ticket", 295 onClick: "printTicket()", 296 is_visible: true, 297 }, 298 { 299 label: "Download ticket", 300 onClick: "downloadTicket()", 301 is_visible: true, 302 }, 303 { 304 label: "Prepone / postpone", 305 onClick: "cancelTicket(1)", 306 is_visible: window.ticketDetail?.is_pre_postpone_allowed, 307 }, 308 { 309 label: "Modify ticket", 310 onClick: "modifyTicket()", 311 is_visible: window.ticketDetail?.is_update_allowed, 312 }, 313 { 314 label: "Cancel ticket", 315 onClick: "cancelTicket(2)", 316 is_visible: window.ticketDetail?.is_cancellation_allowed, 317 }, 318 ] 319 : [ 320 { 321 label: "Print ticket", 322 onClick: "printTicket()", 323 is_visible: true, 324 }, 325 { 326 label: "Modify ticket", 327 onClick: "modifyTicket()", 328 is_visible: 329 window.ticketDetail.sys_confinguration_modify_ticket && 330 window.ticketDetail?.is_update_allowed, 331 }, 332 ]; 333 html = ` 334 <div class="ticket-details-header_container"> 335 <div class="ticket-details-header"> 336 <div class="title-section"> 337 Booking details 338 </div> 339 <div class="action-section">`; 340 actionList.forEach((action) => { 341 if (action.is_visible === false) return; 342 html += `<button class="modify-ticket-button" onclick="${action.onClick}"> 343 ${action.label} 344 <img src="${window.domain}/common/assets/images/arrow_forward.svg" alt="arrow right" /> 345 </button>`; 346 }); 347 html += `</div> 348 </div> 349 </div> 350 `; 351 return html; 352} 353 354function cancelTicket(tabType) { 355 window.cancelTicketModal.setBody(cancelTicketBody(tabType)); 356 window.cancelTicketModal.show(); 357 let contentTemplate = ""; 358 let params = getParamsData(window.location.search); 359 if (window.cancelTicketModal && tabType === 2) { 360 contentTemplate = cancelTicketContent(); 361 const state = window.store?.getState(); 362 const { user } = state?.user || {}; 363 document.getElementById("modify-ticket-body-content").innerHTML = 364 renderTemplate(contentTemplate, { 365 ...window.ticketDetail.onward, 366 is_coupon_allowed: window.ticketDetail.is_coupon_allowed, 367 is_cash_credit: window.ticketDetail.is_cash_credit, 368 is_guest_user: !user, 369 }); 370 if (params.is_from_my_booking !== "true") { 371 document.querySelector( 372 'input[name="refund_type"][value="' + 373 (window.ticketDetail.onward.is_coupon_allowed && 374 !window.ticketDetail.onward.wallet_booking && 375 !window.ticketDetail.onward.default_cash_credit 376 ? "true" 377 : "false") + 378 '"]', 379 ).checked = true; 380 } 381 //#cancel-ticket-button 382 document.getElementById("cancel-ticket-button").style.display = "flex"; 383 document.getElementById("pre-postPone-button").style.display = "none"; 384 } else if (window.cancelTicketModal && tabType === 1) { 385 contentTemplate = prePostPoneTicketContent(); 386 document.getElementById("modify-ticket-body-content").innerHTML = 387 renderTemplate(contentTemplate, { 388 ...window.ticketDetail.onward, 389 reschedule_percentage: 390 window.ticketDetail.reschedule_percentage > 0 && 391 typeof window.ticketDetail.reschedule_percentage === "number" 392 ? `${window.ticketDetail.reschedule_percentage.toString()}%` 393 : "â¹0", 394 }); 395 initPrePostPoneDatePicker(); 396 //#pre-postPone-button 397 document.getElementById("cancel-ticket-button").style.display = "none"; 398 document.getElementById("pre-postPone-button").style.display = "flex"; 399 } 400} 401 402function initPrePostPoneDatePicker() { 403 let prePostPoneDatePicker = null; 404 const newTravelDateInput = document.getElementById("newTravelDate"); 405 const travelDate = window.ticketDetail.onward.travel_date; 406 prePostPoneDatePicker = flatpickr("#newTravelDate", { 407 altInput: true, 408 altFormat: "j F Y", 409 dateFormat: "d/m/Y", // Changed to match original '"24/12/2025"' format 410 minDate: "today", 411 allowInput: true, 412 clickOpens: true, 413 altInputClass: "flatpickr-input", // Custom class for input 414 disableMobile: "true", 415 onReady: function (selectedDates, dateStr, instance) { 416 // Get the alternate input (the visible one) instead of the original hidden input 417 const altInput = instance.altInput; 418 419 if (altInput) { 420 const wrapper = document.createElement("div"); 421 wrapper.className = "PrePostPone-datepicker-wrapper"; 422 wrapper.style.cssText = ` 423 position: relative; 424 display: flex; 425 align-items: center; 426 width: 100%; 427 `; 428 429 // Insert wrapper before the alternate input 430 document.querySelector(".PrePostPone-datepicker-wrapper")?.remove(); // Remove any existing wrapper 431 altInput.parentNode.insertBefore(wrapper, altInput); 432 433 // Move alternate input into wrapper 434 wrapper.appendChild(altInput); 435 436 // Create calendar icon 437 const calendarIcon = document.createElement("img"); 438 calendarIcon.className = "calendar-icon"; 439 calendarIcon.src = `${window.domain}/common/assets/images/calendar_today.svg`; 440 calendarIcon.alt = "Calendar Icon"; 441 calendarIcon.style.cssText = ` 442 position: absolute; 443 right: 0px; 444 margin-right: 16px; 445 top: 50%; 446 transform: translateY(-50%);
447 width: 20px; 448 height: 20px; 449 cursor: pointer; 450 z-index: 1; 451 `; 452 453 // Add click event to icon to open calendar 454 calendarIcon.addEventListener("click", function (e) { 455 e.preventDefault(); 456 instance.open(); 457 }); 458 459 wrapper.appendChild(calendarIcon); 460 } 461 }, 462 onClose: function (selectedDates, dateStr, instance) { 463 if (!dateStr) { 464 document 465 .getElementById("newTravelDate") 466 .classList.add("placeholder-active"); 467 document 468 .getElementById("newTravelDate") 469 .setAttribute("placeholder", "Onward Date"); 470 } 471 }, 472 }); 473} 474 475function cancelTicketHeader() { 476 const html = String.raw; 477 const htmlContent = html`<div class="modify-ticket-header"> 478 <div class="icon-container"> 479 <svg 480 width="24" 481 height="24" 482 viewBox="0 0 24 24" 483 fill="none" 484 xmlns="http://www.w3.org/2000/svg" 485 > 486 <path 487 d="M20 3H19V1H17V3H7V1H5V3H4C2.9 3 2 3.9 2 5V21C2 22.1 2.9 23 4 23H20C21.1 23 22 22.1 22 21V5C22 3.9 21.1 3 20 3ZM20 21H4V10H20V21ZM20 8H4V5H20V8Z" 488 fill="#FF4800" 489 /> 490 </svg> 491 </div> 492 <div class="text-container"> 493 <div class="title">Prepone/ postpone ticket</div> 494 <div class="description">You have a FlexiTicket</div> 495 </div> 496 </div>`; 497 return htmlContent; 498} 499 500function cancelTicketBody(tabType) { 501 let html = ""; 502 html += `<div class="modify-ticket-body">`; 503 html += `<div class="modify-ticket-body-description">You have a FlexiTicket</div>`; 504 html += cancelTicketTabs(tabType); 505 html += `<div class="modify-ticket-body-content" id="modify-ticket-body-content">`; 506 html += `</div>`; 507 html += `</div>`; 508 return html; 509} 510 511function cancelTicketTabs(tabType) { 512 const refund_percentage = 513 100 - window.ticketDetail.onward.cancellation_percentage; 514 const id_reschedule_percentage = 515 window.ticketDetail.reschedule_percentage > 0 && 516 typeof window.ticketDetail.reschedule_percentage === "number"; 517 let html = ""; 518 html += `<div class="modify-ticket-tabs" id="modify-ticket-tabs">`; 519 html += `<div class="modify-ticket-tab" onclick="cancelTicketTabContent(1)">`; 520 html += `<div class="tab-form-container">`; 521 html += `<input type="radio" name="modify-ticket-tab" id="change-date-tab" ${tabType === 1 ? "checked" : ""} />`; 522 html += `<div class="tab-label">`; 523 html += `<div class="tab-title">Prepone / postpone${!id_reschedule_percentage ? ' for <span class="highlight">FREE</span>' : ""}</div>`; 524 html += `<div class="tab-description">Subject to seat availability</div>`; 525 html += `</div>`; 526 html += `</div>`; 527 html += `<div class="tab-action-button-container"><button class="tab-action-button" id="pre-postPone-button" onclick="showTicketTabContent(1)">Date change ${!id_reschedule_percentage ? "for free" : ""}</button></div>`; 528 html += `</div>`; 529 html += `<div class="modify-ticket-tab" onclick="cancelTicketTabContent(2)">`; 530 html += `<div class="tab-form-container">`; 531 html += `<input type="radio" name="modify-ticket-tab" id="cancel-ticket-tab" ${tabType === 2 ? "checked" : ""} />`; 532 html += `<div class="tab-label">`; 533 html += `<div class="tab-title">Cancel ticket</div>`; 534 html += `<div class="tab-description">${refund_percentage || 0}% of total fare will be refunded</div>`; 535 html += `</div>`; 536 html += `</div>`; 537 html += `<div class="tab-action-button-container"><button class="tab-action-button" id="cancel-ticket-button" onclick="showTicketTabContent(2)">Review and cancel</button></div>`; 538 html += `</div>`; 539 html += `</div>`; 540 return html; 541} 542 543function showTicketTabContent(tabType) { 544 this.event.stopPropagation(); 545 window.cancelTicketModal.hideHeader(); 546 document.getElementById("modify-ticket-tabs").style.display = "none"; 547 document.getElementById("modify-ticket-body-content").style.display = "flex"; 548} 549 550function cancelTicketTabContent(tabType) { 551 // stop propagation to avoid triggering parent onclick 552 this.event.stopPropagation(); 553 if (tabType === 1) { 554 window.cancelTicketModal.setBody(cancelTicketBody(tabType)); 555 contentTemplate = prePostPoneTicketContent(); 556 document.getElementById("modify-ticket-body-content").innerHTML = 557 renderTemplate(contentTemplate, { 558 ...window.ticketDetail.onward, 559 reschedule_percentage: 560 window.ticketDetail.reschedule_percentage > 0 && 561 typeof window.ticketDetail.reschedule_percentage === "number" 562 ? `${window.ticketDetail.reschedule_percentage.toString()}%` 563 : "â¹0", 564 }); 565 initPrePostPoneDatePicker(); 566 //#pre-postPone-button 567 document.getElementById("cancel-ticket-button").style.display = "none"; 568 document.getElementById("pre-postPone-button").style.display = "flex"; 569 } else if (tabType === 2) { 570 window.cancelTicketModal.setBody(cancelTicketBody(tabType)); 571 contentTemplate = cancelTicketContent(); 572 const state = window.store?.getState(); 573 const { user } = state?.user || {}; 574 document.getElementById("modify-ticket-body-content").innerHTML = 575 renderTemplate(contentTemplate, { 576 ...window.ticketDetail.onward, 577 is_guest_user: !user, 578 }); 579 580 //refund_type set value to true if isCoupon is true else false 581 if (document.querySelector('input[name="refund_type"]')) { 582 document.querySelector( 583 'input[name="refund_type"][value="' + 584 (window.ticketDetail.onward.is_coupon_allowed && 585 !window.ticketDetail.onward.wallet_booking && 586 !window.ticketDetail.onward.default_cash_credit 587 ? "true" 588 : "false") + 589 '"]', 590 ).checked = true; 591 } 592 document.getElementById("cancel-ticket-button").style.display = "flex"; 593 document.getElementById("pre-postPone-button").style.display = "none"; 594 } 595} 596 597function showTabHeader() { 598 window.cancelTicketModal.showHeader(); 599 document.getElementById("modify-ticket-tabs").style.display = "flex"; 600 document.getElementById("modify-ticket-body-content").style.display = "none"; 601} 602 603function cancelTicketContent() { 604 const html = String.raw; 605 const htmlContent = html` 606 <div class="mobile-cancel-ticket-header" onclick="showTabHeader()"> 607 <svg 608 width="24" 609 height="24"
610 viewBox="0 0 24 24" 611 fill="none" 612 xmlns="http://www.w3.org/2000/svg" 613 > 614 <path 615 d="M20 11H7.83L13.42 5.41L12 4L4 12L12 20L13.41 18.59L7.83 13H20V11Z" 616 fill="black" 617 /> 618 </svg> 619 Cancellation 620 </div> 621 <div class="cancel-ticket-modal-header"> 622 <div class="cancel-ticket-modal-journey-container"> 623 <div class="cancel-ticket-modal-journey-separator-container-mobile"> 624 <div class="cancel-ticket-modal-journey-separator-dot"></div> 625 <div class="cancel-ticket-modal-journey-separator"></div> 626 <div class="cancel-ticket-modal-journey-separator-dot"></div> 627 </div> 628 <div class="cancel-ticket-modal-journey-details"> 629 <div class="cancel-ticket-modal-journey-point"> 630 <div class="cancel-ticket-modal-journey-time-date-container"> 631 <div class="cancel-ticket-modal-journey-time">{{dep_time}}</div> 632 <div class="cancel-ticket-modal-journey-date"> 633 {{dayjs(travel_date, 'DD/MM/YYYY').format('dddd D MMMM YYYY')}} 634 </div> 635 </div> 636 <div class="cancel-ticket-modal-journey-city">From {{origin}}</div> 637 </div> 638 <div class="cancel-ticket-modal-journey-duration-container"> 639 <div class="cancel-ticket-modal-journey-separator-dot"></div> 640 <div class="cancel-ticket-modal-journey-separator"></div> 641 <div class="cancel-ticket-modal-journey-duration">{{duration}}</div> 642 <div class="cancel-ticket-modal-journey-separator"></div> 643 <div class="cancel-ticket-modal-journey-separator-dot"></div> 644 </div> 645 <div class="cancel-ticket-modal-journey-point"> 646 <div class="cancel-ticket-modal-journey-time-date-container"> 647 <div class="cancel-ticket-modal-journey-time">{{arr_time}}</div> 648 <div class="cancel-ticket-modal-journey-date"> 649 {{dayjs(arr_date, 'DD/MM/YYYY').format('dddd D MMMM YYYY')}} 650 </div> 651 </div> 652 <div class="cancel-ticket-modal-journey-city"> 653 To {{destination}} 654 </div> 655 </div> 656 </div> 657 </div> 658 </div> 659 <div class="cancel-ticket-modal-pnr-date-container"> 660 <div class="cancel-ticket-modal-pnr"> 661 PNR: 662 <span class="cancel-ticket-modal-pnr-text">{{ticket_number}}</span> 663 </div> 664 <div class="cancel-ticket-modal-pnr"> 665 Booked on: 666 <span class="cancel-ticket-modal-pnr-text" 667 >{{dayjs(booked_date, 'YYYY-MM-DD').format('D MMMM YYYY')}}</span 668 > 669 </div> 670 </div> 671 <div class="cancel-ticket-modal-passenger-info"> 672 <div class="cancel-ticket-modal-passenger-info-header"> 673 Select passengers 674 </div> 675 <div class="cancel-ticket-modal-passenger-list"> 676 {{#each passenger_details}} 677 <div class="cancel-ticket-modal-passenger-item"> 678 <div class="cancel-ticket-modal-passenger-item-info"> 679 <input 680 type="checkbox" 681 id="passenger-{{seat_number}}" 682 name="passenger-{{seat_number}}" 683 class="cancel-ticket-modal-passenger-checkbox" 684 value="{{seat_number}}" 685 /> 686 <div class="cancel-ticket-modal-passenger-name-age"> 687 <div class="cancel-ticket-modal-passenger-name">{{name}}</div> 688 <div class="cancel-ticket-modal-passenger-age"> 689 {{gender}} , {{age}} years 690 </div> 691 </div> 692 </div> 693 <div class="cancel-ticket-modal-passenger-item-info"> 694 <div class="cancel-ticket-modal-passenger-seat-number"> 695 {{seat_number}} 696 </div> 697 </div> 698 </div> 699 {{/each}} 700 </div> 701 </div> 702 {{#if wallet_booking == false || wallet_booking == 'false'}} 703 <div class="cancel-ticket-modal-cancellation-typeContainer"> 704 <div class="cancel-ticket-modal-cancellation-type-title"> 705 Cancellation refund as : 706 </div> 707 <div class="cancel-ticket-modal-cancellation-type-list"> 708 {{#if is_coupon_allowed}} 709 <label 710 class="cancel-ticket-modal-cancellation-type-item" 711 for="refund-wallet" 712 > 713 <input 714 type="radio" 715 id="Coupon-refund" 716 name="refund_type" 717 value="true" 718 /> 719 Coupon 720 </label> 721 {{/if}} {{#if is_cash_credit}} 722 <label 723 class="cancel-ticket-modal-cancellation-type-item" 724 for="refund-to-source" 725 > 726 <input 727 type="radio" 728 id="refund-to-source" 729 name="refund_type" 730 value="false" 731 /> 732 Refund to source 733 </label> 734 {{/if}} 735 </div> 736 </div> 737 {{/if}} 738 <div class="cancel-ticket-modal-passenger-info-footer"> 739 <button 740 class="ticket-modal-passenger-info-footer-button" 741 onclick="reviewRefundDetails()" 742 id="review-refund-button" 743 > 744 Review refund details 745 </button> 746 </div> 747 `; 748 749 return htmlContent; 750} 751 752function prePostPoneTicketContent() { 753 const html = String.raw; 754 let htmlContent = html` 755 <div class="pre-postpone-ticket-modal-content"> 756 <div class="pre-postpone-ticket-modal-body"> 757 <div class="pre-postpone-ticket-body-form-content"> 758 <div class="pre-postpone-ticket-form-group"> 759 <label for="newTravelDate">Select new travel date</label> 760 </div> 761 <div class="pre-postpone-ticket-form-input-container"> 762 <div class="pre-postpone-ticket-form-input-wrapper"> 763 <input 764 type="text" 765 id="newTravelDate" 766 name="newTravelDate" 767 placeholder="Enter date of journey" 768 required 769 /> 770 </div> 771 <div class="pre-postpone-ticket-form-description-label"> 772 Date change charges : 773 <div id="date-change-charges">{{reschedule_percentage}}</div> 774 </div> 775 </div> 776 </div> 777 <div class="pre-postpone-ticket-info-container"> 778 <div class="pre-postpone-ticket-info-content"> 779 <div class="pre-postpone-ticket-info-header"> 780 <div class="pre-postpone-ticket-info-header-title"> 781 You are changing travel date 782 </div> 783 <div class="pre-postpone-ticket-info-header-subtitle"> 784 for following passenger 785 </div> 786 </div> 787 <div class="pre-postpone-ticket-info-passenger-list"> 788 {{#each passenger_details}} 789 <div class="pre-postpone-ticket-info-passenger-item"> 790 <div class="pre-postpone-ticket-info-passenger-name"> 791 {{name}} 792 </div> 793 <div class="pre-postpone-ticket-info-passenger-seat-number"> 794 {{title}},{{age}} years - Seat No: {{seat_number}} 795 </div> 796 </div> 797 {{/each}} 798 </div> 799 </div> 800 <div class="pre-postpone-ticket-info-footer"> 801 Note: Once you change the date for this ticket, you will not be able 802 to cancel or change date again. 803 </div> 804 </div> 805 </div> 806 <div class="pre-postpone-ticket-modal-footer"> 807 <button 808 class="pre-postpone-ticket-modal-footer-button" 809 onclick="prePostPone()" 810 id="pre-postpone-button" 811 > 812 Select service 813 </button> 814 </div> 815 </div> 816 `; 817 return htmlContent; 818} 819 820async function reviewRefundDetails() { 821 buttonLoading("#review-refund-button"); 822 const selectedPassengers = Array.from( 823 document.querySelectorAll( 824 ".cancel-ticket-modal-passenger-checkbox:checked", 825 ), 826 ).map((checkbox) => checkbox.value); 827 if (selectedPassengers.length === 0) { 828 message.error("Please select at least one passenger to cancel the ticket."); 829 buttonStopLoading("#review-refund-button"); 830 return; 831 } 832 let is_partial_cancelled = 833 window.ticketDetail.onward.passenger_details.length !== 834 selectedPassengers.length; 835 // Proceed with reviewing refund details for selected passengers 836 let contact_details = 837 window.ticketDetail.onward.email || window.ticketDetail.onward.phone || ""; 838 // user selected refund type 839 840 const refundType = document.querySelector( 841 'input[name="refund_type"]:checked', 842 )?.value; 843 try { 844 const { data } = await apiClient.post("api/do_cancel_ticket.json", { 845 partial_cancellation: is_partial_cancelled, 846 ticket_number: window.ticketDetail.onward.ticket_number, 847 contact_details: contact_details, 848 travel_date: window.ticketDetail.onward.travel_date, 849 seat_numbers: selectedPassengers.join(","), 850 });
851 if (data.code === 411) { 852 window.cancelTicketModal.hide(); 853 buttonStopLoading("#review-refund-button"); 854 setTimeout(() => { 855 message.error( 856 data.message || 857 "Unable to process cancellation. Please try again later.", 858 ); 859 }, 500); 860 return; 861 } else if (data) { 862 buttonStopLoading("#review-refund-button"); 863 contentTemplate = cancelTicketConfirmContent(); 864 document.getElementById("modify-ticket-body-content").innerHTML = 865 renderTemplate(contentTemplate, { 866 ...data, 867 refund_amount: data.refund_amount.toString(), 868 total_fare: data.total_fare.toString(), 869 total_cancellation_charge: data.total_cancellation_charge.toString(), 870 cancellation_percentage: data.cancellation_percentage.toString(), 871 refundType: refundType, 872 cancellation_policies: 873 window.ticketDetail?.onward?.cancellation_policies, 874 }); 875 } 876 } catch (error) { 877 buttonStopLoading("#review-refund-button"); 878 console.error("Error reviewing refund details:", error); 879 } 880} 881 882function toggleCancellationPolicyList() { 883 const policyListContainer = document.querySelector( 884 ".cancel-ticket-confirmation-modal .cancel-ticket-policy-list-container", 885 ); 886 const subtitle = document.querySelector( 887 ".cancel-ticket-confirmation-modal .cancel-ticket-confirmation-refund-subtitle", 888 ); 889 if (policyListContainer) { 890 policyListContainer.classList.toggle("active"); 891 } 892 if (subtitle) { 893 subtitle.classList.toggle("active"); 894 } 895} 896 897function cancelTicketConfirmContent() { 898 const html = String.raw; 899 let htmlContent = html` <div 900 class="mobile-cancel-ticket-header" 901 onclick="cancelTicket(2); showTicketTabContent(2);" 902 > 903 <svg 904 width="24" 905 height="24"
906 viewBox="0 0 24 24" 907 fill="none" 908 xmlns="http://www.w3.org/2000/svg" 909 > 910 <path 911 d="M20 11H7.83L13.42 5.41L12 4L4 12L12 20L13.41 18.59L7.83 13H20V11Z" 912 fill="black" 913 /> 914 </svg> 915 Review refund details 916 </div> 917 <div class="cancel-ticket-confirmation-modal"> 918 <div class="cancel-ticket-confirmation-refund-seat-container"> 919 <div class="cancel-ticket-confirmation-refund-container"> 920 <div class="cancel-ticket-confirmation-refund-title-container"> 921 <div class="cancel-ticket-confirmation-refund-title"> 922 Total refund amount 923 </div> 924 {{#if cancellation_policies && cancellation_policies.length > 0}} 925 <div 926 class="cancel-ticket-confirmation-refund-subtitle" 927 onclick="toggleCancellationPolicyList()" 928 > 929 View cancellation policy 930 <div class="cancellation-policy-icon"> 931 <svg 932 width="20" 933 height="20"
934 viewBox="0 0 20 20" 935 fill="none" 936 xmlns="http://www.w3.org/2000/svg" 937 > 938 <path 939 d="M14.1293 6.25C14.4787 6.25 14.6731 6.65427 14.4548 6.92708L10.3215 12.0931C10.1547 12.3016 9.83811 12.3015 9.67129 12.0931L5.53799 6.92708C5.31974 6.65427 5.51414 6.25 5.86351 6.25L14.1293 6.25Z" 940 fill="white" 941 /> 942 </svg> 943 </div> 944 </div> 945 {{else}} 946 <a 947 class="cancel-ticket-confirmation-refund-subtitle" 948 href="cancellation-policy" 949 target="_blank" 950 > 951 View cancellation policy 952 </a> 953 {{/if}} 954 </div> 955 <div class="cancel-ticket-confirmation-refund-amount"> 956 â¹ {{refund_amount}} 957 </div> 958 </div> 959 <div class="cancel-ticket-policy-list-container"> 960 {{#each cancellation_policies}} 961 <div class="cancel-ticket-policy-item"> 962 Between {{time_limit_from}} to {{time_limit_to}} from the station 963 departure time: {{percent}} % Cancellation Charges 964 </div> 965 {{/each}} 966 </div> 967 <div class="cancel-ticket-confirmation-seat-container"> 968 <div class="cancel-ticket-confirmation-seat-title"> 969 Seat to be cancelled : 970 <span class="cancel-ticket-confirmation-seat-number"> 971 <input 972 type="hidden" 973 id="cancel-seat-numbers" 974 value="{{seat_nos}}" 975 /> 976 {{seat_nos}} 977 </span> 978 </div> 979 </div> 980 </div> 981 <div class="cancel-ticket-modal-refund-info"> 982 <div class="cancel-ticket-modal-refund-info-header">Refund breakup</div> 983 <div class="cancel-ticket-modal-refund-info-list"> 984 <div class="cancel-ticket-modal-refund-info-item"> 985 <div class="cancel-ticket-modal-refund-info-item-label"> 986 Amount paid 987 </div> 988 <div class="cancel-ticket-modal-refund-info-item-value highlight"> 989 â¹ {{total_fare}} 990 </div> 991 </div> 992 <div class="cancel-ticket-modal-refund-info-item"> 993 <div class="cancel-ticket-modal-refund-info-item-label"> 994 Cancellation Charge 995 </div> 996 <div class="cancel-ticket-modal-refund-info-item-value"> 997 â¹ {{total_cancellation_charge}} 998 </div> 999 </div> 1000 <div class="cancel-ticket-modal-refund-info-item"> 1001 <div class="cancel-ticket-modal-refund-info-item-label"> 1002 Cancellation Percentage 1003 </div> 1004 <div class="cancel-ticket-modal-refund-info-item-value"> 1005 {{cancellation_percentage}}% 1006 </div> 1007 </div> 1008 <div class="cancel-ticket-modal-refund-info-item-separator"></div> 1009 <div class="cancel-ticket-modal-refund-info-item"> 1010 <div class="cancel-ticket-modal-refund-info-item-label"> 1011 Total refundable amount 1012 </div> 1013 <div class="cancel-ticket-modal-refund-info-item-value highlight"> 1014 â¹ {{refund_amount}} 1015 </div> 1016 </div> 1017 </div> 1018 </div> 1019 </div> 1020 <div class="cancel-ticket-modal-passenger-info-footer"> 1021 <input type="hidden" id="refund-type" value="{{refundType}}" /> 1022 <button 1023 class="ticket-modal-passenger-info-footer-button" 1024 onclick="confirmCancellation()" 1025 id="confirm-cancel-button" 1026 > 1027 Confirm cancellation 1028 </button> 1029 </div>`; 1030 return htmlContent; 1031} 1032 1033async function confirmCancellation() { 1034 buttonLoading("#confirm-cancel-button"); 1035 let seat_numbers = document.getElementById("cancel-seat-numbers").value; 1036 //remove the extra spaces 1037 seat_numbers = seat_numbers 1038 .split(",") 1039 .map((seat) => seat.trim()) 1040 .join(","); 1041 let is_partial_cancelled = 1042 window.ticketDetail.onward.passenger_details.length !== 1043 seat_numbers.split(",").length; 1044 const refundType = 1045 document.getElementById("refund-type")?.value == "true" ? true : false; 1046 try { 1047 const { data } = await apiClient.post("api/cms_booking_engine.json", { 1048 query: is_partial_cancelled ? "q14" : "q13", 1049 ticket_number: window.ticketDetail.onward.ticket_number, 1050 contact_details: 1051 window.ticketDetail.onward.email || 1052 window.ticketDetail.onward.phone || 1053 "", 1054 travel_date: window.ticketDetail.onward.travel_date, 1055 seat_numbers: seat_numbers, 1056 refund_type: window.ticketDetail.onward.wallet_booking 1057 ? "3" 1058 : refundType 1059 ? "1" 1060 : "2", 1061 });
1062 if (data) { 1063 buttonStopLoading("#confirm-cancel-button"); 1064 window.cancelTicketModal.hide(); 1065 if (data.code === 489 || data.code === "489") { 1066 message.error(data.message); 1067 return false; 1068 } 1069 setTimeout(() => { 1070 message.success( 1071 "Your ticket has been successfully cancelled. Refund will be processed shortly.", 1072 ); 1073 const state = window.store?.getState(); 1074 const { user } = state?.user || {}; 1075 setTimeout(() => { 1076 if (user) { 1077 if (is_partial_cancelled) { 1078 window.location.reload(); 1079 } else { 1080 locationRedirect("/my-bookings"); 1081 } 1082 } else { 1083 if (is_partial_cancelled) { 1084 window.location.reload(); 1085 } else { 1086 locationRedirect("/manage-booking"); 1087 } 1088 } 1089 }, 1000); 1090 }, 1000); 1091 // Optionally, refresh the ticket details or update the UI accordingly 1092 } 1093 } catch (error) { 1094 buttonStopLoading("#confirm-cancel-button"); 1095 console.error("Error confirming cancellation:", error); 1096 } 1097} 1098 1099function constructModifyTicketContent({ email = "", mobileNumber = "" }) { 1100 const html = String.raw; 1101 let htmlContent = html` 1102 <form class="modify-ticket-modal-form"> 1103 <div class="modify-ticket-modal-header"> 1104 <div class="modify-ticket-modal-title"> 1105 <div 1106 class="modify-ticket-modal-title-icon" 1107 onclick="window.modifyTicketModal.hide()" 1108 > 1109 <img 1110 src="${window.domain}/common/assets/images/arrow_back.svg" 1111 alt="Modify Ticket Icon" 1112 /> 1113 </div> 1114 <div class="modify-ticket-modal-title-text">Modify ticket</div> 1115 </div> 1116 <div class="modify-ticket-modal-description"> 1117 <ul> 1118 <li>You can change Boarding Address and Mobile No.</li> 1119 <li> 1120 After the Modification, please take the Print of the Ticket. 1121 </li> 1122 </ul> 1123 </div> 1124 </div> 1125 <div class="modify-ticket-modal-body"> 1126 <div class="modify_ticket_form_group_wrapper"> 1127 <div class="modify_ticket_form_group"> 1128 <label for="mobileNumber">PHONE NUMBER</label> 1129 <div class="mobile_number_input_container"> 1130 <div class="country_code_prefix">+91</div> 1131 <div class="mobile_number_input_wrapper"> 1132 <input 1133 type="text" 1134 id="mobileNumber" 1135 name="mobileNumber" 1136 placeholder="Enter mobile number" 1137 oninput="this.value = replaceStringWithEmpty(this.value)" 1138 minlength="10" 1139 maxlength="10" 1140 value="${mobileNumber}" 1141 /> 1142 </div> 1143 </div> 1144 </div> 1145 <div class="modify_ticket_form_group"> 1146 <label for="email">EMAIL</label> 1147 <input 1148 type="text" 1149 id="email" 1150 name="email" 1151 placeholder="Enter email" 1152 value="${email}" 1153 /> 1154 </div> 1155 </div> 1156 <div class="modify_ticket_form_group_wrapper"> 1157 <div class="modify_ticket_form_group"> 1158 <label for="boardingPoint">BOARDING Point</label> 1159 <div 1160 type="text" 1161 id="boardingPoint" 1162 name="boardingPoint" 1163 placeholder="Enter boarding point" 1164 ></div> 1165 </div> 1166 <div class="modify_ticket_form_group"> 1167 <label for="droppingPoint">DROPPING Point</label> 1168 <div 1169 type="text" 1170 id="droppingPoint" 1171 name="droppingPoint" 1172 placeholder="Enter dropping point" 1173 ></div> 1174 </div> 1175 </div> 1176 </div> 1177 <div class="modify-ticket-modal-footer"> 1178 <button 1179 id="modify-ticket-submit-button" 1180 class="modify-ticket-modal-footer-button-confirm" 1181 onclick="updateModifyTicket()" 1182 > 1183 Update ticket 1184 </button> 1185 </div> 1186 </form> 1187 `; 1188 return htmlContent; 1189} 1190 1191async function prePostPone() { 1192 buttonLoading("#pre-postpone-button"); 1193 try { 1194 const newTravelDate = document.getElementById("newTravelDate").value; 1195 if (!newTravelDate) { 1196 buttonStopLoading("#pre-postpone-button"); 1197 message.error("Please select new travel date"); 1198 return; 1199 } 1200 const response = await apiClient.post("/api/do_cancel_ticket.json", { 1201 partial_cancellation: false, 1202 ticket_number: window.ticketDetail.onward.ticket_number, 1203 contact_details: window.ticketDetail.onward.phone, 1204 travel_date: window.ticketDetail.onward.travel_date, 1205 pre_post_open_type: "pre_post", 1206 pre_post_now: true, 1207 pre_post_date: newTravelDate, 1208 });
1209 if (response.data.ticket_no) { 1210 const res = await apiClient.post("/api/cms_booking_engine.json", { 1211 query: "q16", 1212 ticket_number: response.data.ticket_no, 1213 pre_post_open_type: "pre_post", 1214 pre_post_now: true, 1215 pre_post_date: newTravelDate, 1216 }); 1217 if (res.status === 200 && res.data) { 1218 window.cancelTicketModal.hide(); 1219 message.success( 1220 `${res.data.refund_mode}, refund amount ${res.data.refund_amount} will be processed shortly.`, 1221 ); 1222 setTimeout(() => { 1223 let url = res.data.url_path; 1224 window.location.href = res.data.url_path; 1225 }, 1000); 1226 } else { 1227 window.cancelTicketModal.hide(); 1228 buttonStopLoading("#pre-postpone-button"); 1229 setTimeout(() => { 1230 message.error( 1231 res.data.message || 1232 "Could not process date change. Please try again later.", 1233 ); 1234 }, timeout); 1235 } 1236 } else { 1237 window.cancelTicketModal.hide(); 1238 buttonStopLoading("#pre-postpone-button"); 1239 setTimeout(() => { 1240 message.error( 1241 response.data.message || 1242 "Could not process date change. Please try again later.", 1243 ); 1244 }, 1000); 1245 } 1246 } catch (error) { 1247 buttonStopLoading("#pre-postpone-button"); 1248 console.error("Error in prePostPone:", error); 1249 } 1250 //search-results.html?from=16&fromCity=Bangalore&to=1938&toCity=Hyderabad&depart=2025-12-25&pre_postpone=true&res_id=322598 1251}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.