@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-esm/index.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
/* eslint-disable */
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
import React__default, { useLayoutEffect as useLayoutEffect$1, useEffect, useContext, useState, useRef,
|
|
3
|
+
import React__default, { useLayoutEffect as useLayoutEffect$1, useEffect, useContext, useState, useRef, useMemo, useCallback } from 'react';
|
|
4
4
|
import JsonURL from '@jsonurl/jsonurl';
|
|
5
|
-
import { format as format$2, isToday, getDate, startOfWeek, startOfMonth, endOfWeek, addWeeks, endOfMonth, eachDayOfInterval, getYear, getMonth, getISOWeek, getISODay, isSameMonth, startOfDay, isAfter, isSameDay, isEqual, isWithinInterval, endOfDay, isBefore, differenceInCalendarDays, addMonths, addDays, formatISO, addYears, differenceInYears,
|
|
6
|
-
import { merge, cloneDeep, isNil, isArray as isArray$1, compact, isEmpty as isEmpty$1, range, chunk, isFunction as isFunction$1, clamp, now, omit, isPlainObject as isPlainObject$1, mapValues, pickBy,
|
|
5
|
+
import { format as format$2, isToday, getDate, startOfWeek, startOfMonth, endOfWeek, addWeeks, endOfMonth, eachDayOfInterval, getYear, getMonth, getISOWeek, getISODay, isSameMonth, startOfDay, isAfter, isSameDay, isEqual, isWithinInterval, endOfDay, isBefore, differenceInCalendarDays, addMonths, addDays, formatISO, addYears, differenceInYears, parse, isValid, parseISO, differenceInMinutes, startOfToday } from 'date-fns';
|
|
6
|
+
import { merge, cloneDeep, isNil, isArray as isArray$1, compact, isEmpty as isEmpty$1, range, chunk, isFunction as isFunction$1, clamp, now, omit, get, last, first, sortBy, isPlainObject as isPlainObject$1, mapValues, pickBy, orderBy, uniq, uniqBy, sum, findIndex as findIndex$1, set, isEqual as isEqual$1, groupBy, minBy, concat, flatMap } from 'lodash';
|
|
7
7
|
import { arSA, da, de, enGB, es, fr, is, it, nl, nb, pl, pt, sv, ja, enUS } from 'date-fns/locale';
|
|
8
8
|
import { usePopper } from 'react-popper';
|
|
9
9
|
import { useSelector, useDispatch, Provider } from 'react-redux';
|
|
10
10
|
import { createSlice, createSelector, createAsyncThunk, configureStore } from '@reduxjs/toolkit';
|
|
11
|
+
import flat from 'flat';
|
|
11
12
|
import { UNSAFE_NavigationContext, useHref, useNavigate, useLocation, useResolvedPath, createPath, UNSAFE_DataRouterStateContext, UNSAFE_useRouteId, UNSAFE_RouteContext, UNSAFE_DataRouterContext, Routes, Route } from 'react-router';
|
|
12
13
|
import 'react-dom';
|
|
13
14
|
import produce from 'immer';
|
|
14
15
|
import { useFormik } from 'formik';
|
|
15
|
-
import flat from 'flat';
|
|
16
16
|
import { v4 } from 'uuid';
|
|
17
17
|
|
|
18
18
|
function _mergeNamespaces(n, m) {
|
|
@@ -124,7 +124,9 @@ var SHARED$f = {
|
|
|
124
124
|
ADULTS: "بالغون",
|
|
125
125
|
CHILDREN: "أطفال",
|
|
126
126
|
SELECT: "اختر",
|
|
127
|
-
SELECTED: "تم الاختيار"
|
|
127
|
+
SELECTED: "تم الاختيار",
|
|
128
|
+
BOOKED: "محجوز",
|
|
129
|
+
SAVE: "حفظ"
|
|
128
130
|
};
|
|
129
131
|
var SIDEBAR$f = {
|
|
130
132
|
OVERVIEW: "الملخص",
|
|
@@ -182,6 +184,27 @@ var TRAVELERS_FORM$f = {
|
|
|
182
184
|
FIRST_NAME: "الاسم الأول",
|
|
183
185
|
LAST_NAME: "اسم العائلة",
|
|
184
186
|
BIRTHDATE: "تاريخ الميلاد",
|
|
187
|
+
NICKNAME: "Nickname",
|
|
188
|
+
BIRTHPLACE: "Birthplace",
|
|
189
|
+
NATIONALITY: "Nationality",
|
|
190
|
+
PREFERRED_LANGUAGE: "Preferred language",
|
|
191
|
+
TRAVELER_EMAIL: "Email",
|
|
192
|
+
TRAVELER_PHONE: "Phone",
|
|
193
|
+
TRAVELER_MOBILE: "Mobile phone",
|
|
194
|
+
PASSPORT_NUMBER: "Passport number",
|
|
195
|
+
PASSPORT_ISSUE_DATE: "Passport issue date",
|
|
196
|
+
PASSPORT_ISSUED_LOCATION: "Passport issued location",
|
|
197
|
+
PASSPORT_EXPIRY_DATE: "Passport expiry date",
|
|
198
|
+
EMERGENCY_NUMBER: "Emergency number",
|
|
199
|
+
HAS_INSURANCE: "Has insurance",
|
|
200
|
+
INSURANCE_NAME: "Insurance name",
|
|
201
|
+
INFO: "Additional information",
|
|
202
|
+
WEIGHT: "الوزن",
|
|
203
|
+
WEIGHT_UNIT_KG: "kg",
|
|
204
|
+
WEIGHT_UNIT_LB: "lb",
|
|
205
|
+
HEIGHT: "الطول",
|
|
206
|
+
HEIGHT_UNIT_CM: "cm",
|
|
207
|
+
HEIGHT_UNIT_FT_IN: "ft/in",
|
|
185
208
|
STREET: "الشارع",
|
|
186
209
|
STREET_PLACEHOLDER: "اسم الشارع",
|
|
187
210
|
HOUSE_NUMBER: "رقم المنزل",
|
|
@@ -571,7 +594,9 @@ var SHARED$e = {
|
|
|
571
594
|
ADULTS: "Voksne",
|
|
572
595
|
CHILDREN: "Børn",
|
|
573
596
|
SELECT: "Vælg",
|
|
574
|
-
SELECTED: "Valgt"
|
|
597
|
+
SELECTED: "Valgt",
|
|
598
|
+
BOOKED: "Booket",
|
|
599
|
+
SAVE: "Gem"
|
|
575
600
|
};
|
|
576
601
|
var SIDEBAR$e = {
|
|
577
602
|
OVERVIEW: "Oversigt",
|
|
@@ -629,6 +654,27 @@ var TRAVELERS_FORM$e = {
|
|
|
629
654
|
FIRST_NAME: "Fornavn",
|
|
630
655
|
LAST_NAME: "Efternavn",
|
|
631
656
|
BIRTHDATE: "Fødselsdato",
|
|
657
|
+
NICKNAME: "Nickname",
|
|
658
|
+
BIRTHPLACE: "Birthplace",
|
|
659
|
+
NATIONALITY: "Nationality",
|
|
660
|
+
PREFERRED_LANGUAGE: "Preferred language",
|
|
661
|
+
TRAVELER_EMAIL: "Email",
|
|
662
|
+
TRAVELER_PHONE: "Phone",
|
|
663
|
+
TRAVELER_MOBILE: "Mobile phone",
|
|
664
|
+
PASSPORT_NUMBER: "Passport number",
|
|
665
|
+
PASSPORT_ISSUE_DATE: "Passport issue date",
|
|
666
|
+
PASSPORT_ISSUED_LOCATION: "Passport issued location",
|
|
667
|
+
PASSPORT_EXPIRY_DATE: "Passport expiry date",
|
|
668
|
+
EMERGENCY_NUMBER: "Emergency number",
|
|
669
|
+
HAS_INSURANCE: "Has insurance",
|
|
670
|
+
INSURANCE_NAME: "Insurance name",
|
|
671
|
+
INFO: "Additional information",
|
|
672
|
+
WEIGHT: "Vægt",
|
|
673
|
+
WEIGHT_UNIT_KG: "kg",
|
|
674
|
+
WEIGHT_UNIT_LB: "lb",
|
|
675
|
+
HEIGHT: "Højde",
|
|
676
|
+
HEIGHT_UNIT_CM: "cm",
|
|
677
|
+
HEIGHT_UNIT_FT_IN: "ft/in",
|
|
632
678
|
STREET: "Gade",
|
|
633
679
|
STREET_PLACEHOLDER: "Din gade",
|
|
634
680
|
HOUSE_NUMBER: "Husnummer",
|
|
@@ -1018,7 +1064,9 @@ var SHARED$d = {
|
|
|
1018
1064
|
ADULTS: "Erwachsene",
|
|
1019
1065
|
CHILDREN: "Kinder",
|
|
1020
1066
|
SELECT: "Auswählen",
|
|
1021
|
-
SELECTED: "Ausgewählt"
|
|
1067
|
+
SELECTED: "Ausgewählt",
|
|
1068
|
+
BOOKED: "Gebucht",
|
|
1069
|
+
SAVE: "Speichern"
|
|
1022
1070
|
};
|
|
1023
1071
|
var SIDEBAR$d = {
|
|
1024
1072
|
OVERVIEW: "Übersicht",
|
|
@@ -1076,6 +1124,27 @@ var TRAVELERS_FORM$d = {
|
|
|
1076
1124
|
FIRST_NAME: "Vorname",
|
|
1077
1125
|
LAST_NAME: "Nachname",
|
|
1078
1126
|
BIRTHDATE: "Geburtsdatum",
|
|
1127
|
+
NICKNAME: "Nickname",
|
|
1128
|
+
BIRTHPLACE: "Birthplace",
|
|
1129
|
+
NATIONALITY: "Nationality",
|
|
1130
|
+
PREFERRED_LANGUAGE: "Preferred language",
|
|
1131
|
+
TRAVELER_EMAIL: "Email",
|
|
1132
|
+
TRAVELER_PHONE: "Phone",
|
|
1133
|
+
TRAVELER_MOBILE: "Mobile phone",
|
|
1134
|
+
PASSPORT_NUMBER: "Passport number",
|
|
1135
|
+
PASSPORT_ISSUE_DATE: "Passport issue date",
|
|
1136
|
+
PASSPORT_ISSUED_LOCATION: "Passport issued location",
|
|
1137
|
+
PASSPORT_EXPIRY_DATE: "Passport expiry date",
|
|
1138
|
+
EMERGENCY_NUMBER: "Emergency number",
|
|
1139
|
+
HAS_INSURANCE: "Has insurance",
|
|
1140
|
+
INSURANCE_NAME: "Insurance name",
|
|
1141
|
+
INFO: "Additional information",
|
|
1142
|
+
WEIGHT: "Gewicht",
|
|
1143
|
+
WEIGHT_UNIT_KG: "kg",
|
|
1144
|
+
WEIGHT_UNIT_LB: "lb",
|
|
1145
|
+
HEIGHT: "Größe",
|
|
1146
|
+
HEIGHT_UNIT_CM: "cm",
|
|
1147
|
+
HEIGHT_UNIT_FT_IN: "ft/in",
|
|
1079
1148
|
STREET: "Straße",
|
|
1080
1149
|
STREET_PLACEHOLDER: "Ihre Straße",
|
|
1081
1150
|
HOUSE_NUMBER: "Hausnummer",
|
|
@@ -1465,7 +1534,9 @@ var SHARED$c = {
|
|
|
1465
1534
|
ADULTS: "Adults",
|
|
1466
1535
|
CHILDREN: "Children",
|
|
1467
1536
|
SELECT: "Select",
|
|
1468
|
-
SELECTED: "Selected"
|
|
1537
|
+
SELECTED: "Selected",
|
|
1538
|
+
BOOKED: "Booked",
|
|
1539
|
+
SAVE: "Save"
|
|
1469
1540
|
};
|
|
1470
1541
|
var SIDEBAR$c = {
|
|
1471
1542
|
OVERVIEW: "Summary",
|
|
@@ -1524,6 +1595,27 @@ var TRAVELERS_FORM$c = {
|
|
|
1524
1595
|
FIRST_NAME: "First name",
|
|
1525
1596
|
LAST_NAME: "Last name",
|
|
1526
1597
|
BIRTHDATE: "Date of birth",
|
|
1598
|
+
NICKNAME: "Nickname",
|
|
1599
|
+
BIRTHPLACE: "Birthplace",
|
|
1600
|
+
NATIONALITY: "Nationality",
|
|
1601
|
+
PREFERRED_LANGUAGE: "Preferred language",
|
|
1602
|
+
TRAVELER_EMAIL: "Email",
|
|
1603
|
+
TRAVELER_PHONE: "Phone",
|
|
1604
|
+
TRAVELER_MOBILE: "Mobile phone",
|
|
1605
|
+
PASSPORT_NUMBER: "Passport number",
|
|
1606
|
+
PASSPORT_ISSUE_DATE: "Passport issue date",
|
|
1607
|
+
PASSPORT_ISSUED_LOCATION: "Passport issued location",
|
|
1608
|
+
PASSPORT_EXPIRY_DATE: "Passport expiry date",
|
|
1609
|
+
EMERGENCY_NUMBER: "Emergency number",
|
|
1610
|
+
HAS_INSURANCE: "Has insurance",
|
|
1611
|
+
INSURANCE_NAME: "Insurance name",
|
|
1612
|
+
INFO: "Additional information",
|
|
1613
|
+
WEIGHT: "Weight",
|
|
1614
|
+
WEIGHT_UNIT_KG: "kg",
|
|
1615
|
+
WEIGHT_UNIT_LB: "lb",
|
|
1616
|
+
HEIGHT: "Height",
|
|
1617
|
+
HEIGHT_UNIT_CM: "cm",
|
|
1618
|
+
HEIGHT_UNIT_FT_IN: "ft/in",
|
|
1527
1619
|
STREET: "Street",
|
|
1528
1620
|
STREET_PLACEHOLDER: "Your street",
|
|
1529
1621
|
HOUSE_NUMBER: "House number",
|
|
@@ -1916,7 +2008,9 @@ var SHARED$b = {
|
|
|
1916
2008
|
ADULTS: "Adultos",
|
|
1917
2009
|
CHILDREN: "Niños",
|
|
1918
2010
|
SELECT: "Seleccionar",
|
|
1919
|
-
SELECTED: "Seleccionado"
|
|
2011
|
+
SELECTED: "Seleccionado",
|
|
2012
|
+
BOOKED: "Reservado",
|
|
2013
|
+
SAVE: "Guardar"
|
|
1920
2014
|
};
|
|
1921
2015
|
var SIDEBAR$b = {
|
|
1922
2016
|
OVERVIEW: "Resumen",
|
|
@@ -1974,6 +2068,27 @@ var TRAVELERS_FORM$b = {
|
|
|
1974
2068
|
FIRST_NAME: "Nombre",
|
|
1975
2069
|
LAST_NAME: "Apellido",
|
|
1976
2070
|
BIRTHDATE: "Fecha de nacimiento",
|
|
2071
|
+
NICKNAME: "Nickname",
|
|
2072
|
+
BIRTHPLACE: "Birthplace",
|
|
2073
|
+
NATIONALITY: "Nationality",
|
|
2074
|
+
PREFERRED_LANGUAGE: "Preferred language",
|
|
2075
|
+
TRAVELER_EMAIL: "Email",
|
|
2076
|
+
TRAVELER_PHONE: "Phone",
|
|
2077
|
+
TRAVELER_MOBILE: "Mobile phone",
|
|
2078
|
+
PASSPORT_NUMBER: "Passport number",
|
|
2079
|
+
PASSPORT_ISSUE_DATE: "Passport issue date",
|
|
2080
|
+
PASSPORT_ISSUED_LOCATION: "Passport issued location",
|
|
2081
|
+
PASSPORT_EXPIRY_DATE: "Passport expiry date",
|
|
2082
|
+
EMERGENCY_NUMBER: "Emergency number",
|
|
2083
|
+
HAS_INSURANCE: "Has insurance",
|
|
2084
|
+
INSURANCE_NAME: "Insurance name",
|
|
2085
|
+
INFO: "Additional information",
|
|
2086
|
+
WEIGHT: "Peso",
|
|
2087
|
+
WEIGHT_UNIT_KG: "kg",
|
|
2088
|
+
WEIGHT_UNIT_LB: "lb",
|
|
2089
|
+
HEIGHT: "Estatura",
|
|
2090
|
+
HEIGHT_UNIT_CM: "cm",
|
|
2091
|
+
HEIGHT_UNIT_FT_IN: "ft/in",
|
|
1977
2092
|
STREET: "Calle",
|
|
1978
2093
|
STREET_PLACEHOLDER: "Su calle",
|
|
1979
2094
|
HOUSE_NUMBER: "Número",
|
|
@@ -2363,7 +2478,9 @@ var SHARED$a = {
|
|
|
2363
2478
|
ADULTS: "Adultes",
|
|
2364
2479
|
CHILDREN: "Enfants",
|
|
2365
2480
|
SELECT: "Sélectionner",
|
|
2366
|
-
SELECTED: "Sélectionné"
|
|
2481
|
+
SELECTED: "Sélectionné",
|
|
2482
|
+
BOOKED: "Réservé",
|
|
2483
|
+
SAVE: "Enregistrer"
|
|
2367
2484
|
};
|
|
2368
2485
|
var SIDEBAR$a = {
|
|
2369
2486
|
OVERVIEW: "Aperçu",
|
|
@@ -2422,6 +2539,27 @@ var TRAVELERS_FORM$a = {
|
|
|
2422
2539
|
FIRST_NAME: "Prénom",
|
|
2423
2540
|
LAST_NAME: "Nom",
|
|
2424
2541
|
BIRTHDATE: "Date de naissance",
|
|
2542
|
+
NICKNAME: "Nickname",
|
|
2543
|
+
BIRTHPLACE: "Birthplace",
|
|
2544
|
+
NATIONALITY: "Nationality",
|
|
2545
|
+
PREFERRED_LANGUAGE: "Preferred language",
|
|
2546
|
+
TRAVELER_EMAIL: "Email",
|
|
2547
|
+
TRAVELER_PHONE: "Phone",
|
|
2548
|
+
TRAVELER_MOBILE: "Mobile phone",
|
|
2549
|
+
PASSPORT_NUMBER: "Passport number",
|
|
2550
|
+
PASSPORT_ISSUE_DATE: "Passport issue date",
|
|
2551
|
+
PASSPORT_ISSUED_LOCATION: "Passport issued location",
|
|
2552
|
+
PASSPORT_EXPIRY_DATE: "Passport expiry date",
|
|
2553
|
+
EMERGENCY_NUMBER: "Emergency number",
|
|
2554
|
+
HAS_INSURANCE: "Has insurance",
|
|
2555
|
+
INSURANCE_NAME: "Insurance name",
|
|
2556
|
+
INFO: "Additional information",
|
|
2557
|
+
WEIGHT: "Poids",
|
|
2558
|
+
WEIGHT_UNIT_KG: "kg",
|
|
2559
|
+
WEIGHT_UNIT_LB: "lb",
|
|
2560
|
+
HEIGHT: "Taille",
|
|
2561
|
+
HEIGHT_UNIT_CM: "cm",
|
|
2562
|
+
HEIGHT_UNIT_FT_IN: "ft/in",
|
|
2425
2563
|
STREET: "Rue",
|
|
2426
2564
|
STREET_PLACEHOLDER: "Votre rue",
|
|
2427
2565
|
HOUSE_NUMBER: "N°",
|
|
@@ -2814,7 +2952,9 @@ var SHARED$9 = {
|
|
|
2814
2952
|
ADULTS: "Adultes",
|
|
2815
2953
|
CHILDREN: "Enfants",
|
|
2816
2954
|
SELECT: "Sélectionner",
|
|
2817
|
-
SELECTED: "Sélectionné"
|
|
2955
|
+
SELECTED: "Sélectionné",
|
|
2956
|
+
BOOKED: "Réservé",
|
|
2957
|
+
SAVE: "Enregistrer"
|
|
2818
2958
|
};
|
|
2819
2959
|
var SIDEBAR$9 = {
|
|
2820
2960
|
OVERVIEW: "Résumé",
|
|
@@ -2872,6 +3012,27 @@ var TRAVELERS_FORM$9 = {
|
|
|
2872
3012
|
FIRST_NAME: "Prénom",
|
|
2873
3013
|
LAST_NAME: "Nom",
|
|
2874
3014
|
BIRTHDATE: "Date de naissance",
|
|
3015
|
+
NICKNAME: "Nickname",
|
|
3016
|
+
BIRTHPLACE: "Birthplace",
|
|
3017
|
+
NATIONALITY: "Nationality",
|
|
3018
|
+
PREFERRED_LANGUAGE: "Preferred language",
|
|
3019
|
+
TRAVELER_EMAIL: "Email",
|
|
3020
|
+
TRAVELER_PHONE: "Phone",
|
|
3021
|
+
TRAVELER_MOBILE: "Mobile phone",
|
|
3022
|
+
PASSPORT_NUMBER: "Passport number",
|
|
3023
|
+
PASSPORT_ISSUE_DATE: "Passport issue date",
|
|
3024
|
+
PASSPORT_ISSUED_LOCATION: "Passport issued location",
|
|
3025
|
+
PASSPORT_EXPIRY_DATE: "Passport expiry date",
|
|
3026
|
+
EMERGENCY_NUMBER: "Emergency number",
|
|
3027
|
+
HAS_INSURANCE: "Has insurance",
|
|
3028
|
+
INSURANCE_NAME: "Insurance name",
|
|
3029
|
+
INFO: "Additional information",
|
|
3030
|
+
WEIGHT: "Poids",
|
|
3031
|
+
WEIGHT_UNIT_KG: "kg",
|
|
3032
|
+
WEIGHT_UNIT_LB: "lb",
|
|
3033
|
+
HEIGHT: "Taille",
|
|
3034
|
+
HEIGHT_UNIT_CM: "cm",
|
|
3035
|
+
HEIGHT_UNIT_FT_IN: "ft/in",
|
|
2875
3036
|
STREET: "Rue",
|
|
2876
3037
|
STREET_PLACEHOLDER: "Votre rue",
|
|
2877
3038
|
HOUSE_NUMBER: "Numéro",
|
|
@@ -3261,7 +3422,9 @@ var SHARED$8 = {
|
|
|
3261
3422
|
ADULTS: "Fullorðnir",
|
|
3262
3423
|
CHILDREN: "Börn",
|
|
3263
3424
|
SELECT: "Velja",
|
|
3264
|
-
SELECTED: "Valið"
|
|
3425
|
+
SELECTED: "Valið",
|
|
3426
|
+
BOOKED: "Bókað",
|
|
3427
|
+
SAVE: "Vista"
|
|
3265
3428
|
};
|
|
3266
3429
|
var SIDEBAR$8 = {
|
|
3267
3430
|
OVERVIEW: "Yfirlit",
|
|
@@ -3319,6 +3482,27 @@ var TRAVELERS_FORM$8 = {
|
|
|
3319
3482
|
FIRST_NAME: "Fornafn",
|
|
3320
3483
|
LAST_NAME: "Eftirnafn",
|
|
3321
3484
|
BIRTHDATE: "Fæðingardagur",
|
|
3485
|
+
NICKNAME: "Nickname",
|
|
3486
|
+
BIRTHPLACE: "Birthplace",
|
|
3487
|
+
NATIONALITY: "Nationality",
|
|
3488
|
+
PREFERRED_LANGUAGE: "Preferred language",
|
|
3489
|
+
TRAVELER_EMAIL: "Email",
|
|
3490
|
+
TRAVELER_PHONE: "Phone",
|
|
3491
|
+
TRAVELER_MOBILE: "Mobile phone",
|
|
3492
|
+
PASSPORT_NUMBER: "Passport number",
|
|
3493
|
+
PASSPORT_ISSUE_DATE: "Passport issue date",
|
|
3494
|
+
PASSPORT_ISSUED_LOCATION: "Passport issued location",
|
|
3495
|
+
PASSPORT_EXPIRY_DATE: "Passport expiry date",
|
|
3496
|
+
EMERGENCY_NUMBER: "Emergency number",
|
|
3497
|
+
HAS_INSURANCE: "Has insurance",
|
|
3498
|
+
INSURANCE_NAME: "Insurance name",
|
|
3499
|
+
INFO: "Additional information",
|
|
3500
|
+
WEIGHT: "Þyngd",
|
|
3501
|
+
WEIGHT_UNIT_KG: "kg",
|
|
3502
|
+
WEIGHT_UNIT_LB: "lb",
|
|
3503
|
+
HEIGHT: "Hæð",
|
|
3504
|
+
HEIGHT_UNIT_CM: "cm",
|
|
3505
|
+
HEIGHT_UNIT_FT_IN: "ft/in",
|
|
3322
3506
|
STREET: "Gata",
|
|
3323
3507
|
STREET_PLACEHOLDER: "Gatan þín",
|
|
3324
3508
|
HOUSE_NUMBER: "Húsnúmer",
|
|
@@ -3708,7 +3892,9 @@ var SHARED$7 = {
|
|
|
3708
3892
|
ADULTS: "Adulti",
|
|
3709
3893
|
CHILDREN: "Bambini",
|
|
3710
3894
|
SELECT: "Seleziona",
|
|
3711
|
-
SELECTED: "Selezionato"
|
|
3895
|
+
SELECTED: "Selezionato",
|
|
3896
|
+
BOOKED: "Prenotato",
|
|
3897
|
+
SAVE: "Salva"
|
|
3712
3898
|
};
|
|
3713
3899
|
var SIDEBAR$7 = {
|
|
3714
3900
|
OVERVIEW: "Riepilogo",
|
|
@@ -3766,6 +3952,27 @@ var TRAVELERS_FORM$7 = {
|
|
|
3766
3952
|
FIRST_NAME: "Nome",
|
|
3767
3953
|
LAST_NAME: "Cognome",
|
|
3768
3954
|
BIRTHDATE: "Data di nascita",
|
|
3955
|
+
NICKNAME: "Nickname",
|
|
3956
|
+
BIRTHPLACE: "Birthplace",
|
|
3957
|
+
NATIONALITY: "Nationality",
|
|
3958
|
+
PREFERRED_LANGUAGE: "Preferred language",
|
|
3959
|
+
TRAVELER_EMAIL: "Email",
|
|
3960
|
+
TRAVELER_PHONE: "Phone",
|
|
3961
|
+
TRAVELER_MOBILE: "Mobile phone",
|
|
3962
|
+
PASSPORT_NUMBER: "Passport number",
|
|
3963
|
+
PASSPORT_ISSUE_DATE: "Passport issue date",
|
|
3964
|
+
PASSPORT_ISSUED_LOCATION: "Passport issued location",
|
|
3965
|
+
PASSPORT_EXPIRY_DATE: "Passport expiry date",
|
|
3966
|
+
EMERGENCY_NUMBER: "Emergency number",
|
|
3967
|
+
HAS_INSURANCE: "Has insurance",
|
|
3968
|
+
INSURANCE_NAME: "Insurance name",
|
|
3969
|
+
INFO: "Additional information",
|
|
3970
|
+
WEIGHT: "Peso",
|
|
3971
|
+
WEIGHT_UNIT_KG: "kg",
|
|
3972
|
+
WEIGHT_UNIT_LB: "lb",
|
|
3973
|
+
HEIGHT: "Altezza",
|
|
3974
|
+
HEIGHT_UNIT_CM: "cm",
|
|
3975
|
+
HEIGHT_UNIT_FT_IN: "ft/in",
|
|
3769
3976
|
STREET: "Via",
|
|
3770
3977
|
STREET_PLACEHOLDER: "La tua via",
|
|
3771
3978
|
HOUSE_NUMBER: "Numero civico",
|
|
@@ -4155,7 +4362,9 @@ var SHARED$6 = {
|
|
|
4155
4362
|
ADULTS: "Volwassenen",
|
|
4156
4363
|
CHILDREN: "Kinderen",
|
|
4157
4364
|
SELECT: "Selecteer",
|
|
4158
|
-
SELECTED: "Geselecteerd"
|
|
4365
|
+
SELECTED: "Geselecteerd",
|
|
4366
|
+
BOOKED: "Geboekt",
|
|
4367
|
+
SAVE: "Opslaan"
|
|
4159
4368
|
};
|
|
4160
4369
|
var SIDEBAR$6 = {
|
|
4161
4370
|
OVERVIEW: "Overzicht",
|
|
@@ -4214,6 +4423,27 @@ var TRAVELERS_FORM$6 = {
|
|
|
4214
4423
|
FIRST_NAME: "Voornaam",
|
|
4215
4424
|
LAST_NAME: "Naam",
|
|
4216
4425
|
BIRTHDATE: "Geboortedatum",
|
|
4426
|
+
NICKNAME: "Nickname",
|
|
4427
|
+
BIRTHPLACE: "Birthplace",
|
|
4428
|
+
NATIONALITY: "Nationality",
|
|
4429
|
+
PREFERRED_LANGUAGE: "Preferred language",
|
|
4430
|
+
TRAVELER_EMAIL: "Email",
|
|
4431
|
+
TRAVELER_PHONE: "Phone",
|
|
4432
|
+
TRAVELER_MOBILE: "Mobile phone",
|
|
4433
|
+
PASSPORT_NUMBER: "Passport number",
|
|
4434
|
+
PASSPORT_ISSUE_DATE: "Passport issue date",
|
|
4435
|
+
PASSPORT_ISSUED_LOCATION: "Passport issued location",
|
|
4436
|
+
PASSPORT_EXPIRY_DATE: "Passport expiry date",
|
|
4437
|
+
EMERGENCY_NUMBER: "Emergency number",
|
|
4438
|
+
HAS_INSURANCE: "Has insurance",
|
|
4439
|
+
INSURANCE_NAME: "Insurance name",
|
|
4440
|
+
INFO: "Additional information",
|
|
4441
|
+
WEIGHT: "Gewicht",
|
|
4442
|
+
WEIGHT_UNIT_KG: "kg",
|
|
4443
|
+
WEIGHT_UNIT_LB: "lb",
|
|
4444
|
+
HEIGHT: "Lengte",
|
|
4445
|
+
HEIGHT_UNIT_CM: "cm",
|
|
4446
|
+
HEIGHT_UNIT_FT_IN: "ft/in",
|
|
4217
4447
|
STREET: "Straat",
|
|
4218
4448
|
STREET_PLACEHOLDER: "Jouw straat",
|
|
4219
4449
|
HOUSE_NUMBER: "Huisnr",
|
|
@@ -4606,7 +4836,9 @@ var SHARED$5 = {
|
|
|
4606
4836
|
ADULTS: "Volwassenen",
|
|
4607
4837
|
CHILDREN: "Kinderen",
|
|
4608
4838
|
SELECT: "Selecteren",
|
|
4609
|
-
SELECTED: "Geselecteerd"
|
|
4839
|
+
SELECTED: "Geselecteerd",
|
|
4840
|
+
BOOKED: "Geboekt",
|
|
4841
|
+
SAVE: "Opslaan"
|
|
4610
4842
|
};
|
|
4611
4843
|
var SIDEBAR$5 = {
|
|
4612
4844
|
OVERVIEW: "Overzicht",
|
|
@@ -4664,6 +4896,27 @@ var TRAVELERS_FORM$5 = {
|
|
|
4664
4896
|
FIRST_NAME: "Voornaam",
|
|
4665
4897
|
LAST_NAME: "Achternaam",
|
|
4666
4898
|
BIRTHDATE: "Geboortedatum",
|
|
4899
|
+
NICKNAME: "Nickname",
|
|
4900
|
+
BIRTHPLACE: "Birthplace",
|
|
4901
|
+
NATIONALITY: "Nationality",
|
|
4902
|
+
PREFERRED_LANGUAGE: "Preferred language",
|
|
4903
|
+
TRAVELER_EMAIL: "Email",
|
|
4904
|
+
TRAVELER_PHONE: "Phone",
|
|
4905
|
+
TRAVELER_MOBILE: "Mobile phone",
|
|
4906
|
+
PASSPORT_NUMBER: "Passport number",
|
|
4907
|
+
PASSPORT_ISSUE_DATE: "Passport issue date",
|
|
4908
|
+
PASSPORT_ISSUED_LOCATION: "Passport issued location",
|
|
4909
|
+
PASSPORT_EXPIRY_DATE: "Passport expiry date",
|
|
4910
|
+
EMERGENCY_NUMBER: "Emergency number",
|
|
4911
|
+
HAS_INSURANCE: "Has insurance",
|
|
4912
|
+
INSURANCE_NAME: "Insurance name",
|
|
4913
|
+
INFO: "Additional information",
|
|
4914
|
+
WEIGHT: "Gewicht",
|
|
4915
|
+
WEIGHT_UNIT_KG: "kg",
|
|
4916
|
+
WEIGHT_UNIT_LB: "lb",
|
|
4917
|
+
HEIGHT: "Lengte",
|
|
4918
|
+
HEIGHT_UNIT_CM: "cm",
|
|
4919
|
+
HEIGHT_UNIT_FT_IN: "ft/in",
|
|
4667
4920
|
STREET: "Straat",
|
|
4668
4921
|
STREET_PLACEHOLDER: "Uw straat",
|
|
4669
4922
|
HOUSE_NUMBER: "Huisnummer",
|
|
@@ -5053,7 +5306,9 @@ var SHARED$4 = {
|
|
|
5053
5306
|
ADULTS: "Voksne",
|
|
5054
5307
|
CHILDREN: "Barn",
|
|
5055
5308
|
SELECT: "Velg",
|
|
5056
|
-
SELECTED: "Valgt"
|
|
5309
|
+
SELECTED: "Valgt",
|
|
5310
|
+
BOOKED: "Booket",
|
|
5311
|
+
SAVE: "Lagre"
|
|
5057
5312
|
};
|
|
5058
5313
|
var SIDEBAR$4 = {
|
|
5059
5314
|
OVERVIEW: "Oversikt",
|
|
@@ -5111,6 +5366,27 @@ var TRAVELERS_FORM$4 = {
|
|
|
5111
5366
|
FIRST_NAME: "Fornavn",
|
|
5112
5367
|
LAST_NAME: "Etternavn",
|
|
5113
5368
|
BIRTHDATE: "Fødselsdato",
|
|
5369
|
+
NICKNAME: "Nickname",
|
|
5370
|
+
BIRTHPLACE: "Birthplace",
|
|
5371
|
+
NATIONALITY: "Nationality",
|
|
5372
|
+
PREFERRED_LANGUAGE: "Preferred language",
|
|
5373
|
+
TRAVELER_EMAIL: "Email",
|
|
5374
|
+
TRAVELER_PHONE: "Phone",
|
|
5375
|
+
TRAVELER_MOBILE: "Mobile phone",
|
|
5376
|
+
PASSPORT_NUMBER: "Passport number",
|
|
5377
|
+
PASSPORT_ISSUE_DATE: "Passport issue date",
|
|
5378
|
+
PASSPORT_ISSUED_LOCATION: "Passport issued location",
|
|
5379
|
+
PASSPORT_EXPIRY_DATE: "Passport expiry date",
|
|
5380
|
+
EMERGENCY_NUMBER: "Emergency number",
|
|
5381
|
+
HAS_INSURANCE: "Has insurance",
|
|
5382
|
+
INSURANCE_NAME: "Insurance name",
|
|
5383
|
+
INFO: "Additional information",
|
|
5384
|
+
WEIGHT: "Vekt",
|
|
5385
|
+
WEIGHT_UNIT_KG: "kg",
|
|
5386
|
+
WEIGHT_UNIT_LB: "lb",
|
|
5387
|
+
HEIGHT: "Høyde",
|
|
5388
|
+
HEIGHT_UNIT_CM: "cm",
|
|
5389
|
+
HEIGHT_UNIT_FT_IN: "ft/in",
|
|
5114
5390
|
STREET: "Gate",
|
|
5115
5391
|
STREET_PLACEHOLDER: "Din gate",
|
|
5116
5392
|
HOUSE_NUMBER: "Husnummer",
|
|
@@ -5500,7 +5776,9 @@ var SHARED$3 = {
|
|
|
5500
5776
|
ADULTS: "Dorośli",
|
|
5501
5777
|
CHILDREN: "Dzieci",
|
|
5502
5778
|
SELECT: "Wybierz",
|
|
5503
|
-
SELECTED: "Wybrane"
|
|
5779
|
+
SELECTED: "Wybrane",
|
|
5780
|
+
BOOKED: "Zarezerwowane",
|
|
5781
|
+
SAVE: "Zapisz"
|
|
5504
5782
|
};
|
|
5505
5783
|
var SIDEBAR$3 = {
|
|
5506
5784
|
OVERVIEW: "Podsumowanie",
|
|
@@ -5558,6 +5836,27 @@ var TRAVELERS_FORM$3 = {
|
|
|
5558
5836
|
FIRST_NAME: "Imię",
|
|
5559
5837
|
LAST_NAME: "Nazwisko",
|
|
5560
5838
|
BIRTHDATE: "Data urodzenia",
|
|
5839
|
+
NICKNAME: "Nickname",
|
|
5840
|
+
BIRTHPLACE: "Birthplace",
|
|
5841
|
+
NATIONALITY: "Nationality",
|
|
5842
|
+
PREFERRED_LANGUAGE: "Preferred language",
|
|
5843
|
+
TRAVELER_EMAIL: "Email",
|
|
5844
|
+
TRAVELER_PHONE: "Phone",
|
|
5845
|
+
TRAVELER_MOBILE: "Mobile phone",
|
|
5846
|
+
PASSPORT_NUMBER: "Passport number",
|
|
5847
|
+
PASSPORT_ISSUE_DATE: "Passport issue date",
|
|
5848
|
+
PASSPORT_ISSUED_LOCATION: "Passport issued location",
|
|
5849
|
+
PASSPORT_EXPIRY_DATE: "Passport expiry date",
|
|
5850
|
+
EMERGENCY_NUMBER: "Emergency number",
|
|
5851
|
+
HAS_INSURANCE: "Has insurance",
|
|
5852
|
+
INSURANCE_NAME: "Insurance name",
|
|
5853
|
+
INFO: "Additional information",
|
|
5854
|
+
WEIGHT: "Waga",
|
|
5855
|
+
WEIGHT_UNIT_KG: "kg",
|
|
5856
|
+
WEIGHT_UNIT_LB: "lb",
|
|
5857
|
+
HEIGHT: "Wzrost",
|
|
5858
|
+
HEIGHT_UNIT_CM: "cm",
|
|
5859
|
+
HEIGHT_UNIT_FT_IN: "ft/in",
|
|
5561
5860
|
STREET: "Ulica",
|
|
5562
5861
|
STREET_PLACEHOLDER: "Twoja ulica",
|
|
5563
5862
|
HOUSE_NUMBER: "Numer domu",
|
|
@@ -5947,7 +6246,9 @@ var SHARED$2 = {
|
|
|
5947
6246
|
ADULTS: "Adultos",
|
|
5948
6247
|
CHILDREN: "Crianças",
|
|
5949
6248
|
SELECT: "Selecionar",
|
|
5950
|
-
SELECTED: "Selecionado"
|
|
6249
|
+
SELECTED: "Selecionado",
|
|
6250
|
+
BOOKED: "Reservado",
|
|
6251
|
+
SAVE: "Guardar"
|
|
5951
6252
|
};
|
|
5952
6253
|
var SIDEBAR$2 = {
|
|
5953
6254
|
OVERVIEW: "Resumo",
|
|
@@ -6005,6 +6306,27 @@ var TRAVELERS_FORM$2 = {
|
|
|
6005
6306
|
FIRST_NAME: "Nome",
|
|
6006
6307
|
LAST_NAME: "Apelido",
|
|
6007
6308
|
BIRTHDATE: "Data de nascimento",
|
|
6309
|
+
NICKNAME: "Nickname",
|
|
6310
|
+
BIRTHPLACE: "Birthplace",
|
|
6311
|
+
NATIONALITY: "Nationality",
|
|
6312
|
+
PREFERRED_LANGUAGE: "Preferred language",
|
|
6313
|
+
TRAVELER_EMAIL: "Email",
|
|
6314
|
+
TRAVELER_PHONE: "Phone",
|
|
6315
|
+
TRAVELER_MOBILE: "Mobile phone",
|
|
6316
|
+
PASSPORT_NUMBER: "Passport number",
|
|
6317
|
+
PASSPORT_ISSUE_DATE: "Passport issue date",
|
|
6318
|
+
PASSPORT_ISSUED_LOCATION: "Passport issued location",
|
|
6319
|
+
PASSPORT_EXPIRY_DATE: "Passport expiry date",
|
|
6320
|
+
EMERGENCY_NUMBER: "Emergency number",
|
|
6321
|
+
HAS_INSURANCE: "Has insurance",
|
|
6322
|
+
INSURANCE_NAME: "Insurance name",
|
|
6323
|
+
INFO: "Additional information",
|
|
6324
|
+
WEIGHT: "Peso",
|
|
6325
|
+
WEIGHT_UNIT_KG: "kg",
|
|
6326
|
+
WEIGHT_UNIT_LB: "lb",
|
|
6327
|
+
HEIGHT: "Altura",
|
|
6328
|
+
HEIGHT_UNIT_CM: "cm",
|
|
6329
|
+
HEIGHT_UNIT_FT_IN: "ft/in",
|
|
6008
6330
|
STREET: "Rua",
|
|
6009
6331
|
STREET_PLACEHOLDER: "A sua rua",
|
|
6010
6332
|
HOUSE_NUMBER: "Número de porta",
|
|
@@ -6394,7 +6716,9 @@ var SHARED$1 = {
|
|
|
6394
6716
|
ADULTS: "Vuxna",
|
|
6395
6717
|
CHILDREN: "Barn",
|
|
6396
6718
|
SELECT: "Välj",
|
|
6397
|
-
SELECTED: "Vald"
|
|
6719
|
+
SELECTED: "Vald",
|
|
6720
|
+
BOOKED: "Bokad",
|
|
6721
|
+
SAVE: "Spara"
|
|
6398
6722
|
};
|
|
6399
6723
|
var SIDEBAR$1 = {
|
|
6400
6724
|
OVERVIEW: "Översikt",
|
|
@@ -6452,6 +6776,27 @@ var TRAVELERS_FORM$1 = {
|
|
|
6452
6776
|
FIRST_NAME: "Förnamn",
|
|
6453
6777
|
LAST_NAME: "Efternamn",
|
|
6454
6778
|
BIRTHDATE: "Födelsedatum",
|
|
6779
|
+
NICKNAME: "Nickname",
|
|
6780
|
+
BIRTHPLACE: "Birthplace",
|
|
6781
|
+
NATIONALITY: "Nationality",
|
|
6782
|
+
PREFERRED_LANGUAGE: "Preferred language",
|
|
6783
|
+
TRAVELER_EMAIL: "Email",
|
|
6784
|
+
TRAVELER_PHONE: "Phone",
|
|
6785
|
+
TRAVELER_MOBILE: "Mobile phone",
|
|
6786
|
+
PASSPORT_NUMBER: "Passport number",
|
|
6787
|
+
PASSPORT_ISSUE_DATE: "Passport issue date",
|
|
6788
|
+
PASSPORT_ISSUED_LOCATION: "Passport issued location",
|
|
6789
|
+
PASSPORT_EXPIRY_DATE: "Passport expiry date",
|
|
6790
|
+
EMERGENCY_NUMBER: "Emergency number",
|
|
6791
|
+
HAS_INSURANCE: "Has insurance",
|
|
6792
|
+
INSURANCE_NAME: "Insurance name",
|
|
6793
|
+
INFO: "Additional information",
|
|
6794
|
+
WEIGHT: "Vikt",
|
|
6795
|
+
WEIGHT_UNIT_KG: "kg",
|
|
6796
|
+
WEIGHT_UNIT_LB: "lb",
|
|
6797
|
+
HEIGHT: "Längd",
|
|
6798
|
+
HEIGHT_UNIT_CM: "cm",
|
|
6799
|
+
HEIGHT_UNIT_FT_IN: "ft/in",
|
|
6455
6800
|
STREET: "Gata",
|
|
6456
6801
|
STREET_PLACEHOLDER: "Din gata",
|
|
6457
6802
|
HOUSE_NUMBER: "Husnummer",
|
|
@@ -6841,7 +7186,9 @@ var SHARED = {
|
|
|
6841
7186
|
ADULTS: "大人",
|
|
6842
7187
|
CHILDREN: "子供",
|
|
6843
7188
|
SELECT: "選択",
|
|
6844
|
-
SELECTED: "選択済み"
|
|
7189
|
+
SELECTED: "選択済み",
|
|
7190
|
+
BOOKED: "予約済み",
|
|
7191
|
+
SAVE: "保存"
|
|
6845
7192
|
};
|
|
6846
7193
|
var SIDEBAR = {
|
|
6847
7194
|
OVERVIEW: "概要",
|
|
@@ -6899,6 +7246,27 @@ var TRAVELERS_FORM = {
|
|
|
6899
7246
|
FIRST_NAME: "名",
|
|
6900
7247
|
LAST_NAME: "姓",
|
|
6901
7248
|
BIRTHDATE: "生年月日",
|
|
7249
|
+
NICKNAME: "Nickname",
|
|
7250
|
+
BIRTHPLACE: "Birthplace",
|
|
7251
|
+
NATIONALITY: "Nationality",
|
|
7252
|
+
PREFERRED_LANGUAGE: "Preferred language",
|
|
7253
|
+
TRAVELER_EMAIL: "Email",
|
|
7254
|
+
TRAVELER_PHONE: "Phone",
|
|
7255
|
+
TRAVELER_MOBILE: "Mobile phone",
|
|
7256
|
+
PASSPORT_NUMBER: "Passport number",
|
|
7257
|
+
PASSPORT_ISSUE_DATE: "Passport issue date",
|
|
7258
|
+
PASSPORT_ISSUED_LOCATION: "Passport issued location",
|
|
7259
|
+
PASSPORT_EXPIRY_DATE: "Passport expiry date",
|
|
7260
|
+
EMERGENCY_NUMBER: "Emergency number",
|
|
7261
|
+
HAS_INSURANCE: "Has insurance",
|
|
7262
|
+
INSURANCE_NAME: "Insurance name",
|
|
7263
|
+
INFO: "Additional information",
|
|
7264
|
+
WEIGHT: "体重",
|
|
7265
|
+
WEIGHT_UNIT_KG: "kg",
|
|
7266
|
+
WEIGHT_UNIT_LB: "lb",
|
|
7267
|
+
HEIGHT: "身長",
|
|
7268
|
+
HEIGHT_UNIT_CM: "cm",
|
|
7269
|
+
HEIGHT_UNIT_FT_IN: "ft/in",
|
|
6902
7270
|
STREET: "住所",
|
|
6903
7271
|
STREET_PLACEHOLDER: "住所を入力",
|
|
6904
7272
|
HOUSE_NUMBER: "番地",
|
|
@@ -7552,6 +7920,14 @@ exports.AvailabilitySearchType = void 0;
|
|
|
7552
7920
|
AvailabilitySearchType[(AvailabilitySearchType["flight"] = 3)] = "flight";
|
|
7553
7921
|
})(exports.AvailabilitySearchType || (exports.AvailabilitySearchType = {}));
|
|
7554
7922
|
|
|
7923
|
+
var CustomFieldDataType = {
|
|
7924
|
+
bool: 0,
|
|
7925
|
+
date: 1,
|
|
7926
|
+
time: 2,
|
|
7927
|
+
string: 3,
|
|
7928
|
+
numeric: 4,
|
|
7929
|
+
};
|
|
7930
|
+
|
|
7555
7931
|
var DataType = {
|
|
7556
7932
|
text: "text",
|
|
7557
7933
|
numeric: "number",
|
|
@@ -7567,6 +7943,12 @@ var DataType = {
|
|
|
7567
7943
|
template: "template",
|
|
7568
7944
|
};
|
|
7569
7945
|
|
|
7946
|
+
exports.DateAmountType = void 0;
|
|
7947
|
+
(function (DateAmountType) {
|
|
7948
|
+
DateAmountType[(DateAmountType["days"] = 0)] = "days";
|
|
7949
|
+
DateAmountType[(DateAmountType["months"] = 1)] = "months";
|
|
7950
|
+
})(exports.DateAmountType || (exports.DateAmountType = {}));
|
|
7951
|
+
|
|
7570
7952
|
var DossierType = {
|
|
7571
7953
|
hotelOnly: 0,
|
|
7572
7954
|
transportOnly: 1,
|
|
@@ -7574,6 +7956,13 @@ var DossierType = {
|
|
|
7574
7956
|
package: 3,
|
|
7575
7957
|
};
|
|
7576
7958
|
|
|
7959
|
+
var DurationType = {
|
|
7960
|
+
hours: 0,
|
|
7961
|
+
days: 1,
|
|
7962
|
+
nights: 2,
|
|
7963
|
+
minutes: 3,
|
|
7964
|
+
};
|
|
7965
|
+
|
|
7577
7966
|
var EntryLineFlag = {
|
|
7578
7967
|
none: 0,
|
|
7579
7968
|
allotmentNoPlace: 1,
|
|
@@ -7651,6 +8040,11 @@ var Gender = {
|
|
|
7651
8040
|
other: 2,
|
|
7652
8041
|
};
|
|
7653
8042
|
|
|
8043
|
+
var HeightType = {
|
|
8044
|
+
centimeters: 0,
|
|
8045
|
+
feetInches: 1,
|
|
8046
|
+
};
|
|
8047
|
+
|
|
7654
8048
|
exports.MetaDataItemPropertyType = void 0;
|
|
7655
8049
|
(function (MetaDataItemPropertyType) {
|
|
7656
8050
|
MetaDataItemPropertyType[(MetaDataItemPropertyType["Height"] = 0)] = "Height";
|
|
@@ -7687,6 +8081,22 @@ var PaymentType = {
|
|
|
7687
8081
|
cash: 3,
|
|
7688
8082
|
};
|
|
7689
8083
|
|
|
8084
|
+
exports.PortalQsmType = void 0;
|
|
8085
|
+
(function (PortalQsmType) {
|
|
8086
|
+
PortalQsmType[(PortalQsmType["Multidestination"] = 0)] = "Multidestination";
|
|
8087
|
+
PortalQsmType[(PortalQsmType["Accommodation"] = 1)] = "Accommodation";
|
|
8088
|
+
PortalQsmType[(PortalQsmType["AccommodationAndFlight"] = 2)] =
|
|
8089
|
+
"AccommodationAndFlight";
|
|
8090
|
+
PortalQsmType[(PortalQsmType["Flight"] = 3)] = "Flight";
|
|
8091
|
+
PortalQsmType[(PortalQsmType["RoundTrip"] = 4)] = "RoundTrip";
|
|
8092
|
+
PortalQsmType[(PortalQsmType["GroupTour"] = 5)] = "GroupTour";
|
|
8093
|
+
PortalQsmType[(PortalQsmType["Package"] = 6)] = "Package";
|
|
8094
|
+
PortalQsmType[(PortalQsmType["Ticket"] = 7)] = "Ticket";
|
|
8095
|
+
PortalQsmType[(PortalQsmType["Car"] = 8)] = "Car";
|
|
8096
|
+
PortalQsmType[(PortalQsmType["Transfer"] = 9)] = "Transfer";
|
|
8097
|
+
PortalQsmType[(PortalQsmType["Cruise"] = 10)] = "Cruise";
|
|
8098
|
+
})(exports.PortalQsmType || (exports.PortalQsmType = {}));
|
|
8099
|
+
|
|
7690
8100
|
var PriceChartType = {
|
|
7691
8101
|
purchasePrices: 0,
|
|
7692
8102
|
sellingPrices: 1,
|
|
@@ -7720,6 +8130,26 @@ var RoomType = {
|
|
|
7720
8130
|
pk6: 5,
|
|
7721
8131
|
};
|
|
7722
8132
|
|
|
8133
|
+
var RouteTransportType = {
|
|
8134
|
+
none: 0,
|
|
8135
|
+
privateTransport: 1,
|
|
8136
|
+
publicTransport: 2,
|
|
8137
|
+
rentalCar: 3,
|
|
8138
|
+
bus: 4,
|
|
8139
|
+
camper: 5,
|
|
8140
|
+
car: 6,
|
|
8141
|
+
charter: 7,
|
|
8142
|
+
cruise: 8,
|
|
8143
|
+
ferry: 9,
|
|
8144
|
+
flight: 10,
|
|
8145
|
+
taxi: 11,
|
|
8146
|
+
train: 12,
|
|
8147
|
+
transfer: 13,
|
|
8148
|
+
rocket: 14,
|
|
8149
|
+
walking: 15,
|
|
8150
|
+
cycling: 16,
|
|
8151
|
+
};
|
|
8152
|
+
|
|
7723
8153
|
var SaleType = {
|
|
7724
8154
|
ownOrganization: 0,
|
|
7725
8155
|
resale: 1,
|
|
@@ -7759,6 +8189,13 @@ var ServiceType = {
|
|
|
7759
8189
|
camp: 30,
|
|
7760
8190
|
};
|
|
7761
8191
|
|
|
8192
|
+
var TrajectNodeType = {
|
|
8193
|
+
regular: 0,
|
|
8194
|
+
connectingNode: 1,
|
|
8195
|
+
poi: 2,
|
|
8196
|
+
subroute: 3,
|
|
8197
|
+
};
|
|
8198
|
+
|
|
7762
8199
|
exports.UnitOfMeasure = void 0;
|
|
7763
8200
|
(function (UnitOfMeasure) {
|
|
7764
8201
|
UnitOfMeasure[(UnitOfMeasure["cm"] = 0)] = "cm";
|
|
@@ -7791,60 +8228,9 @@ var VendorType = {
|
|
|
7791
8228
|
sunnyCars: 20,
|
|
7792
8229
|
};
|
|
7793
8230
|
|
|
7794
|
-
|
|
7795
|
-
|
|
7796
|
-
|
|
7797
|
-
DateAmountType[(DateAmountType["months"] = 1)] = "months";
|
|
7798
|
-
})(exports.DateAmountType || (exports.DateAmountType = {}));
|
|
7799
|
-
|
|
7800
|
-
exports.PortalQsmType = void 0;
|
|
7801
|
-
(function (PortalQsmType) {
|
|
7802
|
-
PortalQsmType[(PortalQsmType["Multidestination"] = 0)] = "Multidestination";
|
|
7803
|
-
PortalQsmType[(PortalQsmType["Accommodation"] = 1)] = "Accommodation";
|
|
7804
|
-
PortalQsmType[(PortalQsmType["AccommodationAndFlight"] = 2)] =
|
|
7805
|
-
"AccommodationAndFlight";
|
|
7806
|
-
PortalQsmType[(PortalQsmType["Flight"] = 3)] = "Flight";
|
|
7807
|
-
PortalQsmType[(PortalQsmType["RoundTrip"] = 4)] = "RoundTrip";
|
|
7808
|
-
PortalQsmType[(PortalQsmType["GroupTour"] = 5)] = "GroupTour";
|
|
7809
|
-
PortalQsmType[(PortalQsmType["Package"] = 6)] = "Package";
|
|
7810
|
-
PortalQsmType[(PortalQsmType["Ticket"] = 7)] = "Ticket";
|
|
7811
|
-
PortalQsmType[(PortalQsmType["Car"] = 8)] = "Car";
|
|
7812
|
-
PortalQsmType[(PortalQsmType["Transfer"] = 9)] = "Transfer";
|
|
7813
|
-
PortalQsmType[(PortalQsmType["Cruise"] = 10)] = "Cruise";
|
|
7814
|
-
})(exports.PortalQsmType || (exports.PortalQsmType = {}));
|
|
7815
|
-
|
|
7816
|
-
var TrajectNodeType = {
|
|
7817
|
-
regular: 0,
|
|
7818
|
-
connectingNode: 1,
|
|
7819
|
-
poi: 2,
|
|
7820
|
-
subroute: 3,
|
|
7821
|
-
};
|
|
7822
|
-
|
|
7823
|
-
var DurationType = {
|
|
7824
|
-
hours: 0,
|
|
7825
|
-
days: 1,
|
|
7826
|
-
nights: 2,
|
|
7827
|
-
minutes: 3,
|
|
7828
|
-
};
|
|
7829
|
-
|
|
7830
|
-
var RouteTransportType = {
|
|
7831
|
-
none: 0,
|
|
7832
|
-
privateTransport: 1,
|
|
7833
|
-
publicTransport: 2,
|
|
7834
|
-
rentalCar: 3,
|
|
7835
|
-
bus: 4,
|
|
7836
|
-
camper: 5,
|
|
7837
|
-
car: 6,
|
|
7838
|
-
charter: 7,
|
|
7839
|
-
cruise: 8,
|
|
7840
|
-
ferry: 9,
|
|
7841
|
-
flight: 10,
|
|
7842
|
-
taxi: 11,
|
|
7843
|
-
train: 12,
|
|
7844
|
-
transfer: 13,
|
|
7845
|
-
rocket: 14,
|
|
7846
|
-
walking: 15,
|
|
7847
|
-
cycling: 16,
|
|
8231
|
+
var WeightType = {
|
|
8232
|
+
kilograms: 0,
|
|
8233
|
+
pounds: 1,
|
|
7848
8234
|
};
|
|
7849
8235
|
|
|
7850
8236
|
/******************************************************************************
|
|
@@ -14978,6 +15364,34 @@ var getContentDraft = function (config, signal) {
|
|
|
14978
15364
|
var apiKey = config.apiKey;
|
|
14979
15365
|
return get(url, apiKey, config.token, signal, true);
|
|
14980
15366
|
};
|
|
15367
|
+
/**
|
|
15368
|
+
* api/web/content/live/filtered
|
|
15369
|
+
* Gets filtered Live content
|
|
15370
|
+
* @param config
|
|
15371
|
+
* @param request
|
|
15372
|
+
* @param signal
|
|
15373
|
+
* @returns OK if succeeded.
|
|
15374
|
+
*/
|
|
15375
|
+
var getFilteredContentLive = function (config, request, signal) {
|
|
15376
|
+
var url = "" + config.host + ENDPOINT_LIVE + "/filtered";
|
|
15377
|
+
var apiKey = config.apiKey;
|
|
15378
|
+
var body = JSON.stringify(request);
|
|
15379
|
+
return post(url, apiKey, body, config.token, signal, true);
|
|
15380
|
+
};
|
|
15381
|
+
/**
|
|
15382
|
+
* api/web/content/draft/filtered
|
|
15383
|
+
* Gets filtered Draft (Internal) content
|
|
15384
|
+
* @param config
|
|
15385
|
+
* @param request
|
|
15386
|
+
* @param signal
|
|
15387
|
+
* @returns OK if succeeded.
|
|
15388
|
+
*/
|
|
15389
|
+
var getFilteredContentDraft = function (config, request, signal) {
|
|
15390
|
+
var url = "" + config.host + ENDPOINT_DRAFT + "/filtered";
|
|
15391
|
+
var apiKey = config.apiKey;
|
|
15392
|
+
var body = JSON.stringify(request);
|
|
15393
|
+
return post(url, apiKey, body, config.token, signal, true);
|
|
15394
|
+
};
|
|
14981
15395
|
/**
|
|
14982
15396
|
* api/web/content/live/by-property
|
|
14983
15397
|
* Gets Live content matching a property value (or values).
|
|
@@ -15105,6 +15519,7 @@ var ENDPOINT_TRAJECT = ENDPOINT$1 + "/traject";
|
|
|
15105
15519
|
var ENDPOINT_ENTRY = function (magicLinkCode) {
|
|
15106
15520
|
return ENDPOINT$1 + "/entry/" + magicLinkCode;
|
|
15107
15521
|
};
|
|
15522
|
+
var ENDPOINT_SAVE_ENTRY = ENDPOINT$1 + "/entry/save";
|
|
15108
15523
|
var startTransaction = function (config, signal) {
|
|
15109
15524
|
var apiKey = config.apiKey;
|
|
15110
15525
|
var url = "" + config.host + ENDPOINT_START_TRANSACTION;
|
|
@@ -15133,6 +15548,12 @@ var getEntry = function (config, magicLinkCode, signal) {
|
|
|
15133
15548
|
var url = "" + config.host + ENDPOINT_ENTRY(magicLinkCode);
|
|
15134
15549
|
return get(url, apiKey, config.token, signal, true);
|
|
15135
15550
|
};
|
|
15551
|
+
var savePackagingEntry = function (config, request, signal) {
|
|
15552
|
+
var url = "" + config.host + ENDPOINT_SAVE_ENTRY;
|
|
15553
|
+
var apiKey = config.apiKey;
|
|
15554
|
+
var body = JSON.stringify(request);
|
|
15555
|
+
return post(url, apiKey, body, config.token, signal, true);
|
|
15556
|
+
};
|
|
15136
15557
|
var getTrajectEntry = function (config, request, signal) {
|
|
15137
15558
|
var url = "" + config.host + ENDPOINT_TRAJECT;
|
|
15138
15559
|
var apiKey = config.apiKey;
|
|
@@ -15196,6 +15617,7 @@ var submitForm = function (config, id, request, signal) {
|
|
|
15196
15617
|
};
|
|
15197
15618
|
|
|
15198
15619
|
exports.AllotmentType = AllotmentType;
|
|
15620
|
+
exports.CustomFieldDataType = CustomFieldDataType;
|
|
15199
15621
|
exports.DataType = DataType;
|
|
15200
15622
|
exports.DossierType = DossierType;
|
|
15201
15623
|
exports.DurationType = DurationType;
|
|
@@ -15206,6 +15628,7 @@ exports.FormFieldType = FormFieldType;
|
|
|
15206
15628
|
exports.FormFieldWidth = FormFieldWidth;
|
|
15207
15629
|
exports.FormType = FormType;
|
|
15208
15630
|
exports.Gender = Gender;
|
|
15631
|
+
exports.HeightType = HeightType;
|
|
15209
15632
|
exports.MetaDataType = MetaDataType;
|
|
15210
15633
|
exports.PaymentType = PaymentType;
|
|
15211
15634
|
exports.PriceChartType = PriceChartType;
|
|
@@ -15217,6 +15640,7 @@ exports.ServiceTokenError = ServiceTokenError;
|
|
|
15217
15640
|
exports.ServiceType = ServiceType;
|
|
15218
15641
|
exports.TrajectNodeType = TrajectNodeType;
|
|
15219
15642
|
exports.VendorType = VendorType;
|
|
15643
|
+
exports.WeightType = WeightType;
|
|
15220
15644
|
exports.agents = agents;
|
|
15221
15645
|
exports.alternateFlights = alternateFlights;
|
|
15222
15646
|
exports.alternateHotels = alternateHotels;
|
|
@@ -15264,6 +15688,8 @@ exports.getEntryList = getEntryList;
|
|
|
15264
15688
|
exports.getEntryListv2 = getEntryListv2;
|
|
15265
15689
|
exports.getEntryStatus = getEntryStatus;
|
|
15266
15690
|
exports.getEntryTotals = getEntryTotals;
|
|
15691
|
+
exports.getFilteredContentDraft = getFilteredContentDraft;
|
|
15692
|
+
exports.getFilteredContentLive = getFilteredContentLive;
|
|
15267
15693
|
exports.getInvoiceList = getInvoiceList;
|
|
15268
15694
|
exports.getItinerary = getItinerary;
|
|
15269
15695
|
exports.getLocations = getLocations;
|
|
@@ -15284,6 +15710,7 @@ exports.print = print;
|
|
|
15284
15710
|
exports.readForm = readForm;
|
|
15285
15711
|
exports.repriceAndIssueTicket = repriceAndIssueTicket;
|
|
15286
15712
|
exports.resetPassword = resetPassword;
|
|
15713
|
+
exports.savePackagingEntry = savePackagingEntry;
|
|
15287
15714
|
exports.search = search;
|
|
15288
15715
|
exports.searchCountries = searchCountries;
|
|
15289
15716
|
exports.searchElastic = searchElastic;
|
|
@@ -17475,7 +17902,7 @@ const Product = ({ productCode, productName, duration, rating }) => {
|
|
|
17475
17902
|
const [hasFlight, setHasFlight] = useState(false);
|
|
17476
17903
|
const [hasTransfer, setHasTransfer] = useState(false);
|
|
17477
17904
|
const [rooms, setRooms] = useState([{ adults: 2, children: 0, childAges: [] }]);
|
|
17478
|
-
const [searchResponse, setSearchResponse] = useState(
|
|
17905
|
+
const [searchResponse, setSearchResponse] = useState();
|
|
17479
17906
|
const [detailResponse, setDetailResponse] = useState();
|
|
17480
17907
|
const [dateRange, setDateRange] = useState();
|
|
17481
17908
|
const [urlDateRange, setUrlDateRange] = useState({ from: null, to: null });
|
|
@@ -17613,6 +18040,9 @@ const Product = ({ productCode, productName, duration, rating }) => {
|
|
|
17613
18040
|
setPackageProductName(selectedItem.name);
|
|
17614
18041
|
};
|
|
17615
18042
|
useEffect(() => {
|
|
18043
|
+
if (searchResponse === undefined) {
|
|
18044
|
+
return;
|
|
18045
|
+
}
|
|
17616
18046
|
if (searchResponse && !isEmpty$1(searchResponse)) {
|
|
17617
18047
|
setSearchResults(searchResponse);
|
|
17618
18048
|
const selectedItem = searchResponse.find((item) => new Date(item.fromDate).getTime() === dateRange?.fromDate?.getTime() && new Date(item.toDate).getTime() === dateRange?.toDate?.getTime());
|
|
@@ -18611,276 +19041,1102 @@ const SUMMARY_STEP = 5;
|
|
|
18611
19041
|
const CONFIRMATION_STEP = 6;
|
|
18612
19042
|
const ERROR_STEP = 7;
|
|
18613
19043
|
|
|
18614
|
-
const
|
|
18615
|
-
|
|
18616
|
-
|
|
18617
|
-
};
|
|
18618
|
-
const priceDetailsApi = {
|
|
18619
|
-
fetchPriceDetails: fetchPriceDetails$1
|
|
19044
|
+
const LabeledInput = ({ name, type, required, value, defaultValue, min, max, label, placeholder, extraClassName, hasError, onChange, onBlur }) => {
|
|
19045
|
+
return (React__default.createElement("label", { className: buildClassName(['form__group', extraClassName, hasError && 'form__group--error']) },
|
|
19046
|
+
React__default.createElement("span", { className: "form__label" }, compact([label, required && '*']).join(' ')),
|
|
19047
|
+
React__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 })));
|
|
18620
19048
|
};
|
|
18621
19049
|
|
|
18622
|
-
const
|
|
18623
|
-
return {
|
|
18624
|
-
|
|
18625
|
-
|
|
18626
|
-
|
|
18627
|
-
...opt,
|
|
18628
|
-
rooms: opt.isSelected ? updateRooms(opt.rooms, changedLines) : opt.rooms,
|
|
18629
|
-
groups: opt.isSelected ? updateBookingGroups(opt.groups, changedLines) : opt.groups,
|
|
18630
|
-
optionUnits: opt.isSelected ? updateOptionUnits(opt.optionUnits, changedLines) : opt.optionUnits,
|
|
18631
|
-
optionPax: opt.isSelected ? updateOptionPax(opt.optionPax, changedLines) : opt.optionPax
|
|
18632
|
-
};
|
|
18633
|
-
})
|
|
18634
|
-
};
|
|
18635
|
-
};
|
|
18636
|
-
// ROOMS
|
|
18637
|
-
const updateRooms = (rooms, changedLines) => {
|
|
18638
|
-
return rooms.map((room) => ({
|
|
18639
|
-
...room,
|
|
18640
|
-
options: room.options.map((option) => ({
|
|
18641
|
-
...option,
|
|
18642
|
-
price: changedLines.find((x) => x.entryLineGuid == option.entryLineGuid)?.price ?? option.price
|
|
18643
|
-
}))
|
|
18644
|
-
}));
|
|
18645
|
-
};
|
|
18646
|
-
// PAX
|
|
18647
|
-
const updateOptionPax = (pax, changedLines) => {
|
|
18648
|
-
return pax.map((p) => ({
|
|
18649
|
-
...p,
|
|
18650
|
-
groups: updatePaxGroups(p.groups, changedLines)
|
|
18651
|
-
}));
|
|
18652
|
-
};
|
|
18653
|
-
const updatePaxGroups = (groups, changedLines) => {
|
|
18654
|
-
return groups.map((g) => ({
|
|
18655
|
-
...g,
|
|
18656
|
-
options: g.options.map((o) => ({
|
|
18657
|
-
...o,
|
|
18658
|
-
groups: updatePaxGroups(o.groups, changedLines),
|
|
18659
|
-
line: {
|
|
18660
|
-
...o.line,
|
|
18661
|
-
price: changedLines.find((cl) => cl.entryLineGuid === o.line.entryLineGuid)?.price ?? o.line.price
|
|
18662
|
-
},
|
|
18663
|
-
alternatives: o.alternatives.map((a) => ({
|
|
18664
|
-
...a,
|
|
18665
|
-
price: changedLines.find((cl) => cl.entryLineGuid === a.entryLineGuid)?.price ?? a.price
|
|
18666
|
-
}))
|
|
18667
|
-
}))
|
|
18668
|
-
}));
|
|
18669
|
-
};
|
|
18670
|
-
// UNITS
|
|
18671
|
-
const updateOptionUnits = (units, changedLines) => {
|
|
18672
|
-
return units.map((u) => ({
|
|
18673
|
-
...u,
|
|
18674
|
-
groups: updateUnitGroups(u.groups, changedLines)
|
|
18675
|
-
}));
|
|
18676
|
-
};
|
|
18677
|
-
const updateUnitGroups = (groups, changedLines) => {
|
|
18678
|
-
return groups.map((g) => ({
|
|
18679
|
-
...g,
|
|
18680
|
-
options: g.options.map((o) => ({
|
|
18681
|
-
...o,
|
|
18682
|
-
groups: updateUnitGroups(o.groups, changedLines),
|
|
18683
|
-
pax: updateOptionPax(o.pax, changedLines),
|
|
18684
|
-
line: {
|
|
18685
|
-
...o.line,
|
|
18686
|
-
price: changedLines.find((cl) => cl.entryLineGuid === o.line.entryLineGuid)?.price ?? o.line.price
|
|
18687
|
-
},
|
|
18688
|
-
alternatives: o.alternatives.map((a) => ({
|
|
18689
|
-
...a,
|
|
18690
|
-
price: changedLines.find((cl) => cl.entryLineGuid === a.entryLineGuid)?.price ?? a.price
|
|
18691
|
-
}))
|
|
18692
|
-
}))
|
|
18693
|
-
}));
|
|
18694
|
-
};
|
|
18695
|
-
// BOOKING
|
|
18696
|
-
const updateBookingGroups = (groups, changedLines) => {
|
|
18697
|
-
return groups.map((g) => ({
|
|
18698
|
-
...g,
|
|
18699
|
-
options: g.options.map((o) => ({
|
|
18700
|
-
...o,
|
|
18701
|
-
groups: updateBookingGroups(o.groups, changedLines),
|
|
18702
|
-
units: updateOptionUnits(o.units, changedLines),
|
|
18703
|
-
pax: updateOptionPax(o.pax, changedLines),
|
|
18704
|
-
line: {
|
|
18705
|
-
...o.line,
|
|
18706
|
-
price: changedLines.find((cl) => cl.entryLineGuid === o.line.entryLineGuid)?.price ?? o.line.price
|
|
18707
|
-
},
|
|
18708
|
-
alternatives: o.alternatives.map((a) => ({
|
|
18709
|
-
...a,
|
|
18710
|
-
price: changedLines.find((cl) => cl.entryLineGuid === a.entryLineGuid)?.price ?? a.price
|
|
18711
|
-
}))
|
|
18712
|
-
}))
|
|
18713
|
-
}));
|
|
19050
|
+
const LabeledSelect = ({ name, id, required, value, defaultValue, label, extraClassName, extraDropdownClassName, hasError, options, onChange, onBlur }) => {
|
|
19051
|
+
return (React__default.createElement("label", { className: buildClassName(['form__group', extraClassName, hasError && 'form__group--error']) },
|
|
19052
|
+
React__default.createElement("span", { className: "form__label" }, compact([label, required && '*']).join(' ')),
|
|
19053
|
+
React__default.createElement("div", { className: buildClassName(['dropdown', extraDropdownClassName]) },
|
|
19054
|
+
React__default.createElement("select", { name: name, id: id, defaultValue: defaultValue, value: value, onBlur: onBlur, onChange: onChange }, options?.map((option) => (React__default.createElement("option", { key: option.key, value: option.value }, option.label)))))));
|
|
18714
19055
|
};
|
|
18715
19056
|
|
|
18716
|
-
const
|
|
18717
|
-
|
|
18718
|
-
const
|
|
18719
|
-
|
|
18720
|
-
|
|
18721
|
-
|
|
18722
|
-
|
|
18723
|
-
|
|
18724
|
-
|
|
18725
|
-
|
|
18726
|
-
|
|
18727
|
-
|
|
18728
|
-
|
|
18729
|
-
|
|
18730
|
-
dispatch(setPackage(updatedPackageDetails));
|
|
19057
|
+
const normalize = (v) => (v == null ? '' : String(v));
|
|
19058
|
+
const parseCombined = (raw, countries) => {
|
|
19059
|
+
const value = normalize(raw).trim();
|
|
19060
|
+
if (!value)
|
|
19061
|
+
return { prefix: '', number: '' };
|
|
19062
|
+
// Try to match a known prefix at the start. Prefer longest match.
|
|
19063
|
+
const sorted = [...countries].sort((a, b) => b.phonePrefix.length - a.phonePrefix.length);
|
|
19064
|
+
const hit = sorted.find((c) => value.startsWith(c.phonePrefix));
|
|
19065
|
+
if (hit) {
|
|
19066
|
+
const rest = value
|
|
19067
|
+
.slice(hit.phonePrefix.length)
|
|
19068
|
+
.trim()
|
|
19069
|
+
.replace(/^[-\s]+/, '');
|
|
19070
|
+
return { prefix: hit.phonePrefix, number: rest };
|
|
18731
19071
|
}
|
|
18732
|
-
|
|
18733
|
-
});
|
|
18734
|
-
|
|
18735
|
-
|
|
18736
|
-
|
|
18737
|
-
|
|
18738
|
-
|
|
18739
|
-
|
|
18740
|
-
|
|
18741
|
-
|
|
18742
|
-
|
|
18743
|
-
|
|
18744
|
-
|
|
18745
|
-
|
|
18746
|
-
|
|
18747
|
-
|
|
18748
|
-
|
|
18749
|
-
|
|
18750
|
-
|
|
18751
|
-
},
|
|
18752
|
-
|
|
18753
|
-
|
|
18754
|
-
|
|
18755
|
-
|
|
18756
|
-
|
|
18757
|
-
|
|
18758
|
-
|
|
18759
|
-
}
|
|
18760
|
-
});
|
|
18761
|
-
builder.addCase(fetchPriceDetails.rejected, (state) => {
|
|
18762
|
-
state.isBusy = false;
|
|
19072
|
+
// Fallback: split on first space if it looks like a +prefix number
|
|
19073
|
+
const m = value.match(/^(\+\d{1,4})[\s-]*(.*)$/);
|
|
19074
|
+
if (m)
|
|
19075
|
+
return { prefix: m[1], number: m[2] };
|
|
19076
|
+
return { prefix: '', number: value };
|
|
19077
|
+
};
|
|
19078
|
+
const PhoneInput = ({ name, required, value, label, placeholder, extraClassName, hasError, countries, countryIso2, onChange, onBlur }) => {
|
|
19079
|
+
// Derive initial state from `value`
|
|
19080
|
+
const initialCountry = countries.find((c) => c.iso2 === countryIso2);
|
|
19081
|
+
const initial = useMemo(() => parseCombined(normalize(value), countries), [value, countries]);
|
|
19082
|
+
const [prefix, setPrefix] = useState(initial.prefix);
|
|
19083
|
+
const [number, setNumber] = useState(initial.number);
|
|
19084
|
+
// Keep state in sync if the parent changes `value`
|
|
19085
|
+
useEffect(() => {
|
|
19086
|
+
const parsed = !prefix && !number && initialCountry ? { prefix: initialCountry.phonePrefix, number: '' } : parseCombined(normalize(value), countries);
|
|
19087
|
+
if (parsed.prefix)
|
|
19088
|
+
setPrefix(parsed.prefix);
|
|
19089
|
+
if (parsed.number)
|
|
19090
|
+
setNumber(parsed.number);
|
|
19091
|
+
}, [value, countries, countryIso2]);
|
|
19092
|
+
const emitCombinedChange = (e, nextPrefix, nextNumber) => {
|
|
19093
|
+
// Combine with a space, unless the number already starts with a dash or space
|
|
19094
|
+
const combined = nextPrefix && nextNumber ? compact([nextPrefix, nextNumber]).join(' ') : null;
|
|
19095
|
+
onChange?.({
|
|
19096
|
+
...e,
|
|
19097
|
+
type: 'change',
|
|
19098
|
+
target: { name, value: combined },
|
|
19099
|
+
currentTarget: { name, value: combined }
|
|
18763
19100
|
});
|
|
18764
|
-
}
|
|
18765
|
-
|
|
18766
|
-
|
|
18767
|
-
|
|
18768
|
-
const
|
|
18769
|
-
|
|
19101
|
+
};
|
|
19102
|
+
const handleCountryBlur = (e) => {
|
|
19103
|
+
onBlur?.(e);
|
|
19104
|
+
};
|
|
19105
|
+
const onCountryChange = (e) => {
|
|
19106
|
+
const next = e.target.value;
|
|
19107
|
+
setPrefix(next);
|
|
19108
|
+
emitCombinedChange(e, next, number);
|
|
19109
|
+
};
|
|
19110
|
+
const onPhoneChange = (e) => {
|
|
19111
|
+
const cleaned = e.target.value.replace(/[^\d\s-]/g, '');
|
|
19112
|
+
setNumber(cleaned);
|
|
19113
|
+
emitCombinedChange(e, prefix, cleaned);
|
|
19114
|
+
};
|
|
19115
|
+
const handleNumberBlur = (e) => {
|
|
19116
|
+
onBlur?.(e);
|
|
19117
|
+
};
|
|
19118
|
+
// Ensure the select shows a reasonable default when prefix is empty
|
|
19119
|
+
const selectValue = prefix || '';
|
|
19120
|
+
return (React__default.createElement("label", { className: buildClassName(['form__group', extraClassName, hasError && 'form__group--error']) },
|
|
19121
|
+
label && React__default.createElement("span", { className: "form__label" }, compact([label, required && '*']).join(' ')),
|
|
19122
|
+
React__default.createElement("div", { className: "phone-input" },
|
|
19123
|
+
React__default.createElement("div", { className: "dropdown" },
|
|
19124
|
+
React__default.createElement("select", { "aria-label": label ? `${label} – country code` : 'Country calling code', name: `${name}__country`, value: selectValue, onBlur: handleCountryBlur, onChange: onCountryChange },
|
|
19125
|
+
React__default.createElement("option", { value: "", disabled: true }),
|
|
19126
|
+
countries?.map((option) => (React__default.createElement("option", { key: option.iso2, value: option.phonePrefix },
|
|
19127
|
+
option.name,
|
|
19128
|
+
" (",
|
|
19129
|
+
option.phonePrefix,
|
|
19130
|
+
")"))))),
|
|
19131
|
+
React__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 }),
|
|
19132
|
+
React__default.createElement("input", { type: "hidden", name: name, value: compact([prefix, number]).join(' ') }))));
|
|
19133
|
+
};
|
|
18770
19134
|
|
|
18771
|
-
const
|
|
18772
|
-
|
|
18773
|
-
const
|
|
18774
|
-
|
|
18775
|
-
|
|
18776
|
-
|
|
18777
|
-
|
|
18778
|
-
|
|
18779
|
-
|
|
18780
|
-
|
|
18781
|
-
|
|
18782
|
-
|
|
18783
|
-
|
|
18784
|
-
|
|
18785
|
-
|
|
18786
|
-
|
|
18787
|
-
outward: state.booking.package?.outwardFlights,
|
|
18788
|
-
return: state.booking.package?.returnFlights
|
|
18789
|
-
});
|
|
18790
|
-
const selectFlightMetaData = (state) => state.booking.package?.flightInfo.metaData;
|
|
18791
|
-
const selectActiveOption = (state) => state.booking.package?.options.find((x) => x.isSelected);
|
|
18792
|
-
const selectPackageAirlineGroups = createSelector(selectActiveOption, selectDepartureFlight, selectReturnFlight, (option, departureFlight, returnFlight) => option?.airlineGroups.filter((x) => x.flightIds.includes(departureFlight?.entryLineGuid ?? '') || x.flightIds.includes(returnFlight?.entryLineGuid ?? '')));
|
|
18793
|
-
const selectPackageAirportGroups = createSelector(selectActiveOption, selectDepartureFlight, selectReturnFlight, (option, departureFlight, returnFlight) => option?.airportGroups.filter((x) => x.flightIds.includes(departureFlight?.entryLineGuid ?? '') || x.flightIds.includes(returnFlight?.entryLineGuid ?? '')));
|
|
18794
|
-
const selectRoomOptionDepartureFlightsMetaData = createSelector(selectActiveOption, selectFlightMetaData, (ativeOption, flightMetaData) => {
|
|
18795
|
-
const roomOptions = ativeOption?.rooms.map((r) => r.options.filter((o) => o.isSelected).shift());
|
|
18796
|
-
return roomOptions?.map((ro) => flightMetaData?.find((f) => f.entryLineGuid === ro?.entryLineGuid)?.flightMetaData).filter((f) => f !== undefined) ?? [];
|
|
18797
|
-
});
|
|
18798
|
-
const selectRoomOptionReturnFlightsMetaData = createSelector(selectActiveOption, selectFlightMetaData, (ativeOption, flightMetaData) => {
|
|
18799
|
-
const selectedRoomOptions = ativeOption?.rooms.map((r) => r.options.filter((o) => o.isSelected));
|
|
18800
|
-
// check if there are return flights selected
|
|
18801
|
-
if (selectedRoomOptions?.some((ro) => ro.length > 1)) {
|
|
18802
|
-
const roomOptions = ativeOption?.rooms.map((r) => r.options
|
|
18803
|
-
.filter((o) => o.isSelected)
|
|
18804
|
-
.reverse()
|
|
18805
|
-
.shift());
|
|
18806
|
-
return roomOptions?.map((ro) => flightMetaData?.find((f) => f.entryLineGuid === ro?.entryLineGuid)?.flightMetaData).filter((f) => f !== undefined) ?? [];
|
|
18807
|
-
}
|
|
18808
|
-
return undefined;
|
|
18809
|
-
});
|
|
18810
|
-
const selectApiSettings = (state) => state.apiSettings;
|
|
18811
|
-
const selectIsUnavailable = (state) => state.booking.isUnavailable;
|
|
18812
|
-
const selectRequestRooms = (state) => state.booking.package?.options.find((x) => x.isSelected)?.requestRooms;
|
|
18813
|
-
const selectOfficeId = (state) => state.booking.officeId;
|
|
18814
|
-
const selectLanguageCode = (state) => state.booking.languageCode;
|
|
18815
|
-
const selectCurrencyCode = (state) => state.booking.package?.currencyCode ?? 'EUR';
|
|
18816
|
-
const selectLanguage = (state) => state.booking.languageCode;
|
|
18817
|
-
const selectAllDynamicTranslations = (state) => state.booking.translations;
|
|
18818
|
-
const selectDynamicTranslations = createSelector([selectAllDynamicTranslations, selectLanguage], (allDynamicTranslations, language) => {
|
|
18819
|
-
return allDynamicTranslations?.find((x) => x.language == language)?.value ?? {};
|
|
18820
|
-
});
|
|
18821
|
-
// Host override files are hand-maintained, and a null only ever blanks a key the component
|
|
18822
|
-
// then reads, so treat it like a missing key (lodash merge already skips undefined).
|
|
18823
|
-
const withoutNulls = (value) => isPlainObject$1(value)
|
|
18824
|
-
? mapValues(pickBy(value, (x) => x !== null), withoutNulls)
|
|
18825
|
-
: value;
|
|
18826
|
-
// getTranslations deep-merges the overrides over a clone of the built-in translations, so a
|
|
18827
|
-
// host override of a nested group (TRAVELERS_FORM.VALIDATION, TRAVELERS_FORM.COUNTRIES)
|
|
18828
|
-
// only replaces the keys it actually contains. Merging per section one level deep dropped
|
|
18829
|
-
// every other built-in key of such a group, which made those keys resolve to undefined and
|
|
18830
|
-
// crash format() in the consuming component.
|
|
18831
|
-
const selectTranslations = createSelector([selectLanguage, selectDynamicTranslations], (language, dynamicTranslations) => getTranslations(language, isEmpty$1(dynamicTranslations) ? undefined : withoutNulls(dynamicTranslations)));
|
|
18832
|
-
const selectBookingOptions = (state) => state.booking.bookingOptions;
|
|
18833
|
-
const selectBookingType = (state) => state.booking.bookingType;
|
|
18834
|
-
const selectTagIds = (state) => state.booking.tagIds;
|
|
18835
|
-
const selectAgentAdressId = (state) => state.booking.agentAdressId;
|
|
18836
|
-
const selectProductAttributes = (state) => state.booking.productAttributes;
|
|
18837
|
-
const selectBookingAttributes = (state) => state.booking.bookingAttributes;
|
|
18838
|
-
const selectBookingNumber = (state) => state.booking.bookingNumber;
|
|
18839
|
-
const selectIsOption = (state) => state.booking.isOption;
|
|
18840
|
-
const selectBookingRooms = (state) => state.booking.bookingAttributes?.rooms;
|
|
18841
|
-
const selectBookingRemarks = (state) => state.booking.remarks;
|
|
18842
|
-
const selectVoucherCodes = (state) => state.booking.voucherCodes;
|
|
18843
|
-
const selectCalculateDeposit = (state) => state.booking.calculateDeposit;
|
|
18844
|
-
const selectShowCommission = (state) => state.booking.showCommission;
|
|
18845
|
-
const selectIsRetry = (state) => state.booking.isRetry;
|
|
18846
|
-
const selectStartDate = (state) => state.booking.package?.options.find((x) => x.isSelected)?.fromDate;
|
|
18847
|
-
const selectEndDate = (state) => state.booking.package?.options.find((x) => x.isSelected)?.toDate;
|
|
18848
|
-
const selectAgents = (state) => state.booking.agents;
|
|
18849
|
-
const selectCountries = (state) => state.booking.countries;
|
|
18850
|
-
const selectProductCode = (state) => state.booking.productAttributes?.productCode;
|
|
18851
|
-
const selectAccommodationCodes = (state) => {
|
|
18852
|
-
const accommodationCodes = [];
|
|
18853
|
-
state.booking.package?.options.forEach((o) => {
|
|
18854
|
-
o.rooms.forEach((r) => r.options.forEach((ro) => {
|
|
18855
|
-
if (!accommodationCodes.some((y) => y === ro.accommodationCode)) {
|
|
18856
|
-
accommodationCodes.push(ro.accommodationCode);
|
|
18857
|
-
}
|
|
18858
|
-
}));
|
|
18859
|
-
});
|
|
18860
|
-
return accommodationCodes;
|
|
19135
|
+
const hasVisibleError = (formik, key) => get(formik.errors, key) && get(formik.touched, key);
|
|
19136
|
+
|
|
19137
|
+
const GenderControl = ({ translations, value, formik, name }) => {
|
|
19138
|
+
return (React__default.createElement("div", { className: buildClassName(['form__group', hasVisibleError(formik, name) && 'form__group--error']) },
|
|
19139
|
+
React__default.createElement("label", { className: "form__label" },
|
|
19140
|
+
translations.TRAVELERS_FORM.GENDER_ID,
|
|
19141
|
+
" *"),
|
|
19142
|
+
React__default.createElement("div", { className: "radiobutton-group" },
|
|
19143
|
+
React__default.createElement("div", { className: "radiobutton" },
|
|
19144
|
+
React__default.createElement("label", { className: "radiobutton__label" },
|
|
19145
|
+
React__default.createElement("input", { type: "radio", className: "radiobutton__input", name: name, onChange: formik.handleChange, onBlur: formik.handleBlur, value: "m", checked: value.gender === 'm' }),
|
|
19146
|
+
translations.TRAVELERS_FORM.MALE_GENDER)),
|
|
19147
|
+
React__default.createElement("div", { className: "radiobutton" },
|
|
19148
|
+
React__default.createElement("label", { className: "radiobutton__label" },
|
|
19149
|
+
React__default.createElement("input", { type: "radio", className: "radiobutton__input", name: name, onChange: formik.handleChange, onBlur: formik.handleBlur, value: "f", checked: value.gender === 'f' }),
|
|
19150
|
+
translations.TRAVELERS_FORM.FEMALE_GENDER)))));
|
|
18861
19151
|
};
|
|
18862
|
-
|
|
18863
|
-
const
|
|
18864
|
-
const
|
|
18865
|
-
const
|
|
18866
|
-
|
|
18867
|
-
|
|
18868
|
-
}
|
|
18869
|
-
const params = {};
|
|
18870
|
-
Object.entries(bookingAttributes).forEach(([key, value]) => {
|
|
18871
|
-
if (key === 'startDate' || key === 'endDate') {
|
|
18872
|
-
value = format$2(parseISO(value), 'yyyy-MM-dd');
|
|
18873
|
-
}
|
|
18874
|
-
if (key === 'rooms') {
|
|
18875
|
-
value = JsonURL.stringify(value.map((room) => omit(room, ['children'])), {
|
|
18876
|
-
AQF: true
|
|
18877
|
-
});
|
|
19152
|
+
|
|
19153
|
+
const TypeAheadInput = ({ name, value, placeholder, options, onChange, onSelect, onClear }) => {
|
|
19154
|
+
const [showItems, setShowItems] = useState(false);
|
|
19155
|
+
const handleChange = (e) => {
|
|
19156
|
+
if (onChange) {
|
|
19157
|
+
onChange(e.target.value);
|
|
18878
19158
|
}
|
|
18879
|
-
|
|
18880
|
-
|
|
19159
|
+
};
|
|
19160
|
+
const handleClearClick = (e) => {
|
|
19161
|
+
if (onClear) {
|
|
19162
|
+
onClear();
|
|
18881
19163
|
}
|
|
18882
|
-
|
|
18883
|
-
|
|
19164
|
+
setShowItems(false);
|
|
19165
|
+
};
|
|
19166
|
+
return (React__default.createElement("div", { className: "typeahead" },
|
|
19167
|
+
React__default.createElement("div", { className: "typeahead__trigger typeahead__trigger--icon" },
|
|
19168
|
+
React__default.createElement(Icon, { name: "ui-qsm-location" }),
|
|
19169
|
+
React__default.createElement("input", { type: "text", className: "form__input", name: name, value: value, onChange: handleChange, placeholder: placeholder, required: true, autoComplete: "off", onKeyDown: (e) => {
|
|
19170
|
+
if (e.key === 'Tab' && options && onSelect) {
|
|
19171
|
+
onSelect(options[0].key);
|
|
19172
|
+
setShowItems(false);
|
|
19173
|
+
}
|
|
19174
|
+
}, onFocus: () => setShowItems(true) }),
|
|
19175
|
+
React__default.createElement("button", { type: "button", className: "button button--clear", onClick: handleClearClick })),
|
|
19176
|
+
options && options.length > 0 && showItems && (React__default.createElement("div", { className: "typeahead__options" }, (options ?? []).map((option) => {
|
|
19177
|
+
const matchIndex = typeof value !== 'undefined' ? option.text.indexOf(value) : 0;
|
|
19178
|
+
const prefix = option.text.slice(0, matchIndex);
|
|
19179
|
+
const highlight = option.text.slice(matchIndex, value?.length ?? 0);
|
|
19180
|
+
const suffix = option.text.slice(matchIndex);
|
|
19181
|
+
return (React__default.createElement("div", { className: "typeahead__option", key: option.key },
|
|
19182
|
+
React__default.createElement("div", { className: "typeahead__option-header" },
|
|
19183
|
+
React__default.createElement("button", { onClick: () => {
|
|
19184
|
+
if (onSelect)
|
|
19185
|
+
onSelect(option.key);
|
|
19186
|
+
setShowItems(false);
|
|
19187
|
+
}, type: "button", className: "button button--plain" },
|
|
19188
|
+
prefix,
|
|
19189
|
+
React__default.createElement("span", { className: "typeahead__option-highlight" }, highlight),
|
|
19190
|
+
suffix))));
|
|
19191
|
+
})))));
|
|
19192
|
+
};
|
|
19193
|
+
|
|
19194
|
+
// A traveler is a baby when the age they were booked with is at or below the baby
|
|
19195
|
+
// boundary. Babies get their own label and birth date validation (TIDE-3269). Only
|
|
19196
|
+
// meaningful for the regular form: the compact form asks for an age instead of a birth
|
|
19197
|
+
// date, so it has nothing to validate and its own age input already states the age.
|
|
19198
|
+
function isBabyAge(age, maxBabyAge = BABY_MAX_AGE) {
|
|
19199
|
+
return typeof age === 'number' && age <= maxBabyAge;
|
|
19200
|
+
}
|
|
19201
|
+
// The native date input can hold values format() throws on (e.g. a 5+ digit year while typing), so never format an unvalidated birthDate
|
|
19202
|
+
function formatBirthDate$1(birthDate) {
|
|
19203
|
+
if (!birthDate)
|
|
19204
|
+
return '';
|
|
19205
|
+
const parsed = parse(birthDate, 'yyyy-MM-dd', new Date());
|
|
19206
|
+
return isValid(parsed) ? format$2(parsed, 'dd-MM-yyyy') : '';
|
|
19207
|
+
}
|
|
19208
|
+
// A field can have separate "main-booker"/other-pax variants sharing the same identity
|
|
19209
|
+
// (customFieldConfigurationId for custom fields, fieldName for built-in fields).
|
|
19210
|
+
const MAIN_BOOKER_GROUP = 'main-booker';
|
|
19211
|
+
const isCustomPaxField = (field) => field.customFieldConfigurationId != null;
|
|
19212
|
+
function resolveApplicablePaxFields(fields, isMainBooker) {
|
|
19213
|
+
const variantsByKey = new Map();
|
|
19214
|
+
fields.forEach((field) => {
|
|
19215
|
+
const key = field.customFieldConfigurationId ?? field.fieldName;
|
|
19216
|
+
const variants = variantsByKey.get(key) ?? [];
|
|
19217
|
+
variants.push(field);
|
|
19218
|
+
variantsByKey.set(key, variants);
|
|
19219
|
+
});
|
|
19220
|
+
const applicableFields = [];
|
|
19221
|
+
variantsByKey.forEach((variants) => {
|
|
19222
|
+
const hasRoleSplit = variants.some((v) => v.group === MAIN_BOOKER_GROUP);
|
|
19223
|
+
if (!hasRoleSplit) {
|
|
19224
|
+
applicableFields.push(variants[0]);
|
|
19225
|
+
return;
|
|
19226
|
+
}
|
|
19227
|
+
const roleMatch = variants.find((v) => (v.group === MAIN_BOOKER_GROUP) === isMainBooker);
|
|
19228
|
+
if (roleMatch)
|
|
19229
|
+
applicableFields.push(roleMatch);
|
|
19230
|
+
});
|
|
19231
|
+
return applicableFields;
|
|
19232
|
+
}
|
|
19233
|
+
function buildEmptyCustomFields(paxFields = []) {
|
|
19234
|
+
const uniqueIds = Array.from(new Set(paxFields.filter(isCustomPaxField).map((field) => field.customFieldConfigurationId)));
|
|
19235
|
+
return uniqueIds.map((customFieldConfigurationId) => ({ customFieldConfigurationId, value: '' }));
|
|
19236
|
+
}
|
|
19237
|
+
function getApplicablePaxCustomFields(paxFields = [], isMainBooker) {
|
|
19238
|
+
return resolveApplicablePaxFields(paxFields.filter(isCustomPaxField), isMainBooker);
|
|
19239
|
+
}
|
|
19240
|
+
function getApplicableBuiltInPaxFields(paxFields = [], isMainBooker) {
|
|
19241
|
+
return resolveApplicablePaxFields(paxFields.filter((f) => !isCustomPaxField(f)), isMainBooker);
|
|
19242
|
+
}
|
|
19243
|
+
function getBuiltInPaxField(paxFields = [], fieldName, isMainBooker) {
|
|
19244
|
+
return getApplicableBuiltInPaxFields(paxFields, isMainBooker).find((f) => f.fieldName === fieldName);
|
|
19245
|
+
}
|
|
19246
|
+
// fieldName -> path on `Traveler`, shared with validate-form.ts.
|
|
19247
|
+
const BUILT_IN_FIELD_PATHS = {
|
|
19248
|
+
gender: 'gender',
|
|
19249
|
+
firstName: 'firstName',
|
|
19250
|
+
lastName: 'lastName',
|
|
19251
|
+
dateOfBirth: 'birthDate',
|
|
19252
|
+
nickname: 'nickname',
|
|
19253
|
+
birthplace: 'birthplace',
|
|
19254
|
+
nationality: 'nationality',
|
|
19255
|
+
preferredLanguageId: 'preferredLanguageId',
|
|
19256
|
+
email: 'email',
|
|
19257
|
+
phone: 'phone',
|
|
19258
|
+
mobile: 'mobile',
|
|
19259
|
+
passportIssueDate: 'passportIssueDate',
|
|
19260
|
+
passportIssuedLocation: 'passportIssuedLocation',
|
|
19261
|
+
passportExpiryDate: 'passportExpiryDate',
|
|
19262
|
+
passportNumber: 'passportNumber',
|
|
19263
|
+
emergencyNumber: 'emergencyNumber',
|
|
19264
|
+
hasInsurance: 'hasInsurance',
|
|
19265
|
+
insuranceName: 'insuranceName',
|
|
19266
|
+
info: 'info',
|
|
19267
|
+
street: 'address.street',
|
|
19268
|
+
location: 'address.location',
|
|
19269
|
+
countryId: 'address.country',
|
|
19270
|
+
postalCode: 'address.postalCode',
|
|
19271
|
+
number: 'address.number'
|
|
19272
|
+
};
|
|
19273
|
+
// The 4 fields that always render/validate regardless of paxFields config (fallback behavior).
|
|
19274
|
+
const CORE_FIELD_NAMES = new Set(['gender', 'firstName', 'lastName', 'dateOfBirth']);
|
|
19275
|
+
const ADDRESS_FIELD_NAMES = ['street', 'location', 'countryId', 'postalCode', 'number'];
|
|
19276
|
+
// Shared by buildPax (new-booking) and applyTravelersFormValuesToEditablePackagingEntry
|
|
19277
|
+
// (edit-booking), now that PackagingEntryPax and BookingPackagePax have the same shape.
|
|
19278
|
+
function applyTravelerToPax(pax, traveler, mainBookerId) {
|
|
19279
|
+
return {
|
|
19280
|
+
...pax,
|
|
19281
|
+
firstName: traveler.firstName ?? '',
|
|
19282
|
+
lastName: traveler.lastName ?? '',
|
|
19283
|
+
dateOfBirth: traveler.birthDate || null,
|
|
19284
|
+
isMainBooker: mainBookerId != null ? traveler.id === mainBookerId : pax.isMainBooker,
|
|
19285
|
+
weight: traveler.weight ?? null,
|
|
19286
|
+
weightType: traveler.weightType ?? build.WeightType.kilograms,
|
|
19287
|
+
height: traveler.height ?? null,
|
|
19288
|
+
heightType: traveler.heightType ?? build.HeightType.centimeters,
|
|
19289
|
+
nickname: traveler.nickname,
|
|
19290
|
+
birthplace: traveler.birthplace,
|
|
19291
|
+
nationality: traveler.nationality,
|
|
19292
|
+
preferredLanguageId: traveler.preferredLanguageId,
|
|
19293
|
+
email: traveler.email,
|
|
19294
|
+
phone: traveler.phone,
|
|
19295
|
+
mobilePhone: traveler.mobile,
|
|
19296
|
+
emergencyNumber: traveler.emergencyNumber,
|
|
19297
|
+
passportNumber: traveler.passportNumber,
|
|
19298
|
+
passportIssueDate: traveler.passportIssueDate,
|
|
19299
|
+
passportIssuedLocation: traveler.passportIssuedLocation,
|
|
19300
|
+
passportExpiryDate: traveler.passportExpiryDate,
|
|
19301
|
+
hasInsurance: traveler.hasInsurance,
|
|
19302
|
+
insuranceName: traveler.insuranceName,
|
|
19303
|
+
info: traveler.info,
|
|
19304
|
+
address: buildTravelerAddress(traveler),
|
|
19305
|
+
customFields: traveler.customFields ?? []
|
|
19306
|
+
};
|
|
19307
|
+
}
|
|
19308
|
+
// name/email are required on the wire but aren't part of the per-traveler address sub-form -
|
|
19309
|
+
// derive them from the traveler's own name/email instead of asking twice.
|
|
19310
|
+
function buildTravelerAddress(traveler) {
|
|
19311
|
+
if (!traveler.address || !Object.values(traveler.address).some((v) => !!v))
|
|
19312
|
+
return undefined;
|
|
19313
|
+
return {
|
|
19314
|
+
name: `${traveler.firstName} ${traveler.lastName}`.trim(),
|
|
19315
|
+
street: traveler.address.street ?? '',
|
|
19316
|
+
number: traveler.address.number ?? '',
|
|
19317
|
+
postalCode: traveler.address.postalCode ?? '',
|
|
19318
|
+
location: traveler.address.location ?? '',
|
|
19319
|
+
country: traveler.address.country ?? '',
|
|
19320
|
+
email: traveler.email ?? ''
|
|
19321
|
+
};
|
|
19322
|
+
}
|
|
19323
|
+
// Always initialized (even when not shown) to keep Formik inputs controlled, matching weight/height.
|
|
19324
|
+
const EMPTY_BUILT_IN_FIELDS = {
|
|
19325
|
+
nickname: '',
|
|
19326
|
+
birthplace: '',
|
|
19327
|
+
nationality: '',
|
|
19328
|
+
preferredLanguageId: undefined,
|
|
19329
|
+
email: '',
|
|
19330
|
+
phone: '',
|
|
19331
|
+
mobile: '',
|
|
19332
|
+
passportNumber: '',
|
|
19333
|
+
passportIssueDate: '',
|
|
19334
|
+
passportIssuedLocation: '',
|
|
19335
|
+
passportExpiryDate: '',
|
|
19336
|
+
emergencyNumber: '',
|
|
19337
|
+
hasInsurance: false,
|
|
19338
|
+
insuranceName: '',
|
|
19339
|
+
info: '',
|
|
19340
|
+
address: { street: '', number: '', postalCode: '', location: '', country: '' }
|
|
19341
|
+
};
|
|
19342
|
+
function createTraveler(traveler, followNumber, personTranslation, isCompact, maxBabyAge, paxFields) {
|
|
19343
|
+
if (isCompact) {
|
|
19344
|
+
return {
|
|
19345
|
+
id: traveler.id,
|
|
19346
|
+
firstName: personTranslation,
|
|
19347
|
+
lastName: `${followNumber.number++}`,
|
|
19348
|
+
birthDate: '',
|
|
19349
|
+
gender: '',
|
|
19350
|
+
// Keep a booked age of 0 instead of submitting and pricing the traveler as a 30 year
|
|
19351
|
+
// old, while still falling back for a missing or unusable age. `??` alone would let a
|
|
19352
|
+
// NaN (unparseable date of birth upstream) reach the age input and the booked pax.
|
|
19353
|
+
// See TIDE-3269.
|
|
19354
|
+
age: Number.isFinite(traveler.age) ? traveler.age : 30,
|
|
19355
|
+
// No isBaby here on purpose: the age below is editable and nothing revalidates it
|
|
19356
|
+
// in compact mode, so a stored flag would end up contradicting the entered age.
|
|
19357
|
+
weight: null,
|
|
19358
|
+
weightType: build.WeightType.kilograms,
|
|
19359
|
+
height: null,
|
|
19360
|
+
heightType: build.HeightType.centimeters,
|
|
19361
|
+
...EMPTY_BUILT_IN_FIELDS,
|
|
19362
|
+
customFields: buildEmptyCustomFields(paxFields)
|
|
19363
|
+
};
|
|
19364
|
+
}
|
|
19365
|
+
return {
|
|
19366
|
+
id: traveler.id,
|
|
19367
|
+
firstName: '',
|
|
19368
|
+
lastName: '',
|
|
19369
|
+
birthDate: '',
|
|
19370
|
+
gender: '',
|
|
19371
|
+
isBaby: isBabyAge(traveler.age, maxBabyAge),
|
|
19372
|
+
weight: null,
|
|
19373
|
+
weightType: build.WeightType.kilograms,
|
|
19374
|
+
height: null,
|
|
19375
|
+
heightType: build.HeightType.centimeters,
|
|
19376
|
+
...EMPTY_BUILT_IN_FIELDS,
|
|
19377
|
+
customFields: buildEmptyCustomFields(paxFields)
|
|
19378
|
+
};
|
|
19379
|
+
}
|
|
19380
|
+
function createInitialValuesFromRooms(formRooms, startDate, endDate, agentAdressId, personTranslation, isCompact, maxBabyAge, paxFields) {
|
|
19381
|
+
const followNumber = { number: 1 };
|
|
19382
|
+
const initialValues = {
|
|
19383
|
+
startDate,
|
|
19384
|
+
endDate,
|
|
19385
|
+
rooms: formRooms.map((room) => ({
|
|
19386
|
+
adults: room.adults.map((traveler) => createTraveler(traveler, followNumber, personTranslation, isCompact, maxBabyAge, paxFields)),
|
|
19387
|
+
children: room.children.map((traveler) => createTraveler(traveler, followNumber, personTranslation, isCompact, maxBabyAge, paxFields))
|
|
19388
|
+
})),
|
|
19389
|
+
mainBookerId: -1,
|
|
19390
|
+
street: '',
|
|
19391
|
+
houseNumber: '',
|
|
19392
|
+
box: '',
|
|
19393
|
+
zipCode: '',
|
|
19394
|
+
place: '',
|
|
19395
|
+
country: '',
|
|
19396
|
+
phone: '',
|
|
19397
|
+
email: '',
|
|
19398
|
+
emailConfirmation: '',
|
|
19399
|
+
travelAgentId: agentAdressId ?? 0,
|
|
19400
|
+
travelAgentName: ''
|
|
19401
|
+
};
|
|
19402
|
+
if (initialValues.rooms?.[0]?.adults?.[0]) {
|
|
19403
|
+
initialValues.mainBookerId = initialValues.rooms[0].adults[0].id;
|
|
19404
|
+
}
|
|
19405
|
+
return initialValues;
|
|
19406
|
+
}
|
|
19407
|
+
function mapPackagingEntryPaxToTraveler(roomTraveler) {
|
|
19408
|
+
return {
|
|
19409
|
+
weight: roomTraveler.weight ?? null,
|
|
19410
|
+
weightType: roomTraveler.weightType ?? build.WeightType.kilograms,
|
|
19411
|
+
height: roomTraveler.height ?? null,
|
|
19412
|
+
heightType: roomTraveler.heightType ?? build.HeightType.centimeters,
|
|
19413
|
+
nickname: roomTraveler.nickname ?? '',
|
|
19414
|
+
birthplace: roomTraveler.birthplace ?? '',
|
|
19415
|
+
nationality: roomTraveler.nationality ?? '',
|
|
19416
|
+
preferredLanguageId: roomTraveler.preferredLanguageId,
|
|
19417
|
+
email: roomTraveler.email ?? '',
|
|
19418
|
+
phone: roomTraveler.phone ?? '',
|
|
19419
|
+
mobile: roomTraveler.mobilePhone ?? '',
|
|
19420
|
+
passportNumber: roomTraveler.passportNumber ?? '',
|
|
19421
|
+
passportIssueDate: roomTraveler.passportIssueDate ?? '',
|
|
19422
|
+
passportIssuedLocation: roomTraveler.passportIssuedLocation ?? '',
|
|
19423
|
+
passportExpiryDate: roomTraveler.passportExpiryDate ?? '',
|
|
19424
|
+
emergencyNumber: roomTraveler.emergencyNumber ?? '',
|
|
19425
|
+
hasInsurance: roomTraveler.hasInsurance ?? false,
|
|
19426
|
+
insuranceName: roomTraveler.insuranceName ?? '',
|
|
19427
|
+
info: roomTraveler.info ?? '',
|
|
19428
|
+
address: roomTraveler.address
|
|
19429
|
+
? {
|
|
19430
|
+
street: roomTraveler.address.street ?? '',
|
|
19431
|
+
number: roomTraveler.address.number ?? '',
|
|
19432
|
+
postalCode: roomTraveler.address.postalCode ?? '',
|
|
19433
|
+
location: roomTraveler.address.location ?? '',
|
|
19434
|
+
country: roomTraveler.address.country ?? ''
|
|
19435
|
+
}
|
|
19436
|
+
: undefined,
|
|
19437
|
+
customFields: roomTraveler.customFields ?? []
|
|
19438
|
+
};
|
|
19439
|
+
}
|
|
19440
|
+
function createInitialValuesFromEditablePackagingEntry(editablePackagingEntry, agentAdressId) {
|
|
19441
|
+
const pax = editablePackagingEntry?.pax ?? [];
|
|
19442
|
+
const rooms = editablePackagingEntry.rooms.map((room) => {
|
|
19443
|
+
const roomPax = pax.filter((x) => room.paxIds.includes(x.id));
|
|
19444
|
+
const adults = roomPax.filter((x) => x.age >= 18);
|
|
19445
|
+
const children = roomPax.filter((x) => x.age < 18);
|
|
19446
|
+
return {
|
|
19447
|
+
adults: adults.map((roomTraveler) => {
|
|
19448
|
+
return {
|
|
19449
|
+
id: roomTraveler.id,
|
|
19450
|
+
firstName: roomTraveler.firstName ?? '',
|
|
19451
|
+
lastName: roomTraveler.lastName ?? '',
|
|
19452
|
+
birthDate: roomTraveler.dateOfBirth ? format$2(new Date(roomTraveler.dateOfBirth), 'yyyy-MM-dd') : '',
|
|
19453
|
+
gender: '',
|
|
19454
|
+
...mapPackagingEntryPaxToTraveler(roomTraveler)
|
|
19455
|
+
};
|
|
19456
|
+
}),
|
|
19457
|
+
children: children.map((roomTraveler) => {
|
|
19458
|
+
return {
|
|
19459
|
+
id: roomTraveler.id,
|
|
19460
|
+
firstName: roomTraveler.firstName ?? '',
|
|
19461
|
+
lastName: roomTraveler.lastName ?? '',
|
|
19462
|
+
birthDate: roomTraveler.dateOfBirth ? format$2(new Date(roomTraveler.dateOfBirth), 'yyyy-MM-dd') : '',
|
|
19463
|
+
gender: '',
|
|
19464
|
+
isBaby: isBabyAge(roomTraveler.age),
|
|
19465
|
+
...mapPackagingEntryPaxToTraveler(roomTraveler)
|
|
19466
|
+
};
|
|
19467
|
+
})
|
|
19468
|
+
};
|
|
19469
|
+
});
|
|
19470
|
+
const values = createInitialValuesFromRooms(rooms.map((room) => ({ adults: room.adults, children: room.children })), first(editablePackagingEntry?.lines)?.from, last(editablePackagingEntry?.lines)?.to, agentAdressId);
|
|
19471
|
+
values.rooms = rooms;
|
|
19472
|
+
values.mainBookerId = pax.find((x) => x.isMainBooker)?.id ?? rooms[0]?.adults?.[0]?.id ?? -1;
|
|
19473
|
+
const address = editablePackagingEntry?.address;
|
|
19474
|
+
values.street = address?.street ?? '';
|
|
19475
|
+
values.houseNumber = address?.houseNumber ?? '';
|
|
19476
|
+
values.box = address?.box ?? '';
|
|
19477
|
+
values.zipCode = address?.zipCode ?? '';
|
|
19478
|
+
values.place = address?.place ?? '';
|
|
19479
|
+
values.country = address?.country ?? '';
|
|
19480
|
+
values.phone = address?.phone ?? '';
|
|
19481
|
+
values.email = address?.email ?? '';
|
|
19482
|
+
values.emailConfirmation = address?.email ?? '';
|
|
19483
|
+
values.travelAgentId = address?.travelAgentId ?? agentAdressId ?? 0;
|
|
19484
|
+
return values;
|
|
19485
|
+
}
|
|
19486
|
+
function applyTravelersFormValuesToEditablePackagingEntry(editablePackagingEntry, values) {
|
|
19487
|
+
const travelers = values.rooms.flatMap((room) => [...room.adults, ...room.children]);
|
|
19488
|
+
// The backend treats the main booker's per-pax address as authoritative, so keep the
|
|
19489
|
+
// booking-level address in sync with it rather than sending two independent values.
|
|
19490
|
+
const mainBookerAddress = travelers.find((x) => x.id === values.mainBookerId)?.address;
|
|
19491
|
+
return {
|
|
19492
|
+
...editablePackagingEntry,
|
|
19493
|
+
pax: (editablePackagingEntry.pax ?? []).map((pax) => {
|
|
19494
|
+
const traveler = travelers.find((x) => x.id === pax.id);
|
|
19495
|
+
return traveler ? applyTravelerToPax(pax, traveler, values.mainBookerId) : pax;
|
|
19496
|
+
}),
|
|
19497
|
+
address: {
|
|
19498
|
+
...editablePackagingEntry.address,
|
|
19499
|
+
street: mainBookerAddress?.street || values.street,
|
|
19500
|
+
houseNumber: mainBookerAddress?.number || values.houseNumber,
|
|
19501
|
+
box: values.box,
|
|
19502
|
+
zipCode: mainBookerAddress?.postalCode || values.zipCode,
|
|
19503
|
+
place: mainBookerAddress?.location || values.place,
|
|
19504
|
+
country: mainBookerAddress?.country || values.country,
|
|
19505
|
+
travelAgentId: values.travelAgentId,
|
|
19506
|
+
phone: values.phone,
|
|
19507
|
+
email: values.email
|
|
19508
|
+
}
|
|
19509
|
+
};
|
|
19510
|
+
}
|
|
19511
|
+
const SharedTravelersForm = ({ formik, translations, travellersSettings, countries, agents, bookingType, agentAdressId, travelersFirstStep = false, isUnavailable = false, useCompactForm = false, showAllCountries = false, paxFields, showAgentSelection = false, initialShowAgents = false, renderPreviousButton, onBookingTypeChange }) => {
|
|
19512
|
+
const [showAgents, setShowAgents] = useState(initialShowAgents);
|
|
19513
|
+
const typeaheadAgents = useMemo(() => sortBy(agents?.map((agent) => ({
|
|
19514
|
+
key: `${agent.id}`,
|
|
19515
|
+
value: `${agent.name}${agent.postalCode || agent.location ? ` (${compact([agent.postalCode, agent.location]).join(' ')})` : ''}`,
|
|
19516
|
+
text: `${agent.name}${agent.postalCode || agent.location ? ` (${compact([agent.postalCode, agent.location]).join(' ')})` : ''}`
|
|
19517
|
+
})), 'value') ?? [], [agents]);
|
|
19518
|
+
const [filteredAgents, setFilteredAgents] = useState(typeaheadAgents);
|
|
19519
|
+
const flatErrors = flat(formik.errors);
|
|
19520
|
+
const errorKeys = Object.keys(flatErrors).filter((key) => get(formik.touched, key));
|
|
19521
|
+
const hasVisibleError = (key) => get(formik.errors, key) && get(formik.touched, key);
|
|
19522
|
+
const mainBooker = formik.values.rooms
|
|
19523
|
+
.find((room) => room.adults.find((traveler) => traveler.id === formik.values.mainBookerId))
|
|
19524
|
+
?.adults.find((traveler) => traveler.id === formik.values.mainBookerId);
|
|
19525
|
+
const countryOptions = [
|
|
19526
|
+
{ key: 'empty', value: undefined, label: translations.TRAVELERS_FORM.SELECT_COUNTRY },
|
|
19527
|
+
...(showAllCountries
|
|
19528
|
+
? countries?.map((country) => ({ key: country.iso2, value: country.iso2, label: country.name })) ?? []
|
|
19529
|
+
: travellersSettings?.countries?.map((country) => ({ key: country.iso2, value: country.iso2, label: country.name })) ?? [
|
|
19530
|
+
{ key: 'be', value: 'be', label: translations.TRAVELERS_FORM.COUNTRIES.BELGIUM },
|
|
19531
|
+
{ key: 'nl', value: 'nl', label: translations.TRAVELERS_FORM.COUNTRIES.NETHERLANDS },
|
|
19532
|
+
{ key: 'fr', value: 'fr', label: translations.TRAVELERS_FORM.COUNTRIES.FRANCE }
|
|
19533
|
+
])
|
|
19534
|
+
];
|
|
19535
|
+
const handleMainBookerChange = (event) => {
|
|
19536
|
+
formik.setFieldValue('mainBookerId', parseInt(event.currentTarget.value, 10));
|
|
19537
|
+
};
|
|
19538
|
+
const handleAgentChange = (value) => {
|
|
19539
|
+
setFilteredAgents(typeaheadAgents.filter((x) => x.value.toLocaleLowerCase().indexOf(value.toLocaleLowerCase()) > -1));
|
|
19540
|
+
formik.setFieldValue('travelAgentName', value);
|
|
19541
|
+
};
|
|
19542
|
+
const handleAgentSelect = (key) => {
|
|
19543
|
+
const agent = typeaheadAgents.find((x) => x.key === key);
|
|
19544
|
+
formik.setValues({
|
|
19545
|
+
...formik.values,
|
|
19546
|
+
travelAgentId: Number(agent?.key),
|
|
19547
|
+
travelAgentName: agent?.value ?? ''
|
|
19548
|
+
});
|
|
19549
|
+
onBookingTypeChange?.(agentAdressId && agentAdressId !== 0 ? 'b2b' : 'b2b2c');
|
|
19550
|
+
};
|
|
19551
|
+
const handleAgentClear = () => {
|
|
19552
|
+
formik.setValues({ ...formik.values, travelAgentId: 0, travelAgentName: '' });
|
|
19553
|
+
onBookingTypeChange?.('b2c');
|
|
19554
|
+
};
|
|
19555
|
+
const toggleAgent = (value) => {
|
|
19556
|
+
setShowAgents(value);
|
|
19557
|
+
if (!value) {
|
|
19558
|
+
handleAgentClear();
|
|
19559
|
+
setFilteredAgents([]);
|
|
19560
|
+
}
|
|
19561
|
+
};
|
|
19562
|
+
const handleAddTraveler = (roomIndex) => {
|
|
19563
|
+
const rooms = [...formik.values.rooms];
|
|
19564
|
+
const newAdult = {
|
|
19565
|
+
id: Date.now(),
|
|
19566
|
+
firstName: '',
|
|
19567
|
+
lastName: '',
|
|
19568
|
+
birthDate: '',
|
|
19569
|
+
gender: '',
|
|
19570
|
+
weight: null,
|
|
19571
|
+
weightType: build.WeightType.kilograms,
|
|
19572
|
+
height: null,
|
|
19573
|
+
heightType: build.HeightType.centimeters,
|
|
19574
|
+
...EMPTY_BUILT_IN_FIELDS,
|
|
19575
|
+
customFields: buildEmptyCustomFields(paxFields)
|
|
19576
|
+
};
|
|
19577
|
+
rooms[roomIndex] = { ...rooms[roomIndex], adults: [...rooms[roomIndex].adults, newAdult] };
|
|
19578
|
+
formik.setFieldValue('rooms', rooms);
|
|
19579
|
+
};
|
|
19580
|
+
const handleRemoveTraveler = (roomIndex, travelerIndex) => {
|
|
19581
|
+
const rooms = [...formik.values.rooms];
|
|
19582
|
+
const adults = [...rooms[roomIndex].adults];
|
|
19583
|
+
if (adults.length <= 1)
|
|
19584
|
+
return;
|
|
19585
|
+
adults.splice(travelerIndex, 1);
|
|
19586
|
+
rooms[roomIndex] = { ...rooms[roomIndex], adults };
|
|
19587
|
+
formik.setFieldValue('rooms', rooms);
|
|
19588
|
+
};
|
|
19589
|
+
const handleAddRoom = () => {
|
|
19590
|
+
const rooms = [...formik.values.rooms];
|
|
19591
|
+
rooms.push({
|
|
19592
|
+
adults: [
|
|
19593
|
+
{
|
|
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
|
+
],
|
|
19607
|
+
children: []
|
|
19608
|
+
});
|
|
19609
|
+
formik.setFieldValue('rooms', rooms);
|
|
19610
|
+
};
|
|
19611
|
+
const handleRemoveRoom = (roomIndex) => {
|
|
19612
|
+
const rooms = [...formik.values.rooms];
|
|
19613
|
+
rooms.splice(roomIndex, 1);
|
|
19614
|
+
formik.setFieldValue('rooms', rooms);
|
|
19615
|
+
};
|
|
19616
|
+
const renderWeightControl = (namePrefix, value, isRequired) => (React__default.createElement("div", { className: "form__row" },
|
|
19617
|
+
React__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 ?? '' }),
|
|
19618
|
+
React__default.createElement(LabeledSelect, { extraClassName: "form__group--md-33", name: `${namePrefix}.weightType`, onChange: formik.handleChange, onBlur: formik.handleBlur, value: value.weightType, options: [
|
|
19619
|
+
{ key: 'kg', value: build.WeightType.kilograms, label: translations.TRAVELERS_FORM.WEIGHT_UNIT_KG },
|
|
19620
|
+
{ key: 'lb', value: build.WeightType.pounds, label: translations.TRAVELERS_FORM.WEIGHT_UNIT_LB }
|
|
19621
|
+
] })));
|
|
19622
|
+
const renderHeightControl = (namePrefix, value, isRequired) => (React__default.createElement("div", { className: "form__row" },
|
|
19623
|
+
React__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 ?? '' }),
|
|
19624
|
+
React__default.createElement(LabeledSelect, { extraClassName: "form__group--md-33", name: `${namePrefix}.heightType`, onChange: formik.handleChange, onBlur: formik.handleBlur, value: value.heightType, options: [
|
|
19625
|
+
{ key: 'cm', value: build.HeightType.centimeters, label: translations.TRAVELERS_FORM.HEIGHT_UNIT_CM },
|
|
19626
|
+
{ key: 'ftIn', value: build.HeightType.feetInches, label: translations.TRAVELERS_FORM.HEIGHT_UNIT_FT_IN }
|
|
19627
|
+
] })));
|
|
19628
|
+
const getCustomFieldControl = (field, value, name) => {
|
|
19629
|
+
const label = compact([field.fieldName, field.isRequired && '*']).join(' ');
|
|
19630
|
+
switch (field.dataType) {
|
|
19631
|
+
case build.CustomFieldDataType.bool:
|
|
19632
|
+
return (React__default.createElement("div", { className: buildClassName(['form__group', hasVisibleError(name) && 'form__group--error']) },
|
|
19633
|
+
React__default.createElement("div", { className: "checkbox" },
|
|
19634
|
+
React__default.createElement("label", { className: "checkbox__label" },
|
|
19635
|
+
React__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 }),
|
|
19636
|
+
label))));
|
|
19637
|
+
case build.CustomFieldDataType.date:
|
|
19638
|
+
return (React__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 }));
|
|
19639
|
+
case build.CustomFieldDataType.time:
|
|
19640
|
+
return (React__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 }));
|
|
19641
|
+
case build.CustomFieldDataType.numeric:
|
|
19642
|
+
return (React__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 }));
|
|
19643
|
+
case build.CustomFieldDataType.string:
|
|
19644
|
+
default:
|
|
19645
|
+
return (React__default.createElement(LabeledInput, { hasError: hasVisibleError(name), label: label, name: name, onChange: (e) => formik.setFieldValue(name, e.target.value), onBlur: formik.handleBlur, value: value }));
|
|
19646
|
+
}
|
|
19647
|
+
};
|
|
19648
|
+
const renderCustomFields = (travelerValues, namePrefix) => {
|
|
19649
|
+
if (!paxFields?.length)
|
|
19650
|
+
return null;
|
|
19651
|
+
const isMainBooker = travelerValues.id === formik.values.mainBookerId;
|
|
19652
|
+
const applicableFields = getApplicablePaxCustomFields(paxFields, isMainBooker);
|
|
19653
|
+
if (!applicableFields.length)
|
|
19654
|
+
return null;
|
|
19655
|
+
return (React__default.createElement("div", { className: "travelers-form__custom-fields-grid" }, applicableFields.map((field) => {
|
|
19656
|
+
const fieldIndex = travelerValues.customFields?.findIndex((c) => c.customFieldConfigurationId === field.customFieldConfigurationId) ?? -1;
|
|
19657
|
+
if (fieldIndex < 0)
|
|
19658
|
+
return null;
|
|
19659
|
+
const name = `${namePrefix}.customFields[${fieldIndex}].value`;
|
|
19660
|
+
const value = travelerValues.customFields[fieldIndex].value;
|
|
19661
|
+
return (React__default.createElement("div", { key: field.customFieldConfigurationId, style: { gridColumn: `span ${field.col || 12}` } }, getCustomFieldControl(field, value, name)));
|
|
19662
|
+
})));
|
|
19663
|
+
};
|
|
19664
|
+
// Fields with a dedicated control (gender, hasInsurance, countryId) aren't listed here.
|
|
19665
|
+
const BUILT_IN_FIELD_CONTROL_META = {
|
|
19666
|
+
firstName: { label: translations.TRAVELERS_FORM.FIRST_NAME },
|
|
19667
|
+
lastName: { label: translations.TRAVELERS_FORM.LAST_NAME },
|
|
19668
|
+
dateOfBirth: { type: 'date', label: translations.TRAVELERS_FORM.BIRTHDATE },
|
|
19669
|
+
nickname: { label: translations.TRAVELERS_FORM.NICKNAME },
|
|
19670
|
+
birthplace: { label: translations.TRAVELERS_FORM.BIRTHPLACE },
|
|
19671
|
+
nationality: { label: translations.TRAVELERS_FORM.NATIONALITY },
|
|
19672
|
+
preferredLanguageId: { type: 'number', label: translations.TRAVELERS_FORM.PREFERRED_LANGUAGE },
|
|
19673
|
+
email: { type: 'email', label: translations.TRAVELERS_FORM.TRAVELER_EMAIL },
|
|
19674
|
+
phone: { label: translations.TRAVELERS_FORM.TRAVELER_PHONE },
|
|
19675
|
+
mobile: { label: translations.TRAVELERS_FORM.TRAVELER_MOBILE },
|
|
19676
|
+
passportNumber: { label: translations.TRAVELERS_FORM.PASSPORT_NUMBER },
|
|
19677
|
+
passportIssueDate: { type: 'date', label: translations.TRAVELERS_FORM.PASSPORT_ISSUE_DATE },
|
|
19678
|
+
passportIssuedLocation: { label: translations.TRAVELERS_FORM.PASSPORT_ISSUED_LOCATION },
|
|
19679
|
+
passportExpiryDate: { type: 'date', label: translations.TRAVELERS_FORM.PASSPORT_EXPIRY_DATE },
|
|
19680
|
+
emergencyNumber: { label: translations.TRAVELERS_FORM.EMERGENCY_NUMBER },
|
|
19681
|
+
insuranceName: { label: translations.TRAVELERS_FORM.INSURANCE_NAME },
|
|
19682
|
+
info: { label: translations.TRAVELERS_FORM.INFO },
|
|
19683
|
+
street: { label: translations.TRAVELERS_FORM.STREET },
|
|
19684
|
+
location: { label: translations.TRAVELERS_FORM.CITY },
|
|
19685
|
+
postalCode: { label: translations.TRAVELERS_FORM.ZIPCODE },
|
|
19686
|
+
number: { label: translations.TRAVELERS_FORM.HOUSE_NUMBER }
|
|
19687
|
+
};
|
|
19688
|
+
const renderBuiltInFieldControl = (fieldName, isRequired, travelerValues, namePrefix) => {
|
|
19689
|
+
const path = BUILT_IN_FIELD_PATHS[fieldName] ?? fieldName;
|
|
19690
|
+
const name = `${namePrefix}.${path}`;
|
|
19691
|
+
const value = get(travelerValues, path);
|
|
19692
|
+
switch (fieldName) {
|
|
19693
|
+
case 'gender':
|
|
19694
|
+
return React__default.createElement(GenderControl, { translations: translations, value: travelerValues, formik: formik, name: name });
|
|
19695
|
+
case 'hasInsurance':
|
|
19696
|
+
return (React__default.createElement("div", { className: buildClassName(['form__group', hasVisibleError(name) && 'form__group--error']) },
|
|
19697
|
+
React__default.createElement("div", { className: "checkbox" },
|
|
19698
|
+
React__default.createElement("label", { className: "checkbox__label" },
|
|
19699
|
+
React__default.createElement("input", { type: "checkbox", className: "checkbox__input", checked: !!value, onChange: (e) => formik.setFieldValue(name, e.target.checked), onBlur: formik.handleBlur, name: name }),
|
|
19700
|
+
compact([translations.TRAVELERS_FORM.HAS_INSURANCE, isRequired && '*']).join(' ')))));
|
|
19701
|
+
case 'countryId':
|
|
19702
|
+
return (React__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 }));
|
|
19703
|
+
case 'weight':
|
|
19704
|
+
return renderWeightControl(namePrefix, travelerValues, isRequired);
|
|
19705
|
+
case 'height':
|
|
19706
|
+
return renderHeightControl(namePrefix, travelerValues, isRequired);
|
|
19707
|
+
default: {
|
|
19708
|
+
const meta = BUILT_IN_FIELD_CONTROL_META[fieldName] ?? { label: fieldName };
|
|
19709
|
+
return (React__default.createElement(LabeledInput, { type: meta.type, hasError: hasVisibleError(name), label: meta.label, required: isRequired, name: name, onChange: formik.handleChange, onBlur: formik.handleBlur, value: value ?? '' }));
|
|
19710
|
+
}
|
|
19711
|
+
}
|
|
19712
|
+
};
|
|
19713
|
+
const CORE_FIELD_DEFAULTS = {
|
|
19714
|
+
gender: { isRequired: true, col: 4 },
|
|
19715
|
+
firstName: { isRequired: true, col: 4 },
|
|
19716
|
+
lastName: { isRequired: true, col: 4 },
|
|
19717
|
+
dateOfBirth: { isRequired: true, col: 4 }
|
|
19718
|
+
};
|
|
19719
|
+
const resolveBuiltInField = (fieldName, travelerValues) => {
|
|
19720
|
+
const isMainBooker = travelerValues.id === formik.values.mainBookerId;
|
|
19721
|
+
const configured = getBuiltInPaxField(paxFields, fieldName, isMainBooker);
|
|
19722
|
+
const fallback = CORE_FIELD_DEFAULTS[fieldName];
|
|
19723
|
+
if (!configured && !fallback)
|
|
19724
|
+
return undefined;
|
|
19725
|
+
return { isRequired: configured?.isRequired ?? fallback.isRequired, col: configured?.col ?? fallback.col };
|
|
19726
|
+
};
|
|
19727
|
+
const renderBuiltInField = (fieldName, travelerValues, namePrefix) => {
|
|
19728
|
+
const resolved = resolveBuiltInField(fieldName, travelerValues);
|
|
19729
|
+
if (!resolved)
|
|
19730
|
+
return null;
|
|
19731
|
+
return (React__default.createElement("div", { key: fieldName, style: { gridColumn: `span ${resolved.col || 12}` } }, renderBuiltInFieldControl(fieldName, resolved.isRequired, travelerValues, namePrefix)));
|
|
19732
|
+
};
|
|
19733
|
+
const renderTravelerAddressFields = (travelerValues, namePrefix) => {
|
|
19734
|
+
const fields = ADDRESS_FIELD_NAMES.map((fieldName) => renderBuiltInField(fieldName, travelerValues, namePrefix)).filter(Boolean);
|
|
19735
|
+
if (!fields.length)
|
|
19736
|
+
return null;
|
|
19737
|
+
return React__default.createElement("div", { className: "travelers-form__custom-fields-grid" }, fields);
|
|
19738
|
+
};
|
|
19739
|
+
const getControl = (type, value, name) => {
|
|
19740
|
+
switch (type) {
|
|
19741
|
+
case 'country':
|
|
19742
|
+
return (React__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 }));
|
|
19743
|
+
case 'phone':
|
|
19744
|
+
return (React__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 }));
|
|
19745
|
+
case 'email':
|
|
19746
|
+
return (React__default.createElement(React__default.Fragment, null,
|
|
19747
|
+
React__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 }),
|
|
19748
|
+
React__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 })));
|
|
19749
|
+
case 'street':
|
|
19750
|
+
return (React__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 }));
|
|
19751
|
+
case 'houseNumber':
|
|
19752
|
+
return (React__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 }));
|
|
19753
|
+
case 'box':
|
|
19754
|
+
return (React__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 }));
|
|
19755
|
+
case 'zipCode':
|
|
19756
|
+
return (React__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 }));
|
|
19757
|
+
case 'place':
|
|
19758
|
+
return (React__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 }));
|
|
19759
|
+
default:
|
|
19760
|
+
return null;
|
|
19761
|
+
}
|
|
19762
|
+
};
|
|
19763
|
+
const renderRoomLabel = (room) => compact([
|
|
19764
|
+
room.adults.length,
|
|
19765
|
+
room.adults.length === 1 && ` ${translations.TRAVELERS_FORM.ADULT}`,
|
|
19766
|
+
room.adults.length > 1 && ` ${translations.TRAVELERS_FORM.ADULTS}`,
|
|
19767
|
+
room.adults?.length && room.children?.length && ', ',
|
|
19768
|
+
room.children.length,
|
|
19769
|
+
room.children.length === 1 && ` ${translations.TRAVELERS_FORM.CHILD}`,
|
|
19770
|
+
room.children.length > 1 && ` ${translations.TRAVELERS_FORM.CHILDREN}`
|
|
19771
|
+
]).join('');
|
|
19772
|
+
const renderTravelerFields = (travelerValues, namePrefix, isAdult, roomIndex, travelerIndex) => {
|
|
19773
|
+
if (useCompactForm) {
|
|
19774
|
+
return (React__default.createElement("div", { className: "form__row" },
|
|
19775
|
+
React__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 })));
|
|
19776
|
+
}
|
|
19777
|
+
// 'birthDate' (the legacy formFields type string) is normalized to the paxFields catalog's
|
|
19778
|
+
// 'dateOfBirth' fieldName for config lookups.
|
|
19779
|
+
const baseFieldNames = (travellersSettings?.formFields?.length ? travellersSettings.formFields.map((field) => field.type) : ['gender', 'firstName', 'lastName', 'dateOfBirth']).map((fieldName) => (fieldName === 'birthDate' ? 'dateOfBirth' : fieldName));
|
|
19780
|
+
const isMainBooker = travelerValues.id === formik.values.mainBookerId;
|
|
19781
|
+
const extraFieldNames = getApplicableBuiltInPaxFields(paxFields, isMainBooker)
|
|
19782
|
+
.map((field) => field.fieldName)
|
|
19783
|
+
.filter((fieldName) => !baseFieldNames.includes(fieldName) && !ADDRESS_FIELD_NAMES.includes(fieldName));
|
|
19784
|
+
return (React__default.createElement(React__default.Fragment, null,
|
|
19785
|
+
React__default.createElement("div", { className: "travelers-form__custom-fields-grid" }, [...baseFieldNames, ...extraFieldNames].map((fieldName) => renderBuiltInField(fieldName, travelerValues, namePrefix))),
|
|
19786
|
+
renderTravelerAddressFields(travelerValues, namePrefix),
|
|
19787
|
+
renderCustomFields(travelerValues, namePrefix),
|
|
19788
|
+
travelersFirstStep && isAdult && formik.values.rooms[roomIndex].adults.length > 1 && (React__default.createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => handleRemoveTraveler(roomIndex, travelerIndex) }, translations.TRAVELERS_FORM.REMOVE_TRAVELER))));
|
|
19789
|
+
};
|
|
19790
|
+
return (React__default.createElement("form", { className: "form form__travelers", name: "booking--travellers", id: "booking--travellers", noValidate: true, onSubmit: formik.handleSubmit, onReset: formik.handleReset },
|
|
19791
|
+
React__default.createElement("div", { className: "form__travelers__wrapper" },
|
|
19792
|
+
formik.values.rooms.map((room, roomIndex) => (React__default.createElement("div", { key: roomIndex },
|
|
19793
|
+
formik.values.rooms.length > 1 && (React__default.createElement("div", { className: "form__region" },
|
|
19794
|
+
React__default.createElement("div", { className: "form__region-header" },
|
|
19795
|
+
React__default.createElement("h5", { className: "form__region-heading" },
|
|
19796
|
+
translations.SHARED.ROOM,
|
|
19797
|
+
" ",
|
|
19798
|
+
roomIndex + 1),
|
|
19799
|
+
React__default.createElement("p", { className: "form__region-label" }, renderRoomLabel(room))),
|
|
19800
|
+
!useCompactForm && travelersFirstStep && formik.values.rooms.length > 1 && (React__default.createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => handleRemoveRoom(roomIndex) }, "Verwijder reisgezelschap")))),
|
|
19801
|
+
room.adults.map((travelerValues, index) => (React__default.createElement("div", { className: "form__region", key: travelerValues.id },
|
|
19802
|
+
React__default.createElement("div", { className: "form__region-header" },
|
|
19803
|
+
React__default.createElement("h5", { className: "form__region-heading" },
|
|
19804
|
+
translations.TRAVELERS_FORM.TRAVELER,
|
|
19805
|
+
" ",
|
|
19806
|
+
index + 1),
|
|
19807
|
+
React__default.createElement("p", { className: "form__region-label" }, translations.TRAVELERS_FORM.ADULT),
|
|
19808
|
+
React__default.createElement("div", { className: "radiobutton" },
|
|
19809
|
+
React__default.createElement("label", { className: "radiobutton__label" },
|
|
19810
|
+
React__default.createElement("input", { type: "radio", name: "mainBookerId", onChange: handleMainBookerChange, onBlur: formik.handleBlur, value: travelerValues.id, checked: formik.values.mainBookerId === travelerValues.id, className: "radiobutton__input" }),
|
|
19811
|
+
translations.TRAVELERS_FORM.MAIN_BOOKER))),
|
|
19812
|
+
renderTravelerFields(travelerValues, `rooms[${roomIndex}].adults[${index}]`, true, roomIndex, index)))),
|
|
19813
|
+
room.children.map((travelerValues, index) => (React__default.createElement("div", { className: "form__region", key: travelerValues.id },
|
|
19814
|
+
React__default.createElement("div", { className: "form__region-header" },
|
|
19815
|
+
React__default.createElement("h5", { className: "form__region-heading" },
|
|
19816
|
+
translations.TRAVELERS_FORM.TRAVELER,
|
|
19817
|
+
" ",
|
|
19818
|
+
room.adults.length + index + 1),
|
|
19819
|
+
React__default.createElement("p", { className: "form__region-label" }, travelerValues.isBaby ? translations.TRAVELERS_FORM.BABY : translations.TRAVELERS_FORM.CHILD)),
|
|
19820
|
+
renderTravelerFields(travelerValues, `rooms[${roomIndex}].children[${index}]`, false, roomIndex, index)))),
|
|
19821
|
+
!useCompactForm && travelersFirstStep && (React__default.createElement("div", { className: "form__region" },
|
|
19822
|
+
React__default.createElement("button", { type: "button", className: "cta cta--select", onClick: () => handleAddTraveler(roomIndex) }, translations.TRAVELERS_FORM.ADD_TRAVELER)))))),
|
|
19823
|
+
!useCompactForm && (bookingType !== 'b2b' || travellersSettings?.mainBookerFormFields?.length) ? (React__default.createElement("div", { className: "form__region" },
|
|
19824
|
+
React__default.createElement("div", { className: "form__region-header" },
|
|
19825
|
+
React__default.createElement("h5", { className: "form__region-heading" }, translations.TRAVELERS_FORM.MAIN_BOOKER),
|
|
19826
|
+
React__default.createElement("p", { className: "form__region-label" }, compact([compact([mainBooker?.firstName, mainBooker?.lastName]).join(' '), formatBirthDate$1(mainBooker?.birthDate)]).join(', '))),
|
|
19827
|
+
travellersSettings?.mainBookerFormFields?.length ? (React__default.createElement("div", { className: "main-booker-form__grid" }, travellersSettings.mainBookerFormFields.map((field, index) => (React__default.createElement("div", { key: index, className: `control control--${field.type}` }, getControl(field.type, {}, field.type)))))) : (React__default.createElement(React__default.Fragment, null,
|
|
19828
|
+
React__default.createElement("div", { className: "form__twocolumn" },
|
|
19829
|
+
React__default.createElement("div", { className: "form__twocolumn-column" },
|
|
19830
|
+
React__default.createElement("div", { className: "form__row" },
|
|
19831
|
+
React__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 }),
|
|
19832
|
+
React__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 }),
|
|
19833
|
+
React__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 }))),
|
|
19834
|
+
React__default.createElement("div", { className: "form__twocolumn-column" },
|
|
19835
|
+
React__default.createElement("div", { className: "form__row" },
|
|
19836
|
+
React__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 }),
|
|
19837
|
+
React__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 }),
|
|
19838
|
+
React__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 })))),
|
|
19839
|
+
React__default.createElement("div", { className: "form__row" },
|
|
19840
|
+
React__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 }),
|
|
19841
|
+
React__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 }),
|
|
19842
|
+
React__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.createElement("div", { className: "form__region" },
|
|
19843
|
+
React__default.createElement("div", { className: "form__row" },
|
|
19844
|
+
React__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,
|
|
19845
|
+
!useCompactForm && showAgentSelection && (React__default.createElement("div", { className: "form__region" },
|
|
19846
|
+
React__default.createElement("div", { className: "form__region-header" },
|
|
19847
|
+
React__default.createElement("h5", { className: "form__region-heading" }, translations.TRAVELERS_FORM.BOOK_WITH_AGENT),
|
|
19848
|
+
React__default.createElement("div", { className: "checkbox", id: "cbxChooseOffice" },
|
|
19849
|
+
React__default.createElement("label", { className: "checkbox__label" },
|
|
19850
|
+
React__default.createElement("input", { type: "checkbox", name: "booking--mainbooker", checked: showAgents, onChange: () => toggleAgent(!showAgents), className: "checkbox__input" }),
|
|
19851
|
+
translations.TRAVELERS_FORM.CHOOSE_OFFICE))),
|
|
19852
|
+
showAgents && (React__default.createElement("div", { className: "form__row form__row--choose-office" },
|
|
19853
|
+
React__default.createElement("div", { className: buildClassName(['form__group', 'form__group--icon', hasVisibleError('travelAgentId') && 'form__group--error']) },
|
|
19854
|
+
React__default.createElement(TypeAheadInput, { value: formik.values.travelAgentName, options: filteredAgents, onChange: handleAgentChange, onSelect: handleAgentSelect, onClear: handleAgentClear, name: "travelAgentName", placeholder: translations.TRAVELERS_FORM.CHOOSE_AGENT_PLACEHOLDER }))))))),
|
|
19855
|
+
!useCompactForm && errorKeys.length > 0 && (React__default.createElement("div", { className: "form__region form__region--errors" },
|
|
19856
|
+
React__default.createElement("div", { className: "form__row" },
|
|
19857
|
+
React__default.createElement("div", { className: "form__group" },
|
|
19858
|
+
React__default.createElement("p", { className: "form__error-heading" },
|
|
19859
|
+
translations.TRAVELERS_FORM.VALIDATION_MESSAGE,
|
|
19860
|
+
":"),
|
|
19861
|
+
React__default.createElement("ul", { className: "list" }, errorKeys.map((key) => (React__default.createElement("li", { key: key }, get(flatErrors, key))))))))),
|
|
19862
|
+
travelersFirstStep && (React__default.createElement("div", { className: "booking__navigator" },
|
|
19863
|
+
React__default.createElement("button", { type: "button", className: "cta cta--select", onClick: handleAddRoom }, translations.TRAVELERS_FORM.ADD_ROOM))),
|
|
19864
|
+
React__default.createElement("div", { className: "booking__navigator" },
|
|
19865
|
+
renderPreviousButton?.(),
|
|
19866
|
+
React__default.createElement("button", { type: "submit", title: translations.STEPS.NEXT, className: 'cta' + (isUnavailable ? ' cta--disabled' : '') }, translations.STEPS.NEXT))));
|
|
19867
|
+
};
|
|
19868
|
+
|
|
19869
|
+
const fetchPriceDetails$1 = async (apiSettings, request, signal, languageCode) => {
|
|
19870
|
+
const tideClientConfig = buildTideClientConfig(apiSettings);
|
|
19871
|
+
return await build.priceDetails(tideClientConfig, request, signal, languageCode);
|
|
19872
|
+
};
|
|
19873
|
+
const priceDetailsApi = {
|
|
19874
|
+
fetchPriceDetails: fetchPriceDetails$1
|
|
19875
|
+
};
|
|
19876
|
+
|
|
19877
|
+
const updatePackageDetails = (packageDetails, changedLines) => {
|
|
19878
|
+
return {
|
|
19879
|
+
...packageDetails,
|
|
19880
|
+
options: packageDetails.options.map((opt) => {
|
|
19881
|
+
return {
|
|
19882
|
+
...opt,
|
|
19883
|
+
rooms: opt.isSelected ? updateRooms(opt.rooms, changedLines) : opt.rooms,
|
|
19884
|
+
groups: opt.isSelected ? updateBookingGroups(opt.groups, changedLines) : opt.groups,
|
|
19885
|
+
optionUnits: opt.isSelected ? updateOptionUnits(opt.optionUnits, changedLines) : opt.optionUnits,
|
|
19886
|
+
optionPax: opt.isSelected ? updateOptionPax(opt.optionPax, changedLines) : opt.optionPax
|
|
19887
|
+
};
|
|
19888
|
+
})
|
|
19889
|
+
};
|
|
19890
|
+
};
|
|
19891
|
+
// ROOMS
|
|
19892
|
+
const updateRooms = (rooms, changedLines) => {
|
|
19893
|
+
return rooms.map((room) => ({
|
|
19894
|
+
...room,
|
|
19895
|
+
options: room.options.map((option) => ({
|
|
19896
|
+
...option,
|
|
19897
|
+
price: changedLines.find((x) => x.entryLineGuid == option.entryLineGuid)?.price ?? option.price
|
|
19898
|
+
}))
|
|
19899
|
+
}));
|
|
19900
|
+
};
|
|
19901
|
+
// PAX
|
|
19902
|
+
const updateOptionPax = (pax, changedLines) => {
|
|
19903
|
+
return pax.map((p) => ({
|
|
19904
|
+
...p,
|
|
19905
|
+
groups: updatePaxGroups(p.groups, changedLines)
|
|
19906
|
+
}));
|
|
19907
|
+
};
|
|
19908
|
+
const updatePaxGroups = (groups, changedLines) => {
|
|
19909
|
+
return groups.map((g) => ({
|
|
19910
|
+
...g,
|
|
19911
|
+
options: g.options.map((o) => ({
|
|
19912
|
+
...o,
|
|
19913
|
+
groups: updatePaxGroups(o.groups, changedLines),
|
|
19914
|
+
line: {
|
|
19915
|
+
...o.line,
|
|
19916
|
+
price: changedLines.find((cl) => cl.entryLineGuid === o.line.entryLineGuid)?.price ?? o.line.price
|
|
19917
|
+
},
|
|
19918
|
+
alternatives: o.alternatives.map((a) => ({
|
|
19919
|
+
...a,
|
|
19920
|
+
price: changedLines.find((cl) => cl.entryLineGuid === a.entryLineGuid)?.price ?? a.price
|
|
19921
|
+
}))
|
|
19922
|
+
}))
|
|
19923
|
+
}));
|
|
19924
|
+
};
|
|
19925
|
+
// UNITS
|
|
19926
|
+
const updateOptionUnits = (units, changedLines) => {
|
|
19927
|
+
return units.map((u) => ({
|
|
19928
|
+
...u,
|
|
19929
|
+
groups: updateUnitGroups(u.groups, changedLines)
|
|
19930
|
+
}));
|
|
19931
|
+
};
|
|
19932
|
+
const updateUnitGroups = (groups, changedLines) => {
|
|
19933
|
+
return groups.map((g) => ({
|
|
19934
|
+
...g,
|
|
19935
|
+
options: g.options.map((o) => ({
|
|
19936
|
+
...o,
|
|
19937
|
+
groups: updateUnitGroups(o.groups, changedLines),
|
|
19938
|
+
pax: updateOptionPax(o.pax, changedLines),
|
|
19939
|
+
line: {
|
|
19940
|
+
...o.line,
|
|
19941
|
+
price: changedLines.find((cl) => cl.entryLineGuid === o.line.entryLineGuid)?.price ?? o.line.price
|
|
19942
|
+
},
|
|
19943
|
+
alternatives: o.alternatives.map((a) => ({
|
|
19944
|
+
...a,
|
|
19945
|
+
price: changedLines.find((cl) => cl.entryLineGuid === a.entryLineGuid)?.price ?? a.price
|
|
19946
|
+
}))
|
|
19947
|
+
}))
|
|
19948
|
+
}));
|
|
19949
|
+
};
|
|
19950
|
+
// BOOKING
|
|
19951
|
+
const updateBookingGroups = (groups, changedLines) => {
|
|
19952
|
+
return groups.map((g) => ({
|
|
19953
|
+
...g,
|
|
19954
|
+
options: g.options.map((o) => ({
|
|
19955
|
+
...o,
|
|
19956
|
+
groups: updateBookingGroups(o.groups, changedLines),
|
|
19957
|
+
units: updateOptionUnits(o.units, changedLines),
|
|
19958
|
+
pax: updateOptionPax(o.pax, changedLines),
|
|
19959
|
+
line: {
|
|
19960
|
+
...o.line,
|
|
19961
|
+
price: changedLines.find((cl) => cl.entryLineGuid === o.line.entryLineGuid)?.price ?? o.line.price
|
|
19962
|
+
},
|
|
19963
|
+
alternatives: o.alternatives.map((a) => ({
|
|
19964
|
+
...a,
|
|
19965
|
+
price: changedLines.find((cl) => cl.entryLineGuid === a.entryLineGuid)?.price ?? a.price
|
|
19966
|
+
}))
|
|
19967
|
+
}))
|
|
19968
|
+
}));
|
|
19969
|
+
};
|
|
19970
|
+
|
|
19971
|
+
const fetchPriceDetails = createAsyncThunk('priceDetails/fetchPriceDetails', async (_, { dispatch, getState, signal }) => {
|
|
19972
|
+
const state = getState();
|
|
19973
|
+
const languageCode = selectLanguageCode(state);
|
|
19974
|
+
const request = selectBookingPackageBookRequest(state);
|
|
19975
|
+
if (!request) {
|
|
19976
|
+
throw Error('Product not available.');
|
|
19977
|
+
}
|
|
19978
|
+
dispatch(setFetchingPriceDetails(true));
|
|
19979
|
+
dispatch(resetPriceDetails());
|
|
19980
|
+
const priceDetails = await priceDetailsApi.fetchPriceDetails(state.apiSettings, request, signal, languageCode);
|
|
19981
|
+
dispatch(setFetchingPriceDetails(false));
|
|
19982
|
+
const packageDetails = selectPackageDetails(state);
|
|
19983
|
+
if (packageDetails) {
|
|
19984
|
+
const updatedPackageDetails = updatePackageDetails(packageDetails, priceDetails.changedLines);
|
|
19985
|
+
dispatch(setPackage(updatedPackageDetails));
|
|
19986
|
+
}
|
|
19987
|
+
return priceDetails;
|
|
19988
|
+
});
|
|
19989
|
+
const priceDetailsSlice = createSlice({
|
|
19990
|
+
name: 'priceDetails',
|
|
19991
|
+
initialState: {
|
|
19992
|
+
priceDetails: [],
|
|
19993
|
+
notifications: [],
|
|
19994
|
+
isBusy: false
|
|
19995
|
+
},
|
|
19996
|
+
reducers: {
|
|
19997
|
+
setFetchingPriceDetails(state, action) {
|
|
19998
|
+
state.isBusy = action.payload;
|
|
19999
|
+
},
|
|
20000
|
+
setNotifications(state, action) {
|
|
20001
|
+
state.notifications = action.payload;
|
|
20002
|
+
},
|
|
20003
|
+
resetPriceDetails(state, _) {
|
|
20004
|
+
state.priceDetails = [];
|
|
20005
|
+
}
|
|
20006
|
+
},
|
|
20007
|
+
extraReducers: (builder) => {
|
|
20008
|
+
builder.addCase(fetchPriceDetails.fulfilled, (state, action) => {
|
|
20009
|
+
if (action.payload) {
|
|
20010
|
+
state.deposit = action.payload.deposit;
|
|
20011
|
+
state.commission = action.payload.commission;
|
|
20012
|
+
state.priceDetails = action.payload.details;
|
|
20013
|
+
state.notifications = action.payload.notifications;
|
|
20014
|
+
}
|
|
20015
|
+
});
|
|
20016
|
+
builder.addCase(fetchPriceDetails.rejected, (state) => {
|
|
20017
|
+
state.isBusy = false;
|
|
20018
|
+
});
|
|
20019
|
+
}
|
|
20020
|
+
});
|
|
20021
|
+
const { setFetchingPriceDetails } = priceDetailsSlice.actions;
|
|
20022
|
+
const { resetPriceDetails, setNotifications } = priceDetailsSlice.actions;
|
|
20023
|
+
const selectNotifications = (state) => state.priceDetails.notifications;
|
|
20024
|
+
var priceDetailsReducer = priceDetailsSlice.reducer;
|
|
20025
|
+
|
|
20026
|
+
const selectHasMounted = (state) => state.booking.hasMounted;
|
|
20027
|
+
const selectGeneratePaymentUrl = (state) => state.booking.generatePaymentUrl;
|
|
20028
|
+
const selectSkipPaymentWithAgent = (state) => state.booking.skipPaymentWithAgent;
|
|
20029
|
+
const selectIsFetchingProductOptions = (state) => state.booking.isBusy;
|
|
20030
|
+
const selectDepartureFlight = (state) => state.booking.package?.outwardFlights?.find((x) => x.isSelected);
|
|
20031
|
+
const selectReturnFlight = (state) => state.booking.package?.returnFlights?.find((x) => x.isSelected);
|
|
20032
|
+
const selectPackageRooms = (state) => state.booking.package?.options.find((x) => x.isSelected)?.rooms;
|
|
20033
|
+
const selectIncludedServiceTypes = (state) => selectActiveOption(state)?.includedServiceTypes;
|
|
20034
|
+
const selectAvailabilities = (state) => state.booking.package?.options.find((x) => x.isSelected)?.availabilities;
|
|
20035
|
+
const selectPackageTags = (state) => state.booking.package?.tags;
|
|
20036
|
+
const selectIsOnRequest = (state) => state.booking.package?.options.find((x) => x.isSelected)?.isOnRequest;
|
|
20037
|
+
const selectPackageOptionUnits = (state) => state.booking.package?.options.find((x) => x.isSelected)?.optionUnits;
|
|
20038
|
+
const selectPackageOptionPax = (state) => state.booking.package?.options.find((x) => x.isSelected)?.optionPax;
|
|
20039
|
+
const selectPackageGroups = (state) => state.booking.package?.options.find((x) => x.isSelected)?.groups;
|
|
20040
|
+
const selectPackageDetails = (state) => state.booking.package;
|
|
20041
|
+
const selectPaxFields = (state) => state.booking.package?.paxFields ?? [];
|
|
20042
|
+
const selectPackageFlights = (state) => ({
|
|
20043
|
+
outward: state.booking.package?.outwardFlights,
|
|
20044
|
+
return: state.booking.package?.returnFlights
|
|
20045
|
+
});
|
|
20046
|
+
const selectFlightMetaData = (state) => state.booking.package?.flightInfo.metaData;
|
|
20047
|
+
const selectActiveOption = (state) => state.booking.package?.options.find((x) => x.isSelected);
|
|
20048
|
+
const selectPackageAirlineGroups = createSelector(selectActiveOption, selectDepartureFlight, selectReturnFlight, (option, departureFlight, returnFlight) => option?.airlineGroups.filter((x) => x.flightIds.includes(departureFlight?.entryLineGuid ?? '') || x.flightIds.includes(returnFlight?.entryLineGuid ?? '')));
|
|
20049
|
+
const selectPackageAirportGroups = createSelector(selectActiveOption, selectDepartureFlight, selectReturnFlight, (option, departureFlight, returnFlight) => option?.airportGroups.filter((x) => x.flightIds.includes(departureFlight?.entryLineGuid ?? '') || x.flightIds.includes(returnFlight?.entryLineGuid ?? '')));
|
|
20050
|
+
const selectRoomOptionDepartureFlightsMetaData = createSelector(selectActiveOption, selectFlightMetaData, (ativeOption, flightMetaData) => {
|
|
20051
|
+
const roomOptions = ativeOption?.rooms.map((r) => r.options.filter((o) => o.isSelected).shift());
|
|
20052
|
+
return roomOptions?.map((ro) => flightMetaData?.find((f) => f.entryLineGuid === ro?.entryLineGuid)?.flightMetaData).filter((f) => f !== undefined) ?? [];
|
|
20053
|
+
});
|
|
20054
|
+
const selectRoomOptionReturnFlightsMetaData = createSelector(selectActiveOption, selectFlightMetaData, (ativeOption, flightMetaData) => {
|
|
20055
|
+
const selectedRoomOptions = ativeOption?.rooms.map((r) => r.options.filter((o) => o.isSelected));
|
|
20056
|
+
// check if there are return flights selected
|
|
20057
|
+
if (selectedRoomOptions?.some((ro) => ro.length > 1)) {
|
|
20058
|
+
const roomOptions = ativeOption?.rooms.map((r) => r.options
|
|
20059
|
+
.filter((o) => o.isSelected)
|
|
20060
|
+
.reverse()
|
|
20061
|
+
.shift());
|
|
20062
|
+
return roomOptions?.map((ro) => flightMetaData?.find((f) => f.entryLineGuid === ro?.entryLineGuid)?.flightMetaData).filter((f) => f !== undefined) ?? [];
|
|
20063
|
+
}
|
|
20064
|
+
return undefined;
|
|
20065
|
+
});
|
|
20066
|
+
const selectApiSettings = (state) => state.apiSettings;
|
|
20067
|
+
const selectIsUnavailable = (state) => state.booking.isUnavailable;
|
|
20068
|
+
const selectRequestRooms = (state) => state.booking.package?.options.find((x) => x.isSelected)?.requestRooms;
|
|
20069
|
+
const selectOfficeId = (state) => state.booking.officeId;
|
|
20070
|
+
const selectLanguageCode = (state) => state.booking.languageCode;
|
|
20071
|
+
const selectCurrencyCode = (state) => state.booking.package?.currencyCode ?? 'EUR';
|
|
20072
|
+
const selectLanguage = (state) => state.booking.languageCode;
|
|
20073
|
+
const selectAllDynamicTranslations = (state) => state.booking.translations;
|
|
20074
|
+
const selectDynamicTranslations = createSelector([selectAllDynamicTranslations, selectLanguage], (allDynamicTranslations, language) => {
|
|
20075
|
+
return allDynamicTranslations?.find((x) => x.language == language)?.value ?? {};
|
|
20076
|
+
});
|
|
20077
|
+
// Host override files are hand-maintained, and a null only ever blanks a key the component
|
|
20078
|
+
// then reads, so treat it like a missing key (lodash merge already skips undefined).
|
|
20079
|
+
const withoutNulls = (value) => isPlainObject$1(value)
|
|
20080
|
+
? mapValues(pickBy(value, (x) => x !== null), withoutNulls)
|
|
20081
|
+
: value;
|
|
20082
|
+
// getTranslations deep-merges the overrides over a clone of the built-in translations, so a
|
|
20083
|
+
// host override of a nested group (TRAVELERS_FORM.VALIDATION, TRAVELERS_FORM.COUNTRIES)
|
|
20084
|
+
// only replaces the keys it actually contains. Merging per section one level deep dropped
|
|
20085
|
+
// every other built-in key of such a group, which made those keys resolve to undefined and
|
|
20086
|
+
// crash format() in the consuming component.
|
|
20087
|
+
const selectTranslations = createSelector([selectLanguage, selectDynamicTranslations], (language, dynamicTranslations) => getTranslations(language, isEmpty$1(dynamicTranslations) ? undefined : withoutNulls(dynamicTranslations)));
|
|
20088
|
+
const selectBookingOptions = (state) => state.booking.bookingOptions;
|
|
20089
|
+
const selectBookingType = (state) => state.booking.bookingType;
|
|
20090
|
+
const selectTagIds = (state) => state.booking.tagIds;
|
|
20091
|
+
const selectAgentAdressId = (state) => state.booking.agentAdressId;
|
|
20092
|
+
const selectProductAttributes = (state) => state.booking.productAttributes;
|
|
20093
|
+
const selectBookingAttributes = (state) => state.booking.bookingAttributes;
|
|
20094
|
+
const selectBookingNumber = (state) => state.booking.bookingNumber;
|
|
20095
|
+
const selectIsOption = (state) => state.booking.isOption;
|
|
20096
|
+
const selectBookingRooms = (state) => state.booking.bookingAttributes?.rooms;
|
|
20097
|
+
const selectBookingRemarks = (state) => state.booking.remarks;
|
|
20098
|
+
const selectVoucherCodes = (state) => state.booking.voucherCodes;
|
|
20099
|
+
const selectCalculateDeposit = (state) => state.booking.calculateDeposit;
|
|
20100
|
+
const selectShowCommission = (state) => state.booking.showCommission;
|
|
20101
|
+
const selectIsRetry = (state) => state.booking.isRetry;
|
|
20102
|
+
const selectStartDate = (state) => state.booking.package?.options.find((x) => x.isSelected)?.fromDate;
|
|
20103
|
+
const selectEndDate = (state) => state.booking.package?.options.find((x) => x.isSelected)?.toDate;
|
|
20104
|
+
const selectAgents = (state) => state.booking.agents;
|
|
20105
|
+
const selectCountries = (state) => state.booking.countries;
|
|
20106
|
+
const selectProductCode = (state) => state.booking.productAttributes?.productCode;
|
|
20107
|
+
const selectAccommodationCodes = (state) => {
|
|
20108
|
+
const accommodationCodes = [];
|
|
20109
|
+
state.booking.package?.options.forEach((o) => {
|
|
20110
|
+
o.rooms.forEach((r) => r.options.forEach((ro) => {
|
|
20111
|
+
if (!accommodationCodes.some((y) => y === ro.accommodationCode)) {
|
|
20112
|
+
accommodationCodes.push(ro.accommodationCode);
|
|
20113
|
+
}
|
|
20114
|
+
}));
|
|
20115
|
+
});
|
|
20116
|
+
return accommodationCodes;
|
|
20117
|
+
};
|
|
20118
|
+
const selectAccommodationViews = (state) => state.booking.accommodationViews;
|
|
20119
|
+
const selectMappingInfo = (state) => state.booking.package?.options.find((x) => x.isSelected)?.mappingInfo;
|
|
20120
|
+
const selectBookingQuery = (state) => {
|
|
20121
|
+
const bookingAttributes = state.booking.bookingAttributes;
|
|
20122
|
+
if (!bookingAttributes) {
|
|
20123
|
+
return undefined;
|
|
20124
|
+
}
|
|
20125
|
+
const params = {};
|
|
20126
|
+
Object.entries(bookingAttributes).forEach(([key, value]) => {
|
|
20127
|
+
if (key === 'startDate' || key === 'endDate') {
|
|
20128
|
+
value = format$2(parseISO(value), 'yyyy-MM-dd');
|
|
20129
|
+
}
|
|
20130
|
+
if (key === 'rooms') {
|
|
20131
|
+
value = JsonURL.stringify(value.map((room) => omit(room, ['children'])), {
|
|
20132
|
+
AQF: true
|
|
20133
|
+
});
|
|
20134
|
+
}
|
|
20135
|
+
if (key === 'allotmentIds' && !value.length) {
|
|
20136
|
+
value = undefined;
|
|
20137
|
+
}
|
|
20138
|
+
if (key === 'flight' && value) {
|
|
20139
|
+
var flightInfo = value;
|
|
18884
20140
|
value = JsonURL.stringify(flightInfo, { AQF: true });
|
|
18885
20141
|
}
|
|
18886
20142
|
if (value) {
|
|
@@ -18918,14 +20174,17 @@ const selectBookingAddress = createSelector(selectTravelersFormValues, selectBoo
|
|
|
18918
20174
|
const mainBooker = pax.find((x) => x.isMainBooker);
|
|
18919
20175
|
if (!mainBooker || bookingType == 'b2b')
|
|
18920
20176
|
return undefined;
|
|
20177
|
+
// The backend treats the main booker's per-pax address as authoritative, so keep the
|
|
20178
|
+
// booking-level address in sync with it rather than sending two independent values.
|
|
20179
|
+
const mainBookerAddress = mainBooker.address;
|
|
18921
20180
|
return {
|
|
18922
20181
|
name: `${mainBooker.firstName} ${mainBooker.lastName}`,
|
|
18923
|
-
street: formValues?.street,
|
|
18924
|
-
number: formValues?.houseNumber,
|
|
20182
|
+
street: mainBookerAddress?.street || formValues?.street,
|
|
20183
|
+
number: mainBookerAddress?.number || formValues?.houseNumber,
|
|
18925
20184
|
box: formValues?.box,
|
|
18926
|
-
postalCode: formValues?.zipCode,
|
|
18927
|
-
location: formValues?.place,
|
|
18928
|
-
country: formValues?.country,
|
|
20185
|
+
postalCode: mainBookerAddress?.postalCode || formValues?.zipCode,
|
|
20186
|
+
location: mainBookerAddress?.location || formValues?.place,
|
|
20187
|
+
country: mainBookerAddress?.country || formValues?.country,
|
|
18929
20188
|
mobilePhone: formValues?.phone,
|
|
18930
20189
|
email: formValues?.email
|
|
18931
20190
|
};
|
|
@@ -19001,17 +20260,11 @@ const selectBookingPackageBookRequest = createSelector(selectBookingPackageReque
|
|
|
19001
20260
|
return bookingPackageRequest;
|
|
19002
20261
|
});
|
|
19003
20262
|
const selectTravelersFirstStep = (state) => state.booking.travelersFirstStep;
|
|
19004
|
-
const buildPax = (traveler, mainBookerId) => {
|
|
19005
|
-
|
|
19006
|
-
|
|
19007
|
-
|
|
19008
|
-
|
|
19009
|
-
lastName: traveler.lastName,
|
|
19010
|
-
dateOfBirth: traveler.birthDate,
|
|
19011
|
-
age: traveler.birthDate ? null : traveler.age,
|
|
19012
|
-
isMainBooker: traveler.id == mainBookerId
|
|
19013
|
-
};
|
|
19014
|
-
};
|
|
20263
|
+
const buildPax = (traveler, mainBookerId) => applyTravelerToPax({
|
|
20264
|
+
id: traveler.id,
|
|
20265
|
+
gender: parseGender(traveler.gender),
|
|
20266
|
+
age: traveler.birthDate ? null : traveler.age
|
|
20267
|
+
}, traveler, mainBookerId);
|
|
19015
20268
|
const parseGender = (gender) => {
|
|
19016
20269
|
switch (gender) {
|
|
19017
20270
|
case 'm':
|
|
@@ -23011,718 +24264,233 @@ const Summary = () => {
|
|
|
23011
24264
|
React__default.createElement("div", { className: "form__row" },
|
|
23012
24265
|
React__default.createElement("div", { className: "form__group" },
|
|
23013
24266
|
React__default.createElement("div", { className: "info-message" },
|
|
23014
|
-
React__default.createElement(Icon, { name: "ui-tooltip", className: "icon--secondary-color" }),
|
|
23015
|
-
React__default.createElement("div", { className: "info-message__copy" },
|
|
23016
|
-
React__default.createElement("h5", null, translations.SUMMARY.VALIDATE_TITLE),
|
|
23017
|
-
settings.customValidateText ? (React__default.createElement("div", { dangerouslySetInnerHTML: {
|
|
23018
|
-
__html: settings.customValidateText
|
|
23019
|
-
} })) : (React__default.createElement(React__default.Fragment, null,
|
|
23020
|
-
React__default.createElement("p", null, settings.isOffer ? translations.SUMMARY.VALIDATE_TEXT_OFFER : translations.SUMMARY.VALIDATE_TEXT_BOOKING),
|
|
23021
|
-
settings.allowOption && React__default.createElement("p", null, translations.SUMMARY.VALIDATE_TEXT_OPTION))),
|
|
23022
|
-
checkboxes &&
|
|
23023
|
-
checkboxes.map((checkbox) => (React__default.createElement("div", { className: "checkbox", key: checkbox.id },
|
|
23024
|
-
React__default.createElement("label", { className: "checkbox__label" },
|
|
23025
|
-
React__default.createElement("input", { type: "checkbox", className: "checkbox__input", checked: checkbox.isSelected, onChange: (e) => handleCheckboxChange(checkbox.id, e.target.checked) }),
|
|
23026
|
-
React__default.createElement("span", { className: "checkbox__label-text", dangerouslySetInnerHTML: {
|
|
23027
|
-
__html: checkbox.text
|
|
23028
|
-
} }))))))))))),
|
|
23029
|
-
React__default.createElement("div", { className: "booking__navigator" },
|
|
23030
|
-
settings.skipRouter ? (React__default.createElement("button", { type: "button", title: translations.STEPS.PREVIOUS, onClick: () => goPrevious(), className: "cta cta--secondary" }, translations.STEPS.PREVIOUS)) : (React__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)),
|
|
23031
|
-
settings.allowOption && (React__default.createElement("button", { title: translations.STEPS.SUBMIT_OPTION, className: buildClassName(['cta', !userValidated && 'cta--disabled']), disabled: !userValidated, name: "option" }, translations.STEPS.SUBMIT_OPTION)),
|
|
23032
|
-
React__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))))));
|
|
23033
|
-
};
|
|
23034
|
-
|
|
23035
|
-
function isValidEmail(email) {
|
|
23036
|
-
return !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,24}$/i.test(email);
|
|
23037
|
-
}
|
|
23038
|
-
const NAME_CHARACTERS_REGEX = /^[\p{L}\s-]+$/u;
|
|
23039
|
-
function hasInvalidNameCharacters(name) {
|
|
23040
|
-
return !NAME_CHARACTERS_REGEX.test(name);
|
|
23041
|
-
}
|
|
23042
|
-
// Rejects values like a 5-digit year, which the native date input allows while typing
|
|
23043
|
-
function isValidBirthDate(birthDateText) {
|
|
23044
|
-
return isValid(parse(birthDateText, 'yyyy-MM-dd', new Date()));
|
|
23045
|
-
}
|
|
23046
|
-
function getAge(birthDateText, startDateText) {
|
|
23047
|
-
var birthDate = new Date(birthDateText);
|
|
23048
|
-
var startDate = new Date(startDateText);
|
|
23049
|
-
var age = startDate.getFullYear() - birthDate.getFullYear();
|
|
23050
|
-
var m = startDate.getMonth() - birthDate.getMonth();
|
|
23051
|
-
if (m < 0 || (m === 0 && startDate.getDate() < birthDate.getDate())) {
|
|
23052
|
-
age--;
|
|
23053
|
-
}
|
|
23054
|
-
return age;
|
|
23055
|
-
}
|
|
23056
|
-
const validateForm$1 = (values, agentRequired, bookingType, translations, formFields, mainBookerFormFields, maxChildAge = CHILD_MAX_AGE, maxBabyAge = BABY_MAX_AGE) => {
|
|
23057
|
-
const errors = {};
|
|
23058
|
-
const isFormFieldPresent = (type) => {
|
|
23059
|
-
return formFields?.some((f) => f.type === type) ?? true;
|
|
23060
|
-
};
|
|
23061
|
-
const isMainBookerFormFieldPresent = (type) => {
|
|
23062
|
-
return mainBookerFormFields?.some((f) => f.type === type) ?? true;
|
|
23063
|
-
};
|
|
23064
|
-
const formatTravelerField = (room, pax, field) => {
|
|
23065
|
-
return values.rooms.length > 1
|
|
23066
|
-
? format$1(translations.TRAVELERS_FORM.VALIDATION.TRAVELER_X_FIELD, [room, pax, field])
|
|
23067
|
-
: format$1(translations.TRAVELERS_FORM.VALIDATION.SINGLE_ROOM_TRAVELER_X_FIELD, [pax, field]);
|
|
23068
|
-
};
|
|
23069
|
-
const formatTravelerIsNoAdult = (room, pax) => {
|
|
23070
|
-
return values.rooms.length > 1
|
|
23071
|
-
? format$1(translations.TRAVELERS_FORM.VALIDATION.TRAVELER_X_IS_NO_ADULT, [room, pax])
|
|
23072
|
-
: format$1(translations.TRAVELERS_FORM.VALIDATION.SINGLE_ROOM_TRAVELER_X_IS_NO_ADULT, [pax]);
|
|
23073
|
-
};
|
|
23074
|
-
const formatTravelerIsNoChild = (room, pax) => {
|
|
23075
|
-
return values.rooms.length > 1
|
|
23076
|
-
? format$1(translations.TRAVELERS_FORM.VALIDATION.TRAVELER_X_IS_NO_CHILD, [room, pax])
|
|
23077
|
-
: format$1(translations.TRAVELERS_FORM.VALIDATION.SINGLE_ROOM_TRAVELER_X_IS_NO_CHILD, [pax]);
|
|
23078
|
-
};
|
|
23079
|
-
const formatTravelerIsNoBaby = (room, pax) => {
|
|
23080
|
-
// The message states the boundary the traveler has to stay under, so we pass
|
|
23081
|
-
// the first age that is too old rather than the inclusive maxBabyAge.
|
|
23082
|
-
const tooOldAge = maxBabyAge + 1;
|
|
23083
|
-
return values.rooms.length > 1
|
|
23084
|
-
? format$1(translations.TRAVELERS_FORM.VALIDATION.TRAVELER_X_IS_NO_BABY, [room, pax, tooOldAge])
|
|
23085
|
-
: format$1(translations.TRAVELERS_FORM.VALIDATION.SINGLE_ROOM_TRAVELER_X_IS_NO_BABY, [pax, tooOldAge]);
|
|
23086
|
-
};
|
|
23087
|
-
const formatTravelerFieldInvalidCharacters = (room, pax, field) => {
|
|
23088
|
-
return values.rooms.length > 1
|
|
23089
|
-
? format$1(translations.TRAVELERS_FORM.VALIDATION.TRAVELER_X_FIELD_INVALID_CHARACTERS, [room, pax, field])
|
|
23090
|
-
: format$1(translations.TRAVELERS_FORM.VALIDATION.SINGLE_ROOM_TRAVELER_X_FIELD_INVALID_CHARACTERS, [pax, field]);
|
|
23091
|
-
};
|
|
23092
|
-
values.rooms.forEach((r, rIndex) => r.adults.forEach((adult, index) => {
|
|
23093
|
-
if (isEmpty$1(adult.gender) && isFormFieldPresent('gender')) {
|
|
23094
|
-
set(errors, `rooms[${rIndex}].adults[${index}].gender`, formatTravelerField(rIndex + 1, index + 1, translations.TRAVELERS_FORM.GENDER));
|
|
23095
|
-
}
|
|
23096
|
-
if (isFormFieldPresent('firstName')) {
|
|
23097
|
-
if (isEmpty$1(adult.firstName)) {
|
|
23098
|
-
set(errors, `rooms[${rIndex}].adults[${index}].firstName`, formatTravelerField(rIndex + 1, index + 1, translations.TRAVELERS_FORM.FIRST_NAME));
|
|
23099
|
-
}
|
|
23100
|
-
else if (hasInvalidNameCharacters(adult.firstName)) {
|
|
23101
|
-
set(errors, `rooms[${rIndex}].adults[${index}].firstName`, formatTravelerFieldInvalidCharacters(rIndex + 1, index + 1, translations.TRAVELERS_FORM.FIRST_NAME));
|
|
23102
|
-
}
|
|
23103
|
-
}
|
|
23104
|
-
if (isFormFieldPresent('lastName')) {
|
|
23105
|
-
if (isEmpty$1(adult.lastName)) {
|
|
23106
|
-
set(errors, `rooms[${rIndex}].adults[${index}].lastName`, formatTravelerField(rIndex + 1, index + 1, translations.TRAVELERS_FORM.LAST_NAME));
|
|
23107
|
-
}
|
|
23108
|
-
else if (hasInvalidNameCharacters(adult.lastName)) {
|
|
23109
|
-
set(errors, `rooms[${rIndex}].adults[${index}].lastName`, formatTravelerFieldInvalidCharacters(rIndex + 1, index + 1, translations.TRAVELERS_FORM.LAST_NAME));
|
|
23110
|
-
}
|
|
23111
|
-
}
|
|
23112
|
-
if (isFormFieldPresent('birthDate')) {
|
|
23113
|
-
if (isEmpty$1(adult.birthDate) || !isValidBirthDate(adult.birthDate)) {
|
|
23114
|
-
set(errors, `rooms[${rIndex}].adults[${index}].birthDate`, formatTravelerField(rIndex + 1, index + 1, translations.TRAVELERS_FORM.BIRTHDATE));
|
|
23115
|
-
}
|
|
23116
|
-
else if (values.endDate) {
|
|
23117
|
-
// 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
|
|
23118
|
-
const age = getAge(adult.birthDate, values.endDate);
|
|
23119
|
-
if (age <= maxChildAge) {
|
|
23120
|
-
set(errors, `rooms[${rIndex}].adults[${index}].birthDate`, formatTravelerIsNoAdult(rIndex + 1, index + 1));
|
|
23121
|
-
}
|
|
23122
|
-
}
|
|
23123
|
-
}
|
|
23124
|
-
}));
|
|
23125
|
-
values.rooms.forEach((r, rIndex) => r.children.forEach((child, index) => {
|
|
23126
|
-
if (isEmpty$1(child.gender) && isFormFieldPresent('gender')) {
|
|
23127
|
-
set(errors, `rooms[${rIndex}].children[${index}].gender`, formatTravelerField(rIndex + 1, r.adults.length + index + 1, translations.TRAVELERS_FORM.GENDER));
|
|
23128
|
-
}
|
|
23129
|
-
if (isFormFieldPresent('firstName')) {
|
|
23130
|
-
if (isEmpty$1(child.firstName)) {
|
|
23131
|
-
set(errors, `rooms[${rIndex}].children[${index}].firstName`, formatTravelerField(rIndex + 1, r.adults.length + index + 1, translations.TRAVELERS_FORM.FIRST_NAME));
|
|
23132
|
-
}
|
|
23133
|
-
else if (hasInvalidNameCharacters(child.firstName)) {
|
|
23134
|
-
set(errors, `rooms[${rIndex}].children[${index}].firstName`, formatTravelerFieldInvalidCharacters(rIndex + 1, r.adults.length + index + 1, translations.TRAVELERS_FORM.FIRST_NAME));
|
|
23135
|
-
}
|
|
23136
|
-
}
|
|
23137
|
-
if (isFormFieldPresent('lastName')) {
|
|
23138
|
-
if (isEmpty$1(child.lastName)) {
|
|
23139
|
-
set(errors, `rooms[${rIndex}].children[${index}].lastName`, formatTravelerField(rIndex + 1, r.adults.length + index + 1, translations.TRAVELERS_FORM.LAST_NAME));
|
|
23140
|
-
}
|
|
23141
|
-
else if (hasInvalidNameCharacters(child.lastName)) {
|
|
23142
|
-
set(errors, `rooms[${rIndex}].children[${index}].lastName`, formatTravelerFieldInvalidCharacters(rIndex + 1, r.adults.length + index + 1, translations.TRAVELERS_FORM.LAST_NAME));
|
|
23143
|
-
}
|
|
23144
|
-
}
|
|
23145
|
-
if (isFormFieldPresent('birthDate')) {
|
|
23146
|
-
if (isEmpty$1(child.birthDate) || !isValidBirthDate(child.birthDate)) {
|
|
23147
|
-
set(errors, `rooms[${rIndex}].children[${index}].birthDate`, formatTravelerField(rIndex + 1, r.adults.length + index + 1, translations.TRAVELERS_FORM.BIRTHDATE));
|
|
23148
|
-
}
|
|
23149
|
-
else if (child.isBaby && values.startDate) {
|
|
23150
|
-
// A baby only has to be under the maxBabyAge on departure, so we check the startDate here instead of the endDate (TIDE-3269)
|
|
23151
|
-
const age = getAge(child.birthDate, values.startDate);
|
|
23152
|
-
if (age > maxBabyAge) {
|
|
23153
|
-
set(errors, `rooms[${rIndex}].children[${index}].birthDate`, formatTravelerIsNoBaby(rIndex + 1, r.adults.length + index + 1));
|
|
23154
|
-
}
|
|
23155
|
-
}
|
|
23156
|
-
else if (values.endDate) {
|
|
23157
|
-
// You need to be under the maxChildAge until the end of the trip, so we check the endDate here instead of the startDate.
|
|
23158
|
-
// Babies land here too when there is no startDate to check them against, so they keep at least the child age check.
|
|
23159
|
-
const age = getAge(child.birthDate, values.endDate);
|
|
23160
|
-
if (age > maxChildAge) {
|
|
23161
|
-
set(errors, `rooms[${rIndex}].children[${index}].birthDate`, formatTravelerIsNoChild(rIndex + 1, r.adults.length + index + 1));
|
|
23162
|
-
}
|
|
23163
|
-
}
|
|
23164
|
-
}
|
|
23165
|
-
}));
|
|
23166
|
-
if (values.mainBookerId < 0) {
|
|
23167
|
-
errors.mainBookerId = translations.TRAVELERS_FORM.VALIDATION.NO_MAIN_BOOKER_SELECTED;
|
|
23168
|
-
}
|
|
23169
|
-
if (bookingType != 'b2b' || mainBookerFormFields?.length) {
|
|
23170
|
-
if (isEmpty$1(values.street) && isMainBookerFormFieldPresent('street')) {
|
|
23171
|
-
errors.street = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.STREET]);
|
|
23172
|
-
}
|
|
23173
|
-
if (isEmpty$1(values.houseNumber) && isMainBookerFormFieldPresent('houseNumber')) {
|
|
23174
|
-
errors.houseNumber = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.HOUSE_NUMBER]);
|
|
23175
|
-
}
|
|
23176
|
-
if (isEmpty$1(values.zipCode) && isMainBookerFormFieldPresent('zipCode')) {
|
|
23177
|
-
errors.zipCode = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.ZIPCODE]);
|
|
23178
|
-
}
|
|
23179
|
-
if (isEmpty$1(values.place) && isMainBookerFormFieldPresent('place')) {
|
|
23180
|
-
errors.place = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.CITY]);
|
|
23181
|
-
}
|
|
23182
|
-
if (isMainBookerFormFieldPresent('email')) {
|
|
23183
|
-
if (isEmpty$1(values.email)) {
|
|
23184
|
-
errors.email = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.EMAIL]);
|
|
23185
|
-
}
|
|
23186
|
-
else if (isValidEmail(values.email)) {
|
|
23187
|
-
errors.email = translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_EMAIL_IS_INVALID;
|
|
23188
|
-
}
|
|
23189
|
-
if (isEmpty$1(values.emailConfirmation)) {
|
|
23190
|
-
errors.emailConfirmation = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.REPEAT_EMAIL]);
|
|
23191
|
-
}
|
|
23192
|
-
else if (values.emailConfirmation !== values.email) {
|
|
23193
|
-
errors.emailConfirmation = translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_EMAIL_DOES_NOT_MATCH;
|
|
23194
|
-
}
|
|
23195
|
-
}
|
|
23196
|
-
if (isEmpty$1(values.country) && isMainBookerFormFieldPresent('country')) {
|
|
23197
|
-
errors.country = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.COUNTRY]);
|
|
23198
|
-
}
|
|
23199
|
-
if (isEmpty$1(values.phone) && isMainBookerFormFieldPresent('phone')) {
|
|
23200
|
-
errors.phone = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.PHONE]);
|
|
23201
|
-
}
|
|
23202
|
-
}
|
|
23203
|
-
else {
|
|
23204
|
-
if (isEmpty$1(values.phone)) {
|
|
23205
|
-
errors.phone = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.PHONE]);
|
|
23206
|
-
}
|
|
23207
|
-
}
|
|
23208
|
-
if (agentRequired && !values.travelAgentId) {
|
|
23209
|
-
errors.travelAgentId = translations.TRAVELERS_FORM.VALIDATION.AGENT_IS_REQUIRED;
|
|
23210
|
-
}
|
|
23211
|
-
return errors;
|
|
23212
|
-
};
|
|
23213
|
-
|
|
23214
|
-
const LabeledInput = ({ name, type, required, value, defaultValue, min, max, label, placeholder, extraClassName, hasError, onChange, onBlur }) => {
|
|
23215
|
-
return (React__default.createElement("label", { className: buildClassName(['form__group', extraClassName, hasError && 'form__group--error']) },
|
|
23216
|
-
React__default.createElement("span", { className: "form__label" }, compact([label, required && '*']).join(' ')),
|
|
23217
|
-
React__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 })));
|
|
23218
|
-
};
|
|
23219
|
-
|
|
23220
|
-
const LabeledSelect = ({ name, id, required, value, defaultValue, label, extraClassName, extraDropdownClassName, hasError, options, onChange, onBlur }) => {
|
|
23221
|
-
return (React__default.createElement("label", { className: buildClassName(['form__group', extraClassName, hasError && 'form__group--error']) },
|
|
23222
|
-
React__default.createElement("span", { className: "form__label" }, compact([label, required && '*']).join(' ')),
|
|
23223
|
-
React__default.createElement("div", { className: buildClassName(['dropdown', extraDropdownClassName]) },
|
|
23224
|
-
React__default.createElement("select", { name: name, id: id, defaultValue: defaultValue, value: value, onBlur: onBlur, onChange: onChange }, options?.map((option) => (React__default.createElement("option", { key: option.key, value: option.value }, option.label)))))));
|
|
23225
|
-
};
|
|
23226
|
-
|
|
23227
|
-
const normalize = (v) => (v == null ? '' : String(v));
|
|
23228
|
-
const parseCombined = (raw, countries) => {
|
|
23229
|
-
const value = normalize(raw).trim();
|
|
23230
|
-
if (!value)
|
|
23231
|
-
return { prefix: '', number: '' };
|
|
23232
|
-
// Try to match a known prefix at the start. Prefer longest match.
|
|
23233
|
-
const sorted = [...countries].sort((a, b) => b.phonePrefix.length - a.phonePrefix.length);
|
|
23234
|
-
const hit = sorted.find((c) => value.startsWith(c.phonePrefix));
|
|
23235
|
-
if (hit) {
|
|
23236
|
-
const rest = value
|
|
23237
|
-
.slice(hit.phonePrefix.length)
|
|
23238
|
-
.trim()
|
|
23239
|
-
.replace(/^[-\s]+/, '');
|
|
23240
|
-
return { prefix: hit.phonePrefix, number: rest };
|
|
23241
|
-
}
|
|
23242
|
-
// Fallback: split on first space if it looks like a +prefix number
|
|
23243
|
-
const m = value.match(/^(\+\d{1,4})[\s-]*(.*)$/);
|
|
23244
|
-
if (m)
|
|
23245
|
-
return { prefix: m[1], number: m[2] };
|
|
23246
|
-
return { prefix: '', number: value };
|
|
23247
|
-
};
|
|
23248
|
-
const PhoneInput = ({ name, required, value, label, placeholder, extraClassName, hasError, countries, countryIso2, onChange, onBlur }) => {
|
|
23249
|
-
// Derive initial state from `value`
|
|
23250
|
-
const initialCountry = countries.find((c) => c.iso2 === countryIso2);
|
|
23251
|
-
const initial = useMemo(() => parseCombined(normalize(value), countries), [value, countries]);
|
|
23252
|
-
const [prefix, setPrefix] = useState(initial.prefix);
|
|
23253
|
-
const [number, setNumber] = useState(initial.number);
|
|
23254
|
-
// Keep state in sync if the parent changes `value`
|
|
23255
|
-
useEffect(() => {
|
|
23256
|
-
const parsed = !prefix && !number && initialCountry ? { prefix: initialCountry.phonePrefix, number: '' } : parseCombined(normalize(value), countries);
|
|
23257
|
-
if (parsed.prefix)
|
|
23258
|
-
setPrefix(parsed.prefix);
|
|
23259
|
-
if (parsed.number)
|
|
23260
|
-
setNumber(parsed.number);
|
|
23261
|
-
}, [value, countries, countryIso2]);
|
|
23262
|
-
const emitCombinedChange = (e, nextPrefix, nextNumber) => {
|
|
23263
|
-
// Combine with a space, unless the number already starts with a dash or space
|
|
23264
|
-
const combined = nextPrefix && nextNumber ? compact([nextPrefix, nextNumber]).join(' ') : null;
|
|
23265
|
-
onChange?.({
|
|
23266
|
-
...e,
|
|
23267
|
-
type: 'change',
|
|
23268
|
-
target: { name, value: combined },
|
|
23269
|
-
currentTarget: { name, value: combined }
|
|
23270
|
-
});
|
|
23271
|
-
};
|
|
23272
|
-
const handleCountryBlur = (e) => {
|
|
23273
|
-
onBlur?.(e);
|
|
23274
|
-
};
|
|
23275
|
-
const onCountryChange = (e) => {
|
|
23276
|
-
const next = e.target.value;
|
|
23277
|
-
setPrefix(next);
|
|
23278
|
-
emitCombinedChange(e, next, number);
|
|
23279
|
-
};
|
|
23280
|
-
const onPhoneChange = (e) => {
|
|
23281
|
-
const cleaned = e.target.value.replace(/[^\d\s-]/g, '');
|
|
23282
|
-
setNumber(cleaned);
|
|
23283
|
-
emitCombinedChange(e, prefix, cleaned);
|
|
23284
|
-
};
|
|
23285
|
-
const handleNumberBlur = (e) => {
|
|
23286
|
-
onBlur?.(e);
|
|
23287
|
-
};
|
|
23288
|
-
// Ensure the select shows a reasonable default when prefix is empty
|
|
23289
|
-
const selectValue = prefix || '';
|
|
23290
|
-
return (React__default.createElement("label", { className: buildClassName(['form__group', extraClassName, hasError && 'form__group--error']) },
|
|
23291
|
-
label && React__default.createElement("span", { className: "form__label" }, compact([label, required && '*']).join(' ')),
|
|
23292
|
-
React__default.createElement("div", { className: "phone-input" },
|
|
23293
|
-
React__default.createElement("div", { className: "dropdown" },
|
|
23294
|
-
React__default.createElement("select", { "aria-label": label ? `${label} – country code` : 'Country calling code', name: `${name}__country`, value: selectValue, onBlur: handleCountryBlur, onChange: onCountryChange },
|
|
23295
|
-
React__default.createElement("option", { value: "", disabled: true }),
|
|
23296
|
-
countries?.map((option) => (React__default.createElement("option", { key: option.iso2, value: option.phonePrefix },
|
|
23297
|
-
option.name,
|
|
23298
|
-
" (",
|
|
23299
|
-
option.phonePrefix,
|
|
23300
|
-
")"))))),
|
|
23301
|
-
React__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 }),
|
|
23302
|
-
React__default.createElement("input", { type: "hidden", name: name, value: compact([prefix, number]).join(' ') }))));
|
|
23303
|
-
};
|
|
23304
|
-
|
|
23305
|
-
const hasVisibleError = (formik, key) => get(formik.errors, key) && get(formik.touched, key);
|
|
23306
|
-
|
|
23307
|
-
const GenderControl = ({ translations, value, formik, name }) => {
|
|
23308
|
-
return (React__default.createElement("div", { className: buildClassName(['form__group', hasVisibleError(formik, name) && 'form__group--error']) },
|
|
23309
|
-
React__default.createElement("label", { className: "form__label" },
|
|
23310
|
-
translations.TRAVELERS_FORM.GENDER_ID,
|
|
23311
|
-
" *"),
|
|
23312
|
-
React__default.createElement("div", { className: "radiobutton-group" },
|
|
23313
|
-
React__default.createElement("div", { className: "radiobutton" },
|
|
23314
|
-
React__default.createElement("label", { className: "radiobutton__label" },
|
|
23315
|
-
React__default.createElement("input", { type: "radio", className: "radiobutton__input", name: name, onChange: formik.handleChange, onBlur: formik.handleBlur, value: "m", checked: value.gender === 'm' }),
|
|
23316
|
-
translations.TRAVELERS_FORM.MALE_GENDER)),
|
|
23317
|
-
React__default.createElement("div", { className: "radiobutton" },
|
|
23318
|
-
React__default.createElement("label", { className: "radiobutton__label" },
|
|
23319
|
-
React__default.createElement("input", { type: "radio", className: "radiobutton__input", name: name, onChange: formik.handleChange, onBlur: formik.handleBlur, value: "f", checked: value.gender === 'f' }),
|
|
23320
|
-
translations.TRAVELERS_FORM.FEMALE_GENDER)))));
|
|
23321
|
-
};
|
|
23322
|
-
|
|
23323
|
-
const TypeAheadInput = ({ name, value, placeholder, options, onChange, onSelect, onClear }) => {
|
|
23324
|
-
const [showItems, setShowItems] = useState(false);
|
|
23325
|
-
const handleChange = (e) => {
|
|
23326
|
-
if (onChange) {
|
|
23327
|
-
onChange(e.target.value);
|
|
23328
|
-
}
|
|
23329
|
-
};
|
|
23330
|
-
const handleClearClick = (e) => {
|
|
23331
|
-
if (onClear) {
|
|
23332
|
-
onClear();
|
|
23333
|
-
}
|
|
23334
|
-
setShowItems(false);
|
|
23335
|
-
};
|
|
23336
|
-
return (React__default.createElement("div", { className: "typeahead" },
|
|
23337
|
-
React__default.createElement("div", { className: "typeahead__trigger typeahead__trigger--icon" },
|
|
23338
|
-
React__default.createElement(Icon, { name: "ui-qsm-location" }),
|
|
23339
|
-
React__default.createElement("input", { type: "text", className: "form__input", name: name, value: value, onChange: handleChange, placeholder: placeholder, required: true, autoComplete: "off", onKeyDown: (e) => {
|
|
23340
|
-
if (e.key === 'Tab' && options && onSelect) {
|
|
23341
|
-
onSelect(options[0].key);
|
|
23342
|
-
setShowItems(false);
|
|
23343
|
-
}
|
|
23344
|
-
}, onFocus: () => setShowItems(true) }),
|
|
23345
|
-
React__default.createElement("button", { type: "button", className: "button button--clear", onClick: handleClearClick })),
|
|
23346
|
-
options && options.length > 0 && showItems && (React__default.createElement("div", { className: "typeahead__options" }, (options ?? []).map((option) => {
|
|
23347
|
-
const matchIndex = typeof value !== 'undefined' ? option.text.indexOf(value) : 0;
|
|
23348
|
-
const prefix = option.text.slice(0, matchIndex);
|
|
23349
|
-
const highlight = option.text.slice(matchIndex, value?.length ?? 0);
|
|
23350
|
-
const suffix = option.text.slice(matchIndex);
|
|
23351
|
-
return (React__default.createElement("div", { className: "typeahead__option", key: option.key },
|
|
23352
|
-
React__default.createElement("div", { className: "typeahead__option-header" },
|
|
23353
|
-
React__default.createElement("button", { onClick: () => {
|
|
23354
|
-
if (onSelect)
|
|
23355
|
-
onSelect(option.key);
|
|
23356
|
-
setShowItems(false);
|
|
23357
|
-
}, type: "button", className: "button button--plain" },
|
|
23358
|
-
prefix,
|
|
23359
|
-
React__default.createElement("span", { className: "typeahead__option-highlight" }, highlight),
|
|
23360
|
-
suffix))));
|
|
23361
|
-
})))));
|
|
23362
|
-
};
|
|
23363
|
-
|
|
23364
|
-
// A traveler is a baby when the age they were booked with is at or below the baby
|
|
23365
|
-
// boundary. Babies get their own label and birth date validation (TIDE-3269). Only
|
|
23366
|
-
// meaningful for the regular form: the compact form asks for an age instead of a birth
|
|
23367
|
-
// date, so it has nothing to validate and its own age input already states the age.
|
|
23368
|
-
function isBabyAge(age, maxBabyAge = BABY_MAX_AGE) {
|
|
23369
|
-
return typeof age === 'number' && age <= maxBabyAge;
|
|
23370
|
-
}
|
|
23371
|
-
// The native date input can hold values format() throws on (e.g. a 5+ digit year while typing), so never format an unvalidated birthDate
|
|
23372
|
-
function formatBirthDate$1(birthDate) {
|
|
23373
|
-
if (!birthDate)
|
|
23374
|
-
return '';
|
|
23375
|
-
const parsed = parse(birthDate, 'yyyy-MM-dd', new Date());
|
|
23376
|
-
return isValid(parsed) ? format$2(parsed, 'dd-MM-yyyy') : '';
|
|
23377
|
-
}
|
|
23378
|
-
function createTraveler(traveler, followNumber, personTranslation, isCompact, maxBabyAge) {
|
|
23379
|
-
if (isCompact) {
|
|
23380
|
-
return {
|
|
23381
|
-
id: traveler.id,
|
|
23382
|
-
firstName: personTranslation,
|
|
23383
|
-
lastName: `${followNumber.number++}`,
|
|
23384
|
-
birthDate: '',
|
|
23385
|
-
gender: '',
|
|
23386
|
-
// Keep a booked age of 0 instead of submitting and pricing the traveler as a 30 year
|
|
23387
|
-
// old, while still falling back for a missing or unusable age. `??` alone would let a
|
|
23388
|
-
// NaN (unparseable date of birth upstream) reach the age input and the booked pax.
|
|
23389
|
-
// See TIDE-3269.
|
|
23390
|
-
age: Number.isFinite(traveler.age) ? traveler.age : 30
|
|
23391
|
-
// No isBaby here on purpose: the age below is editable and nothing revalidates it
|
|
23392
|
-
// in compact mode, so a stored flag would end up contradicting the entered age.
|
|
23393
|
-
};
|
|
23394
|
-
}
|
|
23395
|
-
return {
|
|
23396
|
-
id: traveler.id,
|
|
23397
|
-
firstName: '',
|
|
23398
|
-
lastName: '',
|
|
23399
|
-
birthDate: '',
|
|
23400
|
-
gender: '',
|
|
23401
|
-
isBaby: isBabyAge(traveler.age, maxBabyAge)
|
|
23402
|
-
};
|
|
23403
|
-
}
|
|
23404
|
-
function createInitialValuesFromRooms(formRooms, startDate, endDate, agentAdressId, personTranslation, isCompact, maxBabyAge) {
|
|
23405
|
-
const followNumber = { number: 1 };
|
|
23406
|
-
const initialValues = {
|
|
23407
|
-
startDate,
|
|
23408
|
-
endDate,
|
|
23409
|
-
rooms: formRooms.map((room) => ({
|
|
23410
|
-
adults: room.adults.map((traveler) => createTraveler(traveler, followNumber, personTranslation, isCompact, maxBabyAge)),
|
|
23411
|
-
children: room.children.map((traveler) => createTraveler(traveler, followNumber, personTranslation, isCompact, maxBabyAge))
|
|
23412
|
-
})),
|
|
23413
|
-
mainBookerId: -1,
|
|
23414
|
-
street: '',
|
|
23415
|
-
houseNumber: '',
|
|
23416
|
-
box: '',
|
|
23417
|
-
zipCode: '',
|
|
23418
|
-
place: '',
|
|
23419
|
-
country: '',
|
|
23420
|
-
phone: '',
|
|
23421
|
-
email: '',
|
|
23422
|
-
emailConfirmation: '',
|
|
23423
|
-
travelAgentId: agentAdressId ?? 0,
|
|
23424
|
-
travelAgentName: ''
|
|
23425
|
-
};
|
|
23426
|
-
if (initialValues.rooms?.[0]?.adults?.[0]) {
|
|
23427
|
-
initialValues.mainBookerId = initialValues.rooms[0].adults[0].id;
|
|
23428
|
-
}
|
|
23429
|
-
return initialValues;
|
|
23430
|
-
}
|
|
23431
|
-
function createInitialValuesFromEditablePackagingEntry(editablePackagingEntry, agentAdressId) {
|
|
23432
|
-
const pax = editablePackagingEntry?.pax ?? [];
|
|
23433
|
-
const rooms = editablePackagingEntry.rooms.map((room) => {
|
|
23434
|
-
const roomPax = pax.filter((x) => room.paxIds.includes(x.id));
|
|
23435
|
-
const adults = roomPax.filter((x) => x.age >= 18);
|
|
23436
|
-
const children = roomPax.filter((x) => x.age < 18);
|
|
23437
|
-
return {
|
|
23438
|
-
adults: adults.map((roomTraveler) => {
|
|
23439
|
-
return {
|
|
23440
|
-
id: roomTraveler.id,
|
|
23441
|
-
firstName: roomTraveler.firstName ?? '',
|
|
23442
|
-
lastName: roomTraveler.lastName ?? '',
|
|
23443
|
-
birthDate: roomTraveler.dateOfBirth ? format$2(new Date(roomTraveler.dateOfBirth), 'yyyy-MM-dd') : '',
|
|
23444
|
-
gender: ''
|
|
23445
|
-
};
|
|
23446
|
-
}),
|
|
23447
|
-
children: children.map((roomTraveler) => {
|
|
23448
|
-
return {
|
|
23449
|
-
id: roomTraveler.id,
|
|
23450
|
-
firstName: roomTraveler.firstName ?? '',
|
|
23451
|
-
lastName: roomTraveler.lastName ?? '',
|
|
23452
|
-
birthDate: roomTraveler.dateOfBirth ? format$2(new Date(roomTraveler.dateOfBirth), 'yyyy-MM-dd') : '',
|
|
23453
|
-
gender: '',
|
|
23454
|
-
isBaby: isBabyAge(roomTraveler.age)
|
|
23455
|
-
};
|
|
23456
|
-
})
|
|
23457
|
-
};
|
|
23458
|
-
});
|
|
23459
|
-
const values = createInitialValuesFromRooms(rooms.map((room) => ({ adults: room.adults, children: room.children })), first(editablePackagingEntry?.lines)?.from, last(editablePackagingEntry?.lines)?.to, agentAdressId);
|
|
23460
|
-
values.rooms = rooms;
|
|
23461
|
-
values.mainBookerId = pax.find((x) => x.isMainBooker)?.id ?? rooms[0]?.adults?.[0]?.id ?? -1;
|
|
23462
|
-
const address = editablePackagingEntry?.address;
|
|
23463
|
-
values.street = address?.street ?? '';
|
|
23464
|
-
values.houseNumber = address?.houseNumber ?? '';
|
|
23465
|
-
values.box = address?.box ?? '';
|
|
23466
|
-
values.zipCode = address?.zipCode ?? '';
|
|
23467
|
-
values.place = address?.place ?? '';
|
|
23468
|
-
values.country = address?.country ?? '';
|
|
23469
|
-
values.phone = address?.phone ?? '';
|
|
23470
|
-
values.email = address?.email ?? '';
|
|
23471
|
-
values.emailConfirmation = address?.email ?? '';
|
|
23472
|
-
values.travelAgentId = address?.travelAgentId ?? agentAdressId ?? 0;
|
|
23473
|
-
return values;
|
|
24267
|
+
React__default.createElement(Icon, { name: "ui-tooltip", className: "icon--secondary-color" }),
|
|
24268
|
+
React__default.createElement("div", { className: "info-message__copy" },
|
|
24269
|
+
React__default.createElement("h5", null, translations.SUMMARY.VALIDATE_TITLE),
|
|
24270
|
+
settings.customValidateText ? (React__default.createElement("div", { dangerouslySetInnerHTML: {
|
|
24271
|
+
__html: settings.customValidateText
|
|
24272
|
+
} })) : (React__default.createElement(React__default.Fragment, null,
|
|
24273
|
+
React__default.createElement("p", null, settings.isOffer ? translations.SUMMARY.VALIDATE_TEXT_OFFER : translations.SUMMARY.VALIDATE_TEXT_BOOKING),
|
|
24274
|
+
settings.allowOption && React__default.createElement("p", null, translations.SUMMARY.VALIDATE_TEXT_OPTION))),
|
|
24275
|
+
checkboxes &&
|
|
24276
|
+
checkboxes.map((checkbox) => (React__default.createElement("div", { className: "checkbox", key: checkbox.id },
|
|
24277
|
+
React__default.createElement("label", { className: "checkbox__label" },
|
|
24278
|
+
React__default.createElement("input", { type: "checkbox", className: "checkbox__input", checked: checkbox.isSelected, onChange: (e) => handleCheckboxChange(checkbox.id, e.target.checked) }),
|
|
24279
|
+
React__default.createElement("span", { className: "checkbox__label-text", dangerouslySetInnerHTML: {
|
|
24280
|
+
__html: checkbox.text
|
|
24281
|
+
} }))))))))))),
|
|
24282
|
+
React__default.createElement("div", { className: "booking__navigator" },
|
|
24283
|
+
settings.skipRouter ? (React__default.createElement("button", { type: "button", title: translations.STEPS.PREVIOUS, onClick: () => goPrevious(), className: "cta cta--secondary" }, translations.STEPS.PREVIOUS)) : (React__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)),
|
|
24284
|
+
settings.allowOption && (React__default.createElement("button", { title: translations.STEPS.SUBMIT_OPTION, className: buildClassName(['cta', !userValidated && 'cta--disabled']), disabled: !userValidated, name: "option" }, translations.STEPS.SUBMIT_OPTION)),
|
|
24285
|
+
React__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))))));
|
|
24286
|
+
};
|
|
24287
|
+
|
|
24288
|
+
function isValidEmail(email) {
|
|
24289
|
+
return !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,24}$/i.test(email);
|
|
23474
24290
|
}
|
|
23475
|
-
|
|
23476
|
-
|
|
23477
|
-
return
|
|
23478
|
-
...editablePackagingEntry,
|
|
23479
|
-
pax: (editablePackagingEntry.pax ?? []).map((pax) => {
|
|
23480
|
-
const traveler = travelers.find((x) => x.id === pax.id);
|
|
23481
|
-
if (!traveler)
|
|
23482
|
-
return pax;
|
|
23483
|
-
return {
|
|
23484
|
-
...pax,
|
|
23485
|
-
firstName: traveler.firstName ?? '',
|
|
23486
|
-
lastName: traveler.lastName ?? '',
|
|
23487
|
-
dateOfBirth: traveler.birthDate || null,
|
|
23488
|
-
isMainBooker: traveler.id === values.mainBookerId
|
|
23489
|
-
};
|
|
23490
|
-
}),
|
|
23491
|
-
address: {
|
|
23492
|
-
...editablePackagingEntry.address,
|
|
23493
|
-
street: values.street,
|
|
23494
|
-
houseNumber: values.houseNumber,
|
|
23495
|
-
box: values.box,
|
|
23496
|
-
zipCode: values.zipCode,
|
|
23497
|
-
place: values.place,
|
|
23498
|
-
country: values.country,
|
|
23499
|
-
travelAgentId: values.travelAgentId,
|
|
23500
|
-
phone: values.phone,
|
|
23501
|
-
email: values.email
|
|
23502
|
-
}
|
|
23503
|
-
};
|
|
24291
|
+
const NAME_CHARACTERS_REGEX = /^[\p{L}\s-]+$/u;
|
|
24292
|
+
function hasInvalidNameCharacters(name) {
|
|
24293
|
+
return !NAME_CHARACTERS_REGEX.test(name);
|
|
23504
24294
|
}
|
|
23505
|
-
|
|
23506
|
-
|
|
23507
|
-
|
|
23508
|
-
|
|
23509
|
-
|
|
23510
|
-
|
|
23511
|
-
|
|
23512
|
-
|
|
23513
|
-
|
|
23514
|
-
|
|
23515
|
-
|
|
23516
|
-
|
|
23517
|
-
|
|
23518
|
-
|
|
23519
|
-
|
|
23520
|
-
|
|
23521
|
-
|
|
23522
|
-
|
|
23523
|
-
: travellersSettings?.countries?.map((country) => ({ key: country.iso2, value: country.iso2, label: country.name })) ?? [
|
|
23524
|
-
{ key: 'be', value: 'be', label: translations.TRAVELERS_FORM.COUNTRIES.BELGIUM },
|
|
23525
|
-
{ key: 'nl', value: 'nl', label: translations.TRAVELERS_FORM.COUNTRIES.NETHERLANDS },
|
|
23526
|
-
{ key: 'fr', value: 'fr', label: translations.TRAVELERS_FORM.COUNTRIES.FRANCE }
|
|
23527
|
-
])
|
|
23528
|
-
];
|
|
23529
|
-
const handleMainBookerChange = (event) => {
|
|
23530
|
-
formik.setFieldValue('mainBookerId', parseInt(event.currentTarget.value, 10));
|
|
24295
|
+
// Rejects values like a 5-digit year, which the native date input allows while typing
|
|
24296
|
+
function isValidBirthDate(birthDateText) {
|
|
24297
|
+
return isValid(parse(birthDateText, 'yyyy-MM-dd', new Date()));
|
|
24298
|
+
}
|
|
24299
|
+
function getAge(birthDateText, startDateText) {
|
|
24300
|
+
var birthDate = new Date(birthDateText);
|
|
24301
|
+
var startDate = new Date(startDateText);
|
|
24302
|
+
var age = startDate.getFullYear() - birthDate.getFullYear();
|
|
24303
|
+
var m = startDate.getMonth() - birthDate.getMonth();
|
|
24304
|
+
if (m < 0 || (m === 0 && startDate.getDate() < birthDate.getDate())) {
|
|
24305
|
+
age--;
|
|
24306
|
+
}
|
|
24307
|
+
return age;
|
|
24308
|
+
}
|
|
24309
|
+
const validateForm$1 = (values, agentRequired, bookingType, translations, formFields, mainBookerFormFields, maxChildAge = CHILD_MAX_AGE, maxBabyAge = BABY_MAX_AGE, paxFields = []) => {
|
|
24310
|
+
const errors = {};
|
|
24311
|
+
const isFormFieldPresent = (type) => {
|
|
24312
|
+
return formFields?.some((f) => f.type === type) ?? true;
|
|
23531
24313
|
};
|
|
23532
|
-
const
|
|
23533
|
-
|
|
23534
|
-
formik.setFieldValue('travelAgentName', value);
|
|
24314
|
+
const isMainBookerFormFieldPresent = (type) => {
|
|
24315
|
+
return mainBookerFormFields?.some((f) => f.type === type) ?? true;
|
|
23535
24316
|
};
|
|
23536
|
-
const
|
|
23537
|
-
|
|
23538
|
-
|
|
23539
|
-
|
|
23540
|
-
travelAgentId: Number(agent?.key),
|
|
23541
|
-
travelAgentName: agent?.value ?? ''
|
|
23542
|
-
});
|
|
23543
|
-
onBookingTypeChange?.(agentAdressId && agentAdressId !== 0 ? 'b2b' : 'b2b2c');
|
|
24317
|
+
const formatTravelerField = (room, pax, field) => {
|
|
24318
|
+
return values.rooms.length > 1
|
|
24319
|
+
? format$1(translations.TRAVELERS_FORM.VALIDATION.TRAVELER_X_FIELD, [room, pax, field])
|
|
24320
|
+
: format$1(translations.TRAVELERS_FORM.VALIDATION.SINGLE_ROOM_TRAVELER_X_FIELD, [pax, field]);
|
|
23544
24321
|
};
|
|
23545
|
-
const
|
|
23546
|
-
|
|
23547
|
-
|
|
24322
|
+
const formatTravelerIsNoAdult = (room, pax) => {
|
|
24323
|
+
return values.rooms.length > 1
|
|
24324
|
+
? format$1(translations.TRAVELERS_FORM.VALIDATION.TRAVELER_X_IS_NO_ADULT, [room, pax])
|
|
24325
|
+
: format$1(translations.TRAVELERS_FORM.VALIDATION.SINGLE_ROOM_TRAVELER_X_IS_NO_ADULT, [pax]);
|
|
23548
24326
|
};
|
|
23549
|
-
const
|
|
23550
|
-
|
|
23551
|
-
|
|
23552
|
-
|
|
23553
|
-
setFilteredAgents([]);
|
|
23554
|
-
}
|
|
24327
|
+
const formatTravelerIsNoChild = (room, pax) => {
|
|
24328
|
+
return values.rooms.length > 1
|
|
24329
|
+
? format$1(translations.TRAVELERS_FORM.VALIDATION.TRAVELER_X_IS_NO_CHILD, [room, pax])
|
|
24330
|
+
: format$1(translations.TRAVELERS_FORM.VALIDATION.SINGLE_ROOM_TRAVELER_X_IS_NO_CHILD, [pax]);
|
|
23555
24331
|
};
|
|
23556
|
-
const
|
|
23557
|
-
|
|
23558
|
-
|
|
23559
|
-
|
|
23560
|
-
|
|
24332
|
+
const formatTravelerIsNoBaby = (room, pax) => {
|
|
24333
|
+
// The message states the boundary the traveler has to stay under, so we pass
|
|
24334
|
+
// the first age that is too old rather than the inclusive maxBabyAge.
|
|
24335
|
+
const tooOldAge = maxBabyAge + 1;
|
|
24336
|
+
return values.rooms.length > 1
|
|
24337
|
+
? format$1(translations.TRAVELERS_FORM.VALIDATION.TRAVELER_X_IS_NO_BABY, [room, pax, tooOldAge])
|
|
24338
|
+
: format$1(translations.TRAVELERS_FORM.VALIDATION.SINGLE_ROOM_TRAVELER_X_IS_NO_BABY, [pax, tooOldAge]);
|
|
23561
24339
|
};
|
|
23562
|
-
const
|
|
23563
|
-
|
|
23564
|
-
|
|
23565
|
-
|
|
23566
|
-
return;
|
|
23567
|
-
adults.splice(travelerIndex, 1);
|
|
23568
|
-
rooms[roomIndex] = { ...rooms[roomIndex], adults };
|
|
23569
|
-
formik.setFieldValue('rooms', rooms);
|
|
24340
|
+
const formatTravelerFieldInvalidCharacters = (room, pax, field) => {
|
|
24341
|
+
return values.rooms.length > 1
|
|
24342
|
+
? format$1(translations.TRAVELERS_FORM.VALIDATION.TRAVELER_X_FIELD_INVALID_CHARACTERS, [room, pax, field])
|
|
24343
|
+
: format$1(translations.TRAVELERS_FORM.VALIDATION.SINGLE_ROOM_TRAVELER_X_FIELD_INVALID_CHARACTERS, [pax, field]);
|
|
23570
24344
|
};
|
|
23571
|
-
const
|
|
23572
|
-
const
|
|
23573
|
-
|
|
23574
|
-
|
|
24345
|
+
const validateRequiredCustomFields = (traveler, path, room, pax) => {
|
|
24346
|
+
const isMainBooker = traveler.id === values.mainBookerId;
|
|
24347
|
+
const requiredFields = getApplicablePaxCustomFields(paxFields, isMainBooker).filter((f) => f.isRequired);
|
|
24348
|
+
requiredFields.forEach((field) => {
|
|
24349
|
+
const fieldIndex = traveler.customFields?.findIndex((c) => c.customFieldConfigurationId === field.customFieldConfigurationId) ?? -1;
|
|
24350
|
+
if (fieldIndex < 0)
|
|
24351
|
+
return;
|
|
24352
|
+
if (isEmpty$1(traveler.customFields[fieldIndex].value)) {
|
|
24353
|
+
set(errors, `${path}.customFields[${fieldIndex}].value`, formatTravelerField(room, pax, field.fieldName));
|
|
24354
|
+
}
|
|
24355
|
+
});
|
|
23575
24356
|
};
|
|
23576
|
-
|
|
23577
|
-
|
|
23578
|
-
|
|
23579
|
-
|
|
24357
|
+
// Excludes the 4 core fields, which are already validated unconditionally below.
|
|
24358
|
+
const validateRequiredBuiltInFields = (traveler, path, room, pax) => {
|
|
24359
|
+
const isMainBooker = traveler.id === values.mainBookerId;
|
|
24360
|
+
const requiredFields = getApplicableBuiltInPaxFields(paxFields, isMainBooker).filter((f) => f.isRequired && !CORE_FIELD_NAMES.has(f.fieldName));
|
|
24361
|
+
requiredFields.forEach((field) => {
|
|
24362
|
+
const fieldPath = BUILT_IN_FIELD_PATHS[field.fieldName];
|
|
24363
|
+
if (!fieldPath)
|
|
24364
|
+
return;
|
|
24365
|
+
if (isEmpty$1(get(traveler, fieldPath))) {
|
|
24366
|
+
set(errors, `${path}.${fieldPath}`, formatTravelerField(room, pax, field.fieldName));
|
|
24367
|
+
}
|
|
24368
|
+
});
|
|
23580
24369
|
};
|
|
23581
|
-
|
|
23582
|
-
|
|
23583
|
-
translations.TRAVELERS_FORM.
|
|
23584
|
-
" *"),
|
|
23585
|
-
React__default.createElement("div", { className: "radiobutton-group" }, [
|
|
23586
|
-
['m', translations.TRAVELERS_FORM.MALE_GENDER],
|
|
23587
|
-
['f', translations.TRAVELERS_FORM.FEMALE_GENDER]
|
|
23588
|
-
].map(([gender, label]) => (React__default.createElement("div", { className: "radiobutton", key: gender },
|
|
23589
|
-
React__default.createElement("label", { className: "radiobutton__label" },
|
|
23590
|
-
React__default.createElement("input", { type: "radio", className: "radiobutton__input", name: name, onChange: formik.handleChange, onBlur: formik.handleBlur, value: gender, checked: value.gender === gender }),
|
|
23591
|
-
label)))))));
|
|
23592
|
-
const getControl = (type, value, name) => {
|
|
23593
|
-
switch (type) {
|
|
23594
|
-
case 'gender':
|
|
23595
|
-
return React__default.createElement(GenderControl, { translations: translations, value: value, formik: formik, name: name });
|
|
23596
|
-
case 'firstName':
|
|
23597
|
-
return (React__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 }));
|
|
23598
|
-
case 'lastName':
|
|
23599
|
-
return (React__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 }));
|
|
23600
|
-
case 'birthDate':
|
|
23601
|
-
return (React__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 }));
|
|
23602
|
-
case 'country':
|
|
23603
|
-
return (React__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 }));
|
|
23604
|
-
case 'phone':
|
|
23605
|
-
return (React__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 }));
|
|
23606
|
-
case 'email':
|
|
23607
|
-
return (React__default.createElement(React__default.Fragment, null,
|
|
23608
|
-
React__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 }),
|
|
23609
|
-
React__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 })));
|
|
23610
|
-
case 'street':
|
|
23611
|
-
return (React__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 }));
|
|
23612
|
-
case 'houseNumber':
|
|
23613
|
-
return (React__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 }));
|
|
23614
|
-
case 'box':
|
|
23615
|
-
return (React__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 }));
|
|
23616
|
-
case 'zipCode':
|
|
23617
|
-
return (React__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 }));
|
|
23618
|
-
case 'place':
|
|
23619
|
-
return (React__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 }));
|
|
23620
|
-
default:
|
|
23621
|
-
return null;
|
|
24370
|
+
values.rooms.forEach((r, rIndex) => r.adults.forEach((adult, index) => {
|
|
24371
|
+
if (isEmpty$1(adult.gender) && isFormFieldPresent('gender')) {
|
|
24372
|
+
set(errors, `rooms[${rIndex}].adults[${index}].gender`, formatTravelerField(rIndex + 1, index + 1, translations.TRAVELERS_FORM.GENDER));
|
|
23622
24373
|
}
|
|
23623
|
-
|
|
23624
|
-
|
|
23625
|
-
|
|
23626
|
-
|
|
23627
|
-
|
|
23628
|
-
|
|
23629
|
-
|
|
23630
|
-
room.children.length === 1 && ` ${translations.TRAVELERS_FORM.CHILD}`,
|
|
23631
|
-
room.children.length > 1 && ` ${translations.TRAVELERS_FORM.CHILDREN}`
|
|
23632
|
-
]).join('');
|
|
23633
|
-
const renderTravelerFields = (travelerValues, namePrefix, isAdult, roomIndex, travelerIndex) => {
|
|
23634
|
-
if (useCompactForm) {
|
|
23635
|
-
return (React__default.createElement("div", { className: "form__row" },
|
|
23636
|
-
React__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 })));
|
|
24374
|
+
if (isFormFieldPresent('firstName')) {
|
|
24375
|
+
if (isEmpty$1(adult.firstName)) {
|
|
24376
|
+
set(errors, `rooms[${rIndex}].adults[${index}].firstName`, formatTravelerField(rIndex + 1, index + 1, translations.TRAVELERS_FORM.FIRST_NAME));
|
|
24377
|
+
}
|
|
24378
|
+
else if (hasInvalidNameCharacters(adult.firstName)) {
|
|
24379
|
+
set(errors, `rooms[${rIndex}].adults[${index}].firstName`, formatTravelerFieldInvalidCharacters(rIndex + 1, index + 1, translations.TRAVELERS_FORM.FIRST_NAME));
|
|
24380
|
+
}
|
|
23637
24381
|
}
|
|
23638
|
-
if (
|
|
23639
|
-
|
|
24382
|
+
if (isFormFieldPresent('lastName')) {
|
|
24383
|
+
if (isEmpty$1(adult.lastName)) {
|
|
24384
|
+
set(errors, `rooms[${rIndex}].adults[${index}].lastName`, formatTravelerField(rIndex + 1, index + 1, translations.TRAVELERS_FORM.LAST_NAME));
|
|
24385
|
+
}
|
|
24386
|
+
else if (hasInvalidNameCharacters(adult.lastName)) {
|
|
24387
|
+
set(errors, `rooms[${rIndex}].adults[${index}].lastName`, formatTravelerFieldInvalidCharacters(rIndex + 1, index + 1, translations.TRAVELERS_FORM.LAST_NAME));
|
|
24388
|
+
}
|
|
23640
24389
|
}
|
|
23641
|
-
|
|
23642
|
-
|
|
23643
|
-
|
|
23644
|
-
|
|
23645
|
-
|
|
23646
|
-
|
|
23647
|
-
|
|
23648
|
-
|
|
23649
|
-
|
|
23650
|
-
|
|
23651
|
-
|
|
23652
|
-
|
|
23653
|
-
|
|
23654
|
-
|
|
23655
|
-
|
|
23656
|
-
|
|
23657
|
-
|
|
23658
|
-
|
|
23659
|
-
|
|
23660
|
-
|
|
23661
|
-
|
|
23662
|
-
|
|
23663
|
-
|
|
23664
|
-
|
|
23665
|
-
|
|
23666
|
-
|
|
23667
|
-
|
|
23668
|
-
|
|
23669
|
-
|
|
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
|
-
|
|
24390
|
+
if (isFormFieldPresent('birthDate')) {
|
|
24391
|
+
if (isEmpty$1(adult.birthDate) || !isValidBirthDate(adult.birthDate)) {
|
|
24392
|
+
set(errors, `rooms[${rIndex}].adults[${index}].birthDate`, formatTravelerField(rIndex + 1, index + 1, translations.TRAVELERS_FORM.BIRTHDATE));
|
|
24393
|
+
}
|
|
24394
|
+
else if (values.endDate) {
|
|
24395
|
+
// 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
|
|
24396
|
+
const age = getAge(adult.birthDate, values.endDate);
|
|
24397
|
+
if (age <= maxChildAge) {
|
|
24398
|
+
set(errors, `rooms[${rIndex}].adults[${index}].birthDate`, formatTravelerIsNoAdult(rIndex + 1, index + 1));
|
|
24399
|
+
}
|
|
24400
|
+
}
|
|
24401
|
+
}
|
|
24402
|
+
validateRequiredCustomFields(adult, `rooms[${rIndex}].adults[${index}]`, rIndex + 1, index + 1);
|
|
24403
|
+
validateRequiredBuiltInFields(adult, `rooms[${rIndex}].adults[${index}]`, rIndex + 1, index + 1);
|
|
24404
|
+
}));
|
|
24405
|
+
values.rooms.forEach((r, rIndex) => r.children.forEach((child, index) => {
|
|
24406
|
+
if (isEmpty$1(child.gender) && isFormFieldPresent('gender')) {
|
|
24407
|
+
set(errors, `rooms[${rIndex}].children[${index}].gender`, formatTravelerField(rIndex + 1, r.adults.length + index + 1, translations.TRAVELERS_FORM.GENDER));
|
|
24408
|
+
}
|
|
24409
|
+
if (isFormFieldPresent('firstName')) {
|
|
24410
|
+
if (isEmpty$1(child.firstName)) {
|
|
24411
|
+
set(errors, `rooms[${rIndex}].children[${index}].firstName`, formatTravelerField(rIndex + 1, r.adults.length + index + 1, translations.TRAVELERS_FORM.FIRST_NAME));
|
|
24412
|
+
}
|
|
24413
|
+
else if (hasInvalidNameCharacters(child.firstName)) {
|
|
24414
|
+
set(errors, `rooms[${rIndex}].children[${index}].firstName`, formatTravelerFieldInvalidCharacters(rIndex + 1, r.adults.length + index + 1, translations.TRAVELERS_FORM.FIRST_NAME));
|
|
24415
|
+
}
|
|
24416
|
+
}
|
|
24417
|
+
if (isFormFieldPresent('lastName')) {
|
|
24418
|
+
if (isEmpty$1(child.lastName)) {
|
|
24419
|
+
set(errors, `rooms[${rIndex}].children[${index}].lastName`, formatTravelerField(rIndex + 1, r.adults.length + index + 1, translations.TRAVELERS_FORM.LAST_NAME));
|
|
24420
|
+
}
|
|
24421
|
+
else if (hasInvalidNameCharacters(child.lastName)) {
|
|
24422
|
+
set(errors, `rooms[${rIndex}].children[${index}].lastName`, formatTravelerFieldInvalidCharacters(rIndex + 1, r.adults.length + index + 1, translations.TRAVELERS_FORM.LAST_NAME));
|
|
24423
|
+
}
|
|
24424
|
+
}
|
|
24425
|
+
if (isFormFieldPresent('birthDate')) {
|
|
24426
|
+
if (isEmpty$1(child.birthDate) || !isValidBirthDate(child.birthDate)) {
|
|
24427
|
+
set(errors, `rooms[${rIndex}].children[${index}].birthDate`, formatTravelerField(rIndex + 1, r.adults.length + index + 1, translations.TRAVELERS_FORM.BIRTHDATE));
|
|
24428
|
+
}
|
|
24429
|
+
else if (child.isBaby && values.startDate) {
|
|
24430
|
+
// A baby only has to be under the maxBabyAge on departure, so we check the startDate here instead of the endDate (TIDE-3269)
|
|
24431
|
+
const age = getAge(child.birthDate, values.startDate);
|
|
24432
|
+
if (age > maxBabyAge) {
|
|
24433
|
+
set(errors, `rooms[${rIndex}].children[${index}].birthDate`, formatTravelerIsNoBaby(rIndex + 1, r.adults.length + index + 1));
|
|
24434
|
+
}
|
|
24435
|
+
}
|
|
24436
|
+
else if (values.endDate) {
|
|
24437
|
+
// You need to be under the maxChildAge until the end of the trip, so we check the endDate here instead of the startDate.
|
|
24438
|
+
// Babies land here too when there is no startDate to check them against, so they keep at least the child age check.
|
|
24439
|
+
const age = getAge(child.birthDate, values.endDate);
|
|
24440
|
+
if (age > maxChildAge) {
|
|
24441
|
+
set(errors, `rooms[${rIndex}].children[${index}].birthDate`, formatTravelerIsNoChild(rIndex + 1, r.adults.length + index + 1));
|
|
24442
|
+
}
|
|
24443
|
+
}
|
|
24444
|
+
}
|
|
24445
|
+
validateRequiredCustomFields(child, `rooms[${rIndex}].children[${index}]`, rIndex + 1, r.adults.length + index + 1);
|
|
24446
|
+
validateRequiredBuiltInFields(child, `rooms[${rIndex}].children[${index}]`, rIndex + 1, r.adults.length + index + 1);
|
|
24447
|
+
}));
|
|
24448
|
+
if (values.mainBookerId < 0) {
|
|
24449
|
+
errors.mainBookerId = translations.TRAVELERS_FORM.VALIDATION.NO_MAIN_BOOKER_SELECTED;
|
|
24450
|
+
}
|
|
24451
|
+
if (bookingType != 'b2b' || mainBookerFormFields?.length) {
|
|
24452
|
+
if (isEmpty$1(values.street) && isMainBookerFormFieldPresent('street')) {
|
|
24453
|
+
errors.street = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.STREET]);
|
|
24454
|
+
}
|
|
24455
|
+
if (isEmpty$1(values.houseNumber) && isMainBookerFormFieldPresent('houseNumber')) {
|
|
24456
|
+
errors.houseNumber = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.HOUSE_NUMBER]);
|
|
24457
|
+
}
|
|
24458
|
+
if (isEmpty$1(values.zipCode) && isMainBookerFormFieldPresent('zipCode')) {
|
|
24459
|
+
errors.zipCode = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.ZIPCODE]);
|
|
24460
|
+
}
|
|
24461
|
+
if (isEmpty$1(values.place) && isMainBookerFormFieldPresent('place')) {
|
|
24462
|
+
errors.place = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.CITY]);
|
|
24463
|
+
}
|
|
24464
|
+
if (isMainBookerFormFieldPresent('email')) {
|
|
24465
|
+
if (isEmpty$1(values.email)) {
|
|
24466
|
+
errors.email = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.EMAIL]);
|
|
24467
|
+
}
|
|
24468
|
+
else if (isValidEmail(values.email)) {
|
|
24469
|
+
errors.email = translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_EMAIL_IS_INVALID;
|
|
24470
|
+
}
|
|
24471
|
+
if (isEmpty$1(values.emailConfirmation)) {
|
|
24472
|
+
errors.emailConfirmation = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.REPEAT_EMAIL]);
|
|
24473
|
+
}
|
|
24474
|
+
else if (values.emailConfirmation !== values.email) {
|
|
24475
|
+
errors.emailConfirmation = translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_EMAIL_DOES_NOT_MATCH;
|
|
24476
|
+
}
|
|
24477
|
+
}
|
|
24478
|
+
if (isEmpty$1(values.country) && isMainBookerFormFieldPresent('country')) {
|
|
24479
|
+
errors.country = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.COUNTRY]);
|
|
24480
|
+
}
|
|
24481
|
+
if (isEmpty$1(values.phone) && isMainBookerFormFieldPresent('phone')) {
|
|
24482
|
+
errors.phone = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.PHONE]);
|
|
24483
|
+
}
|
|
24484
|
+
}
|
|
24485
|
+
else {
|
|
24486
|
+
if (isEmpty$1(values.phone)) {
|
|
24487
|
+
errors.phone = format$1(translations.TRAVELERS_FORM.VALIDATION.MAIN_BOOKER_FIELD, [translations.TRAVELERS_FORM.PHONE]);
|
|
24488
|
+
}
|
|
24489
|
+
}
|
|
24490
|
+
if (agentRequired && !values.travelAgentId) {
|
|
24491
|
+
errors.travelAgentId = translations.TRAVELERS_FORM.VALIDATION.AGENT_IS_REQUIRED;
|
|
24492
|
+
}
|
|
24493
|
+
return errors;
|
|
23726
24494
|
};
|
|
23727
24495
|
|
|
23728
24496
|
const TravelersForm = () => {
|
|
@@ -23743,12 +24511,13 @@ const TravelersForm = () => {
|
|
|
23743
24511
|
const bookingAttributes = useSelector(selectBookingAttributes);
|
|
23744
24512
|
const hasMounted = useSelector(selectHasMounted);
|
|
23745
24513
|
const countries = useSelector(selectCountries);
|
|
24514
|
+
const paxFields = useSelector(selectPaxFields);
|
|
23746
24515
|
const inFlight = useRef(false);
|
|
23747
24516
|
const pendingRefetch = useRef(false);
|
|
23748
24517
|
const useCompactForm = !!settings.travellers.compactForm && !!settings.agentAdressId;
|
|
23749
24518
|
const showAllCountries = !!settings.travellers.showAllCountries;
|
|
23750
24519
|
const initialValues = useSelector(selectTravelersFormValues) ??
|
|
23751
|
-
createInitialValuesFromRooms(formRooms, startDate, endDate, agentAdressId, translations.TRAVELERS_FORM.PERSON, useCompactForm, settings.maxBabyAge);
|
|
24520
|
+
createInitialValuesFromRooms(formRooms, startDate, endDate, agentAdressId, translations.TRAVELERS_FORM.PERSON, useCompactForm, settings.maxBabyAge, paxFields);
|
|
23752
24521
|
const [showAgentSelection, setShowAgentSelection] = useState(!settings.agentAdressId && !settings.hideAgentSelection);
|
|
23753
24522
|
const formik = useFormik({
|
|
23754
24523
|
initialValues,
|
|
@@ -23758,7 +24527,7 @@ const TravelersForm = () => {
|
|
|
23758
24527
|
// The travel dates are captured into initialValues once and then persisted in
|
|
23759
24528
|
// redux, so validating against the values would use the dates of whichever
|
|
23760
24529
|
// package option was selected first. Age checks need the current ones.
|
|
23761
|
-
{ ...values, startDate, endDate }, settings.agentRequired, bookingType, translations, settings.travellers.formFields, settings.travellers.mainBookerFormFields, settings.maxChildAge, settings.maxBabyAge),
|
|
24530
|
+
{ ...values, startDate, endDate }, settings.agentRequired, bookingType, translations, settings.travellers.formFields, settings.travellers.mainBookerFormFields, settings.maxChildAge, settings.maxBabyAge, paxFields),
|
|
23762
24531
|
onSubmit: (values) => {
|
|
23763
24532
|
dispatch(setFormValues(values));
|
|
23764
24533
|
if (useCompactForm) {
|
|
@@ -23841,7 +24610,7 @@ const TravelersForm = () => {
|
|
|
23841
24610
|
dispatch(setFormValues(formValues));
|
|
23842
24611
|
setShowAgentSelection(false);
|
|
23843
24612
|
}, [agents, settings.affiliateSlug]);
|
|
23844
|
-
return (React__default.createElement(SharedTravelersForm, { formik: formik, 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.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.createElement(Link, { to: `${!settings.skipBasePathInRouting ? settings.basePath : ''}${settings.options.pathSuffix}?${bookingQueryString}`, title: translations.STEPS.PREVIOUS, className: "cta cta--secondary" }, translations.STEPS.PREVIOUS)) : null }));
|
|
24613
|
+
return (React__default.createElement(SharedTravelersForm, { formik: formik, 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.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.createElement(Link, { to: `${!settings.skipBasePathInRouting ? settings.basePath : ''}${settings.options.pathSuffix}?${bookingQueryString}`, title: translations.STEPS.PREVIOUS, className: "cta cta--secondary" }, translations.STEPS.PREVIOUS)) : null }));
|
|
23845
24614
|
};
|
|
23846
24615
|
|
|
23847
24616
|
const Booking = ({ productCode, productName, thumbnailUrl }) => {
|
|
@@ -27913,7 +28682,12 @@ const mapPackagingPaxToBookingPax = (pax, fallbackId, referenceDate) => ({
|
|
|
27913
28682
|
dateOfBirth: pax?.dateOfBirth ?? undefined,
|
|
27914
28683
|
age: pax?.dateOfBirth ? getAgeAtReferenceDate(pax.dateOfBirth, referenceDate) : undefined,
|
|
27915
28684
|
isMainBooker: pax?.isMainBooker,
|
|
27916
|
-
email: ''
|
|
28685
|
+
email: '',
|
|
28686
|
+
weight: null,
|
|
28687
|
+
weightType: build.WeightType.kilograms,
|
|
28688
|
+
height: null,
|
|
28689
|
+
heightType: build.HeightType.centimeters,
|
|
28690
|
+
customFields: []
|
|
27917
28691
|
});
|
|
27918
28692
|
const getRequestRoomsFromPackagingEntry = (entry) => {
|
|
27919
28693
|
const accommodationLines = (entry.lines ?? []).filter((line) => line.serviceType === ACCOMMODATION_SERVICE_TYPE && line.pax?.length > 0);
|
|
@@ -27983,6 +28757,22 @@ const getPackagingRequestRoomsFromBookingRooms = (rooms) => {
|
|
|
27983
28757
|
});
|
|
27984
28758
|
};
|
|
27985
28759
|
|
|
28760
|
+
// Administrative service lines (fees, insurances, reductions, ...) live on the entry but say
|
|
28761
|
+
// nothing about the journey itself, so the itinerary never draws them.
|
|
28762
|
+
const HIDDEN_ITINERARY_SERVICE_TYPES = new Set([
|
|
28763
|
+
build.ServiceType.insurance,
|
|
28764
|
+
build.ServiceType.extraService,
|
|
28765
|
+
build.ServiceType.serviceFee,
|
|
28766
|
+
build.ServiceType.gift,
|
|
28767
|
+
build.ServiceType.reduction,
|
|
28768
|
+
build.ServiceType.fee,
|
|
28769
|
+
build.ServiceType.visum,
|
|
28770
|
+
build.ServiceType.voucher,
|
|
28771
|
+
build.ServiceType.specialServices
|
|
28772
|
+
]);
|
|
28773
|
+
const isMeaningfulItineraryLine = (line) => {
|
|
28774
|
+
return !HIDDEN_ITINERARY_SERVICE_TYPES.has(line.serviceType ?? -1);
|
|
28775
|
+
};
|
|
27986
28776
|
const getFlightLines = (flight) => {
|
|
27987
28777
|
return flight?.flightInformation?.flightLines ?? [];
|
|
27988
28778
|
};
|
|
@@ -28437,6 +29227,8 @@ const applyResultToLine = (line, result, roomIndex = 0) => {
|
|
|
28437
29227
|
isChanged: true
|
|
28438
29228
|
};
|
|
28439
29229
|
};
|
|
29230
|
+
const buildNodeSelectionLines = (item, result, bookedMode) => bookedMode && matchesCode(result, item.node.code ?? '') ? item.lines : applyResultToLines(item.lines, result);
|
|
29231
|
+
const buildFlightSelectionLine = (item, flight, bookedMode) => bookedMode && flight.outwardGuid === item.line.guid ? item.line : applyFlightToLine(item.line, flight);
|
|
28440
29232
|
const toDateOnlyUtcString = (value) => {
|
|
28441
29233
|
const date = new Date(value);
|
|
28442
29234
|
return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate())).toISOString();
|
|
@@ -28494,11 +29286,21 @@ const buildTrajectFlightRequest = ({ node, line }, context, pax) => ({
|
|
|
28494
29286
|
* mapper gave them. This swaps in the guid of whatever each line resolved onto, leaving the
|
|
28495
29287
|
* lines the traveller added themselves alone — those were built from a search result and already
|
|
28496
29288
|
* carry the right one.
|
|
29289
|
+
*
|
|
29290
|
+
* A booked entry (the traject was rebuilt from a magic-link entry) is the other way around: its
|
|
29291
|
+
* lines live in the dossier under their own guids, and only a line the traveller actually swapped
|
|
29292
|
+
* (isChanged) exists in the search cache. Swapping an untouched line's guid would orphan it —
|
|
29293
|
+
* the backend drops the dossier line and finds nothing to add — so those keep theirs.
|
|
28497
29294
|
*/
|
|
28498
29295
|
const withCachedEntryLineGuids = (entry, trajectEntry, nodeAvailability, flightAvailability) => {
|
|
28499
29296
|
const cachedGuids = new Map();
|
|
29297
|
+
const bookedMode = !!trajectEntry.entry.magicLink;
|
|
29298
|
+
const editableByGuid = new Map((entry.lines ?? []).map((line) => [line.guid, line]));
|
|
29299
|
+
const keepsOwnGuid = (guid) => bookedMode && !editableByGuid.get(guid)?.isChanged;
|
|
28500
29300
|
getTrajectNodesWithLines(trajectEntry).forEach((item) => {
|
|
28501
29301
|
if (isTrajectFlightNode(item.node)) {
|
|
29302
|
+
if (keepsOwnGuid(item.line.guid))
|
|
29303
|
+
return;
|
|
28502
29304
|
// A flight search caches the outward line under the guid it reports as outwardGuid.
|
|
28503
29305
|
const guid = flightAvailability[item.line.guid]?.selectedGuid;
|
|
28504
29306
|
if (guid)
|
|
@@ -28511,6 +29313,8 @@ const withCachedEntryLineGuids = (entry, trajectEntry, nodeAvailability, flightA
|
|
|
28511
29313
|
return;
|
|
28512
29314
|
// Room i took option i when the result was stamped on, so it takes option i here too.
|
|
28513
29315
|
item.lines.forEach((line, index) => {
|
|
29316
|
+
if (keepsOwnGuid(line.guid))
|
|
29317
|
+
return;
|
|
28514
29318
|
const guid = getOptionForRoom(result, index)?.guid;
|
|
28515
29319
|
if (guid)
|
|
28516
29320
|
cachedGuids.set(line.guid, guid);
|
|
@@ -28574,7 +29378,7 @@ const TrajectItinerary = ({ trajectEntry, packagingEntry, translations }) => {
|
|
|
28574
29378
|
return { node, lines };
|
|
28575
29379
|
});
|
|
28576
29380
|
const claimed = new Set(nodes.flatMap((item) => item.lines.map((line) => line.guid)));
|
|
28577
|
-
const extras = (packagingEntry.lines ?? []).filter((line) => !claimed.has(line.guid));
|
|
29381
|
+
const extras = (packagingEntry.lines ?? []).filter((line) => !claimed.has(line.guid)).filter(isMeaningfulItineraryLine);
|
|
28578
29382
|
if (!extras.length)
|
|
28579
29383
|
return nodes;
|
|
28580
29384
|
const dayOrderOf = (date) => trajectEntry.traject.days.find((day) => isSameDay(new Date(day.date), new Date(date)))?.order ??
|
|
@@ -28751,6 +29555,195 @@ const TrajectItinerary = ({ trajectEntry, packagingEntry, translations }) => {
|
|
|
28751
29555
|
})));
|
|
28752
29556
|
};
|
|
28753
29557
|
|
|
29558
|
+
/**
|
|
29559
|
+
* A booked entry opened through its magic link knows nothing about the traject it may once have
|
|
29560
|
+
* been composed from — the dossier only keeps the lines. To give it the same day-by-day services
|
|
29561
|
+
* screen a traject gets, the traject structure is rebuilt from those lines: every booked service
|
|
29562
|
+
* becomes a node whose one "alternative" is the booked product itself, and the days span the trip.
|
|
29563
|
+
*
|
|
29564
|
+
* The synthesized response keeps the entry untouched (magic link included), which is also how the
|
|
29565
|
+
* rest of the component tells this mode apart from a real traject: see isBookedEntryTraject.
|
|
29566
|
+
*/
|
|
29567
|
+
const isBookedEntryTraject = (trajectEntry) => !!trajectEntry?.entry?.magicLink;
|
|
29568
|
+
const NODE_SERVICE_TYPES = new Set([ACCOMMODATION_SERVICE_TYPE, EXCURSION_SERVICE_TYPE, FLIGHT_SERVICE_TYPE]);
|
|
29569
|
+
/**
|
|
29570
|
+
* A room or option line joins its parent's node; a flight line never does — an outward and its
|
|
29571
|
+
* return are separate legs of the trip even though the return points at the outward as parent.
|
|
29572
|
+
*/
|
|
29573
|
+
const groupNodeLines = (lines) => {
|
|
29574
|
+
const groups = [];
|
|
29575
|
+
const groupByGuid = new Map();
|
|
29576
|
+
lines.forEach((line) => {
|
|
29577
|
+
const parentGroup = line.serviceType !== FLIGHT_SERVICE_TYPE && line.parentGuid ? groupByGuid.get(line.parentGuid) : undefined;
|
|
29578
|
+
if (parentGroup) {
|
|
29579
|
+
parentGroup.push(line);
|
|
29580
|
+
return;
|
|
29581
|
+
}
|
|
29582
|
+
const group = [line];
|
|
29583
|
+
groups.push(group);
|
|
29584
|
+
groupByGuid.set(line.guid, group);
|
|
29585
|
+
});
|
|
29586
|
+
return groups;
|
|
29587
|
+
};
|
|
29588
|
+
const buildBookedTrajectEntry = (entry) => {
|
|
29589
|
+
const lines = (entry.lines ?? []).filter((line) => NODE_SERVICE_TYPES.has(line.serviceType));
|
|
29590
|
+
const froms = lines.map((line) => new Date(line.from).getTime());
|
|
29591
|
+
const tos = lines.map((line) => new Date(line.to).getTime());
|
|
29592
|
+
const start = froms.length ? new Date(Math.min(...froms)) : new Date();
|
|
29593
|
+
const end = tos.length ? new Date(Math.max(...tos)) : start;
|
|
29594
|
+
const dayCount = Math.max(differenceInCalendarDays(end, start), 0) + 1;
|
|
29595
|
+
const days = Array.from({ length: dayCount }, (_, order) => ({
|
|
29596
|
+
order,
|
|
29597
|
+
name: '',
|
|
29598
|
+
date: toDateOnlyString(addDays(start, order))
|
|
29599
|
+
}));
|
|
29600
|
+
const nodes = groupNodeLines(lines).map((groupLines, index) => {
|
|
29601
|
+
const line = groupLines[0];
|
|
29602
|
+
const flightLines = line.flightInformation?.flightLines ?? [];
|
|
29603
|
+
return {
|
|
29604
|
+
lineGuid: line.guid,
|
|
29605
|
+
lineGuids: groupLines.map((x) => x.guid),
|
|
29606
|
+
nodeId: index + 1,
|
|
29607
|
+
dayOrder: Math.max(differenceInCalendarDays(new Date(line.from), start), 0),
|
|
29608
|
+
order: line.order,
|
|
29609
|
+
name: line.productName,
|
|
29610
|
+
serviceType: line.serviceType,
|
|
29611
|
+
trajectNodeType: REGULAR_TRAJECT_NODE_TYPE,
|
|
29612
|
+
requirementType: 0,
|
|
29613
|
+
contentSource: 0,
|
|
29614
|
+
code: line.productCode,
|
|
29615
|
+
preferredAccommodationCode: line.productCode,
|
|
29616
|
+
preferredAccommodationName: line.productName,
|
|
29617
|
+
alternatives: [],
|
|
29618
|
+
departureAirportCode: first(flightLines)?.departureAirportCode ?? null,
|
|
29619
|
+
arrivalAirportCode: last(flightLines)?.arrivalAirportCode ?? null,
|
|
29620
|
+
duration: 0,
|
|
29621
|
+
durationType: 0,
|
|
29622
|
+
from: line.from,
|
|
29623
|
+
to: line.to
|
|
29624
|
+
};
|
|
29625
|
+
});
|
|
29626
|
+
return {
|
|
29627
|
+
entry,
|
|
29628
|
+
traject: {
|
|
29629
|
+
id: 0,
|
|
29630
|
+
name: entry.dossierNumber ?? '',
|
|
29631
|
+
imageUrl: null,
|
|
29632
|
+
startDate: days[0]?.date ?? toDateOnlyString(start),
|
|
29633
|
+
endDate: last(days)?.date ?? toDateOnlyString(end),
|
|
29634
|
+
durationInDays: days.length,
|
|
29635
|
+
includeGroupHotelAlternatives: false,
|
|
29636
|
+
days
|
|
29637
|
+
},
|
|
29638
|
+
nodes
|
|
29639
|
+
};
|
|
29640
|
+
};
|
|
29641
|
+
/**
|
|
29642
|
+
* The card for a booked service when no search result carries it any more (sold out, delisted).
|
|
29643
|
+
* Rendered from the line, so the traveller still sees what they booked; price 0 makes the card
|
|
29644
|
+
* leave its price out — the paid price lives in the price details, not on the line.
|
|
29645
|
+
*/
|
|
29646
|
+
const buildBookedNodeResult = (node, lines) => {
|
|
29647
|
+
const line = lines[0];
|
|
29648
|
+
return {
|
|
29649
|
+
code: line.productCode,
|
|
29650
|
+
name: line.productName,
|
|
29651
|
+
serviceType: line.serviceType,
|
|
29652
|
+
countryId: line.country?.id ?? null,
|
|
29653
|
+
countryName: line.country?.name ?? '',
|
|
29654
|
+
regionId: line.region?.id ?? null,
|
|
29655
|
+
regionName: line.region?.name ?? '',
|
|
29656
|
+
oordId: line.oord?.id ?? null,
|
|
29657
|
+
oordName: line.oord?.name ?? '',
|
|
29658
|
+
locationId: line.location?.id ?? null,
|
|
29659
|
+
locationName: line.location?.name ?? '',
|
|
29660
|
+
latitude: line.latitude ?? null,
|
|
29661
|
+
longitude: line.longitude ?? null,
|
|
29662
|
+
price: 0,
|
|
29663
|
+
averagePricePerPerson: 0,
|
|
29664
|
+
originalPrice: 0,
|
|
29665
|
+
originalAveragePricePerPerson: 0,
|
|
29666
|
+
currencyCode: 'EUR',
|
|
29667
|
+
fromDate: line.from,
|
|
29668
|
+
toDate: line.to,
|
|
29669
|
+
contents: '',
|
|
29670
|
+
rooms: lines.map((roomLine) => ({
|
|
29671
|
+
options: [
|
|
29672
|
+
{
|
|
29673
|
+
price: 0,
|
|
29674
|
+
originalPrice: 0,
|
|
29675
|
+
accommodationCode: roomLine.accommodationCode ?? '',
|
|
29676
|
+
accommodationName: roomLine.accommodationName ?? '',
|
|
29677
|
+
regimeCode: roomLine.regimeCode ?? '',
|
|
29678
|
+
regimeName: roomLine.regimeName ?? '',
|
|
29679
|
+
isSelected: true,
|
|
29680
|
+
guid: roomLine.guid,
|
|
29681
|
+
paxIds: (roomLine.pax ?? []).map((pax) => pax.paxId)
|
|
29682
|
+
}
|
|
29683
|
+
]
|
|
29684
|
+
}))
|
|
29685
|
+
};
|
|
29686
|
+
};
|
|
29687
|
+
/**
|
|
29688
|
+
* The flight search API serializes these as ISO strings, whatever the type declaration says, and
|
|
29689
|
+
* the store must stay serializable — so the synthesized segments carry the same string form.
|
|
29690
|
+
*/
|
|
29691
|
+
const toDateTime = (date, time) => `${toDateOnlyString(date)}T${time || '00:00:00'}`;
|
|
29692
|
+
/** The booked flight as a flight result, so the card that shows it is the card the search results use. */
|
|
29693
|
+
const buildBookedFlightResult = (line) => {
|
|
29694
|
+
const flightLines = line.flightInformation?.flightLines ?? [];
|
|
29695
|
+
const segments = flightLines.map((flightLine, index) => ({
|
|
29696
|
+
order: index,
|
|
29697
|
+
flightNumber: flightLine.flightNumber,
|
|
29698
|
+
operatingFlightNumber: flightLine.operatingFlightNumber ?? flightLine.flightNumber,
|
|
29699
|
+
marketingAirlineCode: flightLine.airlineCode,
|
|
29700
|
+
marketingAirlineName: flightLine.airlineDescription,
|
|
29701
|
+
operatingAirlineCode: flightLine.operatingAirlineCode ?? flightLine.airlineCode,
|
|
29702
|
+
operatingAirlineName: flightLine.operatingAirlineDescription ?? flightLine.airlineDescription,
|
|
29703
|
+
departureAirportCode: flightLine.departureAirportCode,
|
|
29704
|
+
departureAirportName: flightLine.departureAirportDescription,
|
|
29705
|
+
arrivalAirportCode: flightLine.arrivalAirportCode,
|
|
29706
|
+
arrivalAirportName: flightLine.arrivalAirportDescription,
|
|
29707
|
+
departureDateTime: toDateTime(flightLine.departureDate, flightLine.departureTime),
|
|
29708
|
+
arrivalDateTime: toDateTime(flightLine.arrivalDate, flightLine.arrivalTime),
|
|
29709
|
+
bookingClassCode: '',
|
|
29710
|
+
travelClass: 0,
|
|
29711
|
+
durationInTicks: flightLine.durationInTicks ?? 0
|
|
29712
|
+
}));
|
|
29713
|
+
const firstSegment = first(segments);
|
|
29714
|
+
const lastSegment = last(segments);
|
|
29715
|
+
// .NET ticks are 100ns, so a millisecond is 10 000 of them.
|
|
29716
|
+
const durationInTicks = firstSegment && lastSegment ? (new Date(lastSegment.arrivalDateTime).getTime() - new Date(firstSegment.departureDateTime).getTime()) * 10_000 : 0;
|
|
29717
|
+
return {
|
|
29718
|
+
code: line.productCode,
|
|
29719
|
+
name: line.productName,
|
|
29720
|
+
serviceType: line.serviceType,
|
|
29721
|
+
countryName: '',
|
|
29722
|
+
regionName: '',
|
|
29723
|
+
oordName: '',
|
|
29724
|
+
locationName: '',
|
|
29725
|
+
price: 0,
|
|
29726
|
+
averagePricePerPerson: 0,
|
|
29727
|
+
originalPrice: 0,
|
|
29728
|
+
originalAveragePricePerPerson: 0,
|
|
29729
|
+
currencyCode: 'EUR',
|
|
29730
|
+
fromDate: line.from,
|
|
29731
|
+
toDate: line.to,
|
|
29732
|
+
flightRouteId: line.guid,
|
|
29733
|
+
airlineCode: firstSegment?.marketingAirlineCode ?? '',
|
|
29734
|
+
airlineName: firstSegment?.marketingAirlineName ?? '',
|
|
29735
|
+
isLuggageIncluded: false,
|
|
29736
|
+
luggageInfo: '',
|
|
29737
|
+
outwardGuid: line.guid,
|
|
29738
|
+
outward: { code: line.productCode, fareType: 0, durationInTicks, segments },
|
|
29739
|
+
returnGuid: '',
|
|
29740
|
+
return: { code: '', fareType: 0, durationInTicks: 0, segments: [] },
|
|
29741
|
+
source: 0,
|
|
29742
|
+
provider: '',
|
|
29743
|
+
paxes: []
|
|
29744
|
+
};
|
|
29745
|
+
};
|
|
29746
|
+
|
|
28754
29747
|
const getSegmentIcon = (segment) => {
|
|
28755
29748
|
switch (segment.serviceType) {
|
|
28756
29749
|
case 3:
|
|
@@ -28790,14 +29783,14 @@ const SERVICE_TYPE_PRIORITY = {
|
|
|
28790
29783
|
const getServiceTypePriority = (serviceType) => {
|
|
28791
29784
|
return SERVICE_TYPE_PRIORITY[serviceType ?? -1] ?? 2;
|
|
28792
29785
|
};
|
|
28793
|
-
const Itinerary = ({ isOpen, handleSetIsOpen, isLoading, onEditAccommodation }) => {
|
|
29786
|
+
const Itinerary = ({ isOpen, handleSetIsOpen, isLoading, onEditAccommodation, onSaveEntry }) => {
|
|
28794
29787
|
const context = useContext(SearchResultsConfigurationContext);
|
|
28795
29788
|
const translations = getTranslations(context?.languageCode ?? 'en-GB');
|
|
28796
29789
|
const { editablePackagingEntry, priceDetails, trajectItineraryView } = useSelector((state) => state.searchResults);
|
|
28797
29790
|
const dispatch = useDispatch();
|
|
28798
29791
|
const packagingEntry = editablePackagingEntry ?? context?.packagingEntry;
|
|
28799
29792
|
const sortedLines = useMemo(() => {
|
|
28800
|
-
return
|
|
29793
|
+
return (packagingEntry?.lines ?? []).filter(isMeaningfulItineraryLine).sort((a, b) => {
|
|
28801
29794
|
const dateA = getDateOnlyTime(a.from);
|
|
28802
29795
|
const dateB = getDateOnlyTime(b.from);
|
|
28803
29796
|
if (dateA !== dateB) {
|
|
@@ -28868,7 +29861,7 @@ const Itinerary = ({ isOpen, handleSetIsOpen, isLoading, onEditAccommodation })
|
|
|
28868
29861
|
" ",
|
|
28869
29862
|
translations.SRP.TOTAL,
|
|
28870
29863
|
")"))),
|
|
28871
|
-
React__default.createElement("button", { className: "cta", onClick: handleConfirm }, translations.QSM.CONFIRM),
|
|
29864
|
+
isBookedEntryTraject(context?.trajectEntry) ? (React__default.createElement("button", { className: "cta", onClick: onSaveEntry, disabled: !(packagingEntry?.lines ?? []).some((line) => line.isChanged) }, translations.SHARED.SAVE)) : (React__default.createElement("button", { className: "cta", onClick: handleConfirm }, translations.QSM.CONFIRM)),
|
|
28872
29865
|
!!context?.trajectEntry && (React__default.createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => handleTrajectView(!trajectItineraryView) }, trajectItineraryView ? translations.SRP.SHOW_SERVICES : translations.SRP.VIEW_FULL_ITINERARY))))),
|
|
28873
29866
|
React__default.createElement("div", { className: "search__filter__itinerary" },
|
|
28874
29867
|
React__default.createElement("p", null, translations.SRP.DAY_BY_DAY),
|
|
@@ -30925,7 +31918,7 @@ const selectSelectedCombinationFlight = createSelector([selectPackagingFlightRes
|
|
|
30925
31918
|
return (packagingFlightResults.find((flight) => getFlightKey(flight.outward.segments) === selectedOutwardKey && getFlightKey(flight.return.segments) === selectedReturnKey) ?? null);
|
|
30926
31919
|
});
|
|
30927
31920
|
|
|
30928
|
-
const IndependentFlightOption = ({ item, onSelect, guid, selectedGuid, isOutward, showSelectedState, currentSelectedPrice, price }) => {
|
|
31921
|
+
const IndependentFlightOption = ({ item, onSelect, guid, selectedGuid, isOutward, showSelectedState, currentSelectedPrice, price, selectLabel, selectedLabel }) => {
|
|
30929
31922
|
const context = useContext(SearchResultsConfigurationContext);
|
|
30930
31923
|
const language = context?.languageCode ?? 'en-GB';
|
|
30931
31924
|
const translations = getTranslations(language);
|
|
@@ -30950,7 +31943,7 @@ const IndependentFlightOption = ({ item, onSelect, guid, selectedGuid, isOutward
|
|
|
30950
31943
|
priceDifference > 0 ? '+' : '-',
|
|
30951
31944
|
"\u00A0",
|
|
30952
31945
|
formatPrice$3(Math.abs(priceDifference), 'EUR', context?.languageCode ?? 'en-GB'))))),
|
|
30953
|
-
React__default.createElement("button", { type: "button", className: `cta ${selectedGuid === guid ? 'cta--selected' : 'cta--select'}`, onClick: () => onSelect?.() }, selectedGuid === guid ?
|
|
31946
|
+
React__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)))),
|
|
30954
31947
|
React__default.createElement("div", { className: "flight__flight" },
|
|
30955
31948
|
React__default.createElement("div", { className: "flight__flight__container" },
|
|
30956
31949
|
React__default.createElement("div", { className: "flight__flight__wrapper" },
|
|
@@ -31584,7 +32577,7 @@ const TrajectNodeCard = ({ node, result, isSelected, languageCode, translations,
|
|
|
31584
32577
|
const image = candidate?.imageUrl ?? null;
|
|
31585
32578
|
const description = toPlainText(candidate?.description);
|
|
31586
32579
|
const title = candidate?.name ?? result.name;
|
|
31587
|
-
const priceBlock = (React__default.createElement("div", { className: "search__result-card__price__wrapper" },
|
|
32580
|
+
const priceBlock = result.price > 0 && (React__default.createElement("div", { className: "search__result-card__price__wrapper" },
|
|
31588
32581
|
React__default.createElement("span", { className: "search__result-card__price__label" }, translations?.SHARED.TOTAL_PRICE),
|
|
31589
32582
|
React__default.createElement("span", { className: "search__result-card__price" }, price)));
|
|
31590
32583
|
const canEditOptions = isSelected && !!onEditOptions && (result.rooms ?? []).some((room) => (room.options ?? []).length > 1);
|
|
@@ -31637,6 +32630,7 @@ const TrajectResultsFlyIn = ({ isLoading }) => {
|
|
|
31637
32630
|
const translations = getTranslations(context?.languageCode ?? 'en-GB');
|
|
31638
32631
|
const { trajectNodeAvailability, trajectEditLineGuid } = useSelector((state) => state.searchResults);
|
|
31639
32632
|
const trajectEntry = context?.trajectEntry;
|
|
32633
|
+
const bookedMode = isBookedEntryTraject(trajectEntry);
|
|
31640
32634
|
const item = useMemo(() => {
|
|
31641
32635
|
if (!trajectEntry || !trajectEditLineGuid)
|
|
31642
32636
|
return null;
|
|
@@ -31656,7 +32650,7 @@ const TrajectResultsFlyIn = ({ isLoading }) => {
|
|
|
31656
32650
|
if (!result)
|
|
31657
32651
|
return;
|
|
31658
32652
|
dispatch(setTrajectNodeSelection({ lineGuid: item.line.guid, code }));
|
|
31659
|
-
|
|
32653
|
+
buildNodeSelectionLines(item, result, bookedMode).forEach((line) => dispatch(updateEditableEntryLine(line)));
|
|
31660
32654
|
dispatch(setTrajectEditLineGuid(null));
|
|
31661
32655
|
dispatch(setFlyInIsOpen(false));
|
|
31662
32656
|
};
|
|
@@ -31669,7 +32663,7 @@ const TrajectResultsFlyIn = ({ isLoading }) => {
|
|
|
31669
32663
|
ordered.length,
|
|
31670
32664
|
"\u00A0",
|
|
31671
32665
|
translations.SRP.TOTAL_RESULTS_LABEL)),
|
|
31672
|
-
ordered.length === 0 ? (React__default.createElement("div", { className: "no-results" }, translations.SRP.NO_RESULTS)) : (React__default.createElement("div", { className: "search__results__cards" }, ordered.map((result) => (React__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 })))))));
|
|
32666
|
+
ordered.length === 0 ? (React__default.createElement("div", { className: "no-results" }, translations.SRP.NO_RESULTS)) : (React__default.createElement("div", { className: "search__results__cards" }, ordered.map((result) => (React__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 })))))));
|
|
31673
32667
|
};
|
|
31674
32668
|
|
|
31675
32669
|
/**
|
|
@@ -31731,6 +32725,7 @@ const TrajectDayActivitiesFlyIn = ({ isLoading }) => {
|
|
|
31731
32725
|
const translations = getTranslations(context?.languageCode ?? 'en-GB');
|
|
31732
32726
|
const { trajectNodeAvailability, trajectDaySuggestions, trajectActivitiesDayOrder, editablePackagingEntry } = useSelector((state) => state.searchResults);
|
|
31733
32727
|
const trajectEntry = context?.trajectEntry;
|
|
32728
|
+
const bookedMode = isBookedEntryTraject(trajectEntry);
|
|
31734
32729
|
const items = useMemo(() => {
|
|
31735
32730
|
if (!trajectEntry || trajectActivitiesDayOrder === null)
|
|
31736
32731
|
return [];
|
|
@@ -31763,7 +32758,7 @@ const TrajectDayActivitiesFlyIn = ({ isLoading }) => {
|
|
|
31763
32758
|
if (!item)
|
|
31764
32759
|
return;
|
|
31765
32760
|
dispatch(setTrajectNodeSelection({ lineGuid: item.line.guid, code: result.code }));
|
|
31766
|
-
|
|
32761
|
+
buildNodeSelectionLines(item, result, bookedMode).forEach((line) => dispatch(updateEditableEntryLine(line)));
|
|
31767
32762
|
dispatch(setTrajectActivitiesDayOrder(null));
|
|
31768
32763
|
dispatch(setFlyInIsOpen(false));
|
|
31769
32764
|
};
|
|
@@ -31779,7 +32774,8 @@ const TrajectDayActivitiesFlyIn = ({ isLoading }) => {
|
|
|
31779
32774
|
cards.length === 0 ? (React__default.createElement("div", { className: "no-results" }, translations.SRP.NO_RESULTS)) : (React__default.createElement("div", { className: "search__results__cards" }, cards.map((card) => {
|
|
31780
32775
|
const { result, item } = card;
|
|
31781
32776
|
const isSelected = item ? trajectNodeAvailability[item.line.guid]?.selectedCode === result.code : getAddedLines(result).length > 0;
|
|
31782
|
-
|
|
32777
|
+
const isBookedCard = !!item && bookedMode && matchesCode(result, item.node.code ?? '');
|
|
32778
|
+
return (React__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 }));
|
|
31783
32779
|
})))));
|
|
31784
32780
|
};
|
|
31785
32781
|
|
|
@@ -31855,7 +32851,7 @@ const FlyIn = ({ srpType, isOpen, setIsOpen, className = '', onPanelRef, details
|
|
|
31855
32851
|
dispatch(setBookPackagingEntry(true));
|
|
31856
32852
|
}
|
|
31857
32853
|
};
|
|
31858
|
-
return (React__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' : ''}
|
|
32854
|
+
return (React__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' : ''}
|
|
31859
32855
|
${flyInType === 'flight-outward-results' || flyInType === 'flight-return-results' || flyInType === 'traject-flight-results' ? 'flyin--flight' : ''}` },
|
|
31860
32856
|
React__default.createElement("div", { className: `flyin__panel ${isOpen ? 'flyin__panel--active' : ''}`, ref: panelRef },
|
|
31861
32857
|
React__default.createElement("div", { className: "flyin__content" },
|
|
@@ -33304,7 +34300,12 @@ const getRequestRoomsFromPackagingSegments = (entry, segments) => {
|
|
|
33304
34300
|
dateOfBirth: paxSource?.dateOfBirth ?? undefined,
|
|
33305
34301
|
age: paxSource?.dateOfBirth ? undefined : 30,
|
|
33306
34302
|
isMainBooker: paxSource?.isMainBooker,
|
|
33307
|
-
email: ''
|
|
34303
|
+
email: '',
|
|
34304
|
+
weight: null,
|
|
34305
|
+
weightType: build.WeightType.kilograms,
|
|
34306
|
+
height: null,
|
|
34307
|
+
heightType: build.HeightType.centimeters,
|
|
34308
|
+
customFields: []
|
|
33308
34309
|
};
|
|
33309
34310
|
if (!groupedByRoom.has(roomIndexWithinLine)) {
|
|
33310
34311
|
groupedByRoom.set(roomIndexWithinLine, []);
|
|
@@ -33384,6 +34385,10 @@ const getUniqueItems = (items = []) => {
|
|
|
33384
34385
|
return true;
|
|
33385
34386
|
});
|
|
33386
34387
|
};
|
|
34388
|
+
const isMeaningfulItem = (item, lines) => {
|
|
34389
|
+
const line = lines.find((l) => l.guid === item.itemGuid);
|
|
34390
|
+
return !line || isMeaningfulItineraryLine(line);
|
|
34391
|
+
};
|
|
33387
34392
|
const findAccommodationForDay = (nodes, currentDay) => {
|
|
33388
34393
|
for (const node of nodes ?? []) {
|
|
33389
34394
|
const { startDay } = getNodeDayRange(node);
|
|
@@ -33443,20 +34448,20 @@ const buildFlightHtml = (line) => {
|
|
|
33443
34448
|
*/
|
|
33444
34449
|
const buildFallbackHtml = (item, line) => {
|
|
33445
34450
|
const details = [line?.accommodationName, line?.regimeName].filter(Boolean).join(' — ');
|
|
33446
|
-
return `
|
|
33447
|
-
<div class="itinerary-item__fallback">
|
|
33448
|
-
<h3 class="itinerary-item__fallback-title">${escapeHtml(item.title)}</h3>
|
|
33449
|
-
${line ? buildFlightHtml(line) : ''}
|
|
33450
|
-
${details ? `<p class="itinerary-item__fallback-details">${escapeHtml(details)}</p>` : ''}
|
|
33451
|
-
</div>
|
|
34451
|
+
return `
|
|
34452
|
+
<div class="itinerary-item__fallback">
|
|
34453
|
+
<h3 class="itinerary-item__fallback-title">${escapeHtml(item.title)}</h3>
|
|
34454
|
+
${line ? buildFlightHtml(line) : ''}
|
|
34455
|
+
${details ? `<p class="itinerary-item__fallback-details">${escapeHtml(details)}</p>` : ''}
|
|
34456
|
+
</div>
|
|
33452
34457
|
`;
|
|
33453
34458
|
};
|
|
33454
34459
|
const buildItineraryHtml = (itinerary, translations, lines) => {
|
|
33455
34460
|
if (!itinerary) {
|
|
33456
|
-
return `
|
|
33457
|
-
<div class="itinerary-shell">
|
|
33458
|
-
<div class="itinerary-empty">Geen reisroute beschikbaar.</div>
|
|
33459
|
-
</div>
|
|
34461
|
+
return `
|
|
34462
|
+
<div class="itinerary-shell">
|
|
34463
|
+
<div class="itinerary-empty">Geen reisroute beschikbaar.</div>
|
|
34464
|
+
</div>
|
|
33460
34465
|
`;
|
|
33461
34466
|
}
|
|
33462
34467
|
const allNodes = itinerary.nodes ?? [];
|
|
@@ -33465,75 +34470,76 @@ const buildItineraryHtml = (itinerary, translations, lines) => {
|
|
|
33465
34470
|
const { startDay } = getNodeDayRange(node);
|
|
33466
34471
|
const day = node.startDate ? format$2(node.startDate, 'd') : null;
|
|
33467
34472
|
const month = node.startDate ? format$2(node.startDate, 'MMM') : null;
|
|
33468
|
-
const uniqueItems = getUniqueItems(node.items ?? []);
|
|
34473
|
+
const uniqueItems = getUniqueItems(node.items ?? []).filter((item) => isMeaningfulItem(item, lines));
|
|
33469
34474
|
const hasItems = uniqueItems.length > 0;
|
|
33470
34475
|
const hasAccommodationInCurrentNode = uniqueItems.some(isAccommodationItem);
|
|
33471
34476
|
const activeAccommodation = findAccommodationForDay(allNodes, startDay);
|
|
33472
34477
|
const accommodationBanner = !hasAccommodationInCurrentNode && activeAccommodation
|
|
33473
|
-
? `
|
|
33474
|
-
<div class="itinerary-node__accommodation-banner">
|
|
33475
|
-
Accommodatie voor deze dag: <strong>${escapeHtml(getAccommodationName(activeAccommodation))}</strong>
|
|
33476
|
-
</div>
|
|
34478
|
+
? `
|
|
34479
|
+
<div class="itinerary-node__accommodation-banner">
|
|
34480
|
+
Accommodatie voor deze dag: <strong>${escapeHtml(getAccommodationName(activeAccommodation))}</strong>
|
|
34481
|
+
</div>
|
|
33477
34482
|
`
|
|
33478
34483
|
: '';
|
|
33479
34484
|
const itemsHtml = hasItems
|
|
33480
34485
|
? uniqueItems
|
|
33481
|
-
.map((item) => `
|
|
33482
|
-
<article class="itinerary-item" data-template="${escapeHtml(item.templateName)}">
|
|
34486
|
+
.map((item) => `
|
|
34487
|
+
<article class="itinerary-item" data-template="${escapeHtml(item.templateName)}">
|
|
33483
34488
|
${hasVisibleContent(item.contents)
|
|
33484
34489
|
? item.contents
|
|
33485
|
-
: buildFallbackHtml(item, lines.find((line) => line.guid === item.itemGuid))}
|
|
33486
|
-
</article>
|
|
34490
|
+
: buildFallbackHtml(item, lines.find((line) => line.guid === item.itemGuid))}
|
|
34491
|
+
</article>
|
|
33487
34492
|
`)
|
|
33488
34493
|
.join('')
|
|
33489
34494
|
: `<div class="itinerary-node__empty">${translations.ITINERARY.NO_ITEMS}</div>`;
|
|
33490
|
-
return `
|
|
33491
|
-
<section class="itinerary-node">
|
|
33492
|
-
<header class="itinerary-node__header">
|
|
33493
|
-
|
|
33494
|
-
<div class="itinerary-node__day">
|
|
33495
|
-
<p class="itinerary-node__day-day">${day}</p>
|
|
33496
|
-
<p class="itinerary-node__day-month">${month}</p>
|
|
33497
|
-
</div>
|
|
33498
|
-
|
|
33499
|
-
<div>
|
|
33500
|
-
<h2 class="itinerary-node__title">${escapeHtml(node.title)}</h2>
|
|
33501
|
-
</div>
|
|
33502
|
-
</header>
|
|
33503
|
-
|
|
33504
|
-
<div class="itinerary-node__content">
|
|
33505
|
-
${accommodationBanner}
|
|
33506
|
-
${itemsHtml}
|
|
33507
|
-
</div>
|
|
33508
|
-
</section>
|
|
34495
|
+
return `
|
|
34496
|
+
<section class="itinerary-node">
|
|
34497
|
+
<header class="itinerary-node__header">
|
|
34498
|
+
|
|
34499
|
+
<div class="itinerary-node__day">
|
|
34500
|
+
<p class="itinerary-node__day-day">${day}</p>
|
|
34501
|
+
<p class="itinerary-node__day-month">${month}</p>
|
|
34502
|
+
</div>
|
|
34503
|
+
|
|
34504
|
+
<div>
|
|
34505
|
+
<h2 class="itinerary-node__title">${escapeHtml(node.title)}</h2>
|
|
34506
|
+
</div>
|
|
34507
|
+
</header>
|
|
34508
|
+
|
|
34509
|
+
<div class="itinerary-node__content">
|
|
34510
|
+
${accommodationBanner}
|
|
34511
|
+
${itemsHtml}
|
|
34512
|
+
</div>
|
|
34513
|
+
</section>
|
|
33509
34514
|
`;
|
|
33510
34515
|
})
|
|
33511
34516
|
.join('');
|
|
33512
|
-
const
|
|
33513
|
-
|
|
33514
|
-
|
|
33515
|
-
|
|
33516
|
-
|
|
33517
|
-
|
|
33518
|
-
|
|
34517
|
+
const defaultItems = getUniqueItems(itinerary.defaultItems ?? []).filter((item) => isMeaningfulItem(item, lines));
|
|
34518
|
+
const defaultItemsHtml = defaultItems.length > 0
|
|
34519
|
+
? `
|
|
34520
|
+
<section class="itinerary-default-items">
|
|
34521
|
+
<h2 class="itinerary-default-items__title">Algemene info</h2>
|
|
34522
|
+
${defaultItems
|
|
34523
|
+
.map((item) => `
|
|
34524
|
+
<article class="itinerary-item" data-template="${escapeHtml(item.templateName)}">
|
|
33519
34525
|
${hasVisibleContent(item.contents)
|
|
33520
34526
|
? item.contents
|
|
33521
|
-
: buildFallbackHtml(item, lines.find((line) => line.guid === item.itemGuid))}
|
|
33522
|
-
</article>
|
|
34527
|
+
: buildFallbackHtml(item, lines.find((line) => line.guid === item.itemGuid))}
|
|
34528
|
+
</article>
|
|
33523
34529
|
`)
|
|
33524
|
-
.join('')}
|
|
33525
|
-
</section>
|
|
34530
|
+
.join('')}
|
|
34531
|
+
</section>
|
|
33526
34532
|
`
|
|
33527
34533
|
: '';
|
|
33528
|
-
return `
|
|
33529
|
-
<div class="itinerary-shell">
|
|
33530
|
-
${itinerary.title ? `<header class="itinerary-shell__header"><h1 class="itinerary-shell__title">${escapeHtml(itinerary.title)}</h1></header>` : ''}
|
|
33531
|
-
|
|
33532
|
-
<div class="itinerary-shell__content">
|
|
33533
|
-
${nodesHtml}
|
|
33534
|
-
${defaultItemsHtml}
|
|
33535
|
-
</div>
|
|
33536
|
-
</div>
|
|
34534
|
+
return `
|
|
34535
|
+
<div class="itinerary-shell">
|
|
34536
|
+
${itinerary.title ? `<header class="itinerary-shell__header"><h1 class="itinerary-shell__title">${escapeHtml(itinerary.title)}</h1></header>` : ''}
|
|
34537
|
+
|
|
34538
|
+
<div class="itinerary-shell__content">
|
|
34539
|
+
${nodesHtml}
|
|
34540
|
+
${defaultItemsHtml}
|
|
34541
|
+
</div>
|
|
34542
|
+
</div>
|
|
33537
34543
|
`;
|
|
33538
34544
|
};
|
|
33539
34545
|
const FullItinerary = ({ isLoading }) => {
|
|
@@ -33555,170 +34561,170 @@ const FullItinerary = ({ isLoading }) => {
|
|
|
33555
34561
|
}
|
|
33556
34562
|
const shadowRoot = host.shadowRoot ?? host.attachShadow({ mode: 'open' });
|
|
33557
34563
|
shadowRootRef.current = shadowRoot;
|
|
33558
|
-
shadowRoot.innerHTML = `
|
|
33559
|
-
<style>
|
|
33560
|
-
:host {
|
|
33561
|
-
all: initial;
|
|
33562
|
-
display: block;
|
|
33563
|
-
width: 100%;
|
|
33564
|
-
}
|
|
33565
|
-
|
|
33566
|
-
*,
|
|
33567
|
-
*::before,
|
|
33568
|
-
*::after {
|
|
33569
|
-
box-sizing: border-box;
|
|
33570
|
-
}
|
|
33571
|
-
|
|
33572
|
-
.itinerary-shell {
|
|
33573
|
-
width: 100%;
|
|
33574
|
-
margin: 0 auto;
|
|
33575
|
-
font-family: 'Outfit', sans-serif;
|
|
33576
|
-
color: #222;
|
|
33577
|
-
}
|
|
33578
|
-
|
|
33579
|
-
.itinerary-shell__header {
|
|
33580
|
-
margin-bottom: 24px;
|
|
33581
|
-
}
|
|
33582
|
-
|
|
33583
|
-
.itinerary-shell__title {
|
|
33584
|
-
margin: 0;
|
|
33585
|
-
font-size: 32px;
|
|
33586
|
-
line-height: 1.2;
|
|
33587
|
-
}
|
|
33588
|
-
|
|
33589
|
-
.itinerary-shell__content {
|
|
33590
|
-
display: grid;
|
|
33591
|
-
gap: 24px;
|
|
33592
|
-
}
|
|
33593
|
-
|
|
33594
|
-
.itinerary-node {
|
|
33595
|
-
border: 1px solid #e5e7eb;
|
|
33596
|
-
border-radius: 16px;
|
|
33597
|
-
overflow: hidden;
|
|
33598
|
-
background: #fff;
|
|
33599
|
-
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.04);
|
|
33600
|
-
}
|
|
33601
|
-
|
|
33602
|
-
.itinerary-node__header {
|
|
33603
|
-
display: flex;
|
|
33604
|
-
gap: 16px;
|
|
33605
|
-
align-items: center;
|
|
33606
|
-
border-bottom: 1px solid #eef0f2;
|
|
33607
|
-
background: #fafafa;
|
|
33608
|
-
}
|
|
33609
|
-
|
|
33610
|
-
.itinerary-node__day {
|
|
33611
|
-
flex: 50px 0 0;
|
|
33612
|
-
display: flex;
|
|
33613
|
-
flex-flow: column;
|
|
33614
|
-
align-items: center;
|
|
33615
|
-
justify-content: center;
|
|
33616
|
-
background: #1f9470;
|
|
33617
|
-
color: white;
|
|
33618
|
-
border: 1.5px solid var(--tide-booking-color-secondary);
|
|
33619
|
-
}
|
|
33620
|
-
|
|
33621
|
-
.itinerary-node__day-month {
|
|
33622
|
-
margin: 0px;
|
|
33623
|
-
}
|
|
33624
|
-
|
|
33625
|
-
.itinerary-node__day-day {
|
|
33626
|
-
margin: 0px;
|
|
33627
|
-
font-weight: var(--tide-booking-search-results-label-date-month-font-weight);
|
|
33628
|
-
color: var(--tide-booking-search-results-label-date-month-color);
|
|
33629
|
-
font-size: 24px;
|
|
33630
|
-
}
|
|
33631
|
-
|
|
33632
|
-
.itinerary-node__title {
|
|
33633
|
-
margin: 0;
|
|
33634
|
-
font-size: 22px;
|
|
33635
|
-
line-height: 1.25;
|
|
33636
|
-
}
|
|
33637
|
-
|
|
33638
|
-
.itinerary-node__content {
|
|
33639
|
-
display: grid;
|
|
33640
|
-
gap: 20px;
|
|
33641
|
-
padding: 20px;
|
|
33642
|
-
}
|
|
33643
|
-
|
|
33644
|
-
.itinerary-node__accommodation-banner {
|
|
33645
|
-
padding: 12px 16px;
|
|
33646
|
-
border: 1px solid #dbeafe;
|
|
33647
|
-
border-radius: 12px;
|
|
33648
|
-
background: #eff6ff;
|
|
33649
|
-
color: #1e3a8a;
|
|
33650
|
-
font-size: 14px;
|
|
33651
|
-
line-height: 1.4;
|
|
33652
|
-
}
|
|
33653
|
-
|
|
33654
|
-
.itinerary-item {
|
|
33655
|
-
display: block;
|
|
33656
|
-
border-radius: 12px;
|
|
33657
|
-
overflow: hidden;
|
|
33658
|
-
background: #fff;
|
|
33659
|
-
}
|
|
33660
|
-
|
|
33661
|
-
/* An item whose template has no content view of its own still has to read as a service. */
|
|
33662
|
-
.itinerary-item__fallback {
|
|
33663
|
-
padding: 16px 20px;
|
|
33664
|
-
border: 1px solid #e5e7eb;
|
|
33665
|
-
border-radius: 12px;
|
|
33666
|
-
}
|
|
33667
|
-
|
|
33668
|
-
.itinerary-item__fallback-title {
|
|
33669
|
-
margin: 0;
|
|
33670
|
-
font-size: 16px;
|
|
33671
|
-
font-weight: 600;
|
|
33672
|
-
color: #111827;
|
|
33673
|
-
}
|
|
33674
|
-
|
|
33675
|
-
.itinerary-item__legs {
|
|
33676
|
-
margin: 8px 0 0;
|
|
33677
|
-
padding-left: 18px;
|
|
33678
|
-
color: #374151;
|
|
33679
|
-
}
|
|
33680
|
-
|
|
33681
|
-
.itinerary-item__legs li + li {
|
|
33682
|
-
margin-top: 4px;
|
|
33683
|
-
}
|
|
33684
|
-
|
|
33685
|
-
.itinerary-item__fallback-details {
|
|
33686
|
-
margin: 8px 0 0;
|
|
33687
|
-
color: #6b7280;
|
|
33688
|
-
}
|
|
33689
|
-
|
|
33690
|
-
.itinerary-node__empty,
|
|
33691
|
-
.itinerary-empty {
|
|
33692
|
-
padding: 20px;
|
|
33693
|
-
border: 1px dashed #d1d5db;
|
|
33694
|
-
border-radius: 12px;
|
|
33695
|
-
color: #6b7280;
|
|
33696
|
-
background: #fafafa;
|
|
33697
|
-
}
|
|
33698
|
-
|
|
33699
|
-
.itinerary-default-items {
|
|
33700
|
-
border: 1px solid #e5e7eb;
|
|
33701
|
-
border-radius: 16px;
|
|
33702
|
-
padding: 20px;
|
|
33703
|
-
background: #fff;
|
|
33704
|
-
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.04);
|
|
33705
|
-
}
|
|
33706
|
-
|
|
33707
|
-
.itinerary-default-items__title {
|
|
33708
|
-
margin: 0 0 16px;
|
|
33709
|
-
font-size: 22px;
|
|
33710
|
-
}
|
|
33711
|
-
|
|
33712
|
-
@media (max-width: 768px) {
|
|
33713
|
-
.itinerary-shell {
|
|
33714
|
-
padding: 16px;
|
|
33715
|
-
}
|
|
33716
|
-
}
|
|
33717
|
-
|
|
33718
|
-
${itinerary?.styleSheetBody ?? ''}
|
|
33719
|
-
</style>
|
|
33720
|
-
|
|
33721
|
-
${html}
|
|
34564
|
+
shadowRoot.innerHTML = `
|
|
34565
|
+
<style>
|
|
34566
|
+
:host {
|
|
34567
|
+
all: initial;
|
|
34568
|
+
display: block;
|
|
34569
|
+
width: 100%;
|
|
34570
|
+
}
|
|
34571
|
+
|
|
34572
|
+
*,
|
|
34573
|
+
*::before,
|
|
34574
|
+
*::after {
|
|
34575
|
+
box-sizing: border-box;
|
|
34576
|
+
}
|
|
34577
|
+
|
|
34578
|
+
.itinerary-shell {
|
|
34579
|
+
width: 100%;
|
|
34580
|
+
margin: 0 auto;
|
|
34581
|
+
font-family: 'Outfit', sans-serif;
|
|
34582
|
+
color: #222;
|
|
34583
|
+
}
|
|
34584
|
+
|
|
34585
|
+
.itinerary-shell__header {
|
|
34586
|
+
margin-bottom: 24px;
|
|
34587
|
+
}
|
|
34588
|
+
|
|
34589
|
+
.itinerary-shell__title {
|
|
34590
|
+
margin: 0;
|
|
34591
|
+
font-size: 32px;
|
|
34592
|
+
line-height: 1.2;
|
|
34593
|
+
}
|
|
34594
|
+
|
|
34595
|
+
.itinerary-shell__content {
|
|
34596
|
+
display: grid;
|
|
34597
|
+
gap: 24px;
|
|
34598
|
+
}
|
|
34599
|
+
|
|
34600
|
+
.itinerary-node {
|
|
34601
|
+
border: 1px solid #e5e7eb;
|
|
34602
|
+
border-radius: 16px;
|
|
34603
|
+
overflow: hidden;
|
|
34604
|
+
background: #fff;
|
|
34605
|
+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.04);
|
|
34606
|
+
}
|
|
34607
|
+
|
|
34608
|
+
.itinerary-node__header {
|
|
34609
|
+
display: flex;
|
|
34610
|
+
gap: 16px;
|
|
34611
|
+
align-items: center;
|
|
34612
|
+
border-bottom: 1px solid #eef0f2;
|
|
34613
|
+
background: #fafafa;
|
|
34614
|
+
}
|
|
34615
|
+
|
|
34616
|
+
.itinerary-node__day {
|
|
34617
|
+
flex: 50px 0 0;
|
|
34618
|
+
display: flex;
|
|
34619
|
+
flex-flow: column;
|
|
34620
|
+
align-items: center;
|
|
34621
|
+
justify-content: center;
|
|
34622
|
+
background: #1f9470;
|
|
34623
|
+
color: white;
|
|
34624
|
+
border: 1.5px solid var(--tide-booking-color-secondary);
|
|
34625
|
+
}
|
|
34626
|
+
|
|
34627
|
+
.itinerary-node__day-month {
|
|
34628
|
+
margin: 0px;
|
|
34629
|
+
}
|
|
34630
|
+
|
|
34631
|
+
.itinerary-node__day-day {
|
|
34632
|
+
margin: 0px;
|
|
34633
|
+
font-weight: var(--tide-booking-search-results-label-date-month-font-weight);
|
|
34634
|
+
color: var(--tide-booking-search-results-label-date-month-color);
|
|
34635
|
+
font-size: 24px;
|
|
34636
|
+
}
|
|
34637
|
+
|
|
34638
|
+
.itinerary-node__title {
|
|
34639
|
+
margin: 0;
|
|
34640
|
+
font-size: 22px;
|
|
34641
|
+
line-height: 1.25;
|
|
34642
|
+
}
|
|
34643
|
+
|
|
34644
|
+
.itinerary-node__content {
|
|
34645
|
+
display: grid;
|
|
34646
|
+
gap: 20px;
|
|
34647
|
+
padding: 20px;
|
|
34648
|
+
}
|
|
34649
|
+
|
|
34650
|
+
.itinerary-node__accommodation-banner {
|
|
34651
|
+
padding: 12px 16px;
|
|
34652
|
+
border: 1px solid #dbeafe;
|
|
34653
|
+
border-radius: 12px;
|
|
34654
|
+
background: #eff6ff;
|
|
34655
|
+
color: #1e3a8a;
|
|
34656
|
+
font-size: 14px;
|
|
34657
|
+
line-height: 1.4;
|
|
34658
|
+
}
|
|
34659
|
+
|
|
34660
|
+
.itinerary-item {
|
|
34661
|
+
display: block;
|
|
34662
|
+
border-radius: 12px;
|
|
34663
|
+
overflow: hidden;
|
|
34664
|
+
background: #fff;
|
|
34665
|
+
}
|
|
34666
|
+
|
|
34667
|
+
/* An item whose template has no content view of its own still has to read as a service. */
|
|
34668
|
+
.itinerary-item__fallback {
|
|
34669
|
+
padding: 16px 20px;
|
|
34670
|
+
border: 1px solid #e5e7eb;
|
|
34671
|
+
border-radius: 12px;
|
|
34672
|
+
}
|
|
34673
|
+
|
|
34674
|
+
.itinerary-item__fallback-title {
|
|
34675
|
+
margin: 0;
|
|
34676
|
+
font-size: 16px;
|
|
34677
|
+
font-weight: 600;
|
|
34678
|
+
color: #111827;
|
|
34679
|
+
}
|
|
34680
|
+
|
|
34681
|
+
.itinerary-item__legs {
|
|
34682
|
+
margin: 8px 0 0;
|
|
34683
|
+
padding-left: 18px;
|
|
34684
|
+
color: #374151;
|
|
34685
|
+
}
|
|
34686
|
+
|
|
34687
|
+
.itinerary-item__legs li + li {
|
|
34688
|
+
margin-top: 4px;
|
|
34689
|
+
}
|
|
34690
|
+
|
|
34691
|
+
.itinerary-item__fallback-details {
|
|
34692
|
+
margin: 8px 0 0;
|
|
34693
|
+
color: #6b7280;
|
|
34694
|
+
}
|
|
34695
|
+
|
|
34696
|
+
.itinerary-node__empty,
|
|
34697
|
+
.itinerary-empty {
|
|
34698
|
+
padding: 20px;
|
|
34699
|
+
border: 1px dashed #d1d5db;
|
|
34700
|
+
border-radius: 12px;
|
|
34701
|
+
color: #6b7280;
|
|
34702
|
+
background: #fafafa;
|
|
34703
|
+
}
|
|
34704
|
+
|
|
34705
|
+
.itinerary-default-items {
|
|
34706
|
+
border: 1px solid #e5e7eb;
|
|
34707
|
+
border-radius: 16px;
|
|
34708
|
+
padding: 20px;
|
|
34709
|
+
background: #fff;
|
|
34710
|
+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.04);
|
|
34711
|
+
}
|
|
34712
|
+
|
|
34713
|
+
.itinerary-default-items__title {
|
|
34714
|
+
margin: 0 0 16px;
|
|
34715
|
+
font-size: 22px;
|
|
34716
|
+
}
|
|
34717
|
+
|
|
34718
|
+
@media (max-width: 768px) {
|
|
34719
|
+
.itinerary-shell {
|
|
34720
|
+
padding: 16px;
|
|
34721
|
+
}
|
|
34722
|
+
}
|
|
34723
|
+
|
|
34724
|
+
${itinerary?.styleSheetBody ?? ''}
|
|
34725
|
+
</style>
|
|
34726
|
+
|
|
34727
|
+
${html}
|
|
33722
34728
|
`;
|
|
33723
34729
|
}, [html, itinerary?.styleSheetBody, isLoading]);
|
|
33724
34730
|
return (React__default.createElement(React__default.Fragment, null,
|
|
@@ -34172,7 +35178,7 @@ const BookPackagingEntry = ({ activeSearchSeed, isLoading, isConfirmationPage })
|
|
|
34172
35178
|
const formik = useFormik({
|
|
34173
35179
|
initialValues,
|
|
34174
35180
|
enableReinitialize: true,
|
|
34175
|
-
validate: (values) => validateForm$1(values, false, 'b2c', translations, travellersSettings.formFields, travellersSettings.mainBookerFormFields),
|
|
35181
|
+
validate: (values) => validateForm$1(values, false, 'b2c', translations, travellersSettings.formFields, travellersSettings.mainBookerFormFields, undefined, undefined, editablePackagingEntry?.paxFields ?? []),
|
|
34176
35182
|
onSubmit: (values) => {
|
|
34177
35183
|
if (!editablePackagingEntry)
|
|
34178
35184
|
return;
|
|
@@ -34257,7 +35263,7 @@ const BookPackagingEntry = ({ activeSearchSeed, isLoading, isConfirmationPage })
|
|
|
34257
35263
|
".\u00A0",
|
|
34258
35264
|
stepLabels[step])) },
|
|
34259
35265
|
isConfirmationPage && isLoading && React__default.createElement(Spinner, { label: translations.SUMMARY.PROCESS_BOOKING }),
|
|
34260
|
-
currentStep === 0 && (React__default.createElement(SharedTravelersForm, { formik: formik, translations: translations, travellersSettings: travellersSettings, countries: countries, travelersFirstStep: false, isUnavailable: false, useCompactForm: false, showAgentSelection: false })),
|
|
35266
|
+
currentStep === 0 && (React__default.createElement(SharedTravelersForm, { formik: formik, translations: translations, travellersSettings: travellersSettings, countries: countries, travelersFirstStep: false, isUnavailable: false, useCompactForm: false, showAgentSelection: false, paxFields: editablePackagingEntry?.paxFields ?? [] })),
|
|
34261
35267
|
currentStep === 1 && (React__default.createElement(SharedSummary, { translations: translations, travelerFormValues: formik.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.createElement("button", { type: "button", title: translations.STEPS.PREVIOUS, onClick: () => dispatch(setCurrentStep(0)), className: "cta cta--secondary" }, translations.STEPS.PREVIOUS)) })),
|
|
34262
35268
|
currentStep === 2 && (React__default.createElement(SharedConfirmation, { bookingNumber: bookingNumber ?? editablePackagingEntry?.dossierNumber ?? '', isOption: false, isOffer: false, translations: translations.CONFIRMATION })),
|
|
34263
35269
|
currentStep === 3 && React__default.createElement("div", null))),
|
|
@@ -34321,6 +35327,7 @@ const TrajectResults = ({ isLoading }) => {
|
|
|
34321
35327
|
const locale = getLocale(context?.languageCode ?? 'en-GB');
|
|
34322
35328
|
const { trajectNodeAvailability, trajectFlightAvailability, trajectDaySuggestions, editablePackagingEntry } = useSelector((state) => state.searchResults);
|
|
34323
35329
|
const trajectEntry = context?.trajectEntry;
|
|
35330
|
+
const bookedMode = isBookedEntryTraject(trajectEntry);
|
|
34324
35331
|
const hideShowMoreResults = !!context?.hideShowMoreResults;
|
|
34325
35332
|
const loadDayActivitiesOnDemand = !!context?.loadDayActivitiesOnDemand;
|
|
34326
35333
|
const showNonPreferredHotels = !!context?.showNonPreferredHotels;
|
|
@@ -34377,7 +35384,7 @@ const TrajectResults = ({ isLoading }) => {
|
|
|
34377
35384
|
if (!result)
|
|
34378
35385
|
return;
|
|
34379
35386
|
dispatch(setTrajectNodeSelection({ lineGuid: item.line.guid, code }));
|
|
34380
|
-
|
|
35387
|
+
buildNodeSelectionLines(item, result, bookedMode).forEach((line) => dispatch(updateEditableEntryLine(line)));
|
|
34381
35388
|
};
|
|
34382
35389
|
/** The remaining legs for a flight node open in the fly-in, as the regular flight results do. */
|
|
34383
35390
|
const handleShowMoreFlights = (item) => {
|
|
@@ -34465,7 +35472,7 @@ const TrajectResults = ({ isLoading }) => {
|
|
|
34465
35472
|
node.preferredAccommodationName,
|
|
34466
35473
|
" \u2014 ",
|
|
34467
35474
|
translations.SRP.NO_RESULTS))),
|
|
34468
|
-
React__default.createElement("div", { className: "search__results__cards search__results__cards--scroll" }, [selected, ...configured, ...fill].map((result) => (React__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 })))),
|
|
35475
|
+
React__default.createElement("div", { className: "search__results__cards search__results__cards--scroll" }, [selected, ...configured, ...fill].map((result) => (React__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 })))),
|
|
34469
35476
|
remaining > 0 && !hideShowMoreResults && (React__default.createElement("div", { className: "search__results__cards__actions" },
|
|
34470
35477
|
React__default.createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => handleShowMore(item) },
|
|
34471
35478
|
translations.SRP.SHOW_MORE,
|
|
@@ -34475,7 +35482,7 @@ const TrajectResults = ({ isLoading }) => {
|
|
|
34475
35482
|
};
|
|
34476
35483
|
const handleSelectFlight = (item, flight) => {
|
|
34477
35484
|
dispatch(setTrajectFlightSelection({ lineGuid: item.line.guid, guid: flight.outwardGuid }));
|
|
34478
|
-
dispatch(updateEditableEntryLine(
|
|
35485
|
+
dispatch(updateEditableEntryLine(buildFlightSelectionLine(item, flight, bookedMode)));
|
|
34479
35486
|
};
|
|
34480
35487
|
const renderFlightNode = (item) => {
|
|
34481
35488
|
const { node, line } = item;
|
|
@@ -34501,7 +35508,7 @@ const TrajectResults = ({ isLoading }) => {
|
|
|
34501
35508
|
status === 'unavailable' && React__default.createElement("div", { className: "no-results" }, translations.SRP.NO_RESULTS),
|
|
34502
35509
|
status === 'resolved' && selected && (React__default.createElement(React__default.Fragment, null,
|
|
34503
35510
|
React__default.createElement("div", { className: "search__results__cards search__results__cards--extended" },
|
|
34504
|
-
React__default.createElement(IndependentFlightOption, { key: `flight-${selected.outwardGuid}`, item: selected.outward, guid: selected.outwardGuid, selectedGuid: selected.outwardGuid, isOutward: true, showSelectedState: true, price: selected.price }),
|
|
35511
|
+
React__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 }),
|
|
34505
35512
|
visible.map((result) => (React__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) })))),
|
|
34506
35513
|
others.length > 2 && !hideShowMoreResults && (React__default.createElement("div", { className: "search__results__cards__actions" },
|
|
34507
35514
|
React__default.createElement("button", { type: "button", className: "cta cta--secondary", onClick: () => handleShowMoreFlights(item) },
|
|
@@ -34585,7 +35592,8 @@ const TrajectResults = ({ isLoading }) => {
|
|
|
34585
35592
|
*/
|
|
34586
35593
|
const renderActivityCard = ({ result, item }) => {
|
|
34587
35594
|
const isSelected = item ? trajectNodeAvailability[item.line.guid]?.selectedCode === result.code : getAddedExcursionLines(result).length > 0;
|
|
34588
|
-
|
|
35595
|
+
const isBookedCard = !!item && bookedMode && matchesCode(result, item.node.code ?? '');
|
|
35596
|
+
return (React__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 }));
|
|
34589
35597
|
};
|
|
34590
35598
|
if (isLoading)
|
|
34591
35599
|
return React__default.createElement(Spinner, { label: translations.SRP.LOADING_ITINERARY });
|
|
@@ -34629,6 +35637,7 @@ const useTrajectAvailability = () => {
|
|
|
34629
35637
|
const controller = new AbortController();
|
|
34630
35638
|
dispatch(resetTrajectAvailability());
|
|
34631
35639
|
dispatch(setTrajectResolving(true));
|
|
35640
|
+
const bookedMode = isBookedEntryTraject(trajectEntry);
|
|
34632
35641
|
const config = {
|
|
34633
35642
|
host: context.tideConnection.host,
|
|
34634
35643
|
apiKey: context.tideConnection.apiKey
|
|
@@ -34690,6 +35699,17 @@ const useTrajectAvailability = () => {
|
|
|
34690
35699
|
});
|
|
34691
35700
|
const configuredCodes = new Set(configuredResults.map((x) => x.code));
|
|
34692
35701
|
const destinationResults = areaResults.filter((x) => !configuredCodes.has(x.code));
|
|
35702
|
+
if (bookedMode) {
|
|
35703
|
+
const results = [...configuredResults, ...destinationResults];
|
|
35704
|
+
let booked = results.find((x) => matchesCode(x, item.node.code ?? '')) ?? null;
|
|
35705
|
+
// Not searchable any more (sold out, delisted) — drawn from the line instead.
|
|
35706
|
+
if (!booked) {
|
|
35707
|
+
booked = buildBookedNodeResult(item.node, item.lines);
|
|
35708
|
+
results.unshift(booked);
|
|
35709
|
+
}
|
|
35710
|
+
dispatch(setTrajectNodeAvailability({ lineGuid, results, selectedCode: booked.code, preferredUnavailable: false }));
|
|
35711
|
+
return [];
|
|
35712
|
+
}
|
|
34693
35713
|
const preferredCandidate = candidates.find((x) => x.isPreferred) ?? candidates[0];
|
|
34694
35714
|
const preferredUnavailable = configuredResults.length
|
|
34695
35715
|
? !!preferredCandidate && !configuredResults.some((x) => matchesCode(x, preferredCandidate.code))
|
|
@@ -34712,6 +35732,11 @@ const useTrajectAvailability = () => {
|
|
|
34712
35732
|
if (controller.signal.aborted)
|
|
34713
35733
|
return [];
|
|
34714
35734
|
console.error('Failed to resolve traject node availability', item.node.name, error);
|
|
35735
|
+
if (bookedMode) {
|
|
35736
|
+
const booked = buildBookedNodeResult(item.node, item.lines);
|
|
35737
|
+
dispatch(setTrajectNodeAvailability({ lineGuid, results: [booked], selectedCode: booked.code, preferredUnavailable: false }));
|
|
35738
|
+
return [];
|
|
35739
|
+
}
|
|
34715
35740
|
dispatch(setTrajectNodeAvailability({ lineGuid, results: [], selectedCode: null, preferredUnavailable: false }));
|
|
34716
35741
|
return item.lines.map((x) => x.guid);
|
|
34717
35742
|
}
|
|
@@ -34719,6 +35744,11 @@ const useTrajectAvailability = () => {
|
|
|
34719
35744
|
const resolveFlightNode = async (item) => {
|
|
34720
35745
|
const lineGuid = item.line.guid;
|
|
34721
35746
|
if (!item.node.departureAirportCode || !item.node.arrivalAirportCode) {
|
|
35747
|
+
if (bookedMode) {
|
|
35748
|
+
const booked = buildBookedFlightResult(item.line);
|
|
35749
|
+
dispatch(setTrajectFlightAvailability({ lineGuid, results: [booked], selectedGuid: booked.outwardGuid }));
|
|
35750
|
+
return [];
|
|
35751
|
+
}
|
|
34722
35752
|
dispatch(setTrajectFlightAvailability({ lineGuid, results: [], selectedGuid: null }));
|
|
34723
35753
|
return item.lines.map((x) => x.guid);
|
|
34724
35754
|
}
|
|
@@ -34727,6 +35757,11 @@ const useTrajectAvailability = () => {
|
|
|
34727
35757
|
const flights = (await build.searchPackagingFlights(config, buildTrajectFlightRequest(item, searchContext, flightPax), controller.signal)) ?? [];
|
|
34728
35758
|
if (controller.signal.aborted)
|
|
34729
35759
|
return [];
|
|
35760
|
+
if (bookedMode) {
|
|
35761
|
+
const booked = buildBookedFlightResult(item.line);
|
|
35762
|
+
dispatch(setTrajectFlightAvailability({ lineGuid, results: [booked, ...flights], selectedGuid: booked.outwardGuid }));
|
|
35763
|
+
return [];
|
|
35764
|
+
}
|
|
34730
35765
|
const picked = pickCheapestFlight(flights);
|
|
34731
35766
|
dispatch(setTrajectFlightAvailability({ lineGuid, results: flights, selectedGuid: picked?.outwardGuid ?? null }));
|
|
34732
35767
|
if (!picked)
|
|
@@ -34738,6 +35773,11 @@ const useTrajectAvailability = () => {
|
|
|
34738
35773
|
if (controller.signal.aborted)
|
|
34739
35774
|
return [];
|
|
34740
35775
|
console.error('Failed to resolve traject flight', item.node.name, error);
|
|
35776
|
+
if (bookedMode) {
|
|
35777
|
+
const booked = buildBookedFlightResult(item.line);
|
|
35778
|
+
dispatch(setTrajectFlightAvailability({ lineGuid, results: [booked], selectedGuid: booked.outwardGuid }));
|
|
35779
|
+
return [];
|
|
35780
|
+
}
|
|
34741
35781
|
dispatch(setTrajectFlightAvailability({ lineGuid, results: [], selectedGuid: null }));
|
|
34742
35782
|
return item.lines.map((x) => x.guid);
|
|
34743
35783
|
}
|
|
@@ -34775,6 +35815,7 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
|
|
|
34775
35815
|
const [detailsIsLoading, setDetailsIsLoading] = useState(false);
|
|
34776
35816
|
const [pricesAreLoading, setPricesAreLoading] = useState(false);
|
|
34777
35817
|
const [itineraryIsLoading, setItineraryIsLoading] = useState(false);
|
|
35818
|
+
const [entrySaving, setEntrySaving] = useState(false);
|
|
34778
35819
|
const [itineraryOpen, setItineraryOpen] = useState(false);
|
|
34779
35820
|
const [isBookingConfirmation, setIsBookingConfirmation] = useState(false);
|
|
34780
35821
|
const [selectedAccommodationSeed, setSelectedAccommodationSeed] = useState(null);
|
|
@@ -35531,6 +36572,31 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
|
|
|
35531
36572
|
useEffect(() => {
|
|
35532
36573
|
setInitialFiltersSet(false);
|
|
35533
36574
|
}, [activeSearchSeed]);
|
|
36575
|
+
const handleSaveEntry = async () => {
|
|
36576
|
+
if (!context || !bookableEntry || entrySaving)
|
|
36577
|
+
return;
|
|
36578
|
+
setEntrySaving(true);
|
|
36579
|
+
try {
|
|
36580
|
+
const config = {
|
|
36581
|
+
host: context.tideConnection.host,
|
|
36582
|
+
apiKey: context.tideConnection.apiKey
|
|
36583
|
+
};
|
|
36584
|
+
const request = {
|
|
36585
|
+
language: context.languageCode ?? 'en-GB',
|
|
36586
|
+
officeId: context.tideConnection.officeId,
|
|
36587
|
+
catalogueId: context.searchConfiguration.defaultCatalogueId ?? first(context?.tideConnection?.catalogueIds) ?? 1,
|
|
36588
|
+
agentId: context.agentId,
|
|
36589
|
+
payload: bookableEntry
|
|
36590
|
+
};
|
|
36591
|
+
await build.savePackagingEntry(config, request);
|
|
36592
|
+
if (typeof window !== 'undefined')
|
|
36593
|
+
window.location.reload();
|
|
36594
|
+
}
|
|
36595
|
+
catch (err) {
|
|
36596
|
+
console.error('Failed to save the entry', err);
|
|
36597
|
+
setEntrySaving(false);
|
|
36598
|
+
}
|
|
36599
|
+
};
|
|
35534
36600
|
useEffect(() => {
|
|
35535
36601
|
const fetchPriceDetails = async () => {
|
|
35536
36602
|
if (!context || !editablePackagingEntry || isEmpty$1(editablePackagingEntry.lines))
|
|
@@ -35915,11 +36981,18 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
|
|
|
35915
36981
|
const id = paxId++;
|
|
35916
36982
|
pax.push({
|
|
35917
36983
|
id,
|
|
36984
|
+
guid: '',
|
|
35918
36985
|
firstName: _.firstName || '',
|
|
35919
36986
|
lastName: _.lastName || '',
|
|
35920
|
-
dateOfBirth: _.dateOfBirth ||
|
|
35921
|
-
age: _.age ||
|
|
35922
|
-
isMainBooker: roomIndex === 0 && paxIndex === 0
|
|
36987
|
+
dateOfBirth: _.dateOfBirth || undefined,
|
|
36988
|
+
age: _.age || undefined,
|
|
36989
|
+
isMainBooker: roomIndex === 0 && paxIndex === 0,
|
|
36990
|
+
email: '',
|
|
36991
|
+
weight: null,
|
|
36992
|
+
weightType: build.WeightType.kilograms,
|
|
36993
|
+
height: null,
|
|
36994
|
+
heightType: build.HeightType.centimeters,
|
|
36995
|
+
customFields: []
|
|
35923
36996
|
});
|
|
35924
36997
|
return id;
|
|
35925
36998
|
});
|
|
@@ -35939,6 +37012,7 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
|
|
|
35939
37012
|
depositAmount: 0,
|
|
35940
37013
|
pax,
|
|
35941
37014
|
rooms,
|
|
37015
|
+
paxFields: [],
|
|
35942
37016
|
lines: [],
|
|
35943
37017
|
address: {}
|
|
35944
37018
|
};
|
|
@@ -35958,7 +37032,7 @@ const SearchResultsContainer = ({ onBookingStarted }) => {
|
|
|
35958
37032
|
context.searchConfiguration.qsmType != build.PortalQsmType.AccommodationAndFlight && context.showFilters && (React__default.createElement(Filters, { initialFilters: initialFilters, filters: filters, isOpen: filtersOpen, handleSetIsOpen: () => setFiltersOpen(!filtersOpen),
|
|
35959
37033
|
// handleApplyFilters={() => setSearchTrigger((prev) => prev + 1)}
|
|
35960
37034
|
isLoading: isLoading, setFilters: (filters) => dispatch(setFilters(filters)), resetFilters: (filters) => dispatch(resetFilters(filters)) })),
|
|
35961
|
-
context.searchConfiguration.qsmType === build.PortalQsmType.AccommodationAndFlight && (React__default.createElement(Itinerary, { isOpen: itineraryOpen, handleSetIsOpen: () => setItineraryOpen(!itineraryOpen), isLoading: isLoading || pricesAreLoading || flightsLoading || itineraryIsLoading || trajectResolving, onEditAccommodation: handleEditAccommodation })),
|
|
37035
|
+
context.searchConfiguration.qsmType === build.PortalQsmType.AccommodationAndFlight && (React__default.createElement(Itinerary, { isOpen: itineraryOpen, handleSetIsOpen: () => setItineraryOpen(!itineraryOpen), isLoading: isLoading || pricesAreLoading || flightsLoading || itineraryIsLoading || trajectResolving || entrySaving, onEditAccommodation: handleEditAccommodation, onSaveEntry: handleSaveEntry })),
|
|
35962
37036
|
React__default.createElement("div", { className: "search__results" },
|
|
35963
37037
|
isMobile && (React__default.createElement("div", { className: "search__result-row" },
|
|
35964
37038
|
React__default.createElement("div", { className: "search__results__actions" },
|
|
@@ -36046,8 +37120,15 @@ const createSearchResultsStore = () => configureStore({
|
|
|
36046
37120
|
|
|
36047
37121
|
const SearchResults = ({ configuration, onBookingStarted }) => {
|
|
36048
37122
|
const store = React__default.useMemo(() => createSearchResultsStore(), []);
|
|
37123
|
+
// A booked entry opened through its magic link gets the traject services screen by carrying a
|
|
37124
|
+
// traject rebuilt from its own lines. The magic link on the entry is what marks the mode.
|
|
37125
|
+
const effectiveConfiguration = React__default.useMemo(() => {
|
|
37126
|
+
if (!configuration.allowBookedEntryChanges || configuration.trajectEntry || !configuration.packagingEntry?.magicLink)
|
|
37127
|
+
return configuration;
|
|
37128
|
+
return { ...configuration, trajectEntry: buildBookedTrajectEntry(configuration.packagingEntry) };
|
|
37129
|
+
}, [configuration]);
|
|
36049
37130
|
return (React__default.createElement(Provider, { store: store },
|
|
36050
|
-
React__default.createElement(SearchResultsConfigurationContext.Provider, { value:
|
|
37131
|
+
React__default.createElement(SearchResultsConfigurationContext.Provider, { value: effectiveConfiguration },
|
|
36051
37132
|
React__default.createElement(SearchResultsContainer, { onBookingStarted: onBookingStarted }))));
|
|
36052
37133
|
};
|
|
36053
37134
|
|
|
@@ -38942,20 +40023,20 @@ function ItineraryMapView({ destinations: propDestinations, adults = 2, dateFrom
|
|
|
38942
40023
|
const isStartEnd = dest.isStart || dest.isEnd;
|
|
38943
40024
|
let iconHtml;
|
|
38944
40025
|
if (isStartEnd) {
|
|
38945
|
-
iconHtml = `
|
|
38946
|
-
<div class="map-view__marker map-view__marker--plane">
|
|
38947
|
-
<svg viewBox="0 0 576 512" fill="currentColor" width="16" height="16" xmlns="http://www.w3.org/2000/svg">
|
|
38948
|
-
<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"/>
|
|
38949
|
-
</svg>
|
|
40026
|
+
iconHtml = `
|
|
40027
|
+
<div class="map-view__marker map-view__marker--plane">
|
|
40028
|
+
<svg viewBox="0 0 576 512" fill="currentColor" width="16" height="16" xmlns="http://www.w3.org/2000/svg">
|
|
40029
|
+
<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"/>
|
|
40030
|
+
</svg>
|
|
38950
40031
|
</div>`;
|
|
38951
40032
|
}
|
|
38952
40033
|
else {
|
|
38953
40034
|
stopIndex++;
|
|
38954
40035
|
const imgStyle = dest.imageUrl ? `style="background-image:url('${dest.imageUrl}')"` : '';
|
|
38955
|
-
iconHtml = `
|
|
38956
|
-
<div class="map-view__marker map-view__marker--stop" ${imgStyle}>
|
|
38957
|
-
${!dest.imageUrl ? `<span class="map-view__marker-number">${stopIndex}</span>` : ''}
|
|
38958
|
-
<span class="map-view__marker-badge">${stopIndex}</span>
|
|
40036
|
+
iconHtml = `
|
|
40037
|
+
<div class="map-view__marker map-view__marker--stop" ${imgStyle}>
|
|
40038
|
+
${!dest.imageUrl ? `<span class="map-view__marker-number">${stopIndex}</span>` : ''}
|
|
40039
|
+
<span class="map-view__marker-badge">${stopIndex}</span>
|
|
38959
40040
|
</div>`;
|
|
38960
40041
|
}
|
|
38961
40042
|
const icon = L.divIcon({
|