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