@qite/tide-components 1.0.12 → 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 (24) hide show
  1. package/build/build-cjs/index.js +2228 -1420
  2. package/build/build-cjs/src/booking-wizard/features/booking/selectors.d.ts +1 -1
  3. package/build/build-cjs/src/booking-wizard/features/travelers-form/validate-form.d.ts +2 -2
  4. package/build/build-cjs/src/booking-wizard/types.d.ts +23 -2
  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 -8
  12. package/build/build-esm/index.js +2232 -1424
  13. package/build/build-esm/src/booking-wizard/features/booking/selectors.d.ts +1 -1
  14. package/build/build-esm/src/booking-wizard/features/travelers-form/validate-form.d.ts +2 -2
  15. package/build/build-esm/src/booking-wizard/types.d.ts +23 -2
  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 -8
  23. package/package.json +2 -2
  24. package/styles/components/_slider.scss +0 -8
@@ -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,21 @@ 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",
214
231
  WEIGHT: "الوزن",
215
232
  WEIGHT_UNIT_KG: "kg",
216
233
  WEIGHT_UNIT_LB: "lb",
@@ -606,7 +623,9 @@ var SHARED$e = {
606
623
  ADULTS: "Voksne",
607
624
  CHILDREN: "Børn",
608
625
  SELECT: "Vælg",
609
- SELECTED: "Valgt"
626
+ SELECTED: "Valgt",
627
+ BOOKED: "Booket",
628
+ SAVE: "Gem"
610
629
  };
611
630
  var SIDEBAR$e = {
612
631
  OVERVIEW: "Oversigt",
@@ -664,6 +683,21 @@ var TRAVELERS_FORM$e = {
664
683
  FIRST_NAME: "Fornavn",
665
684
  LAST_NAME: "Efternavn",
666
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",
667
701
  WEIGHT: "Vægt",
668
702
  WEIGHT_UNIT_KG: "kg",
669
703
  WEIGHT_UNIT_LB: "lb",
@@ -1059,7 +1093,9 @@ var SHARED$d = {
1059
1093
  ADULTS: "Erwachsene",
1060
1094
  CHILDREN: "Kinder",
1061
1095
  SELECT: "Auswählen",
1062
- SELECTED: "Ausgewählt"
1096
+ SELECTED: "Ausgewählt",
1097
+ BOOKED: "Gebucht",
1098
+ SAVE: "Speichern"
1063
1099
  };
1064
1100
  var SIDEBAR$d = {
1065
1101
  OVERVIEW: "Übersicht",
@@ -1117,6 +1153,21 @@ var TRAVELERS_FORM$d = {
1117
1153
  FIRST_NAME: "Vorname",
1118
1154
  LAST_NAME: "Nachname",
1119
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",
1120
1171
  WEIGHT: "Gewicht",
1121
1172
  WEIGHT_UNIT_KG: "kg",
1122
1173
  WEIGHT_UNIT_LB: "lb",
@@ -1512,7 +1563,9 @@ var SHARED$c = {
1512
1563
  ADULTS: "Adults",
1513
1564
  CHILDREN: "Children",
1514
1565
  SELECT: "Select",
1515
- SELECTED: "Selected"
1566
+ SELECTED: "Selected",
1567
+ BOOKED: "Booked",
1568
+ SAVE: "Save"
1516
1569
  };
1517
1570
  var SIDEBAR$c = {
1518
1571
  OVERVIEW: "Summary",
@@ -1571,6 +1624,21 @@ var TRAVELERS_FORM$c = {
1571
1624
  FIRST_NAME: "First name",
1572
1625
  LAST_NAME: "Last name",
1573
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",
1574
1642
  WEIGHT: "Weight",
1575
1643
  WEIGHT_UNIT_KG: "kg",
1576
1644
  WEIGHT_UNIT_LB: "lb",
@@ -1969,7 +2037,9 @@ var SHARED$b = {
1969
2037
  ADULTS: "Adultos",
1970
2038
  CHILDREN: "Niños",
1971
2039
  SELECT: "Seleccionar",
1972
- SELECTED: "Seleccionado"
2040
+ SELECTED: "Seleccionado",
2041
+ BOOKED: "Reservado",
2042
+ SAVE: "Guardar"
1973
2043
  };
1974
2044
  var SIDEBAR$b = {
1975
2045
  OVERVIEW: "Resumen",
@@ -2027,6 +2097,21 @@ var TRAVELERS_FORM$b = {
2027
2097
  FIRST_NAME: "Nombre",
2028
2098
  LAST_NAME: "Apellido",
2029
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",
2030
2115
  WEIGHT: "Peso",
2031
2116
  WEIGHT_UNIT_KG: "kg",
2032
2117
  WEIGHT_UNIT_LB: "lb",
@@ -2422,7 +2507,9 @@ var SHARED$a = {
2422
2507
  ADULTS: "Adultes",
2423
2508
  CHILDREN: "Enfants",
2424
2509
  SELECT: "Sélectionner",
2425
- SELECTED: "Sélectionné"
2510
+ SELECTED: "Sélectionné",
2511
+ BOOKED: "Réservé",
2512
+ SAVE: "Enregistrer"
2426
2513
  };
2427
2514
  var SIDEBAR$a = {
2428
2515
  OVERVIEW: "Aperçu",
@@ -2481,6 +2568,21 @@ var TRAVELERS_FORM$a = {
2481
2568
  FIRST_NAME: "Prénom",
2482
2569
  LAST_NAME: "Nom",
2483
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",
2484
2586
  WEIGHT: "Poids",
2485
2587
  WEIGHT_UNIT_KG: "kg",
2486
2588
  WEIGHT_UNIT_LB: "lb",
@@ -2879,7 +2981,9 @@ var SHARED$9 = {
2879
2981
  ADULTS: "Adultes",
2880
2982
  CHILDREN: "Enfants",
2881
2983
  SELECT: "Sélectionner",
2882
- SELECTED: "Sélectionné"
2984
+ SELECTED: "Sélectionné",
2985
+ BOOKED: "Réservé",
2986
+ SAVE: "Enregistrer"
2883
2987
  };
2884
2988
  var SIDEBAR$9 = {
2885
2989
  OVERVIEW: "Résumé",
@@ -2937,6 +3041,21 @@ var TRAVELERS_FORM$9 = {
2937
3041
  FIRST_NAME: "Prénom",
2938
3042
  LAST_NAME: "Nom",
2939
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",
2940
3059
  WEIGHT: "Poids",
2941
3060
  WEIGHT_UNIT_KG: "kg",
2942
3061
  WEIGHT_UNIT_LB: "lb",
@@ -3332,7 +3451,9 @@ var SHARED$8 = {
3332
3451
  ADULTS: "Fullorðnir",
3333
3452
  CHILDREN: "Börn",
3334
3453
  SELECT: "Velja",
3335
- SELECTED: "Valið"
3454
+ SELECTED: "Valið",
3455
+ BOOKED: "Bókað",
3456
+ SAVE: "Vista"
3336
3457
  };
3337
3458
  var SIDEBAR$8 = {
3338
3459
  OVERVIEW: "Yfirlit",
@@ -3390,6 +3511,21 @@ var TRAVELERS_FORM$8 = {
3390
3511
  FIRST_NAME: "Fornafn",
3391
3512
  LAST_NAME: "Eftirnafn",
3392
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",
3393
3529
  WEIGHT: "Þyngd",
3394
3530
  WEIGHT_UNIT_KG: "kg",
3395
3531
  WEIGHT_UNIT_LB: "lb",
@@ -3785,7 +3921,9 @@ var SHARED$7 = {
3785
3921
  ADULTS: "Adulti",
3786
3922
  CHILDREN: "Bambini",
3787
3923
  SELECT: "Seleziona",
3788
- SELECTED: "Selezionato"
3924
+ SELECTED: "Selezionato",
3925
+ BOOKED: "Prenotato",
3926
+ SAVE: "Salva"
3789
3927
  };
3790
3928
  var SIDEBAR$7 = {
3791
3929
  OVERVIEW: "Riepilogo",
@@ -3843,6 +3981,21 @@ var TRAVELERS_FORM$7 = {
3843
3981
  FIRST_NAME: "Nome",
3844
3982
  LAST_NAME: "Cognome",
3845
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",
3846
3999
  WEIGHT: "Peso",
3847
4000
  WEIGHT_UNIT_KG: "kg",
3848
4001
  WEIGHT_UNIT_LB: "lb",
@@ -4238,7 +4391,9 @@ var SHARED$6 = {
4238
4391
  ADULTS: "Volwassenen",
4239
4392
  CHILDREN: "Kinderen",
4240
4393
  SELECT: "Selecteer",
4241
- SELECTED: "Geselecteerd"
4394
+ SELECTED: "Geselecteerd",
4395
+ BOOKED: "Geboekt",
4396
+ SAVE: "Opslaan"
4242
4397
  };
4243
4398
  var SIDEBAR$6 = {
4244
4399
  OVERVIEW: "Overzicht",
@@ -4297,6 +4452,21 @@ var TRAVELERS_FORM$6 = {
4297
4452
  FIRST_NAME: "Voornaam",
4298
4453
  LAST_NAME: "Naam",
4299
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",
4300
4470
  WEIGHT: "Gewicht",
4301
4471
  WEIGHT_UNIT_KG: "kg",
4302
4472
  WEIGHT_UNIT_LB: "lb",
@@ -4695,7 +4865,9 @@ var SHARED$5 = {
4695
4865
  ADULTS: "Volwassenen",
4696
4866
  CHILDREN: "Kinderen",
4697
4867
  SELECT: "Selecteren",
4698
- SELECTED: "Geselecteerd"
4868
+ SELECTED: "Geselecteerd",
4869
+ BOOKED: "Geboekt",
4870
+ SAVE: "Opslaan"
4699
4871
  };
4700
4872
  var SIDEBAR$5 = {
4701
4873
  OVERVIEW: "Overzicht",
@@ -4753,6 +4925,21 @@ var TRAVELERS_FORM$5 = {
4753
4925
  FIRST_NAME: "Voornaam",
4754
4926
  LAST_NAME: "Achternaam",
4755
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",
4756
4943
  WEIGHT: "Gewicht",
4757
4944
  WEIGHT_UNIT_KG: "kg",
4758
4945
  WEIGHT_UNIT_LB: "lb",
@@ -5148,7 +5335,9 @@ var SHARED$4 = {
5148
5335
  ADULTS: "Voksne",
5149
5336
  CHILDREN: "Barn",
5150
5337
  SELECT: "Velg",
5151
- SELECTED: "Valgt"
5338
+ SELECTED: "Valgt",
5339
+ BOOKED: "Booket",
5340
+ SAVE: "Lagre"
5152
5341
  };
5153
5342
  var SIDEBAR$4 = {
5154
5343
  OVERVIEW: "Oversikt",
@@ -5206,6 +5395,21 @@ var TRAVELERS_FORM$4 = {
5206
5395
  FIRST_NAME: "Fornavn",
5207
5396
  LAST_NAME: "Etternavn",
5208
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",
5209
5413
  WEIGHT: "Vekt",
5210
5414
  WEIGHT_UNIT_KG: "kg",
5211
5415
  WEIGHT_UNIT_LB: "lb",
@@ -5601,7 +5805,9 @@ var SHARED$3 = {
5601
5805
  ADULTS: "Dorośli",
5602
5806
  CHILDREN: "Dzieci",
5603
5807
  SELECT: "Wybierz",
5604
- SELECTED: "Wybrane"
5808
+ SELECTED: "Wybrane",
5809
+ BOOKED: "Zarezerwowane",
5810
+ SAVE: "Zapisz"
5605
5811
  };
5606
5812
  var SIDEBAR$3 = {
5607
5813
  OVERVIEW: "Podsumowanie",
@@ -5659,6 +5865,21 @@ var TRAVELERS_FORM$3 = {
5659
5865
  FIRST_NAME: "Imię",
5660
5866
  LAST_NAME: "Nazwisko",
5661
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",
5662
5883
  WEIGHT: "Waga",
5663
5884
  WEIGHT_UNIT_KG: "kg",
5664
5885
  WEIGHT_UNIT_LB: "lb",
@@ -6054,7 +6275,9 @@ var SHARED$2 = {
6054
6275
  ADULTS: "Adultos",
6055
6276
  CHILDREN: "Crianças",
6056
6277
  SELECT: "Selecionar",
6057
- SELECTED: "Selecionado"
6278
+ SELECTED: "Selecionado",
6279
+ BOOKED: "Reservado",
6280
+ SAVE: "Guardar"
6058
6281
  };
6059
6282
  var SIDEBAR$2 = {
6060
6283
  OVERVIEW: "Resumo",
@@ -6112,6 +6335,21 @@ var TRAVELERS_FORM$2 = {
6112
6335
  FIRST_NAME: "Nome",
6113
6336
  LAST_NAME: "Apelido",
6114
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",
6115
6353
  WEIGHT: "Peso",
6116
6354
  WEIGHT_UNIT_KG: "kg",
6117
6355
  WEIGHT_UNIT_LB: "lb",
@@ -6507,7 +6745,9 @@ var SHARED$1 = {
6507
6745
  ADULTS: "Vuxna",
6508
6746
  CHILDREN: "Barn",
6509
6747
  SELECT: "Välj",
6510
- SELECTED: "Vald"
6748
+ SELECTED: "Vald",
6749
+ BOOKED: "Bokad",
6750
+ SAVE: "Spara"
6511
6751
  };
6512
6752
  var SIDEBAR$1 = {
6513
6753
  OVERVIEW: "Översikt",
@@ -6565,6 +6805,21 @@ var TRAVELERS_FORM$1 = {
6565
6805
  FIRST_NAME: "Förnamn",
6566
6806
  LAST_NAME: "Efternamn",
6567
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",
6568
6823
  WEIGHT: "Vikt",
6569
6824
  WEIGHT_UNIT_KG: "kg",
6570
6825
  WEIGHT_UNIT_LB: "lb",
@@ -6960,7 +7215,9 @@ var SHARED = {
6960
7215
  ADULTS: "大人",
6961
7216
  CHILDREN: "子供",
6962
7217
  SELECT: "選択",
6963
- SELECTED: "選択済み"
7218
+ SELECTED: "選択済み",
7219
+ BOOKED: "予約済み",
7220
+ SAVE: "保存"
6964
7221
  };
6965
7222
  var SIDEBAR = {
6966
7223
  OVERVIEW: "概要",
@@ -7018,6 +7275,21 @@ var TRAVELERS_FORM = {
7018
7275
  FIRST_NAME: "名",
7019
7276
  LAST_NAME: "姓",
7020
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",
7021
7293
  WEIGHT: "体重",
7022
7294
  WEIGHT_UNIT_KG: "kg",
7023
7295
  WEIGHT_UNIT_LB: "lb",
@@ -15121,6 +15393,34 @@ var getContentDraft = function (config, signal) {
15121
15393
  var apiKey = config.apiKey;
15122
15394
  return get(url, apiKey, config.token, signal, true);
15123
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
+ };
15124
15424
  /**
15125
15425
  * api/web/content/live/by-property
15126
15426
  * Gets Live content matching a property value (or values).
@@ -15248,6 +15548,7 @@ var ENDPOINT_TRAJECT = ENDPOINT$1 + "/traject";
15248
15548
  var ENDPOINT_ENTRY = function (magicLinkCode) {
15249
15549
  return ENDPOINT$1 + "/entry/" + magicLinkCode;
15250
15550
  };
15551
+ var ENDPOINT_SAVE_ENTRY = ENDPOINT$1 + "/entry/save";
15251
15552
  var startTransaction = function (config, signal) {
15252
15553
  var apiKey = config.apiKey;
15253
15554
  var url = "" + config.host + ENDPOINT_START_TRANSACTION;
@@ -15276,6 +15577,12 @@ var getEntry = function (config, magicLinkCode, signal) {
15276
15577
  var url = "" + config.host + ENDPOINT_ENTRY(magicLinkCode);
15277
15578
  return get(url, apiKey, config.token, signal, true);
15278
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
+ };
15279
15586
  var getTrajectEntry = function (config, request, signal) {
15280
15587
  var url = "" + config.host + ENDPOINT_TRAJECT;
15281
15588
  var apiKey = config.apiKey;
@@ -15410,6 +15717,8 @@ exports.getEntryList = getEntryList;
15410
15717
  exports.getEntryListv2 = getEntryListv2;
15411
15718
  exports.getEntryStatus = getEntryStatus;
15412
15719
  exports.getEntryTotals = getEntryTotals;
15720
+ exports.getFilteredContentDraft = getFilteredContentDraft;
15721
+ exports.getFilteredContentLive = getFilteredContentLive;
15413
15722
  exports.getInvoiceList = getInvoiceList;
15414
15723
  exports.getItinerary = getItinerary;
15415
15724
  exports.getLocations = getLocations;
@@ -15430,6 +15739,7 @@ exports.print = print;
15430
15739
  exports.readForm = readForm;
15431
15740
  exports.repriceAndIssueTicket = repriceAndIssueTicket;
15432
15741
  exports.resetPassword = resetPassword;
15742
+ exports.savePackagingEntry = savePackagingEntry;
15433
15743
  exports.search = search;
15434
15744
  exports.searchCountries = searchCountries;
15435
15745
  exports.searchElastic = searchElastic;
@@ -18760,332 +19070,1160 @@ const SUMMARY_STEP = 5;
18760
19070
  const CONFIRMATION_STEP = 6;
18761
19071
  const ERROR_STEP = 7;
18762
19072
 
18763
- const fetchPriceDetails$1 = async (apiSettings, request, signal, languageCode) => {
18764
- const tideClientConfig = buildTideClientConfig(apiSettings);
18765
- return await build.priceDetails(tideClientConfig, request, signal, languageCode);
18766
- };
18767
- const priceDetailsApi = {
18768
- 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 })));
18769
19077
  };
18770
19078
 
18771
- const updatePackageDetails = (packageDetails, changedLines) => {
18772
- return {
18773
- ...packageDetails,
18774
- options: packageDetails.options.map((opt) => {
18775
- return {
18776
- ...opt,
18777
- rooms: opt.isSelected ? updateRooms(opt.rooms, changedLines) : opt.rooms,
18778
- groups: opt.isSelected ? updateBookingGroups(opt.groups, changedLines) : opt.groups,
18779
- optionUnits: opt.isSelected ? updateOptionUnits(opt.optionUnits, changedLines) : opt.optionUnits,
18780
- optionPax: opt.isSelected ? updateOptionPax(opt.optionPax, changedLines) : opt.optionPax
18781
- };
18782
- })
18783
- };
18784
- };
18785
- // ROOMS
18786
- const updateRooms = (rooms, changedLines) => {
18787
- return rooms.map((room) => ({
18788
- ...room,
18789
- options: room.options.map((option) => ({
18790
- ...option,
18791
- price: changedLines.find((x) => x.entryLineGuid == option.entryLineGuid)?.price ?? option.price
18792
- }))
18793
- }));
18794
- };
18795
- // PAX
18796
- const updateOptionPax = (pax, changedLines) => {
18797
- return pax.map((p) => ({
18798
- ...p,
18799
- groups: updatePaxGroups(p.groups, changedLines)
18800
- }));
18801
- };
18802
- const updatePaxGroups = (groups, changedLines) => {
18803
- return groups.map((g) => ({
18804
- ...g,
18805
- options: g.options.map((o) => ({
18806
- ...o,
18807
- groups: updatePaxGroups(o.groups, changedLines),
18808
- line: {
18809
- ...o.line,
18810
- price: changedLines.find((cl) => cl.entryLineGuid === o.line.entryLineGuid)?.price ?? o.line.price
18811
- },
18812
- alternatives: o.alternatives.map((a) => ({
18813
- ...a,
18814
- price: changedLines.find((cl) => cl.entryLineGuid === a.entryLineGuid)?.price ?? a.price
18815
- }))
18816
- }))
18817
- }));
18818
- };
18819
- // UNITS
18820
- const updateOptionUnits = (units, changedLines) => {
18821
- return units.map((u) => ({
18822
- ...u,
18823
- groups: updateUnitGroups(u.groups, changedLines)
18824
- }));
18825
- };
18826
- const updateUnitGroups = (groups, changedLines) => {
18827
- return groups.map((g) => ({
18828
- ...g,
18829
- options: g.options.map((o) => ({
18830
- ...o,
18831
- groups: updateUnitGroups(o.groups, changedLines),
18832
- pax: updateOptionPax(o.pax, changedLines),
18833
- line: {
18834
- ...o.line,
18835
- price: changedLines.find((cl) => cl.entryLineGuid === o.line.entryLineGuid)?.price ?? o.line.price
18836
- },
18837
- alternatives: o.alternatives.map((a) => ({
18838
- ...a,
18839
- price: changedLines.find((cl) => cl.entryLineGuid === a.entryLineGuid)?.price ?? a.price
18840
- }))
18841
- }))
18842
- }));
18843
- };
18844
- // BOOKING
18845
- const updateBookingGroups = (groups, changedLines) => {
18846
- return groups.map((g) => ({
18847
- ...g,
18848
- options: g.options.map((o) => ({
18849
- ...o,
18850
- groups: updateBookingGroups(o.groups, changedLines),
18851
- units: updateOptionUnits(o.units, changedLines),
18852
- pax: updateOptionPax(o.pax, changedLines),
18853
- line: {
18854
- ...o.line,
18855
- price: changedLines.find((cl) => cl.entryLineGuid === o.line.entryLineGuid)?.price ?? o.line.price
18856
- },
18857
- alternatives: o.alternatives.map((a) => ({
18858
- ...a,
18859
- price: changedLines.find((cl) => cl.entryLineGuid === a.entryLineGuid)?.price ?? a.price
18860
- }))
18861
- }))
18862
- }));
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)))))));
18863
19084
  };
18864
19085
 
18865
- const fetchPriceDetails = toolkit.createAsyncThunk('priceDetails/fetchPriceDetails', async (_, { dispatch, getState, signal }) => {
18866
- const state = getState();
18867
- const languageCode = selectLanguageCode(state);
18868
- const request = selectBookingPackageBookRequest(state);
18869
- if (!request) {
18870
- throw Error('Product not available.');
18871
- }
18872
- dispatch(setFetchingPriceDetails(true));
18873
- dispatch(resetPriceDetails());
18874
- const priceDetails = await priceDetailsApi.fetchPriceDetails(state.apiSettings, request, signal, languageCode);
18875
- dispatch(setFetchingPriceDetails(false));
18876
- const packageDetails = selectPackageDetails(state);
18877
- if (packageDetails) {
18878
- const updatedPackageDetails = updatePackageDetails(packageDetails, priceDetails.changedLines);
18879
- 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 };
18880
19100
  }
18881
- return priceDetails;
18882
- });
18883
- const priceDetailsSlice = toolkit.createSlice({
18884
- name: 'priceDetails',
18885
- initialState: {
18886
- priceDetails: [],
18887
- notifications: [],
18888
- isBusy: false
18889
- },
18890
- reducers: {
18891
- setFetchingPriceDetails(state, action) {
18892
- state.isBusy = action.payload;
18893
- },
18894
- setNotifications(state, action) {
18895
- state.notifications = action.payload;
18896
- },
18897
- resetPriceDetails(state, _) {
18898
- state.priceDetails = [];
18899
- }
18900
- },
18901
- extraReducers: (builder) => {
18902
- builder.addCase(fetchPriceDetails.fulfilled, (state, action) => {
18903
- if (action.payload) {
18904
- state.deposit = action.payload.deposit;
18905
- state.commission = action.payload.commission;
18906
- state.priceDetails = action.payload.details;
18907
- state.notifications = action.payload.notifications;
18908
- }
18909
- });
18910
- builder.addCase(fetchPriceDetails.rejected, (state) => {
18911
- 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 }
18912
19129
  });
18913
- }
18914
- });
18915
- const { setFetchingPriceDetails } = priceDetailsSlice.actions;
18916
- const { resetPriceDetails, setNotifications } = priceDetailsSlice.actions;
18917
- const selectNotifications = (state) => state.priceDetails.notifications;
18918
- 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
+ };
18919
19163
 
18920
- const selectHasMounted = (state) => state.booking.hasMounted;
18921
- const selectGeneratePaymentUrl = (state) => state.booking.generatePaymentUrl;
18922
- const selectSkipPaymentWithAgent = (state) => state.booking.skipPaymentWithAgent;
18923
- const selectIsFetchingProductOptions = (state) => state.booking.isBusy;
18924
- const selectDepartureFlight = (state) => state.booking.package?.outwardFlights?.find((x) => x.isSelected);
18925
- const selectReturnFlight = (state) => state.booking.package?.returnFlights?.find((x) => x.isSelected);
18926
- const selectPackageRooms = (state) => state.booking.package?.options.find((x) => x.isSelected)?.rooms;
18927
- const selectIncludedServiceTypes = (state) => selectActiveOption(state)?.includedServiceTypes;
18928
- const selectAvailabilities = (state) => state.booking.package?.options.find((x) => x.isSelected)?.availabilities;
18929
- const selectPackageTags = (state) => state.booking.package?.tags;
18930
- const selectIsOnRequest = (state) => state.booking.package?.options.find((x) => x.isSelected)?.isOnRequest;
18931
- const selectPackageOptionUnits = (state) => state.booking.package?.options.find((x) => x.isSelected)?.optionUnits;
18932
- const selectPackageOptionPax = (state) => state.booking.package?.options.find((x) => x.isSelected)?.optionPax;
18933
- const selectPackageGroups = (state) => state.booking.package?.options.find((x) => x.isSelected)?.groups;
18934
- const selectPackageDetails = (state) => state.booking.package;
18935
- const selectPaxCustomFields = (state) => state.booking.package?.paxCustomFields ?? [];
18936
- const selectPackageFlights = (state) => ({
18937
- outward: state.booking.package?.outwardFlights,
18938
- return: state.booking.package?.returnFlights
18939
- });
18940
- const selectFlightMetaData = (state) => state.booking.package?.flightInfo.metaData;
18941
- const selectActiveOption = (state) => state.booking.package?.options.find((x) => x.isSelected);
18942
- const selectPackageAirlineGroups = toolkit.createSelector(selectActiveOption, selectDepartureFlight, selectReturnFlight, (option, departureFlight, returnFlight) => option?.airlineGroups.filter((x) => x.flightIds.includes(departureFlight?.entryLineGuid ?? '') || x.flightIds.includes(returnFlight?.entryLineGuid ?? '')));
18943
- const selectPackageAirportGroups = toolkit.createSelector(selectActiveOption, selectDepartureFlight, selectReturnFlight, (option, departureFlight, returnFlight) => option?.airportGroups.filter((x) => x.flightIds.includes(departureFlight?.entryLineGuid ?? '') || x.flightIds.includes(returnFlight?.entryLineGuid ?? '')));
18944
- const selectRoomOptionDepartureFlightsMetaData = toolkit.createSelector(selectActiveOption, selectFlightMetaData, (ativeOption, flightMetaData) => {
18945
- const roomOptions = ativeOption?.rooms.map((r) => r.options.filter((o) => o.isSelected).shift());
18946
- return roomOptions?.map((ro) => flightMetaData?.find((f) => f.entryLineGuid === ro?.entryLineGuid)?.flightMetaData).filter((f) => f !== undefined) ?? [];
18947
- });
18948
- const selectRoomOptionReturnFlightsMetaData = toolkit.createSelector(selectActiveOption, selectFlightMetaData, (ativeOption, flightMetaData) => {
18949
- const selectedRoomOptions = ativeOption?.rooms.map((r) => r.options.filter((o) => o.isSelected));
18950
- // check if there are return flights selected
18951
- if (selectedRoomOptions?.some((ro) => ro.length > 1)) {
18952
- const roomOptions = ativeOption?.rooms.map((r) => r.options
18953
- .filter((o) => o.isSelected)
18954
- .reverse()
18955
- .shift());
18956
- return roomOptions?.map((ro) => flightMetaData?.find((f) => f.entryLineGuid === ro?.entryLineGuid)?.flightMetaData).filter((f) => f !== undefined) ?? [];
18957
- }
18958
- return undefined;
18959
- });
18960
- const selectApiSettings = (state) => state.apiSettings;
18961
- const selectIsUnavailable = (state) => state.booking.isUnavailable;
18962
- const selectRequestRooms = (state) => state.booking.package?.options.find((x) => x.isSelected)?.requestRooms;
18963
- const selectOfficeId = (state) => state.booking.officeId;
18964
- const selectLanguageCode = (state) => state.booking.languageCode;
18965
- const selectCurrencyCode = (state) => state.booking.package?.currencyCode ?? 'EUR';
18966
- const selectLanguage = (state) => state.booking.languageCode;
18967
- const selectAllDynamicTranslations = (state) => state.booking.translations;
18968
- const selectDynamicTranslations = toolkit.createSelector([selectAllDynamicTranslations, selectLanguage], (allDynamicTranslations, language) => {
18969
- return allDynamicTranslations?.find((x) => x.language == language)?.value ?? {};
18970
- });
18971
- // Host override files are hand-maintained, and a null only ever blanks a key the component
18972
- // then reads, so treat it like a missing key (lodash merge already skips undefined).
18973
- const withoutNulls = (value) => lodash.isPlainObject(value)
18974
- ? lodash.mapValues(lodash.pickBy(value, (x) => x !== null), withoutNulls)
18975
- : value;
18976
- // getTranslations deep-merges the overrides over a clone of the built-in translations, so a
18977
- // host override of a nested group (TRAVELERS_FORM.VALIDATION, TRAVELERS_FORM.COUNTRIES)
18978
- // only replaces the keys it actually contains. Merging per section one level deep dropped
18979
- // every other built-in key of such a group, which made those keys resolve to undefined and
18980
- // crash format() in the consuming component.
18981
- const selectTranslations = toolkit.createSelector([selectLanguage, selectDynamicTranslations], (language, dynamicTranslations) => getTranslations(language, lodash.isEmpty(dynamicTranslations) ? undefined : withoutNulls(dynamicTranslations)));
18982
- const selectBookingOptions = (state) => state.booking.bookingOptions;
18983
- const selectBookingType = (state) => state.booking.bookingType;
18984
- const selectTagIds = (state) => state.booking.tagIds;
18985
- const selectAgentAdressId = (state) => state.booking.agentAdressId;
18986
- const selectProductAttributes = (state) => state.booking.productAttributes;
18987
- const selectBookingAttributes = (state) => state.booking.bookingAttributes;
18988
- const selectBookingNumber = (state) => state.booking.bookingNumber;
18989
- const selectIsOption = (state) => state.booking.isOption;
18990
- const selectBookingRooms = (state) => state.booking.bookingAttributes?.rooms;
18991
- const selectBookingRemarks = (state) => state.booking.remarks;
18992
- const selectVoucherCodes = (state) => state.booking.voucherCodes;
18993
- const selectCalculateDeposit = (state) => state.booking.calculateDeposit;
18994
- const selectShowCommission = (state) => state.booking.showCommission;
18995
- const selectIsRetry = (state) => state.booking.isRetry;
18996
- const selectStartDate = (state) => state.booking.package?.options.find((x) => x.isSelected)?.fromDate;
18997
- const selectEndDate = (state) => state.booking.package?.options.find((x) => x.isSelected)?.toDate;
18998
- const selectAgents = (state) => state.booking.agents;
18999
- const selectCountries = (state) => state.booking.countries;
19000
- const selectProductCode = (state) => state.booking.productAttributes?.productCode;
19001
- const selectAccommodationCodes = (state) => {
19002
- const accommodationCodes = [];
19003
- state.booking.package?.options.forEach((o) => {
19004
- o.rooms.forEach((r) => r.options.forEach((ro) => {
19005
- if (!accommodationCodes.some((y) => y === ro.accommodationCode)) {
19006
- accommodationCodes.push(ro.accommodationCode);
19007
- }
19008
- }));
19009
- });
19010
- 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)))));
19011
19180
  };
19012
- const selectAccommodationViews = (state) => state.booking.accommodationViews;
19013
- const selectMappingInfo = (state) => state.booking.package?.options.find((x) => x.isSelected)?.mappingInfo;
19014
- const selectBookingQuery = (state) => {
19015
- const bookingAttributes = state.booking.bookingAttributes;
19016
- if (!bookingAttributes) {
19017
- return undefined;
19018
- }
19019
- const params = {};
19020
- Object.entries(bookingAttributes).forEach(([key, value]) => {
19021
- if (key === 'startDate' || key === 'endDate') {
19022
- value = dateFns.format(dateFns.parseISO(value), 'yyyy-MM-dd');
19023
- }
19024
- if (key === 'rooms') {
19025
- value = JsonURL__default["default"].stringify(value.map((room) => lodash.omit(room, ['children'])), {
19026
- AQF: true
19027
- });
19028
- }
19029
- if (key === 'allotmentIds' && !value.length) {
19030
- value = undefined;
19031
- }
19032
- if (key === 'flight' && value) {
19033
- var flightInfo = value;
19034
- value = JsonURL__default["default"].stringify(flightInfo, { AQF: true });
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);
19035
19187
  }
19036
- if (value) {
19037
- params[key] = value;
19188
+ };
19189
+ const handleClearClick = (e) => {
19190
+ if (onClear) {
19191
+ onClear();
19038
19192
  }
19039
- });
19040
- return params;
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
+ })))));
19041
19221
  };
19042
- const selectBookingQueryString = toolkit.createSelector(selectBookingQuery, (params) => {
19043
- if (!params) {
19044
- return undefined;
19045
- }
19046
- return Object.keys(params)
19047
- .filter((key) => typeof params[key] !== 'undefined')
19048
- .map((key) => `${key}=${params[key]}`)
19049
- .join('&');
19050
- });
19051
- toolkit.createSelector(selectTravelersFormValues, (formValues) => formValues?.mainBookerId);
19052
- const selectBookingPackagePax = toolkit.createSelector(selectTravelersFormValues, (formValues) => {
19053
- var pax = [];
19054
- formValues?.rooms.forEach((r) => r.adults.forEach((x) => {
19055
- const adultPax = buildPax(x, formValues?.mainBookerId);
19056
- if (adultPax.isMainBooker) {
19057
- adultPax.mobilePhone = formValues?.phone;
19058
- adultPax.email = formValues?.email;
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;
19059
19255
  }
19060
- pax.push(adultPax);
19061
- }));
19062
- formValues?.rooms.forEach((r) => r.children.forEach((x) => {
19063
- pax.push(buildPax(x));
19064
- }));
19065
- return pax;
19066
- });
19067
- const selectBookingAddress = toolkit.createSelector(selectTravelersFormValues, selectBookingPackagePax, selectBookingType, (formValues, pax, bookingType) => {
19068
- const mainBooker = pax.find((x) => x.isMainBooker);
19069
- if (!mainBooker || bookingType == 'b2b')
19070
- return undefined;
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) {
19071
19308
  return {
19072
- name: `${mainBooker.firstName} ${mainBooker.lastName}`,
19073
- street: formValues?.street,
19074
- number: formValues?.houseNumber,
19075
- box: formValues?.box,
19076
- postalCode: formValues?.zipCode,
19077
- location: formValues?.place,
19078
- country: formValues?.country,
19079
- mobilePhone: formValues?.phone,
19080
- email: formValues?.email
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 ?? []
19081
19335
  };
19082
- });
19083
- const selectBookingPackageRequest = toolkit.createSelector(selectOfficeId, selectAgentId, selectAgentAdressId, (officeId, agentId, agentAdressId) => {
19084
- const agencyId = (agentId ?? agentAdressId ?? 0) > 0 ? agentId ?? agentAdressId : null;
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;
19085
19342
  return {
19086
- officeId: officeId,
19087
- agentId: agencyId,
19088
- payload: null
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;
20166
+ }
20167
+ if (key === 'flight' && value) {
20168
+ var flightInfo = value;
20169
+ value = JsonURL__default["default"].stringify(flightInfo, { AQF: true });
20170
+ }
20171
+ if (value) {
20172
+ params[key] = value;
20173
+ }
20174
+ });
20175
+ return params;
20176
+ };
20177
+ const selectBookingQueryString = toolkit.createSelector(selectBookingQuery, (params) => {
20178
+ if (!params) {
20179
+ return undefined;
20180
+ }
20181
+ return Object.keys(params)
20182
+ .filter((key) => typeof params[key] !== 'undefined')
20183
+ .map((key) => `${key}=${params[key]}`)
20184
+ .join('&');
20185
+ });
20186
+ toolkit.createSelector(selectTravelersFormValues, (formValues) => formValues?.mainBookerId);
20187
+ const selectBookingPackagePax = toolkit.createSelector(selectTravelersFormValues, (formValues) => {
20188
+ var pax = [];
20189
+ formValues?.rooms.forEach((r) => r.adults.forEach((x) => {
20190
+ const adultPax = buildPax(x, formValues?.mainBookerId);
20191
+ if (adultPax.isMainBooker) {
20192
+ adultPax.mobilePhone = formValues?.phone;
20193
+ adultPax.email = formValues?.email;
20194
+ }
20195
+ pax.push(adultPax);
20196
+ }));
20197
+ formValues?.rooms.forEach((r) => r.children.forEach((x) => {
20198
+ pax.push(buildPax(x));
20199
+ }));
20200
+ return pax;
20201
+ });
20202
+ const selectBookingAddress = toolkit.createSelector(selectTravelersFormValues, selectBookingPackagePax, selectBookingType, (formValues, pax, bookingType) => {
20203
+ const mainBooker = pax.find((x) => x.isMainBooker);
20204
+ if (!mainBooker || bookingType == 'b2b')
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;
20209
+ return {
20210
+ name: `${mainBooker.firstName} ${mainBooker.lastName}`,
20211
+ street: mainBookerAddress?.street || formValues?.street,
20212
+ number: mainBookerAddress?.number || formValues?.houseNumber,
20213
+ box: formValues?.box,
20214
+ postalCode: mainBookerAddress?.postalCode || formValues?.zipCode,
20215
+ location: mainBookerAddress?.location || formValues?.place,
20216
+ country: mainBookerAddress?.country || formValues?.country,
20217
+ mobilePhone: formValues?.phone,
20218
+ email: formValues?.email
20219
+ };
20220
+ });
20221
+ const selectBookingPackageRequest = toolkit.createSelector(selectOfficeId, selectAgentId, selectAgentAdressId, (officeId, agentId, agentAdressId) => {
20222
+ const agencyId = (agentId ?? agentAdressId ?? 0) > 0 ? agentId ?? agentAdressId : null;
20223
+ return {
20224
+ officeId: officeId,
20225
+ agentId: agencyId,
20226
+ payload: null
19089
20227
  };
19090
20228
  });
19091
20229
  const selectBookingPackageBookRequest = toolkit.createSelector(selectBookingPackageRequest, selectBookingOptions, selectBookingType, selectBookingPackagePax, selectBookingAddress, selectPackageDetails, selectCalculateDeposit, selectShowCommission, selectAgentId, selectGeneratePaymentUrl, selectSkipPaymentWithAgent, selectNotifications, selectTagIds, selectBookingRemarks, selectVoucherCodes, (bookingPackageRequest, bookingOptions, bookingType, pax, address, packageDetails, calculateDeposit, showCommission, agentId, generatePaymentUrl, skipPaymentWithAgent, notifications, tagIds, remarks, voucherCodes) => {
@@ -19151,22 +20289,11 @@ const selectBookingPackageBookRequest = toolkit.createSelector(selectBookingPack
19151
20289
  return bookingPackageRequest;
19152
20290
  });
19153
20291
  const selectTravelersFirstStep = (state) => state.booking.travelersFirstStep;
19154
- const buildPax = (traveler, mainBookerId) => {
19155
- return {
19156
- id: traveler.id,
19157
- gender: parseGender(traveler.gender),
19158
- firstName: traveler.firstName,
19159
- lastName: traveler.lastName,
19160
- dateOfBirth: traveler.birthDate,
19161
- age: traveler.birthDate ? null : traveler.age,
19162
- isMainBooker: traveler.id == mainBookerId,
19163
- weight: traveler.weight ?? null,
19164
- weightType: traveler.weightType ?? build.WeightType.kilograms,
19165
- height: traveler.height ?? null,
19166
- heightType: traveler.heightType ?? build.HeightType.centimeters,
19167
- customFields: traveler.customFields ?? []
19168
- };
19169
- };
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);
19170
20297
  const parseGender = (gender) => {
19171
20298
  switch (gender) {
19172
20299
  case 'm':
@@ -19674,9 +20801,7 @@ const SettingsContext = React__default["default"].createContext({
19674
20801
  travellers: {
19675
20802
  pathSuffix: '/reizigers',
19676
20803
  travelersFirstStep: false,
19677
- showAllCountries: false,
19678
- showWeightField: false,
19679
- showHeightField: false
20804
+ showAllCountries: false
19680
20805
  },
19681
20806
  summary: {
19682
20807
  pathSuffix: '/samenvatting',
@@ -22753,1072 +23878,440 @@ const SummaryPerPaxOptionGroup = ({ group }) => {
22753
23878
  opt.line.accommodationName,
22754
23879
  !lodash.isNil(opt.line?.regimeName) && ', ',
22755
23880
  opt.line.regimeName)),
22756
- getDateText(opt.line.startDate),
22757
- opt.line.startDate != opt.line.endDate ? ` > ${getDateText(opt.line.endDate)}` : '',
22758
- React__default["default"].createElement("ul", { className: "list" }, !lodash.isEmpty(opt?.groups) &&
22759
- opt?.groups.map((x) => {
22760
- if (!x.options.some((y) => y.isSelected))
22761
- return;
22762
- return React__default["default"].createElement(SummaryPerPaxOptionGroup, { group: x });
22763
- })))))));
22764
- return (React__default["default"].createElement(React__default["default"].Fragment, null, group.title ? (React__default["default"].createElement("li", { className: "list__item" },
22765
- React__default["default"].createElement("h6", null, group.title),
22766
- React__default["default"].createElement("ul", { className: "list" }, renderOptions()))) : (React__default["default"].createElement(React__default["default"].Fragment, null, renderOptions()))));
22767
- };
22768
-
22769
- const SummaryBookingOptionPax = ({ pax }) => {
22770
- return (React__default["default"].createElement(React__default["default"].Fragment, null, pax.groups.map((y, i) => {
22771
- const paxOptions = y.options.filter((y) => y.isSelected);
22772
- if (lodash.isEmpty(paxOptions))
22773
- return;
22774
- return React__default["default"].createElement(SummaryPerPaxOptionGroup, { key: i, group: y });
22775
- })));
22776
- };
22777
-
22778
- const SummaryPerUnitOptionGroup = ({ group }) => {
22779
- const groupOptions = group.options.filter((x) => x.isSelected);
22780
- const renderOptions = () => (React__default["default"].createElement(React__default["default"].Fragment, null, groupOptions.map((opt) => (React__default["default"].createElement("li", { key: opt.line.entryLineGuid, className: "list__item" },
22781
- opt.line.productName,
22782
- React__default["default"].createElement("ul", null,
22783
- React__default["default"].createElement("li", null,
22784
- opt.line.accommodationName,
22785
- !lodash.isNil(opt.line?.regimeName) && ', ',
22786
- opt.line.regimeName)),
22787
- getDateText(opt.line.startDate),
22788
- opt.line.startDate != opt.line.endDate ? ` > ${getDateText(opt.line.endDate)}` : '',
22789
- React__default["default"].createElement("ul", null,
22790
- !lodash.isEmpty(opt?.groups) &&
22791
- opt?.groups.map((x) => {
22792
- if (!x.options.some((y) => y.isSelected))
22793
- return;
22794
- return React__default["default"].createElement(SummaryPerUnitOptionGroup, { group: x });
22795
- }),
22796
- !lodash.isEmpty(opt?.pax) && opt?.pax.map((x) => React__default["default"].createElement(SummaryBookingOptionPax, { pax: x }))))))));
22797
- return (React__default["default"].createElement(React__default["default"].Fragment, null, group.title ? (React__default["default"].createElement("li", { className: "list__item" },
22798
- React__default["default"].createElement("h6", null, group.title),
22799
- React__default["default"].createElement("ul", { className: "list" }, renderOptions()))) : (React__default["default"].createElement(React__default["default"].Fragment, null, renderOptions()))));
22800
- };
22801
-
22802
- const SummaryBookingOptionUnit = ({ unit }) => {
22803
- return (React__default["default"].createElement(React__default["default"].Fragment, null, unit.groups.map((y) => {
22804
- const unitOptions = y.options.filter((y) => y.isSelected);
22805
- if (lodash.isEmpty(unitOptions))
22806
- return;
22807
- return React__default["default"].createElement(SummaryPerUnitOptionGroup, { group: y });
22808
- })));
22809
- };
22810
-
22811
- const SummaryFlight = ({ flightMetaData, header }) => {
22812
- const translations = reactRedux.useSelector(selectTranslations);
22813
- return (React__default["default"].createElement("li", null,
22814
- React__default["default"].createElement("h6", null, header),
22815
- parseFlightLines(flightMetaData)?.map((flightLine) => (React__default["default"].createElement("p", { key: flightLine.departureAirportIata },
22816
- flightLine.departureAirportDescription,
22817
- " (",
22818
- flightLine.departureAirportIata,
22819
- ") > ",
22820
- flightLine.arrivalAirportDescription,
22821
- " (",
22822
- flightLine.arrivalAirportIata,
22823
- ") ",
22824
- ' - ',
22825
- flightLine.travelClass,
22826
- " ",
22827
- ' - ',
22828
- flightLine.airlineCode,
22829
- flightLine.number,
22830
- React__default["default"].createElement("br", null),
22831
- getDateText(flightLine.departureDate),
22832
- " ",
22833
- flightLine.departureTime,
22834
- " > ",
22835
- getDateText(flightLine.arrivalDate),
22836
- " ",
22837
- flightLine.arrivalTime,
22838
- React__default["default"].createElement("br", null),
22839
- flightLine.airlineDescription))),
22840
- flightMetaData.luggageIncluded && React__default["default"].createElement("p", null, translations.SIDEBAR.LUGGAGE_INCLUDED)));
22841
- };
22842
-
22843
- const SummaryPerBookingOptionGroup = ({ group }) => {
22844
- const groupOptions = group.options.filter((x) => x.isSelected);
22845
- const renderOptions = () => (React__default["default"].createElement(React__default["default"].Fragment, null, groupOptions.map((opt) => (React__default["default"].createElement("li", { key: opt.line.entryLineGuid, className: "list__item" },
22846
- opt.line.productName,
22847
- React__default["default"].createElement("ul", null,
22848
- React__default["default"].createElement("li", null,
22849
- opt.line.accommodationName,
22850
- !lodash.isNil(opt.line?.regimeName) && ', ',
22851
- opt.line.regimeName)),
22852
- getDateText(opt.line.startDate),
22853
- opt.line.startDate != opt.line.endDate ? ` > ${getDateText(opt.line.endDate)}` : '',
22854
- React__default["default"].createElement("ul", null,
22855
- !lodash.isEmpty(opt?.groups) &&
22856
- opt?.groups.map((x) => {
22857
- if (!x.options.some((y) => y.isSelected))
22858
- return;
22859
- return React__default["default"].createElement(SummaryPerBookingOptionGroup, { group: x });
22860
- }),
22861
- !lodash.isEmpty(opt?.units) && opt?.units.map((x) => React__default["default"].createElement(SummaryBookingOptionUnit, { unit: x })),
22862
- !lodash.isEmpty(opt?.pax) && opt?.pax.map((x) => React__default["default"].createElement(SummaryBookingOptionPax, { pax: x }))))))));
22863
- return (React__default["default"].createElement(React__default["default"].Fragment, null, group.title ? (React__default["default"].createElement("li", { className: "list__item" },
22864
- React__default["default"].createElement("h6", null, group.title),
22865
- React__default["default"].createElement("ul", { className: "list" }, renderOptions()))) : (React__default["default"].createElement(React__default["default"].Fragment, null, renderOptions()))));
22866
- };
22867
-
22868
- const Summary = () => {
22869
- const dispatch = useAppDispatch();
22870
- const settings = React.useContext(SettingsContext);
22871
- const navigate = settings.skipRouter ? () => { } : reactRouter.useNavigate();
22872
- const [isSubmitting, setIsSubmitting] = React.useState(false);
22873
- const [checkboxes, setCheckboxes] = React.useState(settings.summary?.checkboxes);
22874
- const [remarks, setRemarks] = React.useState('');
22875
- const [voucher, setVoucher] = React.useState({});
22876
- const bookingQueryString = reactRedux.useSelector(selectBookingQueryString);
22877
- const travelersFirstStep = reactRedux.useSelector(selectTravelersFirstStep);
22878
- const travelerFormValues = reactRedux.useSelector(selectTravelersFormValues);
22879
- const packageDetails = reactRedux.useSelector(selectPackageDetails);
22880
- const activeOption = reactRedux.useSelector(selectActiveOption);
22881
- const apiSettings = reactRedux.useSelector(selectApiSettings);
22882
- const languageCode = reactRedux.useSelector(selectLanguageCode);
22883
- const officeId = reactRedux.useSelector(selectOfficeId);
22884
- const agentId = reactRedux.useSelector(selectAgentAdressId);
22885
- const departureFlight = reactRedux.useSelector(selectDepartureFlight);
22886
- const departureFlightMetaData = departureFlight?.flightMetaData ?? reactRedux.useSelector(selectRoomOptionDepartureFlightsMetaData)?.[0];
22887
- const returnFlight = reactRedux.useSelector(selectReturnFlight);
22888
- const returnFlightMetaData = returnFlight?.flightMetaData ?? reactRedux.useSelector(selectRoomOptionReturnFlightsMetaData)?.[0];
22889
- if (!travelerFormValues) {
22890
- if (settings.skipRouter) {
22891
- dispatch(setCurrentStep$1(TRAVELERS_FORM_STEP));
22892
- }
22893
- else {
22894
- navigate(`${!settings.skipBasePathInRouting ? settings.basePath : ''}?${bookingQueryString}`);
22895
- }
22896
- }
22897
- const rooms = reactRedux.useSelector(selectRooms);
22898
- const userValidated = reactRedux.useSelector(selectUserValidated);
22899
- const notifications = reactRedux.useSelector(selectNotifications);
22900
- const bookRequest = reactRedux.useSelector(selectBookingPackageBookRequest);
22901
- React.useEffect(() => {
22902
- // Every checkbox should be checked, or no checkboxes
22903
- const checkboxesValidated = !lodash.isNil(checkboxes) ? checkboxes.every((checkbox) => checkbox.isSelected) : true;
22904
- const notificationsValidated = !lodash.isNil(notifications) ? notifications.filter((x) => x.hasToBeConfirmed).every((checkbox) => checkbox.isConfirmed) : true;
22905
- dispatch(setUserValidated(checkboxesValidated && notificationsValidated));
22906
- }, [checkboxes, notifications]);
22907
- const handleSubmit = async (e) => {
22908
- e.preventDefault();
22909
- const submitter = e.nativeEvent.submitter?.name;
22910
- setIsSubmitting(true);
22911
- if (typeof window !== 'undefined') {
22912
- window.scrollTo(0, 0);
22913
- }
22914
- const tideClientConfig = buildTideClientConfig(apiSettings);
22915
- if (!bookRequest || !tideClientConfig) {
22916
- return;
22917
- }
22918
- if (bookRequest.payload.returnPaymentUrl) {
22919
- if (typeof window !== 'undefined') {
22920
- window.scrollTo(0, 0);
22921
- if (settings.paymentRedirectUrl) {
22922
- bookRequest.payload.redirectUrl = settings.paymentRedirectUrl;
22923
- }
22924
- else {
22925
- const location = window.location;
22926
- bookRequest.payload.redirectUrl = `${location.protocol}//${location.host}${settings.basePath ?? location.pathname}?${bookingQueryString}`;
22927
- }
22928
- }
22929
- }
22930
- try {
22931
- if (submitter === 'option') {
22932
- bookRequest.payload.status = build.EntryStatus.option;
22933
- bookRequest.payload.customStatusId = settings.customOptionStatus;
22934
- dispatch(setIsOption(true));
22935
- }
22936
- const bookingResponse = await build.book(tideClientConfig, bookRequest, undefined, languageCode);
22937
- // Booking successfull
22938
- dispatch(setBookingNumber$1(bookingResponse.number));
22939
- if (bookingResponse.paymentUrl) {
22940
- const event = new CustomEvent('tide-booking-payment-url', {
22941
- detail: {
22942
- paymentUrl: bookingResponse.paymentUrl,
22943
- bookingNumber: bookingResponse.number
22944
- },
22945
- cancelable: true,
22946
- bubbles: true
22947
- });
22948
- const target = typeof document !== 'undefined' ? document.getElementById('tide-booking') ?? window : window;
22949
- const shouldAutoRedirect = target.dispatchEvent(event);
22950
- if (shouldAutoRedirect) {
22951
- window.location.href = bookingResponse.paymentUrl;
22952
- }
22953
- }
22954
- else {
22955
- if (settings.skipRouter) {
22956
- dispatch(setCurrentStep$1(CONFIRMATION_STEP));
22957
- }
22958
- else {
22959
- navigate(`${!settings.skipBasePathInRouting ? settings.basePath : ''}${settings.confirmation.pathSuffix}?${bookingQueryString}`);
22960
- }
22961
- }
22962
- }
22963
- catch (error) {
22964
- if (settings.skipRouter) {
22965
- dispatch(setCurrentStep$1(ERROR_STEP));
22966
- }
22967
- else {
22968
- navigate(`${!settings.skipBasePathInRouting ? settings.basePath : ''}${settings.error.pathSuffix}?${bookingQueryString}`);
22969
- }
22970
- }
22971
- finally {
22972
- setIsSubmitting(false);
22973
- }
22974
- };
22975
- const handleNotificationChange = (id, checked) => {
22976
- const updatedNotifications = notifications.map((notification) => (notification.id === id ? { ...notification, isConfirmed: checked } : notification));
22977
- dispatch(setNotifications(updatedNotifications));
22978
- };
22979
- const handleCheckboxChange = (id, checked) => {
22980
- if (lodash.isNil(checkboxes)) {
22981
- return;
22982
- }
22983
- const newCheckboxes = [...checkboxes];
22984
- const index = lodash.findIndex(checkboxes, (checkbox) => checkbox.id === id);
22985
- newCheckboxes[index].isSelected = !newCheckboxes[index].isSelected;
22986
- setCheckboxes(newCheckboxes);
22987
- };
22988
- const handleRemarksChange = (text) => {
22989
- dispatch(setBookingRemarks(text));
22990
- setRemarks(text);
22991
- };
22992
- const handleValidateVoucher = async () => {
22993
- if (!voucher?.code)
22994
- return;
22995
- const request = {
22996
- officeId: officeId,
22997
- agentId: agentId,
22998
- payload: {
22999
- code: voucher.code,
23000
- otherCodes: bookRequest?.payload.voucherCodes ?? []
23001
- }
23002
- };
23003
- const tideClientConfig = buildTideClientConfig(apiSettings);
23004
- if (!tideClientConfig)
23005
- return;
23006
- const result = await build.validateVoucher(tideClientConfig, request);
23007
- if (result) {
23008
- setVoucher({
23009
- ...voucher,
23010
- isValidated: true,
23011
- isValid: result.isValid
23012
- });
23013
- }
23014
- };
23015
- const handleAddVoucher = (event) => {
23016
- if (!voucher.isValid)
23017
- return;
23018
- dispatch(setVoucherCodes([...(bookRequest?.payload.voucherCodes ?? []), voucher.code ?? ''].filter((x) => x !== '')));
23019
- dispatch(fetchPriceDetails());
23020
- setVoucher({});
23021
- };
23022
- const handleRemoveVoucher = (code) => {
23023
- dispatch(setVoucherCodes(bookRequest?.payload.voucherCodes?.filter((x) => x !== code) ?? []));
23024
- dispatch(fetchPriceDetails());
23025
- };
23026
- const goPrevious = () => {
23027
- dispatch(setCurrentStep$1(travelersFirstStep ? OPTIONS_FORM_STEP : TRAVELERS_FORM_STEP));
23028
- };
23029
- const translations = reactRedux.useSelector(selectTranslations);
23030
- return (React__default["default"].createElement(React__default["default"].Fragment, null,
23031
- isSubmitting && (settings.loaderComponent || React__default["default"].createElement(Loader, null)),
23032
- !isSubmitting && (React__default["default"].createElement("form", { className: "form", name: "booking--summary", id: "booking--summary", onSubmit: handleSubmit },
23033
- React__default["default"].createElement("div", { className: "form__booking--summary" },
23034
- React__default["default"].createElement("div", { className: "form__region" },
23035
- React__default["default"].createElement("div", { className: "form__row" },
23036
- React__default["default"].createElement("div", { className: "form__group" },
23037
- React__default["default"].createElement("div", { className: "form__region-header" },
23038
- React__default["default"].createElement("h5", { className: "form__region-heading" }, translations.SUMMARY.PERSONAL_DETAILS)))),
23039
- rooms.map((r, rIndex) => (React__default["default"].createElement("div", { className: "form__row", key: rIndex },
23040
- React__default["default"].createElement("div", { className: "form__group" },
23041
- React__default["default"].createElement("div", { className: "form__region-header" },
23042
- React__default["default"].createElement("h5", { className: "form__region-heading" }, rooms.length > 1 ? `${translations.SHARED.ROOM} ${rIndex + 1}` : translations.ROOM_OPTIONS_FORM.TRAVELER_GROUP),
23043
- React__default["default"].createElement("p", { className: "form__region-label" }, `${r.adults.length + r.children.length} ${r.adults.length + r.children.length === 1 ? translations.SUMMARY.TRAVELER : translations.SUMMARY.TRAVELERS}: ${lodash.compact([
23044
- r.adults.length,
23045
- r.adults.length === 1 && ` ${translations.SUMMARY.ADULT}`,
23046
- r.adults.length > 1 && ` ${translations.SUMMARY.ADULTS}`,
23047
- r.adults && r.adults.length && r.children && r.children.length && ', ',
23048
- r.children.length,
23049
- r.children.length === 1 && ` ${translations.SUMMARY.CHILD}`,
23050
- r.children.length > 1 && ` ${translations.SUMMARY.CHILDREN}`
23051
- ]).join('')}`))),
23052
- [...r.adults, ...r.children].map((traveler) => {
23053
- const isMainBooker = traveler.id === travelerFormValues?.mainBookerId;
23054
- return (React__default["default"].createElement("div", { className: "form__group form__group--sm-50", key: traveler.id },
23055
- React__default["default"].createElement("ul", { className: "list list--plain" },
23056
- React__default["default"].createElement("li", { className: "list__item" },
23057
- React__default["default"].createElement("strong", null,
23058
- traveler.firstName,
23059
- " ",
23060
- traveler.lastName),
23061
- ' ',
23062
- isMainBooker && React__default["default"].createElement("em", null,
23063
- "(",
23064
- translations.SUMMARY.MAIN_BOOKER,
23065
- ")")),
23066
- React__default["default"].createElement("li", { className: "list__item" }, traveler.birthDate.split('-').reverse().join('/')),
23067
- isMainBooker && (React__default["default"].createElement(React__default["default"].Fragment, null,
23068
- travelerFormValues?.street && (React__default["default"].createElement(React__default["default"].Fragment, null,
23069
- React__default["default"].createElement("li", { className: "list__item" }, `${travelerFormValues?.street} ${lodash.compact([
23070
- travelerFormValues?.houseNumber,
23071
- travelerFormValues?.box
23072
- ]).join(' ')}, ${travelerFormValues?.zipCode} ${travelerFormValues?.place}`))),
23073
- travelerFormValues?.phone && React__default["default"].createElement("li", { className: "list__item" }, travelerFormValues?.phone),
23074
- travelerFormValues?.email && React__default["default"].createElement("li", { className: "list__item" }, travelerFormValues?.email))))));
23075
- }))))),
23076
- React__default["default"].createElement("div", { className: "form__region" },
23077
- React__default["default"].createElement("div", { className: "form__row" },
23078
- React__default["default"].createElement("div", { className: "form__group" },
23079
- React__default["default"].createElement("div", { className: "form__region-header" },
23080
- React__default["default"].createElement("h5", { className: "form__region-heading" }, translations.SUMMARY.OPTIONS)))),
23081
- React__default["default"].createElement("div", { className: "form__row" },
23082
- React__default["default"].createElement("div", { className: "form__group" },
23083
- React__default["default"].createElement("ul", { className: "list list--booking-summary" }, activeOption?.serviceType == build.ServiceType.flight ? (React__default["default"].createElement(React__default["default"].Fragment, null,
23084
- departureFlightMetaData && React__default["default"].createElement(SummaryFlight, { flightMetaData: departureFlightMetaData, header: translations.SIDEBAR.DEPARTURE_FLIGHT }),
23085
- returnFlightMetaData && React__default["default"].createElement(SummaryFlight, { flightMetaData: returnFlightMetaData, header: translations.SIDEBAR.ARRIVAL_FLIGHT }))) : (React__default["default"].createElement(React__default["default"].Fragment, null, activeOption?.rooms
23086
- .flatMap((r) => r.options)
23087
- .filter((x) => x.isSelected)
23088
- .map((roomOption, roomOptionIndex) => (React__default["default"].createElement(React__default["default"].Fragment, { key: roomOptionIndex },
23089
- React__default["default"].createElement("li", null,
23090
- React__default["default"].createElement("h6", null, roomOption?.productName),
23091
- React__default["default"].createElement("ul", null,
23092
- React__default["default"].createElement("li", { className: "list__item" },
23093
- roomOption?.accommodationName,
23094
- !lodash.isNil(roomOption?.regimeName) && ', ',
23095
- roomOption?.regimeName)),
23096
- React__default["default"].createElement("p", null,
23097
- "(",
23098
- roomOption?.from === roomOption?.to ? (getDateText(roomOption?.from)) : (React__default["default"].createElement(React__default["default"].Fragment, null,
23099
- getDateText(roomOption?.from),
23100
- " > ",
23101
- getDateText(roomOption?.to))),
23102
- ")")),
23103
- !lodash.isEmpty(activeOption?.groups) &&
23104
- activeOption?.groups.map((x, i) => {
23105
- if (!x.options.some((y) => y.isSelected))
23106
- return;
23107
- return React__default["default"].createElement(SummaryPerBookingOptionGroup, { key: i, group: x });
23108
- }),
23109
- !lodash.isEmpty(activeOption?.optionUnits) && activeOption?.optionUnits.map((x, i) => React__default["default"].createElement(SummaryBookingOptionUnit, { key: i, unit: x })),
23110
- !lodash.isEmpty(activeOption?.optionPax) && activeOption?.optionPax.map((x, i) => React__default["default"].createElement(SummaryBookingOptionPax, { key: i, pax: x })),
23111
- packageDetails?.outwardFlights &&
23112
- packageDetails.outwardFlights
23113
- .filter((x) => x.isSelected)
23114
- .map((flight, i) => (React__default["default"].createElement(SummaryFlight, { key: i, flightMetaData: flight.flightMetaData, header: translations.SIDEBAR.DEPARTURE_FLIGHT }))),
23115
- packageDetails?.returnFlights &&
23116
- packageDetails.returnFlights
23117
- .filter((x) => x.isSelected)
23118
- .map((flight, i) => (React__default["default"].createElement(SummaryFlight, { key: i, flightMetaData: flight.flightMetaData, header: translations.SIDEBAR.ARRIVAL_FLIGHT })))))))))))),
23119
- settings.enableVoucher && (React__default["default"].createElement("div", { className: "form__region" },
23120
- React__default["default"].createElement("div", { className: "form__row" },
23121
- React__default["default"].createElement("div", { className: "form__group" },
23122
- React__default["default"].createElement("div", { className: "form__region-header" },
23123
- React__default["default"].createElement("h5", { className: "form__region-heading" }, translations.SUMMARY.VOUCHERS)))),
23124
- React__default["default"].createElement("div", { className: "form__row" },
23125
- React__default["default"].createElement("div", { className: "form__group" },
23126
- React__default["default"].createElement("input", { type: "text", className: "form__input info-message__voucher__input", defaultValue: voucher.code, onChange: (e) => setVoucher({ code: e.target.value }) }),
23127
- React__default["default"].createElement("button", { type: "button", className: buildClassName(['cta', !voucher.code && 'cta--disabled']), onClick: (e) => handleValidateVoucher() }, translations.SUMMARY.VOUCHER_VALIDATE))),
23128
- React__default["default"].createElement("div", { className: "form__row" },
23129
- React__default["default"].createElement("div", { className: "form__group info-message__voucher" },
23130
- voucher.isValid && voucher.isValidated && (React__default["default"].createElement("div", { className: "info-message info-message__voucher__valid" },
23131
- React__default["default"].createElement("span", null, translations.SUMMARY.VOUCHER_VALID),
23132
- React__default["default"].createElement("button", { type: "button", className: "cta cta--secondary", onClick: handleAddVoucher }, translations.SUMMARY.ADD_VOUCHER))),
23133
- !voucher.isValid && voucher.isValidated && React__default["default"].createElement("div", { className: "info-message--error" }, translations.SUMMARY.VOUCHER_INVALID))),
23134
- React__default["default"].createElement("div", { className: "form__row" },
23135
- React__default["default"].createElement("ul", { className: "info-message__voucher" }, !lodash.isEmpty(bookRequest?.payload.voucherCodes) &&
23136
- bookRequest?.payload.voucherCodes?.map((y, i) => (React__default["default"].createElement("li", { key: i },
23137
- React__default["default"].createElement("div", { className: "info-message__voucher__list" },
23138
- y,
23139
- ' ',
23140
- React__default["default"].createElement("button", { type: "button", className: "cta--add-remove", onClick: (e) => handleRemoveVoucher(y) },
23141
- React__default["default"].createElement(Icon, { height: 16, name: "ui-trashcan" })))))))))),
23142
- !lodash.isEmpty(notifications) && (React__default["default"].createElement("div", { className: "form__region" },
23143
- React__default["default"].createElement("div", { className: "form__row" },
23144
- React__default["default"].createElement("div", { className: "form__group" },
23145
- React__default["default"].createElement("div", { className: "info-message" },
23146
- React__default["default"].createElement(Icon, { name: "ui-tooltip", className: "icon--secondary-color" }),
23147
- React__default["default"].createElement("div", { className: "info-message__copy" },
23148
- React__default["default"].createElement("h5", null, translations.SUMMARY.NOTIFICATIONS_TITLE),
23149
- React__default["default"].createElement(React__default["default"].Fragment, null,
23150
- lodash.uniqBy(notifications.filter((x) => !x.hasToBeConfirmed), 'id').map((notification) => (React__default["default"].createElement("span", { key: notification.id, className: "checkbox__label-text" },
23151
- React__default["default"].createElement("strong", { className: "checkbox__label-text--title" }, notification.title),
23152
- React__default["default"].createElement("span", { className: "checkbox__label-text--description" }, notification.description)))),
23153
- lodash.uniqBy(notifications.filter((x) => x.hasToBeConfirmed), 'id').map((notification) => (React__default["default"].createElement("div", { className: "checkbox", key: notification.id },
23154
- React__default["default"].createElement("label", { className: "checkbox__label" },
23155
- React__default["default"].createElement("input", { type: "checkbox", className: "checkbox__input", checked: notification.isConfirmed, onChange: (e) => handleNotificationChange(notification.id, e.target.checked) }),
23156
- React__default["default"].createElement("span", { className: "checkbox__label-text" },
23157
- React__default["default"].createElement("strong", { className: "checkbox__label-text--title" }, notification.title),
23158
- React__default["default"].createElement("span", { className: "checkbox__label-text--description" }, notification.description))))))))))))),
23159
- React__default["default"].createElement("div", { className: "form__region" },
23160
- React__default["default"].createElement("div", { className: "form__row" },
23161
- React__default["default"].createElement("div", { className: "form__group" },
23162
- React__default["default"].createElement("div", { className: "form__region-header" },
23163
- React__default["default"].createElement("h5", { className: "form__region-heading" }, translations.SUMMARY.REMARKS)))),
23164
- React__default["default"].createElement("div", { className: "form__row" },
23165
- React__default["default"].createElement("div", { className: "form__group" },
23166
- React__default["default"].createElement("textarea", { className: "form__input", defaultValue: remarks, onChange: (e) => handleRemarksChange(e.target.value) })))),
23167
- React__default["default"].createElement("div", { className: "form__region" },
23168
- React__default["default"].createElement("div", { className: "form__row" },
23169
- React__default["default"].createElement("div", { className: "form__group" },
23170
- React__default["default"].createElement("div", { className: "info-message" },
23171
- React__default["default"].createElement(Icon, { name: "ui-tooltip", className: "icon--secondary-color" }),
23172
- React__default["default"].createElement("div", { className: "info-message__copy" },
23173
- React__default["default"].createElement("h5", null, translations.SUMMARY.VALIDATE_TITLE),
23174
- settings.customValidateText ? (React__default["default"].createElement("div", { dangerouslySetInnerHTML: {
23175
- __html: settings.customValidateText
23176
- } })) : (React__default["default"].createElement(React__default["default"].Fragment, null,
23177
- React__default["default"].createElement("p", null, settings.isOffer ? translations.SUMMARY.VALIDATE_TEXT_OFFER : translations.SUMMARY.VALIDATE_TEXT_BOOKING),
23178
- settings.allowOption && React__default["default"].createElement("p", null, translations.SUMMARY.VALIDATE_TEXT_OPTION))),
23179
- checkboxes &&
23180
- checkboxes.map((checkbox) => (React__default["default"].createElement("div", { className: "checkbox", key: checkbox.id },
23181
- React__default["default"].createElement("label", { className: "checkbox__label" },
23182
- React__default["default"].createElement("input", { type: "checkbox", className: "checkbox__input", checked: checkbox.isSelected, onChange: (e) => handleCheckboxChange(checkbox.id, e.target.checked) }),
23183
- React__default["default"].createElement("span", { className: "checkbox__label-text", dangerouslySetInnerHTML: {
23184
- __html: checkbox.text
23185
- } }))))))))))),
23186
- React__default["default"].createElement("div", { className: "booking__navigator" },
23187
- 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)),
23188
- 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)),
23189
- 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))))));
23881
+ getDateText(opt.line.startDate),
23882
+ opt.line.startDate != opt.line.endDate ? ` > ${getDateText(opt.line.endDate)}` : '',
23883
+ React__default["default"].createElement("ul", { className: "list" }, !lodash.isEmpty(opt?.groups) &&
23884
+ opt?.groups.map((x) => {
23885
+ if (!x.options.some((y) => y.isSelected))
23886
+ return;
23887
+ return React__default["default"].createElement(SummaryPerPaxOptionGroup, { group: x });
23888
+ })))))));
23889
+ return (React__default["default"].createElement(React__default["default"].Fragment, null, group.title ? (React__default["default"].createElement("li", { className: "list__item" },
23890
+ React__default["default"].createElement("h6", null, group.title),
23891
+ React__default["default"].createElement("ul", { className: "list" }, renderOptions()))) : (React__default["default"].createElement(React__default["default"].Fragment, null, renderOptions()))));
23190
23892
  };
23191
23893
 
23192
- const LabeledInput = ({ name, type, required, value, defaultValue, min, max, label, placeholder, extraClassName, hasError, onChange, onBlur }) => {
23193
- return (React__default["default"].createElement("label", { className: buildClassName(['form__group', extraClassName, hasError && 'form__group--error']) },
23194
- React__default["default"].createElement("span", { className: "form__label" }, lodash.compact([label, required && '*']).join(' ')),
23195
- 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 })));
23894
+ const SummaryBookingOptionPax = ({ pax }) => {
23895
+ return (React__default["default"].createElement(React__default["default"].Fragment, null, pax.groups.map((y, i) => {
23896
+ const paxOptions = y.options.filter((y) => y.isSelected);
23897
+ if (lodash.isEmpty(paxOptions))
23898
+ return;
23899
+ return React__default["default"].createElement(SummaryPerPaxOptionGroup, { key: i, group: y });
23900
+ })));
23196
23901
  };
23197
23902
 
23198
- const LabeledSelect = ({ name, id, required, value, defaultValue, label, extraClassName, extraDropdownClassName, hasError, options, onChange, onBlur }) => {
23199
- return (React__default["default"].createElement("label", { className: buildClassName(['form__group', extraClassName, hasError && 'form__group--error']) },
23200
- React__default["default"].createElement("span", { className: "form__label" }, lodash.compact([label, required && '*']).join(' ')),
23201
- React__default["default"].createElement("div", { className: buildClassName(['dropdown', extraDropdownClassName]) },
23202
- 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)))))));
23903
+ const SummaryPerUnitOptionGroup = ({ group }) => {
23904
+ const groupOptions = group.options.filter((x) => x.isSelected);
23905
+ const renderOptions = () => (React__default["default"].createElement(React__default["default"].Fragment, null, groupOptions.map((opt) => (React__default["default"].createElement("li", { key: opt.line.entryLineGuid, className: "list__item" },
23906
+ opt.line.productName,
23907
+ React__default["default"].createElement("ul", null,
23908
+ React__default["default"].createElement("li", null,
23909
+ opt.line.accommodationName,
23910
+ !lodash.isNil(opt.line?.regimeName) && ', ',
23911
+ opt.line.regimeName)),
23912
+ getDateText(opt.line.startDate),
23913
+ opt.line.startDate != opt.line.endDate ? ` > ${getDateText(opt.line.endDate)}` : '',
23914
+ React__default["default"].createElement("ul", null,
23915
+ !lodash.isEmpty(opt?.groups) &&
23916
+ opt?.groups.map((x) => {
23917
+ if (!x.options.some((y) => y.isSelected))
23918
+ return;
23919
+ return React__default["default"].createElement(SummaryPerUnitOptionGroup, { group: x });
23920
+ }),
23921
+ !lodash.isEmpty(opt?.pax) && opt?.pax.map((x) => React__default["default"].createElement(SummaryBookingOptionPax, { pax: x }))))))));
23922
+ return (React__default["default"].createElement(React__default["default"].Fragment, null, group.title ? (React__default["default"].createElement("li", { className: "list__item" },
23923
+ React__default["default"].createElement("h6", null, group.title),
23924
+ React__default["default"].createElement("ul", { className: "list" }, renderOptions()))) : (React__default["default"].createElement(React__default["default"].Fragment, null, renderOptions()))));
23203
23925
  };
23204
23926
 
23205
- const normalize = (v) => (v == null ? '' : String(v));
23206
- const parseCombined = (raw, countries) => {
23207
- const value = normalize(raw).trim();
23208
- if (!value)
23209
- return { prefix: '', number: '' };
23210
- // Try to match a known prefix at the start. Prefer longest match.
23211
- const sorted = [...countries].sort((a, b) => b.phonePrefix.length - a.phonePrefix.length);
23212
- const hit = sorted.find((c) => value.startsWith(c.phonePrefix));
23213
- if (hit) {
23214
- const rest = value
23215
- .slice(hit.phonePrefix.length)
23216
- .trim()
23217
- .replace(/^[-\s]+/, '');
23218
- return { prefix: hit.phonePrefix, number: rest };
23219
- }
23220
- // Fallback: split on first space if it looks like a +prefix number
23221
- const m = value.match(/^(\+\d{1,4})[\s-]*(.*)$/);
23222
- if (m)
23223
- return { prefix: m[1], number: m[2] };
23224
- return { prefix: '', number: value };
23225
- };
23226
- const PhoneInput = ({ name, required, value, label, placeholder, extraClassName, hasError, countries, countryIso2, onChange, onBlur }) => {
23227
- // Derive initial state from `value`
23228
- const initialCountry = countries.find((c) => c.iso2 === countryIso2);
23229
- const initial = React.useMemo(() => parseCombined(normalize(value), countries), [value, countries]);
23230
- const [prefix, setPrefix] = React.useState(initial.prefix);
23231
- const [number, setNumber] = React.useState(initial.number);
23232
- // Keep state in sync if the parent changes `value`
23233
- React.useEffect(() => {
23234
- const parsed = !prefix && !number && initialCountry ? { prefix: initialCountry.phonePrefix, number: '' } : parseCombined(normalize(value), countries);
23235
- if (parsed.prefix)
23236
- setPrefix(parsed.prefix);
23237
- if (parsed.number)
23238
- setNumber(parsed.number);
23239
- }, [value, countries, countryIso2]);
23240
- const emitCombinedChange = (e, nextPrefix, nextNumber) => {
23241
- // Combine with a space, unless the number already starts with a dash or space
23242
- const combined = nextPrefix && nextNumber ? lodash.compact([nextPrefix, nextNumber]).join(' ') : null;
23243
- onChange?.({
23244
- ...e,
23245
- type: 'change',
23246
- target: { name, value: combined },
23247
- currentTarget: { name, value: combined }
23248
- });
23249
- };
23250
- const handleCountryBlur = (e) => {
23251
- onBlur?.(e);
23252
- };
23253
- const onCountryChange = (e) => {
23254
- const next = e.target.value;
23255
- setPrefix(next);
23256
- emitCombinedChange(e, next, number);
23257
- };
23258
- const onPhoneChange = (e) => {
23259
- const cleaned = e.target.value.replace(/[^\d\s-]/g, '');
23260
- setNumber(cleaned);
23261
- emitCombinedChange(e, prefix, cleaned);
23262
- };
23263
- const handleNumberBlur = (e) => {
23264
- onBlur?.(e);
23265
- };
23266
- // Ensure the select shows a reasonable default when prefix is empty
23267
- const selectValue = prefix || '';
23268
- return (React__default["default"].createElement("label", { className: buildClassName(['form__group', extraClassName, hasError && 'form__group--error']) },
23269
- label && React__default["default"].createElement("span", { className: "form__label" }, lodash.compact([label, required && '*']).join(' ')),
23270
- React__default["default"].createElement("div", { className: "phone-input" },
23271
- React__default["default"].createElement("div", { className: "dropdown" },
23272
- React__default["default"].createElement("select", { "aria-label": label ? `${label} – country code` : 'Country calling code', name: `${name}__country`, value: selectValue, onBlur: handleCountryBlur, onChange: onCountryChange },
23273
- React__default["default"].createElement("option", { value: "", disabled: true }),
23274
- countries?.map((option) => (React__default["default"].createElement("option", { key: option.iso2, value: option.phonePrefix },
23275
- option.name,
23276
- " (",
23277
- option.phonePrefix,
23278
- ")"))))),
23279
- 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 }),
23280
- React__default["default"].createElement("input", { type: "hidden", name: name, value: lodash.compact([prefix, number]).join(' ') }))));
23927
+ const SummaryBookingOptionUnit = ({ unit }) => {
23928
+ return (React__default["default"].createElement(React__default["default"].Fragment, null, unit.groups.map((y) => {
23929
+ const unitOptions = y.options.filter((y) => y.isSelected);
23930
+ if (lodash.isEmpty(unitOptions))
23931
+ return;
23932
+ return React__default["default"].createElement(SummaryPerUnitOptionGroup, { group: y });
23933
+ })));
23281
23934
  };
23282
23935
 
23283
- const hasVisibleError = (formik, key) => lodash.get(formik.errors, key) && lodash.get(formik.touched, key);
23936
+ const SummaryFlight = ({ flightMetaData, header }) => {
23937
+ const translations = reactRedux.useSelector(selectTranslations);
23938
+ return (React__default["default"].createElement("li", null,
23939
+ React__default["default"].createElement("h6", null, header),
23940
+ parseFlightLines(flightMetaData)?.map((flightLine) => (React__default["default"].createElement("p", { key: flightLine.departureAirportIata },
23941
+ flightLine.departureAirportDescription,
23942
+ " (",
23943
+ flightLine.departureAirportIata,
23944
+ ") > ",
23945
+ flightLine.arrivalAirportDescription,
23946
+ " (",
23947
+ flightLine.arrivalAirportIata,
23948
+ ") ",
23949
+ ' - ',
23950
+ flightLine.travelClass,
23951
+ " ",
23952
+ ' - ',
23953
+ flightLine.airlineCode,
23954
+ flightLine.number,
23955
+ React__default["default"].createElement("br", null),
23956
+ getDateText(flightLine.departureDate),
23957
+ " ",
23958
+ flightLine.departureTime,
23959
+ " > ",
23960
+ getDateText(flightLine.arrivalDate),
23961
+ " ",
23962
+ flightLine.arrivalTime,
23963
+ React__default["default"].createElement("br", null),
23964
+ flightLine.airlineDescription))),
23965
+ flightMetaData.luggageIncluded && React__default["default"].createElement("p", null, translations.SIDEBAR.LUGGAGE_INCLUDED)));
23966
+ };
23284
23967
 
23285
- const GenderControl = ({ translations, value, formik, name }) => {
23286
- return (React__default["default"].createElement("div", { className: buildClassName(['form__group', hasVisibleError(formik, name) && 'form__group--error']) },
23287
- React__default["default"].createElement("label", { className: "form__label" },
23288
- translations.TRAVELERS_FORM.GENDER_ID,
23289
- " *"),
23290
- React__default["default"].createElement("div", { className: "radiobutton-group" },
23291
- React__default["default"].createElement("div", { className: "radiobutton" },
23292
- React__default["default"].createElement("label", { className: "radiobutton__label" },
23293
- React__default["default"].createElement("input", { type: "radio", className: "radiobutton__input", name: name, onChange: formik.handleChange, onBlur: formik.handleBlur, value: "m", checked: value.gender === 'm' }),
23294
- translations.TRAVELERS_FORM.MALE_GENDER)),
23295
- React__default["default"].createElement("div", { className: "radiobutton" },
23296
- React__default["default"].createElement("label", { className: "radiobutton__label" },
23297
- React__default["default"].createElement("input", { type: "radio", className: "radiobutton__input", name: name, onChange: formik.handleChange, onBlur: formik.handleBlur, value: "f", checked: value.gender === 'f' }),
23298
- translations.TRAVELERS_FORM.FEMALE_GENDER)))));
23968
+ const SummaryPerBookingOptionGroup = ({ group }) => {
23969
+ const groupOptions = group.options.filter((x) => x.isSelected);
23970
+ const renderOptions = () => (React__default["default"].createElement(React__default["default"].Fragment, null, groupOptions.map((opt) => (React__default["default"].createElement("li", { key: opt.line.entryLineGuid, className: "list__item" },
23971
+ opt.line.productName,
23972
+ React__default["default"].createElement("ul", null,
23973
+ React__default["default"].createElement("li", null,
23974
+ opt.line.accommodationName,
23975
+ !lodash.isNil(opt.line?.regimeName) && ', ',
23976
+ opt.line.regimeName)),
23977
+ getDateText(opt.line.startDate),
23978
+ opt.line.startDate != opt.line.endDate ? ` > ${getDateText(opt.line.endDate)}` : '',
23979
+ React__default["default"].createElement("ul", null,
23980
+ !lodash.isEmpty(opt?.groups) &&
23981
+ opt?.groups.map((x) => {
23982
+ if (!x.options.some((y) => y.isSelected))
23983
+ return;
23984
+ return React__default["default"].createElement(SummaryPerBookingOptionGroup, { group: x });
23985
+ }),
23986
+ !lodash.isEmpty(opt?.units) && opt?.units.map((x) => React__default["default"].createElement(SummaryBookingOptionUnit, { unit: x })),
23987
+ !lodash.isEmpty(opt?.pax) && opt?.pax.map((x) => React__default["default"].createElement(SummaryBookingOptionPax, { pax: x }))))))));
23988
+ return (React__default["default"].createElement(React__default["default"].Fragment, null, group.title ? (React__default["default"].createElement("li", { className: "list__item" },
23989
+ React__default["default"].createElement("h6", null, group.title),
23990
+ React__default["default"].createElement("ul", { className: "list" }, renderOptions()))) : (React__default["default"].createElement(React__default["default"].Fragment, null, renderOptions()))));
23299
23991
  };
23300
23992
 
23301
- const TypeAheadInput = ({ name, value, placeholder, options, onChange, onSelect, onClear }) => {
23302
- const [showItems, setShowItems] = React.useState(false);
23303
- const handleChange = (e) => {
23304
- if (onChange) {
23305
- onChange(e.target.value);
23993
+ const Summary = () => {
23994
+ const dispatch = useAppDispatch();
23995
+ const settings = React.useContext(SettingsContext);
23996
+ const navigate = settings.skipRouter ? () => { } : reactRouter.useNavigate();
23997
+ const [isSubmitting, setIsSubmitting] = React.useState(false);
23998
+ const [checkboxes, setCheckboxes] = React.useState(settings.summary?.checkboxes);
23999
+ const [remarks, setRemarks] = React.useState('');
24000
+ const [voucher, setVoucher] = React.useState({});
24001
+ const bookingQueryString = reactRedux.useSelector(selectBookingQueryString);
24002
+ const travelersFirstStep = reactRedux.useSelector(selectTravelersFirstStep);
24003
+ const travelerFormValues = reactRedux.useSelector(selectTravelersFormValues);
24004
+ const packageDetails = reactRedux.useSelector(selectPackageDetails);
24005
+ const activeOption = reactRedux.useSelector(selectActiveOption);
24006
+ const apiSettings = reactRedux.useSelector(selectApiSettings);
24007
+ const languageCode = reactRedux.useSelector(selectLanguageCode);
24008
+ const officeId = reactRedux.useSelector(selectOfficeId);
24009
+ const agentId = reactRedux.useSelector(selectAgentAdressId);
24010
+ const departureFlight = reactRedux.useSelector(selectDepartureFlight);
24011
+ const departureFlightMetaData = departureFlight?.flightMetaData ?? reactRedux.useSelector(selectRoomOptionDepartureFlightsMetaData)?.[0];
24012
+ const returnFlight = reactRedux.useSelector(selectReturnFlight);
24013
+ const returnFlightMetaData = returnFlight?.flightMetaData ?? reactRedux.useSelector(selectRoomOptionReturnFlightsMetaData)?.[0];
24014
+ if (!travelerFormValues) {
24015
+ if (settings.skipRouter) {
24016
+ dispatch(setCurrentStep$1(TRAVELERS_FORM_STEP));
23306
24017
  }
23307
- };
23308
- const handleClearClick = (e) => {
23309
- if (onClear) {
23310
- onClear();
24018
+ else {
24019
+ navigate(`${!settings.skipBasePathInRouting ? settings.basePath : ''}?${bookingQueryString}`);
23311
24020
  }
23312
- setShowItems(false);
23313
- };
23314
- return (React__default["default"].createElement("div", { className: "typeahead" },
23315
- React__default["default"].createElement("div", { className: "typeahead__trigger typeahead__trigger--icon" },
23316
- React__default["default"].createElement(Icon, { name: "ui-qsm-location" }),
23317
- React__default["default"].createElement("input", { type: "text", className: "form__input", name: name, value: value, onChange: handleChange, placeholder: placeholder, required: true, autoComplete: "off", onKeyDown: (e) => {
23318
- if (e.key === 'Tab' && options && onSelect) {
23319
- onSelect(options[0].key);
23320
- setShowItems(false);
23321
- }
23322
- }, onFocus: () => setShowItems(true) }),
23323
- React__default["default"].createElement("button", { type: "button", className: "button button--clear", onClick: handleClearClick })),
23324
- options && options.length > 0 && showItems && (React__default["default"].createElement("div", { className: "typeahead__options" }, (options ?? []).map((option) => {
23325
- const matchIndex = typeof value !== 'undefined' ? option.text.indexOf(value) : 0;
23326
- const prefix = option.text.slice(0, matchIndex);
23327
- const highlight = option.text.slice(matchIndex, value?.length ?? 0);
23328
- const suffix = option.text.slice(matchIndex);
23329
- return (React__default["default"].createElement("div", { className: "typeahead__option", key: option.key },
23330
- React__default["default"].createElement("div", { className: "typeahead__option-header" },
23331
- React__default["default"].createElement("button", { onClick: () => {
23332
- if (onSelect)
23333
- onSelect(option.key);
23334
- setShowItems(false);
23335
- }, type: "button", className: "button button--plain" },
23336
- prefix,
23337
- React__default["default"].createElement("span", { className: "typeahead__option-highlight" }, highlight),
23338
- suffix))));
23339
- })))));
23340
- };
23341
-
23342
- // A traveler is a baby when the age they were booked with is at or below the baby
23343
- // boundary. Babies get their own label and birth date validation (TIDE-3269). Only
23344
- // meaningful for the regular form: the compact form asks for an age instead of a birth
23345
- // date, so it has nothing to validate and its own age input already states the age.
23346
- function isBabyAge(age, maxBabyAge = BABY_MAX_AGE) {
23347
- return typeof age === 'number' && age <= maxBabyAge;
23348
- }
23349
- // The native date input can hold values format() throws on (e.g. a 5+ digit year while typing), so never format an unvalidated birthDate
23350
- function formatBirthDate$1(birthDate) {
23351
- if (!birthDate)
23352
- return '';
23353
- const parsed = dateFns.parse(birthDate, 'yyyy-MM-dd', new Date());
23354
- return dateFns.isValid(parsed) ? dateFns.format(parsed, 'dd-MM-yyyy') : '';
23355
- }
23356
- // A customFieldConfigurationId can have separate "main-booker"/other-pax variants; resolved per traveler below.
23357
- const MAIN_BOOKER_GROUP = 'main-booker';
23358
- function buildEmptyCustomFields(paxCustomFields = []) {
23359
- const uniqueIds = Array.from(new Set(paxCustomFields.map((field) => field.customFieldConfigurationId)));
23360
- return uniqueIds.map((customFieldConfigurationId) => ({ customFieldConfigurationId, value: '' }));
23361
- }
23362
- function getApplicablePaxCustomFields(paxCustomFields = [], isMainBooker) {
23363
- const variantsById = new Map();
23364
- paxCustomFields.forEach((field) => {
23365
- const variants = variantsById.get(field.customFieldConfigurationId) ?? [];
23366
- variants.push(field);
23367
- variantsById.set(field.customFieldConfigurationId, variants);
23368
- });
23369
- const applicableFields = [];
23370
- variantsById.forEach((variants) => {
23371
- const hasRoleSplit = variants.some((v) => v.group === MAIN_BOOKER_GROUP);
23372
- if (!hasRoleSplit) {
23373
- applicableFields.push(variants[0]);
24021
+ }
24022
+ const rooms = reactRedux.useSelector(selectRooms);
24023
+ const userValidated = reactRedux.useSelector(selectUserValidated);
24024
+ const notifications = reactRedux.useSelector(selectNotifications);
24025
+ const bookRequest = reactRedux.useSelector(selectBookingPackageBookRequest);
24026
+ React.useEffect(() => {
24027
+ // Every checkbox should be checked, or no checkboxes
24028
+ const checkboxesValidated = !lodash.isNil(checkboxes) ? checkboxes.every((checkbox) => checkbox.isSelected) : true;
24029
+ const notificationsValidated = !lodash.isNil(notifications) ? notifications.filter((x) => x.hasToBeConfirmed).every((checkbox) => checkbox.isConfirmed) : true;
24030
+ dispatch(setUserValidated(checkboxesValidated && notificationsValidated));
24031
+ }, [checkboxes, notifications]);
24032
+ const handleSubmit = async (e) => {
24033
+ e.preventDefault();
24034
+ const submitter = e.nativeEvent.submitter?.name;
24035
+ setIsSubmitting(true);
24036
+ if (typeof window !== 'undefined') {
24037
+ window.scrollTo(0, 0);
24038
+ }
24039
+ const tideClientConfig = buildTideClientConfig(apiSettings);
24040
+ if (!bookRequest || !tideClientConfig) {
23374
24041
  return;
23375
24042
  }
23376
- const roleMatch = variants.find((v) => (v.group === MAIN_BOOKER_GROUP) === isMainBooker);
23377
- if (roleMatch)
23378
- applicableFields.push(roleMatch);
23379
- });
23380
- return applicableFields;
23381
- }
23382
- function createTraveler(traveler, followNumber, personTranslation, isCompact, maxBabyAge, paxCustomFields) {
23383
- if (isCompact) {
23384
- return {
23385
- id: traveler.id,
23386
- firstName: personTranslation,
23387
- lastName: `${followNumber.number++}`,
23388
- birthDate: '',
23389
- gender: '',
23390
- // Keep a booked age of 0 instead of submitting and pricing the traveler as a 30 year
23391
- // old, while still falling back for a missing or unusable age. `??` alone would let a
23392
- // NaN (unparseable date of birth upstream) reach the age input and the booked pax.
23393
- // See TIDE-3269.
23394
- age: Number.isFinite(traveler.age) ? traveler.age : 30,
23395
- // No isBaby here on purpose: the age below is editable and nothing revalidates it
23396
- // in compact mode, so a stored flag would end up contradicting the entered age.
23397
- weight: null,
23398
- weightType: build.WeightType.kilograms,
23399
- height: null,
23400
- heightType: build.HeightType.centimeters,
23401
- customFields: buildEmptyCustomFields(paxCustomFields)
23402
- };
23403
- }
23404
- return {
23405
- id: traveler.id,
23406
- firstName: '',
23407
- lastName: '',
23408
- birthDate: '',
23409
- gender: '',
23410
- isBaby: isBabyAge(traveler.age, maxBabyAge),
23411
- weight: null,
23412
- weightType: build.WeightType.kilograms,
23413
- height: null,
23414
- heightType: build.HeightType.centimeters,
23415
- customFields: buildEmptyCustomFields(paxCustomFields)
23416
- };
23417
- }
23418
- function createInitialValuesFromRooms(formRooms, startDate, endDate, agentAdressId, personTranslation, isCompact, maxBabyAge, paxCustomFields) {
23419
- const followNumber = { number: 1 };
23420
- const initialValues = {
23421
- startDate,
23422
- endDate,
23423
- rooms: formRooms.map((room) => ({
23424
- adults: room.adults.map((traveler) => createTraveler(traveler, followNumber, personTranslation, isCompact, maxBabyAge, paxCustomFields)),
23425
- children: room.children.map((traveler) => createTraveler(traveler, followNumber, personTranslation, isCompact, maxBabyAge, paxCustomFields))
23426
- })),
23427
- mainBookerId: -1,
23428
- street: '',
23429
- houseNumber: '',
23430
- box: '',
23431
- zipCode: '',
23432
- place: '',
23433
- country: '',
23434
- phone: '',
23435
- email: '',
23436
- emailConfirmation: '',
23437
- travelAgentId: agentAdressId ?? 0,
23438
- travelAgentName: ''
23439
- };
23440
- if (initialValues.rooms?.[0]?.adults?.[0]) {
23441
- initialValues.mainBookerId = initialValues.rooms[0].adults[0].id;
23442
- }
23443
- return initialValues;
23444
- }
23445
- function createInitialValuesFromEditablePackagingEntry(editablePackagingEntry, agentAdressId) {
23446
- const pax = editablePackagingEntry?.pax ?? [];
23447
- const rooms = editablePackagingEntry.rooms.map((room) => {
23448
- const roomPax = pax.filter((x) => room.paxIds.includes(x.id));
23449
- const adults = roomPax.filter((x) => x.age >= 18);
23450
- const children = roomPax.filter((x) => x.age < 18);
23451
- return {
23452
- adults: adults.map((roomTraveler) => {
23453
- return {
23454
- id: roomTraveler.id,
23455
- firstName: roomTraveler.firstName ?? '',
23456
- lastName: roomTraveler.lastName ?? '',
23457
- birthDate: roomTraveler.dateOfBirth ? dateFns.format(new Date(roomTraveler.dateOfBirth), 'yyyy-MM-dd') : '',
23458
- gender: ''
23459
- };
23460
- }),
23461
- children: children.map((roomTraveler) => {
23462
- return {
23463
- id: roomTraveler.id,
23464
- firstName: roomTraveler.firstName ?? '',
23465
- lastName: roomTraveler.lastName ?? '',
23466
- birthDate: roomTraveler.dateOfBirth ? dateFns.format(new Date(roomTraveler.dateOfBirth), 'yyyy-MM-dd') : '',
23467
- gender: '',
23468
- isBaby: isBabyAge(roomTraveler.age)
23469
- };
23470
- })
23471
- };
23472
- });
23473
- const values = createInitialValuesFromRooms(rooms.map((room) => ({ adults: room.adults, children: room.children })), lodash.first(editablePackagingEntry?.lines)?.from, lodash.last(editablePackagingEntry?.lines)?.to, agentAdressId);
23474
- values.rooms = rooms;
23475
- values.mainBookerId = pax.find((x) => x.isMainBooker)?.id ?? rooms[0]?.adults?.[0]?.id ?? -1;
23476
- const address = editablePackagingEntry?.address;
23477
- values.street = address?.street ?? '';
23478
- values.houseNumber = address?.houseNumber ?? '';
23479
- values.box = address?.box ?? '';
23480
- values.zipCode = address?.zipCode ?? '';
23481
- values.place = address?.place ?? '';
23482
- values.country = address?.country ?? '';
23483
- values.phone = address?.phone ?? '';
23484
- values.email = address?.email ?? '';
23485
- values.emailConfirmation = address?.email ?? '';
23486
- values.travelAgentId = address?.travelAgentId ?? agentAdressId ?? 0;
23487
- return values;
23488
- }
23489
- function applyTravelersFormValuesToEditablePackagingEntry(editablePackagingEntry, values) {
23490
- const travelers = values.rooms.flatMap((room) => [...room.adults, ...room.children]);
23491
- return {
23492
- ...editablePackagingEntry,
23493
- pax: (editablePackagingEntry.pax ?? []).map((pax) => {
23494
- const traveler = travelers.find((x) => x.id === pax.id);
23495
- if (!traveler)
23496
- return pax;
23497
- return {
23498
- ...pax,
23499
- firstName: traveler.firstName ?? '',
23500
- lastName: traveler.lastName ?? '',
23501
- dateOfBirth: traveler.birthDate || null,
23502
- isMainBooker: traveler.id === values.mainBookerId
23503
- };
23504
- }),
23505
- address: {
23506
- ...editablePackagingEntry.address,
23507
- street: values.street,
23508
- houseNumber: values.houseNumber,
23509
- box: values.box,
23510
- zipCode: values.zipCode,
23511
- place: values.place,
23512
- country: values.country,
23513
- travelAgentId: values.travelAgentId,
23514
- phone: values.phone,
23515
- email: values.email
24043
+ if (bookRequest.payload.returnPaymentUrl) {
24044
+ if (typeof window !== 'undefined') {
24045
+ window.scrollTo(0, 0);
24046
+ if (settings.paymentRedirectUrl) {
24047
+ bookRequest.payload.redirectUrl = settings.paymentRedirectUrl;
24048
+ }
24049
+ else {
24050
+ const location = window.location;
24051
+ bookRequest.payload.redirectUrl = `${location.protocol}//${location.host}${settings.basePath ?? location.pathname}?${bookingQueryString}`;
24052
+ }
24053
+ }
23516
24054
  }
23517
- };
23518
- }
23519
- const SharedTravelersForm = ({ formik, translations, travellersSettings, countries, agents, bookingType, agentAdressId, travelersFirstStep = false, isUnavailable = false, useCompactForm = false, showAllCountries = false, showWeightField = false, showHeightField = false, paxCustomFields, showAgentSelection = false, initialShowAgents = false, renderPreviousButton, onBookingTypeChange }) => {
23520
- const [showAgents, setShowAgents] = React.useState(initialShowAgents);
23521
- const typeaheadAgents = React.useMemo(() => lodash.sortBy(agents?.map((agent) => ({
23522
- key: `${agent.id}`,
23523
- value: `${agent.name}${agent.postalCode || agent.location ? ` (${lodash.compact([agent.postalCode, agent.location]).join(' ')})` : ''}`,
23524
- text: `${agent.name}${agent.postalCode || agent.location ? ` (${lodash.compact([agent.postalCode, agent.location]).join(' ')})` : ''}`
23525
- })), 'value') ?? [], [agents]);
23526
- const [filteredAgents, setFilteredAgents] = React.useState(typeaheadAgents);
23527
- const flatErrors = flat__default["default"](formik.errors);
23528
- const errorKeys = Object.keys(flatErrors).filter((key) => lodash.get(formik.touched, key));
23529
- const hasVisibleError = (key) => lodash.get(formik.errors, key) && lodash.get(formik.touched, key);
23530
- const mainBooker = formik.values.rooms
23531
- .find((room) => room.adults.find((traveler) => traveler.id === formik.values.mainBookerId))
23532
- ?.adults.find((traveler) => traveler.id === formik.values.mainBookerId);
23533
- const countryOptions = [
23534
- { key: 'empty', value: undefined, label: translations.TRAVELERS_FORM.SELECT_COUNTRY },
23535
- ...(showAllCountries
23536
- ? countries?.map((country) => ({ key: country.iso2, value: country.iso2, label: country.name })) ?? []
23537
- : travellersSettings?.countries?.map((country) => ({ key: country.iso2, value: country.iso2, label: country.name })) ?? [
23538
- { key: 'be', value: 'be', label: translations.TRAVELERS_FORM.COUNTRIES.BELGIUM },
23539
- { key: 'nl', value: 'nl', label: translations.TRAVELERS_FORM.COUNTRIES.NETHERLANDS },
23540
- { key: 'fr', value: 'fr', label: translations.TRAVELERS_FORM.COUNTRIES.FRANCE }
23541
- ])
23542
- ];
23543
- const handleMainBookerChange = (event) => {
23544
- formik.setFieldValue('mainBookerId', parseInt(event.currentTarget.value, 10));
23545
- };
23546
- const handleAgentChange = (value) => {
23547
- setFilteredAgents(typeaheadAgents.filter((x) => x.value.toLocaleLowerCase().indexOf(value.toLocaleLowerCase()) > -1));
23548
- formik.setFieldValue('travelAgentName', value);
23549
- };
23550
- const handleAgentSelect = (key) => {
23551
- const agent = typeaheadAgents.find((x) => x.key === key);
23552
- formik.setValues({
23553
- ...formik.values,
23554
- travelAgentId: Number(agent?.key),
23555
- travelAgentName: agent?.value ?? ''
23556
- });
23557
- onBookingTypeChange?.(agentAdressId && agentAdressId !== 0 ? 'b2b' : 'b2b2c');
23558
- };
23559
- const handleAgentClear = () => {
23560
- formik.setValues({ ...formik.values, travelAgentId: 0, travelAgentName: '' });
23561
- onBookingTypeChange?.('b2c');
23562
- };
23563
- const toggleAgent = (value) => {
23564
- setShowAgents(value);
23565
- if (!value) {
23566
- handleAgentClear();
23567
- setFilteredAgents([]);
24055
+ try {
24056
+ if (submitter === 'option') {
24057
+ bookRequest.payload.status = build.EntryStatus.option;
24058
+ bookRequest.payload.customStatusId = settings.customOptionStatus;
24059
+ dispatch(setIsOption(true));
24060
+ }
24061
+ const bookingResponse = await build.book(tideClientConfig, bookRequest, undefined, languageCode);
24062
+ // Booking successfull
24063
+ dispatch(setBookingNumber$1(bookingResponse.number));
24064
+ if (bookingResponse.paymentUrl) {
24065
+ const event = new CustomEvent('tide-booking-payment-url', {
24066
+ detail: {
24067
+ paymentUrl: bookingResponse.paymentUrl,
24068
+ bookingNumber: bookingResponse.number
24069
+ },
24070
+ cancelable: true,
24071
+ bubbles: true
24072
+ });
24073
+ const target = typeof document !== 'undefined' ? document.getElementById('tide-booking') ?? window : window;
24074
+ const shouldAutoRedirect = target.dispatchEvent(event);
24075
+ if (shouldAutoRedirect) {
24076
+ window.location.href = bookingResponse.paymentUrl;
24077
+ }
24078
+ }
24079
+ else {
24080
+ if (settings.skipRouter) {
24081
+ dispatch(setCurrentStep$1(CONFIRMATION_STEP));
24082
+ }
24083
+ else {
24084
+ navigate(`${!settings.skipBasePathInRouting ? settings.basePath : ''}${settings.confirmation.pathSuffix}?${bookingQueryString}`);
24085
+ }
24086
+ }
24087
+ }
24088
+ catch (error) {
24089
+ if (settings.skipRouter) {
24090
+ dispatch(setCurrentStep$1(ERROR_STEP));
24091
+ }
24092
+ else {
24093
+ navigate(`${!settings.skipBasePathInRouting ? settings.basePath : ''}${settings.error.pathSuffix}?${bookingQueryString}`);
24094
+ }
24095
+ }
24096
+ finally {
24097
+ setIsSubmitting(false);
23568
24098
  }
23569
24099
  };
23570
- const handleAddTraveler = (roomIndex) => {
23571
- const rooms = [...formik.values.rooms];
23572
- const newAdult = {
23573
- id: Date.now(),
23574
- firstName: '',
23575
- lastName: '',
23576
- birthDate: '',
23577
- gender: '',
23578
- weight: null,
23579
- weightType: build.WeightType.kilograms,
23580
- height: null,
23581
- heightType: build.HeightType.centimeters,
23582
- customFields: buildEmptyCustomFields(paxCustomFields)
23583
- };
23584
- rooms[roomIndex] = { ...rooms[roomIndex], adults: [...rooms[roomIndex].adults, newAdult] };
23585
- formik.setFieldValue('rooms', rooms);
24100
+ const handleNotificationChange = (id, checked) => {
24101
+ const updatedNotifications = notifications.map((notification) => (notification.id === id ? { ...notification, isConfirmed: checked } : notification));
24102
+ dispatch(setNotifications(updatedNotifications));
23586
24103
  };
23587
- const handleRemoveTraveler = (roomIndex, travelerIndex) => {
23588
- const rooms = [...formik.values.rooms];
23589
- const adults = [...rooms[roomIndex].adults];
23590
- if (adults.length <= 1)
24104
+ const handleCheckboxChange = (id, checked) => {
24105
+ if (lodash.isNil(checkboxes)) {
23591
24106
  return;
23592
- adults.splice(travelerIndex, 1);
23593
- rooms[roomIndex] = { ...rooms[roomIndex], adults };
23594
- formik.setFieldValue('rooms', rooms);
23595
- };
23596
- const handleAddRoom = () => {
23597
- const rooms = [...formik.values.rooms];
23598
- rooms.push({
23599
- adults: [
23600
- {
23601
- id: Date.now(),
23602
- firstName: '',
23603
- lastName: '',
23604
- birthDate: '',
23605
- gender: '',
23606
- weight: null,
23607
- weightType: build.WeightType.kilograms,
23608
- height: null,
23609
- heightType: build.HeightType.centimeters,
23610
- customFields: buildEmptyCustomFields(paxCustomFields)
23611
- }
23612
- ],
23613
- children: []
23614
- });
23615
- formik.setFieldValue('rooms', rooms);
24107
+ }
24108
+ const newCheckboxes = [...checkboxes];
24109
+ const index = lodash.findIndex(checkboxes, (checkbox) => checkbox.id === id);
24110
+ newCheckboxes[index].isSelected = !newCheckboxes[index].isSelected;
24111
+ setCheckboxes(newCheckboxes);
23616
24112
  };
23617
- const handleRemoveRoom = (roomIndex) => {
23618
- const rooms = [...formik.values.rooms];
23619
- rooms.splice(roomIndex, 1);
23620
- formik.setFieldValue('rooms', rooms);
24113
+ const handleRemarksChange = (text) => {
24114
+ dispatch(setBookingRemarks(text));
24115
+ setRemarks(text);
23621
24116
  };
23622
- const renderGenderControl = (name, value) => (React__default["default"].createElement("div", { className: buildClassName(['form__group', hasVisibleError(name) && 'form__group--error']) },
23623
- React__default["default"].createElement("label", { className: "form__label" },
23624
- translations.TRAVELERS_FORM.GENDER_ID,
23625
- " *"),
23626
- React__default["default"].createElement("div", { className: "radiobutton-group" }, [
23627
- ['m', translations.TRAVELERS_FORM.MALE_GENDER],
23628
- ['f', translations.TRAVELERS_FORM.FEMALE_GENDER]
23629
- ].map(([gender, label]) => (React__default["default"].createElement("div", { className: "radiobutton", key: gender },
23630
- React__default["default"].createElement("label", { className: "radiobutton__label" },
23631
- React__default["default"].createElement("input", { type: "radio", className: "radiobutton__input", name: name, onChange: formik.handleChange, onBlur: formik.handleBlur, value: gender, checked: value.gender === gender }),
23632
- label)))))));
23633
- const renderWeightControl = (namePrefix, value) => (React__default["default"].createElement("div", { className: "form__row" },
23634
- React__default["default"].createElement(LabeledInput, { type: "number", min: 0, hasError: hasVisibleError(`${namePrefix}.weight`), extraClassName: "form__group--md-33", label: translations.TRAVELERS_FORM.WEIGHT, name: `${namePrefix}.weight`, onChange: formik.handleChange, onBlur: formik.handleBlur, value: value.weight ?? '' }),
23635
- React__default["default"].createElement(LabeledSelect, { extraClassName: "form__group--md-33", name: `${namePrefix}.weightType`, onChange: formik.handleChange, onBlur: formik.handleBlur, value: value.weightType, options: [
23636
- { key: 'kg', value: build.WeightType.kilograms, label: translations.TRAVELERS_FORM.WEIGHT_UNIT_KG },
23637
- { key: 'lb', value: build.WeightType.pounds, label: translations.TRAVELERS_FORM.WEIGHT_UNIT_LB }
23638
- ] })));
23639
- const renderHeightControl = (namePrefix, value) => (React__default["default"].createElement("div", { className: "form__row" },
23640
- React__default["default"].createElement(LabeledInput, { type: "number", min: 0, hasError: hasVisibleError(`${namePrefix}.height`), extraClassName: "form__group--md-33", label: translations.TRAVELERS_FORM.HEIGHT, name: `${namePrefix}.height`, onChange: formik.handleChange, onBlur: formik.handleBlur, value: value.height ?? '' }),
23641
- React__default["default"].createElement(LabeledSelect, { extraClassName: "form__group--md-33", name: `${namePrefix}.heightType`, onChange: formik.handleChange, onBlur: formik.handleBlur, value: value.heightType, options: [
23642
- { key: 'cm', value: build.HeightType.centimeters, label: translations.TRAVELERS_FORM.HEIGHT_UNIT_CM },
23643
- { key: 'ftIn', value: build.HeightType.feetInches, label: translations.TRAVELERS_FORM.HEIGHT_UNIT_FT_IN }
23644
- ] })));
23645
- const getCustomFieldControl = (field, value, name) => {
23646
- const label = lodash.compact([field.fieldName, field.isRequired && '*']).join(' ');
23647
- switch (field.dataType) {
23648
- case build.CustomFieldDataType.bool:
23649
- return (React__default["default"].createElement("div", { className: buildClassName(['form__group', hasVisibleError(name) && 'form__group--error']) },
23650
- React__default["default"].createElement("div", { className: "checkbox" },
23651
- React__default["default"].createElement("label", { className: "checkbox__label" },
23652
- 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 }),
23653
- label))));
23654
- case build.CustomFieldDataType.date:
23655
- 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 }));
23656
- case build.CustomFieldDataType.time:
23657
- 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 }));
23658
- case build.CustomFieldDataType.numeric:
23659
- 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 }));
23660
- case build.CustomFieldDataType.string:
23661
- default:
23662
- 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 }));
24117
+ const handleValidateVoucher = async () => {
24118
+ if (!voucher?.code)
24119
+ return;
24120
+ const request = {
24121
+ officeId: officeId,
24122
+ agentId: agentId,
24123
+ payload: {
24124
+ code: voucher.code,
24125
+ otherCodes: bookRequest?.payload.voucherCodes ?? []
24126
+ }
24127
+ };
24128
+ const tideClientConfig = buildTideClientConfig(apiSettings);
24129
+ if (!tideClientConfig)
24130
+ return;
24131
+ const result = await build.validateVoucher(tideClientConfig, request);
24132
+ if (result) {
24133
+ setVoucher({
24134
+ ...voucher,
24135
+ isValidated: true,
24136
+ isValid: result.isValid
24137
+ });
23663
24138
  }
23664
24139
  };
23665
- const renderCustomFields = (travelerValues, namePrefix) => {
23666
- if (!paxCustomFields?.length)
23667
- return null;
23668
- const isMainBooker = travelerValues.id === formik.values.mainBookerId;
23669
- const applicableFields = getApplicablePaxCustomFields(paxCustomFields, isMainBooker);
23670
- if (!applicableFields.length)
23671
- return null;
23672
- return (React__default["default"].createElement("div", { className: "travelers-form__custom-fields-grid" }, applicableFields.map((field) => {
23673
- const fieldIndex = travelerValues.customFields?.findIndex((c) => c.customFieldConfigurationId === field.customFieldConfigurationId) ?? -1;
23674
- if (fieldIndex < 0)
23675
- return null;
23676
- const name = `${namePrefix}.customFields[${fieldIndex}].value`;
23677
- const value = travelerValues.customFields[fieldIndex].value;
23678
- return (React__default["default"].createElement("div", { key: field.customFieldConfigurationId, style: { gridColumn: `span ${field.col || 12}` } }, getCustomFieldControl(field, value, name)));
23679
- })));
24140
+ const handleAddVoucher = (event) => {
24141
+ if (!voucher.isValid)
24142
+ return;
24143
+ dispatch(setVoucherCodes([...(bookRequest?.payload.voucherCodes ?? []), voucher.code ?? ''].filter((x) => x !== '')));
24144
+ dispatch(fetchPriceDetails());
24145
+ setVoucher({});
23680
24146
  };
23681
- const getControl = (type, value, name) => {
23682
- switch (type) {
23683
- case 'gender':
23684
- return React__default["default"].createElement(GenderControl, { translations: translations, value: value, formik: formik, name: name });
23685
- case 'firstName':
23686
- 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 }));
23687
- case 'lastName':
23688
- 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 }));
23689
- case 'birthDate':
23690
- 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 }));
23691
- case 'country':
23692
- 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 }));
23693
- case 'phone':
23694
- 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 }));
23695
- case 'email':
23696
- return (React__default["default"].createElement(React__default["default"].Fragment, null,
23697
- 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 }),
23698
- 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 })));
23699
- case 'street':
23700
- 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 }));
23701
- case 'houseNumber':
23702
- 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 }));
23703
- case 'box':
23704
- 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 }));
23705
- case 'zipCode':
23706
- 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 }));
23707
- case 'place':
23708
- 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 }));
23709
- default:
23710
- return null;
23711
- }
24147
+ const handleRemoveVoucher = (code) => {
24148
+ dispatch(setVoucherCodes(bookRequest?.payload.voucherCodes?.filter((x) => x !== code) ?? []));
24149
+ dispatch(fetchPriceDetails());
23712
24150
  };
23713
- const renderRoomLabel = (room) => lodash.compact([
23714
- room.adults.length,
23715
- room.adults.length === 1 && ` ${translations.TRAVELERS_FORM.ADULT}`,
23716
- room.adults.length > 1 && ` ${translations.TRAVELERS_FORM.ADULTS}`,
23717
- room.adults?.length && room.children?.length && ', ',
23718
- room.children.length,
23719
- room.children.length === 1 && ` ${translations.TRAVELERS_FORM.CHILD}`,
23720
- room.children.length > 1 && ` ${translations.TRAVELERS_FORM.CHILDREN}`
23721
- ]).join('');
23722
- const renderTravelerFields = (travelerValues, namePrefix, isAdult, roomIndex, travelerIndex) => {
23723
- if (useCompactForm) {
23724
- return (React__default["default"].createElement("div", { className: "form__row" },
23725
- 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 })));
23726
- }
23727
- if (travellersSettings?.formFields?.length) {
23728
- return (React__default["default"].createElement(React__default["default"].Fragment, null,
23729
- 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}`))))),
23730
- showWeightField && renderWeightControl(namePrefix, travelerValues),
23731
- showHeightField && renderHeightControl(namePrefix, travelerValues),
23732
- renderCustomFields(travelerValues, namePrefix)));
23733
- }
23734
- return (React__default["default"].createElement(React__default["default"].Fragment, null,
23735
- React__default["default"].createElement("div", { className: "form__row" }, renderGenderControl(`${namePrefix}.gender`, travelerValues)),
23736
- React__default["default"].createElement("div", { className: "form__row" },
23737
- 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 }),
23738
- 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 }),
23739
- 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 })),
23740
- showWeightField && renderWeightControl(namePrefix, travelerValues),
23741
- showHeightField && renderHeightControl(namePrefix, travelerValues),
23742
- renderCustomFields(travelerValues, namePrefix),
23743
- 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))));
24151
+ const goPrevious = () => {
24152
+ dispatch(setCurrentStep$1(travelersFirstStep ? OPTIONS_FORM_STEP : TRAVELERS_FORM_STEP));
23744
24153
  };
23745
- return (React__default["default"].createElement("form", { className: "form form__travelers", name: "booking--travellers", id: "booking--travellers", noValidate: true, onSubmit: formik.handleSubmit, onReset: formik.handleReset },
23746
- React__default["default"].createElement("div", { className: "form__travelers__wrapper" },
23747
- formik.values.rooms.map((room, roomIndex) => (React__default["default"].createElement("div", { key: roomIndex },
23748
- formik.values.rooms.length > 1 && (React__default["default"].createElement("div", { className: "form__region" },
23749
- React__default["default"].createElement("div", { className: "form__region-header" },
23750
- React__default["default"].createElement("h5", { className: "form__region-heading" },
23751
- translations.SHARED.ROOM,
23752
- " ",
23753
- roomIndex + 1),
23754
- React__default["default"].createElement("p", { className: "form__region-label" }, renderRoomLabel(room))),
23755
- !useCompactForm && travelersFirstStep && formik.values.rooms.length > 1 && (React__default["default"].createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => handleRemoveRoom(roomIndex) }, "Verwijder reisgezelschap")))),
23756
- room.adults.map((travelerValues, index) => (React__default["default"].createElement("div", { className: "form__region", key: travelerValues.id },
23757
- React__default["default"].createElement("div", { className: "form__region-header" },
23758
- React__default["default"].createElement("h5", { className: "form__region-heading" },
23759
- translations.TRAVELERS_FORM.TRAVELER,
23760
- " ",
23761
- index + 1),
23762
- React__default["default"].createElement("p", { className: "form__region-label" }, translations.TRAVELERS_FORM.ADULT),
23763
- React__default["default"].createElement("div", { className: "radiobutton" },
23764
- React__default["default"].createElement("label", { className: "radiobutton__label" },
23765
- 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" }),
23766
- translations.TRAVELERS_FORM.MAIN_BOOKER))),
23767
- renderTravelerFields(travelerValues, `rooms[${roomIndex}].adults[${index}]`, true, roomIndex, index)))),
23768
- room.children.map((travelerValues, index) => (React__default["default"].createElement("div", { className: "form__region", key: travelerValues.id },
23769
- React__default["default"].createElement("div", { className: "form__region-header" },
23770
- React__default["default"].createElement("h5", { className: "form__region-heading" },
23771
- translations.TRAVELERS_FORM.TRAVELER,
23772
- " ",
23773
- room.adults.length + index + 1),
23774
- React__default["default"].createElement("p", { className: "form__region-label" }, travelerValues.isBaby ? translations.TRAVELERS_FORM.BABY : translations.TRAVELERS_FORM.CHILD)),
23775
- renderTravelerFields(travelerValues, `rooms[${roomIndex}].children[${index}]`, false, roomIndex, index)))),
23776
- !useCompactForm && travelersFirstStep && (React__default["default"].createElement("div", { className: "form__region" },
23777
- React__default["default"].createElement("button", { type: "button", className: "cta cta--select", onClick: () => handleAddTraveler(roomIndex) }, translations.TRAVELERS_FORM.ADD_TRAVELER)))))),
23778
- !useCompactForm && (bookingType !== 'b2b' || travellersSettings?.mainBookerFormFields?.length) ? (React__default["default"].createElement("div", { className: "form__region" },
23779
- React__default["default"].createElement("div", { className: "form__region-header" },
23780
- React__default["default"].createElement("h5", { className: "form__region-heading" }, translations.TRAVELERS_FORM.MAIN_BOOKER),
23781
- React__default["default"].createElement("p", { className: "form__region-label" }, lodash.compact([lodash.compact([mainBooker?.firstName, mainBooker?.lastName]).join(' '), formatBirthDate$1(mainBooker?.birthDate)]).join(', '))),
23782
- 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,
23783
- React__default["default"].createElement("div", { className: "form__twocolumn" },
23784
- React__default["default"].createElement("div", { className: "form__twocolumn-column" },
23785
- React__default["default"].createElement("div", { className: "form__row" },
23786
- 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 }),
23787
- 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 }),
23788
- 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 }))),
23789
- React__default["default"].createElement("div", { className: "form__twocolumn-column" },
23790
- React__default["default"].createElement("div", { className: "form__row" },
23791
- 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 }),
23792
- 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 }),
23793
- 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 })))),
24154
+ const translations = reactRedux.useSelector(selectTranslations);
24155
+ return (React__default["default"].createElement(React__default["default"].Fragment, null,
24156
+ isSubmitting && (settings.loaderComponent || React__default["default"].createElement(Loader, null)),
24157
+ !isSubmitting && (React__default["default"].createElement("form", { className: "form", name: "booking--summary", id: "booking--summary", onSubmit: handleSubmit },
24158
+ React__default["default"].createElement("div", { className: "form__booking--summary" },
24159
+ React__default["default"].createElement("div", { className: "form__region" },
23794
24160
  React__default["default"].createElement("div", { className: "form__row" },
23795
- 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 }),
23796
- 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 }),
23797
- 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" },
23798
- React__default["default"].createElement("div", { className: "form__row" },
23799
- 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,
23800
- !useCompactForm && showAgentSelection && (React__default["default"].createElement("div", { className: "form__region" },
23801
- React__default["default"].createElement("div", { className: "form__region-header" },
23802
- React__default["default"].createElement("h5", { className: "form__region-heading" }, translations.TRAVELERS_FORM.BOOK_WITH_AGENT),
23803
- React__default["default"].createElement("div", { className: "checkbox", id: "cbxChooseOffice" },
23804
- React__default["default"].createElement("label", { className: "checkbox__label" },
23805
- React__default["default"].createElement("input", { type: "checkbox", name: "booking--mainbooker", checked: showAgents, onChange: () => toggleAgent(!showAgents), className: "checkbox__input" }),
23806
- translations.TRAVELERS_FORM.CHOOSE_OFFICE))),
23807
- showAgents && (React__default["default"].createElement("div", { className: "form__row form__row--choose-office" },
23808
- React__default["default"].createElement("div", { className: buildClassName(['form__group', 'form__group--icon', hasVisibleError('travelAgentId') && 'form__group--error']) },
23809
- 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 }))))))),
23810
- !useCompactForm && errorKeys.length > 0 && (React__default["default"].createElement("div", { className: "form__region form__region--errors" },
23811
- React__default["default"].createElement("div", { className: "form__row" },
23812
- React__default["default"].createElement("div", { className: "form__group" },
23813
- React__default["default"].createElement("p", { className: "form__error-heading" },
23814
- translations.TRAVELERS_FORM.VALIDATION_MESSAGE,
23815
- ":"),
23816
- React__default["default"].createElement("ul", { className: "list" }, errorKeys.map((key) => (React__default["default"].createElement("li", { key: key }, lodash.get(flatErrors, key))))))))),
23817
- travelersFirstStep && (React__default["default"].createElement("div", { className: "booking__navigator" },
23818
- React__default["default"].createElement("button", { type: "button", className: "cta cta--select", onClick: handleAddRoom }, translations.TRAVELERS_FORM.ADD_ROOM))),
23819
- React__default["default"].createElement("div", { className: "booking__navigator" },
23820
- renderPreviousButton?.(),
23821
- React__default["default"].createElement("button", { type: "submit", title: translations.STEPS.NEXT, className: 'cta' + (isUnavailable ? ' cta--disabled' : '') }, translations.STEPS.NEXT))));
24161
+ React__default["default"].createElement("div", { className: "form__group" },
24162
+ React__default["default"].createElement("div", { className: "form__region-header" },
24163
+ React__default["default"].createElement("h5", { className: "form__region-heading" }, translations.SUMMARY.PERSONAL_DETAILS)))),
24164
+ rooms.map((r, rIndex) => (React__default["default"].createElement("div", { className: "form__row", key: rIndex },
24165
+ React__default["default"].createElement("div", { className: "form__group" },
24166
+ React__default["default"].createElement("div", { className: "form__region-header" },
24167
+ React__default["default"].createElement("h5", { className: "form__region-heading" }, rooms.length > 1 ? `${translations.SHARED.ROOM} ${rIndex + 1}` : translations.ROOM_OPTIONS_FORM.TRAVELER_GROUP),
24168
+ React__default["default"].createElement("p", { className: "form__region-label" }, `${r.adults.length + r.children.length} ${r.adults.length + r.children.length === 1 ? translations.SUMMARY.TRAVELER : translations.SUMMARY.TRAVELERS}: ${lodash.compact([
24169
+ r.adults.length,
24170
+ r.adults.length === 1 && ` ${translations.SUMMARY.ADULT}`,
24171
+ r.adults.length > 1 && ` ${translations.SUMMARY.ADULTS}`,
24172
+ r.adults && r.adults.length && r.children && r.children.length && ', ',
24173
+ r.children.length,
24174
+ r.children.length === 1 && ` ${translations.SUMMARY.CHILD}`,
24175
+ r.children.length > 1 && ` ${translations.SUMMARY.CHILDREN}`
24176
+ ]).join('')}`))),
24177
+ [...r.adults, ...r.children].map((traveler) => {
24178
+ const isMainBooker = traveler.id === travelerFormValues?.mainBookerId;
24179
+ return (React__default["default"].createElement("div", { className: "form__group form__group--sm-50", key: traveler.id },
24180
+ React__default["default"].createElement("ul", { className: "list list--plain" },
24181
+ React__default["default"].createElement("li", { className: "list__item" },
24182
+ React__default["default"].createElement("strong", null,
24183
+ traveler.firstName,
24184
+ " ",
24185
+ traveler.lastName),
24186
+ ' ',
24187
+ isMainBooker && React__default["default"].createElement("em", null,
24188
+ "(",
24189
+ translations.SUMMARY.MAIN_BOOKER,
24190
+ ")")),
24191
+ React__default["default"].createElement("li", { className: "list__item" }, traveler.birthDate.split('-').reverse().join('/')),
24192
+ isMainBooker && (React__default["default"].createElement(React__default["default"].Fragment, null,
24193
+ travelerFormValues?.street && (React__default["default"].createElement(React__default["default"].Fragment, null,
24194
+ React__default["default"].createElement("li", { className: "list__item" }, `${travelerFormValues?.street} ${lodash.compact([
24195
+ travelerFormValues?.houseNumber,
24196
+ travelerFormValues?.box
24197
+ ]).join(' ')}, ${travelerFormValues?.zipCode} ${travelerFormValues?.place}`))),
24198
+ travelerFormValues?.phone && React__default["default"].createElement("li", { className: "list__item" }, travelerFormValues?.phone),
24199
+ travelerFormValues?.email && React__default["default"].createElement("li", { className: "list__item" }, travelerFormValues?.email))))));
24200
+ }))))),
24201
+ React__default["default"].createElement("div", { className: "form__region" },
24202
+ React__default["default"].createElement("div", { className: "form__row" },
24203
+ React__default["default"].createElement("div", { className: "form__group" },
24204
+ React__default["default"].createElement("div", { className: "form__region-header" },
24205
+ React__default["default"].createElement("h5", { className: "form__region-heading" }, translations.SUMMARY.OPTIONS)))),
24206
+ React__default["default"].createElement("div", { className: "form__row" },
24207
+ React__default["default"].createElement("div", { className: "form__group" },
24208
+ React__default["default"].createElement("ul", { className: "list list--booking-summary" }, activeOption?.serviceType == build.ServiceType.flight ? (React__default["default"].createElement(React__default["default"].Fragment, null,
24209
+ departureFlightMetaData && React__default["default"].createElement(SummaryFlight, { flightMetaData: departureFlightMetaData, header: translations.SIDEBAR.DEPARTURE_FLIGHT }),
24210
+ returnFlightMetaData && React__default["default"].createElement(SummaryFlight, { flightMetaData: returnFlightMetaData, header: translations.SIDEBAR.ARRIVAL_FLIGHT }))) : (React__default["default"].createElement(React__default["default"].Fragment, null, activeOption?.rooms
24211
+ .flatMap((r) => r.options)
24212
+ .filter((x) => x.isSelected)
24213
+ .map((roomOption, roomOptionIndex) => (React__default["default"].createElement(React__default["default"].Fragment, { key: roomOptionIndex },
24214
+ React__default["default"].createElement("li", null,
24215
+ React__default["default"].createElement("h6", null, roomOption?.productName),
24216
+ React__default["default"].createElement("ul", null,
24217
+ React__default["default"].createElement("li", { className: "list__item" },
24218
+ roomOption?.accommodationName,
24219
+ !lodash.isNil(roomOption?.regimeName) && ', ',
24220
+ roomOption?.regimeName)),
24221
+ React__default["default"].createElement("p", null,
24222
+ "(",
24223
+ roomOption?.from === roomOption?.to ? (getDateText(roomOption?.from)) : (React__default["default"].createElement(React__default["default"].Fragment, null,
24224
+ getDateText(roomOption?.from),
24225
+ " > ",
24226
+ getDateText(roomOption?.to))),
24227
+ ")")),
24228
+ !lodash.isEmpty(activeOption?.groups) &&
24229
+ activeOption?.groups.map((x, i) => {
24230
+ if (!x.options.some((y) => y.isSelected))
24231
+ return;
24232
+ return React__default["default"].createElement(SummaryPerBookingOptionGroup, { key: i, group: x });
24233
+ }),
24234
+ !lodash.isEmpty(activeOption?.optionUnits) && activeOption?.optionUnits.map((x, i) => React__default["default"].createElement(SummaryBookingOptionUnit, { key: i, unit: x })),
24235
+ !lodash.isEmpty(activeOption?.optionPax) && activeOption?.optionPax.map((x, i) => React__default["default"].createElement(SummaryBookingOptionPax, { key: i, pax: x })),
24236
+ packageDetails?.outwardFlights &&
24237
+ packageDetails.outwardFlights
24238
+ .filter((x) => x.isSelected)
24239
+ .map((flight, i) => (React__default["default"].createElement(SummaryFlight, { key: i, flightMetaData: flight.flightMetaData, header: translations.SIDEBAR.DEPARTURE_FLIGHT }))),
24240
+ packageDetails?.returnFlights &&
24241
+ packageDetails.returnFlights
24242
+ .filter((x) => x.isSelected)
24243
+ .map((flight, i) => (React__default["default"].createElement(SummaryFlight, { key: i, flightMetaData: flight.flightMetaData, header: translations.SIDEBAR.ARRIVAL_FLIGHT })))))))))))),
24244
+ settings.enableVoucher && (React__default["default"].createElement("div", { className: "form__region" },
24245
+ React__default["default"].createElement("div", { className: "form__row" },
24246
+ React__default["default"].createElement("div", { className: "form__group" },
24247
+ React__default["default"].createElement("div", { className: "form__region-header" },
24248
+ React__default["default"].createElement("h5", { className: "form__region-heading" }, translations.SUMMARY.VOUCHERS)))),
24249
+ React__default["default"].createElement("div", { className: "form__row" },
24250
+ React__default["default"].createElement("div", { className: "form__group" },
24251
+ React__default["default"].createElement("input", { type: "text", className: "form__input info-message__voucher__input", defaultValue: voucher.code, onChange: (e) => setVoucher({ code: e.target.value }) }),
24252
+ React__default["default"].createElement("button", { type: "button", className: buildClassName(['cta', !voucher.code && 'cta--disabled']), onClick: (e) => handleValidateVoucher() }, translations.SUMMARY.VOUCHER_VALIDATE))),
24253
+ React__default["default"].createElement("div", { className: "form__row" },
24254
+ React__default["default"].createElement("div", { className: "form__group info-message__voucher" },
24255
+ voucher.isValid && voucher.isValidated && (React__default["default"].createElement("div", { className: "info-message info-message__voucher__valid" },
24256
+ React__default["default"].createElement("span", null, translations.SUMMARY.VOUCHER_VALID),
24257
+ React__default["default"].createElement("button", { type: "button", className: "cta cta--secondary", onClick: handleAddVoucher }, translations.SUMMARY.ADD_VOUCHER))),
24258
+ !voucher.isValid && voucher.isValidated && React__default["default"].createElement("div", { className: "info-message--error" }, translations.SUMMARY.VOUCHER_INVALID))),
24259
+ React__default["default"].createElement("div", { className: "form__row" },
24260
+ React__default["default"].createElement("ul", { className: "info-message__voucher" }, !lodash.isEmpty(bookRequest?.payload.voucherCodes) &&
24261
+ bookRequest?.payload.voucherCodes?.map((y, i) => (React__default["default"].createElement("li", { key: i },
24262
+ React__default["default"].createElement("div", { className: "info-message__voucher__list" },
24263
+ y,
24264
+ ' ',
24265
+ React__default["default"].createElement("button", { type: "button", className: "cta--add-remove", onClick: (e) => handleRemoveVoucher(y) },
24266
+ React__default["default"].createElement(Icon, { height: 16, name: "ui-trashcan" })))))))))),
24267
+ !lodash.isEmpty(notifications) && (React__default["default"].createElement("div", { className: "form__region" },
24268
+ React__default["default"].createElement("div", { className: "form__row" },
24269
+ React__default["default"].createElement("div", { className: "form__group" },
24270
+ React__default["default"].createElement("div", { className: "info-message" },
24271
+ React__default["default"].createElement(Icon, { name: "ui-tooltip", className: "icon--secondary-color" }),
24272
+ React__default["default"].createElement("div", { className: "info-message__copy" },
24273
+ React__default["default"].createElement("h5", null, translations.SUMMARY.NOTIFICATIONS_TITLE),
24274
+ React__default["default"].createElement(React__default["default"].Fragment, null,
24275
+ lodash.uniqBy(notifications.filter((x) => !x.hasToBeConfirmed), 'id').map((notification) => (React__default["default"].createElement("span", { key: notification.id, className: "checkbox__label-text" },
24276
+ React__default["default"].createElement("strong", { className: "checkbox__label-text--title" }, notification.title),
24277
+ React__default["default"].createElement("span", { className: "checkbox__label-text--description" }, notification.description)))),
24278
+ lodash.uniqBy(notifications.filter((x) => x.hasToBeConfirmed), 'id').map((notification) => (React__default["default"].createElement("div", { className: "checkbox", key: notification.id },
24279
+ React__default["default"].createElement("label", { className: "checkbox__label" },
24280
+ React__default["default"].createElement("input", { type: "checkbox", className: "checkbox__input", checked: notification.isConfirmed, onChange: (e) => handleNotificationChange(notification.id, e.target.checked) }),
24281
+ React__default["default"].createElement("span", { className: "checkbox__label-text" },
24282
+ React__default["default"].createElement("strong", { className: "checkbox__label-text--title" }, notification.title),
24283
+ React__default["default"].createElement("span", { className: "checkbox__label-text--description" }, notification.description))))))))))))),
24284
+ React__default["default"].createElement("div", { className: "form__region" },
24285
+ React__default["default"].createElement("div", { className: "form__row" },
24286
+ React__default["default"].createElement("div", { className: "form__group" },
24287
+ React__default["default"].createElement("div", { className: "form__region-header" },
24288
+ React__default["default"].createElement("h5", { className: "form__region-heading" }, translations.SUMMARY.REMARKS)))),
24289
+ React__default["default"].createElement("div", { className: "form__row" },
24290
+ React__default["default"].createElement("div", { className: "form__group" },
24291
+ React__default["default"].createElement("textarea", { className: "form__input", defaultValue: remarks, onChange: (e) => handleRemarksChange(e.target.value) })))),
24292
+ React__default["default"].createElement("div", { className: "form__region" },
24293
+ React__default["default"].createElement("div", { className: "form__row" },
24294
+ React__default["default"].createElement("div", { className: "form__group" },
24295
+ React__default["default"].createElement("div", { className: "info-message" },
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))))));
23822
24315
  };
23823
24316
 
23824
24317
  function isValidEmail(email) {
@@ -23842,7 +24335,7 @@ function getAge(birthDateText, startDateText) {
23842
24335
  }
23843
24336
  return age;
23844
24337
  }
23845
- const validateForm$1 = (values, agentRequired, bookingType, translations, formFields, mainBookerFormFields, maxChildAge = CHILD_MAX_AGE, maxBabyAge = BABY_MAX_AGE, paxCustomFields = []) => {
24338
+ const validateForm$1 = (values, agentRequired, bookingType, translations, formFields, mainBookerFormFields, maxChildAge = CHILD_MAX_AGE, maxBabyAge = BABY_MAX_AGE, paxFields = []) => {
23846
24339
  const errors = {};
23847
24340
  const isFormFieldPresent = (type) => {
23848
24341
  return formFields?.some((f) => f.type === type) ?? true;
@@ -23880,7 +24373,7 @@ const validateForm$1 = (values, agentRequired, bookingType, translations, formFi
23880
24373
  };
23881
24374
  const validateRequiredCustomFields = (traveler, path, room, pax) => {
23882
24375
  const isMainBooker = traveler.id === values.mainBookerId;
23883
- const requiredFields = getApplicablePaxCustomFields(paxCustomFields, isMainBooker).filter((f) => f.isRequired);
24376
+ const requiredFields = getApplicablePaxCustomFields(paxFields, isMainBooker).filter((f) => f.isRequired);
23884
24377
  requiredFields.forEach((field) => {
23885
24378
  const fieldIndex = traveler.customFields?.findIndex((c) => c.customFieldConfigurationId === field.customFieldConfigurationId) ?? -1;
23886
24379
  if (fieldIndex < 0)
@@ -23890,6 +24383,19 @@ const validateForm$1 = (values, agentRequired, bookingType, translations, formFi
23890
24383
  }
23891
24384
  });
23892
24385
  };
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
+ });
24398
+ };
23893
24399
  values.rooms.forEach((r, rIndex) => r.adults.forEach((adult, index) => {
23894
24400
  if (lodash.isEmpty(adult.gender) && isFormFieldPresent('gender')) {
23895
24401
  lodash.set(errors, `rooms[${rIndex}].adults[${index}].gender`, formatTravelerField(rIndex + 1, index + 1, translations.TRAVELERS_FORM.GENDER));
@@ -23923,6 +24429,7 @@ const validateForm$1 = (values, agentRequired, bookingType, translations, formFi
23923
24429
  }
23924
24430
  }
23925
24431
  validateRequiredCustomFields(adult, `rooms[${rIndex}].adults[${index}]`, rIndex + 1, index + 1);
24432
+ validateRequiredBuiltInFields(adult, `rooms[${rIndex}].adults[${index}]`, rIndex + 1, index + 1);
23926
24433
  }));
23927
24434
  values.rooms.forEach((r, rIndex) => r.children.forEach((child, index) => {
23928
24435
  if (lodash.isEmpty(child.gender) && isFormFieldPresent('gender')) {
@@ -23965,6 +24472,7 @@ const validateForm$1 = (values, agentRequired, bookingType, translations, formFi
23965
24472
  }
23966
24473
  }
23967
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);
23968
24476
  }));
23969
24477
  if (values.mainBookerId < 0) {
23970
24478
  errors.mainBookerId = translations.TRAVELERS_FORM.VALIDATION.NO_MAIN_BOOKER_SELECTED;
@@ -24032,15 +24540,13 @@ const TravelersForm = () => {
24032
24540
  const bookingAttributes = reactRedux.useSelector(selectBookingAttributes);
24033
24541
  const hasMounted = reactRedux.useSelector(selectHasMounted);
24034
24542
  const countries = reactRedux.useSelector(selectCountries);
24035
- const paxCustomFields = reactRedux.useSelector(selectPaxCustomFields);
24543
+ const paxFields = reactRedux.useSelector(selectPaxFields);
24036
24544
  const inFlight = React.useRef(false);
24037
24545
  const pendingRefetch = React.useRef(false);
24038
24546
  const useCompactForm = !!settings.travellers.compactForm && !!settings.agentAdressId;
24039
24547
  const showAllCountries = !!settings.travellers.showAllCountries;
24040
- const showWeightField = !!settings.travellers.showWeightField;
24041
- const showHeightField = !!settings.travellers.showHeightField;
24042
24548
  const initialValues = reactRedux.useSelector(selectTravelersFormValues) ??
24043
- createInitialValuesFromRooms(formRooms, startDate, endDate, agentAdressId, translations.TRAVELERS_FORM.PERSON, useCompactForm, settings.maxBabyAge, paxCustomFields);
24549
+ createInitialValuesFromRooms(formRooms, startDate, endDate, agentAdressId, translations.TRAVELERS_FORM.PERSON, useCompactForm, settings.maxBabyAge, paxFields);
24044
24550
  const [showAgentSelection, setShowAgentSelection] = React.useState(!settings.agentAdressId && !settings.hideAgentSelection);
24045
24551
  const formik$1 = formik.useFormik({
24046
24552
  initialValues,
@@ -24050,7 +24556,7 @@ const TravelersForm = () => {
24050
24556
  // The travel dates are captured into initialValues once and then persisted in
24051
24557
  // redux, so validating against the values would use the dates of whichever
24052
24558
  // package option was selected first. Age checks need the current ones.
24053
- { ...values, startDate, endDate }, settings.agentRequired, bookingType, translations, settings.travellers.formFields, settings.travellers.mainBookerFormFields, settings.maxChildAge, settings.maxBabyAge, paxCustomFields),
24559
+ { ...values, startDate, endDate }, settings.agentRequired, bookingType, translations, settings.travellers.formFields, settings.travellers.mainBookerFormFields, settings.maxChildAge, settings.maxBabyAge, paxFields),
24054
24560
  onSubmit: (values) => {
24055
24561
  dispatch(setFormValues(values));
24056
24562
  if (useCompactForm) {
@@ -24133,7 +24639,7 @@ const TravelersForm = () => {
24133
24639
  dispatch(setFormValues(formValues));
24134
24640
  setShowAgentSelection(false);
24135
24641
  }, [agents, settings.affiliateSlug]);
24136
- 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, showWeightField: showWeightField, showHeightField: showHeightField, paxCustomFields: paxCustomFields, 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 }));
24137
24643
  };
24138
24644
 
24139
24645
  const Booking = ({ productCode, productName, thumbnailUrl }) => {
@@ -28280,6 +28786,22 @@ const getPackagingRequestRoomsFromBookingRooms = (rooms) => {
28280
28786
  });
28281
28787
  };
28282
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
+ };
28283
28805
  const getFlightLines = (flight) => {
28284
28806
  return flight?.flightInformation?.flightLines ?? [];
28285
28807
  };
@@ -28734,6 +29256,8 @@ const applyResultToLine = (line, result, roomIndex = 0) => {
28734
29256
  isChanged: true
28735
29257
  };
28736
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);
28737
29261
  const toDateOnlyUtcString = (value) => {
28738
29262
  const date = new Date(value);
28739
29263
  return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate())).toISOString();
@@ -28791,11 +29315,21 @@ const buildTrajectFlightRequest = ({ node, line }, context, pax) => ({
28791
29315
  * mapper gave them. This swaps in the guid of whatever each line resolved onto, leaving the
28792
29316
  * lines the traveller added themselves alone — those were built from a search result and already
28793
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.
28794
29323
  */
28795
29324
  const withCachedEntryLineGuids = (entry, trajectEntry, nodeAvailability, flightAvailability) => {
28796
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;
28797
29329
  getTrajectNodesWithLines(trajectEntry).forEach((item) => {
28798
29330
  if (isTrajectFlightNode(item.node)) {
29331
+ if (keepsOwnGuid(item.line.guid))
29332
+ return;
28799
29333
  // A flight search caches the outward line under the guid it reports as outwardGuid.
28800
29334
  const guid = flightAvailability[item.line.guid]?.selectedGuid;
28801
29335
  if (guid)
@@ -28808,6 +29342,8 @@ const withCachedEntryLineGuids = (entry, trajectEntry, nodeAvailability, flightA
28808
29342
  return;
28809
29343
  // Room i took option i when the result was stamped on, so it takes option i here too.
28810
29344
  item.lines.forEach((line, index) => {
29345
+ if (keepsOwnGuid(line.guid))
29346
+ return;
28811
29347
  const guid = getOptionForRoom(result, index)?.guid;
28812
29348
  if (guid)
28813
29349
  cachedGuids.set(line.guid, guid);
@@ -28871,7 +29407,7 @@ const TrajectItinerary = ({ trajectEntry, packagingEntry, translations }) => {
28871
29407
  return { node, lines };
28872
29408
  });
28873
29409
  const claimed = new Set(nodes.flatMap((item) => item.lines.map((line) => line.guid)));
28874
- const extras = (packagingEntry.lines ?? []).filter((line) => !claimed.has(line.guid));
29410
+ const extras = (packagingEntry.lines ?? []).filter((line) => !claimed.has(line.guid)).filter(isMeaningfulItineraryLine);
28875
29411
  if (!extras.length)
28876
29412
  return nodes;
28877
29413
  const dayOrderOf = (date) => trajectEntry.traject.days.find((day) => dateFns.isSameDay(new Date(day.date), new Date(date)))?.order ??
@@ -29048,6 +29584,195 @@ const TrajectItinerary = ({ trajectEntry, packagingEntry, translations }) => {
29048
29584
  })));
29049
29585
  };
29050
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
+
29051
29776
  const getSegmentIcon = (segment) => {
29052
29777
  switch (segment.serviceType) {
29053
29778
  case 3:
@@ -29087,14 +29812,14 @@ const SERVICE_TYPE_PRIORITY = {
29087
29812
  const getServiceTypePriority = (serviceType) => {
29088
29813
  return SERVICE_TYPE_PRIORITY[serviceType ?? -1] ?? 2;
29089
29814
  };
29090
- const Itinerary = ({ isOpen, handleSetIsOpen, isLoading, onEditAccommodation }) => {
29815
+ const Itinerary = ({ isOpen, handleSetIsOpen, isLoading, onEditAccommodation, onSaveEntry }) => {
29091
29816
  const context = React.useContext(SearchResultsConfigurationContext);
29092
29817
  const translations = getTranslations(context?.languageCode ?? 'en-GB');
29093
29818
  const { editablePackagingEntry, priceDetails, trajectItineraryView } = reactRedux.useSelector((state) => state.searchResults);
29094
29819
  const dispatch = reactRedux.useDispatch();
29095
29820
  const packagingEntry = editablePackagingEntry ?? context?.packagingEntry;
29096
29821
  const sortedLines = React.useMemo(() => {
29097
- return [...(packagingEntry?.lines ?? [])].sort((a, b) => {
29822
+ return (packagingEntry?.lines ?? []).filter(isMeaningfulItineraryLine).sort((a, b) => {
29098
29823
  const dateA = getDateOnlyTime(a.from);
29099
29824
  const dateB = getDateOnlyTime(b.from);
29100
29825
  if (dateA !== dateB) {
@@ -29165,7 +29890,7 @@ const Itinerary = ({ isOpen, handleSetIsOpen, isLoading, onEditAccommodation })
29165
29890
  " ",
29166
29891
  translations.SRP.TOTAL,
29167
29892
  ")"))),
29168
- 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)),
29169
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))))),
29170
29895
  React__default["default"].createElement("div", { className: "search__filter__itinerary" },
29171
29896
  React__default["default"].createElement("p", null, translations.SRP.DAY_BY_DAY),
@@ -31222,7 +31947,7 @@ const selectSelectedCombinationFlight = toolkit.createSelector([selectPackagingF
31222
31947
  return (packagingFlightResults.find((flight) => getFlightKey(flight.outward.segments) === selectedOutwardKey && getFlightKey(flight.return.segments) === selectedReturnKey) ?? null);
31223
31948
  });
31224
31949
 
31225
- const IndependentFlightOption = ({ item, onSelect, guid, selectedGuid, isOutward, showSelectedState, currentSelectedPrice, price }) => {
31950
+ const IndependentFlightOption = ({ item, onSelect, guid, selectedGuid, isOutward, showSelectedState, currentSelectedPrice, price, selectLabel, selectedLabel }) => {
31226
31951
  const context = React.useContext(SearchResultsConfigurationContext);
31227
31952
  const language = context?.languageCode ?? 'en-GB';
31228
31953
  const translations = getTranslations(language);
@@ -31247,7 +31972,7 @@ const IndependentFlightOption = ({ item, onSelect, guid, selectedGuid, isOutward
31247
31972
  priceDifference > 0 ? '+' : '-',
31248
31973
  "\u00A0",
31249
31974
  formatPrice$3(Math.abs(priceDifference), 'EUR', context?.languageCode ?? 'en-GB'))))),
31250
- 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)))),
31251
31976
  React__default["default"].createElement("div", { className: "flight__flight" },
31252
31977
  React__default["default"].createElement("div", { className: "flight__flight__container" },
31253
31978
  React__default["default"].createElement("div", { className: "flight__flight__wrapper" },
@@ -31881,7 +32606,7 @@ const TrajectNodeCard = ({ node, result, isSelected, languageCode, translations,
31881
32606
  const image = candidate?.imageUrl ?? null;
31882
32607
  const description = toPlainText(candidate?.description);
31883
32608
  const title = candidate?.name ?? result.name;
31884
- 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" },
31885
32610
  React__default["default"].createElement("span", { className: "search__result-card__price__label" }, translations?.SHARED.TOTAL_PRICE),
31886
32611
  React__default["default"].createElement("span", { className: "search__result-card__price" }, price)));
31887
32612
  const canEditOptions = isSelected && !!onEditOptions && (result.rooms ?? []).some((room) => (room.options ?? []).length > 1);
@@ -31934,6 +32659,7 @@ const TrajectResultsFlyIn = ({ isLoading }) => {
31934
32659
  const translations = getTranslations(context?.languageCode ?? 'en-GB');
31935
32660
  const { trajectNodeAvailability, trajectEditLineGuid } = reactRedux.useSelector((state) => state.searchResults);
31936
32661
  const trajectEntry = context?.trajectEntry;
32662
+ const bookedMode = isBookedEntryTraject(trajectEntry);
31937
32663
  const item = React.useMemo(() => {
31938
32664
  if (!trajectEntry || !trajectEditLineGuid)
31939
32665
  return null;
@@ -31953,7 +32679,7 @@ const TrajectResultsFlyIn = ({ isLoading }) => {
31953
32679
  if (!result)
31954
32680
  return;
31955
32681
  dispatch(setTrajectNodeSelection({ lineGuid: item.line.guid, code }));
31956
- applyResultToLines(item.lines, result).forEach((line) => dispatch(updateEditableEntryLine(line)));
32682
+ buildNodeSelectionLines(item, result, bookedMode).forEach((line) => dispatch(updateEditableEntryLine(line)));
31957
32683
  dispatch(setTrajectEditLineGuid(null));
31958
32684
  dispatch(setFlyInIsOpen(false));
31959
32685
  };
@@ -31966,7 +32692,7 @@ const TrajectResultsFlyIn = ({ isLoading }) => {
31966
32692
  ordered.length,
31967
32693
  "\u00A0",
31968
32694
  translations.SRP.TOTAL_RESULTS_LABEL)),
31969
- 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 })))))));
31970
32696
  };
31971
32697
 
31972
32698
  /**
@@ -32028,6 +32754,7 @@ const TrajectDayActivitiesFlyIn = ({ isLoading }) => {
32028
32754
  const translations = getTranslations(context?.languageCode ?? 'en-GB');
32029
32755
  const { trajectNodeAvailability, trajectDaySuggestions, trajectActivitiesDayOrder, editablePackagingEntry } = reactRedux.useSelector((state) => state.searchResults);
32030
32756
  const trajectEntry = context?.trajectEntry;
32757
+ const bookedMode = isBookedEntryTraject(trajectEntry);
32031
32758
  const items = React.useMemo(() => {
32032
32759
  if (!trajectEntry || trajectActivitiesDayOrder === null)
32033
32760
  return [];
@@ -32060,7 +32787,7 @@ const TrajectDayActivitiesFlyIn = ({ isLoading }) => {
32060
32787
  if (!item)
32061
32788
  return;
32062
32789
  dispatch(setTrajectNodeSelection({ lineGuid: item.line.guid, code: result.code }));
32063
- applyResultToLines(item.lines, result).forEach((line) => dispatch(updateEditableEntryLine(line)));
32790
+ buildNodeSelectionLines(item, result, bookedMode).forEach((line) => dispatch(updateEditableEntryLine(line)));
32064
32791
  dispatch(setTrajectActivitiesDayOrder(null));
32065
32792
  dispatch(setFlyInIsOpen(false));
32066
32793
  };
@@ -32076,7 +32803,8 @@ const TrajectDayActivitiesFlyIn = ({ isLoading }) => {
32076
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) => {
32077
32804
  const { result, item } = card;
32078
32805
  const isSelected = item ? trajectNodeAvailability[item.line.guid]?.selectedCode === result.code : getAddedLines(result).length > 0;
32079
- 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 }));
32080
32808
  })))));
32081
32809
  };
32082
32810
 
@@ -33686,6 +34414,10 @@ const getUniqueItems = (items = []) => {
33686
34414
  return true;
33687
34415
  });
33688
34416
  };
34417
+ const isMeaningfulItem = (item, lines) => {
34418
+ const line = lines.find((l) => l.guid === item.itemGuid);
34419
+ return !line || isMeaningfulItineraryLine(line);
34420
+ };
33689
34421
  const findAccommodationForDay = (nodes, currentDay) => {
33690
34422
  for (const node of nodes ?? []) {
33691
34423
  const { startDay } = getNodeDayRange(node);
@@ -33767,7 +34499,7 @@ const buildItineraryHtml = (itinerary, translations, lines) => {
33767
34499
  const { startDay } = getNodeDayRange(node);
33768
34500
  const day = node.startDate ? dateFns.format(node.startDate, 'd') : null;
33769
34501
  const month = node.startDate ? dateFns.format(node.startDate, 'MMM') : null;
33770
- const uniqueItems = getUniqueItems(node.items ?? []);
34502
+ const uniqueItems = getUniqueItems(node.items ?? []).filter((item) => isMeaningfulItem(item, lines));
33771
34503
  const hasItems = uniqueItems.length > 0;
33772
34504
  const hasAccommodationInCurrentNode = uniqueItems.some(isAccommodationItem);
33773
34505
  const activeAccommodation = findAccommodationForDay(allNodes, startDay);
@@ -33811,11 +34543,12 @@ const buildItineraryHtml = (itinerary, translations, lines) => {
33811
34543
  `;
33812
34544
  })
33813
34545
  .join('');
33814
- const defaultItemsHtml = itinerary.defaultItems && itinerary.defaultItems.length > 0
34546
+ const defaultItems = getUniqueItems(itinerary.defaultItems ?? []).filter((item) => isMeaningfulItem(item, lines));
34547
+ const defaultItemsHtml = defaultItems.length > 0
33815
34548
  ? `
33816
34549
  <section class="itinerary-default-items">
33817
34550
  <h2 class="itinerary-default-items__title">Algemene info</h2>
33818
- ${getUniqueItems(itinerary.defaultItems)
34551
+ ${defaultItems
33819
34552
  .map((item) => `
33820
34553
  <article class="itinerary-item" data-template="${escapeHtml(item.templateName)}">
33821
34554
  ${hasVisibleContent(item.contents)
@@ -34474,7 +35207,7 @@ const BookPackagingEntry = ({ activeSearchSeed, isLoading, isConfirmationPage })
34474
35207
  const formik$1 = formik.useFormik({
34475
35208
  initialValues,
34476
35209
  enableReinitialize: true,
34477
- 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 ?? []),
34478
35211
  onSubmit: (values) => {
34479
35212
  if (!editablePackagingEntry)
34480
35213
  return;
@@ -34559,7 +35292,7 @@ const BookPackagingEntry = ({ activeSearchSeed, isLoading, isConfirmationPage })
34559
35292
  ".\u00A0",
34560
35293
  stepLabels[step])) },
34561
35294
  isConfirmationPage && isLoading && React__default["default"].createElement(Spinner, { label: translations.SUMMARY.PROCESS_BOOKING }),
34562
- 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 ?? [] })),
34563
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)) })),
34564
35297
  currentStep === 2 && (React__default["default"].createElement(SharedConfirmation, { bookingNumber: bookingNumber ?? editablePackagingEntry?.dossierNumber ?? '', isOption: false, isOffer: false, translations: translations.CONFIRMATION })),
34565
35298
  currentStep === 3 && React__default["default"].createElement("div", null))),
@@ -34623,6 +35356,7 @@ const TrajectResults = ({ isLoading }) => {
34623
35356
  const locale = getLocale(context?.languageCode ?? 'en-GB');
34624
35357
  const { trajectNodeAvailability, trajectFlightAvailability, trajectDaySuggestions, editablePackagingEntry } = reactRedux.useSelector((state) => state.searchResults);
34625
35358
  const trajectEntry = context?.trajectEntry;
35359
+ const bookedMode = isBookedEntryTraject(trajectEntry);
34626
35360
  const hideShowMoreResults = !!context?.hideShowMoreResults;
34627
35361
  const loadDayActivitiesOnDemand = !!context?.loadDayActivitiesOnDemand;
34628
35362
  const showNonPreferredHotels = !!context?.showNonPreferredHotels;
@@ -34679,7 +35413,7 @@ const TrajectResults = ({ isLoading }) => {
34679
35413
  if (!result)
34680
35414
  return;
34681
35415
  dispatch(setTrajectNodeSelection({ lineGuid: item.line.guid, code }));
34682
- applyResultToLines(item.lines, result).forEach((line) => dispatch(updateEditableEntryLine(line)));
35416
+ buildNodeSelectionLines(item, result, bookedMode).forEach((line) => dispatch(updateEditableEntryLine(line)));
34683
35417
  };
34684
35418
  /** The remaining legs for a flight node open in the fly-in, as the regular flight results do. */
34685
35419
  const handleShowMoreFlights = (item) => {
@@ -34767,7 +35501,7 @@ const TrajectResults = ({ isLoading }) => {
34767
35501
  node.preferredAccommodationName,
34768
35502
  " \u2014 ",
34769
35503
  translations.SRP.NO_RESULTS))),
34770
- 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 })))),
34771
35505
  remaining > 0 && !hideShowMoreResults && (React__default["default"].createElement("div", { className: "search__results__cards__actions" },
34772
35506
  React__default["default"].createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => handleShowMore(item) },
34773
35507
  translations.SRP.SHOW_MORE,
@@ -34777,7 +35511,7 @@ const TrajectResults = ({ isLoading }) => {
34777
35511
  };
34778
35512
  const handleSelectFlight = (item, flight) => {
34779
35513
  dispatch(setTrajectFlightSelection({ lineGuid: item.line.guid, guid: flight.outwardGuid }));
34780
- dispatch(updateEditableEntryLine(applyFlightToLine(item.line, flight)));
35514
+ dispatch(updateEditableEntryLine(buildFlightSelectionLine(item, flight, bookedMode)));
34781
35515
  };
34782
35516
  const renderFlightNode = (item) => {
34783
35517
  const { node, line } = item;
@@ -34803,7 +35537,7 @@ const TrajectResults = ({ isLoading }) => {
34803
35537
  status === 'unavailable' && React__default["default"].createElement("div", { className: "no-results" }, translations.SRP.NO_RESULTS),
34804
35538
  status === 'resolved' && selected && (React__default["default"].createElement(React__default["default"].Fragment, null,
34805
35539
  React__default["default"].createElement("div", { className: "search__results__cards search__results__cards--extended" },
34806
- 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 }),
34807
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) })))),
34808
35542
  others.length > 2 && !hideShowMoreResults && (React__default["default"].createElement("div", { className: "search__results__cards__actions" },
34809
35543
  React__default["default"].createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => handleShowMoreFlights(item) },
@@ -34887,7 +35621,8 @@ const TrajectResults = ({ isLoading }) => {
34887
35621
  */
34888
35622
  const renderActivityCard = ({ result, item }) => {
34889
35623
  const isSelected = item ? trajectNodeAvailability[item.line.guid]?.selectedCode === result.code : getAddedExcursionLines(result).length > 0;
34890
- 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 }));
34891
35626
  };
34892
35627
  if (isLoading)
34893
35628
  return React__default["default"].createElement(Spinner, { label: translations.SRP.LOADING_ITINERARY });
@@ -34931,6 +35666,7 @@ const useTrajectAvailability = () => {
34931
35666
  const controller = new AbortController();
34932
35667
  dispatch(resetTrajectAvailability());
34933
35668
  dispatch(setTrajectResolving(true));
35669
+ const bookedMode = isBookedEntryTraject(trajectEntry);
34934
35670
  const config = {
34935
35671
  host: context.tideConnection.host,
34936
35672
  apiKey: context.tideConnection.apiKey
@@ -34992,6 +35728,17 @@ const useTrajectAvailability = () => {
34992
35728
  });
34993
35729
  const configuredCodes = new Set(configuredResults.map((x) => x.code));
34994
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
+ }
34995
35742
  const preferredCandidate = candidates.find((x) => x.isPreferred) ?? candidates[0];
34996
35743
  const preferredUnavailable = configuredResults.length
34997
35744
  ? !!preferredCandidate && !configuredResults.some((x) => matchesCode(x, preferredCandidate.code))
@@ -35014,6 +35761,11 @@ const useTrajectAvailability = () => {
35014
35761
  if (controller.signal.aborted)
35015
35762
  return [];
35016
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
+ }
35017
35769
  dispatch(setTrajectNodeAvailability({ lineGuid, results: [], selectedCode: null, preferredUnavailable: false }));
35018
35770
  return item.lines.map((x) => x.guid);
35019
35771
  }
@@ -35021,6 +35773,11 @@ const useTrajectAvailability = () => {
35021
35773
  const resolveFlightNode = async (item) => {
35022
35774
  const lineGuid = item.line.guid;
35023
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
+ }
35024
35781
  dispatch(setTrajectFlightAvailability({ lineGuid, results: [], selectedGuid: null }));
35025
35782
  return item.lines.map((x) => x.guid);
35026
35783
  }
@@ -35029,6 +35786,11 @@ const useTrajectAvailability = () => {
35029
35786
  const flights = (await build.searchPackagingFlights(config, buildTrajectFlightRequest(item, searchContext, flightPax), controller.signal)) ?? [];
35030
35787
  if (controller.signal.aborted)
35031
35788
  return [];
35789
+ if (bookedMode) {
35790
+ const booked = buildBookedFlightResult(item.line);
35791
+ dispatch(setTrajectFlightAvailability({ lineGuid, results: [booked, ...flights], selectedGuid: booked.outwardGuid }));
35792
+ return [];
35793
+ }
35032
35794
  const picked = pickCheapestFlight(flights);
35033
35795
  dispatch(setTrajectFlightAvailability({ lineGuid, results: flights, selectedGuid: picked?.outwardGuid ?? null }));
35034
35796
  if (!picked)
@@ -35040,6 +35802,11 @@ const useTrajectAvailability = () => {
35040
35802
  if (controller.signal.aborted)
35041
35803
  return [];
35042
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
+ }
35043
35810
  dispatch(setTrajectFlightAvailability({ lineGuid, results: [], selectedGuid: null }));
35044
35811
  return item.lines.map((x) => x.guid);
35045
35812
  }
@@ -35077,6 +35844,7 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
35077
35844
  const [detailsIsLoading, setDetailsIsLoading] = React.useState(false);
35078
35845
  const [pricesAreLoading, setPricesAreLoading] = React.useState(false);
35079
35846
  const [itineraryIsLoading, setItineraryIsLoading] = React.useState(false);
35847
+ const [entrySaving, setEntrySaving] = React.useState(false);
35080
35848
  const [itineraryOpen, setItineraryOpen] = React.useState(false);
35081
35849
  const [isBookingConfirmation, setIsBookingConfirmation] = React.useState(false);
35082
35850
  const [selectedAccommodationSeed, setSelectedAccommodationSeed] = React.useState(null);
@@ -35833,6 +36601,31 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
35833
36601
  React.useEffect(() => {
35834
36602
  setInitialFiltersSet(false);
35835
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
+ };
35836
36629
  React.useEffect(() => {
35837
36630
  const fetchPriceDetails = async () => {
35838
36631
  if (!context || !editablePackagingEntry || lodash.isEmpty(editablePackagingEntry.lines))
@@ -36217,11 +37010,18 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
36217
37010
  const id = paxId++;
36218
37011
  pax.push({
36219
37012
  id,
37013
+ guid: '',
36220
37014
  firstName: _.firstName || '',
36221
37015
  lastName: _.lastName || '',
36222
- dateOfBirth: _.dateOfBirth || null,
36223
- age: _.age || null,
36224
- 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: []
36225
37025
  });
36226
37026
  return id;
36227
37027
  });
@@ -36241,6 +37041,7 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
36241
37041
  depositAmount: 0,
36242
37042
  pax,
36243
37043
  rooms,
37044
+ paxFields: [],
36244
37045
  lines: [],
36245
37046
  address: {}
36246
37047
  };
@@ -36260,7 +37061,7 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
36260
37061
  context.searchConfiguration.qsmType != build.PortalQsmType.AccommodationAndFlight && context.showFilters && (React__default["default"].createElement(Filters, { initialFilters: initialFilters, filters: filters, isOpen: filtersOpen, handleSetIsOpen: () => setFiltersOpen(!filtersOpen),
36261
37062
  // handleApplyFilters={() => setSearchTrigger((prev) => prev + 1)}
36262
37063
  isLoading: isLoading, setFilters: (filters) => dispatch(setFilters(filters)), resetFilters: (filters) => dispatch(resetFilters(filters)) })),
36263
- 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 })),
36264
37065
  React__default["default"].createElement("div", { className: "search__results" },
36265
37066
  isMobile && (React__default["default"].createElement("div", { className: "search__result-row" },
36266
37067
  React__default["default"].createElement("div", { className: "search__results__actions" },
@@ -36348,8 +37149,15 @@ const createSearchResultsStore = () => toolkit.configureStore({
36348
37149
 
36349
37150
  const SearchResults = ({ configuration, onBookingStarted }) => {
36350
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]);
36351
37159
  return (React__default["default"].createElement(reactRedux.Provider, { store: store },
36352
- React__default["default"].createElement(SearchResultsConfigurationContext.Provider, { value: configuration },
37160
+ React__default["default"].createElement(SearchResultsConfigurationContext.Provider, { value: effectiveConfiguration },
36353
37161
  React__default["default"].createElement(SearchResultsContainer, { onBookingStarted: onBookingStarted }))));
36354
37162
  };
36355
37163