@qite/tide-components 1.0.10 → 1.0.13-preview.0

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 (93) hide show
  1. package/build/build-cjs/index.js +2386 -1305
  2. package/build/build-cjs/src/booking-wizard/features/booking/selectors.d.ts +1 -0
  3. package/build/build-cjs/src/booking-wizard/features/travelers-form/validate-form.d.ts +2 -1
  4. package/build/build-cjs/src/booking-wizard/types.d.ts +29 -1
  5. package/build/build-cjs/src/search-results/components/flight/flight-selection/independent-flight-option.d.ts +2 -0
  6. package/build/build-cjs/src/search-results/components/itinerary/index.d.ts +1 -0
  7. package/build/build-cjs/src/search-results/components/itinerary/itinerary-utils.d.ts +1 -0
  8. package/build/build-cjs/src/search-results/types.d.ts +8 -0
  9. package/build/build-cjs/src/search-results/utils/booked-entry-utils.d.ts +20 -0
  10. package/build/build-cjs/src/search-results/utils/traject-utils.d.ts +7 -0
  11. package/build/build-cjs/src/shared/booking/travelers-form.d.ts +13 -3
  12. package/build/build-esm/index.js +2390 -1309
  13. package/build/build-esm/src/booking-wizard/features/booking/selectors.d.ts +1 -0
  14. package/build/build-esm/src/booking-wizard/features/travelers-form/validate-form.d.ts +2 -1
  15. package/build/build-esm/src/booking-wizard/types.d.ts +29 -1
  16. package/build/build-esm/src/search-results/components/flight/flight-selection/independent-flight-option.d.ts +2 -0
  17. package/build/build-esm/src/search-results/components/itinerary/index.d.ts +1 -0
  18. package/build/build-esm/src/search-results/components/itinerary/itinerary-utils.d.ts +1 -0
  19. package/build/build-esm/src/search-results/types.d.ts +8 -0
  20. package/build/build-esm/src/search-results/utils/booked-entry-utils.d.ts +20 -0
  21. package/build/build-esm/src/search-results/utils/traject-utils.d.ts +7 -0
  22. package/build/build-esm/src/shared/booking/travelers-form.d.ts +13 -3
  23. package/package.json +2 -2
  24. package/styles/abstracts/_mixins.scss +74 -74
  25. package/styles/abstracts/_variables.scss +57 -57
  26. package/styles/base/_fonts.scss +2 -2
  27. package/styles/base/_normalize.scss +227 -227
  28. package/styles/base/_typography.scss +35 -35
  29. package/styles/booking-joker-variables.scss +596 -596
  30. package/styles/booking-product-variables.scss +330 -330
  31. package/styles/booking-product.scss +438 -438
  32. package/styles/booking-qsm-variables.scss +501 -501
  33. package/styles/booking-qsm.scss +52 -52
  34. package/styles/booking-search-results-variables.scss +734 -734
  35. package/styles/booking-search-results.scss +53 -53
  36. package/styles/booking-wizard-variables.scss +603 -603
  37. package/styles/booking-wizard.scss +61 -61
  38. package/styles/components/_accordion.scss +67 -67
  39. package/styles/components/_animations.scss +39 -39
  40. package/styles/components/_base.scss +107 -107
  41. package/styles/components/_booking.scss +906 -906
  42. package/styles/components/_breadcrumb.scss +92 -92
  43. package/styles/components/_button.scss +238 -238
  44. package/styles/components/_checkbox.scss +230 -230
  45. package/styles/components/_contact.scss +255 -255
  46. package/styles/components/_content.scss +336 -336
  47. package/styles/components/_cta.scss +238 -238
  48. package/styles/components/_date-list.scss +41 -41
  49. package/styles/components/_date-range-picker.scss +223 -223
  50. package/styles/components/_decrement-increment.scss +35 -35
  51. package/styles/components/_dropdown.scss +77 -77
  52. package/styles/components/_error.scss +50 -50
  53. package/styles/components/_faq.scss +30 -30
  54. package/styles/components/_flight-option.scss +1432 -1432
  55. package/styles/components/_flyin.scss +830 -830
  56. package/styles/components/_footer.scss +135 -135
  57. package/styles/components/_form.scss +1704 -1693
  58. package/styles/components/_gallery.scss +317 -317
  59. package/styles/components/_header.scss +113 -113
  60. package/styles/components/_image-with-text.scss +206 -206
  61. package/styles/components/_img-slider.scss +175 -175
  62. package/styles/components/_info-message.scss +75 -75
  63. package/styles/components/_input.scss +35 -35
  64. package/styles/components/_list.scss +185 -185
  65. package/styles/components/_loader.scss +152 -152
  66. package/styles/components/_login.scss +140 -140
  67. package/styles/components/_map-view.scss +1100 -1100
  68. package/styles/components/_mixins.scss +579 -579
  69. package/styles/components/_navbar.scss +765 -765
  70. package/styles/components/_passenger-picker.scss +306 -306
  71. package/styles/components/_phone-input.scss +8 -8
  72. package/styles/components/_placeholders.scss +165 -165
  73. package/styles/components/_pricing-summary.scss +163 -163
  74. package/styles/components/_qsm.scss +17 -17
  75. package/styles/components/_radiobutton.scss +170 -170
  76. package/styles/components/_search.scss +2197 -2197
  77. package/styles/components/_select-wrapper.scss +81 -81
  78. package/styles/components/_slider.scss +120 -128
  79. package/styles/components/_spinner.scss +29 -29
  80. package/styles/components/_step-indicators.scss +187 -187
  81. package/styles/components/_table.scss +81 -81
  82. package/styles/components/_tide-form.scss +578 -578
  83. package/styles/components/_tree.scss +648 -648
  84. package/styles/components/_typeahead.scss +275 -275
  85. package/styles/components/_variables.scss +89 -89
  86. package/styles/content-blocks-variables.scss +543 -543
  87. package/styles/content-blocks.scss +66 -66
  88. package/styles/font.scss +2 -2
  89. package/styles/qsm/_calendar.scss +274 -274
  90. package/styles/qsm/_qsm.scss +1097 -1097
  91. package/styles/search.scss +1200 -1200
  92. package/styles/tide-form-variables.scss +275 -275
  93. package/styles/tide-form.scss +56 -56
@@ -11,11 +11,11 @@ var locale = require('date-fns/locale');
11
11
  var reactPopper = require('react-popper');
12
12
  var reactRedux = require('react-redux');
13
13
  var toolkit = require('@reduxjs/toolkit');
14
+ var flat = require('flat');
14
15
  var reactRouter = require('react-router');
15
16
  require('react-dom');
16
17
  var produce = require('immer');
17
18
  var formik = require('formik');
18
- var flat = require('flat');
19
19
  var uuid = require('uuid');
20
20
 
21
21
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
@@ -56,8 +56,8 @@ function _mergeNamespaces(n, m) {
56
56
  var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
57
57
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
58
58
  var JsonURL__default = /*#__PURE__*/_interopDefaultLegacy(JsonURL);
59
- var produce__default = /*#__PURE__*/_interopDefaultLegacy(produce);
60
59
  var flat__default = /*#__PURE__*/_interopDefaultLegacy(flat);
60
+ var produce__default = /*#__PURE__*/_interopDefaultLegacy(produce);
61
61
 
62
62
  var STEPS$f = {
63
63
  PERSONAL_DETAILS: "التفاصيل الشخصية",
@@ -153,7 +153,9 @@ var SHARED$f = {
153
153
  ADULTS: "بالغون",
154
154
  CHILDREN: "أطفال",
155
155
  SELECT: "اختر",
156
- SELECTED: "تم الاختيار"
156
+ SELECTED: "تم الاختيار",
157
+ BOOKED: "محجوز",
158
+ SAVE: "حفظ"
157
159
  };
158
160
  var SIDEBAR$f = {
159
161
  OVERVIEW: "الملخص",
@@ -211,6 +213,27 @@ var TRAVELERS_FORM$f = {
211
213
  FIRST_NAME: "الاسم الأول",
212
214
  LAST_NAME: "اسم العائلة",
213
215
  BIRTHDATE: "تاريخ الميلاد",
216
+ NICKNAME: "Nickname",
217
+ BIRTHPLACE: "Birthplace",
218
+ NATIONALITY: "Nationality",
219
+ PREFERRED_LANGUAGE: "Preferred language",
220
+ TRAVELER_EMAIL: "Email",
221
+ TRAVELER_PHONE: "Phone",
222
+ TRAVELER_MOBILE: "Mobile phone",
223
+ PASSPORT_NUMBER: "Passport number",
224
+ PASSPORT_ISSUE_DATE: "Passport issue date",
225
+ PASSPORT_ISSUED_LOCATION: "Passport issued location",
226
+ PASSPORT_EXPIRY_DATE: "Passport expiry date",
227
+ EMERGENCY_NUMBER: "Emergency number",
228
+ HAS_INSURANCE: "Has insurance",
229
+ INSURANCE_NAME: "Insurance name",
230
+ INFO: "Additional information",
231
+ WEIGHT: "الوزن",
232
+ WEIGHT_UNIT_KG: "kg",
233
+ WEIGHT_UNIT_LB: "lb",
234
+ HEIGHT: "الطول",
235
+ HEIGHT_UNIT_CM: "cm",
236
+ HEIGHT_UNIT_FT_IN: "ft/in",
214
237
  STREET: "الشارع",
215
238
  STREET_PLACEHOLDER: "اسم الشارع",
216
239
  HOUSE_NUMBER: "رقم المنزل",
@@ -600,7 +623,9 @@ var SHARED$e = {
600
623
  ADULTS: "Voksne",
601
624
  CHILDREN: "Børn",
602
625
  SELECT: "Vælg",
603
- SELECTED: "Valgt"
626
+ SELECTED: "Valgt",
627
+ BOOKED: "Booket",
628
+ SAVE: "Gem"
604
629
  };
605
630
  var SIDEBAR$e = {
606
631
  OVERVIEW: "Oversigt",
@@ -658,6 +683,27 @@ var TRAVELERS_FORM$e = {
658
683
  FIRST_NAME: "Fornavn",
659
684
  LAST_NAME: "Efternavn",
660
685
  BIRTHDATE: "Fødselsdato",
686
+ NICKNAME: "Nickname",
687
+ BIRTHPLACE: "Birthplace",
688
+ NATIONALITY: "Nationality",
689
+ PREFERRED_LANGUAGE: "Preferred language",
690
+ TRAVELER_EMAIL: "Email",
691
+ TRAVELER_PHONE: "Phone",
692
+ TRAVELER_MOBILE: "Mobile phone",
693
+ PASSPORT_NUMBER: "Passport number",
694
+ PASSPORT_ISSUE_DATE: "Passport issue date",
695
+ PASSPORT_ISSUED_LOCATION: "Passport issued location",
696
+ PASSPORT_EXPIRY_DATE: "Passport expiry date",
697
+ EMERGENCY_NUMBER: "Emergency number",
698
+ HAS_INSURANCE: "Has insurance",
699
+ INSURANCE_NAME: "Insurance name",
700
+ INFO: "Additional information",
701
+ WEIGHT: "Vægt",
702
+ WEIGHT_UNIT_KG: "kg",
703
+ WEIGHT_UNIT_LB: "lb",
704
+ HEIGHT: "Højde",
705
+ HEIGHT_UNIT_CM: "cm",
706
+ HEIGHT_UNIT_FT_IN: "ft/in",
661
707
  STREET: "Gade",
662
708
  STREET_PLACEHOLDER: "Din gade",
663
709
  HOUSE_NUMBER: "Husnummer",
@@ -1047,7 +1093,9 @@ var SHARED$d = {
1047
1093
  ADULTS: "Erwachsene",
1048
1094
  CHILDREN: "Kinder",
1049
1095
  SELECT: "Auswählen",
1050
- SELECTED: "Ausgewählt"
1096
+ SELECTED: "Ausgewählt",
1097
+ BOOKED: "Gebucht",
1098
+ SAVE: "Speichern"
1051
1099
  };
1052
1100
  var SIDEBAR$d = {
1053
1101
  OVERVIEW: "Übersicht",
@@ -1105,6 +1153,27 @@ var TRAVELERS_FORM$d = {
1105
1153
  FIRST_NAME: "Vorname",
1106
1154
  LAST_NAME: "Nachname",
1107
1155
  BIRTHDATE: "Geburtsdatum",
1156
+ NICKNAME: "Nickname",
1157
+ BIRTHPLACE: "Birthplace",
1158
+ NATIONALITY: "Nationality",
1159
+ PREFERRED_LANGUAGE: "Preferred language",
1160
+ TRAVELER_EMAIL: "Email",
1161
+ TRAVELER_PHONE: "Phone",
1162
+ TRAVELER_MOBILE: "Mobile phone",
1163
+ PASSPORT_NUMBER: "Passport number",
1164
+ PASSPORT_ISSUE_DATE: "Passport issue date",
1165
+ PASSPORT_ISSUED_LOCATION: "Passport issued location",
1166
+ PASSPORT_EXPIRY_DATE: "Passport expiry date",
1167
+ EMERGENCY_NUMBER: "Emergency number",
1168
+ HAS_INSURANCE: "Has insurance",
1169
+ INSURANCE_NAME: "Insurance name",
1170
+ INFO: "Additional information",
1171
+ WEIGHT: "Gewicht",
1172
+ WEIGHT_UNIT_KG: "kg",
1173
+ WEIGHT_UNIT_LB: "lb",
1174
+ HEIGHT: "Größe",
1175
+ HEIGHT_UNIT_CM: "cm",
1176
+ HEIGHT_UNIT_FT_IN: "ft/in",
1108
1177
  STREET: "Straße",
1109
1178
  STREET_PLACEHOLDER: "Ihre Straße",
1110
1179
  HOUSE_NUMBER: "Hausnummer",
@@ -1494,7 +1563,9 @@ var SHARED$c = {
1494
1563
  ADULTS: "Adults",
1495
1564
  CHILDREN: "Children",
1496
1565
  SELECT: "Select",
1497
- SELECTED: "Selected"
1566
+ SELECTED: "Selected",
1567
+ BOOKED: "Booked",
1568
+ SAVE: "Save"
1498
1569
  };
1499
1570
  var SIDEBAR$c = {
1500
1571
  OVERVIEW: "Summary",
@@ -1553,6 +1624,27 @@ var TRAVELERS_FORM$c = {
1553
1624
  FIRST_NAME: "First name",
1554
1625
  LAST_NAME: "Last name",
1555
1626
  BIRTHDATE: "Date of birth",
1627
+ NICKNAME: "Nickname",
1628
+ BIRTHPLACE: "Birthplace",
1629
+ NATIONALITY: "Nationality",
1630
+ PREFERRED_LANGUAGE: "Preferred language",
1631
+ TRAVELER_EMAIL: "Email",
1632
+ TRAVELER_PHONE: "Phone",
1633
+ TRAVELER_MOBILE: "Mobile phone",
1634
+ PASSPORT_NUMBER: "Passport number",
1635
+ PASSPORT_ISSUE_DATE: "Passport issue date",
1636
+ PASSPORT_ISSUED_LOCATION: "Passport issued location",
1637
+ PASSPORT_EXPIRY_DATE: "Passport expiry date",
1638
+ EMERGENCY_NUMBER: "Emergency number",
1639
+ HAS_INSURANCE: "Has insurance",
1640
+ INSURANCE_NAME: "Insurance name",
1641
+ INFO: "Additional information",
1642
+ WEIGHT: "Weight",
1643
+ WEIGHT_UNIT_KG: "kg",
1644
+ WEIGHT_UNIT_LB: "lb",
1645
+ HEIGHT: "Height",
1646
+ HEIGHT_UNIT_CM: "cm",
1647
+ HEIGHT_UNIT_FT_IN: "ft/in",
1556
1648
  STREET: "Street",
1557
1649
  STREET_PLACEHOLDER: "Your street",
1558
1650
  HOUSE_NUMBER: "House number",
@@ -1945,7 +2037,9 @@ var SHARED$b = {
1945
2037
  ADULTS: "Adultos",
1946
2038
  CHILDREN: "Niños",
1947
2039
  SELECT: "Seleccionar",
1948
- SELECTED: "Seleccionado"
2040
+ SELECTED: "Seleccionado",
2041
+ BOOKED: "Reservado",
2042
+ SAVE: "Guardar"
1949
2043
  };
1950
2044
  var SIDEBAR$b = {
1951
2045
  OVERVIEW: "Resumen",
@@ -2003,6 +2097,27 @@ var TRAVELERS_FORM$b = {
2003
2097
  FIRST_NAME: "Nombre",
2004
2098
  LAST_NAME: "Apellido",
2005
2099
  BIRTHDATE: "Fecha de nacimiento",
2100
+ NICKNAME: "Nickname",
2101
+ BIRTHPLACE: "Birthplace",
2102
+ NATIONALITY: "Nationality",
2103
+ PREFERRED_LANGUAGE: "Preferred language",
2104
+ TRAVELER_EMAIL: "Email",
2105
+ TRAVELER_PHONE: "Phone",
2106
+ TRAVELER_MOBILE: "Mobile phone",
2107
+ PASSPORT_NUMBER: "Passport number",
2108
+ PASSPORT_ISSUE_DATE: "Passport issue date",
2109
+ PASSPORT_ISSUED_LOCATION: "Passport issued location",
2110
+ PASSPORT_EXPIRY_DATE: "Passport expiry date",
2111
+ EMERGENCY_NUMBER: "Emergency number",
2112
+ HAS_INSURANCE: "Has insurance",
2113
+ INSURANCE_NAME: "Insurance name",
2114
+ INFO: "Additional information",
2115
+ WEIGHT: "Peso",
2116
+ WEIGHT_UNIT_KG: "kg",
2117
+ WEIGHT_UNIT_LB: "lb",
2118
+ HEIGHT: "Estatura",
2119
+ HEIGHT_UNIT_CM: "cm",
2120
+ HEIGHT_UNIT_FT_IN: "ft/in",
2006
2121
  STREET: "Calle",
2007
2122
  STREET_PLACEHOLDER: "Su calle",
2008
2123
  HOUSE_NUMBER: "Número",
@@ -2392,7 +2507,9 @@ var SHARED$a = {
2392
2507
  ADULTS: "Adultes",
2393
2508
  CHILDREN: "Enfants",
2394
2509
  SELECT: "Sélectionner",
2395
- SELECTED: "Sélectionné"
2510
+ SELECTED: "Sélectionné",
2511
+ BOOKED: "Réservé",
2512
+ SAVE: "Enregistrer"
2396
2513
  };
2397
2514
  var SIDEBAR$a = {
2398
2515
  OVERVIEW: "Aperçu",
@@ -2451,6 +2568,27 @@ var TRAVELERS_FORM$a = {
2451
2568
  FIRST_NAME: "Prénom",
2452
2569
  LAST_NAME: "Nom",
2453
2570
  BIRTHDATE: "Date de naissance",
2571
+ NICKNAME: "Nickname",
2572
+ BIRTHPLACE: "Birthplace",
2573
+ NATIONALITY: "Nationality",
2574
+ PREFERRED_LANGUAGE: "Preferred language",
2575
+ TRAVELER_EMAIL: "Email",
2576
+ TRAVELER_PHONE: "Phone",
2577
+ TRAVELER_MOBILE: "Mobile phone",
2578
+ PASSPORT_NUMBER: "Passport number",
2579
+ PASSPORT_ISSUE_DATE: "Passport issue date",
2580
+ PASSPORT_ISSUED_LOCATION: "Passport issued location",
2581
+ PASSPORT_EXPIRY_DATE: "Passport expiry date",
2582
+ EMERGENCY_NUMBER: "Emergency number",
2583
+ HAS_INSURANCE: "Has insurance",
2584
+ INSURANCE_NAME: "Insurance name",
2585
+ INFO: "Additional information",
2586
+ WEIGHT: "Poids",
2587
+ WEIGHT_UNIT_KG: "kg",
2588
+ WEIGHT_UNIT_LB: "lb",
2589
+ HEIGHT: "Taille",
2590
+ HEIGHT_UNIT_CM: "cm",
2591
+ HEIGHT_UNIT_FT_IN: "ft/in",
2454
2592
  STREET: "Rue",
2455
2593
  STREET_PLACEHOLDER: "Votre rue",
2456
2594
  HOUSE_NUMBER: "N°",
@@ -2843,7 +2981,9 @@ var SHARED$9 = {
2843
2981
  ADULTS: "Adultes",
2844
2982
  CHILDREN: "Enfants",
2845
2983
  SELECT: "Sélectionner",
2846
- SELECTED: "Sélectionné"
2984
+ SELECTED: "Sélectionné",
2985
+ BOOKED: "Réservé",
2986
+ SAVE: "Enregistrer"
2847
2987
  };
2848
2988
  var SIDEBAR$9 = {
2849
2989
  OVERVIEW: "Résumé",
@@ -2901,6 +3041,27 @@ var TRAVELERS_FORM$9 = {
2901
3041
  FIRST_NAME: "Prénom",
2902
3042
  LAST_NAME: "Nom",
2903
3043
  BIRTHDATE: "Date de naissance",
3044
+ NICKNAME: "Nickname",
3045
+ BIRTHPLACE: "Birthplace",
3046
+ NATIONALITY: "Nationality",
3047
+ PREFERRED_LANGUAGE: "Preferred language",
3048
+ TRAVELER_EMAIL: "Email",
3049
+ TRAVELER_PHONE: "Phone",
3050
+ TRAVELER_MOBILE: "Mobile phone",
3051
+ PASSPORT_NUMBER: "Passport number",
3052
+ PASSPORT_ISSUE_DATE: "Passport issue date",
3053
+ PASSPORT_ISSUED_LOCATION: "Passport issued location",
3054
+ PASSPORT_EXPIRY_DATE: "Passport expiry date",
3055
+ EMERGENCY_NUMBER: "Emergency number",
3056
+ HAS_INSURANCE: "Has insurance",
3057
+ INSURANCE_NAME: "Insurance name",
3058
+ INFO: "Additional information",
3059
+ WEIGHT: "Poids",
3060
+ WEIGHT_UNIT_KG: "kg",
3061
+ WEIGHT_UNIT_LB: "lb",
3062
+ HEIGHT: "Taille",
3063
+ HEIGHT_UNIT_CM: "cm",
3064
+ HEIGHT_UNIT_FT_IN: "ft/in",
2904
3065
  STREET: "Rue",
2905
3066
  STREET_PLACEHOLDER: "Votre rue",
2906
3067
  HOUSE_NUMBER: "Numéro",
@@ -3290,7 +3451,9 @@ var SHARED$8 = {
3290
3451
  ADULTS: "Fullorðnir",
3291
3452
  CHILDREN: "Börn",
3292
3453
  SELECT: "Velja",
3293
- SELECTED: "Valið"
3454
+ SELECTED: "Valið",
3455
+ BOOKED: "Bókað",
3456
+ SAVE: "Vista"
3294
3457
  };
3295
3458
  var SIDEBAR$8 = {
3296
3459
  OVERVIEW: "Yfirlit",
@@ -3348,6 +3511,27 @@ var TRAVELERS_FORM$8 = {
3348
3511
  FIRST_NAME: "Fornafn",
3349
3512
  LAST_NAME: "Eftirnafn",
3350
3513
  BIRTHDATE: "Fæðingardagur",
3514
+ NICKNAME: "Nickname",
3515
+ BIRTHPLACE: "Birthplace",
3516
+ NATIONALITY: "Nationality",
3517
+ PREFERRED_LANGUAGE: "Preferred language",
3518
+ TRAVELER_EMAIL: "Email",
3519
+ TRAVELER_PHONE: "Phone",
3520
+ TRAVELER_MOBILE: "Mobile phone",
3521
+ PASSPORT_NUMBER: "Passport number",
3522
+ PASSPORT_ISSUE_DATE: "Passport issue date",
3523
+ PASSPORT_ISSUED_LOCATION: "Passport issued location",
3524
+ PASSPORT_EXPIRY_DATE: "Passport expiry date",
3525
+ EMERGENCY_NUMBER: "Emergency number",
3526
+ HAS_INSURANCE: "Has insurance",
3527
+ INSURANCE_NAME: "Insurance name",
3528
+ INFO: "Additional information",
3529
+ WEIGHT: "Þyngd",
3530
+ WEIGHT_UNIT_KG: "kg",
3531
+ WEIGHT_UNIT_LB: "lb",
3532
+ HEIGHT: "Hæð",
3533
+ HEIGHT_UNIT_CM: "cm",
3534
+ HEIGHT_UNIT_FT_IN: "ft/in",
3351
3535
  STREET: "Gata",
3352
3536
  STREET_PLACEHOLDER: "Gatan þín",
3353
3537
  HOUSE_NUMBER: "Húsnúmer",
@@ -3737,7 +3921,9 @@ var SHARED$7 = {
3737
3921
  ADULTS: "Adulti",
3738
3922
  CHILDREN: "Bambini",
3739
3923
  SELECT: "Seleziona",
3740
- SELECTED: "Selezionato"
3924
+ SELECTED: "Selezionato",
3925
+ BOOKED: "Prenotato",
3926
+ SAVE: "Salva"
3741
3927
  };
3742
3928
  var SIDEBAR$7 = {
3743
3929
  OVERVIEW: "Riepilogo",
@@ -3795,6 +3981,27 @@ var TRAVELERS_FORM$7 = {
3795
3981
  FIRST_NAME: "Nome",
3796
3982
  LAST_NAME: "Cognome",
3797
3983
  BIRTHDATE: "Data di nascita",
3984
+ NICKNAME: "Nickname",
3985
+ BIRTHPLACE: "Birthplace",
3986
+ NATIONALITY: "Nationality",
3987
+ PREFERRED_LANGUAGE: "Preferred language",
3988
+ TRAVELER_EMAIL: "Email",
3989
+ TRAVELER_PHONE: "Phone",
3990
+ TRAVELER_MOBILE: "Mobile phone",
3991
+ PASSPORT_NUMBER: "Passport number",
3992
+ PASSPORT_ISSUE_DATE: "Passport issue date",
3993
+ PASSPORT_ISSUED_LOCATION: "Passport issued location",
3994
+ PASSPORT_EXPIRY_DATE: "Passport expiry date",
3995
+ EMERGENCY_NUMBER: "Emergency number",
3996
+ HAS_INSURANCE: "Has insurance",
3997
+ INSURANCE_NAME: "Insurance name",
3998
+ INFO: "Additional information",
3999
+ WEIGHT: "Peso",
4000
+ WEIGHT_UNIT_KG: "kg",
4001
+ WEIGHT_UNIT_LB: "lb",
4002
+ HEIGHT: "Altezza",
4003
+ HEIGHT_UNIT_CM: "cm",
4004
+ HEIGHT_UNIT_FT_IN: "ft/in",
3798
4005
  STREET: "Via",
3799
4006
  STREET_PLACEHOLDER: "La tua via",
3800
4007
  HOUSE_NUMBER: "Numero civico",
@@ -4184,7 +4391,9 @@ var SHARED$6 = {
4184
4391
  ADULTS: "Volwassenen",
4185
4392
  CHILDREN: "Kinderen",
4186
4393
  SELECT: "Selecteer",
4187
- SELECTED: "Geselecteerd"
4394
+ SELECTED: "Geselecteerd",
4395
+ BOOKED: "Geboekt",
4396
+ SAVE: "Opslaan"
4188
4397
  };
4189
4398
  var SIDEBAR$6 = {
4190
4399
  OVERVIEW: "Overzicht",
@@ -4243,6 +4452,27 @@ var TRAVELERS_FORM$6 = {
4243
4452
  FIRST_NAME: "Voornaam",
4244
4453
  LAST_NAME: "Naam",
4245
4454
  BIRTHDATE: "Geboortedatum",
4455
+ NICKNAME: "Nickname",
4456
+ BIRTHPLACE: "Birthplace",
4457
+ NATIONALITY: "Nationality",
4458
+ PREFERRED_LANGUAGE: "Preferred language",
4459
+ TRAVELER_EMAIL: "Email",
4460
+ TRAVELER_PHONE: "Phone",
4461
+ TRAVELER_MOBILE: "Mobile phone",
4462
+ PASSPORT_NUMBER: "Passport number",
4463
+ PASSPORT_ISSUE_DATE: "Passport issue date",
4464
+ PASSPORT_ISSUED_LOCATION: "Passport issued location",
4465
+ PASSPORT_EXPIRY_DATE: "Passport expiry date",
4466
+ EMERGENCY_NUMBER: "Emergency number",
4467
+ HAS_INSURANCE: "Has insurance",
4468
+ INSURANCE_NAME: "Insurance name",
4469
+ INFO: "Additional information",
4470
+ WEIGHT: "Gewicht",
4471
+ WEIGHT_UNIT_KG: "kg",
4472
+ WEIGHT_UNIT_LB: "lb",
4473
+ HEIGHT: "Lengte",
4474
+ HEIGHT_UNIT_CM: "cm",
4475
+ HEIGHT_UNIT_FT_IN: "ft/in",
4246
4476
  STREET: "Straat",
4247
4477
  STREET_PLACEHOLDER: "Jouw straat",
4248
4478
  HOUSE_NUMBER: "Huisnr",
@@ -4635,7 +4865,9 @@ var SHARED$5 = {
4635
4865
  ADULTS: "Volwassenen",
4636
4866
  CHILDREN: "Kinderen",
4637
4867
  SELECT: "Selecteren",
4638
- SELECTED: "Geselecteerd"
4868
+ SELECTED: "Geselecteerd",
4869
+ BOOKED: "Geboekt",
4870
+ SAVE: "Opslaan"
4639
4871
  };
4640
4872
  var SIDEBAR$5 = {
4641
4873
  OVERVIEW: "Overzicht",
@@ -4693,6 +4925,27 @@ var TRAVELERS_FORM$5 = {
4693
4925
  FIRST_NAME: "Voornaam",
4694
4926
  LAST_NAME: "Achternaam",
4695
4927
  BIRTHDATE: "Geboortedatum",
4928
+ NICKNAME: "Nickname",
4929
+ BIRTHPLACE: "Birthplace",
4930
+ NATIONALITY: "Nationality",
4931
+ PREFERRED_LANGUAGE: "Preferred language",
4932
+ TRAVELER_EMAIL: "Email",
4933
+ TRAVELER_PHONE: "Phone",
4934
+ TRAVELER_MOBILE: "Mobile phone",
4935
+ PASSPORT_NUMBER: "Passport number",
4936
+ PASSPORT_ISSUE_DATE: "Passport issue date",
4937
+ PASSPORT_ISSUED_LOCATION: "Passport issued location",
4938
+ PASSPORT_EXPIRY_DATE: "Passport expiry date",
4939
+ EMERGENCY_NUMBER: "Emergency number",
4940
+ HAS_INSURANCE: "Has insurance",
4941
+ INSURANCE_NAME: "Insurance name",
4942
+ INFO: "Additional information",
4943
+ WEIGHT: "Gewicht",
4944
+ WEIGHT_UNIT_KG: "kg",
4945
+ WEIGHT_UNIT_LB: "lb",
4946
+ HEIGHT: "Lengte",
4947
+ HEIGHT_UNIT_CM: "cm",
4948
+ HEIGHT_UNIT_FT_IN: "ft/in",
4696
4949
  STREET: "Straat",
4697
4950
  STREET_PLACEHOLDER: "Uw straat",
4698
4951
  HOUSE_NUMBER: "Huisnummer",
@@ -5082,7 +5335,9 @@ var SHARED$4 = {
5082
5335
  ADULTS: "Voksne",
5083
5336
  CHILDREN: "Barn",
5084
5337
  SELECT: "Velg",
5085
- SELECTED: "Valgt"
5338
+ SELECTED: "Valgt",
5339
+ BOOKED: "Booket",
5340
+ SAVE: "Lagre"
5086
5341
  };
5087
5342
  var SIDEBAR$4 = {
5088
5343
  OVERVIEW: "Oversikt",
@@ -5140,6 +5395,27 @@ var TRAVELERS_FORM$4 = {
5140
5395
  FIRST_NAME: "Fornavn",
5141
5396
  LAST_NAME: "Etternavn",
5142
5397
  BIRTHDATE: "Fødselsdato",
5398
+ NICKNAME: "Nickname",
5399
+ BIRTHPLACE: "Birthplace",
5400
+ NATIONALITY: "Nationality",
5401
+ PREFERRED_LANGUAGE: "Preferred language",
5402
+ TRAVELER_EMAIL: "Email",
5403
+ TRAVELER_PHONE: "Phone",
5404
+ TRAVELER_MOBILE: "Mobile phone",
5405
+ PASSPORT_NUMBER: "Passport number",
5406
+ PASSPORT_ISSUE_DATE: "Passport issue date",
5407
+ PASSPORT_ISSUED_LOCATION: "Passport issued location",
5408
+ PASSPORT_EXPIRY_DATE: "Passport expiry date",
5409
+ EMERGENCY_NUMBER: "Emergency number",
5410
+ HAS_INSURANCE: "Has insurance",
5411
+ INSURANCE_NAME: "Insurance name",
5412
+ INFO: "Additional information",
5413
+ WEIGHT: "Vekt",
5414
+ WEIGHT_UNIT_KG: "kg",
5415
+ WEIGHT_UNIT_LB: "lb",
5416
+ HEIGHT: "Høyde",
5417
+ HEIGHT_UNIT_CM: "cm",
5418
+ HEIGHT_UNIT_FT_IN: "ft/in",
5143
5419
  STREET: "Gate",
5144
5420
  STREET_PLACEHOLDER: "Din gate",
5145
5421
  HOUSE_NUMBER: "Husnummer",
@@ -5529,7 +5805,9 @@ var SHARED$3 = {
5529
5805
  ADULTS: "Dorośli",
5530
5806
  CHILDREN: "Dzieci",
5531
5807
  SELECT: "Wybierz",
5532
- SELECTED: "Wybrane"
5808
+ SELECTED: "Wybrane",
5809
+ BOOKED: "Zarezerwowane",
5810
+ SAVE: "Zapisz"
5533
5811
  };
5534
5812
  var SIDEBAR$3 = {
5535
5813
  OVERVIEW: "Podsumowanie",
@@ -5587,6 +5865,27 @@ var TRAVELERS_FORM$3 = {
5587
5865
  FIRST_NAME: "Imię",
5588
5866
  LAST_NAME: "Nazwisko",
5589
5867
  BIRTHDATE: "Data urodzenia",
5868
+ NICKNAME: "Nickname",
5869
+ BIRTHPLACE: "Birthplace",
5870
+ NATIONALITY: "Nationality",
5871
+ PREFERRED_LANGUAGE: "Preferred language",
5872
+ TRAVELER_EMAIL: "Email",
5873
+ TRAVELER_PHONE: "Phone",
5874
+ TRAVELER_MOBILE: "Mobile phone",
5875
+ PASSPORT_NUMBER: "Passport number",
5876
+ PASSPORT_ISSUE_DATE: "Passport issue date",
5877
+ PASSPORT_ISSUED_LOCATION: "Passport issued location",
5878
+ PASSPORT_EXPIRY_DATE: "Passport expiry date",
5879
+ EMERGENCY_NUMBER: "Emergency number",
5880
+ HAS_INSURANCE: "Has insurance",
5881
+ INSURANCE_NAME: "Insurance name",
5882
+ INFO: "Additional information",
5883
+ WEIGHT: "Waga",
5884
+ WEIGHT_UNIT_KG: "kg",
5885
+ WEIGHT_UNIT_LB: "lb",
5886
+ HEIGHT: "Wzrost",
5887
+ HEIGHT_UNIT_CM: "cm",
5888
+ HEIGHT_UNIT_FT_IN: "ft/in",
5590
5889
  STREET: "Ulica",
5591
5890
  STREET_PLACEHOLDER: "Twoja ulica",
5592
5891
  HOUSE_NUMBER: "Numer domu",
@@ -5976,7 +6275,9 @@ var SHARED$2 = {
5976
6275
  ADULTS: "Adultos",
5977
6276
  CHILDREN: "Crianças",
5978
6277
  SELECT: "Selecionar",
5979
- SELECTED: "Selecionado"
6278
+ SELECTED: "Selecionado",
6279
+ BOOKED: "Reservado",
6280
+ SAVE: "Guardar"
5980
6281
  };
5981
6282
  var SIDEBAR$2 = {
5982
6283
  OVERVIEW: "Resumo",
@@ -6034,6 +6335,27 @@ var TRAVELERS_FORM$2 = {
6034
6335
  FIRST_NAME: "Nome",
6035
6336
  LAST_NAME: "Apelido",
6036
6337
  BIRTHDATE: "Data de nascimento",
6338
+ NICKNAME: "Nickname",
6339
+ BIRTHPLACE: "Birthplace",
6340
+ NATIONALITY: "Nationality",
6341
+ PREFERRED_LANGUAGE: "Preferred language",
6342
+ TRAVELER_EMAIL: "Email",
6343
+ TRAVELER_PHONE: "Phone",
6344
+ TRAVELER_MOBILE: "Mobile phone",
6345
+ PASSPORT_NUMBER: "Passport number",
6346
+ PASSPORT_ISSUE_DATE: "Passport issue date",
6347
+ PASSPORT_ISSUED_LOCATION: "Passport issued location",
6348
+ PASSPORT_EXPIRY_DATE: "Passport expiry date",
6349
+ EMERGENCY_NUMBER: "Emergency number",
6350
+ HAS_INSURANCE: "Has insurance",
6351
+ INSURANCE_NAME: "Insurance name",
6352
+ INFO: "Additional information",
6353
+ WEIGHT: "Peso",
6354
+ WEIGHT_UNIT_KG: "kg",
6355
+ WEIGHT_UNIT_LB: "lb",
6356
+ HEIGHT: "Altura",
6357
+ HEIGHT_UNIT_CM: "cm",
6358
+ HEIGHT_UNIT_FT_IN: "ft/in",
6037
6359
  STREET: "Rua",
6038
6360
  STREET_PLACEHOLDER: "A sua rua",
6039
6361
  HOUSE_NUMBER: "Número de porta",
@@ -6423,7 +6745,9 @@ var SHARED$1 = {
6423
6745
  ADULTS: "Vuxna",
6424
6746
  CHILDREN: "Barn",
6425
6747
  SELECT: "Välj",
6426
- SELECTED: "Vald"
6748
+ SELECTED: "Vald",
6749
+ BOOKED: "Bokad",
6750
+ SAVE: "Spara"
6427
6751
  };
6428
6752
  var SIDEBAR$1 = {
6429
6753
  OVERVIEW: "Översikt",
@@ -6481,6 +6805,27 @@ var TRAVELERS_FORM$1 = {
6481
6805
  FIRST_NAME: "Förnamn",
6482
6806
  LAST_NAME: "Efternamn",
6483
6807
  BIRTHDATE: "Födelsedatum",
6808
+ NICKNAME: "Nickname",
6809
+ BIRTHPLACE: "Birthplace",
6810
+ NATIONALITY: "Nationality",
6811
+ PREFERRED_LANGUAGE: "Preferred language",
6812
+ TRAVELER_EMAIL: "Email",
6813
+ TRAVELER_PHONE: "Phone",
6814
+ TRAVELER_MOBILE: "Mobile phone",
6815
+ PASSPORT_NUMBER: "Passport number",
6816
+ PASSPORT_ISSUE_DATE: "Passport issue date",
6817
+ PASSPORT_ISSUED_LOCATION: "Passport issued location",
6818
+ PASSPORT_EXPIRY_DATE: "Passport expiry date",
6819
+ EMERGENCY_NUMBER: "Emergency number",
6820
+ HAS_INSURANCE: "Has insurance",
6821
+ INSURANCE_NAME: "Insurance name",
6822
+ INFO: "Additional information",
6823
+ WEIGHT: "Vikt",
6824
+ WEIGHT_UNIT_KG: "kg",
6825
+ WEIGHT_UNIT_LB: "lb",
6826
+ HEIGHT: "Längd",
6827
+ HEIGHT_UNIT_CM: "cm",
6828
+ HEIGHT_UNIT_FT_IN: "ft/in",
6484
6829
  STREET: "Gata",
6485
6830
  STREET_PLACEHOLDER: "Din gata",
6486
6831
  HOUSE_NUMBER: "Husnummer",
@@ -6870,7 +7215,9 @@ var SHARED = {
6870
7215
  ADULTS: "大人",
6871
7216
  CHILDREN: "子供",
6872
7217
  SELECT: "選択",
6873
- SELECTED: "選択済み"
7218
+ SELECTED: "選択済み",
7219
+ BOOKED: "予約済み",
7220
+ SAVE: "保存"
6874
7221
  };
6875
7222
  var SIDEBAR = {
6876
7223
  OVERVIEW: "概要",
@@ -6928,6 +7275,27 @@ var TRAVELERS_FORM = {
6928
7275
  FIRST_NAME: "名",
6929
7276
  LAST_NAME: "姓",
6930
7277
  BIRTHDATE: "生年月日",
7278
+ NICKNAME: "Nickname",
7279
+ BIRTHPLACE: "Birthplace",
7280
+ NATIONALITY: "Nationality",
7281
+ PREFERRED_LANGUAGE: "Preferred language",
7282
+ TRAVELER_EMAIL: "Email",
7283
+ TRAVELER_PHONE: "Phone",
7284
+ TRAVELER_MOBILE: "Mobile phone",
7285
+ PASSPORT_NUMBER: "Passport number",
7286
+ PASSPORT_ISSUE_DATE: "Passport issue date",
7287
+ PASSPORT_ISSUED_LOCATION: "Passport issued location",
7288
+ PASSPORT_EXPIRY_DATE: "Passport expiry date",
7289
+ EMERGENCY_NUMBER: "Emergency number",
7290
+ HAS_INSURANCE: "Has insurance",
7291
+ INSURANCE_NAME: "Insurance name",
7292
+ INFO: "Additional information",
7293
+ WEIGHT: "体重",
7294
+ WEIGHT_UNIT_KG: "kg",
7295
+ WEIGHT_UNIT_LB: "lb",
7296
+ HEIGHT: "身長",
7297
+ HEIGHT_UNIT_CM: "cm",
7298
+ HEIGHT_UNIT_FT_IN: "ft/in",
6931
7299
  STREET: "住所",
6932
7300
  STREET_PLACEHOLDER: "住所を入力",
6933
7301
  HOUSE_NUMBER: "番地",
@@ -7581,6 +7949,14 @@ exports.AvailabilitySearchType = void 0;
7581
7949
  AvailabilitySearchType[(AvailabilitySearchType["flight"] = 3)] = "flight";
7582
7950
  })(exports.AvailabilitySearchType || (exports.AvailabilitySearchType = {}));
7583
7951
 
7952
+ var CustomFieldDataType = {
7953
+ bool: 0,
7954
+ date: 1,
7955
+ time: 2,
7956
+ string: 3,
7957
+ numeric: 4,
7958
+ };
7959
+
7584
7960
  var DataType = {
7585
7961
  text: "text",
7586
7962
  numeric: "number",
@@ -7596,6 +7972,12 @@ var DataType = {
7596
7972
  template: "template",
7597
7973
  };
7598
7974
 
7975
+ exports.DateAmountType = void 0;
7976
+ (function (DateAmountType) {
7977
+ DateAmountType[(DateAmountType["days"] = 0)] = "days";
7978
+ DateAmountType[(DateAmountType["months"] = 1)] = "months";
7979
+ })(exports.DateAmountType || (exports.DateAmountType = {}));
7980
+
7599
7981
  var DossierType = {
7600
7982
  hotelOnly: 0,
7601
7983
  transportOnly: 1,
@@ -7603,6 +7985,13 @@ var DossierType = {
7603
7985
  package: 3,
7604
7986
  };
7605
7987
 
7988
+ var DurationType = {
7989
+ hours: 0,
7990
+ days: 1,
7991
+ nights: 2,
7992
+ minutes: 3,
7993
+ };
7994
+
7606
7995
  var EntryLineFlag = {
7607
7996
  none: 0,
7608
7997
  allotmentNoPlace: 1,
@@ -7680,6 +8069,11 @@ var Gender = {
7680
8069
  other: 2,
7681
8070
  };
7682
8071
 
8072
+ var HeightType = {
8073
+ centimeters: 0,
8074
+ feetInches: 1,
8075
+ };
8076
+
7683
8077
  exports.MetaDataItemPropertyType = void 0;
7684
8078
  (function (MetaDataItemPropertyType) {
7685
8079
  MetaDataItemPropertyType[(MetaDataItemPropertyType["Height"] = 0)] = "Height";
@@ -7716,6 +8110,22 @@ var PaymentType = {
7716
8110
  cash: 3,
7717
8111
  };
7718
8112
 
8113
+ exports.PortalQsmType = void 0;
8114
+ (function (PortalQsmType) {
8115
+ PortalQsmType[(PortalQsmType["Multidestination"] = 0)] = "Multidestination";
8116
+ PortalQsmType[(PortalQsmType["Accommodation"] = 1)] = "Accommodation";
8117
+ PortalQsmType[(PortalQsmType["AccommodationAndFlight"] = 2)] =
8118
+ "AccommodationAndFlight";
8119
+ PortalQsmType[(PortalQsmType["Flight"] = 3)] = "Flight";
8120
+ PortalQsmType[(PortalQsmType["RoundTrip"] = 4)] = "RoundTrip";
8121
+ PortalQsmType[(PortalQsmType["GroupTour"] = 5)] = "GroupTour";
8122
+ PortalQsmType[(PortalQsmType["Package"] = 6)] = "Package";
8123
+ PortalQsmType[(PortalQsmType["Ticket"] = 7)] = "Ticket";
8124
+ PortalQsmType[(PortalQsmType["Car"] = 8)] = "Car";
8125
+ PortalQsmType[(PortalQsmType["Transfer"] = 9)] = "Transfer";
8126
+ PortalQsmType[(PortalQsmType["Cruise"] = 10)] = "Cruise";
8127
+ })(exports.PortalQsmType || (exports.PortalQsmType = {}));
8128
+
7719
8129
  var PriceChartType = {
7720
8130
  purchasePrices: 0,
7721
8131
  sellingPrices: 1,
@@ -7749,6 +8159,26 @@ var RoomType = {
7749
8159
  pk6: 5,
7750
8160
  };
7751
8161
 
8162
+ var RouteTransportType = {
8163
+ none: 0,
8164
+ privateTransport: 1,
8165
+ publicTransport: 2,
8166
+ rentalCar: 3,
8167
+ bus: 4,
8168
+ camper: 5,
8169
+ car: 6,
8170
+ charter: 7,
8171
+ cruise: 8,
8172
+ ferry: 9,
8173
+ flight: 10,
8174
+ taxi: 11,
8175
+ train: 12,
8176
+ transfer: 13,
8177
+ rocket: 14,
8178
+ walking: 15,
8179
+ cycling: 16,
8180
+ };
8181
+
7752
8182
  var SaleType = {
7753
8183
  ownOrganization: 0,
7754
8184
  resale: 1,
@@ -7788,6 +8218,13 @@ var ServiceType = {
7788
8218
  camp: 30,
7789
8219
  };
7790
8220
 
8221
+ var TrajectNodeType = {
8222
+ regular: 0,
8223
+ connectingNode: 1,
8224
+ poi: 2,
8225
+ subroute: 3,
8226
+ };
8227
+
7791
8228
  exports.UnitOfMeasure = void 0;
7792
8229
  (function (UnitOfMeasure) {
7793
8230
  UnitOfMeasure[(UnitOfMeasure["cm"] = 0)] = "cm";
@@ -7820,60 +8257,9 @@ var VendorType = {
7820
8257
  sunnyCars: 20,
7821
8258
  };
7822
8259
 
7823
- exports.DateAmountType = void 0;
7824
- (function (DateAmountType) {
7825
- DateAmountType[(DateAmountType["days"] = 0)] = "days";
7826
- DateAmountType[(DateAmountType["months"] = 1)] = "months";
7827
- })(exports.DateAmountType || (exports.DateAmountType = {}));
7828
-
7829
- exports.PortalQsmType = void 0;
7830
- (function (PortalQsmType) {
7831
- PortalQsmType[(PortalQsmType["Multidestination"] = 0)] = "Multidestination";
7832
- PortalQsmType[(PortalQsmType["Accommodation"] = 1)] = "Accommodation";
7833
- PortalQsmType[(PortalQsmType["AccommodationAndFlight"] = 2)] =
7834
- "AccommodationAndFlight";
7835
- PortalQsmType[(PortalQsmType["Flight"] = 3)] = "Flight";
7836
- PortalQsmType[(PortalQsmType["RoundTrip"] = 4)] = "RoundTrip";
7837
- PortalQsmType[(PortalQsmType["GroupTour"] = 5)] = "GroupTour";
7838
- PortalQsmType[(PortalQsmType["Package"] = 6)] = "Package";
7839
- PortalQsmType[(PortalQsmType["Ticket"] = 7)] = "Ticket";
7840
- PortalQsmType[(PortalQsmType["Car"] = 8)] = "Car";
7841
- PortalQsmType[(PortalQsmType["Transfer"] = 9)] = "Transfer";
7842
- PortalQsmType[(PortalQsmType["Cruise"] = 10)] = "Cruise";
7843
- })(exports.PortalQsmType || (exports.PortalQsmType = {}));
7844
-
7845
- var TrajectNodeType = {
7846
- regular: 0,
7847
- connectingNode: 1,
7848
- poi: 2,
7849
- subroute: 3,
7850
- };
7851
-
7852
- var DurationType = {
7853
- hours: 0,
7854
- days: 1,
7855
- nights: 2,
7856
- minutes: 3,
7857
- };
7858
-
7859
- var RouteTransportType = {
7860
- none: 0,
7861
- privateTransport: 1,
7862
- publicTransport: 2,
7863
- rentalCar: 3,
7864
- bus: 4,
7865
- camper: 5,
7866
- car: 6,
7867
- charter: 7,
7868
- cruise: 8,
7869
- ferry: 9,
7870
- flight: 10,
7871
- taxi: 11,
7872
- train: 12,
7873
- transfer: 13,
7874
- rocket: 14,
7875
- walking: 15,
7876
- cycling: 16,
8260
+ var WeightType = {
8261
+ kilograms: 0,
8262
+ pounds: 1,
7877
8263
  };
7878
8264
 
7879
8265
  /******************************************************************************
@@ -15007,6 +15393,34 @@ var getContentDraft = function (config, signal) {
15007
15393
  var apiKey = config.apiKey;
15008
15394
  return get(url, apiKey, config.token, signal, true);
15009
15395
  };
15396
+ /**
15397
+ * api/web/content/live/filtered
15398
+ * Gets filtered Live content
15399
+ * @param config
15400
+ * @param request
15401
+ * @param signal
15402
+ * @returns OK if succeeded.
15403
+ */
15404
+ var getFilteredContentLive = function (config, request, signal) {
15405
+ var url = "" + config.host + ENDPOINT_LIVE + "/filtered";
15406
+ var apiKey = config.apiKey;
15407
+ var body = JSON.stringify(request);
15408
+ return post(url, apiKey, body, config.token, signal, true);
15409
+ };
15410
+ /**
15411
+ * api/web/content/draft/filtered
15412
+ * Gets filtered Draft (Internal) content
15413
+ * @param config
15414
+ * @param request
15415
+ * @param signal
15416
+ * @returns OK if succeeded.
15417
+ */
15418
+ var getFilteredContentDraft = function (config, request, signal) {
15419
+ var url = "" + config.host + ENDPOINT_DRAFT + "/filtered";
15420
+ var apiKey = config.apiKey;
15421
+ var body = JSON.stringify(request);
15422
+ return post(url, apiKey, body, config.token, signal, true);
15423
+ };
15010
15424
  /**
15011
15425
  * api/web/content/live/by-property
15012
15426
  * Gets Live content matching a property value (or values).
@@ -15134,6 +15548,7 @@ var ENDPOINT_TRAJECT = ENDPOINT$1 + "/traject";
15134
15548
  var ENDPOINT_ENTRY = function (magicLinkCode) {
15135
15549
  return ENDPOINT$1 + "/entry/" + magicLinkCode;
15136
15550
  };
15551
+ var ENDPOINT_SAVE_ENTRY = ENDPOINT$1 + "/entry/save";
15137
15552
  var startTransaction = function (config, signal) {
15138
15553
  var apiKey = config.apiKey;
15139
15554
  var url = "" + config.host + ENDPOINT_START_TRANSACTION;
@@ -15162,6 +15577,12 @@ var getEntry = function (config, magicLinkCode, signal) {
15162
15577
  var url = "" + config.host + ENDPOINT_ENTRY(magicLinkCode);
15163
15578
  return get(url, apiKey, config.token, signal, true);
15164
15579
  };
15580
+ var savePackagingEntry = function (config, request, signal) {
15581
+ var url = "" + config.host + ENDPOINT_SAVE_ENTRY;
15582
+ var apiKey = config.apiKey;
15583
+ var body = JSON.stringify(request);
15584
+ return post(url, apiKey, body, config.token, signal, true);
15585
+ };
15165
15586
  var getTrajectEntry = function (config, request, signal) {
15166
15587
  var url = "" + config.host + ENDPOINT_TRAJECT;
15167
15588
  var apiKey = config.apiKey;
@@ -15225,6 +15646,7 @@ var submitForm = function (config, id, request, signal) {
15225
15646
  };
15226
15647
 
15227
15648
  exports.AllotmentType = AllotmentType;
15649
+ exports.CustomFieldDataType = CustomFieldDataType;
15228
15650
  exports.DataType = DataType;
15229
15651
  exports.DossierType = DossierType;
15230
15652
  exports.DurationType = DurationType;
@@ -15235,6 +15657,7 @@ exports.FormFieldType = FormFieldType;
15235
15657
  exports.FormFieldWidth = FormFieldWidth;
15236
15658
  exports.FormType = FormType;
15237
15659
  exports.Gender = Gender;
15660
+ exports.HeightType = HeightType;
15238
15661
  exports.MetaDataType = MetaDataType;
15239
15662
  exports.PaymentType = PaymentType;
15240
15663
  exports.PriceChartType = PriceChartType;
@@ -15246,6 +15669,7 @@ exports.ServiceTokenError = ServiceTokenError;
15246
15669
  exports.ServiceType = ServiceType;
15247
15670
  exports.TrajectNodeType = TrajectNodeType;
15248
15671
  exports.VendorType = VendorType;
15672
+ exports.WeightType = WeightType;
15249
15673
  exports.agents = agents;
15250
15674
  exports.alternateFlights = alternateFlights;
15251
15675
  exports.alternateHotels = alternateHotels;
@@ -15293,6 +15717,8 @@ exports.getEntryList = getEntryList;
15293
15717
  exports.getEntryListv2 = getEntryListv2;
15294
15718
  exports.getEntryStatus = getEntryStatus;
15295
15719
  exports.getEntryTotals = getEntryTotals;
15720
+ exports.getFilteredContentDraft = getFilteredContentDraft;
15721
+ exports.getFilteredContentLive = getFilteredContentLive;
15296
15722
  exports.getInvoiceList = getInvoiceList;
15297
15723
  exports.getItinerary = getItinerary;
15298
15724
  exports.getLocations = getLocations;
@@ -15313,6 +15739,7 @@ exports.print = print;
15313
15739
  exports.readForm = readForm;
15314
15740
  exports.repriceAndIssueTicket = repriceAndIssueTicket;
15315
15741
  exports.resetPassword = resetPassword;
15742
+ exports.savePackagingEntry = savePackagingEntry;
15316
15743
  exports.search = search;
15317
15744
  exports.searchCountries = searchCountries;
15318
15745
  exports.searchElastic = searchElastic;
@@ -17504,7 +17931,7 @@ const Product = ({ productCode, productName, duration, rating }) => {
17504
17931
  const [hasFlight, setHasFlight] = React.useState(false);
17505
17932
  const [hasTransfer, setHasTransfer] = React.useState(false);
17506
17933
  const [rooms, setRooms] = React.useState([{ adults: 2, children: 0, childAges: [] }]);
17507
- const [searchResponse, setSearchResponse] = React.useState([]);
17934
+ const [searchResponse, setSearchResponse] = React.useState();
17508
17935
  const [detailResponse, setDetailResponse] = React.useState();
17509
17936
  const [dateRange, setDateRange] = React.useState();
17510
17937
  const [urlDateRange, setUrlDateRange] = React.useState({ from: null, to: null });
@@ -17642,6 +18069,9 @@ const Product = ({ productCode, productName, duration, rating }) => {
17642
18069
  setPackageProductName(selectedItem.name);
17643
18070
  };
17644
18071
  React.useEffect(() => {
18072
+ if (searchResponse === undefined) {
18073
+ return;
18074
+ }
17645
18075
  if (searchResponse && !lodash.isEmpty(searchResponse)) {
17646
18076
  setSearchResults(searchResponse);
17647
18077
  const selectedItem = searchResponse.find((item) => new Date(item.fromDate).getTime() === dateRange?.fromDate?.getTime() && new Date(item.toDate).getTime() === dateRange?.toDate?.getTime());
@@ -18640,273 +19070,1099 @@ const SUMMARY_STEP = 5;
18640
19070
  const CONFIRMATION_STEP = 6;
18641
19071
  const ERROR_STEP = 7;
18642
19072
 
18643
- const fetchPriceDetails$1 = async (apiSettings, request, signal, languageCode) => {
18644
- const tideClientConfig = buildTideClientConfig(apiSettings);
18645
- return await build.priceDetails(tideClientConfig, request, signal, languageCode);
18646
- };
18647
- const priceDetailsApi = {
18648
- fetchPriceDetails: fetchPriceDetails$1
19073
+ const LabeledInput = ({ name, type, required, value, defaultValue, min, max, label, placeholder, extraClassName, hasError, onChange, onBlur }) => {
19074
+ return (React__default["default"].createElement("label", { className: buildClassName(['form__group', extraClassName, hasError && 'form__group--error']) },
19075
+ React__default["default"].createElement("span", { className: "form__label" }, lodash.compact([label, required && '*']).join(' ')),
19076
+ React__default["default"].createElement("input", { name: name, type: type ?? 'text', required: required, className: "form__input", min: min, max: max, placeholder: placeholder, onChange: onChange, onBlur: onBlur, value: value, defaultValue: defaultValue })));
18649
19077
  };
18650
19078
 
18651
- const updatePackageDetails = (packageDetails, changedLines) => {
18652
- return {
18653
- ...packageDetails,
18654
- options: packageDetails.options.map((opt) => {
18655
- return {
18656
- ...opt,
18657
- rooms: opt.isSelected ? updateRooms(opt.rooms, changedLines) : opt.rooms,
18658
- groups: opt.isSelected ? updateBookingGroups(opt.groups, changedLines) : opt.groups,
18659
- optionUnits: opt.isSelected ? updateOptionUnits(opt.optionUnits, changedLines) : opt.optionUnits,
18660
- optionPax: opt.isSelected ? updateOptionPax(opt.optionPax, changedLines) : opt.optionPax
18661
- };
18662
- })
18663
- };
18664
- };
18665
- // ROOMS
18666
- const updateRooms = (rooms, changedLines) => {
18667
- return rooms.map((room) => ({
18668
- ...room,
18669
- options: room.options.map((option) => ({
18670
- ...option,
18671
- price: changedLines.find((x) => x.entryLineGuid == option.entryLineGuid)?.price ?? option.price
18672
- }))
18673
- }));
18674
- };
18675
- // PAX
18676
- const updateOptionPax = (pax, changedLines) => {
18677
- return pax.map((p) => ({
18678
- ...p,
18679
- groups: updatePaxGroups(p.groups, changedLines)
18680
- }));
18681
- };
18682
- const updatePaxGroups = (groups, changedLines) => {
18683
- return groups.map((g) => ({
18684
- ...g,
18685
- options: g.options.map((o) => ({
18686
- ...o,
18687
- groups: updatePaxGroups(o.groups, changedLines),
18688
- line: {
18689
- ...o.line,
18690
- price: changedLines.find((cl) => cl.entryLineGuid === o.line.entryLineGuid)?.price ?? o.line.price
18691
- },
18692
- alternatives: o.alternatives.map((a) => ({
18693
- ...a,
18694
- price: changedLines.find((cl) => cl.entryLineGuid === a.entryLineGuid)?.price ?? a.price
18695
- }))
18696
- }))
18697
- }));
18698
- };
18699
- // UNITS
18700
- const updateOptionUnits = (units, changedLines) => {
18701
- return units.map((u) => ({
18702
- ...u,
18703
- groups: updateUnitGroups(u.groups, changedLines)
18704
- }));
18705
- };
18706
- const updateUnitGroups = (groups, changedLines) => {
18707
- return groups.map((g) => ({
18708
- ...g,
18709
- options: g.options.map((o) => ({
18710
- ...o,
18711
- groups: updateUnitGroups(o.groups, changedLines),
18712
- pax: updateOptionPax(o.pax, changedLines),
18713
- line: {
18714
- ...o.line,
18715
- price: changedLines.find((cl) => cl.entryLineGuid === o.line.entryLineGuid)?.price ?? o.line.price
18716
- },
18717
- alternatives: o.alternatives.map((a) => ({
18718
- ...a,
18719
- price: changedLines.find((cl) => cl.entryLineGuid === a.entryLineGuid)?.price ?? a.price
18720
- }))
18721
- }))
18722
- }));
18723
- };
18724
- // BOOKING
18725
- const updateBookingGroups = (groups, changedLines) => {
18726
- return groups.map((g) => ({
18727
- ...g,
18728
- options: g.options.map((o) => ({
18729
- ...o,
18730
- groups: updateBookingGroups(o.groups, changedLines),
18731
- units: updateOptionUnits(o.units, changedLines),
18732
- pax: updateOptionPax(o.pax, changedLines),
18733
- line: {
18734
- ...o.line,
18735
- price: changedLines.find((cl) => cl.entryLineGuid === o.line.entryLineGuid)?.price ?? o.line.price
18736
- },
18737
- alternatives: o.alternatives.map((a) => ({
18738
- ...a,
18739
- price: changedLines.find((cl) => cl.entryLineGuid === a.entryLineGuid)?.price ?? a.price
18740
- }))
18741
- }))
18742
- }));
19079
+ const LabeledSelect = ({ name, id, required, value, defaultValue, label, extraClassName, extraDropdownClassName, hasError, options, onChange, onBlur }) => {
19080
+ return (React__default["default"].createElement("label", { className: buildClassName(['form__group', extraClassName, hasError && 'form__group--error']) },
19081
+ React__default["default"].createElement("span", { className: "form__label" }, lodash.compact([label, required && '*']).join(' ')),
19082
+ React__default["default"].createElement("div", { className: buildClassName(['dropdown', extraDropdownClassName]) },
19083
+ React__default["default"].createElement("select", { name: name, id: id, defaultValue: defaultValue, value: value, onBlur: onBlur, onChange: onChange }, options?.map((option) => (React__default["default"].createElement("option", { key: option.key, value: option.value }, option.label)))))));
18743
19084
  };
18744
19085
 
18745
- const fetchPriceDetails = toolkit.createAsyncThunk('priceDetails/fetchPriceDetails', async (_, { dispatch, getState, signal }) => {
18746
- const state = getState();
18747
- const languageCode = selectLanguageCode(state);
18748
- const request = selectBookingPackageBookRequest(state);
18749
- if (!request) {
18750
- throw Error('Product not available.');
18751
- }
18752
- dispatch(setFetchingPriceDetails(true));
18753
- dispatch(resetPriceDetails());
18754
- const priceDetails = await priceDetailsApi.fetchPriceDetails(state.apiSettings, request, signal, languageCode);
18755
- dispatch(setFetchingPriceDetails(false));
18756
- const packageDetails = selectPackageDetails(state);
18757
- if (packageDetails) {
18758
- const updatedPackageDetails = updatePackageDetails(packageDetails, priceDetails.changedLines);
18759
- dispatch(setPackage(updatedPackageDetails));
19086
+ const normalize = (v) => (v == null ? '' : String(v));
19087
+ const parseCombined = (raw, countries) => {
19088
+ const value = normalize(raw).trim();
19089
+ if (!value)
19090
+ return { prefix: '', number: '' };
19091
+ // Try to match a known prefix at the start. Prefer longest match.
19092
+ const sorted = [...countries].sort((a, b) => b.phonePrefix.length - a.phonePrefix.length);
19093
+ const hit = sorted.find((c) => value.startsWith(c.phonePrefix));
19094
+ if (hit) {
19095
+ const rest = value
19096
+ .slice(hit.phonePrefix.length)
19097
+ .trim()
19098
+ .replace(/^[-\s]+/, '');
19099
+ return { prefix: hit.phonePrefix, number: rest };
18760
19100
  }
18761
- return priceDetails;
18762
- });
18763
- const priceDetailsSlice = toolkit.createSlice({
18764
- name: 'priceDetails',
18765
- initialState: {
18766
- priceDetails: [],
18767
- notifications: [],
18768
- isBusy: false
18769
- },
18770
- reducers: {
18771
- setFetchingPriceDetails(state, action) {
18772
- state.isBusy = action.payload;
18773
- },
18774
- setNotifications(state, action) {
18775
- state.notifications = action.payload;
18776
- },
18777
- resetPriceDetails(state, _) {
18778
- state.priceDetails = [];
18779
- }
18780
- },
18781
- extraReducers: (builder) => {
18782
- builder.addCase(fetchPriceDetails.fulfilled, (state, action) => {
18783
- if (action.payload) {
18784
- state.deposit = action.payload.deposit;
18785
- state.commission = action.payload.commission;
18786
- state.priceDetails = action.payload.details;
18787
- state.notifications = action.payload.notifications;
18788
- }
18789
- });
18790
- builder.addCase(fetchPriceDetails.rejected, (state) => {
18791
- state.isBusy = false;
19101
+ // Fallback: split on first space if it looks like a +prefix number
19102
+ const m = value.match(/^(\+\d{1,4})[\s-]*(.*)$/);
19103
+ if (m)
19104
+ return { prefix: m[1], number: m[2] };
19105
+ return { prefix: '', number: value };
19106
+ };
19107
+ const PhoneInput = ({ name, required, value, label, placeholder, extraClassName, hasError, countries, countryIso2, onChange, onBlur }) => {
19108
+ // Derive initial state from `value`
19109
+ const initialCountry = countries.find((c) => c.iso2 === countryIso2);
19110
+ const initial = React.useMemo(() => parseCombined(normalize(value), countries), [value, countries]);
19111
+ const [prefix, setPrefix] = React.useState(initial.prefix);
19112
+ const [number, setNumber] = React.useState(initial.number);
19113
+ // Keep state in sync if the parent changes `value`
19114
+ React.useEffect(() => {
19115
+ const parsed = !prefix && !number && initialCountry ? { prefix: initialCountry.phonePrefix, number: '' } : parseCombined(normalize(value), countries);
19116
+ if (parsed.prefix)
19117
+ setPrefix(parsed.prefix);
19118
+ if (parsed.number)
19119
+ setNumber(parsed.number);
19120
+ }, [value, countries, countryIso2]);
19121
+ const emitCombinedChange = (e, nextPrefix, nextNumber) => {
19122
+ // Combine with a space, unless the number already starts with a dash or space
19123
+ const combined = nextPrefix && nextNumber ? lodash.compact([nextPrefix, nextNumber]).join(' ') : null;
19124
+ onChange?.({
19125
+ ...e,
19126
+ type: 'change',
19127
+ target: { name, value: combined },
19128
+ currentTarget: { name, value: combined }
18792
19129
  });
18793
- }
18794
- });
18795
- const { setFetchingPriceDetails } = priceDetailsSlice.actions;
18796
- const { resetPriceDetails, setNotifications } = priceDetailsSlice.actions;
18797
- const selectNotifications = (state) => state.priceDetails.notifications;
18798
- var priceDetailsReducer = priceDetailsSlice.reducer;
19130
+ };
19131
+ const handleCountryBlur = (e) => {
19132
+ onBlur?.(e);
19133
+ };
19134
+ const onCountryChange = (e) => {
19135
+ const next = e.target.value;
19136
+ setPrefix(next);
19137
+ emitCombinedChange(e, next, number);
19138
+ };
19139
+ const onPhoneChange = (e) => {
19140
+ const cleaned = e.target.value.replace(/[^\d\s-]/g, '');
19141
+ setNumber(cleaned);
19142
+ emitCombinedChange(e, prefix, cleaned);
19143
+ };
19144
+ const handleNumberBlur = (e) => {
19145
+ onBlur?.(e);
19146
+ };
19147
+ // Ensure the select shows a reasonable default when prefix is empty
19148
+ const selectValue = prefix || '';
19149
+ return (React__default["default"].createElement("label", { className: buildClassName(['form__group', extraClassName, hasError && 'form__group--error']) },
19150
+ label && React__default["default"].createElement("span", { className: "form__label" }, lodash.compact([label, required && '*']).join(' ')),
19151
+ React__default["default"].createElement("div", { className: "phone-input" },
19152
+ React__default["default"].createElement("div", { className: "dropdown" },
19153
+ React__default["default"].createElement("select", { "aria-label": label ? `${label} – country code` : 'Country calling code', name: `${name}__country`, value: selectValue, onBlur: handleCountryBlur, onChange: onCountryChange },
19154
+ React__default["default"].createElement("option", { value: "", disabled: true }),
19155
+ countries?.map((option) => (React__default["default"].createElement("option", { key: option.iso2, value: option.phonePrefix },
19156
+ option.name,
19157
+ " (",
19158
+ option.phonePrefix,
19159
+ ")"))))),
19160
+ React__default["default"].createElement("input", { "aria-label": label ? `${label} – number` : 'Phone number', name: `${name}__number`, type: "tel", inputMode: "tel", required: required, className: "form__input", placeholder: placeholder, onChange: onPhoneChange, onBlur: handleNumberBlur, value: number }),
19161
+ React__default["default"].createElement("input", { type: "hidden", name: name, value: lodash.compact([prefix, number]).join(' ') }))));
19162
+ };
18799
19163
 
18800
- const selectHasMounted = (state) => state.booking.hasMounted;
18801
- const selectGeneratePaymentUrl = (state) => state.booking.generatePaymentUrl;
18802
- const selectSkipPaymentWithAgent = (state) => state.booking.skipPaymentWithAgent;
18803
- const selectIsFetchingProductOptions = (state) => state.booking.isBusy;
18804
- const selectDepartureFlight = (state) => state.booking.package?.outwardFlights?.find((x) => x.isSelected);
18805
- const selectReturnFlight = (state) => state.booking.package?.returnFlights?.find((x) => x.isSelected);
18806
- const selectPackageRooms = (state) => state.booking.package?.options.find((x) => x.isSelected)?.rooms;
18807
- const selectIncludedServiceTypes = (state) => selectActiveOption(state)?.includedServiceTypes;
18808
- const selectAvailabilities = (state) => state.booking.package?.options.find((x) => x.isSelected)?.availabilities;
18809
- const selectPackageTags = (state) => state.booking.package?.tags;
18810
- const selectIsOnRequest = (state) => state.booking.package?.options.find((x) => x.isSelected)?.isOnRequest;
18811
- const selectPackageOptionUnits = (state) => state.booking.package?.options.find((x) => x.isSelected)?.optionUnits;
18812
- const selectPackageOptionPax = (state) => state.booking.package?.options.find((x) => x.isSelected)?.optionPax;
18813
- const selectPackageGroups = (state) => state.booking.package?.options.find((x) => x.isSelected)?.groups;
18814
- const selectPackageDetails = (state) => state.booking.package;
18815
- const selectPackageFlights = (state) => ({
18816
- outward: state.booking.package?.outwardFlights,
18817
- return: state.booking.package?.returnFlights
18818
- });
18819
- const selectFlightMetaData = (state) => state.booking.package?.flightInfo.metaData;
18820
- const selectActiveOption = (state) => state.booking.package?.options.find((x) => x.isSelected);
18821
- const selectPackageAirlineGroups = toolkit.createSelector(selectActiveOption, selectDepartureFlight, selectReturnFlight, (option, departureFlight, returnFlight) => option?.airlineGroups.filter((x) => x.flightIds.includes(departureFlight?.entryLineGuid ?? '') || x.flightIds.includes(returnFlight?.entryLineGuid ?? '')));
18822
- const selectPackageAirportGroups = toolkit.createSelector(selectActiveOption, selectDepartureFlight, selectReturnFlight, (option, departureFlight, returnFlight) => option?.airportGroups.filter((x) => x.flightIds.includes(departureFlight?.entryLineGuid ?? '') || x.flightIds.includes(returnFlight?.entryLineGuid ?? '')));
18823
- const selectRoomOptionDepartureFlightsMetaData = toolkit.createSelector(selectActiveOption, selectFlightMetaData, (ativeOption, flightMetaData) => {
18824
- const roomOptions = ativeOption?.rooms.map((r) => r.options.filter((o) => o.isSelected).shift());
18825
- return roomOptions?.map((ro) => flightMetaData?.find((f) => f.entryLineGuid === ro?.entryLineGuid)?.flightMetaData).filter((f) => f !== undefined) ?? [];
18826
- });
18827
- const selectRoomOptionReturnFlightsMetaData = toolkit.createSelector(selectActiveOption, selectFlightMetaData, (ativeOption, flightMetaData) => {
18828
- const selectedRoomOptions = ativeOption?.rooms.map((r) => r.options.filter((o) => o.isSelected));
18829
- // check if there are return flights selected
18830
- if (selectedRoomOptions?.some((ro) => ro.length > 1)) {
18831
- const roomOptions = ativeOption?.rooms.map((r) => r.options
18832
- .filter((o) => o.isSelected)
18833
- .reverse()
18834
- .shift());
18835
- return roomOptions?.map((ro) => flightMetaData?.find((f) => f.entryLineGuid === ro?.entryLineGuid)?.flightMetaData).filter((f) => f !== undefined) ?? [];
18836
- }
18837
- return undefined;
18838
- });
18839
- const selectApiSettings = (state) => state.apiSettings;
18840
- const selectIsUnavailable = (state) => state.booking.isUnavailable;
18841
- const selectRequestRooms = (state) => state.booking.package?.options.find((x) => x.isSelected)?.requestRooms;
18842
- const selectOfficeId = (state) => state.booking.officeId;
18843
- const selectLanguageCode = (state) => state.booking.languageCode;
18844
- const selectCurrencyCode = (state) => state.booking.package?.currencyCode ?? 'EUR';
18845
- const selectLanguage = (state) => state.booking.languageCode;
18846
- const selectAllDynamicTranslations = (state) => state.booking.translations;
18847
- const selectDynamicTranslations = toolkit.createSelector([selectAllDynamicTranslations, selectLanguage], (allDynamicTranslations, language) => {
18848
- return allDynamicTranslations?.find((x) => x.language == language)?.value ?? {};
18849
- });
18850
- // Host override files are hand-maintained, and a null only ever blanks a key the component
18851
- // then reads, so treat it like a missing key (lodash merge already skips undefined).
18852
- const withoutNulls = (value) => lodash.isPlainObject(value)
18853
- ? lodash.mapValues(lodash.pickBy(value, (x) => x !== null), withoutNulls)
18854
- : value;
18855
- // getTranslations deep-merges the overrides over a clone of the built-in translations, so a
18856
- // host override of a nested group (TRAVELERS_FORM.VALIDATION, TRAVELERS_FORM.COUNTRIES)
18857
- // only replaces the keys it actually contains. Merging per section one level deep dropped
18858
- // every other built-in key of such a group, which made those keys resolve to undefined and
18859
- // crash format() in the consuming component.
18860
- const selectTranslations = toolkit.createSelector([selectLanguage, selectDynamicTranslations], (language, dynamicTranslations) => getTranslations(language, lodash.isEmpty(dynamicTranslations) ? undefined : withoutNulls(dynamicTranslations)));
18861
- const selectBookingOptions = (state) => state.booking.bookingOptions;
18862
- const selectBookingType = (state) => state.booking.bookingType;
18863
- const selectTagIds = (state) => state.booking.tagIds;
18864
- const selectAgentAdressId = (state) => state.booking.agentAdressId;
18865
- const selectProductAttributes = (state) => state.booking.productAttributes;
18866
- const selectBookingAttributes = (state) => state.booking.bookingAttributes;
18867
- const selectBookingNumber = (state) => state.booking.bookingNumber;
18868
- const selectIsOption = (state) => state.booking.isOption;
18869
- const selectBookingRooms = (state) => state.booking.bookingAttributes?.rooms;
18870
- const selectBookingRemarks = (state) => state.booking.remarks;
18871
- const selectVoucherCodes = (state) => state.booking.voucherCodes;
18872
- const selectCalculateDeposit = (state) => state.booking.calculateDeposit;
18873
- const selectShowCommission = (state) => state.booking.showCommission;
18874
- const selectIsRetry = (state) => state.booking.isRetry;
18875
- const selectStartDate = (state) => state.booking.package?.options.find((x) => x.isSelected)?.fromDate;
18876
- const selectEndDate = (state) => state.booking.package?.options.find((x) => x.isSelected)?.toDate;
18877
- const selectAgents = (state) => state.booking.agents;
18878
- const selectCountries = (state) => state.booking.countries;
18879
- const selectProductCode = (state) => state.booking.productAttributes?.productCode;
18880
- const selectAccommodationCodes = (state) => {
18881
- const accommodationCodes = [];
18882
- state.booking.package?.options.forEach((o) => {
18883
- o.rooms.forEach((r) => r.options.forEach((ro) => {
18884
- if (!accommodationCodes.some((y) => y === ro.accommodationCode)) {
18885
- accommodationCodes.push(ro.accommodationCode);
18886
- }
18887
- }));
18888
- });
18889
- return accommodationCodes;
19164
+ const hasVisibleError = (formik, key) => lodash.get(formik.errors, key) && lodash.get(formik.touched, key);
19165
+
19166
+ const GenderControl = ({ translations, value, formik, name }) => {
19167
+ return (React__default["default"].createElement("div", { className: buildClassName(['form__group', hasVisibleError(formik, name) && 'form__group--error']) },
19168
+ React__default["default"].createElement("label", { className: "form__label" },
19169
+ translations.TRAVELERS_FORM.GENDER_ID,
19170
+ " *"),
19171
+ React__default["default"].createElement("div", { className: "radiobutton-group" },
19172
+ React__default["default"].createElement("div", { className: "radiobutton" },
19173
+ React__default["default"].createElement("label", { className: "radiobutton__label" },
19174
+ React__default["default"].createElement("input", { type: "radio", className: "radiobutton__input", name: name, onChange: formik.handleChange, onBlur: formik.handleBlur, value: "m", checked: value.gender === 'm' }),
19175
+ translations.TRAVELERS_FORM.MALE_GENDER)),
19176
+ React__default["default"].createElement("div", { className: "radiobutton" },
19177
+ React__default["default"].createElement("label", { className: "radiobutton__label" },
19178
+ React__default["default"].createElement("input", { type: "radio", className: "radiobutton__input", name: name, onChange: formik.handleChange, onBlur: formik.handleBlur, value: "f", checked: value.gender === 'f' }),
19179
+ translations.TRAVELERS_FORM.FEMALE_GENDER)))));
18890
19180
  };
18891
- const selectAccommodationViews = (state) => state.booking.accommodationViews;
18892
- const selectMappingInfo = (state) => state.booking.package?.options.find((x) => x.isSelected)?.mappingInfo;
18893
- const selectBookingQuery = (state) => {
18894
- const bookingAttributes = state.booking.bookingAttributes;
18895
- if (!bookingAttributes) {
18896
- return undefined;
18897
- }
18898
- const params = {};
18899
- Object.entries(bookingAttributes).forEach(([key, value]) => {
18900
- if (key === 'startDate' || key === 'endDate') {
18901
- value = dateFns.format(dateFns.parseISO(value), 'yyyy-MM-dd');
18902
- }
18903
- if (key === 'rooms') {
18904
- value = JsonURL__default["default"].stringify(value.map((room) => lodash.omit(room, ['children'])), {
18905
- AQF: true
18906
- });
19181
+
19182
+ const TypeAheadInput = ({ name, value, placeholder, options, onChange, onSelect, onClear }) => {
19183
+ const [showItems, setShowItems] = React.useState(false);
19184
+ const handleChange = (e) => {
19185
+ if (onChange) {
19186
+ onChange(e.target.value);
18907
19187
  }
18908
- if (key === 'allotmentIds' && !value.length) {
18909
- value = undefined;
19188
+ };
19189
+ const handleClearClick = (e) => {
19190
+ if (onClear) {
19191
+ onClear();
19192
+ }
19193
+ setShowItems(false);
19194
+ };
19195
+ return (React__default["default"].createElement("div", { className: "typeahead" },
19196
+ React__default["default"].createElement("div", { className: "typeahead__trigger typeahead__trigger--icon" },
19197
+ React__default["default"].createElement(Icon, { name: "ui-qsm-location" }),
19198
+ React__default["default"].createElement("input", { type: "text", className: "form__input", name: name, value: value, onChange: handleChange, placeholder: placeholder, required: true, autoComplete: "off", onKeyDown: (e) => {
19199
+ if (e.key === 'Tab' && options && onSelect) {
19200
+ onSelect(options[0].key);
19201
+ setShowItems(false);
19202
+ }
19203
+ }, onFocus: () => setShowItems(true) }),
19204
+ React__default["default"].createElement("button", { type: "button", className: "button button--clear", onClick: handleClearClick })),
19205
+ options && options.length > 0 && showItems && (React__default["default"].createElement("div", { className: "typeahead__options" }, (options ?? []).map((option) => {
19206
+ const matchIndex = typeof value !== 'undefined' ? option.text.indexOf(value) : 0;
19207
+ const prefix = option.text.slice(0, matchIndex);
19208
+ const highlight = option.text.slice(matchIndex, value?.length ?? 0);
19209
+ const suffix = option.text.slice(matchIndex);
19210
+ return (React__default["default"].createElement("div", { className: "typeahead__option", key: option.key },
19211
+ React__default["default"].createElement("div", { className: "typeahead__option-header" },
19212
+ React__default["default"].createElement("button", { onClick: () => {
19213
+ if (onSelect)
19214
+ onSelect(option.key);
19215
+ setShowItems(false);
19216
+ }, type: "button", className: "button button--plain" },
19217
+ prefix,
19218
+ React__default["default"].createElement("span", { className: "typeahead__option-highlight" }, highlight),
19219
+ suffix))));
19220
+ })))));
19221
+ };
19222
+
19223
+ // A traveler is a baby when the age they were booked with is at or below the baby
19224
+ // boundary. Babies get their own label and birth date validation (TIDE-3269). Only
19225
+ // meaningful for the regular form: the compact form asks for an age instead of a birth
19226
+ // date, so it has nothing to validate and its own age input already states the age.
19227
+ function isBabyAge(age, maxBabyAge = BABY_MAX_AGE) {
19228
+ return typeof age === 'number' && age <= maxBabyAge;
19229
+ }
19230
+ // The native date input can hold values format() throws on (e.g. a 5+ digit year while typing), so never format an unvalidated birthDate
19231
+ function formatBirthDate$1(birthDate) {
19232
+ if (!birthDate)
19233
+ return '';
19234
+ const parsed = dateFns.parse(birthDate, 'yyyy-MM-dd', new Date());
19235
+ return dateFns.isValid(parsed) ? dateFns.format(parsed, 'dd-MM-yyyy') : '';
19236
+ }
19237
+ // A field can have separate "main-booker"/other-pax variants sharing the same identity
19238
+ // (customFieldConfigurationId for custom fields, fieldName for built-in fields).
19239
+ const MAIN_BOOKER_GROUP = 'main-booker';
19240
+ const isCustomPaxField = (field) => field.customFieldConfigurationId != null;
19241
+ function resolveApplicablePaxFields(fields, isMainBooker) {
19242
+ const variantsByKey = new Map();
19243
+ fields.forEach((field) => {
19244
+ const key = field.customFieldConfigurationId ?? field.fieldName;
19245
+ const variants = variantsByKey.get(key) ?? [];
19246
+ variants.push(field);
19247
+ variantsByKey.set(key, variants);
19248
+ });
19249
+ const applicableFields = [];
19250
+ variantsByKey.forEach((variants) => {
19251
+ const hasRoleSplit = variants.some((v) => v.group === MAIN_BOOKER_GROUP);
19252
+ if (!hasRoleSplit) {
19253
+ applicableFields.push(variants[0]);
19254
+ return;
19255
+ }
19256
+ const roleMatch = variants.find((v) => (v.group === MAIN_BOOKER_GROUP) === isMainBooker);
19257
+ if (roleMatch)
19258
+ applicableFields.push(roleMatch);
19259
+ });
19260
+ return applicableFields;
19261
+ }
19262
+ function buildEmptyCustomFields(paxFields = []) {
19263
+ const uniqueIds = Array.from(new Set(paxFields.filter(isCustomPaxField).map((field) => field.customFieldConfigurationId)));
19264
+ return uniqueIds.map((customFieldConfigurationId) => ({ customFieldConfigurationId, value: '' }));
19265
+ }
19266
+ function getApplicablePaxCustomFields(paxFields = [], isMainBooker) {
19267
+ return resolveApplicablePaxFields(paxFields.filter(isCustomPaxField), isMainBooker);
19268
+ }
19269
+ function getApplicableBuiltInPaxFields(paxFields = [], isMainBooker) {
19270
+ return resolveApplicablePaxFields(paxFields.filter((f) => !isCustomPaxField(f)), isMainBooker);
19271
+ }
19272
+ function getBuiltInPaxField(paxFields = [], fieldName, isMainBooker) {
19273
+ return getApplicableBuiltInPaxFields(paxFields, isMainBooker).find((f) => f.fieldName === fieldName);
19274
+ }
19275
+ // fieldName -> path on `Traveler`, shared with validate-form.ts.
19276
+ const BUILT_IN_FIELD_PATHS = {
19277
+ gender: 'gender',
19278
+ firstName: 'firstName',
19279
+ lastName: 'lastName',
19280
+ dateOfBirth: 'birthDate',
19281
+ nickname: 'nickname',
19282
+ birthplace: 'birthplace',
19283
+ nationality: 'nationality',
19284
+ preferredLanguageId: 'preferredLanguageId',
19285
+ email: 'email',
19286
+ phone: 'phone',
19287
+ mobile: 'mobile',
19288
+ passportIssueDate: 'passportIssueDate',
19289
+ passportIssuedLocation: 'passportIssuedLocation',
19290
+ passportExpiryDate: 'passportExpiryDate',
19291
+ passportNumber: 'passportNumber',
19292
+ emergencyNumber: 'emergencyNumber',
19293
+ hasInsurance: 'hasInsurance',
19294
+ insuranceName: 'insuranceName',
19295
+ info: 'info',
19296
+ street: 'address.street',
19297
+ location: 'address.location',
19298
+ countryId: 'address.country',
19299
+ postalCode: 'address.postalCode',
19300
+ number: 'address.number'
19301
+ };
19302
+ // The 4 fields that always render/validate regardless of paxFields config (fallback behavior).
19303
+ const CORE_FIELD_NAMES = new Set(['gender', 'firstName', 'lastName', 'dateOfBirth']);
19304
+ const ADDRESS_FIELD_NAMES = ['street', 'location', 'countryId', 'postalCode', 'number'];
19305
+ // Shared by buildPax (new-booking) and applyTravelersFormValuesToEditablePackagingEntry
19306
+ // (edit-booking), now that PackagingEntryPax and BookingPackagePax have the same shape.
19307
+ function applyTravelerToPax(pax, traveler, mainBookerId) {
19308
+ return {
19309
+ ...pax,
19310
+ firstName: traveler.firstName ?? '',
19311
+ lastName: traveler.lastName ?? '',
19312
+ dateOfBirth: traveler.birthDate || null,
19313
+ isMainBooker: mainBookerId != null ? traveler.id === mainBookerId : pax.isMainBooker,
19314
+ weight: traveler.weight ?? null,
19315
+ weightType: traveler.weightType ?? build.WeightType.kilograms,
19316
+ height: traveler.height ?? null,
19317
+ heightType: traveler.heightType ?? build.HeightType.centimeters,
19318
+ nickname: traveler.nickname,
19319
+ birthplace: traveler.birthplace,
19320
+ nationality: traveler.nationality,
19321
+ preferredLanguageId: traveler.preferredLanguageId,
19322
+ email: traveler.email,
19323
+ phone: traveler.phone,
19324
+ mobilePhone: traveler.mobile,
19325
+ emergencyNumber: traveler.emergencyNumber,
19326
+ passportNumber: traveler.passportNumber,
19327
+ passportIssueDate: traveler.passportIssueDate,
19328
+ passportIssuedLocation: traveler.passportIssuedLocation,
19329
+ passportExpiryDate: traveler.passportExpiryDate,
19330
+ hasInsurance: traveler.hasInsurance,
19331
+ insuranceName: traveler.insuranceName,
19332
+ info: traveler.info,
19333
+ address: buildTravelerAddress(traveler),
19334
+ customFields: traveler.customFields ?? []
19335
+ };
19336
+ }
19337
+ // name/email are required on the wire but aren't part of the per-traveler address sub-form -
19338
+ // derive them from the traveler's own name/email instead of asking twice.
19339
+ function buildTravelerAddress(traveler) {
19340
+ if (!traveler.address || !Object.values(traveler.address).some((v) => !!v))
19341
+ return undefined;
19342
+ return {
19343
+ name: `${traveler.firstName} ${traveler.lastName}`.trim(),
19344
+ street: traveler.address.street ?? '',
19345
+ number: traveler.address.number ?? '',
19346
+ postalCode: traveler.address.postalCode ?? '',
19347
+ location: traveler.address.location ?? '',
19348
+ country: traveler.address.country ?? '',
19349
+ email: traveler.email ?? ''
19350
+ };
19351
+ }
19352
+ // Always initialized (even when not shown) to keep Formik inputs controlled, matching weight/height.
19353
+ const EMPTY_BUILT_IN_FIELDS = {
19354
+ nickname: '',
19355
+ birthplace: '',
19356
+ nationality: '',
19357
+ preferredLanguageId: undefined,
19358
+ email: '',
19359
+ phone: '',
19360
+ mobile: '',
19361
+ passportNumber: '',
19362
+ passportIssueDate: '',
19363
+ passportIssuedLocation: '',
19364
+ passportExpiryDate: '',
19365
+ emergencyNumber: '',
19366
+ hasInsurance: false,
19367
+ insuranceName: '',
19368
+ info: '',
19369
+ address: { street: '', number: '', postalCode: '', location: '', country: '' }
19370
+ };
19371
+ function createTraveler(traveler, followNumber, personTranslation, isCompact, maxBabyAge, paxFields) {
19372
+ if (isCompact) {
19373
+ return {
19374
+ id: traveler.id,
19375
+ firstName: personTranslation,
19376
+ lastName: `${followNumber.number++}`,
19377
+ birthDate: '',
19378
+ gender: '',
19379
+ // Keep a booked age of 0 instead of submitting and pricing the traveler as a 30 year
19380
+ // old, while still falling back for a missing or unusable age. `??` alone would let a
19381
+ // NaN (unparseable date of birth upstream) reach the age input and the booked pax.
19382
+ // See TIDE-3269.
19383
+ age: Number.isFinite(traveler.age) ? traveler.age : 30,
19384
+ // No isBaby here on purpose: the age below is editable and nothing revalidates it
19385
+ // in compact mode, so a stored flag would end up contradicting the entered age.
19386
+ weight: null,
19387
+ weightType: build.WeightType.kilograms,
19388
+ height: null,
19389
+ heightType: build.HeightType.centimeters,
19390
+ ...EMPTY_BUILT_IN_FIELDS,
19391
+ customFields: buildEmptyCustomFields(paxFields)
19392
+ };
19393
+ }
19394
+ return {
19395
+ id: traveler.id,
19396
+ firstName: '',
19397
+ lastName: '',
19398
+ birthDate: '',
19399
+ gender: '',
19400
+ isBaby: isBabyAge(traveler.age, maxBabyAge),
19401
+ weight: null,
19402
+ weightType: build.WeightType.kilograms,
19403
+ height: null,
19404
+ heightType: build.HeightType.centimeters,
19405
+ ...EMPTY_BUILT_IN_FIELDS,
19406
+ customFields: buildEmptyCustomFields(paxFields)
19407
+ };
19408
+ }
19409
+ function createInitialValuesFromRooms(formRooms, startDate, endDate, agentAdressId, personTranslation, isCompact, maxBabyAge, paxFields) {
19410
+ const followNumber = { number: 1 };
19411
+ const initialValues = {
19412
+ startDate,
19413
+ endDate,
19414
+ rooms: formRooms.map((room) => ({
19415
+ adults: room.adults.map((traveler) => createTraveler(traveler, followNumber, personTranslation, isCompact, maxBabyAge, paxFields)),
19416
+ children: room.children.map((traveler) => createTraveler(traveler, followNumber, personTranslation, isCompact, maxBabyAge, paxFields))
19417
+ })),
19418
+ mainBookerId: -1,
19419
+ street: '',
19420
+ houseNumber: '',
19421
+ box: '',
19422
+ zipCode: '',
19423
+ place: '',
19424
+ country: '',
19425
+ phone: '',
19426
+ email: '',
19427
+ emailConfirmation: '',
19428
+ travelAgentId: agentAdressId ?? 0,
19429
+ travelAgentName: ''
19430
+ };
19431
+ if (initialValues.rooms?.[0]?.adults?.[0]) {
19432
+ initialValues.mainBookerId = initialValues.rooms[0].adults[0].id;
19433
+ }
19434
+ return initialValues;
19435
+ }
19436
+ function mapPackagingEntryPaxToTraveler(roomTraveler) {
19437
+ return {
19438
+ weight: roomTraveler.weight ?? null,
19439
+ weightType: roomTraveler.weightType ?? build.WeightType.kilograms,
19440
+ height: roomTraveler.height ?? null,
19441
+ heightType: roomTraveler.heightType ?? build.HeightType.centimeters,
19442
+ nickname: roomTraveler.nickname ?? '',
19443
+ birthplace: roomTraveler.birthplace ?? '',
19444
+ nationality: roomTraveler.nationality ?? '',
19445
+ preferredLanguageId: roomTraveler.preferredLanguageId,
19446
+ email: roomTraveler.email ?? '',
19447
+ phone: roomTraveler.phone ?? '',
19448
+ mobile: roomTraveler.mobilePhone ?? '',
19449
+ passportNumber: roomTraveler.passportNumber ?? '',
19450
+ passportIssueDate: roomTraveler.passportIssueDate ?? '',
19451
+ passportIssuedLocation: roomTraveler.passportIssuedLocation ?? '',
19452
+ passportExpiryDate: roomTraveler.passportExpiryDate ?? '',
19453
+ emergencyNumber: roomTraveler.emergencyNumber ?? '',
19454
+ hasInsurance: roomTraveler.hasInsurance ?? false,
19455
+ insuranceName: roomTraveler.insuranceName ?? '',
19456
+ info: roomTraveler.info ?? '',
19457
+ address: roomTraveler.address
19458
+ ? {
19459
+ street: roomTraveler.address.street ?? '',
19460
+ number: roomTraveler.address.number ?? '',
19461
+ postalCode: roomTraveler.address.postalCode ?? '',
19462
+ location: roomTraveler.address.location ?? '',
19463
+ country: roomTraveler.address.country ?? ''
19464
+ }
19465
+ : undefined,
19466
+ customFields: roomTraveler.customFields ?? []
19467
+ };
19468
+ }
19469
+ function createInitialValuesFromEditablePackagingEntry(editablePackagingEntry, agentAdressId) {
19470
+ const pax = editablePackagingEntry?.pax ?? [];
19471
+ const rooms = editablePackagingEntry.rooms.map((room) => {
19472
+ const roomPax = pax.filter((x) => room.paxIds.includes(x.id));
19473
+ const adults = roomPax.filter((x) => x.age >= 18);
19474
+ const children = roomPax.filter((x) => x.age < 18);
19475
+ return {
19476
+ adults: adults.map((roomTraveler) => {
19477
+ return {
19478
+ id: roomTraveler.id,
19479
+ firstName: roomTraveler.firstName ?? '',
19480
+ lastName: roomTraveler.lastName ?? '',
19481
+ birthDate: roomTraveler.dateOfBirth ? dateFns.format(new Date(roomTraveler.dateOfBirth), 'yyyy-MM-dd') : '',
19482
+ gender: '',
19483
+ ...mapPackagingEntryPaxToTraveler(roomTraveler)
19484
+ };
19485
+ }),
19486
+ children: children.map((roomTraveler) => {
19487
+ return {
19488
+ id: roomTraveler.id,
19489
+ firstName: roomTraveler.firstName ?? '',
19490
+ lastName: roomTraveler.lastName ?? '',
19491
+ birthDate: roomTraveler.dateOfBirth ? dateFns.format(new Date(roomTraveler.dateOfBirth), 'yyyy-MM-dd') : '',
19492
+ gender: '',
19493
+ isBaby: isBabyAge(roomTraveler.age),
19494
+ ...mapPackagingEntryPaxToTraveler(roomTraveler)
19495
+ };
19496
+ })
19497
+ };
19498
+ });
19499
+ const values = createInitialValuesFromRooms(rooms.map((room) => ({ adults: room.adults, children: room.children })), lodash.first(editablePackagingEntry?.lines)?.from, lodash.last(editablePackagingEntry?.lines)?.to, agentAdressId);
19500
+ values.rooms = rooms;
19501
+ values.mainBookerId = pax.find((x) => x.isMainBooker)?.id ?? rooms[0]?.adults?.[0]?.id ?? -1;
19502
+ const address = editablePackagingEntry?.address;
19503
+ values.street = address?.street ?? '';
19504
+ values.houseNumber = address?.houseNumber ?? '';
19505
+ values.box = address?.box ?? '';
19506
+ values.zipCode = address?.zipCode ?? '';
19507
+ values.place = address?.place ?? '';
19508
+ values.country = address?.country ?? '';
19509
+ values.phone = address?.phone ?? '';
19510
+ values.email = address?.email ?? '';
19511
+ values.emailConfirmation = address?.email ?? '';
19512
+ values.travelAgentId = address?.travelAgentId ?? agentAdressId ?? 0;
19513
+ return values;
19514
+ }
19515
+ function applyTravelersFormValuesToEditablePackagingEntry(editablePackagingEntry, values) {
19516
+ const travelers = values.rooms.flatMap((room) => [...room.adults, ...room.children]);
19517
+ // The backend treats the main booker's per-pax address as authoritative, so keep the
19518
+ // booking-level address in sync with it rather than sending two independent values.
19519
+ const mainBookerAddress = travelers.find((x) => x.id === values.mainBookerId)?.address;
19520
+ return {
19521
+ ...editablePackagingEntry,
19522
+ pax: (editablePackagingEntry.pax ?? []).map((pax) => {
19523
+ const traveler = travelers.find((x) => x.id === pax.id);
19524
+ return traveler ? applyTravelerToPax(pax, traveler, values.mainBookerId) : pax;
19525
+ }),
19526
+ address: {
19527
+ ...editablePackagingEntry.address,
19528
+ street: mainBookerAddress?.street || values.street,
19529
+ houseNumber: mainBookerAddress?.number || values.houseNumber,
19530
+ box: values.box,
19531
+ zipCode: mainBookerAddress?.postalCode || values.zipCode,
19532
+ place: mainBookerAddress?.location || values.place,
19533
+ country: mainBookerAddress?.country || values.country,
19534
+ travelAgentId: values.travelAgentId,
19535
+ phone: values.phone,
19536
+ email: values.email
19537
+ }
19538
+ };
19539
+ }
19540
+ const SharedTravelersForm = ({ formik, translations, travellersSettings, countries, agents, bookingType, agentAdressId, travelersFirstStep = false, isUnavailable = false, useCompactForm = false, showAllCountries = false, paxFields, showAgentSelection = false, initialShowAgents = false, renderPreviousButton, onBookingTypeChange }) => {
19541
+ const [showAgents, setShowAgents] = React.useState(initialShowAgents);
19542
+ const typeaheadAgents = React.useMemo(() => lodash.sortBy(agents?.map((agent) => ({
19543
+ key: `${agent.id}`,
19544
+ value: `${agent.name}${agent.postalCode || agent.location ? ` (${lodash.compact([agent.postalCode, agent.location]).join(' ')})` : ''}`,
19545
+ text: `${agent.name}${agent.postalCode || agent.location ? ` (${lodash.compact([agent.postalCode, agent.location]).join(' ')})` : ''}`
19546
+ })), 'value') ?? [], [agents]);
19547
+ const [filteredAgents, setFilteredAgents] = React.useState(typeaheadAgents);
19548
+ const flatErrors = flat__default["default"](formik.errors);
19549
+ const errorKeys = Object.keys(flatErrors).filter((key) => lodash.get(formik.touched, key));
19550
+ const hasVisibleError = (key) => lodash.get(formik.errors, key) && lodash.get(formik.touched, key);
19551
+ const mainBooker = formik.values.rooms
19552
+ .find((room) => room.adults.find((traveler) => traveler.id === formik.values.mainBookerId))
19553
+ ?.adults.find((traveler) => traveler.id === formik.values.mainBookerId);
19554
+ const countryOptions = [
19555
+ { key: 'empty', value: undefined, label: translations.TRAVELERS_FORM.SELECT_COUNTRY },
19556
+ ...(showAllCountries
19557
+ ? countries?.map((country) => ({ key: country.iso2, value: country.iso2, label: country.name })) ?? []
19558
+ : travellersSettings?.countries?.map((country) => ({ key: country.iso2, value: country.iso2, label: country.name })) ?? [
19559
+ { key: 'be', value: 'be', label: translations.TRAVELERS_FORM.COUNTRIES.BELGIUM },
19560
+ { key: 'nl', value: 'nl', label: translations.TRAVELERS_FORM.COUNTRIES.NETHERLANDS },
19561
+ { key: 'fr', value: 'fr', label: translations.TRAVELERS_FORM.COUNTRIES.FRANCE }
19562
+ ])
19563
+ ];
19564
+ const handleMainBookerChange = (event) => {
19565
+ formik.setFieldValue('mainBookerId', parseInt(event.currentTarget.value, 10));
19566
+ };
19567
+ const handleAgentChange = (value) => {
19568
+ setFilteredAgents(typeaheadAgents.filter((x) => x.value.toLocaleLowerCase().indexOf(value.toLocaleLowerCase()) > -1));
19569
+ formik.setFieldValue('travelAgentName', value);
19570
+ };
19571
+ const handleAgentSelect = (key) => {
19572
+ const agent = typeaheadAgents.find((x) => x.key === key);
19573
+ formik.setValues({
19574
+ ...formik.values,
19575
+ travelAgentId: Number(agent?.key),
19576
+ travelAgentName: agent?.value ?? ''
19577
+ });
19578
+ onBookingTypeChange?.(agentAdressId && agentAdressId !== 0 ? 'b2b' : 'b2b2c');
19579
+ };
19580
+ const handleAgentClear = () => {
19581
+ formik.setValues({ ...formik.values, travelAgentId: 0, travelAgentName: '' });
19582
+ onBookingTypeChange?.('b2c');
19583
+ };
19584
+ const toggleAgent = (value) => {
19585
+ setShowAgents(value);
19586
+ if (!value) {
19587
+ handleAgentClear();
19588
+ setFilteredAgents([]);
19589
+ }
19590
+ };
19591
+ const handleAddTraveler = (roomIndex) => {
19592
+ const rooms = [...formik.values.rooms];
19593
+ const newAdult = {
19594
+ id: Date.now(),
19595
+ firstName: '',
19596
+ lastName: '',
19597
+ birthDate: '',
19598
+ gender: '',
19599
+ weight: null,
19600
+ weightType: build.WeightType.kilograms,
19601
+ height: null,
19602
+ heightType: build.HeightType.centimeters,
19603
+ ...EMPTY_BUILT_IN_FIELDS,
19604
+ customFields: buildEmptyCustomFields(paxFields)
19605
+ };
19606
+ rooms[roomIndex] = { ...rooms[roomIndex], adults: [...rooms[roomIndex].adults, newAdult] };
19607
+ formik.setFieldValue('rooms', rooms);
19608
+ };
19609
+ const handleRemoveTraveler = (roomIndex, travelerIndex) => {
19610
+ const rooms = [...formik.values.rooms];
19611
+ const adults = [...rooms[roomIndex].adults];
19612
+ if (adults.length <= 1)
19613
+ return;
19614
+ adults.splice(travelerIndex, 1);
19615
+ rooms[roomIndex] = { ...rooms[roomIndex], adults };
19616
+ formik.setFieldValue('rooms', rooms);
19617
+ };
19618
+ const handleAddRoom = () => {
19619
+ const rooms = [...formik.values.rooms];
19620
+ rooms.push({
19621
+ adults: [
19622
+ {
19623
+ id: Date.now(),
19624
+ firstName: '',
19625
+ lastName: '',
19626
+ birthDate: '',
19627
+ gender: '',
19628
+ weight: null,
19629
+ weightType: build.WeightType.kilograms,
19630
+ height: null,
19631
+ heightType: build.HeightType.centimeters,
19632
+ ...EMPTY_BUILT_IN_FIELDS,
19633
+ customFields: buildEmptyCustomFields(paxFields)
19634
+ }
19635
+ ],
19636
+ children: []
19637
+ });
19638
+ formik.setFieldValue('rooms', rooms);
19639
+ };
19640
+ const handleRemoveRoom = (roomIndex) => {
19641
+ const rooms = [...formik.values.rooms];
19642
+ rooms.splice(roomIndex, 1);
19643
+ formik.setFieldValue('rooms', rooms);
19644
+ };
19645
+ const renderWeightControl = (namePrefix, value, isRequired) => (React__default["default"].createElement("div", { className: "form__row" },
19646
+ React__default["default"].createElement(LabeledInput, { type: "number", min: 0, hasError: hasVisibleError(`${namePrefix}.weight`), extraClassName: "form__group--md-33", label: translations.TRAVELERS_FORM.WEIGHT, required: isRequired, name: `${namePrefix}.weight`, onChange: formik.handleChange, onBlur: formik.handleBlur, value: value.weight ?? '' }),
19647
+ React__default["default"].createElement(LabeledSelect, { extraClassName: "form__group--md-33", name: `${namePrefix}.weightType`, onChange: formik.handleChange, onBlur: formik.handleBlur, value: value.weightType, options: [
19648
+ { key: 'kg', value: build.WeightType.kilograms, label: translations.TRAVELERS_FORM.WEIGHT_UNIT_KG },
19649
+ { key: 'lb', value: build.WeightType.pounds, label: translations.TRAVELERS_FORM.WEIGHT_UNIT_LB }
19650
+ ] })));
19651
+ const renderHeightControl = (namePrefix, value, isRequired) => (React__default["default"].createElement("div", { className: "form__row" },
19652
+ React__default["default"].createElement(LabeledInput, { type: "number", min: 0, hasError: hasVisibleError(`${namePrefix}.height`), extraClassName: "form__group--md-33", label: translations.TRAVELERS_FORM.HEIGHT, required: isRequired, name: `${namePrefix}.height`, onChange: formik.handleChange, onBlur: formik.handleBlur, value: value.height ?? '' }),
19653
+ React__default["default"].createElement(LabeledSelect, { extraClassName: "form__group--md-33", name: `${namePrefix}.heightType`, onChange: formik.handleChange, onBlur: formik.handleBlur, value: value.heightType, options: [
19654
+ { key: 'cm', value: build.HeightType.centimeters, label: translations.TRAVELERS_FORM.HEIGHT_UNIT_CM },
19655
+ { key: 'ftIn', value: build.HeightType.feetInches, label: translations.TRAVELERS_FORM.HEIGHT_UNIT_FT_IN }
19656
+ ] })));
19657
+ const getCustomFieldControl = (field, value, name) => {
19658
+ const label = lodash.compact([field.fieldName, field.isRequired && '*']).join(' ');
19659
+ switch (field.dataType) {
19660
+ case build.CustomFieldDataType.bool:
19661
+ return (React__default["default"].createElement("div", { className: buildClassName(['form__group', hasVisibleError(name) && 'form__group--error']) },
19662
+ React__default["default"].createElement("div", { className: "checkbox" },
19663
+ React__default["default"].createElement("label", { className: "checkbox__label" },
19664
+ React__default["default"].createElement("input", { type: "checkbox", className: "checkbox__input", checked: value === 'true', onChange: (e) => formik.setFieldValue(name, e.target.checked ? 'true' : 'false'), onBlur: formik.handleBlur, name: name }),
19665
+ label))));
19666
+ case build.CustomFieldDataType.date:
19667
+ return (React__default["default"].createElement(LabeledInput, { type: "date", hasError: hasVisibleError(name), label: label, name: name, onChange: (e) => formik.setFieldValue(name, e.target.value), onBlur: formik.handleBlur, value: value }));
19668
+ case build.CustomFieldDataType.time:
19669
+ return (React__default["default"].createElement(LabeledInput, { type: "time", hasError: hasVisibleError(name), label: label, name: name, onChange: (e) => formik.setFieldValue(name, e.target.value), onBlur: formik.handleBlur, value: value }));
19670
+ case build.CustomFieldDataType.numeric:
19671
+ return (React__default["default"].createElement(LabeledInput, { type: "number", hasError: hasVisibleError(name), label: label, name: name, onChange: (e) => formik.setFieldValue(name, e.target.value), onBlur: formik.handleBlur, value: value }));
19672
+ case build.CustomFieldDataType.string:
19673
+ default:
19674
+ return (React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError(name), label: label, name: name, onChange: (e) => formik.setFieldValue(name, e.target.value), onBlur: formik.handleBlur, value: value }));
19675
+ }
19676
+ };
19677
+ const renderCustomFields = (travelerValues, namePrefix) => {
19678
+ if (!paxFields?.length)
19679
+ return null;
19680
+ const isMainBooker = travelerValues.id === formik.values.mainBookerId;
19681
+ const applicableFields = getApplicablePaxCustomFields(paxFields, isMainBooker);
19682
+ if (!applicableFields.length)
19683
+ return null;
19684
+ return (React__default["default"].createElement("div", { className: "travelers-form__custom-fields-grid" }, applicableFields.map((field) => {
19685
+ const fieldIndex = travelerValues.customFields?.findIndex((c) => c.customFieldConfigurationId === field.customFieldConfigurationId) ?? -1;
19686
+ if (fieldIndex < 0)
19687
+ return null;
19688
+ const name = `${namePrefix}.customFields[${fieldIndex}].value`;
19689
+ const value = travelerValues.customFields[fieldIndex].value;
19690
+ return (React__default["default"].createElement("div", { key: field.customFieldConfigurationId, style: { gridColumn: `span ${field.col || 12}` } }, getCustomFieldControl(field, value, name)));
19691
+ })));
19692
+ };
19693
+ // Fields with a dedicated control (gender, hasInsurance, countryId) aren't listed here.
19694
+ const BUILT_IN_FIELD_CONTROL_META = {
19695
+ firstName: { label: translations.TRAVELERS_FORM.FIRST_NAME },
19696
+ lastName: { label: translations.TRAVELERS_FORM.LAST_NAME },
19697
+ dateOfBirth: { type: 'date', label: translations.TRAVELERS_FORM.BIRTHDATE },
19698
+ nickname: { label: translations.TRAVELERS_FORM.NICKNAME },
19699
+ birthplace: { label: translations.TRAVELERS_FORM.BIRTHPLACE },
19700
+ nationality: { label: translations.TRAVELERS_FORM.NATIONALITY },
19701
+ preferredLanguageId: { type: 'number', label: translations.TRAVELERS_FORM.PREFERRED_LANGUAGE },
19702
+ email: { type: 'email', label: translations.TRAVELERS_FORM.TRAVELER_EMAIL },
19703
+ phone: { label: translations.TRAVELERS_FORM.TRAVELER_PHONE },
19704
+ mobile: { label: translations.TRAVELERS_FORM.TRAVELER_MOBILE },
19705
+ passportNumber: { label: translations.TRAVELERS_FORM.PASSPORT_NUMBER },
19706
+ passportIssueDate: { type: 'date', label: translations.TRAVELERS_FORM.PASSPORT_ISSUE_DATE },
19707
+ passportIssuedLocation: { label: translations.TRAVELERS_FORM.PASSPORT_ISSUED_LOCATION },
19708
+ passportExpiryDate: { type: 'date', label: translations.TRAVELERS_FORM.PASSPORT_EXPIRY_DATE },
19709
+ emergencyNumber: { label: translations.TRAVELERS_FORM.EMERGENCY_NUMBER },
19710
+ insuranceName: { label: translations.TRAVELERS_FORM.INSURANCE_NAME },
19711
+ info: { label: translations.TRAVELERS_FORM.INFO },
19712
+ street: { label: translations.TRAVELERS_FORM.STREET },
19713
+ location: { label: translations.TRAVELERS_FORM.CITY },
19714
+ postalCode: { label: translations.TRAVELERS_FORM.ZIPCODE },
19715
+ number: { label: translations.TRAVELERS_FORM.HOUSE_NUMBER }
19716
+ };
19717
+ const renderBuiltInFieldControl = (fieldName, isRequired, travelerValues, namePrefix) => {
19718
+ const path = BUILT_IN_FIELD_PATHS[fieldName] ?? fieldName;
19719
+ const name = `${namePrefix}.${path}`;
19720
+ const value = lodash.get(travelerValues, path);
19721
+ switch (fieldName) {
19722
+ case 'gender':
19723
+ return React__default["default"].createElement(GenderControl, { translations: translations, value: travelerValues, formik: formik, name: name });
19724
+ case 'hasInsurance':
19725
+ return (React__default["default"].createElement("div", { className: buildClassName(['form__group', hasVisibleError(name) && 'form__group--error']) },
19726
+ React__default["default"].createElement("div", { className: "checkbox" },
19727
+ React__default["default"].createElement("label", { className: "checkbox__label" },
19728
+ React__default["default"].createElement("input", { type: "checkbox", className: "checkbox__input", checked: !!value, onChange: (e) => formik.setFieldValue(name, e.target.checked), onBlur: formik.handleBlur, name: name }),
19729
+ lodash.compact([translations.TRAVELERS_FORM.HAS_INSURANCE, isRequired && '*']).join(' ')))));
19730
+ case 'countryId':
19731
+ return (React__default["default"].createElement(LabeledSelect, { hasError: hasVisibleError(name), label: translations.TRAVELERS_FORM.COUNTRY, required: isRequired, name: name, onChange: formik.handleChange, onBlur: formik.handleBlur, value: value, options: countryOptions }));
19732
+ case 'weight':
19733
+ return renderWeightControl(namePrefix, travelerValues, isRequired);
19734
+ case 'height':
19735
+ return renderHeightControl(namePrefix, travelerValues, isRequired);
19736
+ default: {
19737
+ const meta = BUILT_IN_FIELD_CONTROL_META[fieldName] ?? { label: fieldName };
19738
+ return (React__default["default"].createElement(LabeledInput, { type: meta.type, hasError: hasVisibleError(name), label: meta.label, required: isRequired, name: name, onChange: formik.handleChange, onBlur: formik.handleBlur, value: value ?? '' }));
19739
+ }
19740
+ }
19741
+ };
19742
+ const CORE_FIELD_DEFAULTS = {
19743
+ gender: { isRequired: true, col: 4 },
19744
+ firstName: { isRequired: true, col: 4 },
19745
+ lastName: { isRequired: true, col: 4 },
19746
+ dateOfBirth: { isRequired: true, col: 4 }
19747
+ };
19748
+ const resolveBuiltInField = (fieldName, travelerValues) => {
19749
+ const isMainBooker = travelerValues.id === formik.values.mainBookerId;
19750
+ const configured = getBuiltInPaxField(paxFields, fieldName, isMainBooker);
19751
+ const fallback = CORE_FIELD_DEFAULTS[fieldName];
19752
+ if (!configured && !fallback)
19753
+ return undefined;
19754
+ return { isRequired: configured?.isRequired ?? fallback.isRequired, col: configured?.col ?? fallback.col };
19755
+ };
19756
+ const renderBuiltInField = (fieldName, travelerValues, namePrefix) => {
19757
+ const resolved = resolveBuiltInField(fieldName, travelerValues);
19758
+ if (!resolved)
19759
+ return null;
19760
+ return (React__default["default"].createElement("div", { key: fieldName, style: { gridColumn: `span ${resolved.col || 12}` } }, renderBuiltInFieldControl(fieldName, resolved.isRequired, travelerValues, namePrefix)));
19761
+ };
19762
+ const renderTravelerAddressFields = (travelerValues, namePrefix) => {
19763
+ const fields = ADDRESS_FIELD_NAMES.map((fieldName) => renderBuiltInField(fieldName, travelerValues, namePrefix)).filter(Boolean);
19764
+ if (!fields.length)
19765
+ return null;
19766
+ return React__default["default"].createElement("div", { className: "travelers-form__custom-fields-grid" }, fields);
19767
+ };
19768
+ const getControl = (type, value, name) => {
19769
+ switch (type) {
19770
+ case 'country':
19771
+ return (React__default["default"].createElement(LabeledSelect, { hasError: hasVisibleError('country'), label: translations.TRAVELERS_FORM.COUNTRY, required: true, name: "country", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.country, options: countryOptions }));
19772
+ case 'phone':
19773
+ return (React__default["default"].createElement(PhoneInput, { countries: travellersSettings?.countries ?? [], countryIso2: formik.values.country, hasError: hasVisibleError('phone'), label: translations.TRAVELERS_FORM.PHONE, required: true, name: "phone", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.phone }));
19774
+ case 'email':
19775
+ return (React__default["default"].createElement(React__default["default"].Fragment, null,
19776
+ React__default["default"].createElement(LabeledInput, { type: "email", hasError: hasVisibleError('email'), extraClassName: "form__group--md-33", label: translations.TRAVELERS_FORM.EMAIL, required: true, name: "email", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.email }),
19777
+ React__default["default"].createElement(LabeledInput, { type: "email", hasError: hasVisibleError('emailConfirmation'), extraClassName: "form__group--md-33", label: translations.TRAVELERS_FORM.REPEAT_EMAIL, required: true, name: "emailConfirmation", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.emailConfirmation })));
19778
+ case 'street':
19779
+ return (React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError('street'), extraClassName: "form__group--50 form__group--sm-60", label: translations.TRAVELERS_FORM.STREET, required: true, name: "street", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.street }));
19780
+ case 'houseNumber':
19781
+ return (React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError('houseNumber'), extraClassName: "form__group--30 form__group--sm-20", label: translations.TRAVELERS_FORM.HOUSE_NUMBER, required: true, name: "houseNumber", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.houseNumber }));
19782
+ case 'box':
19783
+ return (React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError('box'), extraClassName: "form__group--20", label: translations.TRAVELERS_FORM.POST_BOX, name: "box", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.box }));
19784
+ case 'zipCode':
19785
+ return (React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError('zipCode'), extraClassName: "form__group--40 form__group--sm-20", label: translations.TRAVELERS_FORM.ZIPCODE, required: true, name: "zipCode", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.zipCode }));
19786
+ case 'place':
19787
+ return (React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError('place'), extraClassName: "form__group--60 form__group--sm-40", label: translations.TRAVELERS_FORM.CITY, required: true, name: "place", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.place }));
19788
+ default:
19789
+ return null;
19790
+ }
19791
+ };
19792
+ const renderRoomLabel = (room) => lodash.compact([
19793
+ room.adults.length,
19794
+ room.adults.length === 1 && ` ${translations.TRAVELERS_FORM.ADULT}`,
19795
+ room.adults.length > 1 && ` ${translations.TRAVELERS_FORM.ADULTS}`,
19796
+ room.adults?.length && room.children?.length && ', ',
19797
+ room.children.length,
19798
+ room.children.length === 1 && ` ${translations.TRAVELERS_FORM.CHILD}`,
19799
+ room.children.length > 1 && ` ${translations.TRAVELERS_FORM.CHILDREN}`
19800
+ ]).join('');
19801
+ const renderTravelerFields = (travelerValues, namePrefix, isAdult, roomIndex, travelerIndex) => {
19802
+ if (useCompactForm) {
19803
+ return (React__default["default"].createElement("div", { className: "form__row" },
19804
+ React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError(`${namePrefix}.age`), extraClassName: "form__group--md-33", label: translations.TRAVELERS_FORM.AGE, required: true, name: `${namePrefix}.age`, onChange: formik.handleChange, onBlur: formik.handleBlur, value: travelerValues.age })));
19805
+ }
19806
+ // 'birthDate' (the legacy formFields type string) is normalized to the paxFields catalog's
19807
+ // 'dateOfBirth' fieldName for config lookups.
19808
+ const baseFieldNames = (travellersSettings?.formFields?.length ? travellersSettings.formFields.map((field) => field.type) : ['gender', 'firstName', 'lastName', 'dateOfBirth']).map((fieldName) => (fieldName === 'birthDate' ? 'dateOfBirth' : fieldName));
19809
+ const isMainBooker = travelerValues.id === formik.values.mainBookerId;
19810
+ const extraFieldNames = getApplicableBuiltInPaxFields(paxFields, isMainBooker)
19811
+ .map((field) => field.fieldName)
19812
+ .filter((fieldName) => !baseFieldNames.includes(fieldName) && !ADDRESS_FIELD_NAMES.includes(fieldName));
19813
+ return (React__default["default"].createElement(React__default["default"].Fragment, null,
19814
+ React__default["default"].createElement("div", { className: "travelers-form__custom-fields-grid" }, [...baseFieldNames, ...extraFieldNames].map((fieldName) => renderBuiltInField(fieldName, travelerValues, namePrefix))),
19815
+ renderTravelerAddressFields(travelerValues, namePrefix),
19816
+ renderCustomFields(travelerValues, namePrefix),
19817
+ travelersFirstStep && isAdult && formik.values.rooms[roomIndex].adults.length > 1 && (React__default["default"].createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => handleRemoveTraveler(roomIndex, travelerIndex) }, translations.TRAVELERS_FORM.REMOVE_TRAVELER))));
19818
+ };
19819
+ return (React__default["default"].createElement("form", { className: "form form__travelers", name: "booking--travellers", id: "booking--travellers", noValidate: true, onSubmit: formik.handleSubmit, onReset: formik.handleReset },
19820
+ React__default["default"].createElement("div", { className: "form__travelers__wrapper" },
19821
+ formik.values.rooms.map((room, roomIndex) => (React__default["default"].createElement("div", { key: roomIndex },
19822
+ formik.values.rooms.length > 1 && (React__default["default"].createElement("div", { className: "form__region" },
19823
+ React__default["default"].createElement("div", { className: "form__region-header" },
19824
+ React__default["default"].createElement("h5", { className: "form__region-heading" },
19825
+ translations.SHARED.ROOM,
19826
+ " ",
19827
+ roomIndex + 1),
19828
+ React__default["default"].createElement("p", { className: "form__region-label" }, renderRoomLabel(room))),
19829
+ !useCompactForm && travelersFirstStep && formik.values.rooms.length > 1 && (React__default["default"].createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => handleRemoveRoom(roomIndex) }, "Verwijder reisgezelschap")))),
19830
+ room.adults.map((travelerValues, index) => (React__default["default"].createElement("div", { className: "form__region", key: travelerValues.id },
19831
+ React__default["default"].createElement("div", { className: "form__region-header" },
19832
+ React__default["default"].createElement("h5", { className: "form__region-heading" },
19833
+ translations.TRAVELERS_FORM.TRAVELER,
19834
+ " ",
19835
+ index + 1),
19836
+ React__default["default"].createElement("p", { className: "form__region-label" }, translations.TRAVELERS_FORM.ADULT),
19837
+ React__default["default"].createElement("div", { className: "radiobutton" },
19838
+ React__default["default"].createElement("label", { className: "radiobutton__label" },
19839
+ React__default["default"].createElement("input", { type: "radio", name: "mainBookerId", onChange: handleMainBookerChange, onBlur: formik.handleBlur, value: travelerValues.id, checked: formik.values.mainBookerId === travelerValues.id, className: "radiobutton__input" }),
19840
+ translations.TRAVELERS_FORM.MAIN_BOOKER))),
19841
+ renderTravelerFields(travelerValues, `rooms[${roomIndex}].adults[${index}]`, true, roomIndex, index)))),
19842
+ room.children.map((travelerValues, index) => (React__default["default"].createElement("div", { className: "form__region", key: travelerValues.id },
19843
+ React__default["default"].createElement("div", { className: "form__region-header" },
19844
+ React__default["default"].createElement("h5", { className: "form__region-heading" },
19845
+ translations.TRAVELERS_FORM.TRAVELER,
19846
+ " ",
19847
+ room.adults.length + index + 1),
19848
+ React__default["default"].createElement("p", { className: "form__region-label" }, travelerValues.isBaby ? translations.TRAVELERS_FORM.BABY : translations.TRAVELERS_FORM.CHILD)),
19849
+ renderTravelerFields(travelerValues, `rooms[${roomIndex}].children[${index}]`, false, roomIndex, index)))),
19850
+ !useCompactForm && travelersFirstStep && (React__default["default"].createElement("div", { className: "form__region" },
19851
+ React__default["default"].createElement("button", { type: "button", className: "cta cta--select", onClick: () => handleAddTraveler(roomIndex) }, translations.TRAVELERS_FORM.ADD_TRAVELER)))))),
19852
+ !useCompactForm && (bookingType !== 'b2b' || travellersSettings?.mainBookerFormFields?.length) ? (React__default["default"].createElement("div", { className: "form__region" },
19853
+ React__default["default"].createElement("div", { className: "form__region-header" },
19854
+ React__default["default"].createElement("h5", { className: "form__region-heading" }, translations.TRAVELERS_FORM.MAIN_BOOKER),
19855
+ React__default["default"].createElement("p", { className: "form__region-label" }, lodash.compact([lodash.compact([mainBooker?.firstName, mainBooker?.lastName]).join(' '), formatBirthDate$1(mainBooker?.birthDate)]).join(', '))),
19856
+ travellersSettings?.mainBookerFormFields?.length ? (React__default["default"].createElement("div", { className: "main-booker-form__grid" }, travellersSettings.mainBookerFormFields.map((field, index) => (React__default["default"].createElement("div", { key: index, className: `control control--${field.type}` }, getControl(field.type, {}, field.type)))))) : (React__default["default"].createElement(React__default["default"].Fragment, null,
19857
+ React__default["default"].createElement("div", { className: "form__twocolumn" },
19858
+ React__default["default"].createElement("div", { className: "form__twocolumn-column" },
19859
+ React__default["default"].createElement("div", { className: "form__row" },
19860
+ React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError('street'), extraClassName: "form__group--50 form__group--sm-60", label: translations.TRAVELERS_FORM.STREET, required: true, name: "street", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.street }),
19861
+ React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError('houseNumber'), extraClassName: "form__group--30 form__group--sm-20", label: translations.TRAVELERS_FORM.HOUSE_NUMBER, required: true, name: "houseNumber", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.houseNumber }),
19862
+ React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError('box'), extraClassName: "form__group--20", label: translations.TRAVELERS_FORM.POST_BOX, name: "box", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.box }))),
19863
+ React__default["default"].createElement("div", { className: "form__twocolumn-column" },
19864
+ React__default["default"].createElement("div", { className: "form__row" },
19865
+ React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError('zipCode'), extraClassName: "form__group--40 form__group--sm-20", label: translations.TRAVELERS_FORM.ZIPCODE, required: true, name: "zipCode", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.zipCode }),
19866
+ React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError('place'), extraClassName: "form__group--60 form__group--sm-40", label: translations.TRAVELERS_FORM.CITY, required: true, name: "place", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.place }),
19867
+ React__default["default"].createElement(LabeledSelect, { hasError: hasVisibleError('country'), extraClassName: "form__group--sm-40", label: translations.TRAVELERS_FORM.COUNTRY, required: true, name: "country", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.country, options: countryOptions })))),
19868
+ React__default["default"].createElement("div", { className: "form__row" },
19869
+ React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError('phone'), extraClassName: "form__group--md-33", label: translations.TRAVELERS_FORM.PHONE, required: true, name: "phone", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.phone }),
19870
+ React__default["default"].createElement(LabeledInput, { type: "email", hasError: hasVisibleError('email'), extraClassName: "form__group--md-33", label: translations.TRAVELERS_FORM.EMAIL, required: true, name: "email", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.email }),
19871
+ React__default["default"].createElement(LabeledInput, { type: "email", hasError: hasVisibleError('emailConfirmation'), extraClassName: "form__group--md-33", label: translations.TRAVELERS_FORM.REPEAT_EMAIL, required: true, name: "emailConfirmation", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.emailConfirmation })))))) : !useCompactForm ? (React__default["default"].createElement("div", { className: "form__region" },
19872
+ React__default["default"].createElement("div", { className: "form__row" },
19873
+ React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError('phone'), extraClassName: "form__group--md-33", label: translations.TRAVELERS_FORM.PHONE, required: true, name: "phone", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.phone })))) : null,
19874
+ !useCompactForm && showAgentSelection && (React__default["default"].createElement("div", { className: "form__region" },
19875
+ React__default["default"].createElement("div", { className: "form__region-header" },
19876
+ React__default["default"].createElement("h5", { className: "form__region-heading" }, translations.TRAVELERS_FORM.BOOK_WITH_AGENT),
19877
+ React__default["default"].createElement("div", { className: "checkbox", id: "cbxChooseOffice" },
19878
+ React__default["default"].createElement("label", { className: "checkbox__label" },
19879
+ React__default["default"].createElement("input", { type: "checkbox", name: "booking--mainbooker", checked: showAgents, onChange: () => toggleAgent(!showAgents), className: "checkbox__input" }),
19880
+ translations.TRAVELERS_FORM.CHOOSE_OFFICE))),
19881
+ showAgents && (React__default["default"].createElement("div", { className: "form__row form__row--choose-office" },
19882
+ React__default["default"].createElement("div", { className: buildClassName(['form__group', 'form__group--icon', hasVisibleError('travelAgentId') && 'form__group--error']) },
19883
+ React__default["default"].createElement(TypeAheadInput, { value: formik.values.travelAgentName, options: filteredAgents, onChange: handleAgentChange, onSelect: handleAgentSelect, onClear: handleAgentClear, name: "travelAgentName", placeholder: translations.TRAVELERS_FORM.CHOOSE_AGENT_PLACEHOLDER }))))))),
19884
+ !useCompactForm && errorKeys.length > 0 && (React__default["default"].createElement("div", { className: "form__region form__region--errors" },
19885
+ React__default["default"].createElement("div", { className: "form__row" },
19886
+ React__default["default"].createElement("div", { className: "form__group" },
19887
+ React__default["default"].createElement("p", { className: "form__error-heading" },
19888
+ translations.TRAVELERS_FORM.VALIDATION_MESSAGE,
19889
+ ":"),
19890
+ React__default["default"].createElement("ul", { className: "list" }, errorKeys.map((key) => (React__default["default"].createElement("li", { key: key }, lodash.get(flatErrors, key))))))))),
19891
+ travelersFirstStep && (React__default["default"].createElement("div", { className: "booking__navigator" },
19892
+ React__default["default"].createElement("button", { type: "button", className: "cta cta--select", onClick: handleAddRoom }, translations.TRAVELERS_FORM.ADD_ROOM))),
19893
+ React__default["default"].createElement("div", { className: "booking__navigator" },
19894
+ renderPreviousButton?.(),
19895
+ React__default["default"].createElement("button", { type: "submit", title: translations.STEPS.NEXT, className: 'cta' + (isUnavailable ? ' cta--disabled' : '') }, translations.STEPS.NEXT))));
19896
+ };
19897
+
19898
+ const fetchPriceDetails$1 = async (apiSettings, request, signal, languageCode) => {
19899
+ const tideClientConfig = buildTideClientConfig(apiSettings);
19900
+ return await build.priceDetails(tideClientConfig, request, signal, languageCode);
19901
+ };
19902
+ const priceDetailsApi = {
19903
+ fetchPriceDetails: fetchPriceDetails$1
19904
+ };
19905
+
19906
+ const updatePackageDetails = (packageDetails, changedLines) => {
19907
+ return {
19908
+ ...packageDetails,
19909
+ options: packageDetails.options.map((opt) => {
19910
+ return {
19911
+ ...opt,
19912
+ rooms: opt.isSelected ? updateRooms(opt.rooms, changedLines) : opt.rooms,
19913
+ groups: opt.isSelected ? updateBookingGroups(opt.groups, changedLines) : opt.groups,
19914
+ optionUnits: opt.isSelected ? updateOptionUnits(opt.optionUnits, changedLines) : opt.optionUnits,
19915
+ optionPax: opt.isSelected ? updateOptionPax(opt.optionPax, changedLines) : opt.optionPax
19916
+ };
19917
+ })
19918
+ };
19919
+ };
19920
+ // ROOMS
19921
+ const updateRooms = (rooms, changedLines) => {
19922
+ return rooms.map((room) => ({
19923
+ ...room,
19924
+ options: room.options.map((option) => ({
19925
+ ...option,
19926
+ price: changedLines.find((x) => x.entryLineGuid == option.entryLineGuid)?.price ?? option.price
19927
+ }))
19928
+ }));
19929
+ };
19930
+ // PAX
19931
+ const updateOptionPax = (pax, changedLines) => {
19932
+ return pax.map((p) => ({
19933
+ ...p,
19934
+ groups: updatePaxGroups(p.groups, changedLines)
19935
+ }));
19936
+ };
19937
+ const updatePaxGroups = (groups, changedLines) => {
19938
+ return groups.map((g) => ({
19939
+ ...g,
19940
+ options: g.options.map((o) => ({
19941
+ ...o,
19942
+ groups: updatePaxGroups(o.groups, changedLines),
19943
+ line: {
19944
+ ...o.line,
19945
+ price: changedLines.find((cl) => cl.entryLineGuid === o.line.entryLineGuid)?.price ?? o.line.price
19946
+ },
19947
+ alternatives: o.alternatives.map((a) => ({
19948
+ ...a,
19949
+ price: changedLines.find((cl) => cl.entryLineGuid === a.entryLineGuid)?.price ?? a.price
19950
+ }))
19951
+ }))
19952
+ }));
19953
+ };
19954
+ // UNITS
19955
+ const updateOptionUnits = (units, changedLines) => {
19956
+ return units.map((u) => ({
19957
+ ...u,
19958
+ groups: updateUnitGroups(u.groups, changedLines)
19959
+ }));
19960
+ };
19961
+ const updateUnitGroups = (groups, changedLines) => {
19962
+ return groups.map((g) => ({
19963
+ ...g,
19964
+ options: g.options.map((o) => ({
19965
+ ...o,
19966
+ groups: updateUnitGroups(o.groups, changedLines),
19967
+ pax: updateOptionPax(o.pax, changedLines),
19968
+ line: {
19969
+ ...o.line,
19970
+ price: changedLines.find((cl) => cl.entryLineGuid === o.line.entryLineGuid)?.price ?? o.line.price
19971
+ },
19972
+ alternatives: o.alternatives.map((a) => ({
19973
+ ...a,
19974
+ price: changedLines.find((cl) => cl.entryLineGuid === a.entryLineGuid)?.price ?? a.price
19975
+ }))
19976
+ }))
19977
+ }));
19978
+ };
19979
+ // BOOKING
19980
+ const updateBookingGroups = (groups, changedLines) => {
19981
+ return groups.map((g) => ({
19982
+ ...g,
19983
+ options: g.options.map((o) => ({
19984
+ ...o,
19985
+ groups: updateBookingGroups(o.groups, changedLines),
19986
+ units: updateOptionUnits(o.units, changedLines),
19987
+ pax: updateOptionPax(o.pax, changedLines),
19988
+ line: {
19989
+ ...o.line,
19990
+ price: changedLines.find((cl) => cl.entryLineGuid === o.line.entryLineGuid)?.price ?? o.line.price
19991
+ },
19992
+ alternatives: o.alternatives.map((a) => ({
19993
+ ...a,
19994
+ price: changedLines.find((cl) => cl.entryLineGuid === a.entryLineGuid)?.price ?? a.price
19995
+ }))
19996
+ }))
19997
+ }));
19998
+ };
19999
+
20000
+ const fetchPriceDetails = toolkit.createAsyncThunk('priceDetails/fetchPriceDetails', async (_, { dispatch, getState, signal }) => {
20001
+ const state = getState();
20002
+ const languageCode = selectLanguageCode(state);
20003
+ const request = selectBookingPackageBookRequest(state);
20004
+ if (!request) {
20005
+ throw Error('Product not available.');
20006
+ }
20007
+ dispatch(setFetchingPriceDetails(true));
20008
+ dispatch(resetPriceDetails());
20009
+ const priceDetails = await priceDetailsApi.fetchPriceDetails(state.apiSettings, request, signal, languageCode);
20010
+ dispatch(setFetchingPriceDetails(false));
20011
+ const packageDetails = selectPackageDetails(state);
20012
+ if (packageDetails) {
20013
+ const updatedPackageDetails = updatePackageDetails(packageDetails, priceDetails.changedLines);
20014
+ dispatch(setPackage(updatedPackageDetails));
20015
+ }
20016
+ return priceDetails;
20017
+ });
20018
+ const priceDetailsSlice = toolkit.createSlice({
20019
+ name: 'priceDetails',
20020
+ initialState: {
20021
+ priceDetails: [],
20022
+ notifications: [],
20023
+ isBusy: false
20024
+ },
20025
+ reducers: {
20026
+ setFetchingPriceDetails(state, action) {
20027
+ state.isBusy = action.payload;
20028
+ },
20029
+ setNotifications(state, action) {
20030
+ state.notifications = action.payload;
20031
+ },
20032
+ resetPriceDetails(state, _) {
20033
+ state.priceDetails = [];
20034
+ }
20035
+ },
20036
+ extraReducers: (builder) => {
20037
+ builder.addCase(fetchPriceDetails.fulfilled, (state, action) => {
20038
+ if (action.payload) {
20039
+ state.deposit = action.payload.deposit;
20040
+ state.commission = action.payload.commission;
20041
+ state.priceDetails = action.payload.details;
20042
+ state.notifications = action.payload.notifications;
20043
+ }
20044
+ });
20045
+ builder.addCase(fetchPriceDetails.rejected, (state) => {
20046
+ state.isBusy = false;
20047
+ });
20048
+ }
20049
+ });
20050
+ const { setFetchingPriceDetails } = priceDetailsSlice.actions;
20051
+ const { resetPriceDetails, setNotifications } = priceDetailsSlice.actions;
20052
+ const selectNotifications = (state) => state.priceDetails.notifications;
20053
+ var priceDetailsReducer = priceDetailsSlice.reducer;
20054
+
20055
+ const selectHasMounted = (state) => state.booking.hasMounted;
20056
+ const selectGeneratePaymentUrl = (state) => state.booking.generatePaymentUrl;
20057
+ const selectSkipPaymentWithAgent = (state) => state.booking.skipPaymentWithAgent;
20058
+ const selectIsFetchingProductOptions = (state) => state.booking.isBusy;
20059
+ const selectDepartureFlight = (state) => state.booking.package?.outwardFlights?.find((x) => x.isSelected);
20060
+ const selectReturnFlight = (state) => state.booking.package?.returnFlights?.find((x) => x.isSelected);
20061
+ const selectPackageRooms = (state) => state.booking.package?.options.find((x) => x.isSelected)?.rooms;
20062
+ const selectIncludedServiceTypes = (state) => selectActiveOption(state)?.includedServiceTypes;
20063
+ const selectAvailabilities = (state) => state.booking.package?.options.find((x) => x.isSelected)?.availabilities;
20064
+ const selectPackageTags = (state) => state.booking.package?.tags;
20065
+ const selectIsOnRequest = (state) => state.booking.package?.options.find((x) => x.isSelected)?.isOnRequest;
20066
+ const selectPackageOptionUnits = (state) => state.booking.package?.options.find((x) => x.isSelected)?.optionUnits;
20067
+ const selectPackageOptionPax = (state) => state.booking.package?.options.find((x) => x.isSelected)?.optionPax;
20068
+ const selectPackageGroups = (state) => state.booking.package?.options.find((x) => x.isSelected)?.groups;
20069
+ const selectPackageDetails = (state) => state.booking.package;
20070
+ const selectPaxFields = (state) => state.booking.package?.paxFields ?? [];
20071
+ const selectPackageFlights = (state) => ({
20072
+ outward: state.booking.package?.outwardFlights,
20073
+ return: state.booking.package?.returnFlights
20074
+ });
20075
+ const selectFlightMetaData = (state) => state.booking.package?.flightInfo.metaData;
20076
+ const selectActiveOption = (state) => state.booking.package?.options.find((x) => x.isSelected);
20077
+ const selectPackageAirlineGroups = toolkit.createSelector(selectActiveOption, selectDepartureFlight, selectReturnFlight, (option, departureFlight, returnFlight) => option?.airlineGroups.filter((x) => x.flightIds.includes(departureFlight?.entryLineGuid ?? '') || x.flightIds.includes(returnFlight?.entryLineGuid ?? '')));
20078
+ const selectPackageAirportGroups = toolkit.createSelector(selectActiveOption, selectDepartureFlight, selectReturnFlight, (option, departureFlight, returnFlight) => option?.airportGroups.filter((x) => x.flightIds.includes(departureFlight?.entryLineGuid ?? '') || x.flightIds.includes(returnFlight?.entryLineGuid ?? '')));
20079
+ const selectRoomOptionDepartureFlightsMetaData = toolkit.createSelector(selectActiveOption, selectFlightMetaData, (ativeOption, flightMetaData) => {
20080
+ const roomOptions = ativeOption?.rooms.map((r) => r.options.filter((o) => o.isSelected).shift());
20081
+ return roomOptions?.map((ro) => flightMetaData?.find((f) => f.entryLineGuid === ro?.entryLineGuid)?.flightMetaData).filter((f) => f !== undefined) ?? [];
20082
+ });
20083
+ const selectRoomOptionReturnFlightsMetaData = toolkit.createSelector(selectActiveOption, selectFlightMetaData, (ativeOption, flightMetaData) => {
20084
+ const selectedRoomOptions = ativeOption?.rooms.map((r) => r.options.filter((o) => o.isSelected));
20085
+ // check if there are return flights selected
20086
+ if (selectedRoomOptions?.some((ro) => ro.length > 1)) {
20087
+ const roomOptions = ativeOption?.rooms.map((r) => r.options
20088
+ .filter((o) => o.isSelected)
20089
+ .reverse()
20090
+ .shift());
20091
+ return roomOptions?.map((ro) => flightMetaData?.find((f) => f.entryLineGuid === ro?.entryLineGuid)?.flightMetaData).filter((f) => f !== undefined) ?? [];
20092
+ }
20093
+ return undefined;
20094
+ });
20095
+ const selectApiSettings = (state) => state.apiSettings;
20096
+ const selectIsUnavailable = (state) => state.booking.isUnavailable;
20097
+ const selectRequestRooms = (state) => state.booking.package?.options.find((x) => x.isSelected)?.requestRooms;
20098
+ const selectOfficeId = (state) => state.booking.officeId;
20099
+ const selectLanguageCode = (state) => state.booking.languageCode;
20100
+ const selectCurrencyCode = (state) => state.booking.package?.currencyCode ?? 'EUR';
20101
+ const selectLanguage = (state) => state.booking.languageCode;
20102
+ const selectAllDynamicTranslations = (state) => state.booking.translations;
20103
+ const selectDynamicTranslations = toolkit.createSelector([selectAllDynamicTranslations, selectLanguage], (allDynamicTranslations, language) => {
20104
+ return allDynamicTranslations?.find((x) => x.language == language)?.value ?? {};
20105
+ });
20106
+ // Host override files are hand-maintained, and a null only ever blanks a key the component
20107
+ // then reads, so treat it like a missing key (lodash merge already skips undefined).
20108
+ const withoutNulls = (value) => lodash.isPlainObject(value)
20109
+ ? lodash.mapValues(lodash.pickBy(value, (x) => x !== null), withoutNulls)
20110
+ : value;
20111
+ // getTranslations deep-merges the overrides over a clone of the built-in translations, so a
20112
+ // host override of a nested group (TRAVELERS_FORM.VALIDATION, TRAVELERS_FORM.COUNTRIES)
20113
+ // only replaces the keys it actually contains. Merging per section one level deep dropped
20114
+ // every other built-in key of such a group, which made those keys resolve to undefined and
20115
+ // crash format() in the consuming component.
20116
+ const selectTranslations = toolkit.createSelector([selectLanguage, selectDynamicTranslations], (language, dynamicTranslations) => getTranslations(language, lodash.isEmpty(dynamicTranslations) ? undefined : withoutNulls(dynamicTranslations)));
20117
+ const selectBookingOptions = (state) => state.booking.bookingOptions;
20118
+ const selectBookingType = (state) => state.booking.bookingType;
20119
+ const selectTagIds = (state) => state.booking.tagIds;
20120
+ const selectAgentAdressId = (state) => state.booking.agentAdressId;
20121
+ const selectProductAttributes = (state) => state.booking.productAttributes;
20122
+ const selectBookingAttributes = (state) => state.booking.bookingAttributes;
20123
+ const selectBookingNumber = (state) => state.booking.bookingNumber;
20124
+ const selectIsOption = (state) => state.booking.isOption;
20125
+ const selectBookingRooms = (state) => state.booking.bookingAttributes?.rooms;
20126
+ const selectBookingRemarks = (state) => state.booking.remarks;
20127
+ const selectVoucherCodes = (state) => state.booking.voucherCodes;
20128
+ const selectCalculateDeposit = (state) => state.booking.calculateDeposit;
20129
+ const selectShowCommission = (state) => state.booking.showCommission;
20130
+ const selectIsRetry = (state) => state.booking.isRetry;
20131
+ const selectStartDate = (state) => state.booking.package?.options.find((x) => x.isSelected)?.fromDate;
20132
+ const selectEndDate = (state) => state.booking.package?.options.find((x) => x.isSelected)?.toDate;
20133
+ const selectAgents = (state) => state.booking.agents;
20134
+ const selectCountries = (state) => state.booking.countries;
20135
+ const selectProductCode = (state) => state.booking.productAttributes?.productCode;
20136
+ const selectAccommodationCodes = (state) => {
20137
+ const accommodationCodes = [];
20138
+ state.booking.package?.options.forEach((o) => {
20139
+ o.rooms.forEach((r) => r.options.forEach((ro) => {
20140
+ if (!accommodationCodes.some((y) => y === ro.accommodationCode)) {
20141
+ accommodationCodes.push(ro.accommodationCode);
20142
+ }
20143
+ }));
20144
+ });
20145
+ return accommodationCodes;
20146
+ };
20147
+ const selectAccommodationViews = (state) => state.booking.accommodationViews;
20148
+ const selectMappingInfo = (state) => state.booking.package?.options.find((x) => x.isSelected)?.mappingInfo;
20149
+ const selectBookingQuery = (state) => {
20150
+ const bookingAttributes = state.booking.bookingAttributes;
20151
+ if (!bookingAttributes) {
20152
+ return undefined;
20153
+ }
20154
+ const params = {};
20155
+ Object.entries(bookingAttributes).forEach(([key, value]) => {
20156
+ if (key === 'startDate' || key === 'endDate') {
20157
+ value = dateFns.format(dateFns.parseISO(value), 'yyyy-MM-dd');
20158
+ }
20159
+ if (key === 'rooms') {
20160
+ value = JsonURL__default["default"].stringify(value.map((room) => lodash.omit(room, ['children'])), {
20161
+ AQF: true
20162
+ });
20163
+ }
20164
+ if (key === 'allotmentIds' && !value.length) {
20165
+ value = undefined;
18910
20166
  }
18911
20167
  if (key === 'flight' && value) {
18912
20168
  var flightInfo = value;
@@ -18947,14 +20203,17 @@ const selectBookingAddress = toolkit.createSelector(selectTravelersFormValues, s
18947
20203
  const mainBooker = pax.find((x) => x.isMainBooker);
18948
20204
  if (!mainBooker || bookingType == 'b2b')
18949
20205
  return undefined;
20206
+ // The backend treats the main booker's per-pax address as authoritative, so keep the
20207
+ // booking-level address in sync with it rather than sending two independent values.
20208
+ const mainBookerAddress = mainBooker.address;
18950
20209
  return {
18951
20210
  name: `${mainBooker.firstName} ${mainBooker.lastName}`,
18952
- street: formValues?.street,
18953
- number: formValues?.houseNumber,
20211
+ street: mainBookerAddress?.street || formValues?.street,
20212
+ number: mainBookerAddress?.number || formValues?.houseNumber,
18954
20213
  box: formValues?.box,
18955
- postalCode: formValues?.zipCode,
18956
- location: formValues?.place,
18957
- country: formValues?.country,
20214
+ postalCode: mainBookerAddress?.postalCode || formValues?.zipCode,
20215
+ location: mainBookerAddress?.location || formValues?.place,
20216
+ country: mainBookerAddress?.country || formValues?.country,
18958
20217
  mobilePhone: formValues?.phone,
18959
20218
  email: formValues?.email
18960
20219
  };
@@ -19030,17 +20289,11 @@ const selectBookingPackageBookRequest = toolkit.createSelector(selectBookingPack
19030
20289
  return bookingPackageRequest;
19031
20290
  });
19032
20291
  const selectTravelersFirstStep = (state) => state.booking.travelersFirstStep;
19033
- const buildPax = (traveler, mainBookerId) => {
19034
- return {
19035
- id: traveler.id,
19036
- gender: parseGender(traveler.gender),
19037
- firstName: traveler.firstName,
19038
- lastName: traveler.lastName,
19039
- dateOfBirth: traveler.birthDate,
19040
- age: traveler.birthDate ? null : traveler.age,
19041
- isMainBooker: traveler.id == mainBookerId
19042
- };
19043
- };
20292
+ const buildPax = (traveler, mainBookerId) => applyTravelerToPax({
20293
+ id: traveler.id,
20294
+ gender: parseGender(traveler.gender),
20295
+ age: traveler.birthDate ? null : traveler.age
20296
+ }, traveler, mainBookerId);
19044
20297
  const parseGender = (gender) => {
19045
20298
  switch (gender) {
19046
20299
  case 'm':
@@ -23040,718 +24293,233 @@ const Summary = () => {
23040
24293
  React__default["default"].createElement("div", { className: "form__row" },
23041
24294
  React__default["default"].createElement("div", { className: "form__group" },
23042
24295
  React__default["default"].createElement("div", { className: "info-message" },
23043
- React__default["default"].createElement(Icon, { name: "ui-tooltip", className: "icon--secondary-color" }),
23044
- React__default["default"].createElement("div", { className: "info-message__copy" },
23045
- React__default["default"].createElement("h5", null, translations.SUMMARY.VALIDATE_TITLE),
23046
- settings.customValidateText ? (React__default["default"].createElement("div", { dangerouslySetInnerHTML: {
23047
- __html: settings.customValidateText
23048
- } })) : (React__default["default"].createElement(React__default["default"].Fragment, null,
23049
- React__default["default"].createElement("p", null, settings.isOffer ? translations.SUMMARY.VALIDATE_TEXT_OFFER : translations.SUMMARY.VALIDATE_TEXT_BOOKING),
23050
- settings.allowOption && React__default["default"].createElement("p", null, translations.SUMMARY.VALIDATE_TEXT_OPTION))),
23051
- checkboxes &&
23052
- checkboxes.map((checkbox) => (React__default["default"].createElement("div", { className: "checkbox", key: checkbox.id },
23053
- React__default["default"].createElement("label", { className: "checkbox__label" },
23054
- React__default["default"].createElement("input", { type: "checkbox", className: "checkbox__input", checked: checkbox.isSelected, onChange: (e) => handleCheckboxChange(checkbox.id, e.target.checked) }),
23055
- React__default["default"].createElement("span", { className: "checkbox__label-text", dangerouslySetInnerHTML: {
23056
- __html: checkbox.text
23057
- } }))))))))))),
23058
- React__default["default"].createElement("div", { className: "booking__navigator" },
23059
- settings.skipRouter ? (React__default["default"].createElement("button", { type: "button", title: translations.STEPS.PREVIOUS, onClick: () => goPrevious(), className: "cta cta--secondary" }, translations.STEPS.PREVIOUS)) : (React__default["default"].createElement(Link, { to: `${!settings.skipBasePathInRouting ? settings.basePath : ''}${travelersFirstStep ? settings.options.pathSuffix : settings.travellers.pathSuffix}?${bookingQueryString}`, title: translations.STEPS.PREVIOUS, className: "cta cta--secondary" }, translations.STEPS.PREVIOUS)),
23060
- settings.allowOption && (React__default["default"].createElement("button", { title: translations.STEPS.SUBMIT_OPTION, className: buildClassName(['cta', !userValidated && 'cta--disabled']), disabled: !userValidated, name: "option" }, translations.STEPS.SUBMIT_OPTION)),
23061
- React__default["default"].createElement("button", { title: settings.isOffer ? translations.STEPS.SUBMIT_OFFER : translations.STEPS.SUBMIT_BOOKING, className: buildClassName(['cta', !userValidated && 'cta--disabled']), disabled: !userValidated, name: "default" }, settings.isOffer ? translations.STEPS.SUBMIT_OFFER : translations.STEPS.SUBMIT_BOOKING))))));
23062
- };
23063
-
23064
- function isValidEmail(email) {
23065
- return !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,24}$/i.test(email);
23066
- }
23067
- const NAME_CHARACTERS_REGEX = /^[\p{L}\s-]+$/u;
23068
- function hasInvalidNameCharacters(name) {
23069
- return !NAME_CHARACTERS_REGEX.test(name);
23070
- }
23071
- // Rejects values like a 5-digit year, which the native date input allows while typing
23072
- function isValidBirthDate(birthDateText) {
23073
- return dateFns.isValid(dateFns.parse(birthDateText, 'yyyy-MM-dd', new Date()));
23074
- }
23075
- function getAge(birthDateText, startDateText) {
23076
- var birthDate = new Date(birthDateText);
23077
- var startDate = new Date(startDateText);
23078
- var age = startDate.getFullYear() - birthDate.getFullYear();
23079
- var m = startDate.getMonth() - birthDate.getMonth();
23080
- if (m < 0 || (m === 0 && startDate.getDate() < birthDate.getDate())) {
23081
- age--;
23082
- }
23083
- return age;
23084
- }
23085
- const validateForm$1 = (values, agentRequired, bookingType, translations, formFields, mainBookerFormFields, maxChildAge = CHILD_MAX_AGE, maxBabyAge = BABY_MAX_AGE) => {
23086
- const errors = {};
23087
- const isFormFieldPresent = (type) => {
23088
- return formFields?.some((f) => f.type === type) ?? true;
23089
- };
23090
- const isMainBookerFormFieldPresent = (type) => {
23091
- return mainBookerFormFields?.some((f) => f.type === type) ?? true;
23092
- };
23093
- const formatTravelerField = (room, pax, field) => {
23094
- return values.rooms.length > 1
23095
- ? format$1(translations.TRAVELERS_FORM.VALIDATION.TRAVELER_X_FIELD, [room, pax, field])
23096
- : format$1(translations.TRAVELERS_FORM.VALIDATION.SINGLE_ROOM_TRAVELER_X_FIELD, [pax, field]);
23097
- };
23098
- const formatTravelerIsNoAdult = (room, pax) => {
23099
- return values.rooms.length > 1
23100
- ? format$1(translations.TRAVELERS_FORM.VALIDATION.TRAVELER_X_IS_NO_ADULT, [room, pax])
23101
- : format$1(translations.TRAVELERS_FORM.VALIDATION.SINGLE_ROOM_TRAVELER_X_IS_NO_ADULT, [pax]);
23102
- };
23103
- const formatTravelerIsNoChild = (room, pax) => {
23104
- return values.rooms.length > 1
23105
- ? format$1(translations.TRAVELERS_FORM.VALIDATION.TRAVELER_X_IS_NO_CHILD, [room, pax])
23106
- : format$1(translations.TRAVELERS_FORM.VALIDATION.SINGLE_ROOM_TRAVELER_X_IS_NO_CHILD, [pax]);
23107
- };
23108
- const formatTravelerIsNoBaby = (room, pax) => {
23109
- // The message states the boundary the traveler has to stay under, so we pass
23110
- // the first age that is too old rather than the inclusive maxBabyAge.
23111
- const tooOldAge = maxBabyAge + 1;
23112
- return values.rooms.length > 1
23113
- ? format$1(translations.TRAVELERS_FORM.VALIDATION.TRAVELER_X_IS_NO_BABY, [room, pax, tooOldAge])
23114
- : format$1(translations.TRAVELERS_FORM.VALIDATION.SINGLE_ROOM_TRAVELER_X_IS_NO_BABY, [pax, tooOldAge]);
23115
- };
23116
- const formatTravelerFieldInvalidCharacters = (room, pax, field) => {
23117
- return values.rooms.length > 1
23118
- ? format$1(translations.TRAVELERS_FORM.VALIDATION.TRAVELER_X_FIELD_INVALID_CHARACTERS, [room, pax, field])
23119
- : format$1(translations.TRAVELERS_FORM.VALIDATION.SINGLE_ROOM_TRAVELER_X_FIELD_INVALID_CHARACTERS, [pax, field]);
23120
- };
23121
- values.rooms.forEach((r, rIndex) => r.adults.forEach((adult, index) => {
23122
- if (lodash.isEmpty(adult.gender) && isFormFieldPresent('gender')) {
23123
- lodash.set(errors, `rooms[${rIndex}].adults[${index}].gender`, formatTravelerField(rIndex + 1, index + 1, translations.TRAVELERS_FORM.GENDER));
23124
- }
23125
- if (isFormFieldPresent('firstName')) {
23126
- if (lodash.isEmpty(adult.firstName)) {
23127
- lodash.set(errors, `rooms[${rIndex}].adults[${index}].firstName`, formatTravelerField(rIndex + 1, index + 1, translations.TRAVELERS_FORM.FIRST_NAME));
23128
- }
23129
- else if (hasInvalidNameCharacters(adult.firstName)) {
23130
- lodash.set(errors, `rooms[${rIndex}].adults[${index}].firstName`, formatTravelerFieldInvalidCharacters(rIndex + 1, index + 1, translations.TRAVELERS_FORM.FIRST_NAME));
23131
- }
23132
- }
23133
- if (isFormFieldPresent('lastName')) {
23134
- if (lodash.isEmpty(adult.lastName)) {
23135
- lodash.set(errors, `rooms[${rIndex}].adults[${index}].lastName`, formatTravelerField(rIndex + 1, index + 1, translations.TRAVELERS_FORM.LAST_NAME));
23136
- }
23137
- else if (hasInvalidNameCharacters(adult.lastName)) {
23138
- lodash.set(errors, `rooms[${rIndex}].adults[${index}].lastName`, formatTravelerFieldInvalidCharacters(rIndex + 1, index + 1, translations.TRAVELERS_FORM.LAST_NAME));
23139
- }
23140
- }
23141
- if (isFormFieldPresent('birthDate')) {
23142
- if (lodash.isEmpty(adult.birthDate) || !isValidBirthDate(adult.birthDate)) {
23143
- lodash.set(errors, `rooms[${rIndex}].adults[${index}].birthDate`, formatTravelerField(rIndex + 1, index + 1, translations.TRAVELERS_FORM.BIRTHDATE));
23144
- }
23145
- else if (values.endDate) {
23146
- // If you are younger than the maxChildAge at the end of the trip, you are considered a child, so we check the endDate here instead of the startDate
23147
- const age = getAge(adult.birthDate, values.endDate);
23148
- if (age <= maxChildAge) {
23149
- lodash.set(errors, `rooms[${rIndex}].adults[${index}].birthDate`, formatTravelerIsNoAdult(rIndex + 1, index + 1));
23150
- }
23151
- }
23152
- }
23153
- }));
23154
- values.rooms.forEach((r, rIndex) => r.children.forEach((child, index) => {
23155
- if (lodash.isEmpty(child.gender) && isFormFieldPresent('gender')) {
23156
- lodash.set(errors, `rooms[${rIndex}].children[${index}].gender`, formatTravelerField(rIndex + 1, r.adults.length + index + 1, translations.TRAVELERS_FORM.GENDER));
23157
- }
23158
- if (isFormFieldPresent('firstName')) {
23159
- if (lodash.isEmpty(child.firstName)) {
23160
- lodash.set(errors, `rooms[${rIndex}].children[${index}].firstName`, formatTravelerField(rIndex + 1, r.adults.length + index + 1, translations.TRAVELERS_FORM.FIRST_NAME));
23161
- }
23162
- else if (hasInvalidNameCharacters(child.firstName)) {
23163
- lodash.set(errors, `rooms[${rIndex}].children[${index}].firstName`, formatTravelerFieldInvalidCharacters(rIndex + 1, r.adults.length + index + 1, translations.TRAVELERS_FORM.FIRST_NAME));
23164
- }
23165
- }
23166
- if (isFormFieldPresent('lastName')) {
23167
- if (lodash.isEmpty(child.lastName)) {
23168
- lodash.set(errors, `rooms[${rIndex}].children[${index}].lastName`, formatTravelerField(rIndex + 1, r.adults.length + index + 1, translations.TRAVELERS_FORM.LAST_NAME));
23169
- }
23170
- else if (hasInvalidNameCharacters(child.lastName)) {
23171
- lodash.set(errors, `rooms[${rIndex}].children[${index}].lastName`, formatTravelerFieldInvalidCharacters(rIndex + 1, r.adults.length + index + 1, translations.TRAVELERS_FORM.LAST_NAME));
23172
- }
23173
- }
23174
- if (isFormFieldPresent('birthDate')) {
23175
- if (lodash.isEmpty(child.birthDate) || !isValidBirthDate(child.birthDate)) {
23176
- lodash.set(errors, `rooms[${rIndex}].children[${index}].birthDate`, formatTravelerField(rIndex + 1, r.adults.length + index + 1, translations.TRAVELERS_FORM.BIRTHDATE));
23177
- }
23178
- else if (child.isBaby && values.startDate) {
23179
- // A baby only has to be under the maxBabyAge on departure, so we check the startDate here instead of the endDate (TIDE-3269)
23180
- const age = getAge(child.birthDate, values.startDate);
23181
- if (age > maxBabyAge) {
23182
- lodash.set(errors, `rooms[${rIndex}].children[${index}].birthDate`, formatTravelerIsNoBaby(rIndex + 1, r.adults.length + index + 1));
23183
- }
23184
- }
23185
- else if (values.endDate) {
23186
- // You need to be under the maxChildAge until the end of the trip, so we check the endDate here instead of the startDate.
23187
- // Babies land here too when there is no startDate to check them against, so they keep at least the child age check.
23188
- const age = getAge(child.birthDate, values.endDate);
23189
- if (age > maxChildAge) {
23190
- lodash.set(errors, `rooms[${rIndex}].children[${index}].birthDate`, formatTravelerIsNoChild(rIndex + 1, r.adults.length + index + 1));
23191
- }
23192
- }
23193
- }
23194
- }));
23195
- if (values.mainBookerId < 0) {
23196
- errors.mainBookerId = translations.TRAVELERS_FORM.VALIDATION.NO_MAIN_BOOKER_SELECTED;
23197
- }
23198
- if (bookingType != 'b2b' || mainBookerFormFields?.length) {
23199
- if (lodash.isEmpty(values.street) && isMainBookerFormFieldPresent('street')) {
23200
- errors.street = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.STREET]);
23201
- }
23202
- if (lodash.isEmpty(values.houseNumber) && isMainBookerFormFieldPresent('houseNumber')) {
23203
- errors.houseNumber = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.HOUSE_NUMBER]);
23204
- }
23205
- if (lodash.isEmpty(values.zipCode) && isMainBookerFormFieldPresent('zipCode')) {
23206
- errors.zipCode = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.ZIPCODE]);
23207
- }
23208
- if (lodash.isEmpty(values.place) && isMainBookerFormFieldPresent('place')) {
23209
- errors.place = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.CITY]);
23210
- }
23211
- if (isMainBookerFormFieldPresent('email')) {
23212
- if (lodash.isEmpty(values.email)) {
23213
- errors.email = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.EMAIL]);
23214
- }
23215
- else if (isValidEmail(values.email)) {
23216
- errors.email = translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_EMAIL_IS_INVALID;
23217
- }
23218
- if (lodash.isEmpty(values.emailConfirmation)) {
23219
- errors.emailConfirmation = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.REPEAT_EMAIL]);
23220
- }
23221
- else if (values.emailConfirmation !== values.email) {
23222
- errors.emailConfirmation = translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_EMAIL_DOES_NOT_MATCH;
23223
- }
23224
- }
23225
- if (lodash.isEmpty(values.country) && isMainBookerFormFieldPresent('country')) {
23226
- errors.country = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.COUNTRY]);
23227
- }
23228
- if (lodash.isEmpty(values.phone) && isMainBookerFormFieldPresent('phone')) {
23229
- errors.phone = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.PHONE]);
23230
- }
23231
- }
23232
- else {
23233
- if (lodash.isEmpty(values.phone)) {
23234
- errors.phone = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.PHONE]);
23235
- }
23236
- }
23237
- if (agentRequired && !values.travelAgentId) {
23238
- errors.travelAgentId = translations.TRAVELERS_FORM.VALIDATION.AGENT_IS_REQUIRED;
23239
- }
23240
- return errors;
23241
- };
23242
-
23243
- const LabeledInput = ({ name, type, required, value, defaultValue, min, max, label, placeholder, extraClassName, hasError, onChange, onBlur }) => {
23244
- return (React__default["default"].createElement("label", { className: buildClassName(['form__group', extraClassName, hasError && 'form__group--error']) },
23245
- React__default["default"].createElement("span", { className: "form__label" }, lodash.compact([label, required && '*']).join(' ')),
23246
- React__default["default"].createElement("input", { name: name, type: type ?? 'text', required: required, className: "form__input", min: min, max: max, placeholder: placeholder, onChange: onChange, onBlur: onBlur, value: value, defaultValue: defaultValue })));
23247
- };
23248
-
23249
- const LabeledSelect = ({ name, id, required, value, defaultValue, label, extraClassName, extraDropdownClassName, hasError, options, onChange, onBlur }) => {
23250
- return (React__default["default"].createElement("label", { className: buildClassName(['form__group', extraClassName, hasError && 'form__group--error']) },
23251
- React__default["default"].createElement("span", { className: "form__label" }, lodash.compact([label, required && '*']).join(' ')),
23252
- React__default["default"].createElement("div", { className: buildClassName(['dropdown', extraDropdownClassName]) },
23253
- React__default["default"].createElement("select", { name: name, id: id, defaultValue: defaultValue, value: value, onBlur: onBlur, onChange: onChange }, options?.map((option) => (React__default["default"].createElement("option", { key: option.key, value: option.value }, option.label)))))));
23254
- };
23255
-
23256
- const normalize = (v) => (v == null ? '' : String(v));
23257
- const parseCombined = (raw, countries) => {
23258
- const value = normalize(raw).trim();
23259
- if (!value)
23260
- return { prefix: '', number: '' };
23261
- // Try to match a known prefix at the start. Prefer longest match.
23262
- const sorted = [...countries].sort((a, b) => b.phonePrefix.length - a.phonePrefix.length);
23263
- const hit = sorted.find((c) => value.startsWith(c.phonePrefix));
23264
- if (hit) {
23265
- const rest = value
23266
- .slice(hit.phonePrefix.length)
23267
- .trim()
23268
- .replace(/^[-\s]+/, '');
23269
- return { prefix: hit.phonePrefix, number: rest };
23270
- }
23271
- // Fallback: split on first space if it looks like a +prefix number
23272
- const m = value.match(/^(\+\d{1,4})[\s-]*(.*)$/);
23273
- if (m)
23274
- return { prefix: m[1], number: m[2] };
23275
- return { prefix: '', number: value };
23276
- };
23277
- const PhoneInput = ({ name, required, value, label, placeholder, extraClassName, hasError, countries, countryIso2, onChange, onBlur }) => {
23278
- // Derive initial state from `value`
23279
- const initialCountry = countries.find((c) => c.iso2 === countryIso2);
23280
- const initial = React.useMemo(() => parseCombined(normalize(value), countries), [value, countries]);
23281
- const [prefix, setPrefix] = React.useState(initial.prefix);
23282
- const [number, setNumber] = React.useState(initial.number);
23283
- // Keep state in sync if the parent changes `value`
23284
- React.useEffect(() => {
23285
- const parsed = !prefix && !number && initialCountry ? { prefix: initialCountry.phonePrefix, number: '' } : parseCombined(normalize(value), countries);
23286
- if (parsed.prefix)
23287
- setPrefix(parsed.prefix);
23288
- if (parsed.number)
23289
- setNumber(parsed.number);
23290
- }, [value, countries, countryIso2]);
23291
- const emitCombinedChange = (e, nextPrefix, nextNumber) => {
23292
- // Combine with a space, unless the number already starts with a dash or space
23293
- const combined = nextPrefix && nextNumber ? lodash.compact([nextPrefix, nextNumber]).join(' ') : null;
23294
- onChange?.({
23295
- ...e,
23296
- type: 'change',
23297
- target: { name, value: combined },
23298
- currentTarget: { name, value: combined }
23299
- });
23300
- };
23301
- const handleCountryBlur = (e) => {
23302
- onBlur?.(e);
23303
- };
23304
- const onCountryChange = (e) => {
23305
- const next = e.target.value;
23306
- setPrefix(next);
23307
- emitCombinedChange(e, next, number);
23308
- };
23309
- const onPhoneChange = (e) => {
23310
- const cleaned = e.target.value.replace(/[^\d\s-]/g, '');
23311
- setNumber(cleaned);
23312
- emitCombinedChange(e, prefix, cleaned);
23313
- };
23314
- const handleNumberBlur = (e) => {
23315
- onBlur?.(e);
23316
- };
23317
- // Ensure the select shows a reasonable default when prefix is empty
23318
- const selectValue = prefix || '';
23319
- return (React__default["default"].createElement("label", { className: buildClassName(['form__group', extraClassName, hasError && 'form__group--error']) },
23320
- label && React__default["default"].createElement("span", { className: "form__label" }, lodash.compact([label, required && '*']).join(' ')),
23321
- React__default["default"].createElement("div", { className: "phone-input" },
23322
- React__default["default"].createElement("div", { className: "dropdown" },
23323
- React__default["default"].createElement("select", { "aria-label": label ? `${label} – country code` : 'Country calling code', name: `${name}__country`, value: selectValue, onBlur: handleCountryBlur, onChange: onCountryChange },
23324
- React__default["default"].createElement("option", { value: "", disabled: true }),
23325
- countries?.map((option) => (React__default["default"].createElement("option", { key: option.iso2, value: option.phonePrefix },
23326
- option.name,
23327
- " (",
23328
- option.phonePrefix,
23329
- ")"))))),
23330
- React__default["default"].createElement("input", { "aria-label": label ? `${label} – number` : 'Phone number', name: `${name}__number`, type: "tel", inputMode: "tel", required: required, className: "form__input", placeholder: placeholder, onChange: onPhoneChange, onBlur: handleNumberBlur, value: number }),
23331
- React__default["default"].createElement("input", { type: "hidden", name: name, value: lodash.compact([prefix, number]).join(' ') }))));
23332
- };
23333
-
23334
- const hasVisibleError = (formik, key) => lodash.get(formik.errors, key) && lodash.get(formik.touched, key);
23335
-
23336
- const GenderControl = ({ translations, value, formik, name }) => {
23337
- return (React__default["default"].createElement("div", { className: buildClassName(['form__group', hasVisibleError(formik, name) && 'form__group--error']) },
23338
- React__default["default"].createElement("label", { className: "form__label" },
23339
- translations.TRAVELERS_FORM.GENDER_ID,
23340
- " *"),
23341
- React__default["default"].createElement("div", { className: "radiobutton-group" },
23342
- React__default["default"].createElement("div", { className: "radiobutton" },
23343
- React__default["default"].createElement("label", { className: "radiobutton__label" },
23344
- React__default["default"].createElement("input", { type: "radio", className: "radiobutton__input", name: name, onChange: formik.handleChange, onBlur: formik.handleBlur, value: "m", checked: value.gender === 'm' }),
23345
- translations.TRAVELERS_FORM.MALE_GENDER)),
23346
- React__default["default"].createElement("div", { className: "radiobutton" },
23347
- React__default["default"].createElement("label", { className: "radiobutton__label" },
23348
- React__default["default"].createElement("input", { type: "radio", className: "radiobutton__input", name: name, onChange: formik.handleChange, onBlur: formik.handleBlur, value: "f", checked: value.gender === 'f' }),
23349
- translations.TRAVELERS_FORM.FEMALE_GENDER)))));
23350
- };
23351
-
23352
- const TypeAheadInput = ({ name, value, placeholder, options, onChange, onSelect, onClear }) => {
23353
- const [showItems, setShowItems] = React.useState(false);
23354
- const handleChange = (e) => {
23355
- if (onChange) {
23356
- onChange(e.target.value);
23357
- }
23358
- };
23359
- const handleClearClick = (e) => {
23360
- if (onClear) {
23361
- onClear();
23362
- }
23363
- setShowItems(false);
23364
- };
23365
- return (React__default["default"].createElement("div", { className: "typeahead" },
23366
- React__default["default"].createElement("div", { className: "typeahead__trigger typeahead__trigger--icon" },
23367
- React__default["default"].createElement(Icon, { name: "ui-qsm-location" }),
23368
- React__default["default"].createElement("input", { type: "text", className: "form__input", name: name, value: value, onChange: handleChange, placeholder: placeholder, required: true, autoComplete: "off", onKeyDown: (e) => {
23369
- if (e.key === 'Tab' && options && onSelect) {
23370
- onSelect(options[0].key);
23371
- setShowItems(false);
23372
- }
23373
- }, onFocus: () => setShowItems(true) }),
23374
- React__default["default"].createElement("button", { type: "button", className: "button button--clear", onClick: handleClearClick })),
23375
- options && options.length > 0 && showItems && (React__default["default"].createElement("div", { className: "typeahead__options" }, (options ?? []).map((option) => {
23376
- const matchIndex = typeof value !== 'undefined' ? option.text.indexOf(value) : 0;
23377
- const prefix = option.text.slice(0, matchIndex);
23378
- const highlight = option.text.slice(matchIndex, value?.length ?? 0);
23379
- const suffix = option.text.slice(matchIndex);
23380
- return (React__default["default"].createElement("div", { className: "typeahead__option", key: option.key },
23381
- React__default["default"].createElement("div", { className: "typeahead__option-header" },
23382
- React__default["default"].createElement("button", { onClick: () => {
23383
- if (onSelect)
23384
- onSelect(option.key);
23385
- setShowItems(false);
23386
- }, type: "button", className: "button button--plain" },
23387
- prefix,
23388
- React__default["default"].createElement("span", { className: "typeahead__option-highlight" }, highlight),
23389
- suffix))));
23390
- })))));
23391
- };
23392
-
23393
- // A traveler is a baby when the age they were booked with is at or below the baby
23394
- // boundary. Babies get their own label and birth date validation (TIDE-3269). Only
23395
- // meaningful for the regular form: the compact form asks for an age instead of a birth
23396
- // date, so it has nothing to validate and its own age input already states the age.
23397
- function isBabyAge(age, maxBabyAge = BABY_MAX_AGE) {
23398
- return typeof age === 'number' && age <= maxBabyAge;
23399
- }
23400
- // The native date input can hold values format() throws on (e.g. a 5+ digit year while typing), so never format an unvalidated birthDate
23401
- function formatBirthDate$1(birthDate) {
23402
- if (!birthDate)
23403
- return '';
23404
- const parsed = dateFns.parse(birthDate, 'yyyy-MM-dd', new Date());
23405
- return dateFns.isValid(parsed) ? dateFns.format(parsed, 'dd-MM-yyyy') : '';
23406
- }
23407
- function createTraveler(traveler, followNumber, personTranslation, isCompact, maxBabyAge) {
23408
- if (isCompact) {
23409
- return {
23410
- id: traveler.id,
23411
- firstName: personTranslation,
23412
- lastName: `${followNumber.number++}`,
23413
- birthDate: '',
23414
- gender: '',
23415
- // Keep a booked age of 0 instead of submitting and pricing the traveler as a 30 year
23416
- // old, while still falling back for a missing or unusable age. `??` alone would let a
23417
- // NaN (unparseable date of birth upstream) reach the age input and the booked pax.
23418
- // See TIDE-3269.
23419
- age: Number.isFinite(traveler.age) ? traveler.age : 30
23420
- // No isBaby here on purpose: the age below is editable and nothing revalidates it
23421
- // in compact mode, so a stored flag would end up contradicting the entered age.
23422
- };
23423
- }
23424
- return {
23425
- id: traveler.id,
23426
- firstName: '',
23427
- lastName: '',
23428
- birthDate: '',
23429
- gender: '',
23430
- isBaby: isBabyAge(traveler.age, maxBabyAge)
23431
- };
23432
- }
23433
- function createInitialValuesFromRooms(formRooms, startDate, endDate, agentAdressId, personTranslation, isCompact, maxBabyAge) {
23434
- const followNumber = { number: 1 };
23435
- const initialValues = {
23436
- startDate,
23437
- endDate,
23438
- rooms: formRooms.map((room) => ({
23439
- adults: room.adults.map((traveler) => createTraveler(traveler, followNumber, personTranslation, isCompact, maxBabyAge)),
23440
- children: room.children.map((traveler) => createTraveler(traveler, followNumber, personTranslation, isCompact, maxBabyAge))
23441
- })),
23442
- mainBookerId: -1,
23443
- street: '',
23444
- houseNumber: '',
23445
- box: '',
23446
- zipCode: '',
23447
- place: '',
23448
- country: '',
23449
- phone: '',
23450
- email: '',
23451
- emailConfirmation: '',
23452
- travelAgentId: agentAdressId ?? 0,
23453
- travelAgentName: ''
23454
- };
23455
- if (initialValues.rooms?.[0]?.adults?.[0]) {
23456
- initialValues.mainBookerId = initialValues.rooms[0].adults[0].id;
23457
- }
23458
- return initialValues;
23459
- }
23460
- function createInitialValuesFromEditablePackagingEntry(editablePackagingEntry, agentAdressId) {
23461
- const pax = editablePackagingEntry?.pax ?? [];
23462
- const rooms = editablePackagingEntry.rooms.map((room) => {
23463
- const roomPax = pax.filter((x) => room.paxIds.includes(x.id));
23464
- const adults = roomPax.filter((x) => x.age >= 18);
23465
- const children = roomPax.filter((x) => x.age < 18);
23466
- return {
23467
- adults: adults.map((roomTraveler) => {
23468
- return {
23469
- id: roomTraveler.id,
23470
- firstName: roomTraveler.firstName ?? '',
23471
- lastName: roomTraveler.lastName ?? '',
23472
- birthDate: roomTraveler.dateOfBirth ? dateFns.format(new Date(roomTraveler.dateOfBirth), 'yyyy-MM-dd') : '',
23473
- gender: ''
23474
- };
23475
- }),
23476
- children: children.map((roomTraveler) => {
23477
- return {
23478
- id: roomTraveler.id,
23479
- firstName: roomTraveler.firstName ?? '',
23480
- lastName: roomTraveler.lastName ?? '',
23481
- birthDate: roomTraveler.dateOfBirth ? dateFns.format(new Date(roomTraveler.dateOfBirth), 'yyyy-MM-dd') : '',
23482
- gender: '',
23483
- isBaby: isBabyAge(roomTraveler.age)
23484
- };
23485
- })
23486
- };
23487
- });
23488
- const values = createInitialValuesFromRooms(rooms.map((room) => ({ adults: room.adults, children: room.children })), lodash.first(editablePackagingEntry?.lines)?.from, lodash.last(editablePackagingEntry?.lines)?.to, agentAdressId);
23489
- values.rooms = rooms;
23490
- values.mainBookerId = pax.find((x) => x.isMainBooker)?.id ?? rooms[0]?.adults?.[0]?.id ?? -1;
23491
- const address = editablePackagingEntry?.address;
23492
- values.street = address?.street ?? '';
23493
- values.houseNumber = address?.houseNumber ?? '';
23494
- values.box = address?.box ?? '';
23495
- values.zipCode = address?.zipCode ?? '';
23496
- values.place = address?.place ?? '';
23497
- values.country = address?.country ?? '';
23498
- values.phone = address?.phone ?? '';
23499
- values.email = address?.email ?? '';
23500
- values.emailConfirmation = address?.email ?? '';
23501
- values.travelAgentId = address?.travelAgentId ?? agentAdressId ?? 0;
23502
- return values;
24296
+ React__default["default"].createElement(Icon, { name: "ui-tooltip", className: "icon--secondary-color" }),
24297
+ React__default["default"].createElement("div", { className: "info-message__copy" },
24298
+ React__default["default"].createElement("h5", null, translations.SUMMARY.VALIDATE_TITLE),
24299
+ settings.customValidateText ? (React__default["default"].createElement("div", { dangerouslySetInnerHTML: {
24300
+ __html: settings.customValidateText
24301
+ } })) : (React__default["default"].createElement(React__default["default"].Fragment, null,
24302
+ React__default["default"].createElement("p", null, settings.isOffer ? translations.SUMMARY.VALIDATE_TEXT_OFFER : translations.SUMMARY.VALIDATE_TEXT_BOOKING),
24303
+ settings.allowOption && React__default["default"].createElement("p", null, translations.SUMMARY.VALIDATE_TEXT_OPTION))),
24304
+ checkboxes &&
24305
+ checkboxes.map((checkbox) => (React__default["default"].createElement("div", { className: "checkbox", key: checkbox.id },
24306
+ React__default["default"].createElement("label", { className: "checkbox__label" },
24307
+ React__default["default"].createElement("input", { type: "checkbox", className: "checkbox__input", checked: checkbox.isSelected, onChange: (e) => handleCheckboxChange(checkbox.id, e.target.checked) }),
24308
+ React__default["default"].createElement("span", { className: "checkbox__label-text", dangerouslySetInnerHTML: {
24309
+ __html: checkbox.text
24310
+ } }))))))))))),
24311
+ React__default["default"].createElement("div", { className: "booking__navigator" },
24312
+ settings.skipRouter ? (React__default["default"].createElement("button", { type: "button", title: translations.STEPS.PREVIOUS, onClick: () => goPrevious(), className: "cta cta--secondary" }, translations.STEPS.PREVIOUS)) : (React__default["default"].createElement(Link, { to: `${!settings.skipBasePathInRouting ? settings.basePath : ''}${travelersFirstStep ? settings.options.pathSuffix : settings.travellers.pathSuffix}?${bookingQueryString}`, title: translations.STEPS.PREVIOUS, className: "cta cta--secondary" }, translations.STEPS.PREVIOUS)),
24313
+ settings.allowOption && (React__default["default"].createElement("button", { title: translations.STEPS.SUBMIT_OPTION, className: buildClassName(['cta', !userValidated && 'cta--disabled']), disabled: !userValidated, name: "option" }, translations.STEPS.SUBMIT_OPTION)),
24314
+ React__default["default"].createElement("button", { title: settings.isOffer ? translations.STEPS.SUBMIT_OFFER : translations.STEPS.SUBMIT_BOOKING, className: buildClassName(['cta', !userValidated && 'cta--disabled']), disabled: !userValidated, name: "default" }, settings.isOffer ? translations.STEPS.SUBMIT_OFFER : translations.STEPS.SUBMIT_BOOKING))))));
24315
+ };
24316
+
24317
+ function isValidEmail(email) {
24318
+ return !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,24}$/i.test(email);
23503
24319
  }
23504
- function applyTravelersFormValuesToEditablePackagingEntry(editablePackagingEntry, values) {
23505
- const travelers = values.rooms.flatMap((room) => [...room.adults, ...room.children]);
23506
- return {
23507
- ...editablePackagingEntry,
23508
- pax: (editablePackagingEntry.pax ?? []).map((pax) => {
23509
- const traveler = travelers.find((x) => x.id === pax.id);
23510
- if (!traveler)
23511
- return pax;
23512
- return {
23513
- ...pax,
23514
- firstName: traveler.firstName ?? '',
23515
- lastName: traveler.lastName ?? '',
23516
- dateOfBirth: traveler.birthDate || null,
23517
- isMainBooker: traveler.id === values.mainBookerId
23518
- };
23519
- }),
23520
- address: {
23521
- ...editablePackagingEntry.address,
23522
- street: values.street,
23523
- houseNumber: values.houseNumber,
23524
- box: values.box,
23525
- zipCode: values.zipCode,
23526
- place: values.place,
23527
- country: values.country,
23528
- travelAgentId: values.travelAgentId,
23529
- phone: values.phone,
23530
- email: values.email
23531
- }
23532
- };
24320
+ const NAME_CHARACTERS_REGEX = /^[\p{L}\s-]+$/u;
24321
+ function hasInvalidNameCharacters(name) {
24322
+ return !NAME_CHARACTERS_REGEX.test(name);
23533
24323
  }
23534
- const SharedTravelersForm = ({ formik, translations, travellersSettings, countries, agents, bookingType, agentAdressId, travelersFirstStep = false, isUnavailable = false, useCompactForm = false, showAllCountries = false, showAgentSelection = false, initialShowAgents = false, renderPreviousButton, onBookingTypeChange }) => {
23535
- const [showAgents, setShowAgents] = React.useState(initialShowAgents);
23536
- const typeaheadAgents = React.useMemo(() => lodash.sortBy(agents?.map((agent) => ({
23537
- key: `${agent.id}`,
23538
- value: `${agent.name}${agent.postalCode || agent.location ? ` (${lodash.compact([agent.postalCode, agent.location]).join(' ')})` : ''}`,
23539
- text: `${agent.name}${agent.postalCode || agent.location ? ` (${lodash.compact([agent.postalCode, agent.location]).join(' ')})` : ''}`
23540
- })), 'value') ?? [], [agents]);
23541
- const [filteredAgents, setFilteredAgents] = React.useState(typeaheadAgents);
23542
- const flatErrors = flat__default["default"](formik.errors);
23543
- const errorKeys = Object.keys(flatErrors).filter((key) => lodash.get(formik.touched, key));
23544
- const hasVisibleError = (key) => lodash.get(formik.errors, key) && lodash.get(formik.touched, key);
23545
- const mainBooker = formik.values.rooms
23546
- .find((room) => room.adults.find((traveler) => traveler.id === formik.values.mainBookerId))
23547
- ?.adults.find((traveler) => traveler.id === formik.values.mainBookerId);
23548
- const countryOptions = [
23549
- { key: 'empty', value: undefined, label: translations.TRAVELERS_FORM.SELECT_COUNTRY },
23550
- ...(showAllCountries
23551
- ? countries?.map((country) => ({ key: country.iso2, value: country.iso2, label: country.name })) ?? []
23552
- : travellersSettings?.countries?.map((country) => ({ key: country.iso2, value: country.iso2, label: country.name })) ?? [
23553
- { key: 'be', value: 'be', label: translations.TRAVELERS_FORM.COUNTRIES.BELGIUM },
23554
- { key: 'nl', value: 'nl', label: translations.TRAVELERS_FORM.COUNTRIES.NETHERLANDS },
23555
- { key: 'fr', value: 'fr', label: translations.TRAVELERS_FORM.COUNTRIES.FRANCE }
23556
- ])
23557
- ];
23558
- const handleMainBookerChange = (event) => {
23559
- formik.setFieldValue('mainBookerId', parseInt(event.currentTarget.value, 10));
24324
+ // Rejects values like a 5-digit year, which the native date input allows while typing
24325
+ function isValidBirthDate(birthDateText) {
24326
+ return dateFns.isValid(dateFns.parse(birthDateText, 'yyyy-MM-dd', new Date()));
24327
+ }
24328
+ function getAge(birthDateText, startDateText) {
24329
+ var birthDate = new Date(birthDateText);
24330
+ var startDate = new Date(startDateText);
24331
+ var age = startDate.getFullYear() - birthDate.getFullYear();
24332
+ var m = startDate.getMonth() - birthDate.getMonth();
24333
+ if (m < 0 || (m === 0 && startDate.getDate() < birthDate.getDate())) {
24334
+ age--;
24335
+ }
24336
+ return age;
24337
+ }
24338
+ const validateForm$1 = (values, agentRequired, bookingType, translations, formFields, mainBookerFormFields, maxChildAge = CHILD_MAX_AGE, maxBabyAge = BABY_MAX_AGE, paxFields = []) => {
24339
+ const errors = {};
24340
+ const isFormFieldPresent = (type) => {
24341
+ return formFields?.some((f) => f.type === type) ?? true;
23560
24342
  };
23561
- const handleAgentChange = (value) => {
23562
- setFilteredAgents(typeaheadAgents.filter((x) => x.value.toLocaleLowerCase().indexOf(value.toLocaleLowerCase()) > -1));
23563
- formik.setFieldValue('travelAgentName', value);
24343
+ const isMainBookerFormFieldPresent = (type) => {
24344
+ return mainBookerFormFields?.some((f) => f.type === type) ?? true;
23564
24345
  };
23565
- const handleAgentSelect = (key) => {
23566
- const agent = typeaheadAgents.find((x) => x.key === key);
23567
- formik.setValues({
23568
- ...formik.values,
23569
- travelAgentId: Number(agent?.key),
23570
- travelAgentName: agent?.value ?? ''
23571
- });
23572
- onBookingTypeChange?.(agentAdressId && agentAdressId !== 0 ? 'b2b' : 'b2b2c');
24346
+ const formatTravelerField = (room, pax, field) => {
24347
+ return values.rooms.length > 1
24348
+ ? format$1(translations.TRAVELERS_FORM.VALIDATION.TRAVELER_X_FIELD, [room, pax, field])
24349
+ : format$1(translations.TRAVELERS_FORM.VALIDATION.SINGLE_ROOM_TRAVELER_X_FIELD, [pax, field]);
23573
24350
  };
23574
- const handleAgentClear = () => {
23575
- formik.setValues({ ...formik.values, travelAgentId: 0, travelAgentName: '' });
23576
- onBookingTypeChange?.('b2c');
24351
+ const formatTravelerIsNoAdult = (room, pax) => {
24352
+ return values.rooms.length > 1
24353
+ ? format$1(translations.TRAVELERS_FORM.VALIDATION.TRAVELER_X_IS_NO_ADULT, [room, pax])
24354
+ : format$1(translations.TRAVELERS_FORM.VALIDATION.SINGLE_ROOM_TRAVELER_X_IS_NO_ADULT, [pax]);
23577
24355
  };
23578
- const toggleAgent = (value) => {
23579
- setShowAgents(value);
23580
- if (!value) {
23581
- handleAgentClear();
23582
- setFilteredAgents([]);
23583
- }
24356
+ const formatTravelerIsNoChild = (room, pax) => {
24357
+ return values.rooms.length > 1
24358
+ ? format$1(translations.TRAVELERS_FORM.VALIDATION.TRAVELER_X_IS_NO_CHILD, [room, pax])
24359
+ : format$1(translations.TRAVELERS_FORM.VALIDATION.SINGLE_ROOM_TRAVELER_X_IS_NO_CHILD, [pax]);
23584
24360
  };
23585
- const handleAddTraveler = (roomIndex) => {
23586
- const rooms = [...formik.values.rooms];
23587
- const newAdult = { id: Date.now(), firstName: '', lastName: '', birthDate: '', gender: '' };
23588
- rooms[roomIndex] = { ...rooms[roomIndex], adults: [...rooms[roomIndex].adults, newAdult] };
23589
- formik.setFieldValue('rooms', rooms);
24361
+ const formatTravelerIsNoBaby = (room, pax) => {
24362
+ // The message states the boundary the traveler has to stay under, so we pass
24363
+ // the first age that is too old rather than the inclusive maxBabyAge.
24364
+ const tooOldAge = maxBabyAge + 1;
24365
+ return values.rooms.length > 1
24366
+ ? format$1(translations.TRAVELERS_FORM.VALIDATION.TRAVELER_X_IS_NO_BABY, [room, pax, tooOldAge])
24367
+ : format$1(translations.TRAVELERS_FORM.VALIDATION.SINGLE_ROOM_TRAVELER_X_IS_NO_BABY, [pax, tooOldAge]);
23590
24368
  };
23591
- const handleRemoveTraveler = (roomIndex, travelerIndex) => {
23592
- const rooms = [...formik.values.rooms];
23593
- const adults = [...rooms[roomIndex].adults];
23594
- if (adults.length <= 1)
23595
- return;
23596
- adults.splice(travelerIndex, 1);
23597
- rooms[roomIndex] = { ...rooms[roomIndex], adults };
23598
- formik.setFieldValue('rooms', rooms);
24369
+ const formatTravelerFieldInvalidCharacters = (room, pax, field) => {
24370
+ return values.rooms.length > 1
24371
+ ? format$1(translations.TRAVELERS_FORM.VALIDATION.TRAVELER_X_FIELD_INVALID_CHARACTERS, [room, pax, field])
24372
+ : format$1(translations.TRAVELERS_FORM.VALIDATION.SINGLE_ROOM_TRAVELER_X_FIELD_INVALID_CHARACTERS, [pax, field]);
23599
24373
  };
23600
- const handleAddRoom = () => {
23601
- const rooms = [...formik.values.rooms];
23602
- rooms.push({ adults: [{ id: Date.now(), firstName: '', lastName: '', birthDate: '', gender: '' }], children: [] });
23603
- formik.setFieldValue('rooms', rooms);
24374
+ const validateRequiredCustomFields = (traveler, path, room, pax) => {
24375
+ const isMainBooker = traveler.id === values.mainBookerId;
24376
+ const requiredFields = getApplicablePaxCustomFields(paxFields, isMainBooker).filter((f) => f.isRequired);
24377
+ requiredFields.forEach((field) => {
24378
+ const fieldIndex = traveler.customFields?.findIndex((c) => c.customFieldConfigurationId === field.customFieldConfigurationId) ?? -1;
24379
+ if (fieldIndex < 0)
24380
+ return;
24381
+ if (lodash.isEmpty(traveler.customFields[fieldIndex].value)) {
24382
+ lodash.set(errors, `${path}.customFields[${fieldIndex}].value`, formatTravelerField(room, pax, field.fieldName));
24383
+ }
24384
+ });
23604
24385
  };
23605
- const handleRemoveRoom = (roomIndex) => {
23606
- const rooms = [...formik.values.rooms];
23607
- rooms.splice(roomIndex, 1);
23608
- formik.setFieldValue('rooms', rooms);
24386
+ // Excludes the 4 core fields, which are already validated unconditionally below.
24387
+ const validateRequiredBuiltInFields = (traveler, path, room, pax) => {
24388
+ const isMainBooker = traveler.id === values.mainBookerId;
24389
+ const requiredFields = getApplicableBuiltInPaxFields(paxFields, isMainBooker).filter((f) => f.isRequired && !CORE_FIELD_NAMES.has(f.fieldName));
24390
+ requiredFields.forEach((field) => {
24391
+ const fieldPath = BUILT_IN_FIELD_PATHS[field.fieldName];
24392
+ if (!fieldPath)
24393
+ return;
24394
+ if (lodash.isEmpty(lodash.get(traveler, fieldPath))) {
24395
+ lodash.set(errors, `${path}.${fieldPath}`, formatTravelerField(room, pax, field.fieldName));
24396
+ }
24397
+ });
23609
24398
  };
23610
- const renderGenderControl = (name, value) => (React__default["default"].createElement("div", { className: buildClassName(['form__group', hasVisibleError(name) && 'form__group--error']) },
23611
- React__default["default"].createElement("label", { className: "form__label" },
23612
- translations.TRAVELERS_FORM.GENDER_ID,
23613
- " *"),
23614
- React__default["default"].createElement("div", { className: "radiobutton-group" }, [
23615
- ['m', translations.TRAVELERS_FORM.MALE_GENDER],
23616
- ['f', translations.TRAVELERS_FORM.FEMALE_GENDER]
23617
- ].map(([gender, label]) => (React__default["default"].createElement("div", { className: "radiobutton", key: gender },
23618
- React__default["default"].createElement("label", { className: "radiobutton__label" },
23619
- React__default["default"].createElement("input", { type: "radio", className: "radiobutton__input", name: name, onChange: formik.handleChange, onBlur: formik.handleBlur, value: gender, checked: value.gender === gender }),
23620
- label)))))));
23621
- const getControl = (type, value, name) => {
23622
- switch (type) {
23623
- case 'gender':
23624
- return React__default["default"].createElement(GenderControl, { translations: translations, value: value, formik: formik, name: name });
23625
- case 'firstName':
23626
- return (React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError(name), extraClassName: "form__group--md-33", label: translations.TRAVELERS_FORM.FIRST_NAME, required: true, name: name, onChange: formik.handleChange, onBlur: formik.handleBlur, value: value.firstName }));
23627
- case 'lastName':
23628
- return (React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError(name), extraClassName: "form__group--md-33", label: translations.TRAVELERS_FORM.LAST_NAME, required: true, name: name, onChange: formik.handleChange, onBlur: formik.handleBlur, value: value.lastName }));
23629
- case 'birthDate':
23630
- return (React__default["default"].createElement(LabeledInput, { type: "date", hasError: hasVisibleError(name), extraClassName: "form__group--md-33", label: translations.TRAVELERS_FORM.BIRTHDATE, required: true, name: name, onChange: formik.handleChange, onBlur: formik.handleBlur, value: value.birthDate }));
23631
- case 'country':
23632
- return (React__default["default"].createElement(LabeledSelect, { hasError: hasVisibleError('country'), label: translations.TRAVELERS_FORM.COUNTRY, required: true, name: "country", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.country, options: countryOptions }));
23633
- case 'phone':
23634
- return (React__default["default"].createElement(PhoneInput, { countries: travellersSettings?.countries ?? [], countryIso2: formik.values.country, hasError: hasVisibleError('phone'), label: translations.TRAVELERS_FORM.PHONE, required: true, name: "phone", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.phone }));
23635
- case 'email':
23636
- return (React__default["default"].createElement(React__default["default"].Fragment, null,
23637
- React__default["default"].createElement(LabeledInput, { type: "email", hasError: hasVisibleError('email'), extraClassName: "form__group--md-33", label: translations.TRAVELERS_FORM.EMAIL, required: true, name: "email", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.email }),
23638
- React__default["default"].createElement(LabeledInput, { type: "email", hasError: hasVisibleError('emailConfirmation'), extraClassName: "form__group--md-33", label: translations.TRAVELERS_FORM.REPEAT_EMAIL, required: true, name: "emailConfirmation", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.emailConfirmation })));
23639
- case 'street':
23640
- return (React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError('street'), extraClassName: "form__group--50 form__group--sm-60", label: translations.TRAVELERS_FORM.STREET, required: true, name: "street", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.street }));
23641
- case 'houseNumber':
23642
- return (React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError('houseNumber'), extraClassName: "form__group--30 form__group--sm-20", label: translations.TRAVELERS_FORM.HOUSE_NUMBER, required: true, name: "houseNumber", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.houseNumber }));
23643
- case 'box':
23644
- return (React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError('box'), extraClassName: "form__group--20", label: translations.TRAVELERS_FORM.POST_BOX, name: "box", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.box }));
23645
- case 'zipCode':
23646
- return (React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError('zipCode'), extraClassName: "form__group--40 form__group--sm-20", label: translations.TRAVELERS_FORM.ZIPCODE, required: true, name: "zipCode", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.zipCode }));
23647
- case 'place':
23648
- return (React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError('place'), extraClassName: "form__group--60 form__group--sm-40", label: translations.TRAVELERS_FORM.CITY, required: true, name: "place", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.place }));
23649
- default:
23650
- return null;
24399
+ values.rooms.forEach((r, rIndex) => r.adults.forEach((adult, index) => {
24400
+ if (lodash.isEmpty(adult.gender) && isFormFieldPresent('gender')) {
24401
+ lodash.set(errors, `rooms[${rIndex}].adults[${index}].gender`, formatTravelerField(rIndex + 1, index + 1, translations.TRAVELERS_FORM.GENDER));
23651
24402
  }
23652
- };
23653
- const renderRoomLabel = (room) => lodash.compact([
23654
- room.adults.length,
23655
- room.adults.length === 1 && ` ${translations.TRAVELERS_FORM.ADULT}`,
23656
- room.adults.length > 1 && ` ${translations.TRAVELERS_FORM.ADULTS}`,
23657
- room.adults?.length && room.children?.length && ', ',
23658
- room.children.length,
23659
- room.children.length === 1 && ` ${translations.TRAVELERS_FORM.CHILD}`,
23660
- room.children.length > 1 && ` ${translations.TRAVELERS_FORM.CHILDREN}`
23661
- ]).join('');
23662
- const renderTravelerFields = (travelerValues, namePrefix, isAdult, roomIndex, travelerIndex) => {
23663
- if (useCompactForm) {
23664
- return (React__default["default"].createElement("div", { className: "form__row" },
23665
- React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError(`${namePrefix}.age`), extraClassName: "form__group--md-33", label: translations.TRAVELERS_FORM.AGE, required: true, name: `${namePrefix}.age`, onChange: formik.handleChange, onBlur: formik.handleBlur, value: travelerValues.age })));
24403
+ if (isFormFieldPresent('firstName')) {
24404
+ if (lodash.isEmpty(adult.firstName)) {
24405
+ lodash.set(errors, `rooms[${rIndex}].adults[${index}].firstName`, formatTravelerField(rIndex + 1, index + 1, translations.TRAVELERS_FORM.FIRST_NAME));
24406
+ }
24407
+ else if (hasInvalidNameCharacters(adult.firstName)) {
24408
+ lodash.set(errors, `rooms[${rIndex}].adults[${index}].firstName`, formatTravelerFieldInvalidCharacters(rIndex + 1, index + 1, translations.TRAVELERS_FORM.FIRST_NAME));
24409
+ }
23666
24410
  }
23667
- if (travellersSettings?.formFields?.length) {
23668
- return (React__default["default"].createElement("div", { className: "travelers-form__grid" }, travellersSettings.formFields.map((field, index) => (React__default["default"].createElement("div", { key: index, className: `control control--${field.type}` }, getControl(field.type, travelerValues, `${namePrefix}.${field.type}`))))));
24411
+ if (isFormFieldPresent('lastName')) {
24412
+ if (lodash.isEmpty(adult.lastName)) {
24413
+ lodash.set(errors, `rooms[${rIndex}].adults[${index}].lastName`, formatTravelerField(rIndex + 1, index + 1, translations.TRAVELERS_FORM.LAST_NAME));
24414
+ }
24415
+ else if (hasInvalidNameCharacters(adult.lastName)) {
24416
+ lodash.set(errors, `rooms[${rIndex}].adults[${index}].lastName`, formatTravelerFieldInvalidCharacters(rIndex + 1, index + 1, translations.TRAVELERS_FORM.LAST_NAME));
24417
+ }
23669
24418
  }
23670
- return (React__default["default"].createElement(React__default["default"].Fragment, null,
23671
- React__default["default"].createElement("div", { className: "form__row" }, renderGenderControl(`${namePrefix}.gender`, travelerValues)),
23672
- React__default["default"].createElement("div", { className: "form__row" },
23673
- React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError(`${namePrefix}.firstName`), extraClassName: "form__group--md-33", label: translations.TRAVELERS_FORM.FIRST_NAME, required: true, name: `${namePrefix}.firstName`, onChange: formik.handleChange, onBlur: formik.handleBlur, value: travelerValues.firstName }),
23674
- React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError(`${namePrefix}.lastName`), extraClassName: "form__group--md-33", label: translations.TRAVELERS_FORM.LAST_NAME, required: true, name: `${namePrefix}.lastName`, onChange: formik.handleChange, onBlur: formik.handleBlur, value: travelerValues.lastName }),
23675
- React__default["default"].createElement(LabeledInput, { type: "date", hasError: hasVisibleError(`${namePrefix}.birthDate`), extraClassName: "form__group--md-33", label: translations.TRAVELERS_FORM.BIRTHDATE, required: true, name: `${namePrefix}.birthDate`, onChange: formik.handleChange, onBlur: formik.handleBlur, value: travelerValues.birthDate })),
23676
- travelersFirstStep && isAdult && formik.values.rooms[roomIndex].adults.length > 1 && (React__default["default"].createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => handleRemoveTraveler(roomIndex, travelerIndex) }, translations.TRAVELERS_FORM.REMOVE_TRAVELER))));
23677
- };
23678
- return (React__default["default"].createElement("form", { className: "form form__travelers", name: "booking--travellers", id: "booking--travellers", noValidate: true, onSubmit: formik.handleSubmit, onReset: formik.handleReset },
23679
- React__default["default"].createElement("div", { className: "form__travelers__wrapper" },
23680
- formik.values.rooms.map((room, roomIndex) => (React__default["default"].createElement("div", { key: roomIndex },
23681
- formik.values.rooms.length > 1 && (React__default["default"].createElement("div", { className: "form__region" },
23682
- React__default["default"].createElement("div", { className: "form__region-header" },
23683
- React__default["default"].createElement("h5", { className: "form__region-heading" },
23684
- translations.SHARED.ROOM,
23685
- " ",
23686
- roomIndex + 1),
23687
- React__default["default"].createElement("p", { className: "form__region-label" }, renderRoomLabel(room))),
23688
- !useCompactForm && travelersFirstStep && formik.values.rooms.length > 1 && (React__default["default"].createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => handleRemoveRoom(roomIndex) }, "Verwijder reisgezelschap")))),
23689
- room.adults.map((travelerValues, index) => (React__default["default"].createElement("div", { className: "form__region", key: travelerValues.id },
23690
- React__default["default"].createElement("div", { className: "form__region-header" },
23691
- React__default["default"].createElement("h5", { className: "form__region-heading" },
23692
- translations.TRAVELERS_FORM.TRAVELER,
23693
- " ",
23694
- index + 1),
23695
- React__default["default"].createElement("p", { className: "form__region-label" }, translations.TRAVELERS_FORM.ADULT),
23696
- React__default["default"].createElement("div", { className: "radiobutton" },
23697
- React__default["default"].createElement("label", { className: "radiobutton__label" },
23698
- React__default["default"].createElement("input", { type: "radio", name: "mainBookerId", onChange: handleMainBookerChange, onBlur: formik.handleBlur, value: travelerValues.id, checked: formik.values.mainBookerId === travelerValues.id, className: "radiobutton__input" }),
23699
- translations.TRAVELERS_FORM.MAIN_BOOKER))),
23700
- renderTravelerFields(travelerValues, `rooms[${roomIndex}].adults[${index}]`, true, roomIndex, index)))),
23701
- room.children.map((travelerValues, index) => (React__default["default"].createElement("div", { className: "form__region", key: travelerValues.id },
23702
- React__default["default"].createElement("div", { className: "form__region-header" },
23703
- React__default["default"].createElement("h5", { className: "form__region-heading" },
23704
- translations.TRAVELERS_FORM.TRAVELER,
23705
- " ",
23706
- room.adults.length + index + 1),
23707
- React__default["default"].createElement("p", { className: "form__region-label" }, travelerValues.isBaby ? translations.TRAVELERS_FORM.BABY : translations.TRAVELERS_FORM.CHILD)),
23708
- renderTravelerFields(travelerValues, `rooms[${roomIndex}].children[${index}]`, false, roomIndex, index)))),
23709
- !useCompactForm && travelersFirstStep && (React__default["default"].createElement("div", { className: "form__region" },
23710
- React__default["default"].createElement("button", { type: "button", className: "cta cta--select", onClick: () => handleAddTraveler(roomIndex) }, translations.TRAVELERS_FORM.ADD_TRAVELER)))))),
23711
- !useCompactForm && (bookingType !== 'b2b' || travellersSettings?.mainBookerFormFields?.length) ? (React__default["default"].createElement("div", { className: "form__region" },
23712
- React__default["default"].createElement("div", { className: "form__region-header" },
23713
- React__default["default"].createElement("h5", { className: "form__region-heading" }, translations.TRAVELERS_FORM.MAIN_BOOKER),
23714
- React__default["default"].createElement("p", { className: "form__region-label" }, lodash.compact([lodash.compact([mainBooker?.firstName, mainBooker?.lastName]).join(' '), formatBirthDate$1(mainBooker?.birthDate)]).join(', '))),
23715
- travellersSettings?.mainBookerFormFields?.length ? (React__default["default"].createElement("div", { className: "main-booker-form__grid" }, travellersSettings.mainBookerFormFields.map((field, index) => (React__default["default"].createElement("div", { key: index, className: `control control--${field.type}` }, getControl(field.type, {}, field.type)))))) : (React__default["default"].createElement(React__default["default"].Fragment, null,
23716
- React__default["default"].createElement("div", { className: "form__twocolumn" },
23717
- React__default["default"].createElement("div", { className: "form__twocolumn-column" },
23718
- React__default["default"].createElement("div", { className: "form__row" },
23719
- React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError('street'), extraClassName: "form__group--50 form__group--sm-60", label: translations.TRAVELERS_FORM.STREET, required: true, name: "street", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.street }),
23720
- React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError('houseNumber'), extraClassName: "form__group--30 form__group--sm-20", label: translations.TRAVELERS_FORM.HOUSE_NUMBER, required: true, name: "houseNumber", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.houseNumber }),
23721
- React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError('box'), extraClassName: "form__group--20", label: translations.TRAVELERS_FORM.POST_BOX, name: "box", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.box }))),
23722
- React__default["default"].createElement("div", { className: "form__twocolumn-column" },
23723
- React__default["default"].createElement("div", { className: "form__row" },
23724
- React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError('zipCode'), extraClassName: "form__group--40 form__group--sm-20", label: translations.TRAVELERS_FORM.ZIPCODE, required: true, name: "zipCode", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.zipCode }),
23725
- React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError('place'), extraClassName: "form__group--60 form__group--sm-40", label: translations.TRAVELERS_FORM.CITY, required: true, name: "place", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.place }),
23726
- React__default["default"].createElement(LabeledSelect, { hasError: hasVisibleError('country'), extraClassName: "form__group--sm-40", label: translations.TRAVELERS_FORM.COUNTRY, required: true, name: "country", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.country, options: countryOptions })))),
23727
- React__default["default"].createElement("div", { className: "form__row" },
23728
- React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError('phone'), extraClassName: "form__group--md-33", label: translations.TRAVELERS_FORM.PHONE, required: true, name: "phone", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.phone }),
23729
- React__default["default"].createElement(LabeledInput, { type: "email", hasError: hasVisibleError('email'), extraClassName: "form__group--md-33", label: translations.TRAVELERS_FORM.EMAIL, required: true, name: "email", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.email }),
23730
- React__default["default"].createElement(LabeledInput, { type: "email", hasError: hasVisibleError('emailConfirmation'), extraClassName: "form__group--md-33", label: translations.TRAVELERS_FORM.REPEAT_EMAIL, required: true, name: "emailConfirmation", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.emailConfirmation })))))) : !useCompactForm ? (React__default["default"].createElement("div", { className: "form__region" },
23731
- React__default["default"].createElement("div", { className: "form__row" },
23732
- React__default["default"].createElement(LabeledInput, { hasError: hasVisibleError('phone'), extraClassName: "form__group--md-33", label: translations.TRAVELERS_FORM.PHONE, required: true, name: "phone", onChange: formik.handleChange, onBlur: formik.handleBlur, value: formik.values.phone })))) : null,
23733
- !useCompactForm && showAgentSelection && (React__default["default"].createElement("div", { className: "form__region" },
23734
- React__default["default"].createElement("div", { className: "form__region-header" },
23735
- React__default["default"].createElement("h5", { className: "form__region-heading" }, translations.TRAVELERS_FORM.BOOK_WITH_AGENT),
23736
- React__default["default"].createElement("div", { className: "checkbox", id: "cbxChooseOffice" },
23737
- React__default["default"].createElement("label", { className: "checkbox__label" },
23738
- React__default["default"].createElement("input", { type: "checkbox", name: "booking--mainbooker", checked: showAgents, onChange: () => toggleAgent(!showAgents), className: "checkbox__input" }),
23739
- translations.TRAVELERS_FORM.CHOOSE_OFFICE))),
23740
- showAgents && (React__default["default"].createElement("div", { className: "form__row form__row--choose-office" },
23741
- React__default["default"].createElement("div", { className: buildClassName(['form__group', 'form__group--icon', hasVisibleError('travelAgentId') && 'form__group--error']) },
23742
- React__default["default"].createElement(TypeAheadInput, { value: formik.values.travelAgentName, options: filteredAgents, onChange: handleAgentChange, onSelect: handleAgentSelect, onClear: handleAgentClear, name: "travelAgentName", placeholder: translations.TRAVELERS_FORM.CHOOSE_AGENT_PLACEHOLDER }))))))),
23743
- !useCompactForm && errorKeys.length > 0 && (React__default["default"].createElement("div", { className: "form__region form__region--errors" },
23744
- React__default["default"].createElement("div", { className: "form__row" },
23745
- React__default["default"].createElement("div", { className: "form__group" },
23746
- React__default["default"].createElement("p", { className: "form__error-heading" },
23747
- translations.TRAVELERS_FORM.VALIDATION_MESSAGE,
23748
- ":"),
23749
- React__default["default"].createElement("ul", { className: "list" }, errorKeys.map((key) => (React__default["default"].createElement("li", { key: key }, lodash.get(flatErrors, key))))))))),
23750
- travelersFirstStep && (React__default["default"].createElement("div", { className: "booking__navigator" },
23751
- React__default["default"].createElement("button", { type: "button", className: "cta cta--select", onClick: handleAddRoom }, translations.TRAVELERS_FORM.ADD_ROOM))),
23752
- React__default["default"].createElement("div", { className: "booking__navigator" },
23753
- renderPreviousButton?.(),
23754
- React__default["default"].createElement("button", { type: "submit", title: translations.STEPS.NEXT, className: 'cta' + (isUnavailable ? ' cta--disabled' : '') }, translations.STEPS.NEXT))));
24419
+ if (isFormFieldPresent('birthDate')) {
24420
+ if (lodash.isEmpty(adult.birthDate) || !isValidBirthDate(adult.birthDate)) {
24421
+ lodash.set(errors, `rooms[${rIndex}].adults[${index}].birthDate`, formatTravelerField(rIndex + 1, index + 1, translations.TRAVELERS_FORM.BIRTHDATE));
24422
+ }
24423
+ else if (values.endDate) {
24424
+ // If you are younger than the maxChildAge at the end of the trip, you are considered a child, so we check the endDate here instead of the startDate
24425
+ const age = getAge(adult.birthDate, values.endDate);
24426
+ if (age <= maxChildAge) {
24427
+ lodash.set(errors, `rooms[${rIndex}].adults[${index}].birthDate`, formatTravelerIsNoAdult(rIndex + 1, index + 1));
24428
+ }
24429
+ }
24430
+ }
24431
+ validateRequiredCustomFields(adult, `rooms[${rIndex}].adults[${index}]`, rIndex + 1, index + 1);
24432
+ validateRequiredBuiltInFields(adult, `rooms[${rIndex}].adults[${index}]`, rIndex + 1, index + 1);
24433
+ }));
24434
+ values.rooms.forEach((r, rIndex) => r.children.forEach((child, index) => {
24435
+ if (lodash.isEmpty(child.gender) && isFormFieldPresent('gender')) {
24436
+ lodash.set(errors, `rooms[${rIndex}].children[${index}].gender`, formatTravelerField(rIndex + 1, r.adults.length + index + 1, translations.TRAVELERS_FORM.GENDER));
24437
+ }
24438
+ if (isFormFieldPresent('firstName')) {
24439
+ if (lodash.isEmpty(child.firstName)) {
24440
+ lodash.set(errors, `rooms[${rIndex}].children[${index}].firstName`, formatTravelerField(rIndex + 1, r.adults.length + index + 1, translations.TRAVELERS_FORM.FIRST_NAME));
24441
+ }
24442
+ else if (hasInvalidNameCharacters(child.firstName)) {
24443
+ lodash.set(errors, `rooms[${rIndex}].children[${index}].firstName`, formatTravelerFieldInvalidCharacters(rIndex + 1, r.adults.length + index + 1, translations.TRAVELERS_FORM.FIRST_NAME));
24444
+ }
24445
+ }
24446
+ if (isFormFieldPresent('lastName')) {
24447
+ if (lodash.isEmpty(child.lastName)) {
24448
+ lodash.set(errors, `rooms[${rIndex}].children[${index}].lastName`, formatTravelerField(rIndex + 1, r.adults.length + index + 1, translations.TRAVELERS_FORM.LAST_NAME));
24449
+ }
24450
+ else if (hasInvalidNameCharacters(child.lastName)) {
24451
+ lodash.set(errors, `rooms[${rIndex}].children[${index}].lastName`, formatTravelerFieldInvalidCharacters(rIndex + 1, r.adults.length + index + 1, translations.TRAVELERS_FORM.LAST_NAME));
24452
+ }
24453
+ }
24454
+ if (isFormFieldPresent('birthDate')) {
24455
+ if (lodash.isEmpty(child.birthDate) || !isValidBirthDate(child.birthDate)) {
24456
+ lodash.set(errors, `rooms[${rIndex}].children[${index}].birthDate`, formatTravelerField(rIndex + 1, r.adults.length + index + 1, translations.TRAVELERS_FORM.BIRTHDATE));
24457
+ }
24458
+ else if (child.isBaby && values.startDate) {
24459
+ // A baby only has to be under the maxBabyAge on departure, so we check the startDate here instead of the endDate (TIDE-3269)
24460
+ const age = getAge(child.birthDate, values.startDate);
24461
+ if (age > maxBabyAge) {
24462
+ lodash.set(errors, `rooms[${rIndex}].children[${index}].birthDate`, formatTravelerIsNoBaby(rIndex + 1, r.adults.length + index + 1));
24463
+ }
24464
+ }
24465
+ else if (values.endDate) {
24466
+ // You need to be under the maxChildAge until the end of the trip, so we check the endDate here instead of the startDate.
24467
+ // Babies land here too when there is no startDate to check them against, so they keep at least the child age check.
24468
+ const age = getAge(child.birthDate, values.endDate);
24469
+ if (age > maxChildAge) {
24470
+ lodash.set(errors, `rooms[${rIndex}].children[${index}].birthDate`, formatTravelerIsNoChild(rIndex + 1, r.adults.length + index + 1));
24471
+ }
24472
+ }
24473
+ }
24474
+ validateRequiredCustomFields(child, `rooms[${rIndex}].children[${index}]`, rIndex + 1, r.adults.length + index + 1);
24475
+ validateRequiredBuiltInFields(child, `rooms[${rIndex}].children[${index}]`, rIndex + 1, r.adults.length + index + 1);
24476
+ }));
24477
+ if (values.mainBookerId < 0) {
24478
+ errors.mainBookerId = translations.TRAVELERS_FORM.VALIDATION.NO_MAIN_BOOKER_SELECTED;
24479
+ }
24480
+ if (bookingType != 'b2b' || mainBookerFormFields?.length) {
24481
+ if (lodash.isEmpty(values.street) && isMainBookerFormFieldPresent('street')) {
24482
+ errors.street = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.STREET]);
24483
+ }
24484
+ if (lodash.isEmpty(values.houseNumber) && isMainBookerFormFieldPresent('houseNumber')) {
24485
+ errors.houseNumber = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.HOUSE_NUMBER]);
24486
+ }
24487
+ if (lodash.isEmpty(values.zipCode) && isMainBookerFormFieldPresent('zipCode')) {
24488
+ errors.zipCode = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.ZIPCODE]);
24489
+ }
24490
+ if (lodash.isEmpty(values.place) && isMainBookerFormFieldPresent('place')) {
24491
+ errors.place = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.CITY]);
24492
+ }
24493
+ if (isMainBookerFormFieldPresent('email')) {
24494
+ if (lodash.isEmpty(values.email)) {
24495
+ errors.email = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.EMAIL]);
24496
+ }
24497
+ else if (isValidEmail(values.email)) {
24498
+ errors.email = translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_EMAIL_IS_INVALID;
24499
+ }
24500
+ if (lodash.isEmpty(values.emailConfirmation)) {
24501
+ errors.emailConfirmation = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.REPEAT_EMAIL]);
24502
+ }
24503
+ else if (values.emailConfirmation !== values.email) {
24504
+ errors.emailConfirmation = translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_EMAIL_DOES_NOT_MATCH;
24505
+ }
24506
+ }
24507
+ if (lodash.isEmpty(values.country) && isMainBookerFormFieldPresent('country')) {
24508
+ errors.country = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.COUNTRY]);
24509
+ }
24510
+ if (lodash.isEmpty(values.phone) && isMainBookerFormFieldPresent('phone')) {
24511
+ errors.phone = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.PHONE]);
24512
+ }
24513
+ }
24514
+ else {
24515
+ if (lodash.isEmpty(values.phone)) {
24516
+ errors.phone = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.PHONE]);
24517
+ }
24518
+ }
24519
+ if (agentRequired && !values.travelAgentId) {
24520
+ errors.travelAgentId = translations.TRAVELERS_FORM.VALIDATION.AGENT_IS_REQUIRED;
24521
+ }
24522
+ return errors;
23755
24523
  };
23756
24524
 
23757
24525
  const TravelersForm = () => {
@@ -23772,12 +24540,13 @@ const TravelersForm = () => {
23772
24540
  const bookingAttributes = reactRedux.useSelector(selectBookingAttributes);
23773
24541
  const hasMounted = reactRedux.useSelector(selectHasMounted);
23774
24542
  const countries = reactRedux.useSelector(selectCountries);
24543
+ const paxFields = reactRedux.useSelector(selectPaxFields);
23775
24544
  const inFlight = React.useRef(false);
23776
24545
  const pendingRefetch = React.useRef(false);
23777
24546
  const useCompactForm = !!settings.travellers.compactForm && !!settings.agentAdressId;
23778
24547
  const showAllCountries = !!settings.travellers.showAllCountries;
23779
24548
  const initialValues = reactRedux.useSelector(selectTravelersFormValues) ??
23780
- createInitialValuesFromRooms(formRooms, startDate, endDate, agentAdressId, translations.TRAVELERS_FORM.PERSON, useCompactForm, settings.maxBabyAge);
24549
+ createInitialValuesFromRooms(formRooms, startDate, endDate, agentAdressId, translations.TRAVELERS_FORM.PERSON, useCompactForm, settings.maxBabyAge, paxFields);
23781
24550
  const [showAgentSelection, setShowAgentSelection] = React.useState(!settings.agentAdressId && !settings.hideAgentSelection);
23782
24551
  const formik$1 = formik.useFormik({
23783
24552
  initialValues,
@@ -23787,7 +24556,7 @@ const TravelersForm = () => {
23787
24556
  // The travel dates are captured into initialValues once and then persisted in
23788
24557
  // redux, so validating against the values would use the dates of whichever
23789
24558
  // package option was selected first. Age checks need the current ones.
23790
- { ...values, startDate, endDate }, settings.agentRequired, bookingType, translations, settings.travellers.formFields, settings.travellers.mainBookerFormFields, settings.maxChildAge, settings.maxBabyAge),
24559
+ { ...values, startDate, endDate }, settings.agentRequired, bookingType, translations, settings.travellers.formFields, settings.travellers.mainBookerFormFields, settings.maxChildAge, settings.maxBabyAge, paxFields),
23791
24560
  onSubmit: (values) => {
23792
24561
  dispatch(setFormValues(values));
23793
24562
  if (useCompactForm) {
@@ -23870,7 +24639,7 @@ const TravelersForm = () => {
23870
24639
  dispatch(setFormValues(formValues));
23871
24640
  setShowAgentSelection(false);
23872
24641
  }, [agents, settings.affiliateSlug]);
23873
- return (React__default["default"].createElement(SharedTravelersForm, { formik: formik$1, translations: translations, travellersSettings: settings.travellers, countries: countries, agents: agents, bookingType: bookingType, agentAdressId: agentAdressId, travelersFirstStep: travelersFirstStep, isUnavailable: isUnavailable, useCompactForm: useCompactForm, showAllCountries: showAllCountries, showAgentSelection: showAgentSelection, initialShowAgents: settings.agentRequired ?? false, onBookingTypeChange: (nextBookingType) => dispatch(setBookingType(nextBookingType)), renderPreviousButton: () => !travelersFirstStep && settings.skipRouter ? (React__default["default"].createElement("button", { type: "button", title: translations.STEPS.PREVIOUS, onClick: () => dispatch(setCurrentStep$1(OPTIONS_FORM_STEP)), className: "cta cta--secondary" }, translations.STEPS.PREVIOUS)) : !travelersFirstStep ? (React__default["default"].createElement(Link, { to: `${!settings.skipBasePathInRouting ? settings.basePath : ''}${settings.options.pathSuffix}?${bookingQueryString}`, title: translations.STEPS.PREVIOUS, className: "cta cta--secondary" }, translations.STEPS.PREVIOUS)) : null }));
24642
+ return (React__default["default"].createElement(SharedTravelersForm, { formik: formik$1, translations: translations, travellersSettings: settings.travellers, countries: countries, agents: agents, bookingType: bookingType, agentAdressId: agentAdressId, travelersFirstStep: travelersFirstStep, isUnavailable: isUnavailable, useCompactForm: useCompactForm, showAllCountries: showAllCountries, paxFields: paxFields, showAgentSelection: showAgentSelection, initialShowAgents: settings.agentRequired ?? false, onBookingTypeChange: (nextBookingType) => dispatch(setBookingType(nextBookingType)), renderPreviousButton: () => !travelersFirstStep && settings.skipRouter ? (React__default["default"].createElement("button", { type: "button", title: translations.STEPS.PREVIOUS, onClick: () => dispatch(setCurrentStep$1(OPTIONS_FORM_STEP)), className: "cta cta--secondary" }, translations.STEPS.PREVIOUS)) : !travelersFirstStep ? (React__default["default"].createElement(Link, { to: `${!settings.skipBasePathInRouting ? settings.basePath : ''}${settings.options.pathSuffix}?${bookingQueryString}`, title: translations.STEPS.PREVIOUS, className: "cta cta--secondary" }, translations.STEPS.PREVIOUS)) : null }));
23874
24643
  };
23875
24644
 
23876
24645
  const Booking = ({ productCode, productName, thumbnailUrl }) => {
@@ -27942,7 +28711,12 @@ const mapPackagingPaxToBookingPax = (pax, fallbackId, referenceDate) => ({
27942
28711
  dateOfBirth: pax?.dateOfBirth ?? undefined,
27943
28712
  age: pax?.dateOfBirth ? getAgeAtReferenceDate(pax.dateOfBirth, referenceDate) : undefined,
27944
28713
  isMainBooker: pax?.isMainBooker,
27945
- email: ''
28714
+ email: '',
28715
+ weight: null,
28716
+ weightType: build.WeightType.kilograms,
28717
+ height: null,
28718
+ heightType: build.HeightType.centimeters,
28719
+ customFields: []
27946
28720
  });
27947
28721
  const getRequestRoomsFromPackagingEntry = (entry) => {
27948
28722
  const accommodationLines = (entry.lines ?? []).filter((line) => line.serviceType === ACCOMMODATION_SERVICE_TYPE && line.pax?.length > 0);
@@ -28012,6 +28786,22 @@ const getPackagingRequestRoomsFromBookingRooms = (rooms) => {
28012
28786
  });
28013
28787
  };
28014
28788
 
28789
+ // Administrative service lines (fees, insurances, reductions, ...) live on the entry but say
28790
+ // nothing about the journey itself, so the itinerary never draws them.
28791
+ const HIDDEN_ITINERARY_SERVICE_TYPES = new Set([
28792
+ build.ServiceType.insurance,
28793
+ build.ServiceType.extraService,
28794
+ build.ServiceType.serviceFee,
28795
+ build.ServiceType.gift,
28796
+ build.ServiceType.reduction,
28797
+ build.ServiceType.fee,
28798
+ build.ServiceType.visum,
28799
+ build.ServiceType.voucher,
28800
+ build.ServiceType.specialServices
28801
+ ]);
28802
+ const isMeaningfulItineraryLine = (line) => {
28803
+ return !HIDDEN_ITINERARY_SERVICE_TYPES.has(line.serviceType ?? -1);
28804
+ };
28015
28805
  const getFlightLines = (flight) => {
28016
28806
  return flight?.flightInformation?.flightLines ?? [];
28017
28807
  };
@@ -28466,6 +29256,8 @@ const applyResultToLine = (line, result, roomIndex = 0) => {
28466
29256
  isChanged: true
28467
29257
  };
28468
29258
  };
29259
+ const buildNodeSelectionLines = (item, result, bookedMode) => bookedMode && matchesCode(result, item.node.code ?? '') ? item.lines : applyResultToLines(item.lines, result);
29260
+ const buildFlightSelectionLine = (item, flight, bookedMode) => bookedMode && flight.outwardGuid === item.line.guid ? item.line : applyFlightToLine(item.line, flight);
28469
29261
  const toDateOnlyUtcString = (value) => {
28470
29262
  const date = new Date(value);
28471
29263
  return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate())).toISOString();
@@ -28523,11 +29315,21 @@ const buildTrajectFlightRequest = ({ node, line }, context, pax) => ({
28523
29315
  * mapper gave them. This swaps in the guid of whatever each line resolved onto, leaving the
28524
29316
  * lines the traveller added themselves alone — those were built from a search result and already
28525
29317
  * carry the right one.
29318
+ *
29319
+ * A booked entry (the traject was rebuilt from a magic-link entry) is the other way around: its
29320
+ * lines live in the dossier under their own guids, and only a line the traveller actually swapped
29321
+ * (isChanged) exists in the search cache. Swapping an untouched line's guid would orphan it —
29322
+ * the backend drops the dossier line and finds nothing to add — so those keep theirs.
28526
29323
  */
28527
29324
  const withCachedEntryLineGuids = (entry, trajectEntry, nodeAvailability, flightAvailability) => {
28528
29325
  const cachedGuids = new Map();
29326
+ const bookedMode = !!trajectEntry.entry.magicLink;
29327
+ const editableByGuid = new Map((entry.lines ?? []).map((line) => [line.guid, line]));
29328
+ const keepsOwnGuid = (guid) => bookedMode && !editableByGuid.get(guid)?.isChanged;
28529
29329
  getTrajectNodesWithLines(trajectEntry).forEach((item) => {
28530
29330
  if (isTrajectFlightNode(item.node)) {
29331
+ if (keepsOwnGuid(item.line.guid))
29332
+ return;
28531
29333
  // A flight search caches the outward line under the guid it reports as outwardGuid.
28532
29334
  const guid = flightAvailability[item.line.guid]?.selectedGuid;
28533
29335
  if (guid)
@@ -28540,6 +29342,8 @@ const withCachedEntryLineGuids = (entry, trajectEntry, nodeAvailability, flightA
28540
29342
  return;
28541
29343
  // Room i took option i when the result was stamped on, so it takes option i here too.
28542
29344
  item.lines.forEach((line, index) => {
29345
+ if (keepsOwnGuid(line.guid))
29346
+ return;
28543
29347
  const guid = getOptionForRoom(result, index)?.guid;
28544
29348
  if (guid)
28545
29349
  cachedGuids.set(line.guid, guid);
@@ -28603,7 +29407,7 @@ const TrajectItinerary = ({ trajectEntry, packagingEntry, translations }) => {
28603
29407
  return { node, lines };
28604
29408
  });
28605
29409
  const claimed = new Set(nodes.flatMap((item) => item.lines.map((line) => line.guid)));
28606
- const extras = (packagingEntry.lines ?? []).filter((line) => !claimed.has(line.guid));
29410
+ const extras = (packagingEntry.lines ?? []).filter((line) => !claimed.has(line.guid)).filter(isMeaningfulItineraryLine);
28607
29411
  if (!extras.length)
28608
29412
  return nodes;
28609
29413
  const dayOrderOf = (date) => trajectEntry.traject.days.find((day) => dateFns.isSameDay(new Date(day.date), new Date(date)))?.order ??
@@ -28780,6 +29584,195 @@ const TrajectItinerary = ({ trajectEntry, packagingEntry, translations }) => {
28780
29584
  })));
28781
29585
  };
28782
29586
 
29587
+ /**
29588
+ * A booked entry opened through its magic link knows nothing about the traject it may once have
29589
+ * been composed from — the dossier only keeps the lines. To give it the same day-by-day services
29590
+ * screen a traject gets, the traject structure is rebuilt from those lines: every booked service
29591
+ * becomes a node whose one "alternative" is the booked product itself, and the days span the trip.
29592
+ *
29593
+ * The synthesized response keeps the entry untouched (magic link included), which is also how the
29594
+ * rest of the component tells this mode apart from a real traject: see isBookedEntryTraject.
29595
+ */
29596
+ const isBookedEntryTraject = (trajectEntry) => !!trajectEntry?.entry?.magicLink;
29597
+ const NODE_SERVICE_TYPES = new Set([ACCOMMODATION_SERVICE_TYPE, EXCURSION_SERVICE_TYPE, FLIGHT_SERVICE_TYPE]);
29598
+ /**
29599
+ * A room or option line joins its parent's node; a flight line never does — an outward and its
29600
+ * return are separate legs of the trip even though the return points at the outward as parent.
29601
+ */
29602
+ const groupNodeLines = (lines) => {
29603
+ const groups = [];
29604
+ const groupByGuid = new Map();
29605
+ lines.forEach((line) => {
29606
+ const parentGroup = line.serviceType !== FLIGHT_SERVICE_TYPE && line.parentGuid ? groupByGuid.get(line.parentGuid) : undefined;
29607
+ if (parentGroup) {
29608
+ parentGroup.push(line);
29609
+ return;
29610
+ }
29611
+ const group = [line];
29612
+ groups.push(group);
29613
+ groupByGuid.set(line.guid, group);
29614
+ });
29615
+ return groups;
29616
+ };
29617
+ const buildBookedTrajectEntry = (entry) => {
29618
+ const lines = (entry.lines ?? []).filter((line) => NODE_SERVICE_TYPES.has(line.serviceType));
29619
+ const froms = lines.map((line) => new Date(line.from).getTime());
29620
+ const tos = lines.map((line) => new Date(line.to).getTime());
29621
+ const start = froms.length ? new Date(Math.min(...froms)) : new Date();
29622
+ const end = tos.length ? new Date(Math.max(...tos)) : start;
29623
+ const dayCount = Math.max(dateFns.differenceInCalendarDays(end, start), 0) + 1;
29624
+ const days = Array.from({ length: dayCount }, (_, order) => ({
29625
+ order,
29626
+ name: '',
29627
+ date: toDateOnlyString(dateFns.addDays(start, order))
29628
+ }));
29629
+ const nodes = groupNodeLines(lines).map((groupLines, index) => {
29630
+ const line = groupLines[0];
29631
+ const flightLines = line.flightInformation?.flightLines ?? [];
29632
+ return {
29633
+ lineGuid: line.guid,
29634
+ lineGuids: groupLines.map((x) => x.guid),
29635
+ nodeId: index + 1,
29636
+ dayOrder: Math.max(dateFns.differenceInCalendarDays(new Date(line.from), start), 0),
29637
+ order: line.order,
29638
+ name: line.productName,
29639
+ serviceType: line.serviceType,
29640
+ trajectNodeType: REGULAR_TRAJECT_NODE_TYPE,
29641
+ requirementType: 0,
29642
+ contentSource: 0,
29643
+ code: line.productCode,
29644
+ preferredAccommodationCode: line.productCode,
29645
+ preferredAccommodationName: line.productName,
29646
+ alternatives: [],
29647
+ departureAirportCode: lodash.first(flightLines)?.departureAirportCode ?? null,
29648
+ arrivalAirportCode: lodash.last(flightLines)?.arrivalAirportCode ?? null,
29649
+ duration: 0,
29650
+ durationType: 0,
29651
+ from: line.from,
29652
+ to: line.to
29653
+ };
29654
+ });
29655
+ return {
29656
+ entry,
29657
+ traject: {
29658
+ id: 0,
29659
+ name: entry.dossierNumber ?? '',
29660
+ imageUrl: null,
29661
+ startDate: days[0]?.date ?? toDateOnlyString(start),
29662
+ endDate: lodash.last(days)?.date ?? toDateOnlyString(end),
29663
+ durationInDays: days.length,
29664
+ includeGroupHotelAlternatives: false,
29665
+ days
29666
+ },
29667
+ nodes
29668
+ };
29669
+ };
29670
+ /**
29671
+ * The card for a booked service when no search result carries it any more (sold out, delisted).
29672
+ * Rendered from the line, so the traveller still sees what they booked; price 0 makes the card
29673
+ * leave its price out — the paid price lives in the price details, not on the line.
29674
+ */
29675
+ const buildBookedNodeResult = (node, lines) => {
29676
+ const line = lines[0];
29677
+ return {
29678
+ code: line.productCode,
29679
+ name: line.productName,
29680
+ serviceType: line.serviceType,
29681
+ countryId: line.country?.id ?? null,
29682
+ countryName: line.country?.name ?? '',
29683
+ regionId: line.region?.id ?? null,
29684
+ regionName: line.region?.name ?? '',
29685
+ oordId: line.oord?.id ?? null,
29686
+ oordName: line.oord?.name ?? '',
29687
+ locationId: line.location?.id ?? null,
29688
+ locationName: line.location?.name ?? '',
29689
+ latitude: line.latitude ?? null,
29690
+ longitude: line.longitude ?? null,
29691
+ price: 0,
29692
+ averagePricePerPerson: 0,
29693
+ originalPrice: 0,
29694
+ originalAveragePricePerPerson: 0,
29695
+ currencyCode: 'EUR',
29696
+ fromDate: line.from,
29697
+ toDate: line.to,
29698
+ contents: '',
29699
+ rooms: lines.map((roomLine) => ({
29700
+ options: [
29701
+ {
29702
+ price: 0,
29703
+ originalPrice: 0,
29704
+ accommodationCode: roomLine.accommodationCode ?? '',
29705
+ accommodationName: roomLine.accommodationName ?? '',
29706
+ regimeCode: roomLine.regimeCode ?? '',
29707
+ regimeName: roomLine.regimeName ?? '',
29708
+ isSelected: true,
29709
+ guid: roomLine.guid,
29710
+ paxIds: (roomLine.pax ?? []).map((pax) => pax.paxId)
29711
+ }
29712
+ ]
29713
+ }))
29714
+ };
29715
+ };
29716
+ /**
29717
+ * The flight search API serializes these as ISO strings, whatever the type declaration says, and
29718
+ * the store must stay serializable — so the synthesized segments carry the same string form.
29719
+ */
29720
+ const toDateTime = (date, time) => `${toDateOnlyString(date)}T${time || '00:00:00'}`;
29721
+ /** The booked flight as a flight result, so the card that shows it is the card the search results use. */
29722
+ const buildBookedFlightResult = (line) => {
29723
+ const flightLines = line.flightInformation?.flightLines ?? [];
29724
+ const segments = flightLines.map((flightLine, index) => ({
29725
+ order: index,
29726
+ flightNumber: flightLine.flightNumber,
29727
+ operatingFlightNumber: flightLine.operatingFlightNumber ?? flightLine.flightNumber,
29728
+ marketingAirlineCode: flightLine.airlineCode,
29729
+ marketingAirlineName: flightLine.airlineDescription,
29730
+ operatingAirlineCode: flightLine.operatingAirlineCode ?? flightLine.airlineCode,
29731
+ operatingAirlineName: flightLine.operatingAirlineDescription ?? flightLine.airlineDescription,
29732
+ departureAirportCode: flightLine.departureAirportCode,
29733
+ departureAirportName: flightLine.departureAirportDescription,
29734
+ arrivalAirportCode: flightLine.arrivalAirportCode,
29735
+ arrivalAirportName: flightLine.arrivalAirportDescription,
29736
+ departureDateTime: toDateTime(flightLine.departureDate, flightLine.departureTime),
29737
+ arrivalDateTime: toDateTime(flightLine.arrivalDate, flightLine.arrivalTime),
29738
+ bookingClassCode: '',
29739
+ travelClass: 0,
29740
+ durationInTicks: flightLine.durationInTicks ?? 0
29741
+ }));
29742
+ const firstSegment = lodash.first(segments);
29743
+ const lastSegment = lodash.last(segments);
29744
+ // .NET ticks are 100ns, so a millisecond is 10 000 of them.
29745
+ const durationInTicks = firstSegment && lastSegment ? (new Date(lastSegment.arrivalDateTime).getTime() - new Date(firstSegment.departureDateTime).getTime()) * 10_000 : 0;
29746
+ return {
29747
+ code: line.productCode,
29748
+ name: line.productName,
29749
+ serviceType: line.serviceType,
29750
+ countryName: '',
29751
+ regionName: '',
29752
+ oordName: '',
29753
+ locationName: '',
29754
+ price: 0,
29755
+ averagePricePerPerson: 0,
29756
+ originalPrice: 0,
29757
+ originalAveragePricePerPerson: 0,
29758
+ currencyCode: 'EUR',
29759
+ fromDate: line.from,
29760
+ toDate: line.to,
29761
+ flightRouteId: line.guid,
29762
+ airlineCode: firstSegment?.marketingAirlineCode ?? '',
29763
+ airlineName: firstSegment?.marketingAirlineName ?? '',
29764
+ isLuggageIncluded: false,
29765
+ luggageInfo: '',
29766
+ outwardGuid: line.guid,
29767
+ outward: { code: line.productCode, fareType: 0, durationInTicks, segments },
29768
+ returnGuid: '',
29769
+ return: { code: '', fareType: 0, durationInTicks: 0, segments: [] },
29770
+ source: 0,
29771
+ provider: '',
29772
+ paxes: []
29773
+ };
29774
+ };
29775
+
28783
29776
  const getSegmentIcon = (segment) => {
28784
29777
  switch (segment.serviceType) {
28785
29778
  case 3:
@@ -28819,14 +29812,14 @@ const SERVICE_TYPE_PRIORITY = {
28819
29812
  const getServiceTypePriority = (serviceType) => {
28820
29813
  return SERVICE_TYPE_PRIORITY[serviceType ?? -1] ?? 2;
28821
29814
  };
28822
- const Itinerary = ({ isOpen, handleSetIsOpen, isLoading, onEditAccommodation }) => {
29815
+ const Itinerary = ({ isOpen, handleSetIsOpen, isLoading, onEditAccommodation, onSaveEntry }) => {
28823
29816
  const context = React.useContext(SearchResultsConfigurationContext);
28824
29817
  const translations = getTranslations(context?.languageCode ?? 'en-GB');
28825
29818
  const { editablePackagingEntry, priceDetails, trajectItineraryView } = reactRedux.useSelector((state) => state.searchResults);
28826
29819
  const dispatch = reactRedux.useDispatch();
28827
29820
  const packagingEntry = editablePackagingEntry ?? context?.packagingEntry;
28828
29821
  const sortedLines = React.useMemo(() => {
28829
- return [...(packagingEntry?.lines ?? [])].sort((a, b) => {
29822
+ return (packagingEntry?.lines ?? []).filter(isMeaningfulItineraryLine).sort((a, b) => {
28830
29823
  const dateA = getDateOnlyTime(a.from);
28831
29824
  const dateB = getDateOnlyTime(b.from);
28832
29825
  if (dateA !== dateB) {
@@ -28897,7 +29890,7 @@ const Itinerary = ({ isOpen, handleSetIsOpen, isLoading, onEditAccommodation })
28897
29890
  " ",
28898
29891
  translations.SRP.TOTAL,
28899
29892
  ")"))),
28900
- React__default["default"].createElement("button", { className: "cta", onClick: handleConfirm }, translations.QSM.CONFIRM),
29893
+ isBookedEntryTraject(context?.trajectEntry) ? (React__default["default"].createElement("button", { className: "cta", onClick: onSaveEntry, disabled: !(packagingEntry?.lines ?? []).some((line) => line.isChanged) }, translations.SHARED.SAVE)) : (React__default["default"].createElement("button", { className: "cta", onClick: handleConfirm }, translations.QSM.CONFIRM)),
28901
29894
  !!context?.trajectEntry && (React__default["default"].createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => handleTrajectView(!trajectItineraryView) }, trajectItineraryView ? translations.SRP.SHOW_SERVICES : translations.SRP.VIEW_FULL_ITINERARY))))),
28902
29895
  React__default["default"].createElement("div", { className: "search__filter__itinerary" },
28903
29896
  React__default["default"].createElement("p", null, translations.SRP.DAY_BY_DAY),
@@ -30954,7 +31947,7 @@ const selectSelectedCombinationFlight = toolkit.createSelector([selectPackagingF
30954
31947
  return (packagingFlightResults.find((flight) => getFlightKey(flight.outward.segments) === selectedOutwardKey && getFlightKey(flight.return.segments) === selectedReturnKey) ?? null);
30955
31948
  });
30956
31949
 
30957
- const IndependentFlightOption = ({ item, onSelect, guid, selectedGuid, isOutward, showSelectedState, currentSelectedPrice, price }) => {
31950
+ const IndependentFlightOption = ({ item, onSelect, guid, selectedGuid, isOutward, showSelectedState, currentSelectedPrice, price, selectLabel, selectedLabel }) => {
30958
31951
  const context = React.useContext(SearchResultsConfigurationContext);
30959
31952
  const language = context?.languageCode ?? 'en-GB';
30960
31953
  const translations = getTranslations(language);
@@ -30979,7 +31972,7 @@ const IndependentFlightOption = ({ item, onSelect, guid, selectedGuid, isOutward
30979
31972
  priceDifference > 0 ? '+' : '-',
30980
31973
  "\u00A0",
30981
31974
  formatPrice$3(Math.abs(priceDifference), 'EUR', context?.languageCode ?? 'en-GB'))))),
30982
- React__default["default"].createElement("button", { type: "button", className: `cta ${selectedGuid === guid ? 'cta--selected' : 'cta--select'}`, onClick: () => onSelect?.() }, selectedGuid === guid ? 'Selected' : 'Select')))),
31975
+ React__default["default"].createElement("button", { type: "button", className: `cta ${selectedGuid === guid ? 'cta--selected' : 'cta--select'}`, onClick: () => onSelect?.() }, selectedGuid === guid ? selectedLabel ?? translations.SHARED.SELECTED : selectLabel ?? translations.SHARED.SELECT)))),
30983
31976
  React__default["default"].createElement("div", { className: "flight__flight" },
30984
31977
  React__default["default"].createElement("div", { className: "flight__flight__container" },
30985
31978
  React__default["default"].createElement("div", { className: "flight__flight__wrapper" },
@@ -31613,7 +32606,7 @@ const TrajectNodeCard = ({ node, result, isSelected, languageCode, translations,
31613
32606
  const image = candidate?.imageUrl ?? null;
31614
32607
  const description = toPlainText(candidate?.description);
31615
32608
  const title = candidate?.name ?? result.name;
31616
- const priceBlock = (React__default["default"].createElement("div", { className: "search__result-card__price__wrapper" },
32609
+ const priceBlock = result.price > 0 && (React__default["default"].createElement("div", { className: "search__result-card__price__wrapper" },
31617
32610
  React__default["default"].createElement("span", { className: "search__result-card__price__label" }, translations?.SHARED.TOTAL_PRICE),
31618
32611
  React__default["default"].createElement("span", { className: "search__result-card__price" }, price)));
31619
32612
  const canEditOptions = isSelected && !!onEditOptions && (result.rooms ?? []).some((room) => (room.options ?? []).length > 1);
@@ -31666,6 +32659,7 @@ const TrajectResultsFlyIn = ({ isLoading }) => {
31666
32659
  const translations = getTranslations(context?.languageCode ?? 'en-GB');
31667
32660
  const { trajectNodeAvailability, trajectEditLineGuid } = reactRedux.useSelector((state) => state.searchResults);
31668
32661
  const trajectEntry = context?.trajectEntry;
32662
+ const bookedMode = isBookedEntryTraject(trajectEntry);
31669
32663
  const item = React.useMemo(() => {
31670
32664
  if (!trajectEntry || !trajectEditLineGuid)
31671
32665
  return null;
@@ -31685,7 +32679,7 @@ const TrajectResultsFlyIn = ({ isLoading }) => {
31685
32679
  if (!result)
31686
32680
  return;
31687
32681
  dispatch(setTrajectNodeSelection({ lineGuid: item.line.guid, code }));
31688
- applyResultToLines(item.lines, result).forEach((line) => dispatch(updateEditableEntryLine(line)));
32682
+ buildNodeSelectionLines(item, result, bookedMode).forEach((line) => dispatch(updateEditableEntryLine(line)));
31689
32683
  dispatch(setTrajectEditLineGuid(null));
31690
32684
  dispatch(setFlyInIsOpen(false));
31691
32685
  };
@@ -31698,7 +32692,7 @@ const TrajectResultsFlyIn = ({ isLoading }) => {
31698
32692
  ordered.length,
31699
32693
  "\u00A0",
31700
32694
  translations.SRP.TOTAL_RESULTS_LABEL)),
31701
- ordered.length === 0 ? (React__default["default"].createElement("div", { className: "no-results" }, translations.SRP.NO_RESULTS)) : (React__default["default"].createElement("div", { className: "search__results__cards" }, ordered.map((result) => (React__default["default"].createElement(TrajectNodeCard, { key: result.code, node: item.node, result: result, isSelected: result.code === availability?.selectedCode, languageCode: context.languageCode, translations: translations, onSelect: () => handleSelect(result.code), showNights: item.node.serviceType === ACCOMMODATION_SERVICE_TYPE })))))));
32695
+ ordered.length === 0 ? (React__default["default"].createElement("div", { className: "no-results" }, translations.SRP.NO_RESULTS)) : (React__default["default"].createElement("div", { className: "search__results__cards" }, ordered.map((result) => (React__default["default"].createElement(TrajectNodeCard, { key: result.code, node: item.node, result: result, isSelected: result.code === availability?.selectedCode, languageCode: context.languageCode, translations: translations, onSelect: () => handleSelect(result.code), showNights: item.node.serviceType === ACCOMMODATION_SERVICE_TYPE, selectedLabel: bookedMode && matchesCode(result, item.node.code ?? '') ? translations.SHARED.BOOKED : undefined })))))));
31702
32696
  };
31703
32697
 
31704
32698
  /**
@@ -31760,6 +32754,7 @@ const TrajectDayActivitiesFlyIn = ({ isLoading }) => {
31760
32754
  const translations = getTranslations(context?.languageCode ?? 'en-GB');
31761
32755
  const { trajectNodeAvailability, trajectDaySuggestions, trajectActivitiesDayOrder, editablePackagingEntry } = reactRedux.useSelector((state) => state.searchResults);
31762
32756
  const trajectEntry = context?.trajectEntry;
32757
+ const bookedMode = isBookedEntryTraject(trajectEntry);
31763
32758
  const items = React.useMemo(() => {
31764
32759
  if (!trajectEntry || trajectActivitiesDayOrder === null)
31765
32760
  return [];
@@ -31792,7 +32787,7 @@ const TrajectDayActivitiesFlyIn = ({ isLoading }) => {
31792
32787
  if (!item)
31793
32788
  return;
31794
32789
  dispatch(setTrajectNodeSelection({ lineGuid: item.line.guid, code: result.code }));
31795
- applyResultToLines(item.lines, result).forEach((line) => dispatch(updateEditableEntryLine(line)));
32790
+ buildNodeSelectionLines(item, result, bookedMode).forEach((line) => dispatch(updateEditableEntryLine(line)));
31796
32791
  dispatch(setTrajectActivitiesDayOrder(null));
31797
32792
  dispatch(setFlyInIsOpen(false));
31798
32793
  };
@@ -31808,7 +32803,8 @@ const TrajectDayActivitiesFlyIn = ({ isLoading }) => {
31808
32803
  cards.length === 0 ? (React__default["default"].createElement("div", { className: "no-results" }, translations.SRP.NO_RESULTS)) : (React__default["default"].createElement("div", { className: "search__results__cards" }, cards.map((card) => {
31809
32804
  const { result, item } = card;
31810
32805
  const isSelected = item ? trajectNodeAvailability[item.line.guid]?.selectedCode === result.code : getAddedLines(result).length > 0;
31811
- return (React__default["default"].createElement(TrajectNodeCard, { key: `activity-${result.code}`, node: item?.node, result: result, isSelected: isSelected, languageCode: context.languageCode, translations: translations, onSelect: () => (item ? handleSelect(card) : handleToggle(result)), selectLabel: item ? undefined : translations.SRP.ADD, selectedLabel: item ? undefined : translations.SRP.REMOVE }));
32806
+ const isBookedCard = !!item && bookedMode && matchesCode(result, item.node.code ?? '');
32807
+ return (React__default["default"].createElement(TrajectNodeCard, { key: `activity-${result.code}`, node: item?.node, result: result, isSelected: isSelected, languageCode: context.languageCode, translations: translations, onSelect: () => (item ? handleSelect(card) : handleToggle(result)), selectLabel: item ? undefined : translations.SRP.ADD, selectedLabel: item ? (isBookedCard ? translations.SHARED.BOOKED : undefined) : translations.SRP.REMOVE }));
31812
32808
  })))));
31813
32809
  };
31814
32810
 
@@ -31884,7 +32880,7 @@ const FlyIn = ({ srpType, isOpen, setIsOpen, className = '', onPanelRef, details
31884
32880
  dispatch(setBookPackagingEntry(true));
31885
32881
  }
31886
32882
  };
31887
- return (React__default["default"].createElement("div", { className: `flyin ${isOpen ? 'flyin--active' : ''} ${className} ${isPackageEditFlow || flyInType === 'acco-results' || flyInType === 'traject-day-activities' ? 'flyin--large' : ''} ${flyInType === 'excursion-results' || flyInType === 'excursion-details' ? 'flyin--medium' : ''}
32883
+ return (React__default["default"].createElement("div", { className: `flyin ${isOpen ? 'flyin--active' : ''} ${className} ${isPackageEditFlow || flyInType === 'acco-results' || flyInType === 'traject-day-activities' ? 'flyin--large' : ''} ${flyInType === 'excursion-results' || flyInType === 'excursion-details' ? 'flyin--medium' : ''}
31888
32884
  ${flyInType === 'flight-outward-results' || flyInType === 'flight-return-results' || flyInType === 'traject-flight-results' ? 'flyin--flight' : ''}` },
31889
32885
  React__default["default"].createElement("div", { className: `flyin__panel ${isOpen ? 'flyin__panel--active' : ''}`, ref: panelRef },
31890
32886
  React__default["default"].createElement("div", { className: "flyin__content" },
@@ -33333,7 +34329,12 @@ const getRequestRoomsFromPackagingSegments = (entry, segments) => {
33333
34329
  dateOfBirth: paxSource?.dateOfBirth ?? undefined,
33334
34330
  age: paxSource?.dateOfBirth ? undefined : 30,
33335
34331
  isMainBooker: paxSource?.isMainBooker,
33336
- email: ''
34332
+ email: '',
34333
+ weight: null,
34334
+ weightType: build.WeightType.kilograms,
34335
+ height: null,
34336
+ heightType: build.HeightType.centimeters,
34337
+ customFields: []
33337
34338
  };
33338
34339
  if (!groupedByRoom.has(roomIndexWithinLine)) {
33339
34340
  groupedByRoom.set(roomIndexWithinLine, []);
@@ -33413,6 +34414,10 @@ const getUniqueItems = (items = []) => {
33413
34414
  return true;
33414
34415
  });
33415
34416
  };
34417
+ const isMeaningfulItem = (item, lines) => {
34418
+ const line = lines.find((l) => l.guid === item.itemGuid);
34419
+ return !line || isMeaningfulItineraryLine(line);
34420
+ };
33416
34421
  const findAccommodationForDay = (nodes, currentDay) => {
33417
34422
  for (const node of nodes ?? []) {
33418
34423
  const { startDay } = getNodeDayRange(node);
@@ -33472,20 +34477,20 @@ const buildFlightHtml = (line) => {
33472
34477
  */
33473
34478
  const buildFallbackHtml = (item, line) => {
33474
34479
  const details = [line?.accommodationName, line?.regimeName].filter(Boolean).join(' — ');
33475
- return `
33476
- <div class="itinerary-item__fallback">
33477
- <h3 class="itinerary-item__fallback-title">${escapeHtml(item.title)}</h3>
33478
- ${line ? buildFlightHtml(line) : ''}
33479
- ${details ? `<p class="itinerary-item__fallback-details">${escapeHtml(details)}</p>` : ''}
33480
- </div>
34480
+ return `
34481
+ <div class="itinerary-item__fallback">
34482
+ <h3 class="itinerary-item__fallback-title">${escapeHtml(item.title)}</h3>
34483
+ ${line ? buildFlightHtml(line) : ''}
34484
+ ${details ? `<p class="itinerary-item__fallback-details">${escapeHtml(details)}</p>` : ''}
34485
+ </div>
33481
34486
  `;
33482
34487
  };
33483
34488
  const buildItineraryHtml = (itinerary, translations, lines) => {
33484
34489
  if (!itinerary) {
33485
- return `
33486
- <div class="itinerary-shell">
33487
- <div class="itinerary-empty">Geen reisroute beschikbaar.</div>
33488
- </div>
34490
+ return `
34491
+ <div class="itinerary-shell">
34492
+ <div class="itinerary-empty">Geen reisroute beschikbaar.</div>
34493
+ </div>
33489
34494
  `;
33490
34495
  }
33491
34496
  const allNodes = itinerary.nodes ?? [];
@@ -33494,75 +34499,76 @@ const buildItineraryHtml = (itinerary, translations, lines) => {
33494
34499
  const { startDay } = getNodeDayRange(node);
33495
34500
  const day = node.startDate ? dateFns.format(node.startDate, 'd') : null;
33496
34501
  const month = node.startDate ? dateFns.format(node.startDate, 'MMM') : null;
33497
- const uniqueItems = getUniqueItems(node.items ?? []);
34502
+ const uniqueItems = getUniqueItems(node.items ?? []).filter((item) => isMeaningfulItem(item, lines));
33498
34503
  const hasItems = uniqueItems.length > 0;
33499
34504
  const hasAccommodationInCurrentNode = uniqueItems.some(isAccommodationItem);
33500
34505
  const activeAccommodation = findAccommodationForDay(allNodes, startDay);
33501
34506
  const accommodationBanner = !hasAccommodationInCurrentNode && activeAccommodation
33502
- ? `
33503
- <div class="itinerary-node__accommodation-banner">
33504
- Accommodatie voor deze dag: <strong>${escapeHtml(getAccommodationName(activeAccommodation))}</strong>
33505
- </div>
34507
+ ? `
34508
+ <div class="itinerary-node__accommodation-banner">
34509
+ Accommodatie voor deze dag: <strong>${escapeHtml(getAccommodationName(activeAccommodation))}</strong>
34510
+ </div>
33506
34511
  `
33507
34512
  : '';
33508
34513
  const itemsHtml = hasItems
33509
34514
  ? uniqueItems
33510
- .map((item) => `
33511
- <article class="itinerary-item" data-template="${escapeHtml(item.templateName)}">
34515
+ .map((item) => `
34516
+ <article class="itinerary-item" data-template="${escapeHtml(item.templateName)}">
33512
34517
  ${hasVisibleContent(item.contents)
33513
34518
  ? item.contents
33514
- : buildFallbackHtml(item, lines.find((line) => line.guid === item.itemGuid))}
33515
- </article>
34519
+ : buildFallbackHtml(item, lines.find((line) => line.guid === item.itemGuid))}
34520
+ </article>
33516
34521
  `)
33517
34522
  .join('')
33518
34523
  : `<div class="itinerary-node__empty">${translations.ITINERARY.NO_ITEMS}</div>`;
33519
- return `
33520
- <section class="itinerary-node">
33521
- <header class="itinerary-node__header">
33522
-
33523
- <div class="itinerary-node__day">
33524
- <p class="itinerary-node__day-day">${day}</p>
33525
- <p class="itinerary-node__day-month">${month}</p>
33526
- </div>
33527
-
33528
- <div>
33529
- <h2 class="itinerary-node__title">${escapeHtml(node.title)}</h2>
33530
- </div>
33531
- </header>
33532
-
33533
- <div class="itinerary-node__content">
33534
- ${accommodationBanner}
33535
- ${itemsHtml}
33536
- </div>
33537
- </section>
34524
+ return `
34525
+ <section class="itinerary-node">
34526
+ <header class="itinerary-node__header">
34527
+
34528
+ <div class="itinerary-node__day">
34529
+ <p class="itinerary-node__day-day">${day}</p>
34530
+ <p class="itinerary-node__day-month">${month}</p>
34531
+ </div>
34532
+
34533
+ <div>
34534
+ <h2 class="itinerary-node__title">${escapeHtml(node.title)}</h2>
34535
+ </div>
34536
+ </header>
34537
+
34538
+ <div class="itinerary-node__content">
34539
+ ${accommodationBanner}
34540
+ ${itemsHtml}
34541
+ </div>
34542
+ </section>
33538
34543
  `;
33539
34544
  })
33540
34545
  .join('');
33541
- const defaultItemsHtml = itinerary.defaultItems && itinerary.defaultItems.length > 0
33542
- ? `
33543
- <section class="itinerary-default-items">
33544
- <h2 class="itinerary-default-items__title">Algemene info</h2>
33545
- ${getUniqueItems(itinerary.defaultItems)
33546
- .map((item) => `
33547
- <article class="itinerary-item" data-template="${escapeHtml(item.templateName)}">
34546
+ const defaultItems = getUniqueItems(itinerary.defaultItems ?? []).filter((item) => isMeaningfulItem(item, lines));
34547
+ const defaultItemsHtml = defaultItems.length > 0
34548
+ ? `
34549
+ <section class="itinerary-default-items">
34550
+ <h2 class="itinerary-default-items__title">Algemene info</h2>
34551
+ ${defaultItems
34552
+ .map((item) => `
34553
+ <article class="itinerary-item" data-template="${escapeHtml(item.templateName)}">
33548
34554
  ${hasVisibleContent(item.contents)
33549
34555
  ? item.contents
33550
- : buildFallbackHtml(item, lines.find((line) => line.guid === item.itemGuid))}
33551
- </article>
34556
+ : buildFallbackHtml(item, lines.find((line) => line.guid === item.itemGuid))}
34557
+ </article>
33552
34558
  `)
33553
- .join('')}
33554
- </section>
34559
+ .join('')}
34560
+ </section>
33555
34561
  `
33556
34562
  : '';
33557
- return `
33558
- <div class="itinerary-shell">
33559
- ${itinerary.title ? `<header class="itinerary-shell__header"><h1 class="itinerary-shell__title">${escapeHtml(itinerary.title)}</h1></header>` : ''}
33560
-
33561
- <div class="itinerary-shell__content">
33562
- ${nodesHtml}
33563
- ${defaultItemsHtml}
33564
- </div>
33565
- </div>
34563
+ return `
34564
+ <div class="itinerary-shell">
34565
+ ${itinerary.title ? `<header class="itinerary-shell__header"><h1 class="itinerary-shell__title">${escapeHtml(itinerary.title)}</h1></header>` : ''}
34566
+
34567
+ <div class="itinerary-shell__content">
34568
+ ${nodesHtml}
34569
+ ${defaultItemsHtml}
34570
+ </div>
34571
+ </div>
33566
34572
  `;
33567
34573
  };
33568
34574
  const FullItinerary = ({ isLoading }) => {
@@ -33584,170 +34590,170 @@ const FullItinerary = ({ isLoading }) => {
33584
34590
  }
33585
34591
  const shadowRoot = host.shadowRoot ?? host.attachShadow({ mode: 'open' });
33586
34592
  shadowRootRef.current = shadowRoot;
33587
- shadowRoot.innerHTML = `
33588
- <style>
33589
- :host {
33590
- all: initial;
33591
- display: block;
33592
- width: 100%;
33593
- }
33594
-
33595
- *,
33596
- *::before,
33597
- *::after {
33598
- box-sizing: border-box;
33599
- }
33600
-
33601
- .itinerary-shell {
33602
- width: 100%;
33603
- margin: 0 auto;
33604
- font-family: 'Outfit', sans-serif;
33605
- color: #222;
33606
- }
33607
-
33608
- .itinerary-shell__header {
33609
- margin-bottom: 24px;
33610
- }
33611
-
33612
- .itinerary-shell__title {
33613
- margin: 0;
33614
- font-size: 32px;
33615
- line-height: 1.2;
33616
- }
33617
-
33618
- .itinerary-shell__content {
33619
- display: grid;
33620
- gap: 24px;
33621
- }
33622
-
33623
- .itinerary-node {
33624
- border: 1px solid #e5e7eb;
33625
- border-radius: 16px;
33626
- overflow: hidden;
33627
- background: #fff;
33628
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.04);
33629
- }
33630
-
33631
- .itinerary-node__header {
33632
- display: flex;
33633
- gap: 16px;
33634
- align-items: center;
33635
- border-bottom: 1px solid #eef0f2;
33636
- background: #fafafa;
33637
- }
33638
-
33639
- .itinerary-node__day {
33640
- flex: 50px 0 0;
33641
- display: flex;
33642
- flex-flow: column;
33643
- align-items: center;
33644
- justify-content: center;
33645
- background: #1f9470;
33646
- color: white;
33647
- border: 1.5px solid var(--tide-booking-color-secondary);
33648
- }
33649
-
33650
- .itinerary-node__day-month {
33651
- margin: 0px;
33652
- }
33653
-
33654
- .itinerary-node__day-day {
33655
- margin: 0px;
33656
- font-weight: var(--tide-booking-search-results-label-date-month-font-weight);
33657
- color: var(--tide-booking-search-results-label-date-month-color);
33658
- font-size: 24px;
33659
- }
33660
-
33661
- .itinerary-node__title {
33662
- margin: 0;
33663
- font-size: 22px;
33664
- line-height: 1.25;
33665
- }
33666
-
33667
- .itinerary-node__content {
33668
- display: grid;
33669
- gap: 20px;
33670
- padding: 20px;
33671
- }
33672
-
33673
- .itinerary-node__accommodation-banner {
33674
- padding: 12px 16px;
33675
- border: 1px solid #dbeafe;
33676
- border-radius: 12px;
33677
- background: #eff6ff;
33678
- color: #1e3a8a;
33679
- font-size: 14px;
33680
- line-height: 1.4;
33681
- }
33682
-
33683
- .itinerary-item {
33684
- display: block;
33685
- border-radius: 12px;
33686
- overflow: hidden;
33687
- background: #fff;
33688
- }
33689
-
33690
- /* An item whose template has no content view of its own still has to read as a service. */
33691
- .itinerary-item__fallback {
33692
- padding: 16px 20px;
33693
- border: 1px solid #e5e7eb;
33694
- border-radius: 12px;
33695
- }
33696
-
33697
- .itinerary-item__fallback-title {
33698
- margin: 0;
33699
- font-size: 16px;
33700
- font-weight: 600;
33701
- color: #111827;
33702
- }
33703
-
33704
- .itinerary-item__legs {
33705
- margin: 8px 0 0;
33706
- padding-left: 18px;
33707
- color: #374151;
33708
- }
33709
-
33710
- .itinerary-item__legs li + li {
33711
- margin-top: 4px;
33712
- }
33713
-
33714
- .itinerary-item__fallback-details {
33715
- margin: 8px 0 0;
33716
- color: #6b7280;
33717
- }
33718
-
33719
- .itinerary-node__empty,
33720
- .itinerary-empty {
33721
- padding: 20px;
33722
- border: 1px dashed #d1d5db;
33723
- border-radius: 12px;
33724
- color: #6b7280;
33725
- background: #fafafa;
33726
- }
33727
-
33728
- .itinerary-default-items {
33729
- border: 1px solid #e5e7eb;
33730
- border-radius: 16px;
33731
- padding: 20px;
33732
- background: #fff;
33733
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.04);
33734
- }
33735
-
33736
- .itinerary-default-items__title {
33737
- margin: 0 0 16px;
33738
- font-size: 22px;
33739
- }
33740
-
33741
- @media (max-width: 768px) {
33742
- .itinerary-shell {
33743
- padding: 16px;
33744
- }
33745
- }
33746
-
33747
- ${itinerary?.styleSheetBody ?? ''}
33748
- </style>
33749
-
33750
- ${html}
34593
+ shadowRoot.innerHTML = `
34594
+ <style>
34595
+ :host {
34596
+ all: initial;
34597
+ display: block;
34598
+ width: 100%;
34599
+ }
34600
+
34601
+ *,
34602
+ *::before,
34603
+ *::after {
34604
+ box-sizing: border-box;
34605
+ }
34606
+
34607
+ .itinerary-shell {
34608
+ width: 100%;
34609
+ margin: 0 auto;
34610
+ font-family: 'Outfit', sans-serif;
34611
+ color: #222;
34612
+ }
34613
+
34614
+ .itinerary-shell__header {
34615
+ margin-bottom: 24px;
34616
+ }
34617
+
34618
+ .itinerary-shell__title {
34619
+ margin: 0;
34620
+ font-size: 32px;
34621
+ line-height: 1.2;
34622
+ }
34623
+
34624
+ .itinerary-shell__content {
34625
+ display: grid;
34626
+ gap: 24px;
34627
+ }
34628
+
34629
+ .itinerary-node {
34630
+ border: 1px solid #e5e7eb;
34631
+ border-radius: 16px;
34632
+ overflow: hidden;
34633
+ background: #fff;
34634
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.04);
34635
+ }
34636
+
34637
+ .itinerary-node__header {
34638
+ display: flex;
34639
+ gap: 16px;
34640
+ align-items: center;
34641
+ border-bottom: 1px solid #eef0f2;
34642
+ background: #fafafa;
34643
+ }
34644
+
34645
+ .itinerary-node__day {
34646
+ flex: 50px 0 0;
34647
+ display: flex;
34648
+ flex-flow: column;
34649
+ align-items: center;
34650
+ justify-content: center;
34651
+ background: #1f9470;
34652
+ color: white;
34653
+ border: 1.5px solid var(--tide-booking-color-secondary);
34654
+ }
34655
+
34656
+ .itinerary-node__day-month {
34657
+ margin: 0px;
34658
+ }
34659
+
34660
+ .itinerary-node__day-day {
34661
+ margin: 0px;
34662
+ font-weight: var(--tide-booking-search-results-label-date-month-font-weight);
34663
+ color: var(--tide-booking-search-results-label-date-month-color);
34664
+ font-size: 24px;
34665
+ }
34666
+
34667
+ .itinerary-node__title {
34668
+ margin: 0;
34669
+ font-size: 22px;
34670
+ line-height: 1.25;
34671
+ }
34672
+
34673
+ .itinerary-node__content {
34674
+ display: grid;
34675
+ gap: 20px;
34676
+ padding: 20px;
34677
+ }
34678
+
34679
+ .itinerary-node__accommodation-banner {
34680
+ padding: 12px 16px;
34681
+ border: 1px solid #dbeafe;
34682
+ border-radius: 12px;
34683
+ background: #eff6ff;
34684
+ color: #1e3a8a;
34685
+ font-size: 14px;
34686
+ line-height: 1.4;
34687
+ }
34688
+
34689
+ .itinerary-item {
34690
+ display: block;
34691
+ border-radius: 12px;
34692
+ overflow: hidden;
34693
+ background: #fff;
34694
+ }
34695
+
34696
+ /* An item whose template has no content view of its own still has to read as a service. */
34697
+ .itinerary-item__fallback {
34698
+ padding: 16px 20px;
34699
+ border: 1px solid #e5e7eb;
34700
+ border-radius: 12px;
34701
+ }
34702
+
34703
+ .itinerary-item__fallback-title {
34704
+ margin: 0;
34705
+ font-size: 16px;
34706
+ font-weight: 600;
34707
+ color: #111827;
34708
+ }
34709
+
34710
+ .itinerary-item__legs {
34711
+ margin: 8px 0 0;
34712
+ padding-left: 18px;
34713
+ color: #374151;
34714
+ }
34715
+
34716
+ .itinerary-item__legs li + li {
34717
+ margin-top: 4px;
34718
+ }
34719
+
34720
+ .itinerary-item__fallback-details {
34721
+ margin: 8px 0 0;
34722
+ color: #6b7280;
34723
+ }
34724
+
34725
+ .itinerary-node__empty,
34726
+ .itinerary-empty {
34727
+ padding: 20px;
34728
+ border: 1px dashed #d1d5db;
34729
+ border-radius: 12px;
34730
+ color: #6b7280;
34731
+ background: #fafafa;
34732
+ }
34733
+
34734
+ .itinerary-default-items {
34735
+ border: 1px solid #e5e7eb;
34736
+ border-radius: 16px;
34737
+ padding: 20px;
34738
+ background: #fff;
34739
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.04);
34740
+ }
34741
+
34742
+ .itinerary-default-items__title {
34743
+ margin: 0 0 16px;
34744
+ font-size: 22px;
34745
+ }
34746
+
34747
+ @media (max-width: 768px) {
34748
+ .itinerary-shell {
34749
+ padding: 16px;
34750
+ }
34751
+ }
34752
+
34753
+ ${itinerary?.styleSheetBody ?? ''}
34754
+ </style>
34755
+
34756
+ ${html}
33751
34757
  `;
33752
34758
  }, [html, itinerary?.styleSheetBody, isLoading]);
33753
34759
  return (React__default["default"].createElement(React__default["default"].Fragment, null,
@@ -34201,7 +35207,7 @@ const BookPackagingEntry = ({ activeSearchSeed, isLoading, isConfirmationPage })
34201
35207
  const formik$1 = formik.useFormik({
34202
35208
  initialValues,
34203
35209
  enableReinitialize: true,
34204
- validate: (values) => validateForm$1(values, false, 'b2c', translations, travellersSettings.formFields, travellersSettings.mainBookerFormFields),
35210
+ validate: (values) => validateForm$1(values, false, 'b2c', translations, travellersSettings.formFields, travellersSettings.mainBookerFormFields, undefined, undefined, editablePackagingEntry?.paxFields ?? []),
34205
35211
  onSubmit: (values) => {
34206
35212
  if (!editablePackagingEntry)
34207
35213
  return;
@@ -34286,7 +35292,7 @@ const BookPackagingEntry = ({ activeSearchSeed, isLoading, isConfirmationPage })
34286
35292
  ".\u00A0",
34287
35293
  stepLabels[step])) },
34288
35294
  isConfirmationPage && isLoading && React__default["default"].createElement(Spinner, { label: translations.SUMMARY.PROCESS_BOOKING }),
34289
- currentStep === 0 && (React__default["default"].createElement(SharedTravelersForm, { formik: formik$1, translations: translations, travellersSettings: travellersSettings, countries: countries, travelersFirstStep: false, isUnavailable: false, useCompactForm: false, showAgentSelection: false })),
35295
+ currentStep === 0 && (React__default["default"].createElement(SharedTravelersForm, { formik: formik$1, translations: translations, travellersSettings: travellersSettings, countries: countries, travelersFirstStep: false, isUnavailable: false, useCompactForm: false, showAgentSelection: false, paxFields: editablePackagingEntry?.paxFields ?? [] })),
34290
35296
  currentStep === 1 && (React__default["default"].createElement(SharedSummary, { translations: translations, travelerFormValues: formik$1.values, isSubmitting: isSubmitting, skipPayment: !context.generatePaymentUrl, userValidated: userValidated, remarks: remarks, enableVoucher: false, allowOption: false, isOffer: false, onUserValidatedChange: setUserValidated, onRemarksChange: setRemarks, onSubmit: handleSummarySubmit, renderOptions: () => renderEditablePackagingEntrySummaryOptions(editablePackagingEntry, priceDetails, translations, selectedPackagingAccoResult?.currencyCode), renderPreviousButton: () => (React__default["default"].createElement("button", { type: "button", title: translations.STEPS.PREVIOUS, onClick: () => dispatch(setCurrentStep(0)), className: "cta cta--secondary" }, translations.STEPS.PREVIOUS)) })),
34291
35297
  currentStep === 2 && (React__default["default"].createElement(SharedConfirmation, { bookingNumber: bookingNumber ?? editablePackagingEntry?.dossierNumber ?? '', isOption: false, isOffer: false, translations: translations.CONFIRMATION })),
34292
35298
  currentStep === 3 && React__default["default"].createElement("div", null))),
@@ -34350,6 +35356,7 @@ const TrajectResults = ({ isLoading }) => {
34350
35356
  const locale = getLocale(context?.languageCode ?? 'en-GB');
34351
35357
  const { trajectNodeAvailability, trajectFlightAvailability, trajectDaySuggestions, editablePackagingEntry } = reactRedux.useSelector((state) => state.searchResults);
34352
35358
  const trajectEntry = context?.trajectEntry;
35359
+ const bookedMode = isBookedEntryTraject(trajectEntry);
34353
35360
  const hideShowMoreResults = !!context?.hideShowMoreResults;
34354
35361
  const loadDayActivitiesOnDemand = !!context?.loadDayActivitiesOnDemand;
34355
35362
  const showNonPreferredHotels = !!context?.showNonPreferredHotels;
@@ -34406,7 +35413,7 @@ const TrajectResults = ({ isLoading }) => {
34406
35413
  if (!result)
34407
35414
  return;
34408
35415
  dispatch(setTrajectNodeSelection({ lineGuid: item.line.guid, code }));
34409
- applyResultToLines(item.lines, result).forEach((line) => dispatch(updateEditableEntryLine(line)));
35416
+ buildNodeSelectionLines(item, result, bookedMode).forEach((line) => dispatch(updateEditableEntryLine(line)));
34410
35417
  };
34411
35418
  /** The remaining legs for a flight node open in the fly-in, as the regular flight results do. */
34412
35419
  const handleShowMoreFlights = (item) => {
@@ -34494,7 +35501,7 @@ const TrajectResults = ({ isLoading }) => {
34494
35501
  node.preferredAccommodationName,
34495
35502
  " \u2014 ",
34496
35503
  translations.SRP.NO_RESULTS))),
34497
- React__default["default"].createElement("div", { className: "search__results__cards search__results__cards--scroll" }, [selected, ...configured, ...fill].map((result) => (React__default["default"].createElement(TrajectNodeCard, { key: result.code, node: node, result: result, isSelected: result.code === selected.code, languageCode: context.languageCode, translations: translations, onSelect: () => handleSelect(item, result.code), onEditOptions: node.serviceType === ACCOMMODATION_SERVICE_TYPE ? () => handleEditOptions(item) : undefined, showNights: node.serviceType === ACCOMMODATION_SERVICE_TYPE })))),
35504
+ React__default["default"].createElement("div", { className: "search__results__cards search__results__cards--scroll" }, [selected, ...configured, ...fill].map((result) => (React__default["default"].createElement(TrajectNodeCard, { key: result.code, node: node, result: result, isSelected: result.code === selected.code, languageCode: context.languageCode, translations: translations, onSelect: () => handleSelect(item, result.code), onEditOptions: node.serviceType === ACCOMMODATION_SERVICE_TYPE ? () => handleEditOptions(item) : undefined, showNights: node.serviceType === ACCOMMODATION_SERVICE_TYPE, selectedLabel: bookedMode && matchesCode(result, node.code ?? '') ? translations.SHARED.BOOKED : undefined })))),
34498
35505
  remaining > 0 && !hideShowMoreResults && (React__default["default"].createElement("div", { className: "search__results__cards__actions" },
34499
35506
  React__default["default"].createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => handleShowMore(item) },
34500
35507
  translations.SRP.SHOW_MORE,
@@ -34504,7 +35511,7 @@ const TrajectResults = ({ isLoading }) => {
34504
35511
  };
34505
35512
  const handleSelectFlight = (item, flight) => {
34506
35513
  dispatch(setTrajectFlightSelection({ lineGuid: item.line.guid, guid: flight.outwardGuid }));
34507
- dispatch(updateEditableEntryLine(applyFlightToLine(item.line, flight)));
35514
+ dispatch(updateEditableEntryLine(buildFlightSelectionLine(item, flight, bookedMode)));
34508
35515
  };
34509
35516
  const renderFlightNode = (item) => {
34510
35517
  const { node, line } = item;
@@ -34530,7 +35537,7 @@ const TrajectResults = ({ isLoading }) => {
34530
35537
  status === 'unavailable' && React__default["default"].createElement("div", { className: "no-results" }, translations.SRP.NO_RESULTS),
34531
35538
  status === 'resolved' && selected && (React__default["default"].createElement(React__default["default"].Fragment, null,
34532
35539
  React__default["default"].createElement("div", { className: "search__results__cards search__results__cards--extended" },
34533
- React__default["default"].createElement(IndependentFlightOption, { key: `flight-${selected.outwardGuid}`, item: selected.outward, guid: selected.outwardGuid, selectedGuid: selected.outwardGuid, isOutward: true, showSelectedState: true, price: selected.price }),
35540
+ React__default["default"].createElement(IndependentFlightOption, { key: `flight-${selected.outwardGuid}`, item: selected.outward, guid: selected.outwardGuid, selectedGuid: selected.outwardGuid, isOutward: true, showSelectedState: true, price: selected.price, selectedLabel: bookedMode && selected.outwardGuid === line.guid ? translations.SHARED.BOOKED : undefined }),
34534
35541
  visible.map((result) => (React__default["default"].createElement(IndependentFlightOption, { key: `flight-${result.outwardGuid}`, item: result.outward, guid: result.outwardGuid, isOutward: true, price: result.price, currentSelectedPrice: selected.price, onSelect: () => handleSelectFlight(item, result) })))),
34535
35542
  others.length > 2 && !hideShowMoreResults && (React__default["default"].createElement("div", { className: "search__results__cards__actions" },
34536
35543
  React__default["default"].createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => handleShowMoreFlights(item) },
@@ -34614,7 +35621,8 @@ const TrajectResults = ({ isLoading }) => {
34614
35621
  */
34615
35622
  const renderActivityCard = ({ result, item }) => {
34616
35623
  const isSelected = item ? trajectNodeAvailability[item.line.guid]?.selectedCode === result.code : getAddedExcursionLines(result).length > 0;
34617
- return (React__default["default"].createElement(TrajectNodeCard, { key: `activity-${result.code}`, node: item?.node, result: result, isSelected: isSelected, languageCode: context.languageCode, translations: translations, onSelect: () => (item ? handleSelect(item, result.code) : handleToggleActivity(result)), selectLabel: item ? undefined : translations.SRP.ADD, selectedLabel: item ? undefined : translations.SRP.REMOVE }));
35624
+ const isBookedCard = !!item && bookedMode && matchesCode(result, item.node.code ?? '');
35625
+ return (React__default["default"].createElement(TrajectNodeCard, { key: `activity-${result.code}`, node: item?.node, result: result, isSelected: isSelected, languageCode: context.languageCode, translations: translations, onSelect: () => (item ? handleSelect(item, result.code) : handleToggleActivity(result)), selectLabel: item ? undefined : translations.SRP.ADD, selectedLabel: item ? (isBookedCard ? translations.SHARED.BOOKED : undefined) : translations.SRP.REMOVE }));
34618
35626
  };
34619
35627
  if (isLoading)
34620
35628
  return React__default["default"].createElement(Spinner, { label: translations.SRP.LOADING_ITINERARY });
@@ -34658,6 +35666,7 @@ const useTrajectAvailability = () => {
34658
35666
  const controller = new AbortController();
34659
35667
  dispatch(resetTrajectAvailability());
34660
35668
  dispatch(setTrajectResolving(true));
35669
+ const bookedMode = isBookedEntryTraject(trajectEntry);
34661
35670
  const config = {
34662
35671
  host: context.tideConnection.host,
34663
35672
  apiKey: context.tideConnection.apiKey
@@ -34719,6 +35728,17 @@ const useTrajectAvailability = () => {
34719
35728
  });
34720
35729
  const configuredCodes = new Set(configuredResults.map((x) => x.code));
34721
35730
  const destinationResults = areaResults.filter((x) => !configuredCodes.has(x.code));
35731
+ if (bookedMode) {
35732
+ const results = [...configuredResults, ...destinationResults];
35733
+ let booked = results.find((x) => matchesCode(x, item.node.code ?? '')) ?? null;
35734
+ // Not searchable any more (sold out, delisted) — drawn from the line instead.
35735
+ if (!booked) {
35736
+ booked = buildBookedNodeResult(item.node, item.lines);
35737
+ results.unshift(booked);
35738
+ }
35739
+ dispatch(setTrajectNodeAvailability({ lineGuid, results, selectedCode: booked.code, preferredUnavailable: false }));
35740
+ return [];
35741
+ }
34722
35742
  const preferredCandidate = candidates.find((x) => x.isPreferred) ?? candidates[0];
34723
35743
  const preferredUnavailable = configuredResults.length
34724
35744
  ? !!preferredCandidate && !configuredResults.some((x) => matchesCode(x, preferredCandidate.code))
@@ -34741,6 +35761,11 @@ const useTrajectAvailability = () => {
34741
35761
  if (controller.signal.aborted)
34742
35762
  return [];
34743
35763
  console.error('Failed to resolve traject node availability', item.node.name, error);
35764
+ if (bookedMode) {
35765
+ const booked = buildBookedNodeResult(item.node, item.lines);
35766
+ dispatch(setTrajectNodeAvailability({ lineGuid, results: [booked], selectedCode: booked.code, preferredUnavailable: false }));
35767
+ return [];
35768
+ }
34744
35769
  dispatch(setTrajectNodeAvailability({ lineGuid, results: [], selectedCode: null, preferredUnavailable: false }));
34745
35770
  return item.lines.map((x) => x.guid);
34746
35771
  }
@@ -34748,6 +35773,11 @@ const useTrajectAvailability = () => {
34748
35773
  const resolveFlightNode = async (item) => {
34749
35774
  const lineGuid = item.line.guid;
34750
35775
  if (!item.node.departureAirportCode || !item.node.arrivalAirportCode) {
35776
+ if (bookedMode) {
35777
+ const booked = buildBookedFlightResult(item.line);
35778
+ dispatch(setTrajectFlightAvailability({ lineGuid, results: [booked], selectedGuid: booked.outwardGuid }));
35779
+ return [];
35780
+ }
34751
35781
  dispatch(setTrajectFlightAvailability({ lineGuid, results: [], selectedGuid: null }));
34752
35782
  return item.lines.map((x) => x.guid);
34753
35783
  }
@@ -34756,6 +35786,11 @@ const useTrajectAvailability = () => {
34756
35786
  const flights = (await build.searchPackagingFlights(config, buildTrajectFlightRequest(item, searchContext, flightPax), controller.signal)) ?? [];
34757
35787
  if (controller.signal.aborted)
34758
35788
  return [];
35789
+ if (bookedMode) {
35790
+ const booked = buildBookedFlightResult(item.line);
35791
+ dispatch(setTrajectFlightAvailability({ lineGuid, results: [booked, ...flights], selectedGuid: booked.outwardGuid }));
35792
+ return [];
35793
+ }
34759
35794
  const picked = pickCheapestFlight(flights);
34760
35795
  dispatch(setTrajectFlightAvailability({ lineGuid, results: flights, selectedGuid: picked?.outwardGuid ?? null }));
34761
35796
  if (!picked)
@@ -34767,6 +35802,11 @@ const useTrajectAvailability = () => {
34767
35802
  if (controller.signal.aborted)
34768
35803
  return [];
34769
35804
  console.error('Failed to resolve traject flight', item.node.name, error);
35805
+ if (bookedMode) {
35806
+ const booked = buildBookedFlightResult(item.line);
35807
+ dispatch(setTrajectFlightAvailability({ lineGuid, results: [booked], selectedGuid: booked.outwardGuid }));
35808
+ return [];
35809
+ }
34770
35810
  dispatch(setTrajectFlightAvailability({ lineGuid, results: [], selectedGuid: null }));
34771
35811
  return item.lines.map((x) => x.guid);
34772
35812
  }
@@ -34804,6 +35844,7 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
34804
35844
  const [detailsIsLoading, setDetailsIsLoading] = React.useState(false);
34805
35845
  const [pricesAreLoading, setPricesAreLoading] = React.useState(false);
34806
35846
  const [itineraryIsLoading, setItineraryIsLoading] = React.useState(false);
35847
+ const [entrySaving, setEntrySaving] = React.useState(false);
34807
35848
  const [itineraryOpen, setItineraryOpen] = React.useState(false);
34808
35849
  const [isBookingConfirmation, setIsBookingConfirmation] = React.useState(false);
34809
35850
  const [selectedAccommodationSeed, setSelectedAccommodationSeed] = React.useState(null);
@@ -35560,6 +36601,31 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
35560
36601
  React.useEffect(() => {
35561
36602
  setInitialFiltersSet(false);
35562
36603
  }, [activeSearchSeed]);
36604
+ const handleSaveEntry = async () => {
36605
+ if (!context || !bookableEntry || entrySaving)
36606
+ return;
36607
+ setEntrySaving(true);
36608
+ try {
36609
+ const config = {
36610
+ host: context.tideConnection.host,
36611
+ apiKey: context.tideConnection.apiKey
36612
+ };
36613
+ const request = {
36614
+ language: context.languageCode ?? 'en-GB',
36615
+ officeId: context.tideConnection.officeId,
36616
+ catalogueId: context.searchConfiguration.defaultCatalogueId ?? lodash.first(context?.tideConnection?.catalogueIds) ?? 1,
36617
+ agentId: context.agentId,
36618
+ payload: bookableEntry
36619
+ };
36620
+ await build.savePackagingEntry(config, request);
36621
+ if (typeof window !== 'undefined')
36622
+ window.location.reload();
36623
+ }
36624
+ catch (err) {
36625
+ console.error('Failed to save the entry', err);
36626
+ setEntrySaving(false);
36627
+ }
36628
+ };
35563
36629
  React.useEffect(() => {
35564
36630
  const fetchPriceDetails = async () => {
35565
36631
  if (!context || !editablePackagingEntry || lodash.isEmpty(editablePackagingEntry.lines))
@@ -35944,11 +37010,18 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
35944
37010
  const id = paxId++;
35945
37011
  pax.push({
35946
37012
  id,
37013
+ guid: '',
35947
37014
  firstName: _.firstName || '',
35948
37015
  lastName: _.lastName || '',
35949
- dateOfBirth: _.dateOfBirth || null,
35950
- age: _.age || null,
35951
- isMainBooker: roomIndex === 0 && paxIndex === 0
37016
+ dateOfBirth: _.dateOfBirth || undefined,
37017
+ age: _.age || undefined,
37018
+ isMainBooker: roomIndex === 0 && paxIndex === 0,
37019
+ email: '',
37020
+ weight: null,
37021
+ weightType: build.WeightType.kilograms,
37022
+ height: null,
37023
+ heightType: build.HeightType.centimeters,
37024
+ customFields: []
35952
37025
  });
35953
37026
  return id;
35954
37027
  });
@@ -35968,6 +37041,7 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
35968
37041
  depositAmount: 0,
35969
37042
  pax,
35970
37043
  rooms,
37044
+ paxFields: [],
35971
37045
  lines: [],
35972
37046
  address: {}
35973
37047
  };
@@ -35987,7 +37061,7 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
35987
37061
  context.searchConfiguration.qsmType != build.PortalQsmType.AccommodationAndFlight && context.showFilters && (React__default["default"].createElement(Filters, { initialFilters: initialFilters, filters: filters, isOpen: filtersOpen, handleSetIsOpen: () => setFiltersOpen(!filtersOpen),
35988
37062
  // handleApplyFilters={() => setSearchTrigger((prev) => prev + 1)}
35989
37063
  isLoading: isLoading, setFilters: (filters) => dispatch(setFilters(filters)), resetFilters: (filters) => dispatch(resetFilters(filters)) })),
35990
- context.searchConfiguration.qsmType === build.PortalQsmType.AccommodationAndFlight && (React__default["default"].createElement(Itinerary, { isOpen: itineraryOpen, handleSetIsOpen: () => setItineraryOpen(!itineraryOpen), isLoading: isLoading || pricesAreLoading || flightsLoading || itineraryIsLoading || trajectResolving, onEditAccommodation: handleEditAccommodation })),
37064
+ context.searchConfiguration.qsmType === build.PortalQsmType.AccommodationAndFlight && (React__default["default"].createElement(Itinerary, { isOpen: itineraryOpen, handleSetIsOpen: () => setItineraryOpen(!itineraryOpen), isLoading: isLoading || pricesAreLoading || flightsLoading || itineraryIsLoading || trajectResolving || entrySaving, onEditAccommodation: handleEditAccommodation, onSaveEntry: handleSaveEntry })),
35991
37065
  React__default["default"].createElement("div", { className: "search__results" },
35992
37066
  isMobile && (React__default["default"].createElement("div", { className: "search__result-row" },
35993
37067
  React__default["default"].createElement("div", { className: "search__results__actions" },
@@ -36075,8 +37149,15 @@ const createSearchResultsStore = () => toolkit.configureStore({
36075
37149
 
36076
37150
  const SearchResults = ({ configuration, onBookingStarted }) => {
36077
37151
  const store = React__default["default"].useMemo(() => createSearchResultsStore(), []);
37152
+ // A booked entry opened through its magic link gets the traject services screen by carrying a
37153
+ // traject rebuilt from its own lines. The magic link on the entry is what marks the mode.
37154
+ const effectiveConfiguration = React__default["default"].useMemo(() => {
37155
+ if (!configuration.allowBookedEntryChanges || configuration.trajectEntry || !configuration.packagingEntry?.magicLink)
37156
+ return configuration;
37157
+ return { ...configuration, trajectEntry: buildBookedTrajectEntry(configuration.packagingEntry) };
37158
+ }, [configuration]);
36078
37159
  return (React__default["default"].createElement(reactRedux.Provider, { store: store },
36079
- React__default["default"].createElement(SearchResultsConfigurationContext.Provider, { value: configuration },
37160
+ React__default["default"].createElement(SearchResultsConfigurationContext.Provider, { value: effectiveConfiguration },
36080
37161
  React__default["default"].createElement(SearchResultsContainer, { onBookingStarted: onBookingStarted }))));
36081
37162
  };
36082
37163
 
@@ -38971,20 +40052,20 @@ function ItineraryMapView({ destinations: propDestinations, adults = 2, dateFrom
38971
40052
  const isStartEnd = dest.isStart || dest.isEnd;
38972
40053
  let iconHtml;
38973
40054
  if (isStartEnd) {
38974
- iconHtml = `
38975
- <div class="map-view__marker map-view__marker--plane">
38976
- <svg viewBox="0 0 576 512" fill="currentColor" width="16" height="16" xmlns="http://www.w3.org/2000/svg">
38977
- <path d="M482.3 192c34.2 0 93.7 29 93.7 64c0 36-59.5 64-93.7 64l-116.6 0L265.2 495.9c-5.7 10.1-16.3 16.1-27.8 16.1l-56.2 0c-10.6 0-18.3-10.3-15.8-20.6l49-196.3L112 288 68.8 377.6c-3 6.1-9.2 10.4-16 10.4l-2.8 0c-9.2 0-16-8.8-13.3-17.6L80 256L36.7 141.6C34 132.8 40.8 124 50 124l2.8 0c6.8 0 13 4.3 16 10.4L112 224l102.9 0-49-196.3C163.5 17.3 171.2 7 181.8 7l56.2 0c11.5 0 22.1 6 27.8 16.1L365.7 192l116.6 0z"/>
38978
- </svg>
40055
+ iconHtml = `
40056
+ <div class="map-view__marker map-view__marker--plane">
40057
+ <svg viewBox="0 0 576 512" fill="currentColor" width="16" height="16" xmlns="http://www.w3.org/2000/svg">
40058
+ <path d="M482.3 192c34.2 0 93.7 29 93.7 64c0 36-59.5 64-93.7 64l-116.6 0L265.2 495.9c-5.7 10.1-16.3 16.1-27.8 16.1l-56.2 0c-10.6 0-18.3-10.3-15.8-20.6l49-196.3L112 288 68.8 377.6c-3 6.1-9.2 10.4-16 10.4l-2.8 0c-9.2 0-16-8.8-13.3-17.6L80 256L36.7 141.6C34 132.8 40.8 124 50 124l2.8 0c6.8 0 13 4.3 16 10.4L112 224l102.9 0-49-196.3C163.5 17.3 171.2 7 181.8 7l56.2 0c11.5 0 22.1 6 27.8 16.1L365.7 192l116.6 0z"/>
40059
+ </svg>
38979
40060
  </div>`;
38980
40061
  }
38981
40062
  else {
38982
40063
  stopIndex++;
38983
40064
  const imgStyle = dest.imageUrl ? `style="background-image:url('${dest.imageUrl}')"` : '';
38984
- iconHtml = `
38985
- <div class="map-view__marker map-view__marker--stop" ${imgStyle}>
38986
- ${!dest.imageUrl ? `<span class="map-view__marker-number">${stopIndex}</span>` : ''}
38987
- <span class="map-view__marker-badge">${stopIndex}</span>
40065
+ iconHtml = `
40066
+ <div class="map-view__marker map-view__marker--stop" ${imgStyle}>
40067
+ ${!dest.imageUrl ? `<span class="map-view__marker-number">${stopIndex}</span>` : ''}
40068
+ <span class="map-view__marker-badge">${stopIndex}</span>
38988
40069
  </div>`;
38989
40070
  }
38990
40071
  const icon = L.divIcon({