@qite/tide-booking-component 1.4.122 → 1.4.124

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (706) hide show
  1. package/.claude/settings.local.json +11 -0
  2. package/.husky/pre-commit +22 -1
  3. package/.prettierignore +32 -0
  4. package/.prettierrc +9 -9
  5. package/.vs/ProjectSettings.json +3 -3
  6. package/.vs/VSWorkspaceState.json +5 -5
  7. package/MEMORY.md +97 -0
  8. package/NEXTSTEPS.md +94 -0
  9. package/README.md +26 -24
  10. package/package.json +10 -1
  11. package/rollup.config.js +16 -16
  12. package/src/booking-product/components/age-select.tsx +35 -35
  13. package/src/booking-product/components/amount-input.tsx +51 -51
  14. package/src/booking-product/components/date-range-picker/calendar-day.tsx +46 -46
  15. package/src/booking-product/components/date-range-picker/calendar.tsx +155 -155
  16. package/src/booking-product/components/date-range-picker/index.tsx +185 -185
  17. package/src/booking-product/components/dates.tsx +153 -153
  18. package/src/booking-product/components/footer.tsx +54 -54
  19. package/src/booking-product/components/header.tsx +58 -57
  20. package/src/booking-product/components/list-view.tsx +54 -54
  21. package/src/booking-product/components/product.tsx +447 -379
  22. package/src/booking-product/components/rating.tsx +21 -21
  23. package/src/booking-product/components/rooms.tsx +192 -171
  24. package/src/booking-product/constants.ts +1 -1
  25. package/src/booking-product/index.tsx +21 -21
  26. package/src/booking-product/settings-context.ts +16 -16
  27. package/src/booking-product/types.ts +68 -30
  28. package/src/booking-product/utils/api.ts +26 -26
  29. package/src/booking-product/utils/price.ts +28 -28
  30. package/src/booking-wizard/api-settings-slice.ts +24 -24
  31. package/src/booking-wizard/components/labeled-input.tsx +56 -56
  32. package/src/booking-wizard/components/labeled-select.tsx +54 -54
  33. package/src/booking-wizard/components/message.tsx +21 -21
  34. package/src/booking-wizard/components/multi-range-filter.tsx +99 -99
  35. package/src/booking-wizard/components/phone-input.tsx +146 -146
  36. package/src/booking-wizard/components/print-offer-button.tsx +53 -53
  37. package/src/booking-wizard/components/step-indicator.tsx +36 -36
  38. package/src/booking-wizard/components/step-route.tsx +51 -51
  39. package/src/booking-wizard/declarations.d.ts +4 -4
  40. package/src/booking-wizard/features/booking/api.ts +44 -44
  41. package/src/booking-wizard/features/booking/booking-self-contained.tsx +364 -318
  42. package/src/booking-wizard/features/booking/booking-slice.ts +634 -634
  43. package/src/booking-wizard/features/booking/booking.tsx +388 -344
  44. package/src/booking-wizard/features/booking/constants.ts +16 -16
  45. package/src/booking-wizard/features/booking/selectors.ts +411 -411
  46. package/src/booking-wizard/features/confirmation/confirmation.tsx +46 -46
  47. package/src/booking-wizard/features/error/error.tsx +71 -71
  48. package/src/booking-wizard/features/flight-options/flight-filter.tsx +371 -371
  49. package/src/booking-wizard/features/flight-options/flight-option-flight.tsx +354 -354
  50. package/src/booking-wizard/features/flight-options/flight-option-modal.tsx +211 -211
  51. package/src/booking-wizard/features/flight-options/flight-option.tsx +57 -57
  52. package/src/booking-wizard/features/flight-options/flight-utils.ts +423 -423
  53. package/src/booking-wizard/features/flight-options/index.tsx +170 -170
  54. package/src/booking-wizard/features/price-details/price-details-api.ts +20 -20
  55. package/src/booking-wizard/features/price-details/price-details-slice.ts +79 -79
  56. package/src/booking-wizard/features/price-details/selectors.ts +118 -118
  57. package/src/booking-wizard/features/price-details/util.ts +115 -115
  58. package/src/booking-wizard/features/product-options/no-options.tsx +18 -18
  59. package/src/booking-wizard/features/product-options/none-option.tsx +73 -73
  60. package/src/booking-wizard/features/product-options/option-booking-airline-group.tsx +53 -53
  61. package/src/booking-wizard/features/product-options/option-booking-group.tsx +152 -152
  62. package/src/booking-wizard/features/product-options/option-item.tsx +236 -236
  63. package/src/booking-wizard/features/product-options/option-pax-card.tsx +159 -159
  64. package/src/booking-wizard/features/product-options/option-pax-group.tsx +122 -122
  65. package/src/booking-wizard/features/product-options/option-room.tsx +226 -226
  66. package/src/booking-wizard/features/product-options/option-unit-group.tsx +138 -138
  67. package/src/booking-wizard/features/product-options/option-units-card.tsx +148 -148
  68. package/src/booking-wizard/features/product-options/options-form.tsx +382 -382
  69. package/src/booking-wizard/features/room-options/index.tsx +132 -132
  70. package/src/booking-wizard/features/room-options/room-utils.ts +154 -154
  71. package/src/booking-wizard/features/room-options/room.tsx +123 -123
  72. package/src/booking-wizard/features/room-options/traveler-rooms.tsx +64 -64
  73. package/src/booking-wizard/features/sidebar/index.tsx +89 -89
  74. package/src/booking-wizard/features/sidebar/sidebar-flight.tsx +66 -66
  75. package/src/booking-wizard/features/sidebar/sidebar-util.ts +143 -143
  76. package/src/booking-wizard/features/sidebar/sidebar.tsx +349 -349
  77. package/src/booking-wizard/features/summary/summary-booking-option-pax.tsx +23 -23
  78. package/src/booking-wizard/features/summary/summary-booking-option-unit.tsx +23 -23
  79. package/src/booking-wizard/features/summary/summary-flight.tsx +36 -36
  80. package/src/booking-wizard/features/summary/summary-per-booking-option-group.tsx +60 -60
  81. package/src/booking-wizard/features/summary/summary-per-pax-option-group.tsx +56 -56
  82. package/src/booking-wizard/features/summary/summary-per-unit-option-group.tsx +58 -58
  83. package/src/booking-wizard/features/summary/summary-slice.ts +27 -27
  84. package/src/booking-wizard/features/summary/summary.tsx +19 -3
  85. package/src/booking-wizard/features/travelers-form/controls/gender-control.tsx +60 -60
  86. package/src/booking-wizard/features/travelers-form/travelers-form-slice.ts +157 -157
  87. package/src/booking-wizard/features/travelers-form/travelers-form-util.ts +10 -10
  88. package/src/booking-wizard/features/travelers-form/travelers-form.tsx +181 -175
  89. package/src/booking-wizard/features/travelers-form/type-ahead-input.tsx +85 -85
  90. package/src/booking-wizard/features/travelers-form/validate-form.ts +178 -178
  91. package/src/booking-wizard/index.tsx +27 -27
  92. package/src/booking-wizard/settings-context.ts +64 -64
  93. package/src/booking-wizard/store.ts +26 -26
  94. package/src/booking-wizard/types.ts +17 -0
  95. package/src/booking-wizard/use-offer-printer.ts +108 -108
  96. package/src/content/components/LanguageSwitcher.tsx +158 -158
  97. package/src/content/components/accordion.tsx +30 -30
  98. package/src/content/components/breadcrumb.tsx +67 -67
  99. package/src/content/components/contact.tsx +210 -210
  100. package/src/content/components/faq.tsx +42 -42
  101. package/src/content/components/gallery.tsx +153 -153
  102. package/src/content/components/image-with-text.tsx +119 -119
  103. package/src/content/components/login.tsx +161 -161
  104. package/src/content/components/personal-contact-form.tsx +809 -809
  105. package/src/content/components/slider.tsx +237 -237
  106. package/src/content/error/error.tsx +27 -27
  107. package/src/content/featured-trips/featured-trip-card.tsx +48 -48
  108. package/src/content/featured-trips/index.tsx +19 -19
  109. package/src/content/featured-trips/types.ts +13 -13
  110. package/src/content/features/content-page/content-page-self-contained.tsx +895 -895
  111. package/src/content/footer/index.tsx +159 -159
  112. package/src/content/footer/types.ts +36 -36
  113. package/src/content/header/index.tsx +43 -43
  114. package/src/content/header/types.ts +26 -26
  115. package/src/content/image-card-grid/index.tsx +34 -34
  116. package/src/content/image-card-grid/types.ts +13 -13
  117. package/src/content/image-with-text-section/card.tsx +58 -58
  118. package/src/content/image-with-text-section/index.tsx +22 -22
  119. package/src/content/image-with-text-section/types.ts +20 -20
  120. package/src/content/login/confirm-component.tsx +149 -149
  121. package/src/content/login/index.tsx +70 -70
  122. package/src/content/login/login-component.tsx +159 -159
  123. package/src/content/login/login-services.ts +109 -109
  124. package/src/content/login/reset-password-component.tsx +191 -191
  125. package/src/content/login/types.ts +29 -29
  126. package/src/content/navbar/index.tsx +354 -354
  127. package/src/content/navbar/placeholderData.tsx +173 -173
  128. package/src/content/navbar/types.ts +43 -43
  129. package/src/index.ts +46 -46
  130. package/src/qsm/components/QSMContainer/qsm-container.tsx +671 -671
  131. package/src/qsm/components/date-picker/index.tsx +152 -152
  132. package/src/qsm/components/date-range-picker/calendar-day.tsx +49 -49
  133. package/src/qsm/components/date-range-picker/calendar.tsx +211 -211
  134. package/src/qsm/components/date-range-picker/index.tsx +404 -404
  135. package/src/qsm/components/double-search-input-group/index.tsx +78 -78
  136. package/src/qsm/components/item-picker/index.tsx +65 -65
  137. package/src/qsm/components/mobile-filter-modal/index.tsx +321 -321
  138. package/src/qsm/components/search-input/index.tsx +91 -91
  139. package/src/qsm/components/search-input-group/index.tsx +209 -209
  140. package/src/qsm/components/travel-class-picker/index.tsx +28 -28
  141. package/src/qsm/components/travel-input/index.tsx +241 -241
  142. package/src/qsm/components/travel-input-group/index.tsx +114 -114
  143. package/src/qsm/components/travel-nationality-picker/index.tsx +28 -28
  144. package/src/qsm/components/travel-type-picker/index.tsx +28 -28
  145. package/src/qsm/index.tsx +26 -26
  146. package/src/qsm/qsm-configuration-context.ts +31 -31
  147. package/src/qsm/store/qsm-slice.ts +282 -282
  148. package/src/qsm/store/qsm-store.ts +13 -13
  149. package/src/qsm/types.ts +110 -110
  150. package/src/search-results/components/book-packaging-entry/index.tsx +266 -266
  151. package/src/search-results/components/book-packaging-entry/wl-sidebar.tsx +173 -173
  152. package/src/search-results/components/excursions/day-by-day-excursions.tsx +168 -168
  153. package/src/search-results/components/excursions/excursion-details.tsx +340 -340
  154. package/src/search-results/components/excursions/excursion-results.tsx +186 -186
  155. package/src/search-results/components/filters/filters.tsx +229 -229
  156. package/src/search-results/components/filters/flight-filters.tsx +671 -671
  157. package/src/search-results/components/flight/flight-banner.tsx +35 -35
  158. package/src/search-results/components/flight/flight-card.tsx +38 -38
  159. package/src/search-results/components/flight/flight-leg.tsx +61 -61
  160. package/src/search-results/components/flight/flight-path.tsx +23 -23
  161. package/src/search-results/components/flight/flight-results.tsx +208 -208
  162. package/src/search-results/components/flight/flight-search-context/index.tsx +628 -628
  163. package/src/search-results/components/flight/flight-selection/independent-flight-option.tsx +168 -168
  164. package/src/search-results/components/flight/flight-selection/independent-flight-selection.tsx +184 -184
  165. package/src/search-results/components/flight/flight-selection/index.tsx +19 -19
  166. package/src/search-results/components/flight/flight-selection/paired-flight-option.tsx +255 -255
  167. package/src/search-results/components/flight/flight-selection/paired-flight-selection.tsx +38 -38
  168. package/src/search-results/components/group-tour/group-tour-card.tsx +105 -105
  169. package/src/search-results/components/group-tour/group-tour-results.tsx +62 -62
  170. package/src/search-results/components/hotel/hotel-accommodation-results.tsx +244 -244
  171. package/src/search-results/components/hotel/hotel-card.tsx +110 -110
  172. package/src/search-results/components/item-picker/index.tsx +81 -81
  173. package/src/search-results/components/itinerary/full-itinerary.tsx +374 -374
  174. package/src/search-results/components/itinerary/index.tsx +437 -437
  175. package/src/search-results/components/multi-range-filter.tsx +104 -104
  176. package/src/search-results/components/round-trip/round-trip-results.tsx +199 -199
  177. package/src/search-results/components/search-results-container/flight-search-results.tsx +137 -137
  178. package/src/search-results/components/search-results-container/search-results-container.tsx +1764 -1764
  179. package/src/search-results/components/spinner/spinner.tsx +24 -24
  180. package/src/search-results/components/tab-views/index.tsx +53 -53
  181. package/src/search-results/features/flights/flight-search-results-self-contained.tsx +294 -294
  182. package/src/search-results/features/hotels/hotel-flight-search-results-self-contained.tsx +143 -143
  183. package/src/search-results/features/hotels/hotel-search-results-self-contained.tsx +220 -220
  184. package/src/search-results/features/roundtrips/roundtrip-search-results-self-contained.tsx +65 -65
  185. package/src/search-results/index.tsx +25 -25
  186. package/src/search-results/search-results-configuration-context.ts +6 -6
  187. package/src/search-results/store/search-results-selectors.ts +84 -84
  188. package/src/search-results/store/search-results-slice.ts +351 -351
  189. package/src/search-results/store/search-results-store.ts +13 -13
  190. package/src/search-results/types.ts +266 -266
  191. package/src/search-results/utils/flight-utils.ts +98 -98
  192. package/src/search-results/utils/packaging-utils.ts +75 -75
  193. package/src/search-results/utils/query-utils.ts +153 -153
  194. package/src/search-results/utils/search-results-utils.ts +538 -538
  195. package/src/shared/booking/booking-panel.tsx +25 -25
  196. package/src/shared/booking/product-card.tsx +23 -23
  197. package/src/shared/booking/shared-confirmation.tsx +105 -105
  198. package/src/shared/booking/shared-sidebar.tsx +432 -432
  199. package/src/shared/booking/step-indicators.tsx +30 -30
  200. package/src/shared/booking/summary.tsx +382 -382
  201. package/src/shared/booking/travelers-form.tsx +874 -874
  202. package/src/shared/components/flyin/accommodation-flyin.tsx +424 -424
  203. package/src/shared/components/flyin/flights-flyin.tsx +508 -508
  204. package/src/shared/components/flyin/flyin.tsx +241 -241
  205. package/src/shared/components/flyin/group-tour-flyin.tsx +294 -294
  206. package/src/shared/components/flyin/packaging-flights-flyin.tsx +171 -171
  207. package/src/shared/components/icon.tsx +1114 -1114
  208. package/src/shared/components/loader.tsx +16 -16
  209. package/src/shared/translations/ar-SA.json +398 -398
  210. package/src/shared/translations/da-DK.json +398 -398
  211. package/src/shared/translations/de-DE.json +398 -398
  212. package/src/shared/translations/en-GB.json +402 -402
  213. package/src/shared/translations/es-ES.json +398 -398
  214. package/src/shared/translations/fr-BE.json +402 -402
  215. package/src/shared/translations/fr-FR.json +398 -398
  216. package/src/shared/translations/is-IS.json +398 -398
  217. package/src/shared/translations/it-IT.json +398 -398
  218. package/src/shared/translations/ja-JP.json +398 -398
  219. package/src/shared/translations/nl-BE.json +402 -402
  220. package/src/shared/translations/nl-NL.json +398 -398
  221. package/src/shared/translations/no-NO.json +398 -398
  222. package/src/shared/translations/pl-PL.json +398 -398
  223. package/src/shared/translations/pt-PT.json +398 -398
  224. package/src/shared/translations/sv-SE.json +398 -398
  225. package/src/shared/types.ts +31 -31
  226. package/src/shared/utils/booking-summary.tsx +57 -57
  227. package/src/shared/utils/class-util.ts +7 -7
  228. package/src/shared/utils/localization-util.ts +316 -301
  229. package/src/shared/utils/query-string-util.ts +91 -91
  230. package/src/shared/utils/tide-api-utils.ts +42 -42
  231. package/src/shared/utils/use-media-query-util.ts +19 -19
  232. package/styles/abstracts/_mixins.scss +74 -74
  233. package/styles/abstracts/_variables.scss +57 -57
  234. package/styles/base/_fonts.scss +2 -2
  235. package/styles/base/_normalize.scss +227 -227
  236. package/styles/base/_typography.scss +35 -35
  237. package/styles/booking-joker-variables.scss +596 -596
  238. package/styles/booking-product-variables.scss +330 -330
  239. package/styles/booking-product.scss +438 -438
  240. package/styles/booking-qsm-variables.scss +501 -501
  241. package/styles/booking-qsm.scss +52 -52
  242. package/styles/booking-search-results-variables.scss +728 -728
  243. package/styles/booking-search-results.scss +53 -53
  244. package/styles/booking-wizard-variables.scss +603 -603
  245. package/styles/booking-wizard.scss +61 -61
  246. package/styles/components/_accordion.scss +67 -67
  247. package/styles/components/_animations.scss +39 -39
  248. package/styles/components/_base.scss +107 -107
  249. package/styles/components/_booking.scss +906 -906
  250. package/styles/components/_breadcrumb.scss +92 -92
  251. package/styles/components/_button.scss +238 -238
  252. package/styles/components/_checkbox.scss +230 -230
  253. package/styles/components/_contact.scss +255 -239
  254. package/styles/components/_content.scss +336 -336
  255. package/styles/components/_cta.scss +238 -238
  256. package/styles/components/_date-list.scss +41 -41
  257. package/styles/components/_date-range-picker.scss +223 -223
  258. package/styles/components/_decrement-increment.scss +35 -35
  259. package/styles/components/_dropdown.scss +77 -77
  260. package/styles/components/_error.scss +50 -50
  261. package/styles/components/_faq.scss +30 -30
  262. package/styles/components/_flight-option.scss +1432 -1432
  263. package/styles/components/_flyin.scss +830 -830
  264. package/styles/components/_footer.scss +135 -135
  265. package/styles/components/_form.scss +1693 -1693
  266. package/styles/components/_gallery.scss +317 -317
  267. package/styles/components/_header.scss +113 -113
  268. package/styles/components/_image-with-text.scss +206 -206
  269. package/styles/components/_img-slider.scss +175 -175
  270. package/styles/components/_info-message.scss +75 -75
  271. package/styles/components/_input.scss +35 -35
  272. package/styles/components/_list.scss +185 -185
  273. package/styles/components/_loader.scss +152 -152
  274. package/styles/components/_login.scss +140 -140
  275. package/styles/components/_mixins.scss +579 -579
  276. package/styles/components/_navbar.scss +765 -765
  277. package/styles/components/_passenger-picker.scss +306 -306
  278. package/styles/components/_phone-input.scss +8 -8
  279. package/styles/components/_placeholders.scss +165 -165
  280. package/styles/components/_pricing-summary.scss +163 -163
  281. package/styles/components/_qsm.scss +17 -17
  282. package/styles/components/_radiobutton.scss +170 -170
  283. package/styles/components/_search.scss +2089 -2089
  284. package/styles/components/_select-wrapper.scss +81 -81
  285. package/styles/components/_slider.scss +128 -128
  286. package/styles/components/_spinner.scss +29 -29
  287. package/styles/components/_step-indicators.scss +187 -187
  288. package/styles/components/_table.scss +81 -81
  289. package/styles/components/_tree.scss +648 -648
  290. package/styles/components/_typeahead.scss +275 -275
  291. package/styles/components/_variables.scss +89 -89
  292. package/styles/content-blocks-variables.scss +507 -507
  293. package/styles/content-blocks.scss +65 -65
  294. package/styles/font.scss +2 -2
  295. package/styles/qsm/_calendar.scss +274 -274
  296. package/styles/qsm/_qsm.scss +1097 -1097
  297. package/styles/search.scss +1200 -1200
  298. package/tsconfig.json +24 -24
  299. package/build/build-cjs/index.js +0 -57386
  300. package/build/build-cjs/src/booking-product/components/age-select.d.ts +0 -8
  301. package/build/build-cjs/src/booking-product/components/amount-input.d.ts +0 -10
  302. package/build/build-cjs/src/booking-product/components/date-range-picker/calendar-day.d.ts +0 -13
  303. package/build/build-cjs/src/booking-product/components/date-range-picker/calendar.d.ts +0 -19
  304. package/build/build-cjs/src/booking-product/components/date-range-picker/index.d.ts +0 -22
  305. package/build/build-cjs/src/booking-product/components/dates.d.ts +0 -14
  306. package/build/build-cjs/src/booking-product/components/footer.d.ts +0 -10
  307. package/build/build-cjs/src/booking-product/components/header.d.ts +0 -11
  308. package/build/build-cjs/src/booking-product/components/list-view.d.ts +0 -8
  309. package/build/build-cjs/src/booking-product/components/product.d.ts +0 -9
  310. package/build/build-cjs/src/booking-product/components/rating.d.ts +0 -6
  311. package/build/build-cjs/src/booking-product/components/rooms.d.ts +0 -10
  312. package/build/build-cjs/src/booking-product/constants.d.ts +0 -1
  313. package/build/build-cjs/src/booking-product/index.d.ts +0 -10
  314. package/build/build-cjs/src/booking-product/settings-context.d.ts +0 -5
  315. package/build/build-cjs/src/booking-product/types.d.ts +0 -27
  316. package/build/build-cjs/src/booking-product/utils/api.d.ts +0 -16
  317. package/build/build-cjs/src/booking-product/utils/price.d.ts +0 -10
  318. package/build/build-cjs/src/booking-wizard/api-settings-slice.d.ts +0 -5
  319. package/build/build-cjs/src/booking-wizard/components/labeled-input.d.ts +0 -18
  320. package/build/build-cjs/src/booking-wizard/components/labeled-select.d.ts +0 -21
  321. package/build/build-cjs/src/booking-wizard/components/message.d.ts +0 -9
  322. package/build/build-cjs/src/booking-wizard/components/multi-range-filter.d.ts +0 -11
  323. package/build/build-cjs/src/booking-wizard/components/phone-input.d.ts +0 -17
  324. package/build/build-cjs/src/booking-wizard/components/print-offer-button.d.ts +0 -17
  325. package/build/build-cjs/src/booking-wizard/components/step-indicator.d.ts +0 -6
  326. package/build/build-cjs/src/booking-wizard/components/step-route.d.ts +0 -8
  327. package/build/build-cjs/src/booking-wizard/features/booking/api.d.ts +0 -30
  328. package/build/build-cjs/src/booking-wizard/features/booking/booking-self-contained.d.ts +0 -8
  329. package/build/build-cjs/src/booking-wizard/features/booking/booking-slice.d.ts +0 -121
  330. package/build/build-cjs/src/booking-wizard/features/booking/booking.d.ts +0 -8
  331. package/build/build-cjs/src/booking-wizard/features/booking/constants.d.ts +0 -15
  332. package/build/build-cjs/src/booking-wizard/features/booking/selectors.d.ts +0 -803
  333. package/build/build-cjs/src/booking-wizard/features/confirmation/confirmation.d.ts +0 -4
  334. package/build/build-cjs/src/booking-wizard/features/error/error.d.ts +0 -4
  335. package/build/build-cjs/src/booking-wizard/features/flight-options/flight-filter.d.ts +0 -9
  336. package/build/build-cjs/src/booking-wizard/features/flight-options/flight-option-flight.d.ts +0 -8
  337. package/build/build-cjs/src/booking-wizard/features/flight-options/flight-option-modal.d.ts +0 -3
  338. package/build/build-cjs/src/booking-wizard/features/flight-options/flight-option.d.ts +0 -10
  339. package/build/build-cjs/src/booking-wizard/features/flight-options/flight-utils.d.ts +0 -13
  340. package/build/build-cjs/src/booking-wizard/features/flight-options/index.d.ts +0 -4
  341. package/build/build-cjs/src/booking-wizard/features/price-details/price-details-api.d.ts +0 -11
  342. package/build/build-cjs/src/booking-wizard/features/price-details/price-details-slice.d.ts +0 -28
  343. package/build/build-cjs/src/booking-wizard/features/price-details/selectors.d.ts +0 -393
  344. package/build/build-cjs/src/booking-wizard/features/price-details/util.d.ts +0 -2
  345. package/build/build-cjs/src/booking-wizard/features/product-options/no-options.d.ts +0 -3
  346. package/build/build-cjs/src/booking-wizard/features/product-options/none-option.d.ts +0 -9
  347. package/build/build-cjs/src/booking-wizard/features/product-options/option-booking-airline-group.d.ts +0 -8
  348. package/build/build-cjs/src/booking-wizard/features/product-options/option-booking-group.d.ts +0 -12
  349. package/build/build-cjs/src/booking-wizard/features/product-options/option-item.d.ts +0 -11
  350. package/build/build-cjs/src/booking-wizard/features/product-options/option-pax-card.d.ts +0 -10
  351. package/build/build-cjs/src/booking-wizard/features/product-options/option-pax-group.d.ts +0 -13
  352. package/build/build-cjs/src/booking-wizard/features/product-options/option-room.d.ts +0 -11
  353. package/build/build-cjs/src/booking-wizard/features/product-options/option-unit-group.d.ts +0 -13
  354. package/build/build-cjs/src/booking-wizard/features/product-options/option-units-card.d.ts +0 -9
  355. package/build/build-cjs/src/booking-wizard/features/product-options/options-form.d.ts +0 -4
  356. package/build/build-cjs/src/booking-wizard/features/room-options/index.d.ts +0 -4
  357. package/build/build-cjs/src/booking-wizard/features/room-options/room-utils.d.ts +0 -22
  358. package/build/build-cjs/src/booking-wizard/features/room-options/room.d.ts +0 -12
  359. package/build/build-cjs/src/booking-wizard/features/room-options/traveler-rooms.d.ts +0 -9
  360. package/build/build-cjs/src/booking-wizard/features/sidebar/index.d.ts +0 -7
  361. package/build/build-cjs/src/booking-wizard/features/sidebar/sidebar-flight.d.ts +0 -9
  362. package/build/build-cjs/src/booking-wizard/features/sidebar/sidebar-util.d.ts +0 -16
  363. package/build/build-cjs/src/booking-wizard/features/sidebar/sidebar.d.ts +0 -0
  364. package/build/build-cjs/src/booking-wizard/features/summary/summary-booking-option-pax.d.ts +0 -7
  365. package/build/build-cjs/src/booking-wizard/features/summary/summary-booking-option-unit.d.ts +0 -7
  366. package/build/build-cjs/src/booking-wizard/features/summary/summary-flight.d.ts +0 -8
  367. package/build/build-cjs/src/booking-wizard/features/summary/summary-per-booking-option-group.d.ts +0 -7
  368. package/build/build-cjs/src/booking-wizard/features/summary/summary-per-pax-option-group.d.ts +0 -7
  369. package/build/build-cjs/src/booking-wizard/features/summary/summary-per-unit-option-group.d.ts +0 -7
  370. package/build/build-cjs/src/booking-wizard/features/summary/summary-slice.d.ts +0 -8
  371. package/build/build-cjs/src/booking-wizard/features/summary/summary.d.ts +0 -4
  372. package/build/build-cjs/src/booking-wizard/features/travelers-form/controls/gender-control.d.ts +0 -5
  373. package/build/build-cjs/src/booking-wizard/features/travelers-form/travelers-form-slice.d.ts +0 -104
  374. package/build/build-cjs/src/booking-wizard/features/travelers-form/travelers-form-util.d.ts +0 -7
  375. package/build/build-cjs/src/booking-wizard/features/travelers-form/travelers-form.d.ts +0 -3
  376. package/build/build-cjs/src/booking-wizard/features/travelers-form/type-ahead-input.d.ts +0 -16
  377. package/build/build-cjs/src/booking-wizard/features/travelers-form/validate-form.d.ts +0 -11
  378. package/build/build-cjs/src/booking-wizard/index.d.ts +0 -12
  379. package/build/build-cjs/src/booking-wizard/settings-context.d.ts +0 -5
  380. package/build/build-cjs/src/booking-wizard/store.d.ts +0 -44
  381. package/build/build-cjs/src/booking-wizard/types.d.ts +0 -301
  382. package/build/build-cjs/src/booking-wizard/use-offer-printer.d.ts +0 -13
  383. package/build/build-cjs/src/content/components/LanguageSwitcher.d.ts +0 -12
  384. package/build/build-cjs/src/content/components/accordion.d.ts +0 -9
  385. package/build/build-cjs/src/content/components/breadcrumb.d.ts +0 -14
  386. package/build/build-cjs/src/content/components/contact.d.ts +0 -3
  387. package/build/build-cjs/src/content/components/faq.d.ts +0 -11
  388. package/build/build-cjs/src/content/components/gallery.d.ts +0 -13
  389. package/build/build-cjs/src/content/components/image-with-text.d.ts +0 -29
  390. package/build/build-cjs/src/content/components/login.d.ts +0 -3
  391. package/build/build-cjs/src/content/components/personal-contact-form.d.ts +0 -3
  392. package/build/build-cjs/src/content/components/slider.d.ts +0 -10
  393. package/build/build-cjs/src/content/error/error.d.ts +0 -6
  394. package/build/build-cjs/src/content/featured-trips/featured-trip-card.d.ts +0 -4
  395. package/build/build-cjs/src/content/featured-trips/index.d.ts +0 -4
  396. package/build/build-cjs/src/content/featured-trips/types.d.ts +0 -12
  397. package/build/build-cjs/src/content/features/content-page/content-page-self-contained.d.ts +0 -6
  398. package/build/build-cjs/src/content/footer/index.d.ts +0 -4
  399. package/build/build-cjs/src/content/footer/types.d.ts +0 -27
  400. package/build/build-cjs/src/content/header/index.d.ts +0 -4
  401. package/build/build-cjs/src/content/header/types.d.ts +0 -25
  402. package/build/build-cjs/src/content/image-card-grid/index.d.ts +0 -4
  403. package/build/build-cjs/src/content/image-card-grid/types.d.ts +0 -12
  404. package/build/build-cjs/src/content/image-with-text-section/card.d.ts +0 -4
  405. package/build/build-cjs/src/content/image-with-text-section/index.d.ts +0 -4
  406. package/build/build-cjs/src/content/image-with-text-section/types.d.ts +0 -19
  407. package/build/build-cjs/src/content/login/confirm-component.d.ts +0 -4
  408. package/build/build-cjs/src/content/login/index.d.ts +0 -4
  409. package/build/build-cjs/src/content/login/login-component.d.ts +0 -4
  410. package/build/build-cjs/src/content/login/login-services.d.ts +0 -11
  411. package/build/build-cjs/src/content/login/reset-password-component.d.ts +0 -4
  412. package/build/build-cjs/src/content/login/types.d.ts +0 -24
  413. package/build/build-cjs/src/content/navbar/index.d.ts +0 -4
  414. package/build/build-cjs/src/content/navbar/placeholderData.d.ts +0 -12
  415. package/build/build-cjs/src/content/navbar/types.d.ts +0 -32
  416. package/build/build-cjs/src/index.d.ts +0 -44
  417. package/build/build-cjs/src/qsm/components/QSMContainer/qsm-container.d.ts +0 -3
  418. package/build/build-cjs/src/qsm/components/date-picker/index.d.ts +0 -3
  419. package/build/build-cjs/src/qsm/components/date-range-picker/calendar-day.d.ts +0 -12
  420. package/build/build-cjs/src/qsm/components/date-range-picker/calendar.d.ts +0 -24
  421. package/build/build-cjs/src/qsm/components/date-range-picker/index.d.ts +0 -10
  422. package/build/build-cjs/src/qsm/components/double-search-input-group/index.d.ts +0 -8
  423. package/build/build-cjs/src/qsm/components/item-picker/index.d.ts +0 -13
  424. package/build/build-cjs/src/qsm/components/mobile-filter-modal/index.d.ts +0 -3
  425. package/build/build-cjs/src/qsm/components/search-input/index.d.ts +0 -15
  426. package/build/build-cjs/src/qsm/components/search-input-group/index.d.ts +0 -13
  427. package/build/build-cjs/src/qsm/components/travel-class-picker/index.d.ts +0 -3
  428. package/build/build-cjs/src/qsm/components/travel-input/index.d.ts +0 -3
  429. package/build/build-cjs/src/qsm/components/travel-input-group/index.d.ts +0 -3
  430. package/build/build-cjs/src/qsm/components/travel-nationality-picker/index.d.ts +0 -3
  431. package/build/build-cjs/src/qsm/components/travel-type-picker/index.d.ts +0 -3
  432. package/build/build-cjs/src/qsm/index.d.ts +0 -7
  433. package/build/build-cjs/src/qsm/qsm-configuration-context.d.ts +0 -4
  434. package/build/build-cjs/src/qsm/store/qsm-slice.d.ts +0 -127
  435. package/build/build-cjs/src/qsm/store/qsm-store.d.ts +0 -23
  436. package/build/build-cjs/src/qsm/types.d.ts +0 -82
  437. package/build/build-cjs/src/search-results/components/book-packaging-entry/index.d.ts +0 -9
  438. package/build/build-cjs/src/search-results/components/book-packaging-entry/wl-sidebar.d.ts +0 -9
  439. package/build/build-cjs/src/search-results/components/excursions/day-by-day-excursions.d.ts +0 -4
  440. package/build/build-cjs/src/search-results/components/excursions/excursion-details.d.ts +0 -3
  441. package/build/build-cjs/src/search-results/components/excursions/excursion-results.d.ts +0 -8
  442. package/build/build-cjs/src/search-results/components/filters/filters.d.ts +0 -13
  443. package/build/build-cjs/src/search-results/components/filters/flight-filters.d.ts +0 -8
  444. package/build/build-cjs/src/search-results/components/flight/flight-banner.d.ts +0 -8
  445. package/build/build-cjs/src/search-results/components/flight/flight-card.d.ts +0 -7
  446. package/build/build-cjs/src/search-results/components/flight/flight-leg.d.ts +0 -7
  447. package/build/build-cjs/src/search-results/components/flight/flight-path.d.ts +0 -6
  448. package/build/build-cjs/src/search-results/components/flight/flight-results.d.ts +0 -8
  449. package/build/build-cjs/src/search-results/components/flight/flight-search-context/index.d.ts +0 -39
  450. package/build/build-cjs/src/search-results/components/flight/flight-selection/independent-flight-option.d.ts +0 -14
  451. package/build/build-cjs/src/search-results/components/flight/flight-selection/independent-flight-selection.d.ts +0 -7
  452. package/build/build-cjs/src/search-results/components/flight/flight-selection/index.d.ts +0 -8
  453. package/build/build-cjs/src/search-results/components/flight/flight-selection/paired-flight-option.d.ts +0 -7
  454. package/build/build-cjs/src/search-results/components/flight/flight-selection/paired-flight-selection.d.ts +0 -7
  455. package/build/build-cjs/src/search-results/components/group-tour/group-tour-card.d.ts +0 -9
  456. package/build/build-cjs/src/search-results/components/group-tour/group-tour-results.d.ts +0 -6
  457. package/build/build-cjs/src/search-results/components/hotel/hotel-accommodation-results.d.ts +0 -7
  458. package/build/build-cjs/src/search-results/components/hotel/hotel-card.d.ts +0 -8
  459. package/build/build-cjs/src/search-results/components/item-picker/index.d.ts +0 -15
  460. package/build/build-cjs/src/search-results/components/itinerary/full-itinerary.d.ts +0 -6
  461. package/build/build-cjs/src/search-results/components/itinerary/index.d.ts +0 -10
  462. package/build/build-cjs/src/search-results/components/multi-range-filter.d.ts +0 -11
  463. package/build/build-cjs/src/search-results/components/round-trip/round-trip-results.d.ts +0 -4
  464. package/build/build-cjs/src/search-results/components/search-results-container/flight-search-results.d.ts +0 -6
  465. package/build/build-cjs/src/search-results/components/search-results-container/search-results-container.d.ts +0 -6
  466. package/build/build-cjs/src/search-results/components/spinner/spinner.d.ts +0 -6
  467. package/build/build-cjs/src/search-results/components/tab-views/index.d.ts +0 -4
  468. package/build/build-cjs/src/search-results/features/flights/flight-search-results-self-contained.d.ts +0 -4
  469. package/build/build-cjs/src/search-results/features/hotels/hotel-flight-search-results-self-contained.d.ts +0 -4
  470. package/build/build-cjs/src/search-results/features/hotels/hotel-search-results-self-contained.d.ts +0 -4
  471. package/build/build-cjs/src/search-results/features/roundtrips/roundtrip-search-results-self-contained.d.ts +0 -4
  472. package/build/build-cjs/src/search-results/index.d.ts +0 -8
  473. package/build/build-cjs/src/search-results/search-results-configuration-context.d.ts +0 -4
  474. package/build/build-cjs/src/search-results/store/search-results-selectors.d.ts +0 -546
  475. package/build/build-cjs/src/search-results/store/search-results-slice.d.ts +0 -138
  476. package/build/build-cjs/src/search-results/store/search-results-store.d.ts +0 -23
  477. package/build/build-cjs/src/search-results/types.d.ts +0 -211
  478. package/build/build-cjs/src/search-results/utils/flight-utils.d.ts +0 -16
  479. package/build/build-cjs/src/search-results/utils/packaging-utils.d.ts +0 -7
  480. package/build/build-cjs/src/search-results/utils/query-utils.d.ts +0 -12
  481. package/build/build-cjs/src/search-results/utils/search-results-utils.d.ts +0 -16
  482. package/build/build-cjs/src/shared/booking/booking-panel.d.ts +0 -13
  483. package/build/build-cjs/src/shared/booking/product-card.d.ts +0 -8
  484. package/build/build-cjs/src/shared/booking/shared-confirmation.d.ts +0 -25
  485. package/build/build-cjs/src/shared/booking/shared-sidebar.d.ts +0 -34
  486. package/build/build-cjs/src/shared/booking/step-indicators.d.ts +0 -7
  487. package/build/build-cjs/src/shared/booking/summary.d.ts +0 -44
  488. package/build/build-cjs/src/shared/booking/travelers-form.d.ts +0 -93
  489. package/build/build-cjs/src/shared/components/flyin/accommodation-flyin.d.ts +0 -7
  490. package/build/build-cjs/src/shared/components/flyin/flights-flyin.d.ts +0 -7
  491. package/build/build-cjs/src/shared/components/flyin/flyin.d.ts +0 -20
  492. package/build/build-cjs/src/shared/components/flyin/group-tour-flyin.d.ts +0 -8
  493. package/build/build-cjs/src/shared/components/flyin/packaging-flights-flyin.d.ts +0 -9
  494. package/build/build-cjs/src/shared/components/icon.d.ts +0 -11
  495. package/build/build-cjs/src/shared/components/loader.d.ts +0 -6
  496. package/build/build-cjs/src/shared/types.d.ts +0 -16
  497. package/build/build-cjs/src/shared/utils/booking-summary.d.ts +0 -2
  498. package/build/build-cjs/src/shared/utils/class-util.d.ts +0 -1
  499. package/build/build-cjs/src/shared/utils/localization-util.d.ts +0 -440
  500. package/build/build-cjs/src/shared/utils/query-string-util.d.ts +0 -8
  501. package/build/build-cjs/src/shared/utils/tide-api-utils.d.ts +0 -10
  502. package/build/build-cjs/src/shared/utils/use-media-query-util.d.ts +0 -2
  503. package/build/build-esm/index.js +0 -57136
  504. package/build/build-esm/src/booking-product/components/age-select.d.ts +0 -8
  505. package/build/build-esm/src/booking-product/components/amount-input.d.ts +0 -10
  506. package/build/build-esm/src/booking-product/components/date-range-picker/calendar-day.d.ts +0 -13
  507. package/build/build-esm/src/booking-product/components/date-range-picker/calendar.d.ts +0 -19
  508. package/build/build-esm/src/booking-product/components/date-range-picker/index.d.ts +0 -22
  509. package/build/build-esm/src/booking-product/components/dates.d.ts +0 -14
  510. package/build/build-esm/src/booking-product/components/footer.d.ts +0 -10
  511. package/build/build-esm/src/booking-product/components/header.d.ts +0 -11
  512. package/build/build-esm/src/booking-product/components/list-view.d.ts +0 -8
  513. package/build/build-esm/src/booking-product/components/product.d.ts +0 -9
  514. package/build/build-esm/src/booking-product/components/rating.d.ts +0 -6
  515. package/build/build-esm/src/booking-product/components/rooms.d.ts +0 -10
  516. package/build/build-esm/src/booking-product/constants.d.ts +0 -1
  517. package/build/build-esm/src/booking-product/index.d.ts +0 -10
  518. package/build/build-esm/src/booking-product/settings-context.d.ts +0 -5
  519. package/build/build-esm/src/booking-product/types.d.ts +0 -27
  520. package/build/build-esm/src/booking-product/utils/api.d.ts +0 -16
  521. package/build/build-esm/src/booking-product/utils/price.d.ts +0 -10
  522. package/build/build-esm/src/booking-wizard/api-settings-slice.d.ts +0 -5
  523. package/build/build-esm/src/booking-wizard/components/labeled-input.d.ts +0 -18
  524. package/build/build-esm/src/booking-wizard/components/labeled-select.d.ts +0 -21
  525. package/build/build-esm/src/booking-wizard/components/message.d.ts +0 -9
  526. package/build/build-esm/src/booking-wizard/components/multi-range-filter.d.ts +0 -11
  527. package/build/build-esm/src/booking-wizard/components/phone-input.d.ts +0 -17
  528. package/build/build-esm/src/booking-wizard/components/print-offer-button.d.ts +0 -17
  529. package/build/build-esm/src/booking-wizard/components/step-indicator.d.ts +0 -6
  530. package/build/build-esm/src/booking-wizard/components/step-route.d.ts +0 -8
  531. package/build/build-esm/src/booking-wizard/features/booking/api.d.ts +0 -30
  532. package/build/build-esm/src/booking-wizard/features/booking/booking-self-contained.d.ts +0 -8
  533. package/build/build-esm/src/booking-wizard/features/booking/booking-slice.d.ts +0 -121
  534. package/build/build-esm/src/booking-wizard/features/booking/booking.d.ts +0 -8
  535. package/build/build-esm/src/booking-wizard/features/booking/constants.d.ts +0 -15
  536. package/build/build-esm/src/booking-wizard/features/booking/selectors.d.ts +0 -803
  537. package/build/build-esm/src/booking-wizard/features/confirmation/confirmation.d.ts +0 -4
  538. package/build/build-esm/src/booking-wizard/features/error/error.d.ts +0 -4
  539. package/build/build-esm/src/booking-wizard/features/flight-options/flight-filter.d.ts +0 -9
  540. package/build/build-esm/src/booking-wizard/features/flight-options/flight-option-flight.d.ts +0 -8
  541. package/build/build-esm/src/booking-wizard/features/flight-options/flight-option-modal.d.ts +0 -3
  542. package/build/build-esm/src/booking-wizard/features/flight-options/flight-option.d.ts +0 -10
  543. package/build/build-esm/src/booking-wizard/features/flight-options/flight-utils.d.ts +0 -13
  544. package/build/build-esm/src/booking-wizard/features/flight-options/index.d.ts +0 -4
  545. package/build/build-esm/src/booking-wizard/features/price-details/price-details-api.d.ts +0 -11
  546. package/build/build-esm/src/booking-wizard/features/price-details/price-details-slice.d.ts +0 -28
  547. package/build/build-esm/src/booking-wizard/features/price-details/selectors.d.ts +0 -393
  548. package/build/build-esm/src/booking-wizard/features/price-details/util.d.ts +0 -2
  549. package/build/build-esm/src/booking-wizard/features/product-options/no-options.d.ts +0 -3
  550. package/build/build-esm/src/booking-wizard/features/product-options/none-option.d.ts +0 -9
  551. package/build/build-esm/src/booking-wizard/features/product-options/option-booking-airline-group.d.ts +0 -8
  552. package/build/build-esm/src/booking-wizard/features/product-options/option-booking-group.d.ts +0 -12
  553. package/build/build-esm/src/booking-wizard/features/product-options/option-item.d.ts +0 -11
  554. package/build/build-esm/src/booking-wizard/features/product-options/option-pax-card.d.ts +0 -10
  555. package/build/build-esm/src/booking-wizard/features/product-options/option-pax-group.d.ts +0 -13
  556. package/build/build-esm/src/booking-wizard/features/product-options/option-room.d.ts +0 -11
  557. package/build/build-esm/src/booking-wizard/features/product-options/option-unit-group.d.ts +0 -13
  558. package/build/build-esm/src/booking-wizard/features/product-options/option-units-card.d.ts +0 -9
  559. package/build/build-esm/src/booking-wizard/features/product-options/options-form.d.ts +0 -4
  560. package/build/build-esm/src/booking-wizard/features/room-options/index.d.ts +0 -4
  561. package/build/build-esm/src/booking-wizard/features/room-options/room-utils.d.ts +0 -22
  562. package/build/build-esm/src/booking-wizard/features/room-options/room.d.ts +0 -12
  563. package/build/build-esm/src/booking-wizard/features/room-options/traveler-rooms.d.ts +0 -9
  564. package/build/build-esm/src/booking-wizard/features/sidebar/index.d.ts +0 -7
  565. package/build/build-esm/src/booking-wizard/features/sidebar/sidebar-flight.d.ts +0 -9
  566. package/build/build-esm/src/booking-wizard/features/sidebar/sidebar-util.d.ts +0 -16
  567. package/build/build-esm/src/booking-wizard/features/sidebar/sidebar.d.ts +0 -0
  568. package/build/build-esm/src/booking-wizard/features/summary/summary-booking-option-pax.d.ts +0 -7
  569. package/build/build-esm/src/booking-wizard/features/summary/summary-booking-option-unit.d.ts +0 -7
  570. package/build/build-esm/src/booking-wizard/features/summary/summary-flight.d.ts +0 -8
  571. package/build/build-esm/src/booking-wizard/features/summary/summary-per-booking-option-group.d.ts +0 -7
  572. package/build/build-esm/src/booking-wizard/features/summary/summary-per-pax-option-group.d.ts +0 -7
  573. package/build/build-esm/src/booking-wizard/features/summary/summary-per-unit-option-group.d.ts +0 -7
  574. package/build/build-esm/src/booking-wizard/features/summary/summary-slice.d.ts +0 -8
  575. package/build/build-esm/src/booking-wizard/features/summary/summary.d.ts +0 -4
  576. package/build/build-esm/src/booking-wizard/features/travelers-form/controls/gender-control.d.ts +0 -5
  577. package/build/build-esm/src/booking-wizard/features/travelers-form/travelers-form-slice.d.ts +0 -104
  578. package/build/build-esm/src/booking-wizard/features/travelers-form/travelers-form-util.d.ts +0 -7
  579. package/build/build-esm/src/booking-wizard/features/travelers-form/travelers-form.d.ts +0 -3
  580. package/build/build-esm/src/booking-wizard/features/travelers-form/type-ahead-input.d.ts +0 -16
  581. package/build/build-esm/src/booking-wizard/features/travelers-form/validate-form.d.ts +0 -11
  582. package/build/build-esm/src/booking-wizard/index.d.ts +0 -12
  583. package/build/build-esm/src/booking-wizard/settings-context.d.ts +0 -5
  584. package/build/build-esm/src/booking-wizard/store.d.ts +0 -44
  585. package/build/build-esm/src/booking-wizard/types.d.ts +0 -301
  586. package/build/build-esm/src/booking-wizard/use-offer-printer.d.ts +0 -13
  587. package/build/build-esm/src/content/components/LanguageSwitcher.d.ts +0 -12
  588. package/build/build-esm/src/content/components/accordion.d.ts +0 -9
  589. package/build/build-esm/src/content/components/breadcrumb.d.ts +0 -14
  590. package/build/build-esm/src/content/components/contact.d.ts +0 -3
  591. package/build/build-esm/src/content/components/faq.d.ts +0 -11
  592. package/build/build-esm/src/content/components/gallery.d.ts +0 -13
  593. package/build/build-esm/src/content/components/image-with-text.d.ts +0 -29
  594. package/build/build-esm/src/content/components/login.d.ts +0 -3
  595. package/build/build-esm/src/content/components/personal-contact-form.d.ts +0 -3
  596. package/build/build-esm/src/content/components/slider.d.ts +0 -10
  597. package/build/build-esm/src/content/error/error.d.ts +0 -6
  598. package/build/build-esm/src/content/featured-trips/featured-trip-card.d.ts +0 -4
  599. package/build/build-esm/src/content/featured-trips/index.d.ts +0 -4
  600. package/build/build-esm/src/content/featured-trips/types.d.ts +0 -12
  601. package/build/build-esm/src/content/features/content-page/content-page-self-contained.d.ts +0 -6
  602. package/build/build-esm/src/content/footer/index.d.ts +0 -4
  603. package/build/build-esm/src/content/footer/types.d.ts +0 -27
  604. package/build/build-esm/src/content/header/index.d.ts +0 -4
  605. package/build/build-esm/src/content/header/types.d.ts +0 -25
  606. package/build/build-esm/src/content/image-card-grid/index.d.ts +0 -4
  607. package/build/build-esm/src/content/image-card-grid/types.d.ts +0 -12
  608. package/build/build-esm/src/content/image-with-text-section/card.d.ts +0 -4
  609. package/build/build-esm/src/content/image-with-text-section/index.d.ts +0 -4
  610. package/build/build-esm/src/content/image-with-text-section/types.d.ts +0 -19
  611. package/build/build-esm/src/content/login/confirm-component.d.ts +0 -4
  612. package/build/build-esm/src/content/login/index.d.ts +0 -4
  613. package/build/build-esm/src/content/login/login-component.d.ts +0 -4
  614. package/build/build-esm/src/content/login/login-services.d.ts +0 -11
  615. package/build/build-esm/src/content/login/reset-password-component.d.ts +0 -4
  616. package/build/build-esm/src/content/login/types.d.ts +0 -24
  617. package/build/build-esm/src/content/navbar/index.d.ts +0 -4
  618. package/build/build-esm/src/content/navbar/placeholderData.d.ts +0 -12
  619. package/build/build-esm/src/content/navbar/types.d.ts +0 -32
  620. package/build/build-esm/src/index.d.ts +0 -44
  621. package/build/build-esm/src/qsm/components/QSMContainer/qsm-container.d.ts +0 -3
  622. package/build/build-esm/src/qsm/components/date-picker/index.d.ts +0 -3
  623. package/build/build-esm/src/qsm/components/date-range-picker/calendar-day.d.ts +0 -12
  624. package/build/build-esm/src/qsm/components/date-range-picker/calendar.d.ts +0 -24
  625. package/build/build-esm/src/qsm/components/date-range-picker/index.d.ts +0 -10
  626. package/build/build-esm/src/qsm/components/double-search-input-group/index.d.ts +0 -8
  627. package/build/build-esm/src/qsm/components/item-picker/index.d.ts +0 -13
  628. package/build/build-esm/src/qsm/components/mobile-filter-modal/index.d.ts +0 -3
  629. package/build/build-esm/src/qsm/components/search-input/index.d.ts +0 -15
  630. package/build/build-esm/src/qsm/components/search-input-group/index.d.ts +0 -13
  631. package/build/build-esm/src/qsm/components/travel-class-picker/index.d.ts +0 -3
  632. package/build/build-esm/src/qsm/components/travel-input/index.d.ts +0 -3
  633. package/build/build-esm/src/qsm/components/travel-input-group/index.d.ts +0 -3
  634. package/build/build-esm/src/qsm/components/travel-nationality-picker/index.d.ts +0 -3
  635. package/build/build-esm/src/qsm/components/travel-type-picker/index.d.ts +0 -3
  636. package/build/build-esm/src/qsm/index.d.ts +0 -7
  637. package/build/build-esm/src/qsm/qsm-configuration-context.d.ts +0 -4
  638. package/build/build-esm/src/qsm/store/qsm-slice.d.ts +0 -127
  639. package/build/build-esm/src/qsm/store/qsm-store.d.ts +0 -23
  640. package/build/build-esm/src/qsm/types.d.ts +0 -82
  641. package/build/build-esm/src/search-results/components/book-packaging-entry/index.d.ts +0 -9
  642. package/build/build-esm/src/search-results/components/book-packaging-entry/wl-sidebar.d.ts +0 -9
  643. package/build/build-esm/src/search-results/components/excursions/day-by-day-excursions.d.ts +0 -4
  644. package/build/build-esm/src/search-results/components/excursions/excursion-details.d.ts +0 -3
  645. package/build/build-esm/src/search-results/components/excursions/excursion-results.d.ts +0 -8
  646. package/build/build-esm/src/search-results/components/filters/filters.d.ts +0 -13
  647. package/build/build-esm/src/search-results/components/filters/flight-filters.d.ts +0 -8
  648. package/build/build-esm/src/search-results/components/flight/flight-banner.d.ts +0 -8
  649. package/build/build-esm/src/search-results/components/flight/flight-card.d.ts +0 -7
  650. package/build/build-esm/src/search-results/components/flight/flight-leg.d.ts +0 -7
  651. package/build/build-esm/src/search-results/components/flight/flight-path.d.ts +0 -6
  652. package/build/build-esm/src/search-results/components/flight/flight-results.d.ts +0 -8
  653. package/build/build-esm/src/search-results/components/flight/flight-search-context/index.d.ts +0 -39
  654. package/build/build-esm/src/search-results/components/flight/flight-selection/independent-flight-option.d.ts +0 -14
  655. package/build/build-esm/src/search-results/components/flight/flight-selection/independent-flight-selection.d.ts +0 -7
  656. package/build/build-esm/src/search-results/components/flight/flight-selection/index.d.ts +0 -8
  657. package/build/build-esm/src/search-results/components/flight/flight-selection/paired-flight-option.d.ts +0 -7
  658. package/build/build-esm/src/search-results/components/flight/flight-selection/paired-flight-selection.d.ts +0 -7
  659. package/build/build-esm/src/search-results/components/group-tour/group-tour-card.d.ts +0 -9
  660. package/build/build-esm/src/search-results/components/group-tour/group-tour-results.d.ts +0 -6
  661. package/build/build-esm/src/search-results/components/hotel/hotel-accommodation-results.d.ts +0 -7
  662. package/build/build-esm/src/search-results/components/hotel/hotel-card.d.ts +0 -8
  663. package/build/build-esm/src/search-results/components/item-picker/index.d.ts +0 -15
  664. package/build/build-esm/src/search-results/components/itinerary/full-itinerary.d.ts +0 -6
  665. package/build/build-esm/src/search-results/components/itinerary/index.d.ts +0 -10
  666. package/build/build-esm/src/search-results/components/multi-range-filter.d.ts +0 -11
  667. package/build/build-esm/src/search-results/components/round-trip/round-trip-results.d.ts +0 -4
  668. package/build/build-esm/src/search-results/components/search-results-container/flight-search-results.d.ts +0 -6
  669. package/build/build-esm/src/search-results/components/search-results-container/search-results-container.d.ts +0 -6
  670. package/build/build-esm/src/search-results/components/spinner/spinner.d.ts +0 -6
  671. package/build/build-esm/src/search-results/components/tab-views/index.d.ts +0 -4
  672. package/build/build-esm/src/search-results/features/flights/flight-search-results-self-contained.d.ts +0 -4
  673. package/build/build-esm/src/search-results/features/hotels/hotel-flight-search-results-self-contained.d.ts +0 -4
  674. package/build/build-esm/src/search-results/features/hotels/hotel-search-results-self-contained.d.ts +0 -4
  675. package/build/build-esm/src/search-results/features/roundtrips/roundtrip-search-results-self-contained.d.ts +0 -4
  676. package/build/build-esm/src/search-results/index.d.ts +0 -8
  677. package/build/build-esm/src/search-results/search-results-configuration-context.d.ts +0 -4
  678. package/build/build-esm/src/search-results/store/search-results-selectors.d.ts +0 -546
  679. package/build/build-esm/src/search-results/store/search-results-slice.d.ts +0 -138
  680. package/build/build-esm/src/search-results/store/search-results-store.d.ts +0 -23
  681. package/build/build-esm/src/search-results/types.d.ts +0 -211
  682. package/build/build-esm/src/search-results/utils/flight-utils.d.ts +0 -16
  683. package/build/build-esm/src/search-results/utils/packaging-utils.d.ts +0 -7
  684. package/build/build-esm/src/search-results/utils/query-utils.d.ts +0 -12
  685. package/build/build-esm/src/search-results/utils/search-results-utils.d.ts +0 -16
  686. package/build/build-esm/src/shared/booking/booking-panel.d.ts +0 -13
  687. package/build/build-esm/src/shared/booking/product-card.d.ts +0 -8
  688. package/build/build-esm/src/shared/booking/shared-confirmation.d.ts +0 -25
  689. package/build/build-esm/src/shared/booking/shared-sidebar.d.ts +0 -34
  690. package/build/build-esm/src/shared/booking/step-indicators.d.ts +0 -7
  691. package/build/build-esm/src/shared/booking/summary.d.ts +0 -44
  692. package/build/build-esm/src/shared/booking/travelers-form.d.ts +0 -93
  693. package/build/build-esm/src/shared/components/flyin/accommodation-flyin.d.ts +0 -7
  694. package/build/build-esm/src/shared/components/flyin/flights-flyin.d.ts +0 -7
  695. package/build/build-esm/src/shared/components/flyin/flyin.d.ts +0 -20
  696. package/build/build-esm/src/shared/components/flyin/group-tour-flyin.d.ts +0 -8
  697. package/build/build-esm/src/shared/components/flyin/packaging-flights-flyin.d.ts +0 -9
  698. package/build/build-esm/src/shared/components/icon.d.ts +0 -11
  699. package/build/build-esm/src/shared/components/loader.d.ts +0 -6
  700. package/build/build-esm/src/shared/types.d.ts +0 -16
  701. package/build/build-esm/src/shared/utils/booking-summary.d.ts +0 -2
  702. package/build/build-esm/src/shared/utils/class-util.d.ts +0 -1
  703. package/build/build-esm/src/shared/utils/localization-util.d.ts +0 -440
  704. package/build/build-esm/src/shared/utils/query-string-util.d.ts +0 -8
  705. package/build/build-esm/src/shared/utils/tide-api-utils.d.ts +0 -10
  706. package/build/build-esm/src/shared/utils/use-media-query-util.d.ts +0 -2
@@ -1,1764 +1,1764 @@
1
- import React, { useContext, useEffect, useRef, useState } from 'react';
2
- import { useDispatch, useSelector } from 'react-redux';
3
- import { SearchResultsRootState } from '../../store/search-results-store';
4
- import SearchResultsConfigurationContext from '../../search-results-configuration-context';
5
- import {
6
- resetFilters,
7
- setSortType,
8
- setResults,
9
- setIsLoading,
10
- setSelectedSearchResult,
11
- setBookingPackageDetails,
12
- setFlyInIsOpen,
13
- setFilteredResults,
14
- setPackagingAccoResults,
15
- setFilteredPackagingAccoResults,
16
- setPackagingAccoSearchDetails,
17
- setEditablePackagingEntry,
18
- setTransactionId,
19
- setFlyInType,
20
- setPriceDetails,
21
- setItinerary,
22
- setFlightsLoading,
23
- setPackagingFlightResults,
24
- setSelectedPackagingFlight,
25
- setSelectedPackagingAccoResult,
26
- setSelectedOutwardKey,
27
- setSelectedReturnKey,
28
- setFilteredPackagingFlightResults,
29
- setInitialFlightFilters,
30
- resetFlightFilters,
31
- setFilters,
32
- setInitialFilters,
33
- setBookPackagingEntry
34
- } from '../../store/search-results-slice';
35
- import { FlyInType, SearchSeed, SortByType } from '../../types';
36
- import useMediaQuery from '../../../shared/utils/use-media-query-util';
37
- import ItemPicker from '../item-picker';
38
- import {
39
- TideClientConfig,
40
- details,
41
- search,
42
- searchPackagingAccommodations,
43
- BookingPackageDestination,
44
- BookingPackageDetailsRequest,
45
- BookingPackagePax,
46
- BookingPackageRequest,
47
- BookingPackageRequestRoom,
48
- BookingPackageSearchRequest,
49
- PackagingAccommodationRequest,
50
- PackagingDestination,
51
- PortalQsmType,
52
- PackagingEntry,
53
- startTransaction,
54
- PackagingEntryLine,
55
- getPriceDetails,
56
- getItinerary,
57
- FlightSearchRequest,
58
- searchPackagingFlights,
59
- PackagingFlightResponse,
60
- PackagingAccommodationResponse,
61
- FlightSearchResponseFlightSegment,
62
- PackagingEntryLineFlightLine,
63
- PackagingEntryAddress,
64
- PackagingEntryPax,
65
- PackagingEntryRoom,
66
- PackagingRequestBase
67
- } from '@qite/tide-client';
68
- import { getDateFromParams, getNumberFromParams, getRoomsFromParams, getStringFromParams } from '../../../shared/utils/query-string-util';
69
- import { concat, first, isEmpty, last, range } from 'lodash';
70
- import { Room } from '../../../booking-wizard/types';
71
- import Icon from '../../../shared/components/icon';
72
- import Itinerary from '../itinerary';
73
- import TabViews from '../tab-views';
74
- import FlyIn from '../../../shared/components/flyin/flyin';
75
- import HotelAccommodationResults from '../hotel/hotel-accommodation-results';
76
- import RoundTripResults from '../round-trip/round-trip-results';
77
- import { dateToDateStruct, findSortByType, getSortingName, getTranslations } from '../../../shared/utils/localization-util';
78
- import { FlightSearchProvider } from '../flight/flight-search-context';
79
- import FlightResultsContainer from './flight-search-results';
80
- import Filters from '../filters/filters';
81
- import GroupTourResults from '../group-tour/group-tour-results';
82
- import {
83
- applyFilters,
84
- applyFiltersToPackageAccoResults,
85
- applyFiltersToPackageFlightResults,
86
- enrichFiltersWithPackageAccoResults,
87
- enrichFiltersWithPackageFlightResults,
88
- enrichFiltersWithResults
89
- } from '../../utils/search-results-utils';
90
- import {
91
- ACCOMMODATION_SERVICE_TYPE,
92
- EXCURSION_SERVICE_TYPE,
93
- FLIGHT_SERVICE_TYPE,
94
- getDepartureAirportFromEntry,
95
- getDestinationAirportFromEntry,
96
- getPackagingRequestRoomsFromBookingRooms,
97
- getRequestRoomsFromPackagingEntry,
98
- GROUP_TOUR_SERVICE_TYPE,
99
- parseHotelId,
100
- toDateOnlyString
101
- } from '../../utils/query-utils';
102
- import { getRequestRoomsFromPackagingSegments, getSelectedOptionsPerRoom } from '../../utils/packaging-utils';
103
- import FullItinerary from '../itinerary/full-itinerary';
104
- import { getFlightKey } from '../../utils/flight-utils';
105
- import IndependentFlightOption from '../flight/flight-selection/independent-flight-option';
106
- import { Spinner } from '../../..';
107
- import {
108
- selectSelectedCombinationFlight,
109
- selectSelectedOutward,
110
- selectSelectedOutwardKey,
111
- selectSelectedReturn,
112
- selectSelectedReturnKey,
113
- selectUniqueOutwardFlights,
114
- selectUniqueReturnFlights
115
- } from '../../store/search-results-selectors';
116
- import DayByDayExcursions from '../excursions/day-by-day-excursions';
117
- import BookPackagingEntry from '../book-packaging-entry';
118
- import { format } from 'date-fns';
119
-
120
- type BuildPackagingEntryPartialArgs = {
121
- sourceEntry: PackagingEntry | null | undefined;
122
- selectedHotelCode: string | null | undefined;
123
- accommodationResults: PackagingAccommodationResponse[];
124
- selectedFlight: PackagingFlightResponse | null;
125
- confirmedExcursionsByDay: Record<string, PackagingAccommodationResponse[]>;
126
- seed: SearchSeed;
127
- transactionId: string;
128
- language: string;
129
- };
130
-
131
- interface SearchResultsContainerProps {
132
- onBookingStarted?: () => void;
133
- }
134
-
135
- const SearchResultsContainer: React.FC<SearchResultsContainerProps> = ({ onBookingStarted }) => {
136
- const currentSearch = typeof window !== 'undefined' ? window.location.search : '';
137
-
138
- const dispatch = useDispatch();
139
-
140
- const context = useContext(SearchResultsConfigurationContext);
141
- const translations = getTranslations(context?.languageCode ?? 'en-GB');
142
-
143
- const {
144
- results,
145
- filteredResults,
146
- packagingAccoResults,
147
- filteredPackagingAccoResults,
148
- isLoading,
149
- flightsLoading,
150
- initialFilters,
151
- filters,
152
- flightFilters,
153
- selectedSortType,
154
- selectedFlightSortType,
155
- selectedSearchResult,
156
- selectedPackagingAccoResultCode,
157
- flyInIsOpen,
158
- packagingAccoSearchDetails,
159
- editablePackagingEntry,
160
- transactionId,
161
- flyInType,
162
- packagingFlightResults,
163
- confirmedExcursionsByDay,
164
- bookPackagingEntry
165
- } = useSelector((state: SearchResultsRootState) => state.searchResults);
166
-
167
- const isMobile = useMediaQuery('(max-width: 1200px)');
168
-
169
- const [initialFiltersSet, setInitialFiltersSet] = useState(false);
170
- const [initialFlightFiltersSet, setInitialFlightFiltersSet] = useState(false);
171
- const [filtersOpen, setFiltersOpen] = useState(false);
172
-
173
- const [detailsIsLoading, setDetailsIsLoading] = useState(false);
174
- const [pricesAreLoading, setPricesAreLoading] = useState(false);
175
- const [itineraryIsLoading, setItineraryIsLoading] = useState(false);
176
-
177
- const [itineraryOpen, setItineraryOpen] = useState(false);
178
- const [isBookingConfirmation, setIsBookingConfirmation] = useState(false);
179
-
180
- const [selectedAccommodationSeed, setSelectedAccommodationSeed] = useState<SearchSeed | null>(null);
181
-
182
- const skipInitialPackagingAccoDetailsRef = useRef(false);
183
-
184
- const panelRef = useRef<HTMLDivElement | null>(null);
185
-
186
- const sortByTypes: SortByType[] = [
187
- { direction: 'asc', label: 'default' } as SortByType,
188
- { direction: 'asc', label: 'price' } as SortByType,
189
- { direction: 'desc', label: 'price' } as SortByType
190
- ];
191
-
192
- const handleFlyInToggle = (isOpen: boolean) => {
193
- dispatch(setFlyInIsOpen(isOpen));
194
- };
195
-
196
- const handleSortChange = (newSortKey: string, direction?: string) => {
197
- const newSortByType = findSortByType(sortByTypes, newSortKey, direction ?? 'asc');
198
- if (newSortByType) {
199
- dispatch(setSortType(newSortByType));
200
- }
201
- };
202
-
203
- const getRequestRooms = (rooms: Room[] | null) => {
204
- if (!rooms) {
205
- // Fall back to 2 adults
206
- var room = { index: 0, pax: [] } as BookingPackageRequestRoom;
207
- range(0, 2).forEach(() => {
208
- room.pax.push({
209
- age: 30
210
- } as BookingPackagePax);
211
- });
212
- return [room];
213
- }
214
-
215
- const requestRooms = rooms?.map((x, i) => {
216
- var room = { index: i, pax: [] } as BookingPackageRequestRoom;
217
- range(0, x.adults).forEach(() => {
218
- room.pax.push({
219
- age: 30
220
- } as BookingPackagePax);
221
- });
222
- x.childAges.forEach((x) => {
223
- room.pax.push({
224
- age: x
225
- } as BookingPackagePax);
226
- });
227
- return room;
228
- });
229
-
230
- return requestRooms;
231
- };
232
-
233
- const buildSearchFromSeed = (seed: SearchSeed): BookingPackageRequest<BookingPackageSearchRequest> => {
234
- const country = seed.country;
235
- const region = seed.region;
236
- const oord = seed.oord;
237
- const city = seed.location;
238
- const hotel = seed.hotel;
239
- const tagId = seed.tagId;
240
-
241
- if (typeof window !== 'undefined') {
242
- window.scrollTo(0, 0);
243
- }
244
-
245
- let destinationId: number | null = null;
246
- let destinationIsCountry = false;
247
- let destinationIsRegion = false;
248
- let destinationIsOord = false;
249
- let destinationIsLocation = false;
250
-
251
- if (country) {
252
- destinationId = country;
253
- destinationIsCountry = true;
254
- } else if (region) {
255
- destinationId = region;
256
- destinationIsRegion = true;
257
- } else if (oord) {
258
- destinationId = oord;
259
- destinationIsOord = true;
260
- } else if (city) {
261
- destinationId = city;
262
- destinationIsLocation = true;
263
- }
264
-
265
- return {
266
- officeId: 1,
267
- agentId: context?.agentId,
268
- payload: {
269
- catalogueIds: context!.searchConfiguration.defaultCatalogueId
270
- ? [context!.searchConfiguration.defaultCatalogueId]
271
- : context?.tideConnection?.catalogueIds ?? [1],
272
- serviceType:
273
- context!.searchConfiguration.qsmType === PortalQsmType.Accommodation || context!.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight
274
- ? ACCOMMODATION_SERVICE_TYPE
275
- : context!.searchConfiguration.qsmType === PortalQsmType.Flight
276
- ? FLIGHT_SERVICE_TYPE
277
- : context!.searchConfiguration.qsmType === PortalQsmType.RoundTrip
278
- ? GROUP_TOUR_SERVICE_TYPE
279
- : undefined,
280
- searchType: context!.searchConfiguration.qsmType === PortalQsmType.GroupTour ? 1 : 0,
281
- destination: {
282
- id: Number(destinationId),
283
- isCountry: destinationIsCountry,
284
- isRegion: destinationIsRegion,
285
- isOord: destinationIsOord,
286
- isLocation: destinationIsLocation
287
- } as BookingPackageDestination,
288
- rooms: seed.rooms,
289
- fromDate: seed.fromDate,
290
- toDate: seed.toDate,
291
- earliestFromOffset: 0,
292
- latestToOffset: 0,
293
- includeFlights: context!.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight,
294
- useExactDates: context?.searchConfiguration.qsmType === PortalQsmType.GroupTour ? false : true,
295
- onlyCachedResults: false,
296
- includeAllAllotments: true,
297
- productIds: hotel ? [hotel] : [],
298
- productTagIds: tagId ? [tagId] : []
299
- }
300
- };
301
- };
302
-
303
- const buildPackagingAccommodationRequestFromSeed = (seed: SearchSeed, currentTransactionId: string): PackagingAccommodationRequest => {
304
- const country = seed.country;
305
- const region = seed.region;
306
- const oord = seed.oord;
307
- const city = seed.location;
308
- const hotelCode = seed.hotelCode ?? (seed.hotel ? seed.hotel.toString() : '');
309
- const tagId = seed.tagId;
310
- const destinationAirport = seed.destinationAirport;
311
-
312
- if (typeof window !== 'undefined') {
313
- window.scrollTo(0, 0);
314
- }
315
-
316
- let destinationId: number | null = null;
317
- let destinationIsCountry = false;
318
- let destinationIsRegion = false;
319
- let destinationIsOord = false;
320
- let destinationIsLocation = false;
321
- let destinationCode: string | null = null;
322
- let destinationIsAirport = false;
323
-
324
- if (city) {
325
- destinationId = city;
326
- destinationIsLocation = true;
327
- } else if (oord) {
328
- destinationId = oord;
329
- destinationIsOord = true;
330
- } else if (region) {
331
- destinationId = region;
332
- destinationIsRegion = true;
333
- } else if (country) {
334
- destinationId = country;
335
- destinationIsCountry = true;
336
- } else if (destinationAirport) {
337
- destinationCode = destinationAirport;
338
- destinationIsAirport = true;
339
- }
340
-
341
- return {
342
- transactionId: currentTransactionId,
343
- officeId: context?.tideConnection?.officeId ?? 1,
344
- agentId: context?.agentId ?? null,
345
- catalogueId: context!.searchConfiguration.defaultCatalogueId ?? first(context?.tideConnection?.catalogueIds) ?? 1,
346
- searchConfigurationId: context!.searchConfiguration.id,
347
- language: context!.languageCode ?? 'en-GB',
348
- serviceType: ACCOMMODATION_SERVICE_TYPE,
349
- fromDate: seed.fromDate,
350
- toDate: seed.toDate,
351
- destination: {
352
- id: Number(destinationId),
353
- isCountry: destinationIsCountry,
354
- isRegion: destinationIsRegion,
355
- isOord: destinationIsOord,
356
- isLocation: destinationIsLocation,
357
- isAirport: destinationIsAirport,
358
- code: destinationCode
359
- } as PackagingDestination,
360
- productCode: '',
361
- rooms: getPackagingRequestRoomsFromBookingRooms(seed.rooms),
362
- tagIds: tagId ? [tagId] : []
363
- };
364
- };
365
-
366
- const buildPackagingFlightRequestFromSeed = (seed: SearchSeed, currentTransactionId: string): FlightSearchRequest => {
367
- if (typeof window !== 'undefined') {
368
- window.scrollTo(0, 0);
369
- }
370
- var adults = seed.rooms.flatMap((x) => x.pax).filter((x) => x.age! >= 12).length;
371
- var kids = seed.rooms.flatMap((x) => x.pax).filter((x) => x.age! >= 2 && x.age! < 12).length;
372
- var babies = seed.rooms.flatMap((x) => x.pax).filter((x) => x.age! < 2).length;
373
-
374
- return {
375
- transactionId: currentTransactionId,
376
- officeId: context?.tideConnection?.officeId ?? 1,
377
- catalogueId: context!.searchConfiguration.defaultCatalogueId ?? first(context?.tideConnection?.catalogueIds) ?? 1,
378
- departureAirportCode: seed.departureAirport,
379
- arrivalAirportCode: seed.destinationAirport,
380
- returnAirportCode: seed.returnAirport,
381
- luggageIncluded: null,
382
- maxStops: null,
383
- travelClass: seed.travelClass,
384
- pax: concat(
385
- Array.from({ length: adults ?? 0 }, (_, index) => ({
386
- id: index,
387
- age: 31
388
- })),
389
- Array.from({ length: kids ?? 0 }, (_, index) => ({
390
- id: index + (adults ?? 0),
391
- age: 8
392
- })),
393
- Array.from({ length: babies ?? 0 }, (_, index) => ({
394
- id: index + (adults ?? 0) + (kids ?? 0),
395
- age: 1
396
- }))
397
- ),
398
- outward: seed.fromDate ? { date: dateToDateStruct(new Date(seed.fromDate)) } : null,
399
- return: seed.toDate ? { date: dateToDateStruct(new Date(seed.toDate)) } : null
400
- } as FlightSearchRequest;
401
- };
402
-
403
- const buildSearchSeedFromQueryParams = (params: URLSearchParams): SearchSeed | null => {
404
- const from = getDateFromParams(params, 'fromDate');
405
- const to = getDateFromParams(params, 'toDate');
406
- const rooms = getRoomsFromParams(params, 'rooms');
407
- const country = getNumberFromParams(params, 'country');
408
- const region = getNumberFromParams(params, 'region');
409
- const oord = getNumberFromParams(params, 'oord');
410
- const city = getNumberFromParams(params, 'location');
411
- const hotel = getNumberFromParams(params, 'hotel');
412
- const tagId = getNumberFromParams(params, 'tagId');
413
- const destinationAirport = getStringFromParams(params, 'destinationAirport');
414
- const departureAirport = getStringFromParams(params, 'departureAirport');
415
- const travelClass = getStringFromParams(params, 'travelClass');
416
- const nationality = getStringFromParams(params, 'nationality');
417
-
418
- if (!from || !to) {
419
- return null;
420
- }
421
-
422
- return {
423
- fromDate: from,
424
- toDate: to,
425
- country,
426
- region,
427
- oord,
428
- location: city,
429
- hotel,
430
- hotelCode: hotel ? hotel.toString() : null,
431
- tagId,
432
- destinationAirport,
433
- departureAirport,
434
- travelClass,
435
- nationality,
436
- rooms: getRequestRooms(rooms)
437
- };
438
- };
439
-
440
- const buildSearchSeedFromAccommodationSegments = (entry: PackagingEntry, segments: PackagingEntryLine[]): SearchSeed | null => {
441
- if (!segments?.length) return null;
442
-
443
- const sortedSegments = [...segments].sort((a, b) => new Date(a.from).getTime() - new Date(b.from).getTime());
444
-
445
- const firstSegment = first(sortedSegments);
446
- const lastSegment = last(sortedSegments);
447
-
448
- if (!firstSegment || !lastSegment) return null;
449
-
450
- return {
451
- fromDate: toDateOnlyString(firstSegment.from),
452
- toDate: toDateOnlyString(lastSegment.to),
453
- country: firstSegment.country?.id ?? null,
454
- region: firstSegment.region?.id ?? null,
455
- oord: firstSegment.oord?.id ?? null,
456
- location: firstSegment.location?.id ?? null,
457
- hotel: parseHotelId(firstSegment),
458
- hotelCode: firstSegment.productCode ?? null,
459
- tagId: null,
460
- destinationAirport: getDestinationAirportFromEntry(entry.lines ?? []),
461
- departureAirport: getDepartureAirportFromEntry(entry.lines ?? []),
462
- rooms: getRequestRoomsFromPackagingSegments(entry, sortedSegments)
463
- };
464
- };
465
-
466
- const handleEditAccommodation = async (segments: PackagingEntryLine[]) => {
467
- const sourceEntry = editablePackagingEntry ?? context?.packagingEntry;
468
- if (!sourceEntry) return;
469
-
470
- const seed = buildSearchSeedFromAccommodationSegments(sourceEntry, segments);
471
- if (!seed) return;
472
-
473
- setDetailsIsLoading(true);
474
-
475
- setSelectedAccommodationSeed(seed);
476
- dispatch(setFlyInType('acco-results'));
477
- handleFlyInToggle(true);
478
- const currentTransactionId = await getOrCreateTransactionId();
479
- await runAccommodationFlow(seed, currentTransactionId ?? '');
480
- setDetailsIsLoading(false);
481
- };
482
-
483
- const handleConfirmHotelSwap = () => {
484
- const updatedEntry = swapHotelInPackagingEntry();
485
- if (!updatedEntry) return;
486
- dispatch(setEditablePackagingEntry(updatedEntry));
487
- handleFlyInToggle(false);
488
- };
489
-
490
- const swapHotelInPackagingEntry = (): PackagingEntry | null => {
491
- const sourceEntry = editablePackagingEntry ?? context?.packagingEntry;
492
- const details = packagingAccoSearchDetails;
493
-
494
- if (!sourceEntry || !details?.length) return null;
495
-
496
- const selectedOptionsPerRoom = getSelectedOptionsPerRoom(details);
497
- if (!selectedOptionsPerRoom.length) return null;
498
-
499
- const selectedHotel = details[0];
500
-
501
- let roomIndex = 0;
502
- const updatedLines = sourceEntry.lines.map((line) => {
503
- if (line.serviceType !== ACCOMMODATION_SERVICE_TYPE) {
504
- return line;
505
- }
506
-
507
- const selectedRoom = selectedOptionsPerRoom.find((x) => x.roomIndex === roomIndex);
508
- const selectedOption = selectedRoom?.option;
509
- roomIndex++;
510
-
511
- if (!selectedOption) {
512
- return line;
513
- }
514
-
515
- return {
516
- ...line,
517
- guid: selectedOption.guid,
518
- productName: selectedHotel.name,
519
- productCode: selectedHotel.code,
520
- accommodationName: selectedOption.accommodationName,
521
- accommodationCode: selectedOption.accommodationCode,
522
- regimeName: selectedOption.regimeName,
523
- regimeCode: selectedOption.regimeCode,
524
-
525
- country: line.country
526
- ? { ...line.country, id: selectedHotel.countryId ?? line.country.id, name: selectedHotel.countryName ?? line.country.name }
527
- : selectedHotel.countryId
528
- ? { id: selectedHotel.countryId, name: selectedHotel.countryName, localizations: [] }
529
- : line.country,
530
-
531
- region: line.region
532
- ? { ...line.region, id: selectedHotel.regionId ?? line.region.id, name: selectedHotel.regionName ?? line.region.name }
533
- : selectedHotel.regionId
534
- ? { id: selectedHotel.regionId, name: selectedHotel.regionName, localizations: [] }
535
- : line.region,
536
-
537
- oord: line.oord
538
- ? { ...line.oord, id: selectedHotel.oordId ?? line.oord.id, name: selectedHotel.oordName ?? line.oord.name }
539
- : selectedHotel.oordId
540
- ? { id: selectedHotel.oordId, name: selectedHotel.oordName, localizations: [] }
541
- : line.oord,
542
-
543
- location: line.location
544
- ? { ...line.location, id: selectedHotel.locationId ?? line.location.id, name: selectedHotel.locationName ?? line.location.name }
545
- : selectedHotel.locationId
546
- ? { id: selectedHotel.locationId, name: selectedHotel.locationName, localizations: [] }
547
- : line.location,
548
-
549
- latitude: selectedHotel.latitude ?? line.latitude,
550
- longitude: selectedHotel.longitude ?? line.longitude,
551
-
552
- from: selectedHotel.fromDate ?? line.from,
553
- to: selectedHotel.toDate ?? line.to,
554
- isChanged: true
555
- };
556
- });
557
-
558
- return {
559
- ...sourceEntry,
560
- lines: updatedLines
561
- };
562
- };
563
-
564
- const activeSearchSeed = React.useMemo(() => {
565
- if (selectedAccommodationSeed) {
566
- return selectedAccommodationSeed;
567
- }
568
-
569
- if (typeof window === 'undefined') return null;
570
-
571
- const params = new URLSearchParams(window.location.search);
572
- return buildSearchSeedFromQueryParams(params);
573
- }, [selectedAccommodationSeed, currentSearch]);
574
-
575
- useEffect(() => {
576
- if (typeof document !== 'undefined') {
577
- document.body.classList.toggle('has-overlay', filtersOpen);
578
- }
579
- }, [filtersOpen]);
580
-
581
- const getOrCreateTransactionId = async (): Promise<string | null> => {
582
- if (context?.packagingEntry?.transactionId) {
583
- return context.packagingEntry.transactionId;
584
- }
585
-
586
- if (transactionId) {
587
- return transactionId;
588
- }
589
-
590
- return await runStartTransaction();
591
- };
592
-
593
- const runSearch = async () => {
594
- try {
595
- if (!context) return;
596
- dispatch(setIsLoading(true));
597
-
598
- const config: TideClientConfig = {
599
- host: context.tideConnection.host,
600
- apiKey: context.tideConnection.apiKey
601
- };
602
-
603
- const seed = activeSearchSeed;
604
- if (!seed) {
605
- throw new Error('Invalid search parameters');
606
- }
607
-
608
- const searchRequest = buildSearchFromSeed(seed);
609
- const packageSearchResults = await search(config, searchRequest);
610
-
611
- const enrichedFilters = enrichFiltersWithResults(packageSearchResults, context.filters, context.tags ?? []);
612
- if (!initialFiltersSet) {
613
- dispatch(resetFilters(enrichedFilters));
614
- dispatch(setInitialFilters(enrichedFilters));
615
- setInitialFiltersSet(true);
616
- }
617
-
618
- dispatch(setResults(packageSearchResults));
619
- const initialFilteredResults = applyFilters(packageSearchResults, filters, null);
620
- dispatch(setFilteredResults(initialFilteredResults));
621
-
622
- if (packageSearchResults?.length > 0) {
623
- if (context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight) {
624
- dispatch(setSelectedSearchResult(packageSearchResults[0]));
625
- }
626
- }
627
- } catch (err) {
628
- console.error('Search failed', err);
629
- } finally {
630
- dispatch(setIsLoading(false));
631
- }
632
- };
633
-
634
- const runStartTransaction = async (): Promise<string | null> => {
635
- try {
636
- if (!context) return null;
637
-
638
- dispatch(setIsLoading(true));
639
-
640
- const config: TideClientConfig = {
641
- host: context.tideConnection.host,
642
- apiKey: context.tideConnection.apiKey
643
- };
644
-
645
- const transaction = await startTransaction(config);
646
-
647
- dispatch(setTransactionId(transaction.transactionId));
648
- dispatch(setIsLoading(false));
649
- return transaction.transactionId;
650
- } catch (err) {
651
- console.error('Transaction failed', err);
652
- dispatch(setIsLoading(false));
653
- return null;
654
- }
655
- };
656
-
657
- const runHotelSearch = async (currentTransactionId: string, seed: SearchSeed) => {
658
- try {
659
- if (!context) return;
660
- dispatch(setIsLoading(true));
661
-
662
- const config: TideClientConfig = {
663
- host: context.tideConnection.host,
664
- apiKey: context.tideConnection.apiKey
665
- };
666
-
667
- let searchRequest: PackagingAccommodationRequest = buildPackagingAccommodationRequestFromSeed(seed, currentTransactionId);
668
- searchRequest.portalId = context.portalId;
669
- searchRequest.agentId = context.agentId;
670
-
671
- const packageAccoSearchResults = await searchPackagingAccommodations(config, searchRequest);
672
-
673
- const enrichedFilters = enrichFiltersWithPackageAccoResults(packageAccoSearchResults, context.tags ?? []);
674
- if (!initialFiltersSet) {
675
- dispatch(resetFilters(enrichedFilters));
676
- setInitialFilters(enrichedFilters);
677
- setInitialFiltersSet(true);
678
- }
679
-
680
- dispatch(setPackagingAccoResults(packageAccoSearchResults));
681
- const initialFilteredResults = applyFiltersToPackageAccoResults(packageAccoSearchResults, filters, null);
682
- dispatch(setFilteredPackagingAccoResults(initialFilteredResults));
683
-
684
- if (initialFilteredResults.length > 0) {
685
- skipInitialPackagingAccoDetailsRef.current = true;
686
- dispatch(setSelectedPackagingAccoResult(first(initialFilteredResults)?.code ?? null));
687
- }
688
-
689
- dispatch(setIsLoading(false));
690
- } catch (err) {
691
- console.error('HotelSearch failed', err);
692
- dispatch(setIsLoading(false));
693
- }
694
- };
695
-
696
- const runAccommodationFlow = async (seed: SearchSeed, currentTransactionId: string) => {
697
- if (!context || context.showMockup) return;
698
- await runHotelSearch(currentTransactionId, seed);
699
- };
700
-
701
- const runFlightSearch = async (currentTransactionId: string, seed: SearchSeed) => {
702
- try {
703
- if (!context) return;
704
- dispatch(setFlightsLoading(true));
705
-
706
- const config: TideClientConfig = {
707
- host: context.tideConnection.host,
708
- apiKey: context.tideConnection.apiKey
709
- };
710
-
711
- let searchRequest: FlightSearchRequest = buildPackagingFlightRequestFromSeed(seed, currentTransactionId);
712
- searchRequest.agentId = context.agentId;
713
-
714
- const packageFlightSearchResults = await searchPackagingFlights(config, searchRequest);
715
-
716
- const enrichedFilters = enrichFiltersWithPackageFlightResults(packageFlightSearchResults, context.tags ?? [], translations);
717
- if (!initialFlightFiltersSet) {
718
- dispatch(resetFlightFilters(enrichedFilters));
719
- dispatch(setInitialFlightFilters(enrichedFilters));
720
- setInitialFlightFiltersSet(true);
721
- }
722
-
723
- dispatch(setPackagingFlightResults(packageFlightSearchResults));
724
-
725
- const initialFilteredResults = applyFiltersToPackageFlightResults(packageFlightSearchResults, filters, null);
726
- dispatch(setFilteredPackagingFlightResults(initialFilteredResults));
727
-
728
- if (initialFilteredResults.length > 0) {
729
- const firstResult = first(packageFlightSearchResults);
730
- if (firstResult) {
731
- dispatch(setSelectedOutwardKey(getFlightKey(firstResult.outward.segments)));
732
- dispatch(setSelectedReturnKey(getFlightKey(firstResult.return.segments)));
733
- }
734
- }
735
-
736
- dispatch(setFlightsLoading(false));
737
- } catch (err) {
738
- console.error('FlightSearch failed', err);
739
- dispatch(setFlightsLoading(false));
740
- }
741
- };
742
-
743
- const runFlightFlow = async (seed: SearchSeed, currentTransactionId: string) => {
744
- if (!context || context.showMockup) return;
745
- await runFlightSearch(currentTransactionId, seed);
746
- };
747
-
748
- // separate Search
749
- useEffect(() => {
750
- if (
751
- context?.searchConfiguration.qsmType === PortalQsmType.GroupTour ||
752
- (context?.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && !context.searchConfiguration.enableManualPackaging)
753
- ) {
754
- runSearch();
755
- }
756
-
757
- if (context?.searchConfiguration.qsmType === PortalQsmType.Accommodation) {
758
- const seed = activeSearchSeed;
759
-
760
- if (seed) {
761
- (async () => {
762
- const transactionId = await getOrCreateTransactionId();
763
- if (!transactionId) return;
764
-
765
- await runAccommodationFlow(seed, transactionId);
766
- })();
767
- }
768
- }
769
-
770
- if (
771
- context?.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight &&
772
- context.searchConfiguration.enableManualPackaging &&
773
- !context?.packagingEntry
774
- ) {
775
- const seed = activeSearchSeed;
776
-
777
- if (seed) {
778
- (async () => {
779
- const sharedTransactionId = await getOrCreateTransactionId();
780
- if (!sharedTransactionId) return;
781
-
782
- const tasks: Promise<void>[] = [];
783
-
784
- if (context.searchConfiguration.allowAccommodations) {
785
- tasks.push(runAccommodationFlow(seed, sharedTransactionId));
786
- }
787
-
788
- if (context.searchConfiguration.allowFlights) {
789
- tasks.push(runFlightFlow(seed, sharedTransactionId));
790
- }
791
-
792
- await Promise.all(tasks);
793
- })();
794
- }
795
- }
796
- }, [
797
- location.search,
798
- context?.showMockup,
799
- context?.searchConfiguration.qsmType,
800
- context?.searchConfiguration.enableManualPackaging,
801
- context?.searchConfiguration.allowAccommodations,
802
- context?.packagingEntry?.transactionId,
803
- activeSearchSeed
804
- ]);
805
-
806
- useEffect(() => {
807
- if (context?.packagingEntry) {
808
- dispatch(setEditablePackagingEntry(structuredClone(context.packagingEntry)));
809
- dispatch(setTransactionId(context.packagingEntry.transactionId));
810
-
811
- const params = new URLSearchParams(location.search);
812
- const bookingConfirmation = getStringFromParams(params, 'bookingConfirmation');
813
- console.log('bookingConfirmation', bookingConfirmation);
814
- if (bookingConfirmation == 'true') {
815
- setIsBookingConfirmation(true);
816
- dispatch(setBookPackagingEntry(true));
817
- }
818
- }
819
- }, [context?.packagingEntry]);
820
-
821
- useEffect(() => {
822
- if (bookPackagingEntry && onBookingStarted) {
823
- onBookingStarted();
824
- }
825
- }, [bookPackagingEntry, onBookingStarted]);
826
-
827
- // separate detailsCall
828
- useEffect(() => {
829
- const fetchDetails = async () => {
830
- if (!selectedSearchResult || !context) return;
831
- setDetailsIsLoading(true);
832
- if (context?.searchConfiguration.qsmType === PortalQsmType.Accommodation || context?.searchConfiguration.qsmType === PortalQsmType.GroupTour) {
833
- handleFlyInToggle(true);
834
- }
835
-
836
- try {
837
- const config: TideClientConfig = {
838
- host: context.tideConnection.host,
839
- apiKey: context.tideConnection.apiKey
840
- };
841
-
842
- const selectedItem = results.find((r) => r.productId === selectedSearchResult.productId);
843
- if (!selectedItem) {
844
- // TODO: handle this case better, show an error message to the user
845
- return;
846
- }
847
-
848
- let requestRooms: BookingPackageRequestRoom[];
849
-
850
- if (context?.packagingEntry) {
851
- requestRooms = getRequestRoomsFromPackagingEntry(context.packagingEntry);
852
- } else {
853
- const seed = activeSearchSeed;
854
- requestRooms = seed?.rooms?.length ? seed.rooms : getRequestRooms(null);
855
- }
856
-
857
- const detailsRequest: BookingPackageRequest<BookingPackageDetailsRequest> = {
858
- officeId: 1,
859
- payload: {
860
- catalogueId: selectedItem.catalogueId,
861
- rooms: requestRooms,
862
- searchType: 0, // same as search
863
- productCode: selectedItem.code,
864
- fromDate: selectedItem.fromDate,
865
- toDate: selectedItem.toDate,
866
- includeFlights: context!.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight ? true : false,
867
- includeHotels: true,
868
- includePaxTypes: true,
869
- checkExternalAvailability: true,
870
- expectedPrice: selectedItem.price,
871
- duration: null,
872
- preNights: null,
873
- postNights: null
874
- },
875
- agentId: context.agentId
876
- };
877
-
878
- const detailsResponse = await details(config, detailsRequest);
879
- dispatch(setBookingPackageDetails({ details: detailsResponse?.payload }));
880
- setDetailsIsLoading(false);
881
- } catch (err) {
882
- console.error('Failed to fetch package details', err);
883
- setDetailsIsLoading(false);
884
- }
885
- };
886
-
887
- const fetchPackagingAccoSearchDetails = async () => {
888
- if (!selectedPackagingAccoResultCode || !context) return;
889
-
890
- if (skipInitialPackagingAccoDetailsRef.current) {
891
- skipInitialPackagingAccoDetailsRef.current = false;
892
- return;
893
- }
894
-
895
- setDetailsIsLoading(true);
896
- if (
897
- context?.searchConfiguration.qsmType === PortalQsmType.Accommodation ||
898
- context?.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight ||
899
- context?.searchConfiguration.qsmType === PortalQsmType.GroupTour
900
- ) {
901
- handleFlyInToggle(true);
902
- dispatch(setFlyInType('acco-details'));
903
- }
904
-
905
- try {
906
- const config: TideClientConfig = {
907
- host: context.tideConnection.host,
908
- apiKey: context.tideConnection.apiKey
909
- };
910
-
911
- const selectedItem = packagingAccoResults.find((r) => r.code === selectedPackagingAccoResultCode);
912
- if (!selectedItem) {
913
- // TODO: handle this case better, show an error message to the user
914
- return;
915
- }
916
-
917
- const seed = activeSearchSeed;
918
- const tagId = seed?.tagId ?? null;
919
- const destinationAirport = seed?.destinationAirport ?? null;
920
-
921
- let destinationId: number | null = null;
922
- let destinationIsCountry = false;
923
- let destinationIsRegion = false;
924
- let destinationIsOord = false;
925
- let destinationIsLocation = false;
926
- let destinationCode: string | null = null;
927
- let destinationIsAirport = false;
928
-
929
- if (selectedItem.countryId) {
930
- destinationId = selectedItem.countryId;
931
- destinationIsCountry = true;
932
- } else if (selectedItem.regionId) {
933
- destinationId = selectedItem.regionId;
934
- destinationIsRegion = true;
935
- } else if (selectedItem.oordId) {
936
- destinationId = selectedItem.oordId;
937
- destinationIsOord = true;
938
- } else if (selectedItem.locationId) {
939
- destinationId = selectedItem.locationId;
940
- destinationIsLocation = true;
941
- } else if (destinationAirport) {
942
- destinationCode = destinationAirport;
943
- destinationIsAirport = true;
944
- }
945
-
946
- const detailSearchRequest: PackagingAccommodationRequest = {
947
- transactionId: transactionId ?? '',
948
- officeId: 1,
949
- portalId: context.portalId,
950
- agentId: context.agentId,
951
- catalogueId: context!.searchConfiguration.defaultCatalogueId ?? first(context?.tideConnection?.catalogueIds) ?? 1,
952
- searchConfigurationId: context!.searchConfiguration.id,
953
- vendorConfigurationId: selectedItem.vendorId,
954
- language: context!.languageCode ?? 'en-GB',
955
- serviceType: ACCOMMODATION_SERVICE_TYPE,
956
- fromDate: selectedItem.fromDate,
957
- toDate: selectedItem.toDate,
958
- destination: {
959
- id: Number(destinationId),
960
- isCountry: destinationIsCountry,
961
- isRegion: destinationIsRegion,
962
- isOord: destinationIsOord,
963
- isLocation: destinationIsLocation,
964
- isAirport: destinationIsAirport,
965
- code: destinationCode
966
- } as PackagingDestination,
967
- productCode: selectedItem.code ? selectedItem.code : '',
968
- rooms: getPackagingRequestRoomsFromBookingRooms(seed?.rooms ?? null),
969
- tagIds: tagId ? [tagId] : []
970
- };
971
-
972
- const packageAccoSearchDetails = await searchPackagingAccommodations(config, detailSearchRequest);
973
- dispatch(setPackagingAccoSearchDetails(packageAccoSearchDetails));
974
- setDetailsIsLoading(false);
975
- } catch (err) {
976
- console.error('Failed to fetch package details', err);
977
- setDetailsIsLoading(false);
978
- }
979
- };
980
-
981
- if (selectedSearchResult) {
982
- fetchDetails();
983
- }
984
- if (selectedPackagingAccoResultCode) {
985
- fetchPackagingAccoSearchDetails();
986
- }
987
- }, [selectedSearchResult, selectedPackagingAccoResultCode]);
988
-
989
- useEffect(() => {
990
- if (context?.searchConfiguration.qsmType === PortalQsmType.Accommodation || context?.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight) {
991
- const filteredPackageAccoResults = applyFiltersToPackageAccoResults(packagingAccoResults, filters, selectedSortType);
992
- dispatch(setFilteredPackagingAccoResults(filteredPackageAccoResults));
993
- } else {
994
- const filteredResults = applyFilters(results, filters, selectedSortType);
995
- dispatch(setFilteredResults(filteredResults));
996
- }
997
- }, [filters, results, packagingAccoResults, selectedSortType]);
998
-
999
- useEffect(() => {
1000
- if (context?.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight) {
1001
- const filteredPackageFlightResults = applyFiltersToPackageFlightResults(packagingFlightResults, flightFilters, selectedFlightSortType);
1002
- dispatch(setFilteredPackagingFlightResults(filteredPackageFlightResults));
1003
- }
1004
- }, [flightFilters, packagingFlightResults, selectedFlightSortType]);
1005
-
1006
- useEffect(() => {
1007
- setInitialFiltersSet(false);
1008
- }, [activeSearchSeed]);
1009
-
1010
- useEffect(() => {
1011
- const fetchPriceDetails = async () => {
1012
- if (!context || !editablePackagingEntry || isEmpty(editablePackagingEntry.lines)) return;
1013
- setPricesAreLoading(true);
1014
- try {
1015
- const config: TideClientConfig = {
1016
- host: context.tideConnection.host,
1017
- apiKey: context.tideConnection.apiKey
1018
- };
1019
-
1020
- const request = {
1021
- language: context.languageCode ?? 'en-GB',
1022
- officeId: context.tideConnection.officeId,
1023
- catalogueId: context!.searchConfiguration.defaultCatalogueId ?? first(context?.tideConnection?.catalogueIds) ?? 1,
1024
- agentId: context.agentId,
1025
- payload: editablePackagingEntry
1026
- } as PackagingRequestBase<PackagingEntry>;
1027
-
1028
- const priceDetails = await getPriceDetails(config, request);
1029
- dispatch(setPriceDetails(priceDetails));
1030
- setPricesAreLoading(false);
1031
- } catch (err) {
1032
- console.error('Error fetching price details', err);
1033
- setPricesAreLoading(false);
1034
- }
1035
- };
1036
-
1037
- const fetchItinerary = async () => {
1038
- if (!context || !context.packagingEntry || !editablePackagingEntry || isEmpty(editablePackagingEntry.lines)) return;
1039
- setItineraryIsLoading(true);
1040
- try {
1041
- const config: TideClientConfig = {
1042
- host: context.tideConnection.host,
1043
- apiKey: context.tideConnection.apiKey
1044
- };
1045
-
1046
- const request = {
1047
- language: context.languageCode ?? 'en-GB',
1048
- officeId: context.tideConnection.officeId,
1049
- catalogueId: context!.searchConfiguration.defaultCatalogueId ?? first(context?.tideConnection?.catalogueIds) ?? 1,
1050
- agentId: context.agentId,
1051
- payload: editablePackagingEntry
1052
- } as PackagingRequestBase<PackagingEntry>;
1053
-
1054
- const itinerary = await getItinerary(config, request);
1055
- dispatch(setItinerary(itinerary));
1056
- setItineraryIsLoading(false);
1057
- } catch (err) {
1058
- console.error('Error fetching itinerary', err);
1059
- setItineraryIsLoading(false);
1060
- }
1061
- };
1062
-
1063
- fetchPriceDetails();
1064
- fetchItinerary();
1065
- }, [editablePackagingEntry]);
1066
-
1067
- // Flight selection
1068
- // const [selectedOutwardKey, setSelectedOutwardKey] = useState<string | null>(null);
1069
- // const [selectedReturnKey, setSelectedReturnKey] = useState<string | null>(null);
1070
- const selectedOutwardKey = useSelector(selectSelectedOutwardKey);
1071
- const selectedReturnKey = useSelector(selectSelectedReturnKey);
1072
- const uniqueOutwardFlights = useSelector(selectUniqueOutwardFlights);
1073
- const uniqueReturnFlights = useSelector(selectUniqueReturnFlights);
1074
- const selectedOutward = useSelector(selectSelectedOutward);
1075
- const selectedReturn = useSelector(selectSelectedReturn);
1076
- const selectedCombinationFlight = useSelector(selectSelectedCombinationFlight);
1077
-
1078
- useEffect(() => {
1079
- if (!selectedOutwardKey) {
1080
- dispatch(setSelectedReturnKey(null));
1081
- return;
1082
- }
1083
-
1084
- const matchingCombinations = packagingFlightResults.filter((flight) => getFlightKey(flight.outward.segments) === selectedOutwardKey);
1085
-
1086
- const returnMap = new Map<string, PackagingFlightResponse>();
1087
-
1088
- matchingCombinations.forEach((flight) => {
1089
- const key = getFlightKey(flight.return.segments);
1090
-
1091
- if (!returnMap.has(key)) {
1092
- returnMap.set(key, flight);
1093
- }
1094
- });
1095
-
1096
- const returns = Array.from(returnMap.values());
1097
- const segments = first(returns)?.return.segments;
1098
- const firstReturnKey = returns.length > 0 && segments ? getFlightKey(segments) : null;
1099
-
1100
- if (!returns.some((x) => getFlightKey(x.return.segments) === selectedReturnKey)) {
1101
- dispatch(setSelectedReturnKey(firstReturnKey));
1102
- }
1103
- }, [selectedOutwardKey, packagingFlightResults, selectedReturnKey, dispatch]);
1104
-
1105
- const visibleOutwardFlights = React.useMemo(() => {
1106
- const withoutSelected = uniqueOutwardFlights.filter((x) => getFlightKey(x.outward.segments) !== selectedOutwardKey);
1107
- return withoutSelected.slice(0, 3);
1108
- }, [uniqueOutwardFlights, selectedOutwardKey]);
1109
-
1110
- // TODO: get details for selected combination flight and show in fly-in
1111
- // useEffect(() => {
1112
- // if (!selectedCombinationFlight) return;
1113
-
1114
- // dispatch(setSelectedPackagingFlight(selectedCombinationFlight));
1115
- // // onFlightSearch(selectedCombinationFlight); // Trigger search to update accommodation options based on selected flight
1116
- // dispatch(setFlyInIsOpen(true));
1117
- // }, [selectedCombinationFlight, dispatch]);
1118
-
1119
- // Build packagingEntry
1120
- useEffect(() => {
1121
- if (!context) return;
1122
-
1123
- const seed = activeSearchSeed;
1124
- if (!seed) return;
1125
-
1126
- const nextEntry = buildOrUpdatePackagingEntryPartial({
1127
- sourceEntry: editablePackagingEntry ?? context.packagingEntry ?? null,
1128
- selectedHotelCode: selectedPackagingAccoResultCode,
1129
- accommodationResults: packagingAccoResults,
1130
- selectedFlight: selectedCombinationFlight ?? null,
1131
- confirmedExcursionsByDay,
1132
- seed,
1133
- transactionId: transactionId ?? context.packagingEntry?.transactionId ?? '',
1134
- language: context.languageCode ?? 'en-GB'
1135
- });
1136
-
1137
- if (!nextEntry) return;
1138
- dispatch(setEditablePackagingEntry(nextEntry));
1139
-
1140
- if (selectedCombinationFlight) {
1141
- dispatch(setSelectedPackagingFlight(selectedCombinationFlight));
1142
- }
1143
- }, [
1144
- context,
1145
- activeSearchSeed,
1146
- selectedPackagingAccoResultCode,
1147
- packagingAccoResults,
1148
- packagingAccoSearchDetails,
1149
- selectedCombinationFlight,
1150
- confirmedExcursionsByDay,
1151
- transactionId,
1152
- dispatch
1153
- ]);
1154
-
1155
- const removeAccommodationLines = (lines: PackagingEntryLine[]) => lines.filter((line) => line.serviceType !== ACCOMMODATION_SERVICE_TYPE);
1156
-
1157
- const removeFlightLines = (lines: PackagingEntryLine[]) => lines.filter((line) => line.serviceType !== FLIGHT_SERVICE_TYPE);
1158
-
1159
- const removeExcursionLines = (lines: PackagingEntryLine[]) => lines.filter((line) => line.serviceType !== EXCURSION_SERVICE_TYPE);
1160
-
1161
- const buildAccommodationLinesFromSelection = (selectedHotel: PackagingAccommodationResponse, seed: SearchSeed): PackagingEntryLine[] => {
1162
- return buildPackagingAccommodationLines(selectedHotel, seed, ACCOMMODATION_SERVICE_TYPE);
1163
- };
1164
-
1165
- const buildExcursionLinesFromConfirmedDays = (confirmedExcursionsByDay: Record<string, PackagingAccommodationResponse[]>): PackagingEntryLine[] => {
1166
- return Object.values(confirmedExcursionsByDay)
1167
- .flat()
1168
- .flatMap((excursion) => {
1169
- const selectedOptions = excursion.rooms.flatMap((room) => room.options.filter((option) => option.isSelected));
1170
-
1171
- const parentGuid = crypto.randomUUID();
1172
-
1173
- return selectedOptions.map(
1174
- (option, index) =>
1175
- ({
1176
- guid: option.guid ?? crypto.randomUUID(),
1177
- moment: '',
1178
- parentGuid: index === 0 ? null : parentGuid,
1179
- order: index,
1180
- isChanged: true,
1181
- from: excursion.fromDate,
1182
- to: excursion.toDate,
1183
- serviceType: EXCURSION_SERVICE_TYPE,
1184
- productName: excursion.name,
1185
- productCode: excursion.code,
1186
- accommodationName: option.accommodationName,
1187
- accommodationCode: option.accommodationCode,
1188
- regimeName: option.regimeName,
1189
- regimeCode: option.regimeCode,
1190
- country: excursion.countryId ? { id: excursion.countryId, name: excursion.countryName, localizations: [] } : null,
1191
- region: excursion.regionId ? { id: excursion.regionId, name: excursion.regionName, localizations: [] } : null,
1192
- oord: excursion.oordId ? { id: excursion.oordId, name: excursion.oordName, localizations: [] } : null,
1193
- location: excursion.locationId ? { id: excursion.locationId, name: excursion.locationName, localizations: [] } : null,
1194
- longitude: excursion.longitude ?? null,
1195
- latitude: excursion.latitude ?? null,
1196
- pax: Array.isArray(option.paxIds)
1197
- ? option.paxIds.map((paxId, paxIndex) => ({
1198
- paxId: paxId,
1199
- room: 0,
1200
- order: paxIndex
1201
- }))
1202
- : [],
1203
- flightInformation: null
1204
- } satisfies PackagingEntryLine)
1205
- );
1206
- });
1207
- };
1208
-
1209
- const buildPackagingAccommodationLines = (selectedItem: PackagingAccommodationResponse, seed: SearchSeed, serviceType: number): PackagingEntryLine[] => {
1210
- if (!selectedItem) return [];
1211
- const parentGuid = crypto.randomUUID();
1212
-
1213
- return selectedItem.rooms
1214
- .filter((room) => room.options.some((o) => o.isSelected))
1215
- .map((room, index) => {
1216
- const option = room.options.find((o) => o.isSelected)!;
1217
-
1218
- const pax = option.paxIds.map((p, paxIndex) => ({
1219
- paxId: p,
1220
- room: index,
1221
- order: paxIndex
1222
- }));
1223
-
1224
- return {
1225
- guid: option.guid ?? crypto.randomUUID(),
1226
- moment: '',
1227
- parentGuid: index === 0 ? null : parentGuid,
1228
- order: index,
1229
- isChanged: true,
1230
- from: selectedItem.fromDate,
1231
- to: selectedItem.toDate,
1232
- serviceType: serviceType,
1233
- productName: selectedItem.name,
1234
- productCode: selectedItem.code,
1235
- accommodationName: option.accommodationName,
1236
- accommodationCode: option.accommodationCode,
1237
- regimeName: option.regimeName,
1238
- regimeCode: option.regimeCode,
1239
- country: selectedItem.countryId ? { id: selectedItem.countryId, name: selectedItem.countryName, localizations: [] } : null,
1240
- region: selectedItem.regionId ? { id: selectedItem.regionId, name: selectedItem.regionName, localizations: [] } : null,
1241
- oord: selectedItem.oordId ? { id: selectedItem.oordId, name: selectedItem.oordName, localizations: [] } : null,
1242
- location: selectedItem.locationId ? { id: selectedItem.locationId, name: selectedItem.locationName, localizations: [] } : null,
1243
- longitude: selectedItem.longitude ?? null,
1244
- latitude: selectedItem.latitude ?? null,
1245
- pax,
1246
- flightInformation: null
1247
- } satisfies PackagingEntryLine;
1248
- });
1249
- };
1250
-
1251
- const toDateOnlyUtcString = (value: string | Date): string => {
1252
- const date = new Date(value);
1253
-
1254
- return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate())).toISOString();
1255
- };
1256
-
1257
- const toTimeOnlyString = (value: string | Date): string => {
1258
- const date = new Date(value);
1259
-
1260
- const hh = String(date.getUTCHours()).padStart(2, '0');
1261
- const mm = String(date.getUTCMinutes()).padStart(2, '0');
1262
- const ss = String(date.getUTCSeconds()).padStart(2, '0');
1263
-
1264
- return `${hh}:${mm}:${ss}`;
1265
- };
1266
-
1267
- const mapFlightSegmentsToFlightLines = (segments: FlightSearchResponseFlightSegment[]): PackagingEntryLineFlightLine[] =>
1268
- segments.map((segment) => ({
1269
- airlineCode: segment.marketingAirlineCode,
1270
- airlineDescription: segment.marketingAirlineName,
1271
- operatingAirlineCode: segment.operatingAirlineCode,
1272
- operatingAirlineDescription: segment.operatingAirlineName,
1273
- flightNumber: segment.flightNumber,
1274
- operatingFlightNumber: segment.operatingFlightNumber ?? null,
1275
- departureDate: toDateOnlyUtcString(segment.departureDateTime),
1276
- departureTime: toTimeOnlyString(segment.departureDateTime),
1277
- departureAirportCode: segment.departureAirportCode,
1278
- departureAirportDescription: segment.departureAirportName,
1279
- arrivalDate: toDateOnlyUtcString(segment.arrivalDateTime),
1280
- arrivalTime: toTimeOnlyString(segment.arrivalDateTime),
1281
- arrivalAirportCode: segment.arrivalAirportCode,
1282
- arrivalAirportDescription: segment.arrivalAirportName,
1283
- durationInTicks: segment.durationInTicks
1284
- }));
1285
-
1286
- const buildFlightLabel = (segments: FlightSearchResponseFlightSegment[]) => {
1287
- const firstSegment = first(segments);
1288
- const lastSegment = last(segments);
1289
-
1290
- if (!firstSegment || !lastSegment) {
1291
- return { productName: 'Flight', productCode: 'FLIGHT' };
1292
- }
1293
-
1294
- return {
1295
- productName: `${firstSegment.departureAirportName} - ${lastSegment.arrivalAirportName} (${firstSegment.marketingAirlineName})`,
1296
- productCode: `${firstSegment.departureAirportCode} ${lastSegment.arrivalAirportCode}/${firstSegment.marketingAirlineCode}`
1297
- };
1298
- };
1299
-
1300
- const buildFlightLinesFromSelection = (selectedFlight: PackagingFlightResponse): PackagingEntryLine[] => {
1301
- if (!selectedFlight) return [];
1302
-
1303
- const outwardSegments = selectedFlight.outward?.segments ?? [];
1304
- const returnSegments = selectedFlight.return?.segments ?? [];
1305
-
1306
- if (!outwardSegments.length || !returnSegments.length) return [];
1307
-
1308
- const outwardLabel = buildFlightLabel(outwardSegments);
1309
- const returnLabel = buildFlightLabel(returnSegments);
1310
-
1311
- const outwardFirst = first(outwardSegments);
1312
- const outwardLast = last(outwardSegments);
1313
- const returnFirst = first(returnSegments);
1314
- const returnLast = last(returnSegments);
1315
-
1316
- const outwardLine = {
1317
- guid: selectedFlight.outwardGuid,
1318
- parentGuid: null,
1319
- order: 0,
1320
- isChanged: true,
1321
- from: outwardFirst?.departureDateTime.toString() ?? '',
1322
- to: outwardLast?.arrivalDateTime.toString() ?? '',
1323
- serviceType: FLIGHT_SERVICE_TYPE,
1324
- productName: outwardLabel.productName,
1325
- productCode: outwardLabel.productCode,
1326
- accommodationName: outwardFirst?.metaData?.farePriceClassName,
1327
- accommodationCode: outwardFirst?.metaData?.fareCode,
1328
- regimeName: null,
1329
- regimeCode: null,
1330
- country: null,
1331
- region: null,
1332
- oord: null,
1333
- location: null,
1334
- longitude: null,
1335
- latitude: null,
1336
- // pax: allPaxAssignments,
1337
- flightInformation: {
1338
- pnr: '',
1339
- flightLines: mapFlightSegmentsToFlightLines(outwardSegments)
1340
- }
1341
- } as PackagingEntryLine;
1342
-
1343
- const returnLine = {
1344
- guid: selectedFlight.returnGuid,
1345
- parentGuid: selectedFlight.outwardGuid,
1346
- order: 1,
1347
- isChanged: true,
1348
- from: returnFirst?.departureDateTime.toString() ?? '',
1349
- to: returnLast?.departureDateTime.toString() ?? '',
1350
- serviceType: FLIGHT_SERVICE_TYPE,
1351
- productName: returnLabel.productName,
1352
- productCode: returnLabel.productCode,
1353
- accommodationName: returnFirst?.metaData?.farePriceClassName,
1354
- accommodationCode: returnFirst?.metaData?.fareCode,
1355
- regimeName: null,
1356
- regimeCode: null,
1357
- country: null,
1358
- region: null,
1359
- oord: null,
1360
- location: null,
1361
- longitude: null,
1362
- latitude: null,
1363
- // pax: allPaxAssignments,
1364
- flightInformation: {
1365
- pnr: '',
1366
- flightLines: mapFlightSegmentsToFlightLines(returnSegments)
1367
- }
1368
- } as PackagingEntryLine;
1369
-
1370
- return [outwardLine, returnLine];
1371
- };
1372
-
1373
- const buildOrUpdatePackagingEntryPartial = ({
1374
- sourceEntry,
1375
- selectedHotelCode,
1376
- accommodationResults,
1377
- selectedFlight,
1378
- confirmedExcursionsByDay,
1379
- seed,
1380
- transactionId,
1381
- language
1382
- }: BuildPackagingEntryPartialArgs): PackagingEntry | null => {
1383
- if (!seed?.rooms?.length) return null;
1384
-
1385
- const entry = buildBasePackagingEntry(sourceEntry, seed, transactionId, language);
1386
-
1387
- let nextLines = [...(entry.lines ?? [])];
1388
-
1389
- const selectedHotel = selectedHotelCode ? accommodationResults.find((r) => r.code === selectedHotelCode) : null;
1390
-
1391
- // Update accommodation only when enough data exists
1392
- if (selectedHotel) {
1393
- const accommodationLines = buildAccommodationLinesFromSelection(selectedHotel, seed);
1394
-
1395
- if (accommodationLines.length) {
1396
- nextLines = removeAccommodationLines(nextLines);
1397
- nextLines = [...nextLines, ...accommodationLines];
1398
- }
1399
- }
1400
-
1401
- // Update flights only when full selected combination exists
1402
- if (selectedFlight) {
1403
- const flightLines = buildFlightLinesFromSelection(selectedFlight);
1404
- if (flightLines.length) {
1405
- nextLines = removeFlightLines(nextLines);
1406
- nextLines = [...nextLines, ...flightLines];
1407
- }
1408
- }
1409
-
1410
- // excursions
1411
- const excursionLines = buildExcursionLinesFromConfirmedDays(confirmedExcursionsByDay);
1412
-
1413
- nextLines = removeExcursionLines(nextLines);
1414
-
1415
- if (excursionLines.length) {
1416
- nextLines = [...nextLines, ...excursionLines];
1417
- }
1418
-
1419
- nextLines = nextLines.map((line, index) => ({
1420
- ...line,
1421
- order: index
1422
- }));
1423
-
1424
- return {
1425
- ...entry,
1426
- language,
1427
- transactionId,
1428
- pax: entry.pax,
1429
- lines: nextLines
1430
- };
1431
- };
1432
-
1433
- const buildBasePackagingEntry = (
1434
- sourceEntry: PackagingEntry | null | undefined,
1435
- seed: SearchSeed,
1436
- transactionId: string,
1437
- language: string
1438
- ): PackagingEntry => {
1439
- if (sourceEntry) {
1440
- return structuredClone(sourceEntry);
1441
- }
1442
-
1443
- let paxId = 0;
1444
- const pax: PackagingEntryPax[] = [];
1445
- const rooms: PackagingEntryRoom[] = [];
1446
-
1447
- seed.rooms?.forEach((room, roomIndex) => {
1448
- const paxIds = room.pax.map((_, paxIndex) => {
1449
- const id = paxId++;
1450
- pax.push({
1451
- id,
1452
- firstName: _.firstName || '',
1453
- lastName: _.lastName || '',
1454
- dateOfBirth: _.dateOfBirth || null,
1455
- age: _.age || null,
1456
- isMainBooker: roomIndex === 0 && paxIndex === 0
1457
- });
1458
-
1459
- return id;
1460
- });
1461
-
1462
- rooms.push({
1463
- id: roomIndex,
1464
- paxIds
1465
- });
1466
- });
1467
-
1468
- return {
1469
- language,
1470
- transactionId,
1471
- dossierNumber: '',
1472
- status: context?.entryStatus,
1473
- customStatusId: context?.customEntryStatusId,
1474
- bookingDate: null,
1475
- price: 0,
1476
- depositAmount: 0,
1477
- pax,
1478
- rooms,
1479
- lines: [],
1480
- address: {} as PackagingEntryAddress
1481
- } as PackagingEntry;
1482
- };
1483
-
1484
- const handleShowMoreFlights = (flyInType: FlyInType) => {
1485
- dispatch(setFlyInType(flyInType));
1486
- dispatch(setFlyInIsOpen(true));
1487
- };
1488
-
1489
- return (
1490
- <div id="tide-booking" className="search__bg">
1491
- {context && (
1492
- <div className="search">
1493
- {bookPackagingEntry ? (
1494
- <BookPackagingEntry
1495
- activeSearchSeed={activeSearchSeed}
1496
- isLoading={itineraryIsLoading || pricesAreLoading}
1497
- isConfirmationPage={isBookingConfirmation}
1498
- />
1499
- ) : (
1500
- <div className="search__container">
1501
- {context.searchConfiguration.qsmType === PortalQsmType.Flight && (
1502
- <FlightSearchProvider tideConnection={context.tideConnection}>
1503
- <FlightResultsContainer isMobile={isMobile} />
1504
- <FlyIn
1505
- srpType={context.searchConfiguration.qsmType}
1506
- isOpen={flyInIsOpen}
1507
- setIsOpen={handleFlyInToggle}
1508
- onPanelRef={(el) => (panelRef.current = el)}
1509
- detailsLoading={detailsIsLoading}
1510
- filtersOpen={filtersOpen}
1511
- />
1512
- </FlightSearchProvider>
1513
- )}
1514
- {(context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight ||
1515
- context.searchConfiguration.qsmType === PortalQsmType.Accommodation ||
1516
- context.searchConfiguration.qsmType === PortalQsmType.GroupTour ||
1517
- context.searchConfiguration.qsmType === PortalQsmType.RoundTrip) && (
1518
- <>
1519
- {context.searchConfiguration.qsmType != PortalQsmType.AccommodationAndFlight && context.showFilters && (
1520
- <Filters
1521
- initialFilters={initialFilters}
1522
- filters={filters}
1523
- isOpen={filtersOpen}
1524
- handleSetIsOpen={() => setFiltersOpen(!filtersOpen)}
1525
- // handleApplyFilters={() => setSearchTrigger((prev) => prev + 1)}
1526
- isLoading={isLoading}
1527
- setFilters={(filters) => dispatch(setFilters(filters))}
1528
- resetFilters={(filters) => dispatch(resetFilters(filters))}
1529
- />
1530
- )}
1531
- {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && (
1532
- <Itinerary
1533
- isOpen={itineraryOpen}
1534
- handleSetIsOpen={() => setItineraryOpen(!itineraryOpen)}
1535
- isLoading={isLoading || pricesAreLoading || flightsLoading || itineraryIsLoading}
1536
- onEditAccommodation={handleEditAccommodation}
1537
- />
1538
- )}
1539
- {/* ---------------- Results ---------------- */}
1540
- <div className="search__results">
1541
- {isMobile && (
1542
- <div className="search__result-row">
1543
- <div className="search__results__actions">
1544
- {context.searchConfiguration.qsmType != PortalQsmType.AccommodationAndFlight && context.showFilters && (
1545
- <div className="cta cta--filter" onClick={() => setFiltersOpen(true)}>
1546
- <Icon name="ui-filter" className="mobile-filters-button__icon" height={16} />
1547
- {translations.SRP.FILTERS}
1548
- </div>
1549
- )}
1550
- {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && (
1551
- <div className="cta cta--filter" onClick={() => setItineraryOpen(true)}>
1552
- <Icon name="ui-shopping-cart" className="mobile-filters-button__icon" height={16} />
1553
- {translations.SRP.VIEW_BOOKING}
1554
- </div>
1555
- )}
1556
- </div>
1557
- {context.searchConfiguration.qsmType !== PortalQsmType.AccommodationAndFlight && sortByTypes && sortByTypes.length > 0 && (
1558
- <ItemPicker
1559
- items={sortByTypes}
1560
- selection={selectedSortType?.label || undefined}
1561
- selectedSortByType={selectedSortType}
1562
- label={translations.SRP.SORTBY}
1563
- placeholder={translations.SRP.SORTBY}
1564
- classModifier="travel-class-picker__items"
1565
- valueFormatter={(value, direction) => getSortingName(translations, findSortByType(sortByTypes, value, direction ?? 'asc'))}
1566
- onPick={(newSortKey, direction) => handleSortChange(newSortKey, direction)}
1567
- />
1568
- )}
1569
- </div>
1570
- )}
1571
- {context.searchConfiguration.qsmType !== PortalQsmType.AccommodationAndFlight && (
1572
- <div className="search__result-row">
1573
- <span className="search__result-row-text">
1574
- {!isLoading && (
1575
- <>
1576
- {context.searchConfiguration.qsmType === PortalQsmType.Accommodation &&
1577
- filteredPackagingAccoResults?.length &&
1578
- filteredPackagingAccoResults?.length}
1579
- {context.searchConfiguration.qsmType !== PortalQsmType.Accommodation && filteredResults?.length && filteredResults.length}
1580
- &nbsp;{translations.SRP.TOTAL_RESULTS_LABEL}
1581
- </>
1582
- )}
1583
- </span>
1584
- {!context.packagingEntry && !isMobile && sortByTypes && sortByTypes.length > 0 && (
1585
- <div className="search__result-row-filter">
1586
- <ItemPicker
1587
- items={sortByTypes}
1588
- selection={selectedSortType?.label || undefined}
1589
- selectedSortByType={selectedSortType}
1590
- label={translations.SRP.SORTBY}
1591
- placeholder={translations.SRP.SORTBY}
1592
- classModifier="travel-class-picker__items"
1593
- valueFormatter={(value, direction) => getSortingName(translations, findSortByType(sortByTypes, value, direction ?? 'asc'))}
1594
- onPick={(newSortKey, direction) => handleSortChange(newSortKey, direction)}
1595
- />
1596
- </div>
1597
- )}
1598
- </div>
1599
- )}
1600
-
1601
- <div className="search__results__wrapper">
1602
- {context.showTabViews &&
1603
- (context.searchConfiguration.qsmType === PortalQsmType.GroupTour ||
1604
- context.searchConfiguration.qsmType === PortalQsmType.Accommodation) && <TabViews />}
1605
-
1606
- {context.showRoundTripResults && context.showMockup && <RoundTripResults />}
1607
-
1608
- {context.searchConfiguration.qsmType === PortalQsmType.GroupTour && <GroupTourResults isLoading={isLoading} />}
1609
-
1610
- {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && !context.packagingEntry && context.showFlightResults && (
1611
- <>
1612
- <div className="search__results__label search__results__label--secondary">
1613
- <div className="search__results__label__date">
1614
- {(() => {
1615
- const firstResultDate = uniqueOutwardFlights.length > 0 ? uniqueOutwardFlights[0].outward.segments[0].departureDateTime : null;
1616
-
1617
- const firstResultDay = firstResultDate ? format(firstResultDate, 'd') : null;
1618
- const firstResultMonth = firstResultDate ? format(firstResultDate, 'MMM') : null;
1619
-
1620
- return (
1621
- <>
1622
- <p className="search__results__label__date-date">{firstResultDay}</p>
1623
- <p>{firstResultMonth}</p>
1624
- </>
1625
- );
1626
- })()}
1627
- </div>
1628
- <div className="search__results__label__text">
1629
- <Icon name="ui-flight" height={16} />
1630
- <h3>
1631
- {translations.SRP.SELECT} <strong> {translations.SRP.DEPARTURE}</strong>
1632
- </h3>
1633
- </div>
1634
- </div>
1635
-
1636
- {flightsLoading ? (
1637
- <Spinner label={translations.SRP.LOADING_FLIGHTS} />
1638
- ) : (
1639
- <>
1640
- <div className="search__results__cards search__results__cards--extended">
1641
- {selectedOutwardKey && selectedOutward && (
1642
- <IndependentFlightOption
1643
- key={`flight-${selectedOutwardKey}`}
1644
- item={selectedOutward.outward}
1645
- guid={selectedOutward.outwardGuid}
1646
- onSelect={() => dispatch(setSelectedOutwardKey(null))}
1647
- selectedGuid={selectedOutward.outwardGuid}
1648
- isOutward={true}
1649
- showSelectedState={true}
1650
- price={selectedOutward.price}
1651
- />
1652
- )}
1653
- {visibleOutwardFlights.map((result) => (
1654
- <IndependentFlightOption
1655
- key={`flight-${result.outwardGuid}`}
1656
- item={result.outward}
1657
- onSelect={() => dispatch(setSelectedOutwardKey(getFlightKey(result.outward.segments)))}
1658
- guid={result.outwardGuid}
1659
- isOutward={true}
1660
- price={result.price}
1661
- currentSelectedPrice={selectedOutward?.price}
1662
- />
1663
- ))}
1664
- </div>
1665
- {uniqueOutwardFlights && uniqueOutwardFlights.length > 3 && (
1666
- <div className="search__results__cards__actions">
1667
- <button className="cta cta--secondary" onClick={() => handleShowMoreFlights('flight-outward-results')}>
1668
- {translations.SRP.SHOW_MORE}
1669
- </button>
1670
- </div>
1671
- )}
1672
- </>
1673
- )}
1674
- </>
1675
- )}
1676
-
1677
- {context.showHotelAccommodationResults && !context.packagingEntry && <HotelAccommodationResults isLoading={isLoading} />}
1678
-
1679
- {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && !context.packagingEntry && <DayByDayExcursions />}
1680
-
1681
- {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && !context.packagingEntry && context.showFlightResults && (
1682
- <>
1683
- <div className="search__results__label search__results__label--secondary">
1684
- <div className="search__results__label__date">
1685
- {(() => {
1686
- const firstResultDate = uniqueReturnFlights.length > 0 ? uniqueReturnFlights[0].return.segments[0].departureDateTime : null;
1687
-
1688
- const firstResultDay = firstResultDate ? format(firstResultDate, 'd') : null;
1689
- const firstResultMonth = firstResultDate ? format(firstResultDate, 'MMM') : null;
1690
-
1691
- return (
1692
- <>
1693
- <p className="search__results__label__date-date">{firstResultDay}</p>
1694
- <p>{firstResultMonth}</p>
1695
- </>
1696
- );
1697
- })()}
1698
- </div>
1699
- <div className="search__results__label__text">
1700
- <Icon name="ui-flight" height={16} />
1701
- <h3>
1702
- {translations.SRP.SELECT} <strong> {translations.SRP.RETURN}</strong>
1703
- </h3>
1704
- </div>
1705
- </div>
1706
-
1707
- <div className="search__results__cards search__results__cards--extended">
1708
- {selectedReturnKey && selectedReturn && (
1709
- <IndependentFlightOption
1710
- key={`flight-${selectedReturnKey}`}
1711
- item={selectedReturn.return}
1712
- guid={selectedReturn.outwardGuid}
1713
- selectedGuid={selectedReturn.outwardGuid}
1714
- isOutward={false}
1715
- showSelectedState={true}
1716
- price={selectedReturn.price}
1717
- />
1718
- )}
1719
- {uniqueReturnFlights.map((result) => (
1720
- <IndependentFlightOption
1721
- key={`flight-${result.outwardGuid}`}
1722
- item={result.return}
1723
- onSelect={() => dispatch(setSelectedReturnKey(getFlightKey(result.return.segments)))}
1724
- guid={result.outwardGuid}
1725
- isOutward={false}
1726
- currentSelectedPrice={selectedReturn?.price}
1727
- price={result.price}
1728
- />
1729
- ))}
1730
- </div>
1731
- </>
1732
- )}
1733
-
1734
- {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && context.packagingEntry && (
1735
- <FullItinerary isLoading={itineraryIsLoading} />
1736
- )}
1737
- </div>
1738
- </div>
1739
- {/* <button onClick={() => handleFlyInToggle(!flyInIsOpen)}>Toggle FlyIn</button> */}
1740
- <FlyIn
1741
- srpType={context.searchConfiguration.qsmType}
1742
- isOpen={flyInIsOpen}
1743
- setIsOpen={handleFlyInToggle}
1744
- handleConfirm={() => handleConfirmHotelSwap()}
1745
- onPanelRef={(el) => (panelRef.current = el)}
1746
- detailsLoading={detailsIsLoading}
1747
- flyInType={flyInType}
1748
- isPackageEditFlow={!!context.packagingEntry}
1749
- sortByTypes={sortByTypes}
1750
- activeSearchSeed={activeSearchSeed}
1751
- toggleFilters={() => setFiltersOpen(!filtersOpen)}
1752
- filtersOpen={filtersOpen}
1753
- />
1754
- </>
1755
- )}
1756
- </div>
1757
- )}
1758
- </div>
1759
- )}
1760
- </div>
1761
- );
1762
- };
1763
-
1764
- export default SearchResultsContainer;
1
+ import React, { useContext, useEffect, useRef, useState } from 'react';
2
+ import { useDispatch, useSelector } from 'react-redux';
3
+ import { SearchResultsRootState } from '../../store/search-results-store';
4
+ import SearchResultsConfigurationContext from '../../search-results-configuration-context';
5
+ import {
6
+ resetFilters,
7
+ setSortType,
8
+ setResults,
9
+ setIsLoading,
10
+ setSelectedSearchResult,
11
+ setBookingPackageDetails,
12
+ setFlyInIsOpen,
13
+ setFilteredResults,
14
+ setPackagingAccoResults,
15
+ setFilteredPackagingAccoResults,
16
+ setPackagingAccoSearchDetails,
17
+ setEditablePackagingEntry,
18
+ setTransactionId,
19
+ setFlyInType,
20
+ setPriceDetails,
21
+ setItinerary,
22
+ setFlightsLoading,
23
+ setPackagingFlightResults,
24
+ setSelectedPackagingFlight,
25
+ setSelectedPackagingAccoResult,
26
+ setSelectedOutwardKey,
27
+ setSelectedReturnKey,
28
+ setFilteredPackagingFlightResults,
29
+ setInitialFlightFilters,
30
+ resetFlightFilters,
31
+ setFilters,
32
+ setInitialFilters,
33
+ setBookPackagingEntry
34
+ } from '../../store/search-results-slice';
35
+ import { FlyInType, SearchSeed, SortByType } from '../../types';
36
+ import useMediaQuery from '../../../shared/utils/use-media-query-util';
37
+ import ItemPicker from '../item-picker';
38
+ import {
39
+ TideClientConfig,
40
+ details,
41
+ search,
42
+ searchPackagingAccommodations,
43
+ BookingPackageDestination,
44
+ BookingPackageDetailsRequest,
45
+ BookingPackagePax,
46
+ BookingPackageRequest,
47
+ BookingPackageRequestRoom,
48
+ BookingPackageSearchRequest,
49
+ PackagingAccommodationRequest,
50
+ PackagingDestination,
51
+ PortalQsmType,
52
+ PackagingEntry,
53
+ startTransaction,
54
+ PackagingEntryLine,
55
+ getPriceDetails,
56
+ getItinerary,
57
+ FlightSearchRequest,
58
+ searchPackagingFlights,
59
+ PackagingFlightResponse,
60
+ PackagingAccommodationResponse,
61
+ FlightSearchResponseFlightSegment,
62
+ PackagingEntryLineFlightLine,
63
+ PackagingEntryAddress,
64
+ PackagingEntryPax,
65
+ PackagingEntryRoom,
66
+ PackagingRequestBase
67
+ } from '@qite/tide-client';
68
+ import { getDateFromParams, getNumberFromParams, getRoomsFromParams, getStringFromParams } from '../../../shared/utils/query-string-util';
69
+ import { concat, first, isEmpty, last, range } from 'lodash';
70
+ import { Room } from '../../../booking-wizard/types';
71
+ import Icon from '../../../shared/components/icon';
72
+ import Itinerary from '../itinerary';
73
+ import TabViews from '../tab-views';
74
+ import FlyIn from '../../../shared/components/flyin/flyin';
75
+ import HotelAccommodationResults from '../hotel/hotel-accommodation-results';
76
+ import RoundTripResults from '../round-trip/round-trip-results';
77
+ import { dateToDateStruct, findSortByType, getSortingName, getTranslations } from '../../../shared/utils/localization-util';
78
+ import { FlightSearchProvider } from '../flight/flight-search-context';
79
+ import FlightResultsContainer from './flight-search-results';
80
+ import Filters from '../filters/filters';
81
+ import GroupTourResults from '../group-tour/group-tour-results';
82
+ import {
83
+ applyFilters,
84
+ applyFiltersToPackageAccoResults,
85
+ applyFiltersToPackageFlightResults,
86
+ enrichFiltersWithPackageAccoResults,
87
+ enrichFiltersWithPackageFlightResults,
88
+ enrichFiltersWithResults
89
+ } from '../../utils/search-results-utils';
90
+ import {
91
+ ACCOMMODATION_SERVICE_TYPE,
92
+ EXCURSION_SERVICE_TYPE,
93
+ FLIGHT_SERVICE_TYPE,
94
+ getDepartureAirportFromEntry,
95
+ getDestinationAirportFromEntry,
96
+ getPackagingRequestRoomsFromBookingRooms,
97
+ getRequestRoomsFromPackagingEntry,
98
+ GROUP_TOUR_SERVICE_TYPE,
99
+ parseHotelId,
100
+ toDateOnlyString
101
+ } from '../../utils/query-utils';
102
+ import { getRequestRoomsFromPackagingSegments, getSelectedOptionsPerRoom } from '../../utils/packaging-utils';
103
+ import FullItinerary from '../itinerary/full-itinerary';
104
+ import { getFlightKey } from '../../utils/flight-utils';
105
+ import IndependentFlightOption from '../flight/flight-selection/independent-flight-option';
106
+ import { Spinner } from '../../..';
107
+ import {
108
+ selectSelectedCombinationFlight,
109
+ selectSelectedOutward,
110
+ selectSelectedOutwardKey,
111
+ selectSelectedReturn,
112
+ selectSelectedReturnKey,
113
+ selectUniqueOutwardFlights,
114
+ selectUniqueReturnFlights
115
+ } from '../../store/search-results-selectors';
116
+ import DayByDayExcursions from '../excursions/day-by-day-excursions';
117
+ import BookPackagingEntry from '../book-packaging-entry';
118
+ import { format } from 'date-fns';
119
+
120
+ type BuildPackagingEntryPartialArgs = {
121
+ sourceEntry: PackagingEntry | null | undefined;
122
+ selectedHotelCode: string | null | undefined;
123
+ accommodationResults: PackagingAccommodationResponse[];
124
+ selectedFlight: PackagingFlightResponse | null;
125
+ confirmedExcursionsByDay: Record<string, PackagingAccommodationResponse[]>;
126
+ seed: SearchSeed;
127
+ transactionId: string;
128
+ language: string;
129
+ };
130
+
131
+ interface SearchResultsContainerProps {
132
+ onBookingStarted?: () => void;
133
+ }
134
+
135
+ const SearchResultsContainer: React.FC<SearchResultsContainerProps> = ({ onBookingStarted }) => {
136
+ const currentSearch = typeof window !== 'undefined' ? window.location.search : '';
137
+
138
+ const dispatch = useDispatch();
139
+
140
+ const context = useContext(SearchResultsConfigurationContext);
141
+ const translations = getTranslations(context?.languageCode ?? 'en-GB');
142
+
143
+ const {
144
+ results,
145
+ filteredResults,
146
+ packagingAccoResults,
147
+ filteredPackagingAccoResults,
148
+ isLoading,
149
+ flightsLoading,
150
+ initialFilters,
151
+ filters,
152
+ flightFilters,
153
+ selectedSortType,
154
+ selectedFlightSortType,
155
+ selectedSearchResult,
156
+ selectedPackagingAccoResultCode,
157
+ flyInIsOpen,
158
+ packagingAccoSearchDetails,
159
+ editablePackagingEntry,
160
+ transactionId,
161
+ flyInType,
162
+ packagingFlightResults,
163
+ confirmedExcursionsByDay,
164
+ bookPackagingEntry
165
+ } = useSelector((state: SearchResultsRootState) => state.searchResults);
166
+
167
+ const isMobile = useMediaQuery('(max-width: 1200px)');
168
+
169
+ const [initialFiltersSet, setInitialFiltersSet] = useState(false);
170
+ const [initialFlightFiltersSet, setInitialFlightFiltersSet] = useState(false);
171
+ const [filtersOpen, setFiltersOpen] = useState(false);
172
+
173
+ const [detailsIsLoading, setDetailsIsLoading] = useState(false);
174
+ const [pricesAreLoading, setPricesAreLoading] = useState(false);
175
+ const [itineraryIsLoading, setItineraryIsLoading] = useState(false);
176
+
177
+ const [itineraryOpen, setItineraryOpen] = useState(false);
178
+ const [isBookingConfirmation, setIsBookingConfirmation] = useState(false);
179
+
180
+ const [selectedAccommodationSeed, setSelectedAccommodationSeed] = useState<SearchSeed | null>(null);
181
+
182
+ const skipInitialPackagingAccoDetailsRef = useRef(false);
183
+
184
+ const panelRef = useRef<HTMLDivElement | null>(null);
185
+
186
+ const sortByTypes: SortByType[] = [
187
+ { direction: 'asc', label: 'default' } as SortByType,
188
+ { direction: 'asc', label: 'price' } as SortByType,
189
+ { direction: 'desc', label: 'price' } as SortByType
190
+ ];
191
+
192
+ const handleFlyInToggle = (isOpen: boolean) => {
193
+ dispatch(setFlyInIsOpen(isOpen));
194
+ };
195
+
196
+ const handleSortChange = (newSortKey: string, direction?: string) => {
197
+ const newSortByType = findSortByType(sortByTypes, newSortKey, direction ?? 'asc');
198
+ if (newSortByType) {
199
+ dispatch(setSortType(newSortByType));
200
+ }
201
+ };
202
+
203
+ const getRequestRooms = (rooms: Room[] | null) => {
204
+ if (!rooms) {
205
+ // Fall back to 2 adults
206
+ var room = { index: 0, pax: [] } as BookingPackageRequestRoom;
207
+ range(0, 2).forEach(() => {
208
+ room.pax.push({
209
+ age: 30
210
+ } as BookingPackagePax);
211
+ });
212
+ return [room];
213
+ }
214
+
215
+ const requestRooms = rooms?.map((x, i) => {
216
+ var room = { index: i, pax: [] } as BookingPackageRequestRoom;
217
+ range(0, x.adults).forEach(() => {
218
+ room.pax.push({
219
+ age: 30
220
+ } as BookingPackagePax);
221
+ });
222
+ x.childAges.forEach((x) => {
223
+ room.pax.push({
224
+ age: x
225
+ } as BookingPackagePax);
226
+ });
227
+ return room;
228
+ });
229
+
230
+ return requestRooms;
231
+ };
232
+
233
+ const buildSearchFromSeed = (seed: SearchSeed): BookingPackageRequest<BookingPackageSearchRequest> => {
234
+ const country = seed.country;
235
+ const region = seed.region;
236
+ const oord = seed.oord;
237
+ const city = seed.location;
238
+ const hotel = seed.hotel;
239
+ const tagId = seed.tagId;
240
+
241
+ if (typeof window !== 'undefined') {
242
+ window.scrollTo(0, 0);
243
+ }
244
+
245
+ let destinationId: number | null = null;
246
+ let destinationIsCountry = false;
247
+ let destinationIsRegion = false;
248
+ let destinationIsOord = false;
249
+ let destinationIsLocation = false;
250
+
251
+ if (country) {
252
+ destinationId = country;
253
+ destinationIsCountry = true;
254
+ } else if (region) {
255
+ destinationId = region;
256
+ destinationIsRegion = true;
257
+ } else if (oord) {
258
+ destinationId = oord;
259
+ destinationIsOord = true;
260
+ } else if (city) {
261
+ destinationId = city;
262
+ destinationIsLocation = true;
263
+ }
264
+
265
+ return {
266
+ officeId: 1,
267
+ agentId: context?.agentId,
268
+ payload: {
269
+ catalogueIds: context!.searchConfiguration.defaultCatalogueId
270
+ ? [context!.searchConfiguration.defaultCatalogueId]
271
+ : context?.tideConnection?.catalogueIds ?? [1],
272
+ serviceType:
273
+ context!.searchConfiguration.qsmType === PortalQsmType.Accommodation || context!.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight
274
+ ? ACCOMMODATION_SERVICE_TYPE
275
+ : context!.searchConfiguration.qsmType === PortalQsmType.Flight
276
+ ? FLIGHT_SERVICE_TYPE
277
+ : context!.searchConfiguration.qsmType === PortalQsmType.RoundTrip
278
+ ? GROUP_TOUR_SERVICE_TYPE
279
+ : undefined,
280
+ searchType: context!.searchConfiguration.qsmType === PortalQsmType.GroupTour ? 1 : 0,
281
+ destination: {
282
+ id: Number(destinationId),
283
+ isCountry: destinationIsCountry,
284
+ isRegion: destinationIsRegion,
285
+ isOord: destinationIsOord,
286
+ isLocation: destinationIsLocation
287
+ } as BookingPackageDestination,
288
+ rooms: seed.rooms,
289
+ fromDate: seed.fromDate,
290
+ toDate: seed.toDate,
291
+ earliestFromOffset: 0,
292
+ latestToOffset: 0,
293
+ includeFlights: context!.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight,
294
+ useExactDates: context?.searchConfiguration.qsmType === PortalQsmType.GroupTour ? false : true,
295
+ onlyCachedResults: false,
296
+ includeAllAllotments: true,
297
+ productIds: hotel ? [hotel] : [],
298
+ productTagIds: tagId ? [tagId] : []
299
+ }
300
+ };
301
+ };
302
+
303
+ const buildPackagingAccommodationRequestFromSeed = (seed: SearchSeed, currentTransactionId: string): PackagingAccommodationRequest => {
304
+ const country = seed.country;
305
+ const region = seed.region;
306
+ const oord = seed.oord;
307
+ const city = seed.location;
308
+ const hotelCode = seed.hotelCode ?? (seed.hotel ? seed.hotel.toString() : '');
309
+ const tagId = seed.tagId;
310
+ const destinationAirport = seed.destinationAirport;
311
+
312
+ if (typeof window !== 'undefined') {
313
+ window.scrollTo(0, 0);
314
+ }
315
+
316
+ let destinationId: number | null = null;
317
+ let destinationIsCountry = false;
318
+ let destinationIsRegion = false;
319
+ let destinationIsOord = false;
320
+ let destinationIsLocation = false;
321
+ let destinationCode: string | null = null;
322
+ let destinationIsAirport = false;
323
+
324
+ if (city) {
325
+ destinationId = city;
326
+ destinationIsLocation = true;
327
+ } else if (oord) {
328
+ destinationId = oord;
329
+ destinationIsOord = true;
330
+ } else if (region) {
331
+ destinationId = region;
332
+ destinationIsRegion = true;
333
+ } else if (country) {
334
+ destinationId = country;
335
+ destinationIsCountry = true;
336
+ } else if (destinationAirport) {
337
+ destinationCode = destinationAirport;
338
+ destinationIsAirport = true;
339
+ }
340
+
341
+ return {
342
+ transactionId: currentTransactionId,
343
+ officeId: context?.tideConnection?.officeId ?? 1,
344
+ agentId: context?.agentId ?? null,
345
+ catalogueId: context!.searchConfiguration.defaultCatalogueId ?? first(context?.tideConnection?.catalogueIds) ?? 1,
346
+ searchConfigurationId: context!.searchConfiguration.id,
347
+ language: context!.languageCode ?? 'en-GB',
348
+ serviceType: ACCOMMODATION_SERVICE_TYPE,
349
+ fromDate: seed.fromDate,
350
+ toDate: seed.toDate,
351
+ destination: {
352
+ id: Number(destinationId),
353
+ isCountry: destinationIsCountry,
354
+ isRegion: destinationIsRegion,
355
+ isOord: destinationIsOord,
356
+ isLocation: destinationIsLocation,
357
+ isAirport: destinationIsAirport,
358
+ code: destinationCode
359
+ } as PackagingDestination,
360
+ productCode: '',
361
+ rooms: getPackagingRequestRoomsFromBookingRooms(seed.rooms),
362
+ tagIds: tagId ? [tagId] : []
363
+ };
364
+ };
365
+
366
+ const buildPackagingFlightRequestFromSeed = (seed: SearchSeed, currentTransactionId: string): FlightSearchRequest => {
367
+ if (typeof window !== 'undefined') {
368
+ window.scrollTo(0, 0);
369
+ }
370
+ var adults = seed.rooms.flatMap((x) => x.pax).filter((x) => x.age! >= 12).length;
371
+ var kids = seed.rooms.flatMap((x) => x.pax).filter((x) => x.age! >= 2 && x.age! < 12).length;
372
+ var babies = seed.rooms.flatMap((x) => x.pax).filter((x) => x.age! < 2).length;
373
+
374
+ return {
375
+ transactionId: currentTransactionId,
376
+ officeId: context?.tideConnection?.officeId ?? 1,
377
+ catalogueId: context!.searchConfiguration.defaultCatalogueId ?? first(context?.tideConnection?.catalogueIds) ?? 1,
378
+ departureAirportCode: seed.departureAirport,
379
+ arrivalAirportCode: seed.destinationAirport,
380
+ returnAirportCode: seed.returnAirport,
381
+ luggageIncluded: null,
382
+ maxStops: null,
383
+ travelClass: seed.travelClass,
384
+ pax: concat(
385
+ Array.from({ length: adults ?? 0 }, (_, index) => ({
386
+ id: index,
387
+ age: 31
388
+ })),
389
+ Array.from({ length: kids ?? 0 }, (_, index) => ({
390
+ id: index + (adults ?? 0),
391
+ age: 8
392
+ })),
393
+ Array.from({ length: babies ?? 0 }, (_, index) => ({
394
+ id: index + (adults ?? 0) + (kids ?? 0),
395
+ age: 1
396
+ }))
397
+ ),
398
+ outward: seed.fromDate ? { date: dateToDateStruct(new Date(seed.fromDate)) } : null,
399
+ return: seed.toDate ? { date: dateToDateStruct(new Date(seed.toDate)) } : null
400
+ } as FlightSearchRequest;
401
+ };
402
+
403
+ const buildSearchSeedFromQueryParams = (params: URLSearchParams): SearchSeed | null => {
404
+ const from = getDateFromParams(params, 'fromDate');
405
+ const to = getDateFromParams(params, 'toDate');
406
+ const rooms = getRoomsFromParams(params, 'rooms');
407
+ const country = getNumberFromParams(params, 'country');
408
+ const region = getNumberFromParams(params, 'region');
409
+ const oord = getNumberFromParams(params, 'oord');
410
+ const city = getNumberFromParams(params, 'location');
411
+ const hotel = getNumberFromParams(params, 'hotel');
412
+ const tagId = getNumberFromParams(params, 'tagId');
413
+ const destinationAirport = getStringFromParams(params, 'destinationAirport');
414
+ const departureAirport = getStringFromParams(params, 'departureAirport');
415
+ const travelClass = getStringFromParams(params, 'travelClass');
416
+ const nationality = getStringFromParams(params, 'nationality');
417
+
418
+ if (!from || !to) {
419
+ return null;
420
+ }
421
+
422
+ return {
423
+ fromDate: from,
424
+ toDate: to,
425
+ country,
426
+ region,
427
+ oord,
428
+ location: city,
429
+ hotel,
430
+ hotelCode: hotel ? hotel.toString() : null,
431
+ tagId,
432
+ destinationAirport,
433
+ departureAirport,
434
+ travelClass,
435
+ nationality,
436
+ rooms: getRequestRooms(rooms)
437
+ };
438
+ };
439
+
440
+ const buildSearchSeedFromAccommodationSegments = (entry: PackagingEntry, segments: PackagingEntryLine[]): SearchSeed | null => {
441
+ if (!segments?.length) return null;
442
+
443
+ const sortedSegments = [...segments].sort((a, b) => new Date(a.from).getTime() - new Date(b.from).getTime());
444
+
445
+ const firstSegment = first(sortedSegments);
446
+ const lastSegment = last(sortedSegments);
447
+
448
+ if (!firstSegment || !lastSegment) return null;
449
+
450
+ return {
451
+ fromDate: toDateOnlyString(firstSegment.from),
452
+ toDate: toDateOnlyString(lastSegment.to),
453
+ country: firstSegment.country?.id ?? null,
454
+ region: firstSegment.region?.id ?? null,
455
+ oord: firstSegment.oord?.id ?? null,
456
+ location: firstSegment.location?.id ?? null,
457
+ hotel: parseHotelId(firstSegment),
458
+ hotelCode: firstSegment.productCode ?? null,
459
+ tagId: null,
460
+ destinationAirport: getDestinationAirportFromEntry(entry.lines ?? []),
461
+ departureAirport: getDepartureAirportFromEntry(entry.lines ?? []),
462
+ rooms: getRequestRoomsFromPackagingSegments(entry, sortedSegments)
463
+ };
464
+ };
465
+
466
+ const handleEditAccommodation = async (segments: PackagingEntryLine[]) => {
467
+ const sourceEntry = editablePackagingEntry ?? context?.packagingEntry;
468
+ if (!sourceEntry) return;
469
+
470
+ const seed = buildSearchSeedFromAccommodationSegments(sourceEntry, segments);
471
+ if (!seed) return;
472
+
473
+ setDetailsIsLoading(true);
474
+
475
+ setSelectedAccommodationSeed(seed);
476
+ dispatch(setFlyInType('acco-results'));
477
+ handleFlyInToggle(true);
478
+ const currentTransactionId = await getOrCreateTransactionId();
479
+ await runAccommodationFlow(seed, currentTransactionId ?? '');
480
+ setDetailsIsLoading(false);
481
+ };
482
+
483
+ const handleConfirmHotelSwap = () => {
484
+ const updatedEntry = swapHotelInPackagingEntry();
485
+ if (!updatedEntry) return;
486
+ dispatch(setEditablePackagingEntry(updatedEntry));
487
+ handleFlyInToggle(false);
488
+ };
489
+
490
+ const swapHotelInPackagingEntry = (): PackagingEntry | null => {
491
+ const sourceEntry = editablePackagingEntry ?? context?.packagingEntry;
492
+ const details = packagingAccoSearchDetails;
493
+
494
+ if (!sourceEntry || !details?.length) return null;
495
+
496
+ const selectedOptionsPerRoom = getSelectedOptionsPerRoom(details);
497
+ if (!selectedOptionsPerRoom.length) return null;
498
+
499
+ const selectedHotel = details[0];
500
+
501
+ let roomIndex = 0;
502
+ const updatedLines = sourceEntry.lines.map((line) => {
503
+ if (line.serviceType !== ACCOMMODATION_SERVICE_TYPE) {
504
+ return line;
505
+ }
506
+
507
+ const selectedRoom = selectedOptionsPerRoom.find((x) => x.roomIndex === roomIndex);
508
+ const selectedOption = selectedRoom?.option;
509
+ roomIndex++;
510
+
511
+ if (!selectedOption) {
512
+ return line;
513
+ }
514
+
515
+ return {
516
+ ...line,
517
+ guid: selectedOption.guid,
518
+ productName: selectedHotel.name,
519
+ productCode: selectedHotel.code,
520
+ accommodationName: selectedOption.accommodationName,
521
+ accommodationCode: selectedOption.accommodationCode,
522
+ regimeName: selectedOption.regimeName,
523
+ regimeCode: selectedOption.regimeCode,
524
+
525
+ country: line.country
526
+ ? { ...line.country, id: selectedHotel.countryId ?? line.country.id, name: selectedHotel.countryName ?? line.country.name }
527
+ : selectedHotel.countryId
528
+ ? { id: selectedHotel.countryId, name: selectedHotel.countryName, localizations: [] }
529
+ : line.country,
530
+
531
+ region: line.region
532
+ ? { ...line.region, id: selectedHotel.regionId ?? line.region.id, name: selectedHotel.regionName ?? line.region.name }
533
+ : selectedHotel.regionId
534
+ ? { id: selectedHotel.regionId, name: selectedHotel.regionName, localizations: [] }
535
+ : line.region,
536
+
537
+ oord: line.oord
538
+ ? { ...line.oord, id: selectedHotel.oordId ?? line.oord.id, name: selectedHotel.oordName ?? line.oord.name }
539
+ : selectedHotel.oordId
540
+ ? { id: selectedHotel.oordId, name: selectedHotel.oordName, localizations: [] }
541
+ : line.oord,
542
+
543
+ location: line.location
544
+ ? { ...line.location, id: selectedHotel.locationId ?? line.location.id, name: selectedHotel.locationName ?? line.location.name }
545
+ : selectedHotel.locationId
546
+ ? { id: selectedHotel.locationId, name: selectedHotel.locationName, localizations: [] }
547
+ : line.location,
548
+
549
+ latitude: selectedHotel.latitude ?? line.latitude,
550
+ longitude: selectedHotel.longitude ?? line.longitude,
551
+
552
+ from: selectedHotel.fromDate ?? line.from,
553
+ to: selectedHotel.toDate ?? line.to,
554
+ isChanged: true
555
+ };
556
+ });
557
+
558
+ return {
559
+ ...sourceEntry,
560
+ lines: updatedLines
561
+ };
562
+ };
563
+
564
+ const activeSearchSeed = React.useMemo(() => {
565
+ if (selectedAccommodationSeed) {
566
+ return selectedAccommodationSeed;
567
+ }
568
+
569
+ if (typeof window === 'undefined') return null;
570
+
571
+ const params = new URLSearchParams(window.location.search);
572
+ return buildSearchSeedFromQueryParams(params);
573
+ }, [selectedAccommodationSeed, currentSearch]);
574
+
575
+ useEffect(() => {
576
+ if (typeof document !== 'undefined') {
577
+ document.body.classList.toggle('has-overlay', filtersOpen);
578
+ }
579
+ }, [filtersOpen]);
580
+
581
+ const getOrCreateTransactionId = async (): Promise<string | null> => {
582
+ if (context?.packagingEntry?.transactionId) {
583
+ return context.packagingEntry.transactionId;
584
+ }
585
+
586
+ if (transactionId) {
587
+ return transactionId;
588
+ }
589
+
590
+ return await runStartTransaction();
591
+ };
592
+
593
+ const runSearch = async () => {
594
+ try {
595
+ if (!context) return;
596
+ dispatch(setIsLoading(true));
597
+
598
+ const config: TideClientConfig = {
599
+ host: context.tideConnection.host,
600
+ apiKey: context.tideConnection.apiKey
601
+ };
602
+
603
+ const seed = activeSearchSeed;
604
+ if (!seed) {
605
+ throw new Error('Invalid search parameters');
606
+ }
607
+
608
+ const searchRequest = buildSearchFromSeed(seed);
609
+ const packageSearchResults = await search(config, searchRequest);
610
+
611
+ const enrichedFilters = enrichFiltersWithResults(packageSearchResults, context.filters, context.tags ?? []);
612
+ if (!initialFiltersSet) {
613
+ dispatch(resetFilters(enrichedFilters));
614
+ dispatch(setInitialFilters(enrichedFilters));
615
+ setInitialFiltersSet(true);
616
+ }
617
+
618
+ dispatch(setResults(packageSearchResults));
619
+ const initialFilteredResults = applyFilters(packageSearchResults, filters, null);
620
+ dispatch(setFilteredResults(initialFilteredResults));
621
+
622
+ if (packageSearchResults?.length > 0) {
623
+ if (context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight) {
624
+ dispatch(setSelectedSearchResult(packageSearchResults[0]));
625
+ }
626
+ }
627
+ } catch (err) {
628
+ console.error('Search failed', err);
629
+ } finally {
630
+ dispatch(setIsLoading(false));
631
+ }
632
+ };
633
+
634
+ const runStartTransaction = async (): Promise<string | null> => {
635
+ try {
636
+ if (!context) return null;
637
+
638
+ dispatch(setIsLoading(true));
639
+
640
+ const config: TideClientConfig = {
641
+ host: context.tideConnection.host,
642
+ apiKey: context.tideConnection.apiKey
643
+ };
644
+
645
+ const transaction = await startTransaction(config);
646
+
647
+ dispatch(setTransactionId(transaction.transactionId));
648
+ dispatch(setIsLoading(false));
649
+ return transaction.transactionId;
650
+ } catch (err) {
651
+ console.error('Transaction failed', err);
652
+ dispatch(setIsLoading(false));
653
+ return null;
654
+ }
655
+ };
656
+
657
+ const runHotelSearch = async (currentTransactionId: string, seed: SearchSeed) => {
658
+ try {
659
+ if (!context) return;
660
+ dispatch(setIsLoading(true));
661
+
662
+ const config: TideClientConfig = {
663
+ host: context.tideConnection.host,
664
+ apiKey: context.tideConnection.apiKey
665
+ };
666
+
667
+ let searchRequest: PackagingAccommodationRequest = buildPackagingAccommodationRequestFromSeed(seed, currentTransactionId);
668
+ searchRequest.portalId = context.portalId;
669
+ searchRequest.agentId = context.agentId;
670
+
671
+ const packageAccoSearchResults = await searchPackagingAccommodations(config, searchRequest);
672
+
673
+ const enrichedFilters = enrichFiltersWithPackageAccoResults(packageAccoSearchResults, context.tags ?? []);
674
+ if (!initialFiltersSet) {
675
+ dispatch(resetFilters(enrichedFilters));
676
+ setInitialFilters(enrichedFilters);
677
+ setInitialFiltersSet(true);
678
+ }
679
+
680
+ dispatch(setPackagingAccoResults(packageAccoSearchResults));
681
+ const initialFilteredResults = applyFiltersToPackageAccoResults(packageAccoSearchResults, filters, null);
682
+ dispatch(setFilteredPackagingAccoResults(initialFilteredResults));
683
+
684
+ if (initialFilteredResults.length > 0) {
685
+ skipInitialPackagingAccoDetailsRef.current = true;
686
+ dispatch(setSelectedPackagingAccoResult(first(initialFilteredResults)?.code ?? null));
687
+ }
688
+
689
+ dispatch(setIsLoading(false));
690
+ } catch (err) {
691
+ console.error('HotelSearch failed', err);
692
+ dispatch(setIsLoading(false));
693
+ }
694
+ };
695
+
696
+ const runAccommodationFlow = async (seed: SearchSeed, currentTransactionId: string) => {
697
+ if (!context || context.showMockup) return;
698
+ await runHotelSearch(currentTransactionId, seed);
699
+ };
700
+
701
+ const runFlightSearch = async (currentTransactionId: string, seed: SearchSeed) => {
702
+ try {
703
+ if (!context) return;
704
+ dispatch(setFlightsLoading(true));
705
+
706
+ const config: TideClientConfig = {
707
+ host: context.tideConnection.host,
708
+ apiKey: context.tideConnection.apiKey
709
+ };
710
+
711
+ let searchRequest: FlightSearchRequest = buildPackagingFlightRequestFromSeed(seed, currentTransactionId);
712
+ searchRequest.agentId = context.agentId;
713
+
714
+ const packageFlightSearchResults = await searchPackagingFlights(config, searchRequest);
715
+
716
+ const enrichedFilters = enrichFiltersWithPackageFlightResults(packageFlightSearchResults, context.tags ?? [], translations);
717
+ if (!initialFlightFiltersSet) {
718
+ dispatch(resetFlightFilters(enrichedFilters));
719
+ dispatch(setInitialFlightFilters(enrichedFilters));
720
+ setInitialFlightFiltersSet(true);
721
+ }
722
+
723
+ dispatch(setPackagingFlightResults(packageFlightSearchResults));
724
+
725
+ const initialFilteredResults = applyFiltersToPackageFlightResults(packageFlightSearchResults, filters, null);
726
+ dispatch(setFilteredPackagingFlightResults(initialFilteredResults));
727
+
728
+ if (initialFilteredResults.length > 0) {
729
+ const firstResult = first(packageFlightSearchResults);
730
+ if (firstResult) {
731
+ dispatch(setSelectedOutwardKey(getFlightKey(firstResult.outward.segments)));
732
+ dispatch(setSelectedReturnKey(getFlightKey(firstResult.return.segments)));
733
+ }
734
+ }
735
+
736
+ dispatch(setFlightsLoading(false));
737
+ } catch (err) {
738
+ console.error('FlightSearch failed', err);
739
+ dispatch(setFlightsLoading(false));
740
+ }
741
+ };
742
+
743
+ const runFlightFlow = async (seed: SearchSeed, currentTransactionId: string) => {
744
+ if (!context || context.showMockup) return;
745
+ await runFlightSearch(currentTransactionId, seed);
746
+ };
747
+
748
+ // separate Search
749
+ useEffect(() => {
750
+ if (
751
+ context?.searchConfiguration.qsmType === PortalQsmType.GroupTour ||
752
+ (context?.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && !context.searchConfiguration.enableManualPackaging)
753
+ ) {
754
+ runSearch();
755
+ }
756
+
757
+ if (context?.searchConfiguration.qsmType === PortalQsmType.Accommodation) {
758
+ const seed = activeSearchSeed;
759
+
760
+ if (seed) {
761
+ (async () => {
762
+ const transactionId = await getOrCreateTransactionId();
763
+ if (!transactionId) return;
764
+
765
+ await runAccommodationFlow(seed, transactionId);
766
+ })();
767
+ }
768
+ }
769
+
770
+ if (
771
+ context?.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight &&
772
+ context.searchConfiguration.enableManualPackaging &&
773
+ !context?.packagingEntry
774
+ ) {
775
+ const seed = activeSearchSeed;
776
+
777
+ if (seed) {
778
+ (async () => {
779
+ const sharedTransactionId = await getOrCreateTransactionId();
780
+ if (!sharedTransactionId) return;
781
+
782
+ const tasks: Promise<void>[] = [];
783
+
784
+ if (context.searchConfiguration.allowAccommodations) {
785
+ tasks.push(runAccommodationFlow(seed, sharedTransactionId));
786
+ }
787
+
788
+ if (context.searchConfiguration.allowFlights) {
789
+ tasks.push(runFlightFlow(seed, sharedTransactionId));
790
+ }
791
+
792
+ await Promise.all(tasks);
793
+ })();
794
+ }
795
+ }
796
+ }, [
797
+ location.search,
798
+ context?.showMockup,
799
+ context?.searchConfiguration.qsmType,
800
+ context?.searchConfiguration.enableManualPackaging,
801
+ context?.searchConfiguration.allowAccommodations,
802
+ context?.packagingEntry?.transactionId,
803
+ activeSearchSeed
804
+ ]);
805
+
806
+ useEffect(() => {
807
+ if (context?.packagingEntry) {
808
+ dispatch(setEditablePackagingEntry(structuredClone(context.packagingEntry)));
809
+ dispatch(setTransactionId(context.packagingEntry.transactionId));
810
+
811
+ const params = new URLSearchParams(location.search);
812
+ const bookingConfirmation = getStringFromParams(params, 'bookingConfirmation');
813
+ console.log('bookingConfirmation', bookingConfirmation);
814
+ if (bookingConfirmation == 'true') {
815
+ setIsBookingConfirmation(true);
816
+ dispatch(setBookPackagingEntry(true));
817
+ }
818
+ }
819
+ }, [context?.packagingEntry]);
820
+
821
+ useEffect(() => {
822
+ if (bookPackagingEntry && onBookingStarted) {
823
+ onBookingStarted();
824
+ }
825
+ }, [bookPackagingEntry, onBookingStarted]);
826
+
827
+ // separate detailsCall
828
+ useEffect(() => {
829
+ const fetchDetails = async () => {
830
+ if (!selectedSearchResult || !context) return;
831
+ setDetailsIsLoading(true);
832
+ if (context?.searchConfiguration.qsmType === PortalQsmType.Accommodation || context?.searchConfiguration.qsmType === PortalQsmType.GroupTour) {
833
+ handleFlyInToggle(true);
834
+ }
835
+
836
+ try {
837
+ const config: TideClientConfig = {
838
+ host: context.tideConnection.host,
839
+ apiKey: context.tideConnection.apiKey
840
+ };
841
+
842
+ const selectedItem = results.find((r) => r.productId === selectedSearchResult.productId);
843
+ if (!selectedItem) {
844
+ // TODO: handle this case better, show an error message to the user
845
+ return;
846
+ }
847
+
848
+ let requestRooms: BookingPackageRequestRoom[];
849
+
850
+ if (context?.packagingEntry) {
851
+ requestRooms = getRequestRoomsFromPackagingEntry(context.packagingEntry);
852
+ } else {
853
+ const seed = activeSearchSeed;
854
+ requestRooms = seed?.rooms?.length ? seed.rooms : getRequestRooms(null);
855
+ }
856
+
857
+ const detailsRequest: BookingPackageRequest<BookingPackageDetailsRequest> = {
858
+ officeId: 1,
859
+ payload: {
860
+ catalogueId: selectedItem.catalogueId,
861
+ rooms: requestRooms,
862
+ searchType: 0, // same as search
863
+ productCode: selectedItem.code,
864
+ fromDate: selectedItem.fromDate,
865
+ toDate: selectedItem.toDate,
866
+ includeFlights: context!.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight ? true : false,
867
+ includeHotels: true,
868
+ includePaxTypes: true,
869
+ checkExternalAvailability: true,
870
+ expectedPrice: selectedItem.price,
871
+ duration: null,
872
+ preNights: null,
873
+ postNights: null
874
+ },
875
+ agentId: context.agentId
876
+ };
877
+
878
+ const detailsResponse = await details(config, detailsRequest);
879
+ dispatch(setBookingPackageDetails({ details: detailsResponse?.payload }));
880
+ setDetailsIsLoading(false);
881
+ } catch (err) {
882
+ console.error('Failed to fetch package details', err);
883
+ setDetailsIsLoading(false);
884
+ }
885
+ };
886
+
887
+ const fetchPackagingAccoSearchDetails = async () => {
888
+ if (!selectedPackagingAccoResultCode || !context) return;
889
+
890
+ if (skipInitialPackagingAccoDetailsRef.current) {
891
+ skipInitialPackagingAccoDetailsRef.current = false;
892
+ return;
893
+ }
894
+
895
+ setDetailsIsLoading(true);
896
+ if (
897
+ context?.searchConfiguration.qsmType === PortalQsmType.Accommodation ||
898
+ context?.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight ||
899
+ context?.searchConfiguration.qsmType === PortalQsmType.GroupTour
900
+ ) {
901
+ handleFlyInToggle(true);
902
+ dispatch(setFlyInType('acco-details'));
903
+ }
904
+
905
+ try {
906
+ const config: TideClientConfig = {
907
+ host: context.tideConnection.host,
908
+ apiKey: context.tideConnection.apiKey
909
+ };
910
+
911
+ const selectedItem = packagingAccoResults.find((r) => r.code === selectedPackagingAccoResultCode);
912
+ if (!selectedItem) {
913
+ // TODO: handle this case better, show an error message to the user
914
+ return;
915
+ }
916
+
917
+ const seed = activeSearchSeed;
918
+ const tagId = seed?.tagId ?? null;
919
+ const destinationAirport = seed?.destinationAirport ?? null;
920
+
921
+ let destinationId: number | null = null;
922
+ let destinationIsCountry = false;
923
+ let destinationIsRegion = false;
924
+ let destinationIsOord = false;
925
+ let destinationIsLocation = false;
926
+ let destinationCode: string | null = null;
927
+ let destinationIsAirport = false;
928
+
929
+ if (selectedItem.countryId) {
930
+ destinationId = selectedItem.countryId;
931
+ destinationIsCountry = true;
932
+ } else if (selectedItem.regionId) {
933
+ destinationId = selectedItem.regionId;
934
+ destinationIsRegion = true;
935
+ } else if (selectedItem.oordId) {
936
+ destinationId = selectedItem.oordId;
937
+ destinationIsOord = true;
938
+ } else if (selectedItem.locationId) {
939
+ destinationId = selectedItem.locationId;
940
+ destinationIsLocation = true;
941
+ } else if (destinationAirport) {
942
+ destinationCode = destinationAirport;
943
+ destinationIsAirport = true;
944
+ }
945
+
946
+ const detailSearchRequest: PackagingAccommodationRequest = {
947
+ transactionId: transactionId ?? '',
948
+ officeId: 1,
949
+ portalId: context.portalId,
950
+ agentId: context.agentId,
951
+ catalogueId: context!.searchConfiguration.defaultCatalogueId ?? first(context?.tideConnection?.catalogueIds) ?? 1,
952
+ searchConfigurationId: context!.searchConfiguration.id,
953
+ vendorConfigurationId: selectedItem.vendorId,
954
+ language: context!.languageCode ?? 'en-GB',
955
+ serviceType: ACCOMMODATION_SERVICE_TYPE,
956
+ fromDate: selectedItem.fromDate,
957
+ toDate: selectedItem.toDate,
958
+ destination: {
959
+ id: Number(destinationId),
960
+ isCountry: destinationIsCountry,
961
+ isRegion: destinationIsRegion,
962
+ isOord: destinationIsOord,
963
+ isLocation: destinationIsLocation,
964
+ isAirport: destinationIsAirport,
965
+ code: destinationCode
966
+ } as PackagingDestination,
967
+ productCode: selectedItem.code ? selectedItem.code : '',
968
+ rooms: getPackagingRequestRoomsFromBookingRooms(seed?.rooms ?? null),
969
+ tagIds: tagId ? [tagId] : []
970
+ };
971
+
972
+ const packageAccoSearchDetails = await searchPackagingAccommodations(config, detailSearchRequest);
973
+ dispatch(setPackagingAccoSearchDetails(packageAccoSearchDetails));
974
+ setDetailsIsLoading(false);
975
+ } catch (err) {
976
+ console.error('Failed to fetch package details', err);
977
+ setDetailsIsLoading(false);
978
+ }
979
+ };
980
+
981
+ if (selectedSearchResult) {
982
+ fetchDetails();
983
+ }
984
+ if (selectedPackagingAccoResultCode) {
985
+ fetchPackagingAccoSearchDetails();
986
+ }
987
+ }, [selectedSearchResult, selectedPackagingAccoResultCode]);
988
+
989
+ useEffect(() => {
990
+ if (context?.searchConfiguration.qsmType === PortalQsmType.Accommodation || context?.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight) {
991
+ const filteredPackageAccoResults = applyFiltersToPackageAccoResults(packagingAccoResults, filters, selectedSortType);
992
+ dispatch(setFilteredPackagingAccoResults(filteredPackageAccoResults));
993
+ } else {
994
+ const filteredResults = applyFilters(results, filters, selectedSortType);
995
+ dispatch(setFilteredResults(filteredResults));
996
+ }
997
+ }, [filters, results, packagingAccoResults, selectedSortType]);
998
+
999
+ useEffect(() => {
1000
+ if (context?.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight) {
1001
+ const filteredPackageFlightResults = applyFiltersToPackageFlightResults(packagingFlightResults, flightFilters, selectedFlightSortType);
1002
+ dispatch(setFilteredPackagingFlightResults(filteredPackageFlightResults));
1003
+ }
1004
+ }, [flightFilters, packagingFlightResults, selectedFlightSortType]);
1005
+
1006
+ useEffect(() => {
1007
+ setInitialFiltersSet(false);
1008
+ }, [activeSearchSeed]);
1009
+
1010
+ useEffect(() => {
1011
+ const fetchPriceDetails = async () => {
1012
+ if (!context || !editablePackagingEntry || isEmpty(editablePackagingEntry.lines)) return;
1013
+ setPricesAreLoading(true);
1014
+ try {
1015
+ const config: TideClientConfig = {
1016
+ host: context.tideConnection.host,
1017
+ apiKey: context.tideConnection.apiKey
1018
+ };
1019
+
1020
+ const request = {
1021
+ language: context.languageCode ?? 'en-GB',
1022
+ officeId: context.tideConnection.officeId,
1023
+ catalogueId: context!.searchConfiguration.defaultCatalogueId ?? first(context?.tideConnection?.catalogueIds) ?? 1,
1024
+ agentId: context.agentId,
1025
+ payload: editablePackagingEntry
1026
+ } as PackagingRequestBase<PackagingEntry>;
1027
+
1028
+ const priceDetails = await getPriceDetails(config, request);
1029
+ dispatch(setPriceDetails(priceDetails));
1030
+ setPricesAreLoading(false);
1031
+ } catch (err) {
1032
+ console.error('Error fetching price details', err);
1033
+ setPricesAreLoading(false);
1034
+ }
1035
+ };
1036
+
1037
+ const fetchItinerary = async () => {
1038
+ if (!context || !context.packagingEntry || !editablePackagingEntry || isEmpty(editablePackagingEntry.lines)) return;
1039
+ setItineraryIsLoading(true);
1040
+ try {
1041
+ const config: TideClientConfig = {
1042
+ host: context.tideConnection.host,
1043
+ apiKey: context.tideConnection.apiKey
1044
+ };
1045
+
1046
+ const request = {
1047
+ language: context.languageCode ?? 'en-GB',
1048
+ officeId: context.tideConnection.officeId,
1049
+ catalogueId: context!.searchConfiguration.defaultCatalogueId ?? first(context?.tideConnection?.catalogueIds) ?? 1,
1050
+ agentId: context.agentId,
1051
+ payload: editablePackagingEntry
1052
+ } as PackagingRequestBase<PackagingEntry>;
1053
+
1054
+ const itinerary = await getItinerary(config, request);
1055
+ dispatch(setItinerary(itinerary));
1056
+ setItineraryIsLoading(false);
1057
+ } catch (err) {
1058
+ console.error('Error fetching itinerary', err);
1059
+ setItineraryIsLoading(false);
1060
+ }
1061
+ };
1062
+
1063
+ fetchPriceDetails();
1064
+ fetchItinerary();
1065
+ }, [editablePackagingEntry]);
1066
+
1067
+ // Flight selection
1068
+ // const [selectedOutwardKey, setSelectedOutwardKey] = useState<string | null>(null);
1069
+ // const [selectedReturnKey, setSelectedReturnKey] = useState<string | null>(null);
1070
+ const selectedOutwardKey = useSelector(selectSelectedOutwardKey);
1071
+ const selectedReturnKey = useSelector(selectSelectedReturnKey);
1072
+ const uniqueOutwardFlights = useSelector(selectUniqueOutwardFlights);
1073
+ const uniqueReturnFlights = useSelector(selectUniqueReturnFlights);
1074
+ const selectedOutward = useSelector(selectSelectedOutward);
1075
+ const selectedReturn = useSelector(selectSelectedReturn);
1076
+ const selectedCombinationFlight = useSelector(selectSelectedCombinationFlight);
1077
+
1078
+ useEffect(() => {
1079
+ if (!selectedOutwardKey) {
1080
+ dispatch(setSelectedReturnKey(null));
1081
+ return;
1082
+ }
1083
+
1084
+ const matchingCombinations = packagingFlightResults.filter((flight) => getFlightKey(flight.outward.segments) === selectedOutwardKey);
1085
+
1086
+ const returnMap = new Map<string, PackagingFlightResponse>();
1087
+
1088
+ matchingCombinations.forEach((flight) => {
1089
+ const key = getFlightKey(flight.return.segments);
1090
+
1091
+ if (!returnMap.has(key)) {
1092
+ returnMap.set(key, flight);
1093
+ }
1094
+ });
1095
+
1096
+ const returns = Array.from(returnMap.values());
1097
+ const segments = first(returns)?.return.segments;
1098
+ const firstReturnKey = returns.length > 0 && segments ? getFlightKey(segments) : null;
1099
+
1100
+ if (!returns.some((x) => getFlightKey(x.return.segments) === selectedReturnKey)) {
1101
+ dispatch(setSelectedReturnKey(firstReturnKey));
1102
+ }
1103
+ }, [selectedOutwardKey, packagingFlightResults, selectedReturnKey, dispatch]);
1104
+
1105
+ const visibleOutwardFlights = React.useMemo(() => {
1106
+ const withoutSelected = uniqueOutwardFlights.filter((x) => getFlightKey(x.outward.segments) !== selectedOutwardKey);
1107
+ return withoutSelected.slice(0, 3);
1108
+ }, [uniqueOutwardFlights, selectedOutwardKey]);
1109
+
1110
+ // TODO: get details for selected combination flight and show in fly-in
1111
+ // useEffect(() => {
1112
+ // if (!selectedCombinationFlight) return;
1113
+
1114
+ // dispatch(setSelectedPackagingFlight(selectedCombinationFlight));
1115
+ // // onFlightSearch(selectedCombinationFlight); // Trigger search to update accommodation options based on selected flight
1116
+ // dispatch(setFlyInIsOpen(true));
1117
+ // }, [selectedCombinationFlight, dispatch]);
1118
+
1119
+ // Build packagingEntry
1120
+ useEffect(() => {
1121
+ if (!context) return;
1122
+
1123
+ const seed = activeSearchSeed;
1124
+ if (!seed) return;
1125
+
1126
+ const nextEntry = buildOrUpdatePackagingEntryPartial({
1127
+ sourceEntry: editablePackagingEntry ?? context.packagingEntry ?? null,
1128
+ selectedHotelCode: selectedPackagingAccoResultCode,
1129
+ accommodationResults: packagingAccoResults,
1130
+ selectedFlight: selectedCombinationFlight ?? null,
1131
+ confirmedExcursionsByDay,
1132
+ seed,
1133
+ transactionId: transactionId ?? context.packagingEntry?.transactionId ?? '',
1134
+ language: context.languageCode ?? 'en-GB'
1135
+ });
1136
+
1137
+ if (!nextEntry) return;
1138
+ dispatch(setEditablePackagingEntry(nextEntry));
1139
+
1140
+ if (selectedCombinationFlight) {
1141
+ dispatch(setSelectedPackagingFlight(selectedCombinationFlight));
1142
+ }
1143
+ }, [
1144
+ context,
1145
+ activeSearchSeed,
1146
+ selectedPackagingAccoResultCode,
1147
+ packagingAccoResults,
1148
+ packagingAccoSearchDetails,
1149
+ selectedCombinationFlight,
1150
+ confirmedExcursionsByDay,
1151
+ transactionId,
1152
+ dispatch
1153
+ ]);
1154
+
1155
+ const removeAccommodationLines = (lines: PackagingEntryLine[]) => lines.filter((line) => line.serviceType !== ACCOMMODATION_SERVICE_TYPE);
1156
+
1157
+ const removeFlightLines = (lines: PackagingEntryLine[]) => lines.filter((line) => line.serviceType !== FLIGHT_SERVICE_TYPE);
1158
+
1159
+ const removeExcursionLines = (lines: PackagingEntryLine[]) => lines.filter((line) => line.serviceType !== EXCURSION_SERVICE_TYPE);
1160
+
1161
+ const buildAccommodationLinesFromSelection = (selectedHotel: PackagingAccommodationResponse, seed: SearchSeed): PackagingEntryLine[] => {
1162
+ return buildPackagingAccommodationLines(selectedHotel, seed, ACCOMMODATION_SERVICE_TYPE);
1163
+ };
1164
+
1165
+ const buildExcursionLinesFromConfirmedDays = (confirmedExcursionsByDay: Record<string, PackagingAccommodationResponse[]>): PackagingEntryLine[] => {
1166
+ return Object.values(confirmedExcursionsByDay)
1167
+ .flat()
1168
+ .flatMap((excursion) => {
1169
+ const selectedOptions = excursion.rooms.flatMap((room) => room.options.filter((option) => option.isSelected));
1170
+
1171
+ const parentGuid = crypto.randomUUID();
1172
+
1173
+ return selectedOptions.map(
1174
+ (option, index) =>
1175
+ ({
1176
+ guid: option.guid ?? crypto.randomUUID(),
1177
+ moment: '',
1178
+ parentGuid: index === 0 ? null : parentGuid,
1179
+ order: index,
1180
+ isChanged: true,
1181
+ from: excursion.fromDate,
1182
+ to: excursion.toDate,
1183
+ serviceType: EXCURSION_SERVICE_TYPE,
1184
+ productName: excursion.name,
1185
+ productCode: excursion.code,
1186
+ accommodationName: option.accommodationName,
1187
+ accommodationCode: option.accommodationCode,
1188
+ regimeName: option.regimeName,
1189
+ regimeCode: option.regimeCode,
1190
+ country: excursion.countryId ? { id: excursion.countryId, name: excursion.countryName, localizations: [] } : null,
1191
+ region: excursion.regionId ? { id: excursion.regionId, name: excursion.regionName, localizations: [] } : null,
1192
+ oord: excursion.oordId ? { id: excursion.oordId, name: excursion.oordName, localizations: [] } : null,
1193
+ location: excursion.locationId ? { id: excursion.locationId, name: excursion.locationName, localizations: [] } : null,
1194
+ longitude: excursion.longitude ?? null,
1195
+ latitude: excursion.latitude ?? null,
1196
+ pax: Array.isArray(option.paxIds)
1197
+ ? option.paxIds.map((paxId, paxIndex) => ({
1198
+ paxId: paxId,
1199
+ room: 0,
1200
+ order: paxIndex
1201
+ }))
1202
+ : [],
1203
+ flightInformation: null
1204
+ } satisfies PackagingEntryLine)
1205
+ );
1206
+ });
1207
+ };
1208
+
1209
+ const buildPackagingAccommodationLines = (selectedItem: PackagingAccommodationResponse, seed: SearchSeed, serviceType: number): PackagingEntryLine[] => {
1210
+ if (!selectedItem) return [];
1211
+ const parentGuid = crypto.randomUUID();
1212
+
1213
+ return selectedItem.rooms
1214
+ .filter((room) => room.options.some((o) => o.isSelected))
1215
+ .map((room, index) => {
1216
+ const option = room.options.find((o) => o.isSelected)!;
1217
+
1218
+ const pax = option.paxIds.map((p, paxIndex) => ({
1219
+ paxId: p,
1220
+ room: index,
1221
+ order: paxIndex
1222
+ }));
1223
+
1224
+ return {
1225
+ guid: option.guid ?? crypto.randomUUID(),
1226
+ moment: '',
1227
+ parentGuid: index === 0 ? null : parentGuid,
1228
+ order: index,
1229
+ isChanged: true,
1230
+ from: selectedItem.fromDate,
1231
+ to: selectedItem.toDate,
1232
+ serviceType: serviceType,
1233
+ productName: selectedItem.name,
1234
+ productCode: selectedItem.code,
1235
+ accommodationName: option.accommodationName,
1236
+ accommodationCode: option.accommodationCode,
1237
+ regimeName: option.regimeName,
1238
+ regimeCode: option.regimeCode,
1239
+ country: selectedItem.countryId ? { id: selectedItem.countryId, name: selectedItem.countryName, localizations: [] } : null,
1240
+ region: selectedItem.regionId ? { id: selectedItem.regionId, name: selectedItem.regionName, localizations: [] } : null,
1241
+ oord: selectedItem.oordId ? { id: selectedItem.oordId, name: selectedItem.oordName, localizations: [] } : null,
1242
+ location: selectedItem.locationId ? { id: selectedItem.locationId, name: selectedItem.locationName, localizations: [] } : null,
1243
+ longitude: selectedItem.longitude ?? null,
1244
+ latitude: selectedItem.latitude ?? null,
1245
+ pax,
1246
+ flightInformation: null
1247
+ } satisfies PackagingEntryLine;
1248
+ });
1249
+ };
1250
+
1251
+ const toDateOnlyUtcString = (value: string | Date): string => {
1252
+ const date = new Date(value);
1253
+
1254
+ return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate())).toISOString();
1255
+ };
1256
+
1257
+ const toTimeOnlyString = (value: string | Date): string => {
1258
+ const date = new Date(value);
1259
+
1260
+ const hh = String(date.getUTCHours()).padStart(2, '0');
1261
+ const mm = String(date.getUTCMinutes()).padStart(2, '0');
1262
+ const ss = String(date.getUTCSeconds()).padStart(2, '0');
1263
+
1264
+ return `${hh}:${mm}:${ss}`;
1265
+ };
1266
+
1267
+ const mapFlightSegmentsToFlightLines = (segments: FlightSearchResponseFlightSegment[]): PackagingEntryLineFlightLine[] =>
1268
+ segments.map((segment) => ({
1269
+ airlineCode: segment.marketingAirlineCode,
1270
+ airlineDescription: segment.marketingAirlineName,
1271
+ operatingAirlineCode: segment.operatingAirlineCode,
1272
+ operatingAirlineDescription: segment.operatingAirlineName,
1273
+ flightNumber: segment.flightNumber,
1274
+ operatingFlightNumber: segment.operatingFlightNumber ?? null,
1275
+ departureDate: toDateOnlyUtcString(segment.departureDateTime),
1276
+ departureTime: toTimeOnlyString(segment.departureDateTime),
1277
+ departureAirportCode: segment.departureAirportCode,
1278
+ departureAirportDescription: segment.departureAirportName,
1279
+ arrivalDate: toDateOnlyUtcString(segment.arrivalDateTime),
1280
+ arrivalTime: toTimeOnlyString(segment.arrivalDateTime),
1281
+ arrivalAirportCode: segment.arrivalAirportCode,
1282
+ arrivalAirportDescription: segment.arrivalAirportName,
1283
+ durationInTicks: segment.durationInTicks
1284
+ }));
1285
+
1286
+ const buildFlightLabel = (segments: FlightSearchResponseFlightSegment[]) => {
1287
+ const firstSegment = first(segments);
1288
+ const lastSegment = last(segments);
1289
+
1290
+ if (!firstSegment || !lastSegment) {
1291
+ return { productName: 'Flight', productCode: 'FLIGHT' };
1292
+ }
1293
+
1294
+ return {
1295
+ productName: `${firstSegment.departureAirportName} - ${lastSegment.arrivalAirportName} (${firstSegment.marketingAirlineName})`,
1296
+ productCode: `${firstSegment.departureAirportCode} ${lastSegment.arrivalAirportCode}/${firstSegment.marketingAirlineCode}`
1297
+ };
1298
+ };
1299
+
1300
+ const buildFlightLinesFromSelection = (selectedFlight: PackagingFlightResponse): PackagingEntryLine[] => {
1301
+ if (!selectedFlight) return [];
1302
+
1303
+ const outwardSegments = selectedFlight.outward?.segments ?? [];
1304
+ const returnSegments = selectedFlight.return?.segments ?? [];
1305
+
1306
+ if (!outwardSegments.length || !returnSegments.length) return [];
1307
+
1308
+ const outwardLabel = buildFlightLabel(outwardSegments);
1309
+ const returnLabel = buildFlightLabel(returnSegments);
1310
+
1311
+ const outwardFirst = first(outwardSegments);
1312
+ const outwardLast = last(outwardSegments);
1313
+ const returnFirst = first(returnSegments);
1314
+ const returnLast = last(returnSegments);
1315
+
1316
+ const outwardLine = {
1317
+ guid: selectedFlight.outwardGuid,
1318
+ parentGuid: null,
1319
+ order: 0,
1320
+ isChanged: true,
1321
+ from: outwardFirst?.departureDateTime.toString() ?? '',
1322
+ to: outwardLast?.arrivalDateTime.toString() ?? '',
1323
+ serviceType: FLIGHT_SERVICE_TYPE,
1324
+ productName: outwardLabel.productName,
1325
+ productCode: outwardLabel.productCode,
1326
+ accommodationName: outwardFirst?.metaData?.farePriceClassName,
1327
+ accommodationCode: outwardFirst?.metaData?.fareCode,
1328
+ regimeName: null,
1329
+ regimeCode: null,
1330
+ country: null,
1331
+ region: null,
1332
+ oord: null,
1333
+ location: null,
1334
+ longitude: null,
1335
+ latitude: null,
1336
+ // pax: allPaxAssignments,
1337
+ flightInformation: {
1338
+ pnr: '',
1339
+ flightLines: mapFlightSegmentsToFlightLines(outwardSegments)
1340
+ }
1341
+ } as PackagingEntryLine;
1342
+
1343
+ const returnLine = {
1344
+ guid: selectedFlight.returnGuid,
1345
+ parentGuid: selectedFlight.outwardGuid,
1346
+ order: 1,
1347
+ isChanged: true,
1348
+ from: returnFirst?.departureDateTime.toString() ?? '',
1349
+ to: returnLast?.departureDateTime.toString() ?? '',
1350
+ serviceType: FLIGHT_SERVICE_TYPE,
1351
+ productName: returnLabel.productName,
1352
+ productCode: returnLabel.productCode,
1353
+ accommodationName: returnFirst?.metaData?.farePriceClassName,
1354
+ accommodationCode: returnFirst?.metaData?.fareCode,
1355
+ regimeName: null,
1356
+ regimeCode: null,
1357
+ country: null,
1358
+ region: null,
1359
+ oord: null,
1360
+ location: null,
1361
+ longitude: null,
1362
+ latitude: null,
1363
+ // pax: allPaxAssignments,
1364
+ flightInformation: {
1365
+ pnr: '',
1366
+ flightLines: mapFlightSegmentsToFlightLines(returnSegments)
1367
+ }
1368
+ } as PackagingEntryLine;
1369
+
1370
+ return [outwardLine, returnLine];
1371
+ };
1372
+
1373
+ const buildOrUpdatePackagingEntryPartial = ({
1374
+ sourceEntry,
1375
+ selectedHotelCode,
1376
+ accommodationResults,
1377
+ selectedFlight,
1378
+ confirmedExcursionsByDay,
1379
+ seed,
1380
+ transactionId,
1381
+ language
1382
+ }: BuildPackagingEntryPartialArgs): PackagingEntry | null => {
1383
+ if (!seed?.rooms?.length) return null;
1384
+
1385
+ const entry = buildBasePackagingEntry(sourceEntry, seed, transactionId, language);
1386
+
1387
+ let nextLines = [...(entry.lines ?? [])];
1388
+
1389
+ const selectedHotel = selectedHotelCode ? accommodationResults.find((r) => r.code === selectedHotelCode) : null;
1390
+
1391
+ // Update accommodation only when enough data exists
1392
+ if (selectedHotel) {
1393
+ const accommodationLines = buildAccommodationLinesFromSelection(selectedHotel, seed);
1394
+
1395
+ if (accommodationLines.length) {
1396
+ nextLines = removeAccommodationLines(nextLines);
1397
+ nextLines = [...nextLines, ...accommodationLines];
1398
+ }
1399
+ }
1400
+
1401
+ // Update flights only when full selected combination exists
1402
+ if (selectedFlight) {
1403
+ const flightLines = buildFlightLinesFromSelection(selectedFlight);
1404
+ if (flightLines.length) {
1405
+ nextLines = removeFlightLines(nextLines);
1406
+ nextLines = [...nextLines, ...flightLines];
1407
+ }
1408
+ }
1409
+
1410
+ // excursions
1411
+ const excursionLines = buildExcursionLinesFromConfirmedDays(confirmedExcursionsByDay);
1412
+
1413
+ nextLines = removeExcursionLines(nextLines);
1414
+
1415
+ if (excursionLines.length) {
1416
+ nextLines = [...nextLines, ...excursionLines];
1417
+ }
1418
+
1419
+ nextLines = nextLines.map((line, index) => ({
1420
+ ...line,
1421
+ order: index
1422
+ }));
1423
+
1424
+ return {
1425
+ ...entry,
1426
+ language,
1427
+ transactionId,
1428
+ pax: entry.pax,
1429
+ lines: nextLines
1430
+ };
1431
+ };
1432
+
1433
+ const buildBasePackagingEntry = (
1434
+ sourceEntry: PackagingEntry | null | undefined,
1435
+ seed: SearchSeed,
1436
+ transactionId: string,
1437
+ language: string
1438
+ ): PackagingEntry => {
1439
+ if (sourceEntry) {
1440
+ return structuredClone(sourceEntry);
1441
+ }
1442
+
1443
+ let paxId = 0;
1444
+ const pax: PackagingEntryPax[] = [];
1445
+ const rooms: PackagingEntryRoom[] = [];
1446
+
1447
+ seed.rooms?.forEach((room, roomIndex) => {
1448
+ const paxIds = room.pax.map((_, paxIndex) => {
1449
+ const id = paxId++;
1450
+ pax.push({
1451
+ id,
1452
+ firstName: _.firstName || '',
1453
+ lastName: _.lastName || '',
1454
+ dateOfBirth: _.dateOfBirth || null,
1455
+ age: _.age || null,
1456
+ isMainBooker: roomIndex === 0 && paxIndex === 0
1457
+ });
1458
+
1459
+ return id;
1460
+ });
1461
+
1462
+ rooms.push({
1463
+ id: roomIndex,
1464
+ paxIds
1465
+ });
1466
+ });
1467
+
1468
+ return {
1469
+ language,
1470
+ transactionId,
1471
+ dossierNumber: '',
1472
+ status: context?.entryStatus,
1473
+ customStatusId: context?.customEntryStatusId,
1474
+ bookingDate: null,
1475
+ price: 0,
1476
+ depositAmount: 0,
1477
+ pax,
1478
+ rooms,
1479
+ lines: [],
1480
+ address: {} as PackagingEntryAddress
1481
+ } as PackagingEntry;
1482
+ };
1483
+
1484
+ const handleShowMoreFlights = (flyInType: FlyInType) => {
1485
+ dispatch(setFlyInType(flyInType));
1486
+ dispatch(setFlyInIsOpen(true));
1487
+ };
1488
+
1489
+ return (
1490
+ <div id="tide-booking" className="search__bg">
1491
+ {context && (
1492
+ <div className="search">
1493
+ {bookPackagingEntry ? (
1494
+ <BookPackagingEntry
1495
+ activeSearchSeed={activeSearchSeed}
1496
+ isLoading={itineraryIsLoading || pricesAreLoading}
1497
+ isConfirmationPage={isBookingConfirmation}
1498
+ />
1499
+ ) : (
1500
+ <div className="search__container">
1501
+ {context.searchConfiguration.qsmType === PortalQsmType.Flight && (
1502
+ <FlightSearchProvider tideConnection={context.tideConnection}>
1503
+ <FlightResultsContainer isMobile={isMobile} />
1504
+ <FlyIn
1505
+ srpType={context.searchConfiguration.qsmType}
1506
+ isOpen={flyInIsOpen}
1507
+ setIsOpen={handleFlyInToggle}
1508
+ onPanelRef={(el) => (panelRef.current = el)}
1509
+ detailsLoading={detailsIsLoading}
1510
+ filtersOpen={filtersOpen}
1511
+ />
1512
+ </FlightSearchProvider>
1513
+ )}
1514
+ {(context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight ||
1515
+ context.searchConfiguration.qsmType === PortalQsmType.Accommodation ||
1516
+ context.searchConfiguration.qsmType === PortalQsmType.GroupTour ||
1517
+ context.searchConfiguration.qsmType === PortalQsmType.RoundTrip) && (
1518
+ <>
1519
+ {context.searchConfiguration.qsmType != PortalQsmType.AccommodationAndFlight && context.showFilters && (
1520
+ <Filters
1521
+ initialFilters={initialFilters}
1522
+ filters={filters}
1523
+ isOpen={filtersOpen}
1524
+ handleSetIsOpen={() => setFiltersOpen(!filtersOpen)}
1525
+ // handleApplyFilters={() => setSearchTrigger((prev) => prev + 1)}
1526
+ isLoading={isLoading}
1527
+ setFilters={(filters) => dispatch(setFilters(filters))}
1528
+ resetFilters={(filters) => dispatch(resetFilters(filters))}
1529
+ />
1530
+ )}
1531
+ {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && (
1532
+ <Itinerary
1533
+ isOpen={itineraryOpen}
1534
+ handleSetIsOpen={() => setItineraryOpen(!itineraryOpen)}
1535
+ isLoading={isLoading || pricesAreLoading || flightsLoading || itineraryIsLoading}
1536
+ onEditAccommodation={handleEditAccommodation}
1537
+ />
1538
+ )}
1539
+ {/* ---------------- Results ---------------- */}
1540
+ <div className="search__results">
1541
+ {isMobile && (
1542
+ <div className="search__result-row">
1543
+ <div className="search__results__actions">
1544
+ {context.searchConfiguration.qsmType != PortalQsmType.AccommodationAndFlight && context.showFilters && (
1545
+ <div className="cta cta--filter" onClick={() => setFiltersOpen(true)}>
1546
+ <Icon name="ui-filter" className="mobile-filters-button__icon" height={16} />
1547
+ {translations.SRP.FILTERS}
1548
+ </div>
1549
+ )}
1550
+ {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && (
1551
+ <div className="cta cta--filter" onClick={() => setItineraryOpen(true)}>
1552
+ <Icon name="ui-shopping-cart" className="mobile-filters-button__icon" height={16} />
1553
+ {translations.SRP.VIEW_BOOKING}
1554
+ </div>
1555
+ )}
1556
+ </div>
1557
+ {context.searchConfiguration.qsmType !== PortalQsmType.AccommodationAndFlight && sortByTypes && sortByTypes.length > 0 && (
1558
+ <ItemPicker
1559
+ items={sortByTypes}
1560
+ selection={selectedSortType?.label || undefined}
1561
+ selectedSortByType={selectedSortType}
1562
+ label={translations.SRP.SORTBY}
1563
+ placeholder={translations.SRP.SORTBY}
1564
+ classModifier="travel-class-picker__items"
1565
+ valueFormatter={(value, direction) => getSortingName(translations, findSortByType(sortByTypes, value, direction ?? 'asc'))}
1566
+ onPick={(newSortKey, direction) => handleSortChange(newSortKey, direction)}
1567
+ />
1568
+ )}
1569
+ </div>
1570
+ )}
1571
+ {context.searchConfiguration.qsmType !== PortalQsmType.AccommodationAndFlight && (
1572
+ <div className="search__result-row">
1573
+ <span className="search__result-row-text">
1574
+ {!isLoading && (
1575
+ <>
1576
+ {context.searchConfiguration.qsmType === PortalQsmType.Accommodation &&
1577
+ filteredPackagingAccoResults?.length &&
1578
+ filteredPackagingAccoResults?.length}
1579
+ {context.searchConfiguration.qsmType !== PortalQsmType.Accommodation && filteredResults?.length && filteredResults.length}
1580
+ &nbsp;{translations.SRP.TOTAL_RESULTS_LABEL}
1581
+ </>
1582
+ )}
1583
+ </span>
1584
+ {!context.packagingEntry && !isMobile && sortByTypes && sortByTypes.length > 0 && (
1585
+ <div className="search__result-row-filter">
1586
+ <ItemPicker
1587
+ items={sortByTypes}
1588
+ selection={selectedSortType?.label || undefined}
1589
+ selectedSortByType={selectedSortType}
1590
+ label={translations.SRP.SORTBY}
1591
+ placeholder={translations.SRP.SORTBY}
1592
+ classModifier="travel-class-picker__items"
1593
+ valueFormatter={(value, direction) => getSortingName(translations, findSortByType(sortByTypes, value, direction ?? 'asc'))}
1594
+ onPick={(newSortKey, direction) => handleSortChange(newSortKey, direction)}
1595
+ />
1596
+ </div>
1597
+ )}
1598
+ </div>
1599
+ )}
1600
+
1601
+ <div className="search__results__wrapper">
1602
+ {context.showTabViews &&
1603
+ (context.searchConfiguration.qsmType === PortalQsmType.GroupTour ||
1604
+ context.searchConfiguration.qsmType === PortalQsmType.Accommodation) && <TabViews />}
1605
+
1606
+ {context.showRoundTripResults && context.showMockup && <RoundTripResults />}
1607
+
1608
+ {context.searchConfiguration.qsmType === PortalQsmType.GroupTour && <GroupTourResults isLoading={isLoading} />}
1609
+
1610
+ {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && !context.packagingEntry && context.showFlightResults && (
1611
+ <>
1612
+ <div className="search__results__label search__results__label--secondary">
1613
+ <div className="search__results__label__date">
1614
+ {(() => {
1615
+ const firstResultDate = uniqueOutwardFlights.length > 0 ? uniqueOutwardFlights[0].outward.segments[0].departureDateTime : null;
1616
+
1617
+ const firstResultDay = firstResultDate ? format(firstResultDate, 'd') : null;
1618
+ const firstResultMonth = firstResultDate ? format(firstResultDate, 'MMM') : null;
1619
+
1620
+ return (
1621
+ <>
1622
+ <p className="search__results__label__date-date">{firstResultDay}</p>
1623
+ <p>{firstResultMonth}</p>
1624
+ </>
1625
+ );
1626
+ })()}
1627
+ </div>
1628
+ <div className="search__results__label__text">
1629
+ <Icon name="ui-flight" height={16} />
1630
+ <h3>
1631
+ {translations.SRP.SELECT} <strong> {translations.SRP.DEPARTURE}</strong>
1632
+ </h3>
1633
+ </div>
1634
+ </div>
1635
+
1636
+ {flightsLoading ? (
1637
+ <Spinner label={translations.SRP.LOADING_FLIGHTS} />
1638
+ ) : (
1639
+ <>
1640
+ <div className="search__results__cards search__results__cards--extended">
1641
+ {selectedOutwardKey && selectedOutward && (
1642
+ <IndependentFlightOption
1643
+ key={`flight-${selectedOutwardKey}`}
1644
+ item={selectedOutward.outward}
1645
+ guid={selectedOutward.outwardGuid}
1646
+ onSelect={() => dispatch(setSelectedOutwardKey(null))}
1647
+ selectedGuid={selectedOutward.outwardGuid}
1648
+ isOutward={true}
1649
+ showSelectedState={true}
1650
+ price={selectedOutward.price}
1651
+ />
1652
+ )}
1653
+ {visibleOutwardFlights.map((result) => (
1654
+ <IndependentFlightOption
1655
+ key={`flight-${result.outwardGuid}`}
1656
+ item={result.outward}
1657
+ onSelect={() => dispatch(setSelectedOutwardKey(getFlightKey(result.outward.segments)))}
1658
+ guid={result.outwardGuid}
1659
+ isOutward={true}
1660
+ price={result.price}
1661
+ currentSelectedPrice={selectedOutward?.price}
1662
+ />
1663
+ ))}
1664
+ </div>
1665
+ {uniqueOutwardFlights && uniqueOutwardFlights.length > 3 && (
1666
+ <div className="search__results__cards__actions">
1667
+ <button className="cta cta--secondary" onClick={() => handleShowMoreFlights('flight-outward-results')}>
1668
+ {translations.SRP.SHOW_MORE}
1669
+ </button>
1670
+ </div>
1671
+ )}
1672
+ </>
1673
+ )}
1674
+ </>
1675
+ )}
1676
+
1677
+ {context.showHotelAccommodationResults && !context.packagingEntry && <HotelAccommodationResults isLoading={isLoading} />}
1678
+
1679
+ {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && !context.packagingEntry && <DayByDayExcursions />}
1680
+
1681
+ {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && !context.packagingEntry && context.showFlightResults && (
1682
+ <>
1683
+ <div className="search__results__label search__results__label--secondary">
1684
+ <div className="search__results__label__date">
1685
+ {(() => {
1686
+ const firstResultDate = uniqueReturnFlights.length > 0 ? uniqueReturnFlights[0].return.segments[0].departureDateTime : null;
1687
+
1688
+ const firstResultDay = firstResultDate ? format(firstResultDate, 'd') : null;
1689
+ const firstResultMonth = firstResultDate ? format(firstResultDate, 'MMM') : null;
1690
+
1691
+ return (
1692
+ <>
1693
+ <p className="search__results__label__date-date">{firstResultDay}</p>
1694
+ <p>{firstResultMonth}</p>
1695
+ </>
1696
+ );
1697
+ })()}
1698
+ </div>
1699
+ <div className="search__results__label__text">
1700
+ <Icon name="ui-flight" height={16} />
1701
+ <h3>
1702
+ {translations.SRP.SELECT} <strong> {translations.SRP.RETURN}</strong>
1703
+ </h3>
1704
+ </div>
1705
+ </div>
1706
+
1707
+ <div className="search__results__cards search__results__cards--extended">
1708
+ {selectedReturnKey && selectedReturn && (
1709
+ <IndependentFlightOption
1710
+ key={`flight-${selectedReturnKey}`}
1711
+ item={selectedReturn.return}
1712
+ guid={selectedReturn.outwardGuid}
1713
+ selectedGuid={selectedReturn.outwardGuid}
1714
+ isOutward={false}
1715
+ showSelectedState={true}
1716
+ price={selectedReturn.price}
1717
+ />
1718
+ )}
1719
+ {uniqueReturnFlights.map((result) => (
1720
+ <IndependentFlightOption
1721
+ key={`flight-${result.outwardGuid}`}
1722
+ item={result.return}
1723
+ onSelect={() => dispatch(setSelectedReturnKey(getFlightKey(result.return.segments)))}
1724
+ guid={result.outwardGuid}
1725
+ isOutward={false}
1726
+ currentSelectedPrice={selectedReturn?.price}
1727
+ price={result.price}
1728
+ />
1729
+ ))}
1730
+ </div>
1731
+ </>
1732
+ )}
1733
+
1734
+ {context.searchConfiguration.qsmType === PortalQsmType.AccommodationAndFlight && context.packagingEntry && (
1735
+ <FullItinerary isLoading={itineraryIsLoading} />
1736
+ )}
1737
+ </div>
1738
+ </div>
1739
+ {/* <button onClick={() => handleFlyInToggle(!flyInIsOpen)}>Toggle FlyIn</button> */}
1740
+ <FlyIn
1741
+ srpType={context.searchConfiguration.qsmType}
1742
+ isOpen={flyInIsOpen}
1743
+ setIsOpen={handleFlyInToggle}
1744
+ handleConfirm={() => handleConfirmHotelSwap()}
1745
+ onPanelRef={(el) => (panelRef.current = el)}
1746
+ detailsLoading={detailsIsLoading}
1747
+ flyInType={flyInType}
1748
+ isPackageEditFlow={!!context.packagingEntry}
1749
+ sortByTypes={sortByTypes}
1750
+ activeSearchSeed={activeSearchSeed}
1751
+ toggleFilters={() => setFiltersOpen(!filtersOpen)}
1752
+ filtersOpen={filtersOpen}
1753
+ />
1754
+ </>
1755
+ )}
1756
+ </div>
1757
+ )}
1758
+ </div>
1759
+ )}
1760
+ </div>
1761
+ );
1762
+ };
1763
+
1764
+ export default SearchResultsContainer;